Devicetree
 help / color / mirror / Atom feed
* [PATCH v3 3/7] i2c: i2c-cbus-gpio: Add vendor prefix to retu node in example
From: Javier Martinez Canillas @ 2017-04-01  7:18 UTC (permalink / raw)
  To: linux-kernel
  Cc: devicetree, Rob Herring, Lee Jones, Javier Martinez Canillas,
	Wolfram Sang, linux-i2c, Mark Rutland
In-Reply-To: <20170401071854.23198-1-javier@osg.samsung.com>

The example contains a device node for a retu-mfd device,
but its compatible string doesn't have a vendor prefix.

Signed-off-by: Javier Martinez Canillas <javier@osg.samsung.com>
---

Changes in v3: None
Changes in v2: None

 Documentation/devicetree/bindings/i2c/i2c-cbus-gpio.txt | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/Documentation/devicetree/bindings/i2c/i2c-cbus-gpio.txt b/Documentation/devicetree/bindings/i2c/i2c-cbus-gpio.txt
index 8ce9cd2855b5..32bcb1943f00 100644
--- a/Documentation/devicetree/bindings/i2c/i2c-cbus-gpio.txt
+++ b/Documentation/devicetree/bindings/i2c/i2c-cbus-gpio.txt
@@ -21,7 +21,7 @@ i2c@0 {
 	#size-cells = <0>;
 
 	retu-mfd: retu@1 {
-		compatible = "retu-mfd";
+		compatible = "nokia,retu-mfd";
 		reg = <0x1>;
 	};
 };
-- 
2.9.3

^ permalink raw reply related

* [PATCH v3 2/7] mfd: retu: Add OF device ID table
From: Javier Martinez Canillas @ 2017-04-01  7:18 UTC (permalink / raw)
  To: linux-kernel; +Cc: devicetree, Rob Herring, Lee Jones, Javier Martinez Canillas
In-Reply-To: <20170401071854.23198-1-javier@osg.samsung.com>

The driver doesn't have a struct of_device_id table but supported devices
are registered via Device Trees. This is working on the assumption that a
I2C device registered via OF will always match a legacy I2C device ID and
that the MODALIAS reported will always be of the form i2c:<device>.

But this could change in the future so the correct approach is to have a
OF device ID table if the devices are registered via OF.

Signed-off-by: Javier Martinez Canillas <javier@osg.samsung.com>

---

Changes in v3:
- Add a vendor prefix to the compatible string (Rob Herring).

Changes in v2:
- Don't use of_match_ptr() to avoid build warning when CONFIG_OF is disabled.

 drivers/mfd/retu-mfd.c | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/drivers/mfd/retu-mfd.c b/drivers/mfd/retu-mfd.c
index d4c114abeb75..937a1c21eec4 100644
--- a/drivers/mfd/retu-mfd.c
+++ b/drivers/mfd/retu-mfd.c
@@ -308,9 +308,17 @@ static const struct i2c_device_id retu_id[] = {
 };
 MODULE_DEVICE_TABLE(i2c, retu_id);
 
