summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Documentation/devicetree/bindings/arm/altera.txt14
-rw-r--r--Documentation/devicetree/bindings/arm/arch_timer.txt8
-rw-r--r--Documentation/devicetree/bindings/common-properties.txt60
-rw-r--r--Documentation/devicetree/bindings/vendor-prefixes.txt1
-rw-r--r--arch/frv/include/asm/io.h5
-rw-r--r--arch/mn10300/include/asm/io.h5
-rw-r--r--drivers/of/Kconfig2
-rw-r--r--drivers/of/base.c28
-rw-r--r--drivers/of/fdt.c24
-rw-r--r--drivers/of/unittest.c62
-rw-r--r--drivers/tty/serial/8250/8250_core.c20
-rw-r--r--drivers/tty/serial/8250/8250_early.c5
-rw-r--r--drivers/tty/serial/of_serial.c3
-rw-r--r--include/linux/of.h6
-rw-r--r--include/linux/of_fdt.h2
-rw-r--r--include/linux/of_irq.h9
16 files changed, 243 insertions, 11 deletions
diff --git a/Documentation/devicetree/bindings/arm/altera.txt b/Documentation/devicetree/bindings/arm/altera.txt
new file mode 100644
index 000000000000..558735aacca8
--- /dev/null
+++ b/Documentation/devicetree/bindings/arm/altera.txt
@@ -0,0 +1,14 @@
+Altera's SoCFPGA platform device tree bindings
+---------------------------------------------
+
+Boards with Cyclone 5 SoC:
+Required root node properties:
+compatible = "altr,socfpga-cyclone5", "altr,socfpga";
+
+Boards with Arria 5 SoC:
+Required root node properties:
+compatible = "altr,socfpga-arria5", "altr,socfpga";
+
+Boards with Arria 10 SoC:
+Required root node properties:
+compatible = "altr,socfpga-arria10", "altr,socfpga";
diff --git a/Documentation/devicetree/bindings/arm/arch_timer.txt b/Documentation/devicetree/bindings/arm/arch_timer.txt
index 256b4d8bab7b..e774128935d5 100644
--- a/Documentation/devicetree/bindings/arm/arch_timer.txt
+++ b/Documentation/devicetree/bindings/arm/arch_timer.txt
@@ -17,7 +17,10 @@ to deliver its interrupts via SPIs.
- interrupts : Interrupt list for secure, non-secure, virtual and
hypervisor timers, in that order.
-- clock-frequency : The frequency of the main counter, in Hz. Optional.
+- clock-frequency : The frequency of the main counter, in Hz. Should be present
+ only where necessary to work around broken firmware which does not configure
+ CNTFRQ on all CPUs to a uniform correct value. Use of this property is
+ strongly discouraged; fix your firmware unless absolutely impossible.
- always-on : a boolean property. If present, the timer is powered through an
always-on power domain, therefore it never loses context.
@@ -46,7 +49,8 @@ Example:
- compatible : Should at least contain "arm,armv7-timer-mem".
-- clock-frequency : The frequency of the main counter, in Hz. Optional.
+- clock-frequency : The frequency of the main counter, in Hz. Should be present
+ only when firmware has not configured the MMIO CNTFRQ registers.
- reg : The control frame base address.
diff --git a/Documentation/devicetree/bindings/common-properties.txt b/Documentation/devicetree/bindings/common-properties.txt
new file mode 100644
index 000000000000..3193979b1d05
--- /dev/null
+++ b/Documentation/devicetree/bindings/common-properties.txt
@@ -0,0 +1,60 @@
+Common properties
+
+The ePAPR specification does not define any properties related to hardware
+byteswapping, but endianness issues show up frequently in porting Linux to
+different machine types. This document attempts to provide a consistent
+way of handling byteswapping across drivers.
+
+Optional properties:
+ - big-endian: Boolean; force big endian register accesses
+ unconditionally (e.g. ioread32be/iowrite32be). Use this if you
+ know the peripheral always needs to be accessed in BE mode.
+ - little-endian: Boolean; force little endian register accesses
+ unconditionally (e.g. readl/writel). Use this if you know the
+ peripheral always needs to be accessed in LE mode.
+ - native-endian: Boolean; always use register accesses matched to the
+ endianness of the kernel binary (e.g. LE vmlinux -> readl/writel,
+ BE vmlinux -> ioread32be/iowrite32be). In this case no byteswaps
+ will ever be performed. Use this if the hardware "self-adjusts"
+ register endianness based on the CPU's configured endianness.
+
+If a binding supports these properties, then the binding should also
+specify the default behavior if none of these properties are present.
+In such cases, little-endian is the preferred default, but it is not
+a requirement. The of_device_is_big_endian() and of_fdt_is_big_endian()
+helper functions do assume that little-endian is the default, because
+most existing (PCI-based) drivers implicitly default to LE by using
+readl/writel for MMIO accesses.
+
+Examples:
+Scenario 1 : CPU in LE mode & device in LE mode.
+dev: dev@40031000 {
+ compatible = "name";
+ reg = <0x40031000 0x1000>;
+ ...
+ native-endian;
+};
+
+Scenario 2 : CPU in LE mode & device in BE mode.
+dev: dev@40031000 {
+ compatible = "name";
+ reg = <0x40031000 0x1000>;
+ ...
+ big-endian;
+};
+
+Scenario 3 : CPU in BE mode & device in BE mode.
+dev: dev@40031000 {
+ compatible = "name";
+ reg = <0x40031000 0x1000>;
+ ...
+ native-endian;
+};
+
+Scenario 4 : CPU in BE mode & device in LE mode.
+dev: dev@40031000 {
+ compatible = "name";
+ reg = <0x40031000 0x1000>;
+ ...
+ little-endian;
+};
diff --git a/Documentation/devicetree/bindings/vendor-prefixes.txt b/Documentation/devicetree/bindings/vendor-prefixes.txt
index 83737a3403d7..80339192c93e 100644
--- a/Documentation/devicetree/bindings/vendor-prefixes.txt
+++ b/Documentation/devicetree/bindings/vendor-prefixes.txt
@@ -26,6 +26,7 @@ aptina Aptina Imaging
arasan Arasan Chip Systems
arm ARM Ltd.
armadeus ARMadeus Systems SARL
+artesyn Artesyn Embedded Technologies Inc.
asahi-kasei Asahi Kasei Corp.
atmel Atmel Corporation
auo AU Optronics Corporation
diff --git a/arch/frv/include/asm/io.h b/arch/frv/include/asm/io.h
index 99bb7efaf9b7..0b78bc89e840 100644
--- a/arch/frv/include/asm/io.h
+++ b/arch/frv/include/asm/io.h
@@ -342,6 +342,11 @@ static inline void iowrite32(u32 val, void __iomem *p)
__flush_PCI_writes();
}
+#define ioread16be(addr) be16_to_cpu(ioread16(addr))
+#define ioread32be(addr) be32_to_cpu(ioread32(addr))
+#define iowrite16be(v, addr) iowrite16(cpu_to_be16(v), (addr))
+#define iowrite32be(v, addr) iowrite32(cpu_to_be32(v), (addr))
+
static inline void ioread8_rep(void __iomem *p, void *dst, unsigned long count)
{
io_insb((unsigned long) p, dst, count);
diff --git a/arch/mn10300/include/asm/io.h b/arch/mn10300/include/asm/io.h
index 897ba3c12b32..cc4a2ba9e228 100644
--- a/arch/mn10300/include/asm/io.h
+++ b/arch/mn10300/include/asm/io.h
@@ -197,6 +197,11 @@ static inline void outsl(unsigned long addr, const void *buffer, int count)
#define iowrite16(v, addr) writew((v), (addr))
#define iowrite32(v, addr) writel((v), (addr))
+#define ioread16be(addr) be16_to_cpu(readw(addr))
+#define ioread32be(addr) be32_to_cpu(readl(addr))
+#define iowrite16be(v, addr) writew(cpu_to_be16(v), (addr))
+#define iowrite32be(v, addr) writel(cpu_to_be32(v), (addr))
+
#define ioread8_rep(p, dst, count) \
insb((unsigned long) (p), (dst), (count))
#define ioread16_rep(p, dst, count) \
diff --git a/drivers/of/Kconfig b/drivers/of/Kconfig
index 1470b5227834..07bb3c8f191b 100644
--- a/drivers/of/Kconfig
+++ b/drivers/of/Kconfig
@@ -50,7 +50,7 @@ config OF_ADDRESS_PCI
config OF_IRQ
def_bool y
- depends on !SPARC
+ depends on !SPARC && IRQ_DOMAIN
config OF_NET
depends on NETDEVICES
diff --git a/drivers/of/base.c b/drivers/of/base.c
index a1aa0c7dee50..99764db0875a 100644
--- a/drivers/of/base.c
+++ b/drivers/of/base.c
@@ -568,6 +568,29 @@ bool of_device_is_available(const struct device_node *device)
EXPORT_SYMBOL(of_device_is_available);
/**
+ * of_device_is_big_endian - check if a device has BE registers
+ *
+ * @device: Node to check for endianness
+ *
+ * Returns true if the device has a "big-endian" property, or if the kernel
+ * was compiled for BE *and* the device has a "native-endian" property.
+ * Returns false otherwise.
+ *
+ * Callers would nominally use ioread32be/iowrite32be if
+ * of_device_is_big_endian() == true, or readl/writel otherwise.
+ */
+bool of_device_is_big_endian(const struct device_node *device)
+{
+ if (of_property_read_bool(device, "big-endian"))
+ return true;
+ if (IS_ENABLED(CONFIG_CPU_BIG_ENDIAN) &&
+ of_property_read_bool(device, "native-endian"))
+ return true;
+ return false;
+}
+EXPORT_SYMBOL(of_device_is_big_endian);
+
+/**
* of_get_parent - Get a node's parent if any
* @node: Node to get parent
*
@@ -640,8 +663,9 @@ static struct device_node *__of_get_next_child(const struct device_node *node,
* @node: parent node
* @prev: previous child of the parent node, or NULL to get first
*
- * Returns a node pointer with refcount incremented, use
- * of_node_put() on it when done.
+ * Returns a node pointer with refcount incremented, use of_node_put() on
+ * it when done. Returns NULL when prev is the last child. Decrements the
+ * refcount of prev.
*/
struct device_node *of_get_next_child(const struct device_node *node,
struct device_node *prev)
diff --git a/drivers/of/fdt.c b/drivers/of/fdt.c
index 3a896c9aeb74..cde35c5d0191 100644
--- a/drivers/of/fdt.c
+++ b/drivers/of/fdt.c
@@ -109,6 +109,25 @@ int of_fdt_is_compatible(const void *blob,
}
/**
+ * of_fdt_is_big_endian - Return true if given node needs BE MMIO accesses
+ * @blob: A device tree blob
+ * @node: node to test
+ *
+ * Returns true if the node has a "big-endian" property, or if the kernel
+ * was compiled for BE *and* the node has a "native-endian" property.
+ * Returns false otherwise.
+ */
+bool of_fdt_is_big_endian(const void *blob, unsigned long node)
+{
+ if (fdt_getprop(blob, node, "big-endian", NULL))
+ return true;
+ if (IS_ENABLED(CONFIG_CPU_BIG_ENDIAN) &&
+ fdt_getprop(blob, node, "native-endian", NULL))
+ return true;
+ return false;
+}
+
+/**
* of_fdt_match - Return true if node matches a list of compatible values
*/
int of_fdt_match(const void *blob, unsigned long node,
@@ -172,7 +191,7 @@ static void * unflatten_dt_node(void *blob,
if (!pathp)
return mem;
- allocl = l++;
+ allocl = ++l;
/* version 0x10 has a more compact unit name here instead of the full
* path. we accumulate the full path size using "fpsize", we'll rebuild
@@ -879,8 +898,7 @@ int __init early_init_dt_scan_memory(unsigned long node, const char *uname,
endp = reg + (l / sizeof(__be32));
- pr_debug("memory scan node %s, reg size %d, data: %x %x %x %x,\n",
- uname, l, reg[0], reg[1], reg[2], reg[3]);
+ pr_debug("memory scan node %s, reg size %d,\n", uname, l);
while ((endp - reg) >= (dt_root_addr_cells + dt_root_size_cells)) {
u64 base, size;
diff --git a/drivers/of/unittest.c b/drivers/of/unittest.c
index e844907c9efa..18016341d5a9 100644
--- a/drivers/of/unittest.c
+++ b/drivers/of/unittest.c
@@ -23,6 +23,8 @@
#include <linux/i2c.h>
#include <linux/i2c-mux.h>
+#include <linux/bitops.h>
+
#include "of_private.h"
static struct unittest_results {
@@ -1109,6 +1111,59 @@ static const char *overlay_path(int nr)
static const char *bus_path = "/testcase-data/overlay-node/test-bus";
+/* it is guaranteed that overlay ids are assigned in sequence */
+#define MAX_UNITTEST_OVERLAYS 256
+static unsigned long overlay_id_bits[BITS_TO_LONGS(MAX_UNITTEST_OVERLAYS)];
+static int overlay_first_id = -1;
+
+static void of_unittest_track_overlay(int id)
+{
+ if (overlay_first_id < 0)
+ overlay_first_id = id;
+ id -= overlay_first_id;
+
+ /* we shouldn't need that many */
+ BUG_ON(id >= MAX_UNITTEST_OVERLAYS);
+ overlay_id_bits[BIT_WORD(id)] |= BIT_MASK(id);
+}
+
+static void of_unittest_untrack_overlay(int id)
+{
+ if (overlay_first_id < 0)
+ return;
+ id -= overlay_first_id;
+ BUG_ON(id >= MAX_UNITTEST_OVERLAYS);
+ overlay_id_bits[BIT_WORD(id)] &= ~BIT_MASK(id);
+}
+
+static void of_unittest_destroy_tracked_overlays(void)
+{
+ int id, ret, defers;
+
+ if (overlay_first_id < 0)
+ return;
+
+ /* try until no defers */
+ do {
+ defers = 0;
+ /* remove in reverse order */
+ for (id = MAX_UNITTEST_OVERLAYS - 1; id >= 0; id--) {
+ if (!(overlay_id_bits[BIT_WORD(id)] & BIT_MASK(id)))
+ continue;
+
+ ret = of_overlay_destroy(id + overlay_first_id);
+ if (ret != 0) {
+ defers++;
+ pr_warn("%s: overlay destroy failed for #%d\n",
+ __func__, id + overlay_first_id);
+ continue;
+ }
+
+ overlay_id_bits[BIT_WORD(id)] &= ~BIT_MASK(id);
+ }
+ } while (defers > 0);
+}
+
static int of_unittest_apply_overlay(int unittest_nr, int overlay_nr,
int *overlay_id)
{
@@ -1130,6 +1185,7 @@ static int of_unittest_apply_overlay(int unittest_nr, int overlay_nr,
goto out;
}
id = ret;
+ of_unittest_track_overlay(id);
ret = 0;
@@ -1343,6 +1399,7 @@ static void of_unittest_overlay_6(void)
return;
}
ov_id[i] = ret;
+ of_unittest_track_overlay(ov_id[i]);
}
for (i = 0; i < 2; i++) {
@@ -1367,6 +1424,7 @@ static void of_unittest_overlay_6(void)
PDEV_OVERLAY));
return;
}
+ of_unittest_untrack_overlay(ov_id[i]);
}
for (i = 0; i < 2; i++) {
@@ -1411,6 +1469,7 @@ static void of_unittest_overlay_8(void)
return;
}
ov_id[i] = ret;
+ of_unittest_track_overlay(ov_id[i]);
}
/* now try to remove first overlay (it should fail) */
@@ -1433,6 +1492,7 @@ static void of_unittest_overlay_8(void)
PDEV_OVERLAY));
return;
}
+ of_unittest_untrack_overlay(ov_id[i]);
}
unittest(1, "overlay test %d passed\n", 8);
@@ -1855,6 +1915,8 @@ static void __init of_unittest_overlay(void)
of_unittest_overlay_i2c_cleanup();
#endif
+ of_unittest_destroy_tracked_overlays();
+
out:
of_node_put(bus_np);
}
diff --git a/drivers/tty/serial/8250/8250_core.c b/drivers/tty/serial/8250/8250_core.c
index 422ebea96a64..4506e405c8f3 100644
--- a/drivers/tty/serial/8250/8250_core.c
+++ b/drivers/tty/serial/8250/8250_core.c
@@ -450,6 +450,18 @@ static unsigned int mem32_serial_in(struct uart_port *p, int offset)
return readl(p->membase + offset);
}
+static void mem32be_serial_out(struct uart_port *p, int offset, int value)
+{
+ offset = offset << p->regshift;
+ iowrite32be(value, p->membase + offset);
+}
+
+static unsigned int mem32be_serial_in(struct uart_port *p, int offset)
+{
+ offset = offset << p->regshift;
+ return ioread32be(p->membase + offset);
+}
+
static unsigned int io_serial_in(struct uart_port *p, int offset)
{
offset = offset << p->regshift;
@@ -488,6 +500,11 @@ static void set_io_from_upio(struct uart_port *p)
p->serial_out = mem32_serial_out;
break;
+ case UPIO_MEM32BE:
+ p->serial_in = mem32be_serial_in;
+ p->serial_out = mem32be_serial_out;
+ break;
+
#if defined(CONFIG_MIPS_ALCHEMY) || defined(CONFIG_SERIAL_8250_RT288X)
case UPIO_AU:
p->serial_in = au_serial_in;
@@ -513,6 +530,7 @@ serial_port_out_sync(struct uart_port *p, int offset, int value)
switch (p->iotype) {
case UPIO_MEM:
case UPIO_MEM32:
+ case UPIO_MEM32BE:
case UPIO_AU:
p->serial_out(p, offset, value);
p->serial_in(p, UART_LCR); /* safe, no side-effects */
@@ -2748,6 +2766,7 @@ static int serial8250_request_std_resource(struct uart_8250_port *up)
case UPIO_AU:
case UPIO_TSI:
case UPIO_MEM32:
+ case UPIO_MEM32BE:
case UPIO_MEM:
if (!port->mapbase)
break;
@@ -2784,6 +2803,7 @@ static void serial8250_release_std_resource(struct uart_8250_port *up)
case UPIO_AU:
case UPIO_TSI:
case UPIO_MEM32:
+ case UPIO_MEM32BE:
case UPIO_MEM:
if (!port->mapbase)
break;
diff --git a/drivers/tty/serial/8250/8250_early.c b/drivers/tty/serial/8250/8250_early.c
index 8e119682266a..6c0fd8b9d1c3 100644
--- a/drivers/tty/serial/8250/8250_early.c
+++ b/drivers/tty/serial/8250/8250_early.c
@@ -42,6 +42,8 @@ unsigned int __weak __init serial8250_early_in(struct uart_port *port, int offse
return readb(port->membase + offset);
case UPIO_MEM32:
return readl(port->membase + (offset << 2));
+ case UPIO_MEM32BE:
+ return ioread32be(port->membase + (offset << 2));
case UPIO_PORT:
return inb(port->iobase + offset);
default:
@@ -58,6 +60,9 @@ void __weak __init serial8250_early_out(struct uart_port *port, int offset, int
case UPIO_MEM32:
writel(value, port->membase + (offset << 2));
break;
+ case UPIO_MEM32BE:
+ iowrite32be(value, port->membase + (offset << 2));
+ break;
case UPIO_PORT:
outb(value, port->iobase + offset);
break;
diff --git a/drivers/tty/serial/of_serial.c b/drivers/tty/serial/of_serial.c
index aa00154c4a6d..5b73afb9f9f3 100644
--- a/drivers/tty/serial/of_serial.c
+++ b/drivers/tty/serial/of_serial.c
@@ -116,7 +116,8 @@ static int of_platform_serial_setup(struct platform_device *ofdev,
port->iotype = UPIO_MEM;
break;
case 4:
- port->iotype = UPIO_MEM32;
+ port->iotype = of_device_is_big_endian(np) ?
+ UPIO_MEM32BE : UPIO_MEM32;
break;
default:
dev_warn(&ofdev->dev, "unsupported reg-io-width (%d)\n",
diff --git a/include/linux/of.h b/include/linux/of.h
index 5f124f685e07..ddeaae6d2083 100644
--- a/include/linux/of.h
+++ b/include/linux/of.h
@@ -305,6 +305,7 @@ extern int of_property_read_string_helper(struct device_node *np,
extern int of_device_is_compatible(const struct device_node *device,
const char *);
extern bool of_device_is_available(const struct device_node *device);
+extern bool of_device_is_big_endian(const struct device_node *device);
extern const void *of_get_property(const struct device_node *node,
const char *name,
int *lenp);
@@ -467,6 +468,11 @@ static inline bool of_device_is_available(const struct device_node *device)
return false;
}
+static inline bool of_device_is_big_endian(const struct device_node *device)
+{
+ return false;
+}
+
static inline struct property *of_find_property(const struct device_node *np,
const char *name,
int *lenp)
diff --git a/include/linux/of_fdt.h b/include/linux/of_fdt.h
index 0ff360d5b3b3..587ee507965d 100644
--- a/include/linux/of_fdt.h
+++ b/include/linux/of_fdt.h
@@ -33,6 +33,8 @@ extern void *of_fdt_get_property(const void *blob,
extern int of_fdt_is_compatible(const void *blob,
unsigned long node,
const char *compat);
+extern bool of_fdt_is_big_endian(const void *blob,
+ unsigned long node);
extern int of_fdt_match(const void *blob, unsigned long node,
const char *const *compat);
extern void of_fdt_unflatten_tree(unsigned long *blob,
diff --git a/include/linux/of_irq.h b/include/linux/of_irq.h
index bfec136a6d1e..d884929a7747 100644
--- a/include/linux/of_irq.h
+++ b/include/linux/of_irq.h
@@ -37,8 +37,6 @@ extern int of_irq_parse_one(struct device_node *device, int index,
extern unsigned int irq_create_of_mapping(struct of_phandle_args *irq_data);
extern int of_irq_to_resource(struct device_node *dev, int index,
struct resource *r);
-extern int of_irq_to_resource_table(struct device_node *dev,
- struct resource *res, int nr_irqs);
extern void of_irq_init(const struct of_device_id *matches);
@@ -46,6 +44,8 @@ extern void of_irq_init(const struct of_device_id *matches);
extern int of_irq_count(struct device_node *dev);
extern int of_irq_get(struct device_node *dev, int index);
extern int of_irq_get_byname(struct device_node *dev, const char *name);
+extern int of_irq_to_resource_table(struct device_node *dev,
+ struct resource *res, int nr_irqs);
#else
static inline int of_irq_count(struct device_node *dev)
{
@@ -59,6 +59,11 @@ static inline int of_irq_get_byname(struct device_node *dev, const char *name)
{
return 0;
}
+static inline int of_irq_to_resource_table(struct device_node *dev,
+ struct resource *res, int nr_irqs)
+{
+ return 0;
+}
#endif
#if defined(CONFIG_OF)