All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v13 0/9] LPC: legacy ISA I/O support
@ 2018-02-13 17:45 ` John Garry
  0 siblings, 0 replies; 66+ messages in thread
From: John Garry @ 2018-02-13 17:45 UTC (permalink / raw)
  To: mika.westerberg, rafael, lorenzo.pieralisi, rjw, hanjun.guo,
	robh+dt, bhelgaas, arnd, mark.rutland, olof, dann.frazier,
	andy.shevchenko, robh
  Cc: joe, benh, linux-pci, linux-kernel, linux-acpi, linuxarm,
	minyard, devicetree, linux-arch, rdunlap

This patchset supports the IPMI-bt device attached to the Low-Pin-Count
interface implemented on Hisilicon Hip06/Hip07 SoC.
                        -----------
                        | LPC host|
                        |         |
                        -----------
                             |
                _____________V_______________LPC
                  |                       |
                  V                       V
                                     ------------
                                     |  BT(ipmi)|
                                     ------------

When master accesses those peripherals beneath the Hip06/Hip07 LPC, a specific
LPC driver is needed to make LPC host generate the standard LPC I/O cycles with
the target peripherals'I/O port addresses. But on curent arm64 world, there is
no real I/O accesses. All the I/O operations through in/out accessors are based
on MMIO ranges; on Hip06/Hip07 LPC the I/O accesses are performed through driver
specific accessors rather than MMIO.
To solve this issue and keep the relevant existing peripherals' drivers untouched,
this patchset:
   - introduces a generic I/O space management framework, LIBIO, to support I/O
     operations on host controllers operating either on MMIO buses or on buses
     requiring specific driver I/O accessors;
   - redefines the in/out accessors to provide a unified interface for both MMIO
     and driver specific I/O operations. Using LIBIO, th call of in/out() from
     the host children drivers, such as ipmi-si, will be redirected to the
     corresponding device-specific I/O hooks to perform the I/O accesses.

Based on this patch-set, all the I/O accesses to Hip06/Hip07 LPC peripherals can
be supported without any changes on the existing ipmi-si driver.

The whole patchset has been tested on Hip07 D05 board both using DTB and ACPI.

Differences to v12:
    - Addressed ACPI comments from Rafael and Andy, including:
     - added SPDX license identifiers (other new files in the series got this also)
     - fixed style issues, like superflous newlines and symbol naming
     - add fuller acpi_indirectio.c patch commit message
     - dropped acpi_indirectio_host_data (author's decision) to simplify
    - added Rob Herring's tag
    - rebase to linux-next 20180212
    
Differences to v11:
    - fixed build errors for i386, m68k, and tile
    - added a comment in LPC driver commit log why we set
       the kernel config as bool
    - some tidying logic_pio code

Differences to v10:
    - dropped CONFIG_LOGIC_PIO. Reason is that CONFIG_PCI
      depends on this, and CONFIG_PCI is a per-arch CONFIG.
      So we would require all arch's kconfig to select this.
    - Addressed Dann Frazier's comments on LPC driver, and
      sopme other cleanup
    - Moved logic_pio.h to be included in generic asm io.h
    - Fixed ACPI indirect IO host setup to handle >1 child
    - Relocated ACPI indirect IO host setup code to
      drivers/acpi
    - Rebased to linux next-20180118

Changes from v9:
  - patch 2 has been split into 3 patches according to Bjorn comments on
    v9 thread
  - patch 1 has been reworked accordign to Bjorn comments on v9
  - now logic_pio_trans_hwaddr() has a sanity check to make sure the resource
    size fits into the assigned range
  - in patch 5 the MFD framework has been used to probe the LPC children
    according to the suggestion from Mika Westerberg
  - Maintaner has changed to Huawei Linuxarm mailing list

Changes from v8:
  - Simplified LIB IO framewrok
  - Moved INDIRECT PIO ACPI framework under acpi/arm64
  - Renamed occurrences of "lib io" and "indirect io" to "lib pio" and
    "indirect pio" to keep the patchset nomenclature consistent
  - Removed Alignment reuqirements
  - Moved LPC specific code out of ACPI common framework
  - Now PIO indirect HW ranges can overlap
  - Changed HiSilicon LPC driver maintainer (Gabriele Paoloni now) and split
    maintaner file modifications in a separate commit
  - Removed the commit with the DT nodes support for hip06 and hip07 (to be
    pushed separately)
  - Added a checking on ioport_map() not to break that function as Arnd points
    out in V7 review thread;
  - fixed the compile issues on alpha, m68k;

Changes from V7:
  - Based on Arnd's comment, rename the LIBIO as LOGIC_PIO;
  - Improved the mapping process in LOGIC_PIO to gain better efficiency when
    redirecting the I/O accesses to right device driver;
  - To reduce the impact on PCI MMIO to a minimum, add a new
    CONFIG_INDIRECT_PIO for indirect-IO hosts/devices;
  - Added a new ACPI handler for indirect-IO hosts/devices;
  - Fixed the compile issues on V6;

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

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

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

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

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

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

V12 thread here: https://lkml.org/lkml/2018/1/23/508
V11 thread here: https://lkml.org/lkml/2018/1/21/38
V10 thread here: https://lkml.org/lkml/2017/10/27/465
V9 thread here: https://lkml.org/lkml/2017/5/25/263
V8 thread here: https://lkml.org/lkml/2017/3/30/619
V7 thread here: https://lkml.org/lkml/2017/3/12/279
v6 thread here: https://lkml.org/lkml/2017/1/24/25
v5 thread here: https://lkml.org/lkml/2016/11/7/955
v4 thread here: https://lkml.org/lkml/2016/10/20/149
v3 thread here: https://lkml.org/lkml/2016/9/14/326
v2 thread here: https://lkml.org/lkml/2016/9/7/356
v1 thread here: https://lkml.org/lkml/2015/12/29/154

Gabriele Paoloni (2):
  PCI: Remove unused __weak attribute in pci_register_io_range()
  PCI: Add fwnode handler as input param of pci_register_io_range()

John Garry (3):
  ACPI: Translate the I/O range of non-MMIO devices before scanning
  LPC, ACPI: Add the HISI LPC ACPI support
  MAINTAINERS: Add maintainer for HiSilicon LPC driver

Zhichang Yuan (4):
  LIB: Introduce a generic PIO mapping method
  PCI: Apply the new generic I/O management on PCI IO hosts
  OF: Add missing I/O range exception for indirect-IO devices
  LPC: Support the LPC host on Hip06/Hip07 with DT bindings

 .../arm/hisilicon/hisilicon-low-pin-count.txt      |  33 ++
 MAINTAINERS                                        |   7 +
 drivers/acpi/arm64/Makefile                        |   1 +
 drivers/acpi/arm64/acpi_indirectio.c               | 251 ++++++++++
 drivers/acpi/internal.h                            |   5 +
 drivers/acpi/pci_root.c                            |   8 +-
 drivers/acpi/scan.c                                |   1 +
 drivers/bus/Kconfig                                |   8 +
 drivers/bus/Makefile                               |   1 +
 drivers/bus/hisi_lpc.c                             | 522 +++++++++++++++++++++
 drivers/of/address.c                               |  96 +++-
 drivers/pci/pci.c                                  |  98 +---
 include/asm-generic/io.h                           |   4 +-
 include/linux/logic_pio.h                          | 131 ++++++
 include/linux/pci.h                                |   3 +-
 lib/Kconfig                                        |  15 +
 lib/Makefile                                       |   2 +
 lib/logic_pio.c                                    | 272 +++++++++++
 18 files changed, 1357 insertions(+), 101 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/arm/hisilicon/hisilicon-low-pin-count.txt
 create mode 100644 drivers/acpi/arm64/acpi_indirectio.c
 create mode 100644 drivers/bus/hisi_lpc.c
 create mode 100644 include/linux/logic_pio.h
 create mode 100644 lib/logic_pio.c

-- 
1.9.1

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

* [PATCH v13 0/9] LPC: legacy ISA I/O support
@ 2018-02-13 17:45 ` John Garry
  0 siblings, 0 replies; 66+ messages in thread
From: John Garry @ 2018-02-13 17:45 UTC (permalink / raw)
  To: mika.westerberg, rafael, lorenzo.pieralisi, rjw, hanjun.guo,
	robh+dt, bhelgaas, arnd, mark.rutland, olof, dann.frazier,
	andy.shevchenko, robh
  Cc: joe, benh, linux-pci, linux-kernel, linux-acpi, linuxarm,
	minyard, devicetree, linux-arch, rdunlap

This patchset supports the IPMI-bt device attached to the Low-Pin-Count
interface implemented on Hisilicon Hip06/Hip07 SoC.
                        -----------
                        | LPC host|
                        |         |
                        -----------
                             |
                _____________V_______________LPC
                  |                       |
                  V                       V
                                     ------------
                                     |  BT(ipmi)|
                                     ------------

When master accesses those peripherals beneath the Hip06/Hip07 LPC, a specific
LPC driver is needed to make LPC host generate the standard LPC I/O cycles with
the target peripherals'I/O port addresses. But on curent arm64 world, there is
no real I/O accesses. All the I/O operations through in/out accessors are based
on MMIO ranges; on Hip06/Hip07 LPC the I/O accesses are performed through driver
specific accessors rather than MMIO.
To solve this issue and keep the relevant existing peripherals' drivers untouched,
this patchset:
   - introduces a generic I/O space management framework, LIBIO, to support I/O
     operations on host controllers operating either on MMIO buses or on buses
     requiring specific driver I/O accessors;
   - redefines the in/out accessors to provide a unified interface for both MMIO
     and driver specific I/O operations. Using LIBIO, th call of in/out() from
     the host children drivers, such as ipmi-si, will be redirected to the
     corresponding device-specific I/O hooks to perform the I/O accesses.

Based on this patch-set, all the I/O accesses to Hip06/Hip07 LPC peripherals can
be supported without any changes on the existing ipmi-si driver.

The whole patchset has been tested on Hip07 D05 board both using DTB and ACPI.

Differences to v12:
    - Addressed ACPI comments from Rafael and Andy, including:
     - added SPDX license identifiers (other new files in the series got this also)
     - fixed style issues, like superflous newlines and symbol naming
     - add fuller acpi_indirectio.c patch commit message
     - dropped acpi_indirectio_host_data (author's decision) to simplify
    - added Rob Herring's tag
    - rebase to linux-next 20180212
    
Differences to v11:
    - fixed build errors for i386, m68k, and tile
    - added a comment in LPC driver commit log why we set
       the kernel config as bool
    - some tidying logic_pio code

Differences to v10:
    - dropped CONFIG_LOGIC_PIO. Reason is that CONFIG_PCI
      depends on this, and CONFIG_PCI is a per-arch CONFIG.
      So we would require all arch's kconfig to select this.
    - Addressed Dann Frazier's comments on LPC driver, and
      sopme other cleanup
    - Moved logic_pio.h to be included in generic asm io.h
    - Fixed ACPI indirect IO host setup to handle >1 child
    - Relocated ACPI indirect IO host setup code to
      drivers/acpi
    - Rebased to linux next-20180118

Changes from v9:
  - patch 2 has been split into 3 patches according to Bjorn comments on
    v9 thread
  - patch 1 has been reworked accordign to Bjorn comments on v9
  - now logic_pio_trans_hwaddr() has a sanity check to make sure the resource
    size fits into the assigned range
  - in patch 5 the MFD framework has been used to probe the LPC children
    according to the suggestion from Mika Westerberg
  - Maintaner has changed to Huawei Linuxarm mailing list

Changes from v8:
  - Simplified LIB IO framewrok
  - Moved INDIRECT PIO ACPI framework under acpi/arm64
  - Renamed occurrences of "lib io" and "indirect io" to "lib pio" and
    "indirect pio" to keep the patchset nomenclature consistent
  - Removed Alignment reuqirements
  - Moved LPC specific code out of ACPI common framework
  - Now PIO indirect HW ranges can overlap
  - Changed HiSilicon LPC driver maintainer (Gabriele Paoloni now) and split
    maintaner file modifications in a separate commit
  - Removed the commit with the DT nodes support for hip06 and hip07 (to be
    pushed separately)
  - Added a checking on ioport_map() not to break that function as Arnd points
    out in V7 review thread;
  - fixed the compile issues on alpha, m68k;

Changes from V7:
  - Based on Arnd's comment, rename the LIBIO as LOGIC_PIO;
  - Improved the mapping process in LOGIC_PIO to gain better efficiency when
    redirecting the I/O accesses to right device driver;
  - To reduce the impact on PCI MMIO to a minimum, add a new
    CONFIG_INDIRECT_PIO for indirect-IO hosts/devices;
  - Added a new ACPI handler for indirect-IO hosts/devices;
  - Fixed the compile issues on V6;

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

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

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

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

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

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

V12 thread here: https://lkml.org/lkml/2018/1/23/508
V11 thread here: https://lkml.org/lkml/2018/1/21/38
V10 thread here: https://lkml.org/lkml/2017/10/27/465
V9 thread here: https://lkml.org/lkml/2017/5/25/263
V8 thread here: https://lkml.org/lkml/2017/3/30/619
V7 thread here: https://lkml.org/lkml/2017/3/12/279
v6 thread here: https://lkml.org/lkml/2017/1/24/25
v5 thread here: https://lkml.org/lkml/2016/11/7/955
v4 thread here: https://lkml.org/lkml/2016/10/20/149
v3 thread here: https://lkml.org/lkml/2016/9/14/326
v2 thread here: https://lkml.org/lkml/2016/9/7/356
v1 thread here: https://lkml.org/lkml/2015/12/29/154

Gabriele Paoloni (2):
  PCI: Remove unused __weak attribute in pci_register_io_range()
  PCI: Add fwnode handler as input param of pci_register_io_range()

John Garry (3):
  ACPI: Translate the I/O range of non-MMIO devices before scanning
  LPC, ACPI: Add the HISI LPC ACPI support
  MAINTAINERS: Add maintainer for HiSilicon LPC driver

Zhichang Yuan (4):
  LIB: Introduce a generic PIO mapping method
  PCI: Apply the new generic I/O management on PCI IO hosts
  OF: Add missing I/O range exception for indirect-IO devices
  LPC: Support the LPC host on Hip06/Hip07 with DT bindings

 .../arm/hisilicon/hisilicon-low-pin-count.txt      |  33 ++
 MAINTAINERS                                        |   7 +
 drivers/acpi/arm64/Makefile                        |   1 +
 drivers/acpi/arm64/acpi_indirectio.c               | 251 ++++++++++
 drivers/acpi/internal.h                            |   5 +
 drivers/acpi/pci_root.c                            |   8 +-
 drivers/acpi/scan.c                                |   1 +
 drivers/bus/Kconfig                                |   8 +
 drivers/bus/Makefile                               |   1 +
 drivers/bus/hisi_lpc.c                             | 522 +++++++++++++++++++++
 drivers/of/address.c                               |  96 +++-
 drivers/pci/pci.c                                  |  98 +---
 include/asm-generic/io.h                           |   4 +-
 include/linux/logic_pio.h                          | 131 ++++++
 include/linux/pci.h                                |   3 +-
 lib/Kconfig                                        |  15 +
 lib/Makefile                                       |   2 +
 lib/logic_pio.c                                    | 272 +++++++++++
 18 files changed, 1357 insertions(+), 101 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/arm/hisilicon/hisilicon-low-pin-count.txt
 create mode 100644 drivers/acpi/arm64/acpi_indirectio.c
 create mode 100644 drivers/bus/hisi_lpc.c
 create mode 100644 include/linux/logic_pio.h
 create mode 100644 lib/logic_pio.c

-- 
1.9.1

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

* [PATCH v13 1/9] LIB: Introduce a generic PIO mapping method
  2018-02-13 17:45 ` John Garry
@ 2018-02-13 17:45   ` John Garry
  -1 siblings, 0 replies; 66+ messages in thread
From: John Garry @ 2018-02-13 17:45 UTC (permalink / raw)
  To: mika.westerberg, rafael, lorenzo.pieralisi, rjw, hanjun.guo,
	robh+dt, bhelgaas, arnd, mark.rutland, olof, dann.frazier,
	andy.shevchenko, robh
  Cc: joe, benh, linux-pci, linux-kernel, linux-acpi, linuxarm,
	minyard, devicetree, linux-arch, rdunlap

From: Zhichang Yuan <yuanzhichang@hisilicon.com>

In commit 41f8bba7f555 ("of/pci: Add pci_register_io_range() and
pci_pio_to_address()"), a new I/O space management was supported. With
that driver, the I/O ranges configured for PCI/PCIe hosts on some
architectures can be mapped to logical PIO, converted easily between
CPU address and the corresponding logicial PIO. Based on this, PCI
I/O devices can be accessed in a memory read/write way through the
unified in/out accessors.

But on some archs/platforms, there are bus hosts which access I/O
peripherals with host-local I/O port addresses rather than memory
addresses after memory-mapped.

To support those devices, a more generic I/O mapping method is introduced
here. Through this patch, both the CPU addresses and the host-local port
can be mapped into the logical PIO space with different logical/fake PIOs.
After this, all the I/O accesses to either PCI MMIO devices or host-local
I/O peripherals can be unified into the existing I/O accessors defined in
asm-generic/io.h and be redirected to the right device-specific hooks
based on the input logical PIO.

Signed-off-by: Zhichang Yuan <yuanzhichang@hisilicon.com>
Signed-off-by: Gabriele Paoloni <gabriele.paoloni@huawei.com>
Signed-off-by: John Garry <john.garry@huawei.com>
---
 include/asm-generic/io.h  |   2 +
 include/linux/logic_pio.h | 131 ++++++++++++++++++++++
 lib/Kconfig               |  15 +++
 lib/Makefile              |   2 +
 lib/logic_pio.c           | 272 ++++++++++++++++++++++++++++++++++++++++++++++
 5 files changed, 422 insertions(+)
 create mode 100644 include/linux/logic_pio.h
 create mode 100644 lib/logic_pio.c

diff --git a/include/asm-generic/io.h b/include/asm-generic/io.h
index b4531e3..b7996a79 100644
--- a/include/asm-generic/io.h
+++ b/include/asm-generic/io.h
@@ -351,6 +351,8 @@ static inline void writesq(volatile void __iomem *addr, const void *buffer,
 #define IO_SPACE_LIMIT 0xffff
 #endif
 
+#include <linux/logic_pio.h>
+
 /*
  * {in,out}{b,w,l}() access little endian I/O. {in,out}{b,w,l}_p() can be
  * implemented on hardware that needs an additional delay for I/O accesses to
diff --git a/include/linux/logic_pio.h b/include/linux/logic_pio.h
new file mode 100644
index 0000000..b3322fd
--- /dev/null
+++ b/include/linux/logic_pio.h
@@ -0,0 +1,131 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2017 Hisilicon Limited, All Rights Reserved.
+ * Author: Gabriele Paoloni <gabriele.paoloni@huawei.com>
+ * Author: Zhichang Yuan <yuanzhichang@hisilicon.com>
+ *
+ */
+
+#ifndef __LINUX_LOGIC_PIO_H__
+#define __LINUX_LOGIC_PIO_H__
+
+#ifdef __KERNEL__
+
+#include <linux/fwnode.h>
+
+#define PIO_INDIRECT		0x01UL /* indirect IO flag */
+#define PIO_CPU_MMIO		0x00UL /* memory mapped io flag */
+
+struct logic_pio_hwaddr {
+	struct list_head list;
+	struct fwnode_handle *fwnode;
+	resource_size_t hw_start;
+	resource_size_t io_start;
+	resource_size_t size; /* range size populated */
+	unsigned long flags;
+
+	void *devpara;	/* private parameter of the host device */
+	struct hostio_ops *ops;	/* ops operating on this node */
+};
+
+struct hostio_ops {
+	u32 (*pfin)(void *devobj, unsigned long ptaddr,	size_t dlen);
+	void (*pfout)(void *devobj, unsigned long ptaddr, u32 outval,
+			size_t dlen);
+	u32 (*pfins)(void *devobj, unsigned long ptaddr, void *inbuf,
+			size_t dlen, unsigned int count);
+	void (*pfouts)(void *devobj, unsigned long ptaddr,
+			const void *outbuf, size_t dlen, unsigned int count);
+};
+
+#ifdef CONFIG_INDIRECT_PIO
+u8 logic_inb(unsigned long addr);
+void logic_outb(u8 value, unsigned long addr);
+void logic_outw(u16 value, unsigned long addr);
+void logic_outl(u32 value, unsigned long addr);
+u16 logic_inw(unsigned long addr);
+u32 logic_inl(unsigned long addr);
+void logic_outb(u8 value, unsigned long addr);
+void logic_outw(u16 value, unsigned long addr);
+void logic_outl(u32 value, unsigned long addr);
+void logic_insb(unsigned long addr, void *buffer, unsigned int count);
+void logic_insl(unsigned long addr, void *buffer, unsigned int count);
+void logic_insw(unsigned long addr, void *buffer, unsigned int count);
+void logic_outsb(unsigned long addr, const void *buffer, unsigned int count);
+void logic_outsw(unsigned long addr, const void *buffer, unsigned int count);
+void logic_outsl(unsigned long addr, const void *buffer, unsigned int count);
+
+#ifndef inb
+#define inb logic_inb
+#endif
+
+#ifndef inw
+#define inw logic_inw
+#endif
+
+#ifndef inl
+#define inl logic_inl
+#endif
+
+#ifndef outb
+#define outb logic_outb
+#endif
+
+#ifndef outw
+#define outw logic_outw
+#endif
+
+#ifndef outl
+#define outl logic_outl
+#endif
+
+#ifndef insb
+#define insb logic_insb
+#endif
+
+#ifndef insw
+#define insw logic_insw
+#endif
+
+#ifndef insl
+#define insl logic_insl
+#endif
+
+#ifndef outsb
+#define outsb logic_outsb
+#endif
+
+#ifndef outsw
+#define outsw logic_outsw
+#endif
+
+#ifndef outsl
+#define outsl logic_outsl
+#endif
+
+/*
+ * Below we reserve 0x4000 bytes for Indirect IO as so far this library is only
+ * used by Hisilicon LPC Host. If needed in future we may reserve a wider IO
+ * area by redefining the macro below.
+ */
+#define PIO_INDIRECT_SIZE 0x4000
+#define MMIO_UPPER_LIMIT (IO_SPACE_LIMIT - PIO_INDIRECT_SIZE)
+#else
+#define MMIO_UPPER_LIMIT IO_SPACE_LIMIT
+#endif /* CONFIG_INDIRECT_PIO */
+
+
+struct logic_pio_hwaddr *find_io_range_by_fwnode(struct fwnode_handle *fwnode);
+
+unsigned long logic_pio_trans_hwaddr(struct fwnode_handle *fwnode,
+			resource_size_t hw_addr, resource_size_t size);
+
+int logic_pio_register_range(struct logic_pio_hwaddr *newrange);
+
+
+extern resource_size_t logic_pio_to_hwaddr(unsigned long pio);
+
+extern unsigned long logic_pio_trans_cpuaddr(resource_size_t hw_addr);
+
+#endif /* __KERNEL__ */
+#endif /* __LINUX_LOGIC_PIO_H__ */
diff --git a/lib/Kconfig b/lib/Kconfig
index e960894..7ad1e69 100644
--- a/lib/Kconfig
+++ b/lib/Kconfig
@@ -55,6 +55,21 @@ config ARCH_USE_CMPXCHG_LOCKREF
 config ARCH_HAS_FAST_MULTIPLIER
 	bool
 
+config INDIRECT_PIO
+	bool "Access I/O in non-MMIO mode"
+	depends on ARM64
+	help
+	  On some platforms where no separate I/O space exists, there are I/O
+	  hosts which can not be accessed in MMIO mode. Using the logical PIO
+	  mechanism, the host-local I/O resource can be mapped into system
+	  logic PIO space shared with MMIO hosts, such as PCI/PCIE, then the
+	  system can access the I/O devices with the mapped logic PIO through
+	  I/O accessors.
+	  This way has a relatively little I/O performance cost. Please make
+	  sure your devices really need this configure item enabled.
+
+	  When in doubt, say N.
+
 config CRC_CCITT
 	tristate "CRC-CCITT functions"
 	help
diff --git a/lib/Makefile b/lib/Makefile
index a90d4fc..4a9eacd 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -81,6 +81,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-y += logic_pio.o
+
 obj-$(CONFIG_GENERIC_HWEIGHT) += hweight.o
 
 obj-$(CONFIG_BTREE) += btree.o
diff --git a/lib/logic_pio.c b/lib/logic_pio.c
new file mode 100644
index 0000000..1006d9d
--- /dev/null
+++ b/lib/logic_pio.c
@@ -0,0 +1,272 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2017 Hisilicon Limited, All Rights Reserved.
+ * Author: Gabriele Paoloni <gabriele.paoloni@huawei.com>
+ * Author: Zhichang Yuan <yuanzhichang@hisilicon.com>
+ *
+ */
+
+#define pr_fmt(fmt)	"LOGIC PIO: " fmt
+
+#include <linux/of.h>
+#include <linux/io.h>
+#include <linux/logic_pio.h>
+#include <linux/mm.h>
+#include <linux/rculist.h>
+#include <linux/sizes.h>
+#include <linux/slab.h>
+
+/* The unique hardware address list. */
+static LIST_HEAD(io_range_list);
+static DEFINE_MUTEX(io_range_mutex);
+
+/*
+ * register a new io range node in the io range list.
+ *
+ * @newrange: pointer to the io range to be registered.
+ *
+ * returns 0 on success, the error code in case of failure
+ */
+int logic_pio_register_range(struct logic_pio_hwaddr *new_range)
+{
+	struct logic_pio_hwaddr *range;
+	int ret = 0;
+	resource_size_t start = new_range->hw_start;
+	resource_size_t end = new_range->hw_start + new_range->size;
+	resource_size_t allocated_mmio_size = 0;
+	resource_size_t allocated_iio_size = MMIO_UPPER_LIMIT;
+
+	if (!new_range || !new_range->fwnode || !new_range->size)
+		return -EINVAL;
+
+	mutex_lock(&io_range_mutex);
+	list_for_each_entry_rcu(range, &io_range_list, list) {
+		if (range->fwnode == new_range->fwnode) {
+			/* range already there */
+			ret = -EFAULT;
+			goto end_register;
+		}
+		if (range->flags == PIO_CPU_MMIO &&
+				new_range->flags == PIO_CPU_MMIO) {
+			/* for MMIO ranges we need to check for overlap */
+			if (start >= range->hw_start + range->size ||
+			    end < range->hw_start)
+				allocated_mmio_size += range->size;
+			else {
+				ret = -EFAULT;
+				goto end_register;
+			}
+		} else if (range->flags == PIO_INDIRECT &&
+			   new_range->flags == PIO_INDIRECT) {
+			allocated_iio_size += range->size;
+		}
+	}
+
+	/* range not registered yet, check for available space */
+	if (new_range->flags == PIO_CPU_MMIO) {
+		if (allocated_mmio_size + new_range->size - 1 >
+			MMIO_UPPER_LIMIT) {
+			/* if it's too big check if 64K space can be reserved */
+			if (allocated_mmio_size + SZ_64K - 1 >
+			MMIO_UPPER_LIMIT) {
+				ret = -E2BIG;
+				goto end_register;
+			}
+			new_range->size = SZ_64K;
+			pr_warn("Requested IO range too big, new size set to 64K\n");
+		}
+		new_range->io_start = allocated_mmio_size;
+	} else if (new_range->flags == PIO_INDIRECT) {
+		if (allocated_iio_size + new_range->size - 1 >
+		    IO_SPACE_LIMIT) {
+			ret = -E2BIG;
+			goto end_register;
+		}
+		new_range->io_start = allocated_iio_size;
+	} else {
+		/* invalid flag */
+		ret = -EINVAL;
+		goto end_register;
+	}
+
+	list_add_tail_rcu(&new_range->list, &io_range_list);
+
+end_register:
+	mutex_unlock(&io_range_mutex);
+	return ret;
+}
+
+/*
+ * traverse the io_range_list to find the registered node whose device node
+ * and/or physical IO address match to.
+ */
+struct logic_pio_hwaddr *find_io_range_by_fwnode(struct fwnode_handle *fwnode)
+{
+	struct logic_pio_hwaddr *range;
+
+	list_for_each_entry_rcu(range, &io_range_list, list) {
+		if (range->fwnode == fwnode)
+			return range;
+	}
+	return NULL;
+}
+
+/* return a registered range given an input PIO token */
+static struct logic_pio_hwaddr *find_io_range(unsigned long pio)
+{
+	struct logic_pio_hwaddr *range;
+
+	list_for_each_entry_rcu(range, &io_range_list, list) {
+		if (pio >= range->io_start &&
+		    pio < range->io_start + range->size)
+			return range;
+	}
+	pr_err("PIO entry token invalid\n");
+	return NULL;
+}
+
+/*
+ * Translate the input logical pio to the corresponding hardware address.
+ * The input pio should be unique in the whole logical PIO space.
+ */
+resource_size_t logic_pio_to_hwaddr(unsigned long pio)
+{
+	struct logic_pio_hwaddr *range;
+	resource_size_t hwaddr = -1;
+
+	range = find_io_range(pio);
+	if (range)
+		hwaddr = range->hw_start + pio - range->io_start;
+
+	return hwaddr;
+}
+
+/*
+ * This function is generic for translating a hardware address to logical PIO.
+ * @hw_addr: the hardware address of host, can be CPU address or host-local
+ *		address;
+ */
+unsigned long
+logic_pio_trans_hwaddr(struct fwnode_handle *fwnode, resource_size_t addr,
+		       resource_size_t size)
+{
+	struct logic_pio_hwaddr *range;
+
+	range = find_io_range_by_fwnode(fwnode);
+	if (!range || range->flags == PIO_CPU_MMIO) {
+		pr_err("range not found or invalid\n");
+		return -1;
+	}
+	if (range->size < size) {
+		pr_err("resource size %pa cannot fit in IO range size %pa\n",
+		       &size, &range->size);
+		return -1;
+	}
+	return addr - range->hw_start + range->io_start;
+}
+
+unsigned long
+logic_pio_trans_cpuaddr(resource_size_t addr)
+{
+	struct logic_pio_hwaddr *range;
+
+	list_for_each_entry_rcu(range, &io_range_list, list) {
+		if (range->flags != PIO_CPU_MMIO)
+			continue;
+		if (addr >= range->hw_start &&
+		    addr < range->hw_start + range->size)
+			return addr - range->hw_start +
+				range->io_start;
+	}
+	pr_err("addr not registered in io_range_list\n");
+	return -1;
+}
+
+#if defined(CONFIG_INDIRECT_PIO) && defined(PCI_IOBASE)
+#define BUILD_LOGIC_IO(bw, type)					\
+type logic_in##bw(unsigned long addr)					\
+{									\
+	type ret = -1;							\
+									\
+	if (addr < MMIO_UPPER_LIMIT) {					\
+		ret = read##bw(PCI_IOBASE + addr);			\
+	} else if (addr >= MMIO_UPPER_LIMIT && addr < IO_SPACE_LIMIT) { \
+		struct logic_pio_hwaddr *entry = find_io_range(addr);	\
+									\
+		if (entry && entry->ops)				\
+			ret = entry->ops->pfin(entry->devpara,		\
+					addr, sizeof(type));		\
+		else							\
+			WARN_ON_ONCE(1);				\
+	}								\
+	return ret;							\
+}									\
+									\
+void logic_out##bw(type value, unsigned long addr)			\
+{									\
+	if (addr < MMIO_UPPER_LIMIT) {					\
+		write##bw(value, PCI_IOBASE + addr);			\
+	} else if (addr >= MMIO_UPPER_LIMIT && addr < IO_SPACE_LIMIT) {	\
+		struct logic_pio_hwaddr *entry = find_io_range(addr);	\
+									\
+		if (entry && entry->ops)				\
+			entry->ops->pfout(entry->devpara,		\
+					addr, value, sizeof(type));	\
+		else							\
+			WARN_ON_ONCE(1);				\
+	}								\
+}									\
+									\
+void logic_ins##bw(unsigned long addr, void *buffer, unsigned int count)\
+{									\
+	if (addr < MMIO_UPPER_LIMIT) {					\
+		reads##bw(PCI_IOBASE + addr, buffer, count);		\
+	} else if (addr >= MMIO_UPPER_LIMIT && addr < IO_SPACE_LIMIT) {	\
+		struct logic_pio_hwaddr *entry = find_io_range(addr);	\
+									\
+		if (entry && entry->ops)				\
+			entry->ops->pfins(entry->devpara,		\
+				addr, buffer, sizeof(type), count);	\
+		else							\
+			WARN_ON_ONCE(1);				\
+	}								\
+									\
+}									\
+									\
+void logic_outs##bw(unsigned long addr, const void *buffer,		\
+		    unsigned int count)					\
+{									\
+	if (addr < MMIO_UPPER_LIMIT) {					\
+		writes##bw(PCI_IOBASE + addr, buffer, count);		\
+	} else if (addr >= MMIO_UPPER_LIMIT && addr < IO_SPACE_LIMIT) {	\
+		struct logic_pio_hwaddr *entry = find_io_range(addr);	\
+									\
+		if (entry && entry->ops)				\
+			entry->ops->pfouts(entry->devpara,		\
+				addr, buffer, sizeof(type), count);	\
+		else							\
+			WARN_ON_ONCE(1);				\
+	}								\
+}
+
+BUILD_LOGIC_IO(b, u8)
+
+EXPORT_SYMBOL(logic_inb);
+EXPORT_SYMBOL(logic_outb);
+EXPORT_SYMBOL(logic_insb);
+EXPORT_SYMBOL(logic_outsb);
+
+BUILD_LOGIC_IO(w, u16)
+
+EXPORT_SYMBOL(logic_inw);
+EXPORT_SYMBOL(logic_outw);
+EXPORT_SYMBOL(logic_insw);
+EXPORT_SYMBOL(logic_outsw);
+
+BUILD_LOGIC_IO(l, u32)
+
+EXPORT_SYMBOL(logic_inl);
+EXPORT_SYMBOL(logic_outl);
+EXPORT_SYMBOL(logic_insl);
+EXPORT_SYMBOL(logic_outsl);
+#endif /* CONFIG_INDIRECT_PIO && PCI_IOBASE */
-- 
1.9.1

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

* [PATCH v13 1/9] LIB: Introduce a generic PIO mapping method
@ 2018-02-13 17:45   ` John Garry
  0 siblings, 0 replies; 66+ messages in thread
From: John Garry @ 2018-02-13 17:45 UTC (permalink / raw)
  To: mika.westerberg, rafael, lorenzo.pieralisi, rjw, hanjun.guo,
	robh+dt, bhelgaas, arnd, mark.rutland, olof, dann.frazier,
	andy.shevchenko, robh
  Cc: joe, benh, linux-pci, linux-kernel, linux-acpi, linuxarm,
	minyard, devicetree, linux-arch, rdunlap

From: Zhichang Yuan <yuanzhichang@hisilicon.com>

In commit 41f8bba7f555 ("of/pci: Add pci_register_io_range() and
pci_pio_to_address()"), a new I/O space management was supported. With
that driver, the I/O ranges configured for PCI/PCIe hosts on some
architectures can be mapped to logical PIO, converted easily between
CPU address and the corresponding logicial PIO. Based on this, PCI
I/O devices can be accessed in a memory read/write way through the
unified in/out accessors.

But on some archs/platforms, there are bus hosts which access I/O
peripherals with host-local I/O port addresses rather than memory
addresses after memory-mapped.

To support those devices, a more generic I/O mapping method is introduced
here. Through this patch, both the CPU addresses and the host-local port
can be mapped into the logical PIO space with different logical/fake PIOs.
After this, all the I/O accesses to either PCI MMIO devices or host-local
I/O peripherals can be unified into the existing I/O accessors defined in
asm-generic/io.h and be redirected to the right device-specific hooks
based on the input logical PIO.

Signed-off-by: Zhichang Yuan <yuanzhichang@hisilicon.com>
Signed-off-by: Gabriele Paoloni <gabriele.paoloni@huawei.com>
Signed-off-by: John Garry <john.garry@huawei.com>
---
 include/asm-generic/io.h  |   2 +
 include/linux/logic_pio.h | 131 ++++++++++++++++++++++
 lib/Kconfig               |  15 +++
 lib/Makefile              |   2 +
 lib/logic_pio.c           | 272 ++++++++++++++++++++++++++++++++++++++++++++++
 5 files changed, 422 insertions(+)
 create mode 100644 include/linux/logic_pio.h
 create mode 100644 lib/logic_pio.c

diff --git a/include/asm-generic/io.h b/include/asm-generic/io.h
index b4531e3..b7996a79 100644
--- a/include/asm-generic/io.h
+++ b/include/asm-generic/io.h
@@ -351,6 +351,8 @@ static inline void writesq(volatile void __iomem *addr, const void *buffer,
 #define IO_SPACE_LIMIT 0xffff
 #endif
 
+#include <linux/logic_pio.h>
+
 /*
  * {in,out}{b,w,l}() access little endian I/O. {in,out}{b,w,l}_p() can be
  * implemented on hardware that needs an additional delay for I/O accesses to
diff --git a/include/linux/logic_pio.h b/include/linux/logic_pio.h
new file mode 100644
index 0000000..b3322fd
--- /dev/null
+++ b/include/linux/logic_pio.h
@@ -0,0 +1,131 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2017 Hisilicon Limited, All Rights Reserved.
+ * Author: Gabriele Paoloni <gabriele.paoloni@huawei.com>
+ * Author: Zhichang Yuan <yuanzhichang@hisilicon.com>
+ *
+ */
+
+#ifndef __LINUX_LOGIC_PIO_H__
+#define __LINUX_LOGIC_PIO_H__
+
+#ifdef __KERNEL__
+
+#include <linux/fwnode.h>
+
+#define PIO_INDIRECT		0x01UL /* indirect IO flag */
+#define PIO_CPU_MMIO		0x00UL /* memory mapped io flag */
+
+struct logic_pio_hwaddr {
+	struct list_head list;
+	struct fwnode_handle *fwnode;
+	resource_size_t hw_start;
+	resource_size_t io_start;
+	resource_size_t size; /* range size populated */
+	unsigned long flags;
+
+	void *devpara;	/* private parameter of the host device */
+	struct hostio_ops *ops;	/* ops operating on this node */
+};
+
+struct hostio_ops {
+	u32 (*pfin)(void *devobj, unsigned long ptaddr,	size_t dlen);
+	void (*pfout)(void *devobj, unsigned long ptaddr, u32 outval,
+			size_t dlen);
+	u32 (*pfins)(void *devobj, unsigned long ptaddr, void *inbuf,
+			size_t dlen, unsigned int count);
+	void (*pfouts)(void *devobj, unsigned long ptaddr,
+			const void *outbuf, size_t dlen, unsigned int count);
+};
+
+#ifdef CONFIG_INDIRECT_PIO
+u8 logic_inb(unsigned long addr);
+void logic_outb(u8 value, unsigned long addr);
+void logic_outw(u16 value, unsigned long addr);
+void logic_outl(u32 value, unsigned long addr);
+u16 logic_inw(unsigned long addr);
+u32 logic_inl(unsigned long addr);
+void logic_outb(u8 value, unsigned long addr);
+void logic_outw(u16 value, unsigned long addr);
+void logic_outl(u32 value, unsigned long addr);
+void logic_insb(unsigned long addr, void *buffer, unsigned int count);
+void logic_insl(unsigned long addr, void *buffer, unsigned int count);
+void logic_insw(unsigned long addr, void *buffer, unsigned int count);
+void logic_outsb(unsigned long addr, const void *buffer, unsigned int count);
+void logic_outsw(unsigned long addr, const void *buffer, unsigned int count);
+void logic_outsl(unsigned long addr, const void *buffer, unsigned int count);
+
+#ifndef inb
+#define inb logic_inb
+#endif
+
+#ifndef inw
+#define inw logic_inw
+#endif
+
+#ifndef inl
+#define inl logic_inl
+#endif
+
+#ifndef outb
+#define outb logic_outb
+#endif
+
+#ifndef outw
+#define outw logic_outw
+#endif
+
+#ifndef outl
+#define outl logic_outl
+#endif
+
+#ifndef insb
+#define insb logic_insb
+#endif
+
+#ifndef insw
+#define insw logic_insw
+#endif
+
+#ifndef insl
+#define insl logic_insl
+#endif
+
+#ifndef outsb
+#define outsb logic_outsb
+#endif
+
+#ifndef outsw
+#define outsw logic_outsw
+#endif
+
+#ifndef outsl
+#define outsl logic_outsl
+#endif
+
+/*
+ * Below we reserve 0x4000 bytes for Indirect IO as so far this library is only
+ * used by Hisilicon LPC Host. If needed in future we may reserve a wider IO
+ * area by redefining the macro below.
+ */
+#define PIO_INDIRECT_SIZE 0x4000
+#define MMIO_UPPER_LIMIT (IO_SPACE_LIMIT - PIO_INDIRECT_SIZE)
+#else
+#define MMIO_UPPER_LIMIT IO_SPACE_LIMIT
+#endif /* CONFIG_INDIRECT_PIO */
+
+
+struct logic_pio_hwaddr *find_io_range_by_fwnode(struct fwnode_handle *fwnode);
+
+unsigned long logic_pio_trans_hwaddr(struct fwnode_handle *fwnode,
+			resource_size_t hw_addr, resource_size_t size);
+
+int logic_pio_register_range(struct logic_pio_hwaddr *newrange);
+
+
+extern resource_size_t logic_pio_to_hwaddr(unsigned long pio);
+
+extern unsigned long logic_pio_trans_cpuaddr(resource_size_t hw_addr);
+
+#endif /* __KERNEL__ */
+#endif /* __LINUX_LOGIC_PIO_H__ */
diff --git a/lib/Kconfig b/lib/Kconfig
index e960894..7ad1e69 100644
--- a/lib/Kconfig
+++ b/lib/Kconfig
@@ -55,6 +55,21 @@ config ARCH_USE_CMPXCHG_LOCKREF
 config ARCH_HAS_FAST_MULTIPLIER
 	bool
 
+config INDIRECT_PIO
+	bool "Access I/O in non-MMIO mode"
+	depends on ARM64
+	help
+	  On some platforms where no separate I/O space exists, there are I/O
+	  hosts which can not be accessed in MMIO mode. Using the logical PIO
+	  mechanism, the host-local I/O resource can be mapped into system
+	  logic PIO space shared with MMIO hosts, such as PCI/PCIE, then the
+	  system can access the I/O devices with the mapped logic PIO through
+	  I/O accessors.
+	  This way has a relatively little I/O performance cost. Please make
+	  sure your devices really need this configure item enabled.
+
+	  When in doubt, say N.
+
 config CRC_CCITT
 	tristate "CRC-CCITT functions"
 	help
diff --git a/lib/Makefile b/lib/Makefile
index a90d4fc..4a9eacd 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -81,6 +81,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-y += logic_pio.o
+
 obj-$(CONFIG_GENERIC_HWEIGHT) += hweight.o
 
 obj-$(CONFIG_BTREE) += btree.o
diff --git a/lib/logic_pio.c b/lib/logic_pio.c
new file mode 100644
index 0000000..1006d9d
--- /dev/null
+++ b/lib/logic_pio.c
@@ -0,0 +1,272 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2017 Hisilicon Limited, All Rights Reserved.
+ * Author: Gabriele Paoloni <gabriele.paoloni@huawei.com>
+ * Author: Zhichang Yuan <yuanzhichang@hisilicon.com>
+ *
+ */
+
+#define pr_fmt(fmt)	"LOGIC PIO: " fmt
+
+#include <linux/of.h>
+#include <linux/io.h>
+#include <linux/logic_pio.h>
+#include <linux/mm.h>
+#include <linux/rculist.h>
+#include <linux/sizes.h>
+#include <linux/slab.h>
+
+/* The unique hardware address list. */
+static LIST_HEAD(io_range_list);
+static DEFINE_MUTEX(io_range_mutex);
+
+/*
+ * register a new io range node in the io range list.
+ *
+ * @newrange: pointer to the io range to be registered.
+ *
+ * returns 0 on success, the error code in case of failure
+ */
+int logic_pio_register_range(struct logic_pio_hwaddr *new_range)
+{
+	struct logic_pio_hwaddr *range;
+	int ret = 0;
+	resource_size_t start = new_range->hw_start;
+	resource_size_t end = new_range->hw_start + new_range->size;
+	resource_size_t allocated_mmio_size = 0;
+	resource_size_t allocated_iio_size = MMIO_UPPER_LIMIT;
+
+	if (!new_range || !new_range->fwnode || !new_range->size)
+		return -EINVAL;
+
+	mutex_lock(&io_range_mutex);
+	list_for_each_entry_rcu(range, &io_range_list, list) {
+		if (range->fwnode == new_range->fwnode) {
+			/* range already there */
+			ret = -EFAULT;
+			goto end_register;
+		}
+		if (range->flags == PIO_CPU_MMIO &&
+				new_range->flags == PIO_CPU_MMIO) {
+			/* for MMIO ranges we need to check for overlap */
+			if (start >= range->hw_start + range->size ||
+			    end < range->hw_start)
+				allocated_mmio_size += range->size;
+			else {
+				ret = -EFAULT;
+				goto end_register;
+			}
+		} else if (range->flags == PIO_INDIRECT &&
+			   new_range->flags == PIO_INDIRECT) {
+			allocated_iio_size += range->size;
+		}
+	}
+
+	/* range not registered yet, check for available space */
+	if (new_range->flags == PIO_CPU_MMIO) {
+		if (allocated_mmio_size + new_range->size - 1 >
+			MMIO_UPPER_LIMIT) {
+			/* if it's too big check if 64K space can be reserved */
+			if (allocated_mmio_size + SZ_64K - 1 >
+			MMIO_UPPER_LIMIT) {
+				ret = -E2BIG;
+				goto end_register;
+			}
+			new_range->size = SZ_64K;
+			pr_warn("Requested IO range too big, new size set to 64K\n");
+		}
+		new_range->io_start = allocated_mmio_size;
+	} else if (new_range->flags == PIO_INDIRECT) {
+		if (allocated_iio_size + new_range->size - 1 >
+		    IO_SPACE_LIMIT) {
+			ret = -E2BIG;
+			goto end_register;
+		}
+		new_range->io_start = allocated_iio_size;
+	} else {
+		/* invalid flag */
+		ret = -EINVAL;
+		goto end_register;
+	}
+
+	list_add_tail_rcu(&new_range->list, &io_range_list);
+
+end_register:
+	mutex_unlock(&io_range_mutex);
+	return ret;
+}
+
+/*
+ * traverse the io_range_list to find the registered node whose device node
+ * and/or physical IO address match to.
+ */
+struct logic_pio_hwaddr *find_io_range_by_fwnode(struct fwnode_handle *fwnode)
+{
+	struct logic_pio_hwaddr *range;
+
+	list_for_each_entry_rcu(range, &io_range_list, list) {
+		if (range->fwnode == fwnode)
+			return range;
+	}
+	return NULL;
+}
+
+/* return a registered range given an input PIO token */
+static struct logic_pio_hwaddr *find_io_range(unsigned long pio)
+{
+	struct logic_pio_hwaddr *range;
+
+	list_for_each_entry_rcu(range, &io_range_list, list) {
+		if (pio >= range->io_start &&
+		    pio < range->io_start + range->size)
+			return range;
+	}
+	pr_err("PIO entry token invalid\n");
+	return NULL;
+}
+
+/*
+ * Translate the input logical pio to the corresponding hardware address.
+ * The input pio should be unique in the whole logical PIO space.
+ */
+resource_size_t logic_pio_to_hwaddr(unsigned long pio)
+{
+	struct logic_pio_hwaddr *range;
+	resource_size_t hwaddr = -1;
+
+	range = find_io_range(pio);
+	if (range)
+		hwaddr = range->hw_start + pio - range->io_start;
+
+	return hwaddr;
+}
+
+/*
+ * This function is generic for translating a hardware address to logical PIO.
+ * @hw_addr: the hardware address of host, can be CPU address or host-local
+ *		address;
+ */
+unsigned long
+logic_pio_trans_hwaddr(struct fwnode_handle *fwnode, resource_size_t addr,
+		       resource_size_t size)
+{
+	struct logic_pio_hwaddr *range;
+
+	range = find_io_range_by_fwnode(fwnode);
+	if (!range || range->flags == PIO_CPU_MMIO) {
+		pr_err("range not found or invalid\n");
+		return -1;
+	}
+	if (range->size < size) {
+		pr_err("resource size %pa cannot fit in IO range size %pa\n",
+		       &size, &range->size);
+		return -1;
+	}
+	return addr - range->hw_start + range->io_start;
+}
+
+unsigned long
+logic_pio_trans_cpuaddr(resource_size_t addr)
+{
+	struct logic_pio_hwaddr *range;
+
+	list_for_each_entry_rcu(range, &io_range_list, list) {
+		if (range->flags != PIO_CPU_MMIO)
+			continue;
+		if (addr >= range->hw_start &&
+		    addr < range->hw_start + range->size)
+			return addr - range->hw_start +
+				range->io_start;
+	}
+	pr_err("addr not registered in io_range_list\n");
+	return -1;
+}
+
+#if defined(CONFIG_INDIRECT_PIO) && defined(PCI_IOBASE)
+#define BUILD_LOGIC_IO(bw, type)					\
+type logic_in##bw(unsigned long addr)					\
+{									\
+	type ret = -1;							\
+									\
+	if (addr < MMIO_UPPER_LIMIT) {					\
+		ret = read##bw(PCI_IOBASE + addr);			\
+	} else if (addr >= MMIO_UPPER_LIMIT && addr < IO_SPACE_LIMIT) { \
+		struct logic_pio_hwaddr *entry = find_io_range(addr);	\
+									\
+		if (entry && entry->ops)				\
+			ret = entry->ops->pfin(entry->devpara,		\
+					addr, sizeof(type));		\
+		else							\
+			WARN_ON_ONCE(1);				\
+	}								\
+	return ret;							\
+}									\
+									\
+void logic_out##bw(type value, unsigned long addr)			\
+{									\
+	if (addr < MMIO_UPPER_LIMIT) {					\
+		write##bw(value, PCI_IOBASE + addr);			\
+	} else if (addr >= MMIO_UPPER_LIMIT && addr < IO_SPACE_LIMIT) {	\
+		struct logic_pio_hwaddr *entry = find_io_range(addr);	\
+									\
+		if (entry && entry->ops)				\
+			entry->ops->pfout(entry->devpara,		\
+					addr, value, sizeof(type));	\
+		else							\
+			WARN_ON_ONCE(1);				\
+	}								\
+}									\
+									\
+void logic_ins##bw(unsigned long addr, void *buffer, unsigned int count)\
+{									\
+	if (addr < MMIO_UPPER_LIMIT) {					\
+		reads##bw(PCI_IOBASE + addr, buffer, count);		\
+	} else if (addr >= MMIO_UPPER_LIMIT && addr < IO_SPACE_LIMIT) {	\
+		struct logic_pio_hwaddr *entry = find_io_range(addr);	\
+									\
+		if (entry && entry->ops)				\
+			entry->ops->pfins(entry->devpara,		\
+				addr, buffer, sizeof(type), count);	\
+		else							\
+			WARN_ON_ONCE(1);				\
+	}								\
+									\
+}									\
+									\
+void logic_outs##bw(unsigned long addr, const void *buffer,		\
+		    unsigned int count)					\
+{									\
+	if (addr < MMIO_UPPER_LIMIT) {					\
+		writes##bw(PCI_IOBASE + addr, buffer, count);		\
+	} else if (addr >= MMIO_UPPER_LIMIT && addr < IO_SPACE_LIMIT) {	\
+		struct logic_pio_hwaddr *entry = find_io_range(addr);	\
+									\
+		if (entry && entry->ops)				\
+			entry->ops->pfouts(entry->devpara,		\
+				addr, buffer, sizeof(type), count);	\
+		else							\
+			WARN_ON_ONCE(1);				\
+	}								\
+}
+
+BUILD_LOGIC_IO(b, u8)
+
+EXPORT_SYMBOL(logic_inb);
+EXPORT_SYMBOL(logic_outb);
+EXPORT_SYMBOL(logic_insb);
+EXPORT_SYMBOL(logic_outsb);
+
+BUILD_LOGIC_IO(w, u16)
+
+EXPORT_SYMBOL(logic_inw);
+EXPORT_SYMBOL(logic_outw);
+EXPORT_SYMBOL(logic_insw);
+EXPORT_SYMBOL(logic_outsw);
+
+BUILD_LOGIC_IO(l, u32)
+
+EXPORT_SYMBOL(logic_inl);
+EXPORT_SYMBOL(logic_outl);
+EXPORT_SYMBOL(logic_insl);
+EXPORT_SYMBOL(logic_outsl);
+#endif /* CONFIG_INDIRECT_PIO && PCI_IOBASE */
-- 
1.9.1

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

* [PATCH v13 2/9] PCI: Remove unused __weak attribute in pci_register_io_range()
  2018-02-13 17:45 ` John Garry
@ 2018-02-13 17:45   ` John Garry
  -1 siblings, 0 replies; 66+ messages in thread
From: John Garry @ 2018-02-13 17:45 UTC (permalink / raw)
  To: mika.westerberg, rafael, lorenzo.pieralisi, rjw, hanjun.guo,
	robh+dt, bhelgaas, arnd, mark.rutland, olof, dann.frazier,
	andy.shevchenko, robh
  Cc: joe, benh, linux-pci, linux-kernel, linux-acpi, linuxarm,
	minyard, devicetree, linux-arch, rdunlap

From: Gabriele Paoloni <gabriele.paoloni@huawei.com>

Currently pci_register_io_range() has only one definition;
therefore there is no use of the __weak attribute.

Signed-off-by: Gabriele Paoloni <gabriele.paoloni@huawei.com>
Acked-by: Bjorn Helgaas <bhelgaas@google.com>
---
 drivers/pci/pci.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c
index f6a4dd1..4666a01 100644
--- a/drivers/pci/pci.c
+++ b/drivers/pci/pci.c
@@ -3455,7 +3455,7 @@ 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 pci_register_io_range(phys_addr_t addr, resource_size_t size)
 {
 	int err = 0;
 
-- 
1.9.1


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

* [PATCH v13 2/9] PCI: Remove unused __weak attribute in pci_register_io_range()
@ 2018-02-13 17:45   ` John Garry
  0 siblings, 0 replies; 66+ messages in thread
From: John Garry @ 2018-02-13 17:45 UTC (permalink / raw)
  To: mika.westerberg, rafael, lorenzo.pieralisi, rjw, hanjun.guo,
	robh+dt, bhelgaas, arnd, mark.rutland, olof, dann.frazier,
	andy.shevchenko, robh
  Cc: joe, benh, linux-pci, linux-kernel, linux-acpi, linuxarm,
	minyard, devicetree, linux-arch, rdunlap

From: Gabriele Paoloni <gabriele.paoloni@huawei.com>

Currently pci_register_io_range() has only one definition;
therefore there is no use of the __weak attribute.

Signed-off-by: Gabriele Paoloni <gabriele.paoloni@huawei.com>
Acked-by: Bjorn Helgaas <bhelgaas@google.com>
---
 drivers/pci/pci.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c
index f6a4dd1..4666a01 100644
--- a/drivers/pci/pci.c
+++ b/drivers/pci/pci.c
@@ -3455,7 +3455,7 @@ 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 pci_register_io_range(phys_addr_t addr, resource_size_t size)
 {
 	int err = 0;
 
-- 
1.9.1

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

* [PATCH v13 3/9] PCI: Add fwnode handler as input param of pci_register_io_range()
  2018-02-13 17:45 ` John Garry
@ 2018-02-13 17:45   ` John Garry
  -1 siblings, 0 replies; 66+ messages in thread
From: John Garry @ 2018-02-13 17:45 UTC (permalink / raw)
  To: mika.westerberg, rafael, lorenzo.pieralisi, rjw, hanjun.guo,
	robh+dt, bhelgaas, arnd, mark.rutland, olof, dann.frazier,
	andy.shevchenko, robh
  Cc: joe, benh, linux-pci, linux-kernel, linux-acpi, linuxarm,
	minyard, devicetree, linux-arch, rdunlap

From: Gabriele Paoloni <gabriele.paoloni@huawei.com>

In preparation for having the PCI MMIO helpers to use the new generic
I/O space management(logical PIO) we need to add the fwnode handler as
extra input parameter.
This patch changes the signature of pci_register_io_range() and of
its callers as needed.

Signed-off-by: Gabriele Paoloni <gabriele.paoloni@huawei.com>
Acked-by: Bjorn Helgaas <bhelgaas@google.com>
Acked-by: Rob Herring <robh@kernel.org>
---
 drivers/acpi/pci_root.c | 8 +++++---
 drivers/of/address.c    | 4 +++-
 drivers/pci/pci.c       | 3 ++-
 include/linux/pci.h     | 3 ++-
 4 files changed, 12 insertions(+), 6 deletions(-)

diff --git a/drivers/acpi/pci_root.c b/drivers/acpi/pci_root.c
index 6fc204a..1213479 100644
--- a/drivers/acpi/pci_root.c
+++ b/drivers/acpi/pci_root.c
@@ -729,7 +729,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 *fwnode,
+			struct resource_entry *entry)
 {
 #ifdef PCI_IOBASE
 	struct resource *res = entry->res;
@@ -738,7 +739,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))
+	if (pci_register_io_range(fwnode, cpu_addr, length))
 		goto err;
 
 	port = pci_address_to_pio(cpu_addr);
@@ -780,7 +781,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 ce4d3d8..cdf047b 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>
@@ -333,7 +334,8 @@ 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);
 		if (err)
 			goto invalid_range;
 		port = pci_address_to_pio(range->cpu_addr);
diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c
index 4666a01..07290a3 100644
--- a/drivers/pci/pci.c
+++ b/drivers/pci/pci.c
@@ -3455,7 +3455,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 pci_register_io_range(phys_addr_t addr, resource_size_t size)
+int pci_register_io_range(struct fwnode_handle *fwnode, phys_addr_t addr,
+			resource_size_t	size)
 {
 	int err = 0;
 
diff --git a/include/linux/pci.h b/include/linux/pci.h
index 024a1be..be686fd 100644
--- a/include/linux/pci.h
+++ b/include/linux/pci.h
@@ -1226,7 +1226,8 @@ int __must_check pci_bus_alloc_resource(struct pci_bus *bus,
 			void *alignf_data);
 
 
-int pci_register_io_range(phys_addr_t addr, resource_size_t size);
+int pci_register_io_range(struct fwnode_handle *fwnode, phys_addr_t addr,
+			resource_size_t size);
 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] 66+ messages in thread

* [PATCH v13 3/9] PCI: Add fwnode handler as input param of pci_register_io_range()
@ 2018-02-13 17:45   ` John Garry
  0 siblings, 0 replies; 66+ messages in thread
From: John Garry @ 2018-02-13 17:45 UTC (permalink / raw)
  To: mika.westerberg, rafael, lorenzo.pieralisi, rjw, hanjun.guo,
	robh+dt, bhelgaas, arnd, mark.rutland, olof, dann.frazier,
	andy.shevchenko, robh
  Cc: joe, benh, linux-pci, linux-kernel, linux-acpi, linuxarm,
	minyard, devicetree, linux-arch, rdunlap

From: Gabriele Paoloni <gabriele.paoloni@huawei.com>

In preparation for having the PCI MMIO helpers to use the new generic
I/O space management(logical PIO) we need to add the fwnode handler as
extra input parameter.
This patch changes the signature of pci_register_io_range() and of
its callers as needed.

Signed-off-by: Gabriele Paoloni <gabriele.paoloni@huawei.com>
Acked-by: Bjorn Helgaas <bhelgaas@google.com>
Acked-by: Rob Herring <robh@kernel.org>
---
 drivers/acpi/pci_root.c | 8 +++++---
 drivers/of/address.c    | 4 +++-
 drivers/pci/pci.c       | 3 ++-
 include/linux/pci.h     | 3 ++-
 4 files changed, 12 insertions(+), 6 deletions(-)

diff --git a/drivers/acpi/pci_root.c b/drivers/acpi/pci_root.c
index 6fc204a..1213479 100644
--- a/drivers/acpi/pci_root.c
+++ b/drivers/acpi/pci_root.c
@@ -729,7 +729,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 *fwnode,
+			struct resource_entry *entry)
 {
 #ifdef PCI_IOBASE
 	struct resource *res = entry->res;
@@ -738,7 +739,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))
+	if (pci_register_io_range(fwnode, cpu_addr, length))
 		goto err;
 
 	port = pci_address_to_pio(cpu_addr);
@@ -780,7 +781,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 ce4d3d8..cdf047b 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>
@@ -333,7 +334,8 @@ 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);
 		if (err)
 			goto invalid_range;
 		port = pci_address_to_pio(range->cpu_addr);
diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c
index 4666a01..07290a3 100644
--- a/drivers/pci/pci.c
+++ b/drivers/pci/pci.c
@@ -3455,7 +3455,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 pci_register_io_range(phys_addr_t addr, resource_size_t size)
+int pci_register_io_range(struct fwnode_handle *fwnode, phys_addr_t addr,
+			resource_size_t	size)
 {
 	int err = 0;
 
diff --git a/include/linux/pci.h b/include/linux/pci.h
index 024a1be..be686fd 100644
--- a/include/linux/pci.h
+++ b/include/linux/pci.h
@@ -1226,7 +1226,8 @@ int __must_check pci_bus_alloc_resource(struct pci_bus *bus,
 			void *alignf_data);
 
 
-int pci_register_io_range(phys_addr_t addr, resource_size_t size);
+int pci_register_io_range(struct fwnode_handle *fwnode, phys_addr_t addr,
+			resource_size_t size);
 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] 66+ messages in thread

* [PATCH v13 4/9] PCI: Apply the new generic I/O management on PCI IO hosts
  2018-02-13 17:45 ` John Garry
@ 2018-02-13 17:45   ` John Garry
  -1 siblings, 0 replies; 66+ messages in thread
From: John Garry @ 2018-02-13 17:45 UTC (permalink / raw)
  To: mika.westerberg, rafael, lorenzo.pieralisi, rjw, hanjun.guo,
	robh+dt, bhelgaas, arnd, mark.rutland, olof, dann.frazier,
	andy.shevchenko, robh
  Cc: joe, benh, linux-pci, linux-kernel, linux-acpi, linuxarm,
	minyard, devicetree, linux-arch, rdunlap

From: Zhichang Yuan <yuanzhichang@hisilicon.com>

After introducing the new generic I/O space management in logic pio, the
original PCI MMIO relevant helpers need to be updated based on the new
interfaces.
This patch adapts the corresponding code to match the changes introduced
by logic pio.

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>        #earlier draft
Acked-by: Bjorn Helgaas <bhelgaas@google.com>
---
 drivers/pci/pci.c        | 95 +++++++++---------------------------------------
 include/asm-generic/io.h |  2 +-
 2 files changed, 18 insertions(+), 79 deletions(-)

diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c
index 07290a3..8aa5c54 100644
--- a/drivers/pci/pci.c
+++ b/drivers/pci/pci.c
@@ -22,6 +22,7 @@
 #include <linux/spinlock.h>
 #include <linux/string.h>
 #include <linux/log2.h>
+#include <linux/logic_pio.h>
 #include <linux/pci-aspm.h>
 #include <linux/pm_wakeup.h>
 #include <linux/interrupt.h>
@@ -3440,17 +3441,6 @@ int pci_request_regions_exclusive(struct pci_dev *pdev, const char *res_name)
 }
 EXPORT_SYMBOL(pci_request_regions_exclusive);
 
-#ifdef PCI_IOBASE
-struct io_range {
-	struct list_head list;
-	phys_addr_t start;
-	resource_size_t size;
-};
-
-static LIST_HEAD(io_range_list);
-static DEFINE_SPINLOCK(io_range_lock);
-#endif
-
 /*
  * Record the PCI IO range (expressed as CPU physical address + size).
  * Return a negative value if an error has occured, zero otherwise
@@ -3458,51 +3448,28 @@ struct io_range {
 int pci_register_io_range(struct fwnode_handle *fwnode, phys_addr_t addr,
 			resource_size_t	size)
 {
-	int err = 0;
-
+	int ret = 0;
 #ifdef PCI_IOBASE
-	struct io_range *range;
-	resource_size_t allocated_size = 0;
-
-	/* 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) {
-			/* range already registered, bail out */
-			goto end_register;
-		}
-		allocated_size += range->size;
-	}
+	struct logic_pio_hwaddr *range;
 
-	/* range not registed yet, check for available space */
-	if (allocated_size + size - 1 > IO_SPACE_LIMIT) {
-		/* if it's too big check if 64K space can be reserved */
-		if (allocated_size + SZ_64K - 1 > IO_SPACE_LIMIT) {
-			err = -E2BIG;
-			goto end_register;
-		}
-
-		size = SZ_64K;
-		pr_warn("Requested IO range too big, new size set to 64K\n");
-	}
+	if (!size || addr + size < addr)
+		return -EINVAL;
 
-	/* add the range to the list */
 	range = kzalloc(sizeof(*range), GFP_ATOMIC);
-	if (!range) {
-		err = -ENOMEM;
-		goto end_register;
-	}
+	if (!range)
+		return -ENOMEM;
 
-	range->start = addr;
+	range->fwnode = fwnode;
 	range->size = size;
+	range->hw_start = addr;
+	range->flags = PIO_CPU_MMIO;
 
-	list_add_tail(&range->list, &io_range_list);
-
-end_register:
-	spin_unlock(&io_range_lock);
+	ret = logic_pio_register_range(range);
+	if (ret)
+		kfree(range);
 #endif
 
-	return err;
+	return ret;
 }
 
 phys_addr_t pci_pio_to_address(unsigned long pio)
@@ -3510,21 +3477,10 @@ phys_addr_t pci_pio_to_address(unsigned long pio)
 	phys_addr_t address = (phys_addr_t)OF_BAD_ADDR;
 
 #ifdef PCI_IOBASE
-	struct io_range *range;
-	resource_size_t allocated_size = 0;
-
-	if (pio > IO_SPACE_LIMIT)
+	if (pio >= MMIO_UPPER_LIMIT)
 		return address;
 
-	spin_lock(&io_range_lock);
-	list_for_each_entry(range, &io_range_list, list) {
-		if (pio >= allocated_size && pio < allocated_size + range->size) {
-			address = range->start + pio - allocated_size;
-			break;
-		}
-		allocated_size += range->size;
-	}
-	spin_unlock(&io_range_lock);
+	address = logic_pio_to_hwaddr(pio);
 #endif
 
 	return address;
@@ -3533,25 +3489,8 @@ phys_addr_t pci_pio_to_address(unsigned long pio)
 unsigned long __weak pci_address_to_pio(phys_addr_t address)
 {
 #ifdef PCI_IOBASE
-	struct io_range *res;
-	resource_size_t offset = 0;
-	unsigned long addr = -1;
-
-	spin_lock(&io_range_lock);
-	list_for_each_entry(res, &io_range_list, list) {
-		if (address >= res->start && address < res->start + res->size) {
-			addr = address - res->start + offset;
-			break;
-		}
-		offset += res->size;
-	}
-	spin_unlock(&io_range_lock);
-
-	return addr;
+	return logic_pio_trans_cpuaddr(address);
 #else
-	if (address > IO_SPACE_LIMIT)
-		return (unsigned long)-1;
-
 	return (unsigned long) address;
 #endif
 }
diff --git a/include/asm-generic/io.h b/include/asm-generic/io.h
index b7996a79..5a59931 100644
--- a/include/asm-generic/io.h
+++ b/include/asm-generic/io.h
@@ -901,7 +901,7 @@ static inline void iounmap(void __iomem *addr)
 #define ioport_map ioport_map
 static inline void __iomem *ioport_map(unsigned long port, unsigned int nr)
 {
-	return PCI_IOBASE + (port & IO_SPACE_LIMIT);
+	return PCI_IOBASE + (port & MMIO_UPPER_LIMIT);
 }
 #endif
 
-- 
1.9.1

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

* [PATCH v13 4/9] PCI: Apply the new generic I/O management on PCI IO hosts
@ 2018-02-13 17:45   ` John Garry
  0 siblings, 0 replies; 66+ messages in thread
From: John Garry @ 2018-02-13 17:45 UTC (permalink / raw)
  To: mika.westerberg, rafael, lorenzo.pieralisi, rjw, hanjun.guo,
	robh+dt, bhelgaas, arnd, mark.rutland, olof, dann.frazier,
	andy.shevchenko, robh
  Cc: joe, benh, linux-pci, linux-kernel, linux-acpi, linuxarm,
	minyard, devicetree, linux-arch, rdunlap

From: Zhichang Yuan <yuanzhichang@hisilicon.com>

After introducing the new generic I/O space management in logic pio, the
original PCI MMIO relevant helpers need to be updated based on the new
interfaces.
This patch adapts the corresponding code to match the changes introduced
by logic pio.

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>        #earlier draft
Acked-by: Bjorn Helgaas <bhelgaas@google.com>
---
 drivers/pci/pci.c        | 95 +++++++++---------------------------------------
 include/asm-generic/io.h |  2 +-
 2 files changed, 18 insertions(+), 79 deletions(-)

diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c
index 07290a3..8aa5c54 100644
--- a/drivers/pci/pci.c
+++ b/drivers/pci/pci.c
@@ -22,6 +22,7 @@
 #include <linux/spinlock.h>
 #include <linux/string.h>
 #include <linux/log2.h>
+#include <linux/logic_pio.h>
 #include <linux/pci-aspm.h>
 #include <linux/pm_wakeup.h>
 #include <linux/interrupt.h>
@@ -3440,17 +3441,6 @@ int pci_request_regions_exclusive(struct pci_dev *pdev, const char *res_name)
 }
 EXPORT_SYMBOL(pci_request_regions_exclusive);
 
-#ifdef PCI_IOBASE
-struct io_range {
-	struct list_head list;
-	phys_addr_t start;
-	resource_size_t size;
-};
-
-static LIST_HEAD(io_range_list);
-static DEFINE_SPINLOCK(io_range_lock);
-#endif
-
 /*
  * Record the PCI IO range (expressed as CPU physical address + size).
  * Return a negative value if an error has occured, zero otherwise
@@ -3458,51 +3448,28 @@ struct io_range {
 int pci_register_io_range(struct fwnode_handle *fwnode, phys_addr_t addr,
 			resource_size_t	size)
 {
-	int err = 0;
-
+	int ret = 0;
 #ifdef PCI_IOBASE
-	struct io_range *range;
-	resource_size_t allocated_size = 0;
-
-	/* 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) {
-			/* range already registered, bail out */
-			goto end_register;
-		}
-		allocated_size += range->size;
-	}
+	struct logic_pio_hwaddr *range;
 
-	/* range not registed yet, check for available space */
-	if (allocated_size + size - 1 > IO_SPACE_LIMIT) {
-		/* if it's too big check if 64K space can be reserved */
-		if (allocated_size + SZ_64K - 1 > IO_SPACE_LIMIT) {
-			err = -E2BIG;
-			goto end_register;
-		}
-
-		size = SZ_64K;
-		pr_warn("Requested IO range too big, new size set to 64K\n");
-	}
+	if (!size || addr + size < addr)
+		return -EINVAL;
 
-	/* add the range to the list */
 	range = kzalloc(sizeof(*range), GFP_ATOMIC);
-	if (!range) {
-		err = -ENOMEM;
-		goto end_register;
-	}
+	if (!range)
+		return -ENOMEM;
 
-	range->start = addr;
+	range->fwnode = fwnode;
 	range->size = size;
+	range->hw_start = addr;
+	range->flags = PIO_CPU_MMIO;
 
-	list_add_tail(&range->list, &io_range_list);
-
-end_register:
-	spin_unlock(&io_range_lock);
+	ret = logic_pio_register_range(range);
+	if (ret)
+		kfree(range);
 #endif
 
-	return err;
+	return ret;
 }
 
 phys_addr_t pci_pio_to_address(unsigned long pio)
@@ -3510,21 +3477,10 @@ phys_addr_t pci_pio_to_address(unsigned long pio)
 	phys_addr_t address = (phys_addr_t)OF_BAD_ADDR;
 
 #ifdef PCI_IOBASE
-	struct io_range *range;
-	resource_size_t allocated_size = 0;
-
-	if (pio > IO_SPACE_LIMIT)
+	if (pio >= MMIO_UPPER_LIMIT)
 		return address;
 
-	spin_lock(&io_range_lock);
-	list_for_each_entry(range, &io_range_list, list) {
-		if (pio >= allocated_size && pio < allocated_size + range->size) {
-			address = range->start + pio - allocated_size;
-			break;
-		}
-		allocated_size += range->size;
-	}
-	spin_unlock(&io_range_lock);
+	address = logic_pio_to_hwaddr(pio);
 #endif
 
 	return address;
@@ -3533,25 +3489,8 @@ phys_addr_t pci_pio_to_address(unsigned long pio)
 unsigned long __weak pci_address_to_pio(phys_addr_t address)
 {
 #ifdef PCI_IOBASE
-	struct io_range *res;
-	resource_size_t offset = 0;
-	unsigned long addr = -1;
-
-	spin_lock(&io_range_lock);
-	list_for_each_entry(res, &io_range_list, list) {
-		if (address >= res->start && address < res->start + res->size) {
-			addr = address - res->start + offset;
-			break;
-		}
-		offset += res->size;
-	}
-	spin_unlock(&io_range_lock);
-
-	return addr;
+	return logic_pio_trans_cpuaddr(address);
 #else
-	if (address > IO_SPACE_LIMIT)
-		return (unsigned long)-1;
-
 	return (unsigned long) address;
 #endif
 }
diff --git a/include/asm-generic/io.h b/include/asm-generic/io.h
index b7996a79..5a59931 100644
--- a/include/asm-generic/io.h
+++ b/include/asm-generic/io.h
@@ -901,7 +901,7 @@ static inline void iounmap(void __iomem *addr)
 #define ioport_map ioport_map
 static inline void __iomem *ioport_map(unsigned long port, unsigned int nr)
 {
-	return PCI_IOBASE + (port & IO_SPACE_LIMIT);
+	return PCI_IOBASE + (port & MMIO_UPPER_LIMIT);
 }
 #endif
 
-- 
1.9.1

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

* [PATCH v13 5/9] OF: Add missing I/O range exception for indirect-IO devices
  2018-02-13 17:45 ` John Garry
@ 2018-02-13 17:45   ` John Garry
  -1 siblings, 0 replies; 66+ messages in thread
From: John Garry @ 2018-02-13 17:45 UTC (permalink / raw)
  To: mika.westerberg, rafael, lorenzo.pieralisi, rjw, hanjun.guo,
	robh+dt, bhelgaas, arnd, mark.rutland, olof, dann.frazier,
	andy.shevchenko, robh
  Cc: joe, benh, linux-pci, linux-kernel, linux-acpi, linuxarm,
	minyard, devicetree, linux-arch, rdunlap

From: Zhichang Yuan <yuanzhichang@hisilicon.com>

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>    #earlier draft
Acked-by: Rob Herring <robh@kernel.org>
---
 drivers/of/address.c | 92 +++++++++++++++++++++++++++++++++++++++++++---------
 1 file changed, 76 insertions(+), 16 deletions(-)

diff --git a/drivers/of/address.c b/drivers/of/address.c
index cdf047b..2db8b56 100644
--- a/drivers/of/address.c
+++ b/drivers/of/address.c
@@ -5,6 +5,7 @@
 #include <linux/fwnode.h>
 #include <linux/io.h>
 #include <linux/ioport.h>
+#include <linux/logic_pio.h>
 #include <linux/module.h>
 #include <linux/of_address.h>
 #include <linux/pci.h>
@@ -562,9 +563,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 had registered logical PIO mapping, 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;
@@ -577,6 +583,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)
@@ -597,6 +604,8 @@ static u64 __of_translate_address(struct device_node *dev,
 
 	/* Translate */
 	for (;;) {
+		struct logic_pio_hwaddr *iorange;
+
 		/* Switch to parent bus */
 		of_node_put(dev);
 		dev = parent;
@@ -609,6 +618,19 @@ static u64 __of_translate_address(struct device_node *dev,
 			break;
 		}
 
+		/*
+		 * For indirectIO device which has no ranges property, get
+		 * the address from reg directly.
+		 */
+		iorange = find_io_range_by_fwnode(&dev->fwnode);
+		if (iorange && (iorange->flags != PIO_CPU_MMIO)) {
+			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);
@@ -640,13 +662,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);
 
@@ -688,29 +729,48 @@ 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 size)
+{
+	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 = logic_pio_trans_hwaddr(&host->fwnode, taddr, size);
+		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, size);
+	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] 66+ messages in thread

* [PATCH v13 5/9] OF: Add missing I/O range exception for indirect-IO devices
@ 2018-02-13 17:45   ` John Garry
  0 siblings, 0 replies; 66+ messages in thread
From: John Garry @ 2018-02-13 17:45 UTC (permalink / raw)
  To: mika.westerberg, rafael, lorenzo.pieralisi, rjw, hanjun.guo,
	robh+dt, bhelgaas, arnd, mark.rutland, olof, dann.frazier,
	andy.shevchenko, robh
  Cc: joe, benh, linux-pci, linux-kernel, linux-acpi, linuxarm,
	minyard, devicetree, linux-arch, rdunlap

From: Zhichang Yuan <yuanzhichang@hisilicon.com>

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>    #earlier draft
Acked-by: Rob Herring <robh@kernel.org>
---
 drivers/of/address.c | 92 +++++++++++++++++++++++++++++++++++++++++++---------
 1 file changed, 76 insertions(+), 16 deletions(-)

diff --git a/drivers/of/address.c b/drivers/of/address.c
index cdf047b..2db8b56 100644
--- a/drivers/of/address.c
+++ b/drivers/of/address.c
@@ -5,6 +5,7 @@
 #include <linux/fwnode.h>
 #include <linux/io.h>
 #include <linux/ioport.h>
+#include <linux/logic_pio.h>
 #include <linux/module.h>
 #include <linux/of_address.h>
 #include <linux/pci.h>
@@ -562,9 +563,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 had registered logical PIO mapping, 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;
@@ -577,6 +583,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)
@@ -597,6 +604,8 @@ static u64 __of_translate_address(struct device_node *dev,
 
 	/* Translate */
 	for (;;) {
+		struct logic_pio_hwaddr *iorange;
+
 		/* Switch to parent bus */
 		of_node_put(dev);
 		dev = parent;
@@ -609,6 +618,19 @@ static u64 __of_translate_address(struct device_node *dev,
 			break;
 		}
 
+		/*
+		 * For indirectIO device which has no ranges property, get
+		 * the address from reg directly.
+		 */
+		iorange = find_io_range_by_fwnode(&dev->fwnode);
+		if (iorange && (iorange->flags != PIO_CPU_MMIO)) {
+			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);
@@ -640,13 +662,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);
 
@@ -688,29 +729,48 @@ 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 size)
+{
+	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 = logic_pio_trans_hwaddr(&host->fwnode, taddr, size);
+		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, size);
+	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] 66+ messages in thread

* [PATCH v13 6/9] LPC: Support the LPC host on Hip06/Hip07 with DT bindings
  2018-02-13 17:45 ` John Garry
  (?)
@ 2018-02-13 17:45     ` John Garry
  -1 siblings, 0 replies; 66+ messages in thread
From: John Garry @ 2018-02-13 17:45 UTC (permalink / raw)
  To: mika.westerberg-VuQAYsv1563Yd54FQh9/CA,
	rafael-DgEjT+Ai2ygdnm+yROfE0A, lorenzo.pieralisi-5wv7dgnIgG8,
	rjw-LthD3rsA81gm4RdzfppkhA, hanjun.guo-QSEj5FYQhm4dnm+yROfE0A,
	robh+dt-DgEjT+Ai2ygdnm+yROfE0A, bhelgaas-hpIqsD4AKlfQT0dZR+AlfA,
	arnd-r2nGTMty4D4, mark.rutland-5wv7dgnIgG8,
	olof-nZhT3qVonbNeoWH0uzbU5w, dann.frazier-Z7WLFzj8eWMS+FvcfC7Uqw,
	andy.shevchenko-Re5JQEeQqe8AvxtiuMwx3w,
	robh-DgEjT+Ai2ygdnm+yROfE0A
  Cc: joe-6d6DIl74uiNBDgjK7y7TUQ,
	benh-XVmvHMARGAS8U2dJNN8I7kB+6BGkLq7r,
	linux-pci-u79uwXL29TY76Z2rM5mHXA,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	linux-acpi-u79uwXL29TY76Z2rM5mHXA,
	linuxarm-hv44wF8Li93QT0dZR+AlfA, minyard-HInyCGIudOg,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-arch-u79uwXL29TY76Z2rM5mHXA,
	rdunlap-wEGCiKHe2LqWVfeAwA7xHQ

From: Zhichang Yuan <yuanzhichang-C8/M+/jPZTeaMJb+Lgu22Q@public.gmane.org>

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 to retrieve the host-local I/O port.

The driver config is set as a bool instead of a trisate. The reason
here is that, by the very nature of the driver providing a logical
PIO range, it does not make sense to have this driver as a loadable
module. Another more specific reason is that the Huawei D03 board
which includes hip06 SoC requires the LPC bus for UART console, so
should be built in.

Signed-off-by: Zou Rongrong <zourongrong-hv44wF8Li93QT0dZR+AlfA@public.gmane.org>
Signed-off-by: Zhichang Yuan <yuanzhichang-C8/M+/jPZTeaMJb+Lgu22Q@public.gmane.org>
Signed-off-by: John Garry <john.garry-hv44wF8Li93QT0dZR+AlfA@public.gmane.org>
Acked-by: Rob Herring <robh-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org> #dts part
---
 .../arm/hisilicon/hisilicon-low-pin-count.txt      |  33 ++
 drivers/bus/Kconfig                                |   8 +
 drivers/bus/Makefile                               |   1 +
 drivers/bus/hisi_lpc.c                             | 507 +++++++++++++++++++++
 4 files changed, 549 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/drivers/bus/Kconfig b/drivers/bus/Kconfig
index 57e011d..91debe4 100644
--- a/drivers/bus/Kconfig
+++ b/drivers/bus/Kconfig
@@ -65,6 +65,14 @@ config BRCMSTB_GISB_ARB
 	  arbiter. This driver provides timeout and target abort error handling
 	  and internal bus master decoding.
 
+config HISILICON_LPC
+	bool "Support for ISA I/O space on Hisilicon hip06/7"
+	depends on (ARM64 && (ARCH_HISI || COMPILE_TEST))
+	select INDIRECT_PIO
+	help
+	  Driver needed for some legacy ISA devices attached to Low-Pin-Count
+	  on Hisilicon hip06/7 SoC.
+
 config IMX_WEIM
 	bool "Freescale EIM DRIVER"
 	depends on ARCH_MXC
diff --git a/drivers/bus/Makefile b/drivers/bus/Makefile
index 9bcd0bf..abc7a42 100644
--- a/drivers/bus/Makefile
+++ b/drivers/bus/Makefile
@@ -8,6 +8,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..2290cf2
--- /dev/null
+++ b/drivers/bus/hisi_lpc.c
@@ -0,0 +1,507 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2017 Hisilicon Limited, All Rights Reserved.
+ * Author: Zhichang Yuan <yuanzhichang-C8/M+/jPZTeaMJb+Lgu22Q@public.gmane.org>
+ * Author: Zou Rongrong <zourongrong-hv44wF8Li93QT0dZR+AlfA@public.gmane.org>
+ * Author: John Garry <john.garry-hv44wF8Li93QT0dZR+AlfA@public.gmane.org>
+ *
+ */
+
+#include <linux/acpi.h>
+#include <linux/console.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/logic_pio.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/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 logic_pio_hwaddr *io_host;
+};
+
+/* The maximum continuous cycles per burst */
+#define LPC_MAX_BURST	16
+/* The IO cycle counts supported is four per operation at maximum */
+#define LPC_MAX_DULEN	4
+#if LPC_MAX_DULEN > LPC_MAX_BURST
+#error "LPC.. MAX_DULEN must be not bigger than MAX_OPCNT!"
+#endif
+
+#if LPC_MAX_BURST % LPC_MAX_DULEN
+#error "LPC.. LPC_MAX_BURST must be multiple of LPC_MAX_DULEN!"
+#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 LPC_START_WORK		0x01
+
+/* The minimal nanosecond interval for each query on LPC cycle status. */
+#define LPC_NSEC_PERWAIT	100
+/*
+ * The maximum waiting time is about 128us.
+ * It is specific for stream I/O, such as ins.
+ * 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.
+ * @lpcdev: 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;
+
+	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_relaxed(cnt_per_trans, lpcdev->membase + LPC_REG_OP_LEN);
+
+		writel_relaxed(cmd_word, lpcdev->membase + LPC_REG_CMD);
+
+		writel_relaxed(ptaddr, lpcdev->membase + LPC_REG_ADDR);
+
+		writel(LPC_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--; cnt_per_trans--; buf++)
+				*buf = readb_relaxed(lpcdev->membase +
+					LPC_REG_RDATA);
+			*buf = readb(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.
+ * @lpcdev: 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;
+
+	/* 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_relaxed(cnt_per_trans, lpcdev->membase + LPC_REG_OP_LEN);
+		writel_relaxed(cmd_word, lpcdev->membase + LPC_REG_CMD);
+		writel_relaxed(ptaddr, lpcdev->membase + LPC_REG_ADDR);
+
+		opcnt -= cnt_per_trans;
+		for (; cnt_per_trans--; buf++)
+			writeb_relaxed(*buf, lpcdev->membase + LPC_REG_WDATA);
+
+		writel(LPC_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->io_host->io_start +
+		lpcdev->io_host->hw_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 u32 hisilpc_comm_in(void *devobj, unsigned long pio, size_t dlen)
+{
+	int ret = 0;
+	unsigned char rd_data = 0;
+	unsigned long ptaddr;
+	struct lpc_cycle_para iopara;
+	struct hisilpc_dev *lpcdev = devobj;
+
+	if (!lpcdev || !dlen || dlen > LPC_MAX_DULEN)
+		return -1;
+
+	ptaddr = hisi_lpc_pio_to_addr(lpcdev, pio);
+
+	iopara.opflags = FG_INCRADDR_LPC;
+	iopara.csize = dlen;
+
+	ret = hisilpc_target_in(lpcdev, &iopara, ptaddr, &rd_data, dlen);
+	if (ret)
+		return -1;
+
+	return le32_to_cpu((u32)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.
+ * @pio: the target I/O port address.
+ * @outval: a value to be outputted from caller, maximum is four bytes.
+ * @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)
+{
+	unsigned long ptaddr;
+	struct hisilpc_dev *lpcdev = devobj;
+	struct lpc_cycle_para iopara;
+	const unsigned char *newbuf;
+
+	if (!lpcdev || !dlen || dlen > LPC_MAX_DULEN)
+		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.
+ *
+ * when succeed, the data read back is stored in buffer pointed by inbuf.
+ * Returns 0 on success, -errno otherwise
+ *
+ */
+static u32
+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 long ptaddr;
+
+	if (!lpcdev || !inbuf || !count || !dlen || dlen > LPC_MAX_DULEN ||
+			count % dlen)
+		return -EINVAL;
+
+	iopara.opflags = 0;
+	if (dlen > 1)
+		iopara.opflags |= FG_INCRADDR_LPC;
+	iopara.csize = dlen;
+
+	ptaddr = hisi_lpc_pio_to_addr(lpcdev, pio);
+	newbuf = inbuf;
+	/*
+	 * ensure data stream whose length is multiple of dlen to be processed
+	 * each IO input
+	 */
+	cntleft = count * dlen;
+	do {
+		int ret;
+
+		loopcnt = min_t(unsigned int, LPC_MAX_BURST, 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 long ptaddr;
+
+	if (!lpcdev || !outbuf || !count || !dlen || dlen > LPC_MAX_DULEN ||
+			count % dlen)
+		return;
+
+	iopara.opflags = 0;
+	if (dlen > 1)
+		iopara.opflags |= FG_INCRADDR_LPC;
+	iopara.csize = dlen;
+
+	ptaddr = hisi_lpc_pio_to_addr(lpcdev, pio);
+	newbuf = outbuf;
+	/*
+	 * ensure data stream whose length is multiple of dlen to be processed
+	 * each IO input
+	 */
+	cntleft = count * dlen;
+	do {
+		loopcnt = min_t(unsigned int, LPC_MAX_BURST, cntleft);
+		if (hisilpc_target_out(lpcdev, &iopara, ptaddr, newbuf,
+						loopcnt))
+			break;
+		newbuf += loopcnt;
+		cntleft -= loopcnt;
+	} while (cntleft);
+}
+
+static struct hostio_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;
+
+	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)
+		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);
+	}
+
+	/* register the LPC host PIO resources */
+	{
+		struct logic_pio_hwaddr *range;
+
+		range = devm_kzalloc(dev, sizeof(*range), GFP_KERNEL);
+		if (!range)
+			return -ENOMEM;
+		range->fwnode = dev->fwnode;
+		range->flags = PIO_INDIRECT;
+		range->size = PIO_INDIRECT_SIZE;
+
+		ret = logic_pio_register_range(range);
+		if (ret) {
+			kfree(range);
+			dev_err(dev, "register IO range failed (%d)!\n", ret);
+			return -ret;
+		}
+		lpcdev->io_host = range;
+
+		/*
+		 * It is time to start the children scanning:
+		 * For ACPI children, the corresponding devices had been
+		 * created during the ACPI enumeration.
+		 * The OF scanning must be performed after initialization of
+		 * 'lpcdev' to avoid some children which complete the scanning
+		 * trigger the MMIO accesses which will probably cause a panic.
+		 */
+		ret = of_platform_populate(dev->of_node, NULL, NULL, dev);
+		if (ret) {
+			/*
+			 * When LPC probing is not completely successful, set
+			 * 'devpara' as NULL. This will make all the LPC I/O
+			 * return failure directly without any hardware
+			 * operations. It will prevent some peripherals which
+			 * had not finished the initialization to manipulate
+			 * I/O for safety.
+			 */
+			lpcdev->io_host->devpara = NULL;
+			dev_err(dev, "populate children failed(%d)\n", ret);
+			return ret;
+		}
+	}
+	lpcdev->io_host->devpara = lpcdev;
+	lpcdev->io_host->ops = &hisi_lpc_ops;
+
+	dev_info(dev, "registered range[%pa - sz:%pa]\n",
+		 &lpcdev->io_host->io_start,
+		 &lpcdev->io_host->size);
+
+	return ret;
+}
+
+static const struct of_device_id hisilpc_of_match[] = {
+	{ .compatible = "hisilicon,hip06-lpc", },
+	{ .compatible = "hisilicon,hip07-lpc", },
+	{},
+};
+
+static struct platform_driver hisilpc_driver = {
+	.driver = {
+		.name           = "hisi_lpc",
+		.of_match_table = hisilpc_of_match,
+	},
+	.probe = hisilpc_probe,
+};
+
+builtin_platform_driver(hisilpc_driver);
-- 
1.9.1

--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

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

* [PATCH v13 6/9] LPC: Support the LPC host on Hip06/Hip07 with DT bindings
@ 2018-02-13 17:45     ` John Garry
  0 siblings, 0 replies; 66+ messages in thread
From: John Garry @ 2018-02-13 17:45 UTC (permalink / raw)
  To: mika.westerberg, rafael, lorenzo.pieralisi, rjw, hanjun.guo,
	robh+dt, bhelgaas, arnd, mark.rutland, olof, dann.frazier,
	andy.shevchenko, robh
  Cc: joe, benh, linux-pci, linux-kernel, linux-acpi, linuxarm,
	minyard, devicetree, linux-arch, rdunlap

From: Zhichang Yuan <yuanzhichang@hisilicon.com>

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 to retrieve the host-local I/O port.

The driver config is set as a bool instead of a trisate. The reason
here is that, by the very nature of the driver providing a logical
PIO range, it does not make sense to have this driver as a loadable
module. Another more specific reason is that the Huawei D03 board
which includes hip06 SoC requires the LPC bus for UART console, so
should be built in.

Signed-off-by: Zou Rongrong <zourongrong@huawei.com>
Signed-off-by: Zhichang Yuan <yuanzhichang@hisilicon.com>
Signed-off-by: John Garry <john.garry@huawei.com>
Acked-by: Rob Herring <robh@kernel.org> #dts part
---
 .../arm/hisilicon/hisilicon-low-pin-count.txt      |  33 ++
 drivers/bus/Kconfig                                |   8 +
 drivers/bus/Makefile                               |   1 +
 drivers/bus/hisi_lpc.c                             | 507 +++++++++++++++++++++
 4 files changed, 549 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/drivers/bus/Kconfig b/drivers/bus/Kconfig
index 57e011d..91debe4 100644
--- a/drivers/bus/Kconfig
+++ b/drivers/bus/Kconfig
@@ -65,6 +65,14 @@ config BRCMSTB_GISB_ARB
 	  arbiter. This driver provides timeout and target abort error handling
 	  and internal bus master decoding.
 
+config HISILICON_LPC
+	bool "Support for ISA I/O space on Hisilicon hip06/7"
+	depends on (ARM64 && (ARCH_HISI || COMPILE_TEST))
+	select INDIRECT_PIO
+	help
+	  Driver needed for some legacy ISA devices attached to Low-Pin-Count
+	  on Hisilicon hip06/7 SoC.
+
 config IMX_WEIM
 	bool "Freescale EIM DRIVER"
 	depends on ARCH_MXC
diff --git a/drivers/bus/Makefile b/drivers/bus/Makefile
index 9bcd0bf..abc7a42 100644
--- a/drivers/bus/Makefile
+++ b/drivers/bus/Makefile
@@ -8,6 +8,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..2290cf2
--- /dev/null
+++ b/drivers/bus/hisi_lpc.c
@@ -0,0 +1,507 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2017 Hisilicon Limited, All Rights Reserved.
+ * Author: Zhichang Yuan <yuanzhichang@hisilicon.com>
+ * Author: Zou Rongrong <zourongrong@huawei.com>
+ * Author: John Garry <john.garry@huawei.com>
+ *
+ */
+
+#include <linux/acpi.h>
+#include <linux/console.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/logic_pio.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/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 logic_pio_hwaddr *io_host;
+};
+
+/* The maximum continuous cycles per burst */
+#define LPC_MAX_BURST	16
+/* The IO cycle counts supported is four per operation at maximum */
+#define LPC_MAX_DULEN	4
+#if LPC_MAX_DULEN > LPC_MAX_BURST
+#error "LPC.. MAX_DULEN must be not bigger than MAX_OPCNT!"
+#endif
+
+#if LPC_MAX_BURST % LPC_MAX_DULEN
+#error "LPC.. LPC_MAX_BURST must be multiple of LPC_MAX_DULEN!"
+#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 LPC_START_WORK		0x01
+
+/* The minimal nanosecond interval for each query on LPC cycle status. */
+#define LPC_NSEC_PERWAIT	100
+/*
+ * The maximum waiting time is about 128us.
+ * It is specific for stream I/O, such as ins.
+ * 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.
+ * @lpcdev: 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;
+
+	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_relaxed(cnt_per_trans, lpcdev->membase + LPC_REG_OP_LEN);
+
+		writel_relaxed(cmd_word, lpcdev->membase + LPC_REG_CMD);
+
+		writel_relaxed(ptaddr, lpcdev->membase + LPC_REG_ADDR);
+
+		writel(LPC_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--; cnt_per_trans--; buf++)
+				*buf = readb_relaxed(lpcdev->membase +
+					LPC_REG_RDATA);
+			*buf = readb(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.
+ * @lpcdev: 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;
+
+	/* 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_relaxed(cnt_per_trans, lpcdev->membase + LPC_REG_OP_LEN);
+		writel_relaxed(cmd_word, lpcdev->membase + LPC_REG_CMD);
+		writel_relaxed(ptaddr, lpcdev->membase + LPC_REG_ADDR);
+
+		opcnt -= cnt_per_trans;
+		for (; cnt_per_trans--; buf++)
+			writeb_relaxed(*buf, lpcdev->membase + LPC_REG_WDATA);
+
+		writel(LPC_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->io_host->io_start +
+		lpcdev->io_host->hw_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 u32 hisilpc_comm_in(void *devobj, unsigned long pio, size_t dlen)
+{
+	int ret = 0;
+	unsigned char rd_data = 0;
+	unsigned long ptaddr;
+	struct lpc_cycle_para iopara;
+	struct hisilpc_dev *lpcdev = devobj;
+
+	if (!lpcdev || !dlen || dlen > LPC_MAX_DULEN)
+		return -1;
+
+	ptaddr = hisi_lpc_pio_to_addr(lpcdev, pio);
+
+	iopara.opflags = FG_INCRADDR_LPC;
+	iopara.csize = dlen;
+
+	ret = hisilpc_target_in(lpcdev, &iopara, ptaddr, &rd_data, dlen);
+	if (ret)
+		return -1;
+
+	return le32_to_cpu((u32)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.
+ * @pio: the target I/O port address.
+ * @outval: a value to be outputted from caller, maximum is four bytes.
+ * @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)
+{
+	unsigned long ptaddr;
+	struct hisilpc_dev *lpcdev = devobj;
+	struct lpc_cycle_para iopara;
+	const unsigned char *newbuf;
+
+	if (!lpcdev || !dlen || dlen > LPC_MAX_DULEN)
+		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.
+ *
+ * when succeed, the data read back is stored in buffer pointed by inbuf.
+ * Returns 0 on success, -errno otherwise
+ *
+ */
+static u32
+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 long ptaddr;
+
+	if (!lpcdev || !inbuf || !count || !dlen || dlen > LPC_MAX_DULEN ||
+			count % dlen)
+		return -EINVAL;
+
+	iopara.opflags = 0;
+	if (dlen > 1)
+		iopara.opflags |= FG_INCRADDR_LPC;
+	iopara.csize = dlen;
+
+	ptaddr = hisi_lpc_pio_to_addr(lpcdev, pio);
+	newbuf = inbuf;
+	/*
+	 * ensure data stream whose length is multiple of dlen to be processed
+	 * each IO input
+	 */
+	cntleft = count * dlen;
+	do {
+		int ret;
+
+		loopcnt = min_t(unsigned int, LPC_MAX_BURST, 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 long ptaddr;
+
+	if (!lpcdev || !outbuf || !count || !dlen || dlen > LPC_MAX_DULEN ||
+			count % dlen)
+		return;
+
+	iopara.opflags = 0;
+	if (dlen > 1)
+		iopara.opflags |= FG_INCRADDR_LPC;
+	iopara.csize = dlen;
+
+	ptaddr = hisi_lpc_pio_to_addr(lpcdev, pio);
+	newbuf = outbuf;
+	/*
+	 * ensure data stream whose length is multiple of dlen to be processed
+	 * each IO input
+	 */
+	cntleft = count * dlen;
+	do {
+		loopcnt = min_t(unsigned int, LPC_MAX_BURST, cntleft);
+		if (hisilpc_target_out(lpcdev, &iopara, ptaddr, newbuf,
+						loopcnt))
+			break;
+		newbuf += loopcnt;
+		cntleft -= loopcnt;
+	} while (cntleft);
+}
+
+static struct hostio_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;
+
+	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)
+		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);
+	}
+
+	/* register the LPC host PIO resources */
+	{
+		struct logic_pio_hwaddr *range;
+
+		range = devm_kzalloc(dev, sizeof(*range), GFP_KERNEL);
+		if (!range)
+			return -ENOMEM;
+		range->fwnode = dev->fwnode;
+		range->flags = PIO_INDIRECT;
+		range->size = PIO_INDIRECT_SIZE;
+
+		ret = logic_pio_register_range(range);
+		if (ret) {
+			kfree(range);
+			dev_err(dev, "register IO range failed (%d)!\n", ret);
+			return -ret;
+		}
+		lpcdev->io_host = range;
+
+		/*
+		 * It is time to start the children scanning:
+		 * For ACPI children, the corresponding devices had been
+		 * created during the ACPI enumeration.
+		 * The OF scanning must be performed after initialization of
+		 * 'lpcdev' to avoid some children which complete the scanning
+		 * trigger the MMIO accesses which will probably cause a panic.
+		 */
+		ret = of_platform_populate(dev->of_node, NULL, NULL, dev);
+		if (ret) {
+			/*
+			 * When LPC probing is not completely successful, set
+			 * 'devpara' as NULL. This will make all the LPC I/O
+			 * return failure directly without any hardware
+			 * operations. It will prevent some peripherals which
+			 * had not finished the initialization to manipulate
+			 * I/O for safety.
+			 */
+			lpcdev->io_host->devpara = NULL;
+			dev_err(dev, "populate children failed(%d)\n", ret);
+			return ret;
+		}
+	}
+	lpcdev->io_host->devpara = lpcdev;
+	lpcdev->io_host->ops = &hisi_lpc_ops;
+
+	dev_info(dev, "registered range[%pa - sz:%pa]\n",
+		 &lpcdev->io_host->io_start,
+		 &lpcdev->io_host->size);
+
+	return ret;
+}
+
+static const struct of_device_id hisilpc_of_match[] = {
+	{ .compatible = "hisilicon,hip06-lpc", },
+	{ .compatible = "hisilicon,hip07-lpc", },
+	{},
+};
+
+static struct platform_driver hisilpc_driver = {
+	.driver = {
+		.name           = "hisi_lpc",
+		.of_match_table = hisilpc_of_match,
+	},
+	.probe = hisilpc_probe,
+};
+
+builtin_platform_driver(hisilpc_driver);
-- 
1.9.1

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

* [PATCH v13 6/9] LPC: Support the LPC host on Hip06/Hip07 with DT bindings
@ 2018-02-13 17:45     ` John Garry
  0 siblings, 0 replies; 66+ messages in thread
From: John Garry @ 2018-02-13 17:45 UTC (permalink / raw)
  To: mika.westerberg, rafael, lorenzo.pieralisi, rjw, hanjun.guo,
	robh+dt, bhelgaas, arnd, mark.rutland, olof, dann.frazier,
	andy.shevchenko, robh
  Cc: joe, benh, linux-pci, linux-kernel, linux-acpi, linuxarm,
	minyard, devicetree, linux-arch, rdunlap

From: Zhichang Yuan <yuanzhichang@hisilicon.com>

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 to retrieve the host-local I/O port.

The driver config is set as a bool instead of a trisate. The reason
here is that, by the very nature of the driver providing a logical
PIO range, it does not make sense to have this driver as a loadable
module. Another more specific reason is that the Huawei D03 board
which includes hip06 SoC requires the LPC bus for UART console, so
should be built in.

Signed-off-by: Zou Rongrong <zourongrong@huawei.com>
Signed-off-by: Zhichang Yuan <yuanzhichang@hisilicon.com>
Signed-off-by: John Garry <john.garry@huawei.com>
Acked-by: Rob Herring <robh@kernel.org> #dts part
---
 .../arm/hisilicon/hisilicon-low-pin-count.txt      |  33 ++
 drivers/bus/Kconfig                                |   8 +
 drivers/bus/Makefile                               |   1 +
 drivers/bus/hisi_lpc.c                             | 507 +++++++++++++++++++++
 4 files changed, 549 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/drivers/bus/Kconfig b/drivers/bus/Kconfig
index 57e011d..91debe4 100644
--- a/drivers/bus/Kconfig
+++ b/drivers/bus/Kconfig
@@ -65,6 +65,14 @@ config BRCMSTB_GISB_ARB
 	  arbiter. This driver provides timeout and target abort error handling
 	  and internal bus master decoding.
 
+config HISILICON_LPC
+	bool "Support for ISA I/O space on Hisilicon hip06/7"
+	depends on (ARM64 && (ARCH_HISI || COMPILE_TEST))
+	select INDIRECT_PIO
+	help
+	  Driver needed for some legacy ISA devices attached to Low-Pin-Count
+	  on Hisilicon hip06/7 SoC.
+
 config IMX_WEIM
 	bool "Freescale EIM DRIVER"
 	depends on ARCH_MXC
diff --git a/drivers/bus/Makefile b/drivers/bus/Makefile
index 9bcd0bf..abc7a42 100644
--- a/drivers/bus/Makefile
+++ b/drivers/bus/Makefile
@@ -8,6 +8,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..2290cf2
--- /dev/null
+++ b/drivers/bus/hisi_lpc.c
@@ -0,0 +1,507 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2017 Hisilicon Limited, All Rights Reserved.
+ * Author: Zhichang Yuan <yuanzhichang@hisilicon.com>
+ * Author: Zou Rongrong <zourongrong@huawei.com>
+ * Author: John Garry <john.garry@huawei.com>
+ *
+ */
+
+#include <linux/acpi.h>
+#include <linux/console.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/logic_pio.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/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 logic_pio_hwaddr *io_host;
+};
+
+/* The maximum continuous cycles per burst */
+#define LPC_MAX_BURST	16
+/* The IO cycle counts supported is four per operation at maximum */
+#define LPC_MAX_DULEN	4
+#if LPC_MAX_DULEN > LPC_MAX_BURST
+#error "LPC.. MAX_DULEN must be not bigger than MAX_OPCNT!"
+#endif
+
+#if LPC_MAX_BURST % LPC_MAX_DULEN
+#error "LPC.. LPC_MAX_BURST must be multiple of LPC_MAX_DULEN!"
+#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 LPC_START_WORK		0x01
+
+/* The minimal nanosecond interval for each query on LPC cycle status. */
+#define LPC_NSEC_PERWAIT	100
+/*
+ * The maximum waiting time is about 128us.
+ * It is specific for stream I/O, such as ins.
+ * 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.
+ * @lpcdev: 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;
+
+	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_relaxed(cnt_per_trans, lpcdev->membase + LPC_REG_OP_LEN);
+
+		writel_relaxed(cmd_word, lpcdev->membase + LPC_REG_CMD);
+
+		writel_relaxed(ptaddr, lpcdev->membase + LPC_REG_ADDR);
+
+		writel(LPC_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--; cnt_per_trans--; buf++)
+				*buf = readb_relaxed(lpcdev->membase +
+					LPC_REG_RDATA);
+			*buf = readb(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.
+ * @lpcdev: 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;
+
+	/* 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_relaxed(cnt_per_trans, lpcdev->membase + LPC_REG_OP_LEN);
+		writel_relaxed(cmd_word, lpcdev->membase + LPC_REG_CMD);
+		writel_relaxed(ptaddr, lpcdev->membase + LPC_REG_ADDR);
+
+		opcnt -= cnt_per_trans;
+		for (; cnt_per_trans--; buf++)
+			writeb_relaxed(*buf, lpcdev->membase + LPC_REG_WDATA);
+
+		writel(LPC_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->io_host->io_start +
+		lpcdev->io_host->hw_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 u32 hisilpc_comm_in(void *devobj, unsigned long pio, size_t dlen)
+{
+	int ret = 0;
+	unsigned char rd_data = 0;
+	unsigned long ptaddr;
+	struct lpc_cycle_para iopara;
+	struct hisilpc_dev *lpcdev = devobj;
+
+	if (!lpcdev || !dlen || dlen > LPC_MAX_DULEN)
+		return -1;
+
+	ptaddr = hisi_lpc_pio_to_addr(lpcdev, pio);
+
+	iopara.opflags = FG_INCRADDR_LPC;
+	iopara.csize = dlen;
+
+	ret = hisilpc_target_in(lpcdev, &iopara, ptaddr, &rd_data, dlen);
+	if (ret)
+		return -1;
+
+	return le32_to_cpu((u32)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.
+ * @pio: the target I/O port address.
+ * @outval: a value to be outputted from caller, maximum is four bytes.
+ * @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)
+{
+	unsigned long ptaddr;
+	struct hisilpc_dev *lpcdev = devobj;
+	struct lpc_cycle_para iopara;
+	const unsigned char *newbuf;
+
+	if (!lpcdev || !dlen || dlen > LPC_MAX_DULEN)
+		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.
+ *
+ * when succeed, the data read back is stored in buffer pointed by inbuf.
+ * Returns 0 on success, -errno otherwise
+ *
+ */
+static u32
+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 long ptaddr;
+
+	if (!lpcdev || !inbuf || !count || !dlen || dlen > LPC_MAX_DULEN ||
+			count % dlen)
+		return -EINVAL;
+
+	iopara.opflags = 0;
+	if (dlen > 1)
+		iopara.opflags |= FG_INCRADDR_LPC;
+	iopara.csize = dlen;
+
+	ptaddr = hisi_lpc_pio_to_addr(lpcdev, pio);
+	newbuf = inbuf;
+	/*
+	 * ensure data stream whose length is multiple of dlen to be processed
+	 * each IO input
+	 */
+	cntleft = count * dlen;
+	do {
+		int ret;
+
+		loopcnt = min_t(unsigned int, LPC_MAX_BURST, 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 long ptaddr;
+
+	if (!lpcdev || !outbuf || !count || !dlen || dlen > LPC_MAX_DULEN ||
+			count % dlen)
+		return;
+
+	iopara.opflags = 0;
+	if (dlen > 1)
+		iopara.opflags |= FG_INCRADDR_LPC;
+	iopara.csize = dlen;
+
+	ptaddr = hisi_lpc_pio_to_addr(lpcdev, pio);
+	newbuf = outbuf;
+	/*
+	 * ensure data stream whose length is multiple of dlen to be processed
+	 * each IO input
+	 */
+	cntleft = count * dlen;
+	do {
+		loopcnt = min_t(unsigned int, LPC_MAX_BURST, cntleft);
+		if (hisilpc_target_out(lpcdev, &iopara, ptaddr, newbuf,
+						loopcnt))
+			break;
+		newbuf += loopcnt;
+		cntleft -= loopcnt;
+	} while (cntleft);
+}
+
+static struct hostio_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;
+
+	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)
+		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);
+	}
+
+	/* register the LPC host PIO resources */
+	{
+		struct logic_pio_hwaddr *range;
+
+		range = devm_kzalloc(dev, sizeof(*range), GFP_KERNEL);
+		if (!range)
+			return -ENOMEM;
+		range->fwnode = dev->fwnode;
+		range->flags = PIO_INDIRECT;
+		range->size = PIO_INDIRECT_SIZE;
+
+		ret = logic_pio_register_range(range);
+		if (ret) {
+			kfree(range);
+			dev_err(dev, "register IO range failed (%d)!\n", ret);
+			return -ret;
+		}
+		lpcdev->io_host = range;
+
+		/*
+		 * It is time to start the children scanning:
+		 * For ACPI children, the corresponding devices had been
+		 * created during the ACPI enumeration.
+		 * The OF scanning must be performed after initialization of
+		 * 'lpcdev' to avoid some children which complete the scanning
+		 * trigger the MMIO accesses which will probably cause a panic.
+		 */
+		ret = of_platform_populate(dev->of_node, NULL, NULL, dev);
+		if (ret) {
+			/*
+			 * When LPC probing is not completely successful, set
+			 * 'devpara' as NULL. This will make all the LPC I/O
+			 * return failure directly without any hardware
+			 * operations. It will prevent some peripherals which
+			 * had not finished the initialization to manipulate
+			 * I/O for safety.
+			 */
+			lpcdev->io_host->devpara = NULL;
+			dev_err(dev, "populate children failed(%d)\n", ret);
+			return ret;
+		}
+	}
+	lpcdev->io_host->devpara = lpcdev;
+	lpcdev->io_host->ops = &hisi_lpc_ops;
+
+	dev_info(dev, "registered range[%pa - sz:%pa]\n",
+		 &lpcdev->io_host->io_start,
+		 &lpcdev->io_host->size);
+
+	return ret;
+}
+
+static const struct of_device_id hisilpc_of_match[] = {
+	{ .compatible = "hisilicon,hip06-lpc", },
+	{ .compatible = "hisilicon,hip07-lpc", },
+	{},
+};
+
+static struct platform_driver hisilpc_driver = {
+	.driver = {
+		.name           = "hisi_lpc",
+		.of_match_table = hisilpc_of_match,
+	},
+	.probe = hisilpc_probe,
+};
+
+builtin_platform_driver(hisilpc_driver);
-- 
1.9.1

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

* [PATCH v13 7/9] ACPI: Translate the I/O range of non-MMIO devices before scanning
  2018-02-13 17:45 ` John Garry
@ 2018-02-13 17:45   ` John Garry
  -1 siblings, 0 replies; 66+ messages in thread
From: John Garry @ 2018-02-13 17:45 UTC (permalink / raw)
  To: mika.westerberg, rafael, lorenzo.pieralisi, rjw, hanjun.guo,
	robh+dt, bhelgaas, arnd, mark.rutland, olof, dann.frazier,
	andy.shevchenko, robh
  Cc: joe, benh, linux-pci, linux-kernel, linux-acpi, linuxarm,
	minyard, devicetree, linux-arch, rdunlap

On some platforms (such as arm64-based hip06/hip07), access to legacy
ISA/LPC devices through access IO space is required, similar to x86
platforms. As the I/O for these devices are not memory mapped like
PCI/PCIE MMIO host bridges, they require special low-level device
operations through some host to generate IO accesses, i.e. a non-
transparent bridge.

Through the logical PIO framework, hosts are able to register address
ranges in the logical PIO space for IO accesses. For hosts which require
a LLDD to generate the IO accesses, through the logical PIO framework
the host also registers accessors as a backend to generate the physical
bus transactions for IO space accesses (called indirect IO).

When describing the indirect IO child device in APCI tables, the IO
resource is the host-specific address for the child (generally a
bus address).
An example is as follows:
  Device (LPC0) {
    Name (_HID, "HISI0191")  // HiSi LPC
    Name (_CRS, ResourceTemplate () {
      Memory32Fixed (ReadWrite, 0xa01b0000, 0x1000)
    })
  }

  Device (LPC0.IPMI) {
    Name (_HID, "IPI0001")
    Name (LORS, ResourceTemplate() {
      QWordIO (
        ResourceConsumer,
	MinNotFixed,     // _MIF
	MaxNotFixed,     // _MAF
	PosDecode,
	EntireRange,
	0x0,             // _GRA
	0xe4,            // _MIN
	0x3fff,          // _MAX
	0x0,             // _TRA
	0x04,            // _LEN
	, ,
	BTIO
      )
    })

Since the IO resource for the child is a host-specific address,
special translation are required to retrieve the logical PIO address
for that child.

To overcome the problem of associating this logical PIO address
with the child device, a scan handler is added to scan the ACPI
namespace for known indirect IO hosts. This scan handler creates an
MFD per child with the translated logical PIO address as it's IO
resource, as a substitute for the normal platform device which ACPI
would create during device enumeration.

Signed-off-by: John Garry <john.garry@huawei.com>
Signed-off-by: Zhichang Yuan <yuanzhichang@hisilicon.com>
Signed-off-by: Gabriele Paoloni <gabriele.paoloni@huawei.com>
---
 drivers/acpi/arm64/Makefile          |   1 +
 drivers/acpi/arm64/acpi_indirectio.c | 250 +++++++++++++++++++++++++++++++++++
 drivers/acpi/internal.h              |   5 +
 drivers/acpi/scan.c                  |   1 +
 4 files changed, 257 insertions(+)
 create mode 100644 drivers/acpi/arm64/acpi_indirectio.c

diff --git a/drivers/acpi/arm64/Makefile b/drivers/acpi/arm64/Makefile
index 1017def..f4a7f46 100644
--- a/drivers/acpi/arm64/Makefile
+++ b/drivers/acpi/arm64/Makefile
@@ -1,2 +1,3 @@
 obj-$(CONFIG_ACPI_IORT) 	+= iort.o
 obj-$(CONFIG_ACPI_GTDT) 	+= gtdt.o
+obj-$(CONFIG_INDIRECT_PIO)	+= acpi_indirectio.o
diff --git a/drivers/acpi/arm64/acpi_indirectio.c b/drivers/acpi/arm64/acpi_indirectio.c
new file mode 100644
index 0000000..51a1b92
--- /dev/null
+++ b/drivers/acpi/arm64/acpi_indirectio.c
@@ -0,0 +1,250 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2017 HiSilicon Limited, All Rights Reserved.
+ * Author: Gabriele Paoloni <gabriele.paoloni@huawei.com>
+ * Author: Zhichang Yuan <yuanzhichang@hisilicon.com>
+ * Author: John Garry <john.garry@huawei.com>
+ *
+ * This file implements functunality to scan the ACPI namespace and config
+ * devices under "indirect IO" hosts. An "indirect IO" host allows child
+ * devices to use logical IO accesses when the host, itself, does not provide
+ * a transparent bridge. The device setup creates a per-child MFD with a
+ * logical port IO resource.
+ */
+
+#include <linux/acpi.h>
+#include <linux/logic_pio.h>
+#include <linux/mfd/core.h>
+#include <linux/platform_device.h>
+
+ACPI_MODULE_NAME("indirect IO");
+
+#define ACPI_INDIRECT_IO_NAME_LEN 255
+
+struct acpi_indirect_io_mfd_cell {
+	struct mfd_cell_acpi_match acpi_match;
+	char name[ACPI_INDIRECT_IO_NAME_LEN];
+	char pnpid[ACPI_INDIRECT_IO_NAME_LEN];
+};
+
+static int acpi_indirect_io_xlat_res(struct acpi_device *adev,
+				     struct acpi_device *host,
+				     struct resource *res)
+{
+	unsigned long sys_port;
+	resource_size_t len = res->end - res->start;
+
+	sys_port = logic_pio_trans_hwaddr(&host->fwnode, res->start, len);
+	if (sys_port == -1UL)
+		return -EFAULT;
+
+	res->start = sys_port;
+	res->end = sys_port + len;
+
+	return 0;
+}
+
+/*
+ * acpi_indirect_io_set_res - set the resources for a child device
+ * (MFD) of an "indirect IO" host.
+ * @child: the device node to be updated the I/O resource
+ * @hostdev: the device node associated with the "indirect IO" host
+ * @res: double pointer to be set to the address of translated resources
+ * @num_res: pointer to variable to hold the number of translated resources
+ *
+ * Returns 0 when successful, and a negative value for failure.
+ *
+ * For a given "indirect IO" host, each child device will have associated
+ * host-relevative address resource. This function will return the translated
+ * logical PIO addresses for each child devices resources.
+ */
+static int acpi_indirect_io_set_res(struct device *child,
+				    struct device *hostdev,
+				    const struct resource **res,
+				    int *num_res)
+{
+	struct acpi_device *adev;
+	struct acpi_device *host;
+	struct resource_entry *rentry;
+	LIST_HEAD(resource_list);
+	struct resource *resources;
+	int count;
+	int i;
+	int ret = -EIO;
+
+	if (!child || !hostdev)
+		return -EINVAL;
+
+	host = to_acpi_device(hostdev);
+	adev = to_acpi_device(child);
+
+	/* check the device state */
+	if (!adev->status.present) {
+		dev_info(child, "device is not present\n");
+		return 0;
+	}
+	/* whether the child had been enumerated? */
+	if (acpi_device_enumerated(adev)) {
+		dev_info(child, "had been enumerated\n");
+		return 0;
+	}
+
+	count = acpi_dev_get_resources(adev, &resource_list, NULL, NULL);
+	if (count <= 0) {
+		dev_err(child, "failed to get resources\n");
+		return count ? count : -EIO;
+	}
+
+	resources = kcalloc(count, sizeof(*resources), GFP_KERNEL);
+	if (!resources) {
+		acpi_dev_free_resource_list(&resource_list);
+		return -ENOMEM;
+	}
+	count = 0;
+	list_for_each_entry(rentry, &resource_list, node)
+		resources[count++] = *rentry->res;
+
+	acpi_dev_free_resource_list(&resource_list);
+
+	/* translate the I/O resources */
+	for (i = 0; i < count; i++) {
+		if (!(resources[i].flags & IORESOURCE_IO))
+			continue;
+		ret = acpi_indirect_io_xlat_res(adev, host, &resources[i]);
+		if (ret) {
+			kfree(resources);
+			dev_err(child, "translate IO range failed(%d)\n", ret);
+			return ret;
+		}
+	}
+	*res = resources;
+	*num_res = count;
+
+	return ret;
+}
+
+/*
+ * acpi_indirect_io_setup - scan handler for "indirect IO" host.
+ * @adev: "indirect IO" host ACPI device pointer
+ * Returns 0 when successful, and a negative value for failure.
+ *
+ * Setup an "indirect IO" host by scanning all child devices, and
+ * create a per-device MFD with logical PIO translated IO resources.
+ */
+static int acpi_indirect_io_setup(struct acpi_device *adev)
+{
+	struct platform_device *pdev;
+	struct mfd_cell *mfd_cells;
+	struct logic_pio_hwaddr *range;
+	struct acpi_device *child;
+	struct acpi_indirect_io_mfd_cell *acpi_indirect_io_mfd_cells;
+	int size, ret, count = 0, cell_num = 0;
+
+	range = kzalloc(sizeof(*range), GFP_KERNEL);
+	if (!range)
+		return -ENOMEM;
+	range->fwnode = &adev->fwnode;
+	range->flags = PIO_INDIRECT;
+	range->size = PIO_INDIRECT_SIZE;
+
+	ret = logic_pio_register_range(range);
+	if (ret)
+		goto free_range;
+
+	list_for_each_entry(child, &adev->children, node)
+		cell_num++;
+
+	/* allocate the mfd cell and companion acpi info, one per child */
+	size = sizeof(*mfd_cells) + sizeof(*acpi_indirect_io_mfd_cells);
+	mfd_cells = kcalloc(cell_num, size, GFP_KERNEL);
+	if (!mfd_cells) {
+		ret = -ENOMEM;
+		goto free_range;
+	}
+
+	acpi_indirect_io_mfd_cells = (struct acpi_indirect_io_mfd_cell *)
+					&mfd_cells[cell_num];
+	/* Only consider the children of the host */
+	list_for_each_entry(child, &adev->children, node) {
+		struct mfd_cell *mfd_cell = &mfd_cells[count];
+		struct acpi_indirect_io_mfd_cell *acpi_indirect_io_mfd_cell =
+					&acpi_indirect_io_mfd_cells[count];
+		const struct mfd_cell_acpi_match *acpi_match =
+					&acpi_indirect_io_mfd_cell->acpi_match;
+		char *name = &acpi_indirect_io_mfd_cell[count].name[0];
+		char *pnpid = &acpi_indirect_io_mfd_cell[count].pnpid[0];
+		struct mfd_cell_acpi_match match = {
+			.pnpid = pnpid,
+		};
+
+		snprintf(name, ACPI_INDIRECT_IO_NAME_LEN, "indirect-io-%s",
+			 acpi_device_hid(child));
+		snprintf(pnpid, ACPI_INDIRECT_IO_NAME_LEN, "%s",
+			 acpi_device_hid(child));
+
+		memcpy((void *)acpi_match, (void *)&match, sizeof(*acpi_match));
+		mfd_cell->name = name;
+		mfd_cell->acpi_match = acpi_match;
+
+		ret = acpi_indirect_io_set_res(&child->dev, &adev->dev,
+					       &mfd_cell->resources,
+					       &mfd_cell->num_resources);
+		if (ret) {
+			dev_err(&child->dev, "set resource failed (%d)\n", ret);
+			goto free_mfd_resources;
+		}
+		count++;
+	}
+
+	pdev = acpi_create_platform_device(adev, NULL);
+	if (IS_ERR_OR_NULL(pdev)) {
+		dev_err(&adev->dev, "create platform device for host failed\n");
+		ret = PTR_ERR(pdev);
+		goto free_mfd_resources;
+	}
+	acpi_device_set_enumerated(adev);
+
+	ret = mfd_add_devices(&pdev->dev, PLATFORM_DEVID_NONE,
+			      mfd_cells, cell_num, NULL, 0, NULL);
+	if (ret) {
+		dev_err(&pdev->dev, "failed to add mfd cells (%d)\n", ret);
+		goto free_mfd_resources;
+	}
+
+	return ret;
+
+free_mfd_resources:
+	while (cell_num--)
+		kfree(mfd_cells[cell_num].resources);
+	kfree(mfd_cells);
+free_range:
+	kfree(range);
+
+	return ret;
+}
+
+/* All the host devices which apply indirect-IO can be listed here. */
+static const struct acpi_device_id acpi_indirect_io_host_id[] = {
+	{}
+};
+
+static int acpi_indirect_io_attach(struct acpi_device *adev,
+				   const struct acpi_device_id *id)
+{
+	int ret = acpi_indirect_io_setup(adev);
+
+	if (ret < 0)
+		return ret;
+
+	return 1;
+}
+
+static struct acpi_scan_handler acpi_indirect_io_handler = {
+	.ids = acpi_indirect_io_host_id,
+	.attach = acpi_indirect_io_attach,
+};
+
+void __init acpi_indirect_io_scan_init(void)
+{
+	acpi_scan_add_handler(&acpi_indirect_io_handler);
+}
diff --git a/drivers/acpi/internal.h b/drivers/acpi/internal.h
index 1d0a501..680f3cf 100644
--- a/drivers/acpi/internal.h
+++ b/drivers/acpi/internal.h
@@ -31,6 +31,11 @@
 void acpi_platform_init(void);
 void acpi_pnp_init(void);
 void acpi_int340x_thermal_init(void);
+#ifdef CONFIG_INDIRECT_PIO
+void acpi_indirect_io_scan_init(void);
+#else
+static inline void acpi_indirect_io_scan_init(void) {}
+#endif
 #ifdef CONFIG_ARM_AMBA
 void acpi_amba_init(void);
 #else
diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c
index 8e63d93..204da8a 100644
--- a/drivers/acpi/scan.c
+++ b/drivers/acpi/scan.c
@@ -2155,6 +2155,7 @@ int __init acpi_scan_init(void)
 	acpi_amba_init();
 	acpi_watchdog_init();
 	acpi_init_lpit();
+	acpi_indirect_io_scan_init();
 
 	acpi_scan_add_handler(&generic_device_handler);
 
-- 
1.9.1

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

* [PATCH v13 7/9] ACPI: Translate the I/O range of non-MMIO devices before scanning
@ 2018-02-13 17:45   ` John Garry
  0 siblings, 0 replies; 66+ messages in thread
From: John Garry @ 2018-02-13 17:45 UTC (permalink / raw)
  To: mika.westerberg, rafael, lorenzo.pieralisi, rjw, hanjun.guo,
	robh+dt, bhelgaas, arnd, mark.rutland, olof, dann.frazier,
	andy.shevchenko, robh
  Cc: joe, benh, linux-pci, linux-kernel, linux-acpi, linuxarm,
	minyard, devicetree, linux-arch, rdunlap

On some platforms (such as arm64-based hip06/hip07), access to legacy
ISA/LPC devices through access IO space is required, similar to x86
platforms. As the I/O for these devices are not memory mapped like
PCI/PCIE MMIO host bridges, they require special low-level device
operations through some host to generate IO accesses, i.e. a non-
transparent bridge.

Through the logical PIO framework, hosts are able to register address
ranges in the logical PIO space for IO accesses. For hosts which require
a LLDD to generate the IO accesses, through the logical PIO framework
the host also registers accessors as a backend to generate the physical
bus transactions for IO space accesses (called indirect IO).

When describing the indirect IO child device in APCI tables, the IO
resource is the host-specific address for the child (generally a
bus address).
An example is as follows:
  Device (LPC0) {
    Name (_HID, "HISI0191")  // HiSi LPC
    Name (_CRS, ResourceTemplate () {
      Memory32Fixed (ReadWrite, 0xa01b0000, 0x1000)
    })
  }

  Device (LPC0.IPMI) {
    Name (_HID, "IPI0001")
    Name (LORS, ResourceTemplate() {
      QWordIO (
        ResourceConsumer,
	MinNotFixed,     // _MIF
	MaxNotFixed,     // _MAF
	PosDecode,
	EntireRange,
	0x0,             // _GRA
	0xe4,            // _MIN
	0x3fff,          // _MAX
	0x0,             // _TRA
	0x04,            // _LEN
	, ,
	BTIO
      )
    })

Since the IO resource for the child is a host-specific address,
special translation are required to retrieve the logical PIO address
for that child.

To overcome the problem of associating this logical PIO address
with the child device, a scan handler is added to scan the ACPI
namespace for known indirect IO hosts. This scan handler creates an
MFD per child with the translated logical PIO address as it's IO
resource, as a substitute for the normal platform device which ACPI
would create during device enumeration.

Signed-off-by: John Garry <john.garry@huawei.com>
Signed-off-by: Zhichang Yuan <yuanzhichang@hisilicon.com>
Signed-off-by: Gabriele Paoloni <gabriele.paoloni@huawei.com>
---
 drivers/acpi/arm64/Makefile          |   1 +
 drivers/acpi/arm64/acpi_indirectio.c | 250 +++++++++++++++++++++++++++++++++++
 drivers/acpi/internal.h              |   5 +
 drivers/acpi/scan.c                  |   1 +
 4 files changed, 257 insertions(+)
 create mode 100644 drivers/acpi/arm64/acpi_indirectio.c

diff --git a/drivers/acpi/arm64/Makefile b/drivers/acpi/arm64/Makefile
index 1017def..f4a7f46 100644
--- a/drivers/acpi/arm64/Makefile
+++ b/drivers/acpi/arm64/Makefile
@@ -1,2 +1,3 @@
 obj-$(CONFIG_ACPI_IORT) 	+= iort.o
 obj-$(CONFIG_ACPI_GTDT) 	+= gtdt.o
+obj-$(CONFIG_INDIRECT_PIO)	+= acpi_indirectio.o
diff --git a/drivers/acpi/arm64/acpi_indirectio.c b/drivers/acpi/arm64/acpi_indirectio.c
new file mode 100644
index 0000000..51a1b92
--- /dev/null
+++ b/drivers/acpi/arm64/acpi_indirectio.c
@@ -0,0 +1,250 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2017 HiSilicon Limited, All Rights Reserved.
+ * Author: Gabriele Paoloni <gabriele.paoloni@huawei.com>
+ * Author: Zhichang Yuan <yuanzhichang@hisilicon.com>
+ * Author: John Garry <john.garry@huawei.com>
+ *
+ * This file implements functunality to scan the ACPI namespace and config
+ * devices under "indirect IO" hosts. An "indirect IO" host allows child
+ * devices to use logical IO accesses when the host, itself, does not provide
+ * a transparent bridge. The device setup creates a per-child MFD with a
+ * logical port IO resource.
+ */
+
+#include <linux/acpi.h>
+#include <linux/logic_pio.h>
+#include <linux/mfd/core.h>
+#include <linux/platform_device.h>
+
+ACPI_MODULE_NAME("indirect IO");
+
+#define ACPI_INDIRECT_IO_NAME_LEN 255
+
+struct acpi_indirect_io_mfd_cell {
+	struct mfd_cell_acpi_match acpi_match;
+	char name[ACPI_INDIRECT_IO_NAME_LEN];
+	char pnpid[ACPI_INDIRECT_IO_NAME_LEN];
+};
+
+static int acpi_indirect_io_xlat_res(struct acpi_device *adev,
+				     struct acpi_device *host,
+				     struct resource *res)
+{
+	unsigned long sys_port;
+	resource_size_t len = res->end - res->start;
+
+	sys_port = logic_pio_trans_hwaddr(&host->fwnode, res->start, len);
+	if (sys_port == -1UL)
+		return -EFAULT;
+
+	res->start = sys_port;
+	res->end = sys_port + len;
+
+	return 0;
+}
+
+/*
+ * acpi_indirect_io_set_res - set the resources for a child device
+ * (MFD) of an "indirect IO" host.
+ * @child: the device node to be updated the I/O resource
+ * @hostdev: the device node associated with the "indirect IO" host
+ * @res: double pointer to be set to the address of translated resources
+ * @num_res: pointer to variable to hold the number of translated resources
+ *
+ * Returns 0 when successful, and a negative value for failure.
+ *
+ * For a given "indirect IO" host, each child device will have associated
+ * host-relevative address resource. This function will return the translated
+ * logical PIO addresses for each child devices resources.
+ */
+static int acpi_indirect_io_set_res(struct device *child,
+				    struct device *hostdev,
+				    const struct resource **res,
+				    int *num_res)
+{
+	struct acpi_device *adev;
+	struct acpi_device *host;
+	struct resource_entry *rentry;
+	LIST_HEAD(resource_list);
+	struct resource *resources;
+	int count;
+	int i;
+	int ret = -EIO;
+
+	if (!child || !hostdev)
+		return -EINVAL;
+
+	host = to_acpi_device(hostdev);
+	adev = to_acpi_device(child);
+
+	/* check the device state */
+	if (!adev->status.present) {
+		dev_info(child, "device is not present\n");
+		return 0;
+	}
+	/* whether the child had been enumerated? */
+	if (acpi_device_enumerated(adev)) {
+		dev_info(child, "had been enumerated\n");
+		return 0;
+	}
+
+	count = acpi_dev_get_resources(adev, &resource_list, NULL, NULL);
+	if (count <= 0) {
+		dev_err(child, "failed to get resources\n");
+		return count ? count : -EIO;
+	}
+
+	resources = kcalloc(count, sizeof(*resources), GFP_KERNEL);
+	if (!resources) {
+		acpi_dev_free_resource_list(&resource_list);
+		return -ENOMEM;
+	}
+	count = 0;
+	list_for_each_entry(rentry, &resource_list, node)
+		resources[count++] = *rentry->res;
+
+	acpi_dev_free_resource_list(&resource_list);
+
+	/* translate the I/O resources */
+	for (i = 0; i < count; i++) {
+		if (!(resources[i].flags & IORESOURCE_IO))
+			continue;
+		ret = acpi_indirect_io_xlat_res(adev, host, &resources[i]);
+		if (ret) {
+			kfree(resources);
+			dev_err(child, "translate IO range failed(%d)\n", ret);
+			return ret;
+		}
+	}
+	*res = resources;
+	*num_res = count;
+
+	return ret;
+}
+
+/*
+ * acpi_indirect_io_setup - scan handler for "indirect IO" host.
+ * @adev: "indirect IO" host ACPI device pointer
+ * Returns 0 when successful, and a negative value for failure.
+ *
+ * Setup an "indirect IO" host by scanning all child devices, and
+ * create a per-device MFD with logical PIO translated IO resources.
+ */
+static int acpi_indirect_io_setup(struct acpi_device *adev)
+{
+	struct platform_device *pdev;
+	struct mfd_cell *mfd_cells;
+	struct logic_pio_hwaddr *range;
+	struct acpi_device *child;
+	struct acpi_indirect_io_mfd_cell *acpi_indirect_io_mfd_cells;
+	int size, ret, count = 0, cell_num = 0;
+
+	range = kzalloc(sizeof(*range), GFP_KERNEL);
+	if (!range)
+		return -ENOMEM;
+	range->fwnode = &adev->fwnode;
+	range->flags = PIO_INDIRECT;
+	range->size = PIO_INDIRECT_SIZE;
+
+	ret = logic_pio_register_range(range);
+	if (ret)
+		goto free_range;
+
+	list_for_each_entry(child, &adev->children, node)
+		cell_num++;
+
+	/* allocate the mfd cell and companion acpi info, one per child */
+	size = sizeof(*mfd_cells) + sizeof(*acpi_indirect_io_mfd_cells);
+	mfd_cells = kcalloc(cell_num, size, GFP_KERNEL);
+	if (!mfd_cells) {
+		ret = -ENOMEM;
+		goto free_range;
+	}
+
+	acpi_indirect_io_mfd_cells = (struct acpi_indirect_io_mfd_cell *)
+					&mfd_cells[cell_num];
+	/* Only consider the children of the host */
+	list_for_each_entry(child, &adev->children, node) {
+		struct mfd_cell *mfd_cell = &mfd_cells[count];
+		struct acpi_indirect_io_mfd_cell *acpi_indirect_io_mfd_cell =
+					&acpi_indirect_io_mfd_cells[count];
+		const struct mfd_cell_acpi_match *acpi_match =
+					&acpi_indirect_io_mfd_cell->acpi_match;
+		char *name = &acpi_indirect_io_mfd_cell[count].name[0];
+		char *pnpid = &acpi_indirect_io_mfd_cell[count].pnpid[0];
+		struct mfd_cell_acpi_match match = {
+			.pnpid = pnpid,
+		};
+
+		snprintf(name, ACPI_INDIRECT_IO_NAME_LEN, "indirect-io-%s",
+			 acpi_device_hid(child));
+		snprintf(pnpid, ACPI_INDIRECT_IO_NAME_LEN, "%s",
+			 acpi_device_hid(child));
+
+		memcpy((void *)acpi_match, (void *)&match, sizeof(*acpi_match));
+		mfd_cell->name = name;
+		mfd_cell->acpi_match = acpi_match;
+
+		ret = acpi_indirect_io_set_res(&child->dev, &adev->dev,
+					       &mfd_cell->resources,
+					       &mfd_cell->num_resources);
+		if (ret) {
+			dev_err(&child->dev, "set resource failed (%d)\n", ret);
+			goto free_mfd_resources;
+		}
+		count++;
+	}
+
+	pdev = acpi_create_platform_device(adev, NULL);
+	if (IS_ERR_OR_NULL(pdev)) {
+		dev_err(&adev->dev, "create platform device for host failed\n");
+		ret = PTR_ERR(pdev);
+		goto free_mfd_resources;
+	}
+	acpi_device_set_enumerated(adev);
+
+	ret = mfd_add_devices(&pdev->dev, PLATFORM_DEVID_NONE,
+			      mfd_cells, cell_num, NULL, 0, NULL);
+	if (ret) {
+		dev_err(&pdev->dev, "failed to add mfd cells (%d)\n", ret);
+		goto free_mfd_resources;
+	}
+
+	return ret;
+
+free_mfd_resources:
+	while (cell_num--)
+		kfree(mfd_cells[cell_num].resources);
+	kfree(mfd_cells);
+free_range:
+	kfree(range);
+
+	return ret;
+}
+
+/* All the host devices which apply indirect-IO can be listed here. */
+static const struct acpi_device_id acpi_indirect_io_host_id[] = {
+	{}
+};
+
+static int acpi_indirect_io_attach(struct acpi_device *adev,
+				   const struct acpi_device_id *id)
+{
+	int ret = acpi_indirect_io_setup(adev);
+
+	if (ret < 0)
+		return ret;
+
+	return 1;
+}
+
+static struct acpi_scan_handler acpi_indirect_io_handler = {
+	.ids = acpi_indirect_io_host_id,
+	.attach = acpi_indirect_io_attach,
+};
+
+void __init acpi_indirect_io_scan_init(void)
+{
+	acpi_scan_add_handler(&acpi_indirect_io_handler);
+}
diff --git a/drivers/acpi/internal.h b/drivers/acpi/internal.h
index 1d0a501..680f3cf 100644
--- a/drivers/acpi/internal.h
+++ b/drivers/acpi/internal.h
@@ -31,6 +31,11 @@
 void acpi_platform_init(void);
 void acpi_pnp_init(void);
 void acpi_int340x_thermal_init(void);
+#ifdef CONFIG_INDIRECT_PIO
+void acpi_indirect_io_scan_init(void);
+#else
+static inline void acpi_indirect_io_scan_init(void) {}
+#endif
 #ifdef CONFIG_ARM_AMBA
 void acpi_amba_init(void);
 #else
diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c
index 8e63d93..204da8a 100644
--- a/drivers/acpi/scan.c
+++ b/drivers/acpi/scan.c
@@ -2155,6 +2155,7 @@ int __init acpi_scan_init(void)
 	acpi_amba_init();
 	acpi_watchdog_init();
 	acpi_init_lpit();
+	acpi_indirect_io_scan_init();
 
 	acpi_scan_add_handler(&generic_device_handler);
 
-- 
1.9.1

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

* [PATCH v13 8/9] LPC, ACPI: Add the HISI LPC ACPI support
  2018-02-13 17:45 ` John Garry
@ 2018-02-13 17:45   ` John Garry
  -1 siblings, 0 replies; 66+ messages in thread
From: John Garry @ 2018-02-13 17:45 UTC (permalink / raw)
  To: mika.westerberg, rafael, lorenzo.pieralisi, rjw, hanjun.guo,
	robh+dt, bhelgaas, arnd, mark.rutland, olof, dann.frazier,
	andy.shevchenko, robh
  Cc: joe, benh, linux-pci, linux-kernel, linux-acpi, linuxarm,
	minyard, devicetree, linux-arch, rdunlap

Based on the previous patches, this patch supports the LPC host on
Hip06/Hip07 for ACPI FW.

Signed-off-by: John Garry <john.garry@huawei.com>
Signed-off-by: Zhichang Yuan <yuanzhichang@hisilicon.com>
Signed-off-by: Gabriele Paoloni <gabriele.paoloni@huawei.com>
Tested-by: Dann Frazier <dann.frazier@canonical.com>
---
 drivers/acpi/arm64/acpi_indirectio.c |  1 +
 drivers/bus/hisi_lpc.c               | 17 ++++++++++++++++-
 2 files changed, 17 insertions(+), 1 deletion(-)

diff --git a/drivers/acpi/arm64/acpi_indirectio.c b/drivers/acpi/arm64/acpi_indirectio.c
index 51a1b92..52519a21 100644
--- a/drivers/acpi/arm64/acpi_indirectio.c
+++ b/drivers/acpi/arm64/acpi_indirectio.c
@@ -225,6 +225,7 @@ static int acpi_indirect_io_setup(struct acpi_device *adev)
 
 /* All the host devices which apply indirect-IO can be listed here. */
 static const struct acpi_device_id acpi_indirect_io_host_id[] = {
+	{"HISI0191"},
 	{}
 };
 
diff --git a/drivers/bus/hisi_lpc.c b/drivers/bus/hisi_lpc.c
index 2290cf2..6211c95 100644
--- a/drivers/bus/hisi_lpc.c
+++ b/drivers/bus/hisi_lpc.c
@@ -439,7 +439,13 @@ static int hisilpc_probe(struct platform_device *pdev)
 	}
 
 	/* register the LPC host PIO resources */
-	{
+	if (has_acpi_companion(dev)) {
+		lpcdev->io_host = find_io_range_by_fwnode(dev->fwnode);
+		if (!lpcdev->io_host) {
+			dev_err(dev, "range not registered!\n");
+			return -EFAULT;
+		}
+	} else {
 		struct logic_pio_hwaddr *range;
 
 		range = devm_kzalloc(dev, sizeof(*range), GFP_KERNEL);
@@ -496,10 +502,19 @@ static int hisilpc_probe(struct platform_device *pdev)
 	{},
 };
 
+#ifdef CONFIG_ACPI
+static const struct acpi_device_id hisilpc_acpi_match[] = {
+	{"HISI0191", },
+	{},
+};
+
+#endif
+
 static struct platform_driver hisilpc_driver = {
 	.driver = {
 		.name           = "hisi_lpc",
 		.of_match_table = hisilpc_of_match,
+		.acpi_match_table = ACPI_PTR(hisilpc_acpi_match),
 	},
 	.probe = hisilpc_probe,
 };
-- 
1.9.1

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

* [PATCH v13 8/9] LPC, ACPI: Add the HISI LPC ACPI support
@ 2018-02-13 17:45   ` John Garry
  0 siblings, 0 replies; 66+ messages in thread
From: John Garry @ 2018-02-13 17:45 UTC (permalink / raw)
  To: mika.westerberg, rafael, lorenzo.pieralisi, rjw, hanjun.guo,
	robh+dt, bhelgaas, arnd, mark.rutland, olof, dann.frazier,
	andy.shevchenko, robh
  Cc: joe, benh, linux-pci, linux-kernel, linux-acpi, linuxarm,
	minyard, devicetree, linux-arch, rdunlap

Based on the previous patches, this patch supports the LPC host on
Hip06/Hip07 for ACPI FW.

Signed-off-by: John Garry <john.garry@huawei.com>
Signed-off-by: Zhichang Yuan <yuanzhichang@hisilicon.com>
Signed-off-by: Gabriele Paoloni <gabriele.paoloni@huawei.com>
Tested-by: Dann Frazier <dann.frazier@canonical.com>
---
 drivers/acpi/arm64/acpi_indirectio.c |  1 +
 drivers/bus/hisi_lpc.c               | 17 ++++++++++++++++-
 2 files changed, 17 insertions(+), 1 deletion(-)

diff --git a/drivers/acpi/arm64/acpi_indirectio.c b/drivers/acpi/arm64/acpi_indirectio.c
index 51a1b92..52519a21 100644
--- a/drivers/acpi/arm64/acpi_indirectio.c
+++ b/drivers/acpi/arm64/acpi_indirectio.c
@@ -225,6 +225,7 @@ static int acpi_indirect_io_setup(struct acpi_device *adev)
 
 /* All the host devices which apply indirect-IO can be listed here. */
 static const struct acpi_device_id acpi_indirect_io_host_id[] = {
+	{"HISI0191"},
 	{}
 };
 
diff --git a/drivers/bus/hisi_lpc.c b/drivers/bus/hisi_lpc.c
index 2290cf2..6211c95 100644
--- a/drivers/bus/hisi_lpc.c
+++ b/drivers/bus/hisi_lpc.c
@@ -439,7 +439,13 @@ static int hisilpc_probe(struct platform_device *pdev)
 	}
 
 	/* register the LPC host PIO resources */
-	{
+	if (has_acpi_companion(dev)) {
+		lpcdev->io_host = find_io_range_by_fwnode(dev->fwnode);
+		if (!lpcdev->io_host) {
+			dev_err(dev, "range not registered!\n");
+			return -EFAULT;
+		}
+	} else {
 		struct logic_pio_hwaddr *range;
 
 		range = devm_kzalloc(dev, sizeof(*range), GFP_KERNEL);
@@ -496,10 +502,19 @@ static int hisilpc_probe(struct platform_device *pdev)
 	{},
 };
 
+#ifdef CONFIG_ACPI
+static const struct acpi_device_id hisilpc_acpi_match[] = {
+	{"HISI0191", },
+	{},
+};
+
+#endif
+
 static struct platform_driver hisilpc_driver = {
 	.driver = {
 		.name           = "hisi_lpc",
 		.of_match_table = hisilpc_of_match,
+		.acpi_match_table = ACPI_PTR(hisilpc_acpi_match),
 	},
 	.probe = hisilpc_probe,
 };
-- 
1.9.1

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

* [PATCH v13 9/9] MAINTAINERS: Add maintainer for HiSilicon LPC driver
  2018-02-13 17:45 ` John Garry
@ 2018-02-13 17:45   ` John Garry
  -1 siblings, 0 replies; 66+ messages in thread
From: John Garry @ 2018-02-13 17:45 UTC (permalink / raw)
  To: mika.westerberg, rafael, lorenzo.pieralisi, rjw, hanjun.guo,
	robh+dt, bhelgaas, arnd, mark.rutland, olof, dann.frazier,
	andy.shevchenko, robh
  Cc: joe, benh, linux-pci, linux-kernel, linux-acpi, linuxarm,
	minyard, devicetree, linux-arch, rdunlap

Added maintainer for drivers/bus/hisi_lpc.c

Signed-off-by: John Garry <john.garry@huawei.com>
---
 MAINTAINERS | 7 +++++++
 1 file changed, 7 insertions(+)

diff --git a/MAINTAINERS b/MAINTAINERS
index fc538a9..9a2fe10 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -6386,6 +6386,13 @@ W:	http://www.hisilicon.com
 S:	Maintained
 F:	drivers/net/ethernet/hisilicon/hns3/
 
+HISILICON LPC BUS DRIVER
+M:	john.garry@huawei.com
+W:	http://www.hisilicon.com
+S:	Maintained
+F:	drivers/bus/hisi_lpc.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>
-- 
1.9.1

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

* [PATCH v13 9/9] MAINTAINERS: Add maintainer for HiSilicon LPC driver
@ 2018-02-13 17:45   ` John Garry
  0 siblings, 0 replies; 66+ messages in thread
From: John Garry @ 2018-02-13 17:45 UTC (permalink / raw)
  To: mika.westerberg, rafael, lorenzo.pieralisi, rjw, hanjun.guo,
	robh+dt, bhelgaas, arnd, mark.rutland, olof, dann.frazier,
	andy.shevchenko, robh
  Cc: joe, benh, linux-pci, linux-kernel, linux-acpi, linuxarm,
	minyard, devicetree, linux-arch, rdunlap

Added maintainer for drivers/bus/hisi_lpc.c

Signed-off-by: John Garry <john.garry@huawei.com>
---
 MAINTAINERS | 7 +++++++
 1 file changed, 7 insertions(+)

diff --git a/MAINTAINERS b/MAINTAINERS
index fc538a9..9a2fe10 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -6386,6 +6386,13 @@ W:	http://www.hisilicon.com
 S:	Maintained
 F:	drivers/net/ethernet/hisilicon/hns3/
 
+HISILICON LPC BUS DRIVER
+M:	john.garry@huawei.com
+W:	http://www.hisilicon.com
+S:	Maintained
+F:	drivers/bus/hisi_lpc.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>
-- 
1.9.1

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

* Re: [PATCH v13 4/9] PCI: Apply the new generic I/O management on PCI IO hosts
  2018-02-13 17:45   ` John Garry
  (?)
@ 2018-02-13 22:57   ` dann frazier
  2018-02-14 15:42       ` John Garry
  -1 siblings, 1 reply; 66+ messages in thread
From: dann frazier @ 2018-02-13 22:57 UTC (permalink / raw)
  To: John Garry
  Cc: mika.westerberg, rafael, lorenzo.pieralisi, rjw, hanjun.guo,
	robh+dt, bhelgaas, arnd, mark.rutland, olof, andy.shevchenko,
	robh, joe, benh, linux-pci, linux-kernel, linux-acpi, linuxarm,
	minyard, devicetree, linux-arch, rdunlap

On Wed, Feb 14, 2018 at 01:45:28AM +0800, John Garry wrote:
> From: Zhichang Yuan <yuanzhichang@hisilicon.com>
> 
> After introducing the new generic I/O space management in logic pio, the
> original PCI MMIO relevant helpers need to be updated based on the new
> interfaces.
> This patch adapts the corresponding code to match the changes introduced
> by logic pio.
> 
> 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>        #earlier draft
> Acked-by: Bjorn Helgaas <bhelgaas@google.com>

I saw that Bjorn Acked this back in v6, but it seems like the code in
pci.c was reworked a bit for v7 onwards and I didn't see a follow-up
review (apologies if I just missed it). In which case, maybe his Ack
should have the "#earlier draft" tag as well?

> ---
>  drivers/pci/pci.c        | 95 +++++++++---------------------------------------
>  include/asm-generic/io.h |  2 +-
>  2 files changed, 18 insertions(+), 79 deletions(-)
> 
> diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c
> index 07290a3..8aa5c54 100644
> --- a/drivers/pci/pci.c
> +++ b/drivers/pci/pci.c
> @@ -22,6 +22,7 @@
>  #include <linux/spinlock.h>
>  #include <linux/string.h>
>  #include <linux/log2.h>
> +#include <linux/logic_pio.h>
>  #include <linux/pci-aspm.h>
>  #include <linux/pm_wakeup.h>
>  #include <linux/interrupt.h>
> @@ -3440,17 +3441,6 @@ int pci_request_regions_exclusive(struct pci_dev *pdev, const char *res_name)
>  }
>  EXPORT_SYMBOL(pci_request_regions_exclusive);
>  
> -#ifdef PCI_IOBASE
> -struct io_range {
> -	struct list_head list;
> -	phys_addr_t start;
> -	resource_size_t size;
> -};
> -
> -static LIST_HEAD(io_range_list);
> -static DEFINE_SPINLOCK(io_range_lock);
> -#endif
> -
>  /*
>   * Record the PCI IO range (expressed as CPU physical address + size).
>   * Return a negative value if an error has occured, zero otherwise
> @@ -3458,51 +3448,28 @@ struct io_range {
>  int pci_register_io_range(struct fwnode_handle *fwnode, phys_addr_t addr,
>  			resource_size_t	size)
>  {
> -	int err = 0;
> -
> +	int ret = 0;
>  #ifdef PCI_IOBASE
> -	struct io_range *range;
> -	resource_size_t allocated_size = 0;
> -
> -	/* 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) {
> -			/* range already registered, bail out */
> -			goto end_register;
> -		}
> -		allocated_size += range->size;
> -	}
> +	struct logic_pio_hwaddr *range;
>  
> -	/* range not registed yet, check for available space */
> -	if (allocated_size + size - 1 > IO_SPACE_LIMIT) {
> -		/* if it's too big check if 64K space can be reserved */
> -		if (allocated_size + SZ_64K - 1 > IO_SPACE_LIMIT) {
> -			err = -E2BIG;
> -			goto end_register;
> -		}
> -
> -		size = SZ_64K;
> -		pr_warn("Requested IO range too big, new size set to 64K\n");
> -	}
> +	if (!size || addr + size < addr)
> +		return -EINVAL;
>  
> -	/* add the range to the list */
>  	range = kzalloc(sizeof(*range), GFP_ATOMIC);
> -	if (!range) {
> -		err = -ENOMEM;
> -		goto end_register;
> -	}
> +	if (!range)
> +		return -ENOMEM;
>  
> -	range->start = addr;
> +	range->fwnode = fwnode;
>  	range->size = size;
> +	range->hw_start = addr;
> +	range->flags = PIO_CPU_MMIO;
>  
> -	list_add_tail(&range->list, &io_range_list);
> -
> -end_register:
> -	spin_unlock(&io_range_lock);
> +	ret = logic_pio_register_range(range);
> +	if (ret)
> +		kfree(range);
>  #endif
>  
> -	return err;
> +	return ret;
>  }
>  
>  phys_addr_t pci_pio_to_address(unsigned long pio)
> @@ -3510,21 +3477,10 @@ phys_addr_t pci_pio_to_address(unsigned long pio)
>  	phys_addr_t address = (phys_addr_t)OF_BAD_ADDR;
>  
>  #ifdef PCI_IOBASE
> -	struct io_range *range;
> -	resource_size_t allocated_size = 0;
> -
> -	if (pio > IO_SPACE_LIMIT)
> +	if (pio >= MMIO_UPPER_LIMIT)
>  		return address;
>  
> -	spin_lock(&io_range_lock);
> -	list_for_each_entry(range, &io_range_list, list) {
> -		if (pio >= allocated_size && pio < allocated_size + range->size) {
> -			address = range->start + pio - allocated_size;
> -			break;
> -		}
> -		allocated_size += range->size;
> -	}
> -	spin_unlock(&io_range_lock);
> +	address = logic_pio_to_hwaddr(pio);
>  #endif
>  
>  	return address;
> @@ -3533,25 +3489,8 @@ phys_addr_t pci_pio_to_address(unsigned long pio)
>  unsigned long __weak pci_address_to_pio(phys_addr_t address)
>  {
>  #ifdef PCI_IOBASE
> -	struct io_range *res;
> -	resource_size_t offset = 0;
> -	unsigned long addr = -1;
> -
> -	spin_lock(&io_range_lock);
> -	list_for_each_entry(res, &io_range_list, list) {
> -		if (address >= res->start && address < res->start + res->size) {
> -			addr = address - res->start + offset;
> -			break;
> -		}
> -		offset += res->size;
> -	}
> -	spin_unlock(&io_range_lock);
> -
> -	return addr;
> +	return logic_pio_trans_cpuaddr(address);
>  #else
> -	if (address > IO_SPACE_LIMIT)
> -		return (unsigned long)-1;
> -

Why is this check now safe to drop in the !PCI_IOBASE case?

  -dann

>  	return (unsigned long) address;
>  #endif
>  }
> diff --git a/include/asm-generic/io.h b/include/asm-generic/io.h
> index b7996a79..5a59931 100644
> --- a/include/asm-generic/io.h
> +++ b/include/asm-generic/io.h
> @@ -901,7 +901,7 @@ static inline void iounmap(void __iomem *addr)
>  #define ioport_map ioport_map
>  static inline void __iomem *ioport_map(unsigned long port, unsigned int nr)
>  {
> -	return PCI_IOBASE + (port & IO_SPACE_LIMIT);
> +	return PCI_IOBASE + (port & MMIO_UPPER_LIMIT);
>  }
>  #endif
>  

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

* Re: [PATCH v13 1/9] LIB: Introduce a generic PIO mapping method
  2018-02-13 17:45   ` John Garry
@ 2018-02-13 23:05       ` dann frazier
  -1 siblings, 0 replies; 66+ messages in thread
From: dann frazier @ 2018-02-13 23:05 UTC (permalink / raw)
  To: John Garry
  Cc: mika.westerberg-VuQAYsv1563Yd54FQh9/CA,
	rafael-DgEjT+Ai2ygdnm+yROfE0A, lorenzo.pieralisi-5wv7dgnIgG8,
	rjw-LthD3rsA81gm4RdzfppkhA, hanjun.guo-QSEj5FYQhm4dnm+yROfE0A,
	robh+dt-DgEjT+Ai2ygdnm+yROfE0A, bhelgaas-hpIqsD4AKlfQT0dZR+AlfA,
	arnd-r2nGTMty4D4, mark.rutland-5wv7dgnIgG8,
	olof-nZhT3qVonbNeoWH0uzbU5w,
	andy.shevchenko-Re5JQEeQqe8AvxtiuMwx3w,
	robh-DgEjT+Ai2ygdnm+yROfE0A, joe-6d6DIl74uiNBDgjK7y7TUQ,
	benh-XVmvHMARGAS8U2dJNN8I7kB+6BGkLq7r,
	linux-pci-u79uwXL29TY76Z2rM5mHXA,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	linux-acpi-u79uwXL29TY76Z2rM5mHXA,
	linuxarm-hv44wF8Li93QT0dZR+AlfA, minyard-HInyCGIudOg,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-arch-u79uwXL29TY76Z2rM5mHXA,
	rdunlap-wEGCiKHe2LqWVfeAwA7xHQ

On Wed, Feb 14, 2018 at 01:45:25AM +0800, John Garry wrote:
> From: Zhichang Yuan <yuanzhichang-C8/M+/jPZTeaMJb+Lgu22Q@public.gmane.org>
> 
> In commit 41f8bba7f555 ("of/pci: Add pci_register_io_range() and
> pci_pio_to_address()"), a new I/O space management was supported. With
> that driver, the I/O ranges configured for PCI/PCIe hosts on some
> architectures can be mapped to logical PIO, converted easily between
> CPU address and the corresponding logicial PIO. Based on this, PCI
> I/O devices can be accessed in a memory read/write way through the
> unified in/out accessors.
> 
> But on some archs/platforms, there are bus hosts which access I/O
> peripherals with host-local I/O port addresses rather than memory
> addresses after memory-mapped.
> 
> To support those devices, a more generic I/O mapping method is introduced
> here. Through this patch, both the CPU addresses and the host-local port
> can be mapped into the logical PIO space with different logical/fake PIOs.
> After this, all the I/O accesses to either PCI MMIO devices or host-local
> I/O peripherals can be unified into the existing I/O accessors defined in
> asm-generic/io.h and be redirected to the right device-specific hooks
> based on the input logical PIO.
> 
> Signed-off-by: Zhichang Yuan <yuanzhichang-C8/M+/jPZTeaMJb+Lgu22Q@public.gmane.org>
> Signed-off-by: Gabriele Paoloni <gabriele.paoloni-hv44wF8Li93QT0dZR+AlfA@public.gmane.org>
> Signed-off-by: John Garry <john.garry-hv44wF8Li93QT0dZR+AlfA@public.gmane.org>
> ---
>  include/asm-generic/io.h  |   2 +
>  include/linux/logic_pio.h | 131 ++++++++++++++++++++++
>  lib/Kconfig               |  15 +++
>  lib/Makefile              |   2 +
>  lib/logic_pio.c           | 272 ++++++++++++++++++++++++++++++++++++++++++++++
>  5 files changed, 422 insertions(+)
>  create mode 100644 include/linux/logic_pio.h
>  create mode 100644 lib/logic_pio.c
> 
> diff --git a/include/asm-generic/io.h b/include/asm-generic/io.h
> index b4531e3..b7996a79 100644
> --- a/include/asm-generic/io.h
> +++ b/include/asm-generic/io.h
> @@ -351,6 +351,8 @@ static inline void writesq(volatile void __iomem *addr, const void *buffer,
>  #define IO_SPACE_LIMIT 0xffff
>  #endif
>  
> +#include <linux/logic_pio.h>
> +
>  /*
>   * {in,out}{b,w,l}() access little endian I/O. {in,out}{b,w,l}_p() can be
>   * implemented on hardware that needs an additional delay for I/O accesses to
> diff --git a/include/linux/logic_pio.h b/include/linux/logic_pio.h
> new file mode 100644
> index 0000000..b3322fd
> --- /dev/null
> +++ b/include/linux/logic_pio.h
> @@ -0,0 +1,131 @@
> +// SPDX-License-Identifier: GPL-2.0+
> +/*
> + * Copyright (C) 2017 Hisilicon Limited, All Rights Reserved.
> + * Author: Gabriele Paoloni <gabriele.paoloni-hv44wF8Li93QT0dZR+AlfA@public.gmane.org>
> + * Author: Zhichang Yuan <yuanzhichang-C8/M+/jPZTeaMJb+Lgu22Q@public.gmane.org>
> + *
> + */
> +
> +#ifndef __LINUX_LOGIC_PIO_H__
> +#define __LINUX_LOGIC_PIO_H__
> +
> +#ifdef __KERNEL__
> +
> +#include <linux/fwnode.h>
> +
> +#define PIO_INDIRECT		0x01UL /* indirect IO flag */
> +#define PIO_CPU_MMIO		0x00UL /* memory mapped io flag */
> +
> +struct logic_pio_hwaddr {
> +	struct list_head list;
> +	struct fwnode_handle *fwnode;
> +	resource_size_t hw_start;
> +	resource_size_t io_start;
> +	resource_size_t size; /* range size populated */
> +	unsigned long flags;
> +
> +	void *devpara;	/* private parameter of the host device */
> +	struct hostio_ops *ops;	/* ops operating on this node */
> +};
> +
> +struct hostio_ops {
> +	u32 (*pfin)(void *devobj, unsigned long ptaddr,	size_t dlen);
> +	void (*pfout)(void *devobj, unsigned long ptaddr, u32 outval,
> +			size_t dlen);
> +	u32 (*pfins)(void *devobj, unsigned long ptaddr, void *inbuf,
> +			size_t dlen, unsigned int count);
> +	void (*pfouts)(void *devobj, unsigned long ptaddr,
> +			const void *outbuf, size_t dlen, unsigned int count);
> +};
> +
> +#ifdef CONFIG_INDIRECT_PIO
> +u8 logic_inb(unsigned long addr);
> +void logic_outb(u8 value, unsigned long addr);
> +void logic_outw(u16 value, unsigned long addr);
> +void logic_outl(u32 value, unsigned long addr);
> +u16 logic_inw(unsigned long addr);
> +u32 logic_inl(unsigned long addr);
> +void logic_outb(u8 value, unsigned long addr);
> +void logic_outw(u16 value, unsigned long addr);
> +void logic_outl(u32 value, unsigned long addr);
> +void logic_insb(unsigned long addr, void *buffer, unsigned int count);
> +void logic_insl(unsigned long addr, void *buffer, unsigned int count);
> +void logic_insw(unsigned long addr, void *buffer, unsigned int count);
> +void logic_outsb(unsigned long addr, const void *buffer, unsigned int count);
> +void logic_outsw(unsigned long addr, const void *buffer, unsigned int count);
> +void logic_outsl(unsigned long addr, const void *buffer, unsigned int count);
> +
> +#ifndef inb
> +#define inb logic_inb
> +#endif
> +
> +#ifndef inw
> +#define inw logic_inw
> +#endif
> +
> +#ifndef inl
> +#define inl logic_inl
> +#endif
> +
> +#ifndef outb
> +#define outb logic_outb
> +#endif
> +
> +#ifndef outw
> +#define outw logic_outw
> +#endif
> +
> +#ifndef outl
> +#define outl logic_outl
> +#endif
> +
> +#ifndef insb
> +#define insb logic_insb
> +#endif
> +
> +#ifndef insw
> +#define insw logic_insw
> +#endif
> +
> +#ifndef insl
> +#define insl logic_insl
> +#endif
> +
> +#ifndef outsb
> +#define outsb logic_outsb
> +#endif
> +
> +#ifndef outsw
> +#define outsw logic_outsw
> +#endif
> +
> +#ifndef outsl
> +#define outsl logic_outsl
> +#endif
> +
> +/*
> + * Below we reserve 0x4000 bytes for Indirect IO as so far this library is only
> + * used by Hisilicon LPC Host. If needed in future we may reserve a wider IO
> + * area by redefining the macro below.
> + */
> +#define PIO_INDIRECT_SIZE 0x4000
> +#define MMIO_UPPER_LIMIT (IO_SPACE_LIMIT - PIO_INDIRECT_SIZE)
> +#else
> +#define MMIO_UPPER_LIMIT IO_SPACE_LIMIT
> +#endif /* CONFIG_INDIRECT_PIO */
> +
> +
> +struct logic_pio_hwaddr *find_io_range_by_fwnode(struct fwnode_handle *fwnode);
> +
> +unsigned long logic_pio_trans_hwaddr(struct fwnode_handle *fwnode,
> +			resource_size_t hw_addr, resource_size_t size);
> +
> +int logic_pio_register_range(struct logic_pio_hwaddr *newrange);
> +
> +
> +extern resource_size_t logic_pio_to_hwaddr(unsigned long pio);
> +
> +extern unsigned long logic_pio_trans_cpuaddr(resource_size_t hw_addr);
> +
> +#endif /* __KERNEL__ */
> +#endif /* __LINUX_LOGIC_PIO_H__ */
> diff --git a/lib/Kconfig b/lib/Kconfig
> index e960894..7ad1e69 100644
> --- a/lib/Kconfig
> +++ b/lib/Kconfig
> @@ -55,6 +55,21 @@ config ARCH_USE_CMPXCHG_LOCKREF
>  config ARCH_HAS_FAST_MULTIPLIER
>  	bool
>  
> +config INDIRECT_PIO
> +	bool "Access I/O in non-MMIO mode"
> +	depends on ARM64
> +	help
> +	  On some platforms where no separate I/O space exists, there are I/O
> +	  hosts which can not be accessed in MMIO mode. Using the logical PIO
> +	  mechanism, the host-local I/O resource can be mapped into system
> +	  logic PIO space shared with MMIO hosts, such as PCI/PCIE, then the
> +	  system can access the I/O devices with the mapped logic PIO through
> +	  I/O accessors.
> +	  This way has a relatively little I/O performance cost. Please make

grammar nit - s/a relatively/relatively/

  -dann

> +	  sure your devices really need this configure item enabled.
> +
> +	  When in doubt, say N.
> +
>  config CRC_CCITT
>  	tristate "CRC-CCITT functions"
>  	help
> diff --git a/lib/Makefile b/lib/Makefile
> index a90d4fc..4a9eacd 100644
> --- a/lib/Makefile
> +++ b/lib/Makefile
> @@ -81,6 +81,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-y += logic_pio.o
> +
>  obj-$(CONFIG_GENERIC_HWEIGHT) += hweight.o
>  
>  obj-$(CONFIG_BTREE) += btree.o
> diff --git a/lib/logic_pio.c b/lib/logic_pio.c
> new file mode 100644
> index 0000000..1006d9d
> --- /dev/null
> +++ b/lib/logic_pio.c
> @@ -0,0 +1,272 @@
> +// SPDX-License-Identifier: GPL-2.0+
> +/*
> + * Copyright (C) 2017 Hisilicon Limited, All Rights Reserved.
> + * Author: Gabriele Paoloni <gabriele.paoloni-hv44wF8Li93QT0dZR+AlfA@public.gmane.org>
> + * Author: Zhichang Yuan <yuanzhichang-C8/M+/jPZTeaMJb+Lgu22Q@public.gmane.org>
> + *
> + */
> +
> +#define pr_fmt(fmt)	"LOGIC PIO: " fmt
> +
> +#include <linux/of.h>
> +#include <linux/io.h>
> +#include <linux/logic_pio.h>
> +#include <linux/mm.h>
> +#include <linux/rculist.h>
> +#include <linux/sizes.h>
> +#include <linux/slab.h>
> +
> +/* The unique hardware address list. */
> +static LIST_HEAD(io_range_list);
> +static DEFINE_MUTEX(io_range_mutex);
> +
> +/*
> + * register a new io range node in the io range list.
> + *
> + * @newrange: pointer to the io range to be registered.
> + *
> + * returns 0 on success, the error code in case of failure
> + */
> +int logic_pio_register_range(struct logic_pio_hwaddr *new_range)
> +{
> +	struct logic_pio_hwaddr *range;
> +	int ret = 0;
> +	resource_size_t start = new_range->hw_start;
> +	resource_size_t end = new_range->hw_start + new_range->size;
> +	resource_size_t allocated_mmio_size = 0;
> +	resource_size_t allocated_iio_size = MMIO_UPPER_LIMIT;
> +
> +	if (!new_range || !new_range->fwnode || !new_range->size)
> +		return -EINVAL;
> +
> +	mutex_lock(&io_range_mutex);
> +	list_for_each_entry_rcu(range, &io_range_list, list) {
> +		if (range->fwnode == new_range->fwnode) {
> +			/* range already there */
> +			ret = -EFAULT;
> +			goto end_register;
> +		}
> +		if (range->flags == PIO_CPU_MMIO &&
> +				new_range->flags == PIO_CPU_MMIO) {
> +			/* for MMIO ranges we need to check for overlap */
> +			if (start >= range->hw_start + range->size ||
> +			    end < range->hw_start)
> +				allocated_mmio_size += range->size;
> +			else {
> +				ret = -EFAULT;
> +				goto end_register;
> +			}
> +		} else if (range->flags == PIO_INDIRECT &&
> +			   new_range->flags == PIO_INDIRECT) {
> +			allocated_iio_size += range->size;
> +		}
> +	}
> +
> +	/* range not registered yet, check for available space */
> +	if (new_range->flags == PIO_CPU_MMIO) {
> +		if (allocated_mmio_size + new_range->size - 1 >
> +			MMIO_UPPER_LIMIT) {
> +			/* if it's too big check if 64K space can be reserved */
> +			if (allocated_mmio_size + SZ_64K - 1 >
> +			MMIO_UPPER_LIMIT) {
> +				ret = -E2BIG;
> +				goto end_register;
> +			}
> +			new_range->size = SZ_64K;
> +			pr_warn("Requested IO range too big, new size set to 64K\n");
> +		}
> +		new_range->io_start = allocated_mmio_size;
> +	} else if (new_range->flags == PIO_INDIRECT) {
> +		if (allocated_iio_size + new_range->size - 1 >
> +		    IO_SPACE_LIMIT) {
> +			ret = -E2BIG;
> +			goto end_register;
> +		}
> +		new_range->io_start = allocated_iio_size;
> +	} else {
> +		/* invalid flag */
> +		ret = -EINVAL;
> +		goto end_register;
> +	}
> +
> +	list_add_tail_rcu(&new_range->list, &io_range_list);
> +
> +end_register:
> +	mutex_unlock(&io_range_mutex);
> +	return ret;
> +}
> +
> +/*
> + * traverse the io_range_list to find the registered node whose device node
> + * and/or physical IO address match to.
> + */
> +struct logic_pio_hwaddr *find_io_range_by_fwnode(struct fwnode_handle *fwnode)
> +{
> +	struct logic_pio_hwaddr *range;
> +
> +	list_for_each_entry_rcu(range, &io_range_list, list) {
> +		if (range->fwnode == fwnode)
> +			return range;
> +	}
> +	return NULL;
> +}
> +
> +/* return a registered range given an input PIO token */
> +static struct logic_pio_hwaddr *find_io_range(unsigned long pio)
> +{
> +	struct logic_pio_hwaddr *range;
> +
> +	list_for_each_entry_rcu(range, &io_range_list, list) {
> +		if (pio >= range->io_start &&
> +		    pio < range->io_start + range->size)
> +			return range;
> +	}
> +	pr_err("PIO entry token invalid\n");
> +	return NULL;
> +}
> +
> +/*
> + * Translate the input logical pio to the corresponding hardware address.
> + * The input pio should be unique in the whole logical PIO space.
> + */
> +resource_size_t logic_pio_to_hwaddr(unsigned long pio)
> +{
> +	struct logic_pio_hwaddr *range;
> +	resource_size_t hwaddr = -1;
> +
> +	range = find_io_range(pio);
> +	if (range)
> +		hwaddr = range->hw_start + pio - range->io_start;
> +
> +	return hwaddr;
> +}
> +
> +/*
> + * This function is generic for translating a hardware address to logical PIO.
> + * @hw_addr: the hardware address of host, can be CPU address or host-local
> + *		address;
> + */
> +unsigned long
> +logic_pio_trans_hwaddr(struct fwnode_handle *fwnode, resource_size_t addr,
> +		       resource_size_t size)
> +{
> +	struct logic_pio_hwaddr *range;
> +
> +	range = find_io_range_by_fwnode(fwnode);
> +	if (!range || range->flags == PIO_CPU_MMIO) {
> +		pr_err("range not found or invalid\n");
> +		return -1;
> +	}
> +	if (range->size < size) {
> +		pr_err("resource size %pa cannot fit in IO range size %pa\n",
> +		       &size, &range->size);
> +		return -1;
> +	}
> +	return addr - range->hw_start + range->io_start;
> +}
> +
> +unsigned long
> +logic_pio_trans_cpuaddr(resource_size_t addr)
> +{
> +	struct logic_pio_hwaddr *range;
> +
> +	list_for_each_entry_rcu(range, &io_range_list, list) {
> +		if (range->flags != PIO_CPU_MMIO)
> +			continue;
> +		if (addr >= range->hw_start &&
> +		    addr < range->hw_start + range->size)
> +			return addr - range->hw_start +
> +				range->io_start;
> +	}
> +	pr_err("addr not registered in io_range_list\n");
> +	return -1;
> +}
> +
> +#if defined(CONFIG_INDIRECT_PIO) && defined(PCI_IOBASE)
> +#define BUILD_LOGIC_IO(bw, type)					\
> +type logic_in##bw(unsigned long addr)					\
> +{									\
> +	type ret = -1;							\
> +									\
> +	if (addr < MMIO_UPPER_LIMIT) {					\
> +		ret = read##bw(PCI_IOBASE + addr);			\
> +	} else if (addr >= MMIO_UPPER_LIMIT && addr < IO_SPACE_LIMIT) { \
> +		struct logic_pio_hwaddr *entry = find_io_range(addr);	\
> +									\
> +		if (entry && entry->ops)				\
> +			ret = entry->ops->pfin(entry->devpara,		\
> +					addr, sizeof(type));		\
> +		else							\
> +			WARN_ON_ONCE(1);				\
> +	}								\
> +	return ret;							\
> +}									\
> +									\
> +void logic_out##bw(type value, unsigned long addr)			\
> +{									\
> +	if (addr < MMIO_UPPER_LIMIT) {					\
> +		write##bw(value, PCI_IOBASE + addr);			\
> +	} else if (addr >= MMIO_UPPER_LIMIT && addr < IO_SPACE_LIMIT) {	\
> +		struct logic_pio_hwaddr *entry = find_io_range(addr);	\
> +									\
> +		if (entry && entry->ops)				\
> +			entry->ops->pfout(entry->devpara,		\
> +					addr, value, sizeof(type));	\
> +		else							\
> +			WARN_ON_ONCE(1);				\
> +	}								\
> +}									\
> +									\
> +void logic_ins##bw(unsigned long addr, void *buffer, unsigned int count)\
> +{									\
> +	if (addr < MMIO_UPPER_LIMIT) {					\
> +		reads##bw(PCI_IOBASE + addr, buffer, count);		\
> +	} else if (addr >= MMIO_UPPER_LIMIT && addr < IO_SPACE_LIMIT) {	\
> +		struct logic_pio_hwaddr *entry = find_io_range(addr);	\
> +									\
> +		if (entry && entry->ops)				\
> +			entry->ops->pfins(entry->devpara,		\
> +				addr, buffer, sizeof(type), count);	\
> +		else							\
> +			WARN_ON_ONCE(1);				\
> +	}								\
> +									\
> +}									\
> +									\
> +void logic_outs##bw(unsigned long addr, const void *buffer,		\
> +		    unsigned int count)					\
> +{									\
> +	if (addr < MMIO_UPPER_LIMIT) {					\
> +		writes##bw(PCI_IOBASE + addr, buffer, count);		\
> +	} else if (addr >= MMIO_UPPER_LIMIT && addr < IO_SPACE_LIMIT) {	\
> +		struct logic_pio_hwaddr *entry = find_io_range(addr);	\
> +									\
> +		if (entry && entry->ops)				\
> +			entry->ops->pfouts(entry->devpara,		\
> +				addr, buffer, sizeof(type), count);	\
> +		else							\
> +			WARN_ON_ONCE(1);				\
> +	}								\
> +}
> +
> +BUILD_LOGIC_IO(b, u8)
> +
> +EXPORT_SYMBOL(logic_inb);
> +EXPORT_SYMBOL(logic_outb);
> +EXPORT_SYMBOL(logic_insb);
> +EXPORT_SYMBOL(logic_outsb);
> +
> +BUILD_LOGIC_IO(w, u16)
> +
> +EXPORT_SYMBOL(logic_inw);
> +EXPORT_SYMBOL(logic_outw);
> +EXPORT_SYMBOL(logic_insw);
> +EXPORT_SYMBOL(logic_outsw);
> +
> +BUILD_LOGIC_IO(l, u32)
> +
> +EXPORT_SYMBOL(logic_inl);
> +EXPORT_SYMBOL(logic_outl);
> +EXPORT_SYMBOL(logic_insl);
> +EXPORT_SYMBOL(logic_outsl);
> +#endif /* CONFIG_INDIRECT_PIO && PCI_IOBASE */
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

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

* Re: [PATCH v13 1/9] LIB: Introduce a generic PIO mapping method
@ 2018-02-13 23:05       ` dann frazier
  0 siblings, 0 replies; 66+ messages in thread
From: dann frazier @ 2018-02-13 23:05 UTC (permalink / raw)
  To: John Garry
  Cc: mika.westerberg, rafael, lorenzo.pieralisi, rjw, hanjun.guo,
	robh+dt, bhelgaas, arnd, mark.rutland, olof, andy.shevchenko,
	robh, joe, benh, linux-pci, linux-kernel, linux-acpi, linuxarm,
	minyard, devicetree, linux-arch, rdunlap

On Wed, Feb 14, 2018 at 01:45:25AM +0800, John Garry wrote:
> From: Zhichang Yuan <yuanzhichang@hisilicon.com>
> 
> In commit 41f8bba7f555 ("of/pci: Add pci_register_io_range() and
> pci_pio_to_address()"), a new I/O space management was supported. With
> that driver, the I/O ranges configured for PCI/PCIe hosts on some
> architectures can be mapped to logical PIO, converted easily between
> CPU address and the corresponding logicial PIO. Based on this, PCI
> I/O devices can be accessed in a memory read/write way through the
> unified in/out accessors.
> 
> But on some archs/platforms, there are bus hosts which access I/O
> peripherals with host-local I/O port addresses rather than memory
> addresses after memory-mapped.
> 
> To support those devices, a more generic I/O mapping method is introduced
> here. Through this patch, both the CPU addresses and the host-local port
> can be mapped into the logical PIO space with different logical/fake PIOs.
> After this, all the I/O accesses to either PCI MMIO devices or host-local
> I/O peripherals can be unified into the existing I/O accessors defined in
> asm-generic/io.h and be redirected to the right device-specific hooks
> based on the input logical PIO.
> 
> Signed-off-by: Zhichang Yuan <yuanzhichang@hisilicon.com>
> Signed-off-by: Gabriele Paoloni <gabriele.paoloni@huawei.com>
> Signed-off-by: John Garry <john.garry@huawei.com>
> ---
>  include/asm-generic/io.h  |   2 +
>  include/linux/logic_pio.h | 131 ++++++++++++++++++++++
>  lib/Kconfig               |  15 +++
>  lib/Makefile              |   2 +
>  lib/logic_pio.c           | 272 ++++++++++++++++++++++++++++++++++++++++++++++
>  5 files changed, 422 insertions(+)
>  create mode 100644 include/linux/logic_pio.h
>  create mode 100644 lib/logic_pio.c
> 
> diff --git a/include/asm-generic/io.h b/include/asm-generic/io.h
> index b4531e3..b7996a79 100644
> --- a/include/asm-generic/io.h
> +++ b/include/asm-generic/io.h
> @@ -351,6 +351,8 @@ static inline void writesq(volatile void __iomem *addr, const void *buffer,
>  #define IO_SPACE_LIMIT 0xffff
>  #endif
>  
> +#include <linux/logic_pio.h>
> +
>  /*
>   * {in,out}{b,w,l}() access little endian I/O. {in,out}{b,w,l}_p() can be
>   * implemented on hardware that needs an additional delay for I/O accesses to
> diff --git a/include/linux/logic_pio.h b/include/linux/logic_pio.h
> new file mode 100644
> index 0000000..b3322fd
> --- /dev/null
> +++ b/include/linux/logic_pio.h
> @@ -0,0 +1,131 @@
> +// SPDX-License-Identifier: GPL-2.0+
> +/*
> + * Copyright (C) 2017 Hisilicon Limited, All Rights Reserved.
> + * Author: Gabriele Paoloni <gabriele.paoloni@huawei.com>
> + * Author: Zhichang Yuan <yuanzhichang@hisilicon.com>
> + *
> + */
> +
> +#ifndef __LINUX_LOGIC_PIO_H__
> +#define __LINUX_LOGIC_PIO_H__
> +
> +#ifdef __KERNEL__
> +
> +#include <linux/fwnode.h>
> +
> +#define PIO_INDIRECT		0x01UL /* indirect IO flag */
> +#define PIO_CPU_MMIO		0x00UL /* memory mapped io flag */
> +
> +struct logic_pio_hwaddr {
> +	struct list_head list;
> +	struct fwnode_handle *fwnode;
> +	resource_size_t hw_start;
> +	resource_size_t io_start;
> +	resource_size_t size; /* range size populated */
> +	unsigned long flags;
> +
> +	void *devpara;	/* private parameter of the host device */
> +	struct hostio_ops *ops;	/* ops operating on this node */
> +};
> +
> +struct hostio_ops {
> +	u32 (*pfin)(void *devobj, unsigned long ptaddr,	size_t dlen);
> +	void (*pfout)(void *devobj, unsigned long ptaddr, u32 outval,
> +			size_t dlen);
> +	u32 (*pfins)(void *devobj, unsigned long ptaddr, void *inbuf,
> +			size_t dlen, unsigned int count);
> +	void (*pfouts)(void *devobj, unsigned long ptaddr,
> +			const void *outbuf, size_t dlen, unsigned int count);
> +};
> +
> +#ifdef CONFIG_INDIRECT_PIO
> +u8 logic_inb(unsigned long addr);
> +void logic_outb(u8 value, unsigned long addr);
> +void logic_outw(u16 value, unsigned long addr);
> +void logic_outl(u32 value, unsigned long addr);
> +u16 logic_inw(unsigned long addr);
> +u32 logic_inl(unsigned long addr);
> +void logic_outb(u8 value, unsigned long addr);
> +void logic_outw(u16 value, unsigned long addr);
> +void logic_outl(u32 value, unsigned long addr);
> +void logic_insb(unsigned long addr, void *buffer, unsigned int count);
> +void logic_insl(unsigned long addr, void *buffer, unsigned int count);
> +void logic_insw(unsigned long addr, void *buffer, unsigned int count);
> +void logic_outsb(unsigned long addr, const void *buffer, unsigned int count);
> +void logic_outsw(unsigned long addr, const void *buffer, unsigned int count);
> +void logic_outsl(unsigned long addr, const void *buffer, unsigned int count);
> +
> +#ifndef inb
> +#define inb logic_inb
> +#endif
> +
> +#ifndef inw
> +#define inw logic_inw
> +#endif
> +
> +#ifndef inl
> +#define inl logic_inl
> +#endif
> +
> +#ifndef outb
> +#define outb logic_outb
> +#endif
> +
> +#ifndef outw
> +#define outw logic_outw
> +#endif
> +
> +#ifndef outl
> +#define outl logic_outl
> +#endif
> +
> +#ifndef insb
> +#define insb logic_insb
> +#endif
> +
> +#ifndef insw
> +#define insw logic_insw
> +#endif
> +
> +#ifndef insl
> +#define insl logic_insl
> +#endif
> +
> +#ifndef outsb
> +#define outsb logic_outsb
> +#endif
> +
> +#ifndef outsw
> +#define outsw logic_outsw
> +#endif
> +
> +#ifndef outsl
> +#define outsl logic_outsl
> +#endif
> +
> +/*
> + * Below we reserve 0x4000 bytes for Indirect IO as so far this library is only
> + * used by Hisilicon LPC Host. If needed in future we may reserve a wider IO
> + * area by redefining the macro below.
> + */
> +#define PIO_INDIRECT_SIZE 0x4000
> +#define MMIO_UPPER_LIMIT (IO_SPACE_LIMIT - PIO_INDIRECT_SIZE)
> +#else
> +#define MMIO_UPPER_LIMIT IO_SPACE_LIMIT
> +#endif /* CONFIG_INDIRECT_PIO */
> +
> +
> +struct logic_pio_hwaddr *find_io_range_by_fwnode(struct fwnode_handle *fwnode);
> +
> +unsigned long logic_pio_trans_hwaddr(struct fwnode_handle *fwnode,
> +			resource_size_t hw_addr, resource_size_t size);
> +
> +int logic_pio_register_range(struct logic_pio_hwaddr *newrange);
> +
> +
> +extern resource_size_t logic_pio_to_hwaddr(unsigned long pio);
> +
> +extern unsigned long logic_pio_trans_cpuaddr(resource_size_t hw_addr);
> +
> +#endif /* __KERNEL__ */
> +#endif /* __LINUX_LOGIC_PIO_H__ */
> diff --git a/lib/Kconfig b/lib/Kconfig
> index e960894..7ad1e69 100644
> --- a/lib/Kconfig
> +++ b/lib/Kconfig
> @@ -55,6 +55,21 @@ config ARCH_USE_CMPXCHG_LOCKREF
>  config ARCH_HAS_FAST_MULTIPLIER
>  	bool
>  
> +config INDIRECT_PIO
> +	bool "Access I/O in non-MMIO mode"
> +	depends on ARM64
> +	help
> +	  On some platforms where no separate I/O space exists, there are I/O
> +	  hosts which can not be accessed in MMIO mode. Using the logical PIO
> +	  mechanism, the host-local I/O resource can be mapped into system
> +	  logic PIO space shared with MMIO hosts, such as PCI/PCIE, then the
> +	  system can access the I/O devices with the mapped logic PIO through
> +	  I/O accessors.
> +	  This way has a relatively little I/O performance cost. Please make

grammar nit - s/a relatively/relatively/

  -dann

> +	  sure your devices really need this configure item enabled.
> +
> +	  When in doubt, say N.
> +
>  config CRC_CCITT
>  	tristate "CRC-CCITT functions"
>  	help
> diff --git a/lib/Makefile b/lib/Makefile
> index a90d4fc..4a9eacd 100644
> --- a/lib/Makefile
> +++ b/lib/Makefile
> @@ -81,6 +81,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-y += logic_pio.o
> +
>  obj-$(CONFIG_GENERIC_HWEIGHT) += hweight.o
>  
>  obj-$(CONFIG_BTREE) += btree.o
> diff --git a/lib/logic_pio.c b/lib/logic_pio.c
> new file mode 100644
> index 0000000..1006d9d
> --- /dev/null
> +++ b/lib/logic_pio.c
> @@ -0,0 +1,272 @@
> +// SPDX-License-Identifier: GPL-2.0+
> +/*
> + * Copyright (C) 2017 Hisilicon Limited, All Rights Reserved.
> + * Author: Gabriele Paoloni <gabriele.paoloni@huawei.com>
> + * Author: Zhichang Yuan <yuanzhichang@hisilicon.com>
> + *
> + */
> +
> +#define pr_fmt(fmt)	"LOGIC PIO: " fmt
> +
> +#include <linux/of.h>
> +#include <linux/io.h>
> +#include <linux/logic_pio.h>
> +#include <linux/mm.h>
> +#include <linux/rculist.h>
> +#include <linux/sizes.h>
> +#include <linux/slab.h>
> +
> +/* The unique hardware address list. */
> +static LIST_HEAD(io_range_list);
> +static DEFINE_MUTEX(io_range_mutex);
> +
> +/*
> + * register a new io range node in the io range list.
> + *
> + * @newrange: pointer to the io range to be registered.
> + *
> + * returns 0 on success, the error code in case of failure
> + */
> +int logic_pio_register_range(struct logic_pio_hwaddr *new_range)
> +{
> +	struct logic_pio_hwaddr *range;
> +	int ret = 0;
> +	resource_size_t start = new_range->hw_start;
> +	resource_size_t end = new_range->hw_start + new_range->size;
> +	resource_size_t allocated_mmio_size = 0;
> +	resource_size_t allocated_iio_size = MMIO_UPPER_LIMIT;
> +
> +	if (!new_range || !new_range->fwnode || !new_range->size)
> +		return -EINVAL;
> +
> +	mutex_lock(&io_range_mutex);
> +	list_for_each_entry_rcu(range, &io_range_list, list) {
> +		if (range->fwnode == new_range->fwnode) {
> +			/* range already there */
> +			ret = -EFAULT;
> +			goto end_register;
> +		}
> +		if (range->flags == PIO_CPU_MMIO &&
> +				new_range->flags == PIO_CPU_MMIO) {
> +			/* for MMIO ranges we need to check for overlap */
> +			if (start >= range->hw_start + range->size ||
> +			    end < range->hw_start)
> +				allocated_mmio_size += range->size;
> +			else {
> +				ret = -EFAULT;
> +				goto end_register;
> +			}
> +		} else if (range->flags == PIO_INDIRECT &&
> +			   new_range->flags == PIO_INDIRECT) {
> +			allocated_iio_size += range->size;
> +		}
> +	}
> +
> +	/* range not registered yet, check for available space */
> +	if (new_range->flags == PIO_CPU_MMIO) {
> +		if (allocated_mmio_size + new_range->size - 1 >
> +			MMIO_UPPER_LIMIT) {
> +			/* if it's too big check if 64K space can be reserved */
> +			if (allocated_mmio_size + SZ_64K - 1 >
> +			MMIO_UPPER_LIMIT) {
> +				ret = -E2BIG;
> +				goto end_register;
> +			}
> +			new_range->size = SZ_64K;
> +			pr_warn("Requested IO range too big, new size set to 64K\n");
> +		}
> +		new_range->io_start = allocated_mmio_size;
> +	} else if (new_range->flags == PIO_INDIRECT) {
> +		if (allocated_iio_size + new_range->size - 1 >
> +		    IO_SPACE_LIMIT) {
> +			ret = -E2BIG;
> +			goto end_register;
> +		}
> +		new_range->io_start = allocated_iio_size;
> +	} else {
> +		/* invalid flag */
> +		ret = -EINVAL;
> +		goto end_register;
> +	}
> +
> +	list_add_tail_rcu(&new_range->list, &io_range_list);
> +
> +end_register:
> +	mutex_unlock(&io_range_mutex);
> +	return ret;
> +}
> +
> +/*
> + * traverse the io_range_list to find the registered node whose device node
> + * and/or physical IO address match to.
> + */
> +struct logic_pio_hwaddr *find_io_range_by_fwnode(struct fwnode_handle *fwnode)
> +{
> +	struct logic_pio_hwaddr *range;
> +
> +	list_for_each_entry_rcu(range, &io_range_list, list) {
> +		if (range->fwnode == fwnode)
> +			return range;
> +	}
> +	return NULL;
> +}
> +
> +/* return a registered range given an input PIO token */
> +static struct logic_pio_hwaddr *find_io_range(unsigned long pio)
> +{
> +	struct logic_pio_hwaddr *range;
> +
> +	list_for_each_entry_rcu(range, &io_range_list, list) {
> +		if (pio >= range->io_start &&
> +		    pio < range->io_start + range->size)
> +			return range;
> +	}
> +	pr_err("PIO entry token invalid\n");
> +	return NULL;
> +}
> +
> +/*
> + * Translate the input logical pio to the corresponding hardware address.
> + * The input pio should be unique in the whole logical PIO space.
> + */
> +resource_size_t logic_pio_to_hwaddr(unsigned long pio)
> +{
> +	struct logic_pio_hwaddr *range;
> +	resource_size_t hwaddr = -1;
> +
> +	range = find_io_range(pio);
> +	if (range)
> +		hwaddr = range->hw_start + pio - range->io_start;
> +
> +	return hwaddr;
> +}
> +
> +/*
> + * This function is generic for translating a hardware address to logical PIO.
> + * @hw_addr: the hardware address of host, can be CPU address or host-local
> + *		address;
> + */
> +unsigned long
> +logic_pio_trans_hwaddr(struct fwnode_handle *fwnode, resource_size_t addr,
> +		       resource_size_t size)
> +{
> +	struct logic_pio_hwaddr *range;
> +
> +	range = find_io_range_by_fwnode(fwnode);
> +	if (!range || range->flags == PIO_CPU_MMIO) {
> +		pr_err("range not found or invalid\n");
> +		return -1;
> +	}
> +	if (range->size < size) {
> +		pr_err("resource size %pa cannot fit in IO range size %pa\n",
> +		       &size, &range->size);
> +		return -1;
> +	}
> +	return addr - range->hw_start + range->io_start;
> +}
> +
> +unsigned long
> +logic_pio_trans_cpuaddr(resource_size_t addr)
> +{
> +	struct logic_pio_hwaddr *range;
> +
> +	list_for_each_entry_rcu(range, &io_range_list, list) {
> +		if (range->flags != PIO_CPU_MMIO)
> +			continue;
> +		if (addr >= range->hw_start &&
> +		    addr < range->hw_start + range->size)
> +			return addr - range->hw_start +
> +				range->io_start;
> +	}
> +	pr_err("addr not registered in io_range_list\n");
> +	return -1;
> +}
> +
> +#if defined(CONFIG_INDIRECT_PIO) && defined(PCI_IOBASE)
> +#define BUILD_LOGIC_IO(bw, type)					\
> +type logic_in##bw(unsigned long addr)					\
> +{									\
> +	type ret = -1;							\
> +									\
> +	if (addr < MMIO_UPPER_LIMIT) {					\
> +		ret = read##bw(PCI_IOBASE + addr);			\
> +	} else if (addr >= MMIO_UPPER_LIMIT && addr < IO_SPACE_LIMIT) { \
> +		struct logic_pio_hwaddr *entry = find_io_range(addr);	\
> +									\
> +		if (entry && entry->ops)				\
> +			ret = entry->ops->pfin(entry->devpara,		\
> +					addr, sizeof(type));		\
> +		else							\
> +			WARN_ON_ONCE(1);				\
> +	}								\
> +	return ret;							\
> +}									\
> +									\
> +void logic_out##bw(type value, unsigned long addr)			\
> +{									\
> +	if (addr < MMIO_UPPER_LIMIT) {					\
> +		write##bw(value, PCI_IOBASE + addr);			\
> +	} else if (addr >= MMIO_UPPER_LIMIT && addr < IO_SPACE_LIMIT) {	\
> +		struct logic_pio_hwaddr *entry = find_io_range(addr);	\
> +									\
> +		if (entry && entry->ops)				\
> +			entry->ops->pfout(entry->devpara,		\
> +					addr, value, sizeof(type));	\
> +		else							\
> +			WARN_ON_ONCE(1);				\
> +	}								\
> +}									\
> +									\
> +void logic_ins##bw(unsigned long addr, void *buffer, unsigned int count)\
> +{									\
> +	if (addr < MMIO_UPPER_LIMIT) {					\
> +		reads##bw(PCI_IOBASE + addr, buffer, count);		\
> +	} else if (addr >= MMIO_UPPER_LIMIT && addr < IO_SPACE_LIMIT) {	\
> +		struct logic_pio_hwaddr *entry = find_io_range(addr);	\
> +									\
> +		if (entry && entry->ops)				\
> +			entry->ops->pfins(entry->devpara,		\
> +				addr, buffer, sizeof(type), count);	\
> +		else							\
> +			WARN_ON_ONCE(1);				\
> +	}								\
> +									\
> +}									\
> +									\
> +void logic_outs##bw(unsigned long addr, const void *buffer,		\
> +		    unsigned int count)					\
> +{									\
> +	if (addr < MMIO_UPPER_LIMIT) {					\
> +		writes##bw(PCI_IOBASE + addr, buffer, count);		\
> +	} else if (addr >= MMIO_UPPER_LIMIT && addr < IO_SPACE_LIMIT) {	\
> +		struct logic_pio_hwaddr *entry = find_io_range(addr);	\
> +									\
> +		if (entry && entry->ops)				\
> +			entry->ops->pfouts(entry->devpara,		\
> +				addr, buffer, sizeof(type), count);	\
> +		else							\
> +			WARN_ON_ONCE(1);				\
> +	}								\
> +}
> +
> +BUILD_LOGIC_IO(b, u8)
> +
> +EXPORT_SYMBOL(logic_inb);
> +EXPORT_SYMBOL(logic_outb);
> +EXPORT_SYMBOL(logic_insb);
> +EXPORT_SYMBOL(logic_outsb);
> +
> +BUILD_LOGIC_IO(w, u16)
> +
> +EXPORT_SYMBOL(logic_inw);
> +EXPORT_SYMBOL(logic_outw);
> +EXPORT_SYMBOL(logic_insw);
> +EXPORT_SYMBOL(logic_outsw);
> +
> +BUILD_LOGIC_IO(l, u32)
> +
> +EXPORT_SYMBOL(logic_inl);
> +EXPORT_SYMBOL(logic_outl);
> +EXPORT_SYMBOL(logic_insl);
> +EXPORT_SYMBOL(logic_outsl);
> +#endif /* CONFIG_INDIRECT_PIO && PCI_IOBASE */

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

* Re: [PATCH v13 7/9] ACPI: Translate the I/O range of non-MMIO devices before scanning
  2018-02-13 17:45   ` John Garry
@ 2018-02-14  9:21     ` Rafael J. Wysocki
  -1 siblings, 0 replies; 66+ messages in thread
From: Rafael J. Wysocki @ 2018-02-14  9:21 UTC (permalink / raw)
  To: John Garry
  Cc: Mika Westerberg, Rafael J. Wysocki, Lorenzo Pieralisi,
	Rafael J. Wysocki, Hanjun Guo, Rob Herring, Bjorn Helgaas,
	Arnd Bergmann, Mark Rutland, Olof Johansson, Dann Frazier,
	Andy Shevchenko, Rob Herring, Joe Perches,
	Benjamin Herrenschmidt, Linux PCI, Linux Kernel Mailing List,
	ACPI Devel Maling List, Linuxarm

On Tue, Feb 13, 2018 at 6:45 PM, John Garry <john.garry@huawei.com> wrote:
> On some platforms (such as arm64-based hip06/hip07), access to legacy
> ISA/LPC devices through access IO space is required, similar to x86
> platforms. As the I/O for these devices are not memory mapped like
> PCI/PCIE MMIO host bridges, they require special low-level device
> operations through some host to generate IO accesses, i.e. a non-
> transparent bridge.
>
> Through the logical PIO framework, hosts are able to register address
> ranges in the logical PIO space for IO accesses. For hosts which require
> a LLDD to generate the IO accesses, through the logical PIO framework
> the host also registers accessors as a backend to generate the physical
> bus transactions for IO space accesses (called indirect IO).
>
> When describing the indirect IO child device in APCI tables, the IO
> resource is the host-specific address for the child (generally a
> bus address).
> An example is as follows:
>   Device (LPC0) {
>     Name (_HID, "HISI0191")  // HiSi LPC
>     Name (_CRS, ResourceTemplate () {
>       Memory32Fixed (ReadWrite, 0xa01b0000, 0x1000)
>     })
>   }
>
>   Device (LPC0.IPMI) {
>     Name (_HID, "IPI0001")
>     Name (LORS, ResourceTemplate() {
>       QWordIO (
>         ResourceConsumer,
>         MinNotFixed,     // _MIF
>         MaxNotFixed,     // _MAF
>         PosDecode,
>         EntireRange,
>         0x0,             // _GRA
>         0xe4,            // _MIN
>         0x3fff,          // _MAX
>         0x0,             // _TRA
>         0x04,            // _LEN
>         , ,
>         BTIO
>       )
>     })
>
> Since the IO resource for the child is a host-specific address,
> special translation are required to retrieve the logical PIO address
> for that child.
>
> To overcome the problem of associating this logical PIO address
> with the child device, a scan handler is added to scan the ACPI
> namespace for known indirect IO hosts. This scan handler creates an
> MFD per child with the translated logical PIO address as it's IO
> resource, as a substitute for the normal platform device which ACPI
> would create during device enumeration.
>
> Signed-off-by: John Garry <john.garry@huawei.com>
> Signed-off-by: Zhichang Yuan <yuanzhichang@hisilicon.com>
> Signed-off-by: Gabriele Paoloni <gabriele.paoloni@huawei.com>

Just a few minor nits below.

> ---
>  drivers/acpi/arm64/Makefile          |   1 +
>  drivers/acpi/arm64/acpi_indirectio.c | 250 +++++++++++++++++++++++++++++++++++
>  drivers/acpi/internal.h              |   5 +
>  drivers/acpi/scan.c                  |   1 +
>  4 files changed, 257 insertions(+)
>  create mode 100644 drivers/acpi/arm64/acpi_indirectio.c
>
> diff --git a/drivers/acpi/arm64/Makefile b/drivers/acpi/arm64/Makefile
> index 1017def..f4a7f46 100644
> --- a/drivers/acpi/arm64/Makefile
> +++ b/drivers/acpi/arm64/Makefile
> @@ -1,2 +1,3 @@
>  obj-$(CONFIG_ACPI_IORT)        += iort.o
>  obj-$(CONFIG_ACPI_GTDT)        += gtdt.o
> +obj-$(CONFIG_INDIRECT_PIO)     += acpi_indirectio.o
> diff --git a/drivers/acpi/arm64/acpi_indirectio.c b/drivers/acpi/arm64/acpi_indirectio.c
> new file mode 100644
> index 0000000..51a1b92
> --- /dev/null
> +++ b/drivers/acpi/arm64/acpi_indirectio.c
> @@ -0,0 +1,250 @@
> +// SPDX-License-Identifier: GPL-2.0+
> +/*
> + * Copyright (C) 2017 HiSilicon Limited, All Rights Reserved.
> + * Author: Gabriele Paoloni <gabriele.paoloni@huawei.com>
> + * Author: Zhichang Yuan <yuanzhichang@hisilicon.com>
> + * Author: John Garry <john.garry@huawei.com>
> + *
> + * This file implements functunality to scan the ACPI namespace and config
> + * devices under "indirect IO" hosts. An "indirect IO" host allows child
> + * devices to use logical IO accesses when the host, itself, does not provide
> + * a transparent bridge. The device setup creates a per-child MFD with a
> + * logical port IO resource.
> + */
> +
> +#include <linux/acpi.h>
> +#include <linux/logic_pio.h>
> +#include <linux/mfd/core.h>
> +#include <linux/platform_device.h>
> +
> +ACPI_MODULE_NAME("indirect IO");
> +
> +#define ACPI_INDIRECT_IO_NAME_LEN 255
> +
> +struct acpi_indirect_io_mfd_cell {
> +       struct mfd_cell_acpi_match acpi_match;
> +       char name[ACPI_INDIRECT_IO_NAME_LEN];
> +       char pnpid[ACPI_INDIRECT_IO_NAME_LEN];
> +};
> +
> +static int acpi_indirect_io_xlat_res(struct acpi_device *adev,
> +                                    struct acpi_device *host,
> +                                    struct resource *res)
> +{
> +       unsigned long sys_port;
> +       resource_size_t len = res->end - res->start;
> +
> +       sys_port = logic_pio_trans_hwaddr(&host->fwnode, res->start, len);
> +       if (sys_port == -1UL)
> +               return -EFAULT;
> +
> +       res->start = sys_port;
> +       res->end = sys_port + len;
> +
> +       return 0;
> +}
> +
> +/*
> + * acpi_indirect_io_set_res - set the resources for a child device
> + * (MFD) of an "indirect IO" host.

The above should fit into a single line.

I'd make it something like "acpi_indirect_io_set_res - set "indirect
IO" host child (MFD) resources" and it already is explained in the
comment below.

> + * @child: the device node to be updated the I/O resource
> + * @hostdev: the device node associated with the "indirect IO" host
> + * @res: double pointer to be set to the address of translated resources
> + * @num_res: pointer to variable to hold the number of translated resources
> + *
> + * Returns 0 when successful, and a negative value for failure.
> + *
> + * For a given "indirect IO" host, each child device will have associated
> + * host-relevative address resource. This function will return the translated

host-relative

> + * logical PIO addresses for each child devices resources.
> + */
> +static int acpi_indirect_io_set_res(struct device *child,
> +                                   struct device *hostdev,
> +                                   const struct resource **res,
> +                                   int *num_res)
> +{
> +       struct acpi_device *adev;
> +       struct acpi_device *host;
> +       struct resource_entry *rentry;
> +       LIST_HEAD(resource_list);
> +       struct resource *resources;
> +       int count;
> +       int i;
> +       int ret = -EIO;
> +
> +       if (!child || !hostdev)
> +               return -EINVAL;
> +
> +       host = to_acpi_device(hostdev);
> +       adev = to_acpi_device(child);
> +
> +       /* check the device state */
> +       if (!adev->status.present) {
> +               dev_info(child, "device is not present\n");

dev_dbg()?

> +               return 0;
> +       }
> +       /* whether the child had been enumerated? */
> +       if (acpi_device_enumerated(adev)) {
> +               dev_info(child, "had been enumerated\n");

Again, dev_dbg()?

> +               return 0;
> +       }
> +
> +       count = acpi_dev_get_resources(adev, &resource_list, NULL, NULL);
> +       if (count <= 0) {
> +               dev_err(child, "failed to get resources\n");

I'd use dev_dbg() here too (the message may not even be meaningful to a user).

> +               return count ? count : -EIO;
> +       }
> +
> +       resources = kcalloc(count, sizeof(*resources), GFP_KERNEL);
> +       if (!resources) {

And you don't print anything here, I wonder why?

> +               acpi_dev_free_resource_list(&resource_list);
> +               return -ENOMEM;
> +       }
> +       count = 0;
> +       list_for_each_entry(rentry, &resource_list, node)
> +               resources[count++] = *rentry->res;
> +
> +       acpi_dev_free_resource_list(&resource_list);
> +
> +       /* translate the I/O resources */
> +       for (i = 0; i < count; i++) {
> +               if (!(resources[i].flags & IORESOURCE_IO))
> +                       continue;
> +               ret = acpi_indirect_io_xlat_res(adev, host, &resources[i]);
> +               if (ret) {
> +                       kfree(resources);
> +                       dev_err(child, "translate IO range failed(%d)\n", ret);
> +                       return ret;
> +               }
> +       }
> +       *res = resources;
> +       *num_res = count;
> +
> +       return ret;
> +}
> +
> +/*
> + * acpi_indirect_io_setup - scan handler for "indirect IO" host.
> + * @adev: "indirect IO" host ACPI device pointer

One extra empty comment line here, please.

> + * Returns 0 when successful, and a negative value for failure.
> + *
> + * Setup an "indirect IO" host by scanning all child devices, and
> + * create a per-device MFD with logical PIO translated IO resources.
> + */
> +static int acpi_indirect_io_setup(struct acpi_device *adev)
> +{
> +       struct platform_device *pdev;
> +       struct mfd_cell *mfd_cells;
> +       struct logic_pio_hwaddr *range;
> +       struct acpi_device *child;
> +       struct acpi_indirect_io_mfd_cell *acpi_indirect_io_mfd_cells;
> +       int size, ret, count = 0, cell_num = 0;
> +
> +       range = kzalloc(sizeof(*range), GFP_KERNEL);
> +       if (!range)
> +               return -ENOMEM;
> +       range->fwnode = &adev->fwnode;
> +       range->flags = PIO_INDIRECT;
> +       range->size = PIO_INDIRECT_SIZE;
> +
> +       ret = logic_pio_register_range(range);
> +       if (ret)
> +               goto free_range;
> +
> +       list_for_each_entry(child, &adev->children, node)
> +               cell_num++;
> +
> +       /* allocate the mfd cell and companion acpi info, one per child */
> +       size = sizeof(*mfd_cells) + sizeof(*acpi_indirect_io_mfd_cells);
> +       mfd_cells = kcalloc(cell_num, size, GFP_KERNEL);
> +       if (!mfd_cells) {
> +               ret = -ENOMEM;
> +               goto free_range;
> +       }
> +
> +       acpi_indirect_io_mfd_cells = (struct acpi_indirect_io_mfd_cell *)
> +                                       &mfd_cells[cell_num];
> +       /* Only consider the children of the host */
> +       list_for_each_entry(child, &adev->children, node) {
> +               struct mfd_cell *mfd_cell = &mfd_cells[count];
> +               struct acpi_indirect_io_mfd_cell *acpi_indirect_io_mfd_cell =
> +                                       &acpi_indirect_io_mfd_cells[count];
> +               const struct mfd_cell_acpi_match *acpi_match =
> +                                       &acpi_indirect_io_mfd_cell->acpi_match;
> +               char *name = &acpi_indirect_io_mfd_cell[count].name[0];
> +               char *pnpid = &acpi_indirect_io_mfd_cell[count].pnpid[0];
> +               struct mfd_cell_acpi_match match = {
> +                       .pnpid = pnpid,
> +               };
> +
> +               snprintf(name, ACPI_INDIRECT_IO_NAME_LEN, "indirect-io-%s",
> +                        acpi_device_hid(child));
> +               snprintf(pnpid, ACPI_INDIRECT_IO_NAME_LEN, "%s",
> +                        acpi_device_hid(child));
> +
> +               memcpy((void *)acpi_match, (void *)&match, sizeof(*acpi_match));
> +               mfd_cell->name = name;
> +               mfd_cell->acpi_match = acpi_match;
> +
> +               ret = acpi_indirect_io_set_res(&child->dev, &adev->dev,
> +                                              &mfd_cell->resources,
> +                                              &mfd_cell->num_resources);
> +               if (ret) {
> +                       dev_err(&child->dev, "set resource failed (%d)\n", ret);

Again, please consider using dev_dbg() here and below (for the same
reason as above).

> +                       goto free_mfd_resources;
> +               }
> +               count++;
> +       }
> +
> +       pdev = acpi_create_platform_device(adev, NULL);
> +       if (IS_ERR_OR_NULL(pdev)) {
> +               dev_err(&adev->dev, "create platform device for host failed\n");
> +               ret = PTR_ERR(pdev);
> +               goto free_mfd_resources;
> +       }
> +       acpi_device_set_enumerated(adev);
> +
> +       ret = mfd_add_devices(&pdev->dev, PLATFORM_DEVID_NONE,
> +                             mfd_cells, cell_num, NULL, 0, NULL);
> +       if (ret) {
> +               dev_err(&pdev->dev, "failed to add mfd cells (%d)\n", ret);
> +               goto free_mfd_resources;
> +       }
> +
> +       return ret;

return 0;

You know that ret must be 0 here anyway.

> +
> +free_mfd_resources:
> +       while (cell_num--)
> +               kfree(mfd_cells[cell_num].resources);
> +       kfree(mfd_cells);
> +free_range:
> +       kfree(range);
> +
> +       return ret;
> +}
> +
> +/* All the host devices which apply indirect-IO can be listed here. */
> +static const struct acpi_device_id acpi_indirect_io_host_id[] = {
> +       {}
> +};
> +
> +static int acpi_indirect_io_attach(struct acpi_device *adev,
> +                                  const struct acpi_device_id *id)
> +{
> +       int ret = acpi_indirect_io_setup(adev);
> +
> +       if (ret < 0)
> +               return ret;
> +
> +       return 1;

The above can be written as

return ret < 0 ? ret : 1;

to save a few lines of code (you are using this pattern above, so why
not here?).

> +}
> +
> +static struct acpi_scan_handler acpi_indirect_io_handler = {
> +       .ids = acpi_indirect_io_host_id,
> +       .attach = acpi_indirect_io_attach,
> +};
> +
> +void __init acpi_indirect_io_scan_init(void)
> +{
> +       acpi_scan_add_handler(&acpi_indirect_io_handler);
> +}
> diff --git a/drivers/acpi/internal.h b/drivers/acpi/internal.h
> index 1d0a501..680f3cf 100644
> --- a/drivers/acpi/internal.h
> +++ b/drivers/acpi/internal.h
> @@ -31,6 +31,11 @@
>  void acpi_platform_init(void);
>  void acpi_pnp_init(void);
>  void acpi_int340x_thermal_init(void);
> +#ifdef CONFIG_INDIRECT_PIO
> +void acpi_indirect_io_scan_init(void);
> +#else
> +static inline void acpi_indirect_io_scan_init(void) {}
> +#endif
>  #ifdef CONFIG_ARM_AMBA
>  void acpi_amba_init(void);
>  #else
> diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c
> index 8e63d93..204da8a 100644
> --- a/drivers/acpi/scan.c
> +++ b/drivers/acpi/scan.c
> @@ -2155,6 +2155,7 @@ int __init acpi_scan_init(void)
>         acpi_amba_init();
>         acpi_watchdog_init();
>         acpi_init_lpit();
> +       acpi_indirect_io_scan_init();
>
>         acpi_scan_add_handler(&generic_device_handler);
>
> --

But generally

Acked-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>

for the generic ACPI changes.

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

* Re: [PATCH v13 7/9] ACPI: Translate the I/O range of non-MMIO devices before scanning
@ 2018-02-14  9:21     ` Rafael J. Wysocki
  0 siblings, 0 replies; 66+ messages in thread
From: Rafael J. Wysocki @ 2018-02-14  9:21 UTC (permalink / raw)
  To: John Garry
  Cc: Mika Westerberg, Rafael J. Wysocki, Lorenzo Pieralisi,
	Rafael J. Wysocki, Hanjun Guo, Rob Herring, Bjorn Helgaas,
	Arnd Bergmann, Mark Rutland, Olof Johansson, Dann Frazier,
	Andy Shevchenko, Rob Herring, Joe Perches,
	Benjamin Herrenschmidt, Linux PCI, Linux Kernel Mailing List,
	ACPI Devel Maling List, Linuxarm, Corey Minyard, devicetree,
	linux-arch, Randy Dunlap

On Tue, Feb 13, 2018 at 6:45 PM, John Garry <john.garry@huawei.com> wrote:
> On some platforms (such as arm64-based hip06/hip07), access to legacy
> ISA/LPC devices through access IO space is required, similar to x86
> platforms. As the I/O for these devices are not memory mapped like
> PCI/PCIE MMIO host bridges, they require special low-level device
> operations through some host to generate IO accesses, i.e. a non-
> transparent bridge.
>
> Through the logical PIO framework, hosts are able to register address
> ranges in the logical PIO space for IO accesses. For hosts which require
> a LLDD to generate the IO accesses, through the logical PIO framework
> the host also registers accessors as a backend to generate the physical
> bus transactions for IO space accesses (called indirect IO).
>
> When describing the indirect IO child device in APCI tables, the IO
> resource is the host-specific address for the child (generally a
> bus address).
> An example is as follows:
>   Device (LPC0) {
>     Name (_HID, "HISI0191")  // HiSi LPC
>     Name (_CRS, ResourceTemplate () {
>       Memory32Fixed (ReadWrite, 0xa01b0000, 0x1000)
>     })
>   }
>
>   Device (LPC0.IPMI) {
>     Name (_HID, "IPI0001")
>     Name (LORS, ResourceTemplate() {
>       QWordIO (
>         ResourceConsumer,
>         MinNotFixed,     // _MIF
>         MaxNotFixed,     // _MAF
>         PosDecode,
>         EntireRange,
>         0x0,             // _GRA
>         0xe4,            // _MIN
>         0x3fff,          // _MAX
>         0x0,             // _TRA
>         0x04,            // _LEN
>         , ,
>         BTIO
>       )
>     })
>
> Since the IO resource for the child is a host-specific address,
> special translation are required to retrieve the logical PIO address
> for that child.
>
> To overcome the problem of associating this logical PIO address
> with the child device, a scan handler is added to scan the ACPI
> namespace for known indirect IO hosts. This scan handler creates an
> MFD per child with the translated logical PIO address as it's IO
> resource, as a substitute for the normal platform device which ACPI
> would create during device enumeration.
>
> Signed-off-by: John Garry <john.garry@huawei.com>
> Signed-off-by: Zhichang Yuan <yuanzhichang@hisilicon.com>
> Signed-off-by: Gabriele Paoloni <gabriele.paoloni@huawei.com>

Just a few minor nits below.

> ---
>  drivers/acpi/arm64/Makefile          |   1 +
>  drivers/acpi/arm64/acpi_indirectio.c | 250 +++++++++++++++++++++++++++++++++++
>  drivers/acpi/internal.h              |   5 +
>  drivers/acpi/scan.c                  |   1 +
>  4 files changed, 257 insertions(+)
>  create mode 100644 drivers/acpi/arm64/acpi_indirectio.c
>
> diff --git a/drivers/acpi/arm64/Makefile b/drivers/acpi/arm64/Makefile
> index 1017def..f4a7f46 100644
> --- a/drivers/acpi/arm64/Makefile
> +++ b/drivers/acpi/arm64/Makefile
> @@ -1,2 +1,3 @@
>  obj-$(CONFIG_ACPI_IORT)        += iort.o
>  obj-$(CONFIG_ACPI_GTDT)        += gtdt.o
> +obj-$(CONFIG_INDIRECT_PIO)     += acpi_indirectio.o
> diff --git a/drivers/acpi/arm64/acpi_indirectio.c b/drivers/acpi/arm64/acpi_indirectio.c
> new file mode 100644
> index 0000000..51a1b92
> --- /dev/null
> +++ b/drivers/acpi/arm64/acpi_indirectio.c
> @@ -0,0 +1,250 @@
> +// SPDX-License-Identifier: GPL-2.0+
> +/*
> + * Copyright (C) 2017 HiSilicon Limited, All Rights Reserved.
> + * Author: Gabriele Paoloni <gabriele.paoloni@huawei.com>
> + * Author: Zhichang Yuan <yuanzhichang@hisilicon.com>
> + * Author: John Garry <john.garry@huawei.com>
> + *
> + * This file implements functunality to scan the ACPI namespace and config
> + * devices under "indirect IO" hosts. An "indirect IO" host allows child
> + * devices to use logical IO accesses when the host, itself, does not provide
> + * a transparent bridge. The device setup creates a per-child MFD with a
> + * logical port IO resource.
> + */
> +
> +#include <linux/acpi.h>
> +#include <linux/logic_pio.h>
> +#include <linux/mfd/core.h>
> +#include <linux/platform_device.h>
> +
> +ACPI_MODULE_NAME("indirect IO");
> +
> +#define ACPI_INDIRECT_IO_NAME_LEN 255
> +
> +struct acpi_indirect_io_mfd_cell {
> +       struct mfd_cell_acpi_match acpi_match;
> +       char name[ACPI_INDIRECT_IO_NAME_LEN];
> +       char pnpid[ACPI_INDIRECT_IO_NAME_LEN];
> +};
> +
> +static int acpi_indirect_io_xlat_res(struct acpi_device *adev,
> +                                    struct acpi_device *host,
> +                                    struct resource *res)
> +{
> +       unsigned long sys_port;
> +       resource_size_t len = res->end - res->start;
> +
> +       sys_port = logic_pio_trans_hwaddr(&host->fwnode, res->start, len);
> +       if (sys_port == -1UL)
> +               return -EFAULT;
> +
> +       res->start = sys_port;
> +       res->end = sys_port + len;
> +
> +       return 0;
> +}
> +
> +/*
> + * acpi_indirect_io_set_res - set the resources for a child device
> + * (MFD) of an "indirect IO" host.

The above should fit into a single line.

I'd make it something like "acpi_indirect_io_set_res - set "indirect
IO" host child (MFD) resources" and it already is explained in the
comment below.

> + * @child: the device node to be updated the I/O resource
> + * @hostdev: the device node associated with the "indirect IO" host
> + * @res: double pointer to be set to the address of translated resources
> + * @num_res: pointer to variable to hold the number of translated resources
> + *
> + * Returns 0 when successful, and a negative value for failure.
> + *
> + * For a given "indirect IO" host, each child device will have associated
> + * host-relevative address resource. This function will return the translated

host-relative

> + * logical PIO addresses for each child devices resources.
> + */
> +static int acpi_indirect_io_set_res(struct device *child,
> +                                   struct device *hostdev,
> +                                   const struct resource **res,
> +                                   int *num_res)
> +{
> +       struct acpi_device *adev;
> +       struct acpi_device *host;
> +       struct resource_entry *rentry;
> +       LIST_HEAD(resource_list);
> +       struct resource *resources;
> +       int count;
> +       int i;
> +       int ret = -EIO;
> +
> +       if (!child || !hostdev)
> +               return -EINVAL;
> +
> +       host = to_acpi_device(hostdev);
> +       adev = to_acpi_device(child);
> +
> +       /* check the device state */
> +       if (!adev->status.present) {
> +               dev_info(child, "device is not present\n");

dev_dbg()?

> +               return 0;
> +       }
> +       /* whether the child had been enumerated? */
> +       if (acpi_device_enumerated(adev)) {
> +               dev_info(child, "had been enumerated\n");

Again, dev_dbg()?

> +               return 0;
> +       }
> +
> +       count = acpi_dev_get_resources(adev, &resource_list, NULL, NULL);
> +       if (count <= 0) {
> +               dev_err(child, "failed to get resources\n");

I'd use dev_dbg() here too (the message may not even be meaningful to a user).

> +               return count ? count : -EIO;
> +       }
> +
> +       resources = kcalloc(count, sizeof(*resources), GFP_KERNEL);
> +       if (!resources) {

And you don't print anything here, I wonder why?

> +               acpi_dev_free_resource_list(&resource_list);
> +               return -ENOMEM;
> +       }
> +       count = 0;
> +       list_for_each_entry(rentry, &resource_list, node)
> +               resources[count++] = *rentry->res;
> +
> +       acpi_dev_free_resource_list(&resource_list);
> +
> +       /* translate the I/O resources */
> +       for (i = 0; i < count; i++) {
> +               if (!(resources[i].flags & IORESOURCE_IO))
> +                       continue;
> +               ret = acpi_indirect_io_xlat_res(adev, host, &resources[i]);
> +               if (ret) {
> +                       kfree(resources);
> +                       dev_err(child, "translate IO range failed(%d)\n", ret);
> +                       return ret;
> +               }
> +       }
> +       *res = resources;
> +       *num_res = count;
> +
> +       return ret;
> +}
> +
> +/*
> + * acpi_indirect_io_setup - scan handler for "indirect IO" host.
> + * @adev: "indirect IO" host ACPI device pointer

One extra empty comment line here, please.

> + * Returns 0 when successful, and a negative value for failure.
> + *
> + * Setup an "indirect IO" host by scanning all child devices, and
> + * create a per-device MFD with logical PIO translated IO resources.
> + */
> +static int acpi_indirect_io_setup(struct acpi_device *adev)
> +{
> +       struct platform_device *pdev;
> +       struct mfd_cell *mfd_cells;
> +       struct logic_pio_hwaddr *range;
> +       struct acpi_device *child;
> +       struct acpi_indirect_io_mfd_cell *acpi_indirect_io_mfd_cells;
> +       int size, ret, count = 0, cell_num = 0;
> +
> +       range = kzalloc(sizeof(*range), GFP_KERNEL);
> +       if (!range)
> +               return -ENOMEM;
> +       range->fwnode = &adev->fwnode;
> +       range->flags = PIO_INDIRECT;
> +       range->size = PIO_INDIRECT_SIZE;
> +
> +       ret = logic_pio_register_range(range);
> +       if (ret)
> +               goto free_range;
> +
> +       list_for_each_entry(child, &adev->children, node)
> +               cell_num++;
> +
> +       /* allocate the mfd cell and companion acpi info, one per child */
> +       size = sizeof(*mfd_cells) + sizeof(*acpi_indirect_io_mfd_cells);
> +       mfd_cells = kcalloc(cell_num, size, GFP_KERNEL);
> +       if (!mfd_cells) {
> +               ret = -ENOMEM;
> +               goto free_range;
> +       }
> +
> +       acpi_indirect_io_mfd_cells = (struct acpi_indirect_io_mfd_cell *)
> +                                       &mfd_cells[cell_num];
> +       /* Only consider the children of the host */
> +       list_for_each_entry(child, &adev->children, node) {
> +               struct mfd_cell *mfd_cell = &mfd_cells[count];
> +               struct acpi_indirect_io_mfd_cell *acpi_indirect_io_mfd_cell =
> +                                       &acpi_indirect_io_mfd_cells[count];
> +               const struct mfd_cell_acpi_match *acpi_match =
> +                                       &acpi_indirect_io_mfd_cell->acpi_match;
> +               char *name = &acpi_indirect_io_mfd_cell[count].name[0];
> +               char *pnpid = &acpi_indirect_io_mfd_cell[count].pnpid[0];
> +               struct mfd_cell_acpi_match match = {
> +                       .pnpid = pnpid,
> +               };
> +
> +               snprintf(name, ACPI_INDIRECT_IO_NAME_LEN, "indirect-io-%s",
> +                        acpi_device_hid(child));
> +               snprintf(pnpid, ACPI_INDIRECT_IO_NAME_LEN, "%s",
> +                        acpi_device_hid(child));
> +
> +               memcpy((void *)acpi_match, (void *)&match, sizeof(*acpi_match));
> +               mfd_cell->name = name;
> +               mfd_cell->acpi_match = acpi_match;
> +
> +               ret = acpi_indirect_io_set_res(&child->dev, &adev->dev,
> +                                              &mfd_cell->resources,
> +                                              &mfd_cell->num_resources);
> +               if (ret) {
> +                       dev_err(&child->dev, "set resource failed (%d)\n", ret);

Again, please consider using dev_dbg() here and below (for the same
reason as above).

> +                       goto free_mfd_resources;
> +               }
> +               count++;
> +       }
> +
> +       pdev = acpi_create_platform_device(adev, NULL);
> +       if (IS_ERR_OR_NULL(pdev)) {
> +               dev_err(&adev->dev, "create platform device for host failed\n");
> +               ret = PTR_ERR(pdev);
> +               goto free_mfd_resources;
> +       }
> +       acpi_device_set_enumerated(adev);
> +
> +       ret = mfd_add_devices(&pdev->dev, PLATFORM_DEVID_NONE,
> +                             mfd_cells, cell_num, NULL, 0, NULL);
> +       if (ret) {
> +               dev_err(&pdev->dev, "failed to add mfd cells (%d)\n", ret);
> +               goto free_mfd_resources;
> +       }
> +
> +       return ret;

return 0;

You know that ret must be 0 here anyway.

> +
> +free_mfd_resources:
> +       while (cell_num--)
> +               kfree(mfd_cells[cell_num].resources);
> +       kfree(mfd_cells);
> +free_range:
> +       kfree(range);
> +
> +       return ret;
> +}
> +
> +/* All the host devices which apply indirect-IO can be listed here. */
> +static const struct acpi_device_id acpi_indirect_io_host_id[] = {
> +       {}
> +};
> +
> +static int acpi_indirect_io_attach(struct acpi_device *adev,
> +                                  const struct acpi_device_id *id)
> +{
> +       int ret = acpi_indirect_io_setup(adev);
> +
> +       if (ret < 0)
> +               return ret;
> +
> +       return 1;

The above can be written as

return ret < 0 ? ret : 1;

to save a few lines of code (you are using this pattern above, so why
not here?).

> +}
> +
> +static struct acpi_scan_handler acpi_indirect_io_handler = {
> +       .ids = acpi_indirect_io_host_id,
> +       .attach = acpi_indirect_io_attach,
> +};
> +
> +void __init acpi_indirect_io_scan_init(void)
> +{
> +       acpi_scan_add_handler(&acpi_indirect_io_handler);
> +}
> diff --git a/drivers/acpi/internal.h b/drivers/acpi/internal.h
> index 1d0a501..680f3cf 100644
> --- a/drivers/acpi/internal.h
> +++ b/drivers/acpi/internal.h
> @@ -31,6 +31,11 @@
>  void acpi_platform_init(void);
>  void acpi_pnp_init(void);
>  void acpi_int340x_thermal_init(void);
> +#ifdef CONFIG_INDIRECT_PIO
> +void acpi_indirect_io_scan_init(void);
> +#else
> +static inline void acpi_indirect_io_scan_init(void) {}
> +#endif
>  #ifdef CONFIG_ARM_AMBA
>  void acpi_amba_init(void);
>  #else
> diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c
> index 8e63d93..204da8a 100644
> --- a/drivers/acpi/scan.c
> +++ b/drivers/acpi/scan.c
> @@ -2155,6 +2155,7 @@ int __init acpi_scan_init(void)
>         acpi_amba_init();
>         acpi_watchdog_init();
>         acpi_init_lpit();
> +       acpi_indirect_io_scan_init();
>
>         acpi_scan_add_handler(&generic_device_handler);
>
> --

But generally

Acked-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>

for the generic ACPI changes.

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

* Re: [PATCH v13 7/9] ACPI: Translate the I/O range of non-MMIO devices before scanning
  2018-02-14  9:21     ` Rafael J. Wysocki
@ 2018-02-14 12:48       ` John Garry
  -1 siblings, 0 replies; 66+ messages in thread
From: John Garry @ 2018-02-14 12:48 UTC (permalink / raw)
  To: Rafael J. Wysocki
  Cc: Mika Westerberg, Lorenzo Pieralisi, Rafael J. Wysocki,
	Hanjun Guo, Rob Herring, Bjorn Helgaas, Arnd Bergmann,
	Mark Rutland, Olof Johansson, Dann Frazier, Andy Shevchenko,
	Rob Herring, Joe Perches, Benjamin Herrenschmidt, Linux PCI,
	Linux Kernel Mailing List, ACPI Devel Maling List, Linuxarm,
	Corey Minyard


>> Signed-off-by: John Garry <john.garry@huawei.com>
>> Signed-off-by: Zhichang Yuan <yuanzhichang@hisilicon.com>
>> Signed-off-by: Gabriele Paoloni <gabriele.paoloni@huawei.com>
>

Hi Rafael,

Thanks for checking again.

> Just a few minor nits below.
>
>> ---
>>  drivers/acpi/arm64/Makefile          |   1 +
>>  drivers/acpi/arm64/acpi_indirectio.c | 250 +++++++++++++++++++++++++++++++++++
>>  drivers/acpi/internal.h              |   5 +
>>  drivers/acpi/scan.c                  |   1 +
>>  4 files changed, 257 insertions(+)
>>  create mode 100644 drivers/acpi/arm64/acpi_indirectio.c
>>
>> diff --git a/drivers/acpi/arm64/Makefile b/drivers/acpi/arm64/Makefile
>> index 1017def..f4a7f46 100644
>> --- a/drivers/acpi/arm64/Makefile
>> +++ b/drivers/acpi/arm64/Makefile
>> @@ -1,2 +1,3 @@
>>  obj-$(CONFIG_ACPI_IORT)        += iort.o
>>  obj-$(CONFIG_ACPI_GTDT)        += gtdt.o
>> +obj-$(CONFIG_INDIRECT_PIO)     += acpi_indirectio.o
>> diff --git a/drivers/acpi/arm64/acpi_indirectio.c b/drivers/acpi/arm64/acpi_indirectio.c
>> new file mode 100644
>> index 0000000..51a1b92
>> --- /dev/null
>> +++ b/drivers/acpi/arm64/acpi_indirectio.c
>> @@ -0,0 +1,250 @@
>> +// SPDX-License-Identifier: GPL-2.0+
>> +/*
>> + * Copyright (C) 2017 HiSilicon Limited, All Rights Reserved.
>> + * Author: Gabriele Paoloni <gabriele.paoloni@huawei.com>
>> + * Author: Zhichang Yuan <yuanzhichang@hisilicon.com>
>> + * Author: John Garry <john.garry@huawei.com>
>> + *
>> + * This file implements functunality to scan the ACPI namespace and config
>> + * devices under "indirect IO" hosts. An "indirect IO" host allows child
>> + * devices to use logical IO accesses when the host, itself, does not provide
>> + * a transparent bridge. The device setup creates a per-child MFD with a
>> + * logical port IO resource.
>> + */
>> +
>> +#include <linux/acpi.h>
>> +#include <linux/logic_pio.h>
>> +#include <linux/mfd/core.h>
>> +#include <linux/platform_device.h>
>> +
>> +ACPI_MODULE_NAME("indirect IO");
>> +
>> +#define ACPI_INDIRECT_IO_NAME_LEN 255
>> +
>> +struct acpi_indirect_io_mfd_cell {
>> +       struct mfd_cell_acpi_match acpi_match;
>> +       char name[ACPI_INDIRECT_IO_NAME_LEN];
>> +       char pnpid[ACPI_INDIRECT_IO_NAME_LEN];
>> +};
>> +
>> +static int acpi_indirect_io_xlat_res(struct acpi_device *adev,
>> +                                    struct acpi_device *host,
>> +                                    struct resource *res)
>> +{
>> +       unsigned long sys_port;
>> +       resource_size_t len = res->end - res->start;
>> +
>> +       sys_port = logic_pio_trans_hwaddr(&host->fwnode, res->start, len);
>> +       if (sys_port == -1UL)
>> +               return -EFAULT;
>> +
>> +       res->start = sys_port;
>> +       res->end = sys_port + len;
>> +
>> +       return 0;
>> +}
>> +
>> +/*
>> + * acpi_indirect_io_set_res - set the resources for a child device
>> + * (MFD) of an "indirect IO" host.
>
> The above should fit into a single line.
>
> I'd make it something like "acpi_indirect_io_set_res - set "indirect
> IO" host child (MFD) resources" and it already is explained in the
> comment below.

Fine

>
>> + * @child: the device node to be updated the I/O resource
>> + * @hostdev: the device node associated with the "indirect IO" host
>> + * @res: double pointer to be set to the address of translated resources
>> + * @num_res: pointer to variable to hold the number of translated resources
>> + *
>> + * Returns 0 when successful, and a negative value for failure.
>> + *
>> + * For a given "indirect IO" host, each child device will have associated
>> + * host-relevative address resource. This function will return the translated
>
> host-relative

right

>
>> + * logical PIO addresses for each child devices resources.
>> + */
>> +static int acpi_indirect_io_set_res(struct device *child,
>> +                                   struct device *hostdev,
>> +                                   const struct resource **res,
>> +                                   int *num_res)
>> +{
>> +       struct acpi_device *adev;
>> +       struct acpi_device *host;
>> +       struct resource_entry *rentry;
>> +       LIST_HEAD(resource_list);
>> +       struct resource *resources;
>> +       int count;
>> +       int i;
>> +       int ret = -EIO;
>> +
>> +       if (!child || !hostdev)
>> +               return -EINVAL;
>> +
>> +       host = to_acpi_device(hostdev);
>> +       adev = to_acpi_device(child);
>> +
>> +       /* check the device state */
>> +       if (!adev->status.present) {
>> +               dev_info(child, "device is not present\n");
>
> dev_dbg()?
>
>> +               return 0;
>> +       }
>> +       /* whether the child had been enumerated? */
>> +       if (acpi_device_enumerated(adev)) {
>> +               dev_info(child, "had been enumerated\n");
>
> Again, dev_dbg()?

sure, I think that these can be downgraded

>
>> +               return 0;
>> +       }
>> +
>> +       count = acpi_dev_get_resources(adev, &resource_list, NULL, NULL);
>> +       if (count <= 0) {
>> +               dev_err(child, "failed to get resources\n");
>
> I'd use dev_dbg() here too (the message may not even be meaningful to a user).

I think that it could be ok - having no resources is not really an "error".

>
>> +               return count ? count : -EIO;
>> +       }
>> +
>> +       resources = kcalloc(count, sizeof(*resources), GFP_KERNEL);
>> +       if (!resources) {
>
> And you don't print anything here, I wonder why?

As I see, we generally don't print out-of-memory failure as we expect 
the alloc code to do it.

But I agree it's useful as we know the point of failure in the driver 
and can add diagnostic info like count value, as maybe we're trying to 
allocate a huge amount of memory.

>
>> +               acpi_dev_free_resource_list(&resource_list);
>> +               return -ENOMEM;
>> +       }
>> +       count = 0;
>> +       list_for_each_entry(rentry, &resource_list, node)
>> +               resources[count++] = *rentry->res;
>> +
>> +       acpi_dev_free_resource_list(&resource_list);
>> +
>> +       /* translate the I/O resources */
>> +       for (i = 0; i < count; i++) {
>> +               if (!(resources[i].flags & IORESOURCE_IO))
>> +                       continue;
>> +               ret = acpi_indirect_io_xlat_res(adev, host, &resources[i]);
>> +               if (ret) {
>> +                       kfree(resources);
>> +                       dev_err(child, "translate IO range failed(%d)\n", ret);
>> +                       return ret;
>> +               }
>> +       }
>> +       *res = resources;
>> +       *num_res = count;
>> +
>> +       return ret;
>> +}
>> +
>> +/*
>> + * acpi_indirect_io_setup - scan handler for "indirect IO" host.
>> + * @adev: "indirect IO" host ACPI device pointer
>
> One extra empty comment line here, please.

ok

>
>> + * Returns 0 when successful, and a negative value for failure.
>> + *
>> + * Setup an "indirect IO" host by scanning all child devices, and
>> + * create a per-device MFD with logical PIO translated IO resources.
>> + */
>> +static int acpi_indirect_io_setup(struct acpi_device *adev)
>> +{
>> +       struct platform_device *pdev;
>> +       struct mfd_cell *mfd_cells;
>> +       struct logic_pio_hwaddr *range;
>> +       struct acpi_device *child;
>> +       struct acpi_indirect_io_mfd_cell *acpi_indirect_io_mfd_cells;
>> +       int size, ret, count = 0, cell_num = 0;
>> +
>> +       range = kzalloc(sizeof(*range), GFP_KERNEL);
>> +       if (!range)
>> +               return -ENOMEM;
>> +       range->fwnode = &adev->fwnode;
>> +       range->flags = PIO_INDIRECT;
>> +       range->size = PIO_INDIRECT_SIZE;
>> +
>> +       ret = logic_pio_register_range(range);
>> +       if (ret)
>> +               goto free_range;
>> +
>> +       list_for_each_entry(child, &adev->children, node)
>> +               cell_num++;
>> +
>> +       /* allocate the mfd cell and companion acpi info, one per child */
>> +       size = sizeof(*mfd_cells) + sizeof(*acpi_indirect_io_mfd_cells);
>> +       mfd_cells = kcalloc(cell_num, size, GFP_KERNEL);
>> +       if (!mfd_cells) {
>> +               ret = -ENOMEM;
>> +               goto free_range;
>> +       }
>> +
>> +       acpi_indirect_io_mfd_cells = (struct acpi_indirect_io_mfd_cell *)
>> +                                       &mfd_cells[cell_num];
>> +       /* Only consider the children of the host */
>> +       list_for_each_entry(child, &adev->children, node) {
>> +               struct mfd_cell *mfd_cell = &mfd_cells[count];
>> +               struct acpi_indirect_io_mfd_cell *acpi_indirect_io_mfd_cell =
>> +                                       &acpi_indirect_io_mfd_cells[count];
>> +               const struct mfd_cell_acpi_match *acpi_match =
>> +                                       &acpi_indirect_io_mfd_cell->acpi_match;
>> +               char *name = &acpi_indirect_io_mfd_cell[count].name[0];
>> +               char *pnpid = &acpi_indirect_io_mfd_cell[count].pnpid[0];
>> +               struct mfd_cell_acpi_match match = {
>> +                       .pnpid = pnpid,
>> +               };
>> +
>> +               snprintf(name, ACPI_INDIRECT_IO_NAME_LEN, "indirect-io-%s",
>> +                        acpi_device_hid(child));
>> +               snprintf(pnpid, ACPI_INDIRECT_IO_NAME_LEN, "%s",
>> +                        acpi_device_hid(child));
>> +
>> +               memcpy((void *)acpi_match, (void *)&match, sizeof(*acpi_match));
>> +               mfd_cell->name = name;
>> +               mfd_cell->acpi_match = acpi_match;
>> +
>> +               ret = acpi_indirect_io_set_res(&child->dev, &adev->dev,
>> +                                              &mfd_cell->resources,
>> +                                              &mfd_cell->num_resources);
>> +               if (ret) {
>> +                       dev_err(&child->dev, "set resource failed (%d)\n", ret);
>
> Again, please consider using dev_dbg() here and below (for the same
> reason as above).
>

well if this happens the device is not enumerated, so I think a warn may 
be more appropiate

>> +                       goto free_mfd_resources;
>> +               }
>> +               count++;
>> +       }
>> +
>> +       pdev = acpi_create_platform_device(adev, NULL);
>> +       if (IS_ERR_OR_NULL(pdev)) {
>> +               dev_err(&adev->dev, "create platform device for host failed\n");

as above

>> +               ret = PTR_ERR(pdev);
>> +               goto free_mfd_resources;
>> +       }
>> +       acpi_device_set_enumerated(adev);
>> +
>> +       ret = mfd_add_devices(&pdev->dev, PLATFORM_DEVID_NONE,
>> +                             mfd_cells, cell_num, NULL, 0, NULL);
>> +       if (ret) {
>> +               dev_err(&pdev->dev, "failed to add mfd cells (%d)\n", ret);
>> +               goto free_mfd_resources;
>> +       }
>> +
>> +       return ret;
>
> return 0;
>
> You know that ret must be 0 here anyway.

yes, I prefer this

>
>> +
>> +free_mfd_resources:
>> +       while (cell_num--)
>> +               kfree(mfd_cells[cell_num].resources);
>> +       kfree(mfd_cells);
>> +free_range:
>> +       kfree(range);
>> +
>> +       return ret;
>> +}
>> +
>> +/* All the host devices which apply indirect-IO can be listed here. */
>> +static const struct acpi_device_id acpi_indirect_io_host_id[] = {
>> +       {}
>> +};
>> +
>> +static int acpi_indirect_io_attach(struct acpi_device *adev,
>> +                                  const struct acpi_device_id *id)
>> +{
>> +       int ret = acpi_indirect_io_setup(adev);
>> +
>> +       if (ret < 0)
>> +               return ret;
>> +
>> +       return 1;
>
> The above can be written as
>
> return ret < 0 ? ret : 1;
>
> to save a few lines of code (you are using this pattern above, so why
> not here?).

I can change it.

>
>> +}
>> +
>> +static struct acpi_scan_handler acpi_indirect_io_handler = {
>> +       .ids = acpi_indirect_io_host_id,
>> +       .attach = acpi_indirect_io_attach,
>> +};
>> +
>> +void __init acpi_indirect_io_scan_init(void)
>> +{
>> +       acpi_scan_add_handler(&acpi_indirect_io_handler);
>> +}
>> diff --git a/drivers/acpi/internal.h b/drivers/acpi/internal.h
>> index 1d0a501..680f3cf 100644
>> --- a/drivers/acpi/internal.h
>> +++ b/drivers/acpi/internal.h
>> @@ -31,6 +31,11 @@
>>  void acpi_platform_init(void);
>>  void acpi_pnp_init(void);
>>  void acpi_int340x_thermal_init(void);
>> +#ifdef CONFIG_INDIRECT_PIO
>> +void acpi_indirect_io_scan_init(void);
>> +#else
>> +static inline void acpi_indirect_io_scan_init(void) {}
>> +#endif
>>  #ifdef CONFIG_ARM_AMBA
>>  void acpi_amba_init(void);
>>  #else
>> diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c
>> index 8e63d93..204da8a 100644
>> --- a/drivers/acpi/scan.c
>> +++ b/drivers/acpi/scan.c
>> @@ -2155,6 +2155,7 @@ int __init acpi_scan_init(void)
>>         acpi_amba_init();
>>         acpi_watchdog_init();
>>         acpi_init_lpit();
>> +       acpi_indirect_io_scan_init();
>>
>>         acpi_scan_add_handler(&generic_device_handler);
>>
>> --
>
> But generally
>
> Acked-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
>
> for the generic ACPI changes.
>

Thanks,
John

> .
>



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

* Re: [PATCH v13 7/9] ACPI: Translate the I/O range of non-MMIO devices before scanning
@ 2018-02-14 12:48       ` John Garry
  0 siblings, 0 replies; 66+ messages in thread
From: John Garry @ 2018-02-14 12:48 UTC (permalink / raw)
  To: Rafael J. Wysocki
  Cc: Mika Westerberg, Lorenzo Pieralisi, Rafael J. Wysocki,
	Hanjun Guo, Rob Herring, Bjorn Helgaas, Arnd Bergmann,
	Mark Rutland, Olof Johansson, Dann Frazier, Andy Shevchenko,
	Rob Herring, Joe Perches, Benjamin Herrenschmidt, Linux PCI,
	Linux Kernel Mailing List, ACPI Devel Maling List, Linuxarm,
	Corey Minyard, devicetree, linux-arch, Randy Dunlap


>> Signed-off-by: John Garry <john.garry@huawei.com>
>> Signed-off-by: Zhichang Yuan <yuanzhichang@hisilicon.com>
>> Signed-off-by: Gabriele Paoloni <gabriele.paoloni@huawei.com>
>

Hi Rafael,

Thanks for checking again.

> Just a few minor nits below.
>
>> ---
>>  drivers/acpi/arm64/Makefile          |   1 +
>>  drivers/acpi/arm64/acpi_indirectio.c | 250 +++++++++++++++++++++++++++++++++++
>>  drivers/acpi/internal.h              |   5 +
>>  drivers/acpi/scan.c                  |   1 +
>>  4 files changed, 257 insertions(+)
>>  create mode 100644 drivers/acpi/arm64/acpi_indirectio.c
>>
>> diff --git a/drivers/acpi/arm64/Makefile b/drivers/acpi/arm64/Makefile
>> index 1017def..f4a7f46 100644
>> --- a/drivers/acpi/arm64/Makefile
>> +++ b/drivers/acpi/arm64/Makefile
>> @@ -1,2 +1,3 @@
>>  obj-$(CONFIG_ACPI_IORT)        += iort.o
>>  obj-$(CONFIG_ACPI_GTDT)        += gtdt.o
>> +obj-$(CONFIG_INDIRECT_PIO)     += acpi_indirectio.o
>> diff --git a/drivers/acpi/arm64/acpi_indirectio.c b/drivers/acpi/arm64/acpi_indirectio.c
>> new file mode 100644
>> index 0000000..51a1b92
>> --- /dev/null
>> +++ b/drivers/acpi/arm64/acpi_indirectio.c
>> @@ -0,0 +1,250 @@
>> +// SPDX-License-Identifier: GPL-2.0+
>> +/*
>> + * Copyright (C) 2017 HiSilicon Limited, All Rights Reserved.
>> + * Author: Gabriele Paoloni <gabriele.paoloni@huawei.com>
>> + * Author: Zhichang Yuan <yuanzhichang@hisilicon.com>
>> + * Author: John Garry <john.garry@huawei.com>
>> + *
>> + * This file implements functunality to scan the ACPI namespace and config
>> + * devices under "indirect IO" hosts. An "indirect IO" host allows child
>> + * devices to use logical IO accesses when the host, itself, does not provide
>> + * a transparent bridge. The device setup creates a per-child MFD with a
>> + * logical port IO resource.
>> + */
>> +
>> +#include <linux/acpi.h>
>> +#include <linux/logic_pio.h>
>> +#include <linux/mfd/core.h>
>> +#include <linux/platform_device.h>
>> +
>> +ACPI_MODULE_NAME("indirect IO");
>> +
>> +#define ACPI_INDIRECT_IO_NAME_LEN 255
>> +
>> +struct acpi_indirect_io_mfd_cell {
>> +       struct mfd_cell_acpi_match acpi_match;
>> +       char name[ACPI_INDIRECT_IO_NAME_LEN];
>> +       char pnpid[ACPI_INDIRECT_IO_NAME_LEN];
>> +};
>> +
>> +static int acpi_indirect_io_xlat_res(struct acpi_device *adev,
>> +                                    struct acpi_device *host,
>> +                                    struct resource *res)
>> +{
>> +       unsigned long sys_port;
>> +       resource_size_t len = res->end - res->start;
>> +
>> +       sys_port = logic_pio_trans_hwaddr(&host->fwnode, res->start, len);
>> +       if (sys_port == -1UL)
>> +               return -EFAULT;
>> +
>> +       res->start = sys_port;
>> +       res->end = sys_port + len;
>> +
>> +       return 0;
>> +}
>> +
>> +/*
>> + * acpi_indirect_io_set_res - set the resources for a child device
>> + * (MFD) of an "indirect IO" host.
>
> The above should fit into a single line.
>
> I'd make it something like "acpi_indirect_io_set_res - set "indirect
> IO" host child (MFD) resources" and it already is explained in the
> comment below.

Fine

>
>> + * @child: the device node to be updated the I/O resource
>> + * @hostdev: the device node associated with the "indirect IO" host
>> + * @res: double pointer to be set to the address of translated resources
>> + * @num_res: pointer to variable to hold the number of translated resources
>> + *
>> + * Returns 0 when successful, and a negative value for failure.
>> + *
>> + * For a given "indirect IO" host, each child device will have associated
>> + * host-relevative address resource. This function will return the translated
>
> host-relative

right

>
>> + * logical PIO addresses for each child devices resources.
>> + */
>> +static int acpi_indirect_io_set_res(struct device *child,
>> +                                   struct device *hostdev,
>> +                                   const struct resource **res,
>> +                                   int *num_res)
>> +{
>> +       struct acpi_device *adev;
>> +       struct acpi_device *host;
>> +       struct resource_entry *rentry;
>> +       LIST_HEAD(resource_list);
>> +       struct resource *resources;
>> +       int count;
>> +       int i;
>> +       int ret = -EIO;
>> +
>> +       if (!child || !hostdev)
>> +               return -EINVAL;
>> +
>> +       host = to_acpi_device(hostdev);
>> +       adev = to_acpi_device(child);
>> +
>> +       /* check the device state */
>> +       if (!adev->status.present) {
>> +               dev_info(child, "device is not present\n");
>
> dev_dbg()?
>
>> +               return 0;
>> +       }
>> +       /* whether the child had been enumerated? */
>> +       if (acpi_device_enumerated(adev)) {
>> +               dev_info(child, "had been enumerated\n");
>
> Again, dev_dbg()?

sure, I think that these can be downgraded

>
>> +               return 0;
>> +       }
>> +
>> +       count = acpi_dev_get_resources(adev, &resource_list, NULL, NULL);
>> +       if (count <= 0) {
>> +               dev_err(child, "failed to get resources\n");
>
> I'd use dev_dbg() here too (the message may not even be meaningful to a user).

I think that it could be ok - having no resources is not really an "error".

>
>> +               return count ? count : -EIO;
>> +       }
>> +
>> +       resources = kcalloc(count, sizeof(*resources), GFP_KERNEL);
>> +       if (!resources) {
>
> And you don't print anything here, I wonder why?

As I see, we generally don't print out-of-memory failure as we expect 
the alloc code to do it.

But I agree it's useful as we know the point of failure in the driver 
and can add diagnostic info like count value, as maybe we're trying to 
allocate a huge amount of memory.

>
>> +               acpi_dev_free_resource_list(&resource_list);
>> +               return -ENOMEM;
>> +       }
>> +       count = 0;
>> +       list_for_each_entry(rentry, &resource_list, node)
>> +               resources[count++] = *rentry->res;
>> +
>> +       acpi_dev_free_resource_list(&resource_list);
>> +
>> +       /* translate the I/O resources */
>> +       for (i = 0; i < count; i++) {
>> +               if (!(resources[i].flags & IORESOURCE_IO))
>> +                       continue;
>> +               ret = acpi_indirect_io_xlat_res(adev, host, &resources[i]);
>> +               if (ret) {
>> +                       kfree(resources);
>> +                       dev_err(child, "translate IO range failed(%d)\n", ret);
>> +                       return ret;
>> +               }
>> +       }
>> +       *res = resources;
>> +       *num_res = count;
>> +
>> +       return ret;
>> +}
>> +
>> +/*
>> + * acpi_indirect_io_setup - scan handler for "indirect IO" host.
>> + * @adev: "indirect IO" host ACPI device pointer
>
> One extra empty comment line here, please.

ok

>
>> + * Returns 0 when successful, and a negative value for failure.
>> + *
>> + * Setup an "indirect IO" host by scanning all child devices, and
>> + * create a per-device MFD with logical PIO translated IO resources.
>> + */
>> +static int acpi_indirect_io_setup(struct acpi_device *adev)
>> +{
>> +       struct platform_device *pdev;
>> +       struct mfd_cell *mfd_cells;
>> +       struct logic_pio_hwaddr *range;
>> +       struct acpi_device *child;
>> +       struct acpi_indirect_io_mfd_cell *acpi_indirect_io_mfd_cells;
>> +       int size, ret, count = 0, cell_num = 0;
>> +
>> +       range = kzalloc(sizeof(*range), GFP_KERNEL);
>> +       if (!range)
>> +               return -ENOMEM;
>> +       range->fwnode = &adev->fwnode;
>> +       range->flags = PIO_INDIRECT;
>> +       range->size = PIO_INDIRECT_SIZE;
>> +
>> +       ret = logic_pio_register_range(range);
>> +       if (ret)
>> +               goto free_range;
>> +
>> +       list_for_each_entry(child, &adev->children, node)
>> +               cell_num++;
>> +
>> +       /* allocate the mfd cell and companion acpi info, one per child */
>> +       size = sizeof(*mfd_cells) + sizeof(*acpi_indirect_io_mfd_cells);
>> +       mfd_cells = kcalloc(cell_num, size, GFP_KERNEL);
>> +       if (!mfd_cells) {
>> +               ret = -ENOMEM;
>> +               goto free_range;
>> +       }
>> +
>> +       acpi_indirect_io_mfd_cells = (struct acpi_indirect_io_mfd_cell *)
>> +                                       &mfd_cells[cell_num];
>> +       /* Only consider the children of the host */
>> +       list_for_each_entry(child, &adev->children, node) {
>> +               struct mfd_cell *mfd_cell = &mfd_cells[count];
>> +               struct acpi_indirect_io_mfd_cell *acpi_indirect_io_mfd_cell =
>> +                                       &acpi_indirect_io_mfd_cells[count];
>> +               const struct mfd_cell_acpi_match *acpi_match =
>> +                                       &acpi_indirect_io_mfd_cell->acpi_match;
>> +               char *name = &acpi_indirect_io_mfd_cell[count].name[0];
>> +               char *pnpid = &acpi_indirect_io_mfd_cell[count].pnpid[0];
>> +               struct mfd_cell_acpi_match match = {
>> +                       .pnpid = pnpid,
>> +               };
>> +
>> +               snprintf(name, ACPI_INDIRECT_IO_NAME_LEN, "indirect-io-%s",
>> +                        acpi_device_hid(child));
>> +               snprintf(pnpid, ACPI_INDIRECT_IO_NAME_LEN, "%s",
>> +                        acpi_device_hid(child));
>> +
>> +               memcpy((void *)acpi_match, (void *)&match, sizeof(*acpi_match));
>> +               mfd_cell->name = name;
>> +               mfd_cell->acpi_match = acpi_match;
>> +
>> +               ret = acpi_indirect_io_set_res(&child->dev, &adev->dev,
>> +                                              &mfd_cell->resources,
>> +                                              &mfd_cell->num_resources);
>> +               if (ret) {
>> +                       dev_err(&child->dev, "set resource failed (%d)\n", ret);
>
> Again, please consider using dev_dbg() here and below (for the same
> reason as above).
>

well if this happens the device is not enumerated, so I think a warn may 
be more appropiate

>> +                       goto free_mfd_resources;
>> +               }
>> +               count++;
>> +       }
>> +
>> +       pdev = acpi_create_platform_device(adev, NULL);
>> +       if (IS_ERR_OR_NULL(pdev)) {
>> +               dev_err(&adev->dev, "create platform device for host failed\n");

as above

>> +               ret = PTR_ERR(pdev);
>> +               goto free_mfd_resources;
>> +       }
>> +       acpi_device_set_enumerated(adev);
>> +
>> +       ret = mfd_add_devices(&pdev->dev, PLATFORM_DEVID_NONE,
>> +                             mfd_cells, cell_num, NULL, 0, NULL);
>> +       if (ret) {
>> +               dev_err(&pdev->dev, "failed to add mfd cells (%d)\n", ret);
>> +               goto free_mfd_resources;
>> +       }
>> +
>> +       return ret;
>
> return 0;
>
> You know that ret must be 0 here anyway.

yes, I prefer this

>
>> +
>> +free_mfd_resources:
>> +       while (cell_num--)
>> +               kfree(mfd_cells[cell_num].resources);
>> +       kfree(mfd_cells);
>> +free_range:
>> +       kfree(range);
>> +
>> +       return ret;
>> +}
>> +
>> +/* All the host devices which apply indirect-IO can be listed here. */
>> +static const struct acpi_device_id acpi_indirect_io_host_id[] = {
>> +       {}
>> +};
>> +
>> +static int acpi_indirect_io_attach(struct acpi_device *adev,
>> +                                  const struct acpi_device_id *id)
>> +{
>> +       int ret = acpi_indirect_io_setup(adev);
>> +
>> +       if (ret < 0)
>> +               return ret;
>> +
>> +       return 1;
>
> The above can be written as
>
> return ret < 0 ? ret : 1;
>
> to save a few lines of code (you are using this pattern above, so why
> not here?).

I can change it.

>
>> +}
>> +
>> +static struct acpi_scan_handler acpi_indirect_io_handler = {
>> +       .ids = acpi_indirect_io_host_id,
>> +       .attach = acpi_indirect_io_attach,
>> +};
>> +
>> +void __init acpi_indirect_io_scan_init(void)
>> +{
>> +       acpi_scan_add_handler(&acpi_indirect_io_handler);
>> +}
>> diff --git a/drivers/acpi/internal.h b/drivers/acpi/internal.h
>> index 1d0a501..680f3cf 100644
>> --- a/drivers/acpi/internal.h
>> +++ b/drivers/acpi/internal.h
>> @@ -31,6 +31,11 @@
>>  void acpi_platform_init(void);
>>  void acpi_pnp_init(void);
>>  void acpi_int340x_thermal_init(void);
>> +#ifdef CONFIG_INDIRECT_PIO
>> +void acpi_indirect_io_scan_init(void);
>> +#else
>> +static inline void acpi_indirect_io_scan_init(void) {}
>> +#endif
>>  #ifdef CONFIG_ARM_AMBA
>>  void acpi_amba_init(void);
>>  #else
>> diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c
>> index 8e63d93..204da8a 100644
>> --- a/drivers/acpi/scan.c
>> +++ b/drivers/acpi/scan.c
>> @@ -2155,6 +2155,7 @@ int __init acpi_scan_init(void)
>>         acpi_amba_init();
>>         acpi_watchdog_init();
>>         acpi_init_lpit();
>> +       acpi_indirect_io_scan_init();
>>
>>         acpi_scan_add_handler(&generic_device_handler);
>>
>> --
>
> But generally
>
> Acked-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
>
> for the generic ACPI changes.
>

Thanks,
John

> .
>

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

* Re: [PATCH v13 7/9] ACPI: Translate the I/O range of non-MMIO devices before scanning
  2018-02-13 17:45   ` John Garry
@ 2018-02-14 13:53     ` Andy Shevchenko
  -1 siblings, 0 replies; 66+ messages in thread
From: Andy Shevchenko @ 2018-02-14 13:53 UTC (permalink / raw)
  To: John Garry
  Cc: Mika Westerberg, Rafael J. Wysocki, Lorenzo Pieralisi,
	Rafael J. Wysocki, Hanjun Guo, Rob Herring, Bjorn Helgaas,
	Arnd Bergmann, Mark Rutland, Olof Johansson, Dann Frazier,
	Rob Herring, Joe Perches, Benjamin Herrenschmidt, linux-pci,
	Linux Kernel Mailing List, ACPI Devel Maling List, Linuxarm,
	Corey Minyard

On Tue, Feb 13, 2018 at 7:45 PM, John Garry <john.garry@huawei.com> wrote:
> On some platforms (such as arm64-based hip06/hip07), access to legacy
> ISA/LPC devices through access IO space is required, similar to x86
> platforms. As the I/O for these devices are not memory mapped like
> PCI/PCIE MMIO host bridges, they require special low-level device
> operations through some host to generate IO accesses, i.e. a non-
> transparent bridge.
>
> Through the logical PIO framework, hosts are able to register address
> ranges in the logical PIO space for IO accesses. For hosts which require
> a LLDD to generate the IO accesses, through the logical PIO framework
> the host also registers accessors as a backend to generate the physical
> bus transactions for IO space accesses (called indirect IO).
>
> When describing the indirect IO child device in APCI tables, the IO
> resource is the host-specific address for the child (generally a
> bus address).
> An example is as follows:
>   Device (LPC0) {
>     Name (_HID, "HISI0191")  // HiSi LPC
>     Name (_CRS, ResourceTemplate () {
>       Memory32Fixed (ReadWrite, 0xa01b0000, 0x1000)
>     })
>   }
>
>   Device (LPC0.IPMI) {
>     Name (_HID, "IPI0001")
>     Name (LORS, ResourceTemplate() {
>       QWordIO (
>         ResourceConsumer,
>         MinNotFixed,     // _MIF
>         MaxNotFixed,     // _MAF
>         PosDecode,
>         EntireRange,
>         0x0,             // _GRA
>         0xe4,            // _MIN
>         0x3fff,          // _MAX
>         0x0,             // _TRA
>         0x04,            // _LEN
>         , ,
>         BTIO
>       )
>     })
>
> Since the IO resource for the child is a host-specific address,
> special translation are required to retrieve the logical PIO address
> for that child.
>
> To overcome the problem of associating this logical PIO address
> with the child device, a scan handler is added to scan the ACPI
> namespace for known indirect IO hosts. This scan handler creates an
> MFD per child with the translated logical PIO address as it's IO
> resource, as a substitute for the normal platform device which ACPI
> would create during device enumeration.

> +       unsigned long sys_port;

> +       sys_port = logic_pio_trans_hwaddr(&host->fwnode, res->start, len);
> +       if (sys_port == -1UL)

Wouldn't it be better to compare with ULONG_MAX?

> +               return -EFAULT;


> +/*

Shouldn't be a kernel-doc?

> + * acpi_indirect_io_set_res - set the resources for a child device
> + * (MFD) of an "indirect IO" host.

In that case this would be one line w/o period at the end.

> + * @child: the device node to be updated the I/O resource
> + * @hostdev: the device node associated with the "indirect IO" host
> + * @res: double pointer to be set to the address of translated resources
> + * @num_res: pointer to variable to hold the number of translated resources
> + *
> + * Returns 0 when successful, and a negative value for failure.
> + *
> + * For a given "indirect IO" host, each child device will have associated
> + * host-relevative address resource. This function will return the translated
> + * logical PIO addresses for each child devices resources.
> + */
> +static int acpi_indirect_io_set_res(struct device *child,
> +                                   struct device *hostdev,
> +                                   const struct resource **res,
> +                                   int *num_res)
> +{
> +       struct acpi_device *adev;
> +       struct acpi_device *host;
> +       struct resource_entry *rentry;
> +       LIST_HEAD(resource_list);
> +       struct resource *resources;
> +       int count;
> +       int i;
> +       int ret = -EIO;
> +
> +       if (!child || !hostdev)
> +               return -EINVAL;
> +
> +       host = to_acpi_device(hostdev);
> +       adev = to_acpi_device(child);

> +       count = acpi_dev_get_resources(adev, &resource_list, NULL, NULL);
> +       if (count <= 0) {
> +               dev_err(child, "failed to get resources\n");
> +               return count ? count : -EIO;
> +       }
> +
> +       resources = kcalloc(count, sizeof(*resources), GFP_KERNEL);
> +       if (!resources) {
> +               acpi_dev_free_resource_list(&resource_list);
> +               return -ENOMEM;
> +       }
> +       count = 0;
> +       list_for_each_entry(rentry, &resource_list, node)
> +               resources[count++] = *rentry->res;
> +
> +       acpi_dev_free_resource_list(&resource_list);

It has similarities with acpi_create_platform_device().
I guess we can utilize existing code.

> +       /* translate the I/O resources */
> +       for (i = 0; i < count; i++) {
> +               if (!(resources[i].flags & IORESOURCE_IO))
> +                       continue;

> +               ret = acpi_indirect_io_xlat_res(adev, host, &resources[i]);
> +               if (ret) {
> +                       kfree(resources);
> +                       dev_err(child, "translate IO range failed(%d)\n", ret);
> +                       return ret;
> +               }
> +       }
> +       *res = resources;
> +       *num_res = count;
> +
> +       return ret;

Perhaps,

   ret = ...
   if (ret)
    break;
  }

  if (ret) {
                       kfree(resources);
                       dev_err(child, "translate IO range failed(%d)\n", ret);
                       return ret;
  }

  *res = resources;
  *num_res = count;
  return 0;

?

> +}
> +
> +/*
> + * acpi_indirect_io_setup - scan handler for "indirect IO" host.
> + * @adev: "indirect IO" host ACPI device pointer
> + * Returns 0 when successful, and a negative value for failure.
> + *
> + * Setup an "indirect IO" host by scanning all child devices, and
> + * create a per-device MFD with logical PIO translated IO resources.
> + */
> +static int acpi_indirect_io_setup(struct acpi_device *adev)
> +{
> +       struct platform_device *pdev;
> +       struct mfd_cell *mfd_cells;
> +       struct logic_pio_hwaddr *range;
> +       struct acpi_device *child;
> +       struct acpi_indirect_io_mfd_cell *acpi_indirect_io_mfd_cells;
> +       int size, ret, count = 0, cell_num = 0;
> +
> +       range = kzalloc(sizeof(*range), GFP_KERNEL);
> +       if (!range)
> +               return -ENOMEM;
> +       range->fwnode = &adev->fwnode;
> +       range->flags = PIO_INDIRECT;
> +       range->size = PIO_INDIRECT_SIZE;
> +
> +       ret = logic_pio_register_range(range);
> +       if (ret)
> +               goto free_range;
> +
> +       list_for_each_entry(child, &adev->children, node)
> +               cell_num++;
> +
> +       /* allocate the mfd cell and companion acpi info, one per child */
> +       size = sizeof(*mfd_cells) + sizeof(*acpi_indirect_io_mfd_cells);
> +       mfd_cells = kcalloc(cell_num, size, GFP_KERNEL);
> +       if (!mfd_cells) {
> +               ret = -ENOMEM;
> +               goto free_range;
> +       }
> +
> +       acpi_indirect_io_mfd_cells = (struct acpi_indirect_io_mfd_cell *)
> +                                       &mfd_cells[cell_num];
> +       /* Only consider the children of the host */
> +       list_for_each_entry(child, &adev->children, node) {
> +               struct mfd_cell *mfd_cell = &mfd_cells[count];
> +               struct acpi_indirect_io_mfd_cell *acpi_indirect_io_mfd_cell =
> +                                       &acpi_indirect_io_mfd_cells[count];
> +               const struct mfd_cell_acpi_match *acpi_match =
> +                                       &acpi_indirect_io_mfd_cell->acpi_match;

> +               char *name = &acpi_indirect_io_mfd_cell[count].name[0];
> +               char *pnpid = &acpi_indirect_io_mfd_cell[count].pnpid[0];

Plain x is equivalent to &x[0].

> +               struct mfd_cell_acpi_match match = {
> +                       .pnpid = pnpid,
> +               };
> +
> +               snprintf(name, ACPI_INDIRECT_IO_NAME_LEN, "indirect-io-%s",
> +                        acpi_device_hid(child));
> +               snprintf(pnpid, ACPI_INDIRECT_IO_NAME_LEN, "%s",
> +                        acpi_device_hid(child))

> +               memcpy((void *)acpi_match, (void *)&match, sizeof(*acpi_match));

Casting to void * is pointless. In both cases.

> +               mfd_cell->name = name;
> +               mfd_cell->acpi_match = acpi_match;
> +
> +               ret = acpi_indirect_io_set_res(&child->dev, &adev->dev,
> +                                              &mfd_cell->resources,
> +                                              &mfd_cell->num_resources);
> +               if (ret) {
> +                       dev_err(&child->dev, "set resource failed (%d)\n", ret);
> +                       goto free_mfd_resources;
> +               }
> +               count++;
> +       }
> +
> +       pdev = acpi_create_platform_device(adev, NULL);
> +       if (IS_ERR_OR_NULL(pdev)) {
> +               dev_err(&adev->dev, "create platform device for host failed\n");

> +               ret = PTR_ERR(pdev);

So, NULL case will return 0. Is it expected?

> +               goto free_mfd_resources;
> +       }
> +       acpi_device_set_enumerated(adev);
> +
> +       ret = mfd_add_devices(&pdev->dev, PLATFORM_DEVID_NONE,
> +                             mfd_cells, cell_num, NULL, 0, NULL);
> +       if (ret) {
> +               dev_err(&pdev->dev, "failed to add mfd cells (%d)\n", ret);
> +               goto free_mfd_resources;
> +       }
> +
> +       return ret;
> +
> +free_mfd_resources:
> +       while (cell_num--)
> +               kfree(mfd_cells[cell_num].resources);
> +       kfree(mfd_cells);
> +free_range:
> +       kfree(range);
> +
> +       return ret;
> +}

One question, what a scope of use of this function? Is it ->probe() time?
If it's so, can we use devm_* variants?

-- 
With Best Regards,
Andy Shevchenko

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

* Re: [PATCH v13 7/9] ACPI: Translate the I/O range of non-MMIO devices before scanning
@ 2018-02-14 13:53     ` Andy Shevchenko
  0 siblings, 0 replies; 66+ messages in thread
From: Andy Shevchenko @ 2018-02-14 13:53 UTC (permalink / raw)
  To: John Garry
  Cc: Mika Westerberg, Rafael J. Wysocki, Lorenzo Pieralisi,
	Rafael J. Wysocki, Hanjun Guo, Rob Herring, Bjorn Helgaas,
	Arnd Bergmann, Mark Rutland, Olof Johansson, Dann Frazier,
	Rob Herring, Joe Perches, Benjamin Herrenschmidt, linux-pci,
	Linux Kernel Mailing List, ACPI Devel Maling List, Linuxarm,
	Corey Minyard, devicetree, Linux-Arch, Randy Dunlap

On Tue, Feb 13, 2018 at 7:45 PM, John Garry <john.garry@huawei.com> wrote:
> On some platforms (such as arm64-based hip06/hip07), access to legacy
> ISA/LPC devices through access IO space is required, similar to x86
> platforms. As the I/O for these devices are not memory mapped like
> PCI/PCIE MMIO host bridges, they require special low-level device
> operations through some host to generate IO accesses, i.e. a non-
> transparent bridge.
>
> Through the logical PIO framework, hosts are able to register address
> ranges in the logical PIO space for IO accesses. For hosts which require
> a LLDD to generate the IO accesses, through the logical PIO framework
> the host also registers accessors as a backend to generate the physical
> bus transactions for IO space accesses (called indirect IO).
>
> When describing the indirect IO child device in APCI tables, the IO
> resource is the host-specific address for the child (generally a
> bus address).
> An example is as follows:
>   Device (LPC0) {
>     Name (_HID, "HISI0191")  // HiSi LPC
>     Name (_CRS, ResourceTemplate () {
>       Memory32Fixed (ReadWrite, 0xa01b0000, 0x1000)
>     })
>   }
>
>   Device (LPC0.IPMI) {
>     Name (_HID, "IPI0001")
>     Name (LORS, ResourceTemplate() {
>       QWordIO (
>         ResourceConsumer,
>         MinNotFixed,     // _MIF
>         MaxNotFixed,     // _MAF
>         PosDecode,
>         EntireRange,
>         0x0,             // _GRA
>         0xe4,            // _MIN
>         0x3fff,          // _MAX
>         0x0,             // _TRA
>         0x04,            // _LEN
>         , ,
>         BTIO
>       )
>     })
>
> Since the IO resource for the child is a host-specific address,
> special translation are required to retrieve the logical PIO address
> for that child.
>
> To overcome the problem of associating this logical PIO address
> with the child device, a scan handler is added to scan the ACPI
> namespace for known indirect IO hosts. This scan handler creates an
> MFD per child with the translated logical PIO address as it's IO
> resource, as a substitute for the normal platform device which ACPI
> would create during device enumeration.

> +       unsigned long sys_port;

> +       sys_port = logic_pio_trans_hwaddr(&host->fwnode, res->start, len);
> +       if (sys_port == -1UL)

Wouldn't it be better to compare with ULONG_MAX?

> +               return -EFAULT;


> +/*

Shouldn't be a kernel-doc?

> + * acpi_indirect_io_set_res - set the resources for a child device
> + * (MFD) of an "indirect IO" host.

In that case this would be one line w/o period at the end.

> + * @child: the device node to be updated the I/O resource
> + * @hostdev: the device node associated with the "indirect IO" host
> + * @res: double pointer to be set to the address of translated resources
> + * @num_res: pointer to variable to hold the number of translated resources
> + *
> + * Returns 0 when successful, and a negative value for failure.
> + *
> + * For a given "indirect IO" host, each child device will have associated
> + * host-relevative address resource. This function will return the translated
> + * logical PIO addresses for each child devices resources.
> + */
> +static int acpi_indirect_io_set_res(struct device *child,
> +                                   struct device *hostdev,
> +                                   const struct resource **res,
> +                                   int *num_res)
> +{
> +       struct acpi_device *adev;
> +       struct acpi_device *host;
> +       struct resource_entry *rentry;
> +       LIST_HEAD(resource_list);
> +       struct resource *resources;
> +       int count;
> +       int i;
> +       int ret = -EIO;
> +
> +       if (!child || !hostdev)
> +               return -EINVAL;
> +
> +       host = to_acpi_device(hostdev);
> +       adev = to_acpi_device(child);

> +       count = acpi_dev_get_resources(adev, &resource_list, NULL, NULL);
> +       if (count <= 0) {
> +               dev_err(child, "failed to get resources\n");
> +               return count ? count : -EIO;
> +       }
> +
> +       resources = kcalloc(count, sizeof(*resources), GFP_KERNEL);
> +       if (!resources) {
> +               acpi_dev_free_resource_list(&resource_list);
> +               return -ENOMEM;
> +       }
> +       count = 0;
> +       list_for_each_entry(rentry, &resource_list, node)
> +               resources[count++] = *rentry->res;
> +
> +       acpi_dev_free_resource_list(&resource_list);

It has similarities with acpi_create_platform_device().
I guess we can utilize existing code.

> +       /* translate the I/O resources */
> +       for (i = 0; i < count; i++) {
> +               if (!(resources[i].flags & IORESOURCE_IO))
> +                       continue;

> +               ret = acpi_indirect_io_xlat_res(adev, host, &resources[i]);
> +               if (ret) {
> +                       kfree(resources);
> +                       dev_err(child, "translate IO range failed(%d)\n", ret);
> +                       return ret;
> +               }
> +       }
> +       *res = resources;
> +       *num_res = count;
> +
> +       return ret;

Perhaps,

   ret = ...
   if (ret)
    break;
  }

  if (ret) {
                       kfree(resources);
                       dev_err(child, "translate IO range failed(%d)\n", ret);
                       return ret;
  }

  *res = resources;
  *num_res = count;
  return 0;

?

> +}
> +
> +/*
> + * acpi_indirect_io_setup - scan handler for "indirect IO" host.
> + * @adev: "indirect IO" host ACPI device pointer
> + * Returns 0 when successful, and a negative value for failure.
> + *
> + * Setup an "indirect IO" host by scanning all child devices, and
> + * create a per-device MFD with logical PIO translated IO resources.
> + */
> +static int acpi_indirect_io_setup(struct acpi_device *adev)
> +{
> +       struct platform_device *pdev;
> +       struct mfd_cell *mfd_cells;
> +       struct logic_pio_hwaddr *range;
> +       struct acpi_device *child;
> +       struct acpi_indirect_io_mfd_cell *acpi_indirect_io_mfd_cells;
> +       int size, ret, count = 0, cell_num = 0;
> +
> +       range = kzalloc(sizeof(*range), GFP_KERNEL);
> +       if (!range)
> +               return -ENOMEM;
> +       range->fwnode = &adev->fwnode;
> +       range->flags = PIO_INDIRECT;
> +       range->size = PIO_INDIRECT_SIZE;
> +
> +       ret = logic_pio_register_range(range);
> +       if (ret)
> +               goto free_range;
> +
> +       list_for_each_entry(child, &adev->children, node)
> +               cell_num++;
> +
> +       /* allocate the mfd cell and companion acpi info, one per child */
> +       size = sizeof(*mfd_cells) + sizeof(*acpi_indirect_io_mfd_cells);
> +       mfd_cells = kcalloc(cell_num, size, GFP_KERNEL);
> +       if (!mfd_cells) {
> +               ret = -ENOMEM;
> +               goto free_range;
> +       }
> +
> +       acpi_indirect_io_mfd_cells = (struct acpi_indirect_io_mfd_cell *)
> +                                       &mfd_cells[cell_num];
> +       /* Only consider the children of the host */
> +       list_for_each_entry(child, &adev->children, node) {
> +               struct mfd_cell *mfd_cell = &mfd_cells[count];
> +               struct acpi_indirect_io_mfd_cell *acpi_indirect_io_mfd_cell =
> +                                       &acpi_indirect_io_mfd_cells[count];
> +               const struct mfd_cell_acpi_match *acpi_match =
> +                                       &acpi_indirect_io_mfd_cell->acpi_match;

> +               char *name = &acpi_indirect_io_mfd_cell[count].name[0];
> +               char *pnpid = &acpi_indirect_io_mfd_cell[count].pnpid[0];

Plain x is equivalent to &x[0].

> +               struct mfd_cell_acpi_match match = {
> +                       .pnpid = pnpid,
> +               };
> +
> +               snprintf(name, ACPI_INDIRECT_IO_NAME_LEN, "indirect-io-%s",
> +                        acpi_device_hid(child));
> +               snprintf(pnpid, ACPI_INDIRECT_IO_NAME_LEN, "%s",
> +                        acpi_device_hid(child))

> +               memcpy((void *)acpi_match, (void *)&match, sizeof(*acpi_match));

Casting to void * is pointless. In both cases.

> +               mfd_cell->name = name;
> +               mfd_cell->acpi_match = acpi_match;
> +
> +               ret = acpi_indirect_io_set_res(&child->dev, &adev->dev,
> +                                              &mfd_cell->resources,
> +                                              &mfd_cell->num_resources);
> +               if (ret) {
> +                       dev_err(&child->dev, "set resource failed (%d)\n", ret);
> +                       goto free_mfd_resources;
> +               }
> +               count++;
> +       }
> +
> +       pdev = acpi_create_platform_device(adev, NULL);
> +       if (IS_ERR_OR_NULL(pdev)) {
> +               dev_err(&adev->dev, "create platform device for host failed\n");

> +               ret = PTR_ERR(pdev);

So, NULL case will return 0. Is it expected?

> +               goto free_mfd_resources;
> +       }
> +       acpi_device_set_enumerated(adev);
> +
> +       ret = mfd_add_devices(&pdev->dev, PLATFORM_DEVID_NONE,
> +                             mfd_cells, cell_num, NULL, 0, NULL);
> +       if (ret) {
> +               dev_err(&pdev->dev, "failed to add mfd cells (%d)\n", ret);
> +               goto free_mfd_resources;
> +       }
> +
> +       return ret;
> +
> +free_mfd_resources:
> +       while (cell_num--)
> +               kfree(mfd_cells[cell_num].resources);
> +       kfree(mfd_cells);
> +free_range:
> +       kfree(range);
> +
> +       return ret;
> +}

One question, what a scope of use of this function? Is it ->probe() time?
If it's so, can we use devm_* variants?

-- 
With Best Regards,
Andy Shevchenko

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

* Re: [PATCH v13 7/9] ACPI: Translate the I/O range of non-MMIO devices before scanning
  2018-02-14 13:53     ` Andy Shevchenko
  (?)
@ 2018-02-14 15:33       ` John Garry
  -1 siblings, 0 replies; 66+ messages in thread
From: John Garry @ 2018-02-14 15:33 UTC (permalink / raw)
  To: Andy Shevchenko
  Cc: Mika Westerberg, Rafael J. Wysocki, Lorenzo Pieralisi,
	Rafael J. Wysocki, Hanjun Guo, Rob Herring, Bjorn Helgaas,
	Arnd Bergmann, Mark Rutland, Olof Johansson, Dann Frazier,
	Rob Herring, Joe Perches, Benjamin Herrenschmidt, linux-pci,
	Linux Kernel Mailing List, ACPI Devel Maling List, Linuxarm,
	Corey Minyard, devic

On 14/02/2018 13:53, Andy Shevchenko wrote:
> On Tue, Feb 13, 2018 at 7:45 PM, John Garry <john.garry@huawei.com> wrote:
>> On some platforms (such as arm64-based hip06/hip07), access to legacy
>> ISA/LPC devices through access IO space is required, similar to x86
>> platforms. As the I/O for these devices are not memory mapped like
>> PCI/PCIE MMIO host bridges, they require special low-level device
>> operations through some host to generate IO accesses, i.e. a non-
>> transparent bridge.
>>
>> Through the logical PIO framework, hosts are able to register address
>> ranges in the logical PIO space for IO accesses. For hosts which require
>> a LLDD to generate the IO accesses, through the logical PIO framework
>> the host also registers accessors as a backend to generate the physical
>> bus transactions for IO space accesses (called indirect IO).
>>
>> When describing the indirect IO child device in APCI tables, the IO
>> resource is the host-specific address for the child (generally a
>> bus address).
>> An example is as follows:
>>   Device (LPC0) {
>>     Name (_HID, "HISI0191")  // HiSi LPC
>>     Name (_CRS, ResourceTemplate () {
>>       Memory32Fixed (ReadWrite, 0xa01b0000, 0x1000)
>>     })
>>   }
>>
>>   Device (LPC0.IPMI) {
>>     Name (_HID, "IPI0001")
>>     Name (LORS, ResourceTemplate() {
>>       QWordIO (
>>         ResourceConsumer,
>>         MinNotFixed,     // _MIF
>>         MaxNotFixed,     // _MAF
>>         PosDecode,
>>         EntireRange,
>>         0x0,             // _GRA
>>         0xe4,            // _MIN
>>         0x3fff,          // _MAX
>>         0x0,             // _TRA
>>         0x04,            // _LEN
>>         , ,
>>         BTIO
>>       )
>>     })
>>
>> Since the IO resource for the child is a host-specific address,
>> special translation are required to retrieve the logical PIO address
>> for that child.
>>
>> To overcome the problem of associating this logical PIO address
>> with the child device, a scan handler is added to scan the ACPI
>> namespace for known indirect IO hosts. This scan handler creates an
>> MFD per child with the translated logical PIO address as it's IO
>> resource, as a substitute for the normal platform device which ACPI
>> would create during device enumeration.
>

Hi Andy,

>> +       unsigned long sys_port;
>
>> +       sys_port = logic_pio_trans_hwaddr(&host->fwnode, res->start, len);
>> +       if (sys_port == -1UL)
>
> Wouldn't it be better to compare with ULONG_MAX?

Could do, being the same thing. Maybe people prefer -1UL as it saves 
having to figure out what ULONG_MAX is :)

>
>> +               return -EFAULT;
>
>
>> +/*
>
> Shouldn't be a kernel-doc?

Right, I'll make it /**

>
>> + * acpi_indirect_io_set_res - set the resources for a child device
>> + * (MFD) of an "indirect IO" host.
>
> In that case this would be one line w/o period at the end.
>
>> + * @child: the device node to be updated the I/O resource
>> + * @hostdev: the device node associated with the "indirect IO" host
>> + * @res: double pointer to be set to the address of translated resources
>> + * @num_res: pointer to variable to hold the number of translated resources
>> + *
>> + * Returns 0 when successful, and a negative value for failure.
>> + *
>> + * For a given "indirect IO" host, each child device will have associated
>> + * host-relevative address resource. This function will return the translated
>> + * logical PIO addresses for each child devices resources.
>> + */
>> +static int acpi_indirect_io_set_res(struct device *child,
>> +                                   struct device *hostdev,
>> +                                   const struct resource **res,
>> +                                   int *num_res)
>> +{
>> +       struct acpi_device *adev;
>> +       struct acpi_device *host;
>> +       struct resource_entry *rentry;
>> +       LIST_HEAD(resource_list);
>> +       struct resource *resources;
>> +       int count;
>> +       int i;
>> +       int ret = -EIO;
>> +
>> +       if (!child || !hostdev)
>> +               return -EINVAL;
>> +
>> +       host = to_acpi_device(hostdev);
>> +       adev = to_acpi_device(child);
>

***

>> +       count = acpi_dev_get_resources(adev, &resource_list, NULL, NULL);
>> +       if (count <= 0) {
>> +               dev_err(child, "failed to get resources\n");
>> +               return count ? count : -EIO;
>> +       }
>> +
>> +       resources = kcalloc(count, sizeof(*resources), GFP_KERNEL);
>> +       if (!resources) {
>> +               acpi_dev_free_resource_list(&resource_list);
>> +               return -ENOMEM;
>> +       }
>> +       count = 0;
>> +       list_for_each_entry(rentry, &resource_list, node)
>> +               resources[count++] = *rentry->res;
>> +
>> +       acpi_dev_free_resource_list(&resource_list);
>
> It has similarities with acpi_create_platform_device().
> I guess we can utilize existing code.
>

For sure, this particular segment is effectively same as part of 
acpi_create_platform_device():

struct platform_device *acpi_create_platform_device(struct acpi_device 
*adev,
                     struct property_entry *properties)
{
     struct platform_device *pdev = NULL;
     struct platform_device_info pdevinfo;
     struct resource_entry *rentry;
     struct list_head resource_list;
     struct resource *resources = NULL;
     int count;

     /* If the ACPI node already has a physical device attached, skip it. */
     if (adev->physical_node_count)
         return NULL;

     if (!acpi_match_device_ids(adev, forbidden_id_list))
         return ERR_PTR(-EINVAL);

***>
     INIT_LIST_HEAD(&resource_list);
     count = acpi_dev_get_resources(adev, &resource_list, NULL, NULL);
     if (count < 0) {
         return NULL;
     } else if (count > 0) {
         resources = kzalloc(count * sizeof(struct resource),
                     GFP_KERNEL);
         if (!resources) {
             dev_err(&adev->dev, "No memory for resources\n");
             acpi_dev_free_resource_list(&resource_list);
             return ERR_PTR(-ENOMEM);
         }
         count = 0;
         list_for_each_entry(rentry, &resource_list, node)
             acpi_platform_fill_resource(adev, rentry->res,
                             &resources[count++]);

         acpi_dev_free_resource_list(&resource_list);
     }
<****
     memset(&pdevinfo, 0, sizeof(pdevinfo));
     /*
      * If the ACPI node has a parent and that parent has a physical

So is your idea to refactor this common segment into a helper function?

>> +       /* translate the I/O resources */
>> +       for (i = 0; i < count; i++) {
>> +               if (!(resources[i].flags & IORESOURCE_IO))
>> +                       continue;
>
>> +               ret = acpi_indirect_io_xlat_res(adev, host, &resources[i]);
>> +               if (ret) {
>> +                       kfree(resources);
>> +                       dev_err(child, "translate IO range failed(%d)\n", ret);
>> +                       return ret;
>> +               }
>> +       }
>> +       *res = resources;
>> +       *num_res = count;
>> +
>> +       return ret;
>
> Perhaps,
>
>    ret = ...
>    if (ret)
>     break;
>   }
>
>   if (ret) {
>                        kfree(resources);
>                        dev_err(child, "translate IO range failed(%d)\n", ret);
>                        return ret;
>   }
>
>   *res = resources;
>   *num_res = count;
>   return 0;

seems fine

>
> ?
>
>> +}
>> +
>> +/*
>> + * acpi_indirect_io_setup - scan handler for "indirect IO" host.
>> + * @adev: "indirect IO" host ACPI device pointer
>> + * Returns 0 when successful, and a negative value for failure.
>> + *
>> + * Setup an "indirect IO" host by scanning all child devices, and
>> + * create a per-device MFD with logical PIO translated IO resources.
>> + */
>> +static int acpi_indirect_io_setup(struct acpi_device *adev)
>> +{
>> +       struct platform_device *pdev;
>> +       struct mfd_cell *mfd_cells;
>> +       struct logic_pio_hwaddr *range;
>> +       struct acpi_device *child;
>> +       struct acpi_indirect_io_mfd_cell *acpi_indirect_io_mfd_cells;
>> +       int size, ret, count = 0, cell_num = 0;
>> +
>> +       range = kzalloc(sizeof(*range), GFP_KERNEL);
>> +       if (!range)
>> +               return -ENOMEM;
>> +       range->fwnode = &adev->fwnode;
>> +       range->flags = PIO_INDIRECT;
>> +       range->size = PIO_INDIRECT_SIZE;
>> +
>> +       ret = logic_pio_register_range(range);
>> +       if (ret)
>> +               goto free_range;
>> +
>> +       list_for_each_entry(child, &adev->children, node)
>> +               cell_num++;
>> +
>> +       /* allocate the mfd cell and companion acpi info, one per child */
>> +       size = sizeof(*mfd_cells) + sizeof(*acpi_indirect_io_mfd_cells);
>> +       mfd_cells = kcalloc(cell_num, size, GFP_KERNEL);
>> +       if (!mfd_cells) {
>> +               ret = -ENOMEM;
>> +               goto free_range;
>> +       }
>> +
>> +       acpi_indirect_io_mfd_cells = (struct acpi_indirect_io_mfd_cell *)
>> +                                       &mfd_cells[cell_num];
>> +       /* Only consider the children of the host */
>> +       list_for_each_entry(child, &adev->children, node) {
>> +               struct mfd_cell *mfd_cell = &mfd_cells[count];
>> +               struct acpi_indirect_io_mfd_cell *acpi_indirect_io_mfd_cell =
>> +                                       &acpi_indirect_io_mfd_cells[count];
>> +               const struct mfd_cell_acpi_match *acpi_match =
>> +                                       &acpi_indirect_io_mfd_cell->acpi_match;
>
>> +               char *name = &acpi_indirect_io_mfd_cell[count].name[0];
>> +               char *pnpid = &acpi_indirect_io_mfd_cell[count].pnpid[0];
>
> Plain x is equivalent to &x[0].

Right, but I thought for arrays that we should use address of x rather 
than x itself, no?

>
>> +               struct mfd_cell_acpi_match match = {
>> +                       .pnpid = pnpid,
>> +               };
>> +
>> +               snprintf(name, ACPI_INDIRECT_IO_NAME_LEN, "indirect-io-%s",
>> +                        acpi_device_hid(child));
>> +               snprintf(pnpid, ACPI_INDIRECT_IO_NAME_LEN, "%s",
>> +                        acpi_device_hid(child))
>
>> +               memcpy((void *)acpi_match, (void *)&match, sizeof(*acpi_match));
>
> Casting to void * is pointless. In both cases.

I rechecked this. The casting to void * was there to mask another issue 
which I've now fixed.

>
>> +               mfd_cell->name = name;
>> +               mfd_cell->acpi_match = acpi_match;
>> +
>> +               ret = acpi_indirect_io_set_res(&child->dev, &adev->dev,
>> +                                              &mfd_cell->resources,
>> +                                              &mfd_cell->num_resources);
>> +               if (ret) {
>> +                       dev_err(&child->dev, "set resource failed (%d)\n", ret);
>> +                       goto free_mfd_resources;
>> +               }
>> +               count++;
>> +       }
>> +
>> +       pdev = acpi_create_platform_device(adev, NULL);
>> +       if (IS_ERR_OR_NULL(pdev)) {
>> +               dev_err(&adev->dev, "create platform device for host failed\n");
>
>> +               ret = PTR_ERR(pdev);
>
> So, NULL case will return 0. Is it expected?
>

Should error in that case also, so I'll change.

>> +               goto free_mfd_resources;
>> +       }
>> +       acpi_device_set_enumerated(adev);
>> +
>> +       ret = mfd_add_devices(&pdev->dev, PLATFORM_DEVID_NONE,
>> +                             mfd_cells, cell_num, NULL, 0, NULL);
>> +       if (ret) {
>> +               dev_err(&pdev->dev, "failed to add mfd cells (%d)\n", ret);
>> +               goto free_mfd_resources;
>> +       }
>> +
>> +       return ret;
>> +
>> +free_mfd_resources:
>> +       while (cell_num--)
>> +               kfree(mfd_cells[cell_num].resources);
>> +       kfree(mfd_cells);
>> +free_range:
>> +       kfree(range);
>> +
>> +       return ret;
>> +}
>
> One question, what a scope of use of this function? Is it ->probe() time?
> If it's so, can we use devm_* variants?

It is called from a scan handler, so prior to device probing.

>

Thanks,
John

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

* Re: [PATCH v13 7/9] ACPI: Translate the I/O range of non-MMIO devices before scanning
@ 2018-02-14 15:33       ` John Garry
  0 siblings, 0 replies; 66+ messages in thread
From: John Garry @ 2018-02-14 15:33 UTC (permalink / raw)
  To: Andy Shevchenko
  Cc: Mika Westerberg, Rafael J. Wysocki, Lorenzo Pieralisi,
	Rafael J. Wysocki, Hanjun Guo, Rob Herring, Bjorn Helgaas,
	Arnd Bergmann, Mark Rutland, Olof Johansson, Dann Frazier,
	Rob Herring, Joe Perches, Benjamin Herrenschmidt, linux-pci,
	Linux Kernel Mailing List, ACPI Devel Maling List, Linuxarm,
	Corey Minyard, devicetree, Linux-Arch, Randy Dunlap

On 14/02/2018 13:53, Andy Shevchenko wrote:
> On Tue, Feb 13, 2018 at 7:45 PM, John Garry <john.garry@huawei.com> wrote:
>> On some platforms (such as arm64-based hip06/hip07), access to legacy
>> ISA/LPC devices through access IO space is required, similar to x86
>> platforms. As the I/O for these devices are not memory mapped like
>> PCI/PCIE MMIO host bridges, they require special low-level device
>> operations through some host to generate IO accesses, i.e. a non-
>> transparent bridge.
>>
>> Through the logical PIO framework, hosts are able to register address
>> ranges in the logical PIO space for IO accesses. For hosts which require
>> a LLDD to generate the IO accesses, through the logical PIO framework
>> the host also registers accessors as a backend to generate the physical
>> bus transactions for IO space accesses (called indirect IO).
>>
>> When describing the indirect IO child device in APCI tables, the IO
>> resource is the host-specific address for the child (generally a
>> bus address).
>> An example is as follows:
>>   Device (LPC0) {
>>     Name (_HID, "HISI0191")  // HiSi LPC
>>     Name (_CRS, ResourceTemplate () {
>>       Memory32Fixed (ReadWrite, 0xa01b0000, 0x1000)
>>     })
>>   }
>>
>>   Device (LPC0.IPMI) {
>>     Name (_HID, "IPI0001")
>>     Name (LORS, ResourceTemplate() {
>>       QWordIO (
>>         ResourceConsumer,
>>         MinNotFixed,     // _MIF
>>         MaxNotFixed,     // _MAF
>>         PosDecode,
>>         EntireRange,
>>         0x0,             // _GRA
>>         0xe4,            // _MIN
>>         0x3fff,          // _MAX
>>         0x0,             // _TRA
>>         0x04,            // _LEN
>>         , ,
>>         BTIO
>>       )
>>     })
>>
>> Since the IO resource for the child is a host-specific address,
>> special translation are required to retrieve the logical PIO address
>> for that child.
>>
>> To overcome the problem of associating this logical PIO address
>> with the child device, a scan handler is added to scan the ACPI
>> namespace for known indirect IO hosts. This scan handler creates an
>> MFD per child with the translated logical PIO address as it's IO
>> resource, as a substitute for the normal platform device which ACPI
>> would create during device enumeration.
>

Hi Andy,

>> +       unsigned long sys_port;
>
>> +       sys_port = logic_pio_trans_hwaddr(&host->fwnode, res->start, len);
>> +       if (sys_port == -1UL)
>
> Wouldn't it be better to compare with ULONG_MAX?

Could do, being the same thing. Maybe people prefer -1UL as it saves 
having to figure out what ULONG_MAX is :)

>
>> +               return -EFAULT;
>
>
>> +/*
>
> Shouldn't be a kernel-doc?

Right, I'll make it /**

>
>> + * acpi_indirect_io_set_res - set the resources for a child device
>> + * (MFD) of an "indirect IO" host.
>
> In that case this would be one line w/o period at the end.
>
>> + * @child: the device node to be updated the I/O resource
>> + * @hostdev: the device node associated with the "indirect IO" host
>> + * @res: double pointer to be set to the address of translated resources
>> + * @num_res: pointer to variable to hold the number of translated resources
>> + *
>> + * Returns 0 when successful, and a negative value for failure.
>> + *
>> + * For a given "indirect IO" host, each child device will have associated
>> + * host-relevative address resource. This function will return the translated
>> + * logical PIO addresses for each child devices resources.
>> + */
>> +static int acpi_indirect_io_set_res(struct device *child,
>> +                                   struct device *hostdev,
>> +                                   const struct resource **res,
>> +                                   int *num_res)
>> +{
>> +       struct acpi_device *adev;
>> +       struct acpi_device *host;
>> +       struct resource_entry *rentry;
>> +       LIST_HEAD(resource_list);
>> +       struct resource *resources;
>> +       int count;
>> +       int i;
>> +       int ret = -EIO;
>> +
>> +       if (!child || !hostdev)
>> +               return -EINVAL;
>> +
>> +       host = to_acpi_device(hostdev);
>> +       adev = to_acpi_device(child);
>

***

>> +       count = acpi_dev_get_resources(adev, &resource_list, NULL, NULL);
>> +       if (count <= 0) {
>> +               dev_err(child, "failed to get resources\n");
>> +               return count ? count : -EIO;
>> +       }
>> +
>> +       resources = kcalloc(count, sizeof(*resources), GFP_KERNEL);
>> +       if (!resources) {
>> +               acpi_dev_free_resource_list(&resource_list);
>> +               return -ENOMEM;
>> +       }
>> +       count = 0;
>> +       list_for_each_entry(rentry, &resource_list, node)
>> +               resources[count++] = *rentry->res;
>> +
>> +       acpi_dev_free_resource_list(&resource_list);
>
> It has similarities with acpi_create_platform_device().
> I guess we can utilize existing code.
>

For sure, this particular segment is effectively same as part of 
acpi_create_platform_device():

struct platform_device *acpi_create_platform_device(struct acpi_device 
*adev,
                     struct property_entry *properties)
{
     struct platform_device *pdev = NULL;
     struct platform_device_info pdevinfo;
     struct resource_entry *rentry;
     struct list_head resource_list;
     struct resource *resources = NULL;
     int count;

     /* If the ACPI node already has a physical device attached, skip it. */
     if (adev->physical_node_count)
         return NULL;

     if (!acpi_match_device_ids(adev, forbidden_id_list))
         return ERR_PTR(-EINVAL);

***>
     INIT_LIST_HEAD(&resource_list);
     count = acpi_dev_get_resources(adev, &resource_list, NULL, NULL);
     if (count < 0) {
         return NULL;
     } else if (count > 0) {
         resources = kzalloc(count * sizeof(struct resource),
                     GFP_KERNEL);
         if (!resources) {
             dev_err(&adev->dev, "No memory for resources\n");
             acpi_dev_free_resource_list(&resource_list);
             return ERR_PTR(-ENOMEM);
         }
         count = 0;
         list_for_each_entry(rentry, &resource_list, node)
             acpi_platform_fill_resource(adev, rentry->res,
                             &resources[count++]);

         acpi_dev_free_resource_list(&resource_list);
     }
<****
     memset(&pdevinfo, 0, sizeof(pdevinfo));
     /*
      * If the ACPI node has a parent and that parent has a physical

So is your idea to refactor this common segment into a helper function?

>> +       /* translate the I/O resources */
>> +       for (i = 0; i < count; i++) {
>> +               if (!(resources[i].flags & IORESOURCE_IO))
>> +                       continue;
>
>> +               ret = acpi_indirect_io_xlat_res(adev, host, &resources[i]);
>> +               if (ret) {
>> +                       kfree(resources);
>> +                       dev_err(child, "translate IO range failed(%d)\n", ret);
>> +                       return ret;
>> +               }
>> +       }
>> +       *res = resources;
>> +       *num_res = count;
>> +
>> +       return ret;
>
> Perhaps,
>
>    ret = ...
>    if (ret)
>     break;
>   }
>
>   if (ret) {
>                        kfree(resources);
>                        dev_err(child, "translate IO range failed(%d)\n", ret);
>                        return ret;
>   }
>
>   *res = resources;
>   *num_res = count;
>   return 0;

seems fine

>
> ?
>
>> +}
>> +
>> +/*
>> + * acpi_indirect_io_setup - scan handler for "indirect IO" host.
>> + * @adev: "indirect IO" host ACPI device pointer
>> + * Returns 0 when successful, and a negative value for failure.
>> + *
>> + * Setup an "indirect IO" host by scanning all child devices, and
>> + * create a per-device MFD with logical PIO translated IO resources.
>> + */
>> +static int acpi_indirect_io_setup(struct acpi_device *adev)
>> +{
>> +       struct platform_device *pdev;
>> +       struct mfd_cell *mfd_cells;
>> +       struct logic_pio_hwaddr *range;
>> +       struct acpi_device *child;
>> +       struct acpi_indirect_io_mfd_cell *acpi_indirect_io_mfd_cells;
>> +       int size, ret, count = 0, cell_num = 0;
>> +
>> +       range = kzalloc(sizeof(*range), GFP_KERNEL);
>> +       if (!range)
>> +               return -ENOMEM;
>> +       range->fwnode = &adev->fwnode;
>> +       range->flags = PIO_INDIRECT;
>> +       range->size = PIO_INDIRECT_SIZE;
>> +
>> +       ret = logic_pio_register_range(range);
>> +       if (ret)
>> +               goto free_range;
>> +
>> +       list_for_each_entry(child, &adev->children, node)
>> +               cell_num++;
>> +
>> +       /* allocate the mfd cell and companion acpi info, one per child */
>> +       size = sizeof(*mfd_cells) + sizeof(*acpi_indirect_io_mfd_cells);
>> +       mfd_cells = kcalloc(cell_num, size, GFP_KERNEL);
>> +       if (!mfd_cells) {
>> +               ret = -ENOMEM;
>> +               goto free_range;
>> +       }
>> +
>> +       acpi_indirect_io_mfd_cells = (struct acpi_indirect_io_mfd_cell *)
>> +                                       &mfd_cells[cell_num];
>> +       /* Only consider the children of the host */
>> +       list_for_each_entry(child, &adev->children, node) {
>> +               struct mfd_cell *mfd_cell = &mfd_cells[count];
>> +               struct acpi_indirect_io_mfd_cell *acpi_indirect_io_mfd_cell =
>> +                                       &acpi_indirect_io_mfd_cells[count];
>> +               const struct mfd_cell_acpi_match *acpi_match =
>> +                                       &acpi_indirect_io_mfd_cell->acpi_match;
>
>> +               char *name = &acpi_indirect_io_mfd_cell[count].name[0];
>> +               char *pnpid = &acpi_indirect_io_mfd_cell[count].pnpid[0];
>
> Plain x is equivalent to &x[0].

Right, but I thought for arrays that we should use address of x rather 
than x itself, no?

>
>> +               struct mfd_cell_acpi_match match = {
>> +                       .pnpid = pnpid,
>> +               };
>> +
>> +               snprintf(name, ACPI_INDIRECT_IO_NAME_LEN, "indirect-io-%s",
>> +                        acpi_device_hid(child));
>> +               snprintf(pnpid, ACPI_INDIRECT_IO_NAME_LEN, "%s",
>> +                        acpi_device_hid(child))
>
>> +               memcpy((void *)acpi_match, (void *)&match, sizeof(*acpi_match));
>
> Casting to void * is pointless. In both cases.

I rechecked this. The casting to void * was there to mask another issue 
which I've now fixed.

>
>> +               mfd_cell->name = name;
>> +               mfd_cell->acpi_match = acpi_match;
>> +
>> +               ret = acpi_indirect_io_set_res(&child->dev, &adev->dev,
>> +                                              &mfd_cell->resources,
>> +                                              &mfd_cell->num_resources);
>> +               if (ret) {
>> +                       dev_err(&child->dev, "set resource failed (%d)\n", ret);
>> +                       goto free_mfd_resources;
>> +               }
>> +               count++;
>> +       }
>> +
>> +       pdev = acpi_create_platform_device(adev, NULL);
>> +       if (IS_ERR_OR_NULL(pdev)) {
>> +               dev_err(&adev->dev, "create platform device for host failed\n");
>
>> +               ret = PTR_ERR(pdev);
>
> So, NULL case will return 0. Is it expected?
>

Should error in that case also, so I'll change.

>> +               goto free_mfd_resources;
>> +       }
>> +       acpi_device_set_enumerated(adev);
>> +
>> +       ret = mfd_add_devices(&pdev->dev, PLATFORM_DEVID_NONE,
>> +                             mfd_cells, cell_num, NULL, 0, NULL);
>> +       if (ret) {
>> +               dev_err(&pdev->dev, "failed to add mfd cells (%d)\n", ret);
>> +               goto free_mfd_resources;
>> +       }
>> +
>> +       return ret;
>> +
>> +free_mfd_resources:
>> +       while (cell_num--)
>> +               kfree(mfd_cells[cell_num].resources);
>> +       kfree(mfd_cells);
>> +free_range:
>> +       kfree(range);
>> +
>> +       return ret;
>> +}
>
> One question, what a scope of use of this function? Is it ->probe() time?
> If it's so, can we use devm_* variants?

It is called from a scan handler, so prior to device probing.

>

Thanks,
John

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

* Re: [PATCH v13 7/9] ACPI: Translate the I/O range of non-MMIO devices before scanning
@ 2018-02-14 15:33       ` John Garry
  0 siblings, 0 replies; 66+ messages in thread
From: John Garry @ 2018-02-14 15:33 UTC (permalink / raw)
  To: Andy Shevchenko
  Cc: Mika Westerberg, Rafael J. Wysocki, Lorenzo Pieralisi,
	Rafael J. Wysocki, Hanjun Guo, Rob Herring, Bjorn Helgaas,
	Arnd Bergmann, Mark Rutland, Olof Johansson, Dann Frazier,
	Rob Herring, Joe Perches, Benjamin Herrenschmidt, linux-pci,
	Linux Kernel Mailing List, ACPI Devel Maling List, Linuxarm,
	Corey Minyard, devicetree, Linux-Arch, Randy Dunlap

On 14/02/2018 13:53, Andy Shevchenko wrote:
> On Tue, Feb 13, 2018 at 7:45 PM, John Garry <john.garry@huawei.com> wrote:
>> On some platforms (such as arm64-based hip06/hip07), access to legacy
>> ISA/LPC devices through access IO space is required, similar to x86
>> platforms. As the I/O for these devices are not memory mapped like
>> PCI/PCIE MMIO host bridges, they require special low-level device
>> operations through some host to generate IO accesses, i.e. a non-
>> transparent bridge.
>>
>> Through the logical PIO framework, hosts are able to register address
>> ranges in the logical PIO space for IO accesses. For hosts which require
>> a LLDD to generate the IO accesses, through the logical PIO framework
>> the host also registers accessors as a backend to generate the physical
>> bus transactions for IO space accesses (called indirect IO).
>>
>> When describing the indirect IO child device in APCI tables, the IO
>> resource is the host-specific address for the child (generally a
>> bus address).
>> An example is as follows:
>>   Device (LPC0) {
>>     Name (_HID, "HISI0191")  // HiSi LPC
>>     Name (_CRS, ResourceTemplate () {
>>       Memory32Fixed (ReadWrite, 0xa01b0000, 0x1000)
>>     })
>>   }
>>
>>   Device (LPC0.IPMI) {
>>     Name (_HID, "IPI0001")
>>     Name (LORS, ResourceTemplate() {
>>       QWordIO (
>>         ResourceConsumer,
>>         MinNotFixed,     // _MIF
>>         MaxNotFixed,     // _MAF
>>         PosDecode,
>>         EntireRange,
>>         0x0,             // _GRA
>>         0xe4,            // _MIN
>>         0x3fff,          // _MAX
>>         0x0,             // _TRA
>>         0x04,            // _LEN
>>         , ,
>>         BTIO
>>       )
>>     })
>>
>> Since the IO resource for the child is a host-specific address,
>> special translation are required to retrieve the logical PIO address
>> for that child.
>>
>> To overcome the problem of associating this logical PIO address
>> with the child device, a scan handler is added to scan the ACPI
>> namespace for known indirect IO hosts. This scan handler creates an
>> MFD per child with the translated logical PIO address as it's IO
>> resource, as a substitute for the normal platform device which ACPI
>> would create during device enumeration.
>

Hi Andy,

>> +       unsigned long sys_port;
>
>> +       sys_port = logic_pio_trans_hwaddr(&host->fwnode, res->start, len);
>> +       if (sys_port == -1UL)
>
> Wouldn't it be better to compare with ULONG_MAX?

Could do, being the same thing. Maybe people prefer -1UL as it saves 
having to figure out what ULONG_MAX is :)

>
>> +               return -EFAULT;
>
>
>> +/*
>
> Shouldn't be a kernel-doc?

Right, I'll make it /**

>
>> + * acpi_indirect_io_set_res - set the resources for a child device
>> + * (MFD) of an "indirect IO" host.
>
> In that case this would be one line w/o period at the end.
>
>> + * @child: the device node to be updated the I/O resource
>> + * @hostdev: the device node associated with the "indirect IO" host
>> + * @res: double pointer to be set to the address of translated resources
>> + * @num_res: pointer to variable to hold the number of translated resources
>> + *
>> + * Returns 0 when successful, and a negative value for failure.
>> + *
>> + * For a given "indirect IO" host, each child device will have associated
>> + * host-relevative address resource. This function will return the translated
>> + * logical PIO addresses for each child devices resources.
>> + */
>> +static int acpi_indirect_io_set_res(struct device *child,
>> +                                   struct device *hostdev,
>> +                                   const struct resource **res,
>> +                                   int *num_res)
>> +{
>> +       struct acpi_device *adev;
>> +       struct acpi_device *host;
>> +       struct resource_entry *rentry;
>> +       LIST_HEAD(resource_list);
>> +       struct resource *resources;
>> +       int count;
>> +       int i;
>> +       int ret = -EIO;
>> +
>> +       if (!child || !hostdev)
>> +               return -EINVAL;
>> +
>> +       host = to_acpi_device(hostdev);
>> +       adev = to_acpi_device(child);
>

***

>> +       count = acpi_dev_get_resources(adev, &resource_list, NULL, NULL);
>> +       if (count <= 0) {
>> +               dev_err(child, "failed to get resources\n");
>> +               return count ? count : -EIO;
>> +       }
>> +
>> +       resources = kcalloc(count, sizeof(*resources), GFP_KERNEL);
>> +       if (!resources) {
>> +               acpi_dev_free_resource_list(&resource_list);
>> +               return -ENOMEM;
>> +       }
>> +       count = 0;
>> +       list_for_each_entry(rentry, &resource_list, node)
>> +               resources[count++] = *rentry->res;
>> +
>> +       acpi_dev_free_resource_list(&resource_list);
>
> It has similarities with acpi_create_platform_device().
> I guess we can utilize existing code.
>

For sure, this particular segment is effectively same as part of 
acpi_create_platform_device():

struct platform_device *acpi_create_platform_device(struct acpi_device 
*adev,
                     struct property_entry *properties)
{
     struct platform_device *pdev = NULL;
     struct platform_device_info pdevinfo;
     struct resource_entry *rentry;
     struct list_head resource_list;
     struct resource *resources = NULL;
     int count;

     /* If the ACPI node already has a physical device attached, skip it. */
     if (adev->physical_node_count)
         return NULL;

     if (!acpi_match_device_ids(adev, forbidden_id_list))
         return ERR_PTR(-EINVAL);

***>
     INIT_LIST_HEAD(&resource_list);
     count = acpi_dev_get_resources(adev, &resource_list, NULL, NULL);
     if (count < 0) {
         return NULL;
     } else if (count > 0) {
         resources = kzalloc(count * sizeof(struct resource),
                     GFP_KERNEL);
         if (!resources) {
             dev_err(&adev->dev, "No memory for resources\n");
             acpi_dev_free_resource_list(&resource_list);
             return ERR_PTR(-ENOMEM);
         }
         count = 0;
         list_for_each_entry(rentry, &resource_list, node)
             acpi_platform_fill_resource(adev, rentry->res,
                             &resources[count++]);

         acpi_dev_free_resource_list(&resource_list);
     }
<****
     memset(&pdevinfo, 0, sizeof(pdevinfo));
     /*
      * If the ACPI node has a parent and that parent has a physical

So is your idea to refactor this common segment into a helper function?

>> +       /* translate the I/O resources */
>> +       for (i = 0; i < count; i++) {
>> +               if (!(resources[i].flags & IORESOURCE_IO))
>> +                       continue;
>
>> +               ret = acpi_indirect_io_xlat_res(adev, host, &resources[i]);
>> +               if (ret) {
>> +                       kfree(resources);
>> +                       dev_err(child, "translate IO range failed(%d)\n", ret);
>> +                       return ret;
>> +               }
>> +       }
>> +       *res = resources;
>> +       *num_res = count;
>> +
>> +       return ret;
>
> Perhaps,
>
>    ret = ...
>    if (ret)
>     break;
>   }
>
>   if (ret) {
>                        kfree(resources);
>                        dev_err(child, "translate IO range failed(%d)\n", ret);
>                        return ret;
>   }
>
>   *res = resources;
>   *num_res = count;
>   return 0;

seems fine

>
> ?
>
>> +}
>> +
>> +/*
>> + * acpi_indirect_io_setup - scan handler for "indirect IO" host.
>> + * @adev: "indirect IO" host ACPI device pointer
>> + * Returns 0 when successful, and a negative value for failure.
>> + *
>> + * Setup an "indirect IO" host by scanning all child devices, and
>> + * create a per-device MFD with logical PIO translated IO resources.
>> + */
>> +static int acpi_indirect_io_setup(struct acpi_device *adev)
>> +{
>> +       struct platform_device *pdev;
>> +       struct mfd_cell *mfd_cells;
>> +       struct logic_pio_hwaddr *range;
>> +       struct acpi_device *child;
>> +       struct acpi_indirect_io_mfd_cell *acpi_indirect_io_mfd_cells;
>> +       int size, ret, count = 0, cell_num = 0;
>> +
>> +       range = kzalloc(sizeof(*range), GFP_KERNEL);
>> +       if (!range)
>> +               return -ENOMEM;
>> +       range->fwnode = &adev->fwnode;
>> +       range->flags = PIO_INDIRECT;
>> +       range->size = PIO_INDIRECT_SIZE;
>> +
>> +       ret = logic_pio_register_range(range);
>> +       if (ret)
>> +               goto free_range;
>> +
>> +       list_for_each_entry(child, &adev->children, node)
>> +               cell_num++;
>> +
>> +       /* allocate the mfd cell and companion acpi info, one per child */
>> +       size = sizeof(*mfd_cells) + sizeof(*acpi_indirect_io_mfd_cells);
>> +       mfd_cells = kcalloc(cell_num, size, GFP_KERNEL);
>> +       if (!mfd_cells) {
>> +               ret = -ENOMEM;
>> +               goto free_range;
>> +       }
>> +
>> +       acpi_indirect_io_mfd_cells = (struct acpi_indirect_io_mfd_cell *)
>> +                                       &mfd_cells[cell_num];
>> +       /* Only consider the children of the host */
>> +       list_for_each_entry(child, &adev->children, node) {
>> +               struct mfd_cell *mfd_cell = &mfd_cells[count];
>> +               struct acpi_indirect_io_mfd_cell *acpi_indirect_io_mfd_cell =
>> +                                       &acpi_indirect_io_mfd_cells[count];
>> +               const struct mfd_cell_acpi_match *acpi_match =
>> +                                       &acpi_indirect_io_mfd_cell->acpi_match;
>
>> +               char *name = &acpi_indirect_io_mfd_cell[count].name[0];
>> +               char *pnpid = &acpi_indirect_io_mfd_cell[count].pnpid[0];
>
> Plain x is equivalent to &x[0].

Right, but I thought for arrays that we should use address of x rather 
than x itself, no?

>
>> +               struct mfd_cell_acpi_match match = {
>> +                       .pnpid = pnpid,
>> +               };
>> +
>> +               snprintf(name, ACPI_INDIRECT_IO_NAME_LEN, "indirect-io-%s",
>> +                        acpi_device_hid(child));
>> +               snprintf(pnpid, ACPI_INDIRECT_IO_NAME_LEN, "%s",
>> +                        acpi_device_hid(child))
>
>> +               memcpy((void *)acpi_match, (void *)&match, sizeof(*acpi_match));
>
> Casting to void * is pointless. In both cases.

I rechecked this. The casting to void * was there to mask another issue 
which I've now fixed.

>
>> +               mfd_cell->name = name;
>> +               mfd_cell->acpi_match = acpi_match;
>> +
>> +               ret = acpi_indirect_io_set_res(&child->dev, &adev->dev,
>> +                                              &mfd_cell->resources,
>> +                                              &mfd_cell->num_resources);
>> +               if (ret) {
>> +                       dev_err(&child->dev, "set resource failed (%d)\n", ret);
>> +                       goto free_mfd_resources;
>> +               }
>> +               count++;
>> +       }
>> +
>> +       pdev = acpi_create_platform_device(adev, NULL);
>> +       if (IS_ERR_OR_NULL(pdev)) {
>> +               dev_err(&adev->dev, "create platform device for host failed\n");
>
>> +               ret = PTR_ERR(pdev);
>
> So, NULL case will return 0. Is it expected?
>

Should error in that case also, so I'll change.

>> +               goto free_mfd_resources;
>> +       }
>> +       acpi_device_set_enumerated(adev);
>> +
>> +       ret = mfd_add_devices(&pdev->dev, PLATFORM_DEVID_NONE,
>> +                             mfd_cells, cell_num, NULL, 0, NULL);
>> +       if (ret) {
>> +               dev_err(&pdev->dev, "failed to add mfd cells (%d)\n", ret);
>> +               goto free_mfd_resources;
>> +       }
>> +
>> +       return ret;
>> +
>> +free_mfd_resources:
>> +       while (cell_num--)
>> +               kfree(mfd_cells[cell_num].resources);
>> +       kfree(mfd_cells);
>> +free_range:
>> +       kfree(range);
>> +
>> +       return ret;
>> +}
>
> One question, what a scope of use of this function? Is it ->probe() time?
> If it's so, can we use devm_* variants?

It is called from a scan handler, so prior to device probing.

>

Thanks,
John

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

* Re: [PATCH v13 4/9] PCI: Apply the new generic I/O management on PCI IO hosts
  2018-02-13 22:57   ` dann frazier
@ 2018-02-14 15:42       ` John Garry
  0 siblings, 0 replies; 66+ messages in thread
From: John Garry @ 2018-02-14 15:42 UTC (permalink / raw)
  To: dann frazier
  Cc: mika.westerberg, rafael, lorenzo.pieralisi, rjw, hanjun.guo,
	robh+dt, bhelgaas, arnd, mark.rutland, olof, andy.shevchenko,
	robh, joe, benh, linux-pci, linux-kernel, linux-acpi, linuxarm,
	minyard, devicetree, linux-arch, rdunlap

On 13/02/2018 22:57, dann frazier wrote:
> On Wed, Feb 14, 2018 at 01:45:28AM +0800, John Garry wrote:
>> From: Zhichang Yuan <yuanzhichang@hisilicon.com>
>>
>> After introducing the new generic I/O space management in logic pio, the
>> original PCI MMIO relevant helpers need to be updated based on the new
>> interfaces.
>> This patch adapts the corresponding code to match the changes introduced
>> by logic pio.
>>
>> 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>        #earlier draft
>> Acked-by: Bjorn Helgaas <bhelgaas@google.com>
>
> I saw that Bjorn Acked this back in v6, but it seems like the code in
> pci.c was reworked a bit for v7 onwards and I didn't see a follow-up
> review (apologies if I just missed it). In which case, maybe his Ack
> should have the "#earlier draft" tag as well?
>

Hi Dann,

I see Bjorn acked again later on in the series, here:
https://lkml.org/lkml/2017/5/26/612

>> ---
>>  drivers/pci/pci.c        | 95 +++++++++---------------------------------------
>>  include/asm-generic/io.h |  2 +-
>>  2 files changed, 18 insertions(+), 79 deletions(-)
>>
>> diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c
>> index 07290a3..8aa5c54 100644
>> --- a/drivers/pci/pci.c
>> +++ b/drivers/pci/pci.c
>> @@ -22,6 +22,7 @@
>>  #include <linux/spinlock.h>
>>  #include <linux/string.h>
>>  #include <linux/log2.h>
>> +#include <linux/logic_pio.h>
>>  #include <linux/pci-aspm.h>
>>  #include <linux/pm_wakeup.h>
>>  #include <linux/interrupt.h>
>> @@ -3440,17 +3441,6 @@ int pci_request_regions_exclusive(struct pci_dev *pdev, const char *res_name)
>>  }
>>  EXPORT_SYMBOL(pci_request_regions_exclusive);
>>
>> -#ifdef PCI_IOBASE
>> -struct io_range {
>> -	struct list_head list;
>> -	phys_addr_t start;
>> -	resource_size_t size;
>> -};
>> -
>> -static LIST_HEAD(io_range_list);
>> -static DEFINE_SPINLOCK(io_range_lock);
>> -#endif
>> -
>>  /*
>>   * Record the PCI IO range (expressed as CPU physical address + size).
>>   * Return a negative value if an error has occured, zero otherwise
>> @@ -3458,51 +3448,28 @@ struct io_range {
>>  int pci_register_io_range(struct fwnode_handle *fwnode, phys_addr_t addr,
>>  			resource_size_t	size)
>>  {
>> -	int err = 0;
>> -
>> +	int ret = 0;
>>  #ifdef PCI_IOBASE
>> -	struct io_range *range;
>> -	resource_size_t allocated_size = 0;
>> -
>> -	/* 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) {
>> -			/* range already registered, bail out */
>> -			goto end_register;
>> -		}
>> -		allocated_size += range->size;
>> -	}
>> +	struct logic_pio_hwaddr *range;
>>
>> -	/* range not registed yet, check for available space */
>> -	if (allocated_size + size - 1 > IO_SPACE_LIMIT) {
>> -		/* if it's too big check if 64K space can be reserved */
>> -		if (allocated_size + SZ_64K - 1 > IO_SPACE_LIMIT) {
>> -			err = -E2BIG;
>> -			goto end_register;
>> -		}
>> -
>> -		size = SZ_64K;
>> -		pr_warn("Requested IO range too big, new size set to 64K\n");
>> -	}
>> +	if (!size || addr + size < addr)
>> +		return -EINVAL;
>>
>> -	/* add the range to the list */
>>  	range = kzalloc(sizeof(*range), GFP_ATOMIC);
>> -	if (!range) {
>> -		err = -ENOMEM;
>> -		goto end_register;
>> -	}
>> +	if (!range)
>> +		return -ENOMEM;
>>
>> -	range->start = addr;
>> +	range->fwnode = fwnode;
>>  	range->size = size;
>> +	range->hw_start = addr;
>> +	range->flags = PIO_CPU_MMIO;
>>
>> -	list_add_tail(&range->list, &io_range_list);
>> -
>> -end_register:
>> -	spin_unlock(&io_range_lock);
>> +	ret = logic_pio_register_range(range);
>> +	if (ret)
>> +		kfree(range);
>>  #endif
>>
>> -	return err;
>> +	return ret;
>>  }
>>
>>  phys_addr_t pci_pio_to_address(unsigned long pio)
>> @@ -3510,21 +3477,10 @@ phys_addr_t pci_pio_to_address(unsigned long pio)
>>  	phys_addr_t address = (phys_addr_t)OF_BAD_ADDR;
>>
>>  #ifdef PCI_IOBASE
>> -	struct io_range *range;
>> -	resource_size_t allocated_size = 0;
>> -
>> -	if (pio > IO_SPACE_LIMIT)
>> +	if (pio >= MMIO_UPPER_LIMIT)
>>  		return address;
>>
>> -	spin_lock(&io_range_lock);
>> -	list_for_each_entry(range, &io_range_list, list) {
>> -		if (pio >= allocated_size && pio < allocated_size + range->size) {
>> -			address = range->start + pio - allocated_size;
>> -			break;
>> -		}
>> -		allocated_size += range->size;
>> -	}
>> -	spin_unlock(&io_range_lock);
>> +	address = logic_pio_to_hwaddr(pio);
>>  #endif
>>
>>  	return address;
>> @@ -3533,25 +3489,8 @@ phys_addr_t pci_pio_to_address(unsigned long pio)
>>  unsigned long __weak pci_address_to_pio(phys_addr_t address)
>>  {
>>  #ifdef PCI_IOBASE
>> -	struct io_range *res;
>> -	resource_size_t offset = 0;
>> -	unsigned long addr = -1;
>> -
>> -	spin_lock(&io_range_lock);
>> -	list_for_each_entry(res, &io_range_list, list) {
>> -		if (address >= res->start && address < res->start + res->size) {
>> -			addr = address - res->start + offset;
>> -			break;
>> -		}
>> -		offset += res->size;
>> -	}
>> -	spin_unlock(&io_range_lock);
>> -
>> -	return addr;
>> +	return logic_pio_trans_cpuaddr(address);
>>  #else
>> -	if (address > IO_SPACE_LIMIT)
>> -		return (unsigned long)-1;
>> -
>
> Why is this check now safe to drop in the !PCI_IOBASE case?

So I have been studying the (long) patchset history and I don't see the 
reason for removing this check, and I think it can be reinstated.

The address and pio are analogous here, and pio should not exceed the IO 
space limit, so just a safety check.

>
>   -dann
>
>>  	return (unsigned long) address;
>>  #endif
>>  }
>> diff --git a/include/asm-generic/io.h b/include/asm-generic/io.h
>> index b7996a79..5a59931 100644
>> --- a/include/asm-generic/io.h
>> +++ b/include/asm-generic/io.h
>> @@ -901,7 +901,7 @@ static inline void iounmap(void __iomem *addr)
>>  #define ioport_map ioport_map
>>  static inline void __iomem *ioport_map(unsigned long port, unsigned int nr)
>>  {

Thanks,
John

>> -	return PCI_IOBASE + (port & IO_SPACE_LIMIT);
>> +	return PCI_IOBASE + (port & MMIO_UPPER_LIMIT);
>>  }
>>  #endif
>>
>
> .
>

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

* Re: [PATCH v13 4/9] PCI: Apply the new generic I/O management on PCI IO hosts
@ 2018-02-14 15:42       ` John Garry
  0 siblings, 0 replies; 66+ messages in thread
From: John Garry @ 2018-02-14 15:42 UTC (permalink / raw)
  To: dann frazier
  Cc: mika.westerberg, rafael, lorenzo.pieralisi, rjw, hanjun.guo,
	robh+dt, bhelgaas, arnd, mark.rutland, olof, andy.shevchenko,
	robh, joe, benh, linux-pci, linux-kernel, linux-acpi, linuxarm,
	minyard, devicetree, linux-arch, rdunlap

On 13/02/2018 22:57, dann frazier wrote:
> On Wed, Feb 14, 2018 at 01:45:28AM +0800, John Garry wrote:
>> From: Zhichang Yuan <yuanzhichang@hisilicon.com>
>>
>> After introducing the new generic I/O space management in logic pio, the
>> original PCI MMIO relevant helpers need to be updated based on the new
>> interfaces.
>> This patch adapts the corresponding code to match the changes introduced
>> by logic pio.
>>
>> 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>        #earlier draft
>> Acked-by: Bjorn Helgaas <bhelgaas@google.com>
>
> I saw that Bjorn Acked this back in v6, but it seems like the code in
> pci.c was reworked a bit for v7 onwards and I didn't see a follow-up
> review (apologies if I just missed it). In which case, maybe his Ack
> should have the "#earlier draft" tag as well?
>

Hi Dann,

I see Bjorn acked again later on in the series, here:
https://lkml.org/lkml/2017/5/26/612

>> ---
>>  drivers/pci/pci.c        | 95 +++++++++---------------------------------------
>>  include/asm-generic/io.h |  2 +-
>>  2 files changed, 18 insertions(+), 79 deletions(-)
>>
>> diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c
>> index 07290a3..8aa5c54 100644
>> --- a/drivers/pci/pci.c
>> +++ b/drivers/pci/pci.c
>> @@ -22,6 +22,7 @@
>>  #include <linux/spinlock.h>
>>  #include <linux/string.h>
>>  #include <linux/log2.h>
>> +#include <linux/logic_pio.h>
>>  #include <linux/pci-aspm.h>
>>  #include <linux/pm_wakeup.h>
>>  #include <linux/interrupt.h>
>> @@ -3440,17 +3441,6 @@ int pci_request_regions_exclusive(struct pci_dev *pdev, const char *res_name)
>>  }
>>  EXPORT_SYMBOL(pci_request_regions_exclusive);
>>
>> -#ifdef PCI_IOBASE
>> -struct io_range {
>> -	struct list_head list;
>> -	phys_addr_t start;
>> -	resource_size_t size;
>> -};
>> -
>> -static LIST_HEAD(io_range_list);
>> -static DEFINE_SPINLOCK(io_range_lock);
>> -#endif
>> -
>>  /*
>>   * Record the PCI IO range (expressed as CPU physical address + size).
>>   * Return a negative value if an error has occured, zero otherwise
>> @@ -3458,51 +3448,28 @@ struct io_range {
>>  int pci_register_io_range(struct fwnode_handle *fwnode, phys_addr_t addr,
>>  			resource_size_t	size)
>>  {
>> -	int err = 0;
>> -
>> +	int ret = 0;
>>  #ifdef PCI_IOBASE
>> -	struct io_range *range;
>> -	resource_size_t allocated_size = 0;
>> -
>> -	/* 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) {
>> -			/* range already registered, bail out */
>> -			goto end_register;
>> -		}
>> -		allocated_size += range->size;
>> -	}
>> +	struct logic_pio_hwaddr *range;
>>
>> -	/* range not registed yet, check for available space */
>> -	if (allocated_size + size - 1 > IO_SPACE_LIMIT) {
>> -		/* if it's too big check if 64K space can be reserved */
>> -		if (allocated_size + SZ_64K - 1 > IO_SPACE_LIMIT) {
>> -			err = -E2BIG;
>> -			goto end_register;
>> -		}
>> -
>> -		size = SZ_64K;
>> -		pr_warn("Requested IO range too big, new size set to 64K\n");
>> -	}
>> +	if (!size || addr + size < addr)
>> +		return -EINVAL;
>>
>> -	/* add the range to the list */
>>  	range = kzalloc(sizeof(*range), GFP_ATOMIC);
>> -	if (!range) {
>> -		err = -ENOMEM;
>> -		goto end_register;
>> -	}
>> +	if (!range)
>> +		return -ENOMEM;
>>
>> -	range->start = addr;
>> +	range->fwnode = fwnode;
>>  	range->size = size;
>> +	range->hw_start = addr;
>> +	range->flags = PIO_CPU_MMIO;
>>
>> -	list_add_tail(&range->list, &io_range_list);
>> -
>> -end_register:
>> -	spin_unlock(&io_range_lock);
>> +	ret = logic_pio_register_range(range);
>> +	if (ret)
>> +		kfree(range);
>>  #endif
>>
>> -	return err;
>> +	return ret;
>>  }
>>
>>  phys_addr_t pci_pio_to_address(unsigned long pio)
>> @@ -3510,21 +3477,10 @@ phys_addr_t pci_pio_to_address(unsigned long pio)
>>  	phys_addr_t address = (phys_addr_t)OF_BAD_ADDR;
>>
>>  #ifdef PCI_IOBASE
>> -	struct io_range *range;
>> -	resource_size_t allocated_size = 0;
>> -
>> -	if (pio > IO_SPACE_LIMIT)
>> +	if (pio >= MMIO_UPPER_LIMIT)
>>  		return address;
>>
>> -	spin_lock(&io_range_lock);
>> -	list_for_each_entry(range, &io_range_list, list) {
>> -		if (pio >= allocated_size && pio < allocated_size + range->size) {
>> -			address = range->start + pio - allocated_size;
>> -			break;
>> -		}
>> -		allocated_size += range->size;
>> -	}
>> -	spin_unlock(&io_range_lock);
>> +	address = logic_pio_to_hwaddr(pio);
>>  #endif
>>
>>  	return address;
>> @@ -3533,25 +3489,8 @@ phys_addr_t pci_pio_to_address(unsigned long pio)
>>  unsigned long __weak pci_address_to_pio(phys_addr_t address)
>>  {
>>  #ifdef PCI_IOBASE
>> -	struct io_range *res;
>> -	resource_size_t offset = 0;
>> -	unsigned long addr = -1;
>> -
>> -	spin_lock(&io_range_lock);
>> -	list_for_each_entry(res, &io_range_list, list) {
>> -		if (address >= res->start && address < res->start + res->size) {
>> -			addr = address - res->start + offset;
>> -			break;
>> -		}
>> -		offset += res->size;
>> -	}
>> -	spin_unlock(&io_range_lock);
>> -
>> -	return addr;
>> +	return logic_pio_trans_cpuaddr(address);
>>  #else
>> -	if (address > IO_SPACE_LIMIT)
>> -		return (unsigned long)-1;
>> -
>
> Why is this check now safe to drop in the !PCI_IOBASE case?

So I have been studying the (long) patchset history and I don't see the 
reason for removing this check, and I think it can be reinstated.

The address and pio are analogous here, and pio should not exceed the IO 
space limit, so just a safety check.

>
>   -dann
>
>>  	return (unsigned long) address;
>>  #endif
>>  }
>> diff --git a/include/asm-generic/io.h b/include/asm-generic/io.h
>> index b7996a79..5a59931 100644
>> --- a/include/asm-generic/io.h
>> +++ b/include/asm-generic/io.h
>> @@ -901,7 +901,7 @@ static inline void iounmap(void __iomem *addr)
>>  #define ioport_map ioport_map
>>  static inline void __iomem *ioport_map(unsigned long port, unsigned int nr)
>>  {

Thanks,
John

>> -	return PCI_IOBASE + (port & IO_SPACE_LIMIT);
>> +	return PCI_IOBASE + (port & MMIO_UPPER_LIMIT);
>>  }
>>  #endif
>>
>
> .
>

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

* Re: [PATCH v13 4/9] PCI: Apply the new generic I/O management on PCI IO hosts
  2018-02-14 15:42       ` John Garry
  (?)
@ 2018-02-14 16:05       ` dann frazier
  -1 siblings, 0 replies; 66+ messages in thread
From: dann frazier @ 2018-02-14 16:05 UTC (permalink / raw)
  To: John Garry
  Cc: mika.westerberg, Rafael Wysocki, lorenzo.pieralisi, rjw,
	Hanjun Guo, Rob Herring, Bjorn Helgaas, Arnd Bergmann,
	Mark Rutland, olof, andy.shevchenko, robh, joe, benh, linux-pci,
	linux-kernel, linux-acpi, linuxarm, Corey Minyard, devicetree,
	linux-arch, Randy Dunlap

On Wed, Feb 14, 2018 at 8:42 AM, John Garry <john.garry@huawei.com> wrote:
> On 13/02/2018 22:57, dann frazier wrote:
>>
>> On Wed, Feb 14, 2018 at 01:45:28AM +0800, John Garry wrote:
>>>
>>> From: Zhichang Yuan <yuanzhichang@hisilicon.com>
>>>
>>> After introducing the new generic I/O space management in logic pio, the
>>> original PCI MMIO relevant helpers need to be updated based on the new
>>> interfaces.
>>> This patch adapts the corresponding code to match the changes introduced
>>> by logic pio.
>>>
>>> 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>        #earlier draft
>>> Acked-by: Bjorn Helgaas <bhelgaas@google.com>
>>
>>
>> I saw that Bjorn Acked this back in v6, but it seems like the code in
>> pci.c was reworked a bit for v7 onwards and I didn't see a follow-up
>> review (apologies if I just missed it). In which case, maybe his Ack
>> should have the "#earlier draft" tag as well?
>>
>
> Hi Dann,
>
> I see Bjorn acked again later on in the series, here:
> https://lkml.org/lkml/2017/5/26/612

Yep, you're right, sorry for missing that :)

>
>>> ---
>>>  drivers/pci/pci.c        | 95
>>> +++++++++---------------------------------------
>>>  include/asm-generic/io.h |  2 +-
>>>  2 files changed, 18 insertions(+), 79 deletions(-)
>>>
>>> diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c
>>> index 07290a3..8aa5c54 100644
>>> --- a/drivers/pci/pci.c
>>> +++ b/drivers/pci/pci.c
>>> @@ -22,6 +22,7 @@
>>>  #include <linux/spinlock.h>
>>>  #include <linux/string.h>
>>>  #include <linux/log2.h>
>>> +#include <linux/logic_pio.h>
>>>  #include <linux/pci-aspm.h>
>>>  #include <linux/pm_wakeup.h>
>>>  #include <linux/interrupt.h>
>>> @@ -3440,17 +3441,6 @@ int pci_request_regions_exclusive(struct pci_dev
>>> *pdev, const char *res_name)
>>>  }
>>>  EXPORT_SYMBOL(pci_request_regions_exclusive);
>>>
>>> -#ifdef PCI_IOBASE
>>> -struct io_range {
>>> -       struct list_head list;
>>> -       phys_addr_t start;
>>> -       resource_size_t size;
>>> -};
>>> -
>>> -static LIST_HEAD(io_range_list);
>>> -static DEFINE_SPINLOCK(io_range_lock);
>>> -#endif
>>> -
>>>  /*
>>>   * Record the PCI IO range (expressed as CPU physical address + size).
>>>   * Return a negative value if an error has occured, zero otherwise
>>> @@ -3458,51 +3448,28 @@ struct io_range {
>>>  int pci_register_io_range(struct fwnode_handle *fwnode, phys_addr_t
>>> addr,
>>>                         resource_size_t size)
>>>  {
>>> -       int err = 0;
>>> -
>>> +       int ret = 0;
>>>  #ifdef PCI_IOBASE
>>> -       struct io_range *range;
>>> -       resource_size_t allocated_size = 0;
>>> -
>>> -       /* 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) {
>>> -                       /* range already registered, bail out */
>>> -                       goto end_register;
>>> -               }
>>> -               allocated_size += range->size;
>>> -       }
>>> +       struct logic_pio_hwaddr *range;
>>>
>>> -       /* range not registed yet, check for available space */
>>> -       if (allocated_size + size - 1 > IO_SPACE_LIMIT) {
>>> -               /* if it's too big check if 64K space can be reserved */
>>> -               if (allocated_size + SZ_64K - 1 > IO_SPACE_LIMIT) {
>>> -                       err = -E2BIG;
>>> -                       goto end_register;
>>> -               }
>>> -
>>> -               size = SZ_64K;
>>> -               pr_warn("Requested IO range too big, new size set to
>>> 64K\n");
>>> -       }
>>> +       if (!size || addr + size < addr)
>>> +               return -EINVAL;
>>>
>>> -       /* add the range to the list */
>>>         range = kzalloc(sizeof(*range), GFP_ATOMIC);
>>> -       if (!range) {
>>> -               err = -ENOMEM;
>>> -               goto end_register;
>>> -       }
>>> +       if (!range)
>>> +               return -ENOMEM;
>>>
>>> -       range->start = addr;
>>> +       range->fwnode = fwnode;
>>>         range->size = size;
>>> +       range->hw_start = addr;
>>> +       range->flags = PIO_CPU_MMIO;
>>>
>>> -       list_add_tail(&range->list, &io_range_list);
>>> -
>>> -end_register:
>>> -       spin_unlock(&io_range_lock);
>>> +       ret = logic_pio_register_range(range);
>>> +       if (ret)
>>> +               kfree(range);
>>>  #endif
>>>
>>> -       return err;
>>> +       return ret;
>>>  }
>>>
>>>  phys_addr_t pci_pio_to_address(unsigned long pio)
>>> @@ -3510,21 +3477,10 @@ phys_addr_t pci_pio_to_address(unsigned long pio)
>>>         phys_addr_t address = (phys_addr_t)OF_BAD_ADDR;
>>>
>>>  #ifdef PCI_IOBASE
>>> -       struct io_range *range;
>>> -       resource_size_t allocated_size = 0;
>>> -
>>> -       if (pio > IO_SPACE_LIMIT)
>>> +       if (pio >= MMIO_UPPER_LIMIT)
>>>                 return address;
>>>
>>> -       spin_lock(&io_range_lock);
>>> -       list_for_each_entry(range, &io_range_list, list) {
>>> -               if (pio >= allocated_size && pio < allocated_size +
>>> range->size) {
>>> -                       address = range->start + pio - allocated_size;
>>> -                       break;
>>> -               }
>>> -               allocated_size += range->size;
>>> -       }
>>> -       spin_unlock(&io_range_lock);
>>> +       address = logic_pio_to_hwaddr(pio);
>>>  #endif
>>>
>>>         return address;
>>> @@ -3533,25 +3489,8 @@ phys_addr_t pci_pio_to_address(unsigned long pio)
>>>  unsigned long __weak pci_address_to_pio(phys_addr_t address)
>>>  {
>>>  #ifdef PCI_IOBASE
>>> -       struct io_range *res;
>>> -       resource_size_t offset = 0;
>>> -       unsigned long addr = -1;
>>> -
>>> -       spin_lock(&io_range_lock);
>>> -       list_for_each_entry(res, &io_range_list, list) {
>>> -               if (address >= res->start && address < res->start +
>>> res->size) {
>>> -                       addr = address - res->start + offset;
>>> -                       break;
>>> -               }
>>> -               offset += res->size;
>>> -       }
>>> -       spin_unlock(&io_range_lock);
>>> -
>>> -       return addr;
>>> +       return logic_pio_trans_cpuaddr(address);
>>>  #else
>>> -       if (address > IO_SPACE_LIMIT)
>>> -               return (unsigned long)-1;
>>> -
>>
>>
>> Why is this check now safe to drop in the !PCI_IOBASE case?
>
>
> So I have been studying the (long) patchset history and I don't see the
> reason for removing this check, and I think it can be reinstated.
>
> The address and pio are analogous here, and pio should not exceed the IO
> space limit, so just a safety check.

Sounds good.

 -dann

>>
>>   -dann
>>
>>>         return (unsigned long) address;
>>>  #endif
>>>  }
>>> diff --git a/include/asm-generic/io.h b/include/asm-generic/io.h
>>> index b7996a79..5a59931 100644
>>> --- a/include/asm-generic/io.h
>>> +++ b/include/asm-generic/io.h
>>> @@ -901,7 +901,7 @@ static inline void iounmap(void __iomem *addr)
>>>  #define ioport_map ioport_map
>>>  static inline void __iomem *ioport_map(unsigned long port, unsigned int
>>> nr)
>>>  {
>
>
> Thanks,
> John
>
>>> -       return PCI_IOBASE + (port & IO_SPACE_LIMIT);
>>> +       return PCI_IOBASE + (port & MMIO_UPPER_LIMIT);
>>>  }
>>>  #endif
>>>
>>
>> .
>>
>
>

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

* Re: [PATCH v13 1/9] LIB: Introduce a generic PIO mapping method
  2018-02-13 23:05       ` dann frazier
@ 2018-02-14 16:13         ` John Garry
  -1 siblings, 0 replies; 66+ messages in thread
From: John Garry @ 2018-02-14 16:13 UTC (permalink / raw)
  To: dann frazier
  Cc: mika.westerberg, rafael, lorenzo.pieralisi, rjw, hanjun.guo,
	robh+dt, bhelgaas, arnd, mark.rutland, olof, andy.shevchenko,
	robh, joe, benh, linux-pci, linux-kernel, linux-acpi, linuxarm,
	minyard, devicetree, linux-arch, rdunlap

On 13/02/2018 23:05, dann frazier wrote:
>>  	bool
>> >
>> > +config INDIRECT_PIO
>> > +	bool "Access I/O in non-MMIO mode"
>> > +	depends on ARM64
>> > +	help
>> > +	  On some platforms where no separate I/O space exists, there are I/O
>> > +	  hosts which can not be accessed in MMIO mode. Using the logical PIO
>> > +	  mechanism, the host-local I/O resource can be mapped into system
>> > +	  logic PIO space shared with MMIO hosts, such as PCI/PCIE, then the
>> > +	  system can access the I/O devices with the mapped logic PIO through
>> > +	  I/O accessors.
>> > +	  This way has a relatively little I/O performance cost. Please make
> grammar nit - s/a relatively/relatively/
>

Fine, thanks

John

>   -dann
>
>> > +	  sure your devices really need this configure item enabled.
>> > +

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

* Re: [PATCH v13 1/9] LIB: Introduce a generic PIO mapping method
@ 2018-02-14 16:13         ` John Garry
  0 siblings, 0 replies; 66+ messages in thread
From: John Garry @ 2018-02-14 16:13 UTC (permalink / raw)
  To: dann frazier
  Cc: mika.westerberg, rafael, lorenzo.pieralisi, rjw, hanjun.guo,
	robh+dt, bhelgaas, arnd, mark.rutland, olof, andy.shevchenko,
	robh, joe, benh, linux-pci, linux-kernel, linux-acpi, linuxarm,
	minyard, devicetree, linux-arch, rdunlap

On 13/02/2018 23:05, dann frazier wrote:
>>  	bool
>> >
>> > +config INDIRECT_PIO
>> > +	bool "Access I/O in non-MMIO mode"
>> > +	depends on ARM64
>> > +	help
>> > +	  On some platforms where no separate I/O space exists, there are I/O
>> > +	  hosts which can not be accessed in MMIO mode. Using the logical PIO
>> > +	  mechanism, the host-local I/O resource can be mapped into system
>> > +	  logic PIO space shared with MMIO hosts, such as PCI/PCIE, then the
>> > +	  system can access the I/O devices with the mapped logic PIO through
>> > +	  I/O accessors.
>> > +	  This way has a relatively little I/O performance cost. Please make
> grammar nit - s/a relatively/relatively/
>

Fine, thanks

John

>   -dann
>
>> > +	  sure your devices really need this configure item enabled.
>> > +

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

* Re: [PATCH v13 7/9] ACPI: Translate the I/O range of non-MMIO devices before scanning
  2018-02-13 17:45   ` John Garry
                     ` (2 preceding siblings ...)
  (?)
@ 2018-02-14 16:16   ` Lorenzo Pieralisi
  2018-02-14 16:52       ` John Garry
  -1 siblings, 1 reply; 66+ messages in thread
From: Lorenzo Pieralisi @ 2018-02-14 16:16 UTC (permalink / raw)
  To: John Garry
  Cc: mika.westerberg, rafael, rjw, hanjun.guo, robh+dt, bhelgaas,
	arnd, mark.rutland, olof, dann.frazier, andy.shevchenko, robh,
	joe, benh, linux-pci, linux-kernel, linux-acpi, linuxarm,
	minyard, devicetree, linux-arch, rdunlap

On Wed, Feb 14, 2018 at 01:45:31AM +0800, John Garry wrote:
> On some platforms (such as arm64-based hip06/hip07), access to legacy
> ISA/LPC devices through access IO space is required, similar to x86
> platforms. As the I/O for these devices are not memory mapped like
> PCI/PCIE MMIO host bridges, they require special low-level device
> operations through some host to generate IO accesses, i.e. a non-
> transparent bridge.
> 
> Through the logical PIO framework, hosts are able to register address
> ranges in the logical PIO space for IO accesses. For hosts which require
> a LLDD to generate the IO accesses, through the logical PIO framework
> the host also registers accessors as a backend to generate the physical
> bus transactions for IO space accesses (called indirect IO).
> 
> When describing the indirect IO child device in APCI tables, the IO
> resource is the host-specific address for the child (generally a
> bus address).
> An example is as follows:
>   Device (LPC0) {
>     Name (_HID, "HISI0191")  // HiSi LPC
>     Name (_CRS, ResourceTemplate () {
>       Memory32Fixed (ReadWrite, 0xa01b0000, 0x1000)
>     })
>   }
> 
>   Device (LPC0.IPMI) {
>     Name (_HID, "IPI0001")
>     Name (LORS, ResourceTemplate() {
>       QWordIO (
>         ResourceConsumer,
> 	MinNotFixed,     // _MIF
> 	MaxNotFixed,     // _MAF
> 	PosDecode,
> 	EntireRange,
> 	0x0,             // _GRA
> 	0xe4,            // _MIN
> 	0x3fff,          // _MAX
> 	0x0,             // _TRA
> 	0x04,            // _LEN
> 	, ,
> 	BTIO
>       )
>     })
> 
> Since the IO resource for the child is a host-specific address,
> special translation are required to retrieve the logical PIO address
> for that child.

The problem I have with this patchset and with pretending that the ACPI
bits are generic is that the rules used to translate resources (I am
referring to LPC0.IPMI above) are documented _nowhere_ which means that
making this series generic code is just wishful thinking - there are no
bindings backing it, it will never ever be used on a platform different
from the one you are pushing this code for and I stated this already.

Reworded differently - this is a Hisilicon driver it is not generic ACPI
code; I can't see how it can be used on a multitude of platforms unless
you specify FW level bindings.

> To overcome the problem of associating this logical PIO address
> with the child device, a scan handler is added to scan the ACPI
> namespace for known indirect IO hosts. This scan handler creates an
> MFD per child with the translated logical PIO address as it's IO
> resource, as a substitute for the normal platform device which ACPI
> would create during device enumeration.
> 
> Signed-off-by: John Garry <john.garry@huawei.com>
> Signed-off-by: Zhichang Yuan <yuanzhichang@hisilicon.com>
> Signed-off-by: Gabriele Paoloni <gabriele.paoloni@huawei.com>
> ---
>  drivers/acpi/arm64/Makefile          |   1 +
>  drivers/acpi/arm64/acpi_indirectio.c | 250 +++++++++++++++++++++++++++++++++++

See above (and I do not understand what arm64 has to do with it).

I understand you need to find a place to add the:

acpi_indirect_io_scan_init()

to be called from core ACPI code because ACPI can't handle probe
dependencies in any other way but other than that this patch is
a Hisilicon ACPI driver - there is nothing generic in it (or at
least there are no standard bindings to make it so).

Whether a callback from ACPI core code (acpi_scan_init()) to a driver
specific hook is sane or not that's the question and the only reason
why you want to add this in drivers/acpi/arm64 rather than, say,
drivers/bus (as you do for the DT driver).

I do not know Rafael's opinion on the above, I would like to help
you make forward progress but please understand my concerns, mostly
on FW side.

Thanks,
Lorenzo

>  drivers/acpi/internal.h              |   5 +
>  drivers/acpi/scan.c                  |   1 +
>  4 files changed, 257 insertions(+)
>  create mode 100644 drivers/acpi/arm64/acpi_indirectio.c
> 
> diff --git a/drivers/acpi/arm64/Makefile b/drivers/acpi/arm64/Makefile
> index 1017def..f4a7f46 100644
> --- a/drivers/acpi/arm64/Makefile
> +++ b/drivers/acpi/arm64/Makefile
> @@ -1,2 +1,3 @@
>  obj-$(CONFIG_ACPI_IORT) 	+= iort.o
>  obj-$(CONFIG_ACPI_GTDT) 	+= gtdt.o
> +obj-$(CONFIG_INDIRECT_PIO)	+= acpi_indirectio.o
> diff --git a/drivers/acpi/arm64/acpi_indirectio.c b/drivers/acpi/arm64/acpi_indirectio.c
> new file mode 100644
> index 0000000..51a1b92
> --- /dev/null
> +++ b/drivers/acpi/arm64/acpi_indirectio.c
> @@ -0,0 +1,250 @@
> +// SPDX-License-Identifier: GPL-2.0+
> +/*
> + * Copyright (C) 2017 HiSilicon Limited, All Rights Reserved.
> + * Author: Gabriele Paoloni <gabriele.paoloni@huawei.com>
> + * Author: Zhichang Yuan <yuanzhichang@hisilicon.com>
> + * Author: John Garry <john.garry@huawei.com>
> + *
> + * This file implements functunality to scan the ACPI namespace and config
> + * devices under "indirect IO" hosts. An "indirect IO" host allows child
> + * devices to use logical IO accesses when the host, itself, does not provide
> + * a transparent bridge. The device setup creates a per-child MFD with a
> + * logical port IO resource.
> + */
> +
> +#include <linux/acpi.h>
> +#include <linux/logic_pio.h>
> +#include <linux/mfd/core.h>
> +#include <linux/platform_device.h>
> +
> +ACPI_MODULE_NAME("indirect IO");
> +
> +#define ACPI_INDIRECT_IO_NAME_LEN 255
> +
> +struct acpi_indirect_io_mfd_cell {
> +	struct mfd_cell_acpi_match acpi_match;
> +	char name[ACPI_INDIRECT_IO_NAME_LEN];
> +	char pnpid[ACPI_INDIRECT_IO_NAME_LEN];
> +};
> +
> +static int acpi_indirect_io_xlat_res(struct acpi_device *adev,
> +				     struct acpi_device *host,
> +				     struct resource *res)
> +{
> +	unsigned long sys_port;
> +	resource_size_t len = res->end - res->start;
> +
> +	sys_port = logic_pio_trans_hwaddr(&host->fwnode, res->start, len);
> +	if (sys_port == -1UL)
> +		return -EFAULT;
> +
> +	res->start = sys_port;
> +	res->end = sys_port + len;
> +
> +	return 0;
> +}
> +
> +/*
> + * acpi_indirect_io_set_res - set the resources for a child device
> + * (MFD) of an "indirect IO" host.
> + * @child: the device node to be updated the I/O resource
> + * @hostdev: the device node associated with the "indirect IO" host
> + * @res: double pointer to be set to the address of translated resources
> + * @num_res: pointer to variable to hold the number of translated resources
> + *
> + * Returns 0 when successful, and a negative value for failure.
> + *
> + * For a given "indirect IO" host, each child device will have associated
> + * host-relevative address resource. This function will return the translated
> + * logical PIO addresses for each child devices resources.
> + */
> +static int acpi_indirect_io_set_res(struct device *child,
> +				    struct device *hostdev,
> +				    const struct resource **res,
> +				    int *num_res)
> +{
> +	struct acpi_device *adev;
> +	struct acpi_device *host;
> +	struct resource_entry *rentry;
> +	LIST_HEAD(resource_list);
> +	struct resource *resources;
> +	int count;
> +	int i;
> +	int ret = -EIO;
> +
> +	if (!child || !hostdev)
> +		return -EINVAL;
> +
> +	host = to_acpi_device(hostdev);
> +	adev = to_acpi_device(child);
> +
> +	/* check the device state */
> +	if (!adev->status.present) {
> +		dev_info(child, "device is not present\n");
> +		return 0;
> +	}
> +	/* whether the child had been enumerated? */
> +	if (acpi_device_enumerated(adev)) {
> +		dev_info(child, "had been enumerated\n");
> +		return 0;
> +	}
> +
> +	count = acpi_dev_get_resources(adev, &resource_list, NULL, NULL);
> +	if (count <= 0) {
> +		dev_err(child, "failed to get resources\n");
> +		return count ? count : -EIO;
> +	}
> +
> +	resources = kcalloc(count, sizeof(*resources), GFP_KERNEL);
> +	if (!resources) {
> +		acpi_dev_free_resource_list(&resource_list);
> +		return -ENOMEM;
> +	}
> +	count = 0;
> +	list_for_each_entry(rentry, &resource_list, node)
> +		resources[count++] = *rentry->res;
> +
> +	acpi_dev_free_resource_list(&resource_list);
> +
> +	/* translate the I/O resources */
> +	for (i = 0; i < count; i++) {
> +		if (!(resources[i].flags & IORESOURCE_IO))
> +			continue;
> +		ret = acpi_indirect_io_xlat_res(adev, host, &resources[i]);
> +		if (ret) {
> +			kfree(resources);
> +			dev_err(child, "translate IO range failed(%d)\n", ret);
> +			return ret;
> +		}
> +	}
> +	*res = resources;
> +	*num_res = count;
> +
> +	return ret;
> +}
> +
> +/*
> + * acpi_indirect_io_setup - scan handler for "indirect IO" host.
> + * @adev: "indirect IO" host ACPI device pointer
> + * Returns 0 when successful, and a negative value for failure.
> + *
> + * Setup an "indirect IO" host by scanning all child devices, and
> + * create a per-device MFD with logical PIO translated IO resources.
> + */
> +static int acpi_indirect_io_setup(struct acpi_device *adev)
> +{
> +	struct platform_device *pdev;
> +	struct mfd_cell *mfd_cells;
> +	struct logic_pio_hwaddr *range;
> +	struct acpi_device *child;
> +	struct acpi_indirect_io_mfd_cell *acpi_indirect_io_mfd_cells;
> +	int size, ret, count = 0, cell_num = 0;
> +
> +	range = kzalloc(sizeof(*range), GFP_KERNEL);
> +	if (!range)
> +		return -ENOMEM;
> +	range->fwnode = &adev->fwnode;
> +	range->flags = PIO_INDIRECT;
> +	range->size = PIO_INDIRECT_SIZE;
> +
> +	ret = logic_pio_register_range(range);
> +	if (ret)
> +		goto free_range;
> +
> +	list_for_each_entry(child, &adev->children, node)
> +		cell_num++;
> +
> +	/* allocate the mfd cell and companion acpi info, one per child */
> +	size = sizeof(*mfd_cells) + sizeof(*acpi_indirect_io_mfd_cells);
> +	mfd_cells = kcalloc(cell_num, size, GFP_KERNEL);
> +	if (!mfd_cells) {
> +		ret = -ENOMEM;
> +		goto free_range;
> +	}
> +
> +	acpi_indirect_io_mfd_cells = (struct acpi_indirect_io_mfd_cell *)
> +					&mfd_cells[cell_num];
> +	/* Only consider the children of the host */
> +	list_for_each_entry(child, &adev->children, node) {
> +		struct mfd_cell *mfd_cell = &mfd_cells[count];
> +		struct acpi_indirect_io_mfd_cell *acpi_indirect_io_mfd_cell =
> +					&acpi_indirect_io_mfd_cells[count];
> +		const struct mfd_cell_acpi_match *acpi_match =
> +					&acpi_indirect_io_mfd_cell->acpi_match;
> +		char *name = &acpi_indirect_io_mfd_cell[count].name[0];
> +		char *pnpid = &acpi_indirect_io_mfd_cell[count].pnpid[0];
> +		struct mfd_cell_acpi_match match = {
> +			.pnpid = pnpid,
> +		};
> +
> +		snprintf(name, ACPI_INDIRECT_IO_NAME_LEN, "indirect-io-%s",
> +			 acpi_device_hid(child));
> +		snprintf(pnpid, ACPI_INDIRECT_IO_NAME_LEN, "%s",
> +			 acpi_device_hid(child));
> +
> +		memcpy((void *)acpi_match, (void *)&match, sizeof(*acpi_match));
> +		mfd_cell->name = name;
> +		mfd_cell->acpi_match = acpi_match;
> +
> +		ret = acpi_indirect_io_set_res(&child->dev, &adev->dev,
> +					       &mfd_cell->resources,
> +					       &mfd_cell->num_resources);
> +		if (ret) {
> +			dev_err(&child->dev, "set resource failed (%d)\n", ret);
> +			goto free_mfd_resources;
> +		}
> +		count++;
> +	}
> +
> +	pdev = acpi_create_platform_device(adev, NULL);
> +	if (IS_ERR_OR_NULL(pdev)) {
> +		dev_err(&adev->dev, "create platform device for host failed\n");
> +		ret = PTR_ERR(pdev);
> +		goto free_mfd_resources;
> +	}
> +	acpi_device_set_enumerated(adev);
> +
> +	ret = mfd_add_devices(&pdev->dev, PLATFORM_DEVID_NONE,
> +			      mfd_cells, cell_num, NULL, 0, NULL);
> +	if (ret) {
> +		dev_err(&pdev->dev, "failed to add mfd cells (%d)\n", ret);
> +		goto free_mfd_resources;
> +	}
> +
> +	return ret;
> +
> +free_mfd_resources:
> +	while (cell_num--)
> +		kfree(mfd_cells[cell_num].resources);
> +	kfree(mfd_cells);
> +free_range:
> +	kfree(range);
> +
> +	return ret;
> +}
> +
> +/* All the host devices which apply indirect-IO can be listed here. */
> +static const struct acpi_device_id acpi_indirect_io_host_id[] = {
> +	{}
> +};
> +
> +static int acpi_indirect_io_attach(struct acpi_device *adev,
> +				   const struct acpi_device_id *id)
> +{
> +	int ret = acpi_indirect_io_setup(adev);
> +
> +	if (ret < 0)
> +		return ret;
> +
> +	return 1;
> +}
> +
> +static struct acpi_scan_handler acpi_indirect_io_handler = {
> +	.ids = acpi_indirect_io_host_id,
> +	.attach = acpi_indirect_io_attach,
> +};
> +
> +void __init acpi_indirect_io_scan_init(void)
> +{
> +	acpi_scan_add_handler(&acpi_indirect_io_handler);
> +}
> diff --git a/drivers/acpi/internal.h b/drivers/acpi/internal.h
> index 1d0a501..680f3cf 100644
> --- a/drivers/acpi/internal.h
> +++ b/drivers/acpi/internal.h
> @@ -31,6 +31,11 @@
>  void acpi_platform_init(void);
>  void acpi_pnp_init(void);
>  void acpi_int340x_thermal_init(void);
> +#ifdef CONFIG_INDIRECT_PIO
> +void acpi_indirect_io_scan_init(void);
> +#else
> +static inline void acpi_indirect_io_scan_init(void) {}
> +#endif
>  #ifdef CONFIG_ARM_AMBA
>  void acpi_amba_init(void);
>  #else
> diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c
> index 8e63d93..204da8a 100644
> --- a/drivers/acpi/scan.c
> +++ b/drivers/acpi/scan.c
> @@ -2155,6 +2155,7 @@ int __init acpi_scan_init(void)
>  	acpi_amba_init();
>  	acpi_watchdog_init();
>  	acpi_init_lpit();
> +	acpi_indirect_io_scan_init();
>  
>  	acpi_scan_add_handler(&generic_device_handler);
>  
> -- 
> 1.9.1
> 

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

* Re: [PATCH v13 7/9] ACPI: Translate the I/O range of non-MMIO devices before scanning
  2018-02-14 15:33       ` John Garry
@ 2018-02-14 16:16         ` Andy Shevchenko
  -1 siblings, 0 replies; 66+ messages in thread
From: Andy Shevchenko @ 2018-02-14 16:16 UTC (permalink / raw)
  To: John Garry
  Cc: Mika Westerberg, Rafael J. Wysocki, Lorenzo Pieralisi,
	Rafael J. Wysocki, Hanjun Guo, Rob Herring, Bjorn Helgaas,
	Arnd Bergmann, Mark Rutland, Olof Johansson, Dann Frazier,
	Rob Herring, Joe Perches, Benjamin Herrenschmidt, linux-pci,
	Linux Kernel Mailing List, ACPI Devel Maling List, Linuxarm,
	Corey Minyard

On Wed, Feb 14, 2018 at 5:33 PM, John Garry <john.garry@huawei.com> wrote:
> On 14/02/2018 13:53, Andy Shevchenko wrote:
>> On Tue, Feb 13, 2018 at 7:45 PM, John Garry <john.garry@huawei.com> wrote:

>>> +       sys_port = logic_pio_trans_hwaddr(&host->fwnode, res->start,
>>> len);
>>> +       if (sys_port == -1UL)

>> Wouldn't it be better to compare with ULONG_MAX?

> Could do, being the same thing. Maybe people prefer -1UL as it saves having
> to figure out what ULONG_MAX is :)

-1UL looks confusing.

Another approach is to use ~0UL if that is preferable.

>>> +       list_for_each_entry(rentry, &resource_list, node)
>>> +               resources[count++] = *rentry->res;

>> It has similarities with acpi_create_platform_device().
>> I guess we can utilize existing code.

> For sure, this particular segment is effectively same as part of
> acpi_create_platform_device():

Not the same, acpi_create_platform_device() does a bit more than
copying the resources. If it indeed makes no hurt...

>         list_for_each_entry(rentry, &resource_list, node)
>             acpi_platform_fill_resource(adev, rentry->res,
>                             &resources[count++]);

> So is your idea to refactor this common segment into a helper function?

...I would go with helper.

>>> +               char *name = &acpi_indirect_io_mfd_cell[count].name[0];
>>> +               char *pnpid = &acpi_indirect_io_mfd_cell[count].pnpid[0];
>>
>>
>> Plain x is equivalent to &x[0].

> Right, but I thought for arrays that we should use address of x rather than
> x itself, no?

x is addressed by it's beginning.

-- 
With Best Regards,
Andy Shevchenko

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

* Re: [PATCH v13 7/9] ACPI: Translate the I/O range of non-MMIO devices before scanning
@ 2018-02-14 16:16         ` Andy Shevchenko
  0 siblings, 0 replies; 66+ messages in thread
From: Andy Shevchenko @ 2018-02-14 16:16 UTC (permalink / raw)
  To: John Garry
  Cc: Mika Westerberg, Rafael J. Wysocki, Lorenzo Pieralisi,
	Rafael J. Wysocki, Hanjun Guo, Rob Herring, Bjorn Helgaas,
	Arnd Bergmann, Mark Rutland, Olof Johansson, Dann Frazier,
	Rob Herring, Joe Perches, Benjamin Herrenschmidt, linux-pci,
	Linux Kernel Mailing List, ACPI Devel Maling List, Linuxarm,
	Corey Minyard, devicetree, Linux-Arch, Randy Dunlap

On Wed, Feb 14, 2018 at 5:33 PM, John Garry <john.garry@huawei.com> wrote:
> On 14/02/2018 13:53, Andy Shevchenko wrote:
>> On Tue, Feb 13, 2018 at 7:45 PM, John Garry <john.garry@huawei.com> wrote:

>>> +       sys_port = logic_pio_trans_hwaddr(&host->fwnode, res->start,
>>> len);
>>> +       if (sys_port == -1UL)

>> Wouldn't it be better to compare with ULONG_MAX?

> Could do, being the same thing. Maybe people prefer -1UL as it saves having
> to figure out what ULONG_MAX is :)

-1UL looks confusing.

Another approach is to use ~0UL if that is preferable.

>>> +       list_for_each_entry(rentry, &resource_list, node)
>>> +               resources[count++] = *rentry->res;

>> It has similarities with acpi_create_platform_device().
>> I guess we can utilize existing code.

> For sure, this particular segment is effectively same as part of
> acpi_create_platform_device():

Not the same, acpi_create_platform_device() does a bit more than
copying the resources. If it indeed makes no hurt...

>         list_for_each_entry(rentry, &resource_list, node)
>             acpi_platform_fill_resource(adev, rentry->res,
>                             &resources[count++]);

> So is your idea to refactor this common segment into a helper function?

...I would go with helper.

>>> +               char *name = &acpi_indirect_io_mfd_cell[count].name[0];
>>> +               char *pnpid = &acpi_indirect_io_mfd_cell[count].pnpid[0];
>>
>>
>> Plain x is equivalent to &x[0].

> Right, but I thought for arrays that we should use address of x rather than
> x itself, no?

x is addressed by it's beginning.

-- 
With Best Regards,
Andy Shevchenko

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

* Re: [PATCH v13 7/9] ACPI: Translate the I/O range of non-MMIO devices before scanning
  2018-02-14 16:16   ` Lorenzo Pieralisi
@ 2018-02-14 16:52       ` John Garry
  0 siblings, 0 replies; 66+ messages in thread
From: John Garry @ 2018-02-14 16:52 UTC (permalink / raw)
  To: Lorenzo Pieralisi
  Cc: mika.westerberg, rafael, rjw, hanjun.guo, robh+dt, bhelgaas,
	arnd, mark.rutland, olof, dann.frazier, andy.shevchenko, robh,
	joe, benh, linux-pci, linux-kernel, linux-acpi, linuxarm,
	minyard, devicetree, linux-arch, rdunlap

On 14/02/2018 16:16, Lorenzo Pieralisi wrote:
> On Wed, Feb 14, 2018 at 01:45:31AM +0800, John Garry wrote:
>> On some platforms (such as arm64-based hip06/hip07), access to legacy
>> ISA/LPC devices through access IO space is required, similar to x86
>> platforms. As the I/O for these devices are not memory mapped like
>> PCI/PCIE MMIO host bridges, they require special low-level device
>> operations through some host to generate IO accesses, i.e. a non-
>> transparent bridge.
>>
>> Through the logical PIO framework, hosts are able to register address
>> ranges in the logical PIO space for IO accesses. For hosts which require
>> a LLDD to generate the IO accesses, through the logical PIO framework
>> the host also registers accessors as a backend to generate the physical
>> bus transactions for IO space accesses (called indirect IO).
>>
>> When describing the indirect IO child device in APCI tables, the IO
>> resource is the host-specific address for the child (generally a
>> bus address).
>> An example is as follows:
>>   Device (LPC0) {
>>     Name (_HID, "HISI0191")  // HiSi LPC
>>     Name (_CRS, ResourceTemplate () {
>>       Memory32Fixed (ReadWrite, 0xa01b0000, 0x1000)
>>     })
>>   }
>>
>>   Device (LPC0.IPMI) {
>>     Name (_HID, "IPI0001")
>>     Name (LORS, ResourceTemplate() {
>>       QWordIO (
>>         ResourceConsumer,
>> 	MinNotFixed,     // _MIF
>> 	MaxNotFixed,     // _MAF
>> 	PosDecode,
>> 	EntireRange,
>> 	0x0,             // _GRA
>> 	0xe4,            // _MIN
>> 	0x3fff,          // _MAX
>> 	0x0,             // _TRA
>> 	0x04,            // _LEN
>> 	, ,
>> 	BTIO
>>       )
>>     })
>>
>> Since the IO resource for the child is a host-specific address,
>> special translation are required to retrieve the logical PIO address
>> for that child.
>

Hi Lorenzo,

> The problem I have with this patchset and with pretending that the ACPI
> bits are generic is that the rules used to translate resources (I am
> referring to LPC0.IPMI above) are documented _nowhere_ which means that
> making this series generic code is just wishful thinking - there are no
> bindings backing it, it will never ever be used on a platform different
> from the one you are pushing this code for and I stated this already.
>

Right, it is working on the presumption that this is how all "indirectio 
IO" hosts and children should/would be described in DSDT.

> Reworded differently - this is a Hisilicon driver it is not generic ACPI
> code; I can't see how it can be used on a multitude of platforms unless
> you specify FW level bindings.
>
>> To overcome the problem of associating this logical PIO address
>> with the child device, a scan handler is added to scan the ACPI
>> namespace for known indirect IO hosts. This scan handler creates an
>> MFD per child with the translated logical PIO address as it's IO
>> resource, as a substitute for the normal platform device which ACPI
>> would create during device enumeration.
>>
>> Signed-off-by: John Garry <john.garry@huawei.com>
>> Signed-off-by: Zhichang Yuan <yuanzhichang@hisilicon.com>
>> Signed-off-by: Gabriele Paoloni <gabriele.paoloni@huawei.com>
>> ---
>>  drivers/acpi/arm64/Makefile          |   1 +
>>  drivers/acpi/arm64/acpi_indirectio.c | 250 +++++++++++++++++++++++++++++++++++
>
> See above (and I do not understand what arm64 has to do with it).

Nothing apart from only being used by arm64 platforms today, which is 
circumstantial.

>
> I understand you need to find a place to add the:
>
> acpi_indirect_io_scan_init()
>
> to be called from core ACPI code because ACPI can't handle probe
> dependencies in any other way but other than that this patch is
> a Hisilicon ACPI driver - there is nothing generic in it (or at
> least there are no standard bindings to make it so).
>
> Whether a callback from ACPI core code (acpi_scan_init()) to a driver
> specific hook is sane or not that's the question and the only reason
> why you want to add this in drivers/acpi/arm64 rather than, say,
> drivers/bus (as you do for the DT driver).
>
> I do not know Rafael's opinion on the above, I would like to help
> you make forward progress but please understand my concerns, mostly
> on FW side.
>

I did mention an alternative in my "ping" in v12 patch 7/9 (Feb 1), but 
no response to this specific note so I kept on the same path.

Here's what I then wrote:
"I think another solution - which you may prefer - is to avoid adding
this scan handler (and all this other scan code) and add a check like
acpi_is_serial_bus_slave() [which checks the device parent versus a list 
of known indirectIO hosts] to not enumerate these children, and do it 
from the LLDD host probe instead (https://lkml.org/lkml/2017/6/16/250)"

Please consider this.

> Thanks,
> Lorenzo
>
>>  drivers/acpi/internal.h              |   5 +
>>  drivers/acpi/scan.c                  |   1 +
>>  4 files changed, 257 insertions(+)
>>  create mode 100644 drivers/acpi/arm64/acpi_indirectio.c
>>

Cheers,
John

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

* Re: [PATCH v13 7/9] ACPI: Translate the I/O range of non-MMIO devices before scanning
@ 2018-02-14 16:52       ` John Garry
  0 siblings, 0 replies; 66+ messages in thread
From: John Garry @ 2018-02-14 16:52 UTC (permalink / raw)
  To: Lorenzo Pieralisi
  Cc: mika.westerberg, rafael, rjw, hanjun.guo, robh+dt, bhelgaas,
	arnd, mark.rutland, olof, dann.frazier, andy.shevchenko, robh,
	joe, benh, linux-pci, linux-kernel, linux-acpi, linuxarm,
	minyard, devicetree, linux-arch, rdunlap

On 14/02/2018 16:16, Lorenzo Pieralisi wrote:
> On Wed, Feb 14, 2018 at 01:45:31AM +0800, John Garry wrote:
>> On some platforms (such as arm64-based hip06/hip07), access to legacy
>> ISA/LPC devices through access IO space is required, similar to x86
>> platforms. As the I/O for these devices are not memory mapped like
>> PCI/PCIE MMIO host bridges, they require special low-level device
>> operations through some host to generate IO accesses, i.e. a non-
>> transparent bridge.
>>
>> Through the logical PIO framework, hosts are able to register address
>> ranges in the logical PIO space for IO accesses. For hosts which require
>> a LLDD to generate the IO accesses, through the logical PIO framework
>> the host also registers accessors as a backend to generate the physical
>> bus transactions for IO space accesses (called indirect IO).
>>
>> When describing the indirect IO child device in APCI tables, the IO
>> resource is the host-specific address for the child (generally a
>> bus address).
>> An example is as follows:
>>   Device (LPC0) {
>>     Name (_HID, "HISI0191")  // HiSi LPC
>>     Name (_CRS, ResourceTemplate () {
>>       Memory32Fixed (ReadWrite, 0xa01b0000, 0x1000)
>>     })
>>   }
>>
>>   Device (LPC0.IPMI) {
>>     Name (_HID, "IPI0001")
>>     Name (LORS, ResourceTemplate() {
>>       QWordIO (
>>         ResourceConsumer,
>> 	MinNotFixed,     // _MIF
>> 	MaxNotFixed,     // _MAF
>> 	PosDecode,
>> 	EntireRange,
>> 	0x0,             // _GRA
>> 	0xe4,            // _MIN
>> 	0x3fff,          // _MAX
>> 	0x0,             // _TRA
>> 	0x04,            // _LEN
>> 	, ,
>> 	BTIO
>>       )
>>     })
>>
>> Since the IO resource for the child is a host-specific address,
>> special translation are required to retrieve the logical PIO address
>> for that child.
>

Hi Lorenzo,

> The problem I have with this patchset and with pretending that the ACPI
> bits are generic is that the rules used to translate resources (I am
> referring to LPC0.IPMI above) are documented _nowhere_ which means that
> making this series generic code is just wishful thinking - there are no
> bindings backing it, it will never ever be used on a platform different
> from the one you are pushing this code for and I stated this already.
>

Right, it is working on the presumption that this is how all "indirectio 
IO" hosts and children should/would be described in DSDT.

> Reworded differently - this is a Hisilicon driver it is not generic ACPI
> code; I can't see how it can be used on a multitude of platforms unless
> you specify FW level bindings.
>
>> To overcome the problem of associating this logical PIO address
>> with the child device, a scan handler is added to scan the ACPI
>> namespace for known indirect IO hosts. This scan handler creates an
>> MFD per child with the translated logical PIO address as it's IO
>> resource, as a substitute for the normal platform device which ACPI
>> would create during device enumeration.
>>
>> Signed-off-by: John Garry <john.garry@huawei.com>
>> Signed-off-by: Zhichang Yuan <yuanzhichang@hisilicon.com>
>> Signed-off-by: Gabriele Paoloni <gabriele.paoloni@huawei.com>
>> ---
>>  drivers/acpi/arm64/Makefile          |   1 +
>>  drivers/acpi/arm64/acpi_indirectio.c | 250 +++++++++++++++++++++++++++++++++++
>
> See above (and I do not understand what arm64 has to do with it).

Nothing apart from only being used by arm64 platforms today, which is 
circumstantial.

>
> I understand you need to find a place to add the:
>
> acpi_indirect_io_scan_init()
>
> to be called from core ACPI code because ACPI can't handle probe
> dependencies in any other way but other than that this patch is
> a Hisilicon ACPI driver - there is nothing generic in it (or at
> least there are no standard bindings to make it so).
>
> Whether a callback from ACPI core code (acpi_scan_init()) to a driver
> specific hook is sane or not that's the question and the only reason
> why you want to add this in drivers/acpi/arm64 rather than, say,
> drivers/bus (as you do for the DT driver).
>
> I do not know Rafael's opinion on the above, I would like to help
> you make forward progress but please understand my concerns, mostly
> on FW side.
>

I did mention an alternative in my "ping" in v12 patch 7/9 (Feb 1), but 
no response to this specific note so I kept on the same path.

Here's what I then wrote:
"I think another solution - which you may prefer - is to avoid adding
this scan handler (and all this other scan code) and add a check like
acpi_is_serial_bus_slave() [which checks the device parent versus a list 
of known indirectIO hosts] to not enumerate these children, and do it 
from the LLDD host probe instead (https://lkml.org/lkml/2017/6/16/250)"

Please consider this.

> Thanks,
> Lorenzo
>
>>  drivers/acpi/internal.h              |   5 +
>>  drivers/acpi/scan.c                  |   1 +
>>  4 files changed, 257 insertions(+)
>>  create mode 100644 drivers/acpi/arm64/acpi_indirectio.c
>>

Cheers,
John

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

* Re: [PATCH v13 7/9] ACPI: Translate the I/O range of non-MMIO devices before scanning
  2018-02-14 16:52       ` John Garry
@ 2018-02-15 11:19         ` John Garry
  -1 siblings, 0 replies; 66+ messages in thread
From: John Garry @ 2018-02-15 11:19 UTC (permalink / raw)
  To: Lorenzo Pieralisi, rafael
  Cc: mika.westerberg, rjw, hanjun.guo, robh+dt, bhelgaas, arnd,
	mark.rutland, olof, dann.frazier, andy.shevchenko, robh, joe,
	benh, linux-pci, linux-kernel, linux-acpi, linuxarm, minyard,
	devicetree, linux-arch, rdunlap

> Nothing apart from only being used by arm64 platforms today, which is
> circumstantial.
>
>>
>> I understand you need to find a place to add the:
>>
>> acpi_indirect_io_scan_init()
>>
>> to be called from core ACPI code because ACPI can't handle probe
>> dependencies in any other way but other than that this patch is
>> a Hisilicon ACPI driver - there is nothing generic in it (or at
>> least there are no standard bindings to make it so).
>>
>> Whether a callback from ACPI core code (acpi_scan_init()) to a driver
>> specific hook is sane or not that's the question and the only reason
>> why you want to add this in drivers/acpi/arm64 rather than, say,
>> drivers/bus (as you do for the DT driver).
>>
>> I do not know Rafael's opinion on the above, I would like to help
>> you make forward progress but please understand my concerns, mostly
>> on FW side.
>>
>
> I did mention an alternative in my "ping" in v12 patch 7/9 (Feb 1), but
> no response to this specific note so I kept on the same path.
>
> Here's what I then wrote:
> "I think another solution - which you may prefer - is to avoid adding
> this scan handler (and all this other scan code) and add a check like
> acpi_is_serial_bus_slave() [which checks the device parent versus a list
> of known indirectIO hosts] to not enumerate these children, and do it
> from the LLDD host probe instead (https://lkml.org/lkml/2017/6/16/250)"
>

Hi Rafael, Lorenzo,

I can avoid adding the scan handler in acpi_indirectio.c by skipping the 
child enumeration, like with this change in scan.c:

+static const struct acpi_device_id indirect_io_hosts[] = {
+    {"HISI0191", 0},    /* HiSilicon LPC host */
+    {},
+};
+
+static bool acpi_is_indirect_io_slave(struct acpi_device *device)
+{
+    struct acpi_device *parent = dev->parent;
+
+    if (!parent || acpi_match_device_ids(parent, indirect_io_hosts))
+        return false;
+
+    return true;
+}
+
  static bool acpi_is_serial_bus_slave(struct acpi_device *device)
  {
      struct list_head resource_list;
      bool is_serial_bus_slave = false;

+    if (acpi_is_indirect_io_slave(device))
+        return true;
+
      /* Macs use device properties in lieu of _CRS resources */


This means I can move all this scan code into the LLDD.

What do you think? Please let me know.

John

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

* Re: [PATCH v13 7/9] ACPI: Translate the I/O range of non-MMIO devices before scanning
@ 2018-02-15 11:19         ` John Garry
  0 siblings, 0 replies; 66+ messages in thread
From: John Garry @ 2018-02-15 11:19 UTC (permalink / raw)
  To: Lorenzo Pieralisi, rafael
  Cc: mika.westerberg, rjw, hanjun.guo, robh+dt, bhelgaas, arnd,
	mark.rutland, olof, dann.frazier, andy.shevchenko, robh, joe,
	benh, linux-pci, linux-kernel, linux-acpi, linuxarm, minyard,
	devicetree, linux-arch, rdunlap

> Nothing apart from only being used by arm64 platforms today, which is
> circumstantial.
>
>>
>> I understand you need to find a place to add the:
>>
>> acpi_indirect_io_scan_init()
>>
>> to be called from core ACPI code because ACPI can't handle probe
>> dependencies in any other way but other than that this patch is
>> a Hisilicon ACPI driver - there is nothing generic in it (or at
>> least there are no standard bindings to make it so).
>>
>> Whether a callback from ACPI core code (acpi_scan_init()) to a driver
>> specific hook is sane or not that's the question and the only reason
>> why you want to add this in drivers/acpi/arm64 rather than, say,
>> drivers/bus (as you do for the DT driver).
>>
>> I do not know Rafael's opinion on the above, I would like to help
>> you make forward progress but please understand my concerns, mostly
>> on FW side.
>>
>
> I did mention an alternative in my "ping" in v12 patch 7/9 (Feb 1), but
> no response to this specific note so I kept on the same path.
>
> Here's what I then wrote:
> "I think another solution - which you may prefer - is to avoid adding
> this scan handler (and all this other scan code) and add a check like
> acpi_is_serial_bus_slave() [which checks the device parent versus a list
> of known indirectIO hosts] to not enumerate these children, and do it
> from the LLDD host probe instead (https://lkml.org/lkml/2017/6/16/250)"
>

Hi Rafael, Lorenzo,

I can avoid adding the scan handler in acpi_indirectio.c by skipping the 
child enumeration, like with this change in scan.c:

+static const struct acpi_device_id indirect_io_hosts[] = {
+    {"HISI0191", 0},    /* HiSilicon LPC host */
+    {},
+};
+
+static bool acpi_is_indirect_io_slave(struct acpi_device *device)
+{
+    struct acpi_device *parent = dev->parent;
+
+    if (!parent || acpi_match_device_ids(parent, indirect_io_hosts))
+        return false;
+
+    return true;
+}
+
  static bool acpi_is_serial_bus_slave(struct acpi_device *device)
  {
      struct list_head resource_list;
      bool is_serial_bus_slave = false;

+    if (acpi_is_indirect_io_slave(device))
+        return true;
+
      /* Macs use device properties in lieu of _CRS resources */


This means I can move all this scan code into the LLDD.

What do you think? Please let me know.

John

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

* Re: [PATCH v13 7/9] ACPI: Translate the I/O range of non-MMIO devices before scanning
  2018-02-15 11:19         ` John Garry
@ 2018-02-15 11:47           ` Rafael J. Wysocki
  -1 siblings, 0 replies; 66+ messages in thread
From: Rafael J. Wysocki @ 2018-02-15 11:47 UTC (permalink / raw)
  To: John Garry
  Cc: Lorenzo Pieralisi, Rafael J. Wysocki, Mika Westerberg,
	Rafael J. Wysocki, Hanjun Guo, Rob Herring, Bjorn Helgaas,
	Arnd Bergmann, Mark Rutland, Olof Johansson, Dann Frazier,
	Andy Shevchenko, Rob Herring, Joe Perches,
	Benjamin Herrenschmidt, Linux PCI, Linux Kernel Mailing List,
	ACPI Devel Maling List, Linuxarm

On Thu, Feb 15, 2018 at 12:19 PM, John Garry <john.garry@huawei.com> wrote:
>> Nothing apart from only being used by arm64 platforms today, which is
>> circumstantial.
>>
>>>
>>> I understand you need to find a place to add the:
>>>
>>> acpi_indirect_io_scan_init()
>>>
>>> to be called from core ACPI code because ACPI can't handle probe
>>> dependencies in any other way but other than that this patch is
>>> a Hisilicon ACPI driver - there is nothing generic in it (or at
>>> least there are no standard bindings to make it so).
>>>
>>> Whether a callback from ACPI core code (acpi_scan_init()) to a driver
>>> specific hook is sane or not that's the question and the only reason
>>> why you want to add this in drivers/acpi/arm64 rather than, say,
>>> drivers/bus (as you do for the DT driver).
>>>
>>> I do not know Rafael's opinion on the above, I would like to help
>>> you make forward progress but please understand my concerns, mostly
>>> on FW side.
>>>
>>
>> I did mention an alternative in my "ping" in v12 patch 7/9 (Feb 1), but
>> no response to this specific note so I kept on the same path.
>>
>> Here's what I then wrote:
>> "I think another solution - which you may prefer - is to avoid adding
>> this scan handler (and all this other scan code) and add a check like
>> acpi_is_serial_bus_slave() [which checks the device parent versus a list
>> of known indirectIO hosts] to not enumerate these children, and do it
>> from the LLDD host probe instead (https://lkml.org/lkml/2017/6/16/250)"
>>
>
> Hi Rafael, Lorenzo,
>
> I can avoid adding the scan handler in acpi_indirectio.c by skipping the
> child enumeration, like with this change in scan.c:
>
> +static const struct acpi_device_id indirect_io_hosts[] = {
> +    {"HISI0191", 0},    /* HiSilicon LPC host */
> +    {},
> +};
> +
> +static bool acpi_is_indirect_io_slave(struct acpi_device *device)
> +{

Why don't you put the table definition here?

> +    struct acpi_device *parent = dev->parent;
> +
> +    if (!parent || acpi_match_device_ids(parent, indirect_io_hosts))
> +        return false;
> +
> +    return true;

return parent && !acpi_match_device_ids(parent, indirect_io_hosts);

> +}
> +
>  static bool acpi_is_serial_bus_slave(struct acpi_device *device)
>  {
>      struct list_head resource_list;
>      bool is_serial_bus_slave = false;
>
> +    if (acpi_is_indirect_io_slave(device))
> +        return true;
> +
>      /* Macs use device properties in lieu of _CRS resources */
>
>
> This means I can move all this scan code into the LLDD.
>
> What do you think? Please let me know.

If Lorenzo agrees, that will be fine by me modulo the above remarks.

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

* Re: [PATCH v13 7/9] ACPI: Translate the I/O range of non-MMIO devices before scanning
@ 2018-02-15 11:47           ` Rafael J. Wysocki
  0 siblings, 0 replies; 66+ messages in thread
From: Rafael J. Wysocki @ 2018-02-15 11:47 UTC (permalink / raw)
  To: John Garry
  Cc: Lorenzo Pieralisi, Rafael J. Wysocki, Mika Westerberg,
	Rafael J. Wysocki, Hanjun Guo, Rob Herring, Bjorn Helgaas,
	Arnd Bergmann, Mark Rutland, Olof Johansson, Dann Frazier,
	Andy Shevchenko, Rob Herring, Joe Perches,
	Benjamin Herrenschmidt, Linux PCI, Linux Kernel Mailing List,
	ACPI Devel Maling List, Linuxarm, Corey Minyard, devicetree,
	linux-arch, Randy Dunlap

On Thu, Feb 15, 2018 at 12:19 PM, John Garry <john.garry@huawei.com> wrote:
>> Nothing apart from only being used by arm64 platforms today, which is
>> circumstantial.
>>
>>>
>>> I understand you need to find a place to add the:
>>>
>>> acpi_indirect_io_scan_init()
>>>
>>> to be called from core ACPI code because ACPI can't handle probe
>>> dependencies in any other way but other than that this patch is
>>> a Hisilicon ACPI driver - there is nothing generic in it (or at
>>> least there are no standard bindings to make it so).
>>>
>>> Whether a callback from ACPI core code (acpi_scan_init()) to a driver
>>> specific hook is sane or not that's the question and the only reason
>>> why you want to add this in drivers/acpi/arm64 rather than, say,
>>> drivers/bus (as you do for the DT driver).
>>>
>>> I do not know Rafael's opinion on the above, I would like to help
>>> you make forward progress but please understand my concerns, mostly
>>> on FW side.
>>>
>>
>> I did mention an alternative in my "ping" in v12 patch 7/9 (Feb 1), but
>> no response to this specific note so I kept on the same path.
>>
>> Here's what I then wrote:
>> "I think another solution - which you may prefer - is to avoid adding
>> this scan handler (and all this other scan code) and add a check like
>> acpi_is_serial_bus_slave() [which checks the device parent versus a list
>> of known indirectIO hosts] to not enumerate these children, and do it
>> from the LLDD host probe instead (https://lkml.org/lkml/2017/6/16/250)"
>>
>
> Hi Rafael, Lorenzo,
>
> I can avoid adding the scan handler in acpi_indirectio.c by skipping the
> child enumeration, like with this change in scan.c:
>
> +static const struct acpi_device_id indirect_io_hosts[] = {
> +    {"HISI0191", 0},    /* HiSilicon LPC host */
> +    {},
> +};
> +
> +static bool acpi_is_indirect_io_slave(struct acpi_device *device)
> +{

Why don't you put the table definition here?

> +    struct acpi_device *parent = dev->parent;
> +
> +    if (!parent || acpi_match_device_ids(parent, indirect_io_hosts))
> +        return false;
> +
> +    return true;

return parent && !acpi_match_device_ids(parent, indirect_io_hosts);

> +}
> +
>  static bool acpi_is_serial_bus_slave(struct acpi_device *device)
>  {
>      struct list_head resource_list;
>      bool is_serial_bus_slave = false;
>
> +    if (acpi_is_indirect_io_slave(device))
> +        return true;
> +
>      /* Macs use device properties in lieu of _CRS resources */
>
>
> This means I can move all this scan code into the LLDD.
>
> What do you think? Please let me know.

If Lorenzo agrees, that will be fine by me modulo the above remarks.

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

* Re: [PATCH v13 7/9] ACPI: Translate the I/O range of non-MMIO devices before scanning
  2018-02-15 11:19         ` John Garry
@ 2018-02-15 12:22           ` Andy Shevchenko
  -1 siblings, 0 replies; 66+ messages in thread
From: Andy Shevchenko @ 2018-02-15 12:22 UTC (permalink / raw)
  To: John Garry
  Cc: Lorenzo Pieralisi, Rafael J. Wysocki, Mika Westerberg,
	Rafael J. Wysocki, Hanjun Guo, Rob Herring, Bjorn Helgaas,
	Arnd Bergmann, Mark Rutland, Olof Johansson, Dann Frazier,
	Rob Herring, Joe Perches, Benjamin Herrenschmidt, linux-pci,
	Linux Kernel Mailing List, ACPI Devel Maling List, Linuxarm,
	Corey Minyard

On Thu, Feb 15, 2018 at 1:19 PM, John Garry <john.garry@huawei.com> wrote:

> +static const struct acpi_device_id indirect_io_hosts[] = {
> +    {"HISI0191", 0},    /* HiSilicon LPC host */
> +    {},

Just a nit.

It seems lately this happens more often than usual, I mean a comma in
the terminator line.

If we remove it. we terminate not only at runtime, but at compile
time, which is slightly better.

> +};

-- 
With Best Regards,
Andy Shevchenko

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

* Re: [PATCH v13 7/9] ACPI: Translate the I/O range of non-MMIO devices before scanning
@ 2018-02-15 12:22           ` Andy Shevchenko
  0 siblings, 0 replies; 66+ messages in thread
From: Andy Shevchenko @ 2018-02-15 12:22 UTC (permalink / raw)
  To: John Garry
  Cc: Lorenzo Pieralisi, Rafael J. Wysocki, Mika Westerberg,
	Rafael J. Wysocki, Hanjun Guo, Rob Herring, Bjorn Helgaas,
	Arnd Bergmann, Mark Rutland, Olof Johansson, Dann Frazier,
	Rob Herring, Joe Perches, Benjamin Herrenschmidt, linux-pci,
	Linux Kernel Mailing List, ACPI Devel Maling List, Linuxarm,
	Corey Minyard, devicetree, Linux-Arch, Randy Dunlap

On Thu, Feb 15, 2018 at 1:19 PM, John Garry <john.garry@huawei.com> wrote:

> +static const struct acpi_device_id indirect_io_hosts[] = {
> +    {"HISI0191", 0},    /* HiSilicon LPC host */
> +    {},

Just a nit.

It seems lately this happens more often than usual, I mean a comma in
the terminator line.

If we remove it. we terminate not only at runtime, but at compile
time, which is slightly better.

> +};

-- 
With Best Regards,
Andy Shevchenko

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

* Re: [PATCH v13 7/9] ACPI: Translate the I/O range of non-MMIO devices before scanning
  2018-02-15 11:47           ` Rafael J. Wysocki
@ 2018-02-15 12:36               ` Lorenzo Pieralisi
  -1 siblings, 0 replies; 66+ messages in thread
From: Lorenzo Pieralisi @ 2018-02-15 12:36 UTC (permalink / raw)
  To: Rafael J. Wysocki
  Cc: John Garry, Mika Westerberg, Rafael J. Wysocki, Hanjun Guo,
	Rob Herring, Bjorn Helgaas, Arnd Bergmann, Mark Rutland,
	Olof Johansson, Dann Frazier, Andy Shevchenko, Rob Herring,
	Joe Perches, Benjamin Herrenschmidt, Linux PCI,
	Linux Kernel Mailing List, ACPI Devel Maling List, Linuxarm,
	Corey Minyard, devicet

On Thu, Feb 15, 2018 at 12:47:25PM +0100, Rafael J. Wysocki wrote:
> On Thu, Feb 15, 2018 at 12:19 PM, John Garry <john.garry-hv44wF8Li93QT0dZR+AlfA@public.gmane.org> wrote:
> >> Nothing apart from only being used by arm64 platforms today, which is
> >> circumstantial.
> >>
> >>>
> >>> I understand you need to find a place to add the:
> >>>
> >>> acpi_indirect_io_scan_init()
> >>>
> >>> to be called from core ACPI code because ACPI can't handle probe
> >>> dependencies in any other way but other than that this patch is
> >>> a Hisilicon ACPI driver - there is nothing generic in it (or at
> >>> least there are no standard bindings to make it so).
> >>>
> >>> Whether a callback from ACPI core code (acpi_scan_init()) to a driver
> >>> specific hook is sane or not that's the question and the only reason
> >>> why you want to add this in drivers/acpi/arm64 rather than, say,
> >>> drivers/bus (as you do for the DT driver).
> >>>
> >>> I do not know Rafael's opinion on the above, I would like to help
> >>> you make forward progress but please understand my concerns, mostly
> >>> on FW side.
> >>>
> >>
> >> I did mention an alternative in my "ping" in v12 patch 7/9 (Feb 1), but
> >> no response to this specific note so I kept on the same path.
> >>
> >> Here's what I then wrote:
> >> "I think another solution - which you may prefer - is to avoid adding
> >> this scan handler (and all this other scan code) and add a check like
> >> acpi_is_serial_bus_slave() [which checks the device parent versus a list
> >> of known indirectIO hosts] to not enumerate these children, and do it
> >> from the LLDD host probe instead (https://lkml.org/lkml/2017/6/16/250)"
> >>
> >
> > Hi Rafael, Lorenzo,
> >
> > I can avoid adding the scan handler in acpi_indirectio.c by skipping the
> > child enumeration, like with this change in scan.c:
> >
> > +static const struct acpi_device_id indirect_io_hosts[] = {
> > +    {"HISI0191", 0},    /* HiSilicon LPC host */
> > +    {},
> > +};
> > +
> > +static bool acpi_is_indirect_io_slave(struct acpi_device *device)
> > +{
> 
> Why don't you put the table definition here?
> 
> > +    struct acpi_device *parent = dev->parent;
> > +
> > +    if (!parent || acpi_match_device_ids(parent, indirect_io_hosts))
> > +        return false;
> > +
> > +    return true;
> 
> return parent && !acpi_match_device_ids(parent, indirect_io_hosts);
> 
> > +}
> > +
> >  static bool acpi_is_serial_bus_slave(struct acpi_device *device)
> >  {
> >      struct list_head resource_list;
> >      bool is_serial_bus_slave = false;
> >
> > +    if (acpi_is_indirect_io_slave(device))
> > +        return true;
> > +
> >      /* Macs use device properties in lieu of _CRS resources */
> >
> >
> > This means I can move all this scan code into the LLDD.
> >
> > What do you think? Please let me know.
> 
> If Lorenzo agrees, that will be fine by me modulo the above remarks.

I agree and I thank you for accepting this in core ACPI code, I think
that's much cleaner than a driver specific scan hook.

It is a shame we do not have a generic identifier for such bus in ACPI
but that won't happen overnight anyway (if ever, I think a binding for
LPC in the ACPI specs is hard to justify).

Thank you,
Lorenzo
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

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

* Re: [PATCH v13 7/9] ACPI: Translate the I/O range of non-MMIO devices before scanning
@ 2018-02-15 12:36               ` Lorenzo Pieralisi
  0 siblings, 0 replies; 66+ messages in thread
From: Lorenzo Pieralisi @ 2018-02-15 12:36 UTC (permalink / raw)
  To: Rafael J. Wysocki
  Cc: John Garry, Mika Westerberg, Rafael J. Wysocki, Hanjun Guo,
	Rob Herring, Bjorn Helgaas, Arnd Bergmann, Mark Rutland,
	Olof Johansson, Dann Frazier, Andy Shevchenko, Rob Herring,
	Joe Perches, Benjamin Herrenschmidt, Linux PCI,
	Linux Kernel Mailing List, ACPI Devel Maling List, Linuxarm,
	Corey Minyard, devicetree, linux-arch, Randy Dunlap

On Thu, Feb 15, 2018 at 12:47:25PM +0100, Rafael J. Wysocki wrote:
> On Thu, Feb 15, 2018 at 12:19 PM, John Garry <john.garry@huawei.com> wrote:
> >> Nothing apart from only being used by arm64 platforms today, which is
> >> circumstantial.
> >>
> >>>
> >>> I understand you need to find a place to add the:
> >>>
> >>> acpi_indirect_io_scan_init()
> >>>
> >>> to be called from core ACPI code because ACPI can't handle probe
> >>> dependencies in any other way but other than that this patch is
> >>> a Hisilicon ACPI driver - there is nothing generic in it (or at
> >>> least there are no standard bindings to make it so).
> >>>
> >>> Whether a callback from ACPI core code (acpi_scan_init()) to a driver
> >>> specific hook is sane or not that's the question and the only reason
> >>> why you want to add this in drivers/acpi/arm64 rather than, say,
> >>> drivers/bus (as you do for the DT driver).
> >>>
> >>> I do not know Rafael's opinion on the above, I would like to help
> >>> you make forward progress but please understand my concerns, mostly
> >>> on FW side.
> >>>
> >>
> >> I did mention an alternative in my "ping" in v12 patch 7/9 (Feb 1), but
> >> no response to this specific note so I kept on the same path.
> >>
> >> Here's what I then wrote:
> >> "I think another solution - which you may prefer - is to avoid adding
> >> this scan handler (and all this other scan code) and add a check like
> >> acpi_is_serial_bus_slave() [which checks the device parent versus a list
> >> of known indirectIO hosts] to not enumerate these children, and do it
> >> from the LLDD host probe instead (https://lkml.org/lkml/2017/6/16/250)"
> >>
> >
> > Hi Rafael, Lorenzo,
> >
> > I can avoid adding the scan handler in acpi_indirectio.c by skipping the
> > child enumeration, like with this change in scan.c:
> >
> > +static const struct acpi_device_id indirect_io_hosts[] = {
> > +    {"HISI0191", 0},    /* HiSilicon LPC host */
> > +    {},
> > +};
> > +
> > +static bool acpi_is_indirect_io_slave(struct acpi_device *device)
> > +{
> 
> Why don't you put the table definition here?
> 
> > +    struct acpi_device *parent = dev->parent;
> > +
> > +    if (!parent || acpi_match_device_ids(parent, indirect_io_hosts))
> > +        return false;
> > +
> > +    return true;
> 
> return parent && !acpi_match_device_ids(parent, indirect_io_hosts);
> 
> > +}
> > +
> >  static bool acpi_is_serial_bus_slave(struct acpi_device *device)
> >  {
> >      struct list_head resource_list;
> >      bool is_serial_bus_slave = false;
> >
> > +    if (acpi_is_indirect_io_slave(device))
> > +        return true;
> > +
> >      /* Macs use device properties in lieu of _CRS resources */
> >
> >
> > This means I can move all this scan code into the LLDD.
> >
> > What do you think? Please let me know.
> 
> If Lorenzo agrees, that will be fine by me modulo the above remarks.

I agree and I thank you for accepting this in core ACPI code, I think
that's much cleaner than a driver specific scan hook.

It is a shame we do not have a generic identifier for such bus in ACPI
but that won't happen overnight anyway (if ever, I think a binding for
LPC in the ACPI specs is hard to justify).

Thank you,
Lorenzo

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

* Re: [PATCH v13 7/9] ACPI: Translate the I/O range of non-MMIO devices before scanning
  2018-02-15 12:22           ` Andy Shevchenko
  (?)
@ 2018-02-15 12:52               ` John Garry
  -1 siblings, 0 replies; 66+ messages in thread
From: John Garry @ 2018-02-15 12:52 UTC (permalink / raw)
  To: Andy Shevchenko
  Cc: Lorenzo Pieralisi, Rafael J. Wysocki, Mika Westerberg,
	Rafael J. Wysocki, Hanjun Guo, Rob Herring, Bjorn Helgaas,
	Arnd Bergmann, Mark Rutland, Olof Johansson, Dann Frazier,
	Rob Herring, Joe Perches, Benjamin Herrenschmidt,
	linux-pci-u79uwXL29TY76Z2rM5mHXA, Linux Kernel Mailing List,
	ACPI Devel Maling List, Linuxarm, Corey Minyard, devicet

On 15/02/2018 12:22, Andy Shevchenko wrote:
> On Thu, Feb 15, 2018 at 1:19 PM, John Garry <john.garry-hv44wF8Li93QT0dZR+AlfA@public.gmane.org> wrote:
>

Hi Andy,

>> +static const struct acpi_device_id indirect_io_hosts[] = {
>> +    {"HISI0191", 0},    /* HiSilicon LPC host */
>> +    {},
>
> Just a nit.
>

I noticed that I have this in the LLDD also. I think that this may be a 
force of habit.

> It seems lately this happens more often than usual, I mean a comma in
> the terminator line.
>
> If we remove it. we terminate not only at runtime, but at compile
> time, which is slightly better.

I grepped for "{}," in the drivers folder and it gives many results (I 
do accept that some are not sentinels):
 >grep -R "{}," * | tail
watchdog/gef_wdt.c:    {},
watchdog/asm9260_wdt.c:    {},
watchdog/bcm2835_wdt.c:    {},
watchdog/digicolor_wdt.c:    {},
watchdog/bcm7038_wdt.c:    {},
watchdog/ath79_wdt.c:    {},
watchdog/riowd.c:    {},
watchdog/sbsa_gwdt.c:    {},
watchdog/sbsa_gwdt.c:    {},
watchdog/bcm_kona_wdt.c:    {},

Much appreciated,
John


--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

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

* Re: [PATCH v13 7/9] ACPI: Translate the I/O range of non-MMIO devices before scanning
@ 2018-02-15 12:52               ` John Garry
  0 siblings, 0 replies; 66+ messages in thread
From: John Garry @ 2018-02-15 12:52 UTC (permalink / raw)
  To: Andy Shevchenko
  Cc: Lorenzo Pieralisi, Rafael J. Wysocki, Mika Westerberg,
	Rafael J. Wysocki, Hanjun Guo, Rob Herring, Bjorn Helgaas,
	Arnd Bergmann, Mark Rutland, Olof Johansson, Dann Frazier,
	Rob Herring, Joe Perches, Benjamin Herrenschmidt, linux-pci,
	Linux Kernel Mailing List, ACPI Devel Maling List, Linuxarm,
	Corey Minyard, devicetree, Linux-Arch, Randy Dunlap

On 15/02/2018 12:22, Andy Shevchenko wrote:
> On Thu, Feb 15, 2018 at 1:19 PM, John Garry <john.garry@huawei.com> wrote:
>

Hi Andy,

>> +static const struct acpi_device_id indirect_io_hosts[] = {
>> +    {"HISI0191", 0},    /* HiSilicon LPC host */
>> +    {},
>
> Just a nit.
>

I noticed that I have this in the LLDD also. I think that this may be a 
force of habit.

> It seems lately this happens more often than usual, I mean a comma in
> the terminator line.
>
> If we remove it. we terminate not only at runtime, but at compile
> time, which is slightly better.

I grepped for "{}," in the drivers folder and it gives many results (I 
do accept that some are not sentinels):
 >grep -R "{}," * | tail
watchdog/gef_wdt.c:    {},
watchdog/asm9260_wdt.c:    {},
watchdog/bcm2835_wdt.c:    {},
watchdog/digicolor_wdt.c:    {},
watchdog/bcm7038_wdt.c:    {},
watchdog/ath79_wdt.c:    {},
watchdog/riowd.c:    {},
watchdog/sbsa_gwdt.c:    {},
watchdog/sbsa_gwdt.c:    {},
watchdog/bcm_kona_wdt.c:    {},

Much appreciated,
John

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

* Re: [PATCH v13 7/9] ACPI: Translate the I/O range of non-MMIO devices before scanning
@ 2018-02-15 12:52               ` John Garry
  0 siblings, 0 replies; 66+ messages in thread
From: John Garry @ 2018-02-15 12:52 UTC (permalink / raw)
  To: Andy Shevchenko
  Cc: Lorenzo Pieralisi, Rafael J. Wysocki, Mika Westerberg,
	Rafael J. Wysocki, Hanjun Guo, Rob Herring, Bjorn Helgaas,
	Arnd Bergmann, Mark Rutland, Olof Johansson, Dann Frazier,
	Rob Herring, Joe Perches, Benjamin Herrenschmidt, linux-pci,
	Linux Kernel Mailing List, ACPI Devel Maling List, Linuxarm,
	Corey Minyard, devicetree, Linux-Arch, Randy Dunlap

On 15/02/2018 12:22, Andy Shevchenko wrote:
> On Thu, Feb 15, 2018 at 1:19 PM, John Garry <john.garry@huawei.com> wrote:
>

Hi Andy,

>> +static const struct acpi_device_id indirect_io_hosts[] = {
>> +    {"HISI0191", 0},    /* HiSilicon LPC host */
>> +    {},
>
> Just a nit.
>

I noticed that I have this in the LLDD also. I think that this may be a 
force of habit.

> It seems lately this happens more often than usual, I mean a comma in
> the terminator line.
>
> If we remove it. we terminate not only at runtime, but at compile
> time, which is slightly better.

I grepped for "{}," in the drivers folder and it gives many results (I 
do accept that some are not sentinels):
 >grep -R "{}," * | tail
watchdog/gef_wdt.c:    {},
watchdog/asm9260_wdt.c:    {},
watchdog/bcm2835_wdt.c:    {},
watchdog/digicolor_wdt.c:    {},
watchdog/bcm7038_wdt.c:    {},
watchdog/ath79_wdt.c:    {},
watchdog/riowd.c:    {},
watchdog/sbsa_gwdt.c:    {},
watchdog/sbsa_gwdt.c:    {},
watchdog/bcm_kona_wdt.c:    {},

Much appreciated,
John

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

* Re: [PATCH v13 7/9] ACPI: Translate the I/O range of non-MMIO devices before scanning
  2018-02-15 12:52               ` John Garry
@ 2018-02-15 12:55                   ` Andy Shevchenko
  -1 siblings, 0 replies; 66+ messages in thread
From: Andy Shevchenko @ 2018-02-15 12:55 UTC (permalink / raw)
  To: John Garry
  Cc: Lorenzo Pieralisi, Rafael J. Wysocki, Mika Westerberg,
	Rafael J. Wysocki, Hanjun Guo, Rob Herring, Bjorn Helgaas,
	Arnd Bergmann, Mark Rutland, Olof Johansson, Dann Frazier,
	Rob Herring, Joe Perches, Benjamin Herrenschmidt,
	linux-pci-u79uwXL29TY76Z2rM5mHXA, Linux Kernel Mailing List,
	ACPI Devel Maling List, Linuxarm, Corey Minyard

On Thu, Feb 15, 2018 at 2:52 PM, John Garry <john.garry-hv44wF8Li93QT0dZR+AlfA@public.gmane.org> wrote:
> On 15/02/2018 12:22, Andy Shevchenko wrote:
>> On Thu, Feb 15, 2018 at 1:19 PM, John Garry <john.garry-hv44wF8Li93QT0dZR+AlfA@public.gmane.org> wrote:

>>> +static const struct acpi_device_id indirect_io_hosts[] = {
>>> +    {"HISI0191", 0},    /* HiSilicon LPC host */
>>> +    {},
>>
>>
>> Just a nit.
>>
>
> I noticed that I have this in the LLDD also. I think that this may be a
> force of habit.


>> It seems lately this happens more often than usual, I mean a comma in
>> the terminator line.
>>
>> If we remove it. we terminate not only at runtime, but at compile
>> time, which is slightly better.
>
>
> I grepped for "{}," in the drivers folder and it gives many results (I do
> accept that some are not sentinels):

Yes, in old code it might be, but why to cargo-culting bad (okay, less
worth) practices?

>>grep -R "{}," * | tail

Btw, `git grep ...` is much faster.

> watchdog/gef_wdt.c:    {},
> watchdog/asm9260_wdt.c:    {},
> watchdog/bcm2835_wdt.c:    {},
> watchdog/digicolor_wdt.c:    {},
> watchdog/bcm7038_wdt.c:    {},
> watchdog/ath79_wdt.c:    {},
> watchdog/riowd.c:    {},
> watchdog/sbsa_gwdt.c:    {},
> watchdog/sbsa_gwdt.c:    {},
> watchdog/bcm_kona_wdt.c:    {},



-- 
With Best Regards,
Andy Shevchenko
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

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

* Re: [PATCH v13 7/9] ACPI: Translate the I/O range of non-MMIO devices before scanning
@ 2018-02-15 12:55                   ` Andy Shevchenko
  0 siblings, 0 replies; 66+ messages in thread
From: Andy Shevchenko @ 2018-02-15 12:55 UTC (permalink / raw)
  To: John Garry
  Cc: Lorenzo Pieralisi, Rafael J. Wysocki, Mika Westerberg,
	Rafael J. Wysocki, Hanjun Guo, Rob Herring, Bjorn Helgaas,
	Arnd Bergmann, Mark Rutland, Olof Johansson, Dann Frazier,
	Rob Herring, Joe Perches, Benjamin Herrenschmidt, linux-pci,
	Linux Kernel Mailing List, ACPI Devel Maling List, Linuxarm,
	Corey Minyard, devicetree, Linux-Arch, Randy Dunlap

On Thu, Feb 15, 2018 at 2:52 PM, John Garry <john.garry@huawei.com> wrote:
> On 15/02/2018 12:22, Andy Shevchenko wrote:
>> On Thu, Feb 15, 2018 at 1:19 PM, John Garry <john.garry@huawei.com> wrote:

>>> +static const struct acpi_device_id indirect_io_hosts[] = {
>>> +    {"HISI0191", 0},    /* HiSilicon LPC host */
>>> +    {},
>>
>>
>> Just a nit.
>>
>
> I noticed that I have this in the LLDD also. I think that this may be a
> force of habit.


>> It seems lately this happens more often than usual, I mean a comma in
>> the terminator line.
>>
>> If we remove it. we terminate not only at runtime, but at compile
>> time, which is slightly better.
>
>
> I grepped for "{}," in the drivers folder and it gives many results (I do
> accept that some are not sentinels):

Yes, in old code it might be, but why to cargo-culting bad (okay, less
worth) practices?

>>grep -R "{}," * | tail

Btw, `git grep ...` is much faster.

> watchdog/gef_wdt.c:    {},
> watchdog/asm9260_wdt.c:    {},
> watchdog/bcm2835_wdt.c:    {},
> watchdog/digicolor_wdt.c:    {},
> watchdog/bcm7038_wdt.c:    {},
> watchdog/ath79_wdt.c:    {},
> watchdog/riowd.c:    {},
> watchdog/sbsa_gwdt.c:    {},
> watchdog/sbsa_gwdt.c:    {},
> watchdog/bcm_kona_wdt.c:    {},



-- 
With Best Regards,
Andy Shevchenko

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

* Re: [PATCH v13 7/9] ACPI: Translate the I/O range of non-MMIO devices before scanning
  2018-02-15 11:47           ` Rafael J. Wysocki
@ 2018-02-15 12:59             ` John Garry
  -1 siblings, 0 replies; 66+ messages in thread
From: John Garry @ 2018-02-15 12:59 UTC (permalink / raw)
  To: Rafael J. Wysocki
  Cc: Lorenzo Pieralisi, Mika Westerberg, Rafael J. Wysocki,
	Hanjun Guo, Rob Herring, Bjorn Helgaas, Arnd Bergmann,
	Mark Rutland, Olof Johansson, Dann Frazier, Andy Shevchenko,
	Rob Herring, Joe Perches, Benjamin Herrenschmidt, Linux PCI,
	Linux Kernel Mailing List, ACPI Devel Maling List, Linuxarm,
	Corey Minyard

On 15/02/2018 11:47, Rafael J. Wysocki wrote:
> On Thu, Feb 15, 2018 at 12:19 PM, John Garry <john.garry@huawei.com> wrote:
>>> Nothing apart from only being used by arm64 platforms today, which is
>>> circumstantial.
>>>
>>>>
>>>> I understand you need to find a place to add the:
>>>>
>>>> acpi_indirect_io_scan_init()
>>>>
>>>> to be called from core ACPI code because ACPI can't handle probe
>>>> dependencies in any other way but other than that this patch is
>>>> a Hisilicon ACPI driver - there is nothing generic in it (or at
>>>> least there are no standard bindings to make it so).
>>>>
>>>> Whether a callback from ACPI core code (acpi_scan_init()) to a driver
>>>> specific hook is sane or not that's the question and the only reason
>>>> why you want to add this in drivers/acpi/arm64 rather than, say,
>>>> drivers/bus (as you do for the DT driver).
>>>>
>>>> I do not know Rafael's opinion on the above, I would like to help
>>>> you make forward progress but please understand my concerns, mostly
>>>> on FW side.
>>>>
>>>
>>> I did mention an alternative in my "ping" in v12 patch 7/9 (Feb 1), but
>>> no response to this specific note so I kept on the same path.
>>>
>>> Here's what I then wrote:
>>> "I think another solution - which you may prefer - is to avoid adding
>>> this scan handler (and all this other scan code) and add a check like
>>> acpi_is_serial_bus_slave() [which checks the device parent versus a list
>>> of known indirectIO hosts] to not enumerate these children, and do it
>>> from the LLDD host probe instead (https://lkml.org/lkml/2017/6/16/250)"
>>>
>>
>> Hi Rafael, Lorenzo,
>>
>> I can avoid adding the scan handler in acpi_indirectio.c by skipping the
>> child enumeration, like with this change in scan.c:
>>

Hi Rafael,

>> +static const struct acpi_device_id indirect_io_hosts[] = {
>> +    {"HISI0191", 0},    /* HiSilicon LPC host */
>> +    {},
>> +};
>> +
>> +static bool acpi_is_indirect_io_slave(struct acpi_device *device)
>> +{
>
> Why don't you put the table definition here?
>

I can do.

>> +    struct acpi_device *parent = dev->parent;
>> +
>> +    if (!parent || acpi_match_device_ids(parent, indirect_io_hosts))
>> +        return false;
>> +
>> +    return true;
>
> return parent && !acpi_match_device_ids(parent, indirect_io_hosts);

Fine, a bit more concise

>
>> +}
>> +
>>  static bool acpi_is_serial_bus_slave(struct acpi_device *device)
>>  {
>>      struct list_head resource_list;
>>      bool is_serial_bus_slave = false;
>>
>> +    if (acpi_is_indirect_io_slave(device))
>> +        return true;
>> +
>>      /* Macs use device properties in lieu of _CRS resources */
>>
>>
>> This means I can move all this scan code into the LLDD.
>>
>> What do you think? Please let me know.
>
> If Lorenzo agrees, that will be fine by me modulo the above remarks.
>
> .

I see Lorenzo also finds this ok, so I'll go with that.

Thanks to all,
John

>

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

* Re: [PATCH v13 7/9] ACPI: Translate the I/O range of non-MMIO devices before scanning
@ 2018-02-15 12:59             ` John Garry
  0 siblings, 0 replies; 66+ messages in thread
From: John Garry @ 2018-02-15 12:59 UTC (permalink / raw)
  To: Rafael J. Wysocki
  Cc: Lorenzo Pieralisi, Mika Westerberg, Rafael J. Wysocki,
	Hanjun Guo, Rob Herring, Bjorn Helgaas, Arnd Bergmann,
	Mark Rutland, Olof Johansson, Dann Frazier, Andy Shevchenko,
	Rob Herring, Joe Perches, Benjamin Herrenschmidt, Linux PCI,
	Linux Kernel Mailing List, ACPI Devel Maling List, Linuxarm,
	Corey Minyard, devicetree, linux-arch, Randy Dunlap

On 15/02/2018 11:47, Rafael J. Wysocki wrote:
> On Thu, Feb 15, 2018 at 12:19 PM, John Garry <john.garry@huawei.com> wrote:
>>> Nothing apart from only being used by arm64 platforms today, which is
>>> circumstantial.
>>>
>>>>
>>>> I understand you need to find a place to add the:
>>>>
>>>> acpi_indirect_io_scan_init()
>>>>
>>>> to be called from core ACPI code because ACPI can't handle probe
>>>> dependencies in any other way but other than that this patch is
>>>> a Hisilicon ACPI driver - there is nothing generic in it (or at
>>>> least there are no standard bindings to make it so).
>>>>
>>>> Whether a callback from ACPI core code (acpi_scan_init()) to a driver
>>>> specific hook is sane or not that's the question and the only reason
>>>> why you want to add this in drivers/acpi/arm64 rather than, say,
>>>> drivers/bus (as you do for the DT driver).
>>>>
>>>> I do not know Rafael's opinion on the above, I would like to help
>>>> you make forward progress but please understand my concerns, mostly
>>>> on FW side.
>>>>
>>>
>>> I did mention an alternative in my "ping" in v12 patch 7/9 (Feb 1), but
>>> no response to this specific note so I kept on the same path.
>>>
>>> Here's what I then wrote:
>>> "I think another solution - which you may prefer - is to avoid adding
>>> this scan handler (and all this other scan code) and add a check like
>>> acpi_is_serial_bus_slave() [which checks the device parent versus a list
>>> of known indirectIO hosts] to not enumerate these children, and do it
>>> from the LLDD host probe instead (https://lkml.org/lkml/2017/6/16/250)"
>>>
>>
>> Hi Rafael, Lorenzo,
>>
>> I can avoid adding the scan handler in acpi_indirectio.c by skipping the
>> child enumeration, like with this change in scan.c:
>>

Hi Rafael,

>> +static const struct acpi_device_id indirect_io_hosts[] = {
>> +    {"HISI0191", 0},    /* HiSilicon LPC host */
>> +    {},
>> +};
>> +
>> +static bool acpi_is_indirect_io_slave(struct acpi_device *device)
>> +{
>
> Why don't you put the table definition here?
>

I can do.

>> +    struct acpi_device *parent = dev->parent;
>> +
>> +    if (!parent || acpi_match_device_ids(parent, indirect_io_hosts))
>> +        return false;
>> +
>> +    return true;
>
> return parent && !acpi_match_device_ids(parent, indirect_io_hosts);

Fine, a bit more concise

>
>> +}
>> +
>>  static bool acpi_is_serial_bus_slave(struct acpi_device *device)
>>  {
>>      struct list_head resource_list;
>>      bool is_serial_bus_slave = false;
>>
>> +    if (acpi_is_indirect_io_slave(device))
>> +        return true;
>> +
>>      /* Macs use device properties in lieu of _CRS resources */
>>
>>
>> This means I can move all this scan code into the LLDD.
>>
>> What do you think? Please let me know.
>
> If Lorenzo agrees, that will be fine by me modulo the above remarks.
>
> .

I see Lorenzo also finds this ok, so I'll go with that.

Thanks to all,
John

>

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

* Re: [PATCH v13 7/9] ACPI: Translate the I/O range of non-MMIO devices before scanning
  2018-02-14 16:16         ` Andy Shevchenko
  (?)
@ 2018-02-15 17:07           ` John Garry
  -1 siblings, 0 replies; 66+ messages in thread
From: John Garry @ 2018-02-15 17:07 UTC (permalink / raw)
  To: Andy Shevchenko
  Cc: Mika Westerberg, Rafael J. Wysocki, Lorenzo Pieralisi,
	Rafael J. Wysocki, Hanjun Guo, Rob Herring, Bjorn Helgaas,
	Arnd Bergmann, Mark Rutland, Olof Johansson, Dann Frazier,
	Rob Herring, Joe Perches, Benjamin Herrenschmidt, linux-pci,
	Linux Kernel Mailing List, ACPI Devel Maling List, Linuxarm,
	Corey Minyard, devic

On 14/02/2018 16:16, Andy Shevchenko wrote:
> Another approach is to use ~0UL if that is preferable.
>
>>>> >>> +       list_for_each_entry(rentry, &resource_list, node)
>>>> >>> +               resources[count++] = *rentry->res;
>>> >> It has similarities with acpi_create_platform_device().
>>> >> I guess we can utilize existing code.
>> > For sure, this particular segment is effectively same as part of
>> > acpi_create_platform_device():
> Not the same, acpi_create_platform_device() does a bit more than
> copying the resources. If it indeed makes no hurt...
>
>> >         list_for_each_entry(rentry, &resource_list, node)
>> >             acpi_platform_fill_resource(adev, rentry->res,
>> >                             &resources[count++]);
>> > So is your idea to refactor this common segment into a helper function?
> ...I would go with helper.
>

Hi Andy,

Since the plan now is that this code is no longer going to be added to 
drivers/acpi, but instead pushed to the LLDD, I am pondering whether we 
should still factor out of this common code. Opinion?

Thanks,
John

>>>> >>> +               char *name = &acpi_indirect_io_mfd_cell[count].name[0];

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

* Re: [PATCH v13 7/9] ACPI: Translate the I/O range of non-MMIO devices before scanning
@ 2018-02-15 17:07           ` John Garry
  0 siblings, 0 replies; 66+ messages in thread
From: John Garry @ 2018-02-15 17:07 UTC (permalink / raw)
  To: Andy Shevchenko
  Cc: Mika Westerberg, Rafael J. Wysocki, Lorenzo Pieralisi,
	Rafael J. Wysocki, Hanjun Guo, Rob Herring, Bjorn Helgaas,
	Arnd Bergmann, Mark Rutland, Olof Johansson, Dann Frazier,
	Rob Herring, Joe Perches, Benjamin Herrenschmidt, linux-pci,
	Linux Kernel Mailing List, ACPI Devel Maling List, Linuxarm,
	Corey Minyard, devicetree, Linux-Arch, Randy Dunlap

On 14/02/2018 16:16, Andy Shevchenko wrote:
> Another approach is to use ~0UL if that is preferable.
>
>>>> >>> +       list_for_each_entry(rentry, &resource_list, node)
>>>> >>> +               resources[count++] = *rentry->res;
>>> >> It has similarities with acpi_create_platform_device().
>>> >> I guess we can utilize existing code.
>> > For sure, this particular segment is effectively same as part of
>> > acpi_create_platform_device():
> Not the same, acpi_create_platform_device() does a bit more than
> copying the resources. If it indeed makes no hurt...
>
>> >         list_for_each_entry(rentry, &resource_list, node)
>> >             acpi_platform_fill_resource(adev, rentry->res,
>> >                             &resources[count++]);
>> > So is your idea to refactor this common segment into a helper function?
> ...I would go with helper.
>

Hi Andy,

Since the plan now is that this code is no longer going to be added to 
drivers/acpi, but instead pushed to the LLDD, I am pondering whether we 
should still factor out of this common code. Opinion?

Thanks,
John

>>>> >>> +               char *name = &acpi_indirect_io_mfd_cell[count].name[0];

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

* Re: [PATCH v13 7/9] ACPI: Translate the I/O range of non-MMIO devices before scanning
@ 2018-02-15 17:07           ` John Garry
  0 siblings, 0 replies; 66+ messages in thread
From: John Garry @ 2018-02-15 17:07 UTC (permalink / raw)
  To: Andy Shevchenko
  Cc: Mika Westerberg, Rafael J. Wysocki, Lorenzo Pieralisi,
	Rafael J. Wysocki, Hanjun Guo, Rob Herring, Bjorn Helgaas,
	Arnd Bergmann, Mark Rutland, Olof Johansson, Dann Frazier,
	Rob Herring, Joe Perches, Benjamin Herrenschmidt, linux-pci,
	Linux Kernel Mailing List, ACPI Devel Maling List, Linuxarm,
	Corey Minyard, devicetree, Linux-Arch, Randy Dunlap

On 14/02/2018 16:16, Andy Shevchenko wrote:
> Another approach is to use ~0UL if that is preferable.
>
>>>> >>> +       list_for_each_entry(rentry, &resource_list, node)
>>>> >>> +               resources[count++] = *rentry->res;
>>> >> It has similarities with acpi_create_platform_device().
>>> >> I guess we can utilize existing code.
>> > For sure, this particular segment is effectively same as part of
>> > acpi_create_platform_device():
> Not the same, acpi_create_platform_device() does a bit more than
> copying the resources. If it indeed makes no hurt...
>
>> >         list_for_each_entry(rentry, &resource_list, node)
>> >             acpi_platform_fill_resource(adev, rentry->res,
>> >                             &resources[count++]);
>> > So is your idea to refactor this common segment into a helper function?
> ...I would go with helper.
>

Hi Andy,

Since the plan now is that this code is no longer going to be added to 
drivers/acpi, but instead pushed to the LLDD, I am pondering whether we 
should still factor out of this common code. Opinion?

Thanks,
John

>>>> >>> +               char *name = &acpi_indirect_io_mfd_cell[count].name[0];

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

* Re: [PATCH v13 7/9] ACPI: Translate the I/O range of non-MMIO devices before scanning
  2018-02-15 17:07           ` John Garry
@ 2018-02-16 14:42               ` Andy Shevchenko
  -1 siblings, 0 replies; 66+ messages in thread
From: Andy Shevchenko @ 2018-02-16 14:42 UTC (permalink / raw)
  To: John Garry
  Cc: Mika Westerberg, Rafael J. Wysocki, Lorenzo Pieralisi,
	Rafael J. Wysocki, Hanjun Guo, Rob Herring, Bjorn Helgaas,
	Arnd Bergmann, Mark Rutland, Olof Johansson, Dann Frazier,
	Rob Herring, Joe Perches, Benjamin Herrenschmidt,
	linux-pci-u79uwXL29TY76Z2rM5mHXA, Linux Kernel Mailing List,
	ACPI Devel Maling List, Linuxarm, Corey Minyard

On Thu, Feb 15, 2018 at 7:07 PM, John Garry <john.garry-hv44wF8Li93QT0dZR+AlfA@public.gmane.org> wrote:
> On 14/02/2018 16:16, Andy Shevchenko wrote:

>>>>> >>> +       list_for_each_entry(rentry, &resource_list, node)
>>>>> >>> +               resources[count++] = *rentry->res;
>>>>
>>>> >> It has similarities with acpi_create_platform_device().
>>>> >> I guess we can utilize existing code.
>>>
>>> > For sure, this particular segment is effectively same as part of
>>> > acpi_create_platform_device():
>>
>> Not the same, acpi_create_platform_device() does a bit more than
>> copying the resources. If it indeed makes no hurt...
>>
>>> >         list_for_each_entry(rentry, &resource_list, node)
>>> >             acpi_platform_fill_resource(adev, rentry->res,
>>> >                             &resources[count++]);
>>> > So is your idea to refactor this common segment into a helper function?
>>
>> ...I would go with helper.
>>
>
> Hi Andy,
>
> Since the plan now is that this code is no longer going to be added to
> drivers/acpi, but instead pushed to the LLDD, I am pondering whether we
> should still factor out of this common code. Opinion?

I would still go with a common helper. Though, as first step, we can
make it lazy, i.e. put a comment in your code, like a todo notice (w/o
TODO word :-) ) to consider a common helper.

-- 
With Best Regards,
Andy Shevchenko
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

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

* Re: [PATCH v13 7/9] ACPI: Translate the I/O range of non-MMIO devices before scanning
@ 2018-02-16 14:42               ` Andy Shevchenko
  0 siblings, 0 replies; 66+ messages in thread
From: Andy Shevchenko @ 2018-02-16 14:42 UTC (permalink / raw)
  To: John Garry
  Cc: Mika Westerberg, Rafael J. Wysocki, Lorenzo Pieralisi,
	Rafael J. Wysocki, Hanjun Guo, Rob Herring, Bjorn Helgaas,
	Arnd Bergmann, Mark Rutland, Olof Johansson, Dann Frazier,
	Rob Herring, Joe Perches, Benjamin Herrenschmidt, linux-pci,
	Linux Kernel Mailing List, ACPI Devel Maling List, Linuxarm,
	Corey Minyard, devicetree, Linux-Arch, Randy Dunlap

On Thu, Feb 15, 2018 at 7:07 PM, John Garry <john.garry@huawei.com> wrote:
> On 14/02/2018 16:16, Andy Shevchenko wrote:

>>>>> >>> +       list_for_each_entry(rentry, &resource_list, node)
>>>>> >>> +               resources[count++] = *rentry->res;
>>>>
>>>> >> It has similarities with acpi_create_platform_device().
>>>> >> I guess we can utilize existing code.
>>>
>>> > For sure, this particular segment is effectively same as part of
>>> > acpi_create_platform_device():
>>
>> Not the same, acpi_create_platform_device() does a bit more than
>> copying the resources. If it indeed makes no hurt...
>>
>>> >         list_for_each_entry(rentry, &resource_list, node)
>>> >             acpi_platform_fill_resource(adev, rentry->res,
>>> >                             &resources[count++]);
>>> > So is your idea to refactor this common segment into a helper function?
>>
>> ...I would go with helper.
>>
>
> Hi Andy,
>
> Since the plan now is that this code is no longer going to be added to
> drivers/acpi, but instead pushed to the LLDD, I am pondering whether we
> should still factor out of this common code. Opinion?

I would still go with a common helper. Though, as first step, we can
make it lazy, i.e. put a comment in your code, like a todo notice (w/o
TODO word :-) ) to consider a common helper.

-- 
With Best Regards,
Andy Shevchenko

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

* Re: [PATCH v13 7/9] ACPI: Translate the I/O range of non-MMIO devices before scanning
  2018-02-16 14:42               ` Andy Shevchenko
  (?)
@ 2018-02-16 14:48                   ` John Garry
  -1 siblings, 0 replies; 66+ messages in thread
From: John Garry @ 2018-02-16 14:48 UTC (permalink / raw)
  To: Andy Shevchenko
  Cc: Mika Westerberg, Rafael J. Wysocki, Lorenzo Pieralisi,
	Rafael J. Wysocki, Hanjun Guo, Rob Herring, Bjorn Helgaas,
	Arnd Bergmann, Mark Rutland, Olof Johansson, Dann Frazier,
	Rob Herring, Joe Perches, Benjamin Herrenschmidt,
	linux-pci-u79uwXL29TY76Z2rM5mHXA, Linux Kernel Mailing List,
	ACPI Devel Maling List, Linuxarm, Corey Minyard, devic

On 16/02/2018 14:42, Andy Shevchenko wrote:
> On Thu, Feb 15, 2018 at 7:07 PM, John Garry <john.garry-hv44wF8Li93QT0dZR+AlfA@public.gmane.org> wrote:
>> On 14/02/2018 16:16, Andy Shevchenko wrote:
>
>>>>>>>>> +       list_for_each_entry(rentry, &resource_list, node)
>>>>>>>>> +               resources[count++] = *rentry->res;
>>>>>
>>>>>>> It has similarities with acpi_create_platform_device().
>>>>>>> I guess we can utilize existing code.
>>>>
>>>>> For sure, this particular segment is effectively same as part of
>>>>> acpi_create_platform_device():
>>>
>>> Not the same, acpi_create_platform_device() does a bit more than
>>> copying the resources. If it indeed makes no hurt...
>>>
>>>>>         list_for_each_entry(rentry, &resource_list, node)
>>>>>             acpi_platform_fill_resource(adev, rentry->res,
>>>>>                             &resources[count++]);
>>>>> So is your idea to refactor this common segment into a helper function?
>>>
>>> ...I would go with helper.
>>>
>>
>> Hi Andy,
>>
>> Since the plan now is that this code is no longer going to be added to
>> drivers/acpi, but instead pushed to the LLDD, I am pondering whether we
>> should still factor out of this common code. Opinion?
>
> I would still go with a common helper. Though, as first step, we can
> make it lazy, i.e. put a comment in your code, like a todo notice (w/o
> TODO word :-) ) to consider a common helper.

Fine, I was also thinking that I don't want to do this now as it could 
make merging the patchset more complex. For now, the ACPI change I plan 
creates no dependencies.

Cheers,
John

>


--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

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

* Re: [PATCH v13 7/9] ACPI: Translate the I/O range of non-MMIO devices before scanning
@ 2018-02-16 14:48                   ` John Garry
  0 siblings, 0 replies; 66+ messages in thread
From: John Garry @ 2018-02-16 14:48 UTC (permalink / raw)
  To: Andy Shevchenko
  Cc: Mika Westerberg, Rafael J. Wysocki, Lorenzo Pieralisi,
	Rafael J. Wysocki, Hanjun Guo, Rob Herring, Bjorn Helgaas,
	Arnd Bergmann, Mark Rutland, Olof Johansson, Dann Frazier,
	Rob Herring, Joe Perches, Benjamin Herrenschmidt, linux-pci,
	Linux Kernel Mailing List, ACPI Devel Maling List, Linuxarm,
	Corey Minyard, devicetree, Linux-Arch, Randy Dunlap

On 16/02/2018 14:42, Andy Shevchenko wrote:
> On Thu, Feb 15, 2018 at 7:07 PM, John Garry <john.garry@huawei.com> wrote:
>> On 14/02/2018 16:16, Andy Shevchenko wrote:
>
>>>>>>>>> +       list_for_each_entry(rentry, &resource_list, node)
>>>>>>>>> +               resources[count++] = *rentry->res;
>>>>>
>>>>>>> It has similarities with acpi_create_platform_device().
>>>>>>> I guess we can utilize existing code.
>>>>
>>>>> For sure, this particular segment is effectively same as part of
>>>>> acpi_create_platform_device():
>>>
>>> Not the same, acpi_create_platform_device() does a bit more than
>>> copying the resources. If it indeed makes no hurt...
>>>
>>>>>         list_for_each_entry(rentry, &resource_list, node)
>>>>>             acpi_platform_fill_resource(adev, rentry->res,
>>>>>                             &resources[count++]);
>>>>> So is your idea to refactor this common segment into a helper function?
>>>
>>> ...I would go with helper.
>>>
>>
>> Hi Andy,
>>
>> Since the plan now is that this code is no longer going to be added to
>> drivers/acpi, but instead pushed to the LLDD, I am pondering whether we
>> should still factor out of this common code. Opinion?
>
> I would still go with a common helper. Though, as first step, we can
> make it lazy, i.e. put a comment in your code, like a todo notice (w/o
> TODO word :-) ) to consider a common helper.

Fine, I was also thinking that I don't want to do this now as it could 
make merging the patchset more complex. For now, the ACPI change I plan 
creates no dependencies.

Cheers,
John

>

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

* Re: [PATCH v13 7/9] ACPI: Translate the I/O range of non-MMIO devices before scanning
@ 2018-02-16 14:48                   ` John Garry
  0 siblings, 0 replies; 66+ messages in thread
From: John Garry @ 2018-02-16 14:48 UTC (permalink / raw)
  To: Andy Shevchenko
  Cc: Mika Westerberg, Rafael J. Wysocki, Lorenzo Pieralisi,
	Rafael J. Wysocki, Hanjun Guo, Rob Herring, Bjorn Helgaas,
	Arnd Bergmann, Mark Rutland, Olof Johansson, Dann Frazier,
	Rob Herring, Joe Perches, Benjamin Herrenschmidt, linux-pci,
	Linux Kernel Mailing List, ACPI Devel Maling List, Linuxarm,
	Corey Minyard, devicetree, Linux-Arch, Randy Dunlap

On 16/02/2018 14:42, Andy Shevchenko wrote:
> On Thu, Feb 15, 2018 at 7:07 PM, John Garry <john.garry@huawei.com> wrote:
>> On 14/02/2018 16:16, Andy Shevchenko wrote:
>
>>>>>>>>> +       list_for_each_entry(rentry, &resource_list, node)
>>>>>>>>> +               resources[count++] = *rentry->res;
>>>>>
>>>>>>> It has similarities with acpi_create_platform_device().
>>>>>>> I guess we can utilize existing code.
>>>>
>>>>> For sure, this particular segment is effectively same as part of
>>>>> acpi_create_platform_device():
>>>
>>> Not the same, acpi_create_platform_device() does a bit more than
>>> copying the resources. If it indeed makes no hurt...
>>>
>>>>>         list_for_each_entry(rentry, &resource_list, node)
>>>>>             acpi_platform_fill_resource(adev, rentry->res,
>>>>>                             &resources[count++]);
>>>>> So is your idea to refactor this common segment into a helper function?
>>>
>>> ...I would go with helper.
>>>
>>
>> Hi Andy,
>>
>> Since the plan now is that this code is no longer going to be added to
>> drivers/acpi, but instead pushed to the LLDD, I am pondering whether we
>> should still factor out of this common code. Opinion?
>
> I would still go with a common helper. Though, as first step, we can
> make it lazy, i.e. put a comment in your code, like a todo notice (w/o
> TODO word :-) ) to consider a common helper.

Fine, I was also thinking that I don't want to do this now as it could 
make merging the patchset more complex. For now, the ACPI change I plan 
creates no dependencies.

Cheers,
John

>

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

end of thread, other threads:[~2018-02-16 14:49 UTC | newest]

Thread overview: 66+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2018-02-13 17:45 [PATCH v13 0/9] LPC: legacy ISA I/O support John Garry
2018-02-13 17:45 ` John Garry
2018-02-13 17:45 ` [PATCH v13 1/9] LIB: Introduce a generic PIO mapping method John Garry
2018-02-13 17:45   ` John Garry
     [not found]   ` <1518543933-22456-2-git-send-email-john.garry-hv44wF8Li93QT0dZR+AlfA@public.gmane.org>
2018-02-13 23:05     ` dann frazier
2018-02-13 23:05       ` dann frazier
2018-02-14 16:13       ` John Garry
2018-02-14 16:13         ` John Garry
2018-02-13 17:45 ` [PATCH v13 2/9] PCI: Remove unused __weak attribute in pci_register_io_range() John Garry
2018-02-13 17:45   ` John Garry
2018-02-13 17:45 ` [PATCH v13 3/9] PCI: Add fwnode handler as input param of pci_register_io_range() John Garry
2018-02-13 17:45   ` John Garry
2018-02-13 17:45 ` [PATCH v13 4/9] PCI: Apply the new generic I/O management on PCI IO hosts John Garry
2018-02-13 17:45   ` John Garry
2018-02-13 22:57   ` dann frazier
2018-02-14 15:42     ` John Garry
2018-02-14 15:42       ` John Garry
2018-02-14 16:05       ` dann frazier
2018-02-13 17:45 ` [PATCH v13 5/9] OF: Add missing I/O range exception for indirect-IO devices John Garry
2018-02-13 17:45   ` John Garry
     [not found] ` <1518543933-22456-1-git-send-email-john.garry-hv44wF8Li93QT0dZR+AlfA@public.gmane.org>
2018-02-13 17:45   ` [PATCH v13 6/9] LPC: Support the LPC host on Hip06/Hip07 with DT bindings John Garry
2018-02-13 17:45     ` John Garry
2018-02-13 17:45     ` John Garry
2018-02-13 17:45 ` [PATCH v13 7/9] ACPI: Translate the I/O range of non-MMIO devices before scanning John Garry
2018-02-13 17:45   ` John Garry
2018-02-14  9:21   ` Rafael J. Wysocki
2018-02-14  9:21     ` Rafael J. Wysocki
2018-02-14 12:48     ` John Garry
2018-02-14 12:48       ` John Garry
2018-02-14 13:53   ` Andy Shevchenko
2018-02-14 13:53     ` Andy Shevchenko
2018-02-14 15:33     ` John Garry
2018-02-14 15:33       ` John Garry
2018-02-14 15:33       ` John Garry
2018-02-14 16:16       ` Andy Shevchenko
2018-02-14 16:16         ` Andy Shevchenko
2018-02-15 17:07         ` John Garry
2018-02-15 17:07           ` John Garry
2018-02-15 17:07           ` John Garry
     [not found]           ` <59e5293f-0ea5-12f4-27db-b13bbcf0918b-hv44wF8Li93QT0dZR+AlfA@public.gmane.org>
2018-02-16 14:42             ` Andy Shevchenko
2018-02-16 14:42               ` Andy Shevchenko
     [not found]               ` <CAHp75VeTQg+A9o5Ox-k_9Qx8=JPEnqg1taVjcd8Zd2rfj_TMog-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
2018-02-16 14:48                 ` John Garry
2018-02-16 14:48                   ` John Garry
2018-02-16 14:48                   ` John Garry
2018-02-14 16:16   ` Lorenzo Pieralisi
2018-02-14 16:52     ` John Garry
2018-02-14 16:52       ` John Garry
2018-02-15 11:19       ` John Garry
2018-02-15 11:19         ` John Garry
2018-02-15 11:47         ` Rafael J. Wysocki
2018-02-15 11:47           ` Rafael J. Wysocki
     [not found]           ` <CAJZ5v0jGvo+cutmvi3WSe1JNvQ1UvZhJr1mQ76=8=1E5Gq+iRg-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
2018-02-15 12:36             ` Lorenzo Pieralisi
2018-02-15 12:36               ` Lorenzo Pieralisi
2018-02-15 12:59           ` John Garry
2018-02-15 12:59             ` John Garry
2018-02-15 12:22         ` Andy Shevchenko
2018-02-15 12:22           ` Andy Shevchenko
     [not found]           ` <CAHp75VfFKcnUVQwPUxynzp88RXWdV8VEqtXxd0=_Q_GJKO6UpQ-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
2018-02-15 12:52             ` John Garry
2018-02-15 12:52               ` John Garry
2018-02-15 12:52               ` John Garry
     [not found]               ` <f7b3de31-46fb-f80e-511e-651fa815bddf-hv44wF8Li93QT0dZR+AlfA@public.gmane.org>
2018-02-15 12:55                 ` Andy Shevchenko
2018-02-15 12:55                   ` Andy Shevchenko
2018-02-13 17:45 ` [PATCH v13 8/9] LPC, ACPI: Add the HISI LPC ACPI support John Garry
2018-02-13 17:45   ` John Garry
2018-02-13 17:45 ` [PATCH v13 9/9] MAINTAINERS: Add maintainer for HiSilicon LPC driver John Garry
2018-02-13 17:45   ` John Garry

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.