* [PATCH] arm64: pmuv3: Support v8.1 PMUv3 extension
From: Mark Rutland @ 2017-04-24 14:03 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20170424133929.GA4952@localhost>
On Mon, Apr 24, 2017 at 01:39:30PM +0000, Jayachandran C wrote:
> The v8.1 supplement is quite clear on the field definition:
>
> PMUVer, bits [11:8]
> ....
> Defined values are:
> 0000 Performance Monitors extension System registers not implemented.
> 0001 Performance Monitors extension System registers implemented, PMUv3.
> 0100 Performance Monitors extension System registers implemented, PMUv3, with a 16-bit
> evtCount field, and if EL2 is implemented, the addition of the MDCR_EL2.HPMD bit.
> 1111 IMPLEMENTATION DEFINED form of performance monitors supported, PMUv3 not
> supported.
> All other values are reserved.
> In ARMv8-A the permitted values are 0b0000, 0b0001 and 0b1111.
> In ARMv8.1 the permitted values are 0b0000, 0b0100 and 0b1111.
>
> I changed the code to strictly do this. We have to exclude 0xf, since that is not PMUv3.
> And we cannot predict what the reserved values will represent, so it is best to skip them
> until they are defined to be PMUv3 compatible.
My understanding is that ID_AA64DFR0.PMUVer is intended to be covered by
the usual ID register principles, and thus at least 0x2-0x7 are reserved
for architected backwards compatible extensions to PMUv3.
See ARM DDI 0487B.a, D7.1.4, "Principles of the ID scheme for fields in
ID registers". It is explicitly stated that the scheme applies to
ID_AA64DFR0.
Per those rules, we should check >= the minimum PMUv3 implemented value,
i.e. val >= 1. Due to both 0x0 and 0xF meaning PMUv3 isn't implemented,
it's not clear if the fields should be treated as if it were signed or
unsigned, and I'm awaiting clarification on this.
Either way, I believe that 0x1-0x7 must all be compatible with baseline
PMUv3 per the ID scheme principles.
Thanks,
Mark.
IMPORTANT NOTICE: The contents of this email and any attachments are confidential and may also be privileged. If you are not the intended recipient, please notify the sender immediately and do not disclose the contents to any other person, use it for any purpose, or store or copy the information in any medium. Thank you.
^ permalink raw reply
* [PATCH V9 1/3] irq: Allow to pass the IRQF_TIMER flag with percpu irq request
From: Daniel Lezcano @ 2017-04-24 14:01 UTC (permalink / raw)
To: linux-arm-kernel
In the next changes, we track when the interrupts occur in order to
statistically compute when is supposed to happen the next interrupt.
In all the interruptions, it does not make sense to store the timer interrupt
occurences and try to predict the next interrupt as when know the expiration
time.
The request_irq() has a irq flags parameter and the timer drivers use it to
pass the IRQF_TIMER flag, letting us know the interrupt is coming from a timer.
Based on this flag, we can discard these interrupts when tracking them.
But, the API request_percpu_irq does not allow to pass a flag, hence specifying
if the interrupt type is a timer.
Add a function request_percpu_irq_flags() where we can specify the flags. The
request_percpu_irq() function is changed to be a wrapper to
request_percpu_irq_flags() passing a zero flag parameter.
Change the timers using request_percpu_irq() to use request_percpu_irq_flags()
instead with the IRQF_TIMER flag set.
For now, in order to prevent a misusage of this parameter, only the IRQF_TIMER
flag (or zero) is a valid parameter to be passed to the
request_percpu_irq_flags() function.
Signed-off-by: Daniel Lezcano <daniel.lezcano@linaro.org>
Cc: Mark Rutland <mark.rutland@arm.com>
Cc: Vineet Gupta <vgupta@synopsys.com>
Cc: Marc Zyngier <marc.zyngier@arm.com>
Cc: Patrice Chotard <patrice.chotard@st.com>
Cc: Kukjin Kim <kgene@kernel.org>
Cc: Krzysztof Kozlowski <krzk@kernel.org>
Cc: Javier Martinez Canillas <javier@osg.samsung.com>
Cc: Christoffer Dall <christoffer.dall@linaro.org>
Cc: Paolo Bonzini <pbonzini@redhat.com>
Cc: Radim Kr?m?? <rkrcmar@redhat.com>
---
Changelog:
V9:
- Clarified the patch description
- Fixed EXPORT_SYMBOL_GPL(request_percpu_irq_flags)
---
arch/arm/kernel/smp_twd.c | 3 ++-
drivers/clocksource/arc_timer.c | 4 ++--
drivers/clocksource/arm_arch_timer.c | 20 ++++++++++++--------
drivers/clocksource/arm_global_timer.c | 4 ++--
drivers/clocksource/exynos_mct.c | 7 ++++---
drivers/clocksource/qcom-timer.c | 4 ++--
drivers/clocksource/time-armada-370-xp.c | 9 +++++----
drivers/clocksource/timer-nps.c | 6 +++---
include/linux/interrupt.h | 11 ++++++++++-
kernel/irq/manage.c | 15 ++++++++++-----
virt/kvm/arm/arch_timer.c | 5 +++--
11 files changed, 55 insertions(+), 33 deletions(-)
diff --git a/arch/arm/kernel/smp_twd.c b/arch/arm/kernel/smp_twd.c
index 895ae51..ce9fdcf 100644
--- a/arch/arm/kernel/smp_twd.c
+++ b/arch/arm/kernel/smp_twd.c
@@ -332,7 +332,8 @@ static int __init twd_local_timer_common_register(struct device_node *np)
goto out_free;
}
- err = request_percpu_irq(twd_ppi, twd_handler, "twd", twd_evt);
+ err = request_percpu_irq_flags(twd_ppi, twd_handler, IRQF_TIMER, "twd",
+ twd_evt);
if (err) {
pr_err("twd: can't register interrupt %d (%d)\n", twd_ppi, err);
goto out_free;
diff --git a/drivers/clocksource/arc_timer.c b/drivers/clocksource/arc_timer.c
index 7517f95..993e6af 100644
--- a/drivers/clocksource/arc_timer.c
+++ b/drivers/clocksource/arc_timer.c
@@ -301,8 +301,8 @@ static int __init arc_clockevent_setup(struct device_node *node)
}
/* Needs apriori irq_set_percpu_devid() done in intc map function */
- ret = request_percpu_irq(arc_timer_irq, timer_irq_handler,
- "Timer0 (per-cpu-tick)", evt);
+ ret = request_percpu_irq_flags(arc_timer_irq, timer_irq_handler, IRQF_TIMER,
+ "Timer0 (per-cpu-tick)", evt);
if (ret) {
pr_err("clockevent: unable to request irq\n");
return ret;
diff --git a/drivers/clocksource/arm_arch_timer.c b/drivers/clocksource/arm_arch_timer.c
index 7a8a411..d9d00b0 100644
--- a/drivers/clocksource/arm_arch_timer.c
+++ b/drivers/clocksource/arm_arch_timer.c
@@ -768,25 +768,29 @@ static int __init arch_timer_register(void)
ppi = arch_timer_ppi[arch_timer_uses_ppi];
switch (arch_timer_uses_ppi) {
case VIRT_PPI:
- err = request_percpu_irq(ppi, arch_timer_handler_virt,
- "arch_timer", arch_timer_evt);
+ err = request_percpu_irq_flags(ppi, arch_timer_handler_virt,
+ IRQF_TIMER, "arch_timer",
+ arch_timer_evt);
break;
case PHYS_SECURE_PPI:
case PHYS_NONSECURE_PPI:
- err = request_percpu_irq(ppi, arch_timer_handler_phys,
- "arch_timer", arch_timer_evt);
+ err = request_percpu_irq_flags(ppi, arch_timer_handler_phys,
+ IRQF_TIMER, "arch_timer",
+ arch_timer_evt);
if (!err && arch_timer_ppi[PHYS_NONSECURE_PPI]) {
ppi = arch_timer_ppi[PHYS_NONSECURE_PPI];
- err = request_percpu_irq(ppi, arch_timer_handler_phys,
- "arch_timer", arch_timer_evt);
+ err = request_percpu_irq_flags(ppi, arch_timer_handler_phys,
+ IRQF_TIMER, "arch_timer",
+ arch_timer_evt);
if (err)
free_percpu_irq(arch_timer_ppi[PHYS_SECURE_PPI],
arch_timer_evt);
}
break;
case HYP_PPI:
- err = request_percpu_irq(ppi, arch_timer_handler_phys,
- "arch_timer", arch_timer_evt);
+ err = request_percpu_irq_flags(ppi, arch_timer_handler_phys,
+ IRQF_TIMER, "arch_timer",
+ arch_timer_evt);
break;
default:
BUG();
diff --git a/drivers/clocksource/arm_global_timer.c b/drivers/clocksource/arm_global_timer.c
index 123ed20..5a72ec1 100644
--- a/drivers/clocksource/arm_global_timer.c
+++ b/drivers/clocksource/arm_global_timer.c
@@ -302,8 +302,8 @@ static int __init global_timer_of_register(struct device_node *np)
goto out_clk;
}
- err = request_percpu_irq(gt_ppi, gt_clockevent_interrupt,
- "gt", gt_evt);
+ err = request_percpu_irq_flags(gt_ppi, gt_clockevent_interrupt,
+ IRQF_TIMER, "gt", gt_evt);
if (err) {
pr_warn("global-timer: can't register interrupt %d (%d)\n",
gt_ppi, err);
diff --git a/drivers/clocksource/exynos_mct.c b/drivers/clocksource/exynos_mct.c
index 670ff0f..a48ca0f 100644
--- a/drivers/clocksource/exynos_mct.c
+++ b/drivers/clocksource/exynos_mct.c
@@ -524,9 +524,10 @@ static int __init exynos4_timer_resources(struct device_node *np, void __iomem *
if (mct_int_type == MCT_INT_PPI) {
- err = request_percpu_irq(mct_irqs[MCT_L0_IRQ],
- exynos4_mct_tick_isr, "MCT",
- &percpu_mct_tick);
+ err = request_percpu_irq_flags(mct_irqs[MCT_L0_IRQ],
+ exynos4_mct_tick_isr,
+ IRQF_TIMER, "MCT",
+ &percpu_mct_tick);
WARN(err, "MCT: can't request IRQ %d (%d)\n",
mct_irqs[MCT_L0_IRQ], err);
} else {
diff --git a/drivers/clocksource/qcom-timer.c b/drivers/clocksource/qcom-timer.c
index ee358cd..8e876fc 100644
--- a/drivers/clocksource/qcom-timer.c
+++ b/drivers/clocksource/qcom-timer.c
@@ -174,8 +174,8 @@ static int __init msm_timer_init(u32 dgt_hz, int sched_bits, int irq,
}
if (percpu)
- res = request_percpu_irq(irq, msm_timer_interrupt,
- "gp_timer", msm_evt);
+ res = request_percpu_irq_flags(irq, msm_timer_interrupt,
+ IRQF_TIMER, "gp_timer", msm_evt);
if (res) {
pr_err("request_percpu_irq failed\n");
diff --git a/drivers/clocksource/time-armada-370-xp.c b/drivers/clocksource/time-armada-370-xp.c
index 4440aef..7405e14 100644
--- a/drivers/clocksource/time-armada-370-xp.c
+++ b/drivers/clocksource/time-armada-370-xp.c
@@ -309,10 +309,11 @@ static int __init armada_370_xp_timer_common_init(struct device_node *np)
/*
* Setup clockevent timer (interrupt-driven).
*/
- res = request_percpu_irq(armada_370_xp_clkevt_irq,
- armada_370_xp_timer_interrupt,
- "armada_370_xp_per_cpu_tick",
- armada_370_xp_evt);
+ res = request_percpu_irq_flags(armada_370_xp_clkevt_irq,
+ armada_370_xp_timer_interrupt,
+ IRQF_TIMER,
+ "armada_370_xp_per_cpu_tick",
+ armada_370_xp_evt);
/* Immediately configure the timer on the boot CPU */
if (res) {
pr_err("Failed to request percpu irq");
diff --git a/drivers/clocksource/timer-nps.c b/drivers/clocksource/timer-nps.c
index da1f798..195f039 100644
--- a/drivers/clocksource/timer-nps.c
+++ b/drivers/clocksource/timer-nps.c
@@ -256,9 +256,9 @@ static int __init nps_setup_clockevent(struct device_node *node)
return ret;
/* Needs apriori irq_set_percpu_devid() done in intc map function */
- ret = request_percpu_irq(nps_timer0_irq, timer_irq_handler,
- "Timer0 (per-cpu-tick)",
- &nps_clockevent_device);
+ ret = request_percpu_irq_flags(nps_timer0_irq, timer_irq_handler,
+ IRQF_TIMER, "Timer0 (per-cpu-tick)",
+ &nps_clockevent_device);
if (ret) {
pr_err("Couldn't request irq\n");
clk_disable_unprepare(clk);
diff --git a/include/linux/interrupt.h b/include/linux/interrupt.h
index 53144e7..8f44f23 100644
--- a/include/linux/interrupt.h
+++ b/include/linux/interrupt.h
@@ -152,8 +152,17 @@ struct irqaction {
unsigned long flags, const char *name, void *dev_id);
extern int __must_check
+request_percpu_irq_flags(unsigned int irq, irq_handler_t handler,
+ unsigned long flags, const char *devname,
+ void __percpu *percpu_dev_id);
+
+static inline int __must_check
request_percpu_irq(unsigned int irq, irq_handler_t handler,
- const char *devname, void __percpu *percpu_dev_id);
+ const char *devname, void __percpu *percpu_dev_id)
+{
+ return request_percpu_irq_flags(irq, handler, 0,
+ devname, percpu_dev_id);
+}
extern void free_irq(unsigned int, void *);
extern void free_percpu_irq(unsigned int, void __percpu *);
diff --git a/kernel/irq/manage.c b/kernel/irq/manage.c
index ae1c90f..1ba7734 100644
--- a/kernel/irq/manage.c
+++ b/kernel/irq/manage.c
@@ -1951,9 +1951,10 @@ int setup_percpu_irq(unsigned int irq, struct irqaction *act)
}
/**
- * request_percpu_irq - allocate a percpu interrupt line
+ * request_percpu_irq_flags - allocate a percpu interrupt line
* @irq: Interrupt line to allocate
* @handler: Function to be called when the IRQ occurs.
+ * @flags: Interrupt type flags (IRQF_TIMER only)
* @devname: An ascii name for the claiming device
* @dev_id: A percpu cookie passed back to the handler function
*
@@ -1966,8 +1967,9 @@ int setup_percpu_irq(unsigned int irq, struct irqaction *act)
* the handler gets called with the interrupted CPU's instance of
* that variable.
*/
-int request_percpu_irq(unsigned int irq, irq_handler_t handler,
- const char *devname, void __percpu *dev_id)
+int request_percpu_irq_flags(unsigned int irq, irq_handler_t handler,
+ unsigned long flags, const char *devname,
+ void __percpu *dev_id)
{
struct irqaction *action;
struct irq_desc *desc;
@@ -1981,12 +1983,15 @@ int request_percpu_irq(unsigned int irq, irq_handler_t handler,
!irq_settings_is_per_cpu_devid(desc))
return -EINVAL;
+ if (flags && flags != IRQF_TIMER)
+ return -EINVAL;
+
action = kzalloc(sizeof(struct irqaction), GFP_KERNEL);
if (!action)
return -ENOMEM;
action->handler = handler;
- action->flags = IRQF_PERCPU | IRQF_NO_SUSPEND;
+ action->flags = flags | IRQF_PERCPU | IRQF_NO_SUSPEND;
action->name = devname;
action->percpu_dev_id = dev_id;
@@ -2007,7 +2012,7 @@ int request_percpu_irq(unsigned int irq, irq_handler_t handler,
return retval;
}
-EXPORT_SYMBOL_GPL(request_percpu_irq);
+EXPORT_SYMBOL_GPL(request_percpu_irq_flags);
/**
* irq_get_irqchip_state - returns the irqchip state of a interrupt.
diff --git a/virt/kvm/arm/arch_timer.c b/virt/kvm/arm/arch_timer.c
index 35d7100..602e0a8 100644
--- a/virt/kvm/arm/arch_timer.c
+++ b/virt/kvm/arm/arch_timer.c
@@ -523,8 +523,9 @@ int kvm_timer_hyp_init(void)
host_vtimer_irq_flags = IRQF_TRIGGER_LOW;
}
- err = request_percpu_irq(host_vtimer_irq, kvm_arch_timer_handler,
- "kvm guest timer", kvm_get_running_vcpus());
+ err = request_percpu_irq_flags(host_vtimer_irq, kvm_arch_timer_handler,
+ IRQF_TIMER, "kvm guest timer",
+ kvm_get_running_vcpus());
if (err) {
kvm_err("kvm_arch_timer: can't request interrupt %d (%d)\n",
host_vtimer_irq, err);
--
1.9.1
^ permalink raw reply related
* [PATCH] arm64: pmuv3: Support v8.1 PMUv3 extension
From: Jayachandran C @ 2017-04-24 13:39 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20170424125747.GA5767@leverpostej>
On Mon, Apr 24, 2017 at 01:57:47PM +0100, Mark Rutland wrote:
> Hi,
>
> On Mon, Apr 24, 2017 at 11:31:43AM +0000, Jayachandran C wrote:
> > The PMUVer field can have a value 4 for PMUv3 which supports 16 bit
> > evtCount field (this is documented in ARM Architecture Reference Manual
> > Supplement ARMv8.1).
> >
> > The current check for PMUVer to be equal to 1 fails on ThunderX2 which
> > has value 4 in PMUVer field. Fix this.
> >
> > Signed-off-by: Jayachandran C <jnair@caviumnetworks.com>
> > ---
> >
> > This applies on top of the current arm64 tree and fixes a breakage due
> > to the ACPI perf patches.
>
> Sorry for the delay on this. I'm still awaiting an architectural
> clarification on how this field should be interpreted, as per my prior
> comments [1].
>
> I realise that's not much consolation here, so I'm happy to take an
> intermediate fix. One comment on that below.
I thought I would sent this out since it was getting very late in the cycle.
I don't mind waiting if you are planning to fix this, feel free to drop this
patch and integrate your version.
The v8.1 supplement is quite clear on the field definition:
PMUVer, bits [11:8]
....
Defined values are:
0000 Performance Monitors extension System registers not implemented.
0001 Performance Monitors extension System registers implemented, PMUv3.
0100 Performance Monitors extension System registers implemented, PMUv3, with a 16-bit
evtCount field, and if EL2 is implemented, the addition of the MDCR_EL2.HPMD bit.
1111 IMPLEMENTATION DEFINED form of performance monitors supported, PMUv3 not
supported.
All other values are reserved.
In ARMv8-A the permitted values are 0b0000, 0b0001 and 0b1111.
In ARMv8.1 the permitted values are 0b0000, 0b0100 and 0b1111.
I changed the code to strictly do this. We have to exclude 0xf, since that is not PMUv3.
And we cannot predict what the reserved values will represent, so it is best to skip them
until they are defined to be PMUv3 compatible.
> > arch/arm64/kernel/perf_event.c | 2 +-
> > 1 file changed, 1 insertion(+), 1 deletion(-)
> >
> > diff --git a/arch/arm64/kernel/perf_event.c b/arch/arm64/kernel/perf_event.c
> > index 98c7493..5388ed8 100644
> > --- a/arch/arm64/kernel/perf_event.c
> > +++ b/arch/arm64/kernel/perf_event.c
> > @@ -972,7 +972,7 @@ static void __armv8pmu_probe_pmu(void *info)
> > dfr0 = read_sysreg(id_aa64dfr0_el1);
> > pmuver = cpuid_feature_extract_unsigned_field(dfr0,
> > ID_AA64DFR0_PMUVER_SHIFT);
> > - if (pmuver != 1)
> > + if (pmuver != 1 && pmuver != 4)
> > return;
>
> Can we please make this:
>
> pmuver = cpuid_feature_extract_signed_field(dfr0,
> ID_AA64DFR0_PMUVER_SHIFT);
> if (pmuver < 1)
> return;
>
> With that, I'm happy to take this while we await further clarification.
Please see above, I don't really think this is better.
JC.
^ permalink raw reply
* support autofocus / autogain in libv4l2
From: Mauro Carvalho Chehab @ 2017-04-24 13:38 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20170424093059.GA20427@amd>
Hi Pavel,
Em Mon, 24 Apr 2017 11:30:59 +0200
Pavel Machek <pavel@ucw.cz> escreveu:
> Hi!
>
> For focus to be useful, we need autofocus implmented
> somewhere. Unfortunately, v4l framework does not seem to provide good
> place where to put autofocus. I believe, long-term, we'll need some
> kind of "video server" providing this kind of services.
>
> Anyway, we probably don't want autofocus in kernel (even through some
> cameras do it in hardware), and we probably don't want autofocus in
> each and every user application.
>
> So what remains is libv4l2.
IMO, the best place for autofocus is at libv4l2. Putting it on a
separate "video server" application looks really weird for me.
Btw, libv4l2 already has some autotools for auto gain and auto
white balance. See the implementation under:
lib/libv4lconvert/processing
The libv4l internal controls can be seen at:
lib/libv4lconvert/control/libv4lcontrol.h
The ones implemented by the processing part of the library are:
$ git grep v4lcontrol_get_ctrl lib/libv4lconvert/processing/
lib/libv4lconvert/processing/autogain.c: autogain = v4lcontrol_get_ctrl(data->control, V4LCONTROL_AUTOGAIN);
lib/libv4lconvert/processing/autogain.c: target = v4lcontrol_get_ctrl(data->control, V4LCONTROL_AUTOGAIN_TARGET);
lib/libv4lconvert/processing/gamma.c: int gamma = v4lcontrol_get_ctrl(data->control, V4LCONTROL_GAMMA);
lib/libv4lconvert/processing/gamma.c: gamma = v4lcontrol_get_ctrl(data->control, V4LCONTROL_GAMMA);
lib/libv4lconvert/processing/whitebalance.c: wb = v4lcontrol_get_ctrl(data->control, V4LCONTROL_WHITEBALANCE);
I guess it shouldn't be hard to add an extra processing module
there for auto-focus.
> Now, this is in no way clean or complete,
> and functionality provided by sdl.c and asciicam.c probably _should_
> be in application, but... I'd like to get the code out there.
>
> Oh and yes, I've canibalized decode_tm6000.c application instead of
> introducing my own. Autotools scare me, sorry.
Why replace decode_tm6000.c by something else? If you want to add another
test application, just place it on a new file.
I added a few notes together with the code, pointing the main things
I think it require changes, in order for me to do a better review
at the code. I didn't test nor tried to check the algorithms inside,
as the code, on its current state, requires rework and code cleanup.
>
> Regards,
> Pavel
>
> diff --git a/lib/libv4l2/asciicam.c b/lib/libv4l2/asciicam.c
> new file mode 100644
> index 0000000..5388967
> --- /dev/null
> +++ b/lib/libv4l2/asciicam.c
> @@ -0,0 +1,63 @@
> +/* gcc asciicam.c /usr/lib/i386-linux-gnu/libv4l2.so.0.0.0 -o asciicam
> + gcc asciicam.c /usr/lib/arm-linux-gnueabi/libv4l2.so.0 -o asciicam
> +
> +gcc -std=gnu99 -DHAVE_CONFIG_H -I. -I../.. -fvisibility=hidden -I../../lib/include -Wall -Wpointer-arith -D_GNU_SOURCE -I../../include -g -O2 asciicam.c libv4l2.c /usr/lib/arm-linux-gnueabi/libv4lconvert.so.0 log.c v4l2convert.c v4l2-plugin.c -o asciicam
> + */
> +#include <stdio.h>
> +#include <sys/types.h>
> +#include <sys/stat.h>
> +#include <fcntl.h>
> +
> +#include <linux/videodev2.h>
> +
> +#define SIZE 10*1024*1024
> +
> +char buf[SIZE];
> +
> +void main(void)
Please don't add a new application under lib/. It is fine if you want
some testing application, if the ones there aren't enough, but please
place it under contrib/test/.
You should likely take a look at v4l2grab first, as it could have
almost everything you would need.
> +{
> + int fd = v4l2_open("/dev/video2", O_RDWR);
Hardcoding /dev/video2 doesn't seem a good idea.
> + int i;
> + static struct v4l2_format fmt;
> +
> +#if 1
> + fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
> + fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_RGB24;
> + fmt.fmt.pix.field = V4L2_FIELD_INTERLACED;
> + fmt.fmt.pix.width = 640;
> + fmt.fmt.pix.height = 480;
> + if (fmt.fmt.pix.pixelformat != 'RGB3') /* v4l2_fourcc('R', 'G', 'B', '3'); */
> + printf("hmm. strange format?\n");
> +
> + printf("ioctl = %d\n", v4l2_ioctl(fd, VIDIOC_S_FMT, &fmt));
> +#endif
> +
> + for (i=0; i<500; i++) {
> + int num = v4l2_read(fd, buf, SIZE);
> + int y,x;
> +
> + printf("%d\n", num);
> +#if 0
> + for (y = 0; y < 25; y++) {
> + for (x = 0; x < 80; x++) {
> + int y1 = y * 480/25;
> + int x1 = x * 640/80;
> + int c = buf[fmt.fmt.pix.width*3*y1 + 3*x1] +
> + buf[fmt.fmt.pix.width*3*y1 + 3*x1 + 1] +
> + buf[fmt.fmt.pix.width*3*y1 + 3*x1 + 2];
> +
> + if (c < 30) c = ' ';
> + else if (c < 60) c = '.';
> + else if (c < 120) c = '_';
> + else if (c < 180) c = 'o';
> + else if (c < 300) c = 'x';
> + else if (c < 400) c = 'X';
> + else c = '#';
> +
> + printf("%c", c);
> + }
> + printf("\n");
> + }
IMHO, it would be better to use aalib. Btw, xawtv3 has a code example
using it, under:
console/ttv.c
As it already uses libv4l, prhaps you could use it, instead of adding
a new ascii app.
> +#endif
> + }
> +}
> diff --git a/lib/libv4l2/libv4l2-priv.h b/lib/libv4l2/libv4l2-priv.h
> index 343db5e..af740a7 100644
> --- a/lib/libv4l2/libv4l2-priv.h
> +++ b/lib/libv4l2/libv4l2-priv.h
> @@ -1,3 +1,4 @@
> +/* -*- c-file-style: "linux" -*- */
> /*
> # (C) 2008 Hans de Goede <hdegoede@redhat.com>
>
> @@ -70,6 +71,14 @@
> } while (0)
>
> #define MIN(a, b) (((a) < (b)) ? (a) : (b))
> +#define V4L2_MAX_SUBDEVS 16
> +
> +#define V4L2_MAX_FOCUS 10
> +struct v4l2_focus_step {
> + int num;
> + int sharpness[V4L2_MAX_FOCUS];
> + int position[V4L2_MAX_FOCUS];
> +};
>
> struct v4l2_dev_info {
> int fd;
> @@ -104,6 +113,14 @@ struct v4l2_dev_info {
> void *plugin_library;
> void *dev_ops_priv;
> const struct libv4l_dev_ops *dev_ops;
> + int subdev_fds[V4L2_MAX_SUBDEVS];
> + int exposure;
> + int frame;
> + int focus;
> +
> + /* Autofocus parameters */
> + int focus_frame, focus_exposure, focus_step, sh_prev;
> + struct v4l2_focus_step focus_step_params;
> };
>
> /* From v4l2-plugin.c */
> diff --git a/lib/libv4l2/libv4l2.c b/lib/libv4l2/libv4l2.c
> index 0ba0a88..3b84437 100644
> --- a/lib/libv4l2/libv4l2.c
> +++ b/lib/libv4l2/libv4l2.c
> @@ -1,3 +1,4 @@
> +/* -*- c-file-style: "linux" -*- */
Don't add editor-specific macros inside the code. Not everybody uses emacs.
> /*
> # (C) 2008 Hans de Goede <hdegoede@redhat.com>
>
> @@ -100,6 +101,13 @@ static struct v4l2_dev_info devices[V4L2_MAX_DEVICES] = {
> };
> static int devices_used;
>
> +#include "sdl.c"
> +
> +static struct sdl sdl;
> +
> +int v4l2_get_index(int fd);
> +void my_main(void);
> +
The above looks really odd. Why do you want to make libv4l2 dependent
on sdl?
> static int v4l2_ensure_convert_mmap_buf(int index)
> {
> if (devices[index].convert_mmap_buf != MAP_FAILED) {
> @@ -311,7 +319,409 @@ static int v4l2_queue_read_buffer(int index, int buffer_index)
> return 0;
> }
>
> -static int v4l2_dequeue_and_convert(int index, struct v4l2_buffer *buf,
> +static void v4l2_paint(char *buf, int x1, int y1, const struct v4l2_format *fmt)
> +{
> + int y, x;
> + int width = fmt->fmt.pix.width;
> + printf("width = %d\n", width);
> +
> + for (y = 0; y < 25; y++) {
> + for (x = 0; x < 80; x++) {
> + int c = buf[width*4*y1 + 4*x1] +
> + buf[width*4*y1 + 4*x1 + 1] +
> + buf[width*4*y1 + 4*x1 + 2];
> +
> + if (c < 30) c = ' ';
> + else if (c < 60) c = '.';
> + else if (c < 120) c = '_';
> + else if (c < 180) c = 'o';
> + else if (c < 300) c = 'x';
> + else if (c < 400) c = 'X';
> + else c = '#';
> +
> + printf("%c", c);
> + }
> + printf("\n");
> + }
> +}
Please don't add anything like that inside the library.
> +
> +#define SX 80
> +#define SY 25
> +#define BUCKETS 20
> +
> +static void v4l2_histogram(unsigned char *buf, int cdf[], struct v4l2_format *fmt)
> +{
> + for (int y = 0; y < fmt->fmt.pix.height; y+=19)
> + for (int x = 0; x < fmt->fmt.pix.width; x+=19) {
> + pixel p = buf_pixel(fmt, buf, x, y);
> +
> + int b;
> + /* HACK: we divide green by 2 to have nice picture, undo it here. */
> + b = p.r + 2*p.g + p.b;
> + b = (b * BUCKETS)/(256);
> + cdf[b]++;
> + }
> +}
> +
> +static long v4l2_sharpness(unsigned char *buf, struct v4l2_format *fmt)
> +{
> + int h = fmt->fmt.pix.height;
> + int w = fmt->fmt.pix.width;
> + long r = 0;
> +
> + for (int y = h/3; y < h-h/3; y+=h/9)
> + for (int x = w/3; x < w-w/3; x++) {
> + pixel p1 = buf_pixel(fmt, buf, x, y);
> + pixel p2 = buf_pixel(fmt, buf, x+2, y);
> +
> + int b1, b2;
> + /* HACK: we divide green by 2 to have nice picture, undo it here. */
> + b1 = p1.r + 2*p1.g + p1.b;
> + b2 = p2.r + 2*p2.g + p2.b;
> +
> + int v;
> + v = (b1-b2)*(b1-b2);
> + if (v > 36)
> + r+=v;
> + }
> +
> + return r;
> +}
IMO, the above belongs to a separate processing module under
lib/libv4lconvert/processing/
> +
> +int v4l2_set_exposure(int fd, int exposure)
> +{
> + int index = v4l2_get_index(fd);
> +
> + if (index == -1 || devices[index].convert == NULL) {
> + V4L2_LOG_ERR("v4l2_set_exposure called with invalid fd: %d\n", fd);
> + errno = EBADF;
> + return -1;
> + }
> +
> + struct v4l2_control ctrl;
> + ctrl.id = V4L2_CID_EXPOSURE;
> + ctrl.value = exposure;
> + if (ioctl(devices[index].subdev_fds[0], VIDIOC_S_CTRL, &ctrl) < 0) {
> + printf("Could not set exposure\n");
> + }
> + return 0;
> +}
Shouldn't it be together with lib/libv4lconvert/processing/autogain.c,
perhaps as an alternative implementation, if what's there is not enough?
> +
> +int v4l2_set_gain(int fd, int gain)
> +{
> + int index = v4l2_get_index(fd);
> +
> + if (index == -1 || devices[index].convert == NULL) {
> + V4L2_LOG_ERR("v4l2_set_exposure called with invalid fd: %d\n", fd);
> + errno = EBADF;
> + return -1;
> + }
> +
> + struct v4l2_control ctrl;
> + ctrl.id = 0x00980913;
Don't hardcode control numbers here.
> + ctrl.value = gain;
> + if (ioctl(devices[index].subdev_fds[0], VIDIOC_S_CTRL, &ctrl) < 0) {
> + printf("Could not set exposure\n");
> + }
> + return 0;
> +}
> +
> +int v4l2_set_focus(int fd, int diopt)
> +{
> + int index = v4l2_get_index(fd);
> +
> + if (index == -1 || devices[index].convert == NULL) {
> + V4L2_LOG_ERR("v4l2_set_focus called with invalid fd: %d\n", fd);
> + errno = EBADF;
> + return -1;
> + }
> +
> + struct v4l2_control ctrl;
> + ctrl.id = V4L2_CID_FOCUS_ABSOLUTE;
> + ctrl.value = diopt;
> + if (ioctl(devices[index].subdev_fds[1], VIDIOC_S_CTRL, &ctrl) < 0) {
> + printf("Could not set focus\n");
> + }
> + return 0;
> +}
> +
> +#define LESS 1
> +#define MID 0
> +#define MORE 2
> +
> +static void v4l2_start_focus_step(struct v4l2_focus_step *fs, int focus, int step)
> +{
> + int i;
> +
> + fs->num = 3;
> + for (i=0; i<V4L2_MAX_FOCUS; i++) {
> + fs->sharpness[i] = -1;
> + fs->position[i] = -1;
> + }
> +
> + fs->position[LESS] = focus - step;
> + fs->position[MID] = focus;
> + fs->position[MORE] = focus + step;
> +}
> +
> +static int v4l2_focus_get_best(struct v4l2_focus_step *fs)
> +{
> + int i, max = -1, maxi = -1;
> +
> + for (i=0; i<fs->num; i++)
> + if (max < fs->sharpness[i]) {
> + max = fs->sharpness[i];
> + maxi = i;
> + }
> +
> + return maxi;
> +}
> +
> +static void v4l2_auto_focus_continuous(struct v4l2_dev_info *m, int sh)
> +{
> + int f = m->frame - m->focus_frame;
> + int step;
> + const int max_step = 300, min_step = 20;
> + struct v4l2_focus_step *fs = &m->focus_step_params;
> +
> + if (m->focus_step == 0 || m->focus_step > max_step) {
> + printf("step reset -- max\n");
> + m->focus_step = max_step;
> + }
> + if (m->focus_step < min_step) {
> + printf("step reset -- 10 (%d)\n", m->focus_step);
> + m->focus_step = min_step;
> + /* It takes cca 5.7 seconds to achieve the focus:
> + 0.76user 0.30system 5.66 (0m5.661s) elapsed 18.72%CPU
> + */
The above note seems hardware dependent.
> + printf("Focused at %d\n", m->focus);
> + exit(0);
> + }
> +
> + step = m->focus_step;
> +
> + if (m->exposure != m->focus_exposure) {
> + m->focus_frame = m->frame;
> + m->focus_exposure = m->exposure;
> + v4l2_start_focus_step(fs, m->focus, m->focus_step);
> + return;
> + }
> + if (m->focus < step) {
> + m->focus = step;
> + }
> +
> + const int every = 3;
> + if (f%every)
> + return;
> +
> + {
> + int i = f/every;
> +
> + if (i == 0) {
> + printf("Can not happen?\n");
> + return;
> + }
> + i--;
> + if (i < fs->num)
> + v4l2_set_focus(m->fd, fs->position[i]);
> + if (i > 0)
> + fs->sharpness[i-1] = sh;
> + if (i < fs->num)
> + return;
> + }
> + int i;
> + for (i=0; i<fs->num; i++) {
> + printf("%d: %d | ", fs->position[i], fs->sharpness[i]);
> + }
You should probably print something only if log is enabled. Take a look
at lib/libv4l2/log.c. Same applies to similar printf() calls.
> + int best = v4l2_focus_get_best(fs);
> + if ((fs->sharpness[best] < m->sh_prev) && (m->focus_step < max_step)) {
> + m->focus_step *=2;
> + m->sh_prev = m->sh_prev * 0.9;
> + printf("step up %d\n", m->focus_step);
> + } else if (best == LESS) {
> + printf("less ");
> + m->focus -= step;
> + } else if (best == MORE) {
> + printf("more ");
> + m->focus += step;
> + } else {
> + m->sh_prev = fs->sharpness[MID];
> + m->focus_step = m->focus_step / 2;
> + printf("step %d ", m->focus_step);
> + }
> + m->focus_frame = m->frame;
> + v4l2_start_focus_step(fs, m->focus, m->focus_step);
> + printf("Focus now %d\n", m->focus);
> +}
> +
> +static void v4l2_start_focus_sweep(struct v4l2_focus_step *fs, int focus, int step)
> +{
> + int i;
> +
> + fs->num = V4L2_MAX_FOCUS;
> + for (i=0; i<V4L2_MAX_FOCUS; i++) {
> + fs->sharpness[i] = -1;
> + fs->position[i] = -1;
> + }
> +
> + int f = focus;
> + for (i=0; i<V4L2_MAX_FOCUS; i++) {
> + fs->position[i] = f;
> + f += step;
> + }
> +}
> +
> +static void v4l2_auto_focus_single(struct v4l2_dev_info *m, int sh)
> +{
> + int f = m->frame - m->focus_frame;
> + int step;
> + struct v4l2_focus_step *fs = &m->focus_step_params;
> +
> + if (m->focus_step == 0) {
> + printf("step reset -- max\n");
> + m->focus_step = 1;
> + }
> +
> + if (m->exposure != m->focus_exposure) {
> + m->focus_frame = m->frame;
> + m->focus_exposure = m->exposure;
> + v4l2_start_focus_sweep(fs, 0, 100);
> + return;
> + }
> +
> + const int every = 3;
> + if (f%every)
> + return;
> +
> + {
> + int i = f/every;
> +
> + if (i == 0) {
> + printf("Can not happen?\n");
> + return;
> + }
> + i--;
> + if (i < fs->num)
> + v4l2_set_focus(m->fd, fs->position[i]);
> + if (i > 0)
> + fs->sharpness[i-1] = sh;
> + if (i < fs->num)
> + return;
> + }
> +#if 0
> + int i;
> + for (i=0; i<fs->num; i++) {
> + printf("%d: %d | ", fs->position[i], fs->sharpness[i]);
> + }
> +#endif
> + int best = v4l2_focus_get_best(fs);
> + m->focus_frame = m->frame;
> + switch (m->focus_step) {
> + case 1:
> + printf("Best now %d / %d\n", fs->position[best], fs->sharpness[best]);
> + v4l2_start_focus_sweep(fs, fs->position[best] - 50, 10);
> + m->focus_step = 2;
> + break;
> + case 2:
> + printf("Best now %d / %d\n", fs->position[best], fs->sharpness[best]);
> + v4l2_start_focus_sweep(fs, fs->position[best] - 10, 2);
> + m->focus_step = 3;
> + break;
> + case 3:
> + printf("Best now %d / %d\n", fs->position[best], fs->sharpness[best]);
> + printf("done.\n");
> + exit(0);
> + }
> +}
> +
> +
> +static void v4l2_auto_exposure(int index, struct v4l2_buffer *buf)
> +{
> + struct v4l2_format *fmt;
> + int cdf[BUCKETS] = { 0, };
> + int i;
> +
> + fmt = &devices[index].src_fmt;
> +
> + v4l2_histogram(devices[index].frame_pointers[buf->index], cdf, fmt);
> +
> +#if 0
> + printf("hist: ");
> + for (i = 0; i<BUCKETS; i++)
> + printf("%d ", cdf[i]);
> + printf("\n");
> +#endif
> + for (i=1; i<BUCKETS; i++)
> + cdf[i] += cdf[i-1];
> +
> + int b = BUCKETS;
> + int brightPixels = cdf[b-1] - cdf[b-8];
> + int targetBrightPixels = cdf[b-1]/50;
> + int maxSaturatedPixels = cdf[b-1]/200;
> + int saturatedPixels = cdf[b-1] - cdf[b-2];
> + // how much should I change brightness by
> + float adjustment = 1.0f;
> +#if 0
> + printf( "AutoExposure: totalPixels: %d,"
> + "brightPixels: %d, targetBrightPixels: %d,"
> + "saturatedPixels: %d, maxSaturatedPixels: %d\n",
> + cdf[b-1], brightPixels, targetBrightPixels,
> + saturatedPixels, maxSaturatedPixels);
> +#endif
> +
> + if (saturatedPixels > maxSaturatedPixels) {
> + // first don't let things saturate too much
> + adjustment = 1.0f - ((float)(saturatedPixels - maxSaturatedPixels))/cdf[b-1];
> + } else if (brightPixels < (targetBrightPixels - (saturatedPixels * 4))) {
> + // increase brightness to try and hit the desired number of well exposed pixels
> + int l = b-6;
> + while (brightPixels < targetBrightPixels && l > 0) {
> + brightPixels += cdf[l];
> + brightPixels -= cdf[l-1];
> + l--;
> + }
> +
> + // that level is supposed to be at b-11;
> + adjustment = ((float) (b-6+1))/(l+1);
> + } else {
> + // we're not oversaturated, and we have enough bright pixels. Do nothing.
> + }
> +
> + {
> + float limit = 4;
> + if (adjustment > limit) { adjustment = limit; }
> + if (adjustment < 1/limit) { adjustment = 1/limit; }
> + }
> +
> + if (!devices[index].exposure)
> + devices[index].exposure = 1;
> + devices[index].exposure *= adjustment;
> + if (adjustment != 1.)
> + printf( "AutoExposure: adjustment: %f exposure %d\n", adjustment, devices[index].exposure);
> +
> + v4l2_set_exposure(devices[index].fd, devices[index].exposure);
> +}
> +
> +static void v4l2_statistics(int index, struct v4l2_buffer *buf)
> +{
> + unsigned char *b;
> + struct v4l2_format *fmt;
> +
> + fmt = &devices[index].src_fmt;
> + b = devices[index].frame_pointers[buf->index];
> +
> + if (!(devices[index].frame%3))
> + v4l2_auto_exposure(index, buf);
> +
> + int sh = v4l2_sharpness(b, fmt);
> + v4l2_auto_focus_single(&devices[index], sh);
> +
> + devices[index].frame++;
> + if (!(devices[index].frame%4))
> + sdl_render(&sdl, b, fmt);
> +}
> +
> +int v4l2_dequeue_and_convert(int index, struct v4l2_buffer *buf,
> unsigned char *dest, int dest_size)
> {
> const int max_tries = V4L2_IGNORE_FIRST_FRAME_ERRORS + 1;
> @@ -345,6 +755,13 @@ static int v4l2_dequeue_and_convert(int index, struct v4l2_buffer *buf,
> errno = -EINVAL;
> return -1;
> }
> +
> +#if 1
> + v4l2_statistics(index, buf);
> +#endif
> +#if 0
> + /* This is rather major eater of CPU time. CPU time goes from 80% to 4%
> + when conversion is disabled. */
>
> result = v4lconvert_convert(devices[index].convert,
> &devices[index].src_fmt, &devices[index].dest_fmt,
> @@ -352,7 +769,7 @@ static int v4l2_dequeue_and_convert(int index, struct v4l2_buffer *buf,
> buf->bytesused, dest ? dest : (devices[index].convert_mmap_buf +
> buf->index * devices[index].convert_mmap_frame_size),
> dest_size);
> -
> +#endif
> if (devices[index].first_frame) {
> /* Always treat convert errors as EAGAIN during the first few frames, as
> some cams produce bad frames at the start of the stream
> @@ -789,18 +1206,24 @@ no_capture:
>
> /* Note we always tell v4lconvert to optimize src fmt selection for
> our default fps, the only exception is the app explicitly selecting
> - a fram erate using the S_PARM ioctl after a S_FMT */
> + a frame rate using the S_PARM ioctl after a S_FMT */
> if (devices[index].convert)
> v4lconvert_set_fps(devices[index].convert, V4L2_DEFAULT_FPS);
> v4l2_update_fps(index, &parm);
>
> + devices[index].subdev_fds[0] = SYS_OPEN("/dev/video_sensor", O_RDWR, 0);
> + devices[index].subdev_fds[1] = SYS_OPEN("/dev/video_focus", O_RDWR, 0);
> +
> + printf("Sensor: %d, focus: %d\n", devices[index].subdev_fds[0],
> + devices[index].subdev_fds[1]);
> +
> V4L2_LOG("open: %d\n", fd);
>
> return fd;
> }
>
> /* Is this an fd for which we are emulating v4l1 ? */
> -static int v4l2_get_index(int fd)
> +int v4l2_get_index(int fd)
> {
> int index;
>
> @@ -823,6 +1246,10 @@ int v4l2_close(int fd)
> {
> int index, result;
>
> + if (fd == -2) {
> + my_main();
> + }
> +
That looks a hack!
> index = v4l2_get_index(fd);
> if (index == -1)
> return SYS_CLOSE(fd);
> @@ -1782,3 +2209,65 @@ int v4l2_get_control(int fd, int cid)
> (qctrl.maximum - qctrl.minimum) / 2) /
> (qctrl.maximum - qctrl.minimum);
> }
> +
> +void v4l2_debug(void)
> +{
> + printf("debug\n");
> +}
> +
> +/* ------------------------------------------------------------------ */
> +
> +#define SIZE 10*1024*1024
> +
> +char buf[SIZE];
> +
> +void my_main(void)
> +{
> + int fd = v4l2_open("/dev/video2", O_RDWR);
> + int i;
> + static struct v4l2_format fmt;
> +
> + fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
> + fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_RGB24;
> + fmt.fmt.pix.field = V4L2_FIELD_INTERLACED;
> + fmt.fmt.pix.width = 640;
> + fmt.fmt.pix.height = 480;
> +
> + v4l2_set_gain(fd, 300);
> + v4l2_set_exposure(fd, 10000);
> + v4l2_set_focus(fd, 0);
> +
> +
> + printf("ioctl = %d\n", v4l2_ioctl(fd, VIDIOC_S_FMT, &fmt));
> +
> + printf("capture is %d, %d\n", fmt.fmt.pix.width, fmt.fmt.pix.height);
> + /* factor == 2 still fits window, but very slow. factor == 6 .. */
> + /* factor needs to be odd, otherwise ... fun with BA10 format. */
> + sdl_init(&sdl, fmt.fmt.pix.width, fmt.fmt.pix.height, 5);
> +
> + v4l2_debug();
> +
> + /* In 800x600 "raw" mode, this should take cca 17.8 seconds (without
> + sdl output. CPU usage should be cca 5% without conversion). That's 28 fps.
> + (benchmark with i<500)
> + */
> + for (i=0; i<50000; i++) {
> + int num = v4l2_read(fd, buf, SIZE);
> +
> + if (i==490) {
> + printf("Focus to closest.... -------------------\n");
> + v4l2_set_focus(fd, 99999);
> + }
> +
> +#if 0
> + v4l2_paint(buf, 640/80, 480/25, &fmt);
> +#endif
> + /* Over USB connection, rendering every single frame slows
> + execution down from 23 seconds to 36 seconds. */
> +#if 0
> + if (!(i%4))
> + sdl_render(&sdl, buf, &fmt);
> +#endif
> + }
> +
> +}
> diff --git a/lib/libv4l2/sdl.c b/lib/libv4l2/sdl.c
> new file mode 100644
> index 0000000..17a8d24
> --- /dev/null
> +++ b/lib/libv4l2/sdl.c
> @@ -0,0 +1,530 @@
> +/* -*- c-file-style: "linux" -*- */
> +/* SDL support.
> +
> + Copyright 2017 Pavel Machek, LGPL
> +*/
> +
> +#include <SDL2/SDL.h>
> +#include <SDL2/SDL_image.h>
If you're adding a SDL-specific application, you'll need to add the
needed autoconf bits to detect if SDL devel package is installed,
auto-disabling it if not.
Yet, I don't think that SDL should be part of the library, but,
instead, part of some application.
> +
> +struct sdl {
> + SDL_Window *window;
> + SDL_Surface *liveview, *screen;
> +
> + int wx, wy;
> + int sx, sy;
> + int bx, by;
> + int factor;
> + float focus, gain, exposure, do_focus, do_exposure;
> +};
> +
> +#if 0
> +void loop(void) {
> + int done;
> + SDL_Event event;
> +
> + while(!done){ //While program isn't done
> + while(SDL_PollEvent(&event)){ //Poll events
> + switch(event.type){ //Check event type
> + case SDL_QUIT: //User hit the X (or equivelent)
> + done = true; //Make the loop end
> + break; //We handled the event
> + } //Finished with current event
> + } //Done with all events for now
> + } //Program done, exited
> +}
> +#endif
> +
> +typedef struct {
> + Uint8 r;
> + Uint8 g;
> + Uint8 b;
> + Uint8 alpha;
> +} pixel;
> +
> +#define d_raw 1
> +
> +void sfc_put_pixel(SDL_Surface* liveview, int x, int y, pixel *p)
> +{
> + Uint32* p_liveview = (Uint32*)liveview->pixels;
> + p_liveview += y*liveview->w+x;
> + *p_liveview = SDL_MapRGBA(liveview->format,p->r,p->g,p->b,p->alpha);
> +}
> +
> +#if 0
> +int pix_exposure(float x)
> +{
> + return sy*x / 199410.0;
> +}
> +
> +int pix_gain(float x)
> +{
> + return sy*x / 16.0;
> +}
> +
> +int render_statistics(SDL_Surface* liveview)
> +{
> + pixel white;
> + white.r = (Uint8)0xff;
> + white.g = (Uint8)0xff;
> + white.b = (Uint8)0xff;
> + white.alpha = (Uint8)128;
> +
> + //printf("Stats: focus %d, gain %d, exposure %d\n", focus, gain, exposure);
> +
> + for (int x=0; x<sx && x<sx*focus; x++)
> + put_pixel(liveview, x, 0, &white);
> +
> + for (int y=0; y<sy && y<pix_gain(gain); y++)
> + put_pixel(liveview, 0, y, &white);
> +
> + for (int y=0; y<sy && y<pix_exposure(exposure); y++)
> + put_pixel(liveview, sx-1, y, &white);
> +
> + for (int x=0; x<sx; x++)
> + put_pixel(liveview, x, sy-1, &white);
> +
> + return 0;
> +}
> +#endif
> +
> +void sdl_begin_paint(struct sdl *m) {
> + //Fill the surface white
> + SDL_FillRect(m->liveview, NULL, SDL_MapRGB( m->liveview->format, 0, 0, 0 ));
> +
> + SDL_LockSurface(m->liveview);
> +}
> +
> +void sdl_finish_paint(struct sdl *m) {
> + SDL_UnlockSurface(m->liveview);
> + SDL_Rect rcDest = { m->bx, m->by, m->sx, m->sy };
> +
> + SDL_BlitSurface(m->liveview, NULL, m->screen, &rcDest);
> + //Update the surface
> + SDL_UpdateWindowSurfaceRects(m->window, &rcDest, 1);
> +}
> +
> +void sdl_paint_image(struct sdl *m, char **xpm, int x, int y) {
> + SDL_Surface *image = IMG_ReadXPMFromArray(xpm);
> + if (!image) {
> + printf("IMG_Load: %s\n", IMG_GetError());
> + exit(1);
> + }
> +
> + int x_pos = x - image->w/2, y_pos = y - image->h/2;
> +
> + SDL_Rect rcDest = { x_pos, y_pos, image->w, image->h };
> + int r = SDL_BlitSurface ( image, NULL, m->screen, &rcDest );
> +
> + if (r) {
> + printf("Error blitting: %s\n", SDL_GetError());
> + exit(1);
> + }
> + SDL_FreeSurface ( image );
> +}
> +
> +void sdl_paint_ui(struct sdl *m) {
> + static char *wait_xpm[] = {
> + "16 9 2 1",
> + "# c #ffffff",
> + ". c #000000",
> + "....########....",
> + ".....#....#.....",
> + ".....#....#.....",
> + "......#..#......",
> + ".......##.......",
> + "......#..#......",
> + ".....#....#.....",
> + ".....#....#.....",
> + "....########....",
> + };
> +
> + static char *ok_xpm[] = {
> + "16 9 2 1",
> + "# c #ffffff",
> + ". c #000000",
> + "...............#",
> + "............###.",
> + "..........##....",
> + "#.......##......",
> + ".#.....#........",
> + "..#...#.........",
> + "..#..#..........",
> + "...##...........",
> + "...#............",
> + };
> +
> + static char *exit_xpm[] = {
> + "16 9 2 1",
> + "x c #ffffff",
> + ". c #000000",
> + "....x......x....",
> + ".....x....x.....",
> + "......x..x......",
> + ".......xx.......",
> + ".......xx.......",
> + "......x..x......",
> + ".....x....x.....",
> + "....x......x....",
> + "................",
> + };
> +
> + static char *f1m_xpm[] = {
> + "16 9 2 1",
> + "# c #ffffff",
> + ". c #000000",
> + "....##..........",
> + "...#.#..........",
> + "..#..#..........",
> + ".....#...#.#.##.",
> + ".....#...##.#..#",
> + ".....#...#..#..#",
> + ".....#...#..#..#",
> + ".....#...#..#..#",
> + "................",
> + };
> +
> + static char *f25cm_xpm[] = {
> + "16 9 2 1",
> + "x c #ffffff",
> + ". c #000000",
> + ".xxx..xxxx......",
> + "x...x.x.........",
> + "...x..xxx.......",
> + "..x......x..xx.x",
> + ".x.......x.x.xxx",
> + "xxxxx.xxx...xxxx",
> + "................",
> + "................",
> + "................",
> + };
> +
> + static char *iso400_xpm[] = {
> + "16 12 2 1",
> + "x c #ffffff",
> + ". c #000000",
> + "x..x.xxxx.xxxx..",
> + "x..x.x..x.x..x..",
> + "xxxx.x..x.x..x..",
> + "...x.x..x.x..x..",
> + "...x.xxxx.xxxx..",
> + "................",
> + ".x..xx..x.......",
> + ".x.x...x.x......",
> + ".x..x..x.x......",
> + ".x...x.x.x......",
> + ".x.xx...x.......",
> + "................",
> + };
> +
> + static char *time_1_100_xpm[] = {
> + "16 12 2 1",
> + "x c #ffffff",
> + ". c #000000",
> + ".x....x.........",
> + ".x...x..........",
> + ".x..x...........",
> + ".x.x............",
> + "................",
> + "..x.xxxx.xxxx...",
> + "..x.x..x.x..x...",
> + "..x.x..x.x..x...",
> + "..x.x..x.x..x...",
> + "..x.x..x.x..x...",
> + "..x.xxxx.xxxx...",
> + "................",
> + };
> +
> + static char *time_1_10_xpm[] = {
> + "16 12 2 1",
> + "x c #ffffff",
> + ". c #000000",
> + ".x....x.........",
> + ".x...x..........",
> + ".x..x...........",
> + ".x.x............",
> + "................",
> + "..x.xxxx........",
> + "..x.x..x........",
> + "..x.x..x........",
> + "..x.x..x........",
> + "..x.x..x........",
> + "..x.xxxx........",
> + "................",
> + };
> +
> + static char *af_xpm[] = {
> + "16 12 2 1",
> + "x c #ffffff",
> + ". c #000000",
> + ".....xxxxxxx....",
> + ".....x..........",
> + ".....x..........",
> + ".x...xxxxx......",
> + "x.x..x..........",
> + "xxx..x..........",
> + "x.x..x..........",
> + "x.x..x..........",
> + "................",
> + "................",
> + "................",
> + "................",
> + };
> +
> + static char *ae_xpm[] = {
> + "16 12 2 1",
> + "x c #ffffff",
> + ". c #000000",
> + ".....xxxxxxx....",
> + ".....x..........",
> + ".....x..........",
> + ".x...xxxxx......",
> + "x.x..x..........",
> + "xxx..x..........",
> + "x.x..x..........",
> + "x.x..xxxxxxx....",
> + "................",
> + "................",
> + "................",
> + "................",
> + };
> +
> + static char *not_xpm[] = {
> + "16 12 2 1",
> + "x c #ffffff",
> + ". c #000000",
> + "......xxxxx.....",
> + "....xx.....xx...",
> + "...x.........x..",
> + "..x........xx.x.",
> + "..x......xx...x.",
> + ".x.....xx......x",
> + ".x...xx........x",
> + "..xxx.........x.",
> + "..x...........x.",
> + "...x.........x..",
> + "....xx.....xx...",
> + "......xxxxx.....",
> + };
> +
> + static char *empty_xpm[] = {
> + "16 12 2 1",
> + "x c #ffffff",
> + ". c #000000",
> + "................",
> + "................",
> + "................",
> + "................",
> + "................",
> + "................",
> + "................",
> + "................",
> + "................",
> + "................",
> + "................",
> + "................",
> + };
> +
> + SDL_FillRect(m->screen, NULL, SDL_MapRGB( m->liveview->format, 0, 0, 0 ));
> + sdl_paint_image(m, wait_xpm, m->wx/2, m->wy/2);
> + sdl_paint_image(m, ok_xpm, m->wx-m->bx/2, m->wy-m->by/2);
> + sdl_paint_image(m, exit_xpm, m->bx/2, m->wy-m->by/2);
> + sdl_paint_image(m, f1m_xpm, m->bx+m->sx/20, m->by/2);
> + sdl_paint_image(m, f25cm_xpm, m->bx+m->sx/5, m->by/2);
> +
> + sdl_paint_image(m, af_xpm, m->bx/2, m->by/2);
> + if (!m->do_focus) {
> + sdl_paint_image(m, not_xpm, 16+m->bx/2, m->by/2);
> + }
> + sdl_paint_image(m, ae_xpm, m->wx-m->bx/2, m->by/2);
> + if (!m->do_exposure) {
> + sdl_paint_image(m, not_xpm, 16+m->bx/2, m->by/2);
> + }
> +
> +#if 0
> + sdl_paint_image(m, time_1_100_xpm, m->wx-m->bx/2, m->by+pix_exposure(10000));
> + sdl_paint_image(m, time_1_10_xpm, m->wx-m->bx/2, m->by+pix_exposure(100000));
> + sdl_paint_image(m, iso400_xpm, m->bx/2, m->by+pix_gain(4.0));
> +#endif
> +
> + SDL_UpdateWindowSurface(m->window);
> +}
> +
> +void fmt_print(struct v4l2_format *fmt)
> +{
> + int f;
> + printf("Format: %dx%d. ", fmt->fmt.pix.width, fmt->fmt.pix.height);
> + printf("%x ", fmt->fmt.pix.pixelformat);
> + f = fmt->fmt.pix.pixelformat;
> + for (int i=0; i<4; i++) {
> + printf("%c", f & 0xff);
> + f >>= 8;
> + }
> + printf("\n");
> +}
> +
> +pixel buf_pixel(struct v4l2_format *fmt, unsigned char *buf, int x, int y)
> +{
> + pixel p = { 0, 0, 0, 0 };
> + int pos = x + y*fmt->fmt.pix.width;
> + int b;
> +
> + p.alpha = 128;
> +
> + switch (fmt->fmt.pix.pixelformat) {
> + case '01AB': /* BA10, aka GRBG10,
> + https://www.linuxtv.org/downloads/v4l-dvb-apis-new/uapi/v4l/pixfmt-srggb10.html?highlight=ba10
> + */
> + b = buf[pos*2];
> + b += buf[pos*2+1] << 8;
> + b /= 4;
> + if ((y % 2) == 0) {
> + switch (x % 2) {
> + case 0: p.g = b/2; break;
> + case 1: p.r = b; break;
> + }
> + } else {
> + switch (x % 2) {
> + case 0: p.b = b; break;
> + case 1: p.g = b/2; break;
> + }
> + }
> + break;
> +
> + case V4L2_PIX_FMT_RGB24:
> + pos *= 4;
> + p.r = buf[pos];
> + p.g = buf[pos+1];
> + p.b = buf[pos+2];
> + break;
> +
> + default:
> + printf("Wrong pixel format!\n");
> + fmt_print(fmt);
> + exit(1);
> + }
> +
> + return p;
> +}
> +
> +void sdl_render(struct sdl *m, unsigned char *buf, struct v4l2_format *fmt)
> +{
> + if (!m->window)
> + return;
> + sdl_begin_paint(m);
> + /* do your rendering here */
> +
> + for (int y = 0; y < m->sy; y++)
> + for (int x = 0; x < m->sx; x++) {
> + pixel p = buf_pixel(fmt, buf, x*m->factor, y*m->factor);
> + p.alpha = 128;
> + sfc_put_pixel(m->liveview, x, y, &p);
> + }
> +
> + //render_statistics(liveview);
> +
> + sdl_finish_paint(m);
> +}
> +
> +#if 0
> +void imageStatistics(FCam::Image img)
> +{
> + int sx, sy;
> +
> + sx = img.size().width;
> + sy = img.size().height;
> + printf("Image is %d x %d, ", sx, sy);
> +
> + printf("(%d) ", img.brightness(sx/2, sy/2));
> +
> + int dark = 0;
> + int bright = 0;
> + int total = 0;
> +#define RATIO 10
> + for (int y = sy/(2*RATIO); y < sy; y += sy/RATIO)
> + for (int x = sx/(2*RATIO); x < sx; x += sx/RATIO) {
> + int br = img.brightness(x, y);
> +
> + if (!d_raw) {
> + /* It seems real range is 60 to 218 */
> + if (br > 200)
> + bright++;
> + if (br < 80)
> + dark++;
> + } else {
> + /* there's a lot of noise, it seems black is commonly 65..71,
> + bright is cca 1023 */
> + if (br > 1000)
> + bright++;
> + if (br < 75)
> + dark++;
> + }
> + total++;
> + }
> +
> + printf("%d dark %d bri,", dark, bright);
> +
> + long sharp = 0;
> + for (int y = sy/3; y < 2*(sy/3); y+=sy/12) {
> + int b = -1;
> + for (int x = sx/3; x < 2*(sx/3); x++) {
> + int b2;
> + b2 = img.brightness(x, y/2);
> + if (b != -1)
> + sharp += (b-b2) * (b-b2);
> + b = b2;
> + }
> + }
> + printf("sh %d\n", sharp);
> +}
> +#endif
> +
> +
> +void sdl_init(struct sdl *m, int _sx, int _sy, int _factor)
> +{
> + printf("Initing SDL\n");
> +
> + if (SDL_Init(SDL_INIT_VIDEO) < 0) {
> + printf("Could not init SDL\n");
> + exit(1);
> + }
> +
> + atexit(SDL_Quit);
> +
> + m->wx = 800;
> + m->wy = 400;
> +
> + m->window = SDL_CreateWindow( "Camera", SDL_WINDOWPOS_UNDEFINED,
> + SDL_WINDOWPOS_UNDEFINED, m->wx, m->wy,
> + SDL_WINDOW_SHOWN );
> + if (m->window == NULL) {
> + printf( "Window could not be created! SDL_Error: %s\n", SDL_GetError() );
> + }
> +
> + m->screen = SDL_GetWindowSurface(m->window);
> + if (!m->screen) {
> + printf("Couldn't create liveview\n");
> + exit(1);
> + }
> +
> + m->sx = _sx;
> + m->sy = _sy;
> + m->factor = _factor;
> +
> + m->sx /= m->factor;
> + m->sy /= m->factor;
> +
> + m->focus = 0;
> + m->gain = 0;
> + m->exposure = 0;
> +
> + m->bx = (m->wx-m->sx)/2;
> + m->by = (m->wy-m->sy)/2;
> +
> + m->liveview = SDL_CreateRGBSurface(0,m->sx,m->sy,32,0,0,0,0);
> + if (!m->liveview) {
> + printf("Couldn't create liveview\n");
> + exit(1);
> + }
> + sdl_paint_ui(m);
> +}
> diff --git a/lib/libv4lconvert/libv4lconvert.c b/lib/libv4lconvert/libv4lconvert.c
> index d3d8936..7521ec8 100644
> --- a/lib/libv4lconvert/libv4lconvert.c
> +++ b/lib/libv4lconvert/libv4lconvert.c
> @@ -1205,6 +1205,8 @@ static int v4lconvert_convert_pixfmt(struct v4lconvert_data *data,
> v4lconvert_swap_uv(src, dest, fmt);
> break;
> }
> + default:
> + /* This is bad, fixme? */
> break;
>
> case V4L2_PIX_FMT_YVU420:
> @@ -1228,6 +1230,8 @@ static int v4lconvert_convert_pixfmt(struct v4lconvert_data *data,
> case V4L2_PIX_FMT_YVU420:
> memcpy(dest, src, width * height * 3 / 2);
> break;
> + default:
> + /* This is bad, fixme? */
> }
> break;
>
> diff --git a/utils/decode_tm6000/Makefile.am b/utils/decode_tm6000/Makefile.am
> index ac4e85e..f059e3c 100644
> --- a/utils/decode_tm6000/Makefile.am
> +++ b/utils/decode_tm6000/Makefile.am
> @@ -1,4 +1,4 @@
> bin_PROGRAMS = decode_tm6000
> decode_tm6000_SOURCES = decode_tm6000.c
> -decode_tm6000_LDADD = ../libv4l2util/libv4l2util.la
> +decode_tm6000_LDADD = ../libv4l2/libv4l2.la
> decode_tm6000_LDFLAGS = $(ARGP_LIBS)
> diff --git a/utils/decode_tm6000/decode_tm6000.c b/utils/decode_tm6000/decode_tm6000.c
> index 4bffbdd..fda7e3b 100644
> --- a/utils/decode_tm6000/decode_tm6000.c
> +++ b/utils/decode_tm6000/decode_tm6000.c
Everything below it is completely wrong!
> @@ -1,365 +1,16 @@
> -/*
> - decode_tm6000.c - decode multiplexed format from TM5600/TM6000 USB
> +/* gcc asciicam.c /usr/lib/i386-linux-gnu/libv4l2.so.0.0.0 -o asciicam
> + gcc asciicam.c /usr/lib/arm-linux-gnueabi/libv4l2.so.0 -o asciicam
>
> - Copyright (C) 2007 Mauro Carvalho Chehab <mchehab@infradead.org>
> -
> - 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 version 2.
> -
> - 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, write to the Free Software
> - Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA.
> +gcc -std=gnu99 -DHAVE_CONFIG_H -I. -I../.. -fvisibility=hidden -I../../lib/include -Wall -Wpointer-arith -D_GNU_SOURCE -I../../include -g -O2 asciicam.c libv4l2.c /usr/lib/arm-linux-gnueabi/libv4lconvert.so.0 log.c v4l2convert.c v4l2-plugin.c -o asciicam
> */
> -#include "../libv4l2util/v4l2_driver.h"
> #include <stdio.h>
> -#include <string.h>
> -#include <argp.h>
> -#include <unistd.h>
> #include <sys/types.h>
> #include <sys/stat.h>
> #include <fcntl.h>
> -#include <stdlib.h>
> -#include <errno.h>
> -
> -const char *argp_program_version="decode_tm6000 version 0.0.1";
> -const char *argp_program_bug_address="Mauro Carvalho Chehab <mchehab@infradead.org>";
> -const char doc[]="Decodes tm6000 proprietary format streams";
> -const struct argp_option options[] = {
> - {"verbose", 'v', 0, 0, "enables debug messages", 0},
> - {"device", 'd', "DEV", 0, "uses device for reading", 0},
> - {"output", 'o', "FILE", 0, "outputs raw stream to a file", 0},
> - {"input", 'i', "FILE", 0, "parses a file, instead of a device", 0},
> - {"freq", 'f', "Freq", 0, "station frequency, in MHz (default is 193.25)", 0},
> - {"nbufs", 'n', "quant",0, "number of video buffers", 0},
> - {"audio", 'a', 0, 0, "outputs audio on stdout", 0},
> - {"read", 'r', 0, 0, "use read() instead of mmap method", 0},
> - { 0, 0, 0, 0, 0, 0 }
> -};
> -
> -static char outbuf[692224];
> -static int debug=0, audio=0, use_mmap=1, nbufs=4;
> -static float freq_mhz=193.25;
> -static char *devicename="/dev/video0";
> -static char *filename=NULL;
> -static enum {
> - NORMAL,
> - INPUT,
> - OUTPUT
> -} mode = NORMAL;
> -
> -static FILE *fout;
> -
> -//const char args_doc[]="ARG1 ARG2";
> -
> -static error_t parse_opt (int key, char *arg, struct argp_state *state)
> -{
> - switch (key) {
> - case 'a':
> - audio++;
> - break;
> - case 'r':
> - use_mmap=0;
> - break;
> - case 'v':
> - debug++;
> - break;
> - case 'd':
> - devicename=arg;
> - break;
> - case 'i':
> - case 'o':
> - if (mode!=NORMAL) {
> - argp_error(state,"You can't use input/output options simultaneously.\n");
> - break;
> - }
> - if (key=='i')
> - mode=INPUT;
> - else
> - mode=OUTPUT;
> -
> - filename=arg;
> - break;
> - case 'f':
> - freq_mhz=atof(arg);
> - break;
> - case 'n':
> - nbufs=atoi(arg);
> - if (nbufs<2)
> - nbufs=2;
> - break;
> - default:
> - return ARGP_ERR_UNKNOWN;
> - }
> - return 0;
> -}
> -
> -static struct argp argp = {
> - .options=options,
> - .parser=parse_opt,
> - .args_doc=NULL,
> - .doc=doc,
> -};
>
> -#define TM6000_URB_MSG_LEN 180
> -enum {
> - TM6000_URB_MSG_VIDEO=1,
> - TM6000_URB_MSG_AUDIO,
> - TM6000_URB_MSG_VBI,
> - TM6000_URB_MSG_PTS,
> - TM6000_URB_MSG_ERR,
> -};
> -
> -const char *tm6000_msg_type[]= {
> - "unknown(0)", //0
> - "video", //1
> - "audio", //2
> - "vbi", //3
> - "pts", //4
> - "err", //5
> - "unknown(6)", //6
> - "unknown(7)", //7
> -};
> -
> -#define dprintf(fmt,arg...) \
> - if (debug) fprintf(stderr, fmt, ##arg)
> -
> -static int recebe_buffer (struct v4l2_buffer *v4l2_buf, struct v4l2_t_buf *buf)
> -{
> - dprintf("Received %zd bytes\n", buf->length);
> -fflush(stdout);
> - memcpy (outbuf,buf->start,buf->length);
> - return buf->length;
> -}
> -
> -
> -static int prepare_read (struct v4l2_driver *drv)
> -{
> - struct v4l2_format fmt;
> - double freq;
> - int rc;
> -
> - memset (drv,0,sizeof(*drv));
> -
> - if (v4l2_open (devicename, 1,drv)<0) {
> - perror ("Error opening dev");
> - return -1;
> - }
> -
> - memset (&fmt,0,sizeof(fmt));
> -
> - uint32_t pixelformat=V4L2_PIX_FMT_TM6000;
> -
> - if (v4l2_gettryset_fmt_cap (drv,V4L2_SET,&fmt, 720, 480,
> - pixelformat,V4L2_FIELD_ANY)) {
> - perror("set_input to tm6000 raw format");
> - return -1;
> - }
> -
> - if (freq_mhz) {
> - freq=freq_mhz * 1000 * 1000;
> - rc=v4l2_getset_freq (drv,V4L2_SET, &freq);
> - if (rc<0)
> - printf ("Cannot set freq to %.3f MHz\n",freq_mhz);
> - }
> -
> - if (use_mmap) {
> - printf("Preparing for receiving frames on %d buffers...\n",nbufs);
> - fflush (stdout);
> -
> - rc=v4l2_mmap_bufs(drv, nbufs);
> - if (rc<0) {
> - printf ("Cannot mmap %d buffers\n",nbufs);
> - return -1;
> - }
> -
> -// v4l2_stop_streaming(&drv);
> - rc=v4l2_start_streaming(drv);
> - if (rc<0) {
> - printf ("Cannot start streaming\n");
> - return -1;
> - }
> - }
> - printf("Waiting for frames...\n");
> -
> - return 0;
> -}
> +#include <linux/videodev2.h>
>
> -static int read_stream (struct v4l2_driver *drv, int fd)
> +void main(void)
> {
> - if (use_mmap) {
> - fd_set fds;
> - struct timeval tv;
> - int r;
> -
> - FD_ZERO (&fds);
> - FD_SET (fd, &fds);
> -
> - /* Timeout. */
> - tv.tv_sec = 2;
> - tv.tv_usec = 0;
> -
> - r = select (fd + 1, &fds, NULL, NULL, &tv);
> - if (-1 == r) {
> - if (EINTR == errno) {
> - perror ("select");
> - return -errno;
> - }
> - }
> -
> - if (0 == r) {
> - fprintf (stderr, "select timeout\n");
> - return -errno;
> - }
> -
> - return v4l2_rcvbuf(drv, recebe_buffer);
> - } else {
> - int size=read(fd, outbuf, sizeof(outbuf));
> - return size;
> - }
> -
> - return 0;
> -}
> -
> -static int read_char (struct v4l2_driver *drv, int fd)
> -{
> - static int sizebuf=0;
> - static unsigned char *p=NULL;
> - unsigned char c;
> -
> - if (sizebuf<=0) {
> - sizebuf=read_stream(drv,fd);
> - if (sizebuf<=0)
> - return -1;
> - p=(unsigned char *)outbuf;
> - }
> - c=*p;
> - p++;
> - sizebuf--;
> -
> - return c;
> -}
> -
> -
> -int main (int argc, char*argv[])
> -{
> - int fd;
> - unsigned int i;
> - unsigned char buf[TM6000_URB_MSG_LEN], img[720*2*480];
> - unsigned int cmd, size, field, block, line, pos=0;
> - unsigned long header=0;
> - int linesize=720*2,skip=0;
> - struct v4l2_driver drv;
> -
> - argp_parse (&argp, argc, argv, 0, 0, 0);
> -
> - if (mode!=INPUT) {
> - if (prepare_read (&drv)<0)
> - return -1;
> - fd=drv.fd;
> - } else {
> - /*mode == INPUT */
> -
> - fd=open(filename,O_RDONLY);
> - if (fd<0) {
> - perror ("error opening a file for parsing");
> - return -1;
> - }
> - dprintf("file %s opened for parsing\n",filename);
> - use_mmap=0;
> - }
> -
> - if (mode==OUTPUT) {
> - fout=fopen(filename,"w");
> - if (!fout) {
> - perror ("error opening a file to write");
> - return -1;
> - }
> - dprintf("file %s opened for output\n",filename);
> -
> - do {
> - size=read_stream (&drv,fd);
> -
> - if (size<=0) {
> - close (fd);
> - return -1;
> - }
> - dprintf("writing %d bytes\n",size);
> - fwrite(outbuf,1, size,fout);
> -// fflush(fout);
> - } while (1);
> - }
> -
> -
> - while (1) {
> - skip=0;
> - header=0;
> - do {
> - int c;
> - c=read_char (&drv,fd);
> - if (c<0) {
> - perror("read");
> - return -1;
> - }
> -
> - header=(header>>8)&0xffffff;
> - header=header|(c<<24);
> - skip++;
> - } while ( (((header>>24)&0xff) != 0x47) );
> -
> - /* split the header fields */
> - size = (((header & 0x7e)<<1) -1) * 4;
> - block = (header>>7) & 0xf;
> - field = (header>>11) & 0x1;
> - line = (header>>12) & 0x1ff;
> - cmd = (header>>21) & 0x7;
> -
> - /* Read the remaining buffer */
> - for (i=0;i<sizeof(buf);i++) {
> - int c;
> - c=read_char (&drv,fd);
> - if (c<0) {
> - perror("read");
> - return -1;
> - }
> - buf[i]=c;
> - }
> -
> - /* FIXME: Mounts the image as field0+field1
> - * It should, instead, check if the user selected
> - * entrelaced or non-entrelaced mode
> - */
> - pos=((line<<1)+field)*linesize+
> - block*TM6000_URB_MSG_LEN;
> -
> - /* Prints debug info */
> - dprintf("0x%08x (skip %d), %s size=%d, line=%d, field=%d, block=%d\n",
> - (unsigned int)header, skip,
> - tm6000_msg_type[cmd],
> - size, line, field, block);
> -
> - /* Don't allow to write out of the buffer */
> - if (pos+sizeof(buf) > sizeof(img))
> - cmd = TM6000_URB_MSG_ERR;
> -
> - /* handles each different URB message */
> - switch(cmd) {
> - case TM6000_URB_MSG_VIDEO:
> - /* Fills video buffer */
> - memcpy(buf,&img[pos],sizeof(buf));
> - case TM6000_URB_MSG_AUDIO:
> - if (audio)
> - fwrite(buf,sizeof(buf),1,stdout);
> -// case TM6000_URB_MSG_VBI:
> -// case TM6000_URB_MSG_PTS:
> - break;
> - }
> - }
> - close(fd);
> - return 0;
> + v4l2_close(-2);
> }
>
Thanks,
Mauro
^ permalink raw reply
* [RFC PATCH 0/3] arm64: queued spinlocks and rw-locks
From: Will Deacon @ 2017-04-24 13:36 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <a4f067df-4c22-5c90-d70a-809903c60296@codeaurora.org>
On Wed, Apr 12, 2017 at 01:04:55PM -0400, Adam Wallis wrote:
> On 4/10/2017 5:35 PM, Yury Norov wrote:
> > The patch of Jan Glauber enables queued spinlocks on arm64. I rebased it on
> > latest kernel sources, and added a couple of fixes to headers to apply it
> > smoothly.
> >
> > Though, locktourture test shows significant performance degradation in the
> > acquisition of rw-lock for read on qemu:
> >
> > Before After
> > spin_lock-torture: 38957034 37076367 -4.83
> > rw_lock-torture W: 5369471 18971957 253.33
> > rw_lock-torture R: 6413179 3668160 -42.80
> >
>
> On our 48 core QDF2400 part, I am seeing huge improvements with these patches on
> the torture tests. The improvements go up even further when I apply Jason Low's
> MCS Spinlock patch: https://lkml.org/lkml/2016/4/20/725
Does the QDF2400 implement the large system extensions? If so, how do the
queued lock implementations compare to the LSE-based ticket locks?
Will
^ permalink raw reply
* [PATCH] arm64: prefetch: Change assembly to be compatible with gcc and clang
From: Will Deacon @ 2017-04-24 13:34 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20170420084206.GB31436@leverpostej>
On Thu, Apr 20, 2017 at 09:42:07AM +0100, Mark Rutland wrote:
> On Wed, Apr 19, 2017 at 02:22:11PM -0700, Matthias Kaehlcke wrote:
> > clang fails to build with the current code:
> >
> > arch/arm64/include/asm/processor.h:172:15: error: invalid operand in
> > inline asm: 'prfm pldl1keep, ${0:a}'
> >
> > Apparently clang does not support the 'a' modifier. Change the
> > constraint from 'p' ('An operand that is a valid memory address is
> > allowed') to 'Q' ('A memory address which uses a single base register
> > with no offset'), which works for both gcc and clang.
>
> It looks like the current %a0 template and p constraint were inherited
> from arch/arm, as they've been there from day one on arm64.
>
> Looking at the arch/arm history, the "a" operand modifier and "p"
> constraint were introduced in commit:
>
> 16f719de62809e22 ("[ARM] 5196/1: fix inline asm constraints for preload")
>
> ... so as to avoid GCC assuming prefetch of a pointer implied it was not
> NULL. Until that point, we'd used no operand modifier and "o"
> constraint.
>
> It's not clear to me whether "o", "p", and "Q" constraints differ in
> this regard on AArch64, or if the issue regarding NULL is still
> relevant. The GCC docs say the "p" constraint is used for "a valid
> memory address", which does sound like it shouldn't be NULL.
>
> Otherwise, this does look consistent with what we do elsewhere.
I really don't like using 'Q' here, for two reasons:
1. It means we likely allocate a register where we don't need to, because
we're going to need to use [Xn] as the addressing mode, which means
adding any immediate offsets.
2. As you mention, 16f719de62809e22 says that GCC will use this as an
indication that the address is non-NULL.
We also can't just remove the 'a', because that will result in assembly
failures. I haven't dug into exactly why, but I suspect it's because "p"
can generate a label, which then won't assemble if surrounded by '[]'.
Will
^ permalink raw reply
* [PATCH] arm64: fix the overlap between the kernel image and vmalloc address
From: zhong jiang @ 2017-04-24 13:28 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20170424104456.GA4343@leverpostej>
On 2017/4/24 18:44, Mark Rutland wrote:
> Hi,
>
> Thanks for reporting the problematic usage of is_vmalloc_addr() and
> vmalloc_to_page() here. That is a real problem that we need to address.
>
> On Mon, Apr 24, 2017 at 05:22:09PM +0800, zhongjiang wrote:
>> From: zhong jiang <zhongjiang@huawei.com>
>>
>> Recently, xiaojun report the following issue.
>>
>> [ 4544.984139] Unable to handle kernel paging request at virtual address ffff804392800000
>> [ 4544.991995] pgd = ffff80096745f000
>> [ 4544.995369] [ffff804392800000] *pgd=0000000000000000
>> [ 4545.425416] fc00: ffff0000081fdfd0 0000ffffa9c87440
>> [ 4545.430248] [<ffff0000083a1000>] __memcpy+0x100/0x180
>> [ 4545.435253] [<ffff000008270f64>] read_kcore+0x21c/0x3b0
>> [ 4545.440429] [<ffff00000826340c>] proc_reg_read+0x64/0x90
>> [ 4545.445691] [<ffff0000081fb83c>] __vfs_read+0x1c/0x108
>> [ 4545.450779] [<ffff0000081fcb28>] vfs_read+0x80/0x130
>> [ 4545.455696] [<ffff0000081fe014>] SyS_read+0x44/0xa0
>> [ 4545.460528] [<ffff000008082f30>] el0_svc_naked+0x24/0x28
>> [ 4545.465790] Code: d503201f d503201f d503201f d503201f (a8c12027)
>> [ 4545.471852] ---[ end trace 4d1897f94759f461 ]---
>> [ 4545.476435] note: cat[8976] exited with preempt_count 2
>>
>> I find the issue is introduced when applying commit f9040773b7bb
>> ("arm64: move kernel image to base of vmalloc area"). This patch
>> make the kernel image overlap with vmalloc area. It will result in
>> vmalloc area have the huge page table. but the vmalloc_to_page is
>> not realize the change. and the function is public to any arch.
> So the issue is that we have the callchain below for a kernel image
> address:
>
> read_kcore()
> ->is_vmalloc_or_module_addr() // returns true
> ->vread()
> -->aligned_vread()
> --->vmalloc_to_page()
>
> In is_vmalloc{,or_module}_addr() we just check the addr against
> VMALLOC_START and VMALLOC_END, so they will return true for a kernel
> image address.
>
> Then, we call vmalloc_to_page(). While this only handles mappings made
> at page granularity, the kernel image mapping may have used sections. So
> this tries a bogus walk to the pte level.
>
> Evidently, we assume that any memory in the vmalloc area (or module
> areas) is mapped at page granularity. Is that always the case?
I do not see a vmalloc area mapped in huge page table so far.
> AFAICT, memremap'd memory isn't necessarily, but vread() should skip
> that due to the VM_IOREMAP flag on the vma. The KASAN region should be
> below MODULES_VADDR on arm64. I'm not sure if there's anything else.
> Does it make sense to teach vmalloc_to_page() about section mappings?
I do not know it has any limitation. if it is ok , it is worthy to try.
> Should we special-case kernel image handling, e.g. with new
> is_kernel_image_addr() / kernel_image_to_page() helpers?
yes , it seems to the best way to implents it without performance back.
> Do we need to shuffle things around such that the kernel image is not
> between VMALLOC_START and VMALLOC_END?
>
>> I fix it by change the init mapping to make it keep the accordance
>> with vmalloc area mapping.
>>
>> Fixes: f9040773b7bb ("arm64: move kernel image to base of vmalloc area")
>> Reported-by: tan xiaojun <tanxiaojun@huawei.com>
>> Signed-off-by: zhong jiang <zhongjiang@huawei.com>
>> ---
>> arch/arm64/mm/mmu.c | 5 +++--
>> 1 file changed, 3 insertions(+), 2 deletions(-)
>>
>> diff --git a/arch/arm64/mm/mmu.c b/arch/arm64/mm/mmu.c
>> index 17243e4..2d8b34d 100644
>> --- a/arch/arm64/mm/mmu.c
>> +++ b/arch/arm64/mm/mmu.c
>> @@ -185,7 +185,7 @@ static void alloc_init_pmd(pud_t *pud, unsigned long addr, unsigned long end,
>>
>> /* try section mapping first */
>> if (((addr | next | phys) & ~SECTION_MASK) == 0 &&
>> - !page_mappings_only) {
>> + !page_mappings_only && !is_vmalloc_addr((void *)addr)) {
>> /*
>> * Set the contiguous bit for the subsequent group of
>> * PMDs if its size and alignment are appropriate.
>> @@ -256,7 +256,8 @@ static void alloc_init_pud(pgd_t *pgd, unsigned long addr, unsigned long end,
>> /*
>> * For 4K granule only, attempt to put down a 1GB block
>> */
>> - if (use_1G_block(addr, next, phys) && !page_mappings_only) {
>> + if (use_1G_block(addr, next, phys) && !page_mappings_only &&
>> + !is_vmalloc_addr((void *)addr)) {
>> pud_set_huge(pud, phys, prot);
>>
> This will force the kernel image mappings to use page granularity, which
> will come at a significant TLB pressure cost, and would be incredibly
> unfortunate.
you are right. it seems to so. The simplest way to add is_kernel_image_addr helper.
> I would rather we solved this through other means.
>
> Thanks,
> Mark.
> IMPORTANT NOTICE: The contents of this email and any attachments are confidential and may also be privileged. If you are not the intended recipient, please notify the sender immediately and do not disclose the contents to any other person, use it for any purpose, or store or copy the information in any medium. Thank you.
>
> The following patch is the implment. Any thought?
Signed-off-by: zhong jiang <zhongjiang@huawei.com>
diff --git a/include/linux/mm.h b/include/linux/mm.h
index b84615b..851ac35 100644
--- a/include/linux/mm.h
+++ b/include/linux/mm.h
@@ -475,6 +475,15 @@ static inline bool is_vmalloc_addr(const void *x)
return false;
#endif
}
+
+static inline bool is_kernel_image_addr(const void *x)
+{
+ unsigned long addr = (unsigned long)x;
+
+ return addr >= (unsigned long)_stext && addr < (unsigned long)_end;
+
+}
+
#ifdef CONFIG_MMU
extern int is_vmalloc_or_module_addr(const void *x);
#else
diff --git a/mm/vmalloc.c b/mm/vmalloc.c
index 3ca82d4..9a9ef65 100644
--- a/mm/vmalloc.c
+++ b/mm/vmalloc.c
@@ -229,6 +229,42 @@ int is_vmalloc_or_module_addr(const void *x)
return is_vmalloc_addr(x);
}
+static struct page *kernel_image_to_page(const void *kernel_addr, pgd_t *pgd)
+{
+ unsigned long addr = (unsigned long)kernel_addr;
+ struct page *page = NULL;
+ pud_t *pud;
+ pmd_t *pmd;
+ pte_t *pte;
+
+ if (pgd_none(*pgd))
+ goto out;
+
+ pud = pud_offset(pgd, addr);
+ if (pud_none(*pud))
+ goto out;
+
+ if (pud_sect(*pud))
+ return pud_page(*pud);
+
+ pmd = pmd_offset(*pmd, addr);
+ if (pmd_none(*pmd))
+ goto out;
+
+ if (pmd_sect(*pmd))
+ return pmd_page(*pmd);
+
+ pte = pte_offset_kernel(pmd, addr);
+ if (pte_none(*pte))
+ goto out;
+
+ page = pte_page(*pte);
+
+out:
+ return page;
+
+}
+
/*
* Walk a vmap address to the struct page it maps.
*/
@@ -244,6 +280,9 @@ struct page *vmalloc_to_page(const void *vmalloc_addr)
*/
VIRTUAL_BUG_ON(!is_vmalloc_or_module_addr(vmalloc_addr));
+ if (is_kernel_image_addr(vmalloc_addr))
+ return kernel_image_to_page(vmalloc_addr, pgd);
+
if (!pgd_none(*pgd)) {
pud_t *pud = pud_offset(pgd, addr);
if (!pud_none(*pud)) {
^ permalink raw reply related
* [PATCH RFC 0/5] *** SPI Slave mode support ***
From: Geert Uytterhoeven @ 2017-04-24 13:10 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <58FDF430.5090306@mentor.com>
Hi Jiada,
On Mon, Apr 24, 2017 at 2:48 PM, Jiada Wang <jiada_wang@mentor.com> wrote:
> On 04/24/2017 03:55 AM, Geert Uytterhoeven wrote:
>> On Fri, Apr 14, 2017 at 7:39 AM, Jiada Wang<jiada_wang@mentor.com> wrote:
>>> On 04/13/2017 12:47 PM, Geert Uytterhoeven wrote:
>>>> On Thu, Apr 13, 2017 at 2:59 PM, Mark Brown<broonie@kernel.org> wrote:
>>>>> On Thu, Apr 13, 2017 at 05:13:59AM -0700, jiada_wang at mentor.com wrote:
>>>>>> From: Jiada Wang<jiada_wang@mentor.com>
>>>>>>
>>>>>> v1:
>>>>>> add Slave mode support in SPI core
>>>>>> spidev create slave device when SPI controller work in slave mode
>>>>>> spi-imx support to work in slave mode
>>>>>
>>>>> Adding Geert who also had a series doing this in progress that was
>>>>> getting very near to being merged.
>>>>
>>>> Thank you!
>>>>
>>>> Actually my plan is to fix the last remaining issues and resubmit for
>>>> v4.13.
>>>
>>> I noticed your patch set for SPI slave support,
>>> (I am sure you can find out some of the change
>>> in this patch set is based on your work).
>>> we have similar requirement to add slave mode support to ecspi IP on imx6
>>> Soc.
>>>
>>> Our use case is to use spidev as an interface to communicate with
>>> external
>>> SPI master devices.
>>> meanwhile the SPI bus controller can also act as master device to send
>>> data
>>> to other
>>> SPI slave devices on the board.
>>
>> That sounds a bit hackish to me. SPI was never meant to be a multi-master
>> bus.
>> While it can be done, you will need external synchronization (signals) to
>> avoid conflicts between the SPI masters.
>
> It doesn't need to be a multi-master bus,
> for example A is master device for slave device B.
> while B has its own slave device C
> for each SPI connection A <=> B, and B <=> C, there is only one master
> device.
>
> and I think from use case point of view, it's very normal,
> one CPU upon receives command from external SPI master device,
> it writes data to its own slave device (EEPROM) connected to it.
So "A <=> B" and "B <=> C" are two distinct SPI buses?
Or do they share some signals?
Your comment seems to suggest otherwise:
> > > I found in your implementation, SPI bus controller is limited to either work in master mode or
> > > slave mode, is there any reasoning to not configure SPI mode based on SPI devices use case?
If they are distinct, it should work. Then B has two SPI controllers: one slave
controller controlled by A, and one master controller to control C.
Gr{oetje,eeting}s,
Geert
--
Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert at linux-m68k.org
In personal conversations with technical people, I call myself a hacker. But
when I'm talking to journalists I just say "programmer" or something like that.
-- Linus Torvalds
^ permalink raw reply
* [PATCH v2 1/5] dt-bindings: gpu: add bindings for the ARM Mali Midgard GPU
From: Rob Herring @ 2017-04-24 13:02 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <92e0492a-bd17-0171-10a3-8af5044ba6cc@collabora.com>
On Wed, Apr 12, 2017 at 4:36 AM, Guillaume Tucker
<guillaume.tucker@collabora.com> wrote:
> Hi Neil,
>
>
> On 12/04/17 09:48, Neil Armstrong wrote:
>>
>> Hi Guillaume,
>>
>> On 04/12/2017 10:25 AM, Guillaume Tucker wrote:
>>>
>>> Hi Heiko,
>>>
>>> On 11/04/17 21:52, Heiko St?bner wrote:
>>>>
>>>> Hi Guillaume,
>>>>
>>>> Am Dienstag, 11. April 2017, 18:40:37 CEST schrieb Guillaume Tucker:
>>>>>
>>>>> On 03/04/17 09:12, Neil Armstrong wrote:
>>>>>>
>>>>>> On 04/02/2017 09:59 AM, Guillaume Tucker wrote:
>>>>>>>
>>>>>>> +Optional:
>>>>>>> +
>>>>>>> +- clocks : Phandle to clock for the Mali Midgard device.
>>>>>>> +- clock-names : Shall be "clk_mali".
>>>>>>> +- mali-supply : Phandle to regulator for the Mali device. Refer to
>>>>>>> + Documentation/devicetree/bindings/regulator/regulator.txt for
>>>>>>> details.
>>>>>>> +- operating-points : Refer to
>>>>>>> Documentation/devicetree/bindings/power/opp.txt + for details.
>>>>>>
>>>>>>
>>>>>> Please add :
>>>>>> * Must be one of the following:
>>>>>> "arm,mali-t820"
>>>>>>
>>>>>> * And, optionally, one of the vendor specific compatible:
>>>>>> "amlogic,meson-gxm-mali"
>>>>>>
>>>>>> with my Ack for the amlogic platform.
>>>>>
>>>>>
>>>>> It seems to me that as long as the GPU architecture hasn't been
>>>>> modified (I don't think I've ever encountered such a case) then
>>>>> it has to be a standard ARM Mali type regardless of the SoC
>>>>> vendor. So unless a Mali-T820 in the Amlogic S912 SoC is not the
>>>>> same as a T820 in a different SoC, please forgive me but I don't
>>>>> understand why a vendor compatible string is needed. My main
>>>>> concern is that it's going to be very hard to keep that list
>>>>> up-to-date with all existing Midgard SoC variants. If do we need
>>>>> to add vendor compatible strings to correctly describe the
>>>>> hardware then I'm happy to add the amlogic one in my patch v3; I
>>>>> would just like to understand why that's necessary.
>>>>
>>>>
>>>> SoC vendors in most cases hook ip blocks into their socs in different
>>>> and often strange ways. After all it's not some discrete ic you solder
>>>> onto a board, but instead a part of the soc itself.
>>>
>>>
>>> Thanks for your explanation. I see, it's really about special
>>> things that are not supported by the standard Midgard kernel
>>> driver.
>>>
>>>> So in most cases you will have some hooks outside the actual gpu iospace
>>>> that can be used to tune different things about how the gpu interacts
>>>> with
>>>> the system. Which is probably also the reason the midgard kernel driver
>>>> has this ugly "platform" subdirectory for compile-time platform
>>>> selection.
>>>
>>>
>>> I see the "platform" directory approach as an old and deprecated
>>> way of supporting platforms, upstreaming the dt bindings goes in
>>> the direction of using solely the device tree to describe the GPU
>>> hardware (i.e. CONFIG_MALI_PLATFORM_DEVICETREE). If something
>>> quirky is needed in the platform, it should be possible to
>>> support it outside the GPU driver (platform devfreq etc...).
>>
>>
>> If this was so simple...
>>
>> This is why the "vendor" compatible is optional.
>>
>> And on another side, the binding were written by ARM, are may not be
>> compatible with how the mainline Linux handles these uses-cases.
>>
>> ARM added some tweaks to handle some weird integration using DT
>> properties,
>> but this should definitely go in platform specific code instead.
>
>
> OK, sorry I was approaching the issue from a completely different
> and somewhat more idealistic angle. My impression is that if the
> driver was in mainline then it would be maintained in such a way
> that vendor compatible strings would not be required, but this is
> all hypothetical.
>
> So in practice, I think I now better understand why vendor
> compatible strings may still be needed. And they're optional, so
> harmless to other platforms, so it's all fine with me :)
SoC specific compatibles are required. They are only optional for a
driver to use.
Rob
^ permalink raw reply
* [PATCH V3 2/2] ARM64: dts: hi6220-hikey: Add clock binding for the pmic mfd
From: Lee Jones @ 2017-04-24 13:01 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20170424091609.GA2137@mai>
On Mon, 24 Apr 2017, Daniel Lezcano wrote:
> On Mon, Apr 24, 2017 at 09:59:44AM +0100, Lee Jones wrote:
> > On Sat, 22 Apr 2017, Daniel Lezcano wrote:
> >
> > > On 22/04/2017 04:02, Stephen Boyd wrote:
> > > > On 04/17, Daniel Lezcano wrote:
> > > >> Signed-off-by: Daniel Lezcano <daniel.lezcano@linaro.org>
> > > >> ---
> > > >> Documentation/devicetree/bindings/mfd/hisilicon,hi655x.txt | 6 ++++++
> > > >> arch/arm64/boot/dts/hisilicon/hi6220-hikey.dts | 1 +
> > > >> 2 files changed, 7 insertions(+)
> > > >>
> > > >
> > > > I take it this goes through arm-soc? Not sure why I'm on To:
> > > > line.
> > >
> > > Probably it should go through Lee's tree.
> >
> > Unlikely.
> >
> > The document and the DTS change should really have gone separately,
> > but to save you from having to mess around so close to the merge window:
> >
> > Acked-by: Lee Jones <lee.jones@linaro.org>
>
> Thanks Lee.
>
> Usually, I take the DT changes (including doc) with the timers changes with the
> maintainer and Rob's blessing. So the DT and the driver changes are aligned in
> my tree and make the submission changes easier.
The binding docs go with the bindings, not the DTS changes.
> I agree mixing the patches for different destinations into a single patchset is
> fuzzy, I will take care next time to separate the patches.
--
Lee Jones
Linaro STMicroelectronics Landing Team Lead
Linaro.org ? Open source software for ARM SoCs
Follow Linaro: Facebook | Twitter | Blog
^ permalink raw reply
* [PATCH 2/2] pinctrl: add ZTE ZX pinctrl driver support
From: Shawn Guo @ 2017-04-24 13:01 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1493038873-18354-1-git-send-email-shawnguo@kernel.org>
From: Shawn Guo <shawn.guo@linaro.org>
The pin controller on ZTE ZX platforms is kinda of hybrid. It consists
of a main controller and an auxiliary one. For example, on ZX296718 SoC,
the main controller is TOP_PMM and the auxiliary one is AON_IOCFG. Both
controllers work together to control pin multiplexing and configuration.
For most of pins, the pinmux function is controlled by main controller
only, and this type of pins are meant by term 'TOP pins'. For other
pins, the pinmux is controlled by both main and auxiliary controllers,
as the available multiplexing functions for the pin spread in both
controllers. This type of pins are called 'AON pins'. Though pinmux
implementation is quite different, pinconf is same for both types of
pins. Both are controlled by auxiliary controller, i.e. AON_IOCFG on
ZX296718.
The patch adds the ZTE ZX core pinctrl driver to support this hybrid
pin controller as well as ZX296718 SoC specific pin data.
Signed-off-by: Shawn Guo <shawn.guo@linaro.org>
---
drivers/pinctrl/Kconfig | 1 +
drivers/pinctrl/Makefile | 1 +
drivers/pinctrl/zte/Kconfig | 13 +
drivers/pinctrl/zte/Makefile | 2 +
drivers/pinctrl/zte/pinctrl-zx.c | 445 ++++++++++++++
drivers/pinctrl/zte/pinctrl-zx.h | 105 ++++
drivers/pinctrl/zte/pinctrl-zx296718.c | 1027 ++++++++++++++++++++++++++++++++
7 files changed, 1594 insertions(+)
create mode 100644 drivers/pinctrl/zte/Kconfig
create mode 100644 drivers/pinctrl/zte/Makefile
create mode 100644 drivers/pinctrl/zte/pinctrl-zx.c
create mode 100644 drivers/pinctrl/zte/pinctrl-zx.h
create mode 100644 drivers/pinctrl/zte/pinctrl-zx296718.c
diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig
index 8f8c2af45781..97c202ba9a7a 100644
--- a/drivers/pinctrl/Kconfig
+++ b/drivers/pinctrl/Kconfig
@@ -304,6 +304,7 @@ source "drivers/pinctrl/ti/Kconfig"
source "drivers/pinctrl/uniphier/Kconfig"
source "drivers/pinctrl/vt8500/Kconfig"
source "drivers/pinctrl/mediatek/Kconfig"
+source "drivers/pinctrl/zte/Kconfig"
config PINCTRL_XWAY
bool
diff --git a/drivers/pinctrl/Makefile b/drivers/pinctrl/Makefile
index a251f439626f..dce8ff892b23 100644
--- a/drivers/pinctrl/Makefile
+++ b/drivers/pinctrl/Makefile
@@ -57,3 +57,4 @@ obj-y += ti/
obj-$(CONFIG_PINCTRL_UNIPHIER) += uniphier/
obj-$(CONFIG_ARCH_VT8500) += vt8500/
obj-$(CONFIG_PINCTRL_MTK) += mediatek/
+obj-$(CONFIG_PINCTRL_ZX) += zte/
diff --git a/drivers/pinctrl/zte/Kconfig b/drivers/pinctrl/zte/Kconfig
new file mode 100644
index 000000000000..0d97352a24ec
--- /dev/null
+++ b/drivers/pinctrl/zte/Kconfig
@@ -0,0 +1,13 @@
+config PINCTRL_ZX
+ bool
+ select PINMUX
+ select GENERIC_PINCONF
+ select GENERIC_PINCTRL_GROUPS
+ select GENERIC_PINMUX_FUNCTIONS
+
+config PINCTRL_ZX296718
+ bool "ZTE ZX296718 pinctrl driver"
+ depends on OF && ARCH_ZX
+ select PINCTRL_ZX
+ help
+ Say Y here to enable the ZX296718 pinctrl driver
diff --git a/drivers/pinctrl/zte/Makefile b/drivers/pinctrl/zte/Makefile
new file mode 100644
index 000000000000..c42e651d7a73
--- /dev/null
+++ b/drivers/pinctrl/zte/Makefile
@@ -0,0 +1,2 @@
+obj-$(CONFIG_PINCTRL_ZX) += pinctrl-zx.o
+obj-$(CONFIG_PINCTRL_ZX296718) += pinctrl-zx296718.o
diff --git a/drivers/pinctrl/zte/pinctrl-zx.c b/drivers/pinctrl/zte/pinctrl-zx.c
new file mode 100644
index 000000000000..2aca4e4b3f1c
--- /dev/null
+++ b/drivers/pinctrl/zte/pinctrl-zx.c
@@ -0,0 +1,445 @@
+/*
+ * Copyright (C) 2017 Sanechips Technology Co., Ltd.
+ * Copyright 2017 Linaro Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/io.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_device.h>
+#include <linux/pinctrl/pinctrl.h>
+#include <linux/pinctrl/pinconf-generic.h>
+#include <linux/pinctrl/pinmux.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+
+#include "../core.h"
+#include "../pinctrl-utils.h"
+#include "../pinmux.h"
+#include "pinctrl-zx.h"
+
+#define ZX_PULL_DOWN BIT(0)
+#define ZX_PULL_UP BIT(1)
+#define ZX_INPUT_ENABLE BIT(3)
+#define ZX_DS_SHIFT 4
+#define ZX_DS_MASK (0x7 << ZX_DS_SHIFT)
+#define ZX_DS_VALUE(x) (((x) << ZX_DS_SHIFT) & ZX_DS_MASK)
+#define ZX_SLEW BIT(8)
+
+struct zx_pinctrl {
+ struct pinctrl_dev *pctldev;
+ struct device *dev;
+ void __iomem *base;
+ void __iomem *aux_base;
+ spinlock_t lock;
+ struct zx_pinctrl_soc_info *info;
+};
+
+static int zx_dt_node_to_map(struct pinctrl_dev *pctldev,
+ struct device_node *np_config,
+ struct pinctrl_map **map, u32 *num_maps)
+{
+ return pinconf_generic_dt_node_to_map(pctldev, np_config, map,
+ num_maps, PIN_MAP_TYPE_INVALID);
+}
+
+static const struct pinctrl_ops zx_pinctrl_ops = {
+ .dt_node_to_map = zx_dt_node_to_map,
+ .dt_free_map = pinctrl_utils_free_map,
+ .get_groups_count = pinctrl_generic_get_group_count,
+ .get_group_name = pinctrl_generic_get_group_name,
+ .get_group_pins = pinctrl_generic_get_group_pins,
+};
+
+#define NONAON_MVAL 2
+
+static int zx_set_mux(struct pinctrl_dev *pctldev, unsigned int func_selector,
+ unsigned int group_selector)
+{
+ struct zx_pinctrl *zpctl = pinctrl_dev_get_drvdata(pctldev);
+ struct zx_pinctrl_soc_info *info = zpctl->info;
+ const struct pinctrl_pin_desc *pindesc = info->pins + group_selector;
+ struct zx_pin_data *data = pindesc->drv_data;
+ struct zx_mux_desc *mux = data->muxes;
+ u32 mask = (1 << data->width) - 1;
+ u32 offset = data->offset;
+ u32 bitpos = data->bitpos;
+ struct function_desc *func;
+ unsigned long flags;
+ u32 val, mval;
+
+ /* Skip reserved pin */
+ if (!data)
+ return -EINVAL;
+
+ func = pinmux_generic_get_function(pctldev, func_selector);
+ if (!func)
+ return -EINVAL;
+
+ while (mux->name) {
+ if (strcmp(mux->name, func->name) == 0)
+ break;
+ mux++;
+ }
+
+ /* Found mux value to be written */
+ mval = mux->muxval;
+
+ spin_lock_irqsave(&zpctl->lock, flags);
+
+ if (data->aon_pin) {
+ /*
+ * It's an AON pin, whose mux register offset and bit position
+ * can be caluculated from pin number. Each register covers 16
+ * pins, and each pin occupies 2 bits.
+ */
+ u16 aoffset = pindesc->number / 16 * 4;
+ u16 abitpos = (pindesc->number % 16) * 2;
+
+ if (mval & AON_MUX_FLAG) {
+ /*
+ * This is a mux value that needs to be written into
+ * AON pinmux register. Write it and then we're done.
+ */
+ val = readl(zpctl->aux_base + aoffset);
+ val &= ~(0x3 << abitpos);
+ val |= (mval & 0x3) << abitpos;
+ writel(val, zpctl->aux_base + aoffset);
+ } else {
+ /*
+ * It's a mux value that needs to be written into TOP
+ * pinmux register.
+ */
+ val = readl(zpctl->base + offset);
+ val &= ~(mask << bitpos);
+ val |= (mval & mask) << bitpos;
+ writel(val, zpctl->base + offset);
+
+ /*
+ * In this case, the AON pinmux register needs to be
+ * set up to select non-AON function.
+ */
+ val = readl(zpctl->aux_base + aoffset);
+ val &= ~(0x3 << abitpos);
+ val |= NONAON_MVAL << abitpos;
+ writel(val, zpctl->aux_base + aoffset);
+ }
+
+ } else {
+ /*
+ * This is a TOP pin, and we only need to set up TOP pinmux
+ * register and then we're done with it.
+ */
+ val = readl(zpctl->base + offset);
+ val &= ~(mask << bitpos);
+ val |= (mval & mask) << bitpos;
+ writel(val, zpctl->base + offset);
+ }
+
+ spin_unlock_irqrestore(&zpctl->lock, flags);
+
+ return 0;
+}
+
+static const struct pinmux_ops zx_pinmux_ops = {
+ .get_functions_count = pinmux_generic_get_function_count,
+ .get_function_name = pinmux_generic_get_function_name,
+ .get_function_groups = pinmux_generic_get_function_groups,
+ .set_mux = zx_set_mux,
+};
+
+static int zx_pin_config_get(struct pinctrl_dev *pctldev, unsigned int pin,
+ unsigned long *config)
+{
+ struct zx_pinctrl *zpctl = pinctrl_dev_get_drvdata(pctldev);
+ struct zx_pinctrl_soc_info *info = zpctl->info;
+ const struct pinctrl_pin_desc *pindesc = info->pins + pin;
+ struct zx_pin_data *data = pindesc->drv_data;
+ enum pin_config_param param = pinconf_to_config_param(*config);
+ u32 val;
+
+ /* Skip reserved pin */
+ if (!data)
+ return -EINVAL;
+
+ val = readl(zpctl->aux_base + data->coffset);
+ val = val >> data->cbitpos;
+
+ switch (param) {
+ case PIN_CONFIG_BIAS_PULL_DOWN:
+ val &= ZX_PULL_DOWN;
+ val = !!val;
+ if (val == 0)
+ return -EINVAL;
+ break;
+ case PIN_CONFIG_BIAS_PULL_UP:
+ val &= ZX_PULL_UP;
+ val = !!val;
+ if (val == 0)
+ return -EINVAL;
+ break;
+ case PIN_CONFIG_INPUT_ENABLE:
+ val &= ZX_INPUT_ENABLE;
+ val = !!val;
+ if (val == 0)
+ return -EINVAL;
+ break;
+ case PIN_CONFIG_DRIVE_STRENGTH:
+ val &= ZX_DS_MASK;
+ val = val >> ZX_DS_SHIFT;
+ break;
+ case PIN_CONFIG_SLEW_RATE:
+ val &= ZX_SLEW;
+ val = !!val;
+ break;
+ default:
+ return -ENOTSUPP;
+ }
+
+ *config = pinconf_to_config_packed(param, val);
+
+ return 0;
+}
+
+static int zx_pin_config_set(struct pinctrl_dev *pctldev, unsigned int pin,
+ unsigned long *configs, unsigned int num_configs)
+{
+ struct zx_pinctrl *zpctl = pinctrl_dev_get_drvdata(pctldev);
+ struct zx_pinctrl_soc_info *info = zpctl->info;
+ const struct pinctrl_pin_desc *pindesc = info->pins + pin;
+ struct zx_pin_data *data = pindesc->drv_data;
+ enum pin_config_param param;
+ u32 val, arg;
+ int i;
+
+ /* Skip reserved pin */
+ if (!data)
+ return -EINVAL;
+
+ val = readl(zpctl->aux_base + data->coffset);
+
+ for (i = 0; i < num_configs; i++) {
+ param = pinconf_to_config_param(configs[i]);
+ arg = pinconf_to_config_argument(configs[i]);
+
+ switch (param) {
+ case PIN_CONFIG_BIAS_PULL_DOWN:
+ val |= ZX_PULL_DOWN << data->cbitpos;
+ break;
+ case PIN_CONFIG_BIAS_PULL_UP:
+ val |= ZX_PULL_UP << data->cbitpos;
+ break;
+ case PIN_CONFIG_INPUT_ENABLE:
+ val |= ZX_INPUT_ENABLE << data->cbitpos;
+ break;
+ case PIN_CONFIG_DRIVE_STRENGTH:
+ val &= ~(ZX_DS_MASK << data->cbitpos);
+ val |= ZX_DS_VALUE(arg) << data->cbitpos;
+ break;
+ case PIN_CONFIG_SLEW_RATE:
+ if (arg)
+ val |= ZX_SLEW << data->cbitpos;
+ else
+ val &= ~ZX_SLEW << data->cbitpos;
+ break;
+ default:
+ return -ENOTSUPP;
+ }
+ }
+
+ writel(val, zpctl->aux_base + data->coffset);
+ return 0;
+}
+
+static const struct pinconf_ops zx_pinconf_ops = {
+ .pin_config_set = zx_pin_config_set,
+ .pin_config_get = zx_pin_config_get,
+ .is_generic = true,
+};
+
+static int zx_pinctrl_build_state(struct platform_device *pdev)
+{
+ struct zx_pinctrl *zpctl = platform_get_drvdata(pdev);
+ struct zx_pinctrl_soc_info *info = zpctl->info;
+ struct pinctrl_dev *pctldev = zpctl->pctldev;
+ struct function_desc *functions;
+ int nfunctions;
+ struct group_desc *groups;
+ int ngroups;
+ int i;
+
+ /* Every single pin composes a group */
+ ngroups = info->npins;
+ groups = devm_kzalloc(&pdev->dev, ngroups * sizeof(*groups),
+ GFP_KERNEL);
+ if (!groups)
+ return -ENOMEM;
+
+ for (i = 0; i < ngroups; i++) {
+ const struct pinctrl_pin_desc *pindesc = info->pins + i;
+ struct group_desc *group = groups + i;
+ int id = pindesc->number;
+
+ group->name = pindesc->name;
+ group->pins = &id;
+ radix_tree_insert(&pctldev->pin_group_tree, i, group);
+ }
+
+ pctldev->num_groups = ngroups;
+
+ /* Build function list from pin mux functions */
+ functions = devm_kzalloc(&pdev->dev, info->npins * sizeof(*functions),
+ GFP_KERNEL);
+ if (!functions)
+ return -ENOMEM;
+
+ nfunctions = 0;
+ for (i = 0; i < info->npins; i++) {
+ const struct pinctrl_pin_desc *pindesc = info->pins + i;
+ struct zx_pin_data *data = pindesc->drv_data;
+ struct zx_mux_desc *mux;
+
+ /* Reserved pins do not have a drv_data at all */
+ if (!data)
+ continue;
+
+ /* Loop over all muxes for the pin */
+ mux = data->muxes;
+ while (mux->name) {
+ struct function_desc *func = functions;
+
+ /* Search function list for given mux */
+ while (func->name) {
+ if (strcmp(mux->name, func->name) == 0) {
+ /* Function exists */
+ func->num_group_names++;
+ break;
+ }
+ func++;
+ }
+
+ if (!func->name) {
+ /* New function */
+ func->name = mux->name;
+ func->num_group_names = 1;
+ radix_tree_insert(&pctldev->pin_function_tree,
+ nfunctions++, func);
+ }
+
+ mux++;
+ }
+ }
+
+ pctldev->num_functions = nfunctions;
+ functions = krealloc(functions, nfunctions * sizeof(*functions),
+ GFP_KERNEL);
+
+ /* Find pin groups for every single function */
+ for (i = 0; i < info->npins; i++) {
+ const struct pinctrl_pin_desc *pindesc = info->pins + i;
+ struct zx_pin_data *data = pindesc->drv_data;
+ struct zx_mux_desc *mux;
+
+ if (!data)
+ continue;
+
+ mux = data->muxes;
+ while (mux->name) {
+ struct function_desc *func;
+ const char **group;
+ int j;
+
+ /* Find function for given mux */
+ for (j = 0; j < nfunctions; j++)
+ if (strcmp(functions[j].name, mux->name) == 0)
+ break;
+
+ func = functions + j;
+ if (!func->group_names) {
+ func->group_names = devm_kzalloc(&pdev->dev,
+ func->num_group_names *
+ sizeof(*func->group_names),
+ GFP_KERNEL);
+ if (!func->group_names)
+ return -ENOMEM;
+ }
+
+ group = func->group_names;
+ while (*group)
+ group++;
+ *group = pindesc->name;
+
+ mux++;
+ }
+ }
+
+ return 0;
+}
+
+int zx_pinctrl_init(struct platform_device *pdev,
+ struct zx_pinctrl_soc_info *info)
+{
+ struct pinctrl_desc *pctldesc;
+ struct zx_pinctrl *zpctl;
+ struct device_node *np;
+ struct resource *res;
+ int ret;
+
+ zpctl = devm_kzalloc(&pdev->dev, sizeof(*zpctl), GFP_KERNEL);
+ if (!zpctl)
+ return -ENOMEM;
+
+ spin_lock_init(&zpctl->lock);
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ zpctl->base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(zpctl->base))
+ return PTR_ERR(zpctl->base);
+
+ np = of_parse_phandle(pdev->dev.of_node, "zte,auxiliary-controller", 0);
+ if (!np) {
+ dev_err(&pdev->dev, "failed to find auxiliary controller\n");
+ return -ENODEV;
+ }
+
+ zpctl->aux_base = of_iomap(np, 0);
+ if (!zpctl->aux_base)
+ return -ENOMEM;
+
+ zpctl->dev = &pdev->dev;
+ zpctl->info = info;
+
+ pctldesc = devm_kzalloc(&pdev->dev, sizeof(*pctldesc), GFP_KERNEL);
+ if (!pctldesc)
+ return -ENOMEM;
+
+ pctldesc->name = dev_name(&pdev->dev);
+ pctldesc->owner = THIS_MODULE;
+ pctldesc->pins = info->pins;
+ pctldesc->npins = info->npins;
+ pctldesc->pctlops = &zx_pinctrl_ops;
+ pctldesc->pmxops = &zx_pinmux_ops;
+ pctldesc->confops = &zx_pinconf_ops;
+
+ zpctl->pctldev = devm_pinctrl_register(&pdev->dev, pctldesc, zpctl);
+ if (IS_ERR(zpctl->pctldev)) {
+ ret = PTR_ERR(zpctl->pctldev);
+ dev_err(&pdev->dev, "failed to register pinctrl: %d\n", ret);
+ return ret;
+ }
+
+ platform_set_drvdata(pdev, zpctl);
+
+ ret = zx_pinctrl_build_state(pdev);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to build state: %d\n", ret);
+ return ret;
+ }
+
+ dev_info(&pdev->dev, "initialized pinctrl driver\n");
+ return 0;
+}
diff --git a/drivers/pinctrl/zte/pinctrl-zx.h b/drivers/pinctrl/zte/pinctrl-zx.h
new file mode 100644
index 000000000000..bc67e2be0503
--- /dev/null
+++ b/drivers/pinctrl/zte/pinctrl-zx.h
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2017 Sanechips Technology Co., Ltd.
+ * Copyright 2017 Linaro Ltd.
+ *
+ * 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.
+ */
+
+#ifndef __PINCTRL_ZX_H
+#define __PINCTRL_ZX_H
+
+/**
+ * struct zx_mux_desc - hardware mux descriptor
+ * @name: mux function name
+ * @muxval: mux register bit value
+ */
+struct zx_mux_desc {
+ const char *name;
+ u8 muxval;
+};
+
+/**
+ * struct zx_pin_data - hardware per-pin data
+ * @aon_pin: whether it's an AON pin
+ * @offset: register offset within TOP pinmux controller
+ * @bitpos: bit position within TOP pinmux register
+ * @width: bit width within TOP pinmux register
+ * @coffset: pinconf register offset within AON controller
+ * @cbitpos: pinconf bit position within AON register
+ * @muxes: available mux function names and corresponding register values
+ *
+ * Unlike TOP pinmux and AON pinconf registers which are arranged pretty
+ * arbitrarily, AON pinmux register bits are well organized per pin id, and
+ * each pin occupies two bits, so that we can calculate the AON register offset
+ * and bit position from pin id. Thus, we only need to define TOP pinmux and
+ * AON pinconf register data for the pin.
+ */
+struct zx_pin_data {
+ bool aon_pin;
+ u16 offset;
+ u16 bitpos;
+ u16 width;
+ u16 coffset;
+ u16 cbitpos;
+ struct zx_mux_desc *muxes;
+};
+
+struct zx_pinctrl_soc_info {
+ const struct pinctrl_pin_desc *pins;
+ unsigned int npins;
+};
+
+#define TOP_PIN(pin, off, bp, wd, coff, cbp, ...) { \
+ .number = pin, \
+ .name = #pin, \
+ .drv_data = &(struct zx_pin_data) { \
+ .aon_pin = false, \
+ .offset = off, \
+ .bitpos = bp, \
+ .width = wd, \
+ .coffset = coff, \
+ .cbitpos = cbp, \
+ .muxes = (struct zx_mux_desc[]) { \
+ __VA_ARGS__, { } }, \
+ }, \
+}
+
+#define AON_PIN(pin, off, bp, wd, coff, cbp, ...) { \
+ .number = pin, \
+ .name = #pin, \
+ .drv_data = &(struct zx_pin_data) { \
+ .aon_pin = true, \
+ .offset = off, \
+ .bitpos = bp, \
+ .width = wd, \
+ .coffset = coff, \
+ .cbitpos = cbp, \
+ .muxes = (struct zx_mux_desc[]) { \
+ __VA_ARGS__, { } }, \
+ }, \
+}
+
+#define ZX_RESERVED(pin) PINCTRL_PIN(pin, #pin)
+
+#define TOP_MUX(_val, _name) { \
+ .name = _name, \
+ .muxval = _val, \
+}
+
+/*
+ * When the flag is set, it's a mux configuration for an AON pin that sits in
+ * AON register. Otherwise, it's one for AON pin but sitting in TOP register.
+ */
+#define AON_MUX_FLAG BIT(7)
+
+#define AON_MUX(_val, _name) { \
+ .name = _name, \
+ .muxval = _val | AON_MUX_FLAG, \
+}
+
+int zx_pinctrl_init(struct platform_device *pdev,
+ struct zx_pinctrl_soc_info *info);
+
+#endif /* __PINCTRL_ZX_H */
diff --git a/drivers/pinctrl/zte/pinctrl-zx296718.c b/drivers/pinctrl/zte/pinctrl-zx296718.c
new file mode 100644
index 000000000000..71efec17ee7e
--- /dev/null
+++ b/drivers/pinctrl/zte/pinctrl-zx296718.c
@@ -0,0 +1,1027 @@
+/*
+ * Copyright (C) 2017 Sanechips Technology Co., Ltd.
+ * Copyright 2017 Linaro Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_device.h>
+#include <linux/pinctrl/pinctrl.h>
+#include <linux/platform_device.h>
+
+#include "pinctrl-zx.h"
+
+#define TOP_REG0 0x00
+#define TOP_REG1 0x04
+#define TOP_REG2 0x08
+#define TOP_REG3 0x0c
+#define TOP_REG4 0x10
+#define TOP_REG5 0x14
+#define TOP_REG6 0x18
+#define TOP_REG7 0x1c
+#define TOP_REG8 0x20
+
+/*
+ * The pin numbering starts from AON pins with reserved ones included,
+ * so that register data like offset and bit position for AON pins can
+ * be calculated from pin number.
+ */
+enum zx296718_pin {
+ /* aon_pmm_reg_0 */
+ I2C3_SCL = 0,
+ I2C3_SDA = 1,
+ AON_RESERVED0 = 2,
+ AON_RESERVED1 = 3,
+ SEC_EN = 4,
+ UART0_RXD = 5,
+ UART0_TXD = 6,
+ IR_IN = 7,
+ SPI0_CLK = 8,
+ SPI0_CS = 9,
+ SPI0_TXD = 10,
+ SPI0_RXD = 11,
+ KEY_COL0 = 12,
+ KEY_COL1 = 13,
+ KEY_COL2 = 14,
+ KEY_ROW0 = 15,
+
+ /* aon_pmm_reg_1 */
+ KEY_ROW1 = 16,
+ KEY_ROW2 = 17,
+ HDMI_SCL = 18,
+ HDMI_SDA = 19,
+ JTAG_TCK = 20,
+ JTAG_TRSTN = 21,
+ JTAG_TMS = 22,
+ JTAG_TDI = 23,
+ JTAG_TDO = 24,
+ I2C0_SCL = 25,
+ I2C0_SDA = 26,
+ I2C1_SCL = 27,
+ I2C1_SDA = 28,
+ AON_RESERVED2 = 29,
+ AON_RESERVED3 = 30,
+ AON_RESERVED4 = 31,
+
+ /* aon_pmm_reg_2 */
+ SPI1_CLK = 32,
+ SPI1_CS = 33,
+ SPI1_TXD = 34,
+ SPI1_RXD = 35,
+ AON_RESERVED5 = 36,
+ AON_RESERVED6 = 37,
+ AUDIO_DET = 38,
+ SPDIF_OUT = 39,
+ HDMI_CEC = 40,
+ HDMI_HPD = 41,
+ GMAC_25M_OUT = 42,
+ BOOT_SEL0 = 43,
+ BOOT_SEL1 = 44,
+ BOOT_SEL2 = 45,
+ DEEP_SLEEP_OUT_N = 46,
+ AON_RESERVED7 = 47,
+
+ /* top_pmm_reg_0 */
+ GMII_GTX_CLK = 48,
+ GMII_TX_CLK = 49,
+ GMII_TXD0 = 50,
+ GMII_TXD1 = 51,
+ GMII_TXD2 = 52,
+ GMII_TXD3 = 53,
+ GMII_TXD4 = 54,
+ GMII_TXD5 = 55,
+ GMII_TXD6 = 56,
+ GMII_TXD7 = 57,
+ GMII_TX_ER = 58,
+ GMII_TX_EN = 59,
+ GMII_RX_CLK = 60,
+ GMII_RXD0 = 61,
+ GMII_RXD1 = 62,
+ GMII_RXD2 = 63,
+
+ /* top_pmm_reg_1 */
+ GMII_RXD3 = 64,
+ GMII_RXD4 = 65,
+ GMII_RXD5 = 66,
+ GMII_RXD6 = 67,
+ GMII_RXD7 = 68,
+ GMII_RX_ER = 69,
+ GMII_RX_DV = 70,
+ GMII_COL = 71,
+ GMII_CRS = 72,
+ GMII_MDC = 73,
+ GMII_MDIO = 74,
+ SDIO1_CLK = 75,
+ SDIO1_CMD = 76,
+ SDIO1_DATA0 = 77,
+ SDIO1_DATA1 = 78,
+ SDIO1_DATA2 = 79,
+
+ /* top_pmm_reg_2 */
+ SDIO1_DATA3 = 80,
+ SDIO1_CD = 81,
+ SDIO1_WP = 82,
+ USIM1_CD = 83,
+ USIM1_CLK = 84,
+ USIM1_RST = 85,
+
+ /* top_pmm_reg_3 */
+ USIM1_DATA = 86,
+ SDIO0_CLK = 87,
+ SDIO0_CMD = 88,
+ SDIO0_DATA0 = 89,
+ SDIO0_DATA1 = 90,
+ SDIO0_DATA2 = 91,
+ SDIO0_DATA3 = 92,
+ SDIO0_CD = 93,
+ SDIO0_WP = 94,
+
+ /* top_pmm_reg_4 */
+ TSI0_DATA0 = 95,
+ SPINOR_CLK = 96,
+ TSI2_DATA = 97,
+ TSI2_CLK = 98,
+ TSI2_SYNC = 99,
+ TSI2_VALID = 100,
+ SPINOR_CS = 101,
+ SPINOR_DQ0 = 102,
+ SPINOR_DQ1 = 103,
+ SPINOR_DQ2 = 104,
+ SPINOR_DQ3 = 105,
+ VGA_HS = 106,
+ VGA_VS = 107,
+ TSI3_DATA = 108,
+
+ /* top_pmm_reg_5 */
+ TSI3_CLK = 109,
+ TSI3_SYNC = 110,
+ TSI3_VALID = 111,
+ I2S1_WS = 112,
+ I2S1_BCLK = 113,
+ I2S1_MCLK = 114,
+ I2S1_DIN0 = 115,
+ I2S1_DOUT0 = 116,
+ SPI3_CLK = 117,
+ SPI3_CS = 118,
+ SPI3_TXD = 119,
+ NAND_LDO_MS18_SEL = 120,
+
+ /* top_pmm_reg_6 */
+ SPI3_RXD = 121,
+ I2S0_MCLK = 122,
+ I2S0_BCLK = 123,
+ I2S0_WS = 124,
+ I2S0_DIN0 = 125,
+ I2S0_DOUT0 = 126,
+ I2C5_SCL = 127,
+ I2C5_SDA = 128,
+ SPI2_CLK = 129,
+ SPI2_CS = 130,
+ SPI2_TXD = 131,
+
+ /* top_pmm_reg_7 */
+ SPI2_RXD = 132,
+ NAND_WP_N = 133,
+ NAND_PAGE_SIZE0 = 134,
+ NAND_PAGE_SIZE1 = 135,
+ NAND_ADDR_CYCLE = 136,
+ NAND_RB0 = 137,
+ NAND_RB1 = 138,
+ NAND_RB2 = 139,
+ NAND_RB3 = 140,
+
+ /* top_pmm_reg_8 */
+ GMAC_125M_IN = 141,
+ GMAC_50M_OUT = 142,
+ SPINOR_SSCLK_LOOPBACK = 143,
+ SPINOR_SDIO1CLK_LOOPBACK = 144,
+};
+
+static const struct pinctrl_pin_desc zx296718_pins[] = {
+ /* aon_pmm_reg_0 */
+ AON_PIN(I2C3_SCL, TOP_REG2, 18, 2, 0x48, 0,
+ AON_MUX(0x0, "ANMI"), /* anmi */
+ AON_MUX(0x1, "AGPIO"), /* agpio29 */
+ AON_MUX(0x2, "nonAON"), /* pin0 */
+ AON_MUX(0x3, "EXT_INT"), /* int4 */
+ TOP_MUX(0x0, "I2C3"), /* scl */
+ TOP_MUX(0x1, "SPI2"), /* txd */
+ TOP_MUX(0x2, "I2S1")), /* din0 */
+ AON_PIN(I2C3_SDA, TOP_REG2, 20, 2, 0x48, 9,
+ AON_MUX(0x0, "WD"), /* rst_b */
+ AON_MUX(0x1, "AGPIO"), /* agpio30 */
+ AON_MUX(0x2, "nonAON"), /* pin1 */
+ AON_MUX(0x3, "EXT_INT"), /* int5 */
+ TOP_MUX(0x0, "I2C3"), /* sda */
+ TOP_MUX(0x1, "SPI2"), /* rxd */
+ TOP_MUX(0x2, "I2S0")), /* mclk */
+ ZX_RESERVED(AON_RESERVED0),
+ ZX_RESERVED(AON_RESERVED1),
+ AON_PIN(SEC_EN, TOP_REG3, 5, 1, 0x50, 0,
+ AON_MUX(0x0, "SEC"), /* en */
+ AON_MUX(0x1, "AGPIO"), /* agpio28 */
+ AON_MUX(0x2, "nonAON"), /* pin3 */
+ AON_MUX(0x3, "EXT_INT"), /* int7 */
+ TOP_MUX(0x0, "I2C2"), /* sda */
+ TOP_MUX(0x1, "SPI2")), /* cs */
+ AON_PIN(UART0_RXD, 0, 0, 0, 0x50, 9,
+ AON_MUX(0x0, "UART0"), /* rxd */
+ AON_MUX(0x1, "AGPIO"), /* agpio20 */
+ AON_MUX(0x2, "nonAON")), /* pin34 */
+ AON_PIN(UART0_TXD, 0, 0, 0, 0x50, 18,
+ AON_MUX(0x0, "UART0"), /* txd */
+ AON_MUX(0x1, "AGPIO"), /* agpio21 */
+ AON_MUX(0x2, "nonAON")), /* pin32 */
+ AON_PIN(IR_IN, 0, 0, 0, 0x64, 0,
+ AON_MUX(0x0, "IR"), /* in */
+ AON_MUX(0x1, "AGPIO"), /* agpio0 */
+ AON_MUX(0x2, "nonAON")), /* pin27 */
+ AON_PIN(SPI0_CLK, TOP_REG3, 16, 1, 0x64, 9,
+ AON_MUX(0x0, "EXT_INT"), /* int0 */
+ AON_MUX(0x1, "AGPIO"), /* agpio23 */
+ AON_MUX(0x2, "nonAON"), /* pin5 */
+ AON_MUX(0x3, "PCU"), /* test6 */
+ TOP_MUX(0x0, "SPI0"), /* clk */
+ TOP_MUX(0x1, "ISP")), /* flash_trig */
+ AON_PIN(SPI0_CS, TOP_REG3, 17, 1, 0x64, 18,
+ AON_MUX(0x0, "EXT_INT"), /* int1 */
+ AON_MUX(0x1, "AGPIO"), /* agpio24 */
+ AON_MUX(0x2, "nonAON"), /* pin6 */
+ AON_MUX(0x3, "PCU"), /* test0 */
+ TOP_MUX(0x0, "SPI0"), /* cs */
+ TOP_MUX(0x1, "ISP")), /* prelight_trig */
+ AON_PIN(SPI0_TXD, TOP_REG3, 18, 1, 0x68, 0,
+ AON_MUX(0x0, "EXT_INT"), /* int2 */
+ AON_MUX(0x1, "AGPIO"), /* agpio25 */
+ AON_MUX(0x2, "nonAON"), /* pin7 */
+ AON_MUX(0x3, "PCU"), /* test1 */
+ TOP_MUX(0x0, "SPI0"), /* txd */
+ TOP_MUX(0x1, "ISP")), /* shutter_trig */
+ AON_PIN(SPI0_RXD, TOP_REG3, 19, 1, 0x68, 9,
+ AON_MUX(0x0, "EXT_INT"), /* int3 */
+ AON_MUX(0x1, "AGPIO"), /* agpio26 */
+ AON_MUX(0x2, "nonAON"), /* pin8 */
+ AON_MUX(0x3, "PCU"), /* test2 */
+ TOP_MUX(0x0, "SPI0"), /* rxd */
+ TOP_MUX(0x1, "ISP")), /* shutter_open */
+ AON_PIN(KEY_COL0, TOP_REG3, 20, 1, 0x68, 18,
+ AON_MUX(0x0, "KEY"), /* col0 */
+ AON_MUX(0x1, "AGPIO"), /* agpio5 */
+ AON_MUX(0x2, "nonAON"), /* pin9 */
+ AON_MUX(0x3, "PCU"), /* test3 */
+ TOP_MUX(0x0, "UART3"), /* rxd */
+ TOP_MUX(0x1, "I2S0")), /* din1 */
+ AON_PIN(KEY_COL1, TOP_REG3, 21, 2, 0x6c, 0,
+ AON_MUX(0x0, "KEY"), /* col1 */
+ AON_MUX(0x1, "AGPIO"), /* agpio6 */
+ AON_MUX(0x2, "nonAON"), /* pin10 */
+ TOP_MUX(0x0, "UART3"), /* txd */
+ TOP_MUX(0x1, "I2S0"), /* din2 */
+ TOP_MUX(0x2, "VGA")), /* scl */
+ AON_PIN(KEY_COL2, TOP_REG3, 23, 2, 0x6c, 9,
+ AON_MUX(0x0, "KEY"), /* col2 */
+ AON_MUX(0x1, "AGPIO"), /* agpio7 */
+ AON_MUX(0x2, "nonAON"), /* pin11 */
+ TOP_MUX(0x0, "PWM"), /* out1 */
+ TOP_MUX(0x1, "I2S0"), /* din3 */
+ TOP_MUX(0x2, "VGA")), /* sda */
+ AON_PIN(KEY_ROW0, 0, 0, 0, 0x6c, 18,
+ AON_MUX(0x0, "KEY"), /* row0 */
+ AON_MUX(0x1, "AGPIO"), /* agpio8 */
+ AON_MUX(0x2, "nonAON"), /* pin33 */
+ AON_MUX(0x3, "WD")), /* rst_b */
+
+ /* aon_pmm_reg_1 */
+ AON_PIN(KEY_ROW1, TOP_REG3, 25, 2, 0x70, 0,
+ AON_MUX(0x0, "KEY"), /* row1 */
+ AON_MUX(0x1, "AGPIO"), /* agpio9 */
+ AON_MUX(0x2, "nonAON"), /* pin12 */
+ TOP_MUX(0x0, "LCD"), /* port0 lcd_te */
+ TOP_MUX(0x1, "I2S0"), /* dout2 */
+ TOP_MUX(0x2, "PWM"), /* out2 */
+ TOP_MUX(0x3, "VGA")), /* hs1 */
+ AON_PIN(KEY_ROW2, TOP_REG3, 27, 2, 0x70, 9,
+ AON_MUX(0x0, "KEY"), /* row2 */
+ AON_MUX(0x1, "AGPIO"), /* agpio10 */
+ AON_MUX(0x2, "nonAON"), /* pin13 */
+ TOP_MUX(0x0, "LCD"), /* port1 lcd_te */
+ TOP_MUX(0x1, "I2S0"), /* dout3 */
+ TOP_MUX(0x2, "PWM"), /* out3 */
+ TOP_MUX(0x3, "VGA")), /* vs1 */
+ AON_PIN(HDMI_SCL, TOP_REG3, 29, 1, 0x70, 18,
+ AON_MUX(0x0, "PCU"), /* test7 */
+ AON_MUX(0x1, "AGPIO"), /* agpio3 */
+ AON_MUX(0x2, "nonAON"), /* pin14 */
+ TOP_MUX(0x0, "HDMI"), /* scl */
+ TOP_MUX(0x1, "UART3")), /* rxd */
+ AON_PIN(HDMI_SDA, TOP_REG3, 30, 1, 0x74, 0,
+ AON_MUX(0x0, "PCU"), /* test8 */
+ AON_MUX(0x1, "AGPIO"), /* agpio4 */
+ AON_MUX(0x2, "nonAON"), /* pin15 */
+ TOP_MUX(0x0, "HDMI"), /* sda */
+ TOP_MUX(0x1, "UART3")), /* txd */
+ AON_PIN(JTAG_TCK, TOP_REG7, 3, 1, 0x78, 18,
+ AON_MUX(0x0, "JTAG"), /* tck */
+ AON_MUX(0x1, "AGPIO"), /* agpio11 */
+ AON_MUX(0x2, "nonAON"), /* pin22 */
+ AON_MUX(0x3, "EXT_INT"), /* int4 */
+ TOP_MUX(0x0, "SPI4"), /* clk */
+ TOP_MUX(0x1, "UART1")), /* rxd */
+ AON_PIN(JTAG_TRSTN, TOP_REG7, 4, 1, 0xac, 0,
+ AON_MUX(0x0, "JTAG"), /* trstn */
+ AON_MUX(0x1, "AGPIO"), /* agpio12 */
+ AON_MUX(0x2, "nonAON"), /* pin23 */
+ AON_MUX(0x3, "EXT_INT"), /* int5 */
+ TOP_MUX(0x0, "SPI4"), /* cs */
+ TOP_MUX(0x1, "UART1")), /* txd */
+ AON_PIN(JTAG_TMS, TOP_REG7, 5, 1, 0xac, 9,
+ AON_MUX(0x0, "JTAG"), /* tms */
+ AON_MUX(0x1, "AGPIO"), /* agpio13 */
+ AON_MUX(0x2, "nonAON"), /* pin24 */
+ AON_MUX(0x3, "EXT_INT"), /* int6 */
+ TOP_MUX(0x0, "SPI4"), /* txd */
+ TOP_MUX(0x1, "UART2")), /* rxd */
+ AON_PIN(JTAG_TDI, TOP_REG7, 6, 1, 0xac, 18,
+ AON_MUX(0x0, "JTAG"), /* tdi */
+ AON_MUX(0x1, "AGPIO"), /* agpio14 */
+ AON_MUX(0x2, "nonAON"), /* pin25 */
+ AON_MUX(0x3, "EXT_INT"), /* int7 */
+ TOP_MUX(0x0, "SPI4"), /* rxd */
+ TOP_MUX(0x1, "UART2")), /* txd */
+ AON_PIN(JTAG_TDO, 0, 0, 0, 0xb0, 0,
+ AON_MUX(0x0, "JTAG"), /* tdo */
+ AON_MUX(0x1, "AGPIO"), /* agpio15 */
+ AON_MUX(0x2, "nonAON")), /* pin26 */
+ AON_PIN(I2C0_SCL, 0, 0, 0, 0xb0, 9,
+ AON_MUX(0x0, "I2C0"), /* scl */
+ AON_MUX(0x1, "AGPIO"), /* agpio16 */
+ AON_MUX(0x2, "nonAON")), /* pin28 */
+ AON_PIN(I2C0_SDA, 0, 0, 0, 0xb0, 18,
+ AON_MUX(0x0, "I2C0"), /* sda */
+ AON_MUX(0x1, "AGPIO"), /* agpio17 */
+ AON_MUX(0x2, "nonAON")), /* pin29 */
+ AON_PIN(I2C1_SCL, TOP_REG8, 4, 1, 0xb4, 0,
+ AON_MUX(0x0, "I2C1"), /* scl */
+ AON_MUX(0x1, "AGPIO"), /* agpio18 */
+ AON_MUX(0x2, "nonAON"), /* pin30 */
+ TOP_MUX(0x0, "LCD")), /* port0 lcd_te */
+ AON_PIN(I2C1_SDA, TOP_REG8, 5, 1, 0xb4, 9,
+ AON_MUX(0x0, "I2C1"), /* sda */
+ AON_MUX(0x1, "AGPIO"), /* agpio19 */
+ AON_MUX(0x2, "nonAON"), /* pin31 */
+ TOP_MUX(0x0, "LCD")), /* port1 lcd_te */
+ ZX_RESERVED(AON_RESERVED2),
+ ZX_RESERVED(AON_RESERVED3),
+ ZX_RESERVED(AON_RESERVED4),
+
+ /* aon_pmm_reg_2 */
+ AON_PIN(SPI1_CLK, TOP_REG2, 6, 3, 0x40, 9,
+ AON_MUX(0x0, "EXT_INT"), /* int0 */
+ AON_MUX(0x1, "PCU"), /* test12 */
+ AON_MUX(0x2, "nonAON"), /* pin39 */
+ TOP_MUX(0x0, "SPI1"), /* clk */
+ TOP_MUX(0x1, "PCM"), /* clk */
+ TOP_MUX(0x2, "BGPIO"), /* gpio35 */
+ TOP_MUX(0x3, "I2C4"), /* scl */
+ TOP_MUX(0x4, "I2S1"), /* mclk */
+ TOP_MUX(0x5, "ISP")), /* flash_trig */
+ AON_PIN(SPI1_CS, TOP_REG2, 9, 3, 0x40, 18,
+ AON_MUX(0x0, "EXT_INT"), /* int1 */
+ AON_MUX(0x1, "PCU"), /* test13 */
+ AON_MUX(0x2, "nonAON"), /* pin40 */
+ TOP_MUX(0x0, "SPI1"), /* cs */
+ TOP_MUX(0x1, "PCM"), /* fs */
+ TOP_MUX(0x2, "BGPIO"), /* gpio36 */
+ TOP_MUX(0x3, "I2C4"), /* sda */
+ TOP_MUX(0x4, "I2S1"), /* bclk */
+ TOP_MUX(0x5, "ISP")), /* prelight_trig */
+ AON_PIN(SPI1_TXD, TOP_REG2, 12, 3, 0x44, 0,
+ AON_MUX(0x0, "EXT_INT"), /* int2 */
+ AON_MUX(0x1, "PCU"), /* test14 */
+ AON_MUX(0x2, "nonAON"), /* pin41 */
+ TOP_MUX(0x0, "SPI1"), /* txd */
+ TOP_MUX(0x1, "PCM"), /* txd */
+ TOP_MUX(0x2, "BGPIO"), /* gpio37 */
+ TOP_MUX(0x3, "UART5"), /* rxd */
+ TOP_MUX(0x4, "I2S1"), /* ws */
+ TOP_MUX(0x5, "ISP")), /* shutter_trig */
+ AON_PIN(SPI1_RXD, TOP_REG2, 15, 3, 0x44, 9,
+ AON_MUX(0x0, "EXT_INT"), /* int3 */
+ AON_MUX(0x1, "PCU"), /* test15 */
+ AON_MUX(0x2, "nonAON"), /* pin42 */
+ TOP_MUX(0x0, "SPI1"), /* rxd */
+ TOP_MUX(0x1, "PCM"), /* rxd */
+ TOP_MUX(0x2, "BGPIO"), /* gpio38 */
+ TOP_MUX(0x3, "UART5"), /* txd */
+ TOP_MUX(0x4, "I2S1"), /* dout0 */
+ TOP_MUX(0x5, "ISP")), /* shutter_open */
+ ZX_RESERVED(AON_RESERVED5),
+ ZX_RESERVED(AON_RESERVED6),
+ AON_PIN(AUDIO_DET, TOP_REG3, 3, 2, 0x48, 18,
+ AON_MUX(0x0, "PCU"), /* test4 */
+ AON_MUX(0x1, "AGPIO"), /* agpio27 */
+ AON_MUX(0x2, "nonAON"), /* pin2 */
+ AON_MUX(0x3, "EXT_INT"), /* int16 */
+ TOP_MUX(0x0, "AUDIO"), /* detect */
+ TOP_MUX(0x1, "I2C2"), /* scl */
+ TOP_MUX(0x2, "SPI2")), /* clk */
+ AON_PIN(SPDIF_OUT, TOP_REG3, 14, 2, 0x78, 9,
+ AON_MUX(0x0, "PCU"), /* test5 */
+ AON_MUX(0x1, "AGPIO"), /* agpio22 */
+ AON_MUX(0x2, "nonAON"), /* pin4 */
+ TOP_MUX(0x0, "SPDIF"), /* out */
+ TOP_MUX(0x1, "PWM"), /* out0 */
+ TOP_MUX(0x2, "ISP")), /* fl_trig */
+ AON_PIN(HDMI_CEC, 0, 0, 0, 0x74, 9,
+ AON_MUX(0x0, "PCU"), /* test9 */
+ AON_MUX(0x1, "AGPIO"), /* agpio1 */
+ AON_MUX(0x2, "nonAON")), /* pin16 */
+ AON_PIN(HDMI_HPD, 0, 0, 0, 0x74, 18,
+ AON_MUX(0x0, "PCU"), /* test10 */
+ AON_MUX(0x1, "AGPIO"), /* agpio2 */
+ AON_MUX(0x2, "nonAON")), /* pin17 */
+ AON_PIN(GMAC_25M_OUT, 0, 0, 0, 0x78, 0,
+ AON_MUX(0x0, "PCU"), /* test11 */
+ AON_MUX(0x1, "AGPIO"), /* agpio31 */
+ AON_MUX(0x2, "nonAON")), /* pin43 */
+ AON_PIN(BOOT_SEL0, 0, 0, 0, 0xc0, 9,
+ AON_MUX(0x0, "BOOT"), /* sel0 */
+ AON_MUX(0x1, "AGPIO"), /* agpio18 */
+ AON_MUX(0x2, "nonAON")), /* pin18 */
+ AON_PIN(BOOT_SEL1, 0, 0, 0, 0xc0, 18,
+ AON_MUX(0x0, "BOOT"), /* sel1 */
+ AON_MUX(0x1, "AGPIO"), /* agpio19 */
+ AON_MUX(0x2, "nonAON")), /* pin19 */
+ AON_PIN(BOOT_SEL2, 0, 0, 0, 0xc4, 0,
+ AON_MUX(0x0, "BOOT"), /* sel2 */
+ AON_MUX(0x1, "AGPIO"), /* agpio20 */
+ AON_MUX(0x2, "nonAON")), /* pin20 */
+ AON_PIN(DEEP_SLEEP_OUT_N, 0, 0, 0, 0xc4, 9,
+ AON_MUX(0x0, "DEEPSLP"), /* deep sleep out_n */
+ AON_MUX(0x1, "AGPIO"), /* agpio21 */
+ AON_MUX(0x2, "nonAON")), /* pin21 */
+ ZX_RESERVED(AON_RESERVED7),
+
+ /* top_pmm_reg_0 */
+ TOP_PIN(GMII_GTX_CLK, TOP_REG0, 0, 2, 0x10, 0,
+ TOP_MUX(0x0, "GMII"), /* gtx_clk */
+ TOP_MUX(0x1, "DVI0"), /* clk */
+ TOP_MUX(0x2, "BGPIO")), /* gpio0 */
+ TOP_PIN(GMII_TX_CLK, TOP_REG0, 2, 2, 0x10, 9,
+ TOP_MUX(0x0, "GMII"), /* tx_clk */
+ TOP_MUX(0x1, "DVI0"), /* vs */
+ TOP_MUX(0x2, "BGPIO")), /* gpio1 */
+ TOP_PIN(GMII_TXD0, TOP_REG0, 4, 2, 0x10, 18,
+ TOP_MUX(0x0, "GMII"), /* txd0 */
+ TOP_MUX(0x1, "DVI0"), /* hs */
+ TOP_MUX(0x2, "BGPIO")), /* gpio2 */
+ TOP_PIN(GMII_TXD1, TOP_REG0, 6, 2, 0x14, 0,
+ TOP_MUX(0x0, "GMII"), /* txd1 */
+ TOP_MUX(0x1, "DVI0"), /* d0 */
+ TOP_MUX(0x2, "BGPIO")), /* gpio3 */
+ TOP_PIN(GMII_TXD2, TOP_REG0, 8, 2, 0x14, 9,
+ TOP_MUX(0x0, "GMII"), /* txd2 */
+ TOP_MUX(0x1, "DVI0"), /* d1 */
+ TOP_MUX(0x2, "BGPIO")), /* gpio4 */
+ TOP_PIN(GMII_TXD3, TOP_REG0, 10, 2, 0x14, 18,
+ TOP_MUX(0x0, "GMII"), /* txd3 */
+ TOP_MUX(0x1, "DVI0"), /* d2 */
+ TOP_MUX(0x2, "BGPIO")), /* gpio5 */
+ TOP_PIN(GMII_TXD4, TOP_REG0, 12, 2, 0x18, 0,
+ TOP_MUX(0x0, "GMII"), /* txd4 */
+ TOP_MUX(0x1, "DVI0"), /* d3 */
+ TOP_MUX(0x2, "BGPIO")), /* gpio6 */
+ TOP_PIN(GMII_TXD5, TOP_REG0, 14, 2, 0x18, 9,
+ TOP_MUX(0x0, "GMII"), /* txd5 */
+ TOP_MUX(0x1, "DVI0"), /* d4 */
+ TOP_MUX(0x2, "BGPIO")), /* gpio7 */
+ TOP_PIN(GMII_TXD6, TOP_REG0, 16, 2, 0x18, 18,
+ TOP_MUX(0x0, "GMII"), /* txd6 */
+ TOP_MUX(0x1, "DVI0"), /* d5 */
+ TOP_MUX(0x2, "BGPIO")), /* gpio8 */
+ TOP_PIN(GMII_TXD7, TOP_REG0, 18, 2, 0x1c, 0,
+ TOP_MUX(0x0, "GMII"), /* txd7 */
+ TOP_MUX(0x1, "DVI0"), /* d6 */
+ TOP_MUX(0x2, "BGPIO")), /* gpio9 */
+ TOP_PIN(GMII_TX_ER, TOP_REG0, 20, 2, 0x1c, 9,
+ TOP_MUX(0x0, "GMII"), /* tx_er */
+ TOP_MUX(0x1, "DVI0"), /* d7 */
+ TOP_MUX(0x2, "BGPIO")), /* gpio10 */
+ TOP_PIN(GMII_TX_EN, TOP_REG0, 22, 2, 0x1c, 18,
+ TOP_MUX(0x0, "GMII"), /* tx_en */
+ TOP_MUX(0x1, "DVI0"), /* d8 */
+ TOP_MUX(0x3, "BGPIO")), /* gpio11 */
+ TOP_PIN(GMII_RX_CLK, TOP_REG0, 24, 2, 0x20, 0,
+ TOP_MUX(0x0, "GMII"), /* rx_clk */
+ TOP_MUX(0x1, "DVI0"), /* d9 */
+ TOP_MUX(0x3, "BGPIO")), /* gpio12 */
+ TOP_PIN(GMII_RXD0, TOP_REG0, 26, 2, 0x20, 9,
+ TOP_MUX(0x0, "GMII"), /* rxd0 */
+ TOP_MUX(0x1, "DVI0"), /* d10 */
+ TOP_MUX(0x3, "BGPIO")), /* gpio13 */
+ TOP_PIN(GMII_RXD1, TOP_REG0, 28, 2, 0x20, 18,
+ TOP_MUX(0x0, "GMII"), /* rxd1 */
+ TOP_MUX(0x1, "DVI0"), /* d11 */
+ TOP_MUX(0x2, "BGPIO")), /* gpio14 */
+ TOP_PIN(GMII_RXD2, TOP_REG0, 30, 2, 0x24, 0,
+ TOP_MUX(0x0, "GMII"), /* rxd2 */
+ TOP_MUX(0x1, "DVI1"), /* clk */
+ TOP_MUX(0x2, "BGPIO")), /* gpio15 */
+
+ /* top_pmm_reg_1 */
+ TOP_PIN(GMII_RXD3, TOP_REG1, 0, 2, 0x24, 9,
+ TOP_MUX(0x0, "GMII"), /* rxd3 */
+ TOP_MUX(0x1, "DVI1"), /* hs */
+ TOP_MUX(0x2, "BGPIO")), /* gpio16 */
+ TOP_PIN(GMII_RXD4, TOP_REG1, 2, 2, 0x24, 18,
+ TOP_MUX(0x0, "GMII"), /* rxd4 */
+ TOP_MUX(0x1, "DVI1"), /* vs */
+ TOP_MUX(0x2, "BGPIO")), /* gpio17 */
+ TOP_PIN(GMII_RXD5, TOP_REG1, 4, 2, 0x28, 0,
+ TOP_MUX(0x0, "GMII"), /* rxd5 */
+ TOP_MUX(0x1, "DVI1"), /* d0 */
+ TOP_MUX(0x2, "BGPIO"), /* gpio18 */
+ TOP_MUX(0x3, "TSI0")), /* dat0 */
+ TOP_PIN(GMII_RXD6, TOP_REG1, 6, 2, 0x28, 9,
+ TOP_MUX(0x0, "GMII"), /* rxd6 */
+ TOP_MUX(0x1, "DVI1"), /* d1 */
+ TOP_MUX(0x2, "BGPIO"), /* gpio19 */
+ TOP_MUX(0x3, "TSI0")), /* clk */
+ TOP_PIN(GMII_RXD7, TOP_REG1, 8, 2, 0x28, 18,
+ TOP_MUX(0x0, "GMII"), /* rxd7 */
+ TOP_MUX(0x1, "DVI1"), /* d2 */
+ TOP_MUX(0x2, "BGPIO"), /* gpio20 */
+ TOP_MUX(0x3, "TSI0")), /* sync */
+ TOP_PIN(GMII_RX_ER, TOP_REG1, 10, 2, 0x2c, 0,
+ TOP_MUX(0x0, "GMII"), /* rx_er */
+ TOP_MUX(0x1, "DVI1"), /* d3 */
+ TOP_MUX(0x2, "BGPIO"), /* gpio21 */
+ TOP_MUX(0x3, "TSI0")), /* valid */
+ TOP_PIN(GMII_RX_DV, TOP_REG1, 12, 2, 0x2c, 9,
+ TOP_MUX(0x0, "GMII"), /* rx_dv */
+ TOP_MUX(0x1, "DVI1"), /* d4 */
+ TOP_MUX(0x2, "BGPIO"), /* gpio22 */
+ TOP_MUX(0x3, "TSI1")), /* dat0 */
+ TOP_PIN(GMII_COL, TOP_REG1, 14, 2, 0x2c, 18,
+ TOP_MUX(0x0, "GMII"), /* col */
+ TOP_MUX(0x1, "DVI1"), /* d5 */
+ TOP_MUX(0x2, "BGPIO"), /* gpio23 */
+ TOP_MUX(0x3, "TSI1")), /* clk */
+ TOP_PIN(GMII_CRS, TOP_REG1, 16, 2, 0x30, 0,
+ TOP_MUX(0x0, "GMII"), /* crs */
+ TOP_MUX(0x1, "DVI1"), /* d6 */
+ TOP_MUX(0x2, "BGPIO"), /* gpio24 */
+ TOP_MUX(0x3, "TSI1")), /* sync */
+ TOP_PIN(GMII_MDC, TOP_REG1, 18, 2, 0x30, 9,
+ TOP_MUX(0x0, "GMII"), /* mdc */
+ TOP_MUX(0x1, "DVI1"), /* d7 */
+ TOP_MUX(0x2, "BGPIO"), /* gpio25 */
+ TOP_MUX(0x3, "TSI1")), /* valid */
+ TOP_PIN(GMII_MDIO, TOP_REG1, 20, 1, 0x30, 18,
+ TOP_MUX(0x0, "GMII"), /* mdio */
+ TOP_MUX(0x2, "BGPIO")), /* gpio26 */
+ TOP_PIN(SDIO1_CLK, TOP_REG1, 21, 2, 0x34, 18,
+ TOP_MUX(0x0, "SDIO1"), /* clk */
+ TOP_MUX(0x1, "USIM0"), /* clk */
+ TOP_MUX(0x2, "BGPIO"), /* gpio27 */
+ TOP_MUX(0x3, "SPINOR")), /* clk */
+ TOP_PIN(SDIO1_CMD, TOP_REG1, 23, 2, 0x38, 0,
+ TOP_MUX(0x0, "SDIO1"), /* cmd */
+ TOP_MUX(0x1, "USIM0"), /* cd */
+ TOP_MUX(0x2, "BGPIO"), /* gpio28 */
+ TOP_MUX(0x3, "SPINOR")), /* cs */
+ TOP_PIN(SDIO1_DATA0, TOP_REG1, 25, 2, 0x38, 9,
+ TOP_MUX(0x0, "SDIO1"), /* dat0 */
+ TOP_MUX(0x1, "USIM0"), /* rst */
+ TOP_MUX(0x2, "BGPIO"), /* gpio29 */
+ TOP_MUX(0x3, "SPINOR")), /* dq0 */
+ TOP_PIN(SDIO1_DATA1, TOP_REG1, 27, 2, 0x38, 18,
+ TOP_MUX(0x0, "SDIO1"), /* dat1 */
+ TOP_MUX(0x1, "USIM0"), /* data */
+ TOP_MUX(0x2, "BGPIO"), /* gpio30 */
+ TOP_MUX(0x3, "SPINOR")), /* dq1 */
+ TOP_PIN(SDIO1_DATA2, TOP_REG1, 29, 2, 0x3c, 0,
+ TOP_MUX(0x0, "SDIO1"), /* dat2 */
+ TOP_MUX(0x1, "BGPIO"), /* gpio31 */
+ TOP_MUX(0x2, "SPINOR")), /* dq2 */
+
+ /* top_pmm_reg_2 */
+ TOP_PIN(SDIO1_DATA3, TOP_REG2, 0, 2, 0x3c, 9,
+ TOP_MUX(0x0, "SDIO1"), /* dat3 */
+ TOP_MUX(0x1, "BGPIO"), /* gpio32 */
+ TOP_MUX(0x2, "SPINOR")), /* dq3 */
+ TOP_PIN(SDIO1_CD, TOP_REG2, 2, 2, 0x3c, 18,
+ TOP_MUX(0x0, "SDIO1"), /* cd */
+ TOP_MUX(0x1, "BGPIO"), /* gpio33 */
+ TOP_MUX(0x2, "ISP")), /* fl_trig */
+ TOP_PIN(SDIO1_WP, TOP_REG2, 4, 2, 0x40, 0,
+ TOP_MUX(0x0, "SDIO1"), /* wp */
+ TOP_MUX(0x1, "BGPIO"), /* gpio34 */
+ TOP_MUX(0x2, "ISP")), /* ref_clk */
+ TOP_PIN(USIM1_CD, TOP_REG2, 22, 3, 0x44, 18,
+ TOP_MUX(0x0, "USIM1"), /* cd */
+ TOP_MUX(0x1, "UART4"), /* rxd */
+ TOP_MUX(0x2, "BGPIO"), /* gpio39 */
+ TOP_MUX(0x3, "SPI3"), /* clk */
+ TOP_MUX(0x4, "I2S0"), /* bclk */
+ TOP_MUX(0x5, "B_DVI0")), /* d8 */
+ TOP_PIN(USIM1_CLK, TOP_REG2, 25, 3, 0x4c, 18,
+ TOP_MUX(0x0, "USIM1"), /* clk */
+ TOP_MUX(0x1, "UART4"), /* txd */
+ TOP_MUX(0x2, "BGPIO"), /* gpio40 */
+ TOP_MUX(0x3, "SPI3"), /* cs */
+ TOP_MUX(0x4, "I2S0"), /* ws */
+ TOP_MUX(0x5, "B_DVI0")), /* d9 */
+ TOP_PIN(USIM1_RST, TOP_REG2, 28, 3, 0x4c, 0,
+ TOP_MUX(0x0, "USIM1"), /* rst */
+ TOP_MUX(0x1, "UART4"), /* cts */
+ TOP_MUX(0x2, "BGPIO"), /* gpio41 */
+ TOP_MUX(0x3, "SPI3"), /* txd */
+ TOP_MUX(0x4, "I2S0"), /* dout0 */
+ TOP_MUX(0x5, "B_DVI0")), /* d10 */
+
+ /* top_pmm_reg_3 */
+ TOP_PIN(USIM1_DATA, TOP_REG3, 0, 3, 0x4c, 9,
+ TOP_MUX(0x0, "USIM1"), /* dat */
+ TOP_MUX(0x1, "UART4"), /* rst */
+ TOP_MUX(0x2, "BGPIO"), /* gpio42 */
+ TOP_MUX(0x3, "SPI3"), /* rxd */
+ TOP_MUX(0x4, "I2S0"), /* din0 */
+ TOP_MUX(0x5, "B_DVI0")), /* d11 */
+ TOP_PIN(SDIO0_CLK, TOP_REG3, 6, 1, 0x58, 0,
+ TOP_MUX(0x0, "SDIO0"), /* clk */
+ TOP_MUX(0x1, "GPIO")), /* gpio43 */
+ TOP_PIN(SDIO0_CMD, TOP_REG3, 7, 1, 0x58, 9,
+ TOP_MUX(0x0, "SDIO0"), /* cmd */
+ TOP_MUX(0x1, "GPIO")), /* gpio44 */
+ TOP_PIN(SDIO0_DATA0, TOP_REG3, 8, 1, 0x58, 18,
+ TOP_MUX(0x0, "SDIO0"), /* dat0 */
+ TOP_MUX(0x1, "GPIO")), /* gpio45 */
+ TOP_PIN(SDIO0_DATA1, TOP_REG3, 9, 1, 0x5c, 0,
+ TOP_MUX(0x0, "SDIO0"), /* dat1 */
+ TOP_MUX(0x1, "GPIO")), /* gpio46 */
+ TOP_PIN(SDIO0_DATA2, TOP_REG3, 10, 1, 0x5c, 9,
+ TOP_MUX(0x0, "SDIO0"), /* dat2 */
+ TOP_MUX(0x1, "GPIO")), /* gpio47 */
+ TOP_PIN(SDIO0_DATA3, TOP_REG3, 11, 1, 0x5c, 18,
+ TOP_MUX(0x0, "SDIO0"), /* dat3 */
+ TOP_MUX(0x1, "GPIO")), /* gpio48 */
+ TOP_PIN(SDIO0_CD, TOP_REG3, 12, 1, 0x60, 0,
+ TOP_MUX(0x0, "SDIO0"), /* cd */
+ TOP_MUX(0x1, "GPIO")), /* gpio49 */
+ TOP_PIN(SDIO0_WP, TOP_REG3, 13, 1, 0x60, 9,
+ TOP_MUX(0x0, "SDIO0"), /* wp */
+ TOP_MUX(0x1, "GPIO")), /* gpio50 */
+
+ /* top_pmm_reg_4 */
+ TOP_PIN(TSI0_DATA0, TOP_REG4, 0, 2, 0x60, 18,
+ TOP_MUX(0x0, "TSI0"), /* dat0 */
+ TOP_MUX(0x1, "LCD"), /* clk */
+ TOP_MUX(0x2, "BGPIO")), /* gpio51 */
+ TOP_PIN(SPINOR_CLK, TOP_REG4, 2, 2, 0xa8, 18,
+ TOP_MUX(0x0, "SPINOR"), /* clk */
+ TOP_MUX(0x1, "TSI0"), /* dat1 */
+ TOP_MUX(0x2, "LCD"), /* dat0 */
+ TOP_MUX(0x3, "BGPIO")), /* gpio52 */
+ TOP_PIN(TSI2_DATA, TOP_REG4, 4, 2, 0x7c, 0,
+ TOP_MUX(0x0, "TSI2"), /* dat */
+ TOP_MUX(0x1, "TSI0"), /* dat2 */
+ TOP_MUX(0x2, "LCD"), /* dat1 */
+ TOP_MUX(0x3, "BGPIO")), /* gpio53 */
+ TOP_PIN(TSI2_CLK, TOP_REG4, 6, 2, 0x7c, 9,
+ TOP_MUX(0x0, "TSI2"), /* clk */
+ TOP_MUX(0x1, "TSI0"), /* dat3 */
+ TOP_MUX(0x2, "LCD"), /* dat2 */
+ TOP_MUX(0x3, "BGPIO")), /* gpio54 */
+ TOP_PIN(TSI2_SYNC, TOP_REG4, 8, 2, 0x7c, 18,
+ TOP_MUX(0x0, "TSI2"), /* sync */
+ TOP_MUX(0x1, "TSI0"), /* dat4 */
+ TOP_MUX(0x2, "LCD"), /* dat3 */
+ TOP_MUX(0x3, "BGPIO")), /* gpio55 */
+ TOP_PIN(TSI2_VALID, TOP_REG4, 10, 2, 0x80, 0,
+ TOP_MUX(0x0, "TSI2"), /* valid */
+ TOP_MUX(0x1, "TSI0"), /* dat5 */
+ TOP_MUX(0x2, "LCD"), /* dat4 */
+ TOP_MUX(0x3, "BGPIO")), /* gpio56 */
+ TOP_PIN(SPINOR_CS, TOP_REG4, 12, 2, 0x80, 9,
+ TOP_MUX(0x0, "SPINOR"), /* cs */
+ TOP_MUX(0x1, "TSI0"), /* dat6 */
+ TOP_MUX(0x2, "LCD"), /* dat5 */
+ TOP_MUX(0x3, "BGPIO")), /* gpio57 */
+ TOP_PIN(SPINOR_DQ0, TOP_REG4, 14, 2, 0x80, 18,
+ TOP_MUX(0x0, "SPINOR"), /* dq0 */
+ TOP_MUX(0x1, "TSI0"), /* dat7 */
+ TOP_MUX(0x2, "LCD"), /* dat6 */
+ TOP_MUX(0x3, "BGPIO")), /* gpio58 */
+ TOP_PIN(SPINOR_DQ1, TOP_REG4, 16, 2, 0x84, 0,
+ TOP_MUX(0x0, "SPINOR"), /* dq1 */
+ TOP_MUX(0x1, "TSI0"), /* clk */
+ TOP_MUX(0x2, "LCD"), /* dat7 */
+ TOP_MUX(0x3, "BGPIO")), /* gpio59 */
+ TOP_PIN(SPINOR_DQ2, TOP_REG4, 18, 2, 0x84, 9,
+ TOP_MUX(0x0, "SPINOR"), /* dq2 */
+ TOP_MUX(0x1, "TSI0"), /* sync */
+ TOP_MUX(0x2, "LCD"), /* dat8 */
+ TOP_MUX(0x3, "BGPIO")), /* gpio60 */
+ TOP_PIN(SPINOR_DQ3, TOP_REG4, 20, 2, 0x84, 18,
+ TOP_MUX(0x0, "SPINOR"), /* dq3 */
+ TOP_MUX(0x1, "TSI0"), /* valid */
+ TOP_MUX(0x2, "LCD"), /* dat9 */
+ TOP_MUX(0x3, "BGPIO")), /* gpio61 */
+ TOP_PIN(VGA_HS, TOP_REG4, 22, 3, 0x88, 0,
+ TOP_MUX(0x0, "VGA"), /* hs */
+ TOP_MUX(0x1, "TSI1"), /* dat0 */
+ TOP_MUX(0x2, "LCD"), /* dat10 */
+ TOP_MUX(0x3, "BGPIO"), /* gpio62 */
+ TOP_MUX(0x4, "I2S1"), /* din1 */
+ TOP_MUX(0x5, "B_DVI0")), /* clk */
+ TOP_PIN(VGA_VS, TOP_REG4, 25, 3, 0x88, 9,
+ TOP_MUX(0x0, "VGA"), /* vs0 */
+ TOP_MUX(0x1, "TSI1"), /* dat1 */
+ TOP_MUX(0x2, "LCD"), /* dat11 */
+ TOP_MUX(0x3, "BGPIO"), /* gpio63 */
+ TOP_MUX(0x4, "I2S1"), /* din2 */
+ TOP_MUX(0x5, "B_DVI0")), /* vs */
+ TOP_PIN(TSI3_DATA, TOP_REG4, 28, 3, 0x88, 18,
+ TOP_MUX(0x0, "TSI3"), /* dat */
+ TOP_MUX(0x1, "TSI1"), /* dat2 */
+ TOP_MUX(0x2, "LCD"), /* dat12 */
+ TOP_MUX(0x3, "BGPIO"), /* gpio64 */
+ TOP_MUX(0x4, "I2S1"), /* din3 */
+ TOP_MUX(0x5, "B_DVI0")), /* hs */
+
+ /* top_pmm_reg_5 */
+ TOP_PIN(TSI3_CLK, TOP_REG5, 0, 3, 0x8c, 0,
+ TOP_MUX(0x0, "TSI3"), /* clk */
+ TOP_MUX(0x1, "TSI1"), /* dat3 */
+ TOP_MUX(0x2, "LCD"), /* dat13 */
+ TOP_MUX(0x3, "BGPIO"), /* gpio65 */
+ TOP_MUX(0x4, "I2S1"), /* dout1 */
+ TOP_MUX(0x5, "B_DVI0")), /* d0 */
+ TOP_PIN(TSI3_SYNC, TOP_REG5, 3, 3, 0x8c, 9,
+ TOP_MUX(0x0, "TSI3"), /* sync */
+ TOP_MUX(0x1, "TSI1"), /* dat4 */
+ TOP_MUX(0x2, "LCD"), /* dat14 */
+ TOP_MUX(0x3, "BGPIO"), /* gpio66 */
+ TOP_MUX(0x4, "I2S1"), /* dout2 */
+ TOP_MUX(0x5, "B_DVI0")), /* d1 */
+ TOP_PIN(TSI3_VALID, TOP_REG5, 6, 3, 0x8c, 18,
+ TOP_MUX(0x0, "TSI3"), /* valid */
+ TOP_MUX(0x1, "TSI1"), /* dat5 */
+ TOP_MUX(0x2, "LCD"), /* dat15 */
+ TOP_MUX(0x3, "BGPIO"), /* gpio67 */
+ TOP_MUX(0x4, "I2S1"), /* dout3 */
+ TOP_MUX(0x5, "B_DVI0")), /* d2 */
+ TOP_PIN(I2S1_WS, TOP_REG5, 9, 3, 0x90, 0,
+ TOP_MUX(0x0, "I2S1"), /* ws */
+ TOP_MUX(0x1, "TSI1"), /* dat6 */
+ TOP_MUX(0x2, "LCD"), /* dat16 */
+ TOP_MUX(0x3, "BGPIO"), /* gpio68 */
+ TOP_MUX(0x4, "VGA"), /* scl */
+ TOP_MUX(0x5, "B_DVI0")), /* d3 */
+ TOP_PIN(I2S1_BCLK, TOP_REG5, 12, 3, 0x90, 9,
+ TOP_MUX(0x0, "I2S1"), /* bclk */
+ TOP_MUX(0x1, "TSI1"), /* dat7 */
+ TOP_MUX(0x2, "LCD"), /* dat17 */
+ TOP_MUX(0x3, "BGPIO"), /* gpio69 */
+ TOP_MUX(0x4, "VGA"), /* sda */
+ TOP_MUX(0x5, "B_DVI0")), /* d4 */
+ TOP_PIN(I2S1_MCLK, TOP_REG5, 15, 2, 0x90, 18,
+ TOP_MUX(0x0, "I2S1"), /* mclk */
+ TOP_MUX(0x1, "TSI1"), /* clk */
+ TOP_MUX(0x2, "LCD"), /* dat18 */
+ TOP_MUX(0x3, "BGPIO")), /* gpio70 */
+ TOP_PIN(I2S1_DIN0, TOP_REG5, 17, 2, 0x94, 0,
+ TOP_MUX(0x0, "I2S1"), /* din0 */
+ TOP_MUX(0x1, "TSI1"), /* sync */
+ TOP_MUX(0x2, "LCD"), /* dat19 */
+ TOP_MUX(0x3, "BGPIO")), /* gpio71 */
+ TOP_PIN(I2S1_DOUT0, TOP_REG5, 19, 2, 0x94, 9,
+ TOP_MUX(0x0, "I2S1"), /* dout0 */
+ TOP_MUX(0x1, "TSI1"), /* valid */
+ TOP_MUX(0x2, "LCD"), /* dat20 */
+ TOP_MUX(0x3, "BGPIO")), /* gpio72 */
+ TOP_PIN(SPI3_CLK, TOP_REG5, 21, 3, 0x94, 18,
+ TOP_MUX(0x0, "SPI3"), /* clk */
+ TOP_MUX(0x1, "TSO1"), /* clk */
+ TOP_MUX(0x2, "LCD"), /* dat21 */
+ TOP_MUX(0x3, "BGPIO"), /* gpio73 */
+ TOP_MUX(0x4, "UART5"), /* rxd */
+ TOP_MUX(0x5, "PCM"), /* fs */
+ TOP_MUX(0x6, "I2S0"), /* din1 */
+ TOP_MUX(0x7, "B_DVI0")), /* d5 */
+ TOP_PIN(SPI3_CS, TOP_REG5, 24, 3, 0x98, 0,
+ TOP_MUX(0x0, "SPI3"), /* cs */
+ TOP_MUX(0x1, "TSO1"), /* dat0 */
+ TOP_MUX(0x2, "LCD"), /* dat22 */
+ TOP_MUX(0x3, "BGPIO"), /* gpio74 */
+ TOP_MUX(0x4, "UART5"), /* txd */
+ TOP_MUX(0x5, "PCM"), /* clk */
+ TOP_MUX(0x6, "I2S0"), /* din2 */
+ TOP_MUX(0x7, "B_DVI0")), /* d6 */
+ TOP_PIN(SPI3_TXD, TOP_REG5, 27, 3, 0x98, 9,
+ TOP_MUX(0x0, "SPI3"), /* txd */
+ TOP_MUX(0x1, "TSO1"), /* dat1 */
+ TOP_MUX(0x2, "LCD"), /* dat23 */
+ TOP_MUX(0x3, "BGPIO"), /* gpio75 */
+ TOP_MUX(0x4, "UART5"), /* cts */
+ TOP_MUX(0x5, "PCM"), /* txd */
+ TOP_MUX(0x6, "I2S0"), /* din3 */
+ TOP_MUX(0x7, "B_DVI0")), /* d7 */
+ TOP_PIN(NAND_LDO_MS18_SEL, TOP_REG5, 30, 1, 0xe4, 0,
+ TOP_MUX(0x0, "NAND"), /* ldo_ms18_sel */
+ TOP_MUX(0x1, "BGPIO")), /* gpio99 */
+
+ /* top_pmm_reg_6 */
+ TOP_PIN(SPI3_RXD, TOP_REG6, 0, 3, 0x98, 18,
+ TOP_MUX(0x0, "SPI3"), /* rxd */
+ TOP_MUX(0x1, "TSO1"), /* dat2 */
+ TOP_MUX(0x2, "LCD"), /* stvu_vsync */
+ TOP_MUX(0x3, "BGPIO"), /* gpio76 */
+ TOP_MUX(0x4, "UART5"), /* rts */
+ TOP_MUX(0x5, "PCM"), /* rxd */
+ TOP_MUX(0x6, "I2S0"), /* dout1 */
+ TOP_MUX(0x7, "B_DVI1")), /* clk */
+ TOP_PIN(I2S0_MCLK, TOP_REG6, 3, 3, 0x9c, 0,
+ TOP_MUX(0x0, "I2S0"), /* mclk */
+ TOP_MUX(0x1, "TSO1"), /* dat3 */
+ TOP_MUX(0x2, "LCD"), /* stvd */
+ TOP_MUX(0x3, "BGPIO"), /* gpio77 */
+ TOP_MUX(0x4, "USIM0"), /* cd */
+ TOP_MUX(0x5, "B_DVI1")), /* vs */
+ TOP_PIN(I2S0_BCLK, TOP_REG6, 6, 3, 0x9c, 9,
+ TOP_MUX(0x0, "I2S0"), /* bclk */
+ TOP_MUX(0x1, "TSO1"), /* dat4 */
+ TOP_MUX(0x2, "LCD"), /* sthl_hsync */
+ TOP_MUX(0x3, "BGPIO"), /* gpio78 */
+ TOP_MUX(0x4, "USIM0"), /* clk */
+ TOP_MUX(0x5, "B_DVI1")), /* hs */
+ TOP_PIN(I2S0_WS, TOP_REG6, 9, 3, 0x9c, 18,
+ TOP_MUX(0x0, "I2S0"), /* ws */
+ TOP_MUX(0x1, "TSO1"), /* dat5 */
+ TOP_MUX(0x2, "LCD"), /* sthr */
+ TOP_MUX(0x3, "BGPIO"), /* gpio79 */
+ TOP_MUX(0x4, "USIM0"), /* rst */
+ TOP_MUX(0x5, "B_DVI1")), /* d0 */
+ TOP_PIN(I2S0_DIN0, TOP_REG6, 12, 3, 0xa0, 0,
+ TOP_MUX(0x0, "I2S0"), /* din0 */
+ TOP_MUX(0x1, "TSO1"), /* dat6 */
+ TOP_MUX(0x2, "LCD"), /* oev_dataen */
+ TOP_MUX(0x3, "BGPIO"), /* gpio80 */
+ TOP_MUX(0x4, "USIM0"), /* dat */
+ TOP_MUX(0x5, "B_DVI1")), /* d1 */
+ TOP_PIN(I2S0_DOUT0, TOP_REG6, 15, 2, 0xa0, 9,
+ TOP_MUX(0x0, "I2S0"), /* dout0 */
+ TOP_MUX(0x1, "TSO1"), /* dat7 */
+ TOP_MUX(0x2, "LCD"), /* ckv */
+ TOP_MUX(0x3, "BGPIO")), /* gpio81 */
+ TOP_PIN(I2C5_SCL, TOP_REG6, 17, 3, 0xa0, 18,
+ TOP_MUX(0x0, "I2C5"), /* scl */
+ TOP_MUX(0x1, "TSO1"), /* sync */
+ TOP_MUX(0x2, "LCD"), /* ld */
+ TOP_MUX(0x3, "BGPIO"), /* gpio82 */
+ TOP_MUX(0x4, "PWM"), /* out2 */
+ TOP_MUX(0x5, "I2S0"), /* dout2 */
+ TOP_MUX(0x6, "B_DVI1")), /* d2 */
+ TOP_PIN(I2C5_SDA, TOP_REG6, 20, 3, 0xa4, 0,
+ TOP_MUX(0x0, "I2C5"), /* sda */
+ TOP_MUX(0x1, "TSO1"), /* vld */
+ TOP_MUX(0x2, "LCD"), /* pol */
+ TOP_MUX(0x3, "BGPIO"), /* gpio83 */
+ TOP_MUX(0x4, "PWM"), /* out3 */
+ TOP_MUX(0x5, "I2S0"), /* dout3 */
+ TOP_MUX(0x6, "B_DVI1")), /* d3 */
+ TOP_PIN(SPI2_CLK, TOP_REG6, 23, 3, 0xa4, 9,
+ TOP_MUX(0x0, "SPI2"), /* clk */
+ TOP_MUX(0x1, "TSO0"), /* clk */
+ TOP_MUX(0x2, "LCD"), /* degsl */
+ TOP_MUX(0x3, "BGPIO"), /* gpio84 */
+ TOP_MUX(0x4, "I2C4"), /* scl */
+ TOP_MUX(0x5, "B_DVI1")), /* d4 */
+ TOP_PIN(SPI2_CS, TOP_REG6, 26, 3, 0xa4, 18,
+ TOP_MUX(0x0, "SPI2"), /* cs */
+ TOP_MUX(0x1, "TSO0"), /* data */
+ TOP_MUX(0x2, "LCD"), /* rev */
+ TOP_MUX(0x3, "BGPIO"), /* gpio85 */
+ TOP_MUX(0x4, "I2C4"), /* sda */
+ TOP_MUX(0x5, "B_DVI1")), /* d5 */
+ TOP_PIN(SPI2_TXD, TOP_REG6, 29, 3, 0xa8, 0,
+ TOP_MUX(0x0, "SPI2"), /* txd */
+ TOP_MUX(0x1, "TSO0"), /* sync */
+ TOP_MUX(0x2, "LCD"), /* u_d */
+ TOP_MUX(0x3, "BGPIO"), /* gpio86 */
+ TOP_MUX(0x4, "I2C4"), /* scl */
+ TOP_MUX(0x5, "B_DVI1")), /* d6 */
+
+ /* top_pmm_reg_7 */
+ TOP_PIN(SPI2_RXD, TOP_REG7, 0, 3, 0xa8, 9,
+ TOP_MUX(0x0, "SPI2"), /* rxd */
+ TOP_MUX(0x1, "TSO0"), /* vld */
+ TOP_MUX(0x2, "LCD"), /* r_l */
+ TOP_MUX(0x3, "BGPIO"), /* gpio87 */
+ TOP_MUX(0x4, "I2C3"), /* sda */
+ TOP_MUX(0x5, "B_DVI1")), /* d7 */
+ TOP_PIN(NAND_WP_N, TOP_REG7, 7, 3, 0x54, 9,
+ TOP_MUX(0x0, "NAND"), /* wp */
+ TOP_MUX(0x1, "PWM"), /* out2 */
+ TOP_MUX(0x2, "SPI2"), /* clk */
+ TOP_MUX(0x3, "BGPIO"), /* gpio88 */
+ TOP_MUX(0x4, "TSI0"), /* dat0 */
+ TOP_MUX(0x5, "I2S1")), /* din1 */
+ TOP_PIN(NAND_PAGE_SIZE0, TOP_REG7, 10, 3, 0xb8, 0,
+ TOP_MUX(0x0, "NAND"), /* boot_pagesize0 */
+ TOP_MUX(0x1, "PWM"), /* out3 */
+ TOP_MUX(0x2, "SPI2"), /* cs */
+ TOP_MUX(0x3, "BGPIO"), /* gpio89 */
+ TOP_MUX(0x4, "TSI0"), /* clk */
+ TOP_MUX(0x5, "I2S1")), /* din2 */
+ TOP_PIN(NAND_PAGE_SIZE1, TOP_REG7, 13, 3, 0xb8, 9,
+ TOP_MUX(0x0, "NAND"), /* boot_pagesize1 */
+ TOP_MUX(0x1, "I2C4"), /* scl */
+ TOP_MUX(0x2, "SPI2"), /* txd */
+ TOP_MUX(0x3, "BGPIO"), /* gpio90 */
+ TOP_MUX(0x4, "TSI0"), /* sync */
+ TOP_MUX(0x5, "I2S1")), /* din3 */
+ TOP_PIN(NAND_ADDR_CYCLE, TOP_REG7, 16, 3, 0xb8, 18,
+ TOP_MUX(0x0, "NAND"), /* boot_addr_cycles */
+ TOP_MUX(0x1, "I2C4"), /* sda */
+ TOP_MUX(0x2, "SPI2"), /* rxd */
+ TOP_MUX(0x3, "BGPIO"), /* gpio91 */
+ TOP_MUX(0x4, "TSI0"), /* valid */
+ TOP_MUX(0x5, "I2S1")), /* dout1 */
+ TOP_PIN(NAND_RB0, TOP_REG7, 19, 3, 0xbc, 0,
+ TOP_MUX(0x0, "NAND"), /* rdy_busy0 */
+ TOP_MUX(0x1, "I2C2"), /* scl */
+ TOP_MUX(0x2, "USIM0"), /* cd */
+ TOP_MUX(0x3, "BGPIO"), /* gpio92 */
+ TOP_MUX(0x4, "TSI1")), /* data0 */
+ TOP_PIN(NAND_RB1, TOP_REG7, 22, 3, 0xbc, 9,
+ TOP_MUX(0x0, "NAND"), /* rdy_busy1 */
+ TOP_MUX(0x1, "I2C2"), /* sda */
+ TOP_MUX(0x2, "USIM0"), /* clk */
+ TOP_MUX(0x3, "BGPIO"), /* gpio93 */
+ TOP_MUX(0x4, "TSI1")), /* clk */
+ TOP_PIN(NAND_RB2, TOP_REG7, 25, 3, 0xbc, 18,
+ TOP_MUX(0x0, "NAND"), /* rdy_busy2 */
+ TOP_MUX(0x1, "UART5"), /* rxd */
+ TOP_MUX(0x2, "USIM0"), /* rst */
+ TOP_MUX(0x3, "BGPIO"), /* gpio94 */
+ TOP_MUX(0x4, "TSI1"), /* sync */
+ TOP_MUX(0x4, "I2S1")), /* dout2 */
+ TOP_PIN(NAND_RB3, TOP_REG7, 28, 3, 0x54, 18,
+ TOP_MUX(0x0, "NAND"), /* rdy_busy3 */
+ TOP_MUX(0x1, "UART5"), /* txd */
+ TOP_MUX(0x2, "USIM0"), /* dat */
+ TOP_MUX(0x3, "BGPIO"), /* gpio95 */
+ TOP_MUX(0x4, "TSI1"), /* valid */
+ TOP_MUX(0x4, "I2S1")), /* dout3 */
+
+ /* top_pmm_reg_8 */
+ TOP_PIN(GMAC_125M_IN, TOP_REG8, 0, 2, 0x34, 0,
+ TOP_MUX(0x0, "GMII"), /* 125m_in */
+ TOP_MUX(0x1, "USB2"), /* 0_drvvbus */
+ TOP_MUX(0x2, "ISP"), /* ref_clk */
+ TOP_MUX(0x3, "BGPIO")), /* gpio96 */
+ TOP_PIN(GMAC_50M_OUT, TOP_REG8, 2, 2, 0x34, 9,
+ TOP_MUX(0x0, "GMII"), /* 50m_out */
+ TOP_MUX(0x1, "USB2"), /* 1_drvvbus */
+ TOP_MUX(0x2, "BGPIO"), /* gpio97 */
+ TOP_MUX(0x3, "USB2")), /* 0_drvvbus */
+ TOP_PIN(SPINOR_SSCLK_LOOPBACK, TOP_REG8, 6, 1, 0xc8, 9,
+ TOP_MUX(0x0, "SPINOR")), /* sdio1_clk_i */
+ TOP_PIN(SPINOR_SDIO1CLK_LOOPBACK, TOP_REG8, 7, 1, 0xc8, 18,
+ TOP_MUX(0x0, "SPINOR")), /* ssclk_i */
+};
+
+static struct zx_pinctrl_soc_info zx296718_pinctrl_info = {
+ .pins = zx296718_pins,
+ .npins = ARRAY_SIZE(zx296718_pins),
+};
+
+static int zx296718_pinctrl_probe(struct platform_device *pdev)
+{
+ return zx_pinctrl_init(pdev, &zx296718_pinctrl_info);
+}
+
+static const struct of_device_id zx296718_pinctrl_match[] = {
+ { .compatible = "zte,zx296718-pmm", },
+ {}
+};
+MODULE_DEVICE_TABLE(of, zx296718_pinctrl_match);
+
+static struct platform_driver zx296718_pinctrl_driver = {
+ .probe = zx296718_pinctrl_probe,
+ .driver = {
+ .name = "zx296718-pinctrl",
+ .of_match_table = zx296718_pinctrl_match,
+ },
+};
+builtin_platform_driver(zx296718_pinctrl_driver);
+
+MODULE_DESCRIPTION("ZTE ZX296718 pinctrl driver");
+MODULE_LICENSE("GPL");
--
1.9.1
^ permalink raw reply related
* [PATCH 1/2] dt-bindings: add bindings doc for ZTE pinctrl
From: Shawn Guo @ 2017-04-24 13:01 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1493038873-18354-1-git-send-email-shawnguo@kernel.org>
From: Shawn Guo <shawn.guo@linaro.org>
It adds device tree bindings for ZTE pin controller found on ZX2967xx
family SoCs.
Signed-off-by: Shawn Guo <shawn.guo@linaro.org>
---
.../devicetree/bindings/pinctrl/pinctrl-zx.txt | 85 ++++++++++++++++++++++
1 file changed, 85 insertions(+)
create mode 100644 Documentation/devicetree/bindings/pinctrl/pinctrl-zx.txt
diff --git a/Documentation/devicetree/bindings/pinctrl/pinctrl-zx.txt b/Documentation/devicetree/bindings/pinctrl/pinctrl-zx.txt
new file mode 100644
index 000000000000..e219849b21ca
--- /dev/null
+++ b/Documentation/devicetree/bindings/pinctrl/pinctrl-zx.txt
@@ -0,0 +1,85 @@
+* ZTE ZX Pin Controller
+
+The pin controller on ZTE ZX platforms is kinda of hybrid. It consists of
+a main controller and an auxiliary one. For example, on ZX296718 SoC, the
+main controller is TOP_PMM and the auxiliary one is AON_IOCFG. Both
+controllers work together to control pin multiplexing and configuration in
+the way illustrated as below.
+
+
+ GMII_RXD3 ---+
+ |
+ DVI1_HS ---+----------------------------- GMII_RXD3 (TOP pin)
+ |
+ BGPIO16 ---+ ^
+ | pinconf
+ ^ |
+ | pinmux |
+ | |
+
+ TOP_PMM (main) AON_IOCFG (aux)
+
+ | | |
+ | pinmux | |
+ | pinmux v |
+ v | pinconf
+ KEY_ROW2 ---+ v
+ PORT1_LCD_TE ---+ |
+ | AGPIO10 ---+------ KEY_ROW2 (AON pin)
+ I2S0_DOUT3 ---+ |
+ |-----------------------+
+ PWM_OUT3 ---+
+ |
+ VGA_VS1 ---+
+
+
+For most of pins like GMII_RXD3 in the figure, the pinmux function is
+controlled by TOP_PMM block only, and this type of pins are meant by term
+'TOP pins'. For pins like KEY_ROW2, the pinmux is controlled by both
+TOP_PMM and AON_IOCFG blocks, as the available multiplexing functions for
+the pin spread in both controllers. This type of pins are called 'AON pins'.
+Though pinmux implementation is quite different, pinconf is same for both
+types of pins. Both are controlled by auxiliary controller, i.e. AON_IOCFG
+on ZX296718.
+
+Required properties:
+- compatible: should be "zte,zx296718-pmm".
+- reg: the register physical address and length.
+- zte,auxiliary-controller: phandle to the auxiliary pin controller which
+ implements pinmux for AON pins and pinconf for all pins.
+
+The following pin configuration are supported. Please refer to
+pinctrl-bindings.txt in this directory for more details of the common
+pinctrl bindings used by client devices.
+
+- bias-pull-up
+- bias-pull-down
+- drive-strength
+- input-enable
+- slew-rate
+
+Examples:
+
+iocfg: pin-controller at 119000 {
+ compatible = "zte,zx296718-iocfg";
+ reg = <0x119000 0x1000>;
+};
+
+pmm: pin-controller at 1462000 {
+ compatible = "zte,zx296718-pmm";
+ reg = <0x1462000 0x1000>;
+ zte,auxiliary-controller = <&iocfg>;
+};
+
+&pmm {
+ vga_pins: vga {
+ pins = "KEY_COL1", "KEY_COL2", "KEY_ROW1", "KEY_ROW2";
+ function = "VGA";
+ };
+};
+
+&vga {
+ pinctrl-names = "default";
+ pinctrl-0 = <&vga_pins>;
+ status = "okay";
+};
--
1.9.1
^ permalink raw reply related
* [PATCH 0/2] Add ZTE ZX pinctrl driver support
From: Shawn Guo @ 2017-04-24 13:01 UTC (permalink / raw)
To: linux-arm-kernel
From: Shawn Guo <shawn.guo@linaro.org>
This is basically a rewrite of Jun's ZTE ZX pinctrl driver. It
has quite a few significant changes from Jun's version.
- It handles the relation between main and auxiliary controllers
internally, and then we do not need to export core function
pin_request().
- Update bindings document with a ASCII figure to illustrate how this
'hybrid' pin controller hardware works.
- Rather than making up pin name with the fake port name and pin
number, name the pins in the exactly same way how hardware manual
name them.
- Instead of encoding AON register data, calculate the offset and bit
position from pin id.
- Utilize the generic groups and functions support from core to save
quite some code in our driver.
- Simplify the data structures and add documents for important ones.
- Clean up some unnecessary function calls.
Shawn
[1] https://www.spinics.net/lists/linux-gpio/msg16147.html
Shawn Guo (2):
dt-bindings: add bindings doc for ZTE pinctrl
pinctrl: add ZTE ZX pinctrl driver support
.../devicetree/bindings/pinctrl/pinctrl-zx.txt | 85 ++
drivers/pinctrl/Kconfig | 1 +
drivers/pinctrl/Makefile | 1 +
drivers/pinctrl/zte/Kconfig | 13 +
drivers/pinctrl/zte/Makefile | 2 +
drivers/pinctrl/zte/pinctrl-zx.c | 445 +++++++++
drivers/pinctrl/zte/pinctrl-zx.h | 105 ++
drivers/pinctrl/zte/pinctrl-zx296718.c | 1027 ++++++++++++++++++++
8 files changed, 1679 insertions(+)
create mode 100644 Documentation/devicetree/bindings/pinctrl/pinctrl-zx.txt
create mode 100644 drivers/pinctrl/zte/Kconfig
create mode 100644 drivers/pinctrl/zte/Makefile
create mode 100644 drivers/pinctrl/zte/pinctrl-zx.c
create mode 100644 drivers/pinctrl/zte/pinctrl-zx.h
create mode 100644 drivers/pinctrl/zte/pinctrl-zx296718.c
--
1.9.1
^ permalink raw reply
* [PATCH v3 2/3] crypto: inside-secure: add SafeXcel EIP197 crypto engine driver
From: Stephan Müller @ 2017-04-24 12:59 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20170424075407.19730-3-antoine.tenart@free-electrons.com>
Am Montag, 24. April 2017, 09:54:06 CEST schrieb Antoine Tenart:
Hi Antoine,
> +struct safexcel_cipher_ctx {
> + struct safexcel_context base;
> + struct safexcel_crypto_priv *priv;
> +
> + enum safexcel_cipher_direction direction;
> + u32 mode;
> +
> + __le32 key[8];
Can you please help me find the location where this memory is zeroized when
released?
> + unsigned int key_len;
> +};
> +
> +static void safexcel_cipher_token(struct safexcel_cipher_ctx *ctx,
> + struct crypto_async_request *async,
> + struct safexcel_command_desc *cdesc,
> + u32 length)
> +{
> + struct ablkcipher_request *req = ablkcipher_request_cast(async);
> + struct safexcel_token *token;
> + unsigned offset = 0;
> +
> + if (ctx->mode == CONTEXT_CONTROL_CRYPTO_MODE_CBC) {
> + offset = AES_BLOCK_SIZE / sizeof(u32);
> + memcpy(cdesc->control_data.token, req->info, AES_BLOCK_SIZE);
> +
> + cdesc->control_data.options |= EIP197_OPTION_4_TOKEN_IV_CMD;
> + }
> +
> + token = (struct safexcel_token *)(cdesc->control_data.token + offset);
> +
> + token[0].opcode = EIP197_TOKEN_OPCODE_DIRECTION;
> + token[0].packet_length = length;
> + token[0].stat = EIP197_TOKEN_STAT_LAST_PACKET;
> + token[0].instructions = EIP197_TOKEN_INS_LAST |
> + EIP197_TOKEN_INS_TYPE_CRYTO |
> + EIP197_TOKEN_INS_TYPE_OUTPUT;
> +}
> +
> +static int safexcel_aes_setkey(struct crypto_ablkcipher *ctfm, const u8
> *key, + unsigned int len)
> +{
You still use ablkcipher. I thought that it is on its way out in favor of the
skcipher API. Why do you stick to ablkcipher?
Note, a change could be as simple as s/ablkcipher/skcipher/g
> + struct crypto_tfm *tfm = crypto_ablkcipher_tfm(ctfm);
> + struct safexcel_cipher_ctx *ctx = crypto_tfm_ctx(tfm);
> + struct crypto_aes_ctx aes;
> + int ret, i;
> +
> + ret = crypto_aes_expand_key(&aes, key, len);
> + if (ret) {
> + crypto_ablkcipher_set_flags(ctfm, CRYPTO_TFM_RES_BAD_KEY_LEN);
> + return ret;
> + }
> +
> + for (i = 0; i < len / sizeof(u32); i++) {
> + if (ctx->key[i] != cpu_to_le32(aes.key_enc[i])) {
> + ctx->base.needs_inv = true;
> + break;
> + }
> + }
> +
> + for (i = 0; i < len / sizeof(u32); i++)
> + ctx->key[i] = cpu_to_le32(aes.key_enc[i]);
> +
> + ctx->key_len = len;
memzero_explicit(aes)?
> +
> + return 0;
> +}
> +
> +static int safexcel_context_control(struct safexcel_cipher_ctx *ctx,
> + struct safexcel_command_desc *cdesc)
> +{
> + struct safexcel_crypto_priv *priv = ctx->priv;
> + int ctrl_size;
> +
> + if (ctx->direction == SAFEXCEL_ENCRYPT)
> + cdesc->control_data.control0 |= CONTEXT_CONTROL_TYPE_CRYPTO_OUT;
> + else
> + cdesc->control_data.control0 |= CONTEXT_CONTROL_TYPE_CRYPTO_IN;
> +
> + cdesc->control_data.control0 |= CONTEXT_CONTROL_KEY_EN;
> + cdesc->control_data.control1 |= ctx->mode;
> +
> + switch (ctx->key_len) {
> + case AES_KEYSIZE_128:
> + cdesc->control_data.control0 |= CONTEXT_CONTROL_CRYPTO_ALG_AES128;
> + ctrl_size = 4;
> + break;
> + case AES_KEYSIZE_192:
> + cdesc->control_data.control0 |= CONTEXT_CONTROL_CRYPTO_ALG_AES192;
> + ctrl_size = 6;
> + break;
> + case AES_KEYSIZE_256:
> + cdesc->control_data.control0 |= CONTEXT_CONTROL_CRYPTO_ALG_AES256;
> + ctrl_size = 8;
> + break;
> + default:
> + dev_err(priv->dev, "aes keysize not supported: %u\n",
> + ctx->key_len);
> + return -EINVAL;
> + }
> + cdesc->control_data.control0 |= CONTEXT_CONTROL_SIZE(ctrl_size);
> +
> + return 0;
> +}
> +
> +static int safexcel_handle_result(struct safexcel_crypto_priv *priv, int
> ring, + struct crypto_async_request *async,
> + bool *should_complete, int *ret)
> +{
> + struct ablkcipher_request *req = ablkcipher_request_cast(async);
> + struct safexcel_result_desc *rdesc;
> + int ndesc = 0;
> +
> + *ret = 0;
> +
> + spin_lock_bh(&priv->ring[ring].egress_lock);
> + do {
> + rdesc = safexcel_ring_next_rptr(priv, &priv->ring[ring].rdr);
> + if (IS_ERR(rdesc)) {
> + dev_err(priv->dev,
> + "cipher: result: could not retrieve the result descriptor\n");
> + *ret = PTR_ERR(rdesc);
> + break;
> + }
> +
> + if (rdesc->result_data.error_code) {
> + dev_err(priv->dev,
> + "cipher: result: result descriptor error (%d)\n",
> + rdesc->result_data.error_code);
> + *ret = -EIO;
> + }
> +
> + ndesc++;
> + } while (!rdesc->last_seg);
> +
> + safexcel_complete(priv, ring);
> + spin_unlock_bh(&priv->ring[ring].egress_lock);
> +
> + if (req->src == req->dst) {
> + dma_unmap_sg(priv->dev, req->src,
> + sg_nents_for_len(req->src, req->nbytes),
> + DMA_BIDIRECTIONAL);
> + } else {
> + dma_unmap_sg(priv->dev, req->src,
> + sg_nents_for_len(req->src, req->nbytes),
> + DMA_TO_DEVICE);
> + dma_unmap_sg(priv->dev, req->dst,
> + sg_nents_for_len(req->dst, req->nbytes),
> + DMA_FROM_DEVICE);
> + }
> +
> + *should_complete = true;
> +
> + return ndesc;
> +}
> +
> +static int safexcel_aes_send(struct crypto_async_request *async,
> + int ring, struct safexcel_request *request,
> + int *commands, int *results)
> +{
> + struct ablkcipher_request *req = ablkcipher_request_cast(async);
> + struct safexcel_cipher_ctx *ctx = crypto_tfm_ctx(req->base.tfm);
> + struct safexcel_crypto_priv *priv = ctx->priv;
> + struct safexcel_command_desc *cdesc;
> + struct safexcel_result_desc *rdesc;
> + struct scatterlist *sg;
> + int nr_src, nr_dst, n_cdesc = 0, n_rdesc = 0, queued = req->nbytes;
> + int i, ret = 0;
> +
> + request->req = &req->base;
> +
> + if (req->src == req->dst) {
> + nr_src = dma_map_sg(priv->dev, req->src,
> + sg_nents_for_len(req->src, req->nbytes),
> + DMA_BIDIRECTIONAL);
> + nr_dst = nr_src;
> + if (!nr_src)
> + return -EINVAL;
> + } else {
> + nr_src = dma_map_sg(priv->dev, req->src,
> + sg_nents_for_len(req->src, req->nbytes),
> + DMA_TO_DEVICE);
> + if (!nr_src)
> + return -EINVAL;
> +
> + nr_dst = dma_map_sg(priv->dev, req->dst,
> + sg_nents_for_len(req->dst, req->nbytes),
> + DMA_FROM_DEVICE);
> + if (!nr_dst) {
> + dma_unmap_sg(priv->dev, req->src,
> + sg_nents_for_len(req->src, req->nbytes),
> + DMA_TO_DEVICE);
> + return -EINVAL;
> + }
> + }
> +
> + memcpy(ctx->base.ctxr->data, ctx->key, ctx->key_len);
Is ctxr->data properly zeroized?
Ciao
Stephan
^ permalink raw reply
* [PATCH v4 13/18] arm64: allwinner: sun50i-a64: add dwmac-sun8i Ethernet driver
From: Chen-Yu Tsai @ 2017-04-24 12:58 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20170424122411.GA9349@Red>
On Mon, Apr 24, 2017 at 8:24 PM, Corentin Labbe
<clabbe.montjoie@gmail.com> wrote:
> On Wed, Apr 12, 2017 at 02:41:53PM +0200, Maxime Ripard wrote:
>> On Wed, Apr 12, 2017 at 01:13:55PM +0200, Corentin Labbe wrote:
>> > The dwmac-sun8i is an Ethernet MAC that supports 10/100/1000 Mbit
>> > connections. It is very similar to the device found in the Allwinner
>> > H3, but lacks the internal 100 Mbit PHY and its associated control
>> > bits.
>> > This adds the necessary bits to the Allwinner A64 SoC .dtsi, but keeps
>> > it disabled at this level.
>> >
>> > Signed-off-by: Corentin Labbe <clabbe.montjoie@gmail.com>
>> > ---
>> > arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi | 37 +++++++++++++++++++++++++++
>> > 1 file changed, 37 insertions(+)
>> >
>> > diff --git a/arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi b/arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi
>> > index 0b0f4ab..2569827 100644
>> > --- a/arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi
>> > +++ b/arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi
>> > @@ -287,6 +287,23 @@
>> > bias-pull-up;
>> > };
>> >
>> > + rmii_pins: rmii_pins {
>> > + pins = "PD10", "PD11", "PD13", "PD14",
>> > + "PD17", "PD18", "PD19", "PD20",
>> > + "PD22", "PD23";
>>
>> Please align the wrapped lines on the first pin.
>>
>
> OK
>
>> > + function = "emac";
>> > + drive-strength = <40>;
>>
>> Do you actually need that for all the boards, or only a few of them?
>
> I have tried to use lower value without success on some boards. (opipc/pine64 in my memory)
FYI we need them for all the boards that use RGMII.
The signals at gigabit speed run at 125 MHz DDR.
For RMII we probably don't need it. Even at 100 Mbps,
it's only 50 MHz SDR. drive-strength = <30> should be
enough.
ChenYu
^ permalink raw reply
* [PATCH] arm64: pmuv3: Support v8.1 PMUv3 extension
From: Mark Rutland @ 2017-04-24 12:57 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1493033503-4712-1-git-send-email-jnair@caviumnetworks.com>
Hi,
On Mon, Apr 24, 2017 at 11:31:43AM +0000, Jayachandran C wrote:
> The PMUVer field can have a value 4 for PMUv3 which supports 16 bit
> evtCount field (this is documented in ARM Architecture Reference Manual
> Supplement ARMv8.1).
>
> The current check for PMUVer to be equal to 1 fails on ThunderX2 which
> has value 4 in PMUVer field. Fix this.
>
> Signed-off-by: Jayachandran C <jnair@caviumnetworks.com>
> ---
>
> This applies on top of the current arm64 tree and fixes a breakage due
> to the ACPI perf patches.
Sorry for the delay on this. I'm still awaiting an architectural
clarification on how this field should be interpreted, as per my prior
comments [1].
I realise that's not much consolation here, so I'm happy to take an
intermediate fix. One comment on that below.
> arch/arm64/kernel/perf_event.c | 2 +-
> 1 file changed, 1 insertion(+), 1 deletion(-)
>
> diff --git a/arch/arm64/kernel/perf_event.c b/arch/arm64/kernel/perf_event.c
> index 98c7493..5388ed8 100644
> --- a/arch/arm64/kernel/perf_event.c
> +++ b/arch/arm64/kernel/perf_event.c
> @@ -972,7 +972,7 @@ static void __armv8pmu_probe_pmu(void *info)
> dfr0 = read_sysreg(id_aa64dfr0_el1);
> pmuver = cpuid_feature_extract_unsigned_field(dfr0,
> ID_AA64DFR0_PMUVER_SHIFT);
> - if (pmuver != 1)
> + if (pmuver != 1 && pmuver != 4)
> return;
Can we please make this:
pmuver = cpuid_feature_extract_signed_field(dfr0,
ID_AA64DFR0_PMUVER_SHIFT);
if (pmuver < 1)
return;
With that, I'm happy to take this while we await further clarification.
Thanks,
Mark.
[1] http://lists.infradead.org/pipermail/linux-arm-kernel/2017-April/501305.html
IMPORTANT NOTICE: The contents of this email and any attachments are confidential and may also be privileged. If you are not the intended recipient, please notify the sender immediately and do not disclose the contents to any other person, use it for any purpose, or store or copy the information in any medium. Thank you.
^ permalink raw reply
* [PATCH RFC 0/5] *** SPI Slave mode support ***
From: Jiada Wang @ 2017-04-24 12:48 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <CAMuHMdXa_O6RsqUciiNWQ0Zp6dniS47AUmPN9UWAWitP0csx=Q@mail.gmail.com>
Hello Geert
On 04/24/2017 03:55 AM, Geert Uytterhoeven wrote:
> Hi Jiada,
>
> On Fri, Apr 14, 2017 at 7:39 AM, Jiada Wang<jiada_wang@mentor.com> wrote:
>> On 04/13/2017 12:47 PM, Geert Uytterhoeven wrote:
>>> On Thu, Apr 13, 2017 at 2:59 PM, Mark Brown<broonie@kernel.org> wrote:
>>>> On Thu, Apr 13, 2017 at 05:13:59AM -0700, jiada_wang at mentor.com wrote:
>>>>> From: Jiada Wang<jiada_wang@mentor.com>
>>>>>
>>>>> v1:
>>>>> add Slave mode support in SPI core
>>>>> spidev create slave device when SPI controller work in slave mode
>>>>> spi-imx support to work in slave mode
>>>> Adding Geert who also had a series doing this in progress that was
>>>> getting very near to being merged.
>>> Thank you!
>>>
>>> Actually my plan is to fix the last remaining issues and resubmit for
>>> v4.13.
>> I noticed your patch set for SPI slave support,
>> (I am sure you can find out some of the change
>> in this patch set is based on your work).
>> we have similar requirement to add slave mode support to ecspi IP on imx6
>> Soc.
>>
>> Our use case is to use spidev as an interface to communicate with external
>> SPI master devices.
>> meanwhile the SPI bus controller can also act as master device to send data
>> to other
>> SPI slave devices on the board.
> That sounds a bit hackish to me. SPI was never meant to be a multi-master bus.
> While it can be done, you will need external synchronization (signals) to
> avoid conflicts between the SPI masters.
It doesn't need to be a multi-master bus,
for example A is master device for slave device B.
while B has its own slave device C
for each SPI connection A <=> B, and B <=> C, there is only one master
device.
and I think from use case point of view, it's very normal,
one CPU upon receives command from external SPI master device,
it writes data to its own slave device (EEPROM) connected to it.
Thanks,
Jiada
>> I found in your implementation, SPI bus controller is limited to either work
>> in master mode or
>> slave mode, is there any reasoning to not configure SPI mode based on SPI
>> devices use case?
> If you really need both master and slave support, you can use 2 subnodes
> in DT, the first representing the master, the second the slave.
>
> Mark, what's your opinion about this?
>
> Thanks!
>
> Gr{oetje,eeting}s,
>
> Geert
>
> --
> Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert at linux-m68k.org
>
> In personal conversations with technical people, I call myself a hacker. But
> when I'm talking to journalists I just say "programmer" or something like that.
> -- Linus Torvalds
^ permalink raw reply
* [PATCH 4/4] ARM: dts: stm32: Set gpio controller also as interrupt controller
From: Linus Walleij @ 2017-04-24 12:38 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1491577811-26989-5-git-send-email-alexandre.torgue@st.com>
On Fri, Apr 7, 2017 at 5:10 PM, Alexandre TORGUE
<alexandre.torgue@st.com> wrote:
> This patch set each gpio controller as a interrupt controller. User who
> wants to use gpio as interrupt will have choice to use either "gpiolib"
> interface or "common" interrupt interface.
>
> Signed-off-by: Alexandre TORGUE <alexandre.torgue@st.com>
Acked-by: Linus Walleij <linus.walleij@linaro.org>
Yours,
Linus Walleij
^ permalink raw reply
* [PATCH 3/4] pinctrl: stm32: Implement .get_direction gpio_chip callback
From: Linus Walleij @ 2017-04-24 12:37 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1491577811-26989-4-git-send-email-alexandre.torgue@st.com>
On Fri, Apr 7, 2017 at 5:10 PM, Alexandre TORGUE
<alexandre.torgue@st.com> wrote:
> Add .get_direction() gpiochip callback in STM32 pinctrl driver.
>
> Signed-off-by: Alexandre TORGUE <alexandre.torgue@st.com>
(...)
> +#include <linux/gpio.h>
No this is wrong, drivers should never include this file.
It is a deprecated consumer header.
> + if ((alt == 0) && (mode == 0))
> + ret = GPIOF_DIR_IN;
> + else if ((alt == 0) && (mode == 1))
> + ret = GPIOF_DIR_OUT;
Just return 0 or 1, that is the driver-internal API.
Yours,
Linus Walleij
^ permalink raw reply
* [PATCH 1/4] pinctrl: stm32: set pin to gpio input when used as interrupt
From: Linus Walleij @ 2017-04-24 12:36 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1491577811-26989-2-git-send-email-alexandre.torgue@st.com>
On Fri, Apr 7, 2017 at 5:10 PM, Alexandre TORGUE
<alexandre.torgue@st.com> wrote:
> This patch ensures that pin is correctly set as gpio input when it is used
> as an interrupt.
>
> Signed-off-by: Alexandre TORGUE <alexandre.torgue@st.com>
(...)
> +static int stm32_gpio_irq_request_resources(struct irq_data *irq_data)
> +{
> + struct stm32_gpio_bank *bank = irq_data->domain->host_data;
> + u32 ret;
> +
> + if (!gpiochip_is_requested(&bank->gpio_chip, irq_data->hwirq)) {
> + ret = stm32_gpio_request(&bank->gpio_chip, irq_data->hwirq);
> + if (ret)
> + return ret;
> + }
This is wrong. You should only use gpiochip_lock_as_irq(), because of the
following in Documentation/gpio/driver.txt:
---------------
It is legal for any IRQ consumer to request an IRQ from any irqchip no matter
if that is a combined GPIO+IRQ driver. The basic premise is that gpio_chip and
irq_chip are orthogonal, and offering their services independent of each
other.
(...)
So always prepare the hardware and make it ready for action in respective
callbacks from the GPIO and irqchip APIs. Do not rely on gpiod_to_irq() having
been called first.
This orthogonality leads to ambiguities that we need to solve: if there is
competition inside the subsystem which side is using the resource (a certain
GPIO line and register for example) it needs to deny certain operations and
keep track of usage inside of the gpiolib subsystem. This is why the API
below exists.
Locking IRQ usage
-----------------
Input GPIOs can be used as IRQ signals. When this happens, a driver is requested
to mark the GPIO as being used as an IRQ:
int gpiochip_lock_as_irq(struct gpio_chip *chip, unsigned int offset)
This will prevent the use of non-irq related GPIO APIs until the GPIO IRQ lock
is released:
void gpiochip_unlock_as_irq(struct gpio_chip *chip, unsigned int offset)
When implementing an irqchip inside a GPIO driver, these two functions should
typically be called in the .startup() and .shutdown() callbacks from the
irqchip.
When using the gpiolib irqchip helpers, these callback are automatically
assigned.
--------------
It is because of easy to make errors like this that I prefer that people try
to use GPIOLIB_IRQCHIP helpers insteaf of rolling their own irqchip code.
Yours,
Linus Walleij
^ permalink raw reply
* [PATCH] usb: chipidea: udc: fix NULL pointer dereference if udc_start failed
From: Jisheng Zhang @ 2017-04-24 12:35 UTC (permalink / raw)
To: linux-arm-kernel
Fix below NULL pointer dereference. we set ci->roles[CI_ROLE_GADGET]
too early in ci_hdrc_gadget_init(), if udc_start() fails due to some
reason, the ci->roles[CI_ROLE_GADGET] check in ci_hdrc_gadget_destroy
can't protect us.
We fix this issue by only setting ci->roles[CI_ROLE_GADGET] if
udc_start() succeed.
[ 1.398550] Unable to handle kernel NULL pointer dereference at
virtual address 00000000
...
[ 1.448600] PC is at dma_pool_free+0xb8/0xf0
[ 1.453012] LR is at dma_pool_free+0x28/0xf0
[ 2.113369] [<ffffff80081817d8>] dma_pool_free+0xb8/0xf0
[ 2.118857] [<ffffff800841209c>] destroy_eps+0x4c/0x68
[ 2.124165] [<ffffff8008413770>] ci_hdrc_gadget_destroy+0x28/0x50
[ 2.130461] [<ffffff800840fa30>] ci_hdrc_probe+0x588/0x7e8
[ 2.136129] [<ffffff8008380fb8>] platform_drv_probe+0x50/0xb8
[ 2.142066] [<ffffff800837f494>] driver_probe_device+0x1fc/0x2a8
[ 2.148270] [<ffffff800837f68c>] __device_attach_driver+0x9c/0xf8
[ 2.154563] [<ffffff800837d570>] bus_for_each_drv+0x58/0x98
[ 2.160317] [<ffffff800837f174>] __device_attach+0xc4/0x138
[ 2.166072] [<ffffff800837f738>] device_initial_probe+0x10/0x18
[ 2.172185] [<ffffff800837e58c>] bus_probe_device+0x94/0xa0
[ 2.177940] [<ffffff800837c560>] device_add+0x3f0/0x560
[ 2.183337] [<ffffff8008380d20>] platform_device_add+0x180/0x240
[ 2.189541] [<ffffff800840f0e8>] ci_hdrc_add_device+0x440/0x4f8
[ 2.195654] [<ffffff8008414194>] ci_hdrc_usb2_probe+0x13c/0x2d8
[ 2.201769] [<ffffff8008380fb8>] platform_drv_probe+0x50/0xb8
[ 2.207705] [<ffffff800837f494>] driver_probe_device+0x1fc/0x2a8
[ 2.213910] [<ffffff800837f5ec>] __driver_attach+0xac/0xb0
[ 2.219575] [<ffffff800837d4b0>] bus_for_each_dev+0x60/0xa0
[ 2.225329] [<ffffff800837ec80>] driver_attach+0x20/0x28
[ 2.230816] [<ffffff800837e880>] bus_add_driver+0x1d0/0x238
[ 2.236571] [<ffffff800837fdb0>] driver_register+0x60/0xf8
[ 2.242237] [<ffffff8008380ef4>] __platform_driver_register+0x44/0x50
[ 2.248891] [<ffffff80086fd440>] ci_hdrc_usb2_driver_init+0x18/0x20
[ 2.255365] [<ffffff8008082950>] do_one_initcall+0x38/0x128
[ 2.261121] [<ffffff80086e0d00>] kernel_init_freeable+0x1ac/0x250
[ 2.267414] [<ffffff800852f0b8>] kernel_init+0x10/0x100
[ 2.272810] [<ffffff8008082680>] ret_from_fork+0x10/0x50
Signed-off-by: Jisheng Zhang <jszhang@marvell.com>
---
drivers/usb/chipidea/udc.c | 8 ++++++--
1 file changed, 6 insertions(+), 2 deletions(-)
diff --git a/drivers/usb/chipidea/udc.c b/drivers/usb/chipidea/udc.c
index f88e9157fad0..60a786c87c06 100644
--- a/drivers/usb/chipidea/udc.c
+++ b/drivers/usb/chipidea/udc.c
@@ -1984,6 +1984,7 @@ static void udc_id_switch_for_host(struct ci_hdrc *ci)
int ci_hdrc_gadget_init(struct ci_hdrc *ci)
{
struct ci_role_driver *rdrv;
+ int ret;
if (!hw_read(ci, CAP_DCCPARAMS, DCCPARAMS_DC))
return -ENXIO;
@@ -1996,7 +1997,10 @@ int ci_hdrc_gadget_init(struct ci_hdrc *ci)
rdrv->stop = udc_id_switch_for_host;
rdrv->irq = udc_irq;
rdrv->name = "gadget";
- ci->roles[CI_ROLE_GADGET] = rdrv;
- return udc_start(ci);
+ ret = udc_start(ci);
+ if (!ret)
+ ci->roles[CI_ROLE_GADGET] = rdrv;
+
+ return ret;
}
--
2.11.0
^ permalink raw reply related
* [PATCH v2 6/7] ARM: Kconfig: Introduce MACH_STM32F469 flag
From: Linus Walleij @ 2017-04-24 12:30 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1491568984-20169-7-git-send-email-alexandre.torgue@st.com>
On Fri, Apr 7, 2017 at 2:43 PM, Alexandre TORGUE
<alexandre.torgue@st.com> wrote:
> This patch introduces the MACH_STM32F469 to make possible to only select
> STM32F469 pinctrl driver
>
> By default, all the MACH_STM32Fxxx flags will be set with STM32 defconfig.
>
> Signed-off-by: Alexandre TORGUE <alexandre.torgue@st.com>
Acked-by: Linus Walleij <linus.walleij@linaro.org>
Please funnel this through the ARM SoC tree.
Yours,
Linus Walleij
^ permalink raw reply
* [PATCH v2 5/7] Documentation: dt: Add new compatible to STM32 pinctrl driver bindings
From: Linus Walleij @ 2017-04-24 12:29 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1491568984-20169-6-git-send-email-alexandre.torgue@st.com>
On Fri, Apr 7, 2017 at 2:43 PM, Alexandre TORGUE
<alexandre.torgue@st.com> wrote:
> Add new compatible for stm32f469 MCU.
>
> Signed-off-by: Alexandre TORGUE <alexandre.torgue@st.com>
Fixed up subject and applied.
Yours,
Linus Walleij
^ permalink raw reply
* [PATCH v2 4/7] pinctrl: stm32: Add STM32F469 MCU support
From: Linus Walleij @ 2017-04-24 12:27 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1491568984-20169-5-git-send-email-alexandre.torgue@st.com>
On Fri, Apr 7, 2017 at 2:43 PM, Alexandre TORGUE
<alexandre.torgue@st.com> wrote:
> This patch which adds STM32F469 pinctrl and GPIO support, relies on the
> generic STM32 pinctrl driver.
>
> Signed-off-by: Alexandre TORGUE <alexandre.torgue@st.com>
Patch applied.
Yours,
Linus Walleij
^ permalink raw reply
* [PATCH 2/2] kvm: arm/arm64: Fix race in resetting stage2 PGD
From: Christoffer Dall @ 2017-04-24 12:27 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1493028624-29837-3-git-send-email-suzuki.poulose@arm.com>
On Mon, Apr 24, 2017 at 11:10:24AM +0100, Suzuki K Poulose wrote:
> In kvm_free_stage2_pgd() we check the stage2 PGD before holding
> the lock and proceed to take the lock if it is valid. And we unmap
> the page tables, followed by releasing the lock. We reset the PGD
> only after dropping this lock, which could cause a race condition
> where another thread waiting on the lock could potentially see that
> the PGD is still valid and proceed to perform a stage2 operation.
>
> This patch moves the stage2 PGD manipulation under the lock.
>
> Reported-by: Alexander Graf <agraf@suse.de>
> Cc: Christoffer Dall <christoffer.dall@linaro.org>
> Cc: Marc Zyngier <marc.zyngier@arm.com>
> Cc: Paolo Bonzini <pbonzini@redhat.com>
> Signed-off-by: Suzuki K Poulose <suzuki.poulose@arm.com>
Reviewed-by: Christoffer Dall <cdall@linaro.org>
> ---
> arch/arm/kvm/mmu.c | 14 ++++++++------
> 1 file changed, 8 insertions(+), 6 deletions(-)
>
> diff --git a/arch/arm/kvm/mmu.c b/arch/arm/kvm/mmu.c
> index 582a972..9c4026d 100644
> --- a/arch/arm/kvm/mmu.c
> +++ b/arch/arm/kvm/mmu.c
> @@ -835,16 +835,18 @@ void stage2_unmap_vm(struct kvm *kvm)
> */
> void kvm_free_stage2_pgd(struct kvm *kvm)
> {
> - if (kvm->arch.pgd == NULL)
> - return;
> + void *pgd = NULL;
>
> spin_lock(&kvm->mmu_lock);
> - unmap_stage2_range(kvm, 0, KVM_PHYS_SIZE);
> + if (kvm->arch.pgd) {
> + unmap_stage2_range(kvm, 0, KVM_PHYS_SIZE);
> + pgd = kvm->arch.pgd;
> + kvm->arch.pgd = NULL;
> + }
> spin_unlock(&kvm->mmu_lock);
> -
> /* Free the HW pgd, one page at a time */
> - free_pages_exact(kvm->arch.pgd, S2_PGD_SIZE);
> - kvm->arch.pgd = NULL;
> + if (pgd)
> + free_pages_exact(pgd, S2_PGD_SIZE);
> }
>
> static pud_t *stage2_get_pud(struct kvm *kvm, struct kvm_mmu_memory_cache *cache,
> --
> 2.7.4
>
^ 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