+static const struct of_device_id retu_of_match[] = {
+	{ .compatible = "nokia,retu-mfd" },
+	{ .compatible = "nokia,tahvo-mfd" },
+	{ }
+};
+MODULE_DEVICE_TABLE(of, retu_of_match);
+
 static struct i2c_driver retu_driver = {
 	.driver		= {
 		.name = "retu-mfd",
+		.of_match_table = retu_of_match,
 	},
 	.probe		= retu_probe,
 	.remove		= retu_remove,
-- 
2.9.3

^ permalink raw reply related

* [PATCH v3 1/7] mfd: Add Device Tree bindings document for retu/tahvo ASIC chips
From: Javier Martinez Canillas @ 2017-04-01  7:18 UTC (permalink / raw)
  To: linux-kernel
  Cc: devicetree, Rob Herring, Lee Jones, Javier Martinez Canillas,
	Mark Rutland
In-Reply-To: <20170401071854.23198-1-javier@osg.samsung.com>

There are Device Tree source files defining a device node for the
retu/tahvo I2C chip, but there isn't a DT binding document for it.

Signed-off-by: Javier Martinez Canillas <javier@osg.samsung.com>
---

Changes in v3: None
Changes in v2: None

 Documentation/devicetree/bindings/mfd/retu.txt | 19 +++++++++++++++++++
 1 file changed, 19 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/mfd/retu.txt

diff --git a/Documentation/devicetree/bindings/mfd/retu.txt b/Documentation/devicetree/bindings/mfd/retu.txt
new file mode 100644
index 000000000000..2309e599a731
--- /dev/null
+++ b/Documentation/devicetree/bindings/mfd/retu.txt
@@ -0,0 +1,19 @@
+* Device tree bindings for Nokia Retu and Tahvo multi-function device
+
+Retu and Tahvo are a multi-function devices found on Nokia Internet
+Tablets (770, N800 and N810).
+
+Required properties:
+- compatible:		"nokia,retu-mfd" or "nokia,tahvo-mfd"
+- reg:			Specifies the I2C slave address of the ASIC chip
+
+Example:
+
+i2c0 {
+	retu_mfd: retu@1 {
+		compatible = "nokia,retu-mfd";
+		interrupt-parent = <&gpio4>;
+		interrupts = <12 IRQ_TYPE_EDGE_RISING>;
+		reg = <0x1>;
+	};
+};
-- 
2.9.3

^ permalink raw reply related

* [PATCH v3 0/7] mfd: Add OF device table to I2C drivers that are missing it
From: Javier Martinez Canillas @ 2017-04-01  7:18 UTC (permalink / raw)
  To: linux-kernel-u79uwXL29TY76Z2rM5mHXA
  Cc: devicetree-u79uwXL29TY76Z2rM5mHXA, Rob Herring, Lee Jones,
	Javier Martinez Canillas, Benoît Cousson, Wolfram Sang,
	linux-i2c-u79uwXL29TY76Z2rM5mHXA, Mark Rutland,
	linux-omap-u79uwXL29TY76Z2rM5mHXA, Russell King,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, Tony Lindgren

Hello,

This series add OF device ID tables to mfd I2C drivers whose devices are
either used in Device Tree source files or are listed in binding docs as
a compatible string.

That's done because the plan is to change the I2C core to report proper OF
modaliases instead of always reporting a MODALIAS=i2c:<foo> regardless if
a device was registered via DT or using the legacy platform data mechanism.

So these patches will make sure that mfd I2C drivers modules will continue
to be autoloaded once the I2C core is changed to report proper OF modalias.

Users didn't have a vendor prefix in the used compatible strings, but since
there wasn't a DT binding document for these drivers, it can be said that
were working for mere luck and so this series fixes the users and add a DT
binding doc for the drivers.

It's safe to apply the patches independently.

Best regards,
Javier

Changes in v3:
- Add a vendor prefix to the compatible string (Rob Herring).
- Add a vendor prefix to the compatible string (Rob Herring).

Changes in v2:
- Don't use of_match_ptr() to avoid build warning when CONFIG_OF is disabled.
- Don't use of_match_ptr() to avoid build warning when CONFIG_OF is disabled.

Javier Martinez Canillas (7):
  mfd: Add Device Tree bindings document for retu/tahvo ASIC chips
  mfd: retu: Add OF device ID table
  i2c: i2c-cbus-gpio: Add vendor prefix to retu node in example
  ARM: dts: n8x0: Add vendor prefix to retu node
  mfd: Add Device Tree bindings document for TI tps6105x chip
  mfd: tps6105x: Add OF device ID table
  ARM: ux500: Add vendor prefix to tps61052 node

 .../devicetree/bindings/i2c/i2c-cbus-gpio.txt         |  2 +-
 Documentation/devicetree/bindings/mfd/retu.txt        | 19 +++++++++++++++++++
 Documentation/devicetree/bindings/mfd/tps6105x.txt    | 17 +++++++++++++++++
 arch/arm/boot/dts/omap2420-n8x0-common.dtsi           |  2 +-
 arch/arm/boot/dts/ste-hrefprev60.dtsi                 |  2 +-
 drivers/mfd/retu-mfd.c                                |  8 ++++++++
 drivers/mfd/tps6105x.c                                |  8 ++++++++
 7 files changed, 55 insertions(+), 3 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/mfd/retu.txt
 create mode 100644 Documentation/devicetree/bindings/mfd/tps6105x.txt

-- 
2.9.3

--
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

* Re: [PATCH V8 1/6] LIBIO: Introduce a generic PIO mapping method
From: kbuild test robot @ 2017-04-01  6:31 UTC (permalink / raw)
  Cc: kbuild-all, catalin.marinas, will.deacon, robh+dt, frowand.list,
	bhelgaas, rafael, arnd, linux-arm-kernel, mark.rutland,
	brian.starkey, olof, lorenzo.pieralisi, benh, linux-kernel,
	linux-acpi, linuxarm, devicetree, linux-pci, minyard, zourongrong,
	john.garry, gabriele.paoloni, zhichang.yuan02, kantyzc, xuwei5,
	zhichang.yuan
In-Reply-To: <1490887619-61732-2-git-send-email-yuanzhichang@hisilicon.com>

[-- Attachment #1: Type: text/plain, Size: 29623 bytes --]

Hi zhichang.yuan,

[auto build test ERROR on linus/master]
[also build test ERROR on v4.11-rc4 next-20170331]
[if your patch is applied to the wrong git tree, please drop us a note to help improve the system]

url:    https://github.com/0day-ci/linux/commits/zhichang-yuan/LIBIO-Introduce-a-generic-PIO-mapping-method/20170401-104801
config: m68k-m5475evb_defconfig (attached as .config)
compiler: m68k-linux-gcc (GCC) 4.9.0
reproduce:
        wget https://raw.githubusercontent.com/01org/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
        chmod +x ~/bin/make.cross
        # save the attached .config to linux build tree
        make.cross ARCH=m68k 

All error/warnings (new ones prefixed by >>):

   lib/logic_pio.c:32:50: error: 'PIO_MAX_SECT' undeclared here (not in a function)
    static struct logic_pio_root logic_pio_root_list[PIO_MAX_SECT] = {
                                                     ^
   lib/logic_pio.c:52:3: error: 'PIO_CPU_MMIO' undeclared here (not in a function)
     [PIO_CPU_MMIO] = {
      ^
   lib/logic_pio.c:52:2: error: array index in initializer not of integer type
     [PIO_CPU_MMIO] = {
     ^
   lib/logic_pio.c:52:2: error: (near initialization for 'logic_pio_root_list')
   lib/logic_pio.c:53:3: error: field name not in record or union initializer
      .sec_head = LIST_HEAD_INIT(logic_pio_root_list[PIO_CPU_MMIO].sec_head),
      ^
   lib/logic_pio.c:53:3: error: (near initialization for 'logic_pio_root_list')
   lib/logic_pio.c:54:3: error: field name not in record or union initializer
      .sec_min = PIO_SECT_MIN(PIO_CPU_MMIO),
      ^
   lib/logic_pio.c:54:3: error: (near initialization for 'logic_pio_root_list')
   lib/logic_pio.c:54:3: error: implicit declaration of function 'PIO_SECT_MIN' [-Werror=implicit-function-declaration]
   lib/logic_pio.c:55:3: error: field name not in record or union initializer
      .sec_max = PIO_SECT_MAX(PIO_CPU_MMIO),
      ^
   lib/logic_pio.c:55:3: error: (near initialization for 'logic_pio_root_list')
   lib/logic_pio.c:55:3: error: implicit declaration of function 'PIO_SECT_MAX' [-Werror=implicit-function-declaration]
   In file included from include/linux/list.h:8:0,
                    from include/linux/kobject.h:20,
                    from include/linux/of.h:21,
                    from lib/logic_pio.c:18:
   lib/logic_pio.c: In function 'logic_pio_find_range_byaddr':
>> include/linux/rculist.h:351:49: error: dereferencing pointer to incomplete type
     for (pos = list_entry_rcu((head)->next, typeof(*pos), member); \
                                                    ^
   include/linux/kernel.h:852:18: note: in definition of macro 'container_of'
     const typeof( ((type *)0)->member ) *__mptr = (ptr); \
                     ^
   include/linux/rculist.h:351:13: note: in expansion of macro 'list_entry_rcu'
     for (pos = list_entry_rcu((head)->next, typeof(*pos), member); \
                ^
   lib/logic_pio.c:77:2: note: in expansion of macro 'list_for_each_entry_rcu'
     list_for_each_entry_rcu(range, &io_range_list, list) {
     ^
   include/linux/kernel.h:852:48: warning: initialization from incompatible pointer type
     const typeof( ((type *)0)->member ) *__mptr = (ptr); \
                                                   ^
   include/linux/rculist.h:277:2: note: in expansion of macro 'container_of'
     container_of(lockless_dereference(ptr), type, member)
     ^
   include/linux/rculist.h:351:13: note: in expansion of macro 'list_entry_rcu'
     for (pos = list_entry_rcu((head)->next, typeof(*pos), member); \
                ^
   lib/logic_pio.c:77:2: note: in expansion of macro 'list_for_each_entry_rcu'
     list_for_each_entry_rcu(range, &io_range_list, list) {
     ^
>> include/linux/rculist.h:351:49: error: dereferencing pointer to incomplete type
     for (pos = list_entry_rcu((head)->next, typeof(*pos), member); \
                                                    ^
   include/linux/kernel.h:853:3: note: in definition of macro 'container_of'
     (type *)( (char *)__mptr - offsetof(type,member) );})
      ^
   include/linux/rculist.h:351:13: note: in expansion of macro 'list_entry_rcu'
     for (pos = list_entry_rcu((head)->next, typeof(*pos), member); \
                ^
   lib/logic_pio.c:77:2: note: in expansion of macro 'list_for_each_entry_rcu'
     list_for_each_entry_rcu(range, &io_range_list, list) {
     ^
   In file included from include/linux/compiler.h:62:0,
                    from include/uapi/linux/stddef.h:1,
                    from include/linux/stddef.h:4,
                    from include/uapi/linux/posix_types.h:4,
                    from include/uapi/linux/types.h:13,
                    from include/linux/types.h:5,
                    from include/linux/of.h:18,
                    from lib/logic_pio.c:18:
>> include/linux/rculist.h:351:49: error: dereferencing pointer to incomplete type
     for (pos = list_entry_rcu((head)->next, typeof(*pos), member); \
                                                    ^
   include/linux/compiler-gcc.h:161:21: note: in definition of macro '__compiler_offsetof'
     __builtin_offsetof(a, b)
                        ^
   include/linux/kernel.h:853:29: note: in expansion of macro 'offsetof'
     (type *)( (char *)__mptr - offsetof(type,member) );})
                                ^
   include/linux/rculist.h:277:2: note: in expansion of macro 'container_of'
     container_of(lockless_dereference(ptr), type, member)
     ^
   include/linux/rculist.h:351:13: note: in expansion of macro 'list_entry_rcu'
     for (pos = list_entry_rcu((head)->next, typeof(*pos), member); \
                ^
   lib/logic_pio.c:77:2: note: in expansion of macro 'list_for_each_entry_rcu'
     list_for_each_entry_rcu(range, &io_range_list, list) {
     ^
   In file included from include/linux/pid.h:4:0,
                    from include/linux/sched.h:13,
                    from arch/m68k/include/asm/pgtable_mm.h:10,
                    from arch/m68k/include/asm/pgtable.h:4,
                    from include/linux/mm.h:68,
                    from lib/logic_pio.c:20:
   include/linux/rculist.h:352:7: error: dereferencing pointer to incomplete type
      &pos->member != (head); \
          ^
   lib/logic_pio.c:77:2: note: in expansion of macro 'list_for_each_entry_rcu'
     list_for_each_entry_rcu(range, &io_range_list, list) {
     ^
   In file included from include/linux/list.h:8:0,
                    from include/linux/kobject.h:20,
                    from include/linux/of.h:21,
                    from lib/logic_pio.c:18:
   include/linux/rculist.h:353:49: error: dereferencing pointer to incomplete type
      pos = list_entry_rcu(pos->member.next, typeof(*pos), member))
                                                    ^
   include/linux/kernel.h:852:18: note: in definition of macro 'container_of'
     const typeof( ((type *)0)->member ) *__mptr = (ptr); \
                     ^
   include/linux/rculist.h:353:9: note: in expansion of macro 'list_entry_rcu'
      pos = list_entry_rcu(pos->member.next, typeof(*pos), member))
            ^
   lib/logic_pio.c:77:2: note: in expansion of macro 'list_for_each_entry_rcu'
     list_for_each_entry_rcu(range, &io_range_list, list) {
     ^
   include/linux/rculist.h:353:27: error: dereferencing pointer to incomplete type
      pos = list_entry_rcu(pos->member.next, typeof(*pos), member))
                              ^
   include/linux/kernel.h:852:49: note: in definition of macro 'container_of'
     const typeof( ((type *)0)->member ) *__mptr = (ptr); \
                                                    ^
>> include/linux/rculist.h:277:15: note: in expansion of macro 'lockless_dereference'
     container_of(lockless_dereference(ptr), type, member)
                  ^
   include/linux/rculist.h:353:9: note: in expansion of macro 'list_entry_rcu'
      pos = list_entry_rcu(pos->member.next, typeof(*pos), member))
            ^
   lib/logic_pio.c:77:2: note: in expansion of macro 'list_for_each_entry_rcu'
     list_for_each_entry_rcu(range, &io_range_list, list) {
     ^
   include/linux/rculist.h:353:27: error: dereferencing pointer to incomplete type
      pos = list_entry_rcu(pos->member.next, typeof(*pos), member))
                              ^
   include/linux/kernel.h:852:49: note: in definition of macro 'container_of'
     const typeof( ((type *)0)->member ) *__mptr = (ptr); \
                                                    ^
   include/linux/compiler.h:323:22: note: in expansion of macro '__READ_ONCE'
    #define READ_ONCE(x) __READ_ONCE(x, 1)
                         ^
>> include/linux/compiler.h:574:26: note: in expansion of macro 'READ_ONCE'
     typeof(p) _________p1 = READ_ONCE(p); \
                             ^
>> include/linux/rculist.h:277:15: note: in expansion of macro 'lockless_dereference'
     container_of(lockless_dereference(ptr), type, member)
                  ^
   include/linux/rculist.h:353:9: note: in expansion of macro 'list_entry_rcu'
      pos = list_entry_rcu(pos->member.next, typeof(*pos), member))
            ^
   lib/logic_pio.c:77:2: note: in expansion of macro 'list_for_each_entry_rcu'
     list_for_each_entry_rcu(range, &io_range_list, list) {
     ^
   include/linux/rculist.h:353:27: error: dereferencing pointer to incomplete type
      pos = list_entry_rcu(pos->member.next, typeof(*pos), member))
                              ^
   include/linux/kernel.h:852:49: note: in definition of macro 'container_of'
     const typeof( ((type *)0)->member ) *__mptr = (ptr); \
                                                    ^
   include/linux/compiler.h:323:22: note: in expansion of macro '__READ_ONCE'
    #define READ_ONCE(x) __READ_ONCE(x, 1)
                         ^
>> include/linux/compiler.h:574:26: note: in expansion of macro 'READ_ONCE'
     typeof(p) _________p1 = READ_ONCE(p); \
                             ^
>> include/linux/rculist.h:277:15: note: in expansion of macro 'lockless_dereference'
     container_of(lockless_dereference(ptr), type, member)
                  ^
   include/linux/rculist.h:353:9: note: in expansion of macro 'list_entry_rcu'
      pos = list_entry_rcu(pos->member.next, typeof(*pos), member))
            ^
   lib/logic_pio.c:77:2: note: in expansion of macro 'list_for_each_entry_rcu'
     list_for_each_entry_rcu(range, &io_range_list, list) {
     ^
   include/linux/rculist.h:353:27: error: dereferencing pointer to incomplete type
      pos = list_entry_rcu(pos->member.next, typeof(*pos), member))
                              ^
   include/linux/kernel.h:852:49: note: in definition of macro 'container_of'
     const typeof( ((type *)0)->member ) *__mptr = (ptr); \
                                                    ^
   include/linux/compiler.h:323:22: note: in expansion of macro '__READ_ONCE'
    #define READ_ONCE(x) __READ_ONCE(x, 1)
                         ^
>> include/linux/compiler.h:574:26: note: in expansion of macro 'READ_ONCE'
     typeof(p) _________p1 = READ_ONCE(p); \
                             ^
>> include/linux/rculist.h:277:15: note: in expansion of macro 'lockless_dereference'
     container_of(lockless_dereference(ptr), type, member)
                  ^
   include/linux/rculist.h:353:9: note: in expansion of macro 'list_entry_rcu'
      pos = list_entry_rcu(pos->member.next, typeof(*pos), member))
            ^
   lib/logic_pio.c:77:2: note: in expansion of macro 'list_for_each_entry_rcu'
     list_for_each_entry_rcu(range, &io_range_list, list) {
     ^
   include/linux/rculist.h:353:27: error: dereferencing pointer to incomplete type
      pos = list_entry_rcu(pos->member.next, typeof(*pos), member))
                              ^
   include/linux/kernel.h:852:49: note: in definition of macro 'container_of'
     const typeof( ((type *)0)->member ) *__mptr = (ptr); \
                                                    ^
   include/linux/compiler.h:323:22: note: in expansion of macro '__READ_ONCE'
    #define READ_ONCE(x) __READ_ONCE(x, 1)
                         ^
>> include/linux/compiler.h:574:26: note: in expansion of macro 'READ_ONCE'
     typeof(p) _________p1 = READ_ONCE(p); \
                             ^
>> include/linux/rculist.h:277:15: note: in expansion of macro 'lockless_dereference'
     container_of(lockless_dereference(ptr), type, member)
                  ^
   include/linux/rculist.h:353:9: note: in expansion of macro 'list_entry_rcu'
      pos = list_entry_rcu(pos->member.next, typeof(*pos), member))
            ^
   lib/logic_pio.c:77:2: note: in expansion of macro 'list_for_each_entry_rcu'
     list_for_each_entry_rcu(range, &io_range_list, list) {
     ^
   include/linux/rculist.h:353:27: error: dereferencing pointer to incomplete type
      pos = list_entry_rcu(pos->member.next, typeof(*pos), member))
                              ^
   include/linux/kernel.h:852:49: note: in definition of macro 'container_of'
     const typeof( ((type *)0)->member ) *__mptr = (ptr); \
                                                    ^
   include/linux/compiler.h:323:22: note: in expansion of macro '__READ_ONCE'
    #define READ_ONCE(x) __READ_ONCE(x, 1)
                         ^
>> include/linux/compiler.h:574:26: note: in expansion of macro 'READ_ONCE'
     typeof(p) _________p1 = READ_ONCE(p); \
                             ^
>> include/linux/rculist.h:277:15: note: in expansion of macro 'lockless_dereference'
     container_of(lockless_dereference(ptr), type, member)
                  ^
   include/linux/rculist.h:353:9: note: in expansion of macro 'list_entry_rcu'
      pos = list_entry_rcu(pos->member.next, typeof(*pos), member))
            ^
   lib/logic_pio.c:77:2: note: in expansion of macro 'list_for_each_entry_rcu'
     list_for_each_entry_rcu(range, &io_range_list, list) {
     ^
   include/linux/rculist.h:353:27: error: dereferencing pointer to incomplete type
      pos = list_entry_rcu(pos->member.next, typeof(*pos), member))
                              ^
   include/linux/kernel.h:852:49: note: in definition of macro 'container_of'
     const typeof( ((type *)0)->member ) *__mptr = (ptr); \
                                                    ^
>> include/linux/rculist.h:277:15: note: in expansion of macro 'lockless_dereference'
     container_of(lockless_dereference(ptr), type, member)
                  ^
   include/linux/rculist.h:353:9: note: in expansion of macro 'list_entry_rcu'
      pos = list_entry_rcu(pos->member.next, typeof(*pos), member))
            ^
   lib/logic_pio.c:77:2: note: in expansion of macro 'list_for_each_entry_rcu'
     list_for_each_entry_rcu(range, &io_range_list, list) {
     ^
   include/linux/kernel.h:852:48: warning: initialization makes pointer from integer without a cast
     const typeof( ((type *)0)->member ) *__mptr = (ptr); \
                                                   ^
   include/linux/rculist.h:277:2: note: in expansion of macro 'container_of'
     container_of(lockless_dereference(ptr), type, member)
     ^
   include/linux/rculist.h:353:9: note: in expansion of macro 'list_entry_rcu'
      pos = list_entry_rcu(pos->member.next, typeof(*pos), member))
            ^
   lib/logic_pio.c:77:2: note: in expansion of macro 'list_for_each_entry_rcu'
     list_for_each_entry_rcu(range, &io_range_list, list) {
     ^
   include/linux/rculist.h:353:49: error: dereferencing pointer to incomplete type
      pos = list_entry_rcu(pos->member.next, typeof(*pos), member))
                                                    ^
   include/linux/kernel.h:853:3: note: in definition of macro 'container_of'
     (type *)( (char *)__mptr - offsetof(type,member) );})
      ^
   include/linux/rculist.h:353:9: note: in expansion of macro 'list_entry_rcu'
      pos = list_entry_rcu(pos->member.next, typeof(*pos), member))
            ^
   lib/logic_pio.c:77:2: note: in expansion of macro 'list_for_each_entry_rcu'
     list_for_each_entry_rcu(range, &io_range_list, list) {
     ^
   In file included from include/linux/compiler.h:62:0,
                    from include/uapi/linux/stddef.h:1,
                    from include/linux/stddef.h:4,
                    from include/uapi/linux/posix_types.h:4,
                    from include/uapi/linux/types.h:13,
                    from include/linux/types.h:5,
                    from include/linux/of.h:18,
                    from lib/logic_pio.c:18:
   include/linux/rculist.h:353:49: error: dereferencing pointer to incomplete type
      pos = list_entry_rcu(pos->member.next, typeof(*pos), member))
                                                    ^
   include/linux/compiler-gcc.h:161:21: note: in definition of macro '__compiler_offsetof'
     __builtin_offsetof(a, b)
                        ^
   include/linux/kernel.h:853:29: note: in expansion of macro 'offsetof'
     (type *)( (char *)__mptr - offsetof(type,member) );})
                                ^
   include/linux/rculist.h:277:2: note: in expansion of macro 'container_of'
     container_of(lockless_dereference(ptr), type, member)
     ^
   include/linux/rculist.h:353:9: note: in expansion of macro 'list_entry_rcu'
      pos = list_entry_rcu(pos->member.next, typeof(*pos), member))
            ^
   lib/logic_pio.c:77:2: note: in expansion of macro 'list_for_each_entry_rcu'
     list_for_each_entry_rcu(range, &io_range_list, list) {
     ^
>> lib/logic_pio.c:78:13: error: dereferencing pointer to incomplete type
      if (!range->pio_peer) {
                ^
   In file included from include/linux/kernel.h:13:0,
                    from include/linux/list.h:8,
                    from include/linux/kobject.h:20,
                    from include/linux/of.h:21,
                    from lib/logic_pio.c:18:
   lib/logic_pio.c:80:11: error: dereferencing pointer to incomplete type
        &range->hw_start);
              ^
   include/linux/printk.h:303:37: note: in definition of macro 'pr_warning'
     printk(KERN_WARNING pr_fmt(fmt), ##__VA_ARGS__)
                                        ^
>> lib/logic_pio.c:79:4: note: in expansion of macro 'pr_warn'
       pr_warn("Invalid cpu addr node(%pa) in list!\n",
       ^
   lib/logic_pio.c:83:12: error: dereferencing pointer to incomplete type
      if (range->fwnode != fwnode)
               ^
   lib/logic_pio.c:86:21: error: dereferencing pointer to incomplete type
      if (start >= range->hw_start + range->size ||
                        ^
   lib/logic_pio.c:86:39: error: dereferencing pointer to incomplete type
      if (start >= range->hw_start + range->size ||
                                          ^
   lib/logic_pio.c:87:15: error: dereferencing pointer to incomplete type
       end < range->hw_start)
                  ^
   lib/logic_pio.c:90:20: error: dereferencing pointer to incomplete type
      if (start < range->hw_start ||
                       ^
   lib/logic_pio.c:91:16: error: dereferencing pointer to incomplete type
       end >= range->hw_start + range->size)
                   ^
   lib/logic_pio.c:91:34: error: dereferencing pointer to incomplete type
       end >= range->hw_start + range->size)
                                     ^
   lib/logic_pio.c: In function 'logic_pio_alloc_range':
   lib/logic_pio.c:109:19: error: dereferencing pointer to incomplete type
     idle_start = root->sec_min;
                      ^
   lib/logic_pio.c:110:15: error: dereferencing pointer to incomplete type
     *prev = &root->sec_head;
                  ^
   In file included from include/linux/list.h:8:0,
                    from include/linux/kobject.h:20,
                    from include/linux/of.h:21,
                    from lib/logic_pio.c:18:
>> include/linux/rculist.h:351:49: error: dereferencing pointer to incomplete type
     for (pos = list_entry_rcu((head)->next, typeof(*pos), member); \
                                                    ^
   include/linux/kernel.h:852:18: note: in definition of macro 'container_of'
     const typeof( ((type *)0)->member ) *__mptr = (ptr); \
                     ^
   include/linux/rculist.h:351:13: note: in expansion of macro 'list_entry_rcu'
     for (pos = list_entry_rcu((head)->next, typeof(*pos), member); \
                ^
   lib/logic_pio.c:111:2: note: in expansion of macro 'list_for_each_entry_rcu'
     list_for_each_entry_rcu(entry, &root->sec_head, list) {
     ^
   lib/logic_pio.c:111:38: error: dereferencing pointer to incomplete type
     list_for_each_entry_rcu(entry, &root->sec_head, list) {
                                         ^
   include/linux/kernel.h:852:49: note: in definition of macro 'container_of'
     const typeof( ((type *)0)->member ) *__mptr = (ptr); \
                                                    ^
>> include/linux/rculist.h:277:15: note: in expansion of macro 'lockless_dereference'
     container_of(lockless_dereference(ptr), type, member)
                  ^
   include/linux/rculist.h:351:13: note: in expansion of macro 'list_entry_rcu'
     for (pos = list_entry_rcu((head)->next, typeof(*pos), member); \
                ^
   lib/logic_pio.c:111:2: note: in expansion of macro 'list_for_each_entry_rcu'
     list_for_each_entry_rcu(entry, &root->sec_head, list) {
     ^
   lib/logic_pio.c:111:38: error: dereferencing pointer to incomplete type
     list_for_each_entry_rcu(entry, &root->sec_head, list) {
                                         ^
   include/linux/kernel.h:852:49: note: in definition of macro 'container_of'
     const typeof( ((type *)0)->member ) *__mptr = (ptr); \
                                                    ^
   include/linux/compiler.h:323:22: note: in expansion of macro '__READ_ONCE'
    #define READ_ONCE(x) __READ_ONCE(x, 1)
                         ^
>> include/linux/compiler.h:574:26: note: in expansion of macro 'READ_ONCE'
     typeof(p) _________p1 = READ_ONCE(p); \
                             ^

vim +351 include/linux/rculist.h

3943f42c Andrey Utkin         2014-11-14  271   * @member:     the name of the list_head within the struct.
72c6a987 Jiri Pirko           2009-04-14  272   *
72c6a987 Jiri Pirko           2009-04-14  273   * This primitive may safely run concurrently with the _rcu list-mutation
72c6a987 Jiri Pirko           2009-04-14  274   * primitives such as list_add_rcu() as long as it's guarded by rcu_read_lock().
72c6a987 Jiri Pirko           2009-04-14  275   */
72c6a987 Jiri Pirko           2009-04-14  276  #define list_entry_rcu(ptr, type, member) \
8db70b13 Patrick Marlier      2015-09-11 @277  	container_of(lockless_dereference(ptr), type, member)
72c6a987 Jiri Pirko           2009-04-14  278  
72c6a987 Jiri Pirko           2009-04-14  279  /**
f88022a4 Michel Machado       2012-04-10  280   * Where are list_empty_rcu() and list_first_entry_rcu()?
f88022a4 Michel Machado       2012-04-10  281   *
f88022a4 Michel Machado       2012-04-10  282   * Implementing those functions following their counterparts list_empty() and
f88022a4 Michel Machado       2012-04-10  283   * list_first_entry() is not advisable because they lead to subtle race
f88022a4 Michel Machado       2012-04-10  284   * conditions as the following snippet shows:
f88022a4 Michel Machado       2012-04-10  285   *
f88022a4 Michel Machado       2012-04-10  286   * if (!list_empty_rcu(mylist)) {
f88022a4 Michel Machado       2012-04-10  287   *	struct foo *bar = list_first_entry_rcu(mylist, struct foo, list_member);
f88022a4 Michel Machado       2012-04-10  288   *	do_something(bar);
f88022a4 Michel Machado       2012-04-10  289   * }
f88022a4 Michel Machado       2012-04-10  290   *
f88022a4 Michel Machado       2012-04-10  291   * The list may not be empty when list_empty_rcu checks it, but it may be when
f88022a4 Michel Machado       2012-04-10  292   * list_first_entry_rcu rereads the ->next pointer.
f88022a4 Michel Machado       2012-04-10  293   *
f88022a4 Michel Machado       2012-04-10  294   * Rereading the ->next pointer is not a problem for list_empty() and
f88022a4 Michel Machado       2012-04-10  295   * list_first_entry() because they would be protected by a lock that blocks
f88022a4 Michel Machado       2012-04-10  296   * writers.
f88022a4 Michel Machado       2012-04-10  297   *
f88022a4 Michel Machado       2012-04-10  298   * See list_first_or_null_rcu for an alternative.
f88022a4 Michel Machado       2012-04-10  299   */
f88022a4 Michel Machado       2012-04-10  300  
f88022a4 Michel Machado       2012-04-10  301  /**
f88022a4 Michel Machado       2012-04-10  302   * list_first_or_null_rcu - get the first element from a list
72c6a987 Jiri Pirko           2009-04-14  303   * @ptr:        the list head to take the element from.
72c6a987 Jiri Pirko           2009-04-14  304   * @type:       the type of the struct this is embedded in.
3943f42c Andrey Utkin         2014-11-14  305   * @member:     the name of the list_head within the struct.
72c6a987 Jiri Pirko           2009-04-14  306   *
f88022a4 Michel Machado       2012-04-10  307   * Note that if the list is empty, it returns NULL.
72c6a987 Jiri Pirko           2009-04-14  308   *
72c6a987 Jiri Pirko           2009-04-14  309   * This primitive may safely run concurrently with the _rcu list-mutation
72c6a987 Jiri Pirko           2009-04-14  310   * primitives such as list_add_rcu() as long as it's guarded by rcu_read_lock().
72c6a987 Jiri Pirko           2009-04-14  311   */
f88022a4 Michel Machado       2012-04-10  312  #define list_first_or_null_rcu(ptr, type, member) \
0adab9b9 Joe Perches          2013-12-05  313  ({ \
0adab9b9 Joe Perches          2013-12-05  314  	struct list_head *__ptr = (ptr); \
7d0ae808 Paul E. McKenney     2015-03-03  315  	struct list_head *__next = READ_ONCE(__ptr->next); \
0adab9b9 Joe Perches          2013-12-05  316  	likely(__ptr != __next) ? list_entry_rcu(__next, type, member) : NULL; \
f88022a4 Michel Machado       2012-04-10  317  })
72c6a987 Jiri Pirko           2009-04-14  318  
82524746 Franck Bui-Huu       2008-05-12  319  /**
ff3c44e6 Tom Herbert          2016-03-07  320   * list_next_or_null_rcu - get the first element from a list
ff3c44e6 Tom Herbert          2016-03-07  321   * @head:	the head for the list.
ff3c44e6 Tom Herbert          2016-03-07  322   * @ptr:        the list head to take the next element from.
ff3c44e6 Tom Herbert          2016-03-07  323   * @type:       the type of the struct this is embedded in.
ff3c44e6 Tom Herbert          2016-03-07  324   * @member:     the name of the list_head within the struct.
ff3c44e6 Tom Herbert          2016-03-07  325   *
ff3c44e6 Tom Herbert          2016-03-07  326   * Note that if the ptr is at the end of the list, NULL is returned.
ff3c44e6 Tom Herbert          2016-03-07  327   *
ff3c44e6 Tom Herbert          2016-03-07  328   * This primitive may safely run concurrently with the _rcu list-mutation
ff3c44e6 Tom Herbert          2016-03-07  329   * primitives such as list_add_rcu() as long as it's guarded by rcu_read_lock().
ff3c44e6 Tom Herbert          2016-03-07  330   */
ff3c44e6 Tom Herbert          2016-03-07  331  #define list_next_or_null_rcu(head, ptr, type, member) \
ff3c44e6 Tom Herbert          2016-03-07  332  ({ \
ff3c44e6 Tom Herbert          2016-03-07  333  	struct list_head *__head = (head); \
ff3c44e6 Tom Herbert          2016-03-07  334  	struct list_head *__ptr = (ptr); \
ff3c44e6 Tom Herbert          2016-03-07  335  	struct list_head *__next = READ_ONCE(__ptr->next); \
ff3c44e6 Tom Herbert          2016-03-07  336  	likely(__next != __head) ? list_entry_rcu(__next, type, \
ff3c44e6 Tom Herbert          2016-03-07  337  						  member) : NULL; \
ff3c44e6 Tom Herbert          2016-03-07  338  })
ff3c44e6 Tom Herbert          2016-03-07  339  
ff3c44e6 Tom Herbert          2016-03-07  340  /**
82524746 Franck Bui-Huu       2008-05-12  341   * list_for_each_entry_rcu	-	iterate over rcu list of given type
82524746 Franck Bui-Huu       2008-05-12  342   * @pos:	the type * to use as a loop cursor.
82524746 Franck Bui-Huu       2008-05-12  343   * @head:	the head for your list.
3943f42c Andrey Utkin         2014-11-14  344   * @member:	the name of the list_head within the struct.
82524746 Franck Bui-Huu       2008-05-12  345   *
82524746 Franck Bui-Huu       2008-05-12  346   * This list-traversal primitive may safely run concurrently with
82524746 Franck Bui-Huu       2008-05-12  347   * the _rcu list-mutation primitives such as list_add_rcu()
82524746 Franck Bui-Huu       2008-05-12  348   * as long as the traversal is guarded by rcu_read_lock().
82524746 Franck Bui-Huu       2008-05-12  349   */
82524746 Franck Bui-Huu       2008-05-12  350  #define list_for_each_entry_rcu(pos, head, member) \
72c6a987 Jiri Pirko           2009-04-14 @351  	for (pos = list_entry_rcu((head)->next, typeof(*pos), member); \
e66eed65 Linus Torvalds       2011-05-19  352  		&pos->member != (head); \
72c6a987 Jiri Pirko           2009-04-14 @353  		pos = list_entry_rcu(pos->member.next, typeof(*pos), member))
82524746 Franck Bui-Huu       2008-05-12  354  
82524746 Franck Bui-Huu       2008-05-12  355  /**
69b90729 Alexey Kardashevskiy 2015-12-05  356   * list_entry_lockless - get the struct for this entry

:::::: The code at line 351 was first introduced by commit
:::::: 72c6a9870f901045f2464c3dc6ee8914bfdc07aa rculist.h: introduce list_entry_rcu() and list_first_entry_rcu()

:::::: TO: Jiri Pirko <jpirko@redhat.com>
:::::: CC: Ingo Molnar <mingo@elte.hu>

---
0-DAY kernel test infrastructure                Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all                   Intel Corporation

[-- Attachment #2: .config.gz --]
[-- Type: application/gzip, Size: 6383 bytes --]

^ permalink raw reply

* Re: [PATCH V8 1/6] LIBIO: Introduce a generic PIO mapping method
From: kbuild test robot @ 2017-04-01  5:58 UTC (permalink / raw)
  Cc: kbuild-all, catalin.marinas, will.deacon, robh+dt, frowand.list,
	bhelgaas, rafael, arnd, linux-arm-kernel, mark.rutland,
	brian.starkey, olof, lorenzo.pieralisi, benh, linux-kernel,
	linux-acpi, linuxarm, devicetree, linux-pci, minyard, zourongrong,
	john.garry, gabriele.paoloni, zhichang.yuan02, kantyzc, xuwei5,
	zhichang.yuan
In-Reply-To: <1490887619-61732-2-git-send-email-yuanzhichang@hisilicon.com>

[-- Attachment #1: Type: text/plain, Size: 14599 bytes --]

Hi zhichang.yuan,

[auto build test ERROR on linus/master]
[also build test ERROR on v4.11-rc4 next-20170331]
[if your patch is applied to the wrong git tree, please drop us a note to help improve the system]

url:    https://github.com/0day-ci/linux/commits/zhichang-yuan/LIBIO-Introduce-a-generic-PIO-mapping-method/20170401-104801
config: alpha-allyesconfig (attached as .config)
compiler: alpha-linux-gnu-gcc (Debian 6.1.1-9) 6.1.1 20160705
reproduce:
        wget https://raw.githubusercontent.com/01org/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
        chmod +x ~/bin/make.cross
        # save the attached .config to linux build tree
        make.cross ARCH=alpha 

All error/warnings (new ones prefixed by >>):

>> lib/logic_pio.c:32:50: error: 'PIO_MAX_SECT' undeclared here (not in a function)
    static struct logic_pio_root logic_pio_root_list[PIO_MAX_SECT] = {
                                                     ^~~~~~~~~~~~
>> lib/logic_pio.c:39:3: error: 'PIO_CPU_MMIO' undeclared here (not in a function)
     [PIO_CPU_MMIO ... PIO_INDIRECT - 1] = {
      ^~~~~~~~~~~~
>> lib/logic_pio.c:39:20: error: 'PIO_INDIRECT' undeclared here (not in a function)
     [PIO_CPU_MMIO ... PIO_INDIRECT - 1] = {
                       ^~~~~~~~~~~~
>> lib/logic_pio.c:39:3: error: array index in initializer not of integer type
     [PIO_CPU_MMIO ... PIO_INDIRECT - 1] = {
      ^~~~~~~~~~~~
   lib/logic_pio.c:39:3: note: (near initialization for 'logic_pio_root_list')
>> lib/logic_pio.c:40:3: error: field name not in record or union initializer
      .sec_head = LIST_HEAD_INIT(logic_pio_root_list[PIO_CPU_MMIO].sec_head),
      ^
   lib/logic_pio.c:40:3: note: (near initialization for 'logic_pio_root_list')
   lib/logic_pio.c:41:3: error: field name not in record or union initializer
      .sec_min = PIO_SECT_MIN(PIO_CPU_MMIO),
      ^
   lib/logic_pio.c:41:3: note: (near initialization for 'logic_pio_root_list')
>> lib/logic_pio.c:41:14: error: implicit declaration of function 'PIO_SECT_MIN' [-Werror=implicit-function-declaration]
      .sec_min = PIO_SECT_MIN(PIO_CPU_MMIO),
                 ^~~~~~~~~~~~
   lib/logic_pio.c:42:3: error: field name not in record or union initializer
      .sec_max = PIO_SECT_MAX(PIO_INDIRECT - 1),
      ^
   lib/logic_pio.c:42:3: note: (near initialization for 'logic_pio_root_list')
>> lib/logic_pio.c:42:14: error: implicit declaration of function 'PIO_SECT_MAX' [-Werror=implicit-function-declaration]
      .sec_max = PIO_SECT_MAX(PIO_INDIRECT - 1),
                 ^~~~~~~~~~~~
   lib/logic_pio.c:46:3: error: array index in initializer not of integer type
     [PIO_INDIRECT] = {
      ^~~~~~~~~~~~
   lib/logic_pio.c:46:3: note: (near initialization for 'logic_pio_root_list')
   lib/logic_pio.c:47:3: error: field name not in record or union initializer
      .sec_head = LIST_HEAD_INIT(logic_pio_root_list[PIO_INDIRECT].sec_head),
      ^
   lib/logic_pio.c:47:3: note: (near initialization for 'logic_pio_root_list')
   lib/logic_pio.c:48:3: error: field name not in record or union initializer
      .sec_min = PIO_SECT_MIN(PIO_INDIRECT),
      ^
   lib/logic_pio.c:48:3: note: (near initialization for 'logic_pio_root_list')
   lib/logic_pio.c:49:3: error: field name not in record or union initializer
      .sec_max = PIO_SECT_MAX(PIO_INDIRECT),
      ^
   lib/logic_pio.c:49:3: note: (near initialization for 'logic_pio_root_list')
   In file included from include/linux/list.h:8:0,
                    from include/linux/kobject.h:20,
                    from include/linux/of.h:21,
                    from lib/logic_pio.c:18:
   lib/logic_pio.c: In function 'logic_pio_find_range_byaddr':
>> include/linux/rculist.h:351:49: error: dereferencing pointer to incomplete type 'struct logic_pio_hwaddr'
     for (pos = list_entry_rcu((head)->next, typeof(*pos), member); \
                                                     
   include/linux/kernel.h:852:18: note: in definition of macro 'container_of'
     const typeof( ((type *)0)->member ) *__mptr = (ptr); \
                     ^~~~
>> include/linux/rculist.h:351:13: note: in expansion of macro 'list_entry_rcu'
     for (pos = list_entry_rcu((head)->next, typeof(*pos), member); \
                ^~~~~~~~~~~~~~
>> lib/logic_pio.c:77:2: note: in expansion of macro 'list_for_each_entry_rcu'
     list_for_each_entry_rcu(range, &io_range_list, list) {
     ^~~~~~~~~~~~~~~~~~~~~~~
   include/linux/kernel.h:852:48: error: initialization from incompatible pointer type [-Werror=incompatible-pointer-types]
     const typeof( ((type *)0)->member ) *__mptr = (ptr); \
                                                   ^
>> include/linux/rculist.h:277:2: note: in expansion of macro 'container_of'
     container_of(lockless_dereference(ptr), type, member)
     ^~~~~~~~~~~~
>> include/linux/rculist.h:351:13: note: in expansion of macro 'list_entry_rcu'
     for (pos = list_entry_rcu((head)->next, typeof(*pos), member); \
                ^~~~~~~~~~~~~~
>> lib/logic_pio.c:77:2: note: in expansion of macro 'list_for_each_entry_rcu'
     list_for_each_entry_rcu(range, &io_range_list, list) {
     ^~~~~~~~~~~~~~~~~~~~~~~
>> include/linux/kernel.h:852:48: warning: initialization makes pointer from integer without a cast [-Wint-conversion]
     const typeof( ((type *)0)->member ) *__mptr = (ptr); \
                                                   ^
>> include/linux/rculist.h:277:2: note: in expansion of macro 'container_of'
     container_of(lockless_dereference(ptr), type, member)
     ^~~~~~~~~~~~
   include/linux/rculist.h:353:9: note: in expansion of macro 'list_entry_rcu'
      pos = list_entry_rcu(pos->member.next, typeof(*pos), member))
            ^~~~~~~~~~~~~~
>> lib/logic_pio.c:77:2: note: in expansion of macro 'list_for_each_entry_rcu'
     list_for_each_entry_rcu(range, &io_range_list, list) {
     ^~~~~~~~~~~~~~~~~~~~~~~
   lib/logic_pio.c: In function 'logic_pio_alloc_range':
>> lib/logic_pio.c:109:19: error: dereferencing pointer to incomplete type 'struct logic_pio_root'
     idle_start = root->sec_min;
                      ^~
   In file included from include/linux/list.h:8:0,
                    from include/linux/kobject.h:20,
                    from include/linux/of.h:21,
                    from lib/logic_pio.c:18:
>> include/linux/rculist.h:351:49: error: dereferencing pointer to incomplete type 'struct logic_pio_sect'
     for (pos = list_entry_rcu((head)->next, typeof(*pos), member); \
                                                     
   include/linux/kernel.h:852:18: note: in definition of macro 'container_of'
     const typeof( ((type *)0)->member ) *__mptr = (ptr); \
                     ^~~~
>> include/linux/rculist.h:351:13: note: in expansion of macro 'list_entry_rcu'
     for (pos = list_entry_rcu((head)->next, typeof(*pos), member); \
                ^~~~~~~~~~~~~~
   lib/logic_pio.c:111:2: note: in expansion of macro 'list_for_each_entry_rcu'
     list_for_each_entry_rcu(entry, &root->sec_head, list) {
     ^~~~~~~~~~~~~~~~~~~~~~~
>> include/linux/kernel.h:852:48: warning: initialization makes pointer from integer without a cast [-Wint-conversion]
     const typeof( ((type *)0)->member ) *__mptr = (ptr); \
                                                   ^

vim +/PIO_MAX_SECT +32 lib/logic_pio.c

    12	 * GNU General Public License for more details.
    13	 *
    14	 * You should have received a copy of the GNU General Public License
    15	 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
    16	 */
    17	
  > 18	#include <linux/of.h>
    19	#include <linux/io.h>
    20	#include <linux/mm.h>
    21	#include <linux/rculist.h>
    22	#include <linux/sizes.h>
    23	#include <linux/slab.h>
    24	
    25	/* The unique hardware address list. */
    26	static LIST_HEAD(io_range_list);
    27	static DEFINE_MUTEX(io_range_mutex);
    28	
    29	/*
    30	 * These are the lists for PIO. The highest PIO_SECT_BITS of PIO is the index.
    31	 */
  > 32	static struct logic_pio_root logic_pio_root_list[PIO_MAX_SECT] = {
    33	#ifdef CONFIG_INDIRECT_PIO
    34		/*
    35		 * At this moment, assign all the other logic PIO space to MMIO.
    36		 * If more elements added, please adjust the ending index and .sec_max;
    37		 * Please keep MMIO element started from index ZERO.
    38		 */
  > 39		[PIO_CPU_MMIO ... PIO_INDIRECT - 1] = {
  > 40			.sec_head = LIST_HEAD_INIT(logic_pio_root_list[PIO_CPU_MMIO].sec_head),
  > 41			.sec_min = PIO_SECT_MIN(PIO_CPU_MMIO),
  > 42			.sec_max = PIO_SECT_MAX(PIO_INDIRECT - 1),
    43		},
    44	
    45		/* The last element */
    46		[PIO_INDIRECT] = {
    47			.sec_head = LIST_HEAD_INIT(logic_pio_root_list[PIO_INDIRECT].sec_head),
    48			.sec_min = PIO_SECT_MIN(PIO_INDIRECT),
    49			.sec_max = PIO_SECT_MAX(PIO_INDIRECT),
    50		},
    51	#else
    52		[PIO_CPU_MMIO] = {
    53			.sec_head = LIST_HEAD_INIT(logic_pio_root_list[PIO_CPU_MMIO].sec_head),
    54			.sec_min = PIO_SECT_MIN(PIO_CPU_MMIO),
    55			.sec_max = PIO_SECT_MAX(PIO_CPU_MMIO),
    56		},
    57	
    58	#endif
    59	};
    60	
    61	/*
    62	 * Search a io_range registered which match the fwnode and addr.
    63	 *
    64	 * @fwnode: the host fwnode which must be valid;
    65	 * @start: the start hardware address of this search;
    66	 * @end: the end hardware address of this search. can be equal to @start;
    67	 *
    68	 * return NULL when there is no matched node; IS_ERR() means ERROR;
    69	 * valid virtual address represent a matched node was found.
    70	 */
    71	static struct logic_pio_hwaddr *
    72	logic_pio_find_range_byaddr(struct fwnode_handle *fwnode,
    73				resource_size_t start, resource_size_t end)
    74	{
    75		struct logic_pio_hwaddr *range;
    76	
  > 77		list_for_each_entry_rcu(range, &io_range_list, list) {
    78			if (!range->pio_peer) {
    79				pr_warn("Invalid cpu addr node(%pa) in list!\n",
    80					&range->hw_start);
    81				continue;
    82			}
    83			if (range->fwnode != fwnode)
    84				continue;
    85			/* without any overlap with current range */
    86			if (start >= range->hw_start + range->size ||
    87				end < range->hw_start)
    88				continue;
    89			/* overlap is not supported now. */
    90			if (start < range->hw_start ||
    91				end >= range->hw_start + range->size)
    92				return ERR_PTR(-EBUSY);
    93			/* had been registered. */
    94			return range;
    95		}
    96	
    97		return NULL;
    98	}
    99	
   100	
   101	static int logic_pio_alloc_range(struct logic_pio_root *root,
   102			resource_size_t size, unsigned long align,
   103			struct list_head **prev, resource_size_t *pio_alloc)
   104	{
   105		struct logic_pio_sect *entry;
   106		resource_size_t tmp_start;
   107		resource_size_t idle_start, idle_end;
   108	
 > 109		idle_start = root->sec_min;
   110		*prev = &root->sec_head;
   111		list_for_each_entry_rcu(entry, &root->sec_head, list) {
   112			if (!entry->hwpeer ||
   113				idle_start > entry->io_start) {
   114				WARN(1, "skip an invalid io range during traversal!\n");
   115				goto nextentry;
   116			}
   117			/* set the end edge. */
   118			if (idle_start == entry->io_start) {
   119				struct logic_pio_sect *next;
   120	
   121				idle_start = entry->io_start + entry->hwpeer->size;
 > 122				next = list_next_or_null_rcu(&root->sec_head,
 > 123					&entry->list, struct logic_pio_sect, list);
   124				if (next) {
   125					entry = next;
   126				} else {
   127					*prev = &entry->list;
   128					break;
   129				}
   130			}
   131			idle_end = entry->io_start - 1;
   132	
   133			/* contiguous range... */
   134			if (idle_start > idle_end)
   135				goto nextentry;
   136	
   137			tmp_start = idle_start;
   138			idle_start = ALIGN(idle_start, align);
   139			if (idle_start >= tmp_start &&
   140				idle_start + size <= idle_end) {
   141				*prev = &entry->list;
   142				*pio_alloc = idle_start;
   143				return 0;
   144			}
   145	
   146	nextentry:
   147			idle_start = entry->io_start + entry->hwpeer->size;
   148			*prev = &entry->list;
   149		}
   150		/* check the last free gap... */
   151		idle_end = root->sec_max;
   152	
   153		tmp_start = idle_start;
   154		idle_start = ALIGN(idle_start, align);
   155		if (idle_start >= tmp_start &&
   156			idle_start + size <= idle_end) {
   157			*pio_alloc = idle_start;
   158			return 0;
   159		}
   160	
   161		return -EBUSY;
   162	}
   163	
   164	/*
   165	 * register a io range node in the io range list.
   166	 *
   167	 * @newrange: pointer to the io range to be registered.
   168	 *
   169	 * return 'newrange' when success, ERR_VALUE() is for failures.
   170	 * specially, return a valid pointer which is not equal to 'newrange' when
   171	 * the io range had been registered before.
   172	 */
   173	struct logic_pio_hwaddr
   174	*logic_pio_register_range(struct logic_pio_hwaddr *newrange,
   175			unsigned long align)
   176	{
   177		struct logic_pio_hwaddr *range;
   178		struct logic_pio_sect *newsect;
   179		resource_size_t pio_alloc;
   180		struct list_head *prev, *hwprev;
   181		unsigned long sect_id;
   182		int err;
   183	
   184		if (!newrange || !newrange->fwnode || !newrange->size)
   185			return ERR_PTR(-EINVAL);
   186	
   187		sect_id = newrange->flags;
   188		if (sect_id >= PIO_MAX_SECT)
   189			return ERR_PTR(-EINVAL);
   190	
   191		mutex_lock(&io_range_mutex);
   192		range = logic_pio_find_range_byaddr(newrange->fwnode,
   193				newrange->hw_start,
   194				newrange->hw_start + newrange->size - 1);
   195		if (range) {
   196			if (!IS_ERR(range))
   197				pr_info("the request IO range had been registered!\n");
   198			else
   199				pr_err("registering IO[%pa - sz%pa) got failed!\n",
   200					&newrange->hw_start, &newrange->size);
   201			mutex_unlock(&io_range_mutex);
   202			return range;
   203		}
   204	
   205		err = logic_pio_alloc_range(&logic_pio_root_list[sect_id],
   206				newrange->size, align, &prev, &pio_alloc);
   207		if (err) {
   208			pr_err("can't find free %pa logical IO range!\n",
   209				&newrange->size);
   210			goto exitproc;
   211		}
   212	
   213		if (prev == &logic_pio_root_list[sect_id].sec_head) {
   214			hwprev = &io_range_list;
   215		} else {
 > 216			newsect = to_pio_sect(prev);
 > 217			hwprev = &newsect->hwpeer->list;
   218		}
   219	
   220		newsect = kzalloc(sizeof(*newsect), GFP_KERNEL);

---
0-DAY kernel test infrastructure                Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all                   Intel Corporation

[-- Attachment #2: .config.gz --]
[-- Type: application/gzip, Size: 49584 bytes --]

^ permalink raw reply

* Re: [PATCH 2/2] ARM: dts: imx7: add USDHC NAND clock to SDHC instances
From: Stefan Agner @ 2017-04-01  4:15 UTC (permalink / raw)
  To: Dong Aisheng
  Cc: shawnguo-DgEjT+Ai2ygdnm+yROfE0A, kernel-bIcnvbaLZ9MEGnE8C9+IrQ,
	sboyd-sgV2jX0FEOL9JmXXK+q4OQ, aisheng.dong-3arQi8VN3Tc,
	fabio.estevam-3arQi8VN3Tc, robh+dt-DgEjT+Ai2ygdnm+yROfE0A,
	mark.rutland-5wv7dgnIgG8,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-clk-u79uwXL29TY76Z2rM5mHXA,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <20170401030312.GB24882@b29396-OptiPlex-7040>

On 2017-03-31 20:03, Dong Aisheng wrote:
> On Wed, Mar 29, 2017 at 05:50:29PM -0700, Stefan Agner wrote:
>> The USDHC instances need the USDHC NAND clock in order to operate.
>> Add the clock as ahb bus clock.
>>
>> Signed-off-by: Stefan Agner <stefan-XLVq0VzYD2Y@public.gmane.org>
>> ---
>>  arch/arm/boot/dts/imx7s.dtsi | 6 +++---
>>  1 file changed, 3 insertions(+), 3 deletions(-)
>>
>> diff --git a/arch/arm/boot/dts/imx7s.dtsi b/arch/arm/boot/dts/imx7s.dtsi
>> index 5d3a43b8de20..5794febb19a4 100644
>> --- a/arch/arm/boot/dts/imx7s.dtsi
>> +++ b/arch/arm/boot/dts/imx7s.dtsi
>> @@ -936,7 +936,7 @@
>>  				reg = <0x30b40000 0x10000>;
>>  				interrupts = <GIC_SPI 22 IRQ_TYPE_LEVEL_HIGH>;
>>  				clocks = <&clks IMX7D_CLK_DUMMY>,
> 
> Would you please change the left ipg dummy to IMX7D_IPG_ROOT_CLK as well?

IMX7D_IPG_ROOT_CLK is currently not a valid clock in upstream... So we
would have to add it to the clock driver first.

I guess we could/should add it anyway at one point? But probably also as
init on, just to make sure Linux does not disable it since it is
currently used by several IPs implicitly.

--
Stefan

> 
> Otherwise,
> 
> Acked-by: Dong Aisheng <aisheng.dong-3arQi8VN3Tc@public.gmane.org>
> 
> Regards
> Dong Aisheng
> 
>> -					<&clks IMX7D_CLK_DUMMY>,
>> +					<&clks IMX7D_NAND_USDHC_BUS_ROOT_CLK>,
>>  					<&clks IMX7D_USDHC1_ROOT_CLK>;
>>  				clock-names = "ipg", "ahb", "per";
>>  				bus-width = <4>;
>> @@ -948,7 +948,7 @@
>>  				reg = <0x30b50000 0x10000>;
>>  				interrupts = <GIC_SPI 23 IRQ_TYPE_LEVEL_HIGH>;
>>  				clocks = <&clks IMX7D_CLK_DUMMY>,
>> -					<&clks IMX7D_CLK_DUMMY>,
>> +					<&clks IMX7D_NAND_USDHC_BUS_ROOT_CLK>,
>>  					<&clks IMX7D_USDHC2_ROOT_CLK>;
>>  				clock-names = "ipg", "ahb", "per";
>>  				bus-width = <4>;
>> @@ -960,7 +960,7 @@
>>  				reg = <0x30b60000 0x10000>;
>>  				interrupts = <GIC_SPI 24 IRQ_TYPE_LEVEL_HIGH>;
>>  				clocks = <&clks IMX7D_CLK_DUMMY>,
>> -					<&clks IMX7D_CLK_DUMMY>,
>> +					<&clks IMX7D_NAND_USDHC_BUS_ROOT_CLK>,
>>  					<&clks IMX7D_USDHC3_ROOT_CLK>;
>>  				clock-names = "ipg", "ahb", "per";
>>  				bus-width = <4>;
>> --
>> 2.12.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

* Re: [PATCH 2/2] ARM: dts: imx7: add USDHC NAND clock to SDHC instances
From: Dong Aisheng @ 2017-04-01  3:03 UTC (permalink / raw)
  To: Stefan Agner
  Cc: shawnguo-DgEjT+Ai2ygdnm+yROfE0A, kernel-bIcnvbaLZ9MEGnE8C9+IrQ,
	sboyd-sgV2jX0FEOL9JmXXK+q4OQ, aisheng.dong-3arQi8VN3Tc,
	fabio.estevam-3arQi8VN3Tc, robh+dt-DgEjT+Ai2ygdnm+yROfE0A,
	mark.rutland-5wv7dgnIgG8,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-clk-u79uwXL29TY76Z2rM5mHXA,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <20170330005029.6472-2-stefan-XLVq0VzYD2Y@public.gmane.org>

On Wed, Mar 29, 2017 at 05:50:29PM -0700, Stefan Agner wrote:
> The USDHC instances need the USDHC NAND clock in order to operate.
> Add the clock as ahb bus clock.
> 
> Signed-off-by: Stefan Agner <stefan-XLVq0VzYD2Y@public.gmane.org>
> ---
>  arch/arm/boot/dts/imx7s.dtsi | 6 +++---
>  1 file changed, 3 insertions(+), 3 deletions(-)
> 
> diff --git a/arch/arm/boot/dts/imx7s.dtsi b/arch/arm/boot/dts/imx7s.dtsi
> index 5d3a43b8de20..5794febb19a4 100644
> --- a/arch/arm/boot/dts/imx7s.dtsi
> +++ b/arch/arm/boot/dts/imx7s.dtsi
> @@ -936,7 +936,7 @@
>  				reg = <0x30b40000 0x10000>;
>  				interrupts = <GIC_SPI 22 IRQ_TYPE_LEVEL_HIGH>;
>  				clocks = <&clks IMX7D_CLK_DUMMY>,

Would you please change the left ipg dummy to IMX7D_IPG_ROOT_CLK as well?

Otherwise,

Acked-by: Dong Aisheng <aisheng.dong-3arQi8VN3Tc@public.gmane.org>

Regards
Dong Aisheng

> -					<&clks IMX7D_CLK_DUMMY>,
> +					<&clks IMX7D_NAND_USDHC_BUS_ROOT_CLK>,
>  					<&clks IMX7D_USDHC1_ROOT_CLK>;
>  				clock-names = "ipg", "ahb", "per";
>  				bus-width = <4>;
> @@ -948,7 +948,7 @@
>  				reg = <0x30b50000 0x10000>;
>  				interrupts = <GIC_SPI 23 IRQ_TYPE_LEVEL_HIGH>;
>  				clocks = <&clks IMX7D_CLK_DUMMY>,
> -					<&clks IMX7D_CLK_DUMMY>,
> +					<&clks IMX7D_NAND_USDHC_BUS_ROOT_CLK>,
>  					<&clks IMX7D_USDHC2_ROOT_CLK>;
>  				clock-names = "ipg", "ahb", "per";
>  				bus-width = <4>;
> @@ -960,7 +960,7 @@
>  				reg = <0x30b60000 0x10000>;
>  				interrupts = <GIC_SPI 24 IRQ_TYPE_LEVEL_HIGH>;
>  				clocks = <&clks IMX7D_CLK_DUMMY>,
> -					<&clks IMX7D_CLK_DUMMY>,
> +					<&clks IMX7D_NAND_USDHC_BUS_ROOT_CLK>,
>  					<&clks IMX7D_USDHC3_ROOT_CLK>;
>  				clock-names = "ipg", "ahb", "per";
>  				bus-width = <4>;
> -- 
> 2.12.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

* Re: [PATCH 1/2] clk: imx7d: fix USDHC NAND clock
From: Dong Aisheng @ 2017-04-01  3:00 UTC (permalink / raw)
  To: Stefan Agner
  Cc: shawnguo-DgEjT+Ai2ygdnm+yROfE0A, kernel-bIcnvbaLZ9MEGnE8C9+IrQ,
	sboyd-sgV2jX0FEOL9JmXXK+q4OQ, aisheng.dong-3arQi8VN3Tc,
	fabio.estevam-3arQi8VN3Tc, robh+dt-DgEjT+Ai2ygdnm+yROfE0A,
	mark.rutland-5wv7dgnIgG8,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-clk-u79uwXL29TY76Z2rM5mHXA,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <20170330005029.6472-1-stefan-XLVq0VzYD2Y@public.gmane.org>

On Wed, Mar 29, 2017 at 05:50:28PM -0700, Stefan Agner wrote:
> The USDHC NAND root clock is not gated by any CCM clock gate. Remove
> the bogus gate definition.
> 
> Signed-off-by: Stefan Agner <stefan-XLVq0VzYD2Y@public.gmane.org>
> ---
> The IMX7D_NAND_USDHC_BUS_ROOT_CLK clock is also in clks_init_on.
> In a quick test I removed IMX7D_NAND_USDHC_BUS_ROOT_CLK from
> clks_init_on, and the system including SD-cards seemed to work
> fine... So I guess we could remove the clock from clks_init_on
> after the two both changes got applied, any thoughts?
> 

Yes, it can be removed after apply.

> The gate 0x4130 was actually the DRAM gate, hence disabling that
> clock lead to disabling this gate, and hence a crash before this
> fixes... Maybe that was the reason it landed in clks_init_on...?
> 

Probably a history reason or mistake. :-)

Acked-by: Dong Aisheng <aisheng.dong-3arQi8VN3Tc@public.gmane.org>

Regards
Dong Aisheng

> --
> Stefan
> 
>  drivers/clk/imx/clk-imx7d.c | 3 +--
>  1 file changed, 1 insertion(+), 2 deletions(-)
> 
> diff --git a/drivers/clk/imx/clk-imx7d.c b/drivers/clk/imx/clk-imx7d.c
> index ae1d31be906e..4466acaacb71 100644
> --- a/drivers/clk/imx/clk-imx7d.c
> +++ b/drivers/clk/imx/clk-imx7d.c
> @@ -724,7 +724,7 @@ static void __init imx7d_clocks_init(struct device_node *ccm_node)
>  	clks[IMX7D_MAIN_AXI_ROOT_DIV] = imx_clk_divider2("axi_post_div", "axi_pre_div", base + 0x8800, 0, 6);
>  	clks[IMX7D_DISP_AXI_ROOT_DIV] = imx_clk_divider2("disp_axi_post_div", "disp_axi_pre_div", base + 0x8880, 0, 6);
>  	clks[IMX7D_ENET_AXI_ROOT_DIV] = imx_clk_divider2("enet_axi_post_div", "enet_axi_pre_div", base + 0x8900, 0, 6);
> -	clks[IMX7D_NAND_USDHC_BUS_ROOT_DIV] = imx_clk_divider2("nand_usdhc_post_div", "nand_usdhc_pre_div", base + 0x8980, 0, 6);
> +	clks[IMX7D_NAND_USDHC_BUS_ROOT_CLK] = imx_clk_divider2("nand_usdhc_root_clk", "nand_usdhc_pre_div", base + 0x8980, 0, 6);
>  	clks[IMX7D_AHB_CHANNEL_ROOT_DIV] = imx_clk_divider2("ahb_post_div", "ahb_pre_div", base + 0x9000, 0, 6);
>  	clks[IMX7D_DRAM_ROOT_DIV] = imx_clk_divider2("dram_post_div", "dram_cg", base + 0x9880, 0, 3);
>  	clks[IMX7D_DRAM_PHYM_ALT_ROOT_DIV] = imx_clk_divider2("dram_phym_alt_post_div", "dram_phym_alt_pre_div", base + 0xa000, 0, 3);
> @@ -797,7 +797,6 @@ static void __init imx7d_clocks_init(struct device_node *ccm_node)
>  	clks[IMX7D_ENET_AXI_ROOT_CLK] = imx_clk_gate4("enet_axi_root_clk", "enet_axi_post_div", base + 0x4060, 0);
>  	clks[IMX7D_OCRAM_CLK] = imx_clk_gate4("ocram_clk", "axi_post_div", base + 0x4110, 0);
>  	clks[IMX7D_OCRAM_S_CLK] = imx_clk_gate4("ocram_s_clk", "ahb_post_div", base + 0x4120, 0);
> -	clks[IMX7D_NAND_USDHC_BUS_ROOT_CLK] = imx_clk_gate4("nand_usdhc_root_clk", "nand_usdhc_post_div", base + 0x4130, 0);
>  	clks[IMX7D_AHB_CHANNEL_ROOT_CLK] = imx_clk_gate4("ahb_root_clk", "ahb_post_div", base + 0x4200, 0);
>  	clks[IMX7D_DRAM_ROOT_CLK] = imx_clk_gate4("dram_root_clk", "dram_post_div", base + 0x4130, 0);
>  	clks[IMX7D_DRAM_PHYM_ROOT_CLK] = imx_clk_gate4("dram_phym_root_clk", "dram_phym_cg", base + 0x4130, 0);
> -- 
> 2.12.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

* Re: [PATCH V3 0/5] iommu/arm-smmu: Add runtime pm/sleep support
From: Rob Clark @ 2017-04-01  2:58 UTC (permalink / raw)
  To: Will Deacon
  Cc: Mark Rutland, devicetree-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
	Mathieu Poirier, linux-arm-msm, Stephen Boyd,
	iommu-cunTk1MwBs9QetFLy7KEm3xJsTq8ys+cHZ5vskTnxNA@public.gmane.org,
	Rob Herring, linux-clk-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r@public.gmane.org
In-Reply-To: <20170331175457.GD4897-5wv7dgnIgG8@public.gmane.org>

On Fri, Mar 31, 2017 at 1:54 PM, Will Deacon <will.deacon-5wv7dgnIgG8@public.gmane.org> wrote:
> On Thu, Mar 09, 2017 at 09:05:43PM +0530, Sricharan R wrote:
>> This series provides the support for turning on the arm-smmu's
>> clocks/power domains using runtime pm. This is done using the
>> recently introduced device links patches, which lets the symmu's
>> runtime to follow the master's runtime pm, so the smmu remains
>> powered only when the masters use it.
>
> Do you have any numbers for the power savings you achieve with this?
> How often do we actually manage to stop the SMMU clocks on an SoC with
> a handful of masters?
>
> In other words, is this too coarse-grained to be useful, or is it common
> that all the devices upstream of the SMMU are suspended?

well, if you think about a phone/tablet with a command mode panel,
pretty much all devices will be suspended most of the time ;-)

maybe it's a different case with servers.. unfortunately we have to
share the same driver across both..

BR,
-R

> Thanks,
>
> Will
>
>>
>> Took some reference from the exynos runtime patches [2].
>> Tested this with MDP, GPU, VENUS devices on apq8096-db820c board.
>>
>> Previous version of the patchset [1].
>>
>> [V3]
>>    * Reworked the patches to keep the clocks init/enabling function
>>      seperately for each compatible.
>>
>>    * Added clocks bindings for MMU40x/500.
>>
>>    * Added a new compatible for qcom,smmu-v2 implementation and
>>      the clock bindings for the same.
>>
>>    * Rebased on top of 4.11-rc1
>>
>> [V2]
>>    * Split the patches little differently.
>>
>>    * Addressed comments.
>>
>>    * Removed the patch #4 [3] from previous post
>>      for arm-smmu context save restore. Planning to
>>      post this separately after reworking/addressing Robin's
>>      feedback.
>>
>>    * Reversed the sequence to disable clocks than enabling.
>>      This was required for those cases where the
>>      clocks are populated in a dependent order from DT.
>>
>> [1] https://www.spinics.net/lists/linux-arm-msm/msg23870.html
>> [2] https://lkml.org/lkml/2016/10/20/70
>> [3] https://patchwork.kernel.org/patch/9389717/
>>
>> Sricharan R (5):
>>   iommu/arm-smmu: Add pm_runtime/sleep ops
>>   iommu/arm-smmu: Add support for MMU40x/500 clocks
>>   drivers: arm-smmu: Add clock support for QCOM_SMMUV2
>>   iommu/arm-smmu: Invoke pm_runtime during probe, add/remove device
>>   iommu/arm-smmu: Add the device_link between masters and smmu
>>
>>  .../devicetree/bindings/iommu/arm,smmu.txt         |  35 +++
>>  drivers/iommu/arm-smmu.c                           | 349 ++++++++++++++++++++-
>>  2 files changed, 373 insertions(+), 11 deletions(-)
>>
>> --
>> QUALCOMM INDIA, on behalf of Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation
>>
> _______________________________________________
> iommu mailing list
> iommu-cunTk1MwBs9QetFLy7KEm3xJsTq8ys+cHZ5vskTnxNA@public.gmane.org
> https://lists.linuxfoundation.org/mailman/listinfo/iommu

^ permalink raw reply

* Re: [PATCH V8 5/6] ACPI: Support the probing on the devices which apply indirect-IO
From: zhichang.yuan @ 2017-04-01  2:16 UTC (permalink / raw)
  To: Rafael J. Wysocki, zhichang.yuan
  Cc: Rafael J. Wysocki, Catalin Marinas, Will Deacon, Rob Herring,
	Frank Rowand, Bjorn Helgaas, Arnd Bergmann,
	linux-arm-kernel@lists.infradead.org, Mark Rutland, Brian Starkey,
	Olof Johansson, Lorenzo Pieralisi, Benjamin Herrenschmidt,
	Linux Kernel Mailing List, ACPI Devel Maling List, linuxarm,
	devicetree@vger.kernel.org
In-Reply-To: <CAJZ5v0gZdghH7jjd-zMmqXNkuvVVVSPDHfBrJXx5OXR2NE0KgA@mail.gmail.com>



On 04/01/2017 07:02 AM, Rafael J. Wysocki wrote:
> On Fri, Mar 31, 2017 at 8:52 AM, zhichang.yuan
> <yuanzhichang@hisilicon.com> wrote:
>> Hi, Rafael,
>>
>> Thanks for reviewing this!
>>
>> On 2017/3/31 4:31, Rafael J. Wysocki wrote:
>>> On Thursday, March 30, 2017 11:26:58 PM zhichang.yuan wrote:
>>>> On some platforms(such as Hip06/Hip07), the legacy ISA/LPC devices access I/O
>>>> with some special host-local I/O ports known on x86. To access the I/O
>>>> peripherals, an indirect-IO mechanism is introduced to mapped the host-local
>>>> I/O to system logical/fake PIO similar the PCI MMIO on architectures where no
>>>> separate I/O space exists. Just as PCI MMIO, the host I/O range should be
>>>> registered before probing the downstream devices and set up the I/O mapping.
>>>> But current ACPI bus probing doesn't support these indirect-IO hosts/devices.
>>>>
>>>> This patch introdueces a new ACPI handler for this device category. Through the
>>>> handler attach callback, the indirect-IO hosts I/O registration is done and
>>>> all peripherals' I/O resources are translated into logic/fake PIO before
>>>> starting the enumeration.
>>>
>>> Can you explain to me briefly what exactly this code is expected to be doing?
>>
>> As you know currently for ARM architecture IO space is memory mapped and
>> is only used by pci devices. The port number is dynamically allocated
>> converting the device IO address into a PIO token: i.e.
>> http://lxr.free-electrons.com/source/drivers/acpi/pci_root.c#L745
>> This patch is meant to support a new class of IO host controller
>> that are not PCI based and that still require to have the IO addresses
>> be translated in the same PIO token space as the PCI controller
> 
> IOW, this is ARM-specific, right?

Yes. The current host added in this patch with _HID "HISI0191" is on ARM64.
But, I think the handler driver is architecture dependent.

Thanks,
Zhichang

> 
> Thanks,
> Rafael
> 

^ permalink raw reply

* Re: [PATCH v5 3/6] irqdomain: Add irq_domain_{push,pop}_irq() functions.
From: David Daney @ 2017-04-01  0:21 UTC (permalink / raw)
  To: Marc Zyngier, David Daney, Linus Walleij, Alexandre Courbot,
	Rob Herring, Mark Rutland, Thomas Gleixner, linux-gpio,
	devicetree
  Cc: linux-kernel
In-Reply-To: <4486da82-bc22-4334-3d48-70f4304da591@arm.com>

On 03/14/2017 09:11 AM, Marc Zyngier wrote:
> Hi David,
>
> On 01/03/17 01:48, David Daney wrote:
>> For an already existing irqdomain hierarchy, as might be obtained via
>> a call to pci_enable_msix(), a PCI driver wishing to add an additional
>> irqdomain to the hierarchy needs to be able to insert the irqdomain to
>> that already initialized hierarchy.  Calling
>> irq_domain_create_hierarchy() allows the new irqdomain to be created,
>> but no existing code allows for initializing the associated irq_data.
>
> I must say that I like this idea a lot. Pretty elegant. Now, there is a
> couple of things that do worry me. And instead of worrying, maybe I
> should just ask the questions.
>
>> Add a couple of helper functions (irq_domain_push_irq()
>> irq_domain_pop_irq()) to initialize the irq_data for the new
>> irqdomain.
>>
>> Signed-off-by: David Daney <david.daney@cavium.com>
>> ---
>>  include/linux/irqdomain.h |   3 +
>>  kernel/irq/irqdomain.c    | 137 ++++++++++++++++++++++++++++++++++++++++++++++
>>  2 files changed, 140 insertions(+)
>>
>> diff --git a/include/linux/irqdomain.h b/include/linux/irqdomain.h
>> index 188eced..a7a16b7 100644
>> --- a/include/linux/irqdomain.h
>> +++ b/include/linux/irqdomain.h
>> @@ -425,6 +425,9 @@ extern void irq_domain_free_irqs_common(struct irq_domain *domain,
>>  extern void irq_domain_free_irqs_top(struct irq_domain *domain,
>>  				     unsigned int virq, unsigned int nr_irqs);
>>
>> +extern int irq_domain_push_irq(struct irq_domain *domain, int virq, void *arg);
>> +extern int irq_domain_pop_irq(struct irq_domain *domain, int virq);
>> +
>>  extern int irq_domain_alloc_irqs_parent(struct irq_domain *domain,
>>  					unsigned int irq_base,
>>  					unsigned int nr_irqs, void *arg);
>> diff --git a/kernel/irq/irqdomain.c b/kernel/irq/irqdomain.c
>> index 31805f2..d5d1c01 100644
>> --- a/kernel/irq/irqdomain.c
>> +++ b/kernel/irq/irqdomain.c
>> @@ -1304,6 +1304,143 @@ int __irq_domain_alloc_irqs(struct irq_domain *domain, int irq_base,
>>  	return ret;
>>  }
>>
>> +/* The irq_data was moved, fix the revmap to refer to the new location */
>> +static void irq_domain_fix_revmap(struct irq_data *d)
>> +{
>> +	void **slot;
>> +
>> +	if (d->hwirq < d->domain->revmap_size)
>> +		return; /* Not using radix tree. */
>> +
>> +	/* Fix up the revmap. */
>> +	mutex_lock(&revmap_trees_mutex);
>> +	slot = radix_tree_lookup_slot(&d->domain->revmap_tree, d->hwirq);
>> +	if (slot)
>> +		radix_tree_replace_slot(&d->domain->revmap_tree, slot, d);
>> +	mutex_unlock(&revmap_trees_mutex);
>> +}
>> +
>> +/**
>> + * irq_domain_push_irq() - Push a domain in to the top of a hierarchy.
>> + * @domain:	Domain to push.
>> + * @virq:	Irq to push the domain in to.
>> + * @arg:	Passed to the irq_domain_ops alloc() function.
>> + *
>> + * For an already existing irqdomain hierarchy, as might be obtained
>> + * via a call to pci_enable_msix(), add an additional domain to the
>> + * head of the processing chain.
>> + */
>> +int irq_domain_push_irq(struct irq_domain *domain, int virq, void *arg)
>> +{
>> +	struct irq_data *child_irq_data;
>> +	struct irq_data *root_irq_data = irq_get_irq_data(virq);
>> +
>> +	if (domain == NULL)
>> +		return -EINVAL;
>> +
>> +	if (WARN_ON(!domain->ops->alloc))
>> +		return -EINVAL;
>> +
>> +	if (!root_irq_data)
>> +		return -EINVAL;
>> +
>> +	child_irq_data = kzalloc_node(sizeof(*child_irq_data), GFP_KERNEL,
>> +				      irq_data_get_node(root_irq_data));
>> +	if (!child_irq_data)
>> +		return -ENOMEM;
>> +
>> +	mutex_lock(&irq_domain_mutex);
>> +
>> +	/* Copy the original irq_data. */
>> +	*child_irq_data = *root_irq_data;
>> +
>> +	irq_domain_fix_revmap(child_irq_data);
>> +
>> +	/*
>> +	 * Overwrite the root_irq_data, which is embedded in struct
>> +	 * irq_desc, with values for this domain.
>> +	 */
>> +	root_irq_data->parent_data = child_irq_data;
>> +	root_irq_data->domain = domain;
>> +	root_irq_data->mask = 0;
>> +	root_irq_data->hwirq = 0;
>> +	root_irq_data->chip = NULL;
>> +	root_irq_data->chip_data = NULL;
>
> What guarantees do we have that nobody is using this irqdesc at this
> point? Is it a "don't do that because it will hurt" kind of thing?

Yes.

> I'd be more confident if we had some locking here, just to make sure that we
> don't start processing an interrupt with all these NULL pointers.
>

The only time it makes sense to push/pop is when no request_irq() are 
active.  Perhaps checking (with proper locking) that there are no 
actions registered is the proper thing to do.

> Also, maybe moving the whole stuff to a helper in irqdesc.c if that
> makes it easier/nicer? Your call.
>
>> +	domain->ops->alloc(domain, virq, 1, arg);
>
> Check return value? You may have to revert your previous fixup if it fails.

OK.

>
>> +
>> +	if (root_irq_data->hwirq < domain->revmap_size) {
>> +		domain->linear_revmap[root_irq_data->hwirq] = virq;
>> +	} else {
>> +		mutex_lock(&revmap_trees_mutex);
>> +		radix_tree_insert(&domain->revmap_tree,
>> +				  root_irq_data->hwirq, root_irq_data);
>> +		mutex_unlock(&revmap_trees_mutex);
>> +	}
>> +
>> +	mutex_unlock(&irq_domain_mutex);
>> +
>> +	return 0;
>> +}
>> +EXPORT_SYMBOL_GPL(irq_domain_push_irq);
>> +
>> +/**
>> + * irq_domain_pop_irq() - Remove a domain from the top of a hierarchy.
>> + * @domain:	Domain to remove.
>> + * @virq:	Irq to remove the domain from.
>> + *
>> + * Undo the effects of a call to irq_domain_push_irq().
>> + */
>> +int irq_domain_pop_irq(struct irq_domain *domain, int virq)
>> +{
>> +	struct irq_data *root_irq_data = irq_get_irq_data(virq);
>> +	struct irq_data *child_irq_data;
>> +	struct irq_data *tmp_irq_data;
>> +
>> +	if (domain == NULL)
>> +		return -EINVAL;
>> +
>> +	if (!root_irq_data)
>> +		return -EINVAL;
>> +
>> +	tmp_irq_data = irq_domain_get_irq_data(domain, virq);
>> +
>> +	/* We can only "pop" if this domain is at the top of the list */
>> +	if (WARN_ON(root_irq_data != tmp_irq_data))
>> +		return -EINVAL;
>> +
>> +	if (WARN_ON(root_irq_data->domain != domain))
>> +		return -EINVAL;
>> +
>> +	child_irq_data = root_irq_data->parent_data;
>> +	if (WARN_ON(!child_irq_data))
>> +		return -EINVAL;
>> +
>> +	mutex_lock(&irq_domain_mutex);
>> +
>> +	root_irq_data->parent_data = NULL;
>> +
>> +	if (root_irq_data->hwirq >= domain->revmap_size) {
>> +		mutex_lock(&revmap_trees_mutex);
>> +		radix_tree_delete(&domain->revmap_tree, root_irq_data->hwirq);
>> +		mutex_unlock(&revmap_trees_mutex);
>> +	}
>> +
>> +	if (domain->ops->free)
>> +		domain->ops->free(domain, virq, 1);
>> +
>> +	/* Restore the original irq_data. */
>> +	*root_irq_data = *child_irq_data;
>
> Similar concerns about locking here.
>
>> +
>> +	irq_domain_fix_revmap(root_irq_data);
>> +
>> +	mutex_unlock(&irq_domain_mutex);
>> +
>> +	kfree(child_irq_data);
>> +
>> +	return 0;
>> +}
>> +EXPORT_SYMBOL_GPL(irq_domain_pop_irq);
>> +
>>  /**
>>   * irq_domain_free_irqs - Free IRQ number and associated data structures
>>   * @virq:	base IRQ number
>>
>
> Thanks,
>
> 	M.
>


^ permalink raw reply

* Re: [PATCH v5 2/6] genirq: Add handle_fasteoi_{level,edge}_irq flow handlers.
From: David Daney @ 2017-03-31 23:57 UTC (permalink / raw)
  To: Marc Zyngier, David Daney, Linus Walleij, Alexandre Courbot,
	Rob Herring, Mark Rutland, Thomas Gleixner,
	linux-gpio-u79uwXL29TY76Z2rM5mHXA,
	devicetree-u79uwXL29TY76Z2rM5mHXA
  Cc: linux-kernel-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <254c0505-6890-2e9f-cf04-3c8efbcfeb2d-5wv7dgnIgG8@public.gmane.org>

I am finally getting back to this, sorry for the delay ...

On 03/14/2017 09:54 AM, Marc Zyngier wrote:
> On 01/03/17 01:48, David Daney wrote:
>> Follow-on patch for gpio-thunderx uses a irqdomain hierarchy which
>> requires slightly different flow handlers, add them to chip.c which
>> contains most of the other flow handlers.
>>
>> Signed-off-by: David Daney <david.daney-YGCgFSpz5w/QT0dZR+AlfA@public.gmane.org>
>> ---
>>  include/linux/irq.h |   2 ++
>>  kernel/irq/chip.c   | 102 ++++++++++++++++++++++++++++++++++++++++++++++++++++
>>  2 files changed, 104 insertions(+)
>>
>> diff --git a/include/linux/irq.h b/include/linux/irq.h
>> index f887351..3db0eb8 100644
>> --- a/include/linux/irq.h
>> +++ b/include/linux/irq.h
>> @@ -518,6 +518,8 @@ static inline int irq_set_parent(int irq, int parent_irq)
>>  extern int irq_chip_pm_get(struct irq_data *data);
>>  extern int irq_chip_pm_put(struct irq_data *data);
>>  #ifdef	CONFIG_IRQ_DOMAIN_HIERARCHY
>> +extern void handle_fasteoi_edge_irq(struct irq_desc *desc);
>> +extern void handle_fasteoi_level_irq(struct irq_desc *desc);
>>  extern void irq_chip_enable_parent(struct irq_data *data);
>>  extern void irq_chip_disable_parent(struct irq_data *data);
>>  extern void irq_chip_ack_parent(struct irq_data *data);
>> diff --git a/kernel/irq/chip.c b/kernel/irq/chip.c
>> index 73ea90b..213105d 100644
>> --- a/kernel/irq/chip.c
>> +++ b/kernel/irq/chip.c
>> @@ -981,6 +981,108 @@ void irq_cpu_offline(void)
>>
>>  #ifdef	CONFIG_IRQ_DOMAIN_HIERARCHY
>>  /**
>> + *	handle_fasteoi_edge_irq - irq handler for edge hierarchy
>> + *	stacked on transparent controllers
>> + *
>> + *	@desc:	the interrupt description structure for this irq
>> + *
>> + *	Like handle_fasteoi_irq(), but for use with hierarchy where
>> + *	the irq_chip also needs to have its ->irq_ack() function
>> + *	called.
>> + */
>> +void handle_fasteoi_edge_irq(struct irq_desc *desc)
>> +{
>> +	struct irq_chip *chip = desc->irq_data.chip;
>> +
>> +	raw_spin_lock(&desc->lock);
>> +
>> +	if (!irq_may_run(desc))
>> +		goto out;
>> +
>> +	desc->istate &= ~(IRQS_REPLAY | IRQS_WAITING);
>> +
>> +	/*
>> +	 * If its disabled or no action available
>> +	 * then mask it and get out of here:
>> +	 */
>> +	if (unlikely(!desc->action || irqd_irq_disabled(&desc->irq_data))) {
>> +		desc->istate |= IRQS_PENDING;
>> +		mask_irq(desc);
>> +		goto out;
>> +	}
>> +
>> +	kstat_incr_irqs_this_cpu(desc);
>> +	if (desc->istate & IRQS_ONESHOT)
>> +		mask_irq(desc);
>> +
>> +	/* Start handling the irq */
>> +	desc->irq_data.chip->irq_ack(&desc->irq_data);
>> +
>> +	preflow_handler(desc);
>> +	handle_irq_event(desc);
>> +
>> +	cond_unmask_eoi_irq(desc, chip);
>> +
>> +	raw_spin_unlock(&desc->lock);
>> +	return;
>> +out:
>> +	if (!(chip->flags & IRQCHIP_EOI_IF_HANDLED))
>> +		chip->irq_eoi(&desc->irq_data);
>> +	raw_spin_unlock(&desc->lock);
>> +}
>> +EXPORT_SYMBOL_GPL(handle_fasteoi_edge_irq);
>
> So this is handle_fasteoi_irq with an irq_ack() added in the middle. In
> the spirit of making this a bit more maintainable, how about making the
> handle_fasteoi_irq code reusable (if necessary by forcing the compiler
> to inline stuff)?

You, yourself, said in response to an earlier version of the patch set:


     So you want to generalize CONFIG_IRQ_PREFLOW_FASTEOI so that it is
     on each level of a domain stack? Humm. I personally think that
     this is a massive bloat that is going to impact all the hot paths
     for no gain whatsoever, but I'll let tglx speak his mind on that.

Your current suggestion is, from the point of view of the "impact", 
almost identical to what I was proposing back then.

>
> But the one thing that makes me uncomfortable here is that we're seem to
> have this irq_ack() propagated all along the irqdata chain, which is not
> what's happening. Only the EOI gets propagated.

That is intentional.  The parent domain is handle_fasteoi_irq(), so we 
know that it has no irq_ack().


>
> Why can't you just put the irq_ack call in your top level irq_eoi
> callback? That'd make it similar to what is happening on the mbigen side
> (not exactly surprising, since they are doing very similar things).

irq_ack() must be called before handle_irq_event(), otherwise there is a 
race condition where edge interrupts could be lost.


>
> Same remark about handle_fasteoi_level_irq.
>
> Thoughts?

Several:

1) Adding the additional mask_ack_irq()/irq_ack() to 
handle_fasteoi_irq() is probably the cleanest solution, however ...

2) There is a risk of breaking systems that inadvertently supply the new 
chip methods, but the methods don't currently get called by 
handle_fasteoi_irq().

3) Checking for the presence of mask_ack_irq()/irq_ack() methods may 
introduce additional branch mispredictions and cache misses on all 
non-ThunderGPIO systems.


David Daney
--
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

* [PATCH v6 2/2] mmc: host: s3cmci: allow probing from device tree
From: Sergio Prado @ 2017-03-31 23:50 UTC (permalink / raw)
  To: ulf.hansson, robh+dt, mark.rutland, linux-mmc, devicetree,
	linux-kernel, ben-linux, linux-arm-kernel
  Cc: Sergio Prado
In-Reply-To: <1491004256-13969-1-git-send-email-sergio.prado@e-labworks.com>

Allows configuring Samsung S3C24XX MMC/SD/SDIO controller using a device
tree.

Signed-off-by: Sergio Prado <sergio.prado@e-labworks.com>
---
 drivers/mmc/host/s3cmci.c | 257 +++++++++++++++++++++++-----------------------
 1 file changed, 126 insertions(+), 131 deletions(-)

diff --git a/drivers/mmc/host/s3cmci.c b/drivers/mmc/host/s3cmci.c
index 7a173f8c455b..c101f7e178f7 100644
--- a/drivers/mmc/host/s3cmci.c
+++ b/drivers/mmc/host/s3cmci.c
@@ -24,6 +24,10 @@
 #include <linux/interrupt.h>
 #include <linux/irq.h>
 #include <linux/io.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/of_gpio.h>
+#include <linux/mmc/slot-gpio.h>
 
 #include <plat/gpio-cfg.h>
 #include <mach/dma.h>
@@ -807,21 +811,6 @@ static irqreturn_t s3cmci_irq(int irq, void *dev_id)
 
 }
 
-/*
- * ISR for the CardDetect Pin
-*/
-
-static irqreturn_t s3cmci_irq_cd(int irq, void *dev_id)
-{
-	struct s3cmci_host *host = (struct s3cmci_host *)dev_id;
-
-	dbg(host, dbg_irq, "card detect\n");
-
-	mmc_detect_change(host->mmc, msecs_to_jiffies(500));
-
-	return IRQ_HANDLED;
-}
-
 static void s3cmci_dma_done_callback(void *arg)
 {
 	struct s3cmci_host *host = arg;
@@ -1177,19 +1166,6 @@ static void s3cmci_send_request(struct mmc_host *mmc)
 	s3cmci_enable_irq(host, true);
 }
 
-static int s3cmci_card_present(struct mmc_host *mmc)
-{
-	struct s3cmci_host *host = mmc_priv(mmc);
-	struct s3c24xx_mci_pdata *pdata = host->pdata;
-	int ret;
-
-	if (pdata->no_detect)
-		return -ENOSYS;
-
-	ret = gpio_get_value(pdata->gpio_detect) ? 0 : 1;
-	return ret ^ pdata->detect_invert;
-}
-
 static void s3cmci_request(struct mmc_host *mmc, struct mmc_request *mrq)
 {
 	struct s3cmci_host *host = mmc_priv(mmc);
@@ -1198,7 +1174,7 @@ static void s3cmci_request(struct mmc_host *mmc, struct mmc_request *mrq)
 	host->cmd_is_stop = 0;
 	host->mrq = mrq;
 
-	if (s3cmci_card_present(mmc) == 0) {
+	if (mmc_gpio_get_cd(mmc) == 0) {
 		dbg(host, dbg_err, "%s: no medium present\n", __func__);
 		host->mrq->cmd->error = -ENOMEDIUM;
 		mmc_request_done(mmc, mrq);
@@ -1242,8 +1218,9 @@ static void s3cmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
 	case MMC_POWER_ON:
 	case MMC_POWER_UP:
 		/* Configure GPE5...GPE10 pins in SD mode */
-		s3c_gpio_cfgall_range(S3C2410_GPE(5), 6, S3C_GPIO_SFN(2),
-				      S3C_GPIO_PULL_NONE);
+		if (!host->pdev->dev.of_node)
+			s3c_gpio_cfgall_range(S3C2410_GPE(5), 6, S3C_GPIO_SFN(2),
+					      S3C_GPIO_PULL_NONE);
 
 		if (host->pdata->set_power)
 			host->pdata->set_power(ios->power_mode, ios->vdd);
@@ -1255,7 +1232,8 @@ static void s3cmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
 
 	case MMC_POWER_OFF:
 	default:
-		gpio_direction_output(S3C2410_GPE(5), 0);
+		if (!host->pdev->dev.of_node)
+			gpio_direction_output(S3C2410_GPE(5), 0);
 
 		if (host->is2440)
 			mci_con |= S3C2440_SDICON_SDRESET;
@@ -1295,21 +1273,6 @@ static void s3cmci_reset(struct s3cmci_host *host)
 	writel(con, host->base + S3C2410_SDICON);
 }
 
-static int s3cmci_get_ro(struct mmc_host *mmc)
-{
-	struct s3cmci_host *host = mmc_priv(mmc);
-	struct s3c24xx_mci_pdata *pdata = host->pdata;
-	int ret;
-
-	if (pdata->no_wprotect)
-		return 0;
-
-	ret = gpio_get_value(pdata->gpio_wprotect) ? 1 : 0;
-	ret ^= pdata->wprotect_invert;
-
-	return ret;
-}
-
 static void s3cmci_enable_sdio_irq(struct mmc_host *mmc, int enable)
 {
 	struct s3cmci_host *host = mmc_priv(mmc);
@@ -1353,8 +1316,8 @@ static void s3cmci_enable_sdio_irq(struct mmc_host *mmc, int enable)
 static struct mmc_host_ops s3cmci_ops = {
 	.request	= s3cmci_request,
 	.set_ios	= s3cmci_set_ios,
-	.get_ro		= s3cmci_get_ro,
-	.get_cd		= s3cmci_card_present,
+	.get_ro		= mmc_gpio_get_ro,
+	.get_cd		= mmc_gpio_get_cd,
 	.enable_sdio_irq = s3cmci_enable_sdio_irq,
 };
 
@@ -1545,21 +1508,14 @@ static inline void s3cmci_debugfs_remove(struct s3cmci_host *host) { }
 
 #endif /* CONFIG_DEBUG_FS */
 
-static int s3cmci_probe(struct platform_device *pdev)
+static int s3cmci_probe_pdata(struct s3cmci_host *host)
 {
-	struct s3cmci_host *host;
-	struct mmc_host	*mmc;
-	int ret;
-	int is2440;
-	int i;
+	struct platform_device *pdev = host->pdev;
+	struct mmc_host *mmc = host->mmc;
+	struct s3c24xx_mci_pdata *pdata;
+	int i, ret;
 
-	is2440 = platform_get_device_id(pdev)->driver_data;
-
-	mmc = mmc_alloc_host(sizeof(struct s3cmci_host), &pdev->dev);
-	if (!mmc) {
-		ret = -ENOMEM;
-		goto probe_out;
-	}
+	host->is2440 = platform_get_device_id(pdev)->driver_data;
 
 	for (i = S3C2410_GPE(5); i <= S3C2410_GPE(10); i++) {
 		ret = gpio_request(i, dev_name(&pdev->dev));
@@ -1569,25 +1525,101 @@ static int s3cmci_probe(struct platform_device *pdev)
 			for (i--; i >= S3C2410_GPE(5); i--)
 				gpio_free(i);
 
-			goto probe_free_host;
+			return ret;
+		}
+	}
+
+	if (!pdev->dev.platform_data)
+		pdev->dev.platform_data = &s3cmci_def_pdata;
+
+	pdata = pdev->dev.platform_data;
+
+	if (pdata->no_wprotect)
+		mmc->caps2 |= MMC_CAP2_NO_WRITE_PROTECT;
+
+	if (pdata->no_detect)
+		mmc->caps |= MMC_CAP_NEEDS_POLL;
+
+	if (pdata->wprotect_invert);
+		mmc->caps2 |= MMC_CAP2_RO_ACTIVE_HIGH;
+
+	if (pdata->detect_invert)
+		 mmc->caps2 |= MMC_CAP2_CD_ACTIVE_HIGH;
+
+	if (gpio_is_valid(pdata->gpio_detect)) {
+		ret = mmc_gpio_request_cd(mmc, pdata->gpio_detect, 0);
+		if (ret) {
+			dev_err(&pdev->dev, "error requesting GPIO for CD %d\n",
+				ret);
+			return ret;
 		}
 	}
 
+	if (gpio_is_valid(pdata->gpio_wprotect)) {
+		ret = mmc_gpio_request_ro(mmc, pdata->gpio_wprotect);
+		if (ret) {
+			dev_err(&pdev->dev, "error requesting GPIO for WP %d\n",
+				ret);
+			return ret;
+		}
+	}
+
+	return 0;
+}
+
+static int s3cmci_probe_dt(struct s3cmci_host *host)
+{
+	struct platform_device *pdev = host->pdev;
+	struct s3c24xx_mci_pdata *pdata;
+	struct mmc_host *mmc = host->mmc;
+	int ret;
+
+	host->is2440 = (int) of_device_get_match_data(&pdev->dev);
+
+	ret = mmc_of_parse(mmc);
+	if (ret)
+		return ret;
+
+	pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
+	if (!pdata)
+		return -ENOMEM;
+
+	pdev->dev.platform_data = pdata;
+
+	return 0;
+}
+
+static int s3cmci_probe(struct platform_device *pdev)
+{
+	struct s3cmci_host *host;
+	struct mmc_host	*mmc;
+	int ret;
+	int i;
+
+	mmc = mmc_alloc_host(sizeof(struct s3cmci_host), &pdev->dev);
+	if (!mmc) {
+		ret = -ENOMEM;
+		goto probe_out;
+	}
+
 	host = mmc_priv(mmc);
 	host->mmc 	= mmc;
 	host->pdev	= pdev;
-	host->is2440	= is2440;
+
+	if (pdev->dev.of_node)
+		ret = s3cmci_probe_dt(host);
+	else
+		ret = s3cmci_probe_pdata(host);
+
+	if (ret)
+		goto probe_free_host;
 
 	host->pdata = pdev->dev.platform_data;
-	if (!host->pdata) {
-		pdev->dev.platform_data = &s3cmci_def_pdata;
-		host->pdata = &s3cmci_def_pdata;
-	}
 
 	spin_lock_init(&host->complete_lock);
 	tasklet_init(&host->pio_tasklet, pio_tasklet, (unsigned long) host);
 
-	if (is2440) {
+	if (host->is2440) {
 		host->sdiimsk	= S3C2440_SDIIMSK;
 		host->sdidata	= S3C2440_SDIDATA;
 		host->clk_div	= 1;
@@ -1645,43 +1677,6 @@ static int s3cmci_probe(struct platform_device *pdev)
 	disable_irq(host->irq);
 	host->irq_state = false;
 
-	if (!host->pdata->no_detect) {
-		ret = gpio_request(host->pdata->gpio_detect, "s3cmci detect");
-		if (ret) {
-			dev_err(&pdev->dev, "failed to get detect gpio\n");
-			goto probe_free_irq;
-		}
-
-		host->irq_cd = gpio_to_irq(host->pdata->gpio_detect);
-
-		if (host->irq_cd >= 0) {
-			if (request_irq(host->irq_cd, s3cmci_irq_cd,
-					IRQF_TRIGGER_RISING |
-					IRQF_TRIGGER_FALLING,
-					DRIVER_NAME, host)) {
-				dev_err(&pdev->dev,
-					"can't get card detect irq.\n");
-				ret = -ENOENT;
-				goto probe_free_gpio_cd;
-			}
-		} else {
-			dev_warn(&pdev->dev,
-				 "host detect has no irq available\n");
-			gpio_direction_input(host->pdata->gpio_detect);
-		}
-	} else
-		host->irq_cd = -1;
-
-	if (!host->pdata->no_wprotect) {
-		ret = gpio_request(host->pdata->gpio_wprotect, "s3cmci wp");
-		if (ret) {
-			dev_err(&pdev->dev, "failed to get writeprotect\n");
-			goto probe_free_irq_cd;
-		}
-
-		gpio_direction_input(host->pdata->gpio_wprotect);
-	}
-
 	/* Depending on the dma state, get a DMA channel to use. */
 
 	if (s3cmci_host_usedma(host)) {
@@ -1689,7 +1684,7 @@ static int s3cmci_probe(struct platform_device *pdev)
 		ret = PTR_ERR_OR_ZERO(host->dma);
 		if (ret) {
 			dev_err(&pdev->dev, "cannot get DMA channel.\n");
-			goto probe_free_gpio_wp;
+			goto probe_free_irq;
 		}
 	}
 
@@ -1768,18 +1763,6 @@ static int s3cmci_probe(struct platform_device *pdev)
 	if (s3cmci_host_usedma(host))
 		dma_release_channel(host->dma);
 
- probe_free_gpio_wp:
-	if (!host->pdata->no_wprotect)
-		gpio_free(host->pdata->gpio_wprotect);
-
- probe_free_gpio_cd:
-	if (!host->pdata->no_detect)
-		gpio_free(host->pdata->gpio_detect);
-
- probe_free_irq_cd:
-	if (host->irq_cd >= 0)
-		free_irq(host->irq_cd, host);
-
  probe_free_irq:
 	free_irq(host->irq, host);
 
@@ -1790,8 +1773,9 @@ static int s3cmci_probe(struct platform_device *pdev)
 	release_mem_region(host->mem->start, resource_size(host->mem));
 
  probe_free_gpio:
-	for (i = S3C2410_GPE(5); i <= S3C2410_GPE(10); i++)
-		gpio_free(i);
+	if (!pdev->dev.of_node)
+		for (i = S3C2410_GPE(5); i <= S3C2410_GPE(10); i++)
+			gpio_free(i);
 
  probe_free_host:
 	mmc_free_host(mmc);
@@ -1818,7 +1802,6 @@ static int s3cmci_remove(struct platform_device *pdev)
 {
 	struct mmc_host		*mmc  = platform_get_drvdata(pdev);
 	struct s3cmci_host	*host = mmc_priv(mmc);
-	struct s3c24xx_mci_pdata *pd = host->pdata;
 	int i;
 
 	s3cmci_shutdown(pdev);
@@ -1832,15 +1815,9 @@ static int s3cmci_remove(struct platform_device *pdev)
 
 	free_irq(host->irq, host);
 
-	if (!pd->no_wprotect)
-		gpio_free(pd->gpio_wprotect);
-
-	if (!pd->no_detect)
-		gpio_free(pd->gpio_detect);
-
-	for (i = S3C2410_GPE(5); i <= S3C2410_GPE(10); i++)
-		gpio_free(i);
-
+	if (!pdev->dev.of_node)
+		for (i = S3C2410_GPE(5); i <= S3C2410_GPE(10); i++)
+			gpio_free(i);
 
 	iounmap(host->base);
 	release_mem_region(host->mem->start, resource_size(host->mem));
@@ -1849,6 +1826,23 @@ static int s3cmci_remove(struct platform_device *pdev)
 	return 0;
 }
 
+static const struct of_device_id s3cmci_dt_match[] = {
+	{
+		.compatible = "samsung,s3c2410-sdi",
+		.data = (void *)0,
+	},
+	{
+		.compatible = "samsung,s3c2412-sdi",
+		.data = (void *)1,
+	},
+	{
+		.compatible = "samsung,s3c2440-sdi",
+		.data = (void *)1,
+	},
+	{ /* sentinel */ },
+};
+MODULE_DEVICE_TABLE(of, s3cmci_dt_match);
+
 static const struct platform_device_id s3cmci_driver_ids[] = {
 	{
 		.name	= "s3c2410-sdi",
@@ -1868,6 +1862,7 @@ static int s3cmci_remove(struct platform_device *pdev)
 static struct platform_driver s3cmci_driver = {
 	.driver	= {
 		.name	= "s3c-sdi",
+		.of_match_table = s3cmci_dt_match,
 	},
 	.id_table	= s3cmci_driver_ids,
 	.probe		= s3cmci_probe,
-- 
1.9.1

^ permalink raw reply related

* [PATCH v6 1/2] dt-bindings: mmc: add DT binding for S3C24XX MMC/SD/SDIO controller
From: Sergio Prado @ 2017-03-31 23:50 UTC (permalink / raw)
  To: ulf.hansson, robh+dt, mark.rutland, linux-mmc, devicetree,
	linux-kernel, ben-linux, linux-arm-kernel
  Cc: Sergio Prado
In-Reply-To: <1491004256-13969-1-git-send-email-sergio.prado@e-labworks.com>

Adds the device tree bindings description for Samsung S3C24XX
MMC/SD/SDIO controller, used as a connectivity interface with external
MMC, SD and SDIO storage mediums.

Acked-by: Rob Herring <robh@kernel.org>
Signed-off-by: Sergio Prado <sergio.prado@e-labworks.com>
---
 .../devicetree/bindings/mmc/samsung,s3cmci.txt     | 42 ++++++++++++++++++++++
 1 file changed, 42 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/mmc/samsung,s3cmci.txt

diff --git a/Documentation/devicetree/bindings/mmc/samsung,s3cmci.txt b/Documentation/devicetree/bindings/mmc/samsung,s3cmci.txt
new file mode 100644
index 000000000000..5f68feb9f9d6
--- /dev/null
+++ b/Documentation/devicetree/bindings/mmc/samsung,s3cmci.txt
@@ -0,0 +1,42 @@
+* Samsung's S3C24XX MMC/SD/SDIO controller device tree bindings
+
+Samsung's S3C24XX MMC/SD/SDIO controller is used as a connectivity interface
+with external MMC, SD and SDIO storage mediums.
+
+This file documents differences between the core mmc properties described by
+mmc.txt and the properties used by the Samsung S3C24XX MMC/SD/SDIO controller
+implementation.
+
+Required SoC Specific Properties:
+- compatible: should be one of the following
+  - "samsung,s3c2410-sdi": for controllers compatible with s3c2410
+  - "samsung,s3c2412-sdi": for controllers compatible with s3c2412
+  - "samsung,s3c2440-sdi": for controllers compatible with s3c2440
+- reg: register location and length
+- interrupts: mmc controller interrupt
+- clocks: Should reference the controller clock
+- clock-names: Should contain "sdi"
+
+Required Board Specific Properties:
+- pinctrl-0: Should specify pin control groups used for this controller.
+- pinctrl-names: Should contain only one value - "default".
+
+Optional Properties:
+- bus-width: number of data lines (see mmc.txt)
+- cd-gpios: gpio for card detection (see mmc.txt)
+- wp-gpios: gpio for write protection (see mmc.txt)
+
+Example:
+
+	mmc0: mmc@5a000000 {
+		compatible = "samsung,s3c2440-sdi";
+		pinctrl-names = "default";
+		pinctrl-0 = <&sdi_pins>;
+		reg = <0x5a000000 0x100000>;
+		interrupts = <0 0 21 3>;
+		clocks = <&clocks PCLK_SDI>;
+		clock-names = "sdi";
+		bus-width = <4>;
+		cd-gpios = <&gpg 8 GPIO_ACTIVE_LOW>;
+		wp-gpios = <&gph 8 GPIO_ACTIVE_LOW>;
+	};
-- 
1.9.1

^ permalink raw reply related

* [PATCH v6 0/2] mmc: host: s3cmci: add device tree support
From: Sergio Prado @ 2017-03-31 23:50 UTC (permalink / raw)
  To: ulf.hansson-QSEj5FYQhm4dnm+yROfE0A,
	robh+dt-DgEjT+Ai2ygdnm+yROfE0A, mark.rutland-5wv7dgnIgG8,
	linux-mmc-u79uwXL29TY76Z2rM5mHXA,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	ben-linux-elnMNo+KYs3YtjvyW6yDsg,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r
  Cc: Sergio Prado

This series adds support for configuring Samsung's S3C24XX MMC/SD/SDIO
controller via device tree.

Tested on FriendlyARM mini2440, based on s3c2440 SoC.

Changes since v5 (as reported by Arnd Bergmann)
- fix module device table name that was causing an error when building
  the driver as a module

Changes since v4 (as suggested by Jaehoon Chung):
- using just a flag as a data structure for the driver to check for
  s3c2400 compatibility

Changes since v3 (as suggested by Rob Herring):
- describing properties that wasn't documented and removing unnecessary
  comment in the device tree binding

Changes since v2:
- struct "s3cmci_drv_data" and flag "is2440" renamed
- copying the whole driver data struct instead of only its member so
  it will be easier to extend the information in the future
- using mmc_of_parse() to read "cd-gpios" and "wp-gpios" properties
  from device tree

Changes since v1:
- pinctrl description removed from DT binding
- unit and label on DT binding renamed to mmc

Sergio Prado (2):
  dt-bindings: mmc: add DT binding for S3C24XX MMC/SD/SDIO controller
  mmc: host: s3cmci: allow probing from device tree

 .../devicetree/bindings/mmc/samsung,s3cmci.txt     |  42 ++++
 drivers/mmc/host/s3cmci.c                          | 257 ++++++++++-----------
 2 files changed, 168 insertions(+), 131 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/mmc/samsung,s3cmci.txt

-- 
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

* Re: [PATCH V8 5/6] ACPI: Support the probing on the devices which apply indirect-IO
From: Rafael J. Wysocki @ 2017-03-31 23:02 UTC (permalink / raw)
  To: zhichang.yuan
  Cc: Rafael J. Wysocki, Catalin Marinas, Will Deacon, Rob Herring,
	Frank Rowand, Bjorn Helgaas, Rafael J. Wysocki, Arnd Bergmann,
	linux-arm-kernel@lists.infradead.org, Mark Rutland, Brian Starkey,
	Olof Johansson, Lorenzo Pieralisi, Benjamin Herrenschmidt,
	Linux Kernel Mailing List, ACPI Devel Maling List, linuxarm
In-Reply-To: <58DDFCBE.1030705@hisilicon.com>

On Fri, Mar 31, 2017 at 8:52 AM, zhichang.yuan
<yuanzhichang@hisilicon.com> wrote:
> Hi, Rafael,
>
> Thanks for reviewing this!
>
> On 2017/3/31 4:31, Rafael J. Wysocki wrote:
>> On Thursday, March 30, 2017 11:26:58 PM zhichang.yuan wrote:
>>> On some platforms(such as Hip06/Hip07), the legacy ISA/LPC devices access I/O
>>> with some special host-local I/O ports known on x86. To access the I/O
>>> peripherals, an indirect-IO mechanism is introduced to mapped the host-local
>>> I/O to system logical/fake PIO similar the PCI MMIO on architectures where no
>>> separate I/O space exists. Just as PCI MMIO, the host I/O range should be
>>> registered before probing the downstream devices and set up the I/O mapping.
>>> But current ACPI bus probing doesn't support these indirect-IO hosts/devices.
>>>
>>> This patch introdueces a new ACPI handler for this device category. Through the
>>> handler attach callback, the indirect-IO hosts I/O registration is done and
>>> all peripherals' I/O resources are translated into logic/fake PIO before
>>> starting the enumeration.
>>
>> Can you explain to me briefly what exactly this code is expected to be doing?
>
> As you know currently for ARM architecture IO space is memory mapped and
> is only used by pci devices. The port number is dynamically allocated
> converting the device IO address into a PIO token: i.e.
> http://lxr.free-electrons.com/source/drivers/acpi/pci_root.c#L745
> This patch is meant to support a new class of IO host controller
> that are not PCI based and that still require to have the IO addresses
> be translated in the same PIO token space as the PCI controller

IOW, this is ARM-specific, right?

Thanks,
Rafael

^ permalink raw reply

* Re: Device Tree Binding for Intel FPGA Video and Image Processing Suite
From: Rob Herring @ 2017-03-31 22:48 UTC (permalink / raw)
  To: Ong, Hean Loong; +Cc: devicetree-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
In-Reply-To: <1490949905.21575.8.camel-ral2JQCrhuEAvxtiuMwx3w@public.gmane.org>

On Fri, Mar 31, 2017 at 3:50 AM, Ong, Hean Loong
<hean.loong.ong-ral2JQCrhuEAvxtiuMwx3w@public.gmane.org> wrote:
> Hi Rob,
>
> I would like to upstream the attached device tree binding patch to the
> community. This is required for the support of framebuffer drivers of
> the Intel FPGA Video and Image Processing Suite intended for the
> Arria10 devkit.
>
> The device tree information includes the maximum width and height
> supported by the framebuffer display. These are fixed parameters
> determined by the use who the Quartus design system to program the
> FPGA in the Arria10 devkit and its variants.

Please read Documentation/devicetree/bindings/submitting-patches.txt.

The first problem is sending the patch as an attachment.

Rob
--
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

* Re: [PATCH v5 2/2] mmc: host: s3cmci: allow probing from device tree
From: Sergio Prado @ 2017-03-31 22:46 UTC (permalink / raw)
  To: Ulf Hansson
  Cc: Arnd Bergmann, Rob Herring, Mark Rutland,
	linux-mmc-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
	devicetree-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
	Linux Kernel Mailing List,
	ben-linux-elnMNo+KYs3YtjvyW6yDsg@public.gmane.org, Linux ARM
In-Reply-To: <CAPDyKFquGSgK7tkYDpkJM7C7JChZMqvmHoJyiLE9_ZG4CGBOSw-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>

On Fri, Mar 31, 2017 at 01:11:59PM +0200, Ulf Hansson wrote:
> On 31 March 2017 at 09:25, Arnd Bergmann <arnd-r2nGTMty4D4@public.gmane.org> wrote:
> > On Sat, Mar 18, 2017 at 2:13 AM, Sergio Prado
> > <sergio.prado-1e4yhPs3/ABSwrhanM7KvQ@public.gmane.org> wrote:
> >
> >>
> >> +static const struct of_device_id s3cmci_dt_match[] = {
> >> +       {
> >> +               .compatible = "samsung,s3c2410-sdi",
> >> +               .data = (void *)0,
> >> +       },
> >> +       {
> >> +               .compatible = "samsung,s3c2412-sdi",
> >> +               .data = (void *)1,
> >> +       },
> >> +       {
> >> +               .compatible = "samsung,s3c2440-sdi",
> >> +               .data = (void *)1,
> >> +       },
> >> +       { /* sentinel */ },
> >> +};
> >> +MODULE_DEVICE_TABLE(of, sdhci_s3c_dt_match);
> >> +
> >
> > The names don't match:
> >
> > In file included from /git/arm-soc/drivers/mmc/host/s3cmci.c:14:0:
> > /git/arm-soc/drivers/mmc/host/s3cmci.c:1844:25: error:
> > 'sdhci_s3c_dt_match' undeclared here (not in a function); did you mean
> > 's3cmci_dt_match'?
> >  MODULE_DEVICE_TABLE(of, sdhci_s3c_dt_match);
> >                          ^
> > /git/arm-soc/include/linux/module.h:212:21: note: in definition of
> > macro 'MODULE_DEVICE_TABLE'
> >  extern const typeof(name) __mod_##type##__##name##_device_table  \
> >                      ^~~~
> > /git/arm-soc/include/linux/module.h:212:27: error:
> > '__mod_of__sdhci_s3c_dt_match_device_table' aliased to undefined
> > symbol 'sdhci_s3c_dt_match'
> >  extern const typeof(name) __mod_##type##__##name##_device_table  \
> >                            ^
> > /git/arm-soc/drivers/mmc/host/s3cmci.c:1844:1: note: in expansion of
> > macro 'MODULE_DEVICE_TABLE'
> >  MODULE_DEVICE_TABLE(of, sdhci_s3c_dt_match);
> >  ^~~~~~~~~~~~~~~~~~~
> > /git/arm-soc/scripts/Makefile.build:314: recipe for target
> > 'drivers/mmc/host/s3cmci.o' failed
> >
> > Can you send a fix?
> 
> Never mind, I am dropping the patches from my next branch as those
> clearly haven't been built/tested by Sergio properly. It's better that
> he re-spins them.
> 
> Arnd, thanks for reporting and even fixing some of the problems!
> 
> Kind regards
> Uffe

Arnd, thanks for reporting the error.

I have built/tested the patches, but only as a built-in feature, and
this error occurs when enabled as a module.

I will re-spins them and send v6.

Best regards,

Sergio Prado
--
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

* Re: [PATCH 2/2] drivers/serial: Add driver for Aspeed virtual UART
From: Benjamin Herrenschmidt @ 2017-03-31 21:29 UTC (permalink / raw)
  To: Greg Kroah-Hartman, Joel Stanley
  Cc: Jiri Slaby, Mark Rutland, Rob Herring, Jeremy Kerr, linux-serial,
	linux-kernel, openbmc, devicetree
In-Reply-To: <20170331133120.GA26078@kroah.com>

On Fri, 2017-03-31 at 15:31 +0200, Greg Kroah-Hartman wrote:
> DEVICE_ATTR_RW()?
> 
> And why random sysfs files for a uart?  Where have you documented
> these?

We should stick a file somewhere in Documentation I suppose or
at least put that in a comment blob at the head of the file.

FYI:

The VUART is basically two UART 'front ends' connected by their FIFO
(no actual serial line in between). One is on the BMC side (management
controller) and one is on the host CPU side.

It allows the BMC to provide to the host a "UART" that pipes into
the BMC itself and can then be turned by the BMC into a network console
of some sort for example.

This driver is for the BMC side. The sysfs files allow the BMC
userspace which owns the system configuration policy, to specify
at what IO port and interrupt number the host side will appear
to the host on the Host <-> BMC LPC bus. It could be different on a
different system (though most of them use 3f8/4).

Cheers,
Ben.

^ permalink raw reply

* Re: [PATCH V12 4/4] thermal: bcm2835: add thermal driver for bcm2835 SoC
From: Rafał Miłecki @ 2017-03-31 21:22 UTC (permalink / raw)
  To: Stefan Wahren
  Cc: Eduardo Valentin, Zhang Rui, kernel, Eric Anholt, Rob Herring,
	Frank Rowand, Florian Fainelli, linux-rpi-kernel, devicetree,
	linux-pm
In-Reply-To: <1490990586-30898-5-git-send-email-stefan.wahren@i2se.com>

On 2017-03-31 22:03, Stefan Wahren wrote:
> Add basic thermal driver for bcm2835 SoC.
> 
> This driver currently make sure that tsense HW block is set up
> correctly.
> 
> Signed-off-by: Martin Sperl <kernel@martin.sperl.org>
> Signed-off-by: Stefan Wahren <stefan.wahren@i2se.com>
> Acked-by: Eric Anholt <eric@anholt.net>
> Acked-by: Eduardo Valentin <edubezval@gmail.com>

It works great on Raspberry Pi Zero W!

Tested-by: Rafał Miłecki <rafal@milecki.pl>

Sorry for the earlier false failure report. I guess I was missing 
something,
maybe some DT change? Anyway, it works now! :)

^ permalink raw reply

* [PATCH v3] regulator: Add driver for voltage controlled regulators
From: Matthias Kaehlcke @ 2017-03-31 20:50 UTC (permalink / raw)
  To: Liam Girdwood, Mark Brown, Rob Herring, Mark Rutland
  Cc: linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	devicetree-u79uwXL29TY76Z2rM5mHXA, Douglas Anderson, Brian Norris,
	Matthias Kaehlcke

The output voltage of a voltage controlled regulator can be controlled
through the voltage of another regulator. The current version of this
driver assumes that the output voltage is a linear function of the control
voltage.

Signed-off-by: Matthias Kaehlcke <mka-F7+t8E8rja9g9hUCZPvPmw@public.gmane.org>
---
Changes in v3:
- Pre-calculate limits for OVP-safe downward transitions with a discrete
  control regulator
- Use regulator_get_linear_step() and regulator_count_voltages() to
  determine if the control regulator is continuous or discrete
- Changed some log messages to start with lowercase character for
  consistency
- Misc formatting fixes

Changes in v2:
- Don't configure control regulator as supply
- Added enable/disable ops to turn control regulator on/off
- Use regulator_has_continuous_voltage_range() to determine if control
  regulator is continuous or discrete
- Use regulator-min/max-microvolt as output voltage range
- Updated and fixed example in DT bindings
- Updated commit message

 .../devicetree/bindings/regulator/vctrl.txt        |  50 ++
 drivers/regulator/Kconfig                          |   7 +
 drivers/regulator/Makefile                         |   1 +
 drivers/regulator/vctrl-regulator.c                | 553 +++++++++++++++++++++
 4 files changed, 611 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/regulator/vctrl.txt
 create mode 100644 drivers/regulator/vctrl-regulator.c

diff --git a/Documentation/devicetree/bindings/regulator/vctrl.txt b/Documentation/devicetree/bindings/regulator/vctrl.txt
new file mode 100644
index 000000000000..c7ee9956d62d
--- /dev/null
+++ b/Documentation/devicetree/bindings/regulator/vctrl.txt
@@ -0,0 +1,50 @@
+Bindings for Voltage controlled regulators
+==========================================
+
+Required properties:
+--------------------
+- compatible		  : must be "vctrl-regulator".
+- regulator-min-microvolt : smallest voltage consumers may set
+- regulator-max-microvolt : largest voltage consumers may set
+- ctrl-regulator:	  : the name of the regulator supplying the control
+			    voltage.
+- ctrl-voltage-range	  : an array of two integer values describing the range
+			    (min/max) of the control voltage. The values specify
+			    the control voltage needed to generate the corresponding
+			    regulator-min/max-microvolt output voltage.
+
+Optional properties:
+--------------------
+- ovp-threshold-percent	: overvoltage protection (OVP) threshold of the
+			  regulator in percent. Some regulators have an OVP
+			  circuitry which shuts down the regulator when the
+			  actual output voltage deviates beyond a certain
+			  margin from the expected value for a given control
+			  voltage. On larger voltage decreases this can occur
+			  undesiredly since the output voltage does not adjust
+			  inmediately to changes in the control voltage. To
+			  avoid this situation the vctrl driver breaks down
+			  larger voltage decreases into multiple steps, where
+			  each step is within the OVP threshold.
+- min-slew-down-rate	: Describes how slowly the regulator voltage will decay
+			  down in the worst case (lightest expected load).
+			  Specified in uV / us (like main regulator ramp rate).
+			  This value is required when ovp-threshold-percent is
+			  specified.
+
+Example:
+
+	vctrl-reg {
+		compatible = "vctrl-regulator";
+		regulator-name = "vctrl_reg";
+
+		ctrl-regulator = "ctrl_reg";
+
+		regulator-min-microvolt = <800000>;
+		regulator-max-microvolt = <1500000>;
+
+		ctrl-voltage-range = <200000 500000>;
+
+		min-slew-down-rate = <225>;
+		ovp-threshold-percent = <16>;
+	};
diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig
index be06eb29c681..9a090e338a19 100644
--- a/drivers/regulator/Kconfig
+++ b/drivers/regulator/Kconfig
@@ -850,6 +850,13 @@ config REGULATOR_TWL4030
 	  This driver supports the voltage regulators provided by
 	  this family of companion chips.
 
+config REGULATOR_VCTRL
+	tristate "Voltage controlled regulators"
+	depends on OF
+	help
+	  This driver provides support for voltage regulators whose output
+	  voltage is controlled by the voltage of another regulator.
+
 config REGULATOR_VEXPRESS
 	tristate "Versatile Express regulators"
 	depends on VEXPRESS_CONFIG
diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile
index ef7725e2592a..891dd287f26b 100644
--- a/drivers/regulator/Makefile
+++ b/drivers/regulator/Makefile
@@ -106,6 +106,7 @@ obj-$(CONFIG_REGULATOR_TPS65910) += tps65910-regulator.o
 obj-$(CONFIG_REGULATOR_TPS65912) += tps65912-regulator.o
 obj-$(CONFIG_REGULATOR_TPS80031) += tps80031-regulator.o
 obj-$(CONFIG_REGULATOR_TWL4030) += twl-regulator.o twl6030-regulator.o
+obj-$(CONFIG_REGULATOR_VCTRL) += vctrl-regulator.o
 obj-$(CONFIG_REGULATOR_VEXPRESS) += vexpress-regulator.o
 obj-$(CONFIG_REGULATOR_WM831X) += wm831x-dcdc.o
 obj-$(CONFIG_REGULATOR_WM831X) += wm831x-isink.o
diff --git a/drivers/regulator/vctrl-regulator.c b/drivers/regulator/vctrl-regulator.c
new file mode 100644
index 000000000000..d327ed65bb65
--- /dev/null
+++ b/drivers/regulator/vctrl-regulator.c
@@ -0,0 +1,553 @@
+/*
+ * Driver for voltage controller regulators
+ *
+ * Copyright (C) 2017 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/of_regulator.h>
+#include <linux/sort.h>
+
+struct vctrl_voltage_range {
+	int min_uV;
+	int max_uV;
+};
+
+struct vctrl_voltage_ranges {
+	struct vctrl_voltage_range ctrl;
+	struct vctrl_voltage_range out;
+};
+
+struct vctrl_voltage_table {
+	int ctrl;
+	int out;
+	int ovp_min_sel;
+};
+
+struct vctrl_data {
+	struct regulator_dev *rdev;
+	struct regulator_desc desc;
+	struct regulator *ctrl_reg;
+	bool enabled;
+	unsigned int min_slew_down_rate;
+	unsigned int ovp_threshold;
+	struct vctrl_voltage_ranges vrange;
+	struct vctrl_voltage_table *vtable;
+	unsigned int sel;
+};
+
+static int vctrl_calc_ctrl_voltage(struct vctrl_data *vctrl, int out_uV)
+{
+	struct vctrl_voltage_range *ctrl = &vctrl->vrange.ctrl;
+	struct vctrl_voltage_range *out = &vctrl->vrange.out;
+
+	return ctrl->min_uV +
+		DIV_ROUND_CLOSEST_ULL((s64)(out_uV - out->min_uV) *
+				      (ctrl->max_uV - ctrl->min_uV),
+				      out->max_uV - out->min_uV);
+}
+
+static int vctrl_calc_output_voltage(struct vctrl_data *vctrl, int ctrl_uV)
+{
+	struct vctrl_voltage_range *ctrl = &vctrl->vrange.ctrl;
+	struct vctrl_voltage_range *out = &vctrl->vrange.out;
+
+	if (ctrl_uV < 0) {
+		pr_err("vctrl: failed to get control voltage\n");
+		return ctrl_uV;
+	}
+
+	if (ctrl_uV < ctrl->min_uV)
+		return out->min_uV;
+
+	if (ctrl_uV > ctrl->max_uV)
+		return out->max_uV;
+
+	return out->min_uV +
+		DIV_ROUND_CLOSEST_ULL((s64)(ctrl_uV - ctrl->min_uV) *
+				      (out->max_uV - out->min_uV),
+				      ctrl->max_uV - ctrl->min_uV);
+}
+
+static int vctrl_get_voltage(struct regulator_dev *rdev)
+{
+	struct vctrl_data *vctrl = rdev_get_drvdata(rdev);
+	int ctrl_uV = regulator_get_voltage(vctrl->ctrl_reg);
+
+	return vctrl_calc_output_voltage(vctrl, ctrl_uV);
+}
+
+static int vctrl_set_voltage(struct regulator_dev *rdev,
+			     int req_min_uV, int req_max_uV,
+			     unsigned int *selector)
+{
+	struct vctrl_data *vctrl = rdev_get_drvdata(rdev);
+	struct regulator *ctrl_reg = vctrl->ctrl_reg;
+	int orig_ctrl_uV = regulator_get_voltage(ctrl_reg);
+	int uV = vctrl_calc_output_voltage(vctrl, orig_ctrl_uV);
+	int ret;
+
+	if (req_min_uV >= uV || !vctrl->ovp_threshold)
+		/* voltage rising or no OVP */
+		return regulator_set_voltage(
+			ctrl_reg,
+			vctrl_calc_ctrl_voltage(vctrl, req_min_uV),
+			vctrl_calc_ctrl_voltage(vctrl, req_max_uV));
+
+	while (uV > req_min_uV) {
+		int max_drop_uV = (uV * vctrl->ovp_threshold) / 100;
+		int next_uV;
+		int next_ctrl_uV;
+		int delay;
+
+		/* Make sure no infinite loop even in crazy cases */
+		if (max_drop_uV == 0)
+			max_drop_uV = 1;
+
+		next_uV = max_t(int, req_min_uV, uV - max_drop_uV);
+		next_ctrl_uV = vctrl_calc_ctrl_voltage(vctrl, next_uV);
+
+		ret = regulator_set_voltage(ctrl_reg,
+					    next_ctrl_uV,
+					    next_ctrl_uV);
+		if (ret)
+			goto err;
+
+		delay = DIV_ROUND_UP(uV - next_uV, vctrl->min_slew_down_rate);
+		usleep_range(delay, delay + DIV_ROUND_UP(delay, 10));
+
+		uV = next_uV;
+	}
+
+	return 0;
+
+err:
+	/* Try to go back to original voltage */
+	regulator_set_voltage(ctrl_reg, orig_ctrl_uV, orig_ctrl_uV);
+
+	return ret;
+}
+
+static int vctrl_get_voltage_sel(struct regulator_dev *rdev)
+{
+	struct vctrl_data *vctrl = rdev_get_drvdata(rdev);
+
+	return vctrl->sel;
+}
+
+static int vctrl_set_voltage_sel(struct regulator_dev *rdev,
+				 unsigned int selector)
+{
+	struct vctrl_data *vctrl = rdev_get_drvdata(rdev);
+	struct regulator *ctrl_reg = vctrl->ctrl_reg;
+	unsigned int orig_sel = vctrl->sel;
+	int ret;
+
+	if (selector >= rdev->desc->n_voltages)
+		return -EINVAL;
+
+	if (selector >= vctrl->sel || !vctrl->ovp_threshold) {
+		/* voltage rising or no OVP */
+		ret = regulator_set_voltage(ctrl_reg,
+					    vctrl->vtable[selector].ctrl,
+					    vctrl->vtable[selector].ctrl);
+		if (!ret)
+			vctrl->sel = selector;
+
+		return ret;
+	}
+
+	while (vctrl->sel != selector) {
+		unsigned int next_sel;
+		int delay;
+
+		if (selector >= vctrl->vtable[vctrl->sel].ovp_min_sel)
+			next_sel = selector;
+		else
+			next_sel = vctrl->vtable[vctrl->sel].ovp_min_sel;
+
+		ret = regulator_set_voltage(ctrl_reg,
+					    vctrl->vtable[next_sel].ctrl,
+					    vctrl->vtable[next_sel].ctrl);
+		if (ret) {
+			dev_err(&rdev->dev,
+				"failed to set control voltage to %duV\n",
+				vctrl->vtable[next_sel].ctrl);
+			goto err;
+		}
+		vctrl->sel = next_sel;
+
+		delay = DIV_ROUND_UP(vctrl->vtable[vctrl->sel].out -
+				     vctrl->vtable[next_sel].out,
+				     vctrl->min_slew_down_rate);
+		usleep_range(delay, delay + DIV_ROUND_UP(delay, 10));
+	}
+
+	return 0;
+
+err:
+	if (vctrl->sel != orig_sel) {
+		/* Try to go back to original voltage */
+		if (!regulator_set_voltage(ctrl_reg,
+					   vctrl->vtable[orig_sel].ctrl,
+					   vctrl->vtable[orig_sel].ctrl))
+			vctrl->sel = orig_sel;
+		else
+			dev_warn(&rdev->dev,
+				 "failed to restore original voltage\n");
+	}
+
+	return ret;
+}
+
+static int vctrl_list_voltage(struct regulator_dev *rdev,
+			      unsigned int selector)
+{
+	struct vctrl_data *vctrl = rdev_get_drvdata(rdev);
+
+	if (selector >= rdev->desc->n_voltages)
+		return -EINVAL;
+
+	return vctrl->vtable[selector].out;
+}
+
+static int vctrl_parse_dt(struct platform_device *pdev,
+			  struct vctrl_data *vctrl)
+{
+	int ret;
+	struct device_node *np = pdev->dev.of_node;
+	const char *ctrl_reg;
+	u32 pval;
+	u32 vrange_ctrl[2];
+
+	ret = of_property_read_string(np, "ctrl-regulator", &ctrl_reg);
+	if (ret) {
+		dev_err(&pdev->dev, "failed to read ctrl-regulator: %d\n", ret);
+		return ret;
+	}
+
+	vctrl->ctrl_reg = devm_regulator_get(&pdev->dev, ctrl_reg);
+	if (IS_ERR(vctrl->ctrl_reg))
+		return PTR_ERR(vctrl->ctrl_reg);
+
+	ret = of_property_read_u32(np, "ovp-threshold-percent", &pval);
+	if (!ret) {
+		vctrl->ovp_threshold = pval;
+		if (vctrl->ovp_threshold > 100) {
+			dev_err(&pdev->dev,
+				"ovp-threshold-percent (%u) > 100\n",
+				vctrl->ovp_threshold);
+			return -EINVAL;
+		}
+	}
+
+	ret = of_property_read_u32(np, "min-slew-down-rate", &pval);
+	if (!ret) {
+		vctrl->min_slew_down_rate = pval;
+
+		/* We use the value as int and as divider; sanity check */
+		if (vctrl->min_slew_down_rate == 0) {
+			dev_err(&pdev->dev,
+				"min-slew-down-rate must not be 0\n");
+			return -EINVAL;
+		} else if (vctrl->min_slew_down_rate > INT_MAX) {
+			dev_err(&pdev->dev, "min-slew-down-rate (%u) too big\n",
+				vctrl->min_slew_down_rate);
+			return -EINVAL;
+		}
+	}
+
+	if (vctrl->ovp_threshold && !vctrl->min_slew_down_rate) {
+		dev_err(&pdev->dev,
+			"ovp-threshold-percent requires min-slew-down-rate\n");
+		return -EINVAL;
+	}
+
+	ret = of_property_read_u32(np, "regulator-min-microvolt", &pval);
+	if (ret) {
+		dev_err(&pdev->dev,
+			"failed to read regulator-min-microvolt: %d\n", ret);
+		return ret;
+	}
+	vctrl->vrange.out.min_uV = pval;
+
+	ret = of_property_read_u32(np, "regulator-max-microvolt", &pval);
+	if (ret) {
+		dev_err(&pdev->dev,
+			"failed to read regulator-max-microvolt: %d\n", ret);
+		return ret;
+	}
+	vctrl->vrange.out.max_uV = pval;
+
+	ret = of_property_read_u32_array(np, "ctrl-voltage-range", vrange_ctrl,
+					 2);
+	if (ret) {
+		dev_err(&pdev->dev, "failed to read ctrl-voltage-range: %d\n",
+			ret);
+		return ret;
+	}
+
+	if (vrange_ctrl[0] >= vrange_ctrl[1]) {
+		dev_err(&pdev->dev, "ctrl-voltage-range is invalid: %d-%d\n",
+			vrange_ctrl[0], vrange_ctrl[1]);
+		return -EINVAL;
+	}
+
+	vctrl->vrange.ctrl.min_uV = vrange_ctrl[0];
+	vctrl->vrange.ctrl.max_uV = vrange_ctrl[1];
+
+	return 0;
+}
+
+static int vctrl_cmp_ctrl_uV(const void *a, const void *b)
+{
+	const struct vctrl_voltage_table *at = a;
+	const struct vctrl_voltage_table *bt = b;
+
+	return at->ctrl - bt->ctrl;
+}
+
+static int vctrl_init_vtable(struct platform_device *pdev)
+{
+	struct vctrl_data *vctrl = platform_get_drvdata(pdev);
+	struct regulator_desc *rdesc = &vctrl->desc;
+	struct regulator *ctrl_reg = vctrl->ctrl_reg;
+	struct vctrl_voltage_range *vrange_ctrl = &vctrl->vrange.ctrl;
+	int n_voltages;
+	int ctrl_uV;
+	int i, idx_vt;
+
+	n_voltages = regulator_count_voltages(ctrl_reg);
+
+	rdesc->n_voltages = n_voltages;
+
+	/* determine number of steps within the range of the vctrl regulator */
+	for (i = 0; i < n_voltages; i++) {
+		ctrl_uV = regulator_list_voltage(ctrl_reg, i);
+
+		if (ctrl_uV < vrange_ctrl->min_uV ||
+		    ctrl_uV > vrange_ctrl->max_uV) {
+			rdesc->n_voltages--;
+			continue;
+		}
+	}
+
+	if (rdesc->n_voltages == 0) {
+		dev_err(&pdev->dev, "invalid configuration\n");
+		return -EINVAL;
+	}
+
+	vctrl->vtable = devm_kmalloc_array(
+		&pdev->dev, sizeof(struct vctrl_voltage_table),
+		rdesc->n_voltages, GFP_KERNEL | __GFP_ZERO);
+	if (!vctrl->vtable)
+		return -ENOMEM;
+
+	/* create mapping control <=> output voltage */
+	for (i = 0, idx_vt = 0; i < n_voltages; i++) {
+		ctrl_uV = regulator_list_voltage(ctrl_reg, i);
+
+		if (ctrl_uV < vrange_ctrl->min_uV ||
+		    ctrl_uV > vrange_ctrl->max_uV)
+			continue;
+
+		vctrl->vtable[idx_vt].ctrl = ctrl_uV;
+		vctrl->vtable[idx_vt].out =
+			vctrl_calc_output_voltage(vctrl, ctrl_uV);
+		idx_vt++;
+	}
+
+	/* we rely on the table to be ordered by ascending voltage */
+	sort(vctrl->vtable, rdesc->n_voltages,
+	     sizeof(struct vctrl_voltage_table), vctrl_cmp_ctrl_uV,
+	     NULL);
+
+	/* pre-calculate OVP-safe downward transitions */
+	for (i = n_voltages - 1; i > 0; i--) {
+		int j;
+		int ovp_min_uV = (vctrl->vtable[i].out *
+				  (100 - vctrl->ovp_threshold)) / 100;
+
+		for (j = 0; j < i; j++) {
+			if (vctrl->vtable[j].out >= ovp_min_uV) {
+				vctrl->vtable[i].ovp_min_sel = j;
+				break;
+			}
+		}
+
+		if (j == i) {
+			dev_warn(&pdev->dev, "switching down from %duV may cause OVP shutdown\n",
+				vctrl->vtable[i].out);
+			/* use next lowest voltage */
+			vctrl->vtable[i].ovp_min_sel = i - 1;
+		}
+	}
+
+	return 0;
+}
+
+static int vctrl_enable(struct regulator_dev *rdev)
+{
+	struct vctrl_data *vctrl = rdev_get_drvdata(rdev);
+	int ret = regulator_enable(vctrl->ctrl_reg);
+
+	if (!ret)
+		vctrl->enabled = true;
+
+	return ret;
+}
+
+static int vctrl_disable(struct regulator_dev *rdev)
+{
+	struct vctrl_data *vctrl = rdev_get_drvdata(rdev);
+	int ret = regulator_disable(vctrl->ctrl_reg);
+
+	if (!ret)
+		vctrl->enabled = false;
+
+	return ret;
+}
+
+static int vctrl_is_enabled(struct regulator_dev *rdev)
+{
+	struct vctrl_data *vctrl = rdev_get_drvdata(rdev);
+
+	return vctrl->enabled;
+}
+
+static const struct regulator_ops vctrl_ops_cont = {
+	.enable		  = vctrl_enable,
+	.disable	  = vctrl_disable,
+	.is_enabled	  = vctrl_is_enabled,
+	.get_voltage	  = vctrl_get_voltage,
+	.set_voltage	  = vctrl_set_voltage,
+};
+
+static const struct regulator_ops vctrl_ops_non_cont = {
+	.enable		  = vctrl_enable,
+	.disable	  = vctrl_disable,
+	.is_enabled	  = vctrl_is_enabled,
+	.set_voltage_sel = vctrl_set_voltage_sel,
+	.get_voltage_sel = vctrl_get_voltage_sel,
+	.list_voltage    = vctrl_list_voltage,
+	.map_voltage     = regulator_map_voltage_iterate,
+};
+
+static int vctrl_probe(struct platform_device *pdev)
+{
+	struct device_node *np = pdev->dev.of_node;
+	struct vctrl_data *vctrl;
+	const struct regulator_init_data *init_data;
+	struct regulator_desc *rdesc;
+	struct regulator_config cfg = { };
+	struct vctrl_voltage_range *vrange_ctrl;
+	int ctrl_uV;
+	int ret;
+
+	vctrl = devm_kzalloc(&pdev->dev, sizeof(struct vctrl_data),
+			     GFP_KERNEL);
+	if (!vctrl)
+		return -ENOMEM;
+
+	platform_set_drvdata(pdev, vctrl);
+
+	ret = vctrl_parse_dt(pdev, vctrl);
+	if (ret)
+		return ret;
+
+	vrange_ctrl = &vctrl->vrange.ctrl;
+
+	rdesc = &vctrl->desc;
+	rdesc->name = "vctrl";
+	rdesc->type = REGULATOR_VOLTAGE;
+	rdesc->owner = THIS_MODULE;
+
+	if ((regulator_get_linear_step(vctrl->ctrl_reg) == 1) ||
+	    (regulator_count_voltages(vctrl->ctrl_reg) == -EINVAL)) {
+		rdesc->continuous_voltage_range = true;
+		rdesc->ops = &vctrl_ops_cont;
+	} else {
+		rdesc->ops = &vctrl_ops_non_cont;
+	}
+
+	init_data = of_get_regulator_init_data(&pdev->dev, np, rdesc);
+	if (!init_data)
+		return -ENOMEM;
+
+	cfg.of_node = np;
+	cfg.dev = &pdev->dev;
+	cfg.driver_data = vctrl;
+	cfg.init_data = init_data;
+
+	if (!rdesc->continuous_voltage_range) {
+		ret = vctrl_init_vtable(pdev);
+		if (ret)
+			return ret;
+
+		ctrl_uV = regulator_get_voltage(vctrl->ctrl_reg);
+		if (ctrl_uV < 0) {
+			dev_err(&pdev->dev, "failed to get control voltage\n");
+			return ctrl_uV;
+		}
+
+		/* determine current voltage selector from control voltage */
+		if (ctrl_uV < vrange_ctrl->min_uV) {
+			vctrl->sel = 0;
+		} else if (ctrl_uV > vrange_ctrl->max_uV) {
+			vctrl->sel = rdesc->n_voltages - 1;
+		} else {
+			int i;
+
+			for (i = 0; i < rdesc->n_voltages; i++) {
+				if (ctrl_uV == vctrl->vtable[i].ctrl) {
+					vctrl->sel = i;
+					break;
+				}
+			}
+		}
+	}
+
+	vctrl->rdev = devm_regulator_register(&pdev->dev, rdesc, &cfg);
+	if (IS_ERR(vctrl->rdev)) {
+		ret = PTR_ERR(vctrl->rdev);
+		dev_err(&pdev->dev, "failed to register regulator: %d\n", ret);
+		return ret;
+	}
+
+	return 0;
+}
+
+static const struct of_device_id vctrl_of_match[] = {
+	{ .compatible = "vctrl-regulator", },
+	{},
+};
+MODULE_DEVICE_TABLE(of, vctrl_of_match);
+
+static struct platform_driver vctrl_driver = {
+	.probe		= vctrl_probe,
+	.driver		= {
+		.name		= "vctrl-regulator",
+		.of_match_table = of_match_ptr(vctrl_of_match),
+	},
+};
+
+module_platform_driver(vctrl_driver);
+
+MODULE_DESCRIPTION("Voltage Controlled Regulator Driver");
+MODULE_AUTHOR("Matthias Kaehlcke <mka-F7+t8E8rja9g9hUCZPvPmw@public.gmane.org>");
+MODULE_LICENSE("GPL v2");
-- 
2.12.2.564.g063fe858b8-goog

--
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

* [PATCH V4 2/2] thermal: broadcom: add Northstar thermal driver
From: Rafał Miłecki @ 2017-03-31 20:11 UTC (permalink / raw)
  To: Zhang Rui, Eduardo Valentin
  Cc: Rob Herring, Mark Rutland, Stephen Warren, Lee Jones, Eric Anholt,
	Florian Fainelli, Ray Jui, Scott Branden,
	bcm-kernel-feedback-list-dY08KVG/lbpWk0Htik3J/w,
	linux-pm-u79uwXL29TY76Z2rM5mHXA,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-rpi-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	Rafał Miłecki, Jon Mason
In-Reply-To: <20170331201124.656-1-zajec5-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>

From: Rafał Miłecki <rafal-g1n6cQUeyibVItvQsEIGlw@public.gmane.org>

Northstar is a SoC family commonly used in home routers. This commit
adds a driver for checking CPU temperature. As Northstar Plus seems to
also have this IP block this new symbol gets ARCH_BCM_IPROC dependency.

Signed-off-by: Rafał Miłecki <rafal-g1n6cQUeyibVItvQsEIGlw@public.gmane.org>
Signed-off-by: Jon Mason <jon.mason-dY08KVG/lbpWk0Htik3J/w@public.gmane.org>
---
V2: Make it iProc specific as NSP can also use this driver
    Select proper symbols in config ARCH_BCM_IPROC
    Define PVTMON register bits
    Update code selecting temperature monitor mode
    Thank you Jon!
V3: More details in help message for BCM_NS_THERMAL
    Use slope & offset
    Drop arch code change (I'll be submitted using a proper tree)
    Thank you Eduardo!
V4: Comment operations on PVTMON_CONTROL0 register
---
 drivers/thermal/Kconfig               |   5 ++
 drivers/thermal/Makefile              |   1 +
 drivers/thermal/broadcom/Kconfig      |   8 +++
 drivers/thermal/broadcom/Makefile     |   1 +
 drivers/thermal/broadcom/ns-thermal.c | 105 ++++++++++++++++++++++++++++++++++
 5 files changed, 120 insertions(+)
 create mode 100644 drivers/thermal/broadcom/Kconfig
 create mode 100644 drivers/thermal/broadcom/Makefile
 create mode 100644 drivers/thermal/broadcom/ns-thermal.c

diff --git a/drivers/thermal/Kconfig b/drivers/thermal/Kconfig
index 776b34396144..008e173ec825 100644
--- a/drivers/thermal/Kconfig
+++ b/drivers/thermal/Kconfig
@@ -392,6 +392,11 @@ config MTK_THERMAL
 	  Enable this option if you want to have support for thermal management
 	  controller present in Mediatek SoCs
 
+menu "Broadcom thermal drivers"
+depends on ARCH_BCM || COMPILE_TEST
+source "drivers/thermal/broadcom/Kconfig"
+endmenu
+
 menu "Texas Instruments thermal drivers"
 depends on ARCH_HAS_BANDGAP || COMPILE_TEST
 depends on HAS_IOMEM
diff --git a/drivers/thermal/Makefile b/drivers/thermal/Makefile
index 7adae2029355..549d81b6363c 100644
--- a/drivers/thermal/Makefile
+++ b/drivers/thermal/Makefile
@@ -27,6 +27,7 @@ thermal_sys-$(CONFIG_CLOCK_THERMAL)	+= clock_cooling.o
 thermal_sys-$(CONFIG_DEVFREQ_THERMAL) += devfreq_cooling.o
 
 # platform thermal drivers
+obj-y				+= broadcom/
 obj-$(CONFIG_QCOM_SPMI_TEMP_ALARM)	+= qcom-spmi-temp-alarm.o
 obj-$(CONFIG_SPEAR_THERMAL)	+= spear_thermal.o
 obj-$(CONFIG_ROCKCHIP_THERMAL)	+= rockchip_thermal.o
diff --git a/drivers/thermal/broadcom/Kconfig b/drivers/thermal/broadcom/Kconfig
new file mode 100644
index 000000000000..f0dea8a8e002
--- /dev/null
+++ b/drivers/thermal/broadcom/Kconfig
@@ -0,0 +1,8 @@
+config BCM_NS_THERMAL
+	tristate "Northstar thermal driver"
+	depends on ARCH_BCM_IPROC || COMPILE_TEST
+	help
+	  Northstar is a family of SoCs that includes e.g. BCM4708, BCM47081,
+	  BCM4709 and BCM47094. It contains DMU (Device Management Unit) block
+	  with a thermal sensor that allows checking CPU temperature. This
+	  driver provides support for it.
diff --git a/drivers/thermal/broadcom/Makefile b/drivers/thermal/broadcom/Makefile
new file mode 100644
index 000000000000..059df9a0ed69
--- /dev/null
+++ b/drivers/thermal/broadcom/Makefile
@@ -0,0 +1 @@
+obj-$(CONFIG_BCM_NS_THERMAL)		+= ns-thermal.o
diff --git a/drivers/thermal/broadcom/ns-thermal.c b/drivers/thermal/broadcom/ns-thermal.c
new file mode 100644
index 000000000000..eab96b3572b9
--- /dev/null
+++ b/drivers/thermal/broadcom/ns-thermal.c
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2017 Rafał Miłecki <rafal-g1n6cQUeyibVItvQsEIGlw@public.gmane.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/module.h>
+#include <linux/of_address.h>
+#include <linux/platform_device.h>
+#include <linux/thermal.h>
+
+#define PVTMON_CONTROL0					0x00
+#define PVTMON_CONTROL0_SEL_MASK			0x0000000e
+#define PVTMON_CONTROL0_SEL_TEMP_MONITOR		0x00000000
+#define PVTMON_CONTROL0_SEL_TEST_MODE			0x0000000e
+#define PVTMON_STATUS					0x08
+
+struct ns_thermal {
+	struct thermal_zone_device *tz;
+	void __iomem *pvtmon;
+};
+
+static int ns_thermal_get_temp(void *data, int *temp)
+{
+	struct ns_thermal *ns_thermal = data;
+	int offset = thermal_zone_get_offset(ns_thermal->tz);
+	int slope = thermal_zone_get_slope(ns_thermal->tz);
+	u32 val;
+
+	val = readl(ns_thermal->pvtmon + PVTMON_CONTROL0);
+	if ((val & PVTMON_CONTROL0_SEL_MASK) != PVTMON_CONTROL0_SEL_TEMP_MONITOR) {
+		/* Clear current mode selection */
+		val &= ~PVTMON_CONTROL0_SEL_MASK;
+
+		/* Set temp monitor mode (it's the default actually) */
+		val |= PVTMON_CONTROL0_SEL_TEMP_MONITOR;
+
+		writel(val, ns_thermal->pvtmon + PVTMON_CONTROL0);
+	}
+
+	val = readl(ns_thermal->pvtmon + PVTMON_STATUS);
+	*temp = slope * val + offset;
+
+	return 0;
+}
+
+const struct thermal_zone_of_device_ops ns_thermal_ops = {
+	.get_temp = ns_thermal_get_temp,
+};
+
+static int ns_thermal_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct ns_thermal *ns_thermal;
+
+	ns_thermal = devm_kzalloc(dev, sizeof(*ns_thermal), GFP_KERNEL);
+	if (!ns_thermal)
+		return -ENOMEM;
+
+	ns_thermal->pvtmon = of_iomap(dev_of_node(dev), 0);
+	if (WARN_ON(!ns_thermal->pvtmon))
+		return -ENOENT;
+
+	ns_thermal->tz = devm_thermal_zone_of_sensor_register(dev, 0,
+							      ns_thermal,
+							      &ns_thermal_ops);
+	if (IS_ERR(ns_thermal->tz)) {
+		iounmap(ns_thermal->pvtmon);
+		return PTR_ERR(ns_thermal->tz);
+	}
+
+	platform_set_drvdata(pdev, ns_thermal);
+
+	return 0;
+}
+
+static int ns_thermal_remove(struct platform_device *pdev)
+{
+	struct ns_thermal *ns_thermal = platform_get_drvdata(pdev);
+
+	iounmap(ns_thermal->pvtmon);
+
+	return 0;
+}
+
+static const struct of_device_id ns_thermal_of_match[] = {
+	{ .compatible = "brcm,ns-thermal", },
+	{},
+};
+MODULE_DEVICE_TABLE(of, ns_thermal_of_match);
+
+static struct platform_driver ns_thermal_driver = {
+	.probe		= ns_thermal_probe,
+	.remove		= ns_thermal_remove,
+	.driver = {
+		.name = "ns-thermal",
+		.of_match_table = ns_thermal_of_match,
+	},
+};
+module_platform_driver(ns_thermal_driver);
+
+MODULE_DESCRIPTION("Northstar thermal driver");
+MODULE_LICENSE("GPL v2");
-- 
2.11.0

--
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

* [PATCH V4 1/2] dt-bindings: thermal: add support for Broadcom's Northstar thermal
From: Rafał Miłecki @ 2017-03-31 20:11 UTC (permalink / raw)
  To: Zhang Rui, Eduardo Valentin
  Cc: Rob Herring, Mark Rutland, Stephen Warren, Lee Jones, Eric Anholt,
	Florian Fainelli, Ray Jui, Scott Branden,
	bcm-kernel-feedback-list, linux-pm, devicetree, linux-rpi-kernel,
	linux-arm-kernel, Rafał Miłecki
In-Reply-To: <20170331073132.21457-2-zajec5@gmail.com>

From: Rafał Miłecki <rafal@milecki.pl>

This commit documents binding for thermal used in Northstar family SoCs.

Signed-off-by: Rafał Miłecki <rafal@milecki.pl>
---
V3: Add thermal-zones to the example
    Rob: Because of this update, I didn't include Acked-by I got for V2
---
 .../devicetree/bindings/thermal/brcm,ns-thermal    | 26 ++++++++++++++++++++++
 1 file changed, 26 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/thermal/brcm,ns-thermal

diff --git a/Documentation/devicetree/bindings/thermal/brcm,ns-thermal b/Documentation/devicetree/bindings/thermal/brcm,ns-thermal
new file mode 100644
index 000000000000..c561c7349f17
--- /dev/null
+++ b/Documentation/devicetree/bindings/thermal/brcm,ns-thermal
@@ -0,0 +1,26 @@
+* Broadcom Northstar Thermal
+
+This binding describes thermal sensor that is part of Northstar's DMU (Device
+Management Unit).
+
+Required properties:
+- compatible : Must be "brcm,ns-thermal"
+- reg : iomem address range of PVTMON registers
+- #thermal-sensor-cells : Should be <0>
+
+Example:
+
+thermal: thermal@1800c2c0 {
+	compatible = "brcm,ns-thermal";
+	reg = <0x1800c2c0 0x10>;
+	#thermal-sensor-cells = <0>;
+};
+
+thermal-zones {
+	cpu_thermal: cpu-thermal {
+		polling-delay-passive = <0>;
+		polling-delay = <1000>;
+		coefficients = <(-556) 418000>;
+		thermal-sensors = <&thermal>;
+	};
+};
-- 
2.11.0

^ permalink raw reply related

* [PATCH V12 4/4] thermal: bcm2835: add thermal driver for bcm2835 SoC
From: Stefan Wahren @ 2017-03-31 20:03 UTC (permalink / raw)
  To: Eduardo Valentin, Zhang Rui, kernel
  Cc: Eric Anholt, Rob Herring, Frank Rowand, Florian Fainelli,
	Rafał Miłecki, linux-rpi-kernel, devicetree, linux-pm,
	Stefan Wahren
In-Reply-To: <1490990586-30898-1-git-send-email-stefan.wahren@i2se.com>

Add basic thermal driver for bcm2835 SoC.

This driver currently make sure that tsense HW block is set up
correctly.

Signed-off-by: Martin Sperl <kernel@martin.sperl.org>
Signed-off-by: Stefan Wahren <stefan.wahren@i2se.com>
Acked-by: Eric Anholt <eric@anholt.net>
Acked-by: Eduardo Valentin <edubezval@gmail.com>
---
 drivers/thermal/Kconfig           |    8 +
 drivers/thermal/Makefile          |    1 +
 drivers/thermal/bcm2835_thermal.c |  314 +++++++++++++++++++++++++++++++++++++
 3 files changed, 323 insertions(+)
 create mode 100644 drivers/thermal/bcm2835_thermal.c

diff --git a/drivers/thermal/Kconfig b/drivers/thermal/Kconfig
index 776b343..3bd2406 100644
--- a/drivers/thermal/Kconfig
+++ b/drivers/thermal/Kconfig
@@ -453,4 +453,12 @@ config ZX2967_THERMAL
 	  the primitive temperature sensor embedded in zx2967 SoCs.
 	  This sensor generates the real time die temperature.
 
+config BCM2835_THERMAL
+	tristate "Thermal sensors on bcm2835 SoC"
+	depends on ARCH_BCM2835 || COMPILE_TEST
+	depends on HAS_IOMEM
+	depends on THERMAL_OF
+	help
+	  Support for thermal sensors on Broadcom bcm2835 SoCs.
+
 endif
diff --git a/drivers/thermal/Makefile b/drivers/thermal/Makefile
index 7adae20..f23cde0 100644
--- a/drivers/thermal/Makefile
+++ b/drivers/thermal/Makefile
@@ -58,3 +58,4 @@ obj-$(CONFIG_HISI_THERMAL)     += hisi_thermal.o
 obj-$(CONFIG_MTK_THERMAL)	+= mtk_thermal.o
 obj-$(CONFIG_GENERIC_ADC_THERMAL)	+= thermal-generic-adc.o
 obj-$(CONFIG_ZX2967_THERMAL)	+= zx2967_thermal.o
+obj-$(CONFIG_BCM2835_THERMAL)	+= bcm2835_thermal.o
diff --git a/drivers/thermal/bcm2835_thermal.c b/drivers/thermal/bcm2835_thermal.c
new file mode 100644
index 0000000..0ecf808
--- /dev/null
+++ b/drivers/thermal/bcm2835_thermal.c
@@ -0,0 +1,314 @@
+/*
+ * Driver for Broadcom BCM2835 SoC temperature sensor
+ *
+ * Copyright (C) 2016 Martin Sperl
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/clk.h>
+#include <linux/debugfs.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/thermal.h>
+
+#define BCM2835_TS_TSENSCTL			0x00
+#define BCM2835_TS_TSENSSTAT			0x04
+
+#define BCM2835_TS_TSENSCTL_PRWDW		BIT(0)
+#define BCM2835_TS_TSENSCTL_RSTB		BIT(1)
+
+/*
+ * bandgap reference voltage in 6 mV increments
+ * 000b = 1178 mV, 001b = 1184 mV, ... 111b = 1220 mV
+ */
+#define BCM2835_TS_TSENSCTL_CTRL_BITS		3
+#define BCM2835_TS_TSENSCTL_CTRL_SHIFT		2
+#define BCM2835_TS_TSENSCTL_CTRL_MASK		    \
+	GENMASK(BCM2835_TS_TSENSCTL_CTRL_BITS +     \
+		BCM2835_TS_TSENSCTL_CTRL_SHIFT - 1, \
+		BCM2835_TS_TSENSCTL_CTRL_SHIFT)
+#define BCM2835_TS_TSENSCTL_CTRL_DEFAULT	1
+#define BCM2835_TS_TSENSCTL_EN_INT		BIT(5)
+#define BCM2835_TS_TSENSCTL_DIRECT		BIT(6)
+#define BCM2835_TS_TSENSCTL_CLR_INT		BIT(7)
+#define BCM2835_TS_TSENSCTL_THOLD_SHIFT		8
+#define BCM2835_TS_TSENSCTL_THOLD_BITS		10
+#define BCM2835_TS_TSENSCTL_THOLD_MASK		     \
+	GENMASK(BCM2835_TS_TSENSCTL_THOLD_BITS +     \
+		BCM2835_TS_TSENSCTL_THOLD_SHIFT - 1, \
+		BCM2835_TS_TSENSCTL_THOLD_SHIFT)
+/*
+ * time how long the block to be asserted in reset
+ * which based on a clock counter (TSENS clock assumed)
+ */
+#define BCM2835_TS_TSENSCTL_RSTDELAY_SHIFT	18
+#define BCM2835_TS_TSENSCTL_RSTDELAY_BITS	8
+#define BCM2835_TS_TSENSCTL_REGULEN		BIT(26)
+
+#define BCM2835_TS_TSENSSTAT_DATA_BITS		10
+#define BCM2835_TS_TSENSSTAT_DATA_SHIFT		0
+#define BCM2835_TS_TSENSSTAT_DATA_MASK		     \
+	GENMASK(BCM2835_TS_TSENSSTAT_DATA_BITS +     \
+		BCM2835_TS_TSENSSTAT_DATA_SHIFT - 1, \
+		BCM2835_TS_TSENSSTAT_DATA_SHIFT)
+#define BCM2835_TS_TSENSSTAT_VALID		BIT(10)
+#define BCM2835_TS_TSENSSTAT_INTERRUPT		BIT(11)
+
+struct bcm2835_thermal_data {
+	struct thermal_zone_device *tz;
+	void __iomem *regs;
+	struct clk *clk;
+	struct dentry *debugfsdir;
+};
+
+static int bcm2835_thermal_adc2temp(u32 adc, int offset, int slope)
+{
+	return offset + slope * adc;
+}
+
+static int bcm2835_thermal_temp2adc(int temp, int offset, int slope)
+{
+	temp -= offset;
+	temp /= slope;
+
+	if (temp < 0)
+		temp = 0;
+	if (temp >= BIT(BCM2835_TS_TSENSSTAT_DATA_BITS))
+		temp = BIT(BCM2835_TS_TSENSSTAT_DATA_BITS) - 1;
+
+	return temp;
+}
+
+static int bcm2835_thermal_get_temp(void *d, int *temp)
+{
+	struct bcm2835_thermal_data *data = d;
+	u32 val = readl(data->regs + BCM2835_TS_TSENSSTAT);
+
+	if (!(val & BCM2835_TS_TSENSSTAT_VALID))
+		return -EIO;
+
+	val &= BCM2835_TS_TSENSSTAT_DATA_MASK;
+
+	*temp = bcm2835_thermal_adc2temp(
+		val,
+		thermal_zone_get_offset(data->tz),
+		thermal_zone_get_slope(data->tz));
+
+	return 0;
+}
+
+static const struct debugfs_reg32 bcm2835_thermal_regs[] = {
+	{
+		.name = "ctl",
+		.offset = 0
+	},
+	{
+		.name = "stat",
+		.offset = 4
+	}
+};
+
+static void bcm2835_thermal_debugfs(struct platform_device *pdev)
+{
+	struct thermal_zone_device *tz = platform_get_drvdata(pdev);
+	struct bcm2835_thermal_data *data = tz->devdata;
+	struct debugfs_regset32 *regset;
+
+	data->debugfsdir = debugfs_create_dir("bcm2835_thermal", NULL);
+	if (!data->debugfsdir)
+		return;
+
+	regset = devm_kzalloc(&pdev->dev, sizeof(*regset), GFP_KERNEL);
+	if (!regset)
+		return;
+
+	regset->regs = bcm2835_thermal_regs;
+	regset->nregs = ARRAY_SIZE(bcm2835_thermal_regs);
+	regset->base = data->regs;
+
+	debugfs_create_regset32("regset", 0444, data->debugfsdir, regset);
+}
+
+static struct thermal_zone_of_device_ops bcm2835_thermal_ops = {
+	.get_temp = bcm2835_thermal_get_temp,
+};
+
+/*
+ * Note: as per Raspberry Foundation FAQ
+ * (https://www.raspberrypi.org/help/faqs/#performanceOperatingTemperature)
+ * the recommended temperature range for the SoC -40C to +85C
+ * so the trip limit is set to 80C.
+ * this applies to all the BCM283X SoC
+ */
+
+static const struct of_device_id bcm2835_thermal_of_match_table[] = {
+	{
+		.compatible = "brcm,bcm2835-thermal",
+	},
+	{
+		.compatible = "brcm,bcm2836-thermal",
+	},
+	{
+		.compatible = "brcm,bcm2837-thermal",
+	},
+	{},
+};
+MODULE_DEVICE_TABLE(of, bcm2835_thermal_of_match_table);
+
+static int bcm2835_thermal_probe(struct platform_device *pdev)
+{
+	const struct of_device_id *match;
+	struct thermal_zone_device *tz;
+	struct bcm2835_thermal_data *data;
+	struct resource *res;
+	int err = 0;
+	u32 val;
+	unsigned long rate;
+
+	data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
+	if (!data)
+		return -ENOMEM;
+
+	match = of_match_device(bcm2835_thermal_of_match_table,
+				&pdev->dev);
+	if (!match)
+		return -EINVAL;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	data->regs = devm_ioremap_resource(&pdev->dev, res);
+	if (IS_ERR(data->regs)) {
+		err = PTR_ERR(data->regs);
+		dev_err(&pdev->dev, "Could not get registers: %d\n", err);
+		return err;
+	}
+
+	data->clk = devm_clk_get(&pdev->dev, NULL);
+	if (IS_ERR(data->clk)) {
+		err = PTR_ERR(data->clk);
+		if (err != -EPROBE_DEFER)
+			dev_err(&pdev->dev, "Could not get clk: %d\n", err);
+		return err;
+	}
+
+	err = clk_prepare_enable(data->clk);
+	if (err)
+		return err;
+
+	rate = clk_get_rate(data->clk);
+	if ((rate < 1920000) || (rate > 5000000))
+		dev_warn(&pdev->dev,
+			 "Clock %pCn running at %pCr Hz is outside of the recommended range: 1.92 to 5MHz\n",
+			 data->clk, data->clk);
+
+	/* register of thermal sensor and get info from DT */
+	tz = thermal_zone_of_sensor_register(&pdev->dev, 0, data,
+					     &bcm2835_thermal_ops);
+	if (IS_ERR(tz)) {
+		err = PTR_ERR(tz);
+		dev_err(&pdev->dev,
+			"Failed to register the thermal device: %d\n",
+			err);
+		goto err_clk;
+	}
+
+	/*
+	 * right now the FW does set up the HW-block, so we are not
+	 * touching the configuration registers.
+	 * But if the HW is not enabled, then set it up
+	 * using "sane" values used by the firmware right now.
+	 */
+	val = readl(data->regs + BCM2835_TS_TSENSCTL);
+	if (!(val & BCM2835_TS_TSENSCTL_RSTB)) {
+		int trip_temp, offset, slope;
+
+		slope = thermal_zone_get_slope(tz);
+		offset = thermal_zone_get_offset(tz);
+		/*
+		 * For now we deal only with critical, otherwise
+		 * would need to iterate
+		 */
+		err = tz->ops->get_trip_temp(tz, 0, &trip_temp);
+		if (err < 0) {
+			err = PTR_ERR(tz);
+			dev_err(&pdev->dev,
+				"Not able to read trip_temp: %d\n",
+				err);
+			goto err_tz;
+		}
+
+		/* set bandgap reference voltage and enable voltage regulator */
+		val = (BCM2835_TS_TSENSCTL_CTRL_DEFAULT <<
+		       BCM2835_TS_TSENSCTL_CTRL_SHIFT) |
+		      BCM2835_TS_TSENSCTL_REGULEN;
+
+		/* use the recommended reset duration */
+		val |= (0xFE << BCM2835_TS_TSENSCTL_RSTDELAY_SHIFT);
+
+		/*  trip_adc value from info */
+		val |= bcm2835_thermal_temp2adc(trip_temp,
+						offset,
+						slope)
+			<< BCM2835_TS_TSENSCTL_THOLD_SHIFT;
+
+		/* write the value back to the register as 2 steps */
+		writel(val, data->regs + BCM2835_TS_TSENSCTL);
+		val |= BCM2835_TS_TSENSCTL_RSTB;
+		writel(val, data->regs + BCM2835_TS_TSENSCTL);
+	}
+
+	data->tz = tz;
+
+	platform_set_drvdata(pdev, tz);
+
+	bcm2835_thermal_debugfs(pdev);
+
+	return 0;
+err_tz:
+	thermal_zone_of_sensor_unregister(&pdev->dev, tz);
+err_clk:
+	clk_disable_unprepare(data->clk);
+
+	return err;
+}
+
+static int bcm2835_thermal_remove(struct platform_device *pdev)
+{
+	struct thermal_zone_device *tz = platform_get_drvdata(pdev);
+	struct bcm2835_thermal_data *data = tz->devdata;
+
+	debugfs_remove_recursive(data->debugfsdir);
+	thermal_zone_of_sensor_unregister(&pdev->dev, tz);
+	clk_disable_unprepare(data->clk);
+
+	return 0;
+}
+
+static struct platform_driver bcm2835_thermal_driver = {
+	.probe = bcm2835_thermal_probe,
+	.remove = bcm2835_thermal_remove,
+	.driver = {
+		.name = "bcm2835_thermal",
+		.of_match_table = bcm2835_thermal_of_match_table,
+	},
+};
+module_platform_driver(bcm2835_thermal_driver);
+
+MODULE_AUTHOR("Martin Sperl");
+MODULE_DESCRIPTION("Thermal driver for bcm2835 chip");
+MODULE_LICENSE("GPL");
-- 
1.7.9.5

^ permalink raw reply related


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox