* [PATCH 2/3] irqchip: mtk-cirq: Add mediatek mtk-cirq implement
From: Youlin Pei @ 2016-10-14 8:13 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20161013092808.3c4181dd@arm.com>
Hi Marc,
Thanks for your review.
On Thu, 2016-10-13 at 09:28 +0100, Marc Zyngier wrote:
> On Thu, 13 Oct 2016 13:06:33 +0800
> Youlin Pei <youlin.pei@mediatek.com> wrote:
>
> > This commit add the mtk-cirq implement for mt2701.
> Can you please expand this a bit?
Okay, I will improve it as following:
In Mediatek SOCs, the CIRQ is a low power interrupt controller designed
to works outside MCUSYS which comprises with Cortex-Ax cores,CCI and
GIC.
The CIRQ controller is integrated in between MCUSYS(include Cortex-Ax,
CCI and GIC) and interrupt sources as the second level interrupt
controller. The external interrupts which outside MCUSYS will feed
through CIRQ then bypass to GIC. CIRQ can monitors all edge trigger
interupts. When an edge interrupt is triggered, CIRQ can record the
status and generate a pulse signal to GIC when flush command executed.
When system enters sleep mode, MCUSYS will be turned off to improve
power consumption, also GIC is power down. The edge trigger interrupts
will be lost in this scenario without CIRQ.
This patch provides the CIRQ irqchip implement.
>
> >
> > Signed-off-by: Youlin Pei <youlin.pei@mediatek.com>
> > ---
> > drivers/irqchip/Makefile | 2 +-
> > drivers/irqchip/irq-mtk-cirq.c | 257 ++++++++++++++++++++++++++++++++++++++++
> > 2 files changed, 258 insertions(+), 1 deletion(-)
> > create mode 100644 drivers/irqchip/irq-mtk-cirq.c
> >
> > diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile
> > index 4c203b6..eee95c6 100644
> > --- a/drivers/irqchip/Makefile
> > +++ b/drivers/irqchip/Makefile
> > @@ -59,7 +59,7 @@ obj-$(CONFIG_BCM7120_L2_IRQ) += irq-bcm7120-l2.o
> > obj-$(CONFIG_BRCMSTB_L2_IRQ) += irq-brcmstb-l2.o
> > obj-$(CONFIG_KEYSTONE_IRQ) += irq-keystone.o
> > obj-$(CONFIG_MIPS_GIC) += irq-mips-gic.o
> > -obj-$(CONFIG_ARCH_MEDIATEK) += irq-mtk-sysirq.o
> > +obj-$(CONFIG_ARCH_MEDIATEK) += irq-mtk-sysirq.o irq-mtk-cirq.o
> > obj-$(CONFIG_ARCH_DIGICOLOR) += irq-digicolor.o
> > obj-$(CONFIG_RENESAS_H8300H_INTC) += irq-renesas-h8300h.o
> > obj-$(CONFIG_RENESAS_H8S_INTC) += irq-renesas-h8s.o
> > diff --git a/drivers/irqchip/irq-mtk-cirq.c b/drivers/irqchip/irq-mtk-cirq.c
> > new file mode 100644
> > index 0000000..544767d
> > --- /dev/null
> > +++ b/drivers/irqchip/irq-mtk-cirq.c
> > @@ -0,0 +1,257 @@
> > +/*
> > + * Copyright (c) 2016 MediaTek Inc.
> > + * Author: Youlin.Pei <youlin.pei@mediatek.com>
> > + *
> > + * This program is free software; you can redistribute it and/or modify
> > + * it under the terms of the GNU General Public License version 2 as
> > + * published by the Free Software Foundation.
> > + *
> > + * This program is distributed in the hope that it will be useful,
> > + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> > + * GNU General Public License for more details.
> > + */
> > +
> > +#include <linux/irq.h>
> > +#include <linux/irqchip.h>
> > +#include <linux/irqdomain.h>
> > +#include <linux/of.h>
> > +#include <linux/of_irq.h>
> > +#include <linux/of_address.h>
> > +#include <linux/io.h>
> > +#include <linux/slab.h>
> > +#include <linux/spinlock.h>
> > +#include <linux/syscore_ops.h>
> > +
> > +#define CIRQ_MASK_SET 0xC0
> > +#define CIRQ_MASK_CLR 0x100
> > +#define CIRQ_SENS_SET 0x180
> > +#define CIRQ_SENS_CLR 0x1C0
> > +#define CIRQ_POL_SET 0x240
> > +#define CIRQ_POL_CLR 0x280
> > +#define CIRQ_CONTROL 0x300
> > +
> > +#define CIRQ_EN 0x1
> > +#define CIRQ_EDGE 0x2
> > +#define CIRQ_FLUSH 0x4
> > +
> > +#define CIRQ_IRQ_NUM 0x200
> > +
> > +struct mtk_cirq_chip_data {
> > + void __iomem *base;
> > + unsigned int ext_irq_start;
> > +};
> > +
> > +struct mtk_cirq_chip_data *cirq_data;
>
> Any reason why this is not static?
My fault, will fix it in next version.
>
> > +
> > +static void mtk_cirq_write_mask(struct irq_data *data, unsigned int offset)
> > +{
> > + struct mtk_cirq_chip_data *chip_data = data->chip_data;
> > + unsigned int cirq_num = data->hwirq - chip_data->ext_irq_start;
>
> I wonder why you have to compute this offset. Ideally, data->hwirq
> should be the bit position (starting at zero). The fact that you have
> an offset between the pin number in this driver and the IRQ number in
> the GIC or sysirq should be resolved at alloc time.
Okay, will fix it in next version.
>
> > + u32 mask = 1 << (cirq_num % 32);
> > +
> > + writel(mask, chip_data->base + offset + (cirq_num / 32) * 4);
> > +}
> > +
> > +static void mtk_cirq_mask(struct irq_data *data)
> > +{
> > + mtk_cirq_write_mask(data, CIRQ_MASK_SET);
> > + irq_chip_mask_parent(data);
> > +}
> > +
> > +static void mtk_cirq_unmask(struct irq_data *data)
> > +{
> > + mtk_cirq_write_mask(data, CIRQ_MASK_CLR);
> > + irq_chip_unmask_parent(data);
> > +}
> > +
> > +static int mtk_cirq_set_type(struct irq_data *data, unsigned int type)
> > +{
> > + int ret;
> > +
> > + switch (type & IRQ_TYPE_SENSE_MASK) {
> > + case IRQ_TYPE_EDGE_FALLING:
> > + mtk_cirq_write_mask(data, CIRQ_POL_CLR);
> > + mtk_cirq_write_mask(data, CIRQ_SENS_CLR);
> > + break;
> > + case IRQ_TYPE_EDGE_RISING:
> > + mtk_cirq_write_mask(data, CIRQ_POL_SET);
> > + mtk_cirq_write_mask(data, CIRQ_SENS_CLR);
> > + break;
> > + case IRQ_TYPE_LEVEL_LOW:
> > + mtk_cirq_write_mask(data, CIRQ_POL_CLR);
> > + mtk_cirq_write_mask(data, CIRQ_SENS_SET);
> > + break;
> > + case IRQ_TYPE_LEVEL_HIGH:
> > + mtk_cirq_write_mask(data, CIRQ_POL_SET);
> > + mtk_cirq_write_mask(data, CIRQ_SENS_SET);
> > + break;
> > + default:
> > + break;
> > + }
> > +
> > + data = data->parent_data;
> > + ret = data->chip->irq_set_type(data, type);
> > + return ret;
> > +}
> > +
> > +static struct irq_chip mtk_cirq_chip = {
> > + .name = "MT_CIRQ",
> > + .irq_mask = mtk_cirq_mask,
> > + .irq_unmask = mtk_cirq_unmask,
> > + .irq_eoi = irq_chip_eoi_parent,
> > + .irq_set_type = mtk_cirq_set_type,
> > + .irq_retrigger = irq_chip_retrigger_hierarchy,
> > +#ifdef CONFIG_SMP
> > + .irq_set_affinity = irq_chip_set_affinity_parent,
> > +#endif
> > +};
>
> I'm surprised that you don't implement irq_set_wake. Do you wake-up on
> *any* interrupt?
The CIRQ controller don't have the wake up function, so irq_set_wake
is not implemented.
>
> > +
> > +static int mtk_cirq_domain_translate(struct irq_domain *d,
> > + struct irq_fwspec *fwspec,
> > + unsigned long *hwirq,
> > + unsigned int *type)
> > +{
> > + if (is_of_node(fwspec->fwnode)) {
> > + if (fwspec->param_count != 3)
> > + return -EINVAL;
> > +
> > + /* No PPI should point to this domain */
> > + if (fwspec->param[0] != 0)
> > + return -EINVAL;
> > +
> > + /* cirq support irq number check */
> > + if (fwspec->param[1] < ;)
> > + return -EINVAL;
> > +
> > + *hwirq = fwspec->param[1];
>
> So if you turn this into:
>
> *hwirq = fwspec->param[1] - cirq_data->ext_irq_start;
>
> and drop the above offset computing in your write function, you'd have
> something that'd make a bit more sense.
Okay, will fix it in next version. Thanks!
>
> > + *type = fwspec->param[2] & IRQ_TYPE_SENSE_MASK;
> > + return 0;
> > + }
> > +
> > + return -EINVAL;
> > +}
> > +
> > +static int mtk_cirq_domain_alloc(struct irq_domain *domain, unsigned int virq,
> > + unsigned int nr_irqs, void *arg)
> > +{
> > + int i;
> > + irq_hw_number_t hwirq;
> > + struct irq_fwspec *fwspec = arg;
> > + struct irq_fwspec parent_fwspec = *fwspec;
> > +
> > + if (fwspec->param_count != 3)
> > + return -EINVAL;
> > +
> > + /* cirq doesn't support PPI */
> > + if (fwspec->param[0])
> > + return -EINVAL;
> > +
> > + if (fwspec->param[1] < cirq_data->ext_irq_start)
> > + return -EINVAL;
> > +
> > + hwirq = fwspec->param[1];
> > + for (i = 0; i < nr_irqs; i++)
> > + irq_domain_set_hwirq_and_chip(domain, virq + i, hwirq + i,
> > + &mtk_cirq_chip,
> > + domain->host_data);
> > +
> > + parent_fwspec.fwnode = domain->parent->fwnode;
> > + return irq_domain_alloc_irqs_parent(domain, virq, nr_irqs,
> > + &parent_fwspec);
> > +}
> > +
> > +static const struct irq_domain_ops cirq_domain_ops = {
> > + .translate = mtk_cirq_domain_translate,
> > + .alloc = mtk_cirq_domain_alloc,
> > + .free = irq_domain_free_irqs_common,
> > +};
> > +
> > +#ifdef CONFIG_PM_SLEEP
> > +static int mtk_cirq_suspend(void)
> > +{
> > + u32 value;
> > +
> > + /* set edge_only mode, record edge-triggerd interrupts */
> > + /* enable cirq */
> > + value = readl_relaxed(cirq_data->base + CIRQ_CONTROL);
> > + value |= (CIRQ_EDGE | CIRQ_EN);
> > + writel(value, cirq_data->base + CIRQ_CONTROL);
> > + return 0;
> > +}
> > +
> > +static void mtk_cirq_resume(void)
> > +{
> > + u32 value;
> > +
> > + /* flush recored interrupts, will send signals to parent controller */
> > + value = readl_relaxed(cirq_data->base + CIRQ_CONTROL);
> > + writel(value | CIRQ_FLUSH, cirq_data->base + CIRQ_CONTROL);
> > +
> > + /* disable cirq */
> > + value = readl_relaxed(cirq_data->base + CIRQ_CONTROL);
> > + value &= ~(CIRQ_EDGE | CIRQ_EN);
> > + writel(value, cirq_data->base + CIRQ_CONTROL);
> > +}
> > +
> > +static struct syscore_ops mtk_cirq_syscore_ops = {
> > + .suspend = mtk_cirq_suspend,
> > + .resume = mtk_cirq_resume,
> > +};
> > +
> > +static void mtk_cirq_syscore_init(void)
> > +{
> > + register_syscore_ops(&mtk_cirq_syscore_ops);
> > +}
> > +#else
> > +static inline void mtk_cirq_syscore_init(void) {}
> > +#endif
> > +
> > +static int __init mtk_cirq_of_init(struct device_node *node,
> > + struct device_node *parent)
> > +{
> > + struct irq_domain *domain, *domain_parent;
> > + int ret;
> > +
> > + domain_parent = irq_find_host(parent);
> > + if (!domain_parent) {
> > + pr_err("mtk_cirq: interrupt-parent not found\n");
> > + return -EINVAL;
> > + }
> > +
> > + cirq_data = kzalloc(sizeof(*cirq_data), GFP_KERNEL);
> > + if (!cirq_data)
> > + return -ENOMEM;
> > +
> > + cirq_data->base = of_iomap(node, 0);
> > + if (!cirq_data->base) {
> > + pr_err("mtk_cirq: unable to map cirq register\n");
> > + ret = -ENXIO;
> > + goto out_free;
> > + }
> > +
> > + if (of_property_read_u32(node, "mediatek,ext-irq-start",
> > + &cirq_data->ext_irq_start)) {
> > + ret = -EINVAL;
> > + goto out_free;
> > + }
> > +
> > + domain = irq_domain_add_hierarchy(domain_parent, 0, CIRQ_IRQ_NUM, node,
> > + &cirq_domain_ops, cirq_data);
> > + if (!domain) {
> > + ret = -ENOMEM;
> > + goto out_unmap;
> > + }
> > +
> > + mtk_cirq_syscore_init();
> > +
> > + return 0;
> > +
> > +out_unmap:
> > + iounmap(cirq_data->base);
> > +out_free:
> > + kfree(cirq_data);
> > + return ret;
> > +}
> > +
> > +IRQCHIP_DECLARE(mtk_cirq, "mediatek,mt2701-cirq", mtk_cirq_of_init);
>
> Thanks,
>
> M.
Thanks,
Youlin
^ permalink raw reply
* aarch64 ACPI boot regressed by commit 7ba5f605f3a0 ("arm64/numa: remove the limitation that cpu0 must bind to node0")
From: Andrew Jones @ 2016-10-14 8:05 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <4a64cd93-5ead-aad6-1057-f42224d65b43@redhat.com>
On Fri, Oct 14, 2016 at 12:50:29AM +0200, Laszlo Ersek wrote:
> (4) Analysis (well, a lame attempt at that, because I have zero
> familiarity with this code). Let me quote the patch:
>
> > commit 7ba5f605f3a0d9495aad539eeb8346d726dfc183
> > Author: Zhen Lei <thunder.leizhen@huawei.com>
> > Date: Thu Sep 1 14:55:04 2016 +0800
> >
> > arm64/numa: remove the limitation that cpu0 must bind to node0
> >
> > 1. Remove the old binding code.
> > 2. Read the nid of cpu0 from dts.
> > 3. Fallback the nid of cpu0 to 0 when numa=off is set in bootargs.
> >
> > Signed-off-by: Zhen Lei <thunder.leizhen@huawei.com>
> > Signed-off-by: Will Deacon <will.deacon@arm.com>
> >
> > diff --git a/arch/arm64/kernel/smp.c b/arch/arm64/kernel/smp.c
> > index c3c08368a685..8b048e6ec34a 100644
> > --- a/arch/arm64/kernel/smp.c
> > +++ b/arch/arm64/kernel/smp.c
> > @@ -624,6 +624,7 @@ static void __init of_parse_and_init_cpus(void)
> > }
> >
> > bootcpu_valid = true;
> > + early_map_cpu_to_node(0, of_node_to_nid(dn));
> >
> > /*
> > * cpu_logical_map has already been
> > diff --git a/arch/arm64/mm/numa.c b/arch/arm64/mm/numa.c
> > index 0a15f010b64a..778a985c8a70 100644
> > --- a/arch/arm64/mm/numa.c
> > +++ b/arch/arm64/mm/numa.c
> > @@ -116,16 +116,24 @@ static void __init setup_node_to_cpumask_map(void)
> > */
> > void numa_store_cpu_info(unsigned int cpu)
> > {
> > - map_cpu_to_node(cpu, numa_off ? 0 : cpu_to_node_map[cpu]);
> > + map_cpu_to_node(cpu, cpu_to_node_map[cpu]);
> > }
> >
> > void __init early_map_cpu_to_node(unsigned int cpu, int nid)
> > {
> > /* fallback to node 0 */
> > - if (nid < 0 || nid >= MAX_NUMNODES)
> > + if (nid < 0 || nid >= MAX_NUMNODES || numa_off)
> > nid = 0;
The ACPI equivalent code must be missing (at least) the above, because,
even with DT, mach-virt won't have cpu to node mappings unless numa
is configured on the command line. Can you try adding something like
-m 512 -smp 4 \
-numa node,mem=256M,cpus=0-1,nodeid=0 \
-numa node,mem=256M,cpus=2-3,nodeid=1
to your QEMU command line? Then when you boot with ACPI you'll get a
SRAT. If that works, then we're just missing the "no SRAT, nid = 0"
code (that should have been added with this patch)
Thanks,
drew
^ permalink raw reply
* [PATCH 6/6] misc: q8-hardwaremgr: Add quirk handling
From: Hans de Goede @ 2016-10-14 7:53 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20161014075337.10452-1-hdegoede@redhat.com>
Some models require some special handling, e.g. one q8 tablet I've needs
esp,crystal-26M-en = <1> in the sdio_wifi dt node for the wifi to work
properly.
Add a q8_hardwaremgr_apply_quirks() helper function which identifies the
tablet based on the detected soc, touchscreen, accelerometer combo and
applies model specific quirks where necessary.
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
---
drivers/misc/q8-hardwaremgr.c | 23 +++++++++++++++++++++++
1 file changed, 23 insertions(+)
diff --git a/drivers/misc/q8-hardwaremgr.c b/drivers/misc/q8-hardwaremgr.c
index 95cdaf2..cbf4f61 100644
--- a/drivers/misc/q8-hardwaremgr.c
+++ b/drivers/misc/q8-hardwaremgr.c
@@ -715,6 +715,28 @@ static void q8_hardwaremgr_apply_accelerometer(struct q8_hardwaremgr_data *data)
of_node_put(np);
}
+static void q8_hardwaremgr_apply_quirks(struct q8_hardwaremgr_data *data)
+{
+ struct device_node *np;
+ struct of_changeset cset;
+
+ /* This A33 tzx-723q4 PCB tablet with esp8089 needs crystal_26M_en=1 */
+ if (data->soc == a33 && data->touchscreen.model == gsl1680_b482 &&
+ data->accelerometer.model == dmard09 && !data->has_rda599x) {
+ dev_info(data->dev, "Applying crystal_26M_en=1 sdio_wifi quirk\n");
+ np = of_find_node_by_name(of_root, "sdio_wifi");
+ if (!np) {
+ dev_warn(data->dev, "Could not find sdio_wifi dt node\n");
+ return;
+ }
+ of_changeset_init(&cset);
+ of_changeset_add_property_u32(&cset, np,
+ "esp,crystal-26M-en", 1);
+ of_changeset_apply(&cset);
+ of_node_put(np);
+ }
+}
+
static int q8_hardwaremgr_do_probe(struct q8_hardwaremgr_data *data,
struct q8_hardwaremgr_device *dev,
const char *prefix, bus_probe_func func)
@@ -846,6 +868,7 @@ static int q8_hardwaremgr_probe(struct platform_device *pdev)
q8_hardwaremgr_apply_touchscreen(data);
q8_hardwaremgr_apply_accelerometer(data);
+ q8_hardwaremgr_apply_quirks(data);
error:
kfree(data);
--
2.9.3
^ permalink raw reply related
* [PATCH 5/6] misc: q8-hardwaremgr: Better touchscreen defaults based on heuristics
From: Hans de Goede @ 2016-10-14 7:53 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20161014075337.10452-1-hdegoede@redhat.com>
Some q8s with gsl1680 touchscreens need to use an alterternative
firmware file and/or need their x/y axis inverted.
This commit adds some heuristics to pick defaults based on other detected
tablet properties (mostly which accelerometer is detected) and sets
defaults based on this.
Although there is not necessarily any relationship between these
settings, this does give us the right defaults on all known model q8
tablets, making the touchscreen work out of the box without requiring
any kernel commandline options to be set.
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
---
drivers/misc/q8-hardwaremgr.c | 81 +++++++++++++++++++++++++++++++++++++++----
1 file changed, 74 insertions(+), 7 deletions(-)
diff --git a/drivers/misc/q8-hardwaremgr.c b/drivers/misc/q8-hardwaremgr.c
index 21754f5..95cdaf2 100644
--- a/drivers/misc/q8-hardwaremgr.c
+++ b/drivers/misc/q8-hardwaremgr.c
@@ -84,7 +84,12 @@ enum {
};
#define DA280_REG_CHIP_ID 0x01
+#define DA280_REG_ACC_Z_LSB 0x06
+#define DA280_REG_MODE_BW 0x11
#define DA280_CHIP_ID 0x13
+#define DA280_MODE_ENABLE 0x1e
+#define DA280_MODE_DISABLE 0x9e
+
#define DA311_REG_CHIP_ID 0x0f
#define DA311_CHIP_ID 0x13
@@ -112,6 +117,7 @@ enum {
enum {
accel_unknown,
+ da226,
da280,
da311,
dmard05,
@@ -313,8 +319,23 @@ static int q8_hardwaremgr_probe_touchscreen(struct q8_hardwaremgr_data *data,
static void q8_hardwaremgr_apply_gsl1680_a082_variant(
struct q8_hardwaremgr_data *data)
{
- if (touchscreen_variant != -1)
+ if (touchscreen_variant != -1) {
data->touchscreen_variant = touchscreen_variant;
+ } else {
+ /*
+ * These accelerometer based heuristics select the best
+ * default based on known q8 tablets.
+ */
+ switch (data->accelerometer.model) {
+ case mc3230:
+ data->touchscreen_invert_x = 1;
+ break;
+ case dmard10:
+ case mxc6225:
+ data->touchscreen_variant = 1;
+ break;
+ }
+ }
switch (data->touchscreen_variant) {
default:
@@ -338,8 +359,30 @@ static void q8_hardwaremgr_apply_gsl1680_a082_variant(
static void q8_hardwaremgr_apply_gsl1680_b482_variant(
struct q8_hardwaremgr_data *data)
{
- if (touchscreen_variant != -1)
+ if (touchscreen_variant != -1) {
data->touchscreen_variant = touchscreen_variant;
+ } else {
+ /*
+ * These accelerometer based heuristics select the best
+ * default based on known q8 tablets.
+ */
+ switch (data->accelerometer.model) {
+ case da280:
+ if (data->accelerometer.addr == 0x27)
+ ; /* No-op */
+ else if (data->has_rda599x)
+ data->touchscreen_invert_x = 1;
+ else
+ data->touchscreen_invert_y = 1;
+ break;
+ case dmard09:
+ data->touchscreen_invert_x = 1;
+ break;
+ case mxc6225:
+ data->touchscreen_variant = 1;
+ break;
+ }
+ }
switch (data->touchscreen_variant) {
default:
@@ -587,16 +630,39 @@ static int q8_hardwaremgr_probe_dmard10(struct q8_hardwaremgr_data *data,
static int q8_hardwaremgr_probe_da280(struct q8_hardwaremgr_data *data,
struct i2c_client *client)
{
- int id;
+ int ret;
+
+ ret = i2c_smbus_read_byte_data(client, DA280_REG_CHIP_ID);
+ if (ret != DA280_CHIP_ID)
+ return ret == -ETIMEDOUT ? -ETIMEDOUT : -ENODEV;
- id = i2c_smbus_read_byte_data(client, DA280_REG_CHIP_ID);
- if (id == DA280_CHIP_ID) {
+ /* da226 (2-axis) or da280 (3-axis) ? measure once to detect */
+ ret = i2c_smbus_write_byte_data(client, DA280_REG_MODE_BW,
+ DA280_MODE_ENABLE);
+ if (ret)
+ return ret == -ETIMEDOUT ? -ETIMEDOUT : -ENODEV;
+
+ msleep(10);
+
+ ret = i2c_smbus_read_word_data(client, DA280_REG_ACC_Z_LSB);
+ if (ret < 0)
+ return ret == -ETIMEDOUT ? -ETIMEDOUT : -ENODEV;
+
+ /* If not present Z reports max pos value (14 bits, 2 low bits 0) */
+ if (ret == 32764) {
+ data->accelerometer.compatible = "miramems,da226";
+ data->accelerometer.model = da226;
+ } else {
data->accelerometer.compatible = "miramems,da280";
data->accelerometer.model = da280;
- return 0;
}
- return id == -ETIMEDOUT ? -ETIMEDOUT : -ENODEV;
+ ret = i2c_smbus_write_byte_data(client, DA280_REG_MODE_BW,
+ DA280_MODE_DISABLE);
+ if (ret)
+ return ret == -ETIMEDOUT ? -ETIMEDOUT : -ENODEV;
+
+ return 0;
}
static int q8_hardwaremgr_probe_da311(struct q8_hardwaremgr_data *data,
@@ -625,6 +691,7 @@ static int q8_hardwaremgr_probe_accelerometer(struct q8_hardwaremgr_data *data,
PROBE_CLIENT(&data->accelerometer, 0x1d, q8_hardwaremgr_probe_dmard09);
PROBE_CLIENT(&data->accelerometer, 0x18, q8_hardwaremgr_probe_dmard10);
PROBE_CLIENT(&data->accelerometer, 0x26, q8_hardwaremgr_probe_da280);
+ PROBE_CLIENT(&data->accelerometer, 0x27, q8_hardwaremgr_probe_da280);
PROBE_CLIENT(&data->accelerometer, 0x27, q8_hardwaremgr_probe_da311);
return -ENODEV;
--
2.9.3
^ permalink raw reply related
* [PATCH 4/6] misc: q8-hardwaremgr: Add rda599x wift/bt/fm radio combo detection
From: Hans de Goede @ 2016-10-14 7:53 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20161014075337.10452-1-hdegoede@redhat.com>
Some q8s have a rda599x sdio wifi/bt + i2c fm radio chip with its
i2c interface connected to the same i2c bus as the accelerometer.
There is no kernel driver for these devices yet, but it is useful to
know if one is present for the touchscreen variant selection heuristics.
And when in the future we do get a driver, this detection code will also
be useful to automatically register i2c client dt nodes for these devices.
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
---
drivers/misc/q8-hardwaremgr.c | 28 ++++++++++++++++++++++++++++
1 file changed, 28 insertions(+)
diff --git a/drivers/misc/q8-hardwaremgr.c b/drivers/misc/q8-hardwaremgr.c
index 8ed71a9..21754f5 100644
--- a/drivers/misc/q8-hardwaremgr.c
+++ b/drivers/misc/q8-hardwaremgr.c
@@ -144,6 +144,7 @@ struct q8_hardwaremgr_data {
int touchscreen_invert_y;
int touchscreen_swap_x_y;
const char *touchscreen_fw_name;
+ bool has_rda599x;
};
typedef int (*bus_probe_func)(struct q8_hardwaremgr_data *data,
@@ -454,6 +455,28 @@ static void q8_hardwaremgr_apply_touchscreen(struct q8_hardwaremgr_data *data)
of_node_put(np);
}
+static int q8_hardwaremgr_probe_rda599x(struct q8_hardwaremgr_data *data,
+ struct i2c_client *client)
+{
+ int id;
+
+ /*
+ * Check for the (integrated) rda580x / rda5820 fm receiver at 0x11
+ *
+ * Alternatively we could check for the wifi_rf i2c interface at
+ * address 0x14, by selecting page/bank 1 through:
+ * smbus_write_word_swapped(0x3f, 0x01)
+ * and then doing a smbus_read_word_swapped(0x20) which will
+ * return 0x5990 for a rda5990. We prefer the fm detect method since
+ * we want to avoid doing any smbus_writes while probing.
+ */
+ id = i2c_smbus_read_word_swapped(client, 0x0c);
+ if (id == 0x5802 || id == 0x5803 || id == 0x5805 || id == 0x5820)
+ data->has_rda599x = true;
+
+ return id == -ETIMEDOUT ? -ETIMEDOUT : -ENODEV;
+}
+
static int q8_hardwaremgr_probe_mxc6225(struct q8_hardwaremgr_data *data,
struct i2c_client *client)
{
@@ -594,6 +617,8 @@ static int q8_hardwaremgr_probe_da311(struct q8_hardwaremgr_data *data,
static int q8_hardwaremgr_probe_accelerometer(struct q8_hardwaremgr_data *data,
struct i2c_adapter *adap)
{
+ /* The rda599x wifi/bt/fm shares the i2c bus with the accelerometer */
+ PROBE_CLIENT(NULL, 0x11, q8_hardwaremgr_probe_rda599x);
PROBE_CLIENT(&data->accelerometer, 0x15, q8_hardwaremgr_probe_mxc6225);
PROBE_CLIENT(&data->accelerometer, 0x4c, q8_hardwaremgr_probe_mc3230);
PROBE_CLIENT(&data->accelerometer, 0x1c, q8_hardwaremgr_probe_dmard06);
@@ -749,6 +774,9 @@ static int q8_hardwaremgr_probe(struct platform_device *pdev)
if (ret)
goto error;
+ if (data->has_rda599x)
+ dev_info(data->dev, "Found a rda599x sdio/i2c wifi/bt/fm combo chip\n");
+
q8_hardwaremgr_apply_touchscreen(data);
q8_hardwaremgr_apply_accelerometer(data);
--
2.9.3
^ permalink raw reply related
* [PATCH 3/6] misc: q8-hardwaremgr: Add accelerometer detection
From: Hans de Goede @ 2016-10-14 7:53 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20161014075337.10452-1-hdegoede@redhat.com>
Extend the q8-hardwaremgr with accelerometer detection.
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
---
drivers/misc/Kconfig | 2 +-
drivers/misc/q8-hardwaremgr.c | 219 ++++++++++++++++++++++++++++++++++++++++++
2 files changed, 220 insertions(+), 1 deletion(-)
diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
index d6c1529..11b05b0 100644
--- a/drivers/misc/Kconfig
+++ b/drivers/misc/Kconfig
@@ -801,7 +801,7 @@ config Q8_HARDWAREMGR
default n
help
This option enables support for autodetecting the touchscreen
- on Allwinner Q8 tablets.
+ and accelerometer on Allwinner Q8 tablets.
If unsure, say N.
diff --git a/drivers/misc/q8-hardwaremgr.c b/drivers/misc/q8-hardwaremgr.c
index 346102d..8ed71a9 100644
--- a/drivers/misc/q8-hardwaremgr.c
+++ b/drivers/misc/q8-hardwaremgr.c
@@ -83,6 +83,48 @@ enum {
zet6251,
};
+#define DA280_REG_CHIP_ID 0x01
+#define DA280_CHIP_ID 0x13
+#define DA311_REG_CHIP_ID 0x0f
+#define DA311_CHIP_ID 0x13
+
+#define DMARD06_CHIP_ID_REG 0x0f
+#define DMARD05_CHIP_ID 0x05
+#define DMARD06_CHIP_ID 0x06
+#define DMARD07_CHIP_ID 0x07
+#define DMARD09_REG_CHIPID 0x18
+#define DMARD09_CHIPID 0x95
+#define DMARD10_REG_STADR 0x12
+#define DMARD10_REG_STAINT 0x1c
+#define DMARD10_VALUE_STADR 0x55
+#define DMARD10_VALUE_STAINT 0xaa
+
+#define MC3230_REG_CHIP_ID 0x18
+#define MC3230_CHIP_ID 0x01
+#define MMA7660_CHIP_ID 0x00 /* Factory reserved on MMA7660 */
+#define MC3230_REG_PRODUCT_CODE 0x3b
+#define MMA7660_PRODUCT_CODE 0x00 /* Factory reserved on MMA7660 */
+#define MC3210_PRODUCT_CODE 0x90
+#define MC3230_PRODUCT_CODE 0x19
+
+#define MXC6225_REG_CHIP_ID 0x08
+#define MXC6225_CHIP_ID 0x05
+
+enum {
+ accel_unknown,
+ da280,
+ da311,
+ dmard05,
+ dmard06,
+ dmard07,
+ dmard09,
+ dmard10,
+ mc3210,
+ mc3230,
+ mma7660,
+ mxc6225,
+};
+
struct q8_hardwaremgr_device {
int model;
int addr;
@@ -94,6 +136,7 @@ struct q8_hardwaremgr_data {
struct device *dev;
enum soc soc;
struct q8_hardwaremgr_device touchscreen;
+ struct q8_hardwaremgr_device accelerometer;
int touchscreen_variant;
int touchscreen_width;
int touchscreen_height;
@@ -411,6 +454,175 @@ static void q8_hardwaremgr_apply_touchscreen(struct q8_hardwaremgr_data *data)
of_node_put(np);
}
+static int q8_hardwaremgr_probe_mxc6225(struct q8_hardwaremgr_data *data,
+ struct i2c_client *client)
+{
+ int id;
+
+ id = i2c_smbus_read_byte_data(client, MXC6225_REG_CHIP_ID);
+ /* Bits 7 - 5 of the chip-id register are undefined */
+ if (id >= 0 && (id & 0x1f) == MXC6225_CHIP_ID) {
+ data->accelerometer.compatible = "memsic,mxc6225";
+ data->accelerometer.model = mxc6225;
+ return 0;
+ }
+
+ return id == -ETIMEDOUT ? -ETIMEDOUT : -ENODEV;
+}
+
+static int q8_hardwaremgr_probe_mc3230(struct q8_hardwaremgr_data *data,
+ struct i2c_client *client)
+{
+ int id, pc = 0;
+
+ /* First check chip-id, then product-id */
+ id = i2c_smbus_read_byte_data(client, MC3230_REG_CHIP_ID);
+ if (id == MC3230_CHIP_ID || id == MMA7660_CHIP_ID) {
+ pc = i2c_smbus_read_byte_data(client, MC3230_REG_PRODUCT_CODE);
+ switch (pc) {
+ case MMA7660_PRODUCT_CODE:
+ data->accelerometer.compatible = "fsl,mma7660";
+ data->accelerometer.model = mma7660;
+ return 0;
+ case MC3210_PRODUCT_CODE:
+ data->accelerometer.compatible = "mcube,mc3210";
+ data->accelerometer.model = mc3210;
+ return 0;
+ case MC3230_PRODUCT_CODE:
+ data->accelerometer.compatible = "mcube,mc3230";
+ data->accelerometer.model = mc3230;
+ return 0;
+ case -ETIMEDOUT:
+ return -ETIMEDOUT; /* Bus stuck bail immediately */
+ }
+ }
+
+ return id == -ETIMEDOUT ? -ETIMEDOUT : -ENODEV;
+}
+
+static int q8_hardwaremgr_probe_dmard06(struct q8_hardwaremgr_data *data,
+ struct i2c_client *client)
+{
+ int id;
+
+ id = i2c_smbus_read_byte_data(client, DMARD06_CHIP_ID_REG);
+ switch (id) {
+ case DMARD05_CHIP_ID:
+ data->accelerometer.compatible = "domintech,dmard05";
+ data->accelerometer.model = dmard05;
+ return 0;
+ case DMARD06_CHIP_ID:
+ data->accelerometer.compatible = "domintech,dmard06";
+ data->accelerometer.model = dmard06;
+ return 0;
+ case DMARD07_CHIP_ID:
+ data->accelerometer.compatible = "domintech,dmard07";
+ data->accelerometer.model = dmard07;
+ return 0;
+ }
+
+ return id == -ETIMEDOUT ? -ETIMEDOUT : -ENODEV;
+}
+
+static int q8_hardwaremgr_probe_dmard09(struct q8_hardwaremgr_data *data,
+ struct i2c_client *client)
+{
+ int id;
+
+ id = i2c_smbus_read_byte_data(client, DMARD09_REG_CHIPID);
+ if (id == DMARD09_CHIPID) {
+ data->accelerometer.compatible = "domintech,dmard09";
+ data->accelerometer.model = dmard09;
+ return 0;
+ }
+
+ return id == -ETIMEDOUT ? -ETIMEDOUT : -ENODEV;
+}
+
+static int q8_hardwaremgr_probe_dmard10(struct q8_hardwaremgr_data *data,
+ struct i2c_client *client)
+{
+ int stadr, staint = 0;
+
+ /* These 2 registers have special POR reset values used for id */
+ stadr = i2c_smbus_read_byte_data(client, DMARD10_REG_STADR);
+ if (stadr == DMARD10_VALUE_STADR) {
+ staint = i2c_smbus_read_byte_data(client, DMARD10_REG_STAINT);
+ switch (staint) {
+ case DMARD10_VALUE_STAINT:
+ data->accelerometer.compatible = "domintech,dmard10";
+ data->accelerometer.model = dmard10;
+ return 0;
+ case -ETIMEDOUT:
+ return -ETIMEDOUT; /* Bus stuck bail immediately */
+ }
+ }
+
+ return stadr == -ETIMEDOUT ? -ETIMEDOUT : -ENODEV;
+}
+
+static int q8_hardwaremgr_probe_da280(struct q8_hardwaremgr_data *data,
+ struct i2c_client *client)
+{
+ int id;
+
+ id = i2c_smbus_read_byte_data(client, DA280_REG_CHIP_ID);
+ if (id == DA280_CHIP_ID) {
+ data->accelerometer.compatible = "miramems,da280";
+ data->accelerometer.model = da280;
+ return 0;
+ }
+
+ return id == -ETIMEDOUT ? -ETIMEDOUT : -ENODEV;
+}
+
+static int q8_hardwaremgr_probe_da311(struct q8_hardwaremgr_data *data,
+ struct i2c_client *client)
+{
+ int id;
+
+ id = i2c_smbus_read_byte_data(client, DA311_REG_CHIP_ID);
+ if (id == DA311_CHIP_ID) {
+ data->accelerometer.compatible = "miramems,da311";
+ data->accelerometer.model = da311;
+ return 0;
+ }
+
+ return id == -ETIMEDOUT ? -ETIMEDOUT : -ENODEV;
+}
+
+static int q8_hardwaremgr_probe_accelerometer(struct q8_hardwaremgr_data *data,
+ struct i2c_adapter *adap)
+{
+ PROBE_CLIENT(&data->accelerometer, 0x15, q8_hardwaremgr_probe_mxc6225);
+ PROBE_CLIENT(&data->accelerometer, 0x4c, q8_hardwaremgr_probe_mc3230);
+ PROBE_CLIENT(&data->accelerometer, 0x1c, q8_hardwaremgr_probe_dmard06);
+ PROBE_CLIENT(&data->accelerometer, 0x1d, q8_hardwaremgr_probe_dmard09);
+ PROBE_CLIENT(&data->accelerometer, 0x18, q8_hardwaremgr_probe_dmard10);
+ PROBE_CLIENT(&data->accelerometer, 0x26, q8_hardwaremgr_probe_da280);
+ PROBE_CLIENT(&data->accelerometer, 0x27, q8_hardwaremgr_probe_da311);
+
+ return -ENODEV;
+}
+
+static void q8_hardwaremgr_apply_accelerometer(struct q8_hardwaremgr_data *data)
+{
+ struct of_changeset cset;
+ struct device_node *np;
+
+ if (data->accelerometer.model == accel_unknown)
+ return;
+
+ np = q8_hardware_mgr_apply_common(&data->accelerometer, &cset,
+ "accelerometer");
+ if (!np)
+ return;
+
+ of_changeset_apply(&cset);
+
+ of_node_put(np);
+}
+
static int q8_hardwaremgr_do_probe(struct q8_hardwaremgr_data *data,
struct q8_hardwaremgr_device *dev,
const char *prefix, bus_probe_func func)
@@ -531,7 +743,14 @@ static int q8_hardwaremgr_probe(struct platform_device *pdev)
if (ret)
goto error;
+ ret = q8_hardwaremgr_do_probe(data, &data->accelerometer,
+ "accelerometer",
+ q8_hardwaremgr_probe_accelerometer);
+ if (ret)
+ goto error;
+
q8_hardwaremgr_apply_touchscreen(data);
+ q8_hardwaremgr_apply_accelerometer(data);
error:
kfree(data);
--
2.9.3
^ permalink raw reply related
* [PATCH 2/6] misc: Add Allwinner Q8 tablet hardware manager
From: Hans de Goede @ 2016-10-14 7:53 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20161014075337.10452-1-hdegoede@redhat.com>
Allwinnner A13 / A23 / A33 based Q8 tablets are popular cheap 7" tablets
of which a new batch is produced every few weeks. Each batch uses a
different mix of touchscreen, accelerometer and wifi peripherals.
Given that each batch is different creating a devicetree for each variant
is not desirable. This commit adds a Q8 tablet hardware manager which
auto-detects the touchscreen and accelerometer so that a single generic
dts can be used for these tablets.
The wifi is connected to a discoverable bus (sdio or usb) and will be
autodetected by the mmc resp. usb subsystems.
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
---
drivers/misc/Kconfig | 11 +
drivers/misc/Makefile | 1 +
drivers/misc/q8-hardwaremgr.c | 581 ++++++++++++++++++++++++++++++++++++++++++
3 files changed, 593 insertions(+)
create mode 100644 drivers/misc/q8-hardwaremgr.c
diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
index d002528..d6c1529 100644
--- a/drivers/misc/Kconfig
+++ b/drivers/misc/Kconfig
@@ -794,6 +794,17 @@ config PANEL_BOOT_MESSAGE
An empty message will only clear the display at driver init time. Any other
printf()-formatted message is valid with newline and escape codes.
+config Q8_HARDWAREMGR
+ bool "Allwinner Q8 tablet hardware manager"
+ depends on I2C
+ depends on (ARCH_SUNXI && GPIOLIB && OF && OF_DYNAMIC) || COMPILE_TEST
+ default n
+ help
+ This option enables support for autodetecting the touchscreen
+ on Allwinner Q8 tablets.
+
+ If unsure, say N.
+
source "drivers/misc/c2port/Kconfig"
source "drivers/misc/eeprom/Kconfig"
source "drivers/misc/cb710/Kconfig"
diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
index fb32516..1a0ebb0 100644
--- a/drivers/misc/Makefile
+++ b/drivers/misc/Makefile
@@ -56,6 +56,7 @@ obj-$(CONFIG_ECHO) += echo/
obj-$(CONFIG_VEXPRESS_SYSCFG) += vexpress-syscfg.o
obj-$(CONFIG_CXL_BASE) += cxl/
obj-$(CONFIG_PANEL) += panel.o
+obj-$(CONFIG_Q8_HARDWAREMGR) += q8-hardwaremgr.o
lkdtm-$(CONFIG_LKDTM) += lkdtm_core.o
lkdtm-$(CONFIG_LKDTM) += lkdtm_bugs.o
diff --git a/drivers/misc/q8-hardwaremgr.c b/drivers/misc/q8-hardwaremgr.c
new file mode 100644
index 0000000..346102d
--- /dev/null
+++ b/drivers/misc/q8-hardwaremgr.c
@@ -0,0 +1,581 @@
+/*
+ * Allwinner q8 formfactor tablet hardware manager
+ *
+ * Copyright (C) 2016 Hans de Goede <hdegoede@redhat.com>
+ *
+ * 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 <asm/unaligned.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/gpio/consumer.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/consumer.h>
+#include <linux/slab.h>
+
+/*
+ * We can detect which touchscreen controller is used automatically,
+ * but some controllers can be wired up differently depending on the
+ * q8 PCB variant used, so they need different firmware files / settings.
+ *
+ * We allow the user to specify a firmware_variant to select a config
+ * from a list of known configs. We also allow overriding each setting
+ * individually.
+ */
+
+static int touchscreen_variant = -1;
+module_param(touchscreen_variant, int, 0444);
+MODULE_PARM_DESC(touchscreen_variant, "Touchscreen variant 0-x, -1 for auto");
+
+static int touchscreen_width = -1;
+module_param(touchscreen_width, int, 0444);
+MODULE_PARM_DESC(touchscreen_width, "Touchscreen width, -1 for auto");
+
+static int touchscreen_height = -1;
+module_param(touchscreen_height, int, 0444);
+MODULE_PARM_DESC(touchscreen_height, "Touchscreen height, -1 for auto");
+
+static int touchscreen_invert_x = -1;
+module_param(touchscreen_invert_x, int, 0444);
+MODULE_PARM_DESC(touchscreen_invert_x, "Touchscreen invert x, -1 for auto");
+
+static int touchscreen_invert_y = -1;
+module_param(touchscreen_invert_y, int, 0444);
+MODULE_PARM_DESC(touchscreen_invert_y, "Touchscreen invert y, -1 for auto");
+
+static int touchscreen_swap_x_y = -1;
+module_param(touchscreen_swap_x_y, int, 0444);
+MODULE_PARM_DESC(touchscreen_swap_x_y, "Touchscreen swap x y, -1 for auto");
+
+static char *touchscreen_fw_name;
+module_param(touchscreen_fw_name, charp, 0444);
+MODULE_PARM_DESC(touchscreen_fw_name, "Touchscreen firmware filename");
+
+enum soc {
+ a13,
+ a23,
+ a33,
+};
+
+#define TOUCHSCREEN_POWER_ON_DELAY 20
+#define SILEAD_REG_ID 0xFC
+#define EKTF2127_RESPONSE 0x52
+#define EKTF2127_REQUEST 0x53
+#define EKTF2127_WIDTH 0x63
+
+enum {
+ touchscreen_unknown,
+ gsl1680_a082,
+ gsl1680_b482,
+ ektf2127,
+ zet6251,
+};
+
+struct q8_hardwaremgr_device {
+ int model;
+ int addr;
+ const char *compatible;
+ bool delete_regulator;
+};
+
+struct q8_hardwaremgr_data {
+ struct device *dev;
+ enum soc soc;
+ struct q8_hardwaremgr_device touchscreen;
+ int touchscreen_variant;
+ int touchscreen_width;
+ int touchscreen_height;
+ int touchscreen_invert_x;
+ int touchscreen_invert_y;
+ int touchscreen_swap_x_y;
+ const char *touchscreen_fw_name;
+};
+
+typedef int (*bus_probe_func)(struct q8_hardwaremgr_data *data,
+ struct i2c_adapter *adap);
+typedef int (*client_probe_func)(struct q8_hardwaremgr_data *data,
+ struct i2c_client *client);
+
+static struct device_node *q8_hardware_mgr_apply_common(
+ struct q8_hardwaremgr_device *dev, struct of_changeset *cset,
+ const char *prefix)
+{
+ struct device_node *np;
+
+ np = of_find_node_by_name(of_root, prefix);
+ /* Never happens already checked in q8_hardwaremgr_do_probe() */
+ if (WARN_ON(!np))
+ return NULL;
+
+ of_changeset_init(cset);
+ of_changeset_add_property_u32(cset, np, "reg", dev->addr);
+ of_changeset_add_property_string(cset, np, "compatible",
+ dev->compatible);
+ of_changeset_update_property_string(cset, np, "status", "okay");
+
+ if (dev->delete_regulator) {
+ struct property *p;
+
+ p = of_find_property(np, "vddio-supply", NULL);
+ /* Never happens already checked in q8_hardwaremgr_do_probe() */
+ if (WARN_ON(!p))
+ return np;
+
+ of_changeset_remove_property(cset, np, p);
+ }
+
+ return np; /* Allow the caller to make further changes */
+}
+
+static int q8_hardwaremgr_probe_client(struct q8_hardwaremgr_data *data,
+ struct q8_hardwaremgr_device *dev,
+ struct i2c_adapter *adap, u16 addr,
+ client_probe_func client_probe)
+{
+ struct i2c_client *client;
+ int ret;
+
+ client = i2c_new_dummy(adap, addr);
+ if (!client)
+ return -ENOMEM;
+
+ /* ret will be one of 0: Success, -ETIMEDOUT: Bus stuck or -ENODEV */
+ ret = client_probe(data, client);
+ if (ret == 0)
+ dev->addr = addr;
+
+ i2c_unregister_device(client);
+
+ return ret;
+}
+
+#define PROBE_CLIENT(dev, addr, probe) \
+{ \
+ int ret = q8_hardwaremgr_probe_client(data, dev, adap, addr, probe); \
+ if (ret != -ENODEV) \
+ return ret; \
+}
+
+static int q8_hardwaremgr_probe_silead(struct q8_hardwaremgr_data *data,
+ struct i2c_client *client)
+{
+ __le32 chip_id;
+ int ret;
+
+ ret = i2c_smbus_read_i2c_block_data(client, SILEAD_REG_ID,
+ sizeof(chip_id), (u8 *)&chip_id);
+ if (ret != sizeof(chip_id))
+ return ret == -ETIMEDOUT ? -ETIMEDOUT : -ENODEV;
+
+ switch (le32_to_cpu(chip_id)) {
+ case 0xa0820000:
+ data->touchscreen.compatible = "silead,gsl1680";
+ data->touchscreen.model = gsl1680_a082;
+ dev_info(data->dev, "Silead touchscreen ID: 0xa0820000\n");
+ return 0;
+ case 0xb4820000:
+ data->touchscreen.compatible = "silead,gsl1680";
+ data->touchscreen.model = gsl1680_b482;
+ dev_info(data->dev, "Silead touchscreen ID: 0xb4820000\n");
+ return 0;
+ default:
+ dev_warn(data->dev, "Silead? touchscreen with unknown ID: 0x%08x\n",
+ le32_to_cpu(chip_id));
+ }
+
+ return -ENODEV;
+}
+
+static int q8_hardwaremgr_probe_ektf2127(struct q8_hardwaremgr_data *data,
+ struct i2c_client *client)
+{
+ unsigned char buff[4];
+ int ret;
+
+ /* Read hello, ignore data, depends on initial power state */
+ ret = i2c_master_recv(client, buff, 4);
+ if (ret != 4)
+ return ret == -ETIMEDOUT ? -ETIMEDOUT : -ENODEV;
+
+ /* Request width */
+ buff[0] = EKTF2127_REQUEST;
+ buff[1] = EKTF2127_WIDTH;
+ buff[2] = 0x00;
+ buff[3] = 0x00;
+ ret = i2c_master_send(client, buff, 4);
+ if (ret != 4)
+ return ret == -ETIMEDOUT ? -ETIMEDOUT : -ENODEV;
+
+ msleep(20);
+
+ /* Read response */
+ ret = i2c_master_recv(client, buff, 4);
+ if (ret != 4)
+ return ret == -ETIMEDOUT ? -ETIMEDOUT : -ENODEV;
+
+ if (buff[0] == EKTF2127_RESPONSE && buff[1] == EKTF2127_WIDTH) {
+ data->touchscreen.compatible = "elan,ektf2127";
+ data->touchscreen.model = ektf2127;
+ return 0;
+ }
+
+ return -ENODEV;
+}
+
+static int q8_hardwaremgr_probe_zet6251(struct q8_hardwaremgr_data *data,
+ struct i2c_client *client)
+{
+ unsigned char buff[4];
+ int ret;
+
+ /*
+ * We only do a simple read finger data packet test, because some
+ * versions require firmware to be loaded. If no firmware is loaded
+ * the buffer will be filed with 0xff, so we ignore the contents.
+ */
+ ret = i2c_master_recv(client, buff, 24);
+ if (ret != 24)
+ return ret == -ETIMEDOUT ? -ETIMEDOUT : -ENODEV;
+
+ data->touchscreen.compatible = "zeitec,zet6251";
+ data->touchscreen.model = zet6251;
+ return 0;
+}
+
+static int q8_hardwaremgr_probe_touchscreen(struct q8_hardwaremgr_data *data,
+ struct i2c_adapter *adap)
+{
+ msleep(TOUCHSCREEN_POWER_ON_DELAY);
+
+ PROBE_CLIENT(&data->touchscreen, 0x40, q8_hardwaremgr_probe_silead);
+ PROBE_CLIENT(&data->touchscreen, 0x15, q8_hardwaremgr_probe_ektf2127);
+ PROBE_CLIENT(&data->touchscreen, 0x76, q8_hardwaremgr_probe_zet6251);
+
+ return -ENODEV;
+}
+
+static void q8_hardwaremgr_apply_gsl1680_a082_variant(
+ struct q8_hardwaremgr_data *data)
+{
+ if (touchscreen_variant != -1)
+ data->touchscreen_variant = touchscreen_variant;
+
+ switch (data->touchscreen_variant) {
+ default:
+ dev_warn(data->dev, "Error unknown touchscreen_variant %d using 0\n",
+ touchscreen_variant);
+ /* Fall through */
+ case 0:
+ data->touchscreen_width = 1024;
+ data->touchscreen_height = 600;
+ data->touchscreen_fw_name = "gsl1680-a082-q8-700.fw";
+ break;
+ case 1:
+ data->touchscreen_width = 480;
+ data->touchscreen_height = 800;
+ data->touchscreen_swap_x_y = 1;
+ data->touchscreen_fw_name = "gsl1680-a082-q8-a70.fw";
+ break;
+ }
+}
+
+static void q8_hardwaremgr_apply_gsl1680_b482_variant(
+ struct q8_hardwaremgr_data *data)
+{
+ if (touchscreen_variant != -1)
+ data->touchscreen_variant = touchscreen_variant;
+
+ switch (data->touchscreen_variant) {
+ default:
+ dev_warn(data->dev, "Error unknown touchscreen_variant %d using 0\n",
+ touchscreen_variant);
+ /* Fall through */
+ case 0:
+ data->touchscreen_width = 960;
+ data->touchscreen_height = 640;
+ data->touchscreen_fw_name = "gsl1680-b482-q8-d702.fw";
+ break;
+ case 1:
+ data->touchscreen_width = 960;
+ data->touchscreen_height = 640;
+ data->touchscreen_fw_name = "gsl1680-b482-q8-a70.fw";
+ break;
+ }
+}
+
+static void q8_hardwaremgr_issue_gsl1680_warning(
+ struct q8_hardwaremgr_data *data)
+{
+ dev_warn(data->dev, "gsl1680 touchscreen may require kernel cmdline parameters to function properly\n");
+ dev_warn(data->dev, "Try q8_hardwaremgr.touchscreen_invert_x=%d if x coordinates are inverted\n",
+ !data->touchscreen_invert_x);
+ dev_warn(data->dev, "Try q8_hardwaremgr.touchscreen_variant=%d if coordinates are all over the place\n",
+ !data->touchscreen_variant);
+
+#define show(x) \
+ dev_info(data->dev, #x " %d (%s)\n", data->x, \
+ (x == -1) ? "auto" : "user supplied")
+
+ show(touchscreen_variant);
+ show(touchscreen_width);
+ show(touchscreen_height);
+ show(touchscreen_invert_x);
+ show(touchscreen_invert_y);
+ show(touchscreen_swap_x_y);
+ dev_info(data->dev, "touchscreen_fw_name %s (%s)\n",
+ data->touchscreen_fw_name,
+ (touchscreen_fw_name == NULL) ? "auto" : "user supplied");
+#undef show
+}
+
+static void q8_hardwaremgr_apply_touchscreen(struct q8_hardwaremgr_data *data)
+{
+ struct of_changeset cset;
+ struct device_node *np;
+
+ switch (data->touchscreen.model) {
+ case touchscreen_unknown:
+ return;
+ case gsl1680_a082:
+ q8_hardwaremgr_apply_gsl1680_a082_variant(data);
+ break;
+ case gsl1680_b482:
+ q8_hardwaremgr_apply_gsl1680_b482_variant(data);
+ break;
+ case ektf2127:
+ case zet6251:
+ /* These have only 1 variant */
+ break;
+ }
+
+ if (touchscreen_width != -1)
+ data->touchscreen_width = touchscreen_width;
+
+ if (touchscreen_height != -1)
+ data->touchscreen_height = touchscreen_height;
+
+ if (touchscreen_invert_x != -1)
+ data->touchscreen_invert_x = touchscreen_invert_x;
+
+ if (touchscreen_invert_y != -1)
+ data->touchscreen_invert_y = touchscreen_invert_y;
+
+ if (touchscreen_swap_x_y != -1)
+ data->touchscreen_swap_x_y = touchscreen_swap_x_y;
+
+ if (touchscreen_fw_name)
+ data->touchscreen_fw_name = touchscreen_fw_name;
+
+ if (data->touchscreen.model == gsl1680_a082 ||
+ data->touchscreen.model == gsl1680_b482)
+ q8_hardwaremgr_issue_gsl1680_warning(data);
+
+ np = q8_hardware_mgr_apply_common(&data->touchscreen, &cset,
+ "touchscreen");
+ if (!np)
+ return;
+
+ if (data->touchscreen_width)
+ of_changeset_add_property_u32(&cset, np, "touchscreen-size-x",
+ data->touchscreen_width);
+ if (data->touchscreen_height)
+ of_changeset_add_property_u32(&cset, np, "touchscreen-size-y",
+ data->touchscreen_height);
+ if (data->touchscreen_invert_x)
+ of_changeset_add_property_bool(&cset, np,
+ "touchscreen-inverted-x");
+ if (data->touchscreen_invert_y)
+ of_changeset_add_property_bool(&cset, np,
+ "touchscreen-inverted-y");
+ if (data->touchscreen_swap_x_y)
+ of_changeset_add_property_bool(&cset, np,
+ "touchscreen-swapped-x-y");
+ if (data->touchscreen_fw_name)
+ of_changeset_add_property_string(&cset, np, "firmware-name",
+ data->touchscreen_fw_name);
+
+ of_changeset_apply(&cset);
+
+ of_node_put(np);
+}
+
+static int q8_hardwaremgr_do_probe(struct q8_hardwaremgr_data *data,
+ struct q8_hardwaremgr_device *dev,
+ const char *prefix, bus_probe_func func)
+{
+ struct device_node *np;
+ struct i2c_adapter *adap;
+ struct regulator *reg;
+ struct gpio_desc *gpio;
+ int ret = 0;
+
+ np = of_find_node_by_name(of_root, prefix);
+ if (!np) {
+ dev_err(data->dev, "Error %s node is missing\n", prefix);
+ return -EINVAL;
+ }
+
+ /*
+ * Patch the dt_node into our device since there is no device for
+ * the probed hw yet (status = disabled) .
+ */
+ data->dev->of_node = np;
+
+ ret = pinctrl_bind_pins(data->dev);
+ if (ret)
+ goto put_node;
+
+ adap = of_get_i2c_adapter_by_node(np->parent);
+ if (!adap) {
+ ret = -EPROBE_DEFER;
+ goto put_pins;
+ }
+
+ reg = regulator_get_optional(data->dev, "vddio");
+ if (IS_ERR(reg)) {
+ ret = PTR_ERR(reg);
+ if (ret == -EPROBE_DEFER)
+ goto put_adapter;
+ reg = NULL;
+ }
+
+ gpio = fwnode_get_named_gpiod(&np->fwnode, "power-gpios");
+ if (IS_ERR(gpio)) {
+ ret = PTR_ERR(gpio);
+ if (ret == -EPROBE_DEFER)
+ goto put_reg;
+ gpio = NULL;
+ }
+
+ /* First try with only the power gpio driven high */
+ if (gpio) {
+ ret = gpiod_direction_output(gpio, 1);
+ if (ret)
+ goto put_gpio;
+ }
+
+ dev_info(data->dev, "Probing %s without a regulator\n", prefix);
+ ret = func(data, adap);
+ if (ret != 0 && reg) {
+ /* Second try, also enable the regulator */
+ ret = regulator_enable(reg);
+ if (ret)
+ goto restore_gpio;
+
+ dev_info(data->dev, "Probing %s with a regulator\n", prefix);
+ ret = func(data, adap);
+
+ regulator_disable(reg);
+ } else if (reg)
+ dev->delete_regulator = true; /* Regulator not needed */
+
+ if (ret == 0)
+ dev_info(data->dev, "Found %s at 0x%02x\n",
+ dev->compatible, dev->addr);
+ else
+ ret = 0; /* Not finding a device is not an error */
+
+restore_gpio:
+ if (gpio)
+ gpiod_direction_output(gpio, 0);
+put_gpio:
+ if (gpio)
+ gpiod_put(gpio);
+put_reg:
+ if (reg)
+ regulator_put(reg);
+put_adapter:
+ i2c_put_adapter(adap);
+
+put_pins:
+ /* Undo our manual pinctrl_bind_pins() */
+ if (data->dev->pins) {
+ devm_pinctrl_put(data->dev->pins->p);
+ devm_kfree(data->dev, data->dev->pins);
+ data->dev->pins = NULL;
+ }
+
+put_node:
+ data->dev->of_node = NULL;
+ of_node_put(np);
+
+ return ret;
+}
+
+static int q8_hardwaremgr_probe(struct platform_device *pdev)
+{
+ struct q8_hardwaremgr_data *data;
+ int ret = 0;
+
+ data = kzalloc(sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ data->dev = &pdev->dev;
+ data->soc = (long)pdev->dev.platform_data;
+
+ ret = q8_hardwaremgr_do_probe(data, &data->touchscreen, "touchscreen",
+ q8_hardwaremgr_probe_touchscreen);
+ if (ret)
+ goto error;
+
+ q8_hardwaremgr_apply_touchscreen(data);
+
+error:
+ kfree(data);
+
+ return ret;
+}
+
+static struct platform_driver q8_hardwaremgr_driver = {
+ .driver = {
+ .name = "q8-hwmgr",
+ },
+ .probe = q8_hardwaremgr_probe,
+};
+
+static int __init q8_hardwaremgr_init(void)
+{
+ struct platform_device *pdev;
+ enum soc soc;
+ int ret;
+
+ if (of_machine_is_compatible("allwinner,q8-a13"))
+ soc = a13;
+ else if (of_machine_is_compatible("allwinner,q8-a23"))
+ soc = a23;
+ else if (of_machine_is_compatible("allwinner,q8-a33"))
+ soc = a33;
+ else
+ return 0;
+
+ pdev = platform_device_alloc("q8-hwmgr", 0);
+ if (!pdev)
+ return -ENOMEM;
+
+ pdev->dev.platform_data = (void *)(long)soc;
+
+ ret = platform_device_add(pdev);
+ if (ret)
+ return ret;
+
+ return platform_driver_register(&q8_hardwaremgr_driver);
+}
+
+device_initcall(q8_hardwaremgr_init);
+
+MODULE_DESCRIPTION("Allwinner q8 formfactor tablet hardware manager");
+MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>");
+MODULE_LICENSE("GPL");
--
2.9.3
^ permalink raw reply related
* [PATCH 1/6] dt: bindings: Add Allwinner Q8 tablets hardware manager bindings
From: Hans de Goede @ 2016-10-14 7:53 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20161014075337.10452-1-hdegoede@redhat.com>
Allwinnner A13 / A23 / A33 based Q8 tablets are popular cheap 7" tablets
of which a new batch is produced every few weeks. Each batch uses a
different mix of touchscreen, accelerometer and wifi peripherals.
Given that each batch is different creating a devicetree for each variant
is not desirable, work is being done on a Q8 tablet hardware manager which
auto-detects the touchscreen and accelerometer and will update the dt with
what it has found, so that a single generic dt can be used for these tablets.
This commit adds dt-bindings for this hardware manager.
Note the wifi is connected to a discoverable bus (sdio or usb) and will be
autodetected by the mmc resp. usb subsystems.
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
---
.../misc/allwinner,sunxi-q8-hardwaremgr.txt | 71 ++++++++++++++++++++++
1 file changed, 71 insertions(+)
create mode 100644 Documentation/devicetree/bindings/misc/allwinner,sunxi-q8-hardwaremgr.txt
diff --git a/Documentation/devicetree/bindings/misc/allwinner,sunxi-q8-hardwaremgr.txt b/Documentation/devicetree/bindings/misc/allwinner,sunxi-q8-hardwaremgr.txt
new file mode 100644
index 0000000..a81b258
--- /dev/null
+++ b/Documentation/devicetree/bindings/misc/allwinner,sunxi-q8-hardwaremgr.txt
@@ -0,0 +1,71 @@
+Q8 tablet hardware manager
+--------------------------
+
+Allwinnner A13 / A23 / A33 based Q8 tablets are popular cheap 7" tablets of
+which a new batch is produced every few weeks. Each batch uses a different
+mix of touchscreen, accelerometer and wifi peripherals.
+
+Given that each batch is different creating a devicetree for each variant is
+not desirable. The Q8 tablet hardware manager bindings are bindings for an os
+module which auto-detects the touchscreen and accelerometer so that a single
+generic dts can be used for these tablets.
+
+The wifi is connected to a discoverable bus and will be autodetected by the os.
+
+Required properties:
+ - toplevel / machine compatible, one of:
+ "allwinner,q8-a13"
+ "allwinner,q8-a23"
+ "allwinner,q8-a33"
+ - touchscreen node : There must be a template touchscreen node named
+ "touchscreen", this must be a child node of the
+ touchscreen i2c bus
+ - accelerometer node : There must be a template accelerometer node named
+ "accelerometer", this must be a child node of the
+ accelerometer i2c bus
+
+touchscreen node required properties:
+ - interrupt-parent : phandle pointing to the interrupt controller
+ serving the touchscreen interrupt
+ - interrupts : interrupt specification for the touchscreen interrupt
+ - power-gpios : Specification for the pin connected to the touchscreen's
+ enable / wake pin. This needs to be driven high to
+ enable the touchscreen controller
+
+touchscreen node optional properties:
+ - vddio-supply : regulator phandle for the touchscreen vddio supply
+
+accelerometer node optional properties:
+ - interrupt-parent : phandle pointing to the interrupt controller
+ serving the accelerometer interrupt
+ - interrupts : interrupt specification for the accelerometer interrupt
+
+Example:
+
+/ {
+ compatible = "allwinner,q8-a23", "allwinner,sun8i-a23";
+};
+
+&i2c0 {
+ touchscreen: touchscreen at 0 {
+ interrupt-parent = <&pio>;
+ interrupts = <1 5 IRQ_TYPE_EDGE_FALLING>; /* PB5 */
+ power-gpios = <&pio 7 1 GPIO_ACTIVE_HIGH>; /* PH1 */
+ vddio-supply = <®_ldo_io1>;
+ /*
+ * Enabled by sunxi-q8-hardwaremgr if it detects a
+ * known model touchscreen.
+ */
+ status = "disabled";
+ };
+};
+
+&i2c1 {
+ accelerometer: accelerometer at 0 {
+ /*
+ * Enabled by sunxi-q8-hardwaremgr if it detects a
+ * known model accelerometer.
+ */
+ status = "disabled";
+ };
+};
--
2.9.3
^ permalink raw reply related
* Add Allwinner Q8 tablets hardware manager
From: Hans de Goede @ 2016-10-14 7:53 UTC (permalink / raw)
To: linux-arm-kernel
Hi Rob, Mark, et al.,
Here is a first non RFC posting of my q8 tablet hardwaremanager work,
both the bindings as well as the in kernel implementation of it.
Mark, I know that we discussed this at ELCE and you clearly indicated
that according to you this does not belong in the kernel. I was a bit
surprised by this part of the discussion.
I had posted a RFC earlier and Rob had indicated that given that the q8
tablets are a special case, as my code uses actual probing rather then some
pre-arranged id mechanism with say an eeprom, that doing this in a
non-generic manner would be ok for my special case.
So at ELCE I did not have all the arguments I had to do this in the kernel
ready. I did actually make a concious choice for the kernel before starting
work on this, thinking about this more I remembered most of the arguments I
had when making my u-boot vs kernel decision. I still believe the kernel is
the right place for this. See the list of reasons below.
Regardless of where the implementation should go, please do review the
bindings (the first patch in the set), as we need those regardless of where
the actual code dealing with this is going to live.
So on to why I believe that the kernel is the best place to do this, at least
for my special use-case:
1. Configurability
Since the q8 tablets do not have any id mechanism I'm probing i2c busses to
generate i2c client dt nodes with the right address and compatible.
Some info in these nodes (e.g. touchscreen firmware-name) is not probable at
all and gets set by heuristics. This heuristics may get things wrong.
So my current implementation offers kernel cmdline options to override this.
Although having to specify kernel cmdline options is not the plug and play
experience I want to give end-users most distros do have documentation on
how to do this and doing this is relatively easy for end-users. Moving this
to the bootloader means moving to some bootloader specific config mechanism
which is going to be quite hard (and possibly dangerous) for users to use.
This alone to me is the reason to post the kernel implentation of my work
for upstream inclusion despite of Mark's objections.
2. Upgradability
Most users treat the bootloader like they treat an x86 machine BIOS/EFI,
they will never upgrade it unless they really have to. This means that it
will be very difficult to get users to actual benefit from bug-fixes /
improvements done to the probing code. Where as the kernel on boards running
e.g. Debian or Fedora gets regular updates together with the rest of the
system.
3. Infrastructure
The kernel simply has better infrastructure for doing these kind of things.
Yes we could improve the bootloader, but the kernel is also improving and
likely at a faster rate. Besides that the purpose of the bootloader is
mostly to be simple and small, load the kernel and get out of the way, not
to be a general purpose os kernel. So it will simply always have less
infrastructure and that is a good thing, otherwise we will be writing
another general purpose os which is a waste of effort.
4. This is not a new board file
Mark, at ELCE I got the feeling that your biggest worry / objection is
that this will bring back board files, but that is not the case, if you
look at the actual code it is nothing like a board-file at all. Where a
board file was instantiating device after device after device from c-code,
with maybe a few if-s in there to deal with board revisions. This code is
all about figuring out which accelerometer and touchscreen there are,
to then add nodes to the dt for this 2 devices, almost all the code is
probing, the actual dt modifying code is tiny and no direct device
instantiation is done at all.
5. This is an exception, not the rule
Yes this is introducing board (family of boards) specific c-code into the
kernel, so in a way it is reminiscent of board files. But sometimes this is
necessary, just look at all the vendor / platform specific code in the kernel
in drivers/platform/x86, or all the places where DMI strings are used to
uniquely identify a x86 board and adjust behavior.
But this really is the exception, not the rule. I've written/upstreamed a
number of drivers for q8 tablets hardware and if you look at e.g. the
silead touchscreen driver then in linux-next this is already used for 5
ARM dts files where no board specific C-code is involved at all.
So this again is very different from the board file era, where C-code
had to be used to fill device specific platform-data structs, here all
necessary info is contained in the dt-binding and there are many users
who do not need any board specific C-code in the kernel at all.
So dt is working as it should and is avoiding board specific C-code for
the majority of the cases. But sometimes hardware is not as we ideally
would like it to be; and for those *exceptions* we are sometimes going
to need C-code in the kernel, just like there is "board" specific C-code
in the x86 code.
Regards,
Hans
p.s.
Note that the patches actually implementing the q8 hardware manager depend
on Pantelis' "of: changesets" patches which are not upstream yet, so if we
can agree on doing this in the kernel then this cannot be merged until those
are merged. If we get a concensus on doing this in kernel being ok, then
I'll post a new version of those patches unless Pantelis' beats me to it.
^ permalink raw reply
* Low network throughput on i.MX28
From: Lothar Waßmann @ 2016-10-14 6:13 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <A57BCA39-0977-43C4-B22D-ED60F5E4B06D@embedded.rocks>
Hi,
On Thu, 13 Oct 2016 21:43:00 +0200 J?rg Krause wrote:
> Am 13. Oktober 2016 08:48:07 MESZ, schrieb "Lothar Wa?mann" <LW@KARO-electronics.de>:
> >On Thu, 13 Oct 2016 01:09:13 +0200 J?rg Krause wrote:
[...]
> >This is the iperf output on a TX28 with current mainline kernel
> >(4.8.0-rc5):
> >------------------------------------------------------------
> >Client connecting to 192.168.100.1, TCP port 5001
> >TCP window size: 43.8 KByte (default)
> >------------------------------------------------------------
> >[ 3] local 192.168.100.56 port 60325 connected with 192.168.100.1 port
> >5001
> >[ ID] Interval Transfer Bandwidth
> >[ 3] 0.0-10.0 sec 57.5 MBytes 48.2 Mbits/sec
> >
> >You might check your kernel DEBUG configs (especially
> >CONFIG_DEBUG_PAGEALLOC).
>
> Thanks for sharing the iperf output. What LAN transceiver does the TX28 has assembled?
>
The ethernet PHY is an SMSC LAN8710A.
Lothar Wa?mann
^ permalink raw reply
* [PATCH 2/3] arm64: hw_breakpoint: Handle inexact watchpoint addresses
From: Pratyush Anand @ 2016-10-14 3:15 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <CAJt8pk8OOTbuNhN635kRCot89eLjXPQqd2AG2qJ_t7GComtZCw@mail.gmail.com>
On Thursday 13 October 2016 10:33 PM, Pavel Labath wrote:
>> I think, its easier to go with your implementation. So, I have taken
>> > your patch and updated my perf/upstream_arm64_devel branch. May be you
>> > can give it a test for your test cases.
> I've checked out the new version of your branch, and it works great.
> I'll write a patch with additional test cases to go on top of your
> branch, as the tests there do not capture the bug I was fixing.
That would be great. We can send them all together as V2.
Thanks for your help.
~Pratyush
^ permalink raw reply
* [PATCH v8 8/8] ARM: dts: imx6q-evi: Fix onboard hub reset line
From: Peter Chen @ 2016-10-14 2:59 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1476413995-20361-1-git-send-email-peter.chen@nxp.com>
From: Joshua Clayton <stillcompiling@gmail.com>
Previously the onboard hub was made to work by treating its
reset gpio as a regulator enable.
Get rid of that kludge now that pwseq has added reset gpio support
Move pin muxing the hub reset pin into the usbh1 group
Signed-off-by: Joshua Clayton <stillcompiling@gmail.com>
Signed-off-by: Peter Chen <peter.chen@nxp.com>
---
arch/arm/boot/dts/imx6q-evi.dts | 25 +++++++------------------
1 file changed, 7 insertions(+), 18 deletions(-)
diff --git a/arch/arm/boot/dts/imx6q-evi.dts b/arch/arm/boot/dts/imx6q-evi.dts
index 4fa5601..49c6f61 100644
--- a/arch/arm/boot/dts/imx6q-evi.dts
+++ b/arch/arm/boot/dts/imx6q-evi.dts
@@ -54,18 +54,6 @@
reg = <0x10000000 0x40000000>;
};
- reg_usbh1_vbus: regulator-usbhubreset {
- compatible = "regulator-fixed";
- regulator-name = "usbh1_vbus";
- regulator-min-microvolt = <5000000>;
- regulator-max-microvolt = <5000000>;
- enable-active-high;
- startup-delay-us = <2>;
- pinctrl-names = "default";
- pinctrl-0 = <&pinctrl_usbh1_hubreset>;
- gpio = <&gpio7 12 GPIO_ACTIVE_HIGH>;
- };
-
reg_usb_otg_vbus: regulator-usbotgvbus {
compatible = "regulator-fixed";
regulator-name = "usb_otg_vbus";
@@ -204,12 +192,18 @@
};
&usbh1 {
- vbus-supply = <®_usbh1_vbus>;
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_usbh1>;
dr_mode = "host";
disable-over-current;
status = "okay";
+
+ usb2415host: hub at 1 {
+ compatible = "usb424,2513";
+ reg = <1>;
+ reset-gpios = <&gpio7 12 GPIO_ACTIVE_LOW>;
+ reset-duration-us = <3000>;
+ };
};
&usbotg {
@@ -467,11 +461,6 @@
MX6QDL_PAD_GPIO_3__USB_H1_OC 0x1b0b0
/* usbh1_b OC */
MX6QDL_PAD_GPIO_0__GPIO1_IO00 0x1b0b0
- >;
- };
-
- pinctrl_usbh1_hubreset: usbh1hubresetgrp {
- fsl,pins = <
MX6QDL_PAD_GPIO_17__GPIO7_IO12 0x1b0b0
>;
};
--
2.7.4
^ permalink raw reply related
* [PATCH v8 7/8] ARM: dts: imx6qdl-udoo.dtsi: fix onboard USB HUB property
From: Peter Chen @ 2016-10-14 2:59 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1476413995-20361-1-git-send-email-peter.chen@nxp.com>
The current dts describes USB HUB's property at USB controller's
entry, it is improper. The USB HUB should be the child node
under USB controller, and power sequence properties are under
it. Besides, using gpio pinctrl setting for USB2415's reset pin.
Tested-by: Maciej S. Szmigiero <mail@maciej.szmigiero.name>
Signed-off-by: Peter Chen <peter.chen@nxp.com>
Signed-off-by: Joshua Clayton <stillcompiling@gmail.com>
---
arch/arm/boot/dts/imx6qdl-udoo.dtsi | 26 ++++++++++++--------------
1 file changed, 12 insertions(+), 14 deletions(-)
diff --git a/arch/arm/boot/dts/imx6qdl-udoo.dtsi b/arch/arm/boot/dts/imx6qdl-udoo.dtsi
index 3bee2f9..87fe31f 100644
--- a/arch/arm/boot/dts/imx6qdl-udoo.dtsi
+++ b/arch/arm/boot/dts/imx6qdl-udoo.dtsi
@@ -9,6 +9,8 @@
*
*/
+#include <dt-bindings/gpio/gpio.h>
+
/ {
aliases {
backlight = &backlight;
@@ -58,17 +60,6 @@
#address-cells = <1>;
#size-cells = <0>;
- reg_usb_h1_vbus: regulator at 0 {
- compatible = "regulator-fixed";
- reg = <0>;
- regulator-name = "usb_h1_vbus";
- regulator-min-microvolt = <5000000>;
- regulator-max-microvolt = <5000000>;
- enable-active-high;
- startup-delay-us = <2>; /* USB2415 requires a POR of 1 us minimum */
- gpio = <&gpio7 12 0>;
- };
-
reg_panel: regulator at 1 {
compatible = "regulator-fixed";
reg = <1>;
@@ -188,7 +179,7 @@
pinctrl_usbh: usbhgrp {
fsl,pins = <
- MX6QDL_PAD_GPIO_17__GPIO7_IO12 0x80000000
+ MX6QDL_PAD_GPIO_17__GPIO7_IO12 0x1b0b0
MX6QDL_PAD_NANDF_CS2__CCM_CLKO2 0x130b0
>;
};
@@ -259,9 +250,16 @@
&usbh1 {
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_usbh>;
- vbus-supply = <®_usb_h1_vbus>;
- clocks = <&clks IMX6QDL_CLK_CKO>;
status = "okay";
+
+ usb2415: hub at 1 {
+ compatible = "usb424,2514";
+ reg = <1>;
+
+ clocks = <&clks IMX6QDL_CLK_CKO>;
+ reset-gpios = <&gpio7 12 GPIO_ACTIVE_LOW>;
+ reset-duration-us = <3000>;
+ };
};
&usdhc3 {
--
2.7.4
^ permalink raw reply related
* [PATCH v8 6/8] ARM: dts: imx6qdl: Enable usb node children with <reg>
From: Peter Chen @ 2016-10-14 2:59 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1476413995-20361-1-git-send-email-peter.chen@nxp.com>
From: Joshua Clayton <stillcompiling@gmail.com>
Give usb nodes #address and #size attributes, so that a child node
representing a permanently connected device such as an onboard hub may
be addressed with a <reg> attribute
Signed-off-by: Joshua Clayton <stillcompiling@gmail.com>
Signed-off-by: Peter Chen <peter.chen@nxp.com>
---
arch/arm/boot/dts/imx6qdl.dtsi | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/arch/arm/boot/dts/imx6qdl.dtsi b/arch/arm/boot/dts/imx6qdl.dtsi
index b13b0b2..8fc66e1 100644
--- a/arch/arm/boot/dts/imx6qdl.dtsi
+++ b/arch/arm/boot/dts/imx6qdl.dtsi
@@ -935,6 +935,8 @@
usbh1: usb at 02184200 {
compatible = "fsl,imx6q-usb", "fsl,imx27-usb";
+ #address-cells = <1>;
+ #size-cells = <0>;
reg = <0x02184200 0x200>;
interrupts = <0 40 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&clks IMX6QDL_CLK_USBOH3>;
@@ -949,6 +951,8 @@
usbh2: usb at 02184400 {
compatible = "fsl,imx6q-usb", "fsl,imx27-usb";
+ #address-cells = <1>;
+ #size-cells = <0>;
reg = <0x02184400 0x200>;
interrupts = <0 41 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&clks IMX6QDL_CLK_USBOH3>;
@@ -962,6 +966,8 @@
usbh3: usb at 02184600 {
compatible = "fsl,imx6q-usb", "fsl,imx27-usb";
+ #address-cells = <1>;
+ #size-cells = <0>;
reg = <0x02184600 0x200>;
interrupts = <0 42 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&clks IMX6QDL_CLK_USBOH3>;
--
2.7.4
^ permalink raw reply related
* [PATCH v8 5/8] usb: chipidea: let chipidea core device of_node equal's glue layer device of_node
From: Peter Chen @ 2016-10-14 2:59 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1476413995-20361-1-git-send-email-peter.chen@nxp.com>
From: Peter Chen <peter.chen@freescale.com>
At device tree, we have no device node for chipidea core,
the glue layer's node is the parent node for host and udc
device. But in related driver, the parent device is chipidea
core. So, in order to let the common driver get parent's node,
we let the core's device node equals glue layer device node.
Signed-off-by: Peter Chen <peter.chen@freescale.com>
Tested-by: Maciej S. Szmigiero <mail@maciej.szmigiero.name>
Tested-by Joshua Clayton <stillcompiling@gmail.com>
---
drivers/usb/chipidea/core.c | 27 ++++++++++++++++++++++-----
1 file changed, 22 insertions(+), 5 deletions(-)
diff --git a/drivers/usb/chipidea/core.c b/drivers/usb/chipidea/core.c
index 69426e6..6839e19 100644
--- a/drivers/usb/chipidea/core.c
+++ b/drivers/usb/chipidea/core.c
@@ -927,6 +927,16 @@ static int ci_hdrc_probe(struct platform_device *pdev)
return -ENODEV;
}
+ /*
+ * At device tree, we have no device node for chipidea core,
+ * the glue layer's node is the parent node for host and udc
+ * device. But in related driver, the parent device is chipidea
+ * core. So, in order to let the common driver get parent's node,
+ * we let the core's device node equals glue layer's node.
+ */
+ if (dev->parent && dev->parent->of_node)
+ dev->of_node = dev->parent->of_node;
+
if (ci->platdata->phy) {
ci->phy = ci->platdata->phy;
} else if (ci->platdata->usb_phy) {
@@ -937,11 +947,15 @@ static int ci_hdrc_probe(struct platform_device *pdev)
/* if both generic PHY and USB PHY layers aren't enabled */
if (PTR_ERR(ci->phy) == -ENOSYS &&
- PTR_ERR(ci->usb_phy) == -ENXIO)
- return -ENXIO;
+ PTR_ERR(ci->usb_phy) == -ENXIO) {
+ ret = -ENXIO;
+ goto clear_of_node;
+ }
- if (IS_ERR(ci->phy) && IS_ERR(ci->usb_phy))
- return -EPROBE_DEFER;
+ if (IS_ERR(ci->phy) && IS_ERR(ci->usb_phy)) {
+ ret = -EPROBE_DEFER;
+ goto clear_of_node;
+ }
if (IS_ERR(ci->phy))
ci->phy = NULL;
@@ -952,7 +966,7 @@ static int ci_hdrc_probe(struct platform_device *pdev)
ret = ci_usb_phy_init(ci);
if (ret) {
dev_err(dev, "unable to init phy: %d\n", ret);
- return ret;
+ goto clear_of_node;
}
ci->hw_bank.phys = res->start;
@@ -1058,6 +1072,8 @@ stop:
ci_role_destroy(ci);
deinit_phy:
ci_usb_phy_exit(ci);
+clear_of_node:
+ dev->of_node = NULL;
return ret;
}
@@ -1076,6 +1092,7 @@ static int ci_hdrc_remove(struct platform_device *pdev)
ci_extcon_unregister(ci);
ci_role_destroy(ci);
ci_hdrc_enter_lpm(ci, true);
+ ci->dev->of_node = NULL;
ci_usb_phy_exit(ci);
return 0;
--
2.7.4
^ permalink raw reply related
* [PATCH v8 4/8] usb: core: add power sequence handling for USB devices
From: Peter Chen @ 2016-10-14 2:59 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1476413995-20361-1-git-send-email-peter.chen@nxp.com>
Some hard-wired USB devices need to do power sequence to let the
device work normally, the typical power sequence like: enable USB
PHY clock, toggle reset pin, etc. But current Linux USB driver
lacks of such code to do it, it may cause some hard-wired USB devices
works abnormal or can't be recognized by controller at all.
In this patch, it calls power sequence library APIs to finish
the power sequence events. It will do power on sequence at hub's
probe for all devices under this hub (includes root hub).
At hub_disconnect, it will do power off sequence which is at powered
on list.
Signed-off-by: Peter Chen <peter.chen@nxp.com>
Tested-by Joshua Clayton <stillcompiling@gmail.com>
---
drivers/usb/core/hub.c | 41 ++++++++++++++++++++++++++++++++++++++---
drivers/usb/core/hub.h | 1 +
2 files changed, 39 insertions(+), 3 deletions(-)
diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c
index cbb1467..acbbf0a 100644
--- a/drivers/usb/core/hub.c
+++ b/drivers/usb/core/hub.c
@@ -26,6 +26,7 @@
#include <linux/mutex.h>
#include <linux/random.h>
#include <linux/pm_qos.h>
+#include <linux/power/pwrseq.h>
#include <asm/uaccess.h>
#include <asm/byteorder.h>
@@ -1695,6 +1696,7 @@ static void hub_disconnect(struct usb_interface *intf)
hub->error = 0;
hub_quiesce(hub, HUB_DISCONNECT);
+ of_pwrseq_off_list(&hub->pwrseq_on_list);
mutex_lock(&usb_port_peer_mutex);
/* Avoid races with recursively_mark_NOTATTACHED() */
@@ -1722,12 +1724,41 @@ static void hub_disconnect(struct usb_interface *intf)
kref_put(&hub->kref, hub_release);
}
+#ifdef CONFIG_OF
+static int hub_of_pwrseq_on(struct usb_hub *hub)
+{
+ struct device *parent;
+ struct usb_device *hdev = hub->hdev;
+ struct device_node *np;
+ int ret;
+
+ if (hdev->parent)
+ parent = &hdev->dev;
+ else
+ parent = bus_to_hcd(hdev->bus)->self.controller;
+
+ for_each_child_of_node(parent->of_node, np) {
+ ret = of_pwrseq_on_list(np, &hub->pwrseq_on_list);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+#else
+static int hub_of_pwrseq_on(struct usb_hub *hub)
+{
+ return 0;
+}
+#endif
+
static int hub_probe(struct usb_interface *intf, const struct usb_device_id *id)
{
struct usb_host_interface *desc;
struct usb_endpoint_descriptor *endpoint;
struct usb_device *hdev;
struct usb_hub *hub;
+ int ret = -ENODEV;
desc = intf->cur_altsetting;
hdev = interface_to_usbdev(intf);
@@ -1832,6 +1863,7 @@ descriptor_error:
INIT_DELAYED_WORK(&hub->leds, led_work);
INIT_DELAYED_WORK(&hub->init_work, NULL);
INIT_WORK(&hub->events, hub_event);
+ INIT_LIST_HEAD(&hub->pwrseq_on_list);
usb_get_intf(intf);
usb_get_dev(hdev);
@@ -1845,11 +1877,14 @@ descriptor_error:
if (id->driver_info & HUB_QUIRK_CHECK_PORT_AUTOSUSPEND)
hub->quirk_check_port_auto_suspend = 1;
- if (hub_configure(hub, endpoint) >= 0)
- return 0;
+ if (hub_configure(hub, endpoint) >= 0) {
+ ret = hub_of_pwrseq_on(hub);
+ if (!ret)
+ return 0;
+ }
hub_disconnect(intf);
- return -ENODEV;
+ return ret;
}
static int
diff --git a/drivers/usb/core/hub.h b/drivers/usb/core/hub.h
index 34c1a7e..cd86f91 100644
--- a/drivers/usb/core/hub.h
+++ b/drivers/usb/core/hub.h
@@ -78,6 +78,7 @@ struct usb_hub {
struct delayed_work init_work;
struct work_struct events;
struct usb_port **ports;
+ struct list_head pwrseq_on_list; /* powered pwrseq node list */
};
/**
--
2.7.4
^ permalink raw reply related
* [PATCH v8 3/8] binding-doc: usb: usb-device: add optional properties for power sequence
From: Peter Chen @ 2016-10-14 2:59 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1476413995-20361-1-git-send-email-peter.chen@nxp.com>
Add optional properties for power sequence.
Signed-off-by: Peter Chen <peter.chen@nxp.com>
Acked-by: Rob Herring <robh@kernel.org>
---
Documentation/devicetree/bindings/usb/usb-device.txt | 10 +++++++++-
1 file changed, 9 insertions(+), 1 deletion(-)
diff --git a/Documentation/devicetree/bindings/usb/usb-device.txt b/Documentation/devicetree/bindings/usb/usb-device.txt
index 1c35e7b..3661dd2 100644
--- a/Documentation/devicetree/bindings/usb/usb-device.txt
+++ b/Documentation/devicetree/bindings/usb/usb-device.txt
@@ -13,6 +13,10 @@ Required properties:
- reg: the port number which this device is connecting to, the range
is 1-31.
+Optional properties:
+power sequence properties, see
+Documentation/devicetree/bindings/power/pwrseq/pwrseq-generic.txt for detail
+
Example:
&usb1 {
@@ -21,8 +25,12 @@ Example:
#address-cells = <1>;
#size-cells = <0>;
- hub: genesys at 1 {
+ genesys: hub at 1 {
compatible = "usb5e3,608";
reg = <1>;
+
+ clocks = <&clks IMX6SX_CLK_CKO>;
+ reset-gpios = <&gpio4 5 GPIO_ACTIVE_LOW>; /* hub reset pin */
+ reset-duration-us = <10>;
};
}
--
2.7.4
^ permalink raw reply related
* [PATCH v8 2/8] power: add power sequence library
From: Peter Chen @ 2016-10-14 2:59 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1476413995-20361-1-git-send-email-peter.chen@nxp.com>
We have an well-known problem that the device needs to do some power
sequence before it can be recognized by related host, the typical
example like hard-wired mmc devices and usb devices.
This power sequence is hard to be described at device tree and handled by
related host driver, so we have created a common power sequence
library to cover this requirement. The core code has supplied
some common helpers for host driver, and individual power sequence
libraries handle kinds of power sequence for devices. The pwrseq
librares always need to allocate extra instance for compatible
string match.
pwrseq_generic is intended for general purpose of power sequence, which
handles gpios and clocks currently, and can cover other controls in
future. The host driver just needs to call of_pwrseq_on/of_pwrseq_off
if only one power sequence is needed, else call of_pwrseq_on_list
/of_pwrseq_off_list instead (eg, USB hub driver).
For new power sequence library, it can add its compatible string
to pwrseq_of_match_table, then the pwrseq core will match it with
DT's, and choose this library at runtime.
Signed-off-by: Peter Chen <peter.chen@nxp.com>
Tested-by Joshua Clayton <stillcompiling@gmail.com>
Reviewed-by: Matthias Kaehlcke <mka@chromium.org>
Tested-by: Matthias Kaehlcke <mka@chromium.org>
---
MAINTAINERS | 9 ++
drivers/power/Kconfig | 1 +
drivers/power/Makefile | 1 +
drivers/power/pwrseq/Kconfig | 19 ++++
drivers/power/pwrseq/Makefile | 2 +
drivers/power/pwrseq/core.c | 191 ++++++++++++++++++++++++++++++++++
drivers/power/pwrseq/pwrseq_generic.c | 183 ++++++++++++++++++++++++++++++++
include/linux/power/pwrseq.h | 72 +++++++++++++
8 files changed, 478 insertions(+)
create mode 100644 drivers/power/pwrseq/Kconfig
create mode 100644 drivers/power/pwrseq/Makefile
create mode 100644 drivers/power/pwrseq/core.c
create mode 100644 drivers/power/pwrseq/pwrseq_generic.c
create mode 100644 include/linux/power/pwrseq.h
diff --git a/MAINTAINERS b/MAINTAINERS
index 3bb6640..7e1fcb9 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -9384,6 +9384,15 @@ F: include/linux/pm_*
F: include/linux/powercap.h
F: drivers/powercap/
+POWER SEQUENCE LIBRARY
+M: Peter Chen <Peter.Chen@nxp.com>
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/peter.chen/usb.git
+L: linux-pm at vger.kernel.org
+S: Maintained
+F: Documentation/devicetree/bindings/power/pwrseq/
+F: drivers/power/pwrseq/
+F: include/linux/power/pwrseq.h/
+
POWER SUPPLY CLASS/SUBSYSTEM and DRIVERS
M: Sebastian Reichel <sre@kernel.org>
M: Dmitry Eremin-Solenikov <dbaryshkov@gmail.com>
diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig
index acd4a15..f6aa4fd 100644
--- a/drivers/power/Kconfig
+++ b/drivers/power/Kconfig
@@ -515,3 +515,4 @@ endif # POWER_SUPPLY
source "drivers/power/reset/Kconfig"
source "drivers/power/avs/Kconfig"
+source "drivers/power/pwrseq/Kconfig"
diff --git a/drivers/power/Makefile b/drivers/power/Makefile
index e46b75d..4ed2e12 100644
--- a/drivers/power/Makefile
+++ b/drivers/power/Makefile
@@ -74,3 +74,4 @@ obj-$(CONFIG_CHARGER_TPS65217) += tps65217_charger.o
obj-$(CONFIG_POWER_RESET) += reset/
obj-$(CONFIG_AXP288_FUEL_GAUGE) += axp288_fuel_gauge.o
obj-$(CONFIG_AXP288_CHARGER) += axp288_charger.o
+obj-$(CONFIG_POWER_SEQUENCE) += pwrseq/
diff --git a/drivers/power/pwrseq/Kconfig b/drivers/power/pwrseq/Kconfig
new file mode 100644
index 0000000..3859a67
--- /dev/null
+++ b/drivers/power/pwrseq/Kconfig
@@ -0,0 +1,19 @@
+#
+# Power Sequence library
+#
+
+config POWER_SEQUENCE
+ bool
+
+menu "Power Sequence Support"
+
+config PWRSEQ_GENERIC
+ bool "Generic power sequence control"
+ depends on OF
+ select POWER_SEQUENCE
+ help
+ It is used for drivers which needs to do power sequence
+ (eg, turn on clock, toggle reset gpio) before the related
+ devices can be found by hardware. This generic one can be
+ used for common power sequence control.
+endmenu
diff --git a/drivers/power/pwrseq/Makefile b/drivers/power/pwrseq/Makefile
new file mode 100644
index 0000000..ad82389
--- /dev/null
+++ b/drivers/power/pwrseq/Makefile
@@ -0,0 +1,2 @@
+obj-$(CONFIG_POWER_SEQUENCE) += core.o
+obj-$(CONFIG_PWRSEQ_GENERIC) += pwrseq_generic.o
diff --git a/drivers/power/pwrseq/core.c b/drivers/power/pwrseq/core.c
new file mode 100644
index 0000000..9cb1223
--- /dev/null
+++ b/drivers/power/pwrseq/core.c
@@ -0,0 +1,191 @@
+/*
+ * core.c power sequence core file
+ *
+ * Copyright (C) 2016 Freescale Semiconductor, Inc.
+ * Author: Peter Chen <peter.chen@nxp.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 of
+ * the License as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/list.h>
+#include <linux/mutex.h>
+#include <linux/of.h>
+#include <linux/slab.h>
+#include <linux/power/pwrseq.h>
+
+static DEFINE_MUTEX(pwrseq_list_mutex);
+static LIST_HEAD(pwrseq_list);
+
+int pwrseq_get(struct device_node *np, struct pwrseq *p)
+{
+ if (p && p->get)
+ return p->get(np, p);
+
+ return -ENOTSUPP;
+}
+EXPORT_SYMBOL_GPL(pwrseq_get);
+
+int pwrseq_on(struct pwrseq *p)
+{
+ if (p && p->on)
+ return p->on(p);
+
+ return -ENOTSUPP;
+}
+EXPORT_SYMBOL_GPL(pwrseq_on);
+
+void pwrseq_off(struct pwrseq *p)
+{
+ if (p && p->off)
+ p->off(p);
+}
+EXPORT_SYMBOL_GPL(pwrseq_off);
+
+void pwrseq_put(struct pwrseq *p)
+{
+ if (p && p->put)
+ p->put(p);
+}
+EXPORT_SYMBOL_GPL(pwrseq_put);
+
+int pwrseq_suspend(struct pwrseq *p)
+{
+ if (p && p->suspend)
+ return p->suspend(p);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(pwrseq_suspend);
+
+int pwrseq_resume(struct pwrseq *p)
+{
+ if (p && p->resume)
+ return p->resume(p);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(pwrseq_resume);
+
+void pwrseq_register(struct pwrseq *pwrseq)
+{
+ mutex_lock(&pwrseq_list_mutex);
+ list_add(&pwrseq->node, &pwrseq_list);
+ mutex_unlock(&pwrseq_list_mutex);
+}
+EXPORT_SYMBOL_GPL(pwrseq_register);
+
+void pwrseq_unregister(struct pwrseq *pwrseq)
+{
+ mutex_lock(&pwrseq_list_mutex);
+ list_del(&pwrseq->node);
+ mutex_unlock(&pwrseq_list_mutex);
+}
+EXPORT_SYMBOL_GPL(pwrseq_unregister);
+
+static struct pwrseq *pwrseq_find_available_instance(struct device_node *np)
+{
+ struct pwrseq *pwrseq;
+
+ list_for_each_entry(pwrseq, &pwrseq_list, node) {
+ if (pwrseq->used)
+ continue;
+
+ /* compare compatible string for pwrseq node */
+ if (of_match_node(pwrseq->pwrseq_of_match_table, np)) {
+ pwrseq->used = true;
+ return pwrseq;
+ }
+
+ /* return generic pwrseq instance */
+ if (!strcmp(pwrseq->pwrseq_of_match_table->compatible,
+ "generic")) {
+ pr_debug("using generic pwrseq instance for %s\n",
+ np->full_name);
+ pwrseq->used = true;
+ return pwrseq;
+ }
+ }
+ pr_warn("Can't find any pwrseq instances for %s\n", np->full_name);
+
+ return NULL;
+}
+
+struct pwrseq *of_pwrseq_on(struct device_node *np)
+{
+ struct pwrseq *pwrseq;
+ int ret;
+
+ pwrseq = pwrseq_find_available_instance(np);
+ if (!pwrseq)
+ return ERR_PTR(-ENONET);
+
+ ret = pwrseq_get(np, pwrseq);
+ if (ret) {
+ /* Mark current pwrseq as unused */
+ pwrseq->used = false;
+ return ERR_PTR(ret);
+ }
+
+ ret = pwrseq_on(pwrseq);
+ if (ret)
+ goto pwr_put;
+
+ return pwrseq;
+
+pwr_put:
+ pwrseq_put(pwrseq);
+ return ERR_PTR(ret);
+}
+EXPORT_SYMBOL_GPL(of_pwrseq_on);
+
+void of_pwrseq_off(struct pwrseq *pwrseq)
+{
+ pwrseq_off(pwrseq);
+ pwrseq_put(pwrseq);
+}
+EXPORT_SYMBOL_GPL(of_pwrseq_off);
+
+int of_pwrseq_on_list(struct device_node *np, struct list_head *head)
+{
+ struct pwrseq *pwrseq;
+ struct pwrseq_list_per_dev *pwrseq_list_node;
+
+ pwrseq = of_pwrseq_on(np);
+ if (IS_ERR(pwrseq))
+ return PTR_ERR(pwrseq);
+
+ pwrseq_list_node = kzalloc(sizeof(*pwrseq_list_node), GFP_KERNEL);
+ if (!pwrseq_list_node) {
+ of_pwrseq_off(pwrseq);
+ return -ENOMEM;
+ }
+ pwrseq_list_node->pwrseq = pwrseq;
+ list_add(&pwrseq_list_node->list, head);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(of_pwrseq_on_list);
+
+void of_pwrseq_off_list(struct list_head *head)
+{
+ struct pwrseq *pwrseq;
+ struct pwrseq_list_per_dev *pwrseq_list_node, *tmp_node;
+
+ list_for_each_entry_safe(pwrseq_list_node, tmp_node, head, list) {
+ pwrseq = pwrseq_list_node->pwrseq;
+ of_pwrseq_off(pwrseq);
+ list_del(&pwrseq_list_node->list);
+ kfree(pwrseq_list_node);
+ }
+}
+EXPORT_SYMBOL_GPL(of_pwrseq_off_list);
diff --git a/drivers/power/pwrseq/pwrseq_generic.c b/drivers/power/pwrseq/pwrseq_generic.c
new file mode 100644
index 0000000..d7a77f2
--- /dev/null
+++ b/drivers/power/pwrseq/pwrseq_generic.c
@@ -0,0 +1,183 @@
+/*
+ * pwrseq_generic.c Generic power sequence handling
+ *
+ * Copyright (C) 2016 Freescale Semiconductor, Inc.
+ * Author: Peter Chen <peter.chen@nxp.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 of
+ * the License as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/gpio.h>
+#include <linux/gpio/consumer.h>
+#include <linux/of.h>
+#include <linux/of_gpio.h>
+#include <linux/slab.h>
+
+#include <linux/power/pwrseq.h>
+
+struct pwrseq_generic {
+ struct pwrseq pwrseq;
+ struct gpio_desc *gpiod_reset;
+ struct clk *clks[PWRSEQ_MAX_CLKS];
+ u32 duration_us;
+};
+
+#define to_generic_pwrseq(p) container_of(p, struct pwrseq_generic, pwrseq)
+
+static int pwrseq_generic_alloc_instance(void);
+static const struct of_device_id generic_id_table[] = {
+ { .compatible = "generic",},
+ { /* sentinel */ }
+};
+
+static void pwrseq_generic_put(struct pwrseq *pwrseq)
+{
+ struct pwrseq_generic *pwrseq_gen = to_generic_pwrseq(pwrseq);
+ int clk;
+
+ if (pwrseq_gen->gpiod_reset)
+ gpiod_put(pwrseq_gen->gpiod_reset);
+
+ for (clk = 0; clk < PWRSEQ_MAX_CLKS; clk++)
+ clk_put(pwrseq_gen->clks[clk]);
+
+ pwrseq_unregister(&pwrseq_gen->pwrseq);
+ kfree(pwrseq_gen);
+}
+
+static void pwrseq_generic_off(struct pwrseq *pwrseq)
+{
+ struct pwrseq_generic *pwrseq_gen = to_generic_pwrseq(pwrseq);
+ int clk;
+
+ for (clk = PWRSEQ_MAX_CLKS - 1; clk >= 0; clk--)
+ clk_disable_unprepare(pwrseq_gen->clks[clk]);
+}
+
+static int pwrseq_generic_on(struct pwrseq *pwrseq)
+{
+ struct pwrseq_generic *pwrseq_gen = to_generic_pwrseq(pwrseq);
+ int clk, ret = 0;
+ struct gpio_desc *gpiod_reset = pwrseq_gen->gpiod_reset;
+
+ for (clk = 0; clk < PWRSEQ_MAX_CLKS && pwrseq_gen->clks[clk]; clk++) {
+ ret = clk_prepare_enable(pwrseq_gen->clks[clk]);
+ if (ret) {
+ pr_err("Can't enable clock, ret=%d\n", ret);
+ goto err_disable_clks;
+ }
+ }
+
+ if (gpiod_reset) {
+ u32 duration_us = pwrseq_gen->duration_us;
+
+ if (duration_us <= 10)
+ udelay(10);
+ else
+ usleep_range(duration_us, duration_us + 100);
+ gpiod_set_value(gpiod_reset, 0);
+ }
+
+ return ret;
+
+err_disable_clks:
+ while (--clk >= 0)
+ clk_disable_unprepare(pwrseq_gen->clks[clk]);
+
+ return ret;
+}
+
+static int pwrseq_generic_get(struct device_node *np, struct pwrseq *pwrseq)
+{
+ struct pwrseq_generic *pwrseq_gen = to_generic_pwrseq(pwrseq);
+ enum of_gpio_flags flags;
+ int reset_gpio, clk, ret = 0;
+
+ for (clk = 0; clk < PWRSEQ_MAX_CLKS; clk++) {
+ pwrseq_gen->clks[clk] = of_clk_get(np, clk);
+ if (IS_ERR(pwrseq_gen->clks[clk])) {
+ ret = PTR_ERR(pwrseq_gen->clks[clk]);
+ if (ret != -ENOENT)
+ goto err_put_clks;
+ pwrseq_gen->clks[clk] = NULL;
+ break;
+ }
+ }
+
+ reset_gpio = of_get_named_gpio_flags(np, "reset-gpios", 0, &flags);
+ if (gpio_is_valid(reset_gpio)) {
+ unsigned long gpio_flags;
+
+ if (flags & OF_GPIO_ACTIVE_LOW)
+ gpio_flags = GPIOF_ACTIVE_LOW | GPIOF_OUT_INIT_LOW;
+ else
+ gpio_flags = GPIOF_OUT_INIT_HIGH;
+
+ ret = gpio_request_one(reset_gpio, gpio_flags,
+ "pwrseq-reset-gpios");
+ if (ret)
+ goto err_put_clks;
+
+ pwrseq_gen->gpiod_reset = gpio_to_desc(reset_gpio);
+ of_property_read_u32(np, "reset-duration-us",
+ &pwrseq_gen->duration_us);
+ } else if (reset_gpio == -ENOENT) {
+ ; /* no such gpio */
+ } else {
+ ret = reset_gpio;
+ pr_err("Failed to get reset gpio on %s, err = %d\n",
+ np->full_name, reset_gpio);
+ goto err_put_clks;
+ }
+
+ /* allocate new one for later pwrseq instance request */
+ ret = pwrseq_generic_alloc_instance();
+ if (ret)
+ goto err_put_gpio;
+
+ return 0;
+
+err_put_gpio:
+ if (pwrseq_gen->gpiod_reset)
+ gpiod_put(pwrseq_gen->gpiod_reset);
+err_put_clks:
+ while (--clk >= 0)
+ clk_put(pwrseq_gen->clks[clk]);
+ return ret;
+}
+
+static int pwrseq_generic_alloc_instance(void)
+{
+ struct pwrseq_generic *pwrseq_gen;
+
+ pwrseq_gen = kzalloc(sizeof(*pwrseq_gen), GFP_KERNEL);
+ if (!pwrseq_gen)
+ return -ENOMEM;
+
+ pwrseq_gen->pwrseq.pwrseq_of_match_table = generic_id_table;
+ pwrseq_gen->pwrseq.get = pwrseq_generic_get;
+ pwrseq_gen->pwrseq.on = pwrseq_generic_on;
+ pwrseq_gen->pwrseq.off = pwrseq_generic_off;
+ pwrseq_gen->pwrseq.put = pwrseq_generic_put;
+
+ pwrseq_register(&pwrseq_gen->pwrseq);
+ return 0;
+}
+
+static int __init pwrseq_generic_register(void)
+{
+ return pwrseq_generic_alloc_instance();
+}
+postcore_initcall(pwrseq_generic_register)
diff --git a/include/linux/power/pwrseq.h b/include/linux/power/pwrseq.h
new file mode 100644
index 0000000..4f37275
--- /dev/null
+++ b/include/linux/power/pwrseq.h
@@ -0,0 +1,72 @@
+#ifndef __LINUX_PWRSEQ_H
+#define __LINUX_PWRSEQ_H
+
+#include <linux/of.h>
+
+#define PWRSEQ_MAX_CLKS 3
+
+struct pwrseq {
+ const struct of_device_id *pwrseq_of_match_table;
+ struct list_head node;
+ int (*get)(struct device_node *np, struct pwrseq *p);
+ int (*on)(struct pwrseq *p);
+ void (*off)(struct pwrseq *p);
+ void (*put)(struct pwrseq *p);
+ int (*suspend)(struct pwrseq *p);
+ int (*resume)(struct pwrseq *p);
+ bool used;
+};
+
+/* used for power sequence instance list in one driver */
+struct pwrseq_list_per_dev {
+ struct pwrseq *pwrseq;
+ struct list_head list;
+};
+
+#if IS_ENABLED(CONFIG_POWER_SEQUENCE)
+int pwrseq_get(struct device_node *np, struct pwrseq *p);
+int pwrseq_on(struct pwrseq *p);
+void pwrseq_off(struct pwrseq *p);
+void pwrseq_put(struct pwrseq *p);
+int pwrseq_suspend(struct pwrseq *p);
+int pwrseq_resume(struct pwrseq *p);
+void pwrseq_register(struct pwrseq *pwrseq);
+void pwrseq_unregister(struct pwrseq *pwrseq);
+struct pwrseq *of_pwrseq_on(struct device_node *np);
+void of_pwrseq_off(struct pwrseq *pwrseq);
+int of_pwrseq_on_list(struct device_node *np, struct list_head *head);
+void of_pwrseq_off_list(struct list_head *head);
+#else
+static inline int pwrseq_get(struct device_node *np, struct pwrseq *p)
+{
+ return 0;
+}
+static inline int pwrseq_on(struct pwrseq *p)
+{
+ return 0;
+}
+static inline void pwrseq_off(struct pwrseq *p) {}
+static inline void pwrseq_put(struct pwrseq *p) {}
+static inline int pwrseq_suspend(struct pwrseq *p)
+{
+ return 0;
+}
+static inline int pwrseq_resume(struct pwrseq *p)
+{
+ return 0;
+}
+static inline void pwrseq_register(struct pwrseq *pwrseq) {}
+static inline void pwrseq_unregister(struct pwrseq *pwrseq) {}
+static inline struct pwrseq *of_pwrseq_on(struct device_node *np)
+{
+ return NULL;
+}
+void of_pwrseq_off(struct pwrseq *pwrseq) {}
+int of_pwrseq_on_list(struct device_node *np, struct list_head *head)
+{
+ return 0;
+}
+void of_pwrseq_off_list(struct list_head *head) {}
+#endif /* CONFIG_POWER_SEQUENCE */
+
+#endif /* __LINUX_PWRSEQ_H */
--
2.7.4
^ permalink raw reply related
* [PATCH v8 1/8] binding-doc: power: pwrseq-generic: add binding doc for generic power sequence library
From: Peter Chen @ 2016-10-14 2:59 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1476413995-20361-1-git-send-email-peter.chen@nxp.com>
Add binding doc for generic power sequence library.
Signed-off-by: Peter Chen <peter.chen@nxp.com>
Acked-by: Philipp Zabel <p.zabel@pengutronix.de>
Acked-by: Rob Herring <robh@kernel.org>
---
.../bindings/power/pwrseq/pwrseq-generic.txt | 48 ++++++++++++++++++++++
1 file changed, 48 insertions(+)
create mode 100644 Documentation/devicetree/bindings/power/pwrseq/pwrseq-generic.txt
diff --git a/Documentation/devicetree/bindings/power/pwrseq/pwrseq-generic.txt b/Documentation/devicetree/bindings/power/pwrseq/pwrseq-generic.txt
new file mode 100644
index 0000000..ebf0d47
--- /dev/null
+++ b/Documentation/devicetree/bindings/power/pwrseq/pwrseq-generic.txt
@@ -0,0 +1,48 @@
+The generic power sequence library
+
+Some hard-wired devices (eg USB/MMC) need to do power sequence before
+the device can be enumerated on the bus, the typical power sequence
+like: enable USB PHY clock, toggle reset pin, etc. But current
+Linux device driver lacks of such code to do it, it may cause some
+hard-wired devices works abnormal or can't be recognized by
+controller at all. The power sequence will be done before this device
+can be found at the bus.
+
+The power sequence properties is under the device node.
+
+Optional properties:
+- clocks: the input clocks for device.
+- reset-gpios: Should specify the GPIO for reset.
+- reset-duration-us: the duration in microsecond for assert reset signal.
+
+Below is the example of USB power sequence properties on USB device
+nodes which have two level USB hubs.
+
+&usbotg1 {
+ vbus-supply = <®_usb_otg1_vbus>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_usb_otg1_id>;
+ status = "okay";
+
+ #address-cells = <1>;
+ #size-cells = <0>;
+ genesys: hub at 1 {
+ compatible = "usb5e3,608";
+ reg = <1>;
+
+ clocks = <&clks IMX6SX_CLK_CKO>;
+ reset-gpios = <&gpio4 5 GPIO_ACTIVE_LOW>; /* hub reset pin */
+ reset-duration-us = <10>;
+
+ #address-cells = <1>;
+ #size-cells = <0>;
+ asix: ethernet at 1 {
+ compatible = "usbb95,1708";
+ reg = <1>;
+
+ clocks = <&clks IMX6SX_CLK_IPG>;
+ reset-gpios = <&gpio4 6 GPIO_ACTIVE_LOW>; /* ethernet_rst */
+ reset-duration-us = <15>;
+ };
+ };
+};
--
2.7.4
^ permalink raw reply related
* [PATCH v8 0/8] power: add power sequence library
From: Peter Chen @ 2016-10-14 2:59 UTC (permalink / raw)
To: linux-arm-kernel
Hi all,
This is a follow-up for my last power sequence framework patch set [1].
According to Rob Herring and Ulf Hansson's comments[2]. The kinds of
power sequence instances will be added at postcore_initcall, the match
criteria is compatible string first, if the compatible string is not
matched between dts and library, it will try to use generic power sequence.
The host driver just needs to call of_pwrseq_on/of_pwrseq_off
if only one power sequence instance is needed, for more power sequences
are used, using of_pwrseq_on_list/of_pwrseq_off_list instead (eg, USB hub driver).
In future, if there are special power sequence requirements, the special
power sequence library can be created.
This patch set is tested on i.mx6 sabresx evk using a dts change, I use
two hot-plug devices to simulate this use case, the related binding
change is updated at patch [1/6], The udoo board changes were tested
using my last power sequence patch set.[3]
Except for hard-wired MMC and USB devices, I find the USB ULPI PHY also
need to power on itself before it can be found by ULPI bus.
[1] http://www.spinics.net/lists/linux-usb/msg142755.html
[2] http://www.spinics.net/lists/linux-usb/msg143106.html
[3] http://www.spinics.net/lists/linux-usb/msg142815.html
Changes for v8:
- Allocate one extra pwrseq instance if pwrseq_get has succeed, it can avoid
preallocate instances problem which the number of instance is decided at
compile time, thanks for Heiko Stuebner's suggestion [Patch 2/8]
- Delete pwrseq_compatible_sample.c which is the demo purpose to show compatible
match method. [Patch 2/8]
- Add Maciej S. Szmigiero's tested-by. [Patch 7/8]
Changes for v7:
- Create kinds of power sequence instance at postcore_initcall, and match
the instance with node using compatible string, the beneit of this is
the host driver doesn't need to consider which pwrseq instance needs
to be used, and pwrseq core will match it, however, it eats some memories
if less power sequence instances are used. [Patch 2/8]
- Add pwrseq_compatible_sample.c to test match pwrseq using device_id. [Patch 2/8]
- Fix the comments Vaibhav Hiremath adds for error path for clock and do not
use device_node for parameters at pwrseq_on. [Patch 2/8]
- Simplify the caller to use power sequence, follows Alan's commnets [Patch 4/8]
- Tested three pwrseq instances together using both specific compatible string and
generic libraries.
Changes for v6:
- Add Matthias Kaehlcke's Reviewed-by and Tested-by. (patch [2/6])
- Change chipidea core of_node assignment for coming user. (patch [5/6])
- Applies Joshua Clayton's three dts changes for two boards,
the USB device's reg has only #address-cells, but without #size-cells.
Changes for v5:
- Delete pwrseq_register/pwrseq_unregister, which is useless currently
- Fix the linker error when the pwrseq user is compiled as module
Changes for v4:
- Create the patch on next-20160722
- Fix the of_node is not NULL after chipidea driver is unbinded [Patch 5/6]
- Using more friendly wait method for reset gpio [Patch 2/6]
- Support multiple input clocks [Patch 2/6]
- Add Rob Herring's ack for DT changes
- Add Joshua Clayton's Tested-by
Changes for v3:
- Delete "power-sequence" property at binding-doc, and change related code
at both library and user code.
- Change binding-doc example node name with Rob's comments
- of_get_named_gpio_flags only gets the gpio, but without setting gpio flags,
add additional code request gpio with proper gpio flags
- Add Philipp Zabel's Ack and MAINTAINER's entry
Changes for v2:
- Delete "pwrseq" prefix and clock-names for properties at dt binding
- Should use structure not but its pointer for kzalloc
- Since chipidea core has no of_node, let core's of_node equals glue
layer's at core's probe
Joshua Clayton (2):
ARM: dts: imx6qdl: Enable usb node children with <reg>
ARM: dts: imx6q-evi: Fix onboard hub reset line
Peter Chen (6):
binding-doc: power: pwrseq-generic: add binding doc for generic power
sequence library
power: add power sequence library
binding-doc: usb: usb-device: add optional properties for power
sequence
usb: core: add power sequence handling for USB devices
usb: chipidea: let chipidea core device of_node equal's glue layer
device of_node
ARM: dts: imx6qdl-udoo.dtsi: fix onboard USB HUB property
.../bindings/power/pwrseq/pwrseq-generic.txt | 48 ++++++
.../devicetree/bindings/usb/usb-device.txt | 10 +-
MAINTAINERS | 9 +
arch/arm/boot/dts/imx6q-evi.dts | 25 +--
arch/arm/boot/dts/imx6qdl-udoo.dtsi | 26 ++-
arch/arm/boot/dts/imx6qdl.dtsi | 6 +
drivers/power/Kconfig | 1 +
drivers/power/Makefile | 1 +
drivers/power/pwrseq/Kconfig | 19 ++
drivers/power/pwrseq/Makefile | 2 +
drivers/power/pwrseq/core.c | 191 +++++++++++++++++++++
drivers/power/pwrseq/pwrseq_generic.c | 183 ++++++++++++++++++++
drivers/usb/chipidea/core.c | 27 ++-
drivers/usb/core/hub.c | 41 ++++-
drivers/usb/core/hub.h | 1 +
include/linux/power/pwrseq.h | 72 ++++++++
16 files changed, 621 insertions(+), 41 deletions(-)
create mode 100644 Documentation/devicetree/bindings/power/pwrseq/pwrseq-generic.txt
create mode 100644 drivers/power/pwrseq/Kconfig
create mode 100644 drivers/power/pwrseq/Makefile
create mode 100644 drivers/power/pwrseq/core.c
create mode 100644 drivers/power/pwrseq/pwrseq_generic.c
create mode 100644 include/linux/power/pwrseq.h
--
2.7.4
^ permalink raw reply
* [PATCH 2/2] arm64: dts: add msm8996 reboot mode support
From: Xiaogang Cui @ 2016-10-14 2:09 UTC (permalink / raw)
To: linux-arm-kernel
Add reboot mode entry for MSM8996 platform.
Signed-off-by: Xiaogang Cui <xiaogang@codeaurora.org>
---
arch/arm64/boot/dts/qcom/pm8994.dtsi | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/arch/arm64/boot/dts/qcom/pm8994.dtsi b/arch/arm64/boot/dts/qcom/pm8994.dtsi
index 1222d2e..c9b14ab 100644
--- a/arch/arm64/boot/dts/qcom/pm8994.dtsi
+++ b/arch/arm64/boot/dts/qcom/pm8994.dtsi
@@ -51,6 +51,14 @@
<0 0xa6 0 IRQ_TYPE_NONE>,
<0 0xa7 0 IRQ_TYPE_NONE>;
};
+
+ qcom,reboot-mode at 88f {
+ compatible = "qcom,reboot-mode";
+ offset = <0x88f>;
+ mode-normal = <0x00>;
+ mode-recovery = <0x04>;
+ mode-bootloader = <0x08>;
+ };
};
pmic at 1 {
--
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project
^ permalink raw reply related
* [PATCH v2] perf: xgene: Remove bogus IS_ERR() check
From: Mark Rutland @ 2016-10-14 0:34 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20161013181837.GQ19539@ZenIV.linux.org.uk>
On Thu, Oct 13, 2016 at 07:18:37PM +0100, Al Viro wrote:
> On Thu, Oct 13, 2016 at 11:09:16AM -0700, Tai Nguyen wrote:
> > In acpi_get_pmu_hw_inf we pass the address of a local variable to IS_ERR(),
> > which doesn't make sense, as the pointer must be a real, valid pointer.
> > This doesn't cause a functional problem, as IS_ERR() will evaluate as
> > false, but the check is bogus and causes static checkers to complain.
>
> ... unless the test is actually a misspelled IS_ERR(res) and the current
> code is broken by effectively skipping it.
Sure.
In this case, res is a struct resource, so IS_ERR(res) is also bogus.
None of the pointer fields in struct resource are ever set to an ERR_PTR value,
so nothing in res is worth checking. Nothing else in the function prior to this
would be an ERR_PTR value either.
I believe this case was copy-paste and a thinko. There's some other error
handling in the file that does validly have to handle an ERR_PTR value.
Thanks,
Mark.
^ permalink raw reply
* [RFC] arm64: Enforce observed order for spinlock and data
From: Mark Rutland @ 2016-10-14 0:24 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <f35bbb7a559933abfc559548e860d641@codeaurora.org>
On Thu, Oct 13, 2016 at 04:00:47PM -0400, bdegraaf at codeaurora.org wrote:
> On 2016-10-13 07:02, Will Deacon wrote:
> >Brent,
> >
> >On Wed, Oct 12, 2016 at 04:01:06PM -0400, bdegraaf at codeaurora.org wrote:
> >
> >Everything from this point down needs clarification.
> >
> >>All arm64 lockref accesses that occur without taking the spinlock must
> >>behave like true atomics, ensuring successive operations are all done
> >>sequentially.
> >
> >What is a "true atomic"? What do you mean by "successive"? What do you
> >mean by "done sequentially"?
>
> Despite the use case in dentry, lockref itself is a generic locking API, and
> any problems I describe here are with the generic API itself, not necessarily
> the dentry use case. I'm not patching dentry--I'm fixing lockref.
Having spent the best part of a week looking at this myself, my view is that if
there is any problem it's simply that the semantics of lockref are unclear; we
can fix that by clarifying the imprecise wording in the lockref documentation
(i.e. the comment block in <linux/lockref.h>).
As far as I can tell, the only fundamental requirement is that an agent holding
the lock won't see the count change under its feet. Ordering requirements for
agents performing updates without holding the lock were never strictly defined,
and the de-facto ordering requirements of existing users (e.g. none in the case
of the dentry cache) appear to be met.
[...]
> At the time lockref was introduced, The Linux Foundation gave a presentation
> at LinuxCon 2014 that can be found at the following link:
>
> https://events.linuxfoundation.org/sites/events/files/slides/linuxcon-2014-locking-final.pdf
>
> On page 46, it outlines the lockref API. The first lines of the slide give
> the
> relevant details.
>
> Lockref
> ? *Generic* mechanism to *atomically* update a reference count that is
> protected by a spinlock without actually acquiring the spinlock itself.
... which is exactly what lockref does today. The count is updated atomically
(i.e. there are no intervening stores between the load and store to the count)
it's just that this atomic update has no enforced ordering against other memory
accesses.
This is a generically useful primitive, as-is.
> >Again, why is this a problem? It's exactly the same as if you did:
> >
> > spin_lock(lock);
> > inc_ref_cnt();
> > spin_unlock(lock);
> >
> >Accesses outside of the critical section can still be reordered. Big deal.
>
> Since the current code resembles but actually has *fewer* ordering effects
> compared to the example used by your atomic.h commit, even though A->B->C is
> in program order, it could access out of order according to your own commit
> text on commit 8e86f0b409a44193f1587e87b69c5dcf8f65be67.
This case is not comparable. The atomic_* API has a documented requirement that
the atomic operations in question operate as full barriers, as is called out in
the commit message. Lockref has no such documented requirement.
[...]
> Again, this is a generic API, not an API married to dentry. If it were for
> dentry's sole use, it should not be accessible outside of the dentry code.
> While the cmpxchg64_relaxed case may be OK for dentry, it is not OK for the
> generic case.
Again, you are assuming a specific set of semantics that lockref is not
documented as having (and which contemporary code does not require).
If you have a use-case for which you want stronger semantics, that is a
different story entirely.
Thanks,
Mark.
^ permalink raw reply
* [PATCH V3 06/10] acpi: apei: panic OS with fatal error status block
From: Baicar, Tyler @ 2016-10-13 23:34 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <bf911628-71be-0dca-f1c7-c12e681bd37f@arm.com>
Hello Suzuki,
On 10/13/2016 7:00 AM, Suzuki K Poulose wrote:
> On 07/10/16 22:31, Tyler Baicar wrote:
>> From: "Jonathan (Zhixiong) Zhang" <zjzhang@codeaurora.org>
>>
>> Even if an error status block's severity is fatal, the kernel does not
>> honor the severity level and panic.
>>
>> With the firmware first model, the platform could inform the OS about a
>> fatal hardware error through the non-NMI GHES notification type. The OS
>> should panic when a hardware error record is received with this
>> severity.
>>
>> Call panic() after CPER data in error status block is printed if
>> severity is fatal, before each error section is handled.
>>
>> Signed-off-by: Jonathan (Zhixiong) Zhang <zjzhang@codeaurora.org>
>> ---
>> drivers/acpi/apei/ghes.c | 10 ++++++++--
>> 1 file changed, 8 insertions(+), 2 deletions(-)
>>
>> diff --git a/drivers/acpi/apei/ghes.c b/drivers/acpi/apei/ghes.c
>> index 28d5a09..36894c8 100644
>> --- a/drivers/acpi/apei/ghes.c
>> +++ b/drivers/acpi/apei/ghes.c
>> @@ -141,6 +141,8 @@ static unsigned long ghes_estatus_pool_size_request;
>> static struct ghes_estatus_cache
>> *ghes_estatus_caches[GHES_ESTATUS_CACHES_SIZE];
>> static atomic_t ghes_estatus_cache_alloced;
>>
>> +static int ghes_panic_timeout __read_mostly = 30;
>> +
>> static int ghes_ioremap_init(void)
>> {
>> ghes_ioremap_area = __get_vm_area(PAGE_SIZE * GHES_IOREMAP_PAGES,
>> @@ -715,6 +717,12 @@ static int ghes_proc(struct ghes *ghes)
>> if (ghes_print_estatus(NULL, ghes->generic, ghes->estatus))
>> ghes_estatus_cache_add(ghes->generic, ghes->estatus);
>> }
>> + if (ghes_severity(ghes->estatus->error_severity) >=
>> GHES_SEV_PANIC) {
>> + if (panic_timeout == 0)
>> + panic_timeout = ghes_panic_timeout;
>> + panic("Fatal hardware error!");
>
> I think there is a chance that we might miss the o/p of
> ghes_print_estatus() as we use
> no pfx, and it could default to the normal loglevel and would never
> get printed
> if panic() is encountered before it. On the other hand, there is
> already a
> __ghes_panic() which does similar stuff. Is there a way we could reuse
> (may be even parts of) it ? Or at least use KERN_EMERG for the
> ghes_print_estatus(),
> if the severity could result in panic() ?
__ghes_panic() does additional handling which we do not want to do here.
I could make the following a helper function so it is not duplicated though:
if (panic_timeout == 0)
panic_timeout = ghes_panic_timeout;
panic("Fatal hardware error!");
The pfx is actually being calculated already in __ghes_print_estatus():
if (pfx == NULL) {
if (ghes_severity(estatus->error_severity) <=
GHES_SEV_CORRECTED)
pfx = KERN_WARNING;
else
pfx = KERN_ERR;
}
From ghes.h:
enum {
GHES_SEV_NO = 0x0,
GHES_SEV_CORRECTED = 0x1,
GHES_SEV_RECOVERABLE = 0x2,
GHES_SEV_PANIC = 0x3,
};
This will make the pfx KERN_ERR for the case of a panic.
Thanks,
Tyler
>
> Cheers
> Suzuki
>
--
Qualcomm Datacenter Technologies, Inc. as an affiliate of Qualcomm Technologies, Inc.
Qualcomm Technologies, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project.
^ permalink raw reply
* aarch64 ACPI boot regressed by commit 7ba5f605f3a0 ("arm64/numa: remove the limitation that cpu0 must bind to node0")
From: Laszlo Ersek @ 2016-10-13 22:50 UTC (permalink / raw)
To: linux-arm-kernel
Hi,
the following regression is experienced in aarch64 qemu/KVM virtual
machines, using the ArmVirtQemu virtual UEFI firmware platform built
from edk2 (EFI Development Kit II).
(1) When booting current master (b67be92feb48) or the bisected first bad
commit (7ba5f605f3a0) with DT enabled, everything works fine.
(2) When booting the above two commits with DT disabled -- meaning:
either the firmware provides ACPI only and no DT, or "acpi=force" is
passed on the kernel command line --, the boot breaks with one of
the following symptoms:
(2a) no messages are printed after
> EFI stub: Booting Linux Kernel...
> ConvertPages: Incompatible memory types
> EFI stub: Using DTB from configuration table
> EFI stub: Exiting boot services and installing virtual address map...
and the kernel seems to be spinning in an infinite loop (no
messages despite "earlycon" and friends),
(2b) or the following crash dump is printed:
> EFI stub: Booting Linux Kernel...
> ConvertPages: Incompatible memory types
> EFI stub: Using DTB from configuration table
> EFI stub: Exiting boot services and installing virtual address map...
> Booting Linux on physical CPU 0x0
> Linux version 4.8.0-rc3+ (root at aarch64-vgpu-1) (gcc version 6.2.1 20160916 (Red Hat 6.2.1-2) (GCC) ) #19 SMP Thu Oct 13 22:30:27 CEST 2016
> Boot CPU: AArch64 Processor [500f0000]
> earlycon: pl11 at MMIO 0x0000000009000000 (options '')
> bootconsole [pl11] enabled
> debug: ignoring loglevel setting.
> efi: Getting EFI parameters from FDT:
> efi: EFI v2.60 by EDK II
> efi: SMBIOS 3.0=0x23bdb0000 ACPI 2.0=0x2386d0000 MEMATTR=0x23a665018
> cma: Reserved 512 MiB at 0x00000000c0000000
> ACPI: Early table checksum verification disabled
> ACPI: RSDP 0x00000002386D0000 000024 (v02 BOCHS )
> ACPI: XSDT 0x00000002386C0000 00004C (v01 BOCHS BXPCFACP 00000001 01000013)
> ACPI: FACP 0x0000000238410000 00010C (v05 BOCHS BXPCFACP 00000001 BXPC 00000001)
> ACPI: DSDT 0x0000000238420000 001159 (v02 BOCHS BXPCDSDT 00000001 BXPC 00000001)
> ACPI: APIC 0x0000000238400000 0002BC (v03 BOCHS BXPCAPIC 00000001 BXPC 00000001)
> ACPI: GTDT 0x00000002383F0000 000060 (v02 BOCHS BXPCGTDT 00000001 BXPC 00000001)
> ACPI: MCFG 0x00000002383E0000 00003C (v01 BOCHS BXPCMCFG 00000001 BXPC 00000001)
> ACPI: SPCR 0x00000002383D0000 000050 (v02 BOCHS BXPCSPCR 00000001 BXPC 00000001)
> ACPI: NUMA: Failed to initialise from firmware
> NUMA: Faking a node at [mem 0x0000000000000000-0x000000023fffffff]
> NUMA: Adding memblock [0x40000000 - 0xfffeffff] on node 0
> NUMA: Adding memblock [0xffff0000 - 0xffffffff] on node 0
> NUMA: Adding memblock [0x100000000 - 0x2383cffff] on node 0
> NUMA: Adding memblock [0x2383d0000 - 0x23874ffff] on node 0
> NUMA: Adding memblock [0x238750000 - 0x23bc1ffff] on node 0
> NUMA: Adding memblock [0x23bc20000 - 0x23bffffff] on node 0
> NUMA: Adding memblock [0x23c000000 - 0x23fffffff] on node 0
> NUMA: Initmem setup node 0 [mem 0x40000000-0x23fffffff]
> NUMA: NODE_DATA [mem 0x23fff2580-0x23fffffff]
> Zone ranges:
> DMA [mem 0x0000000040000000-0x00000000ffffffff]
> Normal [mem 0x0000000100000000-0x000000023fffffff]
> Movable zone start for each node
> Early memory node ranges
> node 0: [mem 0x0000000040000000-0x00000000fffeffff]
> node 0: [mem 0x00000000ffff0000-0x00000000ffffffff]
> node 0: [mem 0x0000000100000000-0x00000002383cffff]
> node 0: [mem 0x00000002383d0000-0x000000023874ffff]
> node 0: [mem 0x0000000238750000-0x000000023bc1ffff]
> node 0: [mem 0x000000023bc20000-0x000000023bffffff]
> node 0: [mem 0x000000023c000000-0x000000023fffffff]
> Initmem setup node 0 [mem 0x0000000040000000-0x000000023fffffff]
> On node 0 totalpages: 131072
> DMA zone: 48 pages used for memmap
> DMA zone: 0 pages reserved
> DMA zone: 49152 pages, LIFO batch:1
> Normal zone: 80 pages used for memmap
> Normal zone: 81920 pages, LIFO batch:1
> psci: probing for conduit method from ACPI.
> psci: PSCIv0.2 detected in firmware.
> psci: Using standard PSCI v0.2 function IDs
> psci: Trusted OS migration not required
> percpu: Embedded 3 pages/cpu @fffffe01ffdb0000 s117320 r8192 d71096 u196608
> pcpu-alloc: s117320 r8192 d71096 u196608 alloc=3*65536
> pcpu-alloc: [0] 0 [1] 1 [2] 2 [3] 3 [4] 4 [5] 5 [6] 6 [7] 7
> Detected PIPT I-cache on CPU0
> Built 1 zonelists in Node order, mobility grouping on. Total pages: 130944
> Policy zone: Normal
> Kernel command line: BOOT_IMAGE=/vmlinuz-4.8.0-rc3+ root=/dev/mapper/fedora-root ro rd.lvm.lv=fedora/root rd.lvm.lv=fedora/swap console=ttyAMA0 earlyprintk=pl011,0x9000000 earlycon ignore_loglevel LANG=en_US.UTF-8 acpi=force
> PID hash table entries: 4096 (order: -1, 32768 bytes)
> software IO TLB [mem 0xfbfe0000-0xfffe0000] (64MB) mapped at [fffffe00bbfe0000-fffffe00bffdffff]
> Memory: 7717184K/8388608K available (8956K kernel code, 1564K rwdata, 3712K rodata, 1536K init, 15875K bss, 147136K reserved, 524288K cma-reserved)
> Virtual kernel memory layout:
> modules : 0xfffffc0000000000 - 0xfffffc0008000000 ( 128 MB)
> vmalloc : 0xfffffc0008000000 - 0xfffffdff5fff0000 ( 2045 GB)
> .text : 0xfffffc0008080000 - 0xfffffc0008940000 ( 8960 KB)
> .rodata : 0xfffffc0008940000 - 0xfffffc0008cf0000 ( 3776 KB)
> .init : 0xfffffc0008cf0000 - 0xfffffc0008e70000 ( 1536 KB)
> .data : 0xfffffc0008e70000 - 0xfffffc0008ff7200 ( 1565 KB)
> .bss : 0xfffffc0008ff7200 - 0xfffffc0009f78120 ( 15876 KB)
> fixed : 0xfffffdff7e7d0000 - 0xfffffdff7ec00000 ( 4288 KB)
> PCI I/O : 0xfffffdff7ee00000 - 0xfffffdff7fe00000 ( 16 MB)
> vmemmap : 0xfffffdff80000000 - 0xfffffe0000000000 ( 2 GB maximum)
> 0xfffffdff80000000 - 0xfffffdff80800000 ( 8 MB actual)
> memory : 0xfffffe0000000000 - 0xfffffe0200000000 ( 8192 MB)
> SLUB: HWalign=64, Order=0-3, MinObjects=0, CPUs=8, Nodes=1
> Running RCU self tests
> Hierarchical RCU implementation.
> RCU lockdep checking is enabled.
> Build-time adjustment of leaf fanout to 64.
> RCU restricting CPUs from NR_CPUS=256 to nr_cpu_ids=8.
> RCU: Adjusting geometry for rcu_fanout_leaf=64, nr_cpu_ids=8
> kmemleak: Kernel memory leak detector disabled
> NR_IRQS:64 nr_irqs:64 0
> GICv2m: ACPI overriding V2M MSI_TYPER (base:80, num:64)
> GICv2m: range[mem 0x08020000-0x08020fff], SPI[80:143]
> GIC: PPI11 is secure or misconfigured
> arm_arch_timer: WARNING: Invalid trigger for IRQ3, assuming level low
> arm_arch_timer: WARNING: Please fix your firmware
> arm_arch_timer: Architected cp15 timer(s) running at 50.00MHz (virt).
> clocksource: arch_sys_counter: mask: 0xffffffffffffff max_cycles: 0xb8812736b, max_idle_ns: 440795202655 ns
> sched_clock: 56 bits at 50MHz, resolution 20ns, wraps every 4398046511100ns
> Console: colour dummy device 80x25
> Lock dependency validator: Copyright (c) 2006 Red Hat, Inc., Ingo Molnar
> ... MAX_LOCKDEP_SUBCLASSES: 8
> ... MAX_LOCK_DEPTH: 48
> ... MAX_LOCKDEP_KEYS: 8191
> ... CLASSHASH_SIZE: 4096
> ... MAX_LOCKDEP_ENTRIES: 32768
> ... MAX_LOCKDEP_CHAINS: 65536
> ... CHAINHASH_SIZE: 32768
> memory used by lock dependency info: 8159 kB
> per task-struct memory footprint: 1920 bytes
> Calibrating delay loop (skipped), value calculated using timer frequency.. 100.00 BogoMIPS (lpj=50000)
> pid_max: default: 32768 minimum: 301
> ACPI: Core revision 20160422
> ACPI: 1 ACPI AML tables successfully acquired and loaded
>
> Security Framework initialized
> Yama: becoming mindful.
> SELinux: Initializing.
> SELinux: Starting in permissive mode
> Dentry cache hash table entries: 1048576 (order: 7, 8388608 bytes)
> Inode-cache hash table entries: 524288 (order: 6, 4194304 bytes)
> Mount-cache hash table entries: 16384 (order: 1, 131072 bytes)
> Mountpoint-cache hash table entries: 16384 (order: 1, 131072 bytes)
> ftrace: allocating 28918 entries in 8 pages
> ASID allocator initialised with 65536 entries
> Unable to handle kernel paging request at virtual address e18000009518
> pgd = fffffc0009fb0000
> [e18000009518] *pgd=0000000000000000, *pud=0000000000000000, *pmd=0000000000000000
> Internal error: Oops: 96000004 [#1] SMP
> Modules linked in:
> CPU: 0 PID: 1 Comm: swapper/0 Not tainted 4.8.0-rc3+ #19
> Hardware name: linux,dummy-virt (DT)
> task: fffffe01dc07b600 task.stack: fffffe01fa680000
> PC is at __ll_sc_atomic_add+0x20/0x40
> LR is at __lock_acquire+0xe8/0x698
> pc : [<fffffc000847a9a8>] lr : [<fffffc00081357e8>] pstate: 800000c5
> sp : fffffe01fa6838c0
> x29: fffffe01fa6838c0 x28: fffffc0008ea3000
> x27: fffffc0008ea2358 x26: fffffc0009c84000
> x25: 0000000000000001 x24: 0000000000000000
> x23: fffffe01dc07b600 x22: 0000000000000000
> x21: fffffe01ffd80818 x20: 0000000000000000
> x19: fffffe01ffd80818 x18: 0000000000000000
> x17: 0000000000000000 x16: 0000000000000000
> x15: ffffffffffffffff x14: 00000000000002b7
> x13: 00000000000002b7 x12: 0000000000000038
> x11: 0000000000000005 x10: 0101010101010101
> x9 : 0000000000000001 x8 : 0000e18000009518
> x7 : fffffc000829124c x6 : 0000000000000000
> x5 : 0000000000000080 x4 : 0000e18000009380
> x3 : 0000000000000000 x2 : 000072000000c380
> x1 : 0000e18000009518 x0 : fffffc00081357e8
>
> Process swapper/0 (pid: 1, stack limit = 0xfffffe01fa680020)
> Stack: (0xfffffe01fa6838c0 to 0xfffffe01fa684000)
> 38c0: fffffe01fa6838e0 fffffc00081357e8 fffffe01fa680000 0000000000000001
> 38e0: fffffe01fa683960 fffffc0008136170 fffffe01ffd80818 0000000000000000
> 3900: 0000000000000000 0000000000000000 0000000000000001 0000000000000000
> 3920: fffffc000829124c 00000000000000c0 fffffc0008ea2358 fffffc0008ea3000
> 3940: fffffc000812dde0 00000000000000c0 fffffc0000000000 fffffc0000000000
> 3960: fffffe01fa6839d0 fffffc000892c9ac fffffe01ffd80800 fffffc000829124c
> 3980: fffffe01ffd80800 fffffc000829200c fffffe01f401fc00 000000000000e8e8
> 39a0: fffffe01f401fc00 fffffe01f401fcf8 fffffe01fff1ea90 0000000000000000
> 39c0: fffffe01dc07b680 fffffc0008ea2000 fffffe01fa6839f0 fffffc000829124c
> 39e0: 00000000ffffffff fffffe01ffd80800 fffffe01fa683b10 fffffc0008291ce8
> 3a00: 00000000ffffffff 0000000000000001 00000000024080c0 fffffc000829200c
> 3a20: 0000000000210d00 000000000000e8e8 fffffe01f401fc00 fffffe01f401fcf8
> 3a40: fffffe01fff1ea90 0000000000000000 fffffe01fa683a80 fffffc0008088954
> 3a60: fffffc0009048eb8 fffffe01dc07b600 fffffe01dc07be60 fffffe01fff1eaa0
> 3a80: fffffe01fa683ad0 fffffc00024080c0 fffffc0009048eb8 fffffc00095a3000
> 3aa0: fffffc0008132e80 fffffc0009048eb8 0000000000000000 0000000000000000
> 3ac0: fffffe01fa683eb0 fffffc0008083330 fffffe01fa683b10 fffffc0008291b18
> 3ae0: 7f7f7f7f7f7f7f7f ff1f2877372f2427 0101010101010101 0000000000000005
> 3b00: 0000000000000038 0000000000000000 fffffe01fa683c30 fffffc000829200c
> 3b20: 0000000000000040 fffffe01f401fc00 00000000024080c0 00000000ffffffff
> 3b40: fffffc00084ac9f0 fffffe01fff1ea90 0000000000000000 0000000000000006
> 3b60: 0000000000000043 fffffc0008b89670 fffffc0008f374b8 0000000000000000
> 3b80: fffffe01fa683bd0 fffffc0008135200 fffffe01fff1eaa0 0000000000000000
> 3ba0: fffffe0100000000 fffffc00084ac9f0 fffffe01fa683bf0 fffffc0008131904
> 3bc0: fffffe01fa680000 0000000102000200 fffffc0008bb4cf0 0000000000000189
> 3be0: 0000000000000028 fffffe01dc07b600 fffffe01fa683c10 fffffc00080fff1c
> 3c00: fffffc0008fbb39e 0000000000000000 fffffe01fa683c40 fffffc0008100034
> 3c20: fffffe01fa683c30 fffffc0008291ff4 fffffe01fa683c70 fffffc00082927dc
> 3c40: fffffe01f401fc00 00000000024080c0 fffffc00084ac9f0 fffffe01f401fc00
> 3c60: 0000000000000028 fffffc0008ea4000 fffffe01fa683cd0 fffffc00084ac9f0
> 3c80: fffffc0008ff6090 fffffc0008b89670 fffffc0008fd5f90 0000000000000002
> 3ca0: fffffc0008fd5f10 0000000000000003 00000000000001bd 0000000000000006
> 3cc0: 0000000000000043 fffffc0008b88a10 fffffe01fa683d10 fffffc0008d25090
> 3ce0: fffffc0008ff6090 fffffc0008fd5f90 fffffc0008fd5f90 fffffc0008fd5000
> 3d00: 0000000000000002 fffffc0008ea2398 fffffe01fa683d90 fffffc0008083594
> 3d20: fffffc0008d24fa0 fffffe01fa680000 0000000000000000 0000000000000000
> 3d40: 0000000000000000 0000000000000000 0000000000000000 0000000000000000
> 3d60: 0000000000000000 0000000000000000 fffffe01fa683d90 fffffc0008b89d18
> 3d80: 000000000000000e 0000000000000019 fffffe01fa683e00 fffffc0008cf0d2c
> 3da0: fffffc0008e1c288 fffffc0008e1c2c8 0000000000000040 0000000000000000
> 3dc0: fffffe01fa683e00 fffffc0008cf0d1c fffffc0008e1c200 fffffc0008e1c2c8
> 3de0: 0000000000000040 0000000000000000 0000000000000000 fffffc0008e1c2c8
> 3e00: fffffe01fa683ea0 fffffc0008924978 fffffc0008924960 0000000000000000
> 3e20: 0000000000000000 0000000000000000 0000000000000000 0000000000000000
> 3e40: 0000000000000000 0000000000000000 0000000000000000 0000000000000001
> 3e60: 0000000000000001 0000000000000000 0000000000000000 0000000000000000
> 3e80: 0000000000000000 0000000000000000 0000000000000000 0000000000000000
> 3ea0: 0000000000000000 fffffc0008083330 fffffc0008924960 0000000000000000
> 3ec0: 0000000000000000 0000000000000000 0000000000000000 0000000000000000
> 3ee0: 0000000000000000 0000000000000000 0000000000000000 0000000000000000
> 3f00: 0000000000000000 0000000000000000 0000000000000000 0000000000000000
> 3f20: 0000000000000000 0000000000000000 0000000000000000 0000000000000000
> 3f40: 0000000000000000 0000000000000000 0000000000000000 0000000000000000
> 3f60: 0000000000000000 0000000000000000 0000000000000000 0000000000000000
> 3f80: 0000000000000000 0000000000000000 0000000000000000 0000000000000000
> 3fa0: 0000000000000000 0000000000000000 0000000000000000 0000000000000000
> 3fc0: 0000000000000000 0000000000000005 0000000000000000 0000000000000000
> 3fe0: 0000000000000000 0000000000000000 0000000000000000 0000000000000000
> Call trace:
> Exception stack(0xfffffe01fa6836f0 to 0xfffffe01fa683820)
> 36e0: fffffe01ffd80818 0000040000000000
> 3700: fffffe01fa6838c0 fffffc000847a9a8 fffffe01fff1b580 fffffe01fff1b580
> 3720: fffffc0008927614 fffffc0008ea1000 fffffe01fa683740 00000000000000c0
> 3740: fffffe01fa683780 fffffc000811794c fffffe01fa6837e0 fffffc0008135a64
> 3760: 87cee53ad487914d fffffe01dc07be60 0000000000000001 0000000000000000
> 3780: fffffe01dc07b600 0000000000000000 fffffc00081357e8 0000e18000009518
> 37a0: 000072000000c380 0000000000000000 0000e18000009380 0000000000000080
> 37c0: 0000000000000000 fffffc000829124c 0000e18000009518 0000000000000001
> 37e0: 0101010101010101 0000000000000005 0000000000000038 00000000000002b7
> 3800: 00000000000002b7 ffffffffffffffff 0000000000000000 0000000000000000
> [<fffffc000847a9a8>] __ll_sc_atomic_add+0x20/0x40
> [<fffffc00081357e8>] __lock_acquire+0xe8/0x698
> [<fffffc0008136170>] lock_acquire+0xd8/0x2c0
> [<fffffc000892c9ac>] _raw_spin_lock+0x4c/0x60
> [<fffffc000829124c>] get_partial_node.isra.23+0x4c/0x440
> [<fffffc0008291ce8>] ___slab_alloc+0x438/0x708
> [<fffffc000829200c>] __slab_alloc+0x54/0xa0
> [<fffffc00082927dc>] kmem_cache_alloc_trace+0x35c/0x428
> [<fffffc00084ac9f0>] ddebug_add_module+0x38/0xf0
> [<fffffc0008d25090>] dynamic_debug_init+0xf0/0x2a0
> [<fffffc0008083594>] do_one_initcall+0x44/0x138
> [<fffffc0008cf0d2c>] kernel_init_freeable+0x17c/0x2e0
> [<fffffc0008924978>] kernel_init+0x18/0x110
> [<fffffc0008083330>] ret_from_fork+0x10/0x20
> Code: aa1e03e0 aa0103e8 d503201f f9800111 (885f7d00)
> ---[ end trace 0000000000000000 ]---
> note: swapper/0[1] exited with preempt_count 1
> Kernel panic - not syncing: Attempted to kill init! exitcode=0x0000000b
>
> ---[ end Kernel panic - not syncing: Attempted to kill init! exitcode=0x0000000b
>
(3) Please find the bisection log below:
> git bisect start
> # bad: [b67be92feb486f800d80d72c67fd87b47b79b18e] Merge tag 'pwm/for-4.9-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/thierry.reding/linux-pwm
> git bisect bad b67be92feb486f800d80d72c67fd87b47b79b18e
> # good: [c8d2bc9bc39ebea8437fd974fdbc21847bb897a3] Linux 4.8
> git bisect good c8d2bc9bc39ebea8437fd974fdbc21847bb897a3
> # bad: [41844e36206be90cd4d962ea49b0abc3612a99d0] Merge tag 'staging-4.9-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/staging
> git bisect bad 41844e36206be90cd4d962ea49b0abc3612a99d0
> # bad: [d268dbe76a53d72cc41316eb59e7968db60e77ad] Merge tag 'pinctrl-v4.9-1' of git://git.kernel.org/pub/scm/linux/kernel/git/linusw/linux-pinctrl
> git bisect bad d268dbe76a53d72cc41316eb59e7968db60e77ad
> # bad: [02bafd96f3a5d8e610b19033ffec55b92459aaae] Merge tag 'docs-4.9' of git://git.lwn.net/linux
> git bisect bad 02bafd96f3a5d8e610b19033ffec55b92459aaae
> # bad: [9929780e86854833e649b39b290b5fe921eb1701] Merge tag 'driver-core-4.9-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/driver-core
> git bisect bad 9929780e86854833e649b39b290b5fe921eb1701
> # bad: [12b7bcb43e6ea834ab2f5dc52d971e379a0ca109] Merge branch 'perf-core-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip
> git bisect bad 12b7bcb43e6ea834ab2f5dc52d971e379a0ca109
> # bad: [72d39926f098b0c4ad95e1461595a8d6d403c14d] Merge tag 'acpi-4.9-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm
> git bisect bad 72d39926f098b0c4ad95e1461595a8d6d403c14d
> # bad: [72ec94560d7ee1d3a61d5904fd9a5bf68bf3b11a] Merge tag 'pm-4.9-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm
> git bisect bad 72ec94560d7ee1d3a61d5904fd9a5bf68bf3b11a
> # bad: [792d47379f4d4c76692f1795f33d38582f8907fa] arm64: alternative: add auto-nop infrastructure
> git bisect bad 792d47379f4d4c76692f1795f33d38582f8907fa
> # good: [dc00247576fdb97211e1959b4dfd2a7893cf9d0b] arm64: kernel: re-export _cpu_resume() from sleep.S
> git bisect good dc00247576fdb97211e1959b4dfd2a7893cf9d0b
> # good: [9787ed6e5cee7a62320f3014eb5e7b373502c292] of/numa: remove a duplicated warning
> git bisect good 9787ed6e5cee7a62320f3014eb5e7b373502c292
> # bad: [c47a1900ad710fd2c97127e2ba19da1df79cf733] arm64: Rearrange CPU errata workaround checks
> git bisect bad c47a1900ad710fd2c97127e2ba19da1df79cf733
> # good: [7af3a0a992524ffddc342cd1481cc4dcb3f1da71] arm64/numa: support HAVE_SETUP_PER_CPU_AREA
> git bisect good 7af3a0a992524ffddc342cd1481cc4dcb3f1da71
> # bad: [7ba5f605f3a0d9495aad539eeb8346d726dfc183] arm64/numa: remove the limitation that cpu0 must bind to node0
> git bisect bad 7ba5f605f3a0d9495aad539eeb8346d726dfc183
> # good: [df7ffa34cc0c06bfa7206732df78725ff34633ee] arm64/numa: remove some useless code
> git bisect good df7ffa34cc0c06bfa7206732df78725ff34633ee
> # first bad commit: [7ba5f605f3a0d9495aad539eeb8346d726dfc183] arm64/numa: remove the limitation that cpu0 must bind to node0
I repeatedly retested the first bad commit:
> commit 7ba5f605f3a0d9495aad539eeb8346d726dfc183
> Author: Zhen Lei <thunder.leizhen@huawei.com>
> Date: Thu Sep 1 14:55:04 2016 +0800
>
> arm64/numa: remove the limitation that cpu0 must bind to node0
and its direct ancestor:
> commit df7ffa34cc0c06bfa7206732df78725ff34633ee
> Author: Zhen Lei <thunder.leizhen@huawei.com>
> Date: Thu Sep 1 14:55:03 2016 +0800
>
> arm64/numa: remove some useless code
The offending commit consistently fails to boot with the described
symptoms when DT is disabled, and succeeds to boot when DT is enabled.
The predecessor commit consistently succeeds to boot regardless of DT
versus ACPI.
(4) Analysis (well, a lame attempt at that, because I have zero
familiarity with this code). Let me quote the patch:
> commit 7ba5f605f3a0d9495aad539eeb8346d726dfc183
> Author: Zhen Lei <thunder.leizhen@huawei.com>
> Date: Thu Sep 1 14:55:04 2016 +0800
>
> arm64/numa: remove the limitation that cpu0 must bind to node0
>
> 1. Remove the old binding code.
> 2. Read the nid of cpu0 from dts.
> 3. Fallback the nid of cpu0 to 0 when numa=off is set in bootargs.
>
> Signed-off-by: Zhen Lei <thunder.leizhen@huawei.com>
> Signed-off-by: Will Deacon <will.deacon@arm.com>
>
> diff --git a/arch/arm64/kernel/smp.c b/arch/arm64/kernel/smp.c
> index c3c08368a685..8b048e6ec34a 100644
> --- a/arch/arm64/kernel/smp.c
> +++ b/arch/arm64/kernel/smp.c
> @@ -624,6 +624,7 @@ static void __init of_parse_and_init_cpus(void)
> }
>
> bootcpu_valid = true;
> + early_map_cpu_to_node(0, of_node_to_nid(dn));
>
> /*
> * cpu_logical_map has already been
> diff --git a/arch/arm64/mm/numa.c b/arch/arm64/mm/numa.c
> index 0a15f010b64a..778a985c8a70 100644
> --- a/arch/arm64/mm/numa.c
> +++ b/arch/arm64/mm/numa.c
> @@ -116,16 +116,24 @@ static void __init setup_node_to_cpumask_map(void)
> */
> void numa_store_cpu_info(unsigned int cpu)
> {
> - map_cpu_to_node(cpu, numa_off ? 0 : cpu_to_node_map[cpu]);
> + map_cpu_to_node(cpu, cpu_to_node_map[cpu]);
> }
>
> void __init early_map_cpu_to_node(unsigned int cpu, int nid)
> {
> /* fallback to node 0 */
> - if (nid < 0 || nid >= MAX_NUMNODES)
> + if (nid < 0 || nid >= MAX_NUMNODES || numa_off)
> nid = 0;
>
> cpu_to_node_map[cpu] = nid;
> +
> + /*
> + * We should set the numa node of cpu0 as soon as possible, because it
> + * has already been set up online before. cpu_to_node(0) will soon be
> + * called.
> + */
> + if (!cpu)
> + set_cpu_numa_node(cpu, nid);
> }
>
> #ifdef CONFIG_HAVE_SETUP_PER_CPU_AREA
> @@ -393,10 +401,6 @@ static int __init numa_init(int (*init_func)(void))
>
> setup_node_to_cpumask_map();
>
> - /* init boot processor */
> - cpu_to_node_map[0] = 0;
> - map_cpu_to_node(0, 0);
> -
> return 0;
> }
>
The commit message states that the numa-id (nid) for CPU#0 is read from
the DTS. If there is no DT, I think that won't work so well.
Second, the patch replaces the unconditional, static
CPU#0 <-> numa-node#0
mapping in numa_init(), which is independent of ACPI vs. DT, with a
DT-dependent early_map_cpu_to_node() call, in of_parse_and_init_cpus().
The ACPI branch is regressed by this, because on that branch we now
don't create a
CPU#0 <-> numa-node#whatever
mapping at all.
(5) The entry
ACPI: NUMA: Failed to initialise from firmware
in the dmesg doesn't imply an error in the firmware; it just means that
the firmware does not provide an SRAT table. That is valid if there's
only one NUMA node in the system.
(6) For reference, the MADT is:
> /*
> * Intel ACPI Component Architecture
> * AML/ASL+ Disassembler version 20160831-64
> * Copyright (c) 2000 - 2016 Intel Corporation
> *
> * Disassembly of apic.dat, Fri Oct 14 00:21:16 2016
> *
> * ACPI Data Table [APIC]
> *
> * Format: [HexOffset DecimalOffset ByteLength] FieldName : FieldValue
> */
>
> [000h 0000 4] Signature : "APIC" [Multiple APIC Description Table (MADT)]
> [004h 0004 4] Table Length : 000002BC
> [008h 0008 1] Revision : 03
> [009h 0009 1] Checksum : 18
> [00Ah 0010 6] Oem ID : "BOCHS "
> [010h 0016 8] Oem Table ID : "BXPCAPIC"
> [018h 0024 4] Oem Revision : 00000001
> [01Ch 0028 4] Asl Compiler ID : "BXPC"
> [020h 0032 4] Asl Compiler Revision : 00000001
>
> [024h 0036 4] Local Apic Address : 00000000
> [028h 0040 4] Flags (decoded below) : 00000000
> PC-AT Compatibility : 0
>
> [02Ch 0044 1] Subtable Type : 0C [Generic Interrupt Distributor]
> [02Dh 0045 1] Length : 18
> [02Eh 0046 2] Reserved : 0000
> [030h 0048 4] Local GIC Hardware ID : 00000000
> [034h 0052 8] Base Address : 0000000008000000
> [03Ch 0060 4] Interrupt Base : 00000000
> [040h 0064 1] Version : 02
> [041h 0065 3] Reserved : 000000
>
> [044h 0068 1] Subtable Type : 0B [Generic Interrupt Controller]
> [045h 0069 1] Length : 4C
> [046h 0070 2] Reserved : 0000
> [048h 0072 4] CPU Interface Number : 00000000
> [04Ch 0076 4] Processor UID : 00000000
> [050h 0080 4] Flags (decoded below) : 00000001
> Processor Enabled : 1
> Performance Interrupt Trigger Mode : 0
> Virtual GIC Interrupt Trigger Mode : 0
> [054h 0084 4] Parking Protocol Version : 00000000
> [058h 0088 4] Performance Interrupt : 00000017
> [05Ch 0092 8] Parked Address : 0000000000000000
> [064h 0100 8] Base Address : 0000000008010000
> [06Ch 0108 8] Virtual GIC Base Address : 0000000000000000
> [074h 0116 8] Hypervisor GIC Base Address : 0000000000000000
> [07Ch 0124 4] Virtual GIC Interrupt : 00000000
> [080h 0128 8] Redistributor Base Address : 0000000000000000
> [088h 0136 8] ARM MPIDR : 0000000000000000
> /**** ACPI subtable terminates early - may be older version (dump table) */
>
> [090h 0144 1] Subtable Type : 0B [Generic Interrupt Controller]
> [091h 0145 1] Length : 4C
> [092h 0146 2] Reserved : 0000
> [094h 0148 4] CPU Interface Number : 00000001
> [098h 0152 4] Processor UID : 00000001
> [09Ch 0156 4] Flags (decoded below) : 00000001
> Processor Enabled : 1
> Performance Interrupt Trigger Mode : 0
> Virtual GIC Interrupt Trigger Mode : 0
> [0A0h 0160 4] Parking Protocol Version : 00000000
> [0A4h 0164 4] Performance Interrupt : 00000017
> [0A8h 0168 8] Parked Address : 0000000000000000
> [0B0h 0176 8] Base Address : 0000000008010000
> [0B8h 0184 8] Virtual GIC Base Address : 0000000000000000
> [0C0h 0192 8] Hypervisor GIC Base Address : 0000000000000000
> [0C8h 0200 4] Virtual GIC Interrupt : 00000000
> [0CCh 0204 8] Redistributor Base Address : 0000000000000000
> [0D4h 0212 8] ARM MPIDR : 0000000000000001
> /**** ACPI subtable terminates early - may be older version (dump table) */
>
> [0DCh 0220 1] Subtable Type : 0B [Generic Interrupt Controller]
> [0DDh 0221 1] Length : 4C
> [0DEh 0222 2] Reserved : 0000
> [0E0h 0224 4] CPU Interface Number : 00000002
> [0E4h 0228 4] Processor UID : 00000002
> [0E8h 0232 4] Flags (decoded below) : 00000001
> Processor Enabled : 1
> Performance Interrupt Trigger Mode : 0
> Virtual GIC Interrupt Trigger Mode : 0
> [0ECh 0236 4] Parking Protocol Version : 00000000
> [0F0h 0240 4] Performance Interrupt : 00000017
> [0F4h 0244 8] Parked Address : 0000000000000000
> [0FCh 0252 8] Base Address : 0000000008010000
> [104h 0260 8] Virtual GIC Base Address : 0000000000000000
> [10Ch 0268 8] Hypervisor GIC Base Address : 0000000000000000
> [114h 0276 4] Virtual GIC Interrupt : 00000000
> [118h 0280 8] Redistributor Base Address : 0000000000000000
> [120h 0288 8] ARM MPIDR : 0000000000000002
> /**** ACPI subtable terminates early - may be older version (dump table) */
>
> [128h 0296 1] Subtable Type : 0B [Generic Interrupt Controller]
> [129h 0297 1] Length : 4C
> [12Ah 0298 2] Reserved : 0000
> [12Ch 0300 4] CPU Interface Number : 00000003
> [130h 0304 4] Processor UID : 00000003
> [134h 0308 4] Flags (decoded below) : 00000001
> Processor Enabled : 1
> Performance Interrupt Trigger Mode : 0
> Virtual GIC Interrupt Trigger Mode : 0
> [138h 0312 4] Parking Protocol Version : 00000000
> [13Ch 0316 4] Performance Interrupt : 00000017
> [140h 0320 8] Parked Address : 0000000000000000
> [148h 0328 8] Base Address : 0000000008010000
> [150h 0336 8] Virtual GIC Base Address : 0000000000000000
> [158h 0344 8] Hypervisor GIC Base Address : 0000000000000000
> [160h 0352 4] Virtual GIC Interrupt : 00000000
> [164h 0356 8] Redistributor Base Address : 0000000000000000
> [16Ch 0364 8] ARM MPIDR : 0000000000000003
> /**** ACPI subtable terminates early - may be older version (dump table) */
>
> [174h 0372 1] Subtable Type : 0B [Generic Interrupt Controller]
> [175h 0373 1] Length : 4C
> [176h 0374 2] Reserved : 0000
> [178h 0376 4] CPU Interface Number : 00000004
> [17Ch 0380 4] Processor UID : 00000004
> [180h 0384 4] Flags (decoded below) : 00000001
> Processor Enabled : 1
> Performance Interrupt Trigger Mode : 0
> Virtual GIC Interrupt Trigger Mode : 0
> [184h 0388 4] Parking Protocol Version : 00000000
> [188h 0392 4] Performance Interrupt : 00000017
> [18Ch 0396 8] Parked Address : 0000000000000000
> [194h 0404 8] Base Address : 0000000008010000
> [19Ch 0412 8] Virtual GIC Base Address : 0000000000000000
> [1A4h 0420 8] Hypervisor GIC Base Address : 0000000000000000
> [1ACh 0428 4] Virtual GIC Interrupt : 00000000
> [1B0h 0432 8] Redistributor Base Address : 0000000000000000
> [1B8h 0440 8] ARM MPIDR : 0000000000000004
> /**** ACPI subtable terminates early - may be older version (dump table) */
>
> [1C0h 0448 1] Subtable Type : 0B [Generic Interrupt Controller]
> [1C1h 0449 1] Length : 4C
> [1C2h 0450 2] Reserved : 0000
> [1C4h 0452 4] CPU Interface Number : 00000005
> [1C8h 0456 4] Processor UID : 00000005
> [1CCh 0460 4] Flags (decoded below) : 00000001
> Processor Enabled : 1
> Performance Interrupt Trigger Mode : 0
> Virtual GIC Interrupt Trigger Mode : 0
> [1D0h 0464 4] Parking Protocol Version : 00000000
> [1D4h 0468 4] Performance Interrupt : 00000017
> [1D8h 0472 8] Parked Address : 0000000000000000
> [1E0h 0480 8] Base Address : 0000000008010000
> [1E8h 0488 8] Virtual GIC Base Address : 0000000000000000
> [1F0h 0496 8] Hypervisor GIC Base Address : 0000000000000000
> [1F8h 0504 4] Virtual GIC Interrupt : 00000000
> [1FCh 0508 8] Redistributor Base Address : 0000000000000000
> [204h 0516 8] ARM MPIDR : 0000000000000005
> /**** ACPI subtable terminates early - may be older version (dump table) */
>
> [20Ch 0524 1] Subtable Type : 0B [Generic Interrupt Controller]
> [20Dh 0525 1] Length : 4C
> [20Eh 0526 2] Reserved : 0000
> [210h 0528 4] CPU Interface Number : 00000006
> [214h 0532 4] Processor UID : 00000006
> [218h 0536 4] Flags (decoded below) : 00000001
> Processor Enabled : 1
> Performance Interrupt Trigger Mode : 0
> Virtual GIC Interrupt Trigger Mode : 0
> [21Ch 0540 4] Parking Protocol Version : 00000000
> [220h 0544 4] Performance Interrupt : 00000017
> [224h 0548 8] Parked Address : 0000000000000000
> [22Ch 0556 8] Base Address : 0000000008010000
> [234h 0564 8] Virtual GIC Base Address : 0000000000000000
> [23Ch 0572 8] Hypervisor GIC Base Address : 0000000000000000
> [244h 0580 4] Virtual GIC Interrupt : 00000000
> [248h 0584 8] Redistributor Base Address : 0000000000000000
> [250h 0592 8] ARM MPIDR : 0000000000000006
> /**** ACPI subtable terminates early - may be older version (dump table) */
>
> [258h 0600 1] Subtable Type : 0B [Generic Interrupt Controller]
> [259h 0601 1] Length : 4C
> [25Ah 0602 2] Reserved : 0000
> [25Ch 0604 4] CPU Interface Number : 00000007
> [260h 0608 4] Processor UID : 00000007
> [264h 0612 4] Flags (decoded below) : 00000001
> Processor Enabled : 1
> Performance Interrupt Trigger Mode : 0
> Virtual GIC Interrupt Trigger Mode : 0
> [268h 0616 4] Parking Protocol Version : 00000000
> [26Ch 0620 4] Performance Interrupt : 00000017
> [270h 0624 8] Parked Address : 0000000000000000
> [278h 0632 8] Base Address : 0000000008010000
> [280h 0640 8] Virtual GIC Base Address : 0000000000000000
> [288h 0648 8] Hypervisor GIC Base Address : 0000000000000000
> [290h 0656 4] Virtual GIC Interrupt : 00000000
> [294h 0660 8] Redistributor Base Address : 0000000000000000
> [29Ch 0668 8] ARM MPIDR : 0000000000000007
> /**** ACPI subtable terminates early - may be older version (dump table) */
>
> [2A4h 0676 1] Subtable Type : 0D [Generic MSI Frame]
> [2A5h 0677 1] Length : 18
> [2A6h 0678 2] Reserved : 0000
> [2A8h 0680 4] MSI Frame ID : 00000000
> [2ACh 0684 8] Base Address : 0000000008020000
> [2B4h 0692 4] Flags (decoded below) : 00000001
> Select SPI : 1
> [2B8h 0696 2] SPI Count : 0040
> [2BAh 0698 2] SPI Base : 0050
>
> Raw Table Data: Length 700 (0x2BC)
>
> 0000: 41 50 49 43 BC 02 00 00 03 18 42 4F 43 48 53 20 // APIC......BOCHS
> 0010: 42 58 50 43 41 50 49 43 01 00 00 00 42 58 50 43 // BXPCAPIC....BXPC
> 0020: 01 00 00 00 00 00 00 00 00 00 00 00 0C 18 00 00 // ................
> 0030: 00 00 00 00 00 00 00 08 00 00 00 00 00 00 00 00 // ................
> 0040: 02 00 00 00 0B 4C 00 00 00 00 00 00 00 00 00 00 // .....L..........
> 0050: 01 00 00 00 00 00 00 00 17 00 00 00 00 00 00 00 // ................
> 0060: 00 00 00 00 00 00 01 08 00 00 00 00 00 00 00 00 // ................
> 0070: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 // ................
> 0080: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 // ................
> 0090: 0B 4C 00 00 01 00 00 00 01 00 00 00 01 00 00 00 // .L..............
> 00A0: 00 00 00 00 17 00 00 00 00 00 00 00 00 00 00 00 // ................
> 00B0: 00 00 01 08 00 00 00 00 00 00 00 00 00 00 00 00 // ................
> 00C0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 // ................
> 00D0: 00 00 00 00 01 00 00 00 00 00 00 00 0B 4C 00 00 // .............L..
> 00E0: 02 00 00 00 02 00 00 00 01 00 00 00 00 00 00 00 // ................
> 00F0: 17 00 00 00 00 00 00 00 00 00 00 00 00 00 01 08 // ................
> 0100: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 // ................
> 0110: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 // ................
> 0120: 02 00 00 00 00 00 00 00 0B 4C 00 00 03 00 00 00 // .........L......
> 0130: 03 00 00 00 01 00 00 00 00 00 00 00 17 00 00 00 // ................
> 0140: 00 00 00 00 00 00 00 00 00 00 01 08 00 00 00 00 // ................
> 0150: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 // ................
> 0160: 00 00 00 00 00 00 00 00 00 00 00 00 03 00 00 00 // ................
> 0170: 00 00 00 00 0B 4C 00 00 04 00 00 00 04 00 00 00 // .....L..........
> 0180: 01 00 00 00 00 00 00 00 17 00 00 00 00 00 00 00 // ................
> 0190: 00 00 00 00 00 00 01 08 00 00 00 00 00 00 00 00 // ................
> 01A0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 // ................
> 01B0: 00 00 00 00 00 00 00 00 04 00 00 00 00 00 00 00 // ................
> 01C0: 0B 4C 00 00 05 00 00 00 05 00 00 00 01 00 00 00 // .L..............
> 01D0: 00 00 00 00 17 00 00 00 00 00 00 00 00 00 00 00 // ................
> 01E0: 00 00 01 08 00 00 00 00 00 00 00 00 00 00 00 00 // ................
> 01F0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 // ................
> 0200: 00 00 00 00 05 00 00 00 00 00 00 00 0B 4C 00 00 // .............L..
> 0210: 06 00 00 00 06 00 00 00 01 00 00 00 00 00 00 00 // ................
> 0220: 17 00 00 00 00 00 00 00 00 00 00 00 00 00 01 08 // ................
> 0230: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 // ................
> 0240: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 // ................
> 0250: 06 00 00 00 00 00 00 00 0B 4C 00 00 07 00 00 00 // .........L......
> 0260: 07 00 00 00 01 00 00 00 00 00 00 00 17 00 00 00 // ................
> 0270: 00 00 00 00 00 00 00 00 00 00 01 08 00 00 00 00 // ................
> 0280: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 // ................
> 0290: 00 00 00 00 00 00 00 00 00 00 00 00 07 00 00 00 // ................
> 02A0: 00 00 00 00 0D 18 00 00 00 00 00 00 00 00 02 08 // ................
> 02B0: 00 00 00 00 01 00 00 00 40 00 50 00 // ........ at .P.
I'm unsure if this table is supposed to lead to the creation of the
(apparently missing)
CPU#0 <-> numa-node#0
mapping, via
smp_init_cpus() [arch/arm64/kernel/smp.c]
acpi_table_parse_madt() [drivers/acpi/tables.c]
acpi_parse_gic_cpu_interface() [arch/arm64/kernel/smp.c]
acpi_map_gic_cpu_interface() [arch/arm64/kernel/smp.c]
early_map_cpu_to_node() [arch/arm64/mm/numa.c]
(7) I also tried "numa=off" in addition to "acpi=force", just in case;
it didn't make a difference.
Thanks
Laszlo
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox