* [PATCH v4 4/6] arm64: arch timer: Add timer erratum property for Hip05-d02 and Hip06-d03
From: Ding Tianhong @ 2016-11-15 12:16 UTC (permalink / raw)
To: catalin.marinas-5wv7dgnIgG8, will.deacon-5wv7dgnIgG8,
marc.zyngier-5wv7dgnIgG8, mark.rutland-5wv7dgnIgG8,
oss-fOR+EgIDQEHk1uMJSBkQmQ, devicetree-u79uwXL29TY76Z2rM5mHXA,
shawnguo-DgEjT+Ai2ygdnm+yROfE0A, stuart.yoder-3arQi8VN3Tc,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
linuxarm-hv44wF8Li93QT0dZR+AlfA,
hanjun.guo-QSEj5FYQhm4dnm+yROfE0A
Cc: Ding Tianhong
In-Reply-To: <1479212167-5812-1-git-send-email-dingtianhong-hv44wF8Li93QT0dZR+AlfA@public.gmane.org>
Enable workaround for hisilicon erratum 161601 on Hip05-d02 and Hip06-d03 board.
Signed-off-by: Ding Tianhong <dingtianhong-hv44wF8Li93QT0dZR+AlfA@public.gmane.org>
---
arch/arm64/boot/dts/hisilicon/hip05.dtsi | 1 +
arch/arm64/boot/dts/hisilicon/hip06.dtsi | 1 +
2 files changed, 2 insertions(+)
diff --git a/arch/arm64/boot/dts/hisilicon/hip05.dtsi b/arch/arm64/boot/dts/hisilicon/hip05.dtsi
index bf322ed..f815d94 100644
--- a/arch/arm64/boot/dts/hisilicon/hip05.dtsi
+++ b/arch/arm64/boot/dts/hisilicon/hip05.dtsi
@@ -281,6 +281,7 @@
<GIC_PPI 14 IRQ_TYPE_LEVEL_LOW>,
<GIC_PPI 11 IRQ_TYPE_LEVEL_LOW>,
<GIC_PPI 10 IRQ_TYPE_LEVEL_LOW>;
+ hisilicon,erratum-161601;
};
pmu {
diff --git a/arch/arm64/boot/dts/hisilicon/hip06.dtsi b/arch/arm64/boot/dts/hisilicon/hip06.dtsi
index 5927bc4..d63990b 100644
--- a/arch/arm64/boot/dts/hisilicon/hip06.dtsi
+++ b/arch/arm64/boot/dts/hisilicon/hip06.dtsi
@@ -260,6 +260,7 @@
<GIC_PPI 14 IRQ_TYPE_LEVEL_LOW>,
<GIC_PPI 11 IRQ_TYPE_LEVEL_LOW>,
<GIC_PPI 10 IRQ_TYPE_LEVEL_LOW>;
+ hisilicon,erratum-161601;
};
pmu {
--
1.9.0
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply related
* [PATCH v4 3/6] arm64: arch_timer: Work around Erratum Hisilicon-161601
From: Ding Tianhong @ 2016-11-15 12:16 UTC (permalink / raw)
To: catalin.marinas, will.deacon, marc.zyngier, mark.rutland, oss,
devicetree, shawnguo, stuart.yoder, linux-arm-kernel, linuxarm,
hanjun.guo
Cc: Ding Tianhong
In-Reply-To: <1479212167-5812-1-git-send-email-dingtianhong@huawei.com>
Erratum Hisilicon-161601 says that the ARM generic timer counter "has the
potential to contain an erroneous value when the timer value changes".
Accesses to TVAL (both read and write) are also affected due to the implicit counter
read. Accesses to CVAL are not affected.
The workaround is to reread the system count registers until the value of the second
read is larger than the first one by less than 32, the system counter can be guaranteed
not to return wrong value twice by back-to-back read and the error value is always larger
than the correct one by 32. Writes to TVAL are replaced with an equivalent write to CVAL.
The workaround is enabled if the hisilicon,erratum-161601 property is found in
the timer node in the device tree. This can be overridden with the
clocksource.arm_arch_timer.hisilicon-161601 boot parameter, which allows KVM
users to enable the workaround until a mechanism is implemented to
automatically communicate this information.
Fix some description for fsl erratum a008585.
v2: Significant rework based on feedback, including seperate the fsl erratum a008585
to another patch, update the erratum name and remove unwanted code.
v3: Significant rework based on feedback, including fix some alignment problem, make the
#define __hisi_161601_read_reg to be private to the .c file instead of being globally
visible, add more accurate annotation and modify a bit of logical format to enable
arch_timer_read_ool_enabled, remove the kernel commandline parameter
clocksource.arm_arch_timer.hisilicon-161601.
Signed-off-by: Ding Tianhong <dingtianhong@huawei.com>
---
Documentation/arm64/silicon-errata.txt | 1 +
arch/arm64/include/asm/arch_timer.h | 2 +-
drivers/clocksource/Kconfig | 9 +++++
drivers/clocksource/arm_arch_timer.c | 67 +++++++++++++++++++++++++++++++---
4 files changed, 73 insertions(+), 6 deletions(-)
diff --git a/Documentation/arm64/silicon-errata.txt b/Documentation/arm64/silicon-errata.txt
index 405da11..1c1a95f 100644
--- a/Documentation/arm64/silicon-errata.txt
+++ b/Documentation/arm64/silicon-errata.txt
@@ -63,3 +63,4 @@ stable kernels.
| Cavium | ThunderX SMMUv2 | #27704 | N/A |
| | | | |
| Freescale/NXP | LS2080A/LS1043A | A-008585 | FSL_ERRATUM_A008585 |
+| Hisilicon | Hip0{5,6,7} | #161601 | HISILICON_ERRATUM_161601|
diff --git a/arch/arm64/include/asm/arch_timer.h b/arch/arm64/include/asm/arch_timer.h
index f882c7c..ebf4cde 100644
--- a/arch/arm64/include/asm/arch_timer.h
+++ b/arch/arm64/include/asm/arch_timer.h
@@ -29,7 +29,7 @@
#include <clocksource/arm_arch_timer.h>
-#if IS_ENABLED(CONFIG_FSL_ERRATUM_A008585)
+#if IS_ENABLED(CONFIG_FSL_ERRATUM_A008585) || IS_ENABLED(CONFIG_HISILICON_ERRATUM_161601)
extern struct static_key_false arch_timer_read_ool_enabled;
#define needs_unstable_timer_counter_workaround() \
static_branch_unlikely(&arch_timer_read_ool_enabled)
diff --git a/drivers/clocksource/Kconfig b/drivers/clocksource/Kconfig
index 8a753fd..6c03ed0 100644
--- a/drivers/clocksource/Kconfig
+++ b/drivers/clocksource/Kconfig
@@ -315,6 +315,15 @@ config FSL_ERRATUM_A008585
value"). The workaround will only be active if the
fsl,erratum-a008585 property is found in the timer node.
+config HISILICON_ERRATUM_161601
+ bool "Workaround for Hisilicon Erratum 161601"
+ default y
+ depends on ARM_ARCH_TIMER && ARM64
+ help
+ This option enables a workaround for Hisilicon Erratum
+ 161601. The workaround will be active if the hisilicon,erratum-161601
+ property is found in the timer node.
+
config ARM_GLOBAL_TIMER
bool "Support for the ARM global timer" if COMPILE_TEST
select CLKSRC_OF if OF
diff --git a/drivers/clocksource/arm_arch_timer.c b/drivers/clocksource/arm_arch_timer.c
index 696386f..3d59af1 100644
--- a/drivers/clocksource/arm_arch_timer.c
+++ b/drivers/clocksource/arm_arch_timer.c
@@ -94,15 +94,18 @@ early_param("clocksource.arm_arch_timer.evtstrm", early_evtstrm_cfg);
* Architected system timer support.
*/
-#ifdef CONFIG_FSL_ERRATUM_A008585
+#if CONFIG_FSL_ERRATUM_A008585 || IS_ENABLED(CONFIG_HISILICON_ERRATUM_161601)
struct arch_timer_erratum_workaround *timer_unstable_counter_workaround = NULL;
EXPORT_SYMBOL_GPL(timer_unstable_counter_workaround);
#define FSL_A008585 0x0001
+#define HISILICON_161601 0x0002
DEFINE_STATIC_KEY_FALSE(arch_timer_read_ool_enabled);
EXPORT_SYMBOL_GPL(arch_timer_read_ool_enabled);
+#endif
+#ifdef CONFIG_FSL_ERRATUM_A008585
/*
* The number of retries is an arbitrary value well beyond the highest number
* of iterations the loop has been observed to take.
@@ -144,6 +147,51 @@ static struct arch_timer_erratum_workaround arch_timer_fsl_a008585 = {
};
#endif /* CONFIG_FSL_ERRATUM_A008585 */
+#ifdef CONFIG_HISILICON_ERRATUM_161601
+/*
+ * Theoretically the erratum should not occur more than twice in succession,
+ * so set the retry count to 2 is sufficient here.
+ * Verify whether the value of the second read is larger than the first by
+ * less than 32 is the only way to confirm the value is correct, so clear the
+ * lower 5 bits to check whether the difference is greater than 32 or not.
+ */
+#define __hisi_161601_read_reg(reg) ({ \
+ u64 _old, _new; \
+ int _retries = 2; \
+ \
+ do { \
+ _old = read_sysreg(reg); \
+ _new = read_sysreg(reg); \
+ _retries--; \
+ } while (unlikely((_new - _old) >> 5) && _retries); \
+ \
+ WARN_ON_ONCE(!_retries); \
+ _new; \
+})
+
+static u32 hisi_161601_read_cntp_tval_el0(void)
+{
+ return __hisi_161601_read_reg(cntp_tval_el0);
+}
+
+static u32 hisi_161601_read_cntv_tval_el0(void)
+{
+ return __hisi_161601_read_reg(cntv_tval_el0);
+}
+
+static u64 hisi_161601_read_cntvct_el0(void)
+{
+ return __hisi_161601_read_reg(cntvct_el0);
+}
+
+static struct arch_timer_erratum_workaround arch_timer_hisi_161601 = {
+ .erratum = HISILICON_161601,
+ .read_cntp_tval_el0 = hisi_161601_read_cntp_tval_el0,
+ .read_cntv_tval_el0 = hisi_161601_read_cntv_tval_el0,
+ .read_cntvct_el0 = hisi_161601_read_cntvct_el0,
+};
+#endif /* CONFIG_HISILICON_ERRATUM_161601 */
+
static __always_inline
void arch_timer_reg_write(int access, enum arch_timer_reg reg, u32 val,
struct clock_event_device *clk)
@@ -293,7 +341,7 @@ static __always_inline void set_next_event(const int access, unsigned long evt,
arch_timer_reg_write(access, ARCH_TIMER_REG_CTRL, ctrl, clk);
}
-#ifdef CONFIG_FSL_ERRATUM_A008585
+#if IS_ENABLED(CONFIG_FSL_ERRATUM_A008585) || IS_ENABLED(CONFIG_HISILICON_ERRATUM_161601)
static __always_inline void erratum_set_next_event_generic(const int access,
unsigned long evt, struct clock_event_device *clk)
{
@@ -357,7 +405,7 @@ static int arch_timer_set_next_event_phys_mem(unsigned long evt,
static void erratum_workaround_set_sne(struct clock_event_device *clk)
{
-#ifdef CONFIG_FSL_ERRATUM_A008585
+#if IS_ENABLED(CONFIG_FSL_ERRATUM_A008585) || IS_ENABLED(CONFIG_HISILICON_ERRATUM_161601)
if (!static_branch_unlikely(&arch_timer_read_ool_enabled))
return;
@@ -617,7 +665,7 @@ static void __init arch_counter_register(unsigned type)
clocksource_counter.archdata.vdso_direct = true;
-#ifdef CONFIG_FSL_ERRATUM_A008585
+#if IS_ENABLED(CONFIG_FSL_ERRATUM_A008585) || IS_ENABLED(CONFIG_HISILICON_ERRATUM_161601)
/*
* Don't use the vdso fastpath if errata require using
* the out-of-line counter accessor.
@@ -906,10 +954,19 @@ static int __init arch_timer_of_init(struct device_node *np)
#ifdef CONFIG_FSL_ERRATUM_A008585
if (!timer_unstable_counter_workaround && of_property_read_bool(np, "fsl,erratum-a008585"))
timer_unstable_counter_workaround = &arch_timer_fsl_a008585;
+#endif
+
+#ifdef CONFIG_HISILICON_ERRATUM_161601
+ if (!timer_unstable_counter_workaround && of_property_read_bool(np, "hisilicon,erratum-161601"))
+ timer_unstable_counter_workaround = &arch_timer_hisi_161601;
+#endif
+#if IS_ENABLED(CONFIG_FSL_ERRATUM_A008585) || IS_ENABLED(CONFIG_HISILICON_ERRATUM_161601)
if (timer_unstable_counter_workaround) {
static_branch_enable(&arch_timer_read_ool_enabled);
- pr_info("Enabling workaround for FSL erratum A-008585\n");
+ pr_info("Enabling workaround for %s\n",
+ timer_unstable_counter_workaround->erratum == FSL_A008585 ?
+ "FSL ERRATUM A-008585" : "HISILICON ERRATUM 161601");
}
#endif
--
1.9.0
^ permalink raw reply related
* [PATCH v4 2/6] arm64: arch_timer: Introduce a generic erratum handing mechanism for fsl-a008585
From: Ding Tianhong @ 2016-11-15 12:16 UTC (permalink / raw)
To: catalin.marinas, will.deacon, marc.zyngier, mark.rutland, oss,
devicetree, shawnguo, stuart.yoder, linux-arm-kernel, linuxarm,
hanjun.guo
Cc: Ding Tianhong
In-Reply-To: <1479212167-5812-1-git-send-email-dingtianhong@huawei.com>
The workaround for hisilicon,161601 will check the return value of the system counter
by different way, in order to distinguish with the fsl-a008585 workaround, introduce
a new generic erratum handing mechanism for fsl-a008585 and rename some functions.
v2: Introducing a new generic erratum handling mechanism for fsl erratum a008585.
v3: Introducing the erratum_workaround_set_sne generic function for fsl erratum a008585
and make the #define __fsl_a008585_read_reg to be private to the .c file instead of
being globally visible. After discussion with Marc and Will, a consensus decision was
made to remove the commandline parameter for enabling fsl,erratum-a008585 erratum,
and make some generic name more specific, export timer_unstable_counter_workaround
for module access.
Signed-off-by: Ding Tianhong <dingtianhong@huawei.com>
---
Documentation/kernel-parameters.txt | 9 -----
arch/arm64/include/asm/arch_timer.h | 36 ++++++-----------
drivers/clocksource/arm_arch_timer.c | 78 +++++++++++++++++++++---------------
3 files changed, 58 insertions(+), 65 deletions(-)
diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt
index 6fa1d8a..738de4e 100644
--- a/Documentation/kernel-parameters.txt
+++ b/Documentation/kernel-parameters.txt
@@ -698,15 +698,6 @@ bytes respectively. Such letter suffixes can also be entirely omitted.
loops can be debugged more effectively on production
systems.
- clocksource.arm_arch_timer.fsl-a008585=
- [ARM64]
- Format: <bool>
- Enable/disable the workaround of Freescale/NXP
- erratum A-008585. This can be useful for KVM
- guests, if the guest device tree doesn't show the
- erratum. If unspecified, the workaround is
- enabled based on the device tree.
-
clearcpuid=BITNUM [X86]
Disable CPUID feature X for the kernel. See
arch/x86/include/asm/cpufeatures.h for the valid bit
diff --git a/arch/arm64/include/asm/arch_timer.h b/arch/arm64/include/asm/arch_timer.h
index eaa5bbe..f882c7c 100644
--- a/arch/arm64/include/asm/arch_timer.h
+++ b/arch/arm64/include/asm/arch_timer.h
@@ -31,39 +31,27 @@
#if IS_ENABLED(CONFIG_FSL_ERRATUM_A008585)
extern struct static_key_false arch_timer_read_ool_enabled;
-#define needs_fsl_a008585_workaround() \
+#define needs_unstable_timer_counter_workaround() \
static_branch_unlikely(&arch_timer_read_ool_enabled)
#else
-#define needs_fsl_a008585_workaround() false
+#define needs_unstable_timer_counter_workaround() false
#endif
-u32 __fsl_a008585_read_cntp_tval_el0(void);
-u32 __fsl_a008585_read_cntv_tval_el0(void);
-u64 __fsl_a008585_read_cntvct_el0(void);
-/*
- * The number of retries is an arbitrary value well beyond the highest number
- * of iterations the loop has been observed to take.
- */
-#define __fsl_a008585_read_reg(reg) ({ \
- u64 _old, _new; \
- int _retries = 200; \
- \
- do { \
- _old = read_sysreg(reg); \
- _new = read_sysreg(reg); \
- _retries--; \
- } while (unlikely(_old != _new) && _retries); \
- \
- WARN_ON_ONCE(!_retries); \
- _new; \
-})
+struct arch_timer_erratum_workaround {
+ int erratum; /* Indicate the Erratum ID */
+ u32 (*read_cntp_tval_el0)(void);
+ u32 (*read_cntv_tval_el0)(void);
+ u64 (*read_cntvct_el0)(void);
+};
+
+extern struct arch_timer_erratum_workaround *timer_unstable_counter_workaround;
#define arch_timer_reg_read_stable(reg) \
({ \
u64 _val; \
- if (needs_fsl_a008585_workaround()) \
- _val = __fsl_a008585_read_##reg(); \
+ if (needs_unstable_timer_counter_workaround()) \
+ _val = timer_unstable_counter_workaround->read_##reg();\
else \
_val = read_sysreg(reg); \
_val; \
diff --git a/drivers/clocksource/arm_arch_timer.c b/drivers/clocksource/arm_arch_timer.c
index 73c487d..696386f 100644
--- a/drivers/clocksource/arm_arch_timer.c
+++ b/drivers/clocksource/arm_arch_timer.c
@@ -95,40 +95,53 @@ early_param("clocksource.arm_arch_timer.evtstrm", early_evtstrm_cfg);
*/
#ifdef CONFIG_FSL_ERRATUM_A008585
-DEFINE_STATIC_KEY_FALSE(arch_timer_read_ool_enabled);
-EXPORT_SYMBOL_GPL(arch_timer_read_ool_enabled);
-
-static int fsl_a008585_enable = -1;
+struct arch_timer_erratum_workaround *timer_unstable_counter_workaround = NULL;
+EXPORT_SYMBOL_GPL(timer_unstable_counter_workaround);
-static int __init early_fsl_a008585_cfg(char *buf)
-{
- int ret;
- bool val;
+#define FSL_A008585 0x0001
- ret = strtobool(buf, &val);
- if (ret)
- return ret;
-
- fsl_a008585_enable = val;
- return 0;
-}
-early_param("clocksource.arm_arch_timer.fsl-a008585", early_fsl_a008585_cfg);
+DEFINE_STATIC_KEY_FALSE(arch_timer_read_ool_enabled);
+EXPORT_SYMBOL_GPL(arch_timer_read_ool_enabled);
-u32 __fsl_a008585_read_cntp_tval_el0(void)
+/*
+ * The number of retries is an arbitrary value well beyond the highest number
+ * of iterations the loop has been observed to take.
+ */
+#define __fsl_a008585_read_reg(reg) ({ \
+ u64 _old, _new; \
+ int _retries = 200; \
+ \
+ do { \
+ _old = read_sysreg(reg); \
+ _new = read_sysreg(reg); \
+ _retries--; \
+ } while (unlikely(_old != _new) && _retries); \
+ \
+ WARN_ON_ONCE(!_retries); \
+ _new; \
+})
+
+static u32 fsl_a008585_read_cntp_tval_el0(void)
{
return __fsl_a008585_read_reg(cntp_tval_el0);
}
-u32 __fsl_a008585_read_cntv_tval_el0(void)
+static u32 fsl_a008585_read_cntv_tval_el0(void)
{
return __fsl_a008585_read_reg(cntv_tval_el0);
}
-u64 __fsl_a008585_read_cntvct_el0(void)
+static u64 fsl_a008585_read_cntvct_el0(void)
{
return __fsl_a008585_read_reg(cntvct_el0);
}
-EXPORT_SYMBOL(__fsl_a008585_read_cntvct_el0);
+
+static struct arch_timer_erratum_workaround arch_timer_fsl_a008585 = {
+ .erratum = FSL_A008585,
+ .read_cntp_tval_el0 = fsl_a008585_read_cntp_tval_el0,
+ .read_cntv_tval_el0 = fsl_a008585_read_cntv_tval_el0,
+ .read_cntvct_el0 = fsl_a008585_read_cntvct_el0,
+};
#endif /* CONFIG_FSL_ERRATUM_A008585 */
static __always_inline
@@ -281,7 +294,7 @@ static __always_inline void set_next_event(const int access, unsigned long evt,
}
#ifdef CONFIG_FSL_ERRATUM_A008585
-static __always_inline void fsl_a008585_set_next_event(const int access,
+static __always_inline void erratum_set_next_event_generic(const int access,
unsigned long evt, struct clock_event_device *clk)
{
unsigned long ctrl;
@@ -299,17 +312,17 @@ static __always_inline void fsl_a008585_set_next_event(const int access,
arch_timer_reg_write(access, ARCH_TIMER_REG_CTRL, ctrl, clk);
}
-static int fsl_a008585_set_next_event_virt(unsigned long evt,
+static int erratum_set_next_event_virt(unsigned long evt,
struct clock_event_device *clk)
{
- fsl_a008585_set_next_event(ARCH_TIMER_VIRT_ACCESS, evt, clk);
+ erratum_set_next_event_generic(ARCH_TIMER_VIRT_ACCESS, evt, clk);
return 0;
}
-static int fsl_a008585_set_next_event_phys(unsigned long evt,
+static int erratum_set_next_event_phys(unsigned long evt,
struct clock_event_device *clk)
{
- fsl_a008585_set_next_event(ARCH_TIMER_PHYS_ACCESS, evt, clk);
+ erratum_set_next_event_generic(ARCH_TIMER_PHYS_ACCESS, evt, clk);
return 0;
}
#endif /* CONFIG_FSL_ERRATUM_A008585 */
@@ -342,16 +355,16 @@ static int arch_timer_set_next_event_phys_mem(unsigned long evt,
return 0;
}
-static void fsl_a008585_set_sne(struct clock_event_device *clk)
+static void erratum_workaround_set_sne(struct clock_event_device *clk)
{
#ifdef CONFIG_FSL_ERRATUM_A008585
if (!static_branch_unlikely(&arch_timer_read_ool_enabled))
return;
if (arch_timer_uses_ppi == VIRT_PPI)
- clk->set_next_event = fsl_a008585_set_next_event_virt;
+ clk->set_next_event = erratum_set_next_event_virt;
else
- clk->set_next_event = fsl_a008585_set_next_event_phys;
+ clk->set_next_event = erratum_set_next_event_phys;
#endif
}
@@ -384,7 +397,7 @@ static void __arch_timer_setup(unsigned type,
BUG();
}
- fsl_a008585_set_sne(clk);
+ erratum_workaround_set_sne(clk);
} else {
clk->features |= CLOCK_EVT_FEAT_DYNIRQ;
clk->name = "arch_mem_timer";
@@ -891,9 +904,10 @@ static int __init arch_timer_of_init(struct device_node *np)
arch_timer_c3stop = !of_property_read_bool(np, "always-on");
#ifdef CONFIG_FSL_ERRATUM_A008585
- if (fsl_a008585_enable < 0)
- fsl_a008585_enable = of_property_read_bool(np, "fsl,erratum-a008585");
- if (fsl_a008585_enable) {
+ if (!timer_unstable_counter_workaround && of_property_read_bool(np, "fsl,erratum-a008585"))
+ timer_unstable_counter_workaround = &arch_timer_fsl_a008585;
+
+ if (timer_unstable_counter_workaround) {
static_branch_enable(&arch_timer_read_ool_enabled);
pr_info("Enabling workaround for FSL erratum A-008585\n");
}
--
1.9.0
^ permalink raw reply related
* [PATCH v4 1/6] arm64: arch_timer: Add device tree binding for hisilicon-161601 erratum
From: Ding Tianhong @ 2016-11-15 12:16 UTC (permalink / raw)
To: catalin.marinas, will.deacon, marc.zyngier, mark.rutland, oss,
devicetree, shawnguo, stuart.yoder, linux-arm-kernel, linuxarm,
hanjun.guo
Cc: Ding Tianhong
This erratum describes a bug in logic outside the core, so MIDR can't be
used to identify its presence, and reading an SoC-specific revision
register from common arch timer code would be awkward. So, describe it
in the device tree.
v2: Use the new erratum name and update the description.
Signed-off-by: Ding Tianhong <dingtianhong@huawei.com>
Acked-by: Rob Herring <robh@kernel.org>
---
Documentation/devicetree/bindings/arm/arch_timer.txt | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/Documentation/devicetree/bindings/arm/arch_timer.txt b/Documentation/devicetree/bindings/arm/arch_timer.txt
index ef5fbe9..c27b2c4 100644
--- a/Documentation/devicetree/bindings/arm/arch_timer.txt
+++ b/Documentation/devicetree/bindings/arm/arch_timer.txt
@@ -31,6 +31,14 @@ to deliver its interrupts via SPIs.
This also affects writes to the tval register, due to the implicit
counter read.
+- hisilicon,erratum-161601 : A boolean property. Indicates the presence of
+ erratum 161601, which says that reading the counter is unreliable unless
+ reading twice on the register and the value of the second read is larger
+ than the first by less than 32. If the verification is unsuccessful, then
+ discard the value of this read and repeat this procedure until the verification
+ is successful. This also affects writes to the tval register, due to the
+ implicit counter read.
+
** Optional properties:
- arm,cpu-registers-not-fw-configured : Firmware does not initialize
--
1.9.0
^ permalink raw reply related
* Re: [PATCH v4 2/2] Add support for OV5647 sensor
From: Pavel Machek @ 2016-11-15 12:10 UTC (permalink / raw)
To: Ramiro Oliveira
Cc: mchehab-DgEjT+Ai2ygdnm+yROfE0A,
linux-kernel-u79uwXL29TY76Z2rM5mHXA,
linux-media-u79uwXL29TY76Z2rM5mHXA,
robh+dt-DgEjT+Ai2ygdnm+yROfE0A, devicetree-u79uwXL29TY76Z2rM5mHXA,
davem-fT/PcQaiUtIeIZ0/mPfg9Q,
gregkh-hQyY1W1yCW8ekmWlsbkhG0B+6BGkLq7r,
geert+renesas-gXvu3+zWzMSzQB+pC5nmwQ,
akpm-de/tnXTf+JLsfHDXvbKv3WD2FQJk+8+b,
linux-0h96xk9xTtrk1uMJSBkQmQ, hverkuil-qWit8jRvyhVmR6Xm/wNWPw,
dheitmueller-eb9eJ82Ua7k9XoPSrs7Ehg,
slongerbeam-Re5JQEeQqe8AvxtiuMwx3w, lars-Qo5EllUWu/uELgA04lAiVw,
robert.jarzmik-GANU6spQydw, pali.rohar-Re5JQEeQqe8AvxtiuMwx3w,
sakari.ailus-VuQAYsv1563Yd54FQh9/CA, mark.rutland-5wv7dgnIgG8,
CARLOS.PALMINHA-HKixBCOQz3hWk0Htik3J/w
In-Reply-To: <36447f1f102f648057eb9038a693941794a6c344.1479129004.git.roliveir-HKixBCOQz3hWk0Htik3J/w@public.gmane.org>
[-- Attachment #1: Type: text/plain, Size: 3423 bytes --]
Hi!
> Add support for OV5647 sensor.
>
> +static int ov5647_write(struct v4l2_subdev *sd, u16 reg, u8 val)
> +{
> + int ret;
> + unsigned char data[3] = { reg >> 8, reg & 0xff, val};
> + struct i2c_client *client = v4l2_get_subdevdata(sd);
> +
> + ret = i2c_master_send(client, data, 3);
> + if (ret != 3) {
> + dev_dbg(&client->dev, "%s: i2c write error, reg: %x\n",
> + __func__, reg);
> + return ret < 0 ? ret : -EIO;
> + }
> + return 0;
> +}
Sorry, this is wrong. It should something <0 any time error is detected.
> +static int ov5647_write_array(struct v4l2_subdev *sd,
> + struct regval_list *regs, int array_size)
> +{
> + int i = 0;
> + int ret = 0;
> +
> + if (!regs)
> + return -EINVAL;
> +
> + while (i < array_size) {
> + ret = ov5647_write(sd, regs->addr, regs->data);
> + if (ret < 0)
> + return ret;
> + i++;
> + regs++;
> + }
> + return 0;
> +}
For example this expects <0 on error.
> +static int set_sw_standby(struct v4l2_subdev *sd, bool standby)
> +{
> + int ret;
> + unsigned char rdval;
> +
> + ret = ov5647_read(sd, 0x0100, &rdval);
> + if (ret != 0)
> + return ret;
> +
> + if (standby)
> + ret = ov5647_write(sd, 0x0100, rdval&0xfe);
> + else
> + ret = ov5647_write(sd, 0x0100, rdval|0x01);
> +
> + return ret;
if (standby)
rdval &= 0xfe;
else
rdval |= 0x01;
ret = ov5647_write(sd, 0x0100, rdval);
?
> +/**
> + * @short Store information about the video data format.
> + */
> +static struct sensor_format_struct {
> + __u8 *desc;
> + u32 mbus_code;
u8 is suitable here.
> + ov5647_read(sd, 0x0100, &resetval);
> + if (!resetval&0x01) {
add ()s here.
> +static int sensor_power(struct v4l2_subdev *sd, int on)
> +{
> + int ret;
> + struct ov5647 *ov5647 = to_state(sd);
> + struct i2c_client *client = v4l2_get_subdevdata(sd);
> +
> + ret = 0;
> + mutex_lock(&ov5647->lock);
> +
> + if (on) {
> + dev_dbg(&client->dev, "OV5647 power on!\n");
> +
> + ret = ov5647_write_array(sd, sensor_oe_enable_regs,
> + ARRAY_SIZE(sensor_oe_enable_regs));
> +
> + ret = __sensor_init(sd);
> +
> + if (ret < 0)
> + dev_err(&client->dev,
> + "Camera not available! Check Power!\n");
> + } else {
> + dev_dbg(&client->dev, "OV5647 power off!\n");
> +
> + dev_dbg(&client->dev, "disable oe\n");
> + ret = ov5647_write_array(sd, sensor_oe_disable_regs,
> + ARRAY_SIZE(sensor_oe_disable_regs));
> +
> + if (ret < 0)
> + dev_dbg(&client->dev, "disable oe failed!\n");
> +
> + ret = set_sw_standby(sd, true);
> +
> + if (ret < 0)
> + dev_dbg(&client->dev, "soft stby failed!\n");
dev_err for errors? Little less "!"s in the output?
> +static int sensor_get_register(struct v4l2_subdev *sd,
> + struct v4l2_dbg_register *reg)
> +{
> + unsigned char val = 0;
> + int ret;
> +
> + ret = ov5647_read(sd, reg->reg & 0xff, &val);
> + reg->val = val;
> + reg->size = 1;
> + return ret;
> +}
Filling reg->* when read failed is strange.
> +static int sensor_set_register(struct v4l2_subdev *sd,
> + const struct v4l2_dbg_register *reg)
> +{
> + ov5647_write(sd, reg->reg & 0xff, reg->val & 0xff);
> + return 0;
> +}
error handling?
Best regards,
Pavel
--
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html
[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 181 bytes --]
^ permalink raw reply
* [PATCH v3 2/2] ARM: dts: apq8064: add support to pm8821
From: Srinivas Kandagatla @ 2016-11-15 12:01 UTC (permalink / raw)
To: Lee Jones
Cc: bjorn.andersson, Rob Herring, Andy Gross, devicetree,
linux-kernel, linux-arm-msm, linux-soc, linux-arm-kernel,
Srinivas Kandagatla
In-Reply-To: <1479211312-1431-1-git-send-email-srinivas.kandagatla@linaro.org>
Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
Acked-by: Bjorn Andersson <bjorn.andersson@linaro.org>
---
arch/arm/boot/dts/qcom-apq8064.dtsi | 27 +++++++++++++++++++++++++++
1 file changed, 27 insertions(+)
diff --git a/arch/arm/boot/dts/qcom-apq8064.dtsi b/arch/arm/boot/dts/qcom-apq8064.dtsi
index 268bd47..f725e31 100644
--- a/arch/arm/boot/dts/qcom-apq8064.dtsi
+++ b/arch/arm/boot/dts/qcom-apq8064.dtsi
@@ -627,6 +627,33 @@
clock-names = "core";
};
+ ssbi@c00000 {
+ compatible = "qcom,ssbi";
+ reg = <0x00c00000 0x1000>;
+ qcom,controller-type = "pmic-arbiter";
+
+ pm8821: pmic@1 {
+ compatible = "qcom,pm8821";
+ interrupt-parent = <&tlmm_pinmux>;
+ interrupts = <76 IRQ_TYPE_LEVEL_LOW>;
+ #interrupt-cells = <2>;
+ interrupt-controller;
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ pm8821_mpps: mpps@50 {
+ compatible = "qcom,pm8821-mpp", "qcom,ssbi-mpp";
+ reg = <0x50>;
+ interrupts = <24 IRQ_TYPE_NONE>,
+ <25 IRQ_TYPE_NONE>,
+ <26 IRQ_TYPE_NONE>,
+ <27 IRQ_TYPE_NONE>;
+ gpio-controller;
+ #gpio-cells = <2>;
+ };
+ };
+ };
+
qcom,ssbi@500000 {
compatible = "qcom,ssbi";
reg = <0x00500000 0x1000>;
--
2.10.1
^ permalink raw reply related
* [PATCH v3 1/2] mfd: pm8xxx: add support to pm8821
From: Srinivas Kandagatla @ 2016-11-15 12:01 UTC (permalink / raw)
To: Lee Jones
Cc: bjorn.andersson-QSEj5FYQhm4dnm+yROfE0A, Rob Herring, Andy Gross,
devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-kernel-u79uwXL29TY76Z2rM5mHXA,
linux-arm-msm-u79uwXL29TY76Z2rM5mHXA,
linux-soc-u79uwXL29TY76Z2rM5mHXA,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
Srinivas Kandagatla
This patch adds support to PM8821 PMIC and interrupt support.
PM8821 is companion device that supplements primary PMIC PM8921 IC.
Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>
Acked-by: Rob Herring <robh-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>
---
Changes since v2:
- removed few empty lines spotted by Bjorn
- rephrased some debug prints suggested by Bjorn
.../devicetree/bindings/mfd/qcom-pm8xxx.txt | 1 +
drivers/mfd/qcom-pm8xxx.c | 231 ++++++++++++++++++++-
2 files changed, 222 insertions(+), 10 deletions(-)
diff --git a/Documentation/devicetree/bindings/mfd/qcom-pm8xxx.txt b/Documentation/devicetree/bindings/mfd/qcom-pm8xxx.txt
index 37a088f..9e5eba4 100644
--- a/Documentation/devicetree/bindings/mfd/qcom-pm8xxx.txt
+++ b/Documentation/devicetree/bindings/mfd/qcom-pm8xxx.txt
@@ -10,6 +10,7 @@ voltages and other various functionality to Qualcomm SoCs.
Value type: <string>
Definition: must be one of:
"qcom,pm8058"
+ "qcom,pm8821"
"qcom,pm8921"
- #address-cells:
diff --git a/drivers/mfd/qcom-pm8xxx.c b/drivers/mfd/qcom-pm8xxx.c
index 7f9620e..f08758f 100644
--- a/drivers/mfd/qcom-pm8xxx.c
+++ b/drivers/mfd/qcom-pm8xxx.c
@@ -39,6 +39,20 @@
#define SSBI_REG_ADDR_IRQ_CONFIG (SSBI_REG_ADDR_IRQ_BASE + 7)
#define SSBI_REG_ADDR_IRQ_RT_STATUS (SSBI_REG_ADDR_IRQ_BASE + 8)
+#define PM8821_SSBI_REG_ADDR_IRQ_BASE 0x100
+#define PM8821_SSBI_REG_ADDR_IRQ_MASTER0 (PM8821_SSBI_REG_ADDR_IRQ_BASE + 0x30)
+#define PM8821_SSBI_REG_ADDR_IRQ_MASTER1 (PM8821_SSBI_REG_ADDR_IRQ_BASE + 0xb0)
+#define PM8821_SSBI_REG(m, b, offset) \
+ ((m == 0) ? \
+ (PM8821_SSBI_REG_ADDR_IRQ_MASTER0 + b + offset) : \
+ (PM8821_SSBI_REG_ADDR_IRQ_MASTER1 + b + offset))
+#define PM8821_SSBI_ADDR_IRQ_ROOT(m, b) PM8821_SSBI_REG(m, b, 0x0)
+#define PM8821_SSBI_ADDR_IRQ_CLEAR(m, b) PM8821_SSBI_REG(m, b, 0x01)
+#define PM8821_SSBI_ADDR_IRQ_MASK(m, b) PM8821_SSBI_REG(m, b, 0x08)
+#define PM8821_SSBI_ADDR_IRQ_RT_STATUS(m, b) PM8821_SSBI_REG(m, b, 0x0f)
+
+#define PM8821_BLOCKS_PER_MASTER 7
+
#define PM_IRQF_LVL_SEL 0x01 /* level select */
#define PM_IRQF_MASK_FE 0x02 /* mask falling edge */
#define PM_IRQF_MASK_RE 0x04 /* mask rising edge */
@@ -54,6 +68,7 @@
#define REG_HWREV_2 0x0E8 /* PMIC4 revision 2 */
#define PM8XXX_NR_IRQS 256
+#define PM8821_NR_IRQS 112
struct pm_irq_chip {
struct regmap *regmap;
@@ -65,6 +80,12 @@ struct pm_irq_chip {
u8 config[0];
};
+struct pm_irq_data {
+ int num_irqs;
+ const struct irq_domain_ops *irq_domain_ops;
+ void (*irq_handler)(struct irq_desc *desc);
+};
+
static int pm8xxx_read_block_irq(struct pm_irq_chip *chip, unsigned int bp,
unsigned int *ip)
{
@@ -182,6 +203,78 @@ static void pm8xxx_irq_handler(struct irq_desc *desc)
chained_irq_exit(irq_chip, desc);
}
+static void pm8821_irq_block_handler(struct pm_irq_chip *chip,
+ int master, int block)
+{
+ int pmirq, irq, i, ret;
+ unsigned int bits;
+
+ ret = regmap_read(chip->regmap,
+ PM8821_SSBI_ADDR_IRQ_ROOT(master, block), &bits);
+ if (ret) {
+ pr_err("Reading block %d failed ret=%d", block, ret);
+ return;
+ }
+
+ /* Convert block offset to global block number */
+ block += (master * PM8821_BLOCKS_PER_MASTER) - 1;
+
+ /* Check IRQ bits */
+ for (i = 0; i < 8; i++) {
+ if (bits & BIT(i)) {
+ pmirq = block * 8 + i;
+ irq = irq_find_mapping(chip->irqdomain, pmirq);
+ generic_handle_irq(irq);
+ }
+ }
+}
+
+static inline void pm8821_irq_master_handler(struct pm_irq_chip *chip,
+ int master, u8 master_val)
+{
+ int block;
+
+ for (block = 1; block < 8; block++)
+ if (master_val & BIT(block))
+ pm8821_irq_block_handler(chip, master, block);
+}
+
+static void pm8821_irq_handler(struct irq_desc *desc)
+{
+ struct pm_irq_chip *chip = irq_desc_get_handler_data(desc);
+ struct irq_chip *irq_chip = irq_desc_get_chip(desc);
+ unsigned int master;
+ int ret;
+
+ chained_irq_enter(irq_chip, desc);
+ ret = regmap_read(chip->regmap,
+ PM8821_SSBI_REG_ADDR_IRQ_MASTER0, &master);
+ if (ret) {
+ pr_err("Failed to read master 0 ret=%d\n", ret);
+ goto done;
+ }
+
+ /* bits 1 through 7 marks the first 7 blocks in master 0 */
+ if (master & GENMASK(7, 1))
+ pm8821_irq_master_handler(chip, 0, master);
+
+ /* bit 0 marks if master 1 contains any bits */
+ if (!(master & BIT(0)))
+ goto done;
+
+ ret = regmap_read(chip->regmap,
+ PM8821_SSBI_REG_ADDR_IRQ_MASTER1, &master);
+ if (ret) {
+ pr_err("Failed to read master 1 ret=%d\n", ret);
+ goto done;
+ }
+
+ pm8821_irq_master_handler(chip, 1, master);
+
+done:
+ chained_irq_exit(irq_chip, desc);
+}
+
static void pm8xxx_irq_mask_ack(struct irq_data *d)
{
struct pm_irq_chip *chip = irq_data_get_irq_chip_data(d);
@@ -299,6 +392,104 @@ static const struct irq_domain_ops pm8xxx_irq_domain_ops = {
.map = pm8xxx_irq_domain_map,
};
+static void pm8821_irq_mask_ack(struct irq_data *d)
+{
+ struct pm_irq_chip *chip = irq_data_get_irq_chip_data(d);
+ unsigned int pmirq = irqd_to_hwirq(d);
+ u8 block, master;
+ int irq_bit, rc;
+
+ block = pmirq / 8;
+ master = block / PM8821_BLOCKS_PER_MASTER;
+ irq_bit = pmirq % 8;
+ block %= PM8821_BLOCKS_PER_MASTER;
+
+ rc = regmap_update_bits(chip->regmap,
+ PM8821_SSBI_ADDR_IRQ_MASK(master, block),
+ BIT(irq_bit), BIT(irq_bit));
+ if (rc) {
+ pr_err("Failed to mask IRQ:%d rc=%d\n", pmirq, rc);
+ return;
+ }
+
+ rc = regmap_update_bits(chip->regmap,
+ PM8821_SSBI_ADDR_IRQ_CLEAR(master, block),
+ BIT(irq_bit), BIT(irq_bit));
+ if (rc)
+ pr_err("Failed to CLEAR IRQ:%d rc=%d\n", pmirq, rc);
+}
+
+static void pm8821_irq_unmask(struct irq_data *d)
+{
+ struct pm_irq_chip *chip = irq_data_get_irq_chip_data(d);
+ unsigned int pmirq = irqd_to_hwirq(d);
+ int irq_bit, rc;
+ u8 block, master;
+
+ block = pmirq / 8;
+ master = block / PM8821_BLOCKS_PER_MASTER;
+ irq_bit = pmirq % 8;
+ block %= PM8821_BLOCKS_PER_MASTER;
+
+ rc = regmap_update_bits(chip->regmap,
+ PM8821_SSBI_ADDR_IRQ_MASK(master, block),
+ BIT(irq_bit), ~BIT(irq_bit));
+ if (rc)
+ pr_err("Failed to read/write unmask IRQ:%d rc=%d\n", pmirq, rc);
+
+}
+
+static int pm8821_irq_get_irqchip_state(struct irq_data *d,
+ enum irqchip_irq_state which,
+ bool *state)
+{
+ struct pm_irq_chip *chip = irq_data_get_irq_chip_data(d);
+ int rc, pmirq = irqd_to_hwirq(d);
+ u8 block, irq_bit, master;
+ unsigned int bits;
+
+ block = pmirq / 8;
+ master = block / PM8821_BLOCKS_PER_MASTER;
+ irq_bit = pmirq % 8;
+ block %= PM8821_BLOCKS_PER_MASTER;
+
+ rc = regmap_read(chip->regmap,
+ PM8821_SSBI_ADDR_IRQ_RT_STATUS(master, block), &bits);
+ if (rc) {
+ pr_err("Reading Status of IRQ %d failed rc=%d\n", pmirq, rc);
+ return rc;
+ }
+
+ *state = !!(bits & BIT(irq_bit));
+
+ return rc;
+}
+
+static struct irq_chip pm8821_irq_chip = {
+ .name = "pm8821",
+ .irq_mask_ack = pm8821_irq_mask_ack,
+ .irq_unmask = pm8821_irq_unmask,
+ .irq_get_irqchip_state = pm8821_irq_get_irqchip_state,
+ .flags = IRQCHIP_MASK_ON_SUSPEND | IRQCHIP_SKIP_SET_WAKE,
+};
+
+static int pm8821_irq_domain_map(struct irq_domain *d, unsigned int irq,
+ irq_hw_number_t hwirq)
+{
+ struct pm_irq_chip *chip = d->host_data;
+
+ irq_set_chip_and_handler(irq, &pm8821_irq_chip, handle_level_irq);
+ irq_set_chip_data(irq, chip);
+ irq_set_noprobe(irq);
+
+ return 0;
+}
+
+static const struct irq_domain_ops pm8821_irq_domain_ops = {
+ .xlate = irq_domain_xlate_twocell,
+ .map = pm8821_irq_domain_map,
+};
+
static const struct regmap_config ssbi_regmap_config = {
.reg_bits = 16,
.val_bits = 8,
@@ -308,22 +499,41 @@ static const struct regmap_config ssbi_regmap_config = {
.reg_write = ssbi_reg_write
};
+static const struct pm_irq_data pm8xxx_data = {
+ .num_irqs = PM8XXX_NR_IRQS,
+ .irq_domain_ops = &pm8xxx_irq_domain_ops,
+ .irq_handler = pm8xxx_irq_handler,
+};
+
+static const struct pm_irq_data pm8821_data = {
+ .num_irqs = PM8821_NR_IRQS,
+ .irq_domain_ops = &pm8821_irq_domain_ops,
+ .irq_handler = pm8821_irq_handler,
+};
+
static const struct of_device_id pm8xxx_id_table[] = {
- { .compatible = "qcom,pm8018", },
- { .compatible = "qcom,pm8058", },
- { .compatible = "qcom,pm8921", },
+ { .compatible = "qcom,pm8018", .data = &pm8xxx_data},
+ { .compatible = "qcom,pm8058", .data = &pm8xxx_data},
+ { .compatible = "qcom,pm8821", .data = &pm8821_data},
+ { .compatible = "qcom,pm8921", .data = &pm8xxx_data},
{ }
};
MODULE_DEVICE_TABLE(of, pm8xxx_id_table);
static int pm8xxx_probe(struct platform_device *pdev)
{
+ const struct pm_irq_data *data;
struct regmap *regmap;
int irq, rc;
unsigned int val;
u32 rev;
struct pm_irq_chip *chip;
- unsigned int nirqs = PM8XXX_NR_IRQS;
+
+ data = of_device_get_match_data(&pdev->dev);
+ if (!data) {
+ dev_err(&pdev->dev, "No matching driver data found\n");
+ return -EINVAL;
+ }
irq = platform_get_irq(pdev, 0);
if (irq < 0)
@@ -354,25 +564,26 @@ static int pm8xxx_probe(struct platform_device *pdev)
rev |= val << BITS_PER_BYTE;
chip = devm_kzalloc(&pdev->dev, sizeof(*chip) +
- sizeof(chip->config[0]) * nirqs,
- GFP_KERNEL);
+ sizeof(chip->config[0]) * data->num_irqs,
+ GFP_KERNEL);
if (!chip)
return -ENOMEM;
platform_set_drvdata(pdev, chip);
chip->regmap = regmap;
- chip->num_irqs = nirqs;
+ chip->num_irqs = data->num_irqs;
chip->num_blocks = DIV_ROUND_UP(chip->num_irqs, 8);
chip->num_masters = DIV_ROUND_UP(chip->num_blocks, 8);
spin_lock_init(&chip->pm_irq_lock);
- chip->irqdomain = irq_domain_add_linear(pdev->dev.of_node, nirqs,
- &pm8xxx_irq_domain_ops,
+ chip->irqdomain = irq_domain_add_linear(pdev->dev.of_node,
+ data->num_irqs,
+ data->irq_domain_ops,
chip);
if (!chip->irqdomain)
return -ENODEV;
- irq_set_chained_handler_and_data(irq, pm8xxx_irq_handler, chip);
+ irq_set_chained_handler_and_data(irq, data->irq_handler, chip);
irq_set_irq_wake(irq, 1);
rc = of_platform_populate(pdev->dev.of_node, NULL, NULL, &pdev->dev);
--
2.10.1
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply related
* Re: [PATCH v2 08/10] ARM: dts: add basic support for Rockchip RK1108 SOC
From: Heiko Stuebner @ 2016-11-15 11:45 UTC (permalink / raw)
To: Andy Yan
Cc: linux-rockchip-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
devicetree-u79uwXL29TY76Z2rM5mHXA, mark.rutland-5wv7dgnIgG8,
robh+dt-DgEjT+Ai2ygdnm+yROfE0A, linux-I+IVW8TIWO2tmTQ+vhA3Yw,
linux-kernel-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <1479125688-24528-1-git-send-email-andy.yan-TNX95d0MmH7DzftRWevZcw@public.gmane.org>
Am Montag, 14. November 2016, 20:14:48 CET schrieb Andy Yan:
> RK1108 is embedded with an ARM Cortex-A7 single core and a DSP core.
> It is designed for varies application scenario such as car DVR, sports
> DV, secure camera and UAV camera.
>
> This patch add basic support for it with DMAC / UART / CRU / pinctrl / MMC
> enabled.
>
> Signed-off-by: Andy Yan <andy.yan-TNX95d0MmH7DzftRWevZcw@public.gmane.org>
> Tested-by: Jacob Chen <jacob2.chen-TNX95d0MmH7DzftRWevZcw@public.gmane.org>
>
> ---
>
> Changes in v2:
> - fix timer and gic dt description
> - ordering devices by register address
>
> arch/arm/boot/dts/rk1108.dtsi | 428
> ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 428
> insertions(+)
> create mode 100644 arch/arm/boot/dts/rk1108.dtsi
>
> diff --git a/arch/arm/boot/dts/rk1108.dtsi b/arch/arm/boot/dts/rk1108.dtsi
> new file mode 100644
> index 0000000..636c294
> --- /dev/null
> +++ b/arch/arm/boot/dts/rk1108.dtsi
> @@ -0,0 +1,428 @@
> +/*
> + * This file is dual-licensed: you can use it either under the terms
> + * of the GPL or the X11 license, at your option. Note that this dual
> + * licensing only applies to this file, and not this project as a
> + * whole.
> + *
> + * a) This file is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU General Public License as
> + * published by the Free Software Foundation; either version 2 of the
> + * License, or (at your option) any later version.
> + *
> + * This file 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.
> + *
> + * Or, alternatively,
> + *
> + * b) Permission is hereby granted, free of charge, to any person
> + * obtaining a copy of this software and associated documentation
> + * files (the "Software"), to deal in the Software without
> + * restriction, including without limitation the rights to use,
> + * copy, modify, merge, publish, distribute, sublicense, and/or
> + * sell copies of the Software, and to permit persons to whom the
> + * Software is furnished to do so, subject to the following
> + * conditions:
> + *
> + * The above copyright notice and this permission notice shall be
> + * included in all copies or substantial portions of the Software.
> + *
> + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
> + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
> + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
> + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
> + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
> + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
> + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
> + * OTHER DEALINGS IN THE SOFTWARE.
> + */
> +
> +#include <dt-bindings/gpio/gpio.h>
> +#include <dt-bindings/interrupt-controller/irq.h>
> +#include <dt-bindings/interrupt-controller/arm-gic.h>
> +#include <dt-bindings/clock/rk1108-cru.h>
> +#include <dt-bindings/pinctrl/rockchip.h>
> +/ {
> + #address-cells = <1>;
> + #size-cells = <1>;
> +
> + compatible = "rockchip,rk1108";
> +
> + interrupt-parent = <&gic>;
> +
> + aliases {
> + serial0 = &uart0;
> + serial1 = &uart1;
> + serial2 = &uart2;
> + };
> +
> + cpus {
> + #address-cells = <1>;
> + #size-cells = <0>;
> +
> + cpu0: cpu@f00 {
> + device_type = "cpu";
> + compatible = "arm,cortex-a7";
> + reg = <0xf00>;
> + };
> + };
> +
> + arm-pmu {
> + compatible = "arm,cortex-a7-pmu";
> + interrupts = <GIC_SPI 67 IRQ_TYPE_LEVEL_HIGH>;
> + };
> +
> +
unnecessary empty line (only 1 please)
otherwise looks fine now, just needs to wait for fixed clock ids now.
Heiko
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* Re: [PATCH V2 1/2] pinctrl: tegra: Add DT binding for io pads control
From: Laxman Dewangan @ 2016-11-15 11:45 UTC (permalink / raw)
To: Rob Herring
Cc: linus.walleij-QSEj5FYQhm4dnm+yROfE0A, mark.rutland-5wv7dgnIgG8,
swarren-3lzwWm7+Weoh9ZMKESR00Q,
thierry.reding-Re5JQEeQqe8AvxtiuMwx3w,
gnurou-Re5JQEeQqe8AvxtiuMwx3w,
yamada.masahiro-uWyLwvC0a2jby3iVrkZq2A,
jonathanh-DDmLM1+adcrQT0dZR+AlfA,
linux-gpio-u79uwXL29TY76Z2rM5mHXA,
devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-tegra-u79uwXL29TY76Z2rM5mHXA,
linux-kernel-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <20161114193432.wi5ljtp3t46wt2c7@rob-hp-laptop>
On Tuesday 15 November 2016 01:04 AM, Rob Herring wrote:
> On Wed, Nov 09, 2016 at 06:36:21PM +0530, Laxman Dewangan wrote:
> +
> + audio {
> + pins = "audio", "dmic", "sdmmc3";
> What's the purpose of grouping these?
Just to show that multiple IO pads can be provided here as this is from
pinctrl dt binding.
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* Re: [PATCH 1/2] regulator: pwm: DT: Add ramp delay for exponential voltage transition
From: Laxman Dewangan @ 2016-11-15 11:42 UTC (permalink / raw)
To: Rob Herring
Cc: broonie-DgEjT+Ai2ygdnm+yROfE0A, mark.rutland-5wv7dgnIgG8,
linux-kernel-u79uwXL29TY76Z2rM5mHXA,
devicetree-u79uwXL29TY76Z2rM5mHXA, Douglas Anderson,
Aleksandr Frid
In-Reply-To: <20161114154807.j4oo4veslhknauxy@rob-hp-laptop>
On Monday 14 November 2016 09:18 PM, Rob Herring wrote:
> On Fri, Nov 04, 2016 at 11:07:54PM +0530, Laxman Dewangan wrote:
>> Some PWM regulator has the exponential transition in voltage change as
>> opposite to fixed slew-rate linear transition on other regulators.
>> For such PWM regulators, add the property for providing the delay
>> from DT node.
>>
>> Add DT binding details of the new property
>> "pwm-regulator-voltage-ramp-time-us" added for providing voltage
>> transition delay.
>>
>> Signed-off-by: Laxman Dewangan <ldewangan-DDmLM1+adcrQT0dZR+AlfA@public.gmane.org>
>> CC: Douglas Anderson <dianders-F7+t8E8rja9g9hUCZPvPmw@public.gmane.org>
>> CC: Aleksandr Frid <afrid-DDmLM1+adcrQT0dZR+AlfA@public.gmane.org>
>>
>> ---
>> This patch is continuation of discussion on patch
>> regulator: pwm: Fix regulator ramp delay for continuous mode
>> https://patchwork.kernel.org/patch/9216857/
>> where is it discussed to have separate property for PWM which has
>> exponential voltage transition.
>> ---
>> Documentation/devicetree/bindings/regulator/pwm-regulator.txt | 10 ++++++++++
>> 1 file changed, 10 insertions(+)
>>
>> diff --git a/Documentation/devicetree/bindings/regulator/pwm-regulator.txt b/Documentation/devicetree/bindings/regulator/pwm-regulator.txt
>> index 3aeba9f..a163f42 100644
>> --- a/Documentation/devicetree/bindings/regulator/pwm-regulator.txt
>> +++ b/Documentation/devicetree/bindings/regulator/pwm-regulator.txt
>> @@ -54,6 +54,16 @@ Optional properties:
>> --------------------
>> - enable-gpios: GPIO to use to enable/disable the regulator
>>
>> +- pwm-regulator-voltage-ramp-time-us: Integer, voltage ramp time in
> This is a really long name. Drop the 'pwm-regulator-' part as it is
> redundant. The fact that it is PWM reg specific is captured as it is
> documented that way.
>
We already have the regulator-ramp-delay from the regulator core.
Just wanted to make this (pwm-regulator-voltage-ramp-time-us) for pwm
specific.
Can we have "pwm-regulator-ramp-delay" or "pwm-regulator-settling-time-us"?
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* Re: [PATCH 0/3] usb: dwc2: params revert and rework
From: Felipe Balbi @ 2016-11-15 11:32 UTC (permalink / raw)
To: John Youn
In-Reply-To: <cover.1479176826.git.johnyoun-HKixBCOQz3hWk0Htik3J/w@public.gmane.org>
[-- Attachment #1: Type: text/plain, Size: 537 bytes --]
Hi,
John Youn <johnyoun-HKixBCOQz3hWk0Htik3J/w@public.gmane.org> writes:
> Hi Felipe,
>
> This reverts and fixes a few commits that are queued on your
> testing/next, removing the previously added DT bindings, and the code
> that reads them in.
>
> The feedback was that IP validation is not reason enough to add these.
> So we'll leave them out for now.
these are still in testing/next, so I can actually still drop
them. I'm applying only patch 3/3, please make sure it all looks good on
testing/next.
--
balbi
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 832 bytes --]
^ permalink raw reply
* Re: [PATCH v4 1/3] bus: simple-pm: add support to pm clocks
From: Srinivas Kandagatla @ 2016-11-15 11:25 UTC (permalink / raw)
To: Geert Uytterhoeven, Bjorn Helgaas
Cc: svarbanov, linux-pci, Bjorn Helgaas, Rob Herring,
linux-arm-msm@vger.kernel.org, devicetree@vger.kernel.org,
Geert Uytterhoeven, Kevin Hilman, Simon Horman, Linux PM list,
Rajendra Nayak, Nayak, Rajendra
In-Reply-To: <CAMuHMdUJ8Qn=dR_OMob4BO_4RmY5XemTf_UGM_oJ2VYtBa7Jiw@mail.gmail.com>
+ Rajendra (qcom,gdsc author)
On 15/11/16 08:23, Geert Uytterhoeven wrote:
> +cc linux-pm
>
> On Mon, Nov 14, 2016 at 11:14 PM, Bjorn Helgaas <helgaas@kernel.org> wrote:
>> [+cc Geert, Kevin, Simon]
>>
>> On Mon, Nov 14, 2016 at 11:15:53AM +0000, Srinivas Kandagatla wrote:
>>> This patch adds support to pm clocks via device tree, so that the clocks
>>> can be turned on and off during runtime pm. This patch is required for
>>> Qualcomm msm8996 pcie controller which sits on a bus with its own
>>> power-domain and clocks.
>>>
>>> Without this patch the clock associated with the bus are never turned on.
>>>
>>> Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
>>
>> I don't see a formal maintainer for drivers/bus/simple-pm-bus.c, but I'd
>> like an ack or at least a review from Geert or Simon.
>
> Thanks for letting me know!
>
>>> ---
>>> drivers/bus/simple-pm-bus.c | 13 ++++++++++++-
>>> 1 file changed, 12 insertions(+), 1 deletion(-)
>>>
>>> diff --git a/drivers/bus/simple-pm-bus.c b/drivers/bus/simple-pm-bus.c
>>> index c5eb46c..63b7e8c 100644
>>> --- a/drivers/bus/simple-pm-bus.c
>>> +++ b/drivers/bus/simple-pm-bus.c
>>> @@ -11,6 +11,7 @@
>>> #include <linux/module.h>
>>> #include <linux/of_platform.h>
>>> #include <linux/platform_device.h>
>>> +#include <linux/pm_clock.h>
>>> #include <linux/pm_runtime.h>
>>>
>>>
>>> @@ -22,17 +23,26 @@ static int simple_pm_bus_probe(struct platform_device *pdev)
>>>
>>> pm_runtime_enable(&pdev->dev);
>>>
>>> - if (np)
>>> + if (np) {
>>> + of_pm_clk_add_clks(&pdev->dev);
>
> This should work out-of-the-box (that's the actual purpose of this driver),
> if the platform code that registers your PM Domain would take care
> of registering the clocks needed for PM management of the bus.
Yep, if the pm domain provider takes care of the bus clks, then it would
work.
Am guessing that the clocks property in the DT node would be read by the
PM domain provider and enable/disable during attach/detach callbacks.
If that is true, then any device tree nodes which are not children of
"simple-pm-bus" and consumers of power-domain provider would enable all
(including non-bus clks) clks twice. Once in the power-domain provider
and once in the actual driver. Is this expected behavior from
power-domains in general?
>
> Adding of_pm_clk_add_clks() here will start managing all clocks of the bus,
> which may not be wanted on all platforms.
>
That was the purpose.
Rajendra,
Looks like qcom gdsc pm domain provider driver does not handle bus clks
along with power-domain, Is this something we should do? Or the bus
driver take care of it?
Thanks,
srini
>>> of_platform_populate(np, NULL, NULL, &pdev->dev);
>>> + }
>>>
>>> return 0;
>>> }
>>>
>>> +static const struct dev_pm_ops simple_pm_bus_pm_ops = {
>>> + SET_RUNTIME_PM_OPS(pm_clk_suspend,
>>> + pm_clk_resume, NULL)
>>> +};
>>> +
>>> static int simple_pm_bus_remove(struct platform_device *pdev)
>>> {
>>> dev_dbg(&pdev->dev, "%s\n", __func__);
>>>
>>> pm_runtime_disable(&pdev->dev);
>>> + pm_clk_destroy(&pdev->dev);
>>> +
>>> return 0;
>>> }
>>>
>>> @@ -48,6 +58,7 @@ static struct platform_driver simple_pm_bus_driver = {
>>> .driver = {
>>> .name = "simple-pm-bus",
>>> .of_match_table = simple_pm_bus_of_match,
>>> + .pm = &simple_pm_bus_pm_ops,
>>> },
>>> };
>
> Gr{oetje,eeting}s,
>
> Geert
>
> --
> Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert@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
* Re: [PATCH v4 1/2] i2c: aspeed: added driver for Aspeed I2C
From: Joel Stanley @ 2016-11-15 11:23 UTC (permalink / raw)
To: Brendan Higgins
Cc: Wolfram Sang, Rob Herring, Mark Rutland, linux-i2c, devicetree,
OpenBMC Maillist
In-Reply-To: <1478311099-6771-2-git-send-email-brendanhiggins@google.com>
On Sat, Nov 5, 2016 at 12:28 PM, Brendan Higgins
<brendanhiggins@google.com> wrote:
> Added initial master and slave support for Aspeed I2C controller.
> Supports fourteen busses present in ast24xx and ast25xx BMC SoCs by
> Aspeed.
>
> Signed-off-by: Brendan Higgins <brendanhiggins@google.com>
I've tested this on an OpenPower Palmetto BMC system and on the Qemu
machine for the same board.
Reviewed-by: Joel Stanley <joel@jms.id.au>
Cheers,
Joel
> ---
> Changes for v2:
> - Added single module_init (multiple was breaking some builds).
> Changes for v3:
> - Removed "bus" device tree param; now extracted from bus address offset
> Changes for v4:
> - I2C adapter number is now generated dynamically unless specified in alias.
> ---
> drivers/i2c/busses/Kconfig | 10 +
> drivers/i2c/busses/Makefile | 1 +
> drivers/i2c/busses/i2c-aspeed.c | 807 ++++++++++++++++++++++++++++++++++++++++
> 3 files changed, 818 insertions(+)
> create mode 100644 drivers/i2c/busses/i2c-aspeed.c
>
> diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig
> index d252276..b6caa5d 100644
> --- a/drivers/i2c/busses/Kconfig
> +++ b/drivers/i2c/busses/Kconfig
> @@ -1009,6 +1009,16 @@ config I2C_RCAR
> This driver can also be built as a module. If so, the module
> will be called i2c-rcar.
>
> +config I2C_ASPEED
> + tristate "Aspeed AST2xxx SoC I2C Controller"
> + depends on ARCH_ASPEED
> + help
> + If you say yes to this option, support will be included for the
> + Aspeed AST2xxx SoC I2C controller.
> +
> + This driver can also be built as a module. If so, the module
> + will be called i2c-aspeed.
> +
> comment "External I2C/SMBus adapter drivers"
>
> config I2C_DIOLAN_U2C
> diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile
> index 29764cc..826e780 100644
> --- a/drivers/i2c/busses/Makefile
> +++ b/drivers/i2c/busses/Makefile
> @@ -99,6 +99,7 @@ obj-$(CONFIG_I2C_XILINX) += i2c-xiic.o
> obj-$(CONFIG_I2C_XLR) += i2c-xlr.o
> obj-$(CONFIG_I2C_XLP9XX) += i2c-xlp9xx.o
> obj-$(CONFIG_I2C_RCAR) += i2c-rcar.o
> +obj-$(CONFIG_I2C_ASPEED) += i2c-aspeed.o
>
> # External I2C/SMBus adapter drivers
> obj-$(CONFIG_I2C_DIOLAN_U2C) += i2c-diolan-u2c.o
> diff --git a/drivers/i2c/busses/i2c-aspeed.c b/drivers/i2c/busses/i2c-aspeed.c
> new file mode 100644
> index 0000000..88e078a
> --- /dev/null
> +++ b/drivers/i2c/busses/i2c-aspeed.c
> @@ -0,0 +1,807 @@
> +/*
> + * I2C adapter for the ASPEED I2C bus.
> + *
> + * Copyright (C) 2012-2020 ASPEED Technology Inc.
> + * Copyright 2016 IBM Corporation
> + * Copyright 2016 Google, Inc.
> + *
> + * 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/kernel.h>
> +#include <linux/module.h>
> +#include <linux/i2c.h>
> +#include <linux/irq.h>
> +#include <linux/irqdomain.h>
> +#include <linux/init.h>
> +#include <linux/io.h>
> +#include <linux/errno.h>
> +#include <linux/interrupt.h>
> +#include <linux/completion.h>
> +#include <linux/slab.h>
> +#include <linux/of_address.h>
> +#include <linux/of_platform.h>
> +#include <linux/platform_device.h>
> +#include <linux/err.h>
> +#include <linux/clk.h>
> +
> +/* I2C Register */
> +#define ASPEED_I2C_FUN_CTRL_REG 0x00
> +#define ASPEED_I2C_AC_TIMING_REG1 0x04
> +#define ASPEED_I2C_AC_TIMING_REG2 0x08
> +#define ASPEED_I2C_INTR_CTRL_REG 0x0c
> +#define ASPEED_I2C_INTR_STS_REG 0x10
> +#define ASPEED_I2C_CMD_REG 0x14
> +#define ASPEED_I2C_DEV_ADDR_REG 0x18
> +#define ASPEED_I2C_BYTE_BUF_REG 0x20
> +#define ASPEED_I2C_OFFSET_START 0x40
> +#define ASPEED_I2C_OFFSET_INCREMENT 0x40
> +
> +#define ASPEED_I2C_NUM_BUS 14
> +
> +/* Global Register Definition */
> +/* 0x00 : I2C Interrupt Status Register */
> +/* 0x08 : I2C Interrupt Target Assignment */
> +
> +/* Device Register Definition */
> +/* 0x00 : I2CD Function Control Register */
> +#define ASPEED_I2CD_MULTI_MASTER_DIS BIT(15)
> +#define ASPEED_I2CD_SDA_DRIVE_1T_EN BIT(8)
> +#define ASPEED_I2CD_M_SDA_DRIVE_1T_EN BIT(7)
> +#define ASPEED_I2CD_M_HIGH_SPEED_EN BIT(6)
> +#define ASPEED_I2CD_SLAVE_EN BIT(1)
> +#define ASPEED_I2CD_MASTER_EN BIT(0)
> +
> +/* 0x08 : I2CD Clock and AC Timing Control Register #2 */
> +#define ASPEED_NO_TIMEOUT_CTRL 0
> +
> +
> +/* 0x0c : I2CD Interrupt Control Register &
> + * 0x10 : I2CD Interrupt Status Register
> + *
> + * These share bit definitions, so use the same values for the enable &
> + * status bits.
> + */
> +#define ASPEED_I2CD_INTR_SDA_DL_TIMEOUT BIT(14)
> +#define ASPEED_I2CD_INTR_BUS_RECOVER_DONE BIT(13)
> +#define ASPEED_I2CD_INTR_SLAVE_MATCH BIT(7)
> +#define ASPEED_I2CD_INTR_SCL_TIMEOUT BIT(6)
> +#define ASPEED_I2CD_INTR_ABNORMAL BIT(5)
> +#define ASPEED_I2CD_INTR_NORMAL_STOP BIT(4)
> +#define ASPEED_I2CD_INTR_ARBIT_LOSS BIT(3)
> +#define ASPEED_I2CD_INTR_RX_DONE BIT(2)
> +#define ASPEED_I2CD_INTR_TX_NAK BIT(1)
> +#define ASPEED_I2CD_INTR_TX_ACK BIT(0)
> +
> +/* 0x14 : I2CD Command/Status Register */
> +#define ASPEED_I2CD_SCL_LINE_STS BIT(18)
> +#define ASPEED_I2CD_SDA_LINE_STS BIT(17)
> +#define ASPEED_I2CD_BUS_BUSY_STS BIT(16)
> +#define ASPEED_I2CD_BUS_RECOVER_CMD BIT(11)
> +
> +/* Command Bit */
> +#define ASPEED_I2CD_M_STOP_CMD BIT(5)
> +#define ASPEED_I2CD_M_S_RX_CMD_LAST BIT(4)
> +#define ASPEED_I2CD_M_RX_CMD BIT(3)
> +#define ASPEED_I2CD_S_TX_CMD BIT(2)
> +#define ASPEED_I2CD_M_TX_CMD BIT(1)
> +#define ASPEED_I2CD_M_START_CMD BIT(0)
> +
> +/* 0x18 : I2CD Slave Device Address Register */
> +#define ASPEED_I2CD_DEV_ADDR_MASK GENMASK(6, 0)
> +
> +enum aspeed_i2c_slave_state {
> + ASPEED_I2C_SLAVE_START,
> + ASPEED_I2C_SLAVE_READ_REQUESTED,
> + ASPEED_I2C_SLAVE_READ_PROCESSED,
> + ASPEED_I2C_SLAVE_WRITE_REQUESTED,
> + ASPEED_I2C_SLAVE_WRITE_RECEIVED,
> + ASPEED_I2C_SLAVE_STOP,
> +};
> +
> +struct aspeed_i2c_bus {
> + struct i2c_adapter adap;
> + struct device *dev;
> + void __iomem *base;
> + spinlock_t lock;
> + struct completion cmd_complete;
> + int irq;
> + /* Transaction state. */
> + struct i2c_msg *msg;
> + int msg_pos;
> + u32 cmd_err;
> +#if IS_ENABLED(CONFIG_I2C_SLAVE)
> + struct i2c_client *slave;
> + enum aspeed_i2c_slave_state slave_state;
> +#endif
> +};
> +
> +struct aspeed_i2c_controller {
> + struct device *dev;
> + void __iomem *base;
> + int irq;
> + struct irq_domain *irq_domain;
> +};
> +
> +static inline void aspeed_i2c_write(struct aspeed_i2c_bus *bus, u32 val,
> + u32 reg)
> +{
> + writel(val, bus->base + reg);
> +}
> +
> +static inline u32 aspeed_i2c_read(struct aspeed_i2c_bus *bus, u32 reg)
> +{
> + return readl(bus->base + reg);
> +}
> +
> +static u8 aspeed_i2c_recover_bus(struct aspeed_i2c_bus *bus)
> +{
> + u32 command;
> + unsigned long time_left;
> + unsigned long flags;
> + int ret = 0;
> +
> + spin_lock_irqsave(&bus->lock, flags);
> + command = aspeed_i2c_read(bus, ASPEED_I2C_CMD_REG);
> + /* Bus is idle: no recovery needed. */
> + if ((command & ASPEED_I2CD_SDA_LINE_STS) &&
> + (command & ASPEED_I2CD_SCL_LINE_STS))
> + goto out;
> +
> + dev_dbg(bus->dev, "bus hung (state %x), attempting recovery\n",
> + command);
> +
> + /* Bus held: put bus in stop state. */
> + if ((command & ASPEED_I2CD_SDA_LINE_STS) &&
> + !(command & ASPEED_I2CD_SCL_LINE_STS)) {
> + aspeed_i2c_write(bus, ASPEED_I2CD_M_STOP_CMD,
> + ASPEED_I2C_CMD_REG);
> + reinit_completion(&bus->cmd_complete);
> + spin_unlock_irqrestore(&bus->lock, flags);
> +
> + time_left = wait_for_completion_interruptible_timeout(
> + &bus->cmd_complete, bus->adap.timeout * HZ);
> +
> + spin_lock_irqsave(&bus->lock, flags);
> + if (time_left == 0)
> + ret = -ETIMEDOUT;
> + else if (bus->cmd_err)
> + ret = -EIO;
> + /* Bus error. */
> + } else if (!(command & ASPEED_I2CD_SDA_LINE_STS)) {
> + aspeed_i2c_write(bus, ASPEED_I2CD_BUS_RECOVER_CMD,
> + ASPEED_I2C_CMD_REG);
> + reinit_completion(&bus->cmd_complete);
> + spin_unlock_irqrestore(&bus->lock, flags);
> +
> + time_left = wait_for_completion_interruptible_timeout(
> + &bus->cmd_complete, bus->adap.timeout * HZ);
> +
> + spin_lock_irqsave(&bus->lock, flags);
> + if (time_left == 0)
> + ret = -ETIMEDOUT;
> + else if (bus->cmd_err)
> + ret = -EIO;
> + /* Recovery failed. */
> + else if (!(aspeed_i2c_read(bus, ASPEED_I2C_CMD_REG) &
> + ASPEED_I2CD_SDA_LINE_STS))
> + ret = -EIO;
> + }
> +
> +out:
> + spin_unlock_irqrestore(&bus->lock, flags);
> + return ret;
> +}
> +
> +#if IS_ENABLED(CONFIG_I2C_SLAVE)
> +static bool aspeed_i2c_slave_irq(struct aspeed_i2c_bus *bus)
> +{
> + bool irq_handled = true;
> + u32 command;
> + u32 irq_status;
> + u32 status_ack = 0;
> + u8 value;
> + struct i2c_client *slave = bus->slave;
> +
> + spin_lock(&bus->lock);
> + if (!slave) {
> + irq_handled = false;
> + goto out;
> + }
> + command = aspeed_i2c_read(bus, ASPEED_I2C_CMD_REG);
> + irq_status = aspeed_i2c_read(bus, ASPEED_I2C_INTR_STS_REG);
> +
> + /* Slave was requested, restart state machine. */
> + if (irq_status & ASPEED_I2CD_INTR_SLAVE_MATCH) {
> + status_ack |= ASPEED_I2CD_INTR_SLAVE_MATCH;
> + bus->slave_state = ASPEED_I2C_SLAVE_START;
> + }
> + /* Slave is not currently active, irq was for someone else. */
> + if (bus->slave_state == ASPEED_I2C_SLAVE_STOP) {
> + irq_handled = false;
> + goto out;
> + }
> +
> + dev_dbg(bus->dev, "slave irq status 0x%08x, cmd 0x%08x\n",
> + irq_status, command);
> +
> + /* Slave was sent something. */
> + if (irq_status & ASPEED_I2CD_INTR_RX_DONE) {
> + value = aspeed_i2c_read(bus, ASPEED_I2C_BYTE_BUF_REG) >> 8;
> + /* Handle address frame. */
> + if (bus->slave_state == ASPEED_I2C_SLAVE_START) {
> + if (value & 0x1)
> + bus->slave_state =
> + ASPEED_I2C_SLAVE_READ_REQUESTED;
> + else
> + bus->slave_state =
> + ASPEED_I2C_SLAVE_WRITE_REQUESTED;
> + }
> + status_ack |= ASPEED_I2CD_INTR_RX_DONE;
> + }
> +
> + /* Slave was asked to stop. */
> + if (irq_status & ASPEED_I2CD_INTR_NORMAL_STOP) {
> + status_ack |= ASPEED_I2CD_INTR_NORMAL_STOP;
> + bus->slave_state = ASPEED_I2C_SLAVE_STOP;
> + }
> + if (irq_status & ASPEED_I2CD_INTR_TX_NAK) {
> + status_ack |= ASPEED_I2CD_INTR_TX_NAK;
> + bus->slave_state = ASPEED_I2C_SLAVE_STOP;
> + }
> +
> + if (bus->slave_state == ASPEED_I2C_SLAVE_READ_REQUESTED) {
> + if (irq_status & ASPEED_I2CD_INTR_TX_ACK)
> + dev_err(bus->dev, "Unexpected ACK on read request.\n");
> + bus->slave_state = ASPEED_I2C_SLAVE_READ_PROCESSED;
> +
> + i2c_slave_event(slave, I2C_SLAVE_READ_REQUESTED, &value);
> + aspeed_i2c_write(bus, value, ASPEED_I2C_BYTE_BUF_REG);
> + aspeed_i2c_write(bus, ASPEED_I2CD_S_TX_CMD, ASPEED_I2C_CMD_REG);
> + } else if (bus->slave_state == ASPEED_I2C_SLAVE_READ_PROCESSED) {
> + status_ack |= ASPEED_I2CD_INTR_TX_ACK;
> + if (!(irq_status & ASPEED_I2CD_INTR_TX_ACK))
> + dev_err(bus->dev,
> + "Expected ACK after processed read.\n");
> + i2c_slave_event(slave, I2C_SLAVE_READ_PROCESSED, &value);
> + aspeed_i2c_write(bus, value, ASPEED_I2C_BYTE_BUF_REG);
> + aspeed_i2c_write(bus, ASPEED_I2CD_S_TX_CMD, ASPEED_I2C_CMD_REG);
> + } else if (bus->slave_state == ASPEED_I2C_SLAVE_WRITE_REQUESTED) {
> + bus->slave_state = ASPEED_I2C_SLAVE_WRITE_RECEIVED;
> + i2c_slave_event(slave, I2C_SLAVE_WRITE_REQUESTED, &value);
> + } else if (bus->slave_state == ASPEED_I2C_SLAVE_WRITE_RECEIVED) {
> + i2c_slave_event(slave, I2C_SLAVE_WRITE_RECEIVED, &value);
> + } else if (bus->slave_state == ASPEED_I2C_SLAVE_STOP) {
> + i2c_slave_event(slave, I2C_SLAVE_STOP, &value);
> + }
> +
> + if (status_ack != irq_status)
> + dev_err(bus->dev,
> + "irq handled != irq. expected %x, but was %x\n",
> + irq_status, status_ack);
> + aspeed_i2c_write(bus, status_ack, ASPEED_I2C_INTR_STS_REG);
> +
> +out:
> + spin_unlock(&bus->lock);
> + return irq_handled;
> +}
> +#endif
> +
> +static bool aspeed_i2c_master_irq(struct aspeed_i2c_bus *bus)
> +{
> + const u32 errs = ASPEED_I2CD_INTR_ARBIT_LOSS |
> + ASPEED_I2CD_INTR_ABNORMAL |
> + ASPEED_I2CD_INTR_SCL_TIMEOUT |
> + ASPEED_I2CD_INTR_SDA_DL_TIMEOUT |
> + ASPEED_I2CD_INTR_TX_NAK;
> + u32 irq_status;
> +
> + spin_lock(&bus->lock);
> + irq_status = aspeed_i2c_read(bus, ASPEED_I2C_INTR_STS_REG);
> + bus->cmd_err = irq_status & errs;
> +
> + dev_dbg(bus->dev, "master irq status 0x%08x\n", irq_status);
> +
> + /* No message to transfer. */
> + if (bus->cmd_err ||
> + (irq_status & ASPEED_I2CD_INTR_NORMAL_STOP) ||
> + (irq_status & ASPEED_I2CD_INTR_BUS_RECOVER_DONE)) {
> + complete(&bus->cmd_complete);
> + goto out;
> + } else if (!bus->msg || bus->msg_pos >= bus->msg->len)
> + goto out;
> +
> + if ((bus->msg->flags & I2C_M_RD) &&
> + (irq_status & ASPEED_I2CD_INTR_RX_DONE)) {
> + bus->msg->buf[bus->msg_pos++] = aspeed_i2c_read(
> + bus, ASPEED_I2C_BYTE_BUF_REG) >> 8;
> + if (bus->msg_pos + 1 < bus->msg->len)
> + aspeed_i2c_write(bus, ASPEED_I2CD_M_RX_CMD,
> + ASPEED_I2C_CMD_REG);
> + else if (bus->msg_pos < bus->msg->len)
> + aspeed_i2c_write(bus, ASPEED_I2CD_M_RX_CMD |
> + ASPEED_I2CD_M_S_RX_CMD_LAST,
> + ASPEED_I2C_CMD_REG);
> + } else if (!(bus->msg->flags & I2C_M_RD) &&
> + (irq_status & ASPEED_I2CD_INTR_TX_ACK)) {
> + aspeed_i2c_write(bus, bus->msg->buf[bus->msg_pos++],
> + ASPEED_I2C_BYTE_BUF_REG);
> + aspeed_i2c_write(bus, ASPEED_I2CD_M_TX_CMD, ASPEED_I2C_CMD_REG);
> + }
> +
> + /* Transmission complete: notify caller. */
> + if (bus->msg_pos >= bus->msg->len)
> + complete(&bus->cmd_complete);
> +out:
> + aspeed_i2c_write(bus, irq_status, ASPEED_I2C_INTR_STS_REG);
> + spin_unlock(&bus->lock);
> + return true;
> +}
> +
> +static irqreturn_t aspeed_i2c_bus_irq(int irq, void *dev_id)
> +{
> + struct aspeed_i2c_bus *bus = dev_id;
> +
> +#if IS_ENABLED(CONFIG_I2C_SLAVE)
> + if (aspeed_i2c_slave_irq(bus)) {
> + dev_dbg(bus->dev, "irq handled by slave.\n");
> + return IRQ_HANDLED;
> + }
> +#endif
> + if (aspeed_i2c_master_irq(bus)) {
> + dev_dbg(bus->dev, "irq handled by master.\n");
> + return IRQ_HANDLED;
> + }
> + dev_err(bus->dev, "irq not handled properly!\n");
> + return IRQ_HANDLED;
> +}
> +
> +static int aspeed_i2c_master_single_xfer(struct i2c_adapter *adap,
> + struct i2c_msg *msg)
> +{
> + struct aspeed_i2c_bus *bus = adap->algo_data;
> + unsigned long flags;
> + u8 slave_addr;
> + u32 command = ASPEED_I2CD_M_START_CMD | ASPEED_I2CD_M_TX_CMD;
> + int ret = msg->len;
> + unsigned long time_left;
> +
> + spin_lock_irqsave(&bus->lock, flags);
> + bus->msg = msg;
> + bus->msg_pos = 0;
> + slave_addr = msg->addr << 1;
> + if (msg->flags & I2C_M_RD) {
> + slave_addr |= 1;
> + command |= ASPEED_I2CD_M_RX_CMD;
> + if (msg->len == 1)
> + command |= ASPEED_I2CD_M_S_RX_CMD_LAST;
> + }
> + aspeed_i2c_write(bus, slave_addr, ASPEED_I2C_BYTE_BUF_REG);
> + aspeed_i2c_write(bus, command, ASPEED_I2C_CMD_REG);
> + reinit_completion(&bus->cmd_complete);
> + spin_unlock_irqrestore(&bus->lock, flags);
> +
> + time_left = wait_for_completion_interruptible_timeout(
> + &bus->cmd_complete, bus->adap.timeout * HZ * msg->len);
> + if (time_left == 0)
> + return -ETIMEDOUT;
> +
> + spin_lock_irqsave(&bus->lock, flags);
> + if (bus->cmd_err)
> + ret = -EIO;
> + bus->msg = NULL;
> + spin_unlock_irqrestore(&bus->lock, flags);
> +
> + return ret;
> +}
> +
> +static int aspeed_i2c_master_xfer(struct i2c_adapter *adap,
> + struct i2c_msg *msgs, int num)
> +{
> + struct aspeed_i2c_bus *bus = adap->algo_data;
> + int ret;
> + int i;
> + unsigned long flags;
> + unsigned long time_left;
> +
> + /* If bus is busy, attempt recovery. We assume a single master
> + * environment.
> + */
> + if (aspeed_i2c_read(bus, ASPEED_I2C_CMD_REG) &
> + ASPEED_I2CD_BUS_BUSY_STS) {
> + ret = aspeed_i2c_recover_bus(bus);
> + if (ret)
> + return ret;
> + }
> +
> + for (i = 0; i < num; i++) {
> + ret = aspeed_i2c_master_single_xfer(adap, &msgs[i]);
> + if (ret < 0)
> + break;
> + /* TODO: Support other forms of I2C protocol mangling. */
> + if (msgs[i].flags & I2C_M_STOP) {
> + spin_lock_irqsave(&bus->lock, flags);
> + aspeed_i2c_write(bus, ASPEED_I2CD_M_STOP_CMD,
> + ASPEED_I2C_CMD_REG);
> + reinit_completion(&bus->cmd_complete);
> + spin_unlock_irqrestore(&bus->lock, flags);
> +
> + time_left = wait_for_completion_interruptible_timeout(
> + &bus->cmd_complete,
> + bus->adap.timeout * HZ);
> + if (time_left == 0)
> + return -ETIMEDOUT;
> + }
> + }
> +
> + spin_lock_irqsave(&bus->lock, flags);
> + aspeed_i2c_write(bus, ASPEED_I2CD_M_STOP_CMD, ASPEED_I2C_CMD_REG);
> + reinit_completion(&bus->cmd_complete);
> + spin_unlock_irqrestore(&bus->lock, flags);
> +
> + time_left = wait_for_completion_interruptible_timeout(
> + &bus->cmd_complete, bus->adap.timeout * HZ);
> + if (time_left == 0)
> + return -ETIMEDOUT;
> +
> + /* If nothing went wrong, return number of messages transferred. */
> + if (ret < 0)
> + return ret;
> + else
> + return i;
> +}
> +
> +static u32 aspeed_i2c_functionality(struct i2c_adapter *adap)
> +{
> + return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL | I2C_FUNC_SMBUS_BLOCK_DATA;
> +}
> +
> +#if IS_ENABLED(CONFIG_I2C_SLAVE)
> +static int aspeed_i2c_reg_slave(struct i2c_client *client)
> +{
> + struct aspeed_i2c_bus *bus;
> + unsigned long flags;
> + u32 addr_reg_val;
> + u32 func_ctrl_reg_val;
> +
> + bus = client->adapter->algo_data;
> + spin_lock_irqsave(&bus->lock, flags);
> + if (bus->slave) {
> + spin_unlock_irqrestore(&bus->lock, flags);
> + return -EINVAL;
> + }
> +
> + /* Set slave addr. */
> + addr_reg_val = aspeed_i2c_read(bus, ASPEED_I2C_DEV_ADDR_REG);
> + addr_reg_val &= ~ASPEED_I2CD_DEV_ADDR_MASK;
> + addr_reg_val |= client->addr & ASPEED_I2CD_DEV_ADDR_MASK;
> + aspeed_i2c_write(bus, addr_reg_val, ASPEED_I2C_DEV_ADDR_REG);
> +
> + /* Switch from master mode to slave mode. */
> + func_ctrl_reg_val = aspeed_i2c_read(bus, ASPEED_I2C_FUN_CTRL_REG);
> + func_ctrl_reg_val &= ~ASPEED_I2CD_MASTER_EN;
> + func_ctrl_reg_val |= ASPEED_I2CD_SLAVE_EN;
> + aspeed_i2c_write(bus, func_ctrl_reg_val, ASPEED_I2C_FUN_CTRL_REG);
> +
> + bus->slave = client;
> + bus->slave_state = ASPEED_I2C_SLAVE_STOP;
> + spin_unlock_irqrestore(&bus->lock, flags);
> + return 0;
> +}
> +
> +static int aspeed_i2c_unreg_slave(struct i2c_client *client)
> +{
> + struct aspeed_i2c_bus *bus = client->adapter->algo_data;
> + unsigned long flags;
> + u32 func_ctrl_reg_val;
> +
> + spin_lock_irqsave(&bus->lock, flags);
> + if (!bus->slave) {
> + spin_unlock_irqrestore(&bus->lock, flags);
> + return -EINVAL;
> + }
> +
> + /* Switch from slave mode to master mode. */
> + func_ctrl_reg_val = aspeed_i2c_read(bus, ASPEED_I2C_FUN_CTRL_REG);
> + func_ctrl_reg_val &= ~ASPEED_I2CD_SLAVE_EN;
> + func_ctrl_reg_val |= ASPEED_I2CD_MASTER_EN;
> + aspeed_i2c_write(bus, func_ctrl_reg_val, ASPEED_I2C_FUN_CTRL_REG);
> +
> + bus->slave = NULL;
> + spin_unlock_irqrestore(&bus->lock, flags);
> + return 0;
> +}
> +#endif
> +
> +static const struct i2c_algorithm aspeed_i2c_algo = {
> + .master_xfer = aspeed_i2c_master_xfer,
> + .functionality = aspeed_i2c_functionality,
> +#if IS_ENABLED(CONFIG_I2C_SLAVE)
> + .reg_slave = aspeed_i2c_reg_slave,
> + .unreg_slave = aspeed_i2c_unreg_slave,
> +#endif
> +};
> +
> +static u32 aspeed_i2c_get_clk_reg_val(u32 divider_ratio)
> +{
> + unsigned int inc = 0, div;
> + u32 scl_low, scl_high, data;
> +
> + for (div = 0; divider_ratio >= 16; div++) {
> + inc |= (divider_ratio & 1);
> + divider_ratio >>= 1;
> + }
> + divider_ratio += inc;
> + scl_low = (divider_ratio >> 1) - 1;
> + scl_high = divider_ratio - scl_low - 2;
> + data = 0x77700300 | (scl_high << 16) | (scl_low << 12) | div;
> + return data;
> +}
> +
> +static int aspeed_i2c_init_clk(struct aspeed_i2c_bus *bus,
> + struct platform_device *pdev)
> +{
> + struct clk *pclk;
> + u32 clk_freq;
> + u32 divider_ratio;
> + int ret;
> +
> + pclk = devm_clk_get(&pdev->dev, NULL);
> + if (IS_ERR(pclk)) {
> + dev_err(&pdev->dev, "clk_get failed\n");
> + return PTR_ERR(pclk);
> + }
> + ret = of_property_read_u32(pdev->dev.of_node,
> + "clock-frequency", &clk_freq);
> + if (ret < 0) {
> + dev_err(&pdev->dev,
> + "Could not read clock-frequency property\n");
> + clk_freq = 100000;
> + }
> + divider_ratio = clk_get_rate(pclk) / clk_freq;
> + /* We just need the clock rate, we don't actually use the clk object. */
> + devm_clk_put(&pdev->dev, pclk);
> +
> + /* Set AC Timing */
> + if (clk_freq / 1000 > 400) {
> + aspeed_i2c_write(bus, aspeed_i2c_read(bus,
> + ASPEED_I2C_FUN_CTRL_REG) |
> + ASPEED_I2CD_M_HIGH_SPEED_EN |
> + ASPEED_I2CD_M_SDA_DRIVE_1T_EN |
> + ASPEED_I2CD_SDA_DRIVE_1T_EN,
> + ASPEED_I2C_FUN_CTRL_REG);
> +
> + aspeed_i2c_write(bus, 0x3, ASPEED_I2C_AC_TIMING_REG2);
> + aspeed_i2c_write(bus, aspeed_i2c_get_clk_reg_val(divider_ratio),
> + ASPEED_I2C_AC_TIMING_REG1);
> + } else {
> + aspeed_i2c_write(bus, aspeed_i2c_get_clk_reg_val(divider_ratio),
> + ASPEED_I2C_AC_TIMING_REG1);
> + aspeed_i2c_write(bus, ASPEED_NO_TIMEOUT_CTRL,
> + ASPEED_I2C_AC_TIMING_REG2);
> + }
> +
> + return 0;
> +}
> +
> +static void noop(struct irq_data *data) { }
> +
> +static struct irq_chip aspeed_i2c_irqchip = {
> + .name = "ast-i2c",
> + .irq_unmask = noop,
> + .irq_mask = noop,
> +};
> +
> +static int aspeed_i2c_probe_bus(struct platform_device *pdev)
> +{
> + struct aspeed_i2c_bus *bus;
> + struct aspeed_i2c_controller *controller =
> + dev_get_drvdata(pdev->dev.parent);
> + struct resource *res;
> + int ret, irq;
> + u32 hwirq;
> +
> + bus = devm_kzalloc(&pdev->dev, sizeof(*bus), GFP_KERNEL);
> + if (!bus)
> + return -ENOMEM;
> +
> + res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> + bus->base = devm_ioremap_resource(&pdev->dev, res);
> + if (IS_ERR(bus->base))
> + return PTR_ERR(bus->base);
> +
> + bus->irq = platform_get_irq(pdev, 0);
> + if (bus->irq < 0)
> + return -ENXIO;
> + ret = of_property_read_u32(pdev->dev.of_node, "interrupts", &hwirq);
> + if (ret < 0) {
> + dev_err(&pdev->dev, "no I2C 'interrupts' property\n");
> + return -ENXIO;
> + }
> + irq = irq_create_mapping(controller->irq_domain, hwirq);
> + irq_set_chip_data(irq, controller);
> + irq_set_chip_and_handler(irq, &aspeed_i2c_irqchip, handle_simple_irq);
> + ret = devm_request_irq(&pdev->dev, bus->irq, aspeed_i2c_bus_irq,
> + 0, dev_name(&pdev->dev), bus);
> + if (ret) {
> + dev_err(&pdev->dev, "failed to request interrupt\n");
> + return -ENXIO;
> + }
> +
> + /* Initialize the I2C adapter */
> + spin_lock_init(&bus->lock);
> + init_completion(&bus->cmd_complete);
> + bus->adap.owner = THIS_MODULE;
> + bus->adap.retries = 0;
> + bus->adap.timeout = 5;
> + bus->adap.algo = &aspeed_i2c_algo;
> + bus->adap.algo_data = bus;
> + bus->adap.dev.parent = &pdev->dev;
> + bus->adap.dev.of_node = pdev->dev.of_node;
> + snprintf(bus->adap.name, sizeof(bus->adap.name), "Aspeed i2c");
> +
> + bus->dev = &pdev->dev;
> +
> + /* reset device: disable master & slave functions */
> + aspeed_i2c_write(bus, 0, ASPEED_I2C_FUN_CTRL_REG);
> +
> + ret = aspeed_i2c_init_clk(bus, pdev);
> + if (ret < 0)
> + return ret;
> +
> + /* Enable Master Mode */
> + aspeed_i2c_write(bus, aspeed_i2c_read(bus, ASPEED_I2C_FUN_CTRL_REG) |
> + ASPEED_I2CD_MASTER_EN |
> + ASPEED_I2CD_MULTI_MASTER_DIS, ASPEED_I2C_FUN_CTRL_REG);
> +
> + /* Set interrupt generation of I2C controller */
> + aspeed_i2c_write(bus, ASPEED_I2CD_INTR_SDA_DL_TIMEOUT |
> + ASPEED_I2CD_INTR_BUS_RECOVER_DONE |
> + ASPEED_I2CD_INTR_SCL_TIMEOUT |
> + ASPEED_I2CD_INTR_ABNORMAL |
> + ASPEED_I2CD_INTR_NORMAL_STOP |
> + ASPEED_I2CD_INTR_ARBIT_LOSS |
> + ASPEED_I2CD_INTR_RX_DONE |
> + ASPEED_I2CD_INTR_TX_NAK |
> + ASPEED_I2CD_INTR_TX_ACK,
> + ASPEED_I2C_INTR_CTRL_REG);
> +
> + ret = i2c_add_adapter(&bus->adap);
> + if (ret < 0)
> + return -ENXIO;
> +
> + platform_set_drvdata(pdev, bus);
> +
> + dev_info(bus->dev, "i2c bus %d registered, irq %d\n",
> + bus->adap.nr, bus->irq);
> +
> + return 0;
> +}
> +
> +static int aspeed_i2c_remove_bus(struct platform_device *pdev)
> +{
> + struct aspeed_i2c_bus *bus = platform_get_drvdata(pdev);
> +
> + i2c_del_adapter(&bus->adap);
> + return 0;
> +}
> +
> +static const struct of_device_id aspeed_i2c_bus_of_table[] = {
> + { .compatible = "aspeed,ast2400-i2c-bus", },
> + { .compatible = "aspeed,ast2500-i2c-bus", },
> + { },
> +};
> +MODULE_DEVICE_TABLE(of, aspeed_i2c_bus_of_table);
> +
> +static struct platform_driver aspeed_i2c_bus_driver = {
> + .probe = aspeed_i2c_probe_bus,
> + .remove = aspeed_i2c_remove_bus,
> + .driver = {
> + .name = "ast-i2c-bus",
> + .of_match_table = aspeed_i2c_bus_of_table,
> + },
> +};
> +
> +static void aspeed_i2c_controller_irq(struct irq_desc *desc)
> +{
> + struct aspeed_i2c_controller *c = irq_desc_get_handler_data(desc);
> + unsigned long p, status;
> + unsigned int bus_irq;
> +
> + status = readl(c->base);
> + for_each_set_bit(p, &status, ASPEED_I2C_NUM_BUS) {
> + bus_irq = irq_find_mapping(c->irq_domain, p);
> + generic_handle_irq(bus_irq);
> + }
> +}
> +
> +static int aspeed_i2c_probe_controller(struct platform_device *pdev)
> +{
> + struct aspeed_i2c_controller *controller;
> + struct device_node *np;
> + struct resource *res;
> +
> + controller = kzalloc(sizeof(*controller), GFP_KERNEL);
> + if (!controller)
> + return -ENOMEM;
> +
> + res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> + controller->base = devm_ioremap_resource(&pdev->dev, res);
> + if (IS_ERR(controller->base))
> + return PTR_ERR(controller->base);
> +
> + controller->irq = platform_get_irq(pdev, 0);
> + if (controller->irq < 0)
> + return -ENXIO;
> +
> + controller->irq_domain = irq_domain_add_linear(pdev->dev.of_node,
> + ASPEED_I2C_NUM_BUS, &irq_domain_simple_ops, NULL);
> + if (!controller->irq_domain)
> + return -ENXIO;
> + controller->irq_domain->name = "ast-i2c-domain";
> +
> + irq_set_chained_handler_and_data(controller->irq,
> + aspeed_i2c_controller_irq, controller);
> +
> + controller->dev = &pdev->dev;
> +
> + platform_set_drvdata(pdev, controller);
> +
> + dev_info(controller->dev, "i2c controller registered, irq %d\n",
> + controller->irq);
> +
> + for_each_child_of_node(pdev->dev.of_node, np) {
> + of_platform_device_create(np, NULL, &pdev->dev);
> + of_node_put(np);
> + }
> +
> + return 0;
> +}
> +
> +static int aspeed_i2c_remove_controller(struct platform_device *pdev)
> +{
> + struct aspeed_i2c_controller *controller = platform_get_drvdata(pdev);
> +
> + irq_domain_remove(controller->irq_domain);
> + return 0;
> +}
> +
> +static const struct of_device_id aspeed_i2c_controller_of_table[] = {
> + { .compatible = "aspeed,ast2400-i2c-controller", },
> + { .compatible = "aspeed,ast2500-i2c-controller", },
> + { },
> +};
> +MODULE_DEVICE_TABLE(of, aspeed_i2c_controller_of_table);
> +
> +static struct platform_driver aspeed_i2c_controller_driver = {
> + .probe = aspeed_i2c_probe_controller,
> + .remove = aspeed_i2c_remove_controller,
> + .driver = {
> + .name = "ast-i2c-controller",
> + .of_match_table = aspeed_i2c_controller_of_table,
> + },
> +};
> +
> +static int __init aspeed_i2c_driver_init(void)
> +{
> + int ret;
> +
> + ret = platform_driver_register(&aspeed_i2c_controller_driver);
> + if (ret < 0)
> + return ret;
> + return platform_driver_register(&aspeed_i2c_bus_driver);
> +}
> +module_init(aspeed_i2c_driver_init);
> +
> +static void __exit aspeed_i2c_driver_exit(void)
> +{
> + platform_driver_unregister(&aspeed_i2c_bus_driver);
> + platform_driver_unregister(&aspeed_i2c_controller_driver);
> +}
> +module_exit(aspeed_i2c_driver_exit);
> +
> +MODULE_AUTHOR("Brendan Higgins <brendanhiggins@google.com>");
> +MODULE_DESCRIPTION("Aspeed I2C Bus Driver");
> +MODULE_LICENSE("GPL");
> --
> 2.8.0.rc3.226.g39d4020
>
^ permalink raw reply
* Re: [PATCH v4 2/2] i2c: aspeed: added documentation for Aspeed I2C driver
From: Joel Stanley @ 2016-11-15 11:16 UTC (permalink / raw)
To: Rob Herring
Cc: Brendan Higgins, Wolfram Sang, Mark Rutland,
linux-i2c-u79uwXL29TY76Z2rM5mHXA,
devicetree-u79uwXL29TY76Z2rM5mHXA, OpenBMC Maillist
In-Reply-To: <20161114155731.p4kxmefqevplphtd@rob-hp-laptop>
On Tue, Nov 15, 2016 at 2:27 AM, Rob Herring <robh-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org> wrote:
> On Fri, Nov 04, 2016 at 06:58:19PM -0700, Brendan Higgins wrote:
>> Added device tree binding documentation for Aspeed I2C controller and
>> busses.
>>
>> Signed-off-by: Brendan Higgins <brendanhiggins-hpIqsD4AKlfQT0dZR+AlfA@public.gmane.org>
>> ---
>> Changes for v2:
>> - None
>> Changes for v3:
>> - Removed reference to "bus" device tree param
>> Changes for v4:
>> - None
>> ---
>> .../devicetree/bindings/i2c/i2c-aspeed.txt | 61 ++++++++++++++++++++++
>> 1 file changed, 61 insertions(+)
>> create mode 100644 Documentation/devicetree/bindings/i2c/i2c-aspeed.txt
>
> Acked-by: Rob Herring <robh-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>
Thanks Rob.
Acked-by: Joel Stanley <joel-U3u1mxZcP9KHXe+LvDLADg@public.gmane.org>
Cheers,
Joel
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* [PATCH v2] ARM: dts: da850: add the mstpri and ddrctl nodes
From: Bartosz Golaszewski @ 2016-11-15 11:00 UTC (permalink / raw)
To: Kevin Hilman, Michael Turquette, Sekhar Nori, Rob Herring,
Frank Rowand, Mark Rutland, Peter Ujfalusi, Russell King
Cc: LKML, arm-soc, linux-drm, linux-devicetree, Jyri Sarha,
Tomi Valkeinen, David Airlie, Laurent Pinchart,
Bartosz Golaszewski
Add the nodes for the MSTPRI configuration and DDR2/mDDR memory
controller drivers to da850.dtsi.
Signed-off-by: Bartosz Golaszewski <bgolaszewski@baylibre.com>
---
v1 -> v2:
- moved the priority controller node above the cfgchip node
- renamed added nodes to better reflect their purpose
arch/arm/boot/dts/da850.dtsi | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/arch/arm/boot/dts/da850.dtsi b/arch/arm/boot/dts/da850.dtsi
index 1bb1f6d..412eec6 100644
--- a/arch/arm/boot/dts/da850.dtsi
+++ b/arch/arm/boot/dts/da850.dtsi
@@ -210,6 +210,10 @@
};
};
+ prictrl: priority-controller@14110 {
+ compatible = "ti,da850-mstpri";
+ reg = <0x14110 0x0c>;
+ };
cfgchip: chip-controller@1417c {
compatible = "ti,da830-cfgchip", "syscon", "simple-mfd";
reg = <0x1417c 0x14>;
@@ -451,4 +455,8 @@
1 0 0x68000000 0x00008000>;
status = "disabled";
};
+ memctrl: memory-controller@b0000000 {
+ compatible = "ti,da850-ddr-controller";
+ reg = <0xb0000000 0xe8>;
+ };
};
--
2.9.3
^ permalink raw reply related
* Re: [PATCH resend] input: touchscreen: silead: Add regulator support
From: Hans de Goede @ 2016-11-15 10:56 UTC (permalink / raw)
To: Dmitry Torokhov
Cc: Rob Herring, linux-input-u79uwXL29TY76Z2rM5mHXA,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, devicetree,
Hans de Goede
In-Reply-To: <20161114185057.GA7694@dtor-ws>
Hi,
On 14-11-16 19:50, Dmitry Torokhov wrote:
> Hi Hans,
>
> On Mon, Nov 14, 2016 at 03:45:02PM +0100, Hans de Goede wrote:
>> On some tablets the touchscreen controller is powered by seperate
>> regulators, add support for this.
>>
>> Signed-off-by: Hans de Goede <hdegoede-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>
>> Acked-by: Rob Herring <robh-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>
>> ---
>> .../bindings/input/touchscreen/silead_gsl1680.txt | 2 +
>> drivers/input/touchscreen/silead.c | 51 ++++++++++++++++++++--
>> 2 files changed, 49 insertions(+), 4 deletions(-)
>>
>> diff --git a/Documentation/devicetree/bindings/input/touchscreen/silead_gsl1680.txt b/Documentation/devicetree/bindings/input/touchscreen/silead_gsl1680.txt
>> index e844c3f..b726823 100644
>> --- a/Documentation/devicetree/bindings/input/touchscreen/silead_gsl1680.txt
>> +++ b/Documentation/devicetree/bindings/input/touchscreen/silead_gsl1680.txt
>> @@ -22,6 +22,8 @@ Optional properties:
>> - touchscreen-inverted-y : See touchscreen.txt
>> - touchscreen-swapped-x-y : See touchscreen.txt
>> - silead,max-fingers : maximum number of fingers the touchscreen can detect
>> +- vddio-supply : regulator phandle for controller VDDIO
>> +- avdd-supply : regulator phandle for controller AVDD
>>
>> Example:
>>
>> diff --git a/drivers/input/touchscreen/silead.c b/drivers/input/touchscreen/silead.c
>> index f502c84..c6a1ae9 100644
>> --- a/drivers/input/touchscreen/silead.c
>> +++ b/drivers/input/touchscreen/silead.c
>> @@ -29,6 +29,7 @@
>> #include <linux/input/touchscreen.h>
>> #include <linux/pm.h>
>> #include <linux/irq.h>
>> +#include <linux/regulator/consumer.h>
>>
>> #include <asm/unaligned.h>
>>
>> @@ -72,6 +73,8 @@ enum silead_ts_power {
>> struct silead_ts_data {
>> struct i2c_client *client;
>> struct gpio_desc *gpio_power;
>> + struct regulator *vddio;
>> + struct regulator *avdd;
>> struct input_dev *input;
>> char fw_name[64];
>> struct touchscreen_properties prop;
>> @@ -465,21 +468,52 @@ static int silead_ts_probe(struct i2c_client *client,
>> if (client->irq <= 0)
>> return -ENODEV;
>>
>> + data->vddio = devm_regulator_get_optional(dev, "vddio");
>> + if (IS_ERR(data->vddio)) {
>> + if (PTR_ERR(data->vddio) == -EPROBE_DEFER)
>> + return -EPROBE_DEFER;
>> + data->vddio = NULL;
>
> Why do we ignore other errors?
Other errors indicate that the regulator is not there
specifically I think regulator_get_optional will return -ENOENT
in that case.
> If there is an issue reported by
> regulator framework we should net be ignoring it.
>
> Unless regulator is truly optional (i.e. chip can work with some
> functionality powered off) and not simply hidden (firmware takes care of
> powering up system),
In most systems the vddio is simply hardwired to the always-on
vcc-3.3V
> we should not be using regulator_get_optional():
> if regulator is absent from ACPI/DT/etc, regulator framework will supply
> dummy regulator that you can enable/disable and not bothering checking
> whether it is NULL or not.
Right, the downside of that is that it prints a msg about this
in dmesg which typically will lead to user questions.
Anyways if you prefer the non _optional variant I can do a v3
with that ...
> Also, please consider using devm_regulator_bulk_get().
Hmm, so I would get:
In struct silead_ts_data:
struct regulator_bulk_data regulators[2];
And then in probe() do:
data->regulators[0].supply = "vddio";
data->regulators[1].supply = "avdd";
ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(data->regulators),
data->regulators);
if (ret)
return ret;
And modify the enable / disable code to match.
Yes I can see how that is better / cleaner and it also
answers the question whether or not to use get_optional.
Ok, I'll do a v2 switching to regulator_bulk_get.
Regards,
Hans
>
>> + }
>> +
>> + data->avdd = devm_regulator_get_optional(dev, "avdd");
>> + if (IS_ERR(data->avdd)) {
>> + if (PTR_ERR(data->avdd) == -EPROBE_DEFER)
>> + return -EPROBE_DEFER;
>> + data->avdd = NULL;
>> + }
>> +
>> + /*
>> + * Enable regulators at probe and disable them at remove, we need
>> + * to keep the chip powered otherwise it forgets its firmware.
>> + */
>> + if (data->vddio) {
>> + error = regulator_enable(data->vddio);
>> + if (error)
>> + return error;
>> + }
>> +
>> + if (data->avdd) {
>> + error = regulator_enable(data->avdd);
>> + if (error)
>> + goto disable_vddio;
>> + }
>
> Use devm_add_action_or_reset() to work regulator_bulk_disable call into
> devm stream. As it is you are leaving regulators on on unbind/remove.
>
>> +
>> /* Power GPIO pin */
>> data->gpio_power = devm_gpiod_get_optional(dev, "power", GPIOD_OUT_LOW);
>> if (IS_ERR(data->gpio_power)) {
>> if (PTR_ERR(data->gpio_power) != -EPROBE_DEFER)
>> dev_err(dev, "Shutdown GPIO request failed\n");
>> - return PTR_ERR(data->gpio_power);
>> + error = PTR_ERR(data->gpio_power);
>> + goto disable_avdd;
>> }
>>
>> error = silead_ts_setup(client);
>> if (error)
>> - return error;
>> + goto disable_avdd;
>>
>> error = silead_ts_request_input_dev(data);
>> if (error)
>> - return error;
>> + goto disable_avdd;
>>
>> error = devm_request_threaded_irq(dev, client->irq,
>> NULL, silead_ts_threaded_irq_handler,
>> @@ -487,10 +521,19 @@ static int silead_ts_probe(struct i2c_client *client,
>> if (error) {
>> if (error != -EPROBE_DEFER)
>> dev_err(dev, "IRQ request failed %d\n", error);
>> - return error;
>> + goto disable_avdd;
>> }
>>
>> return 0;
>> +
>> +disable_avdd:
>> + if (data->avdd)
>> + regulator_disable(data->avdd);
>> +disable_vddio:
>> + if (data->vddio)
>> + regulator_disable(data->vddio);
>> +
>> + return error;
>> }
>>
>> static int __maybe_unused silead_ts_suspend(struct device *dev)
>> --
>> 2.9.3
>>
>
> Thanks.
>
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* Re: [PATCH v2 2/6] mfd: stm32-adc: Add support for stm32 ADC
From: Fabrice Gasnier @ 2016-11-15 10:47 UTC (permalink / raw)
To: Lee Jones, Jonathan Cameron
Cc: linux-iio, linux-arm-kernel, devicetree, linux-kernel, linux,
robh+dt, mark.rutland, mcoquelin.stm32, alexandre.torgue, lars,
knaack.h, pmeerw
In-Reply-To: <20161114164701.GA2668@dell>
On 11/14/2016 05:47 PM, Lee Jones wrote:
> On Sat, 12 Nov 2016, Jonathan Cameron wrote:
>
>> On 10/11/16 16:18, Fabrice Gasnier wrote:
>>> Add core driver for STMicroelectronics STM32 ADC (Analog to Digital
>>> Converter). STM32 ADC can be composed of up to 3 ADCs with shared
>>> resources like clock prescaler, common interrupt line and analog
>>> reference voltage.
>>> This core driver basically manages shared resources.
>>>
>>> Signed-off-by: Fabrice Gasnier <fabrice.gasnier@st.com>
>> Looks good to me (other than the build issue obviously ;)
Hi Jonathan,
Thanks for your review.
I'll fix build issue, sure ;-)
>>
>> The fun bit will be trying to keep the whole thing this clean as you
>> add the more 'interesting' functionality. *fingers crossed*
Yes... But in the end, splitting shared resources in core driver makes
it more simple.
Not sure there will be more complexity here.
>>
>> Acked-by: Jonathan Cameron <jic23@kernel.org>
> There isn't anything MFD about this driver.
>
> Please move it into IIO.
Hmm, there is no other sub sysbtem that may be used here, ADC driver
belongs to IIO.
Also, of_platform_populate() is being used here. This can perfectly be
called from within IIO.
Jonathan, can this "stm32-adc-core" driver be moved to, and live in
drivers/iio/adc ?
(e.g. in addition to stm32-adc iio driver)
Is it ok for you ?
Please advise,
Best Regards,
Fabrice
>
>>> ---
>>> drivers/mfd/Kconfig | 14 ++
>>> drivers/mfd/Makefile | 1 +
>>> drivers/mfd/stm32-adc-core.c | 301 +++++++++++++++++++++++++++++++++++++
>>> include/linux/mfd/stm32-adc-core.h | 52 +++++++
>>> 4 files changed, 368 insertions(+)
>>> create mode 100644 drivers/mfd/stm32-adc-core.c
>>> create mode 100644 include/linux/mfd/stm32-adc-core.h
>>>
>>> diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
>>> index c6df644..2580cee 100644
>>> --- a/drivers/mfd/Kconfig
>>> +++ b/drivers/mfd/Kconfig
>>> @@ -1152,6 +1152,20 @@ config MFD_PALMAS
>>> If you say yes here you get support for the Palmas
>>> series of PMIC chips from Texas Instruments.
>>>
>>> +config MFD_STM32_ADC
>>> + tristate "STMicroelectronics STM32 adc"
>>> + depends on ARCH_STM32 || COMPILE_TEST
>>> + depends on OF
>>> + select MFD_CORE
>>> + select REGULATOR
>>> + select REGULATOR_FIXED_VOLTAGE
>>> + help
>>> + Select this option to enable the core driver for STMicroelectronics
>>> + STM32 analog-to-digital converter (ADC).
>>> +
>>> + This driver can also be built as a module. If so, the module
>>> + will be called stm32-adc-core.
>>> +
>>> config TPS6105X
>>> tristate "TI TPS61050/61052 Boost Converters"
>>> depends on I2C
>>> diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
>>> index 9834e66..4571506 100644
>>> --- a/drivers/mfd/Makefile
>>> +++ b/drivers/mfd/Makefile
>>> @@ -185,6 +185,7 @@ obj-$(CONFIG_MFD_INTEL_LPSS_PCI) += intel-lpss-pci.o
>>> obj-$(CONFIG_MFD_INTEL_LPSS_ACPI) += intel-lpss-acpi.o
>>> obj-$(CONFIG_MFD_INTEL_MSIC) += intel_msic.o
>>> obj-$(CONFIG_MFD_PALMAS) += palmas.o
>>> +obj-$(CONFIG_MFD_STM32_ADC) += stm32-adc-core.o
>>> obj-$(CONFIG_MFD_VIPERBOARD) += viperboard.o
>>> obj-$(CONFIG_MFD_RC5T583) += rc5t583.o rc5t583-irq.o
>>> obj-$(CONFIG_MFD_RK808) += rk808.o
>>> diff --git a/drivers/mfd/stm32-adc-core.c b/drivers/mfd/stm32-adc-core.c
>>> new file mode 100644
>>> index 0000000..bcf52fb
>>> --- /dev/null
>>> +++ b/drivers/mfd/stm32-adc-core.c
>>> @@ -0,0 +1,301 @@
>>> +/*
>>> + * This file is part of STM32 ADC driver
>>> + *
>>> + * Copyright (C) 2016, STMicroelectronics - All Rights Reserved
>>> + * Author: Fabrice Gasnier <fabrice.gasnier@st.com>.
>>> + *
>>> + * Inspired from: fsl-imx25-tsadc
>>> + *
>>> + * License type: GPLv2
>>> + *
>>> + * This program is free software; you can redistribute it and/or modify it
>>> + * under the terms of the GNU General Public License version 2 as published by
>>> + * the Free Software Foundation.
>>> + *
>>> + * This program is distributed in the hope that it will be useful, but
>>> + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
>>> + * or FITNESS FOR A PARTICULAR PURPOSE.
>>> + * See the GNU General Public License for more details.
>>> + *
>>> + * You should have received a copy of the GNU General Public License along with
>>> + * this program. If not, see <http://www.gnu.org/licenses/>.
>>> + */
>>> +
>>> +#include <linux/clk.h>
>>> +#include <linux/interrupt.h>
>>> +#include <linux/irqchip/chained_irq.h>
>>> +#include <linux/irqdesc.h>
>>> +#include <linux/irqdomain.h>
>>> +#include <linux/mfd/stm32-adc-core.h>
>>> +#include <linux/module.h>
>>> +#include <linux/of_device.h>
>>> +#include <linux/regulator/consumer.h>
>>> +#include <linux/slab.h>
>>> +
>>> +/* STM32F4 - common registers for all ADC instances: 1, 2 & 3 */
>>> +#define STM32F4_ADC_CSR (STM32_ADCX_COMN_OFFSET + 0x00)
>>> +#define STM32F4_ADC_CCR (STM32_ADCX_COMN_OFFSET + 0x04)
>>> +
>>> +/* STM32F4_ADC_CSR - bit fields */
>>> +#define STM32F4_EOC3 BIT(17)
>>> +#define STM32F4_EOC2 BIT(9)
>>> +#define STM32F4_EOC1 BIT(1)
>>> +
>>> +/* STM32F4_ADC_CCR - bit fields */
>>> +#define STM32F4_ADC_ADCPRE_SHIFT 16
>>> +#define STM32F4_ADC_ADCPRE_MASK GENMASK(17, 16)
>>> +
>>> +/* STM32 F4 maximum analog clock rate (from datasheet) */
>>> +#define STM32F4_ADC_MAX_CLK_RATE 36000000
>>> +
>>> +/**
>>> + * struct stm32_adc_priv - stm32 ADC core private data
>>> + * @irq: irq for ADC block
>>> + * @domain: irq domain reference
>>> + * @aclk: clock reference for the analog circuitry
>>> + * @vref: regulator reference
>>> + * @common: common data for all ADC instances
>>> + */
>>> +struct stm32_adc_priv {
>>> + int irq;
>>> + struct irq_domain *domain;
>>> + struct clk *aclk;
>>> + struct regulator *vref;
>>> + struct stm32_adc_common common;
>>> +};
>>> +
>>> +static struct stm32_adc_priv *to_stm32_adc_priv(struct stm32_adc_common *com)
>>> +{
>>> + return container_of(com, struct stm32_adc_priv, common);
>>> +}
>>> +
>>> +/* STM32F4 ADC internal common clock prescaler division ratios */
>>> +static int stm32f4_pclk_div[] = {2, 4, 6, 8};
>>> +
>>> +/**
>>> + * stm32f4_adc_clk_sel() - Select stm32f4 ADC common clock prescaler
>>> + * @priv: stm32 ADC core private data
>>> + * Select clock prescaler used for analog conversions, before using ADC.
>>> + */
>>> +static int stm32f4_adc_clk_sel(struct platform_device *pdev,
>>> + struct stm32_adc_priv *priv)
>>> +{
>>> + unsigned long rate;
>>> + u32 val;
>>> + int i;
>>> +
>>> + rate = clk_get_rate(priv->aclk);
>>> + for (i = 0; i < ARRAY_SIZE(stm32f4_pclk_div); i++) {
>>> + if ((rate / stm32f4_pclk_div[i]) <= STM32F4_ADC_MAX_CLK_RATE)
>>> + break;
>>> + }
>>> + if (i >= ARRAY_SIZE(stm32f4_pclk_div))
>>> + return -EINVAL;
>>> +
>>> + val = readl_relaxed(priv->common.base + STM32F4_ADC_CCR);
>>> + val &= ~STM32F4_ADC_ADCPRE_MASK;
>>> + val |= i << STM32F4_ADC_ADCPRE_SHIFT;
>>> + writel_relaxed(val, priv->common.base + STM32F4_ADC_CCR);
>>> +
>>> + dev_dbg(&pdev->dev, "Using analog clock source at %ld kHz\n",
>>> + rate / (stm32f4_pclk_div[i] * 1000));
>>> +
>>> + return 0;
>>> +}
>>> +
>>> +/* ADC common interrupt for all instances */
>>> +static void stm32_adc_irq_handler(struct irq_desc *desc)
>>> +{
>>> + struct stm32_adc_priv *priv = irq_desc_get_handler_data(desc);
>>> + struct irq_chip *chip = irq_desc_get_chip(desc);
>>> + u32 status;
>>> +
>>> + chained_irq_enter(chip, desc);
>>> + status = readl_relaxed(priv->common.base + STM32F4_ADC_CSR);
>>> +
>>> + if (status & STM32F4_EOC1)
>>> + generic_handle_irq(irq_find_mapping(priv->domain, 0));
>>> +
>>> + if (status & STM32F4_EOC2)
>>> + generic_handle_irq(irq_find_mapping(priv->domain, 1));
>>> +
>>> + if (status & STM32F4_EOC3)
>>> + generic_handle_irq(irq_find_mapping(priv->domain, 2));
>>> +
>>> + chained_irq_exit(chip, desc);
>>> +};
>>> +
>>> +static int stm32_adc_domain_map(struct irq_domain *d, unsigned int irq,
>>> + irq_hw_number_t hwirq)
>>> +{
>>> + irq_set_chip_data(irq, d->host_data);
>>> + irq_set_chip_and_handler(irq, &dummy_irq_chip, handle_level_irq);
>>> +
>>> + return 0;
>>> +}
>>> +
>>> +static void stm32_adc_domain_unmap(struct irq_domain *d, unsigned int irq)
>>> +{
>>> + irq_set_chip_and_handler(irq, NULL, NULL);
>>> + irq_set_chip_data(irq, NULL);
>>> +}
>>> +
>>> +static const struct irq_domain_ops stm32_adc_domain_ops = {
>>> + .map = stm32_adc_domain_map,
>>> + .unmap = stm32_adc_domain_unmap,
>>> + .xlate = irq_domain_xlate_onecell,
>>> +};
>>> +
>>> +static int stm32_adc_irq_probe(struct platform_device *pdev,
>>> + struct stm32_adc_priv *priv)
>>> +{
>>> + struct device_node *np = pdev->dev.of_node;
>>> +
>>> + priv->irq = platform_get_irq(pdev, 0);
>>> + if (priv->irq < 0) {
>>> + dev_err(&pdev->dev, "failed to get irq\n");
>>> + return priv->irq;
>>> + }
>>> +
>>> + priv->domain = irq_domain_add_simple(np, STM32_ADC_MAX_ADCS, 0,
>>> + &stm32_adc_domain_ops,
>>> + priv);
>>> + if (!priv->domain) {
>>> + dev_err(&pdev->dev, "Failed to add irq domain\n");
>>> + return -ENOMEM;
>>> + }
>>> +
>>> + irq_set_chained_handler(priv->irq, stm32_adc_irq_handler);
>>> + irq_set_handler_data(priv->irq, priv);
>>> +
>>> + return 0;
>>> +}
>>> +
>>> +static void stm32_adc_irq_remove(struct platform_device *pdev,
>>> + struct stm32_adc_priv *priv)
>>> +{
>>> + int hwirq;
>>> +
>>> + for (hwirq = 0; hwirq < STM32_ADC_MAX_ADCS; hwirq++)
>>> + irq_dispose_mapping(irq_find_mapping(priv->domain, hwirq));
>>> + irq_domain_remove(priv->domain);
>>> + irq_set_chained_handler(priv->irq, NULL);
>>> +}
>>> +
>>> +static int stm32_adc_probe(struct platform_device *pdev)
>>> +{
>>> + struct stm32_adc_priv *priv;
>>> + struct device_node *np = pdev->dev.of_node;
>>> + struct resource *res;
>>> + int ret;
>>> +
>>> + if (!pdev->dev.of_node)
>>> + return -ENODEV;
>>> +
>>> + priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
>>> + if (!priv)
>>> + return -ENOMEM;
>>> +
>>> + res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
>>> + priv->common.base = devm_ioremap_resource(&pdev->dev, res);
>>> + if (IS_ERR(priv->common.base))
>>> + return PTR_ERR(priv->common.base);
>>> +
>>> + priv->vref = devm_regulator_get(&pdev->dev, "vref");
>>> + if (IS_ERR(priv->vref)) {
>>> + ret = PTR_ERR(priv->vref);
>>> + dev_err(&pdev->dev, "vref get failed, %d\n", ret);
>>> + return ret;
>>> + }
>>> +
>>> + ret = regulator_enable(priv->vref);
>>> + if (ret < 0) {
>>> + dev_err(&pdev->dev, "vref enable failed\n");
>>> + return ret;
>>> + }
>>> +
>>> + ret = regulator_get_voltage(priv->vref);
>>> + if (ret < 0) {
>>> + dev_err(&pdev->dev, "vref get voltage failed, %d\n", ret);
>>> + goto err_regulator_disable;
>>> + }
>>> + priv->common.vref_mv = ret / 1000;
>>> + dev_dbg(&pdev->dev, "vref+=%dmV\n", priv->common.vref_mv);
>>> +
>>> + priv->aclk = devm_clk_get(&pdev->dev, "adc");
>>> + if (IS_ERR(priv->aclk)) {
>>> + ret = PTR_ERR(priv->aclk);
>>> + dev_err(&pdev->dev, "Can't get 'adc' clock\n");
>>> + goto err_regulator_disable;
>>> + }
>>> +
>>> + ret = clk_prepare_enable(priv->aclk);
>>> + if (ret < 0) {
>>> + dev_err(&pdev->dev, "adc clk enable failed\n");
>>> + goto err_regulator_disable;
>>> + }
>>> +
>>> + ret = stm32f4_adc_clk_sel(pdev, priv);
>>> + if (ret < 0) {
>>> + dev_err(&pdev->dev, "adc clk selection failed\n");
>>> + goto err_clk_disable;
>>> + }
>>> +
>>> + ret = stm32_adc_irq_probe(pdev, priv);
>>> + if (ret < 0)
>>> + goto err_clk_disable;
>>> +
>>> + platform_set_drvdata(pdev, &priv->common);
>>> +
>>> + ret = of_platform_populate(np, NULL, NULL, &pdev->dev);
>>> + if (ret < 0) {
>>> + dev_err(&pdev->dev, "failed to populate DT children\n");
>>> + goto err_irq_remove;
>>> + }
>>> +
>>> + return 0;
>>> +
>>> +err_irq_remove:
>>> + stm32_adc_irq_remove(pdev, priv);
>>> +
>>> +err_clk_disable:
>>> + clk_disable_unprepare(priv->aclk);
>>> +
>>> +err_regulator_disable:
>>> + regulator_disable(priv->vref);
>>> +
>>> + return ret;
>>> +}
>>> +
>>> +static int stm32_adc_remove(struct platform_device *pdev)
>>> +{
>>> + struct stm32_adc_common *common = platform_get_drvdata(pdev);
>>> + struct stm32_adc_priv *priv = to_stm32_adc_priv(common);
>>> +
>>> + of_platform_depopulate(&pdev->dev);
>>> + stm32_adc_irq_remove(pdev, priv);
>>> + clk_disable_unprepare(priv->aclk);
>>> + regulator_disable(priv->vref);
>>> +
>>> + return 0;
>>> +}
>>> +
>>> +static const struct of_device_id stm32_adc_of_match[] = {
>>> + { .compatible = "st,stm32f4-adc-core" },
>>> +};
>>> +MODULE_DEVICE_TABLE(of, stm32_adc_of_match);
>>> +
>>> +static struct platform_driver stm32_adc_driver = {
>>> + .probe = stm32_adc_probe,
>>> + .remove = stm32_adc_remove,
>>> + .driver = {
>>> + .name = "stm32-adc-core",
>>> + .of_match_table = stm32_adc_of_match,
>>> + },
>>> +};
>>> +module_platform_driver(stm32_adc_driver);
>>> +
>>> +MODULE_AUTHOR("Fabrice Gasnier <fabrice.gasnier@st.com>");
>>> +MODULE_DESCRIPTION("STMicroelectronics STM32 ADC MFD driver");
>>> +MODULE_LICENSE("GPL v2");
>>> +MODULE_ALIAS("platform:stm32-adc-core");
>>> diff --git a/include/linux/mfd/stm32-adc-core.h b/include/linux/mfd/stm32-adc-core.h
>>> new file mode 100644
>>> index 0000000..081fa5f
>>> --- /dev/null
>>> +++ b/include/linux/mfd/stm32-adc-core.h
>>> @@ -0,0 +1,52 @@
>>> +/*
>>> + * This file is part of STM32 ADC driver
>>> + *
>>> + * Copyright (C) 2016, STMicroelectronics - All Rights Reserved
>>> + * Author: Fabrice Gasnier <fabrice.gasnier@st.com>.
>>> + *
>>> + * License type: GPLv2
>>> + *
>>> + * This program is free software; you can redistribute it and/or modify it
>>> + * under the terms of the GNU General Public License version 2 as published by
>>> + * the Free Software Foundation.
>>> + *
>>> + * This program is distributed in the hope that it will be useful, but
>>> + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
>>> + * or FITNESS FOR A PARTICULAR PURPOSE.
>>> + * See the GNU General Public License for more details.
>>> + *
>>> + * You should have received a copy of the GNU General Public License along with
>>> + * this program. If not, see <http://www.gnu.org/licenses/>.
>>> + */
>>> +
>>> +#ifndef __STM32_ADC_H
>>> +#define __STM32_ADC_H
>>> +
>>> +/*
>>> + * STM32 - ADC global register map
>>> + * ________________________________________________________
>>> + * | Offset | Register |
>>> + * --------------------------------------------------------
>>> + * | 0x000 | Master ADC1 |
>>> + * --------------------------------------------------------
>>> + * | 0x100 | Slave ADC2 |
>>> + * --------------------------------------------------------
>>> + * | 0x200 | Slave ADC3 |
>>> + * --------------------------------------------------------
>>> + * | 0x300 | Master & Slave common regs |
>>> + * --------------------------------------------------------
>>> + */
>>> +#define STM32_ADC_MAX_ADCS 3
>>> +#define STM32_ADCX_COMN_OFFSET 0x300
>>> +
>>> +/**
>>> + * struct stm32_adc_common - stm32 ADC driver common data (for all instances)
>>> + * @base: control registers base cpu addr
>>> + * @vref_mv: vref voltage (mv)
>>> + */
>>> +struct stm32_adc_common {
>>> + void __iomem *base;
>>> + int vref_mv;
>>> +};
>>> +
>>> +#endif
>>>
^ permalink raw reply
* Re: [PATCH v4 4/4] ARM: dts: da850: Add the usb otg device node
From: Sekhar Nori @ 2016-11-15 10:46 UTC (permalink / raw)
To: Alexandre Bailon, khilman, robh+dt, b-liu
Cc: linux-kernel, linux-usb, devicetree, linux-arm-kernel
In-Reply-To: <1478188752-22447-5-git-send-email-abailon@baylibre.com>
On Thursday 03 November 2016 09:29 PM, Alexandre Bailon wrote:
> This adds the device tree node for the usb otg
> controller present in the da850 family of SoC's.
> This also enables the otg usb controller for the lcdk board.
>
> Signed-off-by: Alexandre Bailon <abailon@baylibre.com>
> ---
> arch/arm/boot/dts/da850-lcdk.dts | 8 ++++++++
> arch/arm/boot/dts/da850.dtsi | 15 +++++++++++++++
> 2 files changed, 23 insertions(+)
>
> diff --git a/arch/arm/boot/dts/da850-lcdk.dts b/arch/arm/boot/dts/da850-lcdk.dts
> index 7b8ab21..9f5040c 100644
> --- a/arch/arm/boot/dts/da850-lcdk.dts
> +++ b/arch/arm/boot/dts/da850-lcdk.dts
> @@ -158,6 +158,14 @@
> rx-num-evt = <32>;
> };
>
> +&usb_phy {
> + status = "okay";
> + };
As mentioned by David already, this node needs to be removed. Please
rebase this on top of latest linux-davinci/master when ready for merging
(driver changes accepted).
> +
> +&usb0 {
> + status = "okay";
> +};
> +
> &aemif {
> pinctrl-names = "default";
> pinctrl-0 = <&nand_pins>;
> diff --git a/arch/arm/boot/dts/da850.dtsi b/arch/arm/boot/dts/da850.dtsi
> index f79e1b9..322a31a 100644
> --- a/arch/arm/boot/dts/da850.dtsi
> +++ b/arch/arm/boot/dts/da850.dtsi
> @@ -372,6 +372,21 @@
> >;
> status = "disabled";
> };
> + usb_phy: usb-phy {
> + compatible = "ti,da830-usb-phy";
> + #phy-cells = <1>;
> + status = "disabled";
> + };
> + usb0: usb@200000 {
> + compatible = "ti,da830-musb";
> + reg = <0x200000 0x10000>;
> + interrupts = <58>;
> + interrupt-names = "mc";
> + dr_mode = "otg";
> + phys = <&usb_phy 0>;
> + phy-names = "usb-phy";
> + status = "disabled";
> + };
Can you separate out the soc specific changes from board changes? Please
place the usb0 node above the mdio node. I am trying to get to a rough
ordering based on reg property.
Thanks,
Sekhar
^ permalink raw reply
* Re: [PATCH v9 2/4] soc: mediatek: Init MT8173 scpsys driver earlier
From: Honghui Zhang @ 2016-11-15 10:45 UTC (permalink / raw)
To: James Liao
Cc: Matthias Brugger, Yong Wu, Rob Herring,
srv_heupstream-NuS5LvNUpcJWk0Htik3J/w,
devicetree-u79uwXL29TY76Z2rM5mHXA, Kevin Hilman,
linux-kernel-u79uwXL29TY76Z2rM5mHXA,
linux-mediatek-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, Sascha Hauer,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r
In-Reply-To: <1477879702.6649.4.camel@mtksdaap41>
On Mon, 2016-10-31 at 10:08 +0800, James Liao wrote:
> On Mon, 2016-10-31 at 01:08 +0100, Matthias Brugger wrote:
> > Hi James,
> >
> > On 10/20/2016 10:56 AM, James Liao wrote:
> > > Some power domain comsumers may init before module_init.
> > > So the power domain provider (scpsys) need to be initialized
> > > earlier too.
> > >
> > > Take an example for our IOMMU (M4U) and SMI. SMI is a bridge
> > > between IOMMU and multimedia HW. SMI is responsible to
> > > enable/disable iommu and help transfer data for each multimedia
> > > HW. Both of them have to wait until the power and clocks are
> > > enabled.
> > >
> > > So scpsys driver should be initialized before SMI, and SMI should
> > > be initialized before IOMMU, and then init IOMMU consumers
> > > (display/vdec/venc/camera etc.).
> > >
> > > IOMMU is subsys_init by default. So we need to init scpsys driver
> > > before subsys_init.
> > >
> > > Signed-off-by: James Liao <jamesjj.liao-NuS5LvNUpcJWk0Htik3J/w@public.gmane.org>
> > > Reviewed-by: Kevin Hilman <khilman-rdvid1DuHRBWk0Htik3J/w@public.gmane.org>
> > > ---
> >
> > I didn't applied this patch for now.
> > I answered you in v7 of this series [1]. I would prefer to see if we can
> > fix that otherwise.
> >
> > Would be great if you or Yong could provide some feedback.
> >
Hi, Matthias,
Yong had verified your propose and it seems didn't work[1].
But I'm looking at the recently merged device link patches[2], it maybe
could help with the probe sequence issue.
If we set the iommu client as the smi larb's device consumer of
device_link with the DL_DEV_PROBING flags, it will be wait for smi
larb's probe done. But I'm not test that solution yet, need to do a bit
more test to verify this.
thanks.
[1]
https://lists.linuxfoundation.org/pipermail/iommu/2016-July/018034.html
[2]https://www.mail-archive.com/linux-kernel-u79uwXL29TY76Z2rM5mHXA@public.gmane.org/msg1261311.html
> > Thanks,
> > Matthias
> >
> > [1] https://patchwork.kernel.org/patch/9397405/
> >
> > > drivers/soc/mediatek/mtk-scpsys.c | 19 ++++++++++++++++++-
> > > 1 file changed, 18 insertions(+), 1 deletion(-)
> > >
> > > diff --git a/drivers/soc/mediatek/mtk-scpsys.c b/drivers/soc/mediatek/mtk-scpsys.c
> > > index fa9ee69..dd7a07d 100644
> > > --- a/drivers/soc/mediatek/mtk-scpsys.c
> > > +++ b/drivers/soc/mediatek/mtk-scpsys.c
> > > @@ -613,4 +613,21 @@ static int scpsys_probe(struct platform_device *pdev)
> > > .of_match_table = of_match_ptr(of_scpsys_match_tbl),
> > > },
> _______________________________________________
> Linux-mediatek mailing list
> Linux-mediatek-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r@public.gmane.org
> http://lists.infradead.org/mailman/listinfo/linux-mediatek
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* Re: [PATCH v7 0/3] Add clockevent for timer-nps driver to NPS400 SoC
From: Daniel Lezcano @ 2016-11-15 10:33 UTC (permalink / raw)
To: Noam Camus
Cc: robh+dt-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org,
mark.rutland-5wv7dgnIgG8@public.gmane.org,
tglx-hfZtesqFncYOwBW4kG4KsQ@public.gmane.org,
devicetree-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
linux-kernel-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
Vineet Gupta
In-Reply-To: <DB6PR0501MB2518DAE81C1F5F87A6A1A2DDAABF0-wTfl6qNNZ1PL+HUNrKNnF8DSnupUy6xnnBOFsp37pqbUKgpGm//BTAC/G2K4zDHf@public.gmane.org>
On Tue, Nov 15, 2016 at 10:02:01AM +0000, Noam Camus wrote:
> > From: Daniel Lezcano [mailto:daniel.lezcano-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org]
> > Sent: Tuesday, November 15, 2016 11:58 AM
>
> >Do you want me to take the entire series in my tree, or do you want my acked-by to push them in your tree ?
> I do not have tree of my own.
> Will appreciate if you take entire series.
> I believe it is same as you do with Synopsys ARC driver (by Vineet Gupta)
>
> Many Thanks
> Noam
Ok, I will sort it out with Vineet.
Thanks.
-- Daniel
--
<http://www.linaro.org/> Linaro.org │ Open source software for ARM SoCs
Follow Linaro: <http://www.facebook.com/pages/Linaro> Facebook |
<http://twitter.com/#!/linaroorg> Twitter |
<http://www.linaro.org/linaro-blog/> Blog
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* Re: [PATCH v5 2/3] vcodec: mediatek: Add Mediatek JPEG Decoder Driver
From: Rick Chang @ 2016-11-15 10:31 UTC (permalink / raw)
To: Hans Verkuil
Cc: Hans Verkuil, Laurent Pinchart, Mauro Carvalho Chehab,
Matthias Brugger, Rob Herring,
linux-kernel-u79uwXL29TY76Z2rM5mHXA,
linux-media-u79uwXL29TY76Z2rM5mHXA,
srv_heupstream-NuS5LvNUpcJWk0Htik3J/w,
linux-mediatek-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
devicetree-u79uwXL29TY76Z2rM5mHXA, Minghsiu Tsai
In-Reply-To: <c0c2be8e-51ff-843a-6f3d-1fa0170cb209-qWit8jRvyhVmR6Xm/wNWPw@public.gmane.org>
Hi Hans,
Thank you for your review. I will fix those problems in patch v6.
On Fri, 2016-11-11 at 16:10 +0100, Hans Verkuil wrote:
> A quick review:
>
> On 11/08/2016 07:34 AM, Rick Chang wrote:
> > Add v4l2 driver for Mediatek JPEG Decoder
> >
> > Signed-off-by: Rick Chang <rick.chang-NuS5LvNUpcJWk0Htik3J/w@public.gmane.org>
> > Signed-off-by: Minghsiu Tsai <minghsiu.tsai-NuS5LvNUpcJWk0Htik3J/w@public.gmane.org>
> > ---
> > drivers/media/platform/Kconfig | 15 +
> > drivers/media/platform/Makefile | 2 +
> > drivers/media/platform/mtk-jpeg/Makefile | 2 +
> > drivers/media/platform/mtk-jpeg/mtk_jpeg_core.c | 1275 ++++++++++++++++++++++
> > drivers/media/platform/mtk-jpeg/mtk_jpeg_core.h | 141 +++
> > drivers/media/platform/mtk-jpeg/mtk_jpeg_hw.c | 417 +++++++
> > drivers/media/platform/mtk-jpeg/mtk_jpeg_hw.h | 91 ++
> > drivers/media/platform/mtk-jpeg/mtk_jpeg_parse.c | 160 +++
> > drivers/media/platform/mtk-jpeg/mtk_jpeg_parse.h | 25 +
> > drivers/media/platform/mtk-jpeg/mtk_jpeg_reg.h | 58 +
> > 10 files changed, 2186 insertions(+)
> > create mode 100644 drivers/media/platform/mtk-jpeg/Makefile
> > create mode 100644 drivers/media/platform/mtk-jpeg/mtk_jpeg_core.c
> > create mode 100644 drivers/media/platform/mtk-jpeg/mtk_jpeg_core.h
> > create mode 100644 drivers/media/platform/mtk-jpeg/mtk_jpeg_hw.c
> > create mode 100644 drivers/media/platform/mtk-jpeg/mtk_jpeg_hw.h
> > create mode 100644 drivers/media/platform/mtk-jpeg/mtk_jpeg_parse.c
> > create mode 100644 drivers/media/platform/mtk-jpeg/mtk_jpeg_parse.h
> > create mode 100644 drivers/media/platform/mtk-jpeg/mtk_jpeg_reg.h
> >
> > diff --git a/drivers/media/platform/Kconfig b/drivers/media/platform/Kconfig
> > index 754edbf1..96c9887 100644
> > --- a/drivers/media/platform/Kconfig
> > +++ b/drivers/media/platform/Kconfig
> > @@ -162,6 +162,21 @@ config VIDEO_CODA
> > Coda is a range of video codec IPs that supports
> > H.264, MPEG-4, and other video formats.
> >
> > +config VIDEO_MEDIATEK_JPEG
> > + tristate "Mediatek JPEG Codec driver"
> > + depends on MTK_IOMMU_V1 || COMPILE_TEST
> > + depends on VIDEO_DEV && VIDEO_V4L2
> > + depends on ARCH_MEDIATEK || COMPILE_TEST
> > + depends on HAS_DMA
> > + select VIDEOBUF2_DMA_CONTIG
> > + select V4L2_MEM2MEM_DEV
> > + ---help---
> > + Mediatek jpeg codec driver provides HW capability to decode
> > + JPEG format
> > +
> > + To compile this driver as a module, choose M here: the
> > + module will be called mtk-jpeg
> > +
> > config VIDEO_MEDIATEK_VPU
> > tristate "Mediatek Video Processor Unit"
> > depends on VIDEO_DEV && VIDEO_V4L2 && HAS_DMA
> > diff --git a/drivers/media/platform/Makefile b/drivers/media/platform/Makefile
> > index f842933..cf701e3 100644
> > --- a/drivers/media/platform/Makefile
> > +++ b/drivers/media/platform/Makefile
> > @@ -68,3 +68,5 @@ obj-$(CONFIG_VIDEO_MEDIATEK_VPU) += mtk-vpu/
> > obj-$(CONFIG_VIDEO_MEDIATEK_VCODEC) += mtk-vcodec/
> >
> > obj-$(CONFIG_VIDEO_MEDIATEK_MDP) += mtk-mdp/
> > +
> > +obj-$(CONFIG_VIDEO_MEDIATEK_JPEG) += mtk-jpeg/
> > diff --git a/drivers/media/platform/mtk-jpeg/Makefile b/drivers/media/platform/mtk-jpeg/Makefile
> > new file mode 100644
> > index 0000000..b2e6069
> > --- /dev/null
> > +++ b/drivers/media/platform/mtk-jpeg/Makefile
> > @@ -0,0 +1,2 @@
> > +mtk_jpeg-objs := mtk_jpeg_core.o mtk_jpeg_hw.o mtk_jpeg_parse.o
> > +obj-$(CONFIG_VIDEO_MEDIATEK_JPEG) += mtk_jpeg.o
> > diff --git a/drivers/media/platform/mtk-jpeg/mtk_jpeg_core.c b/drivers/media/platform/mtk-jpeg/mtk_jpeg_core.c
> > new file mode 100644
> > index 0000000..33ddf79
> > --- /dev/null
> > +++ b/drivers/media/platform/mtk-jpeg/mtk_jpeg_core.c
> > @@ -0,0 +1,1275 @@
> > +/*
> > + * Copyright (c) 2016 MediaTek Inc.
> > + * Author: Ming Hsiu Tsai <minghsiu.tsai-NuS5LvNUpcJWk0Htik3J/w@public.gmane.org>
> > + * Rick Chang <rick.chang-NuS5LvNUpcJWk0Htik3J/w@public.gmane.org>
> > + *
> > + * This program is free software; you can redistribute it and/or modify
> > + * it under the terms of the GNU General Public License version 2 as
> > + * published by the Free Software Foundation.
> > + *
> > + * This program is distributed in the hope that it will be useful,
> > + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> > + * GNU General Public License for more details.
> > + */
> > +
> > +#include <linux/clk.h>
> > +#include <linux/err.h>
> > +#include <linux/interrupt.h>
> > +#include <linux/io.h>
> > +#include <linux/kernel.h>
> > +#include <linux/module.h>
> > +#include <linux/of_platform.h>
> > +#include <linux/platform_device.h>
> > +#include <linux/pm_runtime.h>
> > +#include <linux/spinlock.h>
> > +#include <media/v4l2-event.h>
> > +#include <media/v4l2-mem2mem.h>
> > +#include <media/v4l2-ioctl.h>
> > +#include <media/videobuf2-core.h>
> > +#include <media/videobuf2-dma-contig.h>
> > +#include <soc/mediatek/smi.h>
> > +#include <asm/dma-iommu.h>
> > +
> > +#include "mtk_jpeg_hw.h"
> > +#include "mtk_jpeg_core.h"
> > +#include "mtk_jpeg_parse.h"
> > +
> > +static struct mtk_jpeg_fmt mtk_jpeg_formats[] = {
> > + {
> > + .name = "JPEG JFIF",
> > + .fourcc = V4L2_PIX_FMT_JPEG,
> > + .colplanes = 1,
> > + .flags = MTK_JPEG_FMT_FLAG_DEC_OUTPUT,
> > + },
> > + {
> > + .name = "YUV 4:2:0 non-contiguous 3-planar, Y/Cb/Cr",
> > + .fourcc = V4L2_PIX_FMT_YUV420M,
> > + .h_sample = {4, 2, 2},
> > + .v_sample = {4, 2, 2},
> > + .colplanes = 3,
> > + .h_align = 5,
> > + .v_align = 4,
> > + .flags = MTK_JPEG_FMT_FLAG_DEC_CAPTURE,
> > + },
> > + {
> > + .name = "YUV 4:2:2 non-contiguous 3-planar, Y/Cb/Cr",
> > + .fourcc = V4L2_PIX_FMT_YUV422M,
> > + .h_sample = {4, 2, 2},
> > + .v_sample = {4, 4, 4},
> > + .colplanes = 3,
> > + .h_align = 5,
> > + .v_align = 3,
> > + .flags = MTK_JPEG_FMT_FLAG_DEC_CAPTURE,
>
> You probably don't need the name since it is filled in by the v4l2 core
> (v4l2-ioctls.c).
Ok.
> > + },
> > +};
> > +
> > +#define MTK_JPEG_NUM_FORMATS ARRAY_SIZE(mtk_jpeg_formats)
> > +
> > +enum {
> > + MTK_JPEG_BUF_FLAGS_INIT = 0,
> > + MTK_JPEG_BUF_FLAGS_LAST_FRAME = 1,
> > +};
> > +
> > +struct mtk_jpeg_src_buf {
> > + struct vb2_v4l2_buffer b;
> > + struct list_head list;
> > + int flags;
> > + struct mtk_jpeg_dec_param dec_param;
> > +};
> > +
> > +static int debug;
> > +module_param(debug, int, 0644);
> > +
> > +static inline struct mtk_jpeg_ctx *mtk_jpeg_fh_to_ctx(struct v4l2_fh *fh)
> > +{
> > + return container_of(fh, struct mtk_jpeg_ctx, fh);
> > +}
> > +
> > +static inline struct mtk_jpeg_src_buf *mtk_jpeg_vb2_to_srcbuf(
> > + struct vb2_buffer *vb)
> > +{
> > + return container_of(to_vb2_v4l2_buffer(vb), struct mtk_jpeg_src_buf, b);
> > +}
> > +
> > +static int mtk_jpeg_querycap(struct file *file, void *priv,
> > + struct v4l2_capability *cap)
> > +{
> > + struct mtk_jpeg_dev *jpeg = video_drvdata(file);
> > +
> > + strlcpy(cap->driver, MTK_JPEG_NAME " decoder", sizeof(cap->driver));
> > + strlcpy(cap->card, MTK_JPEG_NAME " decoder", sizeof(cap->card));
> > + snprintf(cap->bus_info, sizeof(cap->bus_info), "platform:%s",
> > + dev_name(jpeg->dev));
> > +
> > + return 0;
> > +}
> > +
> > +static int mtk_jpeg_enum_fmt(struct mtk_jpeg_fmt *mtk_jpeg_formats, int n,
> > + struct v4l2_fmtdesc *f, u32 type)
> > +{
> > + int i, num = 0;
> > +
> > + for (i = 0; i < n; ++i) {
> > + if (mtk_jpeg_formats[i].flags & type) {
> > + if (num == f->index)
> > + break;
> > + ++num;
> > + }
> > + }
> > +
> > + if (i >= n)
> > + return -EINVAL;
> > +
> > + f->pixelformat = mtk_jpeg_formats[i].fourcc;
> > +
> > + return 0;
> > +}
> > +
> > +static int mtk_jpeg_enum_fmt_vid_cap(struct file *file, void *priv,
> > + struct v4l2_fmtdesc *f)
> > +{
> > + return mtk_jpeg_enum_fmt(mtk_jpeg_formats, MTK_JPEG_NUM_FORMATS, f,
> > + MTK_JPEG_FMT_FLAG_DEC_CAPTURE);
> > +}
> > +
> > +static int mtk_jpeg_enum_fmt_vid_out(struct file *file, void *priv,
> > + struct v4l2_fmtdesc *f)
> > +{
> > + return mtk_jpeg_enum_fmt(mtk_jpeg_formats, MTK_JPEG_NUM_FORMATS, f,
> > + MTK_JPEG_FMT_FLAG_DEC_OUTPUT);
> > +}
> > +
> > +static struct mtk_jpeg_q_data *mtk_jpeg_get_q_data(struct mtk_jpeg_ctx *ctx,
> > + enum v4l2_buf_type type)
> > +{
> > + if (V4L2_TYPE_IS_OUTPUT(type))
> > + return &ctx->out_q;
> > + else
>
> No need for 'else'.
Ok.
> > + return &ctx->cap_q;
> > +}
> > +
> > +static struct mtk_jpeg_fmt *mtk_jpeg_find_format(struct mtk_jpeg_ctx *ctx,
> > + u32 pixelformat,
> > + unsigned int fmt_type)
> > +{
> > + unsigned int k, fmt_flag;
> > +
> > + fmt_flag = (fmt_type == MTK_JPEG_FMT_TYPE_OUTPUT) ?
> > + MTK_JPEG_FMT_FLAG_DEC_OUTPUT :
> > + MTK_JPEG_FMT_FLAG_DEC_CAPTURE;
> > +
> > + for (k = 0; k < MTK_JPEG_NUM_FORMATS; k++) {
> > + struct mtk_jpeg_fmt *fmt = &mtk_jpeg_formats[k];
> > +
> > + if (fmt->fourcc == pixelformat && fmt->flags & fmt_flag)
> > + return fmt;
> > + }
> > +
> > + return NULL;
> > +}
> > +
> > +static void mtk_jpeg_bound_align_image(u32 *w, unsigned int wmin,
> > + unsigned int wmax, unsigned int walign,
> > + u32 *h, unsigned int hmin,
> > + unsigned int hmax, unsigned int halign)
> > +{
> > + int width, height, w_step, h_step;
> > +
> > + width = *w;
> > + height = *h;
> > + w_step = 1 << walign;
> > + h_step = 1 << halign;
> > +
> > + v4l_bound_align_image(w, wmin, wmax, walign, h, hmin, hmax, halign, 0);
> > + if (*w < width && (*w + w_step) <= wmax)
> > + *w += w_step;
> > + if (*h < height && (*h + h_step) <= hmax)
> > + *h += h_step;
> > +}
> > +
> > +static void mtk_jpeg_adjust_fmt_mplane(struct mtk_jpeg_ctx *ctx,
> > + struct v4l2_format *f)
> > +{
> > + struct v4l2_pix_format_mplane *pix_mp = &f->fmt.pix_mp;
> > + struct mtk_jpeg_q_data *q_data;
> > + int i;
> > +
> > + q_data = mtk_jpeg_get_q_data(ctx, f->type);
> > +
> > + pix_mp->width = q_data->w;
> > + pix_mp->height = q_data->h;
> > + pix_mp->pixelformat = q_data->fmt->fourcc;
> > + pix_mp->num_planes = q_data->fmt->colplanes;
> > +
> > + for (i = 0; i < pix_mp->num_planes; i++) {
> > + pix_mp->plane_fmt[i].bytesperline = q_data->bytesperline[i];
> > + pix_mp->plane_fmt[i].sizeimage = q_data->sizeimage[i];
> > + }
> > +}
> > +
> > +static int mtk_jpeg_try_fmt_mplane(struct v4l2_format *f,
> > + struct mtk_jpeg_fmt *fmt,
> > + struct mtk_jpeg_ctx *ctx, int q_type)
> > +{
> > + struct v4l2_pix_format_mplane *pix_mp = &f->fmt.pix_mp;
> > + struct mtk_jpeg_dev *jpeg = ctx->jpeg;
> > + int i;
> > +
> > + memset(pix_mp->reserved, 0, sizeof(pix_mp->reserved));
> > + pix_mp->field = V4L2_FIELD_NONE;
> > +
> > + if (ctx->state != MTK_JPEG_INIT) {
> > + mtk_jpeg_adjust_fmt_mplane(ctx, f);
> > + goto end;
> > + }
> > +
> > + pix_mp->num_planes = fmt->colplanes;
> > + pix_mp->pixelformat = fmt->fourcc;
> > +
> > + if (q_type == MTK_JPEG_FMT_TYPE_OUTPUT) {
> > + struct v4l2_plane_pix_format *pfmt = &pix_mp->plane_fmt[0];
> > +
> > + mtk_jpeg_bound_align_image(&pix_mp->width, MTK_JPEG_MIN_WIDTH,
> > + MTK_JPEG_MAX_WIDTH, 0,
> > + &pix_mp->height, MTK_JPEG_MIN_HEIGHT,
> > + MTK_JPEG_MAX_HEIGHT, 0);
> > +
> > + memset(pfmt->reserved, 0, sizeof(pfmt->reserved));
> > + pfmt->bytesperline = 0;
> > + /* Source size must be aligned to 128 */
> > + pfmt->sizeimage = mtk_jpeg_align(pfmt->sizeimage, 128);
> > + if (pfmt->sizeimage == 0)
> > + pfmt->sizeimage = MTK_JPEG_DEFAULT_SIZEIMAGE;
> > + goto end;
> > + }
> > +
> > + /* type is MTK_JPEG_FMT_TYPE_CAPTURE */
> > + mtk_jpeg_bound_align_image(&pix_mp->width, MTK_JPEG_MIN_WIDTH,
> > + MTK_JPEG_MAX_WIDTH, fmt->h_align,
> > + &pix_mp->height, MTK_JPEG_MIN_HEIGHT,
> > + MTK_JPEG_MAX_HEIGHT, fmt->v_align);
> > +
> > + for (i = 0; i < fmt->colplanes; i++) {
> > + struct v4l2_plane_pix_format *pfmt = &pix_mp->plane_fmt[i];
> > + u32 stride = pix_mp->width * fmt->h_sample[i] / 4;
> > + u32 h = pix_mp->height * fmt->v_sample[i] / 4;
> > +
> > + memset(pfmt->reserved, 0, sizeof(pfmt->reserved));
> > + pfmt->bytesperline = stride;
> > + pfmt->sizeimage = stride * h;
> > + }
> > +end:
> > + v4l2_dbg(2, debug, &jpeg->v4l2_dev, "wxh:%ux%u\n",
> > + pix_mp->width, pix_mp->height);
> > + for (i = 0; i < pix_mp->num_planes; i++) {
> > + v4l2_dbg(2, debug, &jpeg->v4l2_dev,
> > + "plane[%d] bpl=%u, size=%u\n",
> > + i,
> > + pix_mp->plane_fmt[i].bytesperline,
> > + pix_mp->plane_fmt[i].sizeimage);
> > + }
> > + return 0;
> > +}
> > +
> > +static int mtk_jpeg_g_fmt_vid_mplane(struct file *file, void *priv,
> > + struct v4l2_format *f)
> > +{
> > + struct vb2_queue *vq;
> > + struct mtk_jpeg_q_data *q_data = NULL;
> > + struct v4l2_pix_format_mplane *pix_mp = &f->fmt.pix_mp;
> > + struct mtk_jpeg_ctx *ctx = mtk_jpeg_fh_to_ctx(priv);
> > + struct mtk_jpeg_dev *jpeg = ctx->jpeg;
> > + int i;
> > +
> > + vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type);
> > + if (!vq)
> > + return -EINVAL;
> > +
> > + q_data = mtk_jpeg_get_q_data(ctx, f->type);
> > +
> > + memset(pix_mp->reserved, 0, sizeof(pix_mp->reserved));
> > + pix_mp->width = q_data->w;
> > + pix_mp->height = q_data->h;
> > + pix_mp->field = V4L2_FIELD_NONE;
> > + pix_mp->pixelformat = q_data->fmt->fourcc;
> > + pix_mp->num_planes = q_data->fmt->colplanes;
> > + pix_mp->colorspace = ctx->colorspace;
> > + pix_mp->ycbcr_enc = ctx->ycbcr_enc;
> > + pix_mp->xfer_func = ctx->xfer_func;
> > + pix_mp->quantization = ctx->quantization;
> > +
> > + v4l2_dbg(1, debug, &jpeg->v4l2_dev, "(%d) g_fmt:%s wxh:%ux%u\n",
> > + f->type, q_data->fmt->name, pix_mp->width, pix_mp->height);
> > +
> > + for (i = 0; i < pix_mp->num_planes; i++) {
> > + struct v4l2_plane_pix_format *pfmt = &pix_mp->plane_fmt[i];
> > +
> > + pfmt->bytesperline = q_data->bytesperline[i];
> > + pfmt->sizeimage = q_data->sizeimage[i];
> > + memset(pfmt->reserved, 0, sizeof(pfmt->reserved));
> > +
> > + v4l2_dbg(1, debug, &jpeg->v4l2_dev,
> > + "plane[%d] bpl=%u, size=%u\n",
> > + i,
> > + pfmt->bytesperline,
> > + pfmt->sizeimage);
> > + }
> > + return 0;
> > +}
> > +
> > +static int mtk_jpeg_try_fmt_vid_cap_mplane(struct file *file, void *priv,
> > + struct v4l2_format *f)
> > +{
> > + struct mtk_jpeg_ctx *ctx = mtk_jpeg_fh_to_ctx(priv);
> > + struct mtk_jpeg_fmt *fmt;
> > +
> > + fmt = mtk_jpeg_find_format(ctx, f->fmt.pix_mp.pixelformat,
> > + MTK_JPEG_FMT_TYPE_CAPTURE);
> > + if (!fmt)
> > + fmt = ctx->cap_q.fmt;
> > +
> > + v4l2_dbg(2, debug, &ctx->jpeg->v4l2_dev, "(%d) try_fmt:%s\n",
> > + f->type, fmt->name);
> > +
> > + return mtk_jpeg_try_fmt_mplane(f, fmt, ctx, MTK_JPEG_FMT_TYPE_CAPTURE);
> > +}
> > +
> > +static int mtk_jpeg_try_fmt_vid_out_mplane(struct file *file, void *priv,
> > + struct v4l2_format *f)
> > +{
> > + struct mtk_jpeg_ctx *ctx = mtk_jpeg_fh_to_ctx(priv);
> > + struct mtk_jpeg_fmt *fmt;
> > +
> > + fmt = mtk_jpeg_find_format(ctx, f->fmt.pix_mp.pixelformat,
> > + MTK_JPEG_FMT_TYPE_OUTPUT);
> > + if (!fmt)
> > + fmt = ctx->out_q.fmt;
> > +
> > + v4l2_dbg(2, debug, &ctx->jpeg->v4l2_dev, "(%d) try_fmt:%s\n",
> > + f->type, fmt->name);
> > +
> > + return mtk_jpeg_try_fmt_mplane(f, fmt, ctx, MTK_JPEG_FMT_TYPE_OUTPUT);
> > +}
> > +
> > +static int mtk_jpeg_s_fmt_mplane(struct mtk_jpeg_ctx *ctx,
> > + struct v4l2_format *f)
> > +{
> > + struct vb2_queue *vq;
> > + struct mtk_jpeg_q_data *q_data = NULL;
> > + struct v4l2_pix_format_mplane *pix_mp = &f->fmt.pix_mp;
> > + struct mtk_jpeg_dev *jpeg = ctx->jpeg;
> > + unsigned int f_type;
> > + int i;
> > +
> > + vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type);
> > + if (!vq)
> > + return -EINVAL;
> > +
> > + q_data = mtk_jpeg_get_q_data(ctx, f->type);
> > +
> > + if (vb2_is_busy(vq)) {
> > + v4l2_err(&jpeg->v4l2_dev, "queue busy\n");
> > + return -EBUSY;
> > + }
> > +
> > + f_type = V4L2_TYPE_IS_OUTPUT(f->type) ?
> > + MTK_JPEG_FMT_TYPE_OUTPUT : MTK_JPEG_FMT_TYPE_CAPTURE;
> > +
> > + q_data->fmt = mtk_jpeg_find_format(ctx, pix_mp->pixelformat, f_type);
> > + q_data->w = pix_mp->width;
> > + q_data->h = pix_mp->height;
> > + ctx->colorspace = pix_mp->colorspace;
> > + ctx->ycbcr_enc = pix_mp->ycbcr_enc;
> > + ctx->xfer_func = pix_mp->xfer_func;
> > + ctx->quantization = pix_mp->quantization;
> > +
> > + v4l2_dbg(1, debug, &jpeg->v4l2_dev, "(%d) s_fmt:%s wxh:%ux%u\n",
> > + f->type, q_data->fmt->name, q_data->w, q_data->h);
> > +
> > + for (i = 0; i < q_data->fmt->colplanes; i++) {
> > + q_data->bytesperline[i] = pix_mp->plane_fmt[i].bytesperline;
> > + q_data->sizeimage[i] = pix_mp->plane_fmt[i].sizeimage;
> > +
> > + v4l2_dbg(1, debug, &jpeg->v4l2_dev,
> > + "plane[%d] bpl=%u, size=%u\n",
> > + i, q_data->bytesperline[i], q_data->sizeimage[i]);
> > + }
> > +
> > + return 0;
> > +}
> > +
> > +static int mtk_jpeg_s_fmt_vid_out_mplane(struct file *file, void *priv,
> > + struct v4l2_format *f)
> > +{
> > + int ret;
> > +
> > + ret = mtk_jpeg_try_fmt_vid_out_mplane(file, priv, f);
> > + if (ret)
> > + return ret;
> > +
> > + return mtk_jpeg_s_fmt_mplane(mtk_jpeg_fh_to_ctx(priv), f);
> > +}
> > +
> > +static int mtk_jpeg_s_fmt_vid_cap_mplane(struct file *file, void *priv,
> > + struct v4l2_format *f)
> > +{
> > + int ret;
> > +
> > + ret = mtk_jpeg_try_fmt_vid_cap_mplane(file, priv, f);
> > + if (ret)
> > + return ret;
> > +
> > + return mtk_jpeg_s_fmt_mplane(mtk_jpeg_fh_to_ctx(priv), f);
> > +}
> > +
> > +static void mtk_jpeg_queue_src_chg_event(struct mtk_jpeg_ctx *ctx)
> > +{
> > + static const struct v4l2_event ev_src_ch = {
> > + .type = V4L2_EVENT_SOURCE_CHANGE,
> > + .u.src_change.changes =
> > + V4L2_EVENT_SRC_CH_RESOLUTION,
> > + };
> > +
> > + v4l2_event_queue_fh(&ctx->fh, &ev_src_ch);
> > +}
> > +
> > +static int mtk_jpeg_subscribe_event(struct v4l2_fh *fh,
> > + const struct v4l2_event_subscription *sub)
> > +{
> > + switch (sub->type) {
> > + case V4L2_EVENT_SOURCE_CHANGE:
> > + return v4l2_src_change_event_subscribe(fh, sub);
> > + default:
> > + return -EINVAL;
> > + }
> > +}
> > +
> > +static int mtk_jpeg_g_selection(struct file *file, void *priv,
> > + struct v4l2_selection *s)
> > +{
> > + struct mtk_jpeg_ctx *ctx = mtk_jpeg_fh_to_ctx(priv);
> > +
> > + if (s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
> > + return -EINVAL;
> > +
> > + switch (s->target) {
> > + case V4L2_SEL_TGT_COMPOSE:
> > + case V4L2_SEL_TGT_COMPOSE_DEFAULT:
> > + s->r.width = ctx->out_q.w;
> > + s->r.height = ctx->out_q.h;
> > + s->r.left = 0;
> > + s->r.top = 0;
> > + break;
> > + case V4L2_SEL_TGT_COMPOSE_BOUNDS:
> > + case V4L2_SEL_TGT_COMPOSE_PADDED:
> > + s->r.width = ctx->cap_q.w;
> > + s->r.height = ctx->cap_q.h;
> > + s->r.left = 0;
> > + s->r.top = 0;
> > + break;
> > + default:
> > + return -EINVAL;
> > + }
> > + return 0;
> > +}
> > +
> > +static int mtk_jpeg_s_selection(struct file *file, void *priv,
> > + struct v4l2_selection *s)
> > +{
> > + struct mtk_jpeg_ctx *ctx = mtk_jpeg_fh_to_ctx(priv);
> > +
> > + if (s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
> > + return -EINVAL;
> > +
> > + switch (s->target) {
> > + case V4L2_SEL_TGT_COMPOSE:
> > + s->r.left = 0;
> > + s->r.top = 0;
> > + s->r.width = ctx->out_q.w;
> > + s->r.height = ctx->out_q.h;
> > + break;
> > + default:
> > + return -EINVAL;
> > + }
> > + return 0;
> > +}
> > +
> > +static int mtk_jpeg_qbuf(struct file *file, void *priv, struct v4l2_buffer *buf)
> > +{
> > + struct v4l2_fh *fh = file->private_data;
> > + struct vb2_queue *vq;
> > + struct vb2_buffer *vb;
> > + struct mtk_jpeg_src_buf *jpeg_src_buf;
> > +
> > + if (buf->type != V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
> > + goto end;
> > +
> > + vq = v4l2_m2m_get_vq(fh->m2m_ctx, buf->type);
> > + vb = vq->bufs[buf->index];
> > + jpeg_src_buf = mtk_jpeg_vb2_to_srcbuf(vb);
> > + jpeg_src_buf->flags = (buf->m.planes[0].bytesused == 0) ?
> > + MTK_JPEG_BUF_FLAGS_LAST_FRAME : MTK_JPEG_BUF_FLAGS_INIT;
> > +end:
> > + return v4l2_m2m_qbuf(file, fh->m2m_ctx, buf);
> > +}
> > +
> > +static const struct v4l2_ioctl_ops mtk_jpeg_ioctl_ops = {
> > + .vidioc_querycap = mtk_jpeg_querycap,
> > + .vidioc_enum_fmt_vid_cap_mplane = mtk_jpeg_enum_fmt_vid_cap,
> > + .vidioc_enum_fmt_vid_out_mplane = mtk_jpeg_enum_fmt_vid_out,
> > + .vidioc_try_fmt_vid_cap_mplane = mtk_jpeg_try_fmt_vid_cap_mplane,
> > + .vidioc_try_fmt_vid_out_mplane = mtk_jpeg_try_fmt_vid_out_mplane,
> > + .vidioc_g_fmt_vid_cap_mplane = mtk_jpeg_g_fmt_vid_mplane,
> > + .vidioc_g_fmt_vid_out_mplane = mtk_jpeg_g_fmt_vid_mplane,
> > + .vidioc_s_fmt_vid_cap_mplane = mtk_jpeg_s_fmt_vid_cap_mplane,
> > + .vidioc_s_fmt_vid_out_mplane = mtk_jpeg_s_fmt_vid_out_mplane,
> > + .vidioc_qbuf = mtk_jpeg_qbuf,
> > + .vidioc_subscribe_event = mtk_jpeg_subscribe_event,
> > + .vidioc_g_selection = mtk_jpeg_g_selection,
> > + .vidioc_s_selection = mtk_jpeg_s_selection,
> > +
> > + .vidioc_create_bufs = v4l2_m2m_ioctl_create_bufs,
> > + .vidioc_prepare_buf = v4l2_m2m_ioctl_prepare_buf,
> > + .vidioc_reqbufs = v4l2_m2m_ioctl_reqbufs,
> > + .vidioc_querybuf = v4l2_m2m_ioctl_querybuf,
> > + .vidioc_dqbuf = v4l2_m2m_ioctl_dqbuf,
> > + .vidioc_expbuf = v4l2_m2m_ioctl_expbuf,
> > + .vidioc_streamon = v4l2_m2m_ioctl_streamon,
> > + .vidioc_streamoff = v4l2_m2m_ioctl_streamoff,
> > +
> > + .vidioc_unsubscribe_event = v4l2_event_unsubscribe,
> > +};
> > +
> > +static int mtk_jpeg_queue_setup(struct vb2_queue *q,
> > + unsigned int *num_buffers,
> > + unsigned int *num_planes,
> > + unsigned int sizes[],
> > + struct device *alloc_ctxs[])
> > +{
> > + struct mtk_jpeg_ctx *ctx = vb2_get_drv_priv(q);
> > + struct mtk_jpeg_q_data *q_data = NULL;
> > + struct mtk_jpeg_dev *jpeg = ctx->jpeg;
> > + int i;
> > +
> > + v4l2_dbg(1, debug, &jpeg->v4l2_dev, "(%d) buf_req count=%u\n",
> > + q->type, *num_buffers);
> > +
> > + q_data = mtk_jpeg_get_q_data(ctx, q->type);
> > + if (!q_data)
> > + return -EINVAL;
> > +
> > + *num_planes = q_data->fmt->colplanes;
> > + for (i = 0; i < q_data->fmt->colplanes; i++) {
> > + sizes[i] = q_data->sizeimage[i];
> > + v4l2_dbg(1, debug, &jpeg->v4l2_dev, "sizeimage[%d]=%u\n",
> > + i, sizes[i]);
> > + }
> > +
> > + return 0;
> > +}
> > +
> > +static int mtk_jpeg_buf_prepare(struct vb2_buffer *vb)
> > +{
> > + struct mtk_jpeg_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
> > + struct mtk_jpeg_q_data *q_data = NULL;
> > + int i;
> > +
> > + q_data = mtk_jpeg_get_q_data(ctx, vb->vb2_queue->type);
> > + if (!q_data)
> > + return -EINVAL;
> > +
> > + for (i = 0; i < q_data->fmt->colplanes; i++)
> > + vb2_set_plane_payload(vb, i, q_data->sizeimage[i]);
> > +
> > + return 0;
> > +}
> > +
> > +static bool mtk_jpeg_check_resolution_change(struct mtk_jpeg_ctx *ctx,
> > + struct mtk_jpeg_dec_param *param)
> > +{
> > + struct mtk_jpeg_dev *jpeg = ctx->jpeg;
> > + struct mtk_jpeg_q_data *q_data;
> > +
> > + q_data = &ctx->out_q;
> > + if (q_data->w != param->pic_w || q_data->h != param->pic_h) {
> > + v4l2_dbg(1, debug, &jpeg->v4l2_dev, "Picture size change\n");
> > + return true;
> > + }
> > +
> > + q_data = &ctx->cap_q;
> > + if (q_data->fmt != mtk_jpeg_find_format(ctx, param->dst_fourcc,
> > + MTK_JPEG_FMT_TYPE_CAPTURE)) {
> > + v4l2_dbg(1, debug, &jpeg->v4l2_dev, "format change\n");
> > + return true;
> > + }
> > + return false;
> > +}
> > +
> > +static void mtk_jpeg_set_queue_data(struct mtk_jpeg_ctx *ctx,
> > + struct mtk_jpeg_dec_param *param)
> > +{
> > + struct mtk_jpeg_dev *jpeg = ctx->jpeg;
> > + struct mtk_jpeg_q_data *q_data;
> > + int i;
> > +
> > + q_data = &ctx->out_q;
> > + q_data->w = param->pic_w;
> > + q_data->h = param->pic_h;
> > +
> > + q_data = &ctx->cap_q;
> > + q_data->w = param->dec_w;
> > + q_data->h = param->dec_h;
> > + q_data->fmt = mtk_jpeg_find_format(ctx,
> > + param->dst_fourcc,
> > + MTK_JPEG_FMT_TYPE_CAPTURE);
> > +
> > + for (i = 0; i < q_data->fmt->colplanes; i++) {
> > + q_data->bytesperline[i] = param->mem_stride[i];
> > + q_data->sizeimage[i] = param->comp_size[i];
> > + }
> > +
> > + v4l2_dbg(1, debug, &jpeg->v4l2_dev,
> > + "set_parse cap:%s pic(%u, %u), buf(%u, %u)\n",
> > + q_data->fmt->name, param->pic_w, param->pic_h,
> > + param->dec_w, param->dec_h);
> > +}
> > +
> > +static void mtk_jpeg_buf_queue(struct vb2_buffer *vb)
> > +{
> > + struct mtk_jpeg_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
> > + struct mtk_jpeg_dec_param *param;
> > + struct mtk_jpeg_dev *jpeg = ctx->jpeg;
> > + struct mtk_jpeg_src_buf *jpeg_src_buf;
> > + bool header_valid;
> > +
> > + v4l2_dbg(2, debug, &jpeg->v4l2_dev, "(%d) buf_q id=%d, vb=%p",
> > + vb->vb2_queue->type, vb->index, vb);
> > +
> > + if (vb->vb2_queue->type != V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
> > + goto end;
> > +
> > + jpeg_src_buf = mtk_jpeg_vb2_to_srcbuf(vb);
> > + param = &jpeg_src_buf->dec_param;
> > + memset(param, 0, sizeof(*param));
> > +
> > + if (jpeg_src_buf->flags & MTK_JPEG_BUF_FLAGS_LAST_FRAME) {
> > + v4l2_dbg(1, debug, &jpeg->v4l2_dev, "Got eos");
> > + goto end;
> > + }
> > + header_valid = mtk_jpeg_parse(param, (u8 *)vb2_plane_vaddr(vb, 0),
> > + vb2_get_plane_payload(vb, 0));
> > + if (!header_valid) {
> > + v4l2_err(&jpeg->v4l2_dev, "Header invalid.\n");
> > + vb2_buffer_done(vb, VB2_BUF_STATE_ERROR);
> > + return;
> > + }
> > +
> > + if (ctx->state == MTK_JPEG_INIT) {
> > + mtk_jpeg_queue_src_chg_event(ctx);
> > + mtk_jpeg_set_queue_data(ctx, param);
> > + ctx->state = MTK_JPEG_RUNNING;
> > + }
> > +end:
> > + v4l2_m2m_buf_queue(ctx->fh.m2m_ctx, to_vb2_v4l2_buffer(vb));
> > +}
> > +
> > +static void *mtk_jpeg_buf_remove(struct mtk_jpeg_ctx *ctx,
> > + enum v4l2_buf_type type)
> > +{
> > + if (V4L2_TYPE_IS_OUTPUT(type))
> > + return v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx);
> > + else
> > + return v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx);
> > +}
> > +
> > +static int mtk_jpeg_start_streaming(struct vb2_queue *q, unsigned int count)
> > +{
> > + struct mtk_jpeg_ctx *ctx = vb2_get_drv_priv(q);
> > + int ret = 0;
> > +
> > + ret = pm_runtime_get_sync(ctx->jpeg->dev);
> > +
>
> If start_streaming returns an error, then you must call
> v4l2_m2m_buf_done(to_vb2_v4l2_buffer(vb), VB2_BUF_STATE_QUEUED) for all
> buffers. Similar to what happens in stop_streaming, but with a different VB2_BUF_STATE.
Ok.
> > + return ret > 0 ? 0 : ret;
> > +}
> > +
> > +static void mtk_jpeg_stop_streaming(struct vb2_queue *q)
> > +{
> > + struct mtk_jpeg_ctx *ctx = vb2_get_drv_priv(q);
> > + struct vb2_buffer *vb;
> > +
> > + /*
> > + * STREAMOFF is an acknowledgment for source change event.
> > + * Before STREAMOFF, we still have to return the old resolution and
> > + * subsampling. Update capture queue when the stream is off.
> > + */
> > + if (ctx->state == MTK_JPEG_SOURCE_CHANGE &&
> > + !V4L2_TYPE_IS_OUTPUT(q->type)) {
> > + struct mtk_jpeg_src_buf *src_buf;
> > +
> > + vb = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx);
> > + src_buf = mtk_jpeg_vb2_to_srcbuf(vb);
> > + mtk_jpeg_set_queue_data(ctx, &src_buf->dec_param);
> > + ctx->state = MTK_JPEG_RUNNING;
> > + } else if (V4L2_TYPE_IS_OUTPUT(q->type)) {
> > + ctx->state = MTK_JPEG_INIT;
> > + }
> > +
> > + vb = mtk_jpeg_buf_remove(ctx, q->type);
> > + while (vb) {
> > + v4l2_m2m_buf_done(to_vb2_v4l2_buffer(vb), VB2_BUF_STATE_ERROR);
> > + vb = mtk_jpeg_buf_remove(ctx, q->type);
> > + }
> > +
> > + pm_runtime_put_sync(ctx->jpeg->dev);
> > +}
> > +
> > +static struct vb2_ops mtk_jpeg_qops = {
> > + .queue_setup = mtk_jpeg_queue_setup,
> > + .buf_prepare = mtk_jpeg_buf_prepare,
> > + .buf_queue = mtk_jpeg_buf_queue,
> > + .wait_prepare = vb2_ops_wait_prepare,
> > + .wait_finish = vb2_ops_wait_finish,
> > + .start_streaming = mtk_jpeg_start_streaming,
> > + .stop_streaming = mtk_jpeg_stop_streaming,
> > +};
> > +
> > +static void mtk_jpeg_set_dec_src(struct mtk_jpeg_ctx *ctx,
> > + struct vb2_buffer *src_buf,
> > + struct mtk_jpeg_bs *bs)
> > +{
> > + bs->str_addr = vb2_dma_contig_plane_dma_addr(src_buf, 0);
> > + bs->end_addr = bs->str_addr +
> > + mtk_jpeg_align(vb2_get_plane_payload(src_buf, 0), 16);
> > + bs->size = mtk_jpeg_align(vb2_plane_size(src_buf, 0), 128);
> > +}
> > +
> > +static int mtk_jpeg_set_dec_dst(struct mtk_jpeg_ctx *ctx,
> > + struct mtk_jpeg_dec_param *param,
> > + struct vb2_buffer *dst_buf,
> > + struct mtk_jpeg_fb *fb)
> > +{
> > + int i;
> > +
> > + if (param->comp_num != dst_buf->num_planes) {
> > + dev_err(ctx->jpeg->dev, "plane number mismatch (%u != %u)\n",
> > + param->comp_num, dst_buf->num_planes);
> > + return -EINVAL;
> > + }
> > +
> > + for (i = 0; i < dst_buf->num_planes; i++) {
> > + if (vb2_plane_size(dst_buf, i) < param->comp_size[i]) {
> > + dev_err(ctx->jpeg->dev,
> > + "buffer size is underflow (%lu < %u)\n",
> > + vb2_plane_size(dst_buf, 0),
> > + param->comp_size[i]);
> > + return -EINVAL;
> > + }
> > + fb->plane_addr[i] = vb2_dma_contig_plane_dma_addr(dst_buf, i);
> > + }
> > +
> > + return 0;
> > +}
> > +
> > +static void mtk_jpeg_device_run(void *priv)
> > +{
> > + struct mtk_jpeg_ctx *ctx = priv;
> > + struct mtk_jpeg_dev *jpeg = ctx->jpeg;
> > + struct vb2_buffer *src_buf, *dst_buf;
> > + enum vb2_buffer_state buf_state = VB2_BUF_STATE_ERROR;
> > + unsigned long flags;
> > + struct mtk_jpeg_src_buf *jpeg_src_buf;
> > + struct mtk_jpeg_bs bs;
> > + struct mtk_jpeg_fb fb;
> > + int i;
> > +
> > + src_buf = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx);
> > + dst_buf = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx);
> > + jpeg_src_buf = mtk_jpeg_vb2_to_srcbuf(src_buf);
> > +
> > + if (jpeg_src_buf->flags & MTK_JPEG_BUF_FLAGS_LAST_FRAME) {
> > + for (i = 0; i < dst_buf->num_planes; i++)
> > + vb2_set_plane_payload(dst_buf, i, 0);
> > + buf_state = VB2_BUF_STATE_DONE;
> > + goto dec_end;
> > + }
> > +
> > + if (mtk_jpeg_check_resolution_change(ctx, &jpeg_src_buf->dec_param)) {
> > + mtk_jpeg_queue_src_chg_event(ctx);
> > + ctx->state = MTK_JPEG_SOURCE_CHANGE;
> > + v4l2_m2m_job_finish(jpeg->m2m_dev, ctx->fh.m2m_ctx);
> > + return;
> > + }
> > +
> > + mtk_jpeg_set_dec_src(ctx, src_buf, &bs);
> > + if (mtk_jpeg_set_dec_dst(ctx, &jpeg_src_buf->dec_param, dst_buf, &fb))
> > + goto dec_end;
> > +
> > + spin_lock_irqsave(&jpeg->hw_lock, flags);
> > + mtk_jpeg_dec_reset(jpeg->dec_reg_base);
> > + mtk_jpeg_dec_set_config(jpeg->dec_reg_base,
> > + &jpeg_src_buf->dec_param, &bs, &fb);
> > +
> > + mtk_jpeg_dec_start(jpeg->dec_reg_base);
> > + spin_unlock_irqrestore(&jpeg->hw_lock, flags);
> > + return;
> > +
> > +dec_end:
> > + v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx);
> > + v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx);
> > + v4l2_m2m_buf_done(to_vb2_v4l2_buffer(src_buf), buf_state);
> > + v4l2_m2m_buf_done(to_vb2_v4l2_buffer(dst_buf), buf_state);
> > + v4l2_m2m_job_finish(jpeg->m2m_dev, ctx->fh.m2m_ctx);
> > +}
> > +
> > +static int mtk_jpeg_job_ready(void *priv)
> > +{
> > + struct mtk_jpeg_ctx *ctx = priv;
> > +
> > + return (ctx->state == MTK_JPEG_RUNNING) ? 1 : 0;
> > +}
> > +
> > +static void mtk_jpeg_job_abort(void *priv)
> > +{
> > + struct mtk_jpeg_ctx *ctx = priv;
> > + struct mtk_jpeg_dev *jpeg = ctx->jpeg;
> > + struct vb2_buffer *src_buf, *dst_buf;
> > +
> > + src_buf = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx);
> > + dst_buf = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx);
> > + v4l2_m2m_buf_done(to_vb2_v4l2_buffer(src_buf), VB2_BUF_STATE_ERROR);
> > + v4l2_m2m_buf_done(to_vb2_v4l2_buffer(dst_buf), VB2_BUF_STATE_ERROR);
> > + v4l2_m2m_job_finish(jpeg->m2m_dev, ctx->fh.m2m_ctx);
> > +}
> > +
> > +static struct v4l2_m2m_ops mtk_jpeg_m2m_ops = {
> > + .device_run = mtk_jpeg_device_run,
> > + .job_ready = mtk_jpeg_job_ready,
> > + .job_abort = mtk_jpeg_job_abort,
> > +};
> > +
> > +static int mtk_jpeg_queue_init(void *priv, struct vb2_queue *src_vq,
> > + struct vb2_queue *dst_vq)
> > +{
> > + struct mtk_jpeg_ctx *ctx = priv;
> > + int ret;
> > +
> > + src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
> > + src_vq->io_modes = VB2_DMABUF | VB2_MMAP | VB2_USERPTR;
>
> I would drop USERPTR, it really makes little sense for dma_contig.
Ok.
> > + src_vq->drv_priv = ctx;
> > + src_vq->buf_struct_size = sizeof(struct mtk_jpeg_src_buf);
> > + src_vq->ops = &mtk_jpeg_qops;
> > + src_vq->mem_ops = &vb2_dma_contig_memops;
> > + src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
> > + src_vq->lock = &ctx->jpeg->lock;
> > + src_vq->dev = ctx->jpeg->dev;
> > + ret = vb2_queue_init(src_vq);
> > + if (ret)
> > + return ret;
> > +
> > + dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
> > + dst_vq->io_modes = VB2_DMABUF | VB2_MMAP | VB2_USERPTR;
>
> Ditto.
Ok.
> > + dst_vq->drv_priv = ctx;
> > + dst_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer);
> > + dst_vq->ops = &mtk_jpeg_qops;
> > + dst_vq->mem_ops = &vb2_dma_contig_memops;
> > + dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
> > + dst_vq->lock = &ctx->jpeg->lock;
> > + dst_vq->dev = ctx->jpeg->dev;
> > + ret = vb2_queue_init(dst_vq);
> > +
> > + return ret;
> > +}
> > +
> > +static void mtk_jpeg_clk_on(struct mtk_jpeg_dev *jpeg)
> > +{
> > + int ret;
> > +
> > + ret = mtk_smi_larb_get(jpeg->larb);
> > + if (ret)
> > + dev_err(jpeg->dev, "mtk_smi_larb_get larbvdec fail %d\n", ret);
> > + clk_prepare_enable(jpeg->clk_jdec_smi);
> > + clk_prepare_enable(jpeg->clk_jdec);
> > +}
> > +
> > +static void mtk_jpeg_clk_off(struct mtk_jpeg_dev *jpeg)
> > +{
> > + clk_disable_unprepare(jpeg->clk_jdec);
> > + clk_disable_unprepare(jpeg->clk_jdec_smi);
> > + mtk_smi_larb_put(jpeg->larb);
> > +}
> > +
> > +static irqreturn_t mtk_jpeg_dec_irq(int irq, void *priv)
> > +{
> > + struct mtk_jpeg_dev *jpeg = priv;
> > + struct mtk_jpeg_ctx *ctx;
> > + struct vb2_buffer *src_buf, *dst_buf;
> > + struct mtk_jpeg_src_buf *jpeg_src_buf;
> > + enum vb2_buffer_state buf_state = VB2_BUF_STATE_ERROR;
> > + u32 dec_irq_ret;
> > + u32 dec_ret;
> > + int i;
> > +
> > + ctx = v4l2_m2m_get_curr_priv(jpeg->m2m_dev);
> > + if (!ctx) {
> > + v4l2_err(&jpeg->v4l2_dev, "Context is NULL\n");
> > + return IRQ_HANDLED;
> > + }
> > +
> > + src_buf = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx);
> > + dst_buf = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx);
> > + jpeg_src_buf = mtk_jpeg_vb2_to_srcbuf(src_buf);
> > +
> > + dec_ret = mtk_jpeg_dec_get_int_status(jpeg->dec_reg_base);
> > + dec_irq_ret = mtk_jpeg_dec_enum_result(dec_ret);
> > +
> > + if (dec_irq_ret >= MTK_JPEG_DEC_RESULT_UNDERFLOW)
> > + mtk_jpeg_dec_reset(jpeg->dec_reg_base);
> > +
> > + if (dec_irq_ret != MTK_JPEG_DEC_RESULT_EOF_DONE) {
> > + dev_err(jpeg->dev, "decode failed\n");
> > + goto dec_end;
> > + }
> > +
> > + for (i = 0; i < dst_buf->num_planes; i++)
> > + vb2_set_plane_payload(dst_buf, i,
> > + jpeg_src_buf->dec_param.comp_size[i]);
> > +
> > + buf_state = VB2_BUF_STATE_DONE;
> > +
> > +dec_end:
> > + v4l2_m2m_buf_done(to_vb2_v4l2_buffer(src_buf), buf_state);
> > + v4l2_m2m_buf_done(to_vb2_v4l2_buffer(dst_buf), buf_state);
> > + v4l2_m2m_job_finish(jpeg->m2m_dev, ctx->fh.m2m_ctx);
> > + return IRQ_HANDLED;
> > +}
> > +
> > +static void mtk_jpeg_set_default_params(struct mtk_jpeg_ctx *ctx)
> > +{
> > + struct mtk_jpeg_q_data *q = &ctx->out_q;
> > + int i;
> > +
> > + ctx->colorspace = V4L2_COLORSPACE_JPEG,
> > + ctx->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT;
> > + ctx->quantization = V4L2_QUANTIZATION_DEFAULT;
> > + ctx->xfer_func = V4L2_XFER_FUNC_DEFAULT;
> > +
> > + q->fmt = mtk_jpeg_find_format(ctx, V4L2_PIX_FMT_JPEG,
> > + MTK_JPEG_FMT_TYPE_OUTPUT);
> > + q->w = MTK_JPEG_MIN_WIDTH;
> > + q->h = MTK_JPEG_MIN_HEIGHT;
> > + q->bytesperline[0] = 0;
> > + q->sizeimage[0] = MTK_JPEG_DEFAULT_SIZEIMAGE;
> > +
> > + q = &ctx->cap_q;
> > + q->fmt = mtk_jpeg_find_format(ctx, V4L2_PIX_FMT_YUV420M,
> > + MTK_JPEG_FMT_TYPE_CAPTURE);
> > + q->w = MTK_JPEG_MIN_WIDTH;
> > + q->h = MTK_JPEG_MIN_HEIGHT;
> > +
> > + for (i = 0; i < q->fmt->colplanes; i++) {
> > + u32 stride = q->w * q->fmt->h_sample[i] / 4;
> > + u32 h = q->h * q->fmt->v_sample[i] / 4;
> > +
> > + q->bytesperline[i] = stride;
> > + q->sizeimage[i] = stride * h;
> > + }
> > +}
> > +
> > +static int mtk_jpeg_open(struct file *file)
> > +{
> > + struct mtk_jpeg_dev *jpeg = video_drvdata(file);
> > + struct video_device *vfd = video_devdata(file);
> > + struct mtk_jpeg_ctx *ctx;
> > + int ret = 0;
> > +
> > + ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
> > + if (!ctx)
> > + return -ENOMEM;
> > +
> > + if (mutex_lock_interruptible(&jpeg->lock)) {
> > + ret = -ERESTARTSYS;
> > + goto free;
> > + }
> > +
> > + v4l2_fh_init(&ctx->fh, vfd);
> > + file->private_data = &ctx->fh;
> > + v4l2_fh_add(&ctx->fh);
> > +
> > + ctx->jpeg = jpeg;
> > + ctx->fh.m2m_ctx = v4l2_m2m_ctx_init(jpeg->m2m_dev, ctx,
> > + mtk_jpeg_queue_init);
> > + if (IS_ERR(ctx->fh.m2m_ctx)) {
> > + ret = PTR_ERR(ctx->fh.m2m_ctx);
> > + goto error;
> > + }
> > +
> > + mtk_jpeg_set_default_params(ctx);
> > + mutex_unlock(&jpeg->lock);
> > + return 0;
> > +
> > +error:
> > + v4l2_fh_del(&ctx->fh);
> > + v4l2_fh_exit(&ctx->fh);
> > + mutex_unlock(&jpeg->lock);
> > +free:
> > + kfree(ctx);
> > + return ret;
> > +}
> > +
> > +static int mtk_jpeg_release(struct file *file)
> > +{
> > + struct mtk_jpeg_dev *jpeg = video_drvdata(file);
> > + struct mtk_jpeg_ctx *ctx = mtk_jpeg_fh_to_ctx(file->private_data);
> > +
> > + mutex_lock(&jpeg->lock);
> > + v4l2_m2m_ctx_release(ctx->fh.m2m_ctx);
> > + v4l2_fh_del(&ctx->fh);
> > + v4l2_fh_exit(&ctx->fh);
> > + kfree(ctx);
> > + mutex_unlock(&jpeg->lock);
> > + return 0;
> > +}
> > +
> > +static const struct v4l2_file_operations mtk_jpeg_fops = {
> > + .owner = THIS_MODULE,
> > + .open = mtk_jpeg_open,
> > + .release = mtk_jpeg_release,
> > + .poll = v4l2_m2m_fop_poll,
> > + .unlocked_ioctl = video_ioctl2,
> > + .mmap = v4l2_m2m_fop_mmap,
> > +};
> > +
> > +static int mtk_jpeg_clk_init(struct mtk_jpeg_dev *jpeg)
> > +{
> > + struct device_node *node;
> > + struct platform_device *pdev;
> > +
> > + node = of_parse_phandle(jpeg->dev->of_node, "mediatek,larb", 0);
> > + if (!node)
> > + return -EINVAL;
> > + pdev = of_find_device_by_node(node);
> > + if (WARN_ON(!pdev)) {
> > + of_node_put(node);
> > + return -EINVAL;
> > + }
> > + of_node_put(node);
> > +
> > + jpeg->larb = &pdev->dev;
> > +
> > + jpeg->clk_jdec = devm_clk_get(jpeg->dev, "jpgdec");
> > + if (IS_ERR(jpeg->clk_jdec))
> > + return -EINVAL;
> > +
> > + jpeg->clk_jdec_smi = devm_clk_get(jpeg->dev, "jpgdec-smi");
> > + if (IS_ERR(jpeg->clk_jdec_smi))
> > + return -EINVAL;
> > +
> > + return 0;
> > +}
> > +
> > +static int mtk_jpeg_probe(struct platform_device *pdev)
> > +{
> > + struct mtk_jpeg_dev *jpeg;
> > + struct resource *res;
> > + int dec_irq;
> > + int ret;
> > +
> > + jpeg = devm_kzalloc(&pdev->dev, sizeof(*jpeg), GFP_KERNEL);
> > + if (!jpeg)
> > + return -ENOMEM;
> > +
> > + mutex_init(&jpeg->lock);
> > + spin_lock_init(&jpeg->hw_lock);
> > + jpeg->dev = &pdev->dev;
> > +
> > + res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> > + jpeg->dec_reg_base = devm_ioremap_resource(&pdev->dev, res);
> > + if (IS_ERR(jpeg->dec_reg_base)) {
> > + ret = PTR_ERR(jpeg->dec_reg_base);
> > + return ret;
> > + }
> > +
> > + res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
> > + dec_irq = platform_get_irq(pdev, 0);
> > + if (!res || dec_irq < 0) {
> > + dev_err(&pdev->dev, "Failed to get dec_irq %d.\n", dec_irq);
> > + ret = -EINVAL;
> > + return ret;
> > + }
> > +
> > + ret = devm_request_irq(&pdev->dev, dec_irq, mtk_jpeg_dec_irq, 0,
> > + pdev->name, jpeg);
> > + if (ret) {
> > + dev_err(&pdev->dev, "Failed to request dec_irq %d (%d)\n",
> > + dec_irq, ret);
> > + ret = -EINVAL;
> > + goto err_req_irq;
> > + }
> > +
> > + ret = mtk_jpeg_clk_init(jpeg);
> > + if (ret) {
> > + dev_err(&pdev->dev, "Failed to init clk, err %d\n", ret);
> > + goto err_clk_init;
> > + }
> > +
> > + ret = v4l2_device_register(&pdev->dev, &jpeg->v4l2_dev);
> > + if (ret) {
> > + dev_err(&pdev->dev, "Failed to register v4l2 device\n");
> > + ret = -EINVAL;
> > + goto err_dev_register;
> > + }
> > +
> > + jpeg->m2m_dev = v4l2_m2m_init(&mtk_jpeg_m2m_ops);
> > + if (IS_ERR(jpeg->m2m_dev)) {
> > + v4l2_err(&jpeg->v4l2_dev, "Failed to init mem2mem device\n");
> > + ret = PTR_ERR(jpeg->m2m_dev);
> > + goto err_m2m_init;
> > + }
> > +
> > + jpeg->dec_vdev = video_device_alloc();
> > + if (!jpeg->dec_vdev) {
> > + ret = -ENOMEM;
> > + goto err_dec_vdev_alloc;
> > + }
> > + snprintf(jpeg->dec_vdev->name, sizeof(jpeg->dec_vdev->name),
> > + "%s-dec", MTK_JPEG_NAME);
> > + jpeg->dec_vdev->fops = &mtk_jpeg_fops;
> > + jpeg->dec_vdev->ioctl_ops = &mtk_jpeg_ioctl_ops;
> > + jpeg->dec_vdev->minor = -1;
> > + jpeg->dec_vdev->release = video_device_release;
> > + jpeg->dec_vdev->lock = &jpeg->lock;
> > + jpeg->dec_vdev->v4l2_dev = &jpeg->v4l2_dev;
> > + jpeg->dec_vdev->vfl_dir = VFL_DIR_M2M;
> > + jpeg->dec_vdev->device_caps = V4L2_CAP_STREAMING |
> > + V4L2_CAP_VIDEO_M2M_MPLANE;
> > +
> > + ret = video_register_device(jpeg->dec_vdev, VFL_TYPE_GRABBER, 3);
> > + if (ret) {
> > + v4l2_err(&jpeg->v4l2_dev, "Failed to register video device\n");
> > + goto err_dec_vdev_register;
> > + }
> > +
> > + video_set_drvdata(jpeg->dec_vdev, jpeg);
> > + v4l2_info(&jpeg->v4l2_dev,
> > + "decoder device registered as /dev/video%d (%d,%d)\n",
> > + jpeg->dec_vdev->num, VIDEO_MAJOR, jpeg->dec_vdev->minor);
> > +
> > + platform_set_drvdata(pdev, jpeg);
> > +
> > + pm_runtime_enable(&pdev->dev);
> > +
> > + return 0;
> > +
> > +err_dec_vdev_register:
> > + video_device_release(jpeg->dec_vdev);
> > +
> > +err_dec_vdev_alloc:
> > + v4l2_m2m_release(jpeg->m2m_dev);
> > +
> > +err_m2m_init:
> > + v4l2_device_unregister(&jpeg->v4l2_dev);
> > +
> > +err_dev_register:
> > +
> > +err_clk_init:
> > +
> > +err_req_irq:
> > +
> > + return ret;
> > +}
> > +
> > +static int mtk_jpeg_remove(struct platform_device *pdev)
> > +{
> > + struct mtk_jpeg_dev *jpeg = platform_get_drvdata(pdev);
> > +
> > + pm_runtime_disable(&pdev->dev);
> > + video_unregister_device(jpeg->dec_vdev);
> > + video_device_release(jpeg->dec_vdev);
> > + v4l2_m2m_release(jpeg->m2m_dev);
> > + v4l2_device_unregister(&jpeg->v4l2_dev);
> > +
> > + return 0;
> > +}
> > +
> > +#ifdef CONFIG_PM
> > +static int mtk_jpeg_pm_suspend(struct device *dev)
> > +{
> > + struct mtk_jpeg_dev *jpeg = dev_get_drvdata(dev);
> > +
> > + mtk_jpeg_dec_reset(jpeg->dec_reg_base);
> > + mtk_jpeg_clk_off(jpeg);
> > +
> > + return 0;
> > +}
> > +
> > +static int mtk_jpeg_pm_resume(struct device *dev)
> > +{
> > + struct mtk_jpeg_dev *jpeg = dev_get_drvdata(dev);
> > +
> > + mtk_jpeg_clk_on(jpeg);
> > + mtk_jpeg_dec_reset(jpeg->dec_reg_base);
> > +
> > + return 0;
> > +}
> > +#endif /* CONFIG_PM */
> > +
> > +#ifdef CONFIG_PM_SLEEP
> > +static int mtk_jpeg_suspend(struct device *dev)
> > +{
> > + int ret;
> > +
> > + if (pm_runtime_suspended(dev))
> > + return 0;
> > +
> > + ret = mtk_jpeg_pm_suspend(dev);
> > + return ret;
> > +}
> > +
> > +static int mtk_jpeg_resume(struct device *dev)
> > +{
> > + int ret;
> > +
> > + if (pm_runtime_suspended(dev))
> > + return 0;
> > +
> > + ret = mtk_jpeg_pm_resume(dev);
> > +
> > + return ret;
> > +}
> > +#endif /* CONFIG_PM_SLEEP */
> > +
> > +static const struct dev_pm_ops mtk_jpeg_pm_ops = {
> > + SET_SYSTEM_SLEEP_PM_OPS(mtk_jpeg_suspend, mtk_jpeg_resume)
> > + SET_RUNTIME_PM_OPS(mtk_jpeg_pm_suspend, mtk_jpeg_pm_resume, NULL)
> > +};
> > +
> > +static const struct of_device_id mtk_jpeg_match[] = {
> > + {
> > + .compatible = "mediatek,mt8173-jpgdec",
> > + .data = NULL,
> > + },
> > + {
> > + .compatible = "mediatek,mt2701-jpgdec",
> > + .data = NULL,
> > + },
> > + {},
> > +};
> > +
> > +MODULE_DEVICE_TABLE(of, mtk_jpeg_match);
> > +
> > +static struct platform_driver mtk_jpeg_driver = {
> > + .probe = mtk_jpeg_probe,
> > + .remove = mtk_jpeg_remove,
> > + .driver = {
> > + .owner = THIS_MODULE,
> > + .name = MTK_JPEG_NAME,
> > + .of_match_table = mtk_jpeg_match,
> > + .pm = &mtk_jpeg_pm_ops,
> > + },
> > +};
> > +
> > +module_platform_driver(mtk_jpeg_driver);
> > +
> > +MODULE_DESCRIPTION("MediaTek JPEG codec driver");
> > +MODULE_LICENSE("GPL v2");
> > diff --git a/drivers/media/platform/mtk-jpeg/mtk_jpeg_core.h b/drivers/media/platform/mtk-jpeg/mtk_jpeg_core.h
> > new file mode 100644
> > index 0000000..d862e3b
> > --- /dev/null
> > +++ b/drivers/media/platform/mtk-jpeg/mtk_jpeg_core.h
> > @@ -0,0 +1,141 @@
> > +/*
> > + * Copyright (c) 2016 MediaTek Inc.
> > + * Author: Ming Hsiu Tsai <minghsiu.tsai-NuS5LvNUpcJWk0Htik3J/w@public.gmane.org>
> > + * Rick Chang <rick.chang-NuS5LvNUpcJWk0Htik3J/w@public.gmane.org>
> > + *
> > + * This program is free software; you can redistribute it and/or modify
> > + * it under the terms of the GNU General Public License version 2 as
> > + * published by the Free Software Foundation.
> > + *
> > + * 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.
> > + */
> > +
> > +#ifndef _MTK_JPEG_CORE_H
> > +#define _MTK_JPEG_CORE_H
> > +
> > +#include <linux/interrupt.h>
> > +#include <media/v4l2-ctrls.h>
> > +#include <media/v4l2-device.h>
> > +#include <media/v4l2-fh.h>
> > +
> > +#define MTK_JPEG_NAME "mtk-jpeg"
> > +
> > +#define MTK_JPEG_FMT_FLAG_DEC_OUTPUT BIT(0)
> > +#define MTK_JPEG_FMT_FLAG_DEC_CAPTURE BIT(1)
> > +
> > +#define MTK_JPEG_FMT_TYPE_OUTPUT 1
> > +#define MTK_JPEG_FMT_TYPE_CAPTURE 2
> > +
> > +#define MTK_JPEG_MIN_WIDTH 32
> > +#define MTK_JPEG_MIN_HEIGHT 32
> > +#define MTK_JPEG_MAX_WIDTH 8192
> > +#define MTK_JPEG_MAX_HEIGHT 8192
> > +
> > +#define MTK_JPEG_DEFAULT_SIZEIMAGE (1 * 1024 * 1024)
> > +
> > +enum mtk_jpeg_ctx_state {
> > + MTK_JPEG_INIT = 0,
> > + MTK_JPEG_RUNNING,
> > + MTK_JPEG_SOURCE_CHANGE,
> > +};
> > +
> > +/**
> > + * struct mt_jpeg - JPEG IP abstraction
> > + * @lock: the mutex protecting this structure
> > + * @hw_lock: spinlock protecting the hw device resource
> > + * @workqueue: decode work queue
> > + * @dev: JPEG device
> > + * @v4l2_dev: v4l2 device for mem2mem mode
> > + * @m2m_dev: v4l2 mem2mem device data
> > + * @alloc_ctx: videobuf2 memory allocator's context
> > + * @dec_vdev: video device node for decoder mem2mem mode
> > + * @dec_reg_base: JPEG registers mapping
> > + * @clk_jdec: JPEG hw working clock
> > + * @clk_jdec_smi: JPEG SMI bus clock
> > + * @larb: SMI device
> > + */
> > +struct mtk_jpeg_dev {
> > + struct mutex lock;
> > + spinlock_t hw_lock;
> > + struct workqueue_struct *workqueue;
> > + struct device *dev;
> > + struct v4l2_device v4l2_dev;
> > + struct v4l2_m2m_dev *m2m_dev;
> > + void *alloc_ctx;
> > + struct video_device *dec_vdev;
> > + void __iomem *dec_reg_base;
> > + struct clk *clk_jdec;
> > + struct clk *clk_jdec_smi;
> > + struct device *larb;
> > +};
> > +
> > +/**
> > + * struct jpeg_fmt - driver's internal color format data
> > + * @name: format descritpion
> > + * @fourcc: the fourcc code, 0 if not applicable
> > + * @h_sample: horizontal sample count of plane in 4 * 4 pixel image
> > + * @v_sample: vertical sample count of plane in 4 * 4 pixel image
> > + * @colplanes: number of color planes (1 for packed formats)
> > + * @h_align: horizontal alignment order (align to 2^h_align)
> > + * @v_align: vertical alignment order (align to 2^v_align)
> > + * @flags: flags describing format applicability
> > + */
> > +struct mtk_jpeg_fmt {
> > + char *name;
> > + u32 fourcc;
> > + int h_sample[VIDEO_MAX_PLANES];
> > + int v_sample[VIDEO_MAX_PLANES];
> > + int colplanes;
> > + int h_align;
> > + int v_align;
> > + u32 flags;
> > +};
> > +
> > +/**
> > + * mtk_jpeg_q_data - parameters of one queue
> > + * @fmt: driver-specific format of this queue
> > + * @w: image width
> > + * @h: image height
> > + * @bytesperline: distance in bytes between the leftmost pixels in two adjacent
> > + * lines
> > + * @sizeimage: image buffer size in bytes
> > + */
> > +struct mtk_jpeg_q_data {
> > + struct mtk_jpeg_fmt *fmt;
> > + u32 w;
> > + u32 h;
> > + u32 bytesperline[VIDEO_MAX_PLANES];
> > + u32 sizeimage[VIDEO_MAX_PLANES];
> > +};
> > +
> > +/**
> > + * mtk_jpeg_ctx - the device context data
> > + * @jpeg: JPEG IP device for this context
> > + * @out_q: source (output) queue information
> > + * @cap_q: destination (capture) queue queue information
> > + * @fh: V4L2 file handle
> > + * @dec_param parameters for HW decoding
> > + * @state: state of the context
> > + * @header_valid: set if header has been parsed and valid
> > + * @colorspace: enum v4l2_colorspace; supplemental to pixelformat
> > + * @ycbcr_enc: enum v4l2_ycbcr_encoding, Y'CbCr encoding
> > + * @quantization: enum v4l2_quantization, colorspace quantization
> > + * @xfer_func: enum v4l2_xfer_func, colorspace transfer function
> > + */
> > +struct mtk_jpeg_ctx {
> > + struct mtk_jpeg_dev *jpeg;
> > + struct mtk_jpeg_q_data out_q;
> > + struct mtk_jpeg_q_data cap_q;
> > + struct v4l2_fh fh;
> > + enum mtk_jpeg_ctx_state state;
> > +
> > + enum v4l2_colorspace colorspace;
> > + enum v4l2_ycbcr_encoding ycbcr_enc;
> > + enum v4l2_quantization quantization;
> > + enum v4l2_xfer_func xfer_func;
> > +};
> > +
> > +#endif /* _MTK_JPEG_CORE_H */
> > diff --git a/drivers/media/platform/mtk-jpeg/mtk_jpeg_hw.c b/drivers/media/platform/mtk-jpeg/mtk_jpeg_hw.c
> > new file mode 100644
> > index 0000000..a6315f3
> > --- /dev/null
> > +++ b/drivers/media/platform/mtk-jpeg/mtk_jpeg_hw.c
> > @@ -0,0 +1,417 @@
> > +/*
> > + * Copyright (c) 2016 MediaTek Inc.
> > + * Author: Ming Hsiu Tsai <minghsiu.tsai-NuS5LvNUpcJWk0Htik3J/w@public.gmane.org>
> > + * Rick Chang <rick.chang-NuS5LvNUpcJWk0Htik3J/w@public.gmane.org>
> > + *
> > + * This program is free software; you can redistribute it and/or modify
> > + * it under the terms of the GNU General Public License version 2 as
> > + * published by the Free Software Foundation.
> > + *
> > + * This program is distributed in the hope that it will be useful,
> > + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> > + * GNU General Public License for more details.
> > + */
> > +
> > +#include <linux/io.h>
> > +#include <linux/kernel.h>
> > +#include <media/videobuf2-core.h>
> > +
> > +#include "mtk_jpeg_hw.h"
> > +
> > +#define MTK_JPEG_DUNUM_MASK(val) (((val) - 1) & 0x3)
> > +
> > +enum mtk_jpeg_color {
> > + MTK_JPEG_COLOR_420 = 0x00221111,
> > + MTK_JPEG_COLOR_422 = 0x00211111,
> > + MTK_JPEG_COLOR_444 = 0x00111111,
> > + MTK_JPEG_COLOR_422V = 0x00121111,
> > + MTK_JPEG_COLOR_422X2 = 0x00412121,
> > + MTK_JPEG_COLOR_422VX2 = 0x00222121,
> > + MTK_JPEG_COLOR_400 = 0x00110000
> > +};
> > +
> > +static inline int mtk_jpeg_verify_align(u32 val, int align, u32 reg)
> > +{
> > + if (val & (align - 1)) {
> > + pr_err("mtk-jpeg: write reg %x without %d align\n", reg, align);
> > + return -1;
> > + }
> > +
> > + return 0;
> > +}
> > +
> > +static int mtk_jpeg_decide_format(struct mtk_jpeg_dec_param *param)
> > +{
> > + param->src_color = (param->sampling_w[0] << 20) |
> > + (param->sampling_h[0] << 16) |
> > + (param->sampling_w[1] << 12) |
> > + (param->sampling_h[1] << 8) |
> > + (param->sampling_w[2] << 4) |
> > + (param->sampling_h[2]);
> > +
> > + param->uv_brz_w = 0;
> > + switch (param->src_color) {
> > + case MTK_JPEG_COLOR_444:
> > + param->uv_brz_w = 1;
> > + param->dst_fourcc = V4L2_PIX_FMT_YUV422M;
> > + break;
> > + case MTK_JPEG_COLOR_422X2:
> > + case MTK_JPEG_COLOR_422:
> > + param->dst_fourcc = V4L2_PIX_FMT_YUV422M;
> > + break;
> > + case MTK_JPEG_COLOR_422V:
> > + case MTK_JPEG_COLOR_422VX2:
> > + param->uv_brz_w = 1;
> > + param->dst_fourcc = V4L2_PIX_FMT_YUV420M;
> > + break;
> > + case MTK_JPEG_COLOR_420:
> > + param->dst_fourcc = V4L2_PIX_FMT_YUV420M;
> > + break;
> > + case MTK_JPEG_COLOR_400:
> > + param->dst_fourcc = V4L2_PIX_FMT_GREY;
> > + break;
> > + default:
> > + param->dst_fourcc = 0;
> > + return -1;
> > + }
> > +
> > + return 0;
> > +}
> > +
> > +static void mtk_jpeg_calc_mcu(struct mtk_jpeg_dec_param *param)
> > +{
> > + u32 factor_w, factor_h;
> > + u32 i, comp, blk;
> > +
> > + factor_w = 2 + param->sampling_w[0];
> > + factor_h = 2 + param->sampling_h[0];
> > + param->mcu_w = (param->pic_w + (1 << factor_w) - 1) >> factor_w;
> > + param->mcu_h = (param->pic_h + (1 << factor_h) - 1) >> factor_h;
> > + param->total_mcu = param->mcu_w * param->mcu_h;
> > + param->unit_num = ((param->pic_w + 7) >> 3) * ((param->pic_h + 7) >> 3);
> > + param->blk_num = 0;
> > + for (i = 0; i < MTK_JPEG_COMP_MAX; i++) {
> > + param->blk_comp[i] = 0;
> > + if (i >= param->comp_num)
> > + continue;
> > + param->blk_comp[i] = param->sampling_w[i] *
> > + param->sampling_h[i];
> > + param->blk_num += param->blk_comp[i];
> > + }
> > +
> > + param->membership = 0;
> > + for (i = 0, blk = 0, comp = 0; i < MTK_JPEG_BLOCK_MAX; i++) {
> > + if (i < param->blk_num && comp < param->comp_num) {
> > + u32 tmp;
> > +
> > + tmp = (0x04 + (comp & 0x3));
> > + param->membership |= tmp << (i * 3);
> > + if (++blk == param->blk_comp[comp]) {
> > + comp++;
> > + blk = 0;
> > + }
> > + } else {
> > + param->membership |= 7 << (i * 3);
> > + }
> > + }
> > +}
> > +
> > +static void mtk_jpeg_calc_dma_group(struct mtk_jpeg_dec_param *param)
> > +{
> > + u32 factor_mcu = 3;
> > +
> > + if (param->src_color == MTK_JPEG_COLOR_444 &&
> > + param->dst_fourcc == V4L2_PIX_FMT_YUV422M)
> > + factor_mcu = 4;
> > + else if (param->src_color == MTK_JPEG_COLOR_422V &&
> > + param->dst_fourcc == V4L2_PIX_FMT_YUV420M)
> > + factor_mcu = 4;
> > + else if (param->src_color == MTK_JPEG_COLOR_422X2 &&
> > + param->dst_fourcc == V4L2_PIX_FMT_YUV422M)
> > + factor_mcu = 2;
> > + else if (param->src_color == MTK_JPEG_COLOR_400 ||
> > + (param->src_color & 0x0FFFF) == 0)
> > + factor_mcu = 4;
> > +
> > + param->dma_mcu = 1 << factor_mcu;
> > + param->dma_group = param->mcu_w / param->dma_mcu;
> > + param->dma_last_mcu = param->mcu_w % param->dma_mcu;
> > + if (param->dma_last_mcu)
> > + param->dma_group++;
> > + else
> > + param->dma_last_mcu = param->dma_mcu;
> > +}
> > +
> > +static int mtk_jpeg_calc_dst_size(struct mtk_jpeg_dec_param *param)
> > +{
> > + u32 i, padding_w;
> > + u32 ds_row_h[3];
> > + u32 brz_w[3];
> > +
> > + brz_w[0] = 0;
> > + brz_w[1] = param->uv_brz_w;
> > + brz_w[2] = brz_w[1];
> > +
> > + for (i = 0; i < param->comp_num; i++) {
> > + if (brz_w[i] > 3)
> > + return -1;
> > +
> > + padding_w = param->mcu_w * MTK_JPEG_DCTSIZE *
> > + param->sampling_w[i];
> > + /* output format is 420/422 */
> > + param->comp_w[i] = padding_w >> brz_w[i];
> > + param->comp_w[i] = mtk_jpeg_align(param->comp_w[i],
> > + MTK_JPEG_DCTSIZE);
> > + param->img_stride[i] = i ? mtk_jpeg_align(param->comp_w[i], 16)
> > + : mtk_jpeg_align(param->comp_w[i], 32);
> > + ds_row_h[i] = (MTK_JPEG_DCTSIZE * param->sampling_h[i]);
> > + }
> > + param->dec_w = param->img_stride[0];
> > + param->dec_h = ds_row_h[0] * param->mcu_h;
> > +
> > + for (i = 0; i < MTK_JPEG_COMP_MAX; i++) {
> > + /* They must be equal in frame mode. */
> > + param->mem_stride[i] = param->img_stride[i];
> > + param->comp_size[i] = param->mem_stride[i] * ds_row_h[i] *
> > + param->mcu_h;
> > + }
> > +
> > + param->y_size = param->comp_size[0];
> > + param->uv_size = param->comp_size[1];
> > + param->dec_size = param->y_size + (param->uv_size << 1);
> > +
> > + return 0;
> > +}
> > +
> > +int mtk_jpeg_dec_fill_param(struct mtk_jpeg_dec_param *param)
> > +{
> > + if (mtk_jpeg_decide_format(param))
> > + return -1;
> > +
> > + mtk_jpeg_calc_mcu(param);
> > + mtk_jpeg_calc_dma_group(param);
> > + if (mtk_jpeg_calc_dst_size(param))
> > + return -2;
> > +
> > + return 0;
> > +}
> > +
> > +u32 mtk_jpeg_dec_get_int_status(void __iomem *base)
> > +{
> > + u32 ret;
> > +
> > + ret = readl(base + JPGDEC_REG_INTERRUPT_STATUS) & BIT_INQST_MASK_ALLIRQ;
> > + if (ret)
> > + writel(ret, base + JPGDEC_REG_INTERRUPT_STATUS);
> > +
> > + return ret;
> > +}
> > +
> > +u32 mtk_jpeg_dec_enum_result(u32 irq_result)
> > +{
> > + if (irq_result & BIT_INQST_MASK_EOF)
> > + return MTK_JPEG_DEC_RESULT_EOF_DONE;
> > + else if (irq_result & BIT_INQST_MASK_PAUSE)
> > + return MTK_JPEG_DEC_RESULT_PAUSE;
> > + else if (irq_result & BIT_INQST_MASK_UNDERFLOW)
> > + return MTK_JPEG_DEC_RESULT_UNDERFLOW;
> > + else if (irq_result & BIT_INQST_MASK_OVERFLOW)
> > + return MTK_JPEG_DEC_RESULT_OVERFLOW;
> > + else if (irq_result & BIT_INQST_MASK_ERROR_BS)
> > + return MTK_JPEG_DEC_RESULT_ERROR_BS;
>
> No need for 'else' here since the previous 'if' always returns if true.
Ok.
> > +
> > + return MTK_JPEG_DEC_RESULT_ERROR_UNKNOWN;
> > +}
> > +
> > +void mtk_jpeg_dec_start(void __iomem *base)
> > +{
> > + writel(0, base + JPGDEC_REG_TRIG);
> > +}
> > +
> > +static void mtk_jpeg_dec_soft_reset(void __iomem *base)
> > +{
> > + writel(0x0000FFFF, base + JPGDEC_REG_INTERRUPT_STATUS);
> > + writel(0x00, base + JPGDEC_REG_RESET);
> > + writel(0x01, base + JPGDEC_REG_RESET);
> > +}
> > +
> > +static void mtk_jpeg_dec_hard_reset(void __iomem *base)
> > +{
> > + writel(0x00, base + JPGDEC_REG_RESET);
> > + writel(0x10, base + JPGDEC_REG_RESET);
> > +}
> > +
> > +void mtk_jpeg_dec_reset(void __iomem *base)
> > +{
> > + mtk_jpeg_dec_soft_reset(base);
> > + mtk_jpeg_dec_hard_reset(base);
> > +}
> > +
> > +static void mtk_jpeg_dec_set_brz_factor(void __iomem *base, u8 yscale_w,
> > + u8 yscale_h, u8 uvscale_w, u8 uvscale_h)
> > +{
> > + u32 val;
> > +
> > + val = (uvscale_h << 12) | (uvscale_w << 8) |
> > + (yscale_h << 4) | yscale_w;
> > + writel(val, base + JPGDEC_REG_BRZ_FACTOR);
> > +}
> > +
> > +static void mtk_jpeg_dec_set_dst_bank0(void __iomem *base, u32 addr_y,
> > + u32 addr_u, u32 addr_v)
> > +{
> > + mtk_jpeg_verify_align(addr_y, 16, JPGDEC_REG_DEST_ADDR0_Y);
> > + writel(addr_y, base + JPGDEC_REG_DEST_ADDR0_Y);
> > + mtk_jpeg_verify_align(addr_u, 16, JPGDEC_REG_DEST_ADDR0_U);
> > + writel(addr_u, base + JPGDEC_REG_DEST_ADDR0_U);
> > + mtk_jpeg_verify_align(addr_v, 16, JPGDEC_REG_DEST_ADDR0_V);
> > + writel(addr_v, base + JPGDEC_REG_DEST_ADDR0_V);
> > +}
> > +
> > +static void mtk_jpeg_dec_set_dst_bank1(void __iomem *base, u32 addr_y,
> > + u32 addr_u, u32 addr_v)
> > +{
> > + writel(addr_y, base + JPGDEC_REG_DEST_ADDR1_Y);
> > + writel(addr_u, base + JPGDEC_REG_DEST_ADDR1_U);
> > + writel(addr_v, base + JPGDEC_REG_DEST_ADDR1_V);
> > +}
> > +
> > +static void mtk_jpeg_dec_set_mem_stride(void __iomem *base, u32 stride_y,
> > + u32 stride_uv)
> > +{
> > + writel((stride_y & 0xFFFF), base + JPGDEC_REG_STRIDE_Y);
> > + writel((stride_uv & 0xFFFF), base + JPGDEC_REG_STRIDE_UV);
> > +}
> > +
> > +static void mtk_jpeg_dec_set_img_stride(void __iomem *base, u32 stride_y,
> > + u32 stride_uv)
> > +{
> > + writel((stride_y & 0xFFFF), base + JPGDEC_REG_IMG_STRIDE_Y);
> > + writel((stride_uv & 0xFFFF), base + JPGDEC_REG_IMG_STRIDE_UV);
> > +}
> > +
> > +static void mtk_jpeg_dec_set_pause_mcu_idx(void __iomem *base, u32 idx)
> > +{
> > + writel(idx & 0x0003FFFFFF, base + JPGDEC_REG_PAUSE_MCU_NUM);
> > +}
> > +
> > +static void mtk_jpeg_dec_set_dec_mode(void __iomem *base, u32 mode)
> > +{
> > + writel(mode & 0x03, base + JPGDEC_REG_OPERATION_MODE);
> > +}
> > +
> > +static void mtk_jpeg_dec_set_bs_write_ptr(void __iomem *base, u32 ptr)
> > +{
> > + mtk_jpeg_verify_align(ptr, 16, JPGDEC_REG_FILE_BRP);
> > + writel(ptr, base + JPGDEC_REG_FILE_BRP);
> > +}
> > +
> > +static void mtk_jpeg_dec_set_bs_info(void __iomem *base, u32 addr, u32 size)
> > +{
> > + mtk_jpeg_verify_align(addr, 16, JPGDEC_REG_FILE_ADDR);
> > + mtk_jpeg_verify_align(size, 128, JPGDEC_REG_FILE_TOTAL_SIZE);
> > + writel(addr, base + JPGDEC_REG_FILE_ADDR);
> > + writel(size, base + JPGDEC_REG_FILE_TOTAL_SIZE);
> > +}
> > +
> > +static void mtk_jpeg_dec_set_comp_id(void __iomem *base, u32 id_y, u32 id_u,
> > + u32 id_v)
> > +{
> > + u32 val;
> > +
> > + val = ((id_y & 0x00FF) << 24) | ((id_u & 0x00FF) << 16) |
> > + ((id_v & 0x00FF) << 8);
> > + writel(val, base + JPGDEC_REG_COMP_ID);
> > +}
> > +
> > +static void mtk_jpeg_dec_set_total_mcu(void __iomem *base, u32 num)
> > +{
> > + writel(num - 1, base + JPGDEC_REG_TOTAL_MCU_NUM);
> > +}
> > +
> > +static void mtk_jpeg_dec_set_comp0_du(void __iomem *base, u32 num)
> > +{
> > + writel(num - 1, base + JPGDEC_REG_COMP0_DATA_UNIT_NUM);
> > +}
> > +
> > +static void mtk_jpeg_dec_set_du_membership(void __iomem *base, u32 member,
> > + u32 gmc, u32 isgray)
> > +{
> > + if (isgray)
> > + member = 0x3FFFFFFC;
> > + member |= (isgray << 31) | (gmc << 30);
> > + writel(member, base + JPGDEC_REG_DU_CTRL);
> > +}
> > +
> > +static void mtk_jpeg_dec_set_q_table(void __iomem *base, u32 id0, u32 id1,
> > + u32 id2)
> > +{
> > + u32 val;
> > +
> > + val = ((id0 & 0x0f) << 8) | ((id1 & 0x0f) << 4) | ((id2 & 0x0f) << 0);
> > + writel(val, base + JPGDEC_REG_QT_ID);
> > +}
> > +
> > +static void mtk_jpeg_dec_set_dma_group(void __iomem *base, u32 mcu_group,
> > + u32 group_num, u32 last_mcu)
> > +{
> > + u32 val;
> > +
> > + val = (((mcu_group - 1) & 0x00FF) << 16) |
> > + (((group_num - 1) & 0x007F) << 8) |
> > + ((last_mcu - 1) & 0x00FF);
> > + writel(val, base + JPGDEC_REG_WDMA_CTRL);
> > +}
> > +
> > +static void mtk_jpeg_dec_set_sampling_factor(void __iomem *base, u32 comp_num,
> > + u32 y_w, u32 y_h, u32 u_w,
> > + u32 u_h, u32 v_w, u32 v_h)
> > +{
> > + u32 val;
> > + u32 y_wh = (MTK_JPEG_DUNUM_MASK(y_w) << 2) | MTK_JPEG_DUNUM_MASK(y_h);
> > + u32 u_wh = (MTK_JPEG_DUNUM_MASK(u_w) << 2) | MTK_JPEG_DUNUM_MASK(u_h);
> > + u32 v_wh = (MTK_JPEG_DUNUM_MASK(v_w) << 2) | MTK_JPEG_DUNUM_MASK(v_h);
> > +
> > + if (comp_num == 1)
> > + val = 0;
> > + else
> > + val = (y_wh << 8) | (u_wh << 4) | v_wh;
> > + writel(val, base + JPGDEC_REG_DU_NUM);
> > +}
> > +
> > +void mtk_jpeg_dec_set_config(void __iomem *base,
> > + struct mtk_jpeg_dec_param *config,
> > + struct mtk_jpeg_bs *bs,
> > + struct mtk_jpeg_fb *fb)
> > +{
> > + mtk_jpeg_dec_set_brz_factor(base, 0, 0, config->uv_brz_w, 0);
> > + mtk_jpeg_dec_set_dec_mode(base, 0);
> > + mtk_jpeg_dec_set_comp0_du(base, config->unit_num);
> > + mtk_jpeg_dec_set_total_mcu(base, config->total_mcu);
> > + mtk_jpeg_dec_set_bs_info(base, bs->str_addr, bs->size);
> > + mtk_jpeg_dec_set_bs_write_ptr(base, bs->end_addr);
> > + mtk_jpeg_dec_set_du_membership(base, config->membership, 1,
> > + (config->comp_num == 1) ? 1 : 0);
> > + mtk_jpeg_dec_set_comp_id(base, config->comp_id[0], config->comp_id[1],
> > + config->comp_id[2]);
> > + mtk_jpeg_dec_set_q_table(base, config->qtbl_num[0],
> > + config->qtbl_num[1], config->qtbl_num[2]);
> > + mtk_jpeg_dec_set_sampling_factor(base, config->comp_num,
> > + config->sampling_w[0],
> > + config->sampling_h[0],
> > + config->sampling_w[1],
> > + config->sampling_h[1],
> > + config->sampling_w[2],
> > + config->sampling_h[2]);
> > + mtk_jpeg_dec_set_mem_stride(base, config->mem_stride[0],
> > + config->mem_stride[1]);
> > + mtk_jpeg_dec_set_img_stride(base, config->img_stride[0],
> > + config->img_stride[1]);
> > + mtk_jpeg_dec_set_dst_bank0(base, fb->plane_addr[0],
> > + fb->plane_addr[1], fb->plane_addr[2]);
> > + mtk_jpeg_dec_set_dst_bank1(base, 0, 0, 0);
> > + mtk_jpeg_dec_set_dma_group(base, config->dma_mcu, config->dma_group,
> > + config->dma_last_mcu);
> > + mtk_jpeg_dec_set_pause_mcu_idx(base, config->total_mcu);
> > +}
>
> Regards,
>
> Hans
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* Re: [RESEND PATCH v1 03/11] drivers: soc: hisi: Add support for Hisilicon Djtag driver
From: Anurup M @ 2016-11-15 10:15 UTC (permalink / raw)
To: Mark Rutland
Cc: devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
linux-doc-u79uwXL29TY76Z2rM5mHXA, will.deacon-5wv7dgnIgG8,
corbet-T1hC0tSOHrs, catalin.marinas-5wv7dgnIgG8,
robh+dt-DgEjT+Ai2ygdnm+yROfE0A, arnd-r2nGTMty4D4,
f.fainelli-Re5JQEeQqe8AvxtiuMwx3w,
rmk+kernel-lFZ/pmaqli7XmaaqVzeoHQ, krzk-DgEjT+Ai2ygdnm+yROfE0A,
anurup.m-hv44wF8Li93QT0dZR+AlfA,
zhangshaokun-C8/M+/jPZTeaMJb+Lgu22Q,
tanxiaojun-hv44wF8Li93QT0dZR+AlfA, xuwei5-C8/M+/jPZTeaMJb+Lgu22Q,
sanil.kumar-C8/M+/jPZTeaMJb+Lgu22Q,
john.garry-hv44wF8Li93QT0dZR+AlfA,
gabriele.paoloni-hv44wF8Li93QT0dZR+AlfA,
shiju.jose-hv44wF8Li93QT0dZR+AlfA,
wangkefeng.wang-hv44wF8Li93QT0dZR+AlfA,
guohanjun-hv44wF8Li93QT0dZR+AlfA, shyju.pv-hv44wF8Li93QT0dZR+AlfA,
linuxarm-hv44wF8Li93QT0dZR+AlfA
In-Reply-To: <20161110175510.GB10137@leverpostej>
On Thursday 10 November 2016 11:25 PM, Mark Rutland wrote:
> On Thu, Nov 03, 2016 at 01:41:59AM -0400, Anurup M wrote:
>> From: Tan Xiaojun <tanxiaojun-hv44wF8Li93QT0dZR+AlfA@public.gmane.org>
>>
>> The Hisilicon Djtag is an independent component which connects
>> with some other components in the SoC by Debug Bus. This driver
>> can be configured to access the registers of connecting components
>> (like L3 cache) during real time debugging.
>>
> Just to check, is this likely to be used in multi-socket hardware, and
> if so, are instances always-on?
Yes, It could be used in multi-socket hardware also.
The sockets are always enabled after bootup. Sorry I didn't get the
>> Signed-off-by: Tan Xiaojun <tanxiaojun-hv44wF8Li93QT0dZR+AlfA@public.gmane.org>
>> Signed-off-by: John Garry <john.garry-hv44wF8Li93QT0dZR+AlfA@public.gmane.org>
>> Signed-off-by: Anurup M <anurup.m-hv44wF8Li93QT0dZR+AlfA@public.gmane.org>
>> ---
>> drivers/soc/Kconfig | 1 +
>> drivers/soc/Makefile | 1 +
>> drivers/soc/hisilicon/Kconfig | 12 +
>> drivers/soc/hisilicon/Makefile | 1 +
>> drivers/soc/hisilicon/djtag.c | 639 ++++++++++++++++++++++++++++++++++++
>> include/linux/soc/hisilicon/djtag.h | 38 +++
>> 6 files changed, 692 insertions(+)
>> create mode 100644 drivers/soc/hisilicon/Kconfig
>> create mode 100644 drivers/soc/hisilicon/Makefile
>> create mode 100644 drivers/soc/hisilicon/djtag.c
>> create mode 100644 include/linux/soc/hisilicon/djtag.h
> Other than the PMU driver(s), what is going to use this?
>
> If you don't have something in particular, please also place this under
> drivers/perf/hisilicon, along with the PMU driver(s).
>
> We can always move it later if necessary.
>
> [...]
Arnd also suggested the same. Currently as there are no other users I
shall move it to
drivers/perf/hisilicon.
>> +#define SC_DJTAG_TIMEOUT 100000 /* 100ms */
> This would be better as:
>
> #define SC_DJTAG_TIMEOUT_US (100 * USEC_PER_MSEC)
>
> (you'll need to include <linux/time64.h>)
>
> [...]
OK.
>> +static void djtag_read32_relaxed(void __iomem *regs_base, u32 off, u32 *value)
>> +{
>> + void __iomem *reg_addr = regs_base + off;
>> +
>> + *value = readl_relaxed(reg_addr);
>> +}
>> +
>> +static void djtag_write32(void __iomem *regs_base, u32 off, u32 val)
>> +{
>> + void __iomem *reg_addr = regs_base + off;
>> +
>> + writel(val, reg_addr);
>> +}
> I think these make the driver harder to read, especially given the read
> function is void and takes an output pointer.
>
> In either case you can call readl/writel directly with base + off for
> the __iomem ptr. Please do that.
Ok.
>> +
>> +/*
>> + * djtag_readwrite_v1/v2: djtag read/write interface
>> + * @reg_base: djtag register base address
>> + * @offset: register's offset
>> + * @mod_sel: module selection
>> + * @mod_mask: mask to select specific modules for write
>> + * @is_w: write -> true, read -> false
>> + * @wval: value to register for write
>> + * @chain_id: which sub module for read
>> + * @rval: value in register for read
>> + *
>> + * Return non-zero if error, else return 0.
>> + */
>> +static int djtag_readwrite_v1(void __iomem *regs_base, u32 offset, u32 mod_sel,
>> + u32 mod_mask, bool is_w, u32 wval, int chain_id, u32 *rval)
>> +{
>> + u32 rd;
>> + int timeout = SC_DJTAG_TIMEOUT;
>> +
>> + if (!(mod_mask & CHAIN_UNIT_CFG_EN)) {
>> + pr_warn("djtag: do nothing.\n");
>> + return 0;
>> + }
>> +
>> + /* djtag mster enable & accelerate R,W */
>> + djtag_write32(regs_base, SC_DJTAG_MSTR_EN,
>> + DJTAG_NOR_CFG | DJTAG_MSTR_EN);
>> +
>> + /* select module */
>> + djtag_write32(regs_base, SC_DJTAG_DEBUG_MODULE_SEL, mod_sel);
>> + djtag_write32(regs_base, SC_DJTAG_CHAIN_UNIT_CFG_EN,
>> + mod_mask & CHAIN_UNIT_CFG_EN);
>> +
>> + if (is_w) {
>> + djtag_write32(regs_base, SC_DJTAG_MSTR_WR, DJTAG_MSTR_W);
>> + djtag_write32(regs_base, SC_DJTAG_MSTR_DATA, wval);
>> + } else
>> + djtag_write32(regs_base, SC_DJTAG_MSTR_WR, DJTAG_MSTR_R);
>> +
>> + /* address offset */
>> + djtag_write32(regs_base, SC_DJTAG_MSTR_ADDR, offset);
>> +
>> + /* start to write to djtag register */
>> + djtag_write32(regs_base, SC_DJTAG_MSTR_START_EN, DJTAG_MSTR_START_EN);
>> +
>> + /* ensure the djtag operation is done */
>> + do {
>> + djtag_read32_relaxed(regs_base, SC_DJTAG_MSTR_START_EN, &rd);
>> + if (!(rd & DJTAG_MSTR_EN))
>> + break;
>> +
>> + udelay(1);
>> + } while (timeout--);
>> +
>> + if (timeout < 0) {
>> + pr_err("djtag: %s timeout!\n", is_w ? "write" : "read");
>> + return -EBUSY;
>> + }
>> +
>> + if (!is_w)
>> + djtag_read32_relaxed(regs_base,
>> + SC_DJTAG_RD_DATA_BASE + chain_id * 0x4, rval);
>> +
>> + return 0;
>> +}
> Please factor out the common bits into helpers and have separate
> read/write functions. It's incredibly difficult to follow the code with
> read/write hidden behind a boolean parameter.
Ok. Shall change it.
>> +static const struct of_device_id djtag_of_match[] = {
>> + /* for hip05(D02) cpu die */
>> + { .compatible = "hisilicon,hip05-cpu-djtag-v1",
>> + .data = (void *)djtag_readwrite_v1 },
>> + /* for hip05(D02) io die */
>> + { .compatible = "hisilicon,hip05-io-djtag-v1",
>> + .data = (void *)djtag_readwrite_v1 },
>> + /* for hip06(D03) cpu die */
>> + { .compatible = "hisilicon,hip06-cpu-djtag-v1",
>> + .data = (void *)djtag_readwrite_v1 },
>> + /* for hip06(D03) io die */
>> + { .compatible = "hisilicon,hip06-io-djtag-v2",
>> + .data = (void *)djtag_readwrite_v2 },
>> + /* for hip07(D05) cpu die */
>> + { .compatible = "hisilicon,hip07-cpu-djtag-v2",
>> + .data = (void *)djtag_readwrite_v2 },
>> + /* for hip07(D05) io die */
>> + { .compatible = "hisilicon,hip07-io-djtag-v2",
>> + .data = (void *)djtag_readwrite_v2 },
>> + {},
>> +};
> Binding documentation for all of these should be added *before* this
> patch, per Documentation/devicetree/bindings/submitting-patches.txt.
> Please move any relevant binding documentation earlier in the series.
The binding documentation added in "[RESEND PATCH v1 02/11] dt-bindings:
hisi: Add Hisilicon HiP05/06/07 Sysctrl and Djtag dts bindings]".
>> +MODULE_DEVICE_TABLE(of, djtag_of_match);
>> +
>> +static ssize_t
>> +show_modalias(struct device *dev, struct device_attribute *attr, char *buf)
>> +{
>> + struct hisi_djtag_client *client = to_hisi_djtag_client(dev);
>> +
>> + return sprintf(buf, "%s%s\n", MODULE_PREFIX, client->name);
>> +}
>> +static DEVICE_ATTR(modalias, 0444, show_modalias, NULL);
>> +
>> +static struct attribute *hisi_djtag_dev_attrs[] = {
>> + NULL,
>> + /* modalias helps coldplug: modprobe $(cat .../modalias) */
>> + &dev_attr_modalias.attr,
>> + NULL
>> +};
>> +ATTRIBUTE_GROUPS(hisi_djtag_dev);
> Why do we need to expose this under sysfs?
This is to know the djtag clients registered with the bus.
>> +struct bus_type hisi_djtag_bus = {
>> + .name = "hisi-djtag",
>> + .match = hisi_djtag_device_match,
>> + .probe = hisi_djtag_device_probe,
>> + .remove = hisi_djtag_device_remove,
>> +};
> I take it the core bus code handles reference-counting users of the bus?
The bus_register registers with kobject. Did It answer the question?
>> +struct hisi_djtag_client *hisi_djtag_new_device(struct hisi_djtag_host *host,
>> + struct device_node *node)
>> +{
>> + struct hisi_djtag_client *client;
>> + int status;
>> +
>> + client = kzalloc(sizeof(*client), GFP_KERNEL);
>> + if (!client)
>> + return NULL;
>> +
>> + client->host = host;
>> +
>> + client->dev.parent = &client->host->dev;
>> + client->dev.bus = &hisi_djtag_bus;
>> + client->dev.type = &hisi_djtag_client_type;
>> + client->dev.of_node = node;
> I suspect that we should do:
>
> client->dev.of_node = of_node_get(node);
>
> ... so that it's guaranteed we retain a reference.
Ok.
>> + snprintf(client->name, DJTAG_CLIENT_NAME_LEN, "%s%s",
>> + DJTAG_PREFIX, node->name);
>> + dev_set_name(&client->dev, "%s", client->name);
>> +
>> + status = device_register(&client->dev);
>> + if (status < 0) {
>> + pr_err("error adding new device, status=%d\n", status);
>> + kfree(client);
>> + return NULL;
>> + }
>> +
>> + return client;
>> +}
>> +
>> +static struct hisi_djtag_client *hisi_djtag_of_register_device(
>> + struct hisi_djtag_host *host,
>> + struct device_node *node)
>> +{
>> + struct hisi_djtag_client *client;
>> +
>> + client = hisi_djtag_new_device(host, node);
>> + if (client == NULL) {
>> + dev_err(&host->dev, "error registering device %s\n",
>> + node->full_name);
>> + of_node_put(node);
> I don't think this should be here, given what djtag_register_devices()
> does.
Will move this to djtag_register_devices.
>> + return ERR_PTR(-EINVAL);
>> + }
>> +
>> + return client;
>> +}
>> +
>> +static void djtag_register_devices(struct hisi_djtag_host *host)
>> +{
>> + struct device_node *node;
>> + struct hisi_djtag_client *client;
>> +
>> + if (!host->of_node)
>> + return;
>> +
>> + for_each_available_child_of_node(host->of_node, node) {
>> + if (of_node_test_and_set_flag(node, OF_POPULATED))
>> + continue;
>> + client = hisi_djtag_of_register_device(host, node);
>> + list_add(&client->next, &host->client_list);
>> + }
>> +}
> Given hisi_djtag_of_register_device() can return ERR_PTR(-EINVAL), the
> list_add is not safe.
Shall add the check and handle accordingly.
>> +static int djtag_host_probe(struct platform_device *pdev)
>> +{
>> + struct device *dev = &pdev->dev;
>> + struct hisi_djtag_host *host;
>> + const struct of_device_id *of_id;
>> + struct resource *res;
>> + int rc;
>> +
>> + of_id = of_match_device(djtag_of_match, dev);
>> + if (!of_id)
>> + return -EINVAL;
>> +
>> + host = kzalloc(sizeof(*host), GFP_KERNEL);
>> + if (!host)
>> + return -ENOMEM;
>> +
>> + host->of_node = dev->of_node;
> host->of_node = of_node_get(dev->of_node);
>
>> + host->djtag_readwrite = of_id->data;
>> + spin_lock_init(&host->lock);
>> +
>> + INIT_LIST_HEAD(&host->client_list);
>> +
>> + res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
>> + if (!res) {
>> + dev_err(&pdev->dev, "No reg resorces!\n");
>> + kfree(host);
>> + return -EINVAL;
>> + }
>> +
>> + if (!resource_size(res)) {
>> + dev_err(&pdev->dev, "Zero reg entry!\n");
>> + kfree(host);
>> + return -EINVAL;
>> + }
>> +
>> + host->sysctl_reg_map = devm_ioremap_resource(dev, res);
>> + if (IS_ERR(host->sysctl_reg_map)) {
>> + dev_warn(dev, "Unable to map sysctl registers.\n");
>> + kfree(host);
>> + return -EINVAL;
>> + }
> Please have a common error path rather than duplicating this free.
>
> e.g. at the end of the function have:
>
> err_free:
> kfree(host);
> return err;
>
> ... and in cases like this, have:
>
> if (whatever()) {
> dev_warn(dev, "this failed xxx\n");
> err = -EINVAL;
> goto err_free;
> }
Ok. thanks. will change it.
Thanks,
Anurup
> Thanks,
> Mark.
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* Re: [PATCH 1/2] ARM: dts: sun5i: Add touchscreen node to reference-design-tablet.dtsi
From: Hans de Goede @ 2016-11-15 10:12 UTC (permalink / raw)
To: Maxime Ripard; +Cc: devicetree, Chen-Yu Tsai, linux-arm-kernel
In-Reply-To: <20161114200802.vdduhyxnkgmpsyd5@lukather>
Hi,
On 14-11-16 21:08, Maxime Ripard wrote:
> Hi,
>
> On Sun, Nov 13, 2016 at 08:22:02PM +0100, Hans de Goede wrote:
>> Just like on sun8i all sun5i tablets use the same interrupt and power
>> gpios for their touchscreens. I've checked all known a13 fex files and
>> only the UTOO P66 uses a different gpio for the interrupt.
>>
>> Add a touchscreen node to sun5i-reference-design-tablet.dtsi, which
>> fills in the necessary gpios to avoid duplication in the tablet dts files,
>> just like we do in sun8i-reference-design-tablet.dtsi.
>>
>> This will make future patches adding touchscreen nodes to a13 tablets
>> simpler.
>>
>> Signed-off-by: Hans de Goede <hdegoede@redhat.com>
>> ---
>> arch/arm/boot/dts/sun5i-a13-utoo-p66.dts | 38 ++++++++--------------
>> .../boot/dts/sun5i-reference-design-tablet.dtsi | 25 ++++++++++++++
>> 2 files changed, 39 insertions(+), 24 deletions(-)
>>
>> diff --git a/arch/arm/boot/dts/sun5i-a13-utoo-p66.dts b/arch/arm/boot/dts/sun5i-a13-utoo-p66.dts
>> index a8b0bcc..3d7ff10 100644
>> --- a/arch/arm/boot/dts/sun5i-a13-utoo-p66.dts
>> +++ b/arch/arm/boot/dts/sun5i-a13-utoo-p66.dts
>> @@ -83,22 +83,6 @@
>> allwinner,pins = "PG3";
>> };
>>
>> -&i2c1 {
>> - icn8318: touchscreen@40 {
>> - compatible = "chipone,icn8318";
>> - reg = <0x40>;
>> - interrupt-parent = <&pio>;
>> - interrupts = <6 9 IRQ_TYPE_EDGE_FALLING>; /* EINT9 (PG9) */
>> - pinctrl-names = "default";
>> - pinctrl-0 = <&ts_wake_pin_p66>;
>> - wake-gpios = <&pio 1 3 GPIO_ACTIVE_HIGH>; /* PB3 */
>> - touchscreen-size-x = <800>;
>> - touchscreen-size-y = <480>;
>> - touchscreen-inverted-x;
>> - touchscreen-swapped-x-y;
>> - };
>> -};
>> -
>> &mmc2 {
>> pinctrl-names = "default";
>> pinctrl-0 = <&mmc2_pins_a>;
>> @@ -121,20 +105,26 @@
>> allwinner,drive = <SUN4I_PINCTRL_10_MA>;
>> allwinner,pull = <SUN4I_PINCTRL_PULL_UP>;
>> };
>> -
>> - ts_wake_pin_p66: ts_wake_pin@0 {
>> - allwinner,pins = "PB3";
>> - allwinner,function = "gpio_out";
>> - allwinner,drive = <SUN4I_PINCTRL_10_MA>;
>> - allwinner,pull = <SUN4I_PINCTRL_NO_PULL>;
>> - };
>> -
>> };
>>
>> ®_usb0_vbus {
>> gpio = <&pio 1 4 GPIO_ACTIVE_HIGH>; /* PB4 */
>> };
>>
>> +&touchscreen {
>> + compatible = "chipone,icn8318";
>> + reg = <0x40>;
>> + /* The P66 uses a different EINT then the reference design */
>> + interrupts = <6 9 IRQ_TYPE_EDGE_FALLING>; /* EINT9 (PG9) */
>> + /* The icn8318 binding expects wake-gpios instead of power-gpios */
>> + wake-gpios = <&pio 1 3 GPIO_ACTIVE_HIGH>; /* PB3 */
>> + touchscreen-size-x = <800>;
>> + touchscreen-size-y = <480>;
>> + touchscreen-inverted-x;
>> + touchscreen-swapped-x-y;
>> + status = "okay";
>> +};
>> +
>> &uart1 {
>> /* The P66 uses the uart pins as gpios */
>> status = "disabled";
>> diff --git a/arch/arm/boot/dts/sun5i-reference-design-tablet.dtsi b/arch/arm/boot/dts/sun5i-reference-design-tablet.dtsi
>> index 20cc940..7af488a 100644
>> --- a/arch/arm/boot/dts/sun5i-reference-design-tablet.dtsi
>> +++ b/arch/arm/boot/dts/sun5i-reference-design-tablet.dtsi
>> @@ -41,6 +41,7 @@
>> */
>> #include "sunxi-reference-design-tablet.dtsi"
>>
>> +#include <dt-bindings/interrupt-controller/irq.h>
>> #include <dt-bindings/pwm/pwm.h>
>>
>> / {
>> @@ -84,6 +85,23 @@
>> };
>>
>> &i2c1 {
>> + /*
>> + * The gsl1680 is rated at 400KHz and it will not work reliable at
>> + * 100KHz, this has been confirmed on multiple different q8 tablets.
>> + * All other devices on this bus are also rated for 400KHz.
>> + */
>> + clock-frequency = <400000>;
>> +
>> + touchscreen: touchscreen {
>> + interrupt-parent = <&pio>;
>> + interrupts = <6 11 IRQ_TYPE_EDGE_FALLING>; /* EINT11 (PG11) */
>> + pinctrl-names = "default";
>> + pinctrl-0 = <&ts_power_pin>;
>> + power-gpios = <&pio 1 3 GPIO_ACTIVE_HIGH>; /* PB3 */
>> + /* Tablet dts must provide reg and compatible */
>> + status = "disabled";
>> + };
>> +
>> pcf8563: rtc@51 {
>> compatible = "nxp,pcf8563";
>> reg = <0x51>;
>> @@ -125,6 +143,13 @@
>> allwinner,pull = <SUN4I_PINCTRL_PULL_UP>;
>> };
>>
>> + ts_power_pin: ts_power_pin {
>> + allwinner,pins = "PB3";
>> + allwinner,function = "gpio_out";
>> + allwinner,drive = <SUN4I_PINCTRL_10_MA>;
>> + allwinner,pull = <SUN4I_PINCTRL_NO_PULL>;
>> + };
>> +
>
> For the next release, we'll switch to the generic pin mux properties
> ("pins" and "function"), and we actually implemented the fact that the
> drive and pull properties are optional, so you can drop them both.
>
> You'll need next + http://lists.infradead.org/pipermail/linux-arm-kernel/2016-November/467123.html
Ok, before I send a v2 first a question about this, for the touchscreen
case I actually need:
allwinner,drive = <SUN4I_PINCTRL_10_MA>;
allwinner,pull = <SUN4I_PINCTRL_NO_PULL>;
Because otherwise when the touchscreen controller is powered by a separate
regulator and that regulator is off, then it may draw just enough current
from its enable pin to be sort-of listening to the i2c bus and mess up
that bus.
So is this the default, or do we get the power-on default when not
specifying these? If it is the power-on default then we do need to
specify these, because AFAICT the power-on drive strength typically
is 20 mA.
Regards,
Hans
^ permalink raw reply
* [PATCHv2 10/10] Documentation: synopsys-dw-mshc: remove the unused properties
From: Jaehoon Chung @ 2016-11-15 10:12 UTC (permalink / raw)
To: linux-mmc-u79uwXL29TY76Z2rM5mHXA
Cc: devicetree-u79uwXL29TY76Z2rM5mHXA,
ulf.hansson-QSEj5FYQhm4dnm+yROfE0A, heiko-4mtYJXux2i+zQB+pC5nmwQ,
shawn.lin-TNX95d0MmH7DzftRWevZcw, robh+dt-DgEjT+Ai2ygdnm+yROfE0A,
Jaehoon Chung
In-Reply-To: <20161115101232.3854-1-jh80.chung-Sze3O3UU22JBDgjK7y7TUQ@public.gmane.org>
"support-highspeed" was the obsoleted property.
And "broken-cd" is not synopsys specific property.
It can be referred to mmc.txt binding Documentation.
Signed-off-by: Jaehoon Chung <jh80.chung-Sze3O3UU22JBDgjK7y7TUQ@public.gmane.org>
Acked-by: Rob Herring <robh-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>
---
Documentation/devicetree/bindings/mmc/synopsys-dw-mshc.txt | 5 -----
1 file changed, 5 deletions(-)
diff --git a/Documentation/devicetree/bindings/mmc/synopsys-dw-mshc.txt b/Documentation/devicetree/bindings/mmc/synopsys-dw-mshc.txt
index 1279a22..7fd17c3 100644
--- a/Documentation/devicetree/bindings/mmc/synopsys-dw-mshc.txt
+++ b/Documentation/devicetree/bindings/mmc/synopsys-dw-mshc.txt
@@ -75,11 +75,6 @@ Optional properties:
* card-detect-delay: Delay in milli-seconds before detecting card after card
insert event. The default value is 0.
-* supports-highspeed (DEPRECATED): Enables support for high speed cards (up to 50MHz)
- (use "cap-mmc-highspeed" or "cap-sd-highspeed" instead)
-
-* broken-cd: as documented in mmc core bindings.
-
* vmmc-supply: The phandle to the regulator to use for vmmc. If this is
specified we'll defer probe until we can find this regulator.
--
2.10.1
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply related
* [PATCHv2 09/10] mmc: dw_mmc: The "clock-freq-min-max" property was deprecated
From: Jaehoon Chung @ 2016-11-15 10:12 UTC (permalink / raw)
To: linux-mmc-u79uwXL29TY76Z2rM5mHXA
Cc: devicetree-u79uwXL29TY76Z2rM5mHXA,
ulf.hansson-QSEj5FYQhm4dnm+yROfE0A, heiko-4mtYJXux2i+zQB+pC5nmwQ,
shawn.lin-TNX95d0MmH7DzftRWevZcw, robh+dt-DgEjT+Ai2ygdnm+yROfE0A,
Jaehoon Chung
In-Reply-To: <20161115101232.3854-1-jh80.chung-Sze3O3UU22JBDgjK7y7TUQ@public.gmane.org>
The "clock-freq-min-max" property was deprecated.
There is "max-frequency" property in drivers/mmc/core/host.c
"max-frequency" can be replaced with "clock-freq-min-max".
Minimum clock value might be set to 100K by default.
Then MMC core should try to find the correct value from 400K to 100K.
So it just needs to set Maximum clock value.
Signed-off-by: Jaehoon Chung <jh80.chung-Sze3O3UU22JBDgjK7y7TUQ@public.gmane.org>
---
Documentation/devicetree/bindings/mmc/synopsys-dw-mshc.txt | 3 ++-
drivers/mmc/host/dw_mmc.c | 2 ++
2 files changed, 4 insertions(+), 1 deletion(-)
diff --git a/Documentation/devicetree/bindings/mmc/synopsys-dw-mshc.txt b/Documentation/devicetree/bindings/mmc/synopsys-dw-mshc.txt
index bfa461a..1279a22 100644
--- a/Documentation/devicetree/bindings/mmc/synopsys-dw-mshc.txt
+++ b/Documentation/devicetree/bindings/mmc/synopsys-dw-mshc.txt
@@ -59,8 +59,9 @@ Optional properties:
is specified and the ciu clock is specified then we'll try to set the ciu
clock to this at probe time.
-* clock-freq-min-max: Minimum and Maximum clock frequency for card output
+* clock-freq-min-max (DEPRECATED): Minimum and Maximum clock frequency for card output
clock(cclk_out). If it's not specified, max is 200MHZ and min is 400KHz by default.
+ (Use the "max-frequency" instead of "clock-freq-min-max".)
* num-slots: specifies the number of slots supported by the controller.
The number of physical slots actually used could be equal or less than the
diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c
index ec0ba79..48e968a 100644
--- a/drivers/mmc/host/dw_mmc.c
+++ b/drivers/mmc/host/dw_mmc.c
@@ -2608,6 +2608,8 @@ static int dw_mci_init_slot(struct dw_mci *host, unsigned int id)
mmc->f_min = DW_MCI_FREQ_MIN;
mmc->f_max = DW_MCI_FREQ_MAX;
} else {
+ dev_info(host->dev,
+ "'clock-freq-min-max' property was deprecated.\n");
mmc->f_min = freq[0];
mmc->f_max = freq[1];
}
--
2.10.1
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply 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