* [PATCH 2/3] arm64: hw_breakpoint: Handle inexact watchpoint addresses
From: Pratyush Anand @ 2016-10-20 5:53 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20161019120714.GM9193@arm.com>
Hi Will,
On Wednesday 19 October 2016 05:37 PM, Will Deacon wrote:
> On Fri, Oct 14, 2016 at 08:45:43AM +0530, Pratyush Anand wrote:
>>
>>
>> On Thursday 13 October 2016 10:33 PM, Pavel Labath wrote:
>>>> I think, its easier to go with your implementation. So, I have taken
>>>>> your patch and updated my perf/upstream_arm64_devel branch. May be you
>>>>> can give it a test for your test cases.
>>> I've checked out the new version of your branch, and it works great.
>>> I'll write a patch with additional test cases to go on top of your
>>> branch, as the tests there do not capture the bug I was fixing.
>>
>> That would be great. We can send them all together as V2.
>
> Did you send a v2? I've been holding off reviewing this, but I just want
> to make sure I didn't miss the update.
I just posted V2.
http://www.spinics.net/lists/arm-kernel/msg537194.html
~Pratyush
^ permalink raw reply
* [PATCH V2 5/5] selftests: arm64: add test for unaligned/inexact watchpoint handling
From: Pratyush Anand @ 2016-10-20 5:48 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <cover.1476941895.git.panand@redhat.com>
ARM64 hardware expects 64bit aligned address for watchpoint invocation.
However, it provides byte selection method to select any number of
consecutive byte set within the range of 1-8.
This patch adds support to test all such byte selection option for
different memory write sizes.
Patch also adds a test for handling the case when the cpu does not
report an address which exactly matches one of the regions we have
been watching (which is a situation permitted by the spec if an
instruction accesses both watched and unwatched regions). The test
was failing on a MSM8996pro before this patch series and is
passing now.
Signed-off-by: Pavel Labath <labath@google.com>
Signed-off-by: Pratyush Anand <panand@redhat.com>
---
tools/testing/selftests/breakpoints/Makefile | 5 +-
.../selftests/breakpoints/breakpoint_test_arm64.c | 236 +++++++++++++++++++++
2 files changed, 240 insertions(+), 1 deletion(-)
create mode 100644 tools/testing/selftests/breakpoints/breakpoint_test_arm64.c
diff --git a/tools/testing/selftests/breakpoints/Makefile b/tools/testing/selftests/breakpoints/Makefile
index 74e533fd4bc5..61b79e8df1f4 100644
--- a/tools/testing/selftests/breakpoints/Makefile
+++ b/tools/testing/selftests/breakpoints/Makefile
@@ -5,6 +5,9 @@ ARCH ?= $(shell echo $(uname_M) | sed -e s/i.86/x86/ -e s/x86_64/x86/)
ifeq ($(ARCH),x86)
TEST_PROGS := breakpoint_test
endif
+ifeq ($(ARCH),aarch64)
+TEST_PROGS := breakpoint_test_arm64
+endif
TEST_PROGS += step_after_suspend_test
@@ -13,4 +16,4 @@ all: $(TEST_PROGS)
include ../lib.mk
clean:
- rm -fr breakpoint_test step_after_suspend_test
+ rm -fr breakpoint_test breakpoint_test_arm64 step_after_suspend_test
diff --git a/tools/testing/selftests/breakpoints/breakpoint_test_arm64.c b/tools/testing/selftests/breakpoints/breakpoint_test_arm64.c
new file mode 100644
index 000000000000..3897e996541e
--- /dev/null
+++ b/tools/testing/selftests/breakpoints/breakpoint_test_arm64.c
@@ -0,0 +1,236 @@
+/*
+ * Copyright (C) 2016 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * Original Code by Pavel Labath <labath@google.com>
+ *
+ * Code modified by Pratyush Anand <panand@redhat.com>
+ * for testing different byte select for each access size.
+ *
+ */
+
+#define _GNU_SOURCE
+
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <sys/ptrace.h>
+#include <sys/param.h>
+#include <sys/uio.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <string.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <elf.h>
+#include <errno.h>
+#include <signal.h>
+
+#include "../kselftest.h"
+
+static volatile uint8_t var[96] __attribute__((__aligned__(32)));
+
+static void child(int size, int wr)
+{
+ volatile uint8_t *addr = &var[32 + wr];
+
+ if (ptrace(PTRACE_TRACEME, 0, NULL, NULL) != 0) {
+ perror("ptrace(PTRACE_TRACEME) failed");
+ _exit(1);
+ }
+
+ if (raise(SIGSTOP) != 0) {
+ perror("raise(SIGSTOP) failed");
+ _exit(1);
+ }
+
+ if ((uintptr_t) addr % size) {
+ perror("Wrong address write for the given size\n");
+ _exit(1);
+ }
+ switch (size) {
+ case 1:
+ *addr = 47;
+ break;
+ case 2:
+ *(uint16_t *)addr = 47;
+ break;
+ case 4:
+ *(uint32_t *)addr = 47;
+ break;
+ case 8:
+ *(uint64_t *)addr = 47;
+ break;
+ case 16:
+ __asm__ volatile ("stp x29, x30, %0" : "=m" (addr[0]));
+ break;
+ case 32:
+ __asm__ volatile ("stp q29, q30, %0" : "=m" (addr[0]));
+ break;
+ }
+
+ _exit(0);
+}
+
+static bool set_watchpoint(pid_t pid, int size, int wp)
+{
+ const volatile uint8_t *addr = &var[32 + wp];
+ const int offset = (uintptr_t)addr % 8;
+ const unsigned int byte_mask = ((1 << size) - 1) << offset;
+ const unsigned int type = 2; /* Write */
+ const unsigned int enable = 1;
+ const unsigned int control = byte_mask << 5 | type << 3 | enable;
+ struct user_hwdebug_state dreg_state;
+ struct iovec iov;
+
+ memset(&dreg_state, 0, sizeof(dreg_state));
+ dreg_state.dbg_regs[0].addr = (uintptr_t)(addr - offset);
+ dreg_state.dbg_regs[0].ctrl = control;
+ iov.iov_base = &dreg_state;
+ iov.iov_len = offsetof(struct user_hwdebug_state, dbg_regs) +
+ sizeof(dreg_state.dbg_regs[0]);
+ if (ptrace(PTRACE_SETREGSET, pid, NT_ARM_HW_WATCH, &iov) == 0)
+ return true;
+
+ if (errno == EIO) {
+ printf("ptrace(PTRACE_SETREGSET, NT_ARM_HW_WATCH) "
+ "not supported on this hardware\n");
+ ksft_exit_skip();
+ }
+ perror("ptrace(PTRACE_SETREGSET, NT_ARM_HW_WATCH) failed");
+ return false;
+}
+
+static bool run_test(int wr_size, int wp_size, int wr, int wp)
+{
+ int status;
+ siginfo_t siginfo;
+ pid_t pid = fork();
+ pid_t wpid;
+
+ if (pid < 0) {
+ perror("fork() failed");
+ return false;
+ }
+ if (pid == 0)
+ child(wr_size, wr);
+
+ wpid = waitpid(pid, &status, __WALL);
+ if (wpid != pid) {
+ perror("waitpid() failed");
+ return false;
+ }
+ if (!WIFSTOPPED(status)) {
+ printf("child did not stop\n");
+ return false;
+ }
+ if (WSTOPSIG(status) != SIGSTOP) {
+ printf("child did not stop with SIGSTOP\n");
+ return false;
+ }
+
+ if (!set_watchpoint(pid, wp_size, wp))
+ return false;
+
+ if (ptrace(PTRACE_CONT, pid, NULL, NULL) < 0) {
+ perror("ptrace(PTRACE_SINGLESTEP) failed");
+ return false;
+ }
+
+ alarm(3);
+ wpid = waitpid(pid, &status, __WALL);
+ if (wpid != pid) {
+ perror("waitpid() failed");
+ return false;
+ }
+ alarm(0);
+ if (WIFEXITED(status)) {
+ printf("child did not single-step\t");
+ return false;
+ }
+ if (!WIFSTOPPED(status)) {
+ printf("child did not stop\n");
+ return false;
+ }
+ if (WSTOPSIG(status) != SIGTRAP) {
+ printf("child did not stop with SIGTRAP\n");
+ return false;
+ }
+ if (ptrace(PTRACE_GETSIGINFO, pid, NULL, &siginfo) != 0) {
+ perror("ptrace(PTRACE_GETSIGINFO)");
+ return false;
+ }
+ if (siginfo.si_code != TRAP_HWBKPT) {
+ printf("Unexpected si_code %d\n", siginfo.si_code);
+ return false;
+ }
+
+ kill(pid, SIGKILL);
+ wpid = waitpid(pid, &status, 0);
+ if (wpid != pid) {
+ perror("waitpid() failed");
+ return false;
+ }
+ return true;
+}
+
+static void sigalrm(int sig)
+{
+}
+
+int main(int argc, char **argv)
+{
+ int opt;
+ bool succeeded = true;
+ struct sigaction act;
+ int wr, wp, size;
+ bool result;
+
+ act.sa_handler = sigalrm;
+ sigemptyset(&act.sa_mask);
+ act.sa_flags = 0;
+ sigaction(SIGALRM, &act, NULL);
+ for (size = 1; size <= 32; size = size*2) {
+ for (wr = 0; wr <= 32; wr = wr + size) {
+ for (wp = wr - size; wp <= wr + size; wp = wp + size) {
+ printf("Test size = %d write offset = %d watchpoint offset = %d\t", size, wr, wp);
+ result = run_test(size, MIN(size, 8), wr, wp);
+ if ((result && wr == wp) || (!result && wr != wp)) {
+ printf("[OK]\n");
+ ksft_inc_pass_cnt();
+ } else {
+ printf("[FAILED]\n");
+ ksft_inc_fail_cnt();
+ succeeded = false;
+ }
+ }
+ }
+ }
+
+ for (size = 1; size <= 32; size = size*2) {
+ printf("Test size = %d write offset = %d watchpoint offset = -8\t", size, -size);
+
+ if (run_test(size, 8, -size, -8)) {
+ printf("[OK]\n");
+ ksft_inc_pass_cnt();
+ } else {
+ printf("[FAILED]\n");
+ ksft_inc_fail_cnt();
+ succeeded = false;
+ }
+ }
+
+ ksft_print_cnts();
+ if (succeeded)
+ ksft_exit_pass();
+ else
+ ksft_exit_fail();
+}
--
2.7.4
^ permalink raw reply related
* [PATCH V2 4/5] arm64: Allow hw watchpoint of length 3,5,6 and 7
From: Pratyush Anand @ 2016-10-20 5:48 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <cover.1476941895.git.panand@redhat.com>
Since, arm64 can support all offset within a double word limit. Therefore,
now support other lengths within that range as well.
Signed-off-by: Pratyush Anand <panand@redhat.com>
---
arch/arm64/include/asm/hw_breakpoint.h | 4 ++++
arch/arm64/kernel/hw_breakpoint.c | 36 ++++++++++++++++++++++++++++++++++
2 files changed, 40 insertions(+)
diff --git a/arch/arm64/include/asm/hw_breakpoint.h b/arch/arm64/include/asm/hw_breakpoint.h
index 4f4e58bee9bc..7a18c8520588 100644
--- a/arch/arm64/include/asm/hw_breakpoint.h
+++ b/arch/arm64/include/asm/hw_breakpoint.h
@@ -76,7 +76,11 @@ static inline void decode_ctrl_reg(u32 reg,
/* Lengths */
#define ARM_BREAKPOINT_LEN_1 0x1
#define ARM_BREAKPOINT_LEN_2 0x3
+#define ARM_BREAKPOINT_LEN_3 0x7
#define ARM_BREAKPOINT_LEN_4 0xf
+#define ARM_BREAKPOINT_LEN_5 0x1f
+#define ARM_BREAKPOINT_LEN_6 0x3f
+#define ARM_BREAKPOINT_LEN_7 0x7f
#define ARM_BREAKPOINT_LEN_8 0xff
/* Kernel stepping */
diff --git a/arch/arm64/kernel/hw_breakpoint.c b/arch/arm64/kernel/hw_breakpoint.c
index c57bc90b8286..4125c2152e85 100644
--- a/arch/arm64/kernel/hw_breakpoint.c
+++ b/arch/arm64/kernel/hw_breakpoint.c
@@ -317,9 +317,21 @@ static int get_hbp_len(u8 hbp_len)
case ARM_BREAKPOINT_LEN_2:
len_in_bytes = 2;
break;
+ case ARM_BREAKPOINT_LEN_3:
+ len_in_bytes = 3;
+ break;
case ARM_BREAKPOINT_LEN_4:
len_in_bytes = 4;
break;
+ case ARM_BREAKPOINT_LEN_5:
+ len_in_bytes = 5;
+ break;
+ case ARM_BREAKPOINT_LEN_6:
+ len_in_bytes = 6;
+ break;
+ case ARM_BREAKPOINT_LEN_7:
+ len_in_bytes = 7;
+ break;
case ARM_BREAKPOINT_LEN_8:
len_in_bytes = 8;
break;
@@ -379,9 +391,21 @@ int arch_bp_generic_fields(struct arch_hw_breakpoint_ctrl ctrl,
case ARM_BREAKPOINT_LEN_2:
*gen_len = HW_BREAKPOINT_LEN_2;
break;
+ case ARM_BREAKPOINT_LEN_3:
+ *gen_len = HW_BREAKPOINT_LEN_3;
+ break;
case ARM_BREAKPOINT_LEN_4:
*gen_len = HW_BREAKPOINT_LEN_4;
break;
+ case ARM_BREAKPOINT_LEN_5:
+ *gen_len = HW_BREAKPOINT_LEN_5;
+ break;
+ case ARM_BREAKPOINT_LEN_6:
+ *gen_len = HW_BREAKPOINT_LEN_6;
+ break;
+ case ARM_BREAKPOINT_LEN_7:
+ *gen_len = HW_BREAKPOINT_LEN_7;
+ break;
case ARM_BREAKPOINT_LEN_8:
*gen_len = HW_BREAKPOINT_LEN_8;
break;
@@ -425,9 +449,21 @@ static int arch_build_bp_info(struct perf_event *bp)
case HW_BREAKPOINT_LEN_2:
info->ctrl.len = ARM_BREAKPOINT_LEN_2;
break;
+ case HW_BREAKPOINT_LEN_3:
+ info->ctrl.len = ARM_BREAKPOINT_LEN_3;
+ break;
case HW_BREAKPOINT_LEN_4:
info->ctrl.len = ARM_BREAKPOINT_LEN_4;
break;
+ case HW_BREAKPOINT_LEN_5:
+ info->ctrl.len = ARM_BREAKPOINT_LEN_5;
+ break;
+ case HW_BREAKPOINT_LEN_6:
+ info->ctrl.len = ARM_BREAKPOINT_LEN_6;
+ break;
+ case HW_BREAKPOINT_LEN_7:
+ info->ctrl.len = ARM_BREAKPOINT_LEN_7;
+ break;
case HW_BREAKPOINT_LEN_8:
info->ctrl.len = ARM_BREAKPOINT_LEN_8;
break;
--
2.7.4
^ permalink raw reply related
* [PATCH V2 3/5] arm64: hw_breakpoint: Handle inexact watchpoint addresses
From: Pratyush Anand @ 2016-10-20 5:48 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <cover.1476941895.git.panand@redhat.com>
From: Pavel Labath <test.tberghammer@gmail.com>
Arm64 hardware does not always report a watchpoint hit address that
matches one of the watchpoints set. It can also report an address
"near" the watchpoint if a single instruction access both watched and
unwatched addresses. There is no straight-forward way, short of
disassembling the offending instruction, to map that address back to
the watchpoint.
Previously, when the hardware reported a watchpoint hit on an address
that did not match our watchpoint (this happens in case of instructions
which access large chunks of memory such as "stp") the process would
enter a loop where we would be continually resuming it (because we did
not recognise that watchpoint hit) and it would keep hitting the
watchpoint again and again. The tracing process would never get
notified of the watchpoint hit.
This commit fixes the problem by looking at the watchpoints near the
address reported by the hardware. If the address does not exactly match
one of the watchpoints we have set, it attributes the hit to the
nearest watchpoint we have. This heuristic is a bit dodgy, but I don't
think we can do much more, given the hardware limitations.
[panand: reworked to rebase on his patches]
Signed-off-by: Pavel Labath <labath@google.com>
Signed-off-by: Pratyush Anand <panand@redhat.com>
---
arch/arm64/kernel/hw_breakpoint.c | 94 +++++++++++++++++++++++++++------------
1 file changed, 66 insertions(+), 28 deletions(-)
diff --git a/arch/arm64/kernel/hw_breakpoint.c b/arch/arm64/kernel/hw_breakpoint.c
index 3c2b96803eba..c57bc90b8286 100644
--- a/arch/arm64/kernel/hw_breakpoint.c
+++ b/arch/arm64/kernel/hw_breakpoint.c
@@ -662,11 +662,46 @@ unlock:
}
NOKPROBE_SYMBOL(breakpoint_handler);
+/*
+ * Arm64 hardware does not always report a watchpoint hit address that matches
+ * one of the watchpoints set. It can also report an address "near" the
+ * watchpoint if a single instruction access both watched and unwatched
+ * addresses. There is no straight-forward way, short of disassembling the
+ * offending instruction, to map that address back to the watchpoint. This
+ * function computes the distance of the memory access from the watchpoint as a
+ * heuristic for the likelyhood that a given access triggered the watchpoint.
+ *
+ * See Section D2.10.5 "Determining the memory location that caused a Watchpoint
+ * exception" of ARMv8 Architecture Reference Manual for details.
+ *
+ * The function returns the distance of the address from the bytes watched by
+ * the watchpoint. In case of an exact match, it returns 0.
+ */
+static u64 get_distance_from_watchpoint(unsigned long addr, u64 val,
+ struct arch_hw_breakpoint_ctrl *ctrl)
+{
+ u64 wp_low, wp_high;
+ u32 lens, lene;
+
+ lens = ffs(ctrl->len) - 1;
+ lene = fls(ctrl->len) - 1;
+
+ wp_low = val + lens;
+ wp_high = val + lene;
+ if (addr < wp_low)
+ return wp_low - addr;
+ else if (addr > wp_high)
+ return addr - wp_high;
+ else
+ return 0;
+}
+
static int watchpoint_handler(unsigned long addr, unsigned int esr,
struct pt_regs *regs)
{
- int i, step = 0, *kernel_step, access;
- u32 ctrl_reg, lens, lene;
+ int i, step = 0, *kernel_step, access, closest_match = 0;
+ u64 min_dist = -1, dist;
+ u32 ctrl_reg;
u64 val;
struct perf_event *wp, **slots;
struct debug_info *debug_info;
@@ -676,31 +711,15 @@ static int watchpoint_handler(unsigned long addr, unsigned int esr,
slots = this_cpu_ptr(wp_on_reg);
debug_info = ¤t->thread.debug;
+ /*
+ * Find all watchpoints that match the reported address. If no exact
+ * match is found. Attribute the hit to the closest watchpoint.
+ */
+ rcu_read_lock();
for (i = 0; i < core_num_wrps; ++i) {
- rcu_read_lock();
-
wp = slots[i];
-
if (wp == NULL)
- goto unlock;
-
- info = counter_arch_bp(wp);
-
- /* Check if the watchpoint value and byte select match. */
- val = read_wb_reg(AARCH64_DBG_REG_WVR, i);
- ctrl_reg = read_wb_reg(AARCH64_DBG_REG_WCR, i);
- decode_ctrl_reg(ctrl_reg, &ctrl);
- lens = ffs(ctrl.len) - 1;
- lene = fls(ctrl.len) - 1;
- /*
- * FIXME: reported address can be anywhere between "the
- * lowest address accessed by the memory access that
- * triggered the watchpoint" and "the highest watchpointed
- * address accessed by the memory access". So, it may not
- * lie in the interval of watchpoint address range.
- */
- if (addr < val + lens || addr > val + lene)
- goto unlock;
+ continue;
/*
* Check that the access type matches.
@@ -709,18 +728,37 @@ static int watchpoint_handler(unsigned long addr, unsigned int esr,
access = (esr & AARCH64_ESR_ACCESS_MASK) ? HW_BREAKPOINT_W :
HW_BREAKPOINT_R;
if (!(access & hw_breakpoint_type(wp)))
- goto unlock;
+ continue;
+
+ /* Check if the watchpoint value and byte select match. */
+ val = read_wb_reg(AARCH64_DBG_REG_WVR, i);
+ ctrl_reg = read_wb_reg(AARCH64_DBG_REG_WCR, i);
+ decode_ctrl_reg(ctrl_reg, &ctrl);
+ dist = get_distance_from_watchpoint(addr, val, &ctrl);
+ if (dist < min_dist) {
+ min_dist = dist;
+ closest_match = i;
+ }
+ /* Is this an exact match? */
+ if (dist != 0)
+ continue;
+ info = counter_arch_bp(wp);
info->trigger = addr;
perf_bp_event(wp, regs);
/* Do we need to handle the stepping? */
if (is_default_overflow_handler(wp))
step = 1;
-
-unlock:
- rcu_read_unlock();
}
+ if (min_dist > 0 && min_dist != -1) {
+ /* No exact match found. */
+ wp = slots[closest_match];
+ info = counter_arch_bp(wp);
+ info->trigger = addr;
+ perf_bp_event(wp, regs);
+ }
+ rcu_read_unlock();
if (!step)
return 0;
--
2.7.4
^ permalink raw reply related
* [PATCH V2 2/5] arm64: Allow hw watchpoint at varied offset from base address
From: Pratyush Anand @ 2016-10-20 5:48 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <cover.1476941895.git.panand@redhat.com>
ARM64 hardware supports watchpoint at any double word aligned address.
However, it can select any consecutive bytes from offset 0 to 7 from that
base address. For example, if base address is programmed as 0x420030 and
byte select is 0x1C, then access of 0x420032,0x420033 and 0x420034 will
generate a watchpoint exception.
Currently, we do not have such modularity. We can only program byte,
halfword, word and double word access exception from any base address.
This patch adds support to overcome above limitations.
Signed-off-by: Pratyush Anand <panand@redhat.com>
---
arch/arm64/include/asm/hw_breakpoint.h | 2 +-
arch/arm64/kernel/hw_breakpoint.c | 45 ++++++++++++++++------------------
arch/arm64/kernel/ptrace.c | 5 ++--
3 files changed, 25 insertions(+), 27 deletions(-)
diff --git a/arch/arm64/include/asm/hw_breakpoint.h b/arch/arm64/include/asm/hw_breakpoint.h
index 115ea2a64520..4f4e58bee9bc 100644
--- a/arch/arm64/include/asm/hw_breakpoint.h
+++ b/arch/arm64/include/asm/hw_breakpoint.h
@@ -118,7 +118,7 @@ struct perf_event;
struct pmu;
extern int arch_bp_generic_fields(struct arch_hw_breakpoint_ctrl ctrl,
- int *gen_len, int *gen_type);
+ int *gen_len, int *gen_type, int *offset);
extern int arch_check_bp_in_kernelspace(struct perf_event *bp);
extern int arch_validate_hwbkpt_settings(struct perf_event *bp);
extern int hw_breakpoint_exceptions_notify(struct notifier_block *unused,
diff --git a/arch/arm64/kernel/hw_breakpoint.c b/arch/arm64/kernel/hw_breakpoint.c
index 26a6bf77d272..3c2b96803eba 100644
--- a/arch/arm64/kernel/hw_breakpoint.c
+++ b/arch/arm64/kernel/hw_breakpoint.c
@@ -349,7 +349,7 @@ int arch_check_bp_in_kernelspace(struct perf_event *bp)
* to generic breakpoint descriptions.
*/
int arch_bp_generic_fields(struct arch_hw_breakpoint_ctrl ctrl,
- int *gen_len, int *gen_type)
+ int *gen_len, int *gen_type, int *offset)
{
/* Type */
switch (ctrl.type) {
@@ -369,8 +369,10 @@ int arch_bp_generic_fields(struct arch_hw_breakpoint_ctrl ctrl,
return -EINVAL;
}
+ *offset = ffs(ctrl.len) - 1;
+
/* Len */
- switch (ctrl.len) {
+ switch (ctrl.len >> *offset) {
case ARM_BREAKPOINT_LEN_1:
*gen_len = HW_BREAKPOINT_LEN_1;
break;
@@ -517,18 +519,17 @@ int arch_validate_hwbkpt_settings(struct perf_event *bp)
default:
return -EINVAL;
}
-
- info->address &= ~alignment_mask;
- info->ctrl.len <<= offset;
} else {
if (info->ctrl.type == ARM_BREAKPOINT_EXECUTE)
alignment_mask = 0x3;
else
alignment_mask = 0x7;
- if (info->address & alignment_mask)
- return -EINVAL;
+ offset = info->address & alignment_mask;
}
+ info->address &= ~alignment_mask;
+ info->ctrl.len <<= offset;
+
/*
* Disallow per-task kernel breakpoints since these would
* complicate the stepping code.
@@ -665,8 +666,8 @@ static int watchpoint_handler(unsigned long addr, unsigned int esr,
struct pt_regs *regs)
{
int i, step = 0, *kernel_step, access;
- u32 ctrl_reg;
- u64 val, alignment_mask;
+ u32 ctrl_reg, lens, lene;
+ u64 val;
struct perf_event *wp, **slots;
struct debug_info *debug_info;
struct arch_hw_breakpoint *info;
@@ -684,25 +685,21 @@ static int watchpoint_handler(unsigned long addr, unsigned int esr,
goto unlock;
info = counter_arch_bp(wp);
- /* AArch32 watchpoints are either 4 or 8 bytes aligned. */
- if (is_compat_task()) {
- if (info->ctrl.len == ARM_BREAKPOINT_LEN_8)
- alignment_mask = 0x7;
- else
- alignment_mask = 0x3;
- } else {
- alignment_mask = 0x7;
- }
- /* Check if the watchpoint value matches. */
+ /* Check if the watchpoint value and byte select match. */
val = read_wb_reg(AARCH64_DBG_REG_WVR, i);
- if (val != (addr & ~alignment_mask))
- goto unlock;
-
- /* Possible match, check the byte address select to confirm. */
ctrl_reg = read_wb_reg(AARCH64_DBG_REG_WCR, i);
decode_ctrl_reg(ctrl_reg, &ctrl);
- if (!((1 << (addr & alignment_mask)) & ctrl.len))
+ lens = ffs(ctrl.len) - 1;
+ lene = fls(ctrl.len) - 1;
+ /*
+ * FIXME: reported address can be anywhere between "the
+ * lowest address accessed by the memory access that
+ * triggered the watchpoint" and "the highest watchpointed
+ * address accessed by the memory access". So, it may not
+ * lie in the interval of watchpoint address range.
+ */
+ if (addr < val + lens || addr > val + lene)
goto unlock;
/*
diff --git a/arch/arm64/kernel/ptrace.c b/arch/arm64/kernel/ptrace.c
index e0c81da60f76..0eb366a94382 100644
--- a/arch/arm64/kernel/ptrace.c
+++ b/arch/arm64/kernel/ptrace.c
@@ -327,13 +327,13 @@ static int ptrace_hbp_fill_attr_ctrl(unsigned int note_type,
struct arch_hw_breakpoint_ctrl ctrl,
struct perf_event_attr *attr)
{
- int err, len, type, disabled = !ctrl.enabled;
+ int err, len, type, offset, disabled = !ctrl.enabled;
attr->disabled = disabled;
if (disabled)
return 0;
- err = arch_bp_generic_fields(ctrl, &len, &type);
+ err = arch_bp_generic_fields(ctrl, &len, &type, &offset);
if (err)
return err;
@@ -352,6 +352,7 @@ static int ptrace_hbp_fill_attr_ctrl(unsigned int note_type,
attr->bp_len = len;
attr->bp_type = type;
+ attr->bp_addr += offset;
return 0;
}
--
2.7.4
^ permalink raw reply related
* [PATCH V2 1/5] hw_breakpoint: Allow watchpoint of length 3,5,6 and 7
From: Pratyush Anand @ 2016-10-20 5:48 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <cover.1476941895.git.panand@redhat.com>
We only support breakpoint/watchpoint of length 1, 2, 4 and 8. If we can
support other length as well, then user may watch more data with less
number of watchpoints (provided hardware supports it). For example: if we
have to watch only 4th, 5th and 6th byte from a 64 bit aligned address, we
will have to use two slots to implement it currently. One slot will watch a
half word at offset 4 and other a byte at offset 6. If we can have a
watchpoint of length 3 then we can watch it with single slot as well.
ARM64 hardware does support such functionality, therefore adding these new
definitions in generic layer.
Signed-off-by: Pratyush Anand <panand@redhat.com>
---
include/uapi/linux/hw_breakpoint.h | 4 ++++
tools/include/uapi/linux/hw_breakpoint.h | 4 ++++
2 files changed, 8 insertions(+)
diff --git a/include/uapi/linux/hw_breakpoint.h b/include/uapi/linux/hw_breakpoint.h
index b04000a2296a..2b65efd19a46 100644
--- a/include/uapi/linux/hw_breakpoint.h
+++ b/include/uapi/linux/hw_breakpoint.h
@@ -4,7 +4,11 @@
enum {
HW_BREAKPOINT_LEN_1 = 1,
HW_BREAKPOINT_LEN_2 = 2,
+ HW_BREAKPOINT_LEN_3 = 3,
HW_BREAKPOINT_LEN_4 = 4,
+ HW_BREAKPOINT_LEN_5 = 5,
+ HW_BREAKPOINT_LEN_6 = 6,
+ HW_BREAKPOINT_LEN_7 = 7,
HW_BREAKPOINT_LEN_8 = 8,
};
diff --git a/tools/include/uapi/linux/hw_breakpoint.h b/tools/include/uapi/linux/hw_breakpoint.h
index b04000a2296a..2b65efd19a46 100644
--- a/tools/include/uapi/linux/hw_breakpoint.h
+++ b/tools/include/uapi/linux/hw_breakpoint.h
@@ -4,7 +4,11 @@
enum {
HW_BREAKPOINT_LEN_1 = 1,
HW_BREAKPOINT_LEN_2 = 2,
+ HW_BREAKPOINT_LEN_3 = 3,
HW_BREAKPOINT_LEN_4 = 4,
+ HW_BREAKPOINT_LEN_5 = 5,
+ HW_BREAKPOINT_LEN_6 = 6,
+ HW_BREAKPOINT_LEN_7 = 7,
HW_BREAKPOINT_LEN_8 = 8,
};
--
2.7.4
^ permalink raw reply related
* [PATCH V2 0/5] ARM64: More flexible HW watchpoint
From: Pratyush Anand @ 2016-10-20 5:48 UTC (permalink / raw)
To: linux-arm-kernel
Currently, we do not support all the byte select option provided by ARM64
specs for a HW watchpoint.
This patch set will help user to instrument a watchpoint with all possible
byte select options.
Changes since v1:
Introduced a new patch 3/5 where it takes care of the situation when HW
does not report a watchpoint hit with the address that matches one of the
watchpoints set.
Added corresponding test case to test that functionality.
Pavel Labath (1):
arm64: hw_breakpoint: Handle inexact watchpoint addresses
Pratyush Anand (4):
hw_breakpoint: Allow watchpoint of length 3,5,6 and 7
arm64: Allow hw watchpoint at varied offset from base address
arm64: Allow hw watchpoint of length 3,5,6 and 7
selftests: arm64: add test for unaligned/inexact watchpoint handling
arch/arm64/include/asm/hw_breakpoint.h | 6 +-
arch/arm64/kernel/hw_breakpoint.c | 149 +++++++++----
arch/arm64/kernel/ptrace.c | 5 +-
include/uapi/linux/hw_breakpoint.h | 4 +
tools/include/uapi/linux/hw_breakpoint.h | 4 +
tools/testing/selftests/breakpoints/Makefile | 5 +-
.../selftests/breakpoints/breakpoint_test_arm64.c | 236 +++++++++++++++++++++
7 files changed, 366 insertions(+), 43 deletions(-)
create mode 100644 tools/testing/selftests/breakpoints/breakpoint_test_arm64.c
--
2.7.4
^ permalink raw reply
* [PATCH 0/5] Switch to the DT cpufreq policy on the Integrator
From: Viresh Kumar @ 2016-10-20 4:47 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20161020042012.GI11766@vireshk-i7>
On 20-10-16, 09:50, Viresh Kumar wrote:
> On 19-10-16, 11:59, Linus Walleij wrote:
> > This switches the ARM Integrator/AP and Integrator/CP to use the
> > Device Tree cpufreq policy with its definition of operating points
> > using the generic OPP library.
> >
> > The Integrators does not need to control a regulator to switch
> > frequency, only a clock. This clock and its device tree bindings
> > have been defined in the device tree and merged upstream for v4.9.
> >
> > This approach provides a little better granularity on the
> > Integrator/AP where I defined a few operating points: before
> > this change the Integrator would just switch between min speed
> > (12 MHz) and max speed (71 MHz). Now it can switch between a
> > few arbitrarily chosen OPPs as in the examples below:
> >
> > The following tests were made on the Integrator/AP:
> >
> > /sys/devices/system/cpu/cpufreq/policy0 echo ondemand > scaling_governor
> > cpu cpu0: dev_pm_opp_set_rate: switching OPP: 71000000 Hz --> 24000000 Hz
> > cpu cpu0: dev_pm_opp_set_rate: switching OPP: 24000000 Hz --> 12000000 Hz
> > cpu cpu0: dev_pm_opp_set_rate: switching OPP: 12000000 Hz --> 24000000 Hz
> > cpu cpu0: dev_pm_opp_set_rate: switching OPP: 24000000 Hz --> 12000000 Hz
> >
> > The switch from 12 to 24 MHz is triggered by as little as using the
> > command line with the ondemand governor. If we do this:
> >
> > yes > /dev/null &
> > cpu cpu0: dev_pm_opp_set_rate: switching OPP: 12000000 Hz --> 36000000 Hz
> > cpu cpu0: dev_pm_opp_set_rate: switching OPP: 36000000 Hz --> 71000000 Hz
> > pkill yes
> > cpu cpu0: dev_pm_opp_set_rate: switching OPP: 71000000 Hz --> 12000000 Hz
> >
> > So the algorithm is indeed using all the OPPs.
> >
> > Also as a confirmation test to make sure performance is affected by the
> > speed changes, we set the performance governor and:
> >
> > echo 12000 > scaling_max_freq
> > cpu cpu0: dev_pm_opp_set_rate: switching OPP: 71000000 Hz --> 12000000 Hz
> > time find /sys|sort|uniq > /dev/null
> > real 0m 45.51s
> > user 0m 11.15s
> > sys 0m 26.74s
> >
> > echo 71000 > scaling_max_freq
> > cpu cpu0: dev_pm_opp_set_rate: switching OPP: 12000000 Hz --> 71000000 Hz
> > time find /sys|sort|uniq
> > > /dev/null
> > real 0m 9.13s
> > user 0m 2.35s
> > sys 0m 5.50s
> >
> > So it is working.
> >
> > If people are happy with this approach and approve of the patches
> > I'd like an ACK from the cpufreq people and then merge the whole set
> > through ARM SoC.
>
> Acked-by: Viresh Kumar <viresh.kumar@linaro.org>
Integrator is an UP, right? Otherwise you may want to use operating-points-v2
bindings instead.
--
viresh
^ permalink raw reply
* [PATCH v3 1/2] arm64: dts: uniphier: add CPU clock and OPP table for LD11 SoC
From: Viresh Kumar @ 2016-10-20 4:47 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1476938647-26376-1-git-send-email-yamada.masahiro@socionext.com>
On 20-10-16, 13:44, Masahiro Yamada wrote:
> Add a CPU clock to every CPU node and a CPU OPP table to use the
> generic cpufreq driver.
>
> Note:
> clock-latency-ns (300ns) was calculated based on the CPU-gear switch
> sequencer spec; it takes 12 clock cycles on the sequencer running
> at 50 MHz, plus a bit additional latency.
>
> Signed-off-by: Masahiro Yamada <yamada.masahiro@socionext.com>
> ---
>
> Changes in v2:
> - Match the node name to the opp-hz property.
For both the patches ..
Acked-by: Viresh Kumar <viresh.kumar@linaro.org>
--
viresh
^ permalink raw reply
* [PATCH v3 2/2] arm64: dts: uniphier: add CPU clocks and OPP tables for LD20 SoC
From: Masahiro Yamada @ 2016-10-20 4:44 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1476938647-26376-1-git-send-email-yamada.masahiro@socionext.com>
Add a CPU clock to every CPU node and CPU OPP tables to use the
generic cpufreq driver. All the CPUs in each cluster share the
same OPP table.
Note:
clock-latency-ns (300ns) was calculated based on the CPU-gear switch
sequencer spec; it takes 12 clock cycles on the sequencer running
at 50 MHz, plus a bit additional latency.
Signed-off-by: Masahiro Yamada <yamada.masahiro@socionext.com>
- Fix cluster1 OPP as well.
---
Changes in v2:
- Match the node name to the opp-hz property.
arch/arm64/boot/dts/socionext/uniphier-ld20.dtsi | 84 ++++++++++++++++++++++++
1 file changed, 84 insertions(+)
diff --git a/arch/arm64/boot/dts/socionext/uniphier-ld20.dtsi b/arch/arm64/boot/dts/socionext/uniphier-ld20.dtsi
index 6f48e82..a9a08dd 100644
--- a/arch/arm64/boot/dts/socionext/uniphier-ld20.dtsi
+++ b/arch/arm64/boot/dts/socionext/uniphier-ld20.dtsi
@@ -79,28 +79,36 @@
device_type = "cpu";
compatible = "arm,cortex-a72", "arm,armv8";
reg = <0 0x000>;
+ clocks = <&sys_clk 32>;
enable-method = "psci";
+ operating-points-v2 = <&cluster0_opp>;
};
cpu1: cpu at 1 {
device_type = "cpu";
compatible = "arm,cortex-a72", "arm,armv8";
reg = <0 0x001>;
+ clocks = <&sys_clk 32>;
enable-method = "psci";
+ operating-points-v2 = <&cluster0_opp>;
};
cpu2: cpu at 100 {
device_type = "cpu";
compatible = "arm,cortex-a53", "arm,armv8";
reg = <0 0x100>;
+ clocks = <&sys_clk 33>;
enable-method = "psci";
+ operating-points-v2 = <&cluster1_opp>;
};
cpu3: cpu at 101 {
device_type = "cpu";
compatible = "arm,cortex-a53", "arm,armv8";
reg = <0 0x101>;
+ clocks = <&sys_clk 33>;
enable-method = "psci";
+ operating-points-v2 = <&cluster1_opp>;
};
};
@@ -109,6 +117,82 @@
method = "smc";
};
+ cluster0_opp: opp_table0 {
+ compatible = "operating-points-v2";
+ opp-shared;
+
+ opp at 250000000 {
+ opp-hz = /bits/ 64 <250000000>;
+ clock-latency-ns = <300>;
+ };
+ opp at 275000000 {
+ opp-hz = /bits/ 64 <275000000>;
+ clock-latency-ns = <300>;
+ };
+ opp at 500000000 {
+ opp-hz = /bits/ 64 <500000000>;
+ clock-latency-ns = <300>;
+ };
+ opp at 550000000 {
+ opp-hz = /bits/ 64 <550000000>;
+ clock-latency-ns = <300>;
+ };
+ opp at 666667000 {
+ opp-hz = /bits/ 64 <666667000>;
+ clock-latency-ns = <300>;
+ };
+ opp at 733334000 {
+ opp-hz = /bits/ 64 <733334000>;
+ clock-latency-ns = <300>;
+ };
+ opp at 1000000000 {
+ opp-hz = /bits/ 64 <1000000000>;
+ clock-latency-ns = <300>;
+ };
+ opp at 1100000000 {
+ opp-hz = /bits/ 64 <1100000000>;
+ clock-latency-ns = <300>;
+ };
+ };
+
+ cluster1_opp: opp_table1 {
+ compatible = "operating-points-v2";
+ opp-shared;
+
+ opp at 250000000 {
+ opp-hz = /bits/ 64 <250000000>;
+ clock-latency-ns = <300>;
+ };
+ opp at 275000000 {
+ opp-hz = /bits/ 64 <275000000>;
+ clock-latency-ns = <300>;
+ };
+ opp at 500000000 {
+ opp-hz = /bits/ 64 <500000000>;
+ clock-latency-ns = <300>;
+ };
+ opp at 550000000 {
+ opp-hz = /bits/ 64 <550000000>;
+ clock-latency-ns = <300>;
+ };
+ opp at 666667000 {
+ opp-hz = /bits/ 64 <666667000>;
+ clock-latency-ns = <300>;
+ };
+ opp at 733334000 {
+ opp-hz = /bits/ 64 <733334000>;
+ clock-latency-ns = <300>;
+ };
+ opp at 1000000000 {
+ opp-hz = /bits/ 64 <1000000000>;
+ clock-latency-ns = <300>;
+ };
+ opp at 1100000000 {
+ opp-hz = /bits/ 64 <1100000000>;
+ clock-latency-ns = <300>;
+ };
+ };
+
clocks {
refclk: ref {
compatible = "fixed-clock";
--
1.9.1
^ permalink raw reply related
* [PATCH v3 1/2] arm64: dts: uniphier: add CPU clock and OPP table for LD11 SoC
From: Masahiro Yamada @ 2016-10-20 4:44 UTC (permalink / raw)
To: linux-arm-kernel
Add a CPU clock to every CPU node and a CPU OPP table to use the
generic cpufreq driver.
Note:
clock-latency-ns (300ns) was calculated based on the CPU-gear switch
sequencer spec; it takes 12 clock cycles on the sequencer running
at 50 MHz, plus a bit additional latency.
Signed-off-by: Masahiro Yamada <yamada.masahiro@socionext.com>
---
Changes in v2:
- Match the node name to the opp-hz property.
arch/arm64/boot/dts/socionext/uniphier-ld11.dtsi | 38 ++++++++++++++++++++++++
1 file changed, 38 insertions(+)
diff --git a/arch/arm64/boot/dts/socionext/uniphier-ld11.dtsi b/arch/arm64/boot/dts/socionext/uniphier-ld11.dtsi
index 73e0acf..bb05f0a 100644
--- a/arch/arm64/boot/dts/socionext/uniphier-ld11.dtsi
+++ b/arch/arm64/boot/dts/socionext/uniphier-ld11.dtsi
@@ -70,14 +70,18 @@
device_type = "cpu";
compatible = "arm,cortex-a53", "arm,armv8";
reg = <0 0x000>;
+ clocks = <&sys_clk 33>;
enable-method = "psci";
+ operating-points-v2 = <&cluster0_opp>;
};
cpu1: cpu at 1 {
device_type = "cpu";
compatible = "arm,cortex-a53", "arm,armv8";
reg = <0 0x001>;
+ clocks = <&sys_clk 33>;
enable-method = "psci";
+ operating-points-v2 = <&cluster0_opp>;
};
};
@@ -86,6 +90,40 @@
method = "smc";
};
+ cluster0_opp: opp_table {
+ compatible = "operating-points-v2";
+ opp-shared;
+
+ opp at 245000000 {
+ opp-hz = /bits/ 64 <245000000>;
+ clock-latency-ns = <300>;
+ };
+ opp at 250000000 {
+ opp-hz = /bits/ 64 <250000000>;
+ clock-latency-ns = <300>;
+ };
+ opp at 490000000 {
+ opp-hz = /bits/ 64 <490000000>;
+ clock-latency-ns = <300>;
+ };
+ opp at 500000000 {
+ opp-hz = /bits/ 64 <500000000>;
+ clock-latency-ns = <300>;
+ };
+ opp at 653334000 {
+ opp-hz = /bits/ 64 <653334000>;
+ clock-latency-ns = <300>;
+ };
+ opp at 666667000 {
+ opp-hz = /bits/ 64 <666667000>;
+ clock-latency-ns = <300>;
+ };
+ opp at 980000000 {
+ opp-hz = /bits/ 64 <980000000>;
+ clock-latency-ns = <300>;
+ };
+ };
+
clocks {
refclk: ref {
compatible = "fixed-clock";
--
1.9.1
^ permalink raw reply related
* [PATCH 0/5] Switch to the DT cpufreq policy on the Integrator
From: Viresh Kumar @ 2016-10-20 4:20 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1476871154-32243-1-git-send-email-linus.walleij@linaro.org>
On 19-10-16, 11:59, Linus Walleij wrote:
> This switches the ARM Integrator/AP and Integrator/CP to use the
> Device Tree cpufreq policy with its definition of operating points
> using the generic OPP library.
>
> The Integrators does not need to control a regulator to switch
> frequency, only a clock. This clock and its device tree bindings
> have been defined in the device tree and merged upstream for v4.9.
>
> This approach provides a little better granularity on the
> Integrator/AP where I defined a few operating points: before
> this change the Integrator would just switch between min speed
> (12 MHz) and max speed (71 MHz). Now it can switch between a
> few arbitrarily chosen OPPs as in the examples below:
>
> The following tests were made on the Integrator/AP:
>
> /sys/devices/system/cpu/cpufreq/policy0 echo ondemand > scaling_governor
> cpu cpu0: dev_pm_opp_set_rate: switching OPP: 71000000 Hz --> 24000000 Hz
> cpu cpu0: dev_pm_opp_set_rate: switching OPP: 24000000 Hz --> 12000000 Hz
> cpu cpu0: dev_pm_opp_set_rate: switching OPP: 12000000 Hz --> 24000000 Hz
> cpu cpu0: dev_pm_opp_set_rate: switching OPP: 24000000 Hz --> 12000000 Hz
>
> The switch from 12 to 24 MHz is triggered by as little as using the
> command line with the ondemand governor. If we do this:
>
> yes > /dev/null &
> cpu cpu0: dev_pm_opp_set_rate: switching OPP: 12000000 Hz --> 36000000 Hz
> cpu cpu0: dev_pm_opp_set_rate: switching OPP: 36000000 Hz --> 71000000 Hz
> pkill yes
> cpu cpu0: dev_pm_opp_set_rate: switching OPP: 71000000 Hz --> 12000000 Hz
>
> So the algorithm is indeed using all the OPPs.
>
> Also as a confirmation test to make sure performance is affected by the
> speed changes, we set the performance governor and:
>
> echo 12000 > scaling_max_freq
> cpu cpu0: dev_pm_opp_set_rate: switching OPP: 71000000 Hz --> 12000000 Hz
> time find /sys|sort|uniq > /dev/null
> real 0m 45.51s
> user 0m 11.15s
> sys 0m 26.74s
>
> echo 71000 > scaling_max_freq
> cpu cpu0: dev_pm_opp_set_rate: switching OPP: 12000000 Hz --> 71000000 Hz
> time find /sys|sort|uniq
> > /dev/null
> real 0m 9.13s
> user 0m 2.35s
> sys 0m 5.50s
>
> So it is working.
>
> If people are happy with this approach and approve of the patches
> I'd like an ACK from the cpufreq people and then merge the whole set
> through ARM SoC.
Acked-by: Viresh Kumar <viresh.kumar@linaro.org>
--
viresh
^ permalink raw reply
* [PATCH 1/2] arm64/numa: fix pcpu_cpu_distance() to get correct CPU proximity
From: Hanjun Guo @ 2016-10-20 4:03 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1476935576-59941-1-git-send-email-guohanjun@huawei.com>
On 2016/10/20 11:52, Hanjun Guo wrote:
> From: Yisheng Xie <xieyisheng1@huawei.com>
>
> The pcpu_build_alloc_info() function group CPUs according to their
> proximity, by call callback function @cpu_distance_fn from different
> ARCHs.
>
> For arm64 the callback of @cpu_distance_fn is
> pcpu_cpu_distance(from, to)
> -> node_distance(from, to)
> The @from and @to for function node_distance() should be nid.
>
> However, pcpu_cpu_distance() in arch/arm64/mm/numa.c just past the
> cpu id for @from and @to.
>
> For this incorrect cpu proximity get from ARCH, it may cause each CPU
> in one group and make group_cnt out of bound:
>
> setup_per_cpu_areas()
> pcpu_embed_first_chunk()
> pcpu_build_alloc_info()
> in pcpu_build_alloc_info, since cpu_distance_fn will return
> REMOTE_DISTANCE if we pass cpu ids (0,1,2...), so
> cpu_distance_fn(cpu, tcpu) > LOCAL_DISTANCE will wrongly be ture.
>
> This may results in triggering the BUG_ON(unit != nr_units) later:
>
> [ 0.000000] kernel BUG at mm/percpu.c:1916!
> [ 0.000000] Internal error: Oops - BUG: 0 [#1] PREEMPT SMP
> [ 0.000000] Modules linked in:
> [ 0.000000] CPU: 0 PID: 0 Comm: swapper Not tainted 4.9.0-rc1-00003-g14155ca-dirty #26
> [ 0.000000] Hardware name: Hisilicon Hi1616 Evaluation Board (DT)
> [ 0.000000] task: ffff000008d6e900 task.stack: ffff000008d60000
> [ 0.000000] PC is at pcpu_embed_first_chunk+0x420/0x704
> [ 0.000000] LR is at pcpu_embed_first_chunk+0x3bc/0x704
> [ 0.000000] pc : [<ffff000008c754f4>] lr : [<ffff000008c75490>] pstate: 800000c5
> [ 0.000000] sp : ffff000008d63eb0
> [ 0.000000] x29: ffff000008d63eb0 [ 0.000000] x28: 0000000000000000
> [ 0.000000] x27: 0000000000000040 [ 0.000000] x26: ffff8413fbfcef00
> [ 0.000000] x25: 0000000000000042 [ 0.000000] x24: 0000000000000042
> [ 0.000000] x23: 0000000000001000 [ 0.000000] x22: 0000000000000046
> [ 0.000000] x21: 0000000000000001 [ 0.000000] x20: ffff000008cb3bc8
> [ 0.000000] x19: ffff8413fbfcf570 [ 0.000000] x18: 0000000000000000
> [ 0.000000] x17: ffff000008e49ae0 [ 0.000000] x16: 0000000000000003
> [ 0.000000] x15: 000000000000001e [ 0.000000] x14: 0000000000000004
> [ 0.000000] x13: 0000000000000000 [ 0.000000] x12: 000000000000006f
> [ 0.000000] x11: 00000413fbffff00 [ 0.000000] x10: 0000000000000004
> [ 0.000000] x9 : 0000000000000000 [ 0.000000] x8 : 0000000000000001
> [ 0.000000] x7 : ffff8413fbfcf63c [ 0.000000] x6 : ffff000008d65d28
> [ 0.000000] x5 : ffff000008d65e50 [ 0.000000] x4 : 0000000000000000
> [ 0.000000] x3 : ffff000008cb3cc8 [ 0.000000] x2 : 0000000000000040
> [ 0.000000] x1 : 0000000000000040 [ 0.000000] x0 : 0000000000000000
> [...]
> [ 0.000000] Call trace:
> [ 0.000000] Exception stack(0xffff000008d63ce0 to 0xffff000008d63e10)
> [ 0.000000] 3ce0: ffff8413fbfcf570 0001000000000000 ffff000008d63eb0 ffff000008c754f4
> [ 0.000000] 3d00: ffff000008d63d50 ffff0000081af210 00000413fbfff010 0000000000001000
> [ 0.000000] 3d20: ffff000008d63d50 ffff0000081af220 00000413fbfff010 0000000000001000
> [ 0.000000] 3d40: 00000413fbfcef00 0000000000000004 ffff000008d63db0 ffff0000081af390
> [ 0.000000] 3d60: 00000413fbfcef00 0000000000001000 0000000000000000 0000000000001000
> [ 0.000000] 3d80: 0000000000000000 0000000000000040 0000000000000040 ffff000008cb3cc8
> [ 0.000000] 3da0: 0000000000000000 ffff000008d65e50 ffff000008d65d28 ffff8413fbfcf63c
> [ 0.000000] 3dc0: 0000000000000001 0000000000000000 0000000000000004 00000413fbffff00
> [ 0.000000] 3de0: 000000000000006f 0000000000000000 0000000000000004 000000000000001e
> [ 0.000000] 3e00: 0000000000000003 ffff000008e49ae0
> [ 0.000000] [<ffff000008c754f4>] pcpu_embed_first_chunk+0x420/0x704
> [ 0.000000] [<ffff000008c6658c>] setup_per_cpu_areas+0x38/0xc8
> [ 0.000000] [<ffff000008c608d8>] start_kernel+0x10c/0x390
> [ 0.000000] [<ffff000008c601d8>] __primary_switched+0x5c/0x64
> [ 0.000000] Code: b8018660 17ffffd7 6b16037f 54000080 (d4210000)
> [ 0.000000] ---[ end trace 0000000000000000 ]---
> [ 0.000000] Kernel panic - not syncing: Attempted to kill the idle task!
>
> Fix by getting CPUs proximity through its node. We only care about
> whether it is LOCAL_DISTANCE or not, for pcpu_build_alloc_info() only
> use this to group CPUs.
>
> Fixes: 7af3a0a99252 ("arm64/numa: support HAVE_SETUP_PER_CPU_AREA")
> Signed-off-by: Yisheng Xie <xieyisheng1@huawei.com>
> Signed-off-by: Hanjun Guo <hanjun.guo@linaro.org>
> Cc: Catalin Marinas <catalin.marinas@arm.com>
> Cc: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
> Cc: Will Deacon <will.deacon@arm.com>
> Cc: Zhen Lei <thunder.leizhen@huawei.com>
> ---
> arch/arm64/mm/numa.c | 5 ++++-
> 1 file changed, 4 insertions(+), 1 deletion(-)
>
> diff --git a/arch/arm64/mm/numa.c b/arch/arm64/mm/numa.c
> index 778a985..34415fc 100644
> --- a/arch/arm64/mm/numa.c
> +++ b/arch/arm64/mm/numa.c
> @@ -147,7 +147,10 @@ static int __init early_cpu_to_node(int cpu)
>
> static int __init pcpu_cpu_distance(unsigned int from, unsigned int to)
> {
> - return node_distance(from, to);
> + if (early_cpu_to_node(from) == early_cpu_to_node(to))
> + return LOCAL_DISTANCE;
> + else
> + return REMOTE_DISTANCE;
> }
>
> static void * __init pcpu_fc_alloc(unsigned int cpu, size_t size,
We trigger this bug with NR_CPUS=64 in the config instead of 4096 in defconfig
for arm64, and we have 64 cpus in the system. I think that's why this bug wasn't
triggered yet on other ARM64 NUMA platforms, this bug should generic for all ARM64
platforms.
Thanks
Hanjun
^ permalink raw reply
* [PATCH v2 2/2] arm64: dts: uniphier: add CPU clocks and OPP tables for LD20 SoC
From: Masahiro Yamada @ 2016-10-20 3:59 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20161020035542.GG11766@vireshk-i7>
2016-10-20 12:55 GMT+09:00 Viresh Kumar <viresh.kumar@linaro.org>:
> On 20-10-16, 10:15, Masahiro Yamada wrote:
>> + opp at 666666666 {
>> + opp-hz = /bits/ 64 <666667000>;
>> + clock-latency-ns = <300>;
>> + };
>> + opp at 733333333 {
>> + opp-hz = /bits/ 64 <733334000>;
>> + clock-latency-ns = <300>;
>> + };
>
> Forgot changing these ? Please make sure to update all of them.
>
Oops, I missed that.
Will fix soon.
Thanks!
--
Best Regards
Masahiro Yamada
^ permalink raw reply
* [PATCH v2 2/2] arm64: dts: uniphier: add CPU clocks and OPP tables for LD20 SoC
From: Viresh Kumar @ 2016-10-20 3:55 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1476926126-6079-2-git-send-email-yamada.masahiro@socionext.com>
On 20-10-16, 10:15, Masahiro Yamada wrote:
> + opp at 666666666 {
> + opp-hz = /bits/ 64 <666667000>;
> + clock-latency-ns = <300>;
> + };
> + opp at 733333333 {
> + opp-hz = /bits/ 64 <733334000>;
> + clock-latency-ns = <300>;
> + };
Forgot changing these ? Please make sure to update all of them.
--
viresh
^ permalink raw reply
* [PATCH 2/2] arm64/numa: fix incorrect print of end_pfn
From: Hanjun Guo @ 2016-10-20 3:52 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1476935576-59941-1-git-send-email-guohanjun@huawei.com>
From: Hanjun Guo <hanjun.guo@linaro.org>
When booting on NUMA system with memory-less node (no
memory dimm on this memory controller), the print
for setup_node_data() is incorrect:
NUMA: Initmem setup node 2 [mem 0x00000000-0xffffffffffffffff]
It should be 0, not 0xffffffffffffffff as there is
no memory on that node.
Fixes: 1a2db300348b ("arm64, numa: Add NUMA support for arm64 platforms.")
Signed-off-by: Hanjun Guo <hanjun.guo@linaro.org>
Cc: Catalin Marinas <catalin.marinas@arm.com>
Cc: Ganapatrao Kulkarni <gkulkarni@caviumnetworks.com>
Cc: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
Cc: Will Deacon <will.deacon@arm.com>
---
arch/arm64/mm/numa.c | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/arch/arm64/mm/numa.c b/arch/arm64/mm/numa.c
index 34415fc..148e1fc 100644
--- a/arch/arm64/mm/numa.c
+++ b/arch/arm64/mm/numa.c
@@ -227,7 +227,8 @@ static void __init setup_node_data(int nid, u64 start_pfn, u64 end_pfn)
int tnid;
pr_info("Initmem setup node %d [mem %#010Lx-%#010Lx]\n",
- nid, start_pfn << PAGE_SHIFT, (end_pfn << PAGE_SHIFT) - 1);
+ nid, start_pfn << PAGE_SHIFT,
+ end_pfn ? (end_pfn << PAGE_SHIFT) - 1 : 0);
nd_pa = memblock_alloc_try_nid(nd_size, SMP_CACHE_BYTES, nid);
nd = __va(nd_pa);
--
1.7.12.4
^ permalink raw reply related
* [PATCH 1/2] arm64/numa: fix pcpu_cpu_distance() to get correct CPU proximity
From: Hanjun Guo @ 2016-10-20 3:52 UTC (permalink / raw)
To: linux-arm-kernel
From: Yisheng Xie <xieyisheng1@huawei.com>
The pcpu_build_alloc_info() function group CPUs according to their
proximity, by call callback function @cpu_distance_fn from different
ARCHs.
For arm64 the callback of @cpu_distance_fn is
pcpu_cpu_distance(from, to)
-> node_distance(from, to)
The @from and @to for function node_distance() should be nid.
However, pcpu_cpu_distance() in arch/arm64/mm/numa.c just past the
cpu id for @from and @to.
For this incorrect cpu proximity get from ARCH, it may cause each CPU
in one group and make group_cnt out of bound:
setup_per_cpu_areas()
pcpu_embed_first_chunk()
pcpu_build_alloc_info()
in pcpu_build_alloc_info, since cpu_distance_fn will return
REMOTE_DISTANCE if we pass cpu ids (0,1,2...), so
cpu_distance_fn(cpu, tcpu) > LOCAL_DISTANCE will wrongly be ture.
This may results in triggering the BUG_ON(unit != nr_units) later:
[ 0.000000] kernel BUG at mm/percpu.c:1916!
[ 0.000000] Internal error: Oops - BUG: 0 [#1] PREEMPT SMP
[ 0.000000] Modules linked in:
[ 0.000000] CPU: 0 PID: 0 Comm: swapper Not tainted 4.9.0-rc1-00003-g14155ca-dirty #26
[ 0.000000] Hardware name: Hisilicon Hi1616 Evaluation Board (DT)
[ 0.000000] task: ffff000008d6e900 task.stack: ffff000008d60000
[ 0.000000] PC is at pcpu_embed_first_chunk+0x420/0x704
[ 0.000000] LR is at pcpu_embed_first_chunk+0x3bc/0x704
[ 0.000000] pc : [<ffff000008c754f4>] lr : [<ffff000008c75490>] pstate: 800000c5
[ 0.000000] sp : ffff000008d63eb0
[ 0.000000] x29: ffff000008d63eb0 [ 0.000000] x28: 0000000000000000
[ 0.000000] x27: 0000000000000040 [ 0.000000] x26: ffff8413fbfcef00
[ 0.000000] x25: 0000000000000042 [ 0.000000] x24: 0000000000000042
[ 0.000000] x23: 0000000000001000 [ 0.000000] x22: 0000000000000046
[ 0.000000] x21: 0000000000000001 [ 0.000000] x20: ffff000008cb3bc8
[ 0.000000] x19: ffff8413fbfcf570 [ 0.000000] x18: 0000000000000000
[ 0.000000] x17: ffff000008e49ae0 [ 0.000000] x16: 0000000000000003
[ 0.000000] x15: 000000000000001e [ 0.000000] x14: 0000000000000004
[ 0.000000] x13: 0000000000000000 [ 0.000000] x12: 000000000000006f
[ 0.000000] x11: 00000413fbffff00 [ 0.000000] x10: 0000000000000004
[ 0.000000] x9 : 0000000000000000 [ 0.000000] x8 : 0000000000000001
[ 0.000000] x7 : ffff8413fbfcf63c [ 0.000000] x6 : ffff000008d65d28
[ 0.000000] x5 : ffff000008d65e50 [ 0.000000] x4 : 0000000000000000
[ 0.000000] x3 : ffff000008cb3cc8 [ 0.000000] x2 : 0000000000000040
[ 0.000000] x1 : 0000000000000040 [ 0.000000] x0 : 0000000000000000
[...]
[ 0.000000] Call trace:
[ 0.000000] Exception stack(0xffff000008d63ce0 to 0xffff000008d63e10)
[ 0.000000] 3ce0: ffff8413fbfcf570 0001000000000000 ffff000008d63eb0 ffff000008c754f4
[ 0.000000] 3d00: ffff000008d63d50 ffff0000081af210 00000413fbfff010 0000000000001000
[ 0.000000] 3d20: ffff000008d63d50 ffff0000081af220 00000413fbfff010 0000000000001000
[ 0.000000] 3d40: 00000413fbfcef00 0000000000000004 ffff000008d63db0 ffff0000081af390
[ 0.000000] 3d60: 00000413fbfcef00 0000000000001000 0000000000000000 0000000000001000
[ 0.000000] 3d80: 0000000000000000 0000000000000040 0000000000000040 ffff000008cb3cc8
[ 0.000000] 3da0: 0000000000000000 ffff000008d65e50 ffff000008d65d28 ffff8413fbfcf63c
[ 0.000000] 3dc0: 0000000000000001 0000000000000000 0000000000000004 00000413fbffff00
[ 0.000000] 3de0: 000000000000006f 0000000000000000 0000000000000004 000000000000001e
[ 0.000000] 3e00: 0000000000000003 ffff000008e49ae0
[ 0.000000] [<ffff000008c754f4>] pcpu_embed_first_chunk+0x420/0x704
[ 0.000000] [<ffff000008c6658c>] setup_per_cpu_areas+0x38/0xc8
[ 0.000000] [<ffff000008c608d8>] start_kernel+0x10c/0x390
[ 0.000000] [<ffff000008c601d8>] __primary_switched+0x5c/0x64
[ 0.000000] Code: b8018660 17ffffd7 6b16037f 54000080 (d4210000)
[ 0.000000] ---[ end trace 0000000000000000 ]---
[ 0.000000] Kernel panic - not syncing: Attempted to kill the idle task!
Fix by getting CPUs proximity through its node. We only care about
whether it is LOCAL_DISTANCE or not, for pcpu_build_alloc_info() only
use this to group CPUs.
Fixes: 7af3a0a99252 ("arm64/numa: support HAVE_SETUP_PER_CPU_AREA")
Signed-off-by: Yisheng Xie <xieyisheng1@huawei.com>
Signed-off-by: Hanjun Guo <hanjun.guo@linaro.org>
Cc: Catalin Marinas <catalin.marinas@arm.com>
Cc: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
Cc: Will Deacon <will.deacon@arm.com>
Cc: Zhen Lei <thunder.leizhen@huawei.com>
---
arch/arm64/mm/numa.c | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/arch/arm64/mm/numa.c b/arch/arm64/mm/numa.c
index 778a985..34415fc 100644
--- a/arch/arm64/mm/numa.c
+++ b/arch/arm64/mm/numa.c
@@ -147,7 +147,10 @@ static int __init early_cpu_to_node(int cpu)
static int __init pcpu_cpu_distance(unsigned int from, unsigned int to)
{
- return node_distance(from, to);
+ if (early_cpu_to_node(from) == early_cpu_to_node(to))
+ return LOCAL_DISTANCE;
+ else
+ return REMOTE_DISTANCE;
}
static void * __init pcpu_fc_alloc(unsigned int cpu, size_t size,
--
1.7.12.4
^ permalink raw reply related
* [PATCH v2 8/8] ARM: dts: sun6i: hummingbird-a31: Enable display output through VGA bridge
From: Chen-Yu Tsai @ 2016-10-20 3:43 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20161020034344.14154-1-wens@csie.org>
The Hummingbird A31 board has a RGB-to-VGA bridge which converts RGB
output from the LCD interface to VGA signals.
Enable this part of the display pipeline.
Signed-off-by: Chen-Yu Tsai <wens@csie.org>
---
arch/arm/boot/dts/sun6i-a31-hummingbird.dts | 56 +++++++++++++++++++++++++++++
1 file changed, 56 insertions(+)
diff --git a/arch/arm/boot/dts/sun6i-a31-hummingbird.dts b/arch/arm/boot/dts/sun6i-a31-hummingbird.dts
index 9a74637f677f..05a49b2147f1 100644
--- a/arch/arm/boot/dts/sun6i-a31-hummingbird.dts
+++ b/arch/arm/boot/dts/sun6i-a31-hummingbird.dts
@@ -63,6 +63,49 @@
stdout-path = "serial0:115200n8";
};
+ bridge {
+ compatible = "dumb-vga-dac";
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ ports {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ port at 0 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <0>;
+
+ vga_bridge_in: endpoint at 0 {
+ reg = <0>;
+ remote-endpoint = <&tcon0_out_vga>;
+ };
+ };
+
+ port at 1 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <1>;
+
+ vga_bridge_out: endpoint at 0 {
+ reg = <0>;
+ remote-endpoint = <&vga_con_in>;
+ };
+ };
+ };
+ };
+
+ vga {
+ compatible = "vga-connector";
+
+ port {
+ vga_con_in: endpoint {
+ remote-endpoint = <&vga_bridge_out>;
+ };
+ };
+ };
+
wifi_pwrseq: wifi_pwrseq {
compatible = "mmc-pwrseq-simple";
reset-gpios = <&pio 6 10 GPIO_ACTIVE_LOW>; /* PG10 */
@@ -245,6 +288,19 @@
status = "okay";
};
+&tcon0 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&lcd0_rgb888_pins>;
+ status = "okay";
+};
+
+&tcon0_out {
+ tcon0_out_vga: endpoint at 0 {
+ reg = <0>;
+ remote-endpoint = <&vga_bridge_in>;
+ };
+};
+
&uart0 {
pinctrl-names = "default";
pinctrl-0 = <&uart0_pins_a>;
--
2.9.3
^ permalink raw reply related
* [PATCH v2 7/8] ARM: dts: sun6i: Add A31 LCD0 RGB888 pins
From: Chen-Yu Tsai @ 2016-10-20 3:43 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20161020034344.14154-1-wens@csie.org>
The LCD0 controller on the A31 can do RGB output up to 8 bits per
channel. Add the pins for RGB888 output.
Signed-off-by: Chen-Yu Tsai <wens@csie.org>
---
arch/arm/boot/dts/sun6i-a31.dtsi | 13 +++++++++++++
1 file changed, 13 insertions(+)
diff --git a/arch/arm/boot/dts/sun6i-a31.dtsi b/arch/arm/boot/dts/sun6i-a31.dtsi
index 4d2c7786b92a..2e8bf93dcfb2 100644
--- a/arch/arm/boot/dts/sun6i-a31.dtsi
+++ b/arch/arm/boot/dts/sun6i-a31.dtsi
@@ -540,6 +540,19 @@
allwinner,pull = <SUN4I_PINCTRL_NO_PULL>;
};
+ lcd0_rgb888_pins: lcd0_rgb888 {
+ allwinner,pins = "PD0", "PD1", "PD2", "PD3",
+ "PD4", "PD5", "PD6", "PD7",
+ "PD8", "PD9", "PD10", "PD11",
+ "PD12", "PD13", "PD14", "PD15",
+ "PD16", "PD17", "PD18", "PD19",
+ "PD20", "PD21", "PD22", "PD23",
+ "PD24", "PD25", "PD26", "PD27";
+ allwinner,function = "lcd0";
+ allwinner,drive = <SUN4I_PINCTRL_10_MA>;
+ allwinner,pull = <SUN4I_PINCTRL_NO_PULL>;
+ };
+
mmc0_pins_a: mmc0 at 0 {
allwinner,pins = "PF0", "PF1", "PF2",
"PF3", "PF4", "PF5";
--
2.9.3
^ permalink raw reply related
* [PATCH v2 6/8] ARM: dts: sun6i: Add device nodes for first display pipeline
From: Chen-Yu Tsai @ 2016-10-20 3:43 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20161020034344.14154-1-wens@csie.org>
The A31 has 2 parallel display pipelines, which can be intermixed.
However the driver currently only supports one of them.
Signed-off-by: Chen-Yu Tsai <wens@csie.org>
---
arch/arm/boot/dts/sun6i-a31.dtsi | 152 ++++++++++++++++++++++++++++++++++++++
arch/arm/boot/dts/sun6i-a31s.dtsi | 8 ++
2 files changed, 160 insertions(+)
diff --git a/arch/arm/boot/dts/sun6i-a31.dtsi b/arch/arm/boot/dts/sun6i-a31.dtsi
index c1b891e75f18..4d2c7786b92a 100644
--- a/arch/arm/boot/dts/sun6i-a31.dtsi
+++ b/arch/arm/boot/dts/sun6i-a31.dtsi
@@ -231,6 +231,11 @@
};
};
+ de: display-engine {
+ compatible = "allwinner,sun6i-a31-display-engine";
+ allwinner,pipelines = <&fe0>;
+ };
+
soc at 01c00000 {
compatible = "simple-bus";
#address-cells = <1>;
@@ -246,6 +251,44 @@
#dma-cells = <1>;
};
+ tcon0: lcd-controller at 01c0c000 {
+ compatible = "allwinner,sun6i-a31-tcon";
+ reg = <0x01c0c000 0x1000>;
+ interrupts = <GIC_SPI 86 IRQ_TYPE_LEVEL_HIGH>;
+ resets = <&ccu RST_AHB1_LCD0>;
+ reset-names = "lcd";
+ clocks = <&ccu CLK_AHB1_LCD0>,
+ <&ccu CLK_LCD0_CH0>,
+ <&ccu CLK_LCD0_CH1>;
+ clock-names = "ahb",
+ "tcon-ch0",
+ "tcon-ch1";
+ clock-output-names = "tcon0-pixel-clock";
+ status = "disabled";
+
+ ports {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ tcon0_in: port at 0 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <0>;
+
+ tcon0_in_drc0: endpoint at 0 {
+ reg = <0>;
+ remote-endpoint = <&drc0_out_tcon0>;
+ };
+ };
+
+ tcon0_out: port at 1 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <1>;
+ };
+ };
+ };
+
mmc0: mmc at 01c0f000 {
compatible = "allwinner,sun7i-a20-mmc";
reg = <0x01c0f000 0x1000>;
@@ -799,6 +842,115 @@
interrupts = <GIC_PPI 9 (GIC_CPU_MASK_SIMPLE(4) | IRQ_TYPE_LEVEL_HIGH)>;
};
+ fe0: display-frontend at 01e00000 {
+ compatible = "allwinner,sun6i-a31-display-frontend";
+ reg = <0x01e00000 0x20000>;
+ interrupts = <GIC_SPI 93 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&ccu CLK_AHB1_FE0>, <&ccu CLK_FE0>,
+ <&ccu CLK_DRAM_FE0>;
+ clock-names = "ahb", "mod",
+ "ram";
+ resets = <&ccu RST_AHB1_FE0>;
+
+ ports {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ fe0_out: port at 1 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <1>;
+
+ fe0_out_be0: endpoint at 0 {
+ reg = <0>;
+ remote-endpoint = <&be0_in_fe0>;
+ };
+ };
+ };
+ };
+
+ be0: display-backend at 01e60000 {
+ compatible = "allwinner,sun6i-a31-display-backend";
+ reg = <0x01e60000 0x10000>;
+ interrupts = <GIC_SPI 95 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&ccu CLK_AHB1_BE0>, <&ccu CLK_BE0>,
+ <&ccu CLK_DRAM_BE0>;
+ clock-names = "ahb", "mod",
+ "ram";
+ resets = <&ccu RST_AHB1_BE0>;
+
+ assigned-clocks = <&ccu CLK_BE0>;
+ assigned-clock-rates = <300000000>;
+
+ ports {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ be0_in: port at 0 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <0>;
+
+ be0_in_fe0: endpoint at 0 {
+ reg = <0>;
+ remote-endpoint = <&fe0_out_be0>;
+ };
+ };
+
+ be0_out: port at 1 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <1>;
+
+ be0_out_drc0: endpoint at 0 {
+ reg = <0>;
+ remote-endpoint = <&drc0_in_be0>;
+ };
+ };
+ };
+ };
+
+ drc0: drc at 01e70000 {
+ compatible = "allwinner,sun6i-a31-drc";
+ reg = <0x01e70000 0x10000>;
+ interrupts = <GIC_SPI 91 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&ccu CLK_AHB1_DRC0>, <&ccu CLK_IEP_DRC0>,
+ <&ccu CLK_DRAM_DRC0>;
+ clock-names = "ahb", "mod",
+ "ram";
+ resets = <&ccu RST_AHB1_DRC0>;
+
+ assigned-clocks = <&ccu CLK_IEP_DRC0>;
+ assigned-clock-rates = <300000000>;
+
+ ports {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ drc0_in: port at 0 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <0>;
+
+ drc0_in_be0: endpoint at 0 {
+ reg = <0>;
+ remote-endpoint = <&be0_out_drc0>;
+ };
+ };
+
+ drc0_out: port at 1 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <1>;
+
+ drc0_out_tcon0: endpoint at 0 {
+ reg = <0>;
+ remote-endpoint = <&tcon0_in_drc0>;
+ };
+ };
+ };
+ };
+
rtc: rtc at 01f00000 {
compatible = "allwinner,sun6i-a31-rtc";
reg = <0x01f00000 0x54>;
diff --git a/arch/arm/boot/dts/sun6i-a31s.dtsi b/arch/arm/boot/dts/sun6i-a31s.dtsi
index c17a32771b98..97e2c51d0aea 100644
--- a/arch/arm/boot/dts/sun6i-a31s.dtsi
+++ b/arch/arm/boot/dts/sun6i-a31s.dtsi
@@ -48,6 +48,14 @@
#include "sun6i-a31.dtsi"
+&de {
+ compatible = "allwinner,sun6i-a31s-display-engine";
+};
+
&pio {
compatible = "allwinner,sun6i-a31s-pinctrl";
};
+
+&tcon0 {
+ compatible = "allwinner,sun6i-a31s-tcon";
+};
--
2.9.3
^ permalink raw reply related
* [PATCH v2 5/8] drm/sun4i: Add compatible strings for A31/A31s display pipelines
From: Chen-Yu Tsai @ 2016-10-20 3:43 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20161020034344.14154-1-wens@csie.org>
The A31's display pipeline has 2 frontends, 2 backends, and 2 TCONs. It
also has new display enhancement blocks, such as the DRC (Dynamic Range
Controller), the DEU (Display Enhancement Unit), and the CMU (Color
Management Unit). It supports HDMI, MIPI DSI, and 2 LCD/LVDS channels.
The A31s display pipeline is almost the same, just without MIPI DSI.
Only the TCON seems to be different, due to the missing mux for MIPI
DSI.
Add compatible strings for both of them.
Signed-off-by: Chen-Yu Tsai <wens@csie.org>
Acked-by: Rob Herring <robh@kernel.org>
---
Documentation/devicetree/bindings/display/sunxi/sun4i-drm.txt | 4 ++++
drivers/gpu/drm/sun4i/sun4i_backend.c | 1 +
drivers/gpu/drm/sun4i/sun4i_drv.c | 3 +++
3 files changed, 8 insertions(+)
diff --git a/Documentation/devicetree/bindings/display/sunxi/sun4i-drm.txt b/Documentation/devicetree/bindings/display/sunxi/sun4i-drm.txt
index 15fdca8909f2..b82c00449468 100644
--- a/Documentation/devicetree/bindings/display/sunxi/sun4i-drm.txt
+++ b/Documentation/devicetree/bindings/display/sunxi/sun4i-drm.txt
@@ -91,6 +91,7 @@ system.
Required properties:
- compatible: value must be one of:
* allwinner,sun5i-a13-display-backend
+ * allwinner,sun6i-a31-display-backend
* allwinner,sun8i-a33-display-backend
- reg: base address and size of the memory-mapped region.
- clocks: phandles to the clocks feeding the frontend and backend
@@ -121,6 +122,7 @@ deinterlacing and color space conversion.
Required properties:
- compatible: value must be one of:
* allwinner,sun5i-a13-display-frontend
+ * allwinner,sun6i-a31-display-frontend
* allwinner,sun8i-a33-display-frontend
- reg: base address and size of the memory-mapped region.
- interrupts: interrupt associated to this IP
@@ -146,6 +148,8 @@ extra node.
Required properties:
- compatible: value must be one of:
* allwinner,sun5i-a13-display-engine
+ * allwinner,sun6i-a31-display-engine
+ * allwinner,sun6i-a31s-display-engine
* allwinner,sun8i-a33-display-engine
- allwinner,pipelines: list of phandle to the display engine
diff --git a/drivers/gpu/drm/sun4i/sun4i_backend.c b/drivers/gpu/drm/sun4i/sun4i_backend.c
index 32c0584e3c35..6e6c59a661b6 100644
--- a/drivers/gpu/drm/sun4i/sun4i_backend.c
+++ b/drivers/gpu/drm/sun4i/sun4i_backend.c
@@ -408,6 +408,7 @@ static int sun4i_backend_remove(struct platform_device *pdev)
static const struct of_device_id sun4i_backend_of_table[] = {
{ .compatible = "allwinner,sun5i-a13-display-backend" },
+ { .compatible = "allwinner,sun6i-a31-display-backend" },
{ .compatible = "allwinner,sun8i-a33-display-backend" },
{ }
};
diff --git a/drivers/gpu/drm/sun4i/sun4i_drv.c b/drivers/gpu/drm/sun4i/sun4i_drv.c
index a15c231fbd59..fa6568e1822a 100644
--- a/drivers/gpu/drm/sun4i/sun4i_drv.c
+++ b/drivers/gpu/drm/sun4i/sun4i_drv.c
@@ -201,6 +201,7 @@ static const struct component_master_ops sun4i_drv_master_ops = {
static bool sun4i_drv_node_is_frontend(struct device_node *node)
{
return of_device_is_compatible(node, "allwinner,sun5i-a13-display-frontend") ||
+ of_device_is_compatible(node, "allwinner,sun6i-a31-display-frontend") ||
of_device_is_compatible(node, "allwinner,sun8i-a33-display-frontend");
}
@@ -324,6 +325,8 @@ static int sun4i_drv_remove(struct platform_device *pdev)
static const struct of_device_id sun4i_drv_of_table[] = {
{ .compatible = "allwinner,sun5i-a13-display-engine" },
+ { .compatible = "allwinner,sun6i-a31-display-engine" },
+ { .compatible = "allwinner,sun6i-a31s-display-engine" },
{ .compatible = "allwinner,sun8i-a33-display-engine" },
{ }
};
--
2.9.3
^ permalink raw reply related
* [PATCH v2 4/8] drm/sun4i: Add compatible string for A31/A31s TCON (timing controller)
From: Chen-Yu Tsai @ 2016-10-20 3:43 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20161020034344.14154-1-wens@csie.org>
The A31 TCON has mux controls for how TCON outputs are routed to the
HDMI and MIPI DSI blocks.
Since the A31s does not have MIPI DSI, it only has a mux for the HDMI
controller input.
This patch only adds support for the compatible strings. Actual support
for the mux controls should be added with HDMI and MIPI DSI support.
Signed-off-by: Chen-Yu Tsai <wens@csie.org>
---
Documentation/devicetree/bindings/display/sunxi/sun4i-drm.txt | 4 +++-
drivers/gpu/drm/sun4i/sun4i_drv.c | 2 ++
drivers/gpu/drm/sun4i/sun4i_tcon.c | 10 ++++++++++
3 files changed, 15 insertions(+), 1 deletion(-)
diff --git a/Documentation/devicetree/bindings/display/sunxi/sun4i-drm.txt b/Documentation/devicetree/bindings/display/sunxi/sun4i-drm.txt
index 5368961cd727..15fdca8909f2 100644
--- a/Documentation/devicetree/bindings/display/sunxi/sun4i-drm.txt
+++ b/Documentation/devicetree/bindings/display/sunxi/sun4i-drm.txt
@@ -28,6 +28,8 @@ The TCON acts as a timing controller for RGB, LVDS and TV interfaces.
Required properties:
- compatible: value must be either:
* allwinner,sun5i-a13-tcon
+ * allwinner,sun6i-a31-tcon
+ * allwinner,sun6i-a31s-tcon
* allwinner,sun8i-a33-tcon
- reg: base address and size of memory-mapped region
- interrupts: interrupt associated to this IP
@@ -50,7 +52,7 @@ Required properties:
second the block connected to the TCON channel 1 (usually the TV
encoder)
-On the A13, there is one more clock required:
+On SoCs other than the A33, there is one more clock required:
- 'tcon-ch1': The clock driving the TCON channel 1
DRC
diff --git a/drivers/gpu/drm/sun4i/sun4i_drv.c b/drivers/gpu/drm/sun4i/sun4i_drv.c
index 0da9862ad8ed..a15c231fbd59 100644
--- a/drivers/gpu/drm/sun4i/sun4i_drv.c
+++ b/drivers/gpu/drm/sun4i/sun4i_drv.c
@@ -207,6 +207,8 @@ static bool sun4i_drv_node_is_frontend(struct device_node *node)
static bool sun4i_drv_node_is_tcon(struct device_node *node)
{
return of_device_is_compatible(node, "allwinner,sun5i-a13-tcon") ||
+ of_device_is_compatible(node, "allwinner,sun6i-a31-tcon") ||
+ of_device_is_compatible(node, "allwinner,sun6i-a31s-tcon") ||
of_device_is_compatible(node, "allwinner,sun8i-a33-tcon");
}
diff --git a/drivers/gpu/drm/sun4i/sun4i_tcon.c b/drivers/gpu/drm/sun4i/sun4i_tcon.c
index 7658f0337e0b..c6afb2448655 100644
--- a/drivers/gpu/drm/sun4i/sun4i_tcon.c
+++ b/drivers/gpu/drm/sun4i/sun4i_tcon.c
@@ -587,12 +587,22 @@ static const struct sun4i_tcon_quirks sun5i_a13_quirks = {
.has_channel_1 = true,
};
+static const struct sun4i_tcon_quirks sun6i_a31_quirks = {
+ .has_channel_1 = true,
+};
+
+static const struct sun4i_tcon_quirks sun6i_a31s_quirks = {
+ .has_channel_1 = true,
+};
+
static const struct sun4i_tcon_quirks sun8i_a33_quirks = {
/* nothing is supported */
};
static const struct of_device_id sun4i_tcon_of_table[] = {
{ .compatible = "allwinner,sun5i-a13-tcon", .data = &sun5i_a13_quirks },
+ { .compatible = "allwinner,sun6i-a31-tcon", .data = &sun6i_a31_quirks },
+ { .compatible = "allwinner,sun6i-a31s-tcon", .data = &sun6i_a31s_quirks },
{ .compatible = "allwinner,sun8i-a33-tcon", .data = &sun8i_a33_quirks },
{ }
};
--
2.9.3
^ permalink raw reply related
* [PATCH v2 3/8] drm/sun4i: tcon: Move SoC specific quirks to a DT matched data structure
From: Chen-Yu Tsai @ 2016-10-20 3:43 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20161020034344.14154-1-wens@csie.org>
We already have some differences between the 2 supported SoCs.
More will be added as we support other SoCs. To avoid bloating
the probe function with even more conditionals, move the quirks
to a separate data structure that's tied to the compatible string.
Signed-off-by: Chen-Yu Tsai <wens@csie.org>
---
drivers/gpu/drm/sun4i/sun4i_tcon.c | 33 ++++++++++++++++++---------------
drivers/gpu/drm/sun4i/sun4i_tcon.h | 11 +++++++----
2 files changed, 25 insertions(+), 19 deletions(-)
diff --git a/drivers/gpu/drm/sun4i/sun4i_tcon.c b/drivers/gpu/drm/sun4i/sun4i_tcon.c
index cadacb517f95..7658f0337e0b 100644
--- a/drivers/gpu/drm/sun4i/sun4i_tcon.c
+++ b/drivers/gpu/drm/sun4i/sun4i_tcon.c
@@ -20,6 +20,7 @@
#include <linux/component.h>
#include <linux/ioport.h>
#include <linux/of_address.h>
+#include <linux/of_device.h>
#include <linux/of_graph.h>
#include <linux/of_irq.h>
#include <linux/regmap.h>
@@ -62,7 +63,7 @@ void sun4i_tcon_channel_disable(struct sun4i_tcon *tcon, int channel)
return;
}
- WARN_ON(!tcon->has_channel_1);
+ WARN_ON(!tcon->quirks->has_channel_1);
regmap_update_bits(tcon->regs, SUN4I_TCON1_CTL_REG,
SUN4I_TCON1_CTL_TCON_ENABLE, 0);
clk_disable_unprepare(tcon->sclk1);
@@ -80,7 +81,7 @@ void sun4i_tcon_channel_enable(struct sun4i_tcon *tcon, int channel)
return;
}
- WARN_ON(!tcon->has_channel_1);
+ WARN_ON(!tcon->quirks->has_channel_1);
regmap_update_bits(tcon->regs, SUN4I_TCON1_CTL_REG,
SUN4I_TCON1_CTL_TCON_ENABLE,
SUN4I_TCON1_CTL_TCON_ENABLE);
@@ -202,7 +203,7 @@ void sun4i_tcon1_mode_set(struct sun4i_tcon *tcon,
u8 clk_delay;
u32 val;
- WARN_ON(!tcon->has_channel_1);
+ WARN_ON(!tcon->quirks->has_channel_1);
/* Adjust clock delay */
clk_delay = sun4i_tcon_get_clk_delay(mode, 1);
@@ -266,7 +267,7 @@ void sun4i_tcon1_mode_set(struct sun4i_tcon *tcon,
/*
* FIXME: Undocumented bits
*/
- if (tcon->has_mux)
+ if (tcon->quirks->has_unknown_mux)
regmap_write(tcon->regs, SUN4I_TCON_MUX_CTRL_REG, 1);
}
EXPORT_SYMBOL(sun4i_tcon1_mode_set);
@@ -327,7 +328,7 @@ static int sun4i_tcon_init_clocks(struct device *dev,
return PTR_ERR(tcon->sclk0);
}
- if (tcon->has_channel_1) {
+ if (tcon->quirks->has_channel_1) {
tcon->sclk1 = devm_clk_get(dev, "tcon-ch1");
if (IS_ERR(tcon->sclk1)) {
dev_err(dev, "Couldn't get the TCON channel 1 clock\n");
@@ -487,14 +488,7 @@ static int sun4i_tcon_bind(struct device *dev, struct device *master,
drv->tcon = tcon;
tcon->drm = drm;
tcon->dev = dev;
-
- if (of_device_is_compatible(dev->of_node, "allwinner,sun5i-a13-tcon")) {
- tcon->has_mux = true;
- tcon->has_channel_1 = true;
- } else {
- tcon->has_mux = false;
- tcon->has_channel_1 = false;
- }
+ tcon->quirks = of_device_get_match_data(dev);
tcon->lcd_rst = devm_reset_control_get(dev, "lcd");
if (IS_ERR(tcon->lcd_rst)) {
@@ -588,9 +582,18 @@ static int sun4i_tcon_remove(struct platform_device *pdev)
return 0;
}
+static const struct sun4i_tcon_quirks sun5i_a13_quirks = {
+ .has_unknown_mux = true,
+ .has_channel_1 = true,
+};
+
+static const struct sun4i_tcon_quirks sun8i_a33_quirks = {
+ /* nothing is supported */
+};
+
static const struct of_device_id sun4i_tcon_of_table[] = {
- { .compatible = "allwinner,sun5i-a13-tcon" },
- { .compatible = "allwinner,sun8i-a33-tcon" },
+ { .compatible = "allwinner,sun5i-a13-tcon", .data = &sun5i_a13_quirks },
+ { .compatible = "allwinner,sun8i-a33-tcon", .data = &sun8i_a33_quirks },
{ }
};
MODULE_DEVICE_TABLE(of, sun4i_tcon_of_table);
diff --git a/drivers/gpu/drm/sun4i/sun4i_tcon.h b/drivers/gpu/drm/sun4i/sun4i_tcon.h
index 12bd48925f4d..166064bafe2e 100644
--- a/drivers/gpu/drm/sun4i/sun4i_tcon.h
+++ b/drivers/gpu/drm/sun4i/sun4i_tcon.h
@@ -142,6 +142,11 @@
#define SUN4I_TCON_MAX_CHANNELS 2
+struct sun4i_tcon_quirks {
+ bool has_unknown_mux; /* sun5i has undocumented mux */
+ bool has_channel_1; /* a33 does not have channel 1 */
+};
+
struct sun4i_tcon {
struct device *dev;
struct drm_device *drm;
@@ -160,12 +165,10 @@ struct sun4i_tcon {
/* Reset control */
struct reset_control *lcd_rst;
- /* Platform adjustments */
- bool has_mux;
-
struct drm_panel *panel;
- bool has_channel_1;
+ /* Platform adjustments */
+ const struct sun4i_tcon_quirks *quirks;
};
struct drm_bridge *sun4i_tcon_find_bridge(struct device_node *node);
--
2.9.3
^ permalink raw reply related
* [PATCH v2 2/8] drm/sun4i: sun6i-drc: Support DRC on A31 and A31s
From: Chen-Yu Tsai @ 2016-10-20 3:43 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20161020034344.14154-1-wens@csie.org>
The A31 and A31s also have the DRC as part of the display pipeline.
As we know virtually nothing about them, just add compatible strings
for both SoCs to the stub driver.
Signed-off-by: Chen-Yu Tsai <wens@csie.org>
Acked-by: Rob Herring <robh@kernel.org>
---
Documentation/devicetree/bindings/display/sunxi/sun4i-drm.txt | 2 ++
drivers/gpu/drm/sun4i/sun6i_drc.c | 2 ++
2 files changed, 4 insertions(+)
diff --git a/Documentation/devicetree/bindings/display/sunxi/sun4i-drm.txt b/Documentation/devicetree/bindings/display/sunxi/sun4i-drm.txt
index b95696d748c7..5368961cd727 100644
--- a/Documentation/devicetree/bindings/display/sunxi/sun4i-drm.txt
+++ b/Documentation/devicetree/bindings/display/sunxi/sun4i-drm.txt
@@ -64,6 +64,8 @@ adaptive backlight control.
Required properties:
- compatible: value must be one of:
+ * allwinner,sun6i-a31-drc
+ * allwinner,sun6i-a31s-drc
* allwinner,sun8i-a33-drc
- reg: base address and size of the memory-mapped region.
- interrupts: interrupt associated to this IP
diff --git a/drivers/gpu/drm/sun4i/sun6i_drc.c b/drivers/gpu/drm/sun4i/sun6i_drc.c
index bf6d846d8132..6ef707c5a719 100644
--- a/drivers/gpu/drm/sun4i/sun6i_drc.c
+++ b/drivers/gpu/drm/sun4i/sun6i_drc.c
@@ -98,6 +98,8 @@ static int sun6i_drc_remove(struct platform_device *pdev)
}
static const struct of_device_id sun6i_drc_of_table[] = {
+ { .compatible = "allwinner,sun6i-a31-drc" },
+ { .compatible = "allwinner,sun6i-a31s-drc" },
{ .compatible = "allwinner,sun8i-a33-drc" },
{ }
};
--
2.9.3
^ permalink raw reply related
* [PATCH v2 1/8] drm/bridge: rgb-to-vga: Support an enable GPIO
From: Chen-Yu Tsai @ 2016-10-20 3:43 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20161020034344.14154-1-wens@csie.org>
Some rgb-to-vga bridges have an enable GPIO, either directly tied to
an enable pin on the bridge IC, or indirectly controlling a power
switch.
Add support for it.
Signed-off-by: Chen-Yu Tsai <wens@csie.org>
---
.../bindings/display/bridge/dumb-vga-dac.txt | 2 ++
drivers/gpu/drm/bridge/dumb-vga-dac.c | 28 ++++++++++++++++++++++
2 files changed, 30 insertions(+)
diff --git a/Documentation/devicetree/bindings/display/bridge/dumb-vga-dac.txt b/Documentation/devicetree/bindings/display/bridge/dumb-vga-dac.txt
index 003bc246a270..d3484822bf77 100644
--- a/Documentation/devicetree/bindings/display/bridge/dumb-vga-dac.txt
+++ b/Documentation/devicetree/bindings/display/bridge/dumb-vga-dac.txt
@@ -16,6 +16,8 @@ graph bindings specified in Documentation/devicetree/bindings/graph.txt.
- Video port 0 for RGB input
- Video port 1 for VGA output
+Optional properties:
+- enable-gpios: GPIO pin to enable or disable the bridge
Example
-------
diff --git a/drivers/gpu/drm/bridge/dumb-vga-dac.c b/drivers/gpu/drm/bridge/dumb-vga-dac.c
index afec232185a7..b487e5e9b56d 100644
--- a/drivers/gpu/drm/bridge/dumb-vga-dac.c
+++ b/drivers/gpu/drm/bridge/dumb-vga-dac.c
@@ -10,6 +10,7 @@
* the License, or (at your option) any later version.
*/
+#include <linux/gpio/consumer.h>
#include <linux/module.h>
#include <linux/of_graph.h>
@@ -23,6 +24,7 @@ struct dumb_vga {
struct drm_connector connector;
struct i2c_adapter *ddc;
+ struct gpio_desc *enable_gpio;
};
static inline struct dumb_vga *
@@ -124,8 +126,26 @@ static int dumb_vga_attach(struct drm_bridge *bridge)
return 0;
}
+static void dumb_vga_enable(struct drm_bridge *bridge)
+{
+ struct dumb_vga *vga = drm_bridge_to_dumb_vga(bridge);
+
+ if (vga->enable_gpio)
+ gpiod_set_value_cansleep(vga->enable_gpio, 1);
+}
+
+static void dumb_vga_disable(struct drm_bridge *bridge)
+{
+ struct dumb_vga *vga = drm_bridge_to_dumb_vga(bridge);
+
+ if (vga->enable_gpio)
+ gpiod_set_value_cansleep(vga->enable_gpio, 0);
+}
+
static const struct drm_bridge_funcs dumb_vga_bridge_funcs = {
.attach = dumb_vga_attach,
+ .enable = dumb_vga_enable,
+ .disable = dumb_vga_disable,
};
static struct i2c_adapter *dumb_vga_retrieve_ddc(struct device *dev)
@@ -169,6 +189,14 @@ static int dumb_vga_probe(struct platform_device *pdev)
return -ENOMEM;
platform_set_drvdata(pdev, vga);
+ vga->enable_gpio = devm_gpiod_get_optional(&pdev->dev, "enable",
+ GPIOD_OUT_LOW);
+ if (IS_ERR(vga->enable_gpio)) {
+ ret = PTR_ERR(vga->enable_gpio);
+ dev_err(&pdev->dev, "failed to request GPIO: %d\n", ret);
+ return ret;
+ }
+
vga->ddc = dumb_vga_retrieve_ddc(&pdev->dev);
if (IS_ERR(vga->ddc)) {
if (PTR_ERR(vga->ddc) == -ENODEV) {
--
2.9.3
^ permalink raw reply related
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