* [PATCH v2] arm64: dts: qcom: sdm845: Sort nodes in the reserved mem by address
From: Douglas Anderson @ 2018-05-15 4:43 UTC (permalink / raw)
To: linux-arm-kernel
Let's keep the reserved-memory node tidy and neat and keep it sorted
by address. This should have no functional change.
Signed-off-by: Douglas Anderson <dianders@chromium.org>
---
Changes in v2:
- Oops! v1 accidentally changed the node name. Fixed.
arch/arm64/boot/dts/qcom/sdm845.dtsi | 12 ++++++------
1 file changed, 6 insertions(+), 6 deletions(-)
diff --git a/arch/arm64/boot/dts/qcom/sdm845.dtsi b/arch/arm64/boot/dts/qcom/sdm845.dtsi
index 7c85e7c596db..73f71061fef8 100644
--- a/arch/arm64/boot/dts/qcom/sdm845.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm845.dtsi
@@ -31,6 +31,12 @@
no-map;
};
+ memory at 85fe0000 {
+ compatible = "qcom,cmd-db";
+ reg = <0x0 0x85fe0000 0x0 0x20000>;
+ no-map;
+ };
+
smem_mem: memory at 86000000 {
reg = <0x0 0x86000000 0x0 0x200000>;
no-map;
@@ -40,12 +46,6 @@
reg = <0 0x86200000 0 0x2d00000>;
no-map;
};
-
- memory at 85fe0000 {
- compatible = "qcom,cmd-db";
- reg = <0x0 0x85fe0000 0x0 0x20000>;
- no-map;
- };
};
cpus {
--
2.17.0.441.gb46fe60e1d-goog
^ permalink raw reply related
* [PATCH] arm64: dts: qcom: sdm845: Sort nodes in the reserved mem by address
From: Douglas Anderson @ 2018-05-15 4:37 UTC (permalink / raw)
To: linux-arm-kernel
Let's keep the reserved-memory node tidy and neat and keep it sorted
by address. This should have no functional change.
Signed-off-by: Douglas Anderson <dianders@chromium.org>
---
arch/arm64/boot/dts/qcom/sdm845.dtsi | 12 ++++++------
1 file changed, 6 insertions(+), 6 deletions(-)
diff --git a/arch/arm64/boot/dts/qcom/sdm845.dtsi b/arch/arm64/boot/dts/qcom/sdm845.dtsi
index 7c85e7c596db..63026133feab 100644
--- a/arch/arm64/boot/dts/qcom/sdm845.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm845.dtsi
@@ -31,6 +31,12 @@
no-map;
};
+ reserved-memory at 85fe0000 {
+ compatible = "qcom,cmd-db";
+ reg = <0x0 0x85fe0000 0x0 0x20000>;
+ no-map;
+ };
+
smem_mem: memory at 86000000 {
reg = <0x0 0x86000000 0x0 0x200000>;
no-map;
@@ -40,12 +46,6 @@
reg = <0 0x86200000 0 0x2d00000>;
no-map;
};
-
- memory at 85fe0000 {
- compatible = "qcom,cmd-db";
- reg = <0x0 0x85fe0000 0x0 0x20000>;
- no-map;
- };
};
cpus {
--
2.17.0.441.gb46fe60e1d-goog
^ permalink raw reply related
* [PATCH v9 04/11] arm64: kexec_file: allocate memory walking through memblock list
From: AKASHI Takahiro @ 2018-05-15 4:35 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20180507055906.GE11326@linaro.org>
James,
On Mon, May 07, 2018 at 02:59:07PM +0900, AKASHI Takahiro wrote:
> James,
>
> On Tue, May 01, 2018 at 06:46:09PM +0100, James Morse wrote:
> > Hi Akashi,
> >
> > On 25/04/18 07:26, AKASHI Takahiro wrote:
> > > We need to prevent firmware-reserved memory regions, particularly EFI
> > > memory map as well as ACPI tables, from being corrupted by loading
> > > kernel/initrd (or other kexec buffers). We also want to support memory
> > > allocation in top-down manner in addition to default bottom-up.
> > > So let's have arm64 specific arch_kexec_walk_mem() which will search
> > > for available memory ranges in usable memblock list,
> > > i.e. !NOMAP & !reserved,
> >
> > > instead of system resource tree.
> >
> > Didn't we try to fix the system-resource-tree in order to fix regular-kexec to
> > be safe in the EFI-memory-map/ACPI-tables case?
> >
> > It would be good to avoid having two ways of doing this, and I would like to
> > avoid having extra arch code...
>
> I know what you mean.
> /proc/iomem or system resource is, in my opinion, not the best place to
> describe memory usage of kernel but rather to describe *physical* hardware
> layout. As we are still discussing about "reserved" memory, I don't want
> to depend on it.
> Along with memblock list, we will have more accurate control over memory
> usage.
If you don't have further objection, I will take memblock approach
(with factoring out powerpc's arch_kexec_walk_mem()).
Thanks,
-Takahiro AKASHI
> >
> > > diff --git a/arch/arm64/kernel/machine_kexec_file.c b/arch/arm64/kernel/machine_kexec_file.c
> > > new file mode 100644
> > > index 000000000000..f9ebf54ca247
> > > --- /dev/null
> > > +++ b/arch/arm64/kernel/machine_kexec_file.c
> > > @@ -0,0 +1,57 @@
> > > +// SPDX-License-Identifier: GPL-2.0
> > > +/*
> > > + * kexec_file for arm64
> > > + *
> > > + * Copyright (C) 2018 Linaro Limited
> > > + * Author: AKASHI Takahiro <takahiro.akashi@linaro.org>
> > > + *
> >
> > > + * Most code is derived from arm64 port of kexec-tools
> >
> > How does kexec-tools walk memblock?
>
> Will remove this comment from this patch.
> Obviously, this comment is for the rest of the code which will be
> added to succeeding patches (patch #5 and #7).
>
>
> >
> > > + */
> > > +
> > > +#define pr_fmt(fmt) "kexec_file: " fmt
> > > +
> > > +#include <linux/ioport.h>
> > > +#include <linux/kernel.h>
> > > +#include <linux/kexec.h>
> > > +#include <linux/memblock.h>
> > > +
> > > +int arch_kexec_walk_mem(struct kexec_buf *kbuf,
> > > + int (*func)(struct resource *, void *))
> > > +{
> > > + phys_addr_t start, end;
> > > + struct resource res;
> > > + u64 i;
> > > + int ret = 0;
> > > +
> > > + if (kbuf->image->type == KEXEC_TYPE_CRASH)
> > > + return func(&crashk_res, kbuf);
> > > +
> > > + if (kbuf->top_down)
> > > + for_each_mem_range_rev(i, &memblock.memory, &memblock.reserved,
> > > + NUMA_NO_NODE, MEMBLOCK_NONE,
> > > + &start, &end, NULL) {
> >
> > for_each_free_mem_range_reverse() is a more readable version of this helper.
>
> OK. I used to use my own limited list of reserved memory instead of
> memblock.reserved here to exclude verbose ranges.
>
>
> > > + if (!memblock_is_map_memory(start))
> > > + continue;
> >
> > Passing MEMBLOCK_NONE means this walk will never find MEMBLOCK_NOMAP memory.
>
> Sure, I confirmed it.
>
> >
> > > + res.start = start;
> > > + res.end = end;
> > > + ret = func(&res, kbuf);
> > > + if (ret)
> > > + break;
> > > + }
> > > + else
> > > + for_each_mem_range(i, &memblock.memory, &memblock.reserved,
> > > + NUMA_NO_NODE, MEMBLOCK_NONE,
> > > + &start, &end, NULL) {
> >
> > for_each_free_mem_range()?
>
> OK.
>
> > > + if (!memblock_is_map_memory(start))
> > > + continue;
> > > +
> > > + res.start = start;
> > > + res.end = end;
> > > + ret = func(&res, kbuf);
> > > + if (ret)
> > > + break;
> > > + }
> > > +
> > > + return ret;
> > > +}
> > >
> >
> > With these changes, what we have is almost:
> > arch/powerpc/kernel/machine_kexec_file_64.c::arch_kexec_walk_mem() !
> > (the difference being powerpc doesn't yet support crash-kernels here)
> >
> > If the argument is walking memblock gives a better answer than the stringy
> > walk_system_ram_res() thing, is there any mileage in moving this code into
> > kexec_file.c, and using it if !IS_ENABLED(CONFIG_ARCH_DISCARD_MEMBLOCK)?
> >
> > This would save arm64/powerpc having near-identical implementations.
> > 32bit arm keeps memblock if it has kexec, so it may be useful there too if
> > kexec_file_load() support is added.
>
> Thanks. I've forgot ppc.
>
> -Takahiro AKASHI
>
>
> >
> > Thanks,
> >
> > James
^ permalink raw reply
* [PATCH] media: dvb-frontends: add Socionext SC1501A ISDB-S/T demodulator driver
From: kbuild test robot @ 2018-05-15 4:23 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20180515003749.9980-1-suzuki.katsuhiro@socionext.com>
Hi Katsuhiro,
I love your patch! Perhaps something to improve:
[auto build test WARNING on linuxtv-media/master]
[also build test WARNING on v4.17-rc5 next-20180514]
[if your patch is applied to the wrong git tree, please drop us a note to help improve the system]
url: https://github.com/0day-ci/linux/commits/Katsuhiro-Suzuki/media-dvb-frontends-add-Socionext-SC1501A-ISDB-S-T-demodulator-driver/20180515-091453
base: git://linuxtv.org/media_tree.git master
reproduce:
# apt-get install sparse
make ARCH=x86_64 allmodconfig
make C=1 CF=-D__CHECK_ENDIAN__
sparse warnings: (new ones prefixed by >>)
>> drivers/media/dvb-frontends/sc1501a.c:313:47: sparse: constant 211243671486 is so big it is long
vim +313 drivers/media/dvb-frontends/sc1501a.c
258
259 static int sc1501a_s_read_status(struct sc1501a_priv *chip,
260 struct dtv_frontend_properties *c,
261 enum fe_status *status)
262 {
263 struct regmap *r_s = chip->regmap_s;
264 u32 cpmon, tmpu, tmpl, flg;
265 u64 tmp;
266
267 /* Sync detection */
268 regmap_read(r_s, CPMON1_S, &cpmon);
269
270 *status = 0;
271 if (cpmon & CPMON1_S_FSYNC)
272 *status |= FE_HAS_VITERBI | FE_HAS_SYNC | FE_HAS_LOCK;
273 if (cpmon & CPMON1_S_W2LOCK)
274 *status |= FE_HAS_SIGNAL | FE_HAS_CARRIER;
275
276 /* Signal strength */
277 c->strength.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
278
279 if (*status & FE_HAS_SIGNAL) {
280 u32 agc;
281
282 regmap_read(r_s, AGCREAD_S, &tmpu);
283 agc = tmpu << 8;
284
285 c->strength.len = 1;
286 c->strength.stat[0].scale = FE_SCALE_RELATIVE;
287 c->strength.stat[0].uvalue = agc;
288 }
289
290 /* C/N rate */
291 c->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
292
293 if (*status & FE_HAS_VITERBI) {
294 u32 cnr = 0, x, y, d;
295 u64 d_3 = 0;
296
297 regmap_read(r_s, CNRDXU_S, &tmpu);
298 regmap_read(r_s, CNRDXL_S, &tmpl);
299 x = (tmpu << 8) | tmpl;
300 regmap_read(r_s, CNRDYU_S, &tmpu);
301 regmap_read(r_s, CNRDYL_S, &tmpl);
302 y = (tmpu << 8) | tmpl;
303
304 /* CNR[dB]: 10 * log10(D) - 30.74 / D^3 - 3 */
305 /* D = x^2 / (2^15 * y - x^2) */
306 d = (y << 15) - x * x;
307 if (d > 0) {
308 /* (2^4 * D)^3 = 2^12 * D^3 */
309 /* 3.074 * 2^(12 + 24) = 211243671486 */
310 d_3 = div_u64(16 * x * x, d);
311 d_3 = d_3 * d_3 * d_3;
312 if (d_3)
> 313 d_3 = div_u64(211243671486, d_3);
314 }
315
316 if (d_3) {
317 /* 0.3 * 2^24 = 5033164 */
318 tmp = (s64)2 * intlog10(x) - intlog10(abs(d)) - d_3
319 - 5033164;
320 cnr = div_u64(tmp * 10000, 1 << 24);
321 }
322
323 if (cnr) {
324 c->cnr.len = 1;
325 c->cnr.stat[0].scale = FE_SCALE_DECIBEL;
326 c->cnr.stat[0].uvalue = cnr;
327 }
328 }
329
330 /* BER */
331 c->post_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
332 c->post_bit_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
333
334 regmap_read(r_s, BERCNFLG_S, &flg);
335
336 if ((*status & FE_HAS_VITERBI) && (flg & BERCNFLG_S_BERVRDY)) {
337 u32 bit_err, bit_cnt;
338
339 regmap_read(r_s, BERVRDU_S, &tmpu);
340 regmap_read(r_s, BERVRDL_S, &tmpl);
341 bit_err = (tmpu << 8) | tmpl;
342 bit_cnt = (1 << 13) * 204;
343
344 if (bit_cnt) {
345 c->post_bit_error.len = 1;
346 c->post_bit_error.stat[0].scale = FE_SCALE_COUNTER;
347 c->post_bit_error.stat[0].uvalue = bit_err;
348 c->post_bit_count.len = 1;
349 c->post_bit_count.stat[0].scale = FE_SCALE_COUNTER;
350 c->post_bit_count.stat[0].uvalue = bit_cnt;
351 }
352 }
353
354 return 0;
355 }
356
---
0-DAY kernel test infrastructure Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all Intel Corporation
^ permalink raw reply
* [PATCH v6 14/14] dt: qcom: Add qcom-cpufreq-kryo driver configuration
From: Viresh Kumar @ 2018-05-15 4:11 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1526303520-5843-15-git-send-email-ilialin@codeaurora.org>
On 14-05-18, 16:12, Ilia Lin wrote:
> Signed-off-by: Ilia Lin <ilialin@codeaurora.org>
> ---
> arch/arm64/boot/dts/qcom/apq8096-db820c.dts | 2 +-
> arch/arm64/boot/dts/qcom/msm8996.dtsi | 310 +++++++++++++++++++++++++++-
> 2 files changed, 309 insertions(+), 3 deletions(-)
>
> diff --git a/arch/arm64/boot/dts/qcom/apq8096-db820c.dts b/arch/arm64/boot/dts/qcom/apq8096-db820c.dts
> index 230e9c8..da23bda 100644
> --- a/arch/arm64/boot/dts/qcom/apq8096-db820c.dts
> +++ b/arch/arm64/boot/dts/qcom/apq8096-db820c.dts
> @@ -17,5 +17,5 @@
>
> / {
> model = "Qualcomm Technologies, Inc. DB820c";
> - compatible = "arrow,apq8096-db820c", "qcom,apq8096-sbc";
> + compatible = "arrow,apq8096-db820c", "qcom,apq8096-sbc", "qcom,apq8096";
> };
> diff --git a/arch/arm64/boot/dts/qcom/msm8996.dtsi b/arch/arm64/boot/dts/qcom/msm8996.dtsi
> index d7adef9..fbf92f6 100644
> --- a/arch/arm64/boot/dts/qcom/msm8996.dtsi
> +++ b/arch/arm64/boot/dts/qcom/msm8996.dtsi
> @@ -174,218 +174,519 @@
> };
>
> cluster0_opp: opp_table0 {
> - compatible = "operating-points-v2";
> + compatible = "operating-points-v2-kryo-cpu";
I think you need to mention both the above compatible strings here
with the kyro one mentioned first.
--
viresh
^ permalink raw reply
* [PATCH v6 13/14] dt-bindings: cpufreq: Document operating-points-v2-kryo-cpu
From: Viresh Kumar @ 2018-05-15 4:09 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1526303520-5843-14-git-send-email-ilialin@codeaurora.org>
On 14-05-18, 16:11, Ilia Lin wrote:
> In Certain Qualcomm Technologies, Inc. SoCs like apq8096 and msm8996
> that have KRYO processors, the CPU ferequencies subset and voltage value
> of each OPP varies based on the silicon variant in use.
> Qualcomm Technologies, Inc. Process Voltage Scaling Tables
> defines the voltage and frequency value based on the msm-id in SMEM
> and speedbin blown in the efuse combination.
> The qcom-cpufreq-kryo driver reads the msm-id and efuse value from the SoC
> to provide the OPP framework with required information.
> This is used to determine the voltage and frequency value for each OPP of
> operating-points-v2 table when it is parsed by the OPP framework.
>
> This change adds documentation.
>
> Signed-off-by: Ilia Lin <ilialin@codeaurora.org>
> ---
> .../devicetree/bindings/opp/kryo-cpufreq.txt | 680 +++++++++++++++++++++
> 1 file changed, 680 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/opp/kryo-cpufreq.txt
Acked-by: Viresh Kumar <viresh.kumar@linaro.org>
--
viresh
^ permalink raw reply
* [PATCH v6 12/14] cpufreq: Add Kryo CPU scaling driver
From: Viresh Kumar @ 2018-05-15 4:09 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1526303520-5843-13-git-send-email-ilialin@codeaurora.org>
On 14-05-18, 16:11, Ilia Lin wrote:
> +static int __init qcom_cpufreq_kryo_driver_init(void)
> +{
> + size_t len;
> + int ret;
> + u32 versions;
> + enum _msm8996_version msm8996_version;
> + u8 *speedbin;
> + struct device *cpu_dev;
> + struct device_node *np;
> + struct nvmem_cell *speedbin_nvmem;
> + struct opp_table *opp_temp = NULL;
> +
> + cpu_dev = get_cpu_device(SILVER_LEAD);
> + if (IS_ERR_OR_NULL(cpu_dev))
> + return PTR_ERR(cpu_dev);
> +
> + msm8996_version = qcom_cpufreq_kryo_get_msm_id();
> + if (NUM_OF_MSM8996_VERSIONS == msm8996_version) {
> + dev_err(cpu_dev, "Not Snapdragon 820/821!");
> + return -ENODEV;
> + }
> +
> + np = dev_pm_opp_of_get_opp_desc_node(cpu_dev);
> + if (IS_ERR_OR_NULL(np))
> + return PTR_ERR(np);
> +
> + if (!of_device_is_compatible(np, "operating-points-v2-kryo-cpu")) {
> + ret = -ENOENT;
> + goto free_np;
> + }
> +
> + speedbin_nvmem = of_nvmem_cell_get(np, NULL);
> + if (IS_ERR(speedbin_nvmem)) {
> + ret = PTR_ERR(speedbin_nvmem);
> + dev_err(cpu_dev, "Could not get nvmem cell: %d\n", ret);
> + goto free_np;
> + }
> +
> + speedbin = nvmem_cell_read(speedbin_nvmem, &len);
> +
> + switch (msm8996_version) {
> + case MSM8996_V3:
> + versions = 1 << (unsigned int)(*speedbin);
> + break;
> + case MSM8996_SG:
> + versions = 1 << ((unsigned int)(*speedbin) + 4);
> + break;
> + default:
> + BUG();
> + break;
> + }
> +
> + ret = PTR_ERR_OR_ZERO(opp_temp = \
Why back slash here ?
> + dev_pm_opp_set_supported_hw(cpu_dev,&versions,1));
> + if (0 > ret)
> + goto free_np;
> +
> + dev_pm_opp_put_supported_hw(opp_temp);
> +
> + cpu_dev = get_cpu_device(GOLD_LEAD);
> + ret = PTR_ERR_OR_ZERO(opp_temp = \
And here.
> + dev_pm_opp_set_supported_hw(cpu_dev,&versions,1));
> + if (0 > ret)
> + goto free_np;
> +
> + ret = PTR_ERR_OR_ZERO(platform_device_register_simple("cpufreq-dt", \
and here.
> + -1, NULL, 0));
> +
> + dev_pm_opp_put_supported_hw(opp_temp);
And this is wrong. You don't need to call this in success case here.
It may have worked for you as cpufreq-dt driver would have already
been initialized, but that's not the case always. For example try
inserting cpufreq-dt module after kernel boots and it will fail.
> +
> +free_np:
> + of_node_put(np);
> +
> + return ret;
> +}
> +late_initcall(qcom_cpufreq_kryo_driver_init);
> +
> +MODULE_DESCRIPTION("Qualcomm Technologies, Inc. Kryo CPUfreq driver");
> +MODULE_LICENSE("GPL v2");
> --
> 1.9.1
--
viresh
^ permalink raw reply
* [PATCH v6 11/14] dt: qcom: Add SAW regulator for 8x96 CPUs
From: Viresh Kumar @ 2018-05-15 4:04 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1526303520-5843-12-git-send-email-ilialin@codeaurora.org>
On 14-05-18, 16:11, Ilia Lin wrote:
> 1. Add syscon node for the SAW CPU registers
> 2. Add SAW regulators gang definition for s8-s11
> 3. Add voltages to the OPP tables
> 4. Add the s11 SAW regulator as CPU regulator
>
> Signed-off-by: Ilia Lin <ilialin@codeaurora.org>
> ---
> arch/arm64/boot/dts/qcom/msm8996.dtsi | 75 +++++++++++++++++++++++++++++++++++
> 1 file changed, 75 insertions(+)
Acked-by: Viresh Kumar <viresh.kumar@linaro.org>
--
viresh
^ permalink raw reply
* [PATCH v6 08/14] dt: qcom: Add opp and thermal to the msm8996
From: Viresh Kumar @ 2018-05-15 4:04 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1526303520-5843-9-git-send-email-ilialin@codeaurora.org>
On 14-05-18, 16:11, Ilia Lin wrote:
> Signed-off-by: Ilia Lin <ilialin@codeaurora.org>
> ---
> arch/arm64/boot/dts/qcom/msm8996.dtsi | 269 ++++++++++++++++++++++++++++++++--
> 1 file changed, 260 insertions(+), 9 deletions(-)
Acked-by: Viresh Kumar <viresh.kumar@linaro.org>
--
viresh
^ permalink raw reply
* [PATCH v2] arm64: dts: qcom: sdm845: Sort nodes in the soc by address
From: Douglas Anderson @ 2018-05-15 3:59 UTC (permalink / raw)
To: linux-arm-kernel
This is pure-churn and should be a no-op. I'm doing it in the hopes
of reducing merge conflicts. When things are sorted in a sane way
(and by base address seems sane) then it's less likely that future
patches will cause merge conflicts.
Signed-off-by: Douglas Anderson <dianders@chromium.org>
Acked-by: Bjorn Andersson <bjorn.andersson@linaro.org>
---
Changes in v2:
- rebase atop tree today
arch/arm64/boot/dts/qcom/sdm845.dtsi | 96 ++++++++++++++--------------
1 file changed, 48 insertions(+), 48 deletions(-)
diff --git a/arch/arm64/boot/dts/qcom/sdm845.dtsi b/arch/arm64/boot/dts/qcom/sdm845.dtsi
index 7c85e7c596db..96dd4b2a41d6 100644
--- a/arch/arm64/boot/dts/qcom/sdm845.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm845.dtsi
@@ -198,6 +198,54 @@
ranges = <0 0 0 0xffffffff>;
compatible = "simple-bus";
+ gcc: clock-controller at 100000 {
+ compatible = "qcom,gcc-sdm845";
+ reg = <0x100000 0x1f0000>;
+ #clock-cells = <1>;
+ #reset-cells = <1>;
+ #power-domain-cells = <1>;
+ };
+
+ tcsr_mutex_regs: syscon at 1f40000 {
+ compatible = "syscon";
+ reg = <0x1f40000 0x40000>;
+ };
+
+ tlmm: pinctrl at 3400000 {
+ compatible = "qcom,sdm845-pinctrl";
+ reg = <0x03400000 0xc00000>;
+ interrupts = <GIC_SPI 208 IRQ_TYPE_LEVEL_HIGH>;
+ gpio-controller;
+ #gpio-cells = <2>;
+ interrupt-controller;
+ #interrupt-cells = <2>;
+ };
+
+ spmi_bus: spmi at c440000 {
+ compatible = "qcom,spmi-pmic-arb";
+ reg = <0xc440000 0x1100>,
+ <0xc600000 0x2000000>,
+ <0xe600000 0x100000>,
+ <0xe700000 0xa0000>,
+ <0xc40a000 0x26000>;
+ reg-names = "core", "chnls", "obsrvr", "intr", "cnfg";
+ interrupt-names = "periph_irq";
+ interrupts = <GIC_SPI 481 IRQ_TYPE_LEVEL_HIGH>;
+ qcom,ee = <0>;
+ qcom,channel = <0>;
+ #address-cells = <2>;
+ #size-cells = <0>;
+ interrupt-controller;
+ #interrupt-cells = <4>;
+ cell-index = <0>;
+ };
+
+ apss_shared: mailbox at 17990000 {
+ compatible = "qcom,sdm845-apss-shared";
+ reg = <0x17990000 0x1000>;
+ #mbox-cells = <1>;
+ };
+
intc: interrupt-controller at 17a00000 {
compatible = "arm,gic-v3";
#address-cells = <1>;
@@ -218,24 +266,6 @@
};
};
- gcc: clock-controller at 100000 {
- compatible = "qcom,gcc-sdm845";
- reg = <0x100000 0x1f0000>;
- #clock-cells = <1>;
- #reset-cells = <1>;
- #power-domain-cells = <1>;
- };
-
- tlmm: pinctrl at 3400000 {
- compatible = "qcom,sdm845-pinctrl";
- reg = <0x03400000 0xc00000>;
- interrupts = <GIC_SPI 208 IRQ_TYPE_LEVEL_HIGH>;
- gpio-controller;
- #gpio-cells = <2>;
- interrupt-controller;
- #interrupt-cells = <2>;
- };
-
timer at 17c90000 {
#address-cells = <1>;
#size-cells = <1>;
@@ -293,35 +323,5 @@
status = "disabled";
};
};
-
- spmi_bus: spmi at c440000 {
- compatible = "qcom,spmi-pmic-arb";
- reg = <0xc440000 0x1100>,
- <0xc600000 0x2000000>,
- <0xe600000 0x100000>,
- <0xe700000 0xa0000>,
- <0xc40a000 0x26000>;
- reg-names = "core", "chnls", "obsrvr", "intr", "cnfg";
- interrupt-names = "periph_irq";
- interrupts = <GIC_SPI 481 IRQ_TYPE_LEVEL_HIGH>;
- qcom,ee = <0>;
- qcom,channel = <0>;
- #address-cells = <2>;
- #size-cells = <0>;
- interrupt-controller;
- #interrupt-cells = <4>;
- cell-index = <0>;
- };
-
- tcsr_mutex_regs: syscon at 1f40000 {
- compatible = "syscon";
- reg = <0x1f40000 0x40000>;
- };
-
- apss_shared: mailbox at 17990000 {
- compatible = "qcom,sdm845-apss-shared";
- reg = <0x17990000 0x1000>;
- #mbox-cells = <1>;
- };
};
};
--
2.17.0.441.gb46fe60e1d-goog
^ permalink raw reply related
* [PATCH v4 4/4] drm/rockchip: support dp training outside dp firmware
From: Lin Huang @ 2018-05-15 3:22 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1526354560-23135-1-git-send-email-hl@rock-chips.com>
DP firmware uses fixed phy config values to do training, but some
boards need to adjust these values to fit for their unique hardware
design. So get phy config values from dts and use software link training
instead of relying on firmware, if software training fail, keep firmware
training as a fallback if sw training fails.
Signed-off-by: Chris Zhong <zyw@rock-chips.com>
Signed-off-by: Lin Huang <hl@rock-chips.com>
---
Changes in v2:
- update patch following Enric suggest
Changes in v3:
- use variable fw_training instead sw_training_success
- base on DP SPCE, if training fail use lower link rate to retry training
Changes in v4:
- improve cdn_dp_get_lower_link_rate() and cdn_dp_software_train_link() follow Sean suggest
drivers/gpu/drm/rockchip/Makefile | 3 +-
drivers/gpu/drm/rockchip/cdn-dp-core.c | 24 +-
drivers/gpu/drm/rockchip/cdn-dp-core.h | 2 +
drivers/gpu/drm/rockchip/cdn-dp-link-training.c | 420 ++++++++++++++++++++++++
drivers/gpu/drm/rockchip/cdn-dp-reg.c | 31 +-
drivers/gpu/drm/rockchip/cdn-dp-reg.h | 38 ++-
6 files changed, 505 insertions(+), 13 deletions(-)
create mode 100644 drivers/gpu/drm/rockchip/cdn-dp-link-training.c
diff --git a/drivers/gpu/drm/rockchip/Makefile b/drivers/gpu/drm/rockchip/Makefile
index a314e21..b932f62 100644
--- a/drivers/gpu/drm/rockchip/Makefile
+++ b/drivers/gpu/drm/rockchip/Makefile
@@ -9,7 +9,8 @@ rockchipdrm-y := rockchip_drm_drv.o rockchip_drm_fb.o \
rockchipdrm-$(CONFIG_DRM_FBDEV_EMULATION) += rockchip_drm_fbdev.o
rockchipdrm-$(CONFIG_ROCKCHIP_ANALOGIX_DP) += analogix_dp-rockchip.o
-rockchipdrm-$(CONFIG_ROCKCHIP_CDN_DP) += cdn-dp-core.o cdn-dp-reg.o
+rockchipdrm-$(CONFIG_ROCKCHIP_CDN_DP) += cdn-dp-core.o cdn-dp-reg.o \
+ cdn-dp-link-training.o
rockchipdrm-$(CONFIG_ROCKCHIP_DW_HDMI) += dw_hdmi-rockchip.o
rockchipdrm-$(CONFIG_ROCKCHIP_DW_MIPI_DSI) += dw-mipi-dsi.o
rockchipdrm-$(CONFIG_ROCKCHIP_INNO_HDMI) += inno_hdmi.o
diff --git a/drivers/gpu/drm/rockchip/cdn-dp-core.c b/drivers/gpu/drm/rockchip/cdn-dp-core.c
index cce64c1..d9d0d4d 100644
--- a/drivers/gpu/drm/rockchip/cdn-dp-core.c
+++ b/drivers/gpu/drm/rockchip/cdn-dp-core.c
@@ -629,11 +629,13 @@ static void cdn_dp_encoder_enable(struct drm_encoder *encoder)
goto out;
}
}
-
- ret = cdn_dp_set_video_status(dp, CONTROL_VIDEO_IDLE);
- if (ret) {
- DRM_DEV_ERROR(dp->dev, "Failed to idle video %d\n", ret);
- goto out;
+ if (dp->use_fw_training == true) {
+ ret = cdn_dp_set_video_status(dp, CONTROL_VIDEO_IDLE);
+ if (ret) {
+ DRM_DEV_ERROR(dp->dev,
+ "Failed to idle video %d\n", ret);
+ goto out;
+ }
}
ret = cdn_dp_config_video(dp);
@@ -642,11 +644,15 @@ static void cdn_dp_encoder_enable(struct drm_encoder *encoder)
goto out;
}
- ret = cdn_dp_set_video_status(dp, CONTROL_VIDEO_VALID);
- if (ret) {
- DRM_DEV_ERROR(dp->dev, "Failed to valid video %d\n", ret);
- goto out;
+ if (dp->use_fw_training == true) {
+ ret = cdn_dp_set_video_status(dp, CONTROL_VIDEO_VALID);
+ if (ret) {
+ DRM_DEV_ERROR(dp->dev,
+ "Failed to valid video %d\n", ret);
+ goto out;
+ }
}
+
out:
mutex_unlock(&dp->lock);
}
diff --git a/drivers/gpu/drm/rockchip/cdn-dp-core.h b/drivers/gpu/drm/rockchip/cdn-dp-core.h
index 46159b2..77a9793 100644
--- a/drivers/gpu/drm/rockchip/cdn-dp-core.h
+++ b/drivers/gpu/drm/rockchip/cdn-dp-core.h
@@ -84,6 +84,7 @@ struct cdn_dp_device {
bool connected;
bool active;
bool suspended;
+ bool use_fw_training;
const struct firmware *fw; /* cdn dp firmware */
unsigned int fw_version; /* cdn fw version */
@@ -106,6 +107,7 @@ struct cdn_dp_device {
u8 ports;
u8 lanes;
int active_port;
+ u8 train_set[4];
u8 dpcd[DP_RECEIVER_CAP_SIZE];
bool sink_has_audio;
diff --git a/drivers/gpu/drm/rockchip/cdn-dp-link-training.c b/drivers/gpu/drm/rockchip/cdn-dp-link-training.c
new file mode 100644
index 0000000..7efd070
--- /dev/null
+++ b/drivers/gpu/drm/rockchip/cdn-dp-link-training.c
@@ -0,0 +1,420 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
+ * Author: Chris Zhong <zyw@rock-chips.com>
+ */
+
+#include <linux/device.h>
+#include <linux/delay.h>
+#include <linux/phy/phy.h>
+#include <soc/rockchip/rockchip_phy_typec.h>
+
+#include "cdn-dp-core.h"
+#include "cdn-dp-reg.h"
+
+static void cdn_dp_set_signal_levels(struct cdn_dp_device *dp)
+{
+ struct cdn_dp_port *port = dp->port[dp->active_port];
+ struct rockchip_typec_phy *tcphy = phy_get_drvdata(port->phy);
+
+ int rate = drm_dp_bw_code_to_link_rate(dp->link.rate);
+ u8 swing = (dp->train_set[0] & DP_TRAIN_VOLTAGE_SWING_MASK) >>
+ DP_TRAIN_VOLTAGE_SWING_SHIFT;
+ u8 pre_emphasis = (dp->train_set[0] & DP_TRAIN_PRE_EMPHASIS_MASK)
+ >> DP_TRAIN_PRE_EMPHASIS_SHIFT;
+
+ tcphy->typec_phy_config(port->phy, rate, dp->link.num_lanes,
+ swing, pre_emphasis);
+}
+
+static int cdn_dp_set_pattern(struct cdn_dp_device *dp, uint8_t dp_train_pat)
+{
+ u32 phy_config, global_config;
+ int ret;
+ uint8_t pattern = dp_train_pat & DP_TRAINING_PATTERN_MASK;
+
+ global_config = NUM_LANES(dp->link.num_lanes - 1) | SST_MODE |
+ GLOBAL_EN | RG_EN | ENC_RST_DIS | WR_VHSYNC_FALL;
+
+ phy_config = DP_TX_PHY_ENCODER_BYPASS(0) |
+ DP_TX_PHY_SKEW_BYPASS(0) |
+ DP_TX_PHY_DISPARITY_RST(0) |
+ DP_TX_PHY_LANE0_SKEW(0) |
+ DP_TX_PHY_LANE1_SKEW(1) |
+ DP_TX_PHY_LANE2_SKEW(2) |
+ DP_TX_PHY_LANE3_SKEW(3) |
+ DP_TX_PHY_10BIT_ENABLE(0);
+
+ if (pattern != DP_TRAINING_PATTERN_DISABLE) {
+ global_config |= NO_VIDEO;
+ phy_config |= DP_TX_PHY_TRAINING_ENABLE(1) |
+ DP_TX_PHY_SCRAMBLER_BYPASS(1) |
+ DP_TX_PHY_TRAINING_PATTERN(pattern);
+ }
+
+ ret = cdn_dp_reg_write(dp, DP_FRAMER_GLOBAL_CONFIG, global_config);
+ if (ret) {
+ DRM_ERROR("fail to set DP_FRAMER_GLOBAL_CONFIG, error: %d\n",
+ ret);
+ return ret;
+ }
+
+ ret = cdn_dp_reg_write(dp, DP_TX_PHY_CONFIG_REG, phy_config);
+ if (ret) {
+ DRM_ERROR("fail to set DP_TX_PHY_CONFIG_REG, error: %d\n",
+ ret);
+ return ret;
+ }
+
+ ret = cdn_dp_reg_write(dp, DPTX_LANE_EN, BIT(dp->link.num_lanes) - 1);
+ if (ret) {
+ DRM_ERROR("fail to set DPTX_LANE_EN, error: %d\n", ret);
+ return ret;
+ }
+
+ if (drm_dp_enhanced_frame_cap(dp->dpcd))
+ ret = cdn_dp_reg_write(dp, DPTX_ENHNCD, 1);
+ else
+ ret = cdn_dp_reg_write(dp, DPTX_ENHNCD, 0);
+ if (ret)
+ DRM_ERROR("failed to set DPTX_ENHNCD, error: %x\n", ret);
+
+ return ret;
+}
+
+static u8 cdn_dp_pre_emphasis_max(u8 voltage_swing)
+{
+ switch (voltage_swing & DP_TRAIN_VOLTAGE_SWING_MASK) {
+ case DP_TRAIN_VOLTAGE_SWING_LEVEL_0:
+ return DP_TRAIN_PRE_EMPH_LEVEL_3;
+ case DP_TRAIN_VOLTAGE_SWING_LEVEL_1:
+ return DP_TRAIN_PRE_EMPH_LEVEL_2;
+ case DP_TRAIN_VOLTAGE_SWING_LEVEL_2:
+ return DP_TRAIN_PRE_EMPH_LEVEL_1;
+ default:
+ return DP_TRAIN_PRE_EMPH_LEVEL_0;
+ }
+}
+
+static void cdn_dp_get_adjust_train(struct cdn_dp_device *dp,
+ uint8_t link_status[DP_LINK_STATUS_SIZE])
+{
+ int i;
+ uint8_t v = 0, p = 0;
+ uint8_t preemph_max;
+
+ for (i = 0; i < dp->link.num_lanes; i++) {
+ v = max(v, drm_dp_get_adjust_request_voltage(link_status, i));
+ p = max(p, drm_dp_get_adjust_request_pre_emphasis(link_status,
+ i));
+ }
+
+ if (v >= VOLTAGE_LEVEL_2)
+ v = VOLTAGE_LEVEL_2 | DP_TRAIN_MAX_SWING_REACHED;
+
+ preemph_max = cdn_dp_pre_emphasis_max(v);
+ if (p >= preemph_max)
+ p = preemph_max | DP_TRAIN_MAX_PRE_EMPHASIS_REACHED;
+
+ for (i = 0; i < dp->link.num_lanes; i++)
+ dp->train_set[i] = v | p;
+}
+
+/*
+ * Pick training pattern for channel equalization. Training Pattern 3 for HBR2
+ * or 1.2 devices that support it, Training Pattern 2 otherwise.
+ */
+static u32 cdn_dp_select_chaneq_pattern(struct cdn_dp_device *dp)
+{
+ u32 training_pattern = DP_TRAINING_PATTERN_2;
+
+ /*
+ * cdn dp support HBR2 also support TPS3. TPS3 support is also mandatory
+ * for downstream devices that support HBR2. However, not all sinks
+ * follow the spec.
+ */
+ if (drm_dp_tps3_supported(dp->dpcd))
+ training_pattern = DP_TRAINING_PATTERN_3;
+ else
+ DRM_DEBUG_KMS("5.4 Gbps link rate without sink TPS3 support\n");
+
+ return training_pattern;
+}
+
+
+static bool cdn_dp_link_max_vswing_reached(struct cdn_dp_device *dp)
+{
+ int lane;
+
+ for (lane = 0; lane < dp->link.num_lanes; lane++)
+ if ((dp->train_set[lane] & DP_TRAIN_MAX_SWING_REACHED) == 0)
+ return false;
+
+ return true;
+}
+
+static int cdn_dp_update_link_train(struct cdn_dp_device *dp)
+{
+ int ret;
+
+ cdn_dp_set_signal_levels(dp);
+
+ ret = drm_dp_dpcd_write(&dp->aux, DP_TRAINING_LANE0_SET,
+ dp->train_set, dp->link.num_lanes);
+ if (ret != dp->link.num_lanes)
+ return -EINVAL;
+
+ return 0;
+}
+
+static int cdn_dp_set_link_train(struct cdn_dp_device *dp,
+ uint8_t dp_train_pat)
+{
+ uint8_t buf[sizeof(dp->train_set) + 1];
+ int ret, len;
+
+ buf[0] = dp_train_pat;
+ if ((dp_train_pat & DP_TRAINING_PATTERN_MASK) ==
+ DP_TRAINING_PATTERN_DISABLE) {
+ /* don't write DP_TRAINING_LANEx_SET on disable */
+ len = 1;
+ } else {
+ /* DP_TRAINING_LANEx_SET follow DP_TRAINING_PATTERN_SET */
+ memcpy(buf + 1, dp->train_set, dp->link.num_lanes);
+ len = dp->link.num_lanes + 1;
+ }
+
+ ret = drm_dp_dpcd_write(&dp->aux, DP_TRAINING_PATTERN_SET,
+ buf, len);
+ if (ret != len)
+ return -EINVAL;
+
+ return 0;
+}
+
+static int cdn_dp_reset_link_train(struct cdn_dp_device *dp,
+ uint8_t dp_train_pat)
+{
+ int ret;
+
+ memset(dp->train_set, 0, sizeof(dp->train_set));
+
+ cdn_dp_set_signal_levels(dp);
+
+ ret = cdn_dp_set_pattern(dp, dp_train_pat);
+ if (ret)
+ return ret;
+
+ return cdn_dp_set_link_train(dp, dp_train_pat);
+}
+
+/* Enable corresponding port and start training pattern 1 */
+static int cdn_dp_link_training_clock_recovery(struct cdn_dp_device *dp)
+{
+ u8 voltage;
+ u8 link_status[DP_LINK_STATUS_SIZE];
+ u32 voltage_tries, max_vswing_tries;
+ int ret;
+
+ /* clock recovery */
+ ret = cdn_dp_reset_link_train(dp, DP_TRAINING_PATTERN_1 |
+ DP_LINK_SCRAMBLING_DISABLE);
+ if (ret) {
+ DRM_ERROR("failed to start link train\n");
+ return ret;
+ }
+
+ voltage_tries = 1;
+ max_vswing_tries = 0;
+ for (;;) {
+ drm_dp_link_train_clock_recovery_delay(dp->dpcd);
+ if (drm_dp_dpcd_read_link_status(&dp->aux, link_status) !=
+ DP_LINK_STATUS_SIZE) {
+ DRM_ERROR("failed to get link status\n");
+ return -EINVAL;
+ }
+
+ if (drm_dp_clock_recovery_ok(link_status, dp->link.num_lanes)) {
+ DRM_DEBUG_KMS("clock recovery OK\n");
+ return 0;
+ }
+
+ if (voltage_tries >= 5) {
+ DRM_DEBUG_KMS("Same voltage tried 5 times\n");
+ return -EINVAL;
+ }
+
+ if (max_vswing_tries >= 1) {
+ DRM_DEBUG_KMS("Max Voltage Swing reached\n");
+ return -EINVAL;
+ }
+
+ voltage = dp->train_set[0] & DP_TRAIN_VOLTAGE_SWING_MASK;
+
+ /* Update training set as requested by target */
+ cdn_dp_get_adjust_train(dp, link_status);
+ if (cdn_dp_update_link_train(dp)) {
+ DRM_ERROR("failed to update link training\n");
+ return -EINVAL;
+ }
+
+ if ((dp->train_set[0] & DP_TRAIN_VOLTAGE_SWING_MASK) ==
+ voltage)
+ ++voltage_tries;
+ else
+ voltage_tries = 1;
+
+ if (cdn_dp_link_max_vswing_reached(dp))
+ ++max_vswing_tries;
+ }
+}
+
+static int cdn_dp_link_training_channel_equalization(struct cdn_dp_device *dp)
+{
+ int tries, ret;
+ u32 training_pattern;
+ uint8_t link_status[DP_LINK_STATUS_SIZE];
+
+ training_pattern = cdn_dp_select_chaneq_pattern(dp);
+ training_pattern |= DP_LINK_SCRAMBLING_DISABLE;
+
+ ret = cdn_dp_set_pattern(dp, training_pattern);
+ if (ret)
+ return ret;
+
+ ret = cdn_dp_set_link_train(dp, training_pattern);
+ if (ret) {
+ DRM_ERROR("failed to start channel equalization\n");
+ return ret;
+ }
+
+ for (tries = 0; tries < 5; tries++) {
+ drm_dp_link_train_channel_eq_delay(dp->dpcd);
+ if (drm_dp_dpcd_read_link_status(&dp->aux, link_status) !=
+ DP_LINK_STATUS_SIZE) {
+ DRM_ERROR("failed to get link status\n");
+ break;
+ }
+
+ /* Make sure clock is still ok */
+ if (!drm_dp_clock_recovery_ok(link_status,
+ dp->link.num_lanes)) {
+ DRM_DEBUG_KMS("Clock recovery check failed\n");
+ break;
+ }
+
+ if (drm_dp_channel_eq_ok(link_status, dp->link.num_lanes)) {
+ DRM_DEBUG_KMS("Channel EQ done\n");
+ return 0;
+ }
+
+ /* Update training set as requested by target */
+ cdn_dp_get_adjust_train(dp, link_status);
+ if (cdn_dp_update_link_train(dp)) {
+ DRM_ERROR("failed to update link training\n");
+ break;
+ }
+ }
+
+ /* Try 5 times, else fail and try at lower BW */
+ if (tries == 5)
+ DRM_DEBUG_KMS("Channel equalization failed 5 times\n");
+
+ return -EINVAL;
+}
+
+static int cdn_dp_stop_link_train(struct cdn_dp_device *dp)
+{
+ int ret = cdn_dp_set_pattern(dp, DP_TRAINING_PATTERN_DISABLE);
+
+ if (ret)
+ return ret;
+
+ return cdn_dp_set_link_train(dp, DP_TRAINING_PATTERN_DISABLE);
+}
+
+static int cdn_dp_get_lower_link_rate(struct cdn_dp_device *dp)
+{
+ switch (dp->link.rate) {
+ case DP_LINK_BW_1_62:
+ return -EINVAL;
+ case DP_LINK_BW_2_7:
+ dp->link.rate = DP_LINK_BW_1_62;
+ break;
+ case DP_LINK_BW_5_4:
+ dp->link.rate = DP_LINK_BW_2_7;
+ break;
+ default:
+ dp->link.rate = DP_LINK_BW_5_4;
+ break;
+ }
+
+ return 0;
+}
+
+int cdn_dp_software_train_link(struct cdn_dp_device *dp)
+{
+ int ret, stop_err;
+ u8 link_config[2];
+ u32 rate, sink_max, source_max;
+
+ ret = drm_dp_dpcd_read(&dp->aux, DP_DPCD_REV, dp->dpcd,
+ sizeof(dp->dpcd));
+ if (ret < 0) {
+ DRM_DEV_ERROR(dp->dev, "Failed to get caps %d\n", ret);
+ return ret;
+ }
+
+ source_max = dp->lanes;
+ sink_max = drm_dp_max_lane_count(dp->dpcd);
+ dp->link.num_lanes = min(source_max, sink_max);
+
+ source_max = drm_dp_bw_code_to_link_rate(CDN_DP_MAX_LINK_RATE);
+ sink_max = drm_dp_max_link_rate(dp->dpcd);
+ rate = min(source_max, sink_max);
+ dp->link.rate = drm_dp_link_rate_to_bw_code(rate);
+
+ link_config[0] = 0;
+ link_config[1] = 0;
+ if (dp->dpcd[DP_MAIN_LINK_CHANNEL_CODING] & 0x01)
+ link_config[1] = DP_SET_ANSI_8B10B;
+ drm_dp_dpcd_write(&dp->aux, DP_DOWNSPREAD_CTRL, link_config, 2);
+
+ while (true) {
+
+ /* Write the link configuration data */
+ link_config[0] = dp->link.rate;
+ link_config[1] = dp->link.num_lanes;
+ if (drm_dp_enhanced_frame_cap(dp->dpcd))
+ link_config[1] |= DP_LANE_COUNT_ENHANCED_FRAME_EN;
+ drm_dp_dpcd_write(&dp->aux, DP_LINK_BW_SET, link_config, 2);
+
+ ret = cdn_dp_link_training_clock_recovery(dp);
+ if (ret) {
+ if (!cdn_dp_get_lower_link_rate(dp))
+ continue;
+
+ DRM_ERROR("training clock recovery failed: %d\n", ret);
+ break;
+ }
+
+ ret = cdn_dp_link_training_channel_equalization(dp);
+ if (ret) {
+ if (!cdn_dp_get_lower_link_rate(dp))
+ continue;
+
+ DRM_ERROR("training channel eq failed: %d\n", ret);
+ break;
+ }
+
+ break;
+ }
+
+ stop_err = cdn_dp_stop_link_train(dp);
+ if (stop_err) {
+ DRM_ERROR("stop training fail, error: %d\n", stop_err);
+ return stop_err;
+ }
+
+ return ret;
+}
diff --git a/drivers/gpu/drm/rockchip/cdn-dp-reg.c b/drivers/gpu/drm/rockchip/cdn-dp-reg.c
index 979355d..e1273e6 100644
--- a/drivers/gpu/drm/rockchip/cdn-dp-reg.c
+++ b/drivers/gpu/drm/rockchip/cdn-dp-reg.c
@@ -17,7 +17,9 @@
#include <linux/delay.h>
#include <linux/io.h>
#include <linux/iopoll.h>
+#include <linux/phy/phy.h>
#include <linux/reset.h>
+#include <soc/rockchip/rockchip_phy_typec.h>
#include "cdn-dp-core.h"
#include "cdn-dp-reg.h"
@@ -189,7 +191,7 @@ static int cdn_dp_mailbox_send(struct cdn_dp_device *dp, u8 module_id,
return 0;
}
-static int cdn_dp_reg_write(struct cdn_dp_device *dp, u16 addr, u32 val)
+int cdn_dp_reg_write(struct cdn_dp_device *dp, u16 addr, u32 val)
{
u8 msg[6];
@@ -609,6 +611,31 @@ int cdn_dp_train_link(struct cdn_dp_device *dp)
{
int ret;
+ /*
+ * DP firmware uses fixed phy config values to do training, but some
+ * boards need to adjust these values to fit for their unique hardware
+ * design. So if the phy is using custom config values, do software
+ * link training instead of relying on firmware, if software training
+ * fail, keep firmware training as a fallback if sw training fails.
+ */
+ ret = cdn_dp_software_train_link(dp);
+ if (ret) {
+ DRM_DEV_ERROR(dp->dev,
+ "Failed to do software training %d\n", ret);
+ goto do_fw_training;
+ }
+ ret = cdn_dp_reg_write(dp, SOURCE_HDTX_CAR, 0xf);
+ if (ret) {
+ DRM_DEV_ERROR(dp->dev,
+ "Failed to write SOURCE_HDTX_CAR register %d\n", ret);
+ goto do_fw_training;
+ }
+ dp->use_fw_training = false;
+ return 0;
+
+do_fw_training:
+ dp->use_fw_training = true;
+ DRM_DEV_DEBUG_KMS(dp->dev, "use fw training\n");
ret = cdn_dp_training_start(dp);
if (ret) {
DRM_DEV_ERROR(dp->dev, "Failed to start training %d\n", ret);
@@ -623,7 +650,7 @@ int cdn_dp_train_link(struct cdn_dp_device *dp)
DRM_DEV_DEBUG_KMS(dp->dev, "rate:0x%x, lanes:%d\n", dp->link.rate,
dp->link.num_lanes);
- return ret;
+ return 0;
}
int cdn_dp_set_video_status(struct cdn_dp_device *dp, int active)
diff --git a/drivers/gpu/drm/rockchip/cdn-dp-reg.h b/drivers/gpu/drm/rockchip/cdn-dp-reg.h
index 6580b11..3420771 100644
--- a/drivers/gpu/drm/rockchip/cdn-dp-reg.h
+++ b/drivers/gpu/drm/rockchip/cdn-dp-reg.h
@@ -137,7 +137,7 @@
#define HPD_EVENT_MASK 0x211c
#define HPD_EVENT_DET 0x2120
-/* dpyx framer addr */
+/* dptx framer addr */
#define DP_FRAMER_GLOBAL_CONFIG 0x2200
#define DP_SW_RESET 0x2204
#define DP_FRAMER_TU 0x2208
@@ -431,6 +431,40 @@
/* Reference cycles when using lane clock as reference */
#define LANE_REF_CYC 0x8000
+/* register CM_VID_CTRL */
+#define LANE_VID_REF_CYC(x) (((x) & (BIT(24) - 1)) << 0)
+#define NMVID_MEAS_TOLERANCE(x) (((x) & 0xf) << 24)
+
+/* register DP_TX_PHY_CONFIG_REG */
+#define DP_TX_PHY_TRAINING_ENABLE(x) ((x) & 1)
+#define DP_TX_PHY_TRAINING_TYPE_PRBS7 (0 << 1)
+#define DP_TX_PHY_TRAINING_TYPE_TPS1 (1 << 1)
+#define DP_TX_PHY_TRAINING_TYPE_TPS2 (2 << 1)
+#define DP_TX_PHY_TRAINING_TYPE_TPS3 (3 << 1)
+#define DP_TX_PHY_TRAINING_TYPE_TPS4 (4 << 1)
+#define DP_TX_PHY_TRAINING_TYPE_PLTPAT (5 << 1)
+#define DP_TX_PHY_TRAINING_TYPE_D10_2 (6 << 1)
+#define DP_TX_PHY_TRAINING_TYPE_HBR2CPAT (8 << 1)
+#define DP_TX_PHY_TRAINING_PATTERN(x) ((x) << 1)
+#define DP_TX_PHY_SCRAMBLER_BYPASS(x) (((x) & 1) << 5)
+#define DP_TX_PHY_ENCODER_BYPASS(x) (((x) & 1) << 6)
+#define DP_TX_PHY_SKEW_BYPASS(x) (((x) & 1) << 7)
+#define DP_TX_PHY_DISPARITY_RST(x) (((x) & 1) << 8)
+#define DP_TX_PHY_LANE0_SKEW(x) (((x) & 7) << 9)
+#define DP_TX_PHY_LANE1_SKEW(x) (((x) & 7) << 12)
+#define DP_TX_PHY_LANE2_SKEW(x) (((x) & 7) << 15)
+#define DP_TX_PHY_LANE3_SKEW(x) (((x) & 7) << 18)
+#define DP_TX_PHY_10BIT_ENABLE(x) (((x) & 1) << 21)
+
+/* register DP_FRAMER_GLOBAL_CONFIG */
+#define NUM_LANES(x) ((x) & 3)
+#define SST_MODE (0 << 2)
+#define RG_EN (0 << 4)
+#define GLOBAL_EN BIT(3)
+#define NO_VIDEO BIT(5)
+#define ENC_RST_DIS BIT(6)
+#define WR_VHSYNC_FALL BIT(7)
+
enum voltage_swing_level {
VOLTAGE_LEVEL_0,
VOLTAGE_LEVEL_1,
@@ -476,6 +510,7 @@ int cdn_dp_set_host_cap(struct cdn_dp_device *dp, u8 lanes, bool flip);
int cdn_dp_event_config(struct cdn_dp_device *dp);
u32 cdn_dp_get_event(struct cdn_dp_device *dp);
int cdn_dp_get_hpd_status(struct cdn_dp_device *dp);
+int cdn_dp_reg_write(struct cdn_dp_device *dp, u16 addr, u32 val);
ssize_t cdn_dp_dpcd_write(struct cdn_dp_device *dp, u32 addr,
u8 *data, u16 len);
ssize_t cdn_dp_dpcd_read(struct cdn_dp_device *dp, u32 addr,
@@ -489,4 +524,5 @@ int cdn_dp_config_video(struct cdn_dp_device *dp);
int cdn_dp_audio_stop(struct cdn_dp_device *dp, struct audio_info *audio);
int cdn_dp_audio_mute(struct cdn_dp_device *dp, bool enable);
int cdn_dp_audio_config(struct cdn_dp_device *dp, struct audio_info *audio);
+int cdn_dp_software_train_link(struct cdn_dp_device *dp);
#endif /* _CDN_DP_REG_H */
--
2.7.4
^ permalink raw reply related
* [PATCH v4 3/4] phy: rockchip-typec: support variable phy config value
From: Lin Huang @ 2018-05-15 3:22 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1526354560-23135-1-git-send-email-hl@rock-chips.com>
the phy config values used to fix in dp firmware, but some boards
need change these values to do training and get the better eye diagram
result. So support that in phy driver.
Signed-off-by: Chris Zhong <zyw@rock-chips.com>
Signed-off-by: Lin Huang <hl@rock-chips.com>
---
Changes in v2:
- update patch following Enric suggest
Changes in v3:
- delete need_software_training variable
- add default phy config value, if dts do not define phy config value, use these value
Changes in v4:
- rename variable config to tcphy_default_config
drivers/phy/rockchip/phy-rockchip-typec.c | 306 ++++++++++++++++++++----------
include/soc/rockchip/rockchip_phy_typec.h | 63 ++++++
2 files changed, 271 insertions(+), 98 deletions(-)
create mode 100644 include/soc/rockchip/rockchip_phy_typec.h
diff --git a/drivers/phy/rockchip/phy-rockchip-typec.c b/drivers/phy/rockchip/phy-rockchip-typec.c
index 76a4b58..5d8692d 100644
--- a/drivers/phy/rockchip/phy-rockchip-typec.c
+++ b/drivers/phy/rockchip/phy-rockchip-typec.c
@@ -63,6 +63,7 @@
#include <linux/mfd/syscon.h>
#include <linux/phy/phy.h>
+#include <soc/rockchip/rockchip_phy_typec.h>
#define CMN_SSM_BANDGAP (0x21 << 2)
#define CMN_SSM_BIAS (0x22 << 2)
@@ -323,21 +324,29 @@
* clock 0: PLL 0 div 1
* clock 1: PLL 1 div 2
*/
-#define CLK_PLL_CONFIG 0X30
+#define CLK_PLL1_DIV1 0x20
+#define CLK_PLL1_DIV2 0x30
#define CLK_PLL_MASK 0x33
#define CMN_READY BIT(0)
+#define DP_PLL_CLOCK_ENABLE_ACK BIT(3)
#define DP_PLL_CLOCK_ENABLE BIT(2)
+#define DP_PLL_ENABLE_ACK BIT(1)
#define DP_PLL_ENABLE BIT(0)
#define DP_PLL_DATA_RATE_RBR ((2 << 12) | (4 << 8))
#define DP_PLL_DATA_RATE_HBR ((2 << 12) | (4 << 8))
#define DP_PLL_DATA_RATE_HBR2 ((1 << 12) | (2 << 8))
+#define DP_PLL_DATA_RATE_MASK 0xff00
-#define DP_MODE_A0 BIT(4)
-#define DP_MODE_A2 BIT(6)
-#define DP_MODE_ENTER_A0 0xc101
-#define DP_MODE_ENTER_A2 0xc104
+#define DP_MODE_MASK 0xf
+#define DP_MODE_ENTER_A0 BIT(0)
+#define DP_MODE_ENTER_A2 BIT(2)
+#define DP_MODE_ENTER_A3 BIT(3)
+#define DP_MODE_A0_ACK BIT(4)
+#define DP_MODE_A2_ACK BIT(6)
+#define DP_MODE_A3_ACK BIT(7)
+#define DP_LINK_RESET_DEASSERTED BIT(8)
#define PHY_MODE_SET_TIMEOUT 100000
@@ -349,51 +358,7 @@
#define MODE_DFP_USB BIT(1)
#define MODE_DFP_DP BIT(2)
-struct usb3phy_reg {
- u32 offset;
- u32 enable_bit;
- u32 write_enable;
-};
-
-/**
- * struct rockchip_usb3phy_port_cfg: usb3-phy port configuration.
- * @reg: the base address for usb3-phy config.
- * @typec_conn_dir: the register of type-c connector direction.
- * @usb3tousb2_en: the register of type-c force usb2 to usb2 enable.
- * @external_psm: the register of type-c phy external psm clock.
- * @pipe_status: the register of type-c phy pipe status.
- * @usb3_host_disable: the register of type-c usb3 host disable.
- * @usb3_host_port: the register of type-c usb3 host port.
- * @uphy_dp_sel: the register of type-c phy DP select control.
- */
-struct rockchip_usb3phy_port_cfg {
- unsigned int reg;
- struct usb3phy_reg typec_conn_dir;
- struct usb3phy_reg usb3tousb2_en;
- struct usb3phy_reg external_psm;
- struct usb3phy_reg pipe_status;
- struct usb3phy_reg usb3_host_disable;
- struct usb3phy_reg usb3_host_port;
- struct usb3phy_reg uphy_dp_sel;
-};
-
-struct rockchip_typec_phy {
- struct device *dev;
- void __iomem *base;
- struct extcon_dev *extcon;
- struct regmap *grf_regs;
- struct clk *clk_core;
- struct clk *clk_ref;
- struct reset_control *uphy_rst;
- struct reset_control *pipe_rst;
- struct reset_control *tcphy_rst;
- const struct rockchip_usb3phy_port_cfg *port_cfgs;
- /* mutex to protect access to individual PHYs */
- struct mutex lock;
-
- bool flip;
- u8 mode;
-};
+#define DP_DEFAULT_RATE 162000
struct phy_reg {
u16 value;
@@ -417,15 +382,15 @@ struct phy_reg usb3_pll_cfg[] = {
{ 0x8, CMN_DIAG_PLL0_LF_PROG },
};
-struct phy_reg dp_pll_cfg[] = {
+struct phy_reg dp_pll_rbr_cfg[] = {
{ 0xf0, CMN_PLL1_VCOCAL_INIT },
{ 0x18, CMN_PLL1_VCOCAL_ITER },
{ 0x30b9, CMN_PLL1_VCOCAL_START },
- { 0x21c, CMN_PLL1_INTDIV },
+ { 0x87, CMN_PLL1_INTDIV },
{ 0, CMN_PLL1_FRACDIV },
- { 0x5, CMN_PLL1_HIGH_THR },
- { 0x35, CMN_PLL1_SS_CTRL1 },
- { 0x7f1e, CMN_PLL1_SS_CTRL2 },
+ { 0x22, CMN_PLL1_HIGH_THR },
+ { 0x8000, CMN_PLL1_SS_CTRL1 },
+ { 0, CMN_PLL1_SS_CTRL2 },
{ 0x20, CMN_PLL1_DSM_DIAG },
{ 0, CMN_PLLSM1_USER_DEF_CTRL },
{ 0, CMN_DIAG_PLL1_OVRD },
@@ -436,9 +401,52 @@ struct phy_reg dp_pll_cfg[] = {
{ 0x8, CMN_DIAG_PLL1_LF_PROG },
{ 0x100, CMN_DIAG_PLL1_PTATIS_TUNE1 },
{ 0x7, CMN_DIAG_PLL1_PTATIS_TUNE2 },
- { 0x4, CMN_DIAG_PLL1_INCLK_CTRL },
+ { 0x1, CMN_DIAG_PLL1_INCLK_CTRL },
+};
+
+struct phy_reg dp_pll_hbr_cfg[] = {
+ { 0xf0, CMN_PLL1_VCOCAL_INIT },
+ { 0x18, CMN_PLL1_VCOCAL_ITER },
+ { 0x30b4, CMN_PLL1_VCOCAL_START },
+ { 0xe1, CMN_PLL1_INTDIV },
+ { 0, CMN_PLL1_FRACDIV },
+ { 0x5, CMN_PLL1_HIGH_THR },
+ { 0x8000, CMN_PLL1_SS_CTRL1 },
+ { 0, CMN_PLL1_SS_CTRL2 },
+ { 0x20, CMN_PLL1_DSM_DIAG },
+ { 0x1000, CMN_PLLSM1_USER_DEF_CTRL },
+ { 0, CMN_DIAG_PLL1_OVRD },
+ { 0, CMN_DIAG_PLL1_FBH_OVRD },
+ { 0, CMN_DIAG_PLL1_FBL_OVRD },
+ { 0x7, CMN_DIAG_PLL1_V2I_TUNE },
+ { 0x45, CMN_DIAG_PLL1_CP_TUNE },
+ { 0x8, CMN_DIAG_PLL1_LF_PROG },
+ { 0x1, CMN_DIAG_PLL1_PTATIS_TUNE1 },
+ { 0x1, CMN_DIAG_PLL1_PTATIS_TUNE2 },
+ { 0x1, CMN_DIAG_PLL1_INCLK_CTRL },
};
+struct phy_reg dp_pll_hbr2_cfg[] = {
+ { 0xf0, CMN_PLL1_VCOCAL_INIT },
+ { 0x18, CMN_PLL1_VCOCAL_ITER },
+ { 0x30b4, CMN_PLL1_VCOCAL_START },
+ { 0xe1, CMN_PLL1_INTDIV },
+ { 0, CMN_PLL1_FRACDIV },
+ { 0x5, CMN_PLL1_HIGH_THR },
+ { 0x8000, CMN_PLL1_SS_CTRL1 },
+ { 0, CMN_PLL1_SS_CTRL2 },
+ { 0x20, CMN_PLL1_DSM_DIAG },
+ { 0x1000, CMN_PLLSM1_USER_DEF_CTRL },
+ { 0, CMN_DIAG_PLL1_OVRD },
+ { 0, CMN_DIAG_PLL1_FBH_OVRD },
+ { 0, CMN_DIAG_PLL1_FBL_OVRD },
+ { 0x7, CMN_DIAG_PLL1_V2I_TUNE },
+ { 0x45, CMN_DIAG_PLL1_CP_TUNE },
+ { 0x8, CMN_DIAG_PLL1_LF_PROG },
+ { 0x1, CMN_DIAG_PLL1_PTATIS_TUNE1 },
+ { 0x1, CMN_DIAG_PLL1_PTATIS_TUNE2 },
+ { 0x1, CMN_DIAG_PLL1_INCLK_CTRL },
+};
static const struct rockchip_usb3phy_port_cfg rk3399_usb3phy_port_cfgs[] = {
{
.reg = 0xff7c0000,
@@ -463,6 +471,24 @@ static const struct rockchip_usb3phy_port_cfg rk3399_usb3phy_port_cfgs[] = {
{ /* sentinel */ }
};
+/* default phy config */
+static const struct phy_config tcphy_default_config[3][4] = {
+ {{ .swing = 0x2a, .pe = 0x00 },
+ { .swing = 0x1f, .pe = 0x15 },
+ { .swing = 0x14, .pe = 0x22 },
+ { .swing = 0x02, .pe = 0x2b } },
+
+ {{ .swing = 0x21, .pe = 0x00 },
+ { .swing = 0x12, .pe = 0x15 },
+ { .swing = 0x02, .pe = 0x22 },
+ { .swing = 0, .pe = 0 } },
+
+ {{ .swing = 0x15, .pe = 0x00 },
+ { .swing = 0x00, .pe = 0x15 },
+ { .swing = 0, .pe = 0 },
+ { .swing = 0, .pe = 0 } },
+};
+
static void tcphy_cfg_24m(struct rockchip_typec_phy *tcphy)
{
u32 i, rdata;
@@ -484,7 +510,7 @@ static void tcphy_cfg_24m(struct rockchip_typec_phy *tcphy)
rdata = readl(tcphy->base + CMN_DIAG_HSCLK_SEL);
rdata &= ~CLK_PLL_MASK;
- rdata |= CLK_PLL_CONFIG;
+ rdata |= CLK_PLL1_DIV2;
writel(rdata, tcphy->base + CMN_DIAG_HSCLK_SEL);
}
@@ -498,17 +524,44 @@ static void tcphy_cfg_usb3_pll(struct rockchip_typec_phy *tcphy)
tcphy->base + usb3_pll_cfg[i].addr);
}
-static void tcphy_cfg_dp_pll(struct rockchip_typec_phy *tcphy)
+static void tcphy_cfg_dp_pll(struct rockchip_typec_phy *tcphy, int link_rate)
{
- u32 i;
+ struct phy_reg *phy_cfg;
+ u32 clk_ctrl;
+ u32 i, cfg_size, hsclk_sel;
+
+ hsclk_sel = readl(tcphy->base + CMN_DIAG_HSCLK_SEL);
+ hsclk_sel &= ~CLK_PLL_MASK;
+
+ switch (link_rate) {
+ case 162000:
+ clk_ctrl = DP_PLL_DATA_RATE_RBR;
+ hsclk_sel |= CLK_PLL1_DIV2;
+ phy_cfg = dp_pll_rbr_cfg;
+ cfg_size = ARRAY_SIZE(dp_pll_rbr_cfg);
+ break;
+ case 270000:
+ clk_ctrl = DP_PLL_DATA_RATE_HBR;
+ hsclk_sel |= CLK_PLL1_DIV2;
+ phy_cfg = dp_pll_hbr_cfg;
+ cfg_size = ARRAY_SIZE(dp_pll_hbr_cfg);
+ break;
+ case 540000:
+ clk_ctrl = DP_PLL_DATA_RATE_HBR2;
+ hsclk_sel |= CLK_PLL1_DIV1;
+ phy_cfg = dp_pll_hbr2_cfg;
+ cfg_size = ARRAY_SIZE(dp_pll_hbr2_cfg);
+ break;
+ }
+
+ clk_ctrl |= DP_PLL_CLOCK_ENABLE | DP_PLL_ENABLE;
+ writel(clk_ctrl, tcphy->base + DP_CLK_CTL);
- /* set the default mode to RBR */
- writel(DP_PLL_CLOCK_ENABLE | DP_PLL_ENABLE | DP_PLL_DATA_RATE_RBR,
- tcphy->base + DP_CLK_CTL);
+ writel(hsclk_sel, tcphy->base + CMN_DIAG_HSCLK_SEL);
/* load the configuration of PLL1 */
- for (i = 0; i < ARRAY_SIZE(dp_pll_cfg); i++)
- writel(dp_pll_cfg[i].value, tcphy->base + dp_pll_cfg[i].addr);
+ for (i = 0; i < cfg_size; i++)
+ writel(phy_cfg[i].value, tcphy->base + phy_cfg[i].addr);
}
static void tcphy_tx_usb3_cfg_lane(struct rockchip_typec_phy *tcphy, u32 lane)
@@ -535,9 +588,10 @@ static void tcphy_rx_usb3_cfg_lane(struct rockchip_typec_phy *tcphy, u32 lane)
writel(0xfb, tcphy->base + XCVR_DIAG_BIDI_CTRL(lane));
}
-static void tcphy_dp_cfg_lane(struct rockchip_typec_phy *tcphy, u32 lane)
+static void tcphy_dp_cfg_lane(struct rockchip_typec_phy *tcphy, int link_rate,
+ u8 swing, u8 pre_emp, u32 lane)
{
- u16 rdata;
+ u16 val;
writel(0xbefc, tcphy->base + XCVR_PSM_RCTRL(lane));
writel(0x6799, tcphy->base + TX_PSC_A0(lane));
@@ -545,25 +599,31 @@ static void tcphy_dp_cfg_lane(struct rockchip_typec_phy *tcphy, u32 lane)
writel(0x98, tcphy->base + TX_PSC_A2(lane));
writel(0x98, tcphy->base + TX_PSC_A3(lane));
- writel(0, tcphy->base + TX_TXCC_MGNFS_MULT_000(lane));
- writel(0, tcphy->base + TX_TXCC_MGNFS_MULT_001(lane));
- writel(0, tcphy->base + TX_TXCC_MGNFS_MULT_010(lane));
- writel(0, tcphy->base + TX_TXCC_MGNFS_MULT_011(lane));
- writel(0, tcphy->base + TX_TXCC_MGNFS_MULT_100(lane));
- writel(0, tcphy->base + TX_TXCC_MGNFS_MULT_101(lane));
- writel(0, tcphy->base + TX_TXCC_MGNFS_MULT_110(lane));
- writel(0, tcphy->base + TX_TXCC_MGNFS_MULT_111(lane));
- writel(0, tcphy->base + TX_TXCC_CPOST_MULT_10(lane));
- writel(0, tcphy->base + TX_TXCC_CPOST_MULT_01(lane));
- writel(0, tcphy->base + TX_TXCC_CPOST_MULT_00(lane));
- writel(0, tcphy->base + TX_TXCC_CPOST_MULT_11(lane));
-
- writel(0x128, tcphy->base + TX_TXCC_CAL_SCLR_MULT(lane));
- writel(0x400, tcphy->base + TX_DIAG_TX_DRV(lane));
-
- rdata = readl(tcphy->base + XCVR_DIAG_PLLDRC_CTRL(lane));
- rdata = (rdata & 0x8fff) | 0x6000;
- writel(rdata, tcphy->base + XCVR_DIAG_PLLDRC_CTRL(lane));
+ writel(tcphy->config[swing][pre_emp].swing,
+ tcphy->base + TX_TXCC_MGNFS_MULT_000(lane));
+ writel(tcphy->config[swing][pre_emp].pe,
+ tcphy->base + TX_TXCC_CPOST_MULT_00(lane));
+
+ if (swing == 2 && pre_emp == 0 && link_rate != 540000) {
+ writel(0x700, tcphy->base + TX_DIAG_TX_DRV(lane));
+ writel(0x13c, tcphy->base + TX_TXCC_CAL_SCLR_MULT(lane));
+ } else {
+ writel(0x128, tcphy->base + TX_TXCC_CAL_SCLR_MULT(lane));
+ writel(0x0400, tcphy->base + TX_DIAG_TX_DRV(lane));
+ }
+
+ val = readl(tcphy->base + XCVR_DIAG_PLLDRC_CTRL(lane));
+ val = val & 0x8fff;
+ switch (link_rate) {
+ case 162000:
+ case 270000:
+ val |= (6 << 12);
+ break;
+ case 540000:
+ val |= (4 << 12);
+ break;
+ }
+ writel(val, tcphy->base + XCVR_DIAG_PLLDRC_CTRL(lane));
}
static inline int property_enable(struct rockchip_typec_phy *tcphy,
@@ -754,30 +814,33 @@ static int tcphy_phy_init(struct rockchip_typec_phy *tcphy, u8 mode)
tcphy_cfg_24m(tcphy);
if (mode == MODE_DFP_DP) {
- tcphy_cfg_dp_pll(tcphy);
+ tcphy_cfg_dp_pll(tcphy, DP_DEFAULT_RATE);
for (i = 0; i < 4; i++)
- tcphy_dp_cfg_lane(tcphy, i);
+ tcphy_dp_cfg_lane(tcphy, DP_DEFAULT_RATE, 0, 0, i);
writel(PIN_ASSIGN_C_E, tcphy->base + PMA_LANE_CFG);
} else {
tcphy_cfg_usb3_pll(tcphy);
- tcphy_cfg_dp_pll(tcphy);
+ tcphy_cfg_dp_pll(tcphy, DP_DEFAULT_RATE);
if (tcphy->flip) {
tcphy_tx_usb3_cfg_lane(tcphy, 3);
tcphy_rx_usb3_cfg_lane(tcphy, 2);
- tcphy_dp_cfg_lane(tcphy, 0);
- tcphy_dp_cfg_lane(tcphy, 1);
+ tcphy_dp_cfg_lane(tcphy, DP_DEFAULT_RATE, 0, 0, 0);
+ tcphy_dp_cfg_lane(tcphy, DP_DEFAULT_RATE, 0, 0, 1);
} else {
tcphy_tx_usb3_cfg_lane(tcphy, 0);
tcphy_rx_usb3_cfg_lane(tcphy, 1);
- tcphy_dp_cfg_lane(tcphy, 2);
- tcphy_dp_cfg_lane(tcphy, 3);
+ tcphy_dp_cfg_lane(tcphy, DP_DEFAULT_RATE, 0, 0, 2);
+ tcphy_dp_cfg_lane(tcphy, DP_DEFAULT_RATE, 0, 0, 3);
}
writel(PIN_ASSIGN_D_F, tcphy->base + PMA_LANE_CFG);
}
- writel(DP_MODE_ENTER_A2, tcphy->base + DP_MODE_CTL);
+ val = readl(tcphy->base + DP_MODE_CTL);
+ val &= ~DP_MODE_MASK;
+ val |= DP_MODE_ENTER_A2 | DP_LINK_RESET_DEASSERTED;
+ writel(val, tcphy->base + DP_MODE_CTL);
reset_control_deassert(tcphy->uphy_rst);
@@ -990,7 +1053,7 @@ static int rockchip_dp_phy_power_on(struct phy *phy)
property_enable(tcphy, &cfg->uphy_dp_sel, 1);
ret = readx_poll_timeout(readl, tcphy->base + DP_MODE_CTL,
- val, val & DP_MODE_A2, 1000,
+ val, val & DP_MODE_A2_ACK, 1000,
PHY_MODE_SET_TIMEOUT);
if (ret < 0) {
dev_err(tcphy->dev, "failed to wait TCPHY enter A2\n");
@@ -999,13 +1062,19 @@ static int rockchip_dp_phy_power_on(struct phy *phy)
tcphy_dp_aux_calibration(tcphy);
- writel(DP_MODE_ENTER_A0, tcphy->base + DP_MODE_CTL);
+ /* enter A0 mode */
+ val = readl(tcphy->base + DP_MODE_CTL);
+ val &= ~DP_MODE_MASK;
+ val |= DP_MODE_ENTER_A0;
+ writel(val, tcphy->base + DP_MODE_CTL);
ret = readx_poll_timeout(readl, tcphy->base + DP_MODE_CTL,
- val, val & DP_MODE_A0, 1000,
+ val, val & DP_MODE_A0_ACK, 1000,
PHY_MODE_SET_TIMEOUT);
if (ret < 0) {
- writel(DP_MODE_ENTER_A2, tcphy->base + DP_MODE_CTL);
+ val &= ~DP_MODE_MASK;
+ val |= DP_MODE_ENTER_A2;
+ writel(val, tcphy->base + DP_MODE_CTL);
dev_err(tcphy->dev, "failed to wait TCPHY enter A0\n");
goto power_on_finish;
}
@@ -1023,6 +1092,7 @@ static int rockchip_dp_phy_power_on(struct phy *phy)
static int rockchip_dp_phy_power_off(struct phy *phy)
{
struct rockchip_typec_phy *tcphy = phy_get_drvdata(phy);
+ u32 val;
mutex_lock(&tcphy->lock);
@@ -1031,7 +1101,10 @@ static int rockchip_dp_phy_power_off(struct phy *phy)
tcphy->mode &= ~MODE_DFP_DP;
- writel(DP_MODE_ENTER_A2, tcphy->base + DP_MODE_CTL);
+ val = readl(tcphy->base + DP_MODE_CTL);
+ val &= ~DP_MODE_MASK;
+ val |= DP_MODE_ENTER_A2;
+ writel(val, tcphy->base + DP_MODE_CTL);
if (tcphy->mode == MODE_DISCONNECT)
tcphy_phy_deinit(tcphy);
@@ -1047,9 +1120,35 @@ static const struct phy_ops rockchip_dp_phy_ops = {
.owner = THIS_MODULE,
};
+static int typec_dp_phy_config(struct phy *phy, int link_rate,
+ int lanes, u8 swing, u8 pre_emp)
+{
+ struct rockchip_typec_phy *tcphy = phy_get_drvdata(phy);
+ u8 i;
+
+ tcphy_cfg_dp_pll(tcphy, link_rate);
+
+ if (tcphy->mode == MODE_DFP_DP) {
+ for (i = 0; i < 4; i++)
+ tcphy_dp_cfg_lane(tcphy, link_rate, swing, pre_emp, i);
+ } else {
+ if (tcphy->flip) {
+ tcphy_dp_cfg_lane(tcphy, link_rate, swing, pre_emp, 0);
+ tcphy_dp_cfg_lane(tcphy, link_rate, swing, pre_emp, 1);
+ } else {
+ tcphy_dp_cfg_lane(tcphy, link_rate, swing, pre_emp, 2);
+ tcphy_dp_cfg_lane(tcphy, link_rate, swing, pre_emp, 3);
+ }
+ }
+
+ return 0;
+}
+
static int tcphy_parse_dt(struct rockchip_typec_phy *tcphy,
struct device *dev)
{
+ int ret;
+
tcphy->grf_regs = syscon_regmap_lookup_by_phandle(dev->of_node,
"rockchip,grf");
if (IS_ERR(tcphy->grf_regs)) {
@@ -1087,6 +1186,16 @@ static int tcphy_parse_dt(struct rockchip_typec_phy *tcphy,
return PTR_ERR(tcphy->tcphy_rst);
}
+ /*
+ * check if phy_config pass from dts, if no,
+ * use default phy config value.
+ */
+ ret = of_property_read_u32_array(dev->of_node, "rockchip,phy_config",
+ (u32 *)tcphy->config, sizeof(tcphy->config) / sizeof(u32));
+ if (ret)
+ memcpy(tcphy->config, tcphy_default_config,
+ sizeof(tcphy->config));
+
return 0;
}
@@ -1171,6 +1280,7 @@ static int rockchip_typec_phy_probe(struct platform_device *pdev)
}
}
+ tcphy->typec_phy_config = typec_dp_phy_config;
pm_runtime_enable(dev);
for_each_available_child_of_node(np, child_np) {
diff --git a/include/soc/rockchip/rockchip_phy_typec.h b/include/soc/rockchip/rockchip_phy_typec.h
new file mode 100644
index 0000000..be6af0e
--- /dev/null
+++ b/include/soc/rockchip/rockchip_phy_typec.h
@@ -0,0 +1,63 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
+ * Author: Lin Huang <hl@rock-chips.com>
+ */
+
+#ifndef __SOC_ROCKCHIP_PHY_TYPEC_H
+#define __SOC_ROCKCHIP_PHY_TYPEC_H
+
+struct usb3phy_reg {
+ u32 offset;
+ u32 enable_bit;
+ u32 write_enable;
+};
+
+/**
+ * struct rockchip_usb3phy_port_cfg: usb3-phy port configuration.
+ * @reg: the base address for usb3-phy config.
+ * @typec_conn_dir: the register of type-c connector direction.
+ * @usb3tousb2_en: the register of type-c force usb2 to usb2 enable.
+ * @external_psm: the register of type-c phy external psm clock.
+ * @pipe_status: the register of type-c phy pipe status.
+ * @usb3_host_disable: the register of type-c usb3 host disable.
+ * @usb3_host_port: the register of type-c usb3 host port.
+ * @uphy_dp_sel: the register of type-c phy DP select control.
+ */
+struct rockchip_usb3phy_port_cfg {
+ unsigned int reg;
+ struct usb3phy_reg typec_conn_dir;
+ struct usb3phy_reg usb3tousb2_en;
+ struct usb3phy_reg external_psm;
+ struct usb3phy_reg pipe_status;
+ struct usb3phy_reg usb3_host_disable;
+ struct usb3phy_reg usb3_host_port;
+ struct usb3phy_reg uphy_dp_sel;
+};
+
+struct phy_config {
+ int swing;
+ int pe;
+};
+
+struct rockchip_typec_phy {
+ struct device *dev;
+ void __iomem *base;
+ struct extcon_dev *extcon;
+ struct regmap *grf_regs;
+ struct clk *clk_core;
+ struct clk *clk_ref;
+ struct reset_control *uphy_rst;
+ struct reset_control *pipe_rst;
+ struct reset_control *tcphy_rst;
+ const struct rockchip_usb3phy_port_cfg *port_cfgs;
+ /* mutex to protect access to individual PHYs */
+ struct mutex lock;
+ struct phy_config config[3][4];
+ bool flip;
+ u8 mode;
+ int (*typec_phy_config)(struct phy *phy, int link_rate,
+ int lanes, u8 swing, u8 pre_emp);
+};
+
+#endif
--
2.7.4
^ permalink raw reply related
* [PATCH v4 2/4] Documentation: bindings: add phy_config for Rockchip USB Type-C PHY
From: Lin Huang @ 2018-05-15 3:22 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1526354560-23135-1-git-send-email-hl@rock-chips.com>
If want to do training outside DP Firmware, need phy voltage swing
and pre_emphasis value.
Signed-off-by: Lin Huang <hl@rock-chips.com>
---
Changes in v2:
- None
Changes in v3:
- modify property description and add this property to Example
Change in v4:
- None
.../devicetree/bindings/phy/phy-rockchip-typec.txt | 29 +++++++++++++++++++++-
1 file changed, 28 insertions(+), 1 deletion(-)
diff --git a/Documentation/devicetree/bindings/phy/phy-rockchip-typec.txt b/Documentation/devicetree/bindings/phy/phy-rockchip-typec.txt
index 960da7f..af298f2 100644
--- a/Documentation/devicetree/bindings/phy/phy-rockchip-typec.txt
+++ b/Documentation/devicetree/bindings/phy/phy-rockchip-typec.txt
@@ -17,7 +17,8 @@ Required properties:
Optional properties:
- extcon : extcon specifier for the Power Delivery
-
+ - rockchip,phy_config : A list of voltage swing(mv) and pre-emphasis
+ (dB) pairs.
Required nodes : a sub-node is required for each port the phy provides.
The sub-node name is used to identify dp or usb3 port,
and shall be the following entries:
@@ -50,6 +51,19 @@ Example:
<&cru SRST_P_UPHY0_TCPHY>;
reset-names = "uphy", "uphy-pipe", "uphy-tcphy";
+ rockchip,phy_config =<0x2a 0x00
+ 0x1f 0x15
+ 0x14 0x22
+ 0x02 0x2b
+ 0x21 0x00
+ 0x12 0x15
+ 0x02 0x22
+ 0 0
+ 0x15 0x00
+ 0x00 0x15
+ 0 0
+ 0 0>;
+
tcphy0_dp: dp-port {
#phy-cells = <0>;
};
@@ -74,6 +88,19 @@ Example:
<&cru SRST_P_UPHY1_TCPHY>;
reset-names = "uphy", "uphy-pipe", "uphy-tcphy";
+ rockchip,phy_config =<0x2a 0x00
+ 0x1f 0x15
+ 0x14 0x22
+ 0x02 0x2b
+ 0x21 0x00
+ 0x12 0x15
+ 0x02 0x22
+ 0 0
+ 0x15 0x00
+ 0x00 0x15
+ 0 0
+ 0 0>;
+
tcphy1_dp: dp-port {
#phy-cells = <0>;
};
--
2.7.4
^ permalink raw reply related
* [PATCH v4 1/4] drm/rockchip: add transfer function for cdn-dp
From: Lin Huang @ 2018-05-15 3:22 UTC (permalink / raw)
To: linux-arm-kernel
From: Chris Zhong <zyw@rock-chips.com>
We may support training outside firmware, so we need support
dpcd read/write to get the message or do some setting with
display.
Signed-off-by: Chris Zhong <zyw@rock-chips.com>
Signed-off-by: Lin Huang <hl@rock-chips.com>
Reviewed-by: Sean Paul <seanpaul@chromium.org>
Reviewed-by: Enric Balletbo <enric.balletbo@collabora.com>
---
Changes in v2:
- update patch following Enric suggest
Changes in v3:
- None
Changes in v4:
- None
drivers/gpu/drm/rockchip/cdn-dp-core.c | 55 +++++++++++++++++++++++----
drivers/gpu/drm/rockchip/cdn-dp-core.h | 1 +
drivers/gpu/drm/rockchip/cdn-dp-reg.c | 69 ++++++++++++++++++++++++++++++----
drivers/gpu/drm/rockchip/cdn-dp-reg.h | 14 ++++++-
4 files changed, 122 insertions(+), 17 deletions(-)
diff --git a/drivers/gpu/drm/rockchip/cdn-dp-core.c b/drivers/gpu/drm/rockchip/cdn-dp-core.c
index c6fbdcd..cce64c1 100644
--- a/drivers/gpu/drm/rockchip/cdn-dp-core.c
+++ b/drivers/gpu/drm/rockchip/cdn-dp-core.c
@@ -176,8 +176,8 @@ static int cdn_dp_get_sink_count(struct cdn_dp_device *dp, u8 *sink_count)
u8 value;
*sink_count = 0;
- ret = cdn_dp_dpcd_read(dp, DP_SINK_COUNT, &value, 1);
- if (ret)
+ ret = drm_dp_dpcd_read(&dp->aux, DP_SINK_COUNT, &value, 1);
+ if (ret < 0)
return ret;
*sink_count = DP_GET_SINK_COUNT(value);
@@ -374,9 +374,9 @@ static int cdn_dp_get_sink_capability(struct cdn_dp_device *dp)
if (!cdn_dp_check_sink_connection(dp))
return -ENODEV;
- ret = cdn_dp_dpcd_read(dp, DP_DPCD_REV, dp->dpcd,
- DP_RECEIVER_CAP_SIZE);
- if (ret) {
+ ret = drm_dp_dpcd_read(&dp->aux, DP_DPCD_REV, dp->dpcd,
+ sizeof(dp->dpcd));
+ if (ret < 0) {
DRM_DEV_ERROR(dp->dev, "Failed to get caps %d\n", ret);
return ret;
}
@@ -582,8 +582,8 @@ static bool cdn_dp_check_link_status(struct cdn_dp_device *dp)
if (!port || !dp->link.rate || !dp->link.num_lanes)
return false;
- if (cdn_dp_dpcd_read(dp, DP_LANE0_1_STATUS, link_status,
- DP_LINK_STATUS_SIZE)) {
+ if (drm_dp_dpcd_read_link_status(&dp->aux, link_status) !=
+ DP_LINK_STATUS_SIZE) {
DRM_ERROR("Failed to get link status\n");
return false;
}
@@ -1012,6 +1012,40 @@ static int cdn_dp_pd_event(struct notifier_block *nb,
return NOTIFY_DONE;
}
+static ssize_t cdn_dp_aux_transfer(struct drm_dp_aux *aux,
+ struct drm_dp_aux_msg *msg)
+{
+ struct cdn_dp_device *dp = container_of(aux, struct cdn_dp_device, aux);
+ int ret;
+ u8 status;
+
+ switch (msg->request & ~DP_AUX_I2C_MOT) {
+ case DP_AUX_NATIVE_WRITE:
+ case DP_AUX_I2C_WRITE:
+ case DP_AUX_I2C_WRITE_STATUS_UPDATE:
+ ret = cdn_dp_dpcd_write(dp, msg->address, msg->buffer,
+ msg->size);
+ break;
+ case DP_AUX_NATIVE_READ:
+ case DP_AUX_I2C_READ:
+ ret = cdn_dp_dpcd_read(dp, msg->address, msg->buffer,
+ msg->size);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ status = cdn_dp_get_aux_status(dp);
+ if (status == AUX_STATUS_ACK)
+ msg->reply = DP_AUX_NATIVE_REPLY_ACK;
+ else if (status == AUX_STATUS_NACK)
+ msg->reply = DP_AUX_NATIVE_REPLY_NACK;
+ else if (status == AUX_STATUS_DEFER)
+ msg->reply = DP_AUX_NATIVE_REPLY_DEFER;
+
+ return ret;
+}
+
static int cdn_dp_bind(struct device *dev, struct device *master, void *data)
{
struct cdn_dp_device *dp = dev_get_drvdata(dev);
@@ -1030,6 +1064,13 @@ static int cdn_dp_bind(struct device *dev, struct device *master, void *data)
dp->active = false;
dp->active_port = -1;
dp->fw_loaded = false;
+ dp->aux.name = "DP-AUX";
+ dp->aux.transfer = cdn_dp_aux_transfer;
+ dp->aux.dev = dev;
+
+ ret = drm_dp_aux_register(&dp->aux);
+ if (ret)
+ return ret;
INIT_WORK(&dp->event_work, cdn_dp_pd_event_work);
diff --git a/drivers/gpu/drm/rockchip/cdn-dp-core.h b/drivers/gpu/drm/rockchip/cdn-dp-core.h
index f57e296..46159b2 100644
--- a/drivers/gpu/drm/rockchip/cdn-dp-core.h
+++ b/drivers/gpu/drm/rockchip/cdn-dp-core.h
@@ -78,6 +78,7 @@ struct cdn_dp_device {
struct platform_device *audio_pdev;
struct work_struct event_work;
struct edid *edid;
+ struct drm_dp_aux aux;
struct mutex lock;
bool connected;
diff --git a/drivers/gpu/drm/rockchip/cdn-dp-reg.c b/drivers/gpu/drm/rockchip/cdn-dp-reg.c
index eb3042c..979355d 100644
--- a/drivers/gpu/drm/rockchip/cdn-dp-reg.c
+++ b/drivers/gpu/drm/rockchip/cdn-dp-reg.c
@@ -221,7 +221,12 @@ static int cdn_dp_reg_write_bit(struct cdn_dp_device *dp, u16 addr,
sizeof(field), field);
}
-int cdn_dp_dpcd_read(struct cdn_dp_device *dp, u32 addr, u8 *data, u16 len)
+/*
+ * Returns the number of bytes transferred on success, or a negative
+ * error code on failure. -ETIMEDOUT is returned if mailbox message was
+ * not send successfully;
+ */
+ssize_t cdn_dp_dpcd_read(struct cdn_dp_device *dp, u32 addr, u8 *data, u16 len)
{
u8 msg[5], reg[5];
int ret;
@@ -247,24 +252,41 @@ int cdn_dp_dpcd_read(struct cdn_dp_device *dp, u32 addr, u8 *data, u16 len)
goto err_dpcd_read;
ret = cdn_dp_mailbox_read_receive(dp, data, len);
+ if (!ret)
+ return len;
err_dpcd_read:
+ DRM_DEV_ERROR(dp->dev, "dpcd read failed: %d\n", ret);
return ret;
}
-int cdn_dp_dpcd_write(struct cdn_dp_device *dp, u32 addr, u8 value)
+#define CDN_AUX_HEADER_SIZE 5
+#define CDN_AUX_MSG_SIZE 20
+/*
+ * Returns the number of bytes transferred on success, or a negative error
+ * code on failure. -ETIMEDOUT is returned if mailbox message was not send
+ * success; -EINVAL is returned if get the wrong data size after message
+ * is sent
+ */
+ssize_t cdn_dp_dpcd_write(struct cdn_dp_device *dp, u32 addr, u8 *data, u16 len)
{
- u8 msg[6], reg[5];
+ u8 msg[CDN_AUX_MSG_SIZE + CDN_AUX_HEADER_SIZE];
+ u8 reg[CDN_AUX_HEADER_SIZE];
int ret;
- msg[0] = 0;
- msg[1] = 1;
+ if (WARN_ON(len > CDN_AUX_MSG_SIZE) || WARN_ON(len <= 0))
+ return -EINVAL;
+
+ msg[0] = (len >> 8) & 0xff;
+ msg[1] = len & 0xff;
msg[2] = (addr >> 16) & 0xff;
msg[3] = (addr >> 8) & 0xff;
msg[4] = addr & 0xff;
- msg[5] = value;
+
+ memcpy(msg + CDN_AUX_HEADER_SIZE, data, len);
+
ret = cdn_dp_mailbox_send(dp, MB_MODULE_ID_DP_TX, DPTX_WRITE_DPCD,
- sizeof(msg), msg);
+ CDN_AUX_HEADER_SIZE + len, msg);
if (ret)
goto err_dpcd_write;
@@ -277,8 +299,12 @@ int cdn_dp_dpcd_write(struct cdn_dp_device *dp, u32 addr, u8 value)
if (ret)
goto err_dpcd_write;
- if (addr != (reg[2] << 16 | reg[3] << 8 | reg[4]))
+ if ((len != (reg[0] << 8 | reg[1])) ||
+ (addr != (reg[2] << 16 | reg[3] << 8 | reg[4]))) {
ret = -EINVAL;
+ } else {
+ return len;
+ }
err_dpcd_write:
if (ret)
@@ -286,6 +312,33 @@ int cdn_dp_dpcd_write(struct cdn_dp_device *dp, u32 addr, u8 value)
return ret;
}
+int cdn_dp_get_aux_status(struct cdn_dp_device *dp)
+{
+ u8 status;
+ int ret;
+
+ ret = cdn_dp_mailbox_send(dp, MB_MODULE_ID_DP_TX,
+ DPTX_GET_LAST_AUX_STAUS, 0, NULL);
+ if (ret)
+ goto err_get_hpd;
+
+ ret = cdn_dp_mailbox_validate_receive(dp, MB_MODULE_ID_DP_TX,
+ DPTX_GET_LAST_AUX_STAUS,
+ sizeof(status));
+ if (ret)
+ goto err_get_hpd;
+
+ ret = cdn_dp_mailbox_read_receive(dp, &status, sizeof(status));
+ if (ret)
+ goto err_get_hpd;
+
+ return status;
+
+err_get_hpd:
+ DRM_DEV_ERROR(dp->dev, "get aux status failed: %d\n", ret);
+ return ret;
+}
+
int cdn_dp_load_firmware(struct cdn_dp_device *dp, const u32 *i_mem,
u32 i_size, const u32 *d_mem, u32 d_size)
{
diff --git a/drivers/gpu/drm/rockchip/cdn-dp-reg.h b/drivers/gpu/drm/rockchip/cdn-dp-reg.h
index c4bbb4a83..6580b11 100644
--- a/drivers/gpu/drm/rockchip/cdn-dp-reg.h
+++ b/drivers/gpu/drm/rockchip/cdn-dp-reg.h
@@ -328,6 +328,13 @@
#define GENERAL_BUS_SETTINGS 0x03
#define GENERAL_TEST_ACCESS 0x04
+/* AUX status*/
+#define AUX_STATUS_ACK 0
+#define AUX_STATUS_NACK 1
+#define AUX_STATUS_DEFER 2
+#define AUX_STATUS_SINK_ERROR 3
+#define AUX_STATUS_BUS_ERROR 4
+
#define DPTX_SET_POWER_MNG 0x00
#define DPTX_SET_HOST_CAPABILITIES 0x01
#define DPTX_GET_EDID 0x02
@@ -469,8 +476,11 @@ int cdn_dp_set_host_cap(struct cdn_dp_device *dp, u8 lanes, bool flip);
int cdn_dp_event_config(struct cdn_dp_device *dp);
u32 cdn_dp_get_event(struct cdn_dp_device *dp);
int cdn_dp_get_hpd_status(struct cdn_dp_device *dp);
-int cdn_dp_dpcd_write(struct cdn_dp_device *dp, u32 addr, u8 value);
-int cdn_dp_dpcd_read(struct cdn_dp_device *dp, u32 addr, u8 *data, u16 len);
+ssize_t cdn_dp_dpcd_write(struct cdn_dp_device *dp, u32 addr,
+ u8 *data, u16 len);
+ssize_t cdn_dp_dpcd_read(struct cdn_dp_device *dp, u32 addr,
+ u8 *data, u16 len);
+int cdn_dp_get_aux_status(struct cdn_dp_device *dp);
int cdn_dp_get_edid_block(void *dp, u8 *edid,
unsigned int block, size_t length);
int cdn_dp_train_link(struct cdn_dp_device *dp);
--
2.7.4
^ permalink raw reply related
* [PATCH v2 5/5] hisi: Consolidate the Kconfigs for the CLOCK_STUB and the MAILBOX
From: Leo Yan @ 2018-05-15 2:53 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1526352795-6991-1-git-send-email-leo.yan@linaro.org>
From: Daniel Lezcano <daniel.lezcano@linaro.org>
The current defconfig is inconsistent as it selects the mailbox and
the clock for the hi6220 and the hi3660 without having their Kconfigs
making sure the dependencies are correct. It ends up when selecting
different versions for the kernel (for example when git bisecting)
those options disappear and they don't get back, leading to unexpected
behaviors. In our case, the cpufreq driver does no longer work because
the clock fails to initialize due to the clock stub and the mailbox
missing.
In order to have the dependencies correctly set when defaulting, let's
do the same as commit 3a49afb84ca074e ("clk: enable hi655x common clk
automatically") where we select automatically the driver when the
parent driver is selected. With sensible defaults in place, we can leave
other choices for EXPERT.
Acked-by: Stephen Boyd <sboyd@kernel.org>
Acked-by: Jassi Brar <jaswinder.singh@linaro.org>
Signed-off-by: Daniel Lezcano <daniel.lezcano@linaro.org>
Signed-off-by: Leo Yan <leo.yan@linaro.org>
---
arch/arm64/configs/defconfig | 1 -
drivers/clk/hisilicon/Kconfig | 13 ++++++++-----
drivers/mailbox/Kconfig | 12 ++++++++----
3 files changed, 16 insertions(+), 10 deletions(-)
diff --git a/arch/arm64/configs/defconfig b/arch/arm64/configs/defconfig
index ecf6137..1d9d8b9 100644
--- a/arch/arm64/configs/defconfig
+++ b/arch/arm64/configs/defconfig
@@ -549,7 +549,6 @@ CONFIG_HWSPINLOCK_QCOM=y
CONFIG_ARM_MHU=y
CONFIG_PLATFORM_MHU=y
CONFIG_BCM2835_MBOX=y
-CONFIG_HI6220_MBOX=y
CONFIG_QCOM_APCS_IPC=y
CONFIG_ROCKCHIP_IOMMU=y
CONFIG_TEGRA_IOMMU_SMMU=y
diff --git a/drivers/clk/hisilicon/Kconfig b/drivers/clk/hisilicon/Kconfig
index 1bd4355..becdb1d 100644
--- a/drivers/clk/hisilicon/Kconfig
+++ b/drivers/clk/hisilicon/Kconfig
@@ -44,14 +44,17 @@ config RESET_HISI
Build reset controller driver for HiSilicon device chipsets.
config STUB_CLK_HI6220
- bool "Hi6220 Stub Clock Driver"
- depends on COMMON_CLK_HI6220 && MAILBOX
- default ARCH_HISI
+ bool "Hi6220 Stub Clock Driver" if EXPERT
+ depends on (COMMON_CLK_HI6220 || COMPILE_TEST)
+ depends on MAILBOX
+ default COMMON_CLK_HI6220
help
Build the Hisilicon Hi6220 stub clock driver.
config STUB_CLK_HI3660
- bool "Hi3660 Stub Clock Driver"
- depends on COMMON_CLK_HI3660 && MAILBOX
+ bool "Hi3660 Stub Clock Driver" if EXPERT
+ depends on (COMMON_CLK_HI3660 || COMPILE_TEST)
+ depends on MAILBOX
+ default COMMON_CLK_HI3660
help
Build the Hisilicon Hi3660 stub clock driver.
diff --git a/drivers/mailbox/Kconfig b/drivers/mailbox/Kconfig
index a2bb274..567cd02 100644
--- a/drivers/mailbox/Kconfig
+++ b/drivers/mailbox/Kconfig
@@ -109,16 +109,20 @@ config TI_MESSAGE_MANAGER
platform has support for the hardware block.
config HI3660_MBOX
- tristate "Hi3660 Mailbox"
- depends on ARCH_HISI && OF
+ tristate "Hi3660 Mailbox" if EXPERT
+ depends on (ARCH_HISI || COMPILE_TEST)
+ depends on OF
+ default ARCH_HISI
help
An implementation of the hi3660 mailbox. It is used to send message
between application processors and other processors/MCU/DSP. Select
Y here if you want to use Hi3660 mailbox controller.
config HI6220_MBOX
- tristate "Hi6220 Mailbox"
- depends on ARCH_HISI
+ tristate "Hi6220 Mailbox" if EXPERT
+ depends on (ARCH_HISI || COMPILE_TEST)
+ depends on OF
+ default ARCH_HISI
help
An implementation of the hi6220 mailbox. It is used to send message
between application processors and MCU. Say Y here if you want to
--
1.9.1
^ permalink raw reply related
* [PATCH v2 4/5] arm64: dts: hi3660: Add thermal cooling management
From: Leo Yan @ 2018-05-15 2:53 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1526352795-6991-1-git-send-email-leo.yan@linaro.org>
From: Tao Wang <jean.wangtao@linaro.org>
Add nodes and properties for thermal cooling management support.
Signed-off-by: Tao Wang <jean.wangtao@linaro.org>
Signed-off-by: Leo Yan <leo.yan@linaro.org>
---
arch/arm64/boot/dts/hisilicon/hi3660.dtsi | 44 +++++++++++++++++++++++++++++++
1 file changed, 44 insertions(+)
diff --git a/arch/arm64/boot/dts/hisilicon/hi3660.dtsi b/arch/arm64/boot/dts/hisilicon/hi3660.dtsi
index a39da09..e20edd9 100644
--- a/arch/arm64/boot/dts/hisilicon/hi3660.dtsi
+++ b/arch/arm64/boot/dts/hisilicon/hi3660.dtsi
@@ -7,6 +7,7 @@
#include <dt-bindings/interrupt-controller/arm-gic.h>
#include <dt-bindings/clock/hi3660-clock.h>
+#include <dt-bindings/thermal/thermal.h>
/ {
compatible = "hisilicon,hi3660";
@@ -64,6 +65,8 @@
capacity-dmips-mhz = <592>;
clocks = <&stub_clock HI3660_CLK_STUB_CLUSTER0>;
operating-points-v2 = <&cluster0_opp>;
+ #cooling-cells = <2>;
+ dynamic-power-coefficient = <110>;
};
cpu1: cpu at 1 {
@@ -112,6 +115,8 @@
capacity-dmips-mhz = <1024>;
clocks = <&stub_clock HI3660_CLK_STUB_CLUSTER1>;
operating-points-v2 = <&cluster1_opp>;
+ #cooling-cells = <2>;
+ dynamic-power-coefficient = <550>;
};
cpu5: cpu at 101 {
@@ -1073,5 +1078,44 @@
interrupts = <GIC_SPI 145 IRQ_TYPE_LEVEL_HIGH>;
#thermal-sensor-cells = <1>;
};
+
+ thermal-zones {
+
+ cls0: cls0 {
+ polling-delay = <1000>;
+ polling-delay-passive = <100>;
+ sustainable-power = <4500>;
+
+ /* sensor ID */
+ thermal-sensors = <&tsensor 1>;
+
+ trips {
+ threshold: trip-point at 0 {
+ temperature = <65000>;
+ hysteresis = <1000>;
+ type = "passive";
+ };
+
+ target: trip-point at 1 {
+ temperature = <75000>;
+ hysteresis = <1000>;
+ type = "passive";
+ };
+ };
+
+ cooling-maps {
+ map0 {
+ trip = <&target>;
+ contribution = <1024>;
+ cooling-device = <&cpu0 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>;
+ };
+ map1 {
+ trip = <&target>;
+ contribution = <512>;
+ cooling-device = <&cpu4 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>;
+ };
+ };
+ };
+ };
};
};
--
1.9.1
^ permalink raw reply related
* [PATCH v2 3/5] arm64: dts: hi3660: Add CPU frequency scaling support
From: Leo Yan @ 2018-05-15 2:53 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1526352795-6991-1-git-send-email-leo.yan@linaro.org>
Add two CPU OPP tables, one table is corresponding to one cluster,
which allow CPU frequency scaling on hi3660 platforms.
Signed-off-by: Leo Yan <leo.yan@linaro.org>
---
arch/arm64/boot/dts/hisilicon/hi3660.dtsi | 86 +++++++++++++++++++++++++++++++
1 file changed, 86 insertions(+)
diff --git a/arch/arm64/boot/dts/hisilicon/hi3660.dtsi b/arch/arm64/boot/dts/hisilicon/hi3660.dtsi
index 3a3bcff..a39da09 100644
--- a/arch/arm64/boot/dts/hisilicon/hi3660.dtsi
+++ b/arch/arm64/boot/dts/hisilicon/hi3660.dtsi
@@ -62,6 +62,8 @@
next-level-cache = <&A53_L2>;
cpu-idle-states = <&CPU_SLEEP &CLUSTER_SLEEP_0>;
capacity-dmips-mhz = <592>;
+ clocks = <&stub_clock HI3660_CLK_STUB_CLUSTER0>;
+ operating-points-v2 = <&cluster0_opp>;
};
cpu1: cpu at 1 {
@@ -72,6 +74,8 @@
next-level-cache = <&A53_L2>;
cpu-idle-states = <&CPU_SLEEP &CLUSTER_SLEEP_0>;
capacity-dmips-mhz = <592>;
+ clocks = <&stub_clock HI3660_CLK_STUB_CLUSTER0>;
+ operating-points-v2 = <&cluster0_opp>;
};
cpu2: cpu at 2 {
@@ -82,6 +86,8 @@
next-level-cache = <&A53_L2>;
cpu-idle-states = <&CPU_SLEEP &CLUSTER_SLEEP_0>;
capacity-dmips-mhz = <592>;
+ clocks = <&stub_clock HI3660_CLK_STUB_CLUSTER0>;
+ operating-points-v2 = <&cluster0_opp>;
};
cpu3: cpu at 3 {
@@ -92,6 +98,8 @@
next-level-cache = <&A53_L2>;
cpu-idle-states = <&CPU_SLEEP &CLUSTER_SLEEP_0>;
capacity-dmips-mhz = <592>;
+ clocks = <&stub_clock HI3660_CLK_STUB_CLUSTER0>;
+ operating-points-v2 = <&cluster0_opp>;
};
cpu4: cpu at 100 {
@@ -102,6 +110,8 @@
next-level-cache = <&A73_L2>;
cpu-idle-states = <&CPU_SLEEP &CLUSTER_SLEEP_1>;
capacity-dmips-mhz = <1024>;
+ clocks = <&stub_clock HI3660_CLK_STUB_CLUSTER1>;
+ operating-points-v2 = <&cluster1_opp>;
};
cpu5: cpu at 101 {
@@ -112,6 +122,8 @@
next-level-cache = <&A73_L2>;
cpu-idle-states = <&CPU_SLEEP &CLUSTER_SLEEP_1>;
capacity-dmips-mhz = <1024>;
+ clocks = <&stub_clock HI3660_CLK_STUB_CLUSTER1>;
+ operating-points-v2 = <&cluster1_opp>;
};
cpu6: cpu at 102 {
@@ -122,6 +134,8 @@
next-level-cache = <&A73_L2>;
cpu-idle-states = <&CPU_SLEEP &CLUSTER_SLEEP_1>;
capacity-dmips-mhz = <1024>;
+ clocks = <&stub_clock HI3660_CLK_STUB_CLUSTER1>;
+ operating-points-v2 = <&cluster1_opp>;
};
cpu7: cpu at 103 {
@@ -132,6 +146,8 @@
next-level-cache = <&A73_L2>;
cpu-idle-states = <&CPU_SLEEP &CLUSTER_SLEEP_1>;
capacity-dmips-mhz = <1024>;
+ clocks = <&stub_clock HI3660_CLK_STUB_CLUSTER1>;
+ operating-points-v2 = <&cluster1_opp>;
};
idle-states {
@@ -174,6 +190,76 @@
};
};
+ cluster0_opp: opp_table0 {
+ compatible = "operating-points-v2";
+ opp-shared;
+
+ opp00 {
+ opp-hz = /bits/ 64 <533000000>;
+ opp-microvolt = <700000>;
+ clock-latency-ns = <300000>;
+ };
+
+ opp01 {
+ opp-hz = /bits/ 64 <999000000>;
+ opp-microvolt = <800000>;
+ clock-latency-ns = <300000>;
+ };
+
+ opp02 {
+ opp-hz = /bits/ 64 <1402000000>;
+ opp-microvolt = <900000>;
+ clock-latency-ns = <300000>;
+ };
+
+ opp03 {
+ opp-hz = /bits/ 64 <1709000000>;
+ opp-microvolt = <1000000>;
+ clock-latency-ns = <300000>;
+ };
+
+ opp04 {
+ opp-hz = /bits/ 64 <1844000000>;
+ opp-microvolt = <1100000>;
+ clock-latency-ns = <300000>;
+ };
+ };
+
+ cluster1_opp: opp_table1 {
+ compatible = "operating-points-v2";
+ opp-shared;
+
+ opp10 {
+ opp-hz = /bits/ 64 <903000000>;
+ opp-microvolt = <700000>;
+ clock-latency-ns = <300000>;
+ };
+
+ opp11 {
+ opp-hz = /bits/ 64 <1421000000>;
+ opp-microvolt = <800000>;
+ clock-latency-ns = <300000>;
+ };
+
+ opp12 {
+ opp-hz = /bits/ 64 <1805000000>;
+ opp-microvolt = <900000>;
+ clock-latency-ns = <300000>;
+ };
+
+ opp13 {
+ opp-hz = /bits/ 64 <2112000000>;
+ opp-microvolt = <1000000>;
+ clock-latency-ns = <300000>;
+ };
+
+ opp14 {
+ opp-hz = /bits/ 64 <2362000000>;
+ opp-microvolt = <1100000>;
+ clock-latency-ns = <300000>;
+ };
+ };
+
gic: interrupt-controller at e82b0000 {
compatible = "arm,gic-400";
reg = <0x0 0xe82b1000 0 0x1000>, /* GICD */
--
1.9.1
^ permalink raw reply related
* [PATCH v2 2/5] arm64: dts: hi3660: Add stub clock node
From: Leo Yan @ 2018-05-15 2:53 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1526352795-6991-1-git-send-email-leo.yan@linaro.org>
From: Kaihua Zhong <zhongkaihua@huawei.com>
Add stub clock node for hi3660 platform.
Reviewed-by: Leo Yan <leo.yan@linaro.org>
Signed-off-by: Kaihua Zhong <zhongkaihua@huawei.com>
Signed-off-by: Leo Yan <leo.yan@linaro.org>
---
arch/arm64/boot/dts/hisilicon/hi3660.dtsi | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/arch/arm64/boot/dts/hisilicon/hi3660.dtsi b/arch/arm64/boot/dts/hisilicon/hi3660.dtsi
index b9e7c91..3a3bcff 100644
--- a/arch/arm64/boot/dts/hisilicon/hi3660.dtsi
+++ b/arch/arm64/boot/dts/hisilicon/hi3660.dtsi
@@ -282,6 +282,13 @@
#mbox-cells = <3>;
};
+ stub_clock: stub_clock at e896b500 {
+ compatible = "hisilicon,hi3660-stub-clk";
+ reg = <0x0 0xe896b500 0x0 0x0100>;
+ #clock-cells = <1>;
+ mboxes = <&mailbox 13 3 0>;
+ };
+
dual_timer0: timer at fff14000 {
compatible = "arm,sp804", "arm,primecell";
reg = <0x0 0xfff14000 0x0 0x1000>;
--
1.9.1
^ permalink raw reply related
* [PATCH v2 1/5] arm64: dts: hi3660: Add mailbox node
From: Leo Yan @ 2018-05-15 2:53 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1526352795-6991-1-git-send-email-leo.yan@linaro.org>
From: Kaihua Zhong <zhongkaihua@huawei.com>
Add the mailbox controller node for hi3660 platform.
Signed-off-by: Kaihua Zhong <zhongkaihua@huawei.com>
Signed-off-by: Leo Yan <leo.yan@linaro.org>
---
arch/arm64/boot/dts/hisilicon/hi3660.dtsi | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/arch/arm64/boot/dts/hisilicon/hi3660.dtsi b/arch/arm64/boot/dts/hisilicon/hi3660.dtsi
index ec3eb8e..b9e7c91 100644
--- a/arch/arm64/boot/dts/hisilicon/hi3660.dtsi
+++ b/arch/arm64/boot/dts/hisilicon/hi3660.dtsi
@@ -274,6 +274,14 @@
#reset-cells = <2>;
};
+ mailbox: mailbox at e896b000 {
+ compatible = "hisilicon,hi3660-mbox";
+ reg = <0x0 0xe896b000 0x0 0x1000>;
+ interrupts = <GIC_SPI 192 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 193 IRQ_TYPE_LEVEL_HIGH>;
+ #mbox-cells = <3>;
+ };
+
dual_timer0: timer at fff14000 {
compatible = "arm,sp804", "arm,primecell";
reg = <0x0 0xfff14000 0x0 0x1000>;
--
1.9.1
^ permalink raw reply related
* [PATCH v2 0/5] Hi3660: enable power management features
From: Leo Yan @ 2018-05-15 2:53 UTC (permalink / raw)
To: linux-arm-kernel
Since hi3660 drivers have been merged into Linux kernel (mailbox driver is in
Linux-next branch and other drivers are existed in Linux mainline kernel), so
this patch series is to enable power management features on hi3660.
This patch series includes device tree binding for mailbox, stub clock and CPU
OPPs, and has one patch to consolidate the Kconfigs for driver modules.
This patch set have been tested on Hikey960 and also verified the patch 'hisi:
Consolidate the Kconfigs for the CLOCK_STUB and the MAILBOX' for Hikey620.
Changes from v1:
* Changed patch subject from "dts: arm64: hi3660" to "arm64: dts: hi3660".
Daniel Lezcano (1):
hisi: Consolidate the Kconfigs for the CLOCK_STUB and the MAILBOX
Kaihua Zhong (2):
arm64: dts: hi3660: Add mailbox node
arm64: dts: hi3660: Add stub clock node
Leo Yan (1):
arm64: dts: hi3660: Add CPU frequency scaling support
Tao Wang (1):
arm64: dts: hi3660: Add thermal cooling management
arch/arm64/boot/dts/hisilicon/hi3660.dtsi | 145 ++++++++++++++++++++++++++++++
arch/arm64/configs/defconfig | 1 -
drivers/clk/hisilicon/Kconfig | 13 +--
drivers/mailbox/Kconfig | 12 ++-
4 files changed, 161 insertions(+), 10 deletions(-)
--
1.9.1
^ permalink raw reply
* [PATCH 1/1] drm/mediatek: Add support for mediatek SOC MT2712
From: CK Hu @ 2018-05-15 2:30 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20180514095910.22618-2-stu.hsieh@mediatek.com>
Hi, Stu:
I've some inline comments.
On Mon, 2018-05-14 at 17:59 +0800, Stu Hsieh wrote:
> This patch add support for the Mediatek MT2712 DISP subsystem.
> There are two OVL engine and three disp output in MT2712.
>
> Signed-off-by: Stu Hsieh <stu.hsieh@mediatek.com>
> ---
> drivers/gpu/drm/mediatek/mtk_drm_ddp.c | 77 ++++++++++++++++++++++++++---
> drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.c | 4 ++
> drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.h | 3 ++
> drivers/gpu/drm/mediatek/mtk_drm_drv.c | 44 +++++++++++++++++
> drivers/gpu/drm/mediatek/mtk_drm_drv.h | 7 ++-
> 5 files changed, 127 insertions(+), 8 deletions(-)
>
> diff --git a/drivers/gpu/drm/mediatek/mtk_drm_ddp.c b/drivers/gpu/drm/mediatek/mtk_drm_ddp.c
> index 8130f3dab661..641f4361b006 100644
> --- a/drivers/gpu/drm/mediatek/mtk_drm_ddp.c
> +++ b/drivers/gpu/drm/mediatek/mtk_drm_ddp.c
> @@ -29,6 +29,8 @@
> #define DISP_REG_CONFIG_DISP_COLOR0_SEL_IN 0x084
> #define DISP_REG_CONFIG_DISP_COLOR1_SEL_IN 0x088
> #define DISP_REG_CONFIG_DPI_SEL_IN 0x0ac
> +#define DISP_REG_CONFIG_DISP_RDMA2_SOUT 0x0b8
> +#define DISP_REG_CONFIG_DISP_RDMA0_MOUT_EN 0x0c4
> #define DISP_REG_CONFIG_DISP_RDMA1_MOUT_EN 0x0c8
> #define DISP_REG_CONFIG_MMSYS_CG_CON0 0x100
>
> @@ -41,6 +43,7 @@
> #define DISP_REG_MUTEX_RST(n) (0x28 + 0x20 * (n))
> #define DISP_REG_MUTEX_MOD(n) (0x2c + 0x20 * (n))
> #define DISP_REG_MUTEX_SOF(n) (0x30 + 0x20 * (n))
> +#define DISP_REG_MUTEX_MOD2(n) (0x34 + 0x20 * (n))
>
> #define INT_MUTEX BIT(1)
>
> @@ -60,6 +63,25 @@
> #define MT8173_MUTEX_MOD_DISP_PWM1 BIT(24)
> #define MT8173_MUTEX_MOD_DISP_OD BIT(25)
>
> +#define MT2712_MUTEX_MOD_DISP_OVL0 BIT(11)
> +#define MT2712_MUTEX_MOD_DISP_OVL1 BIT(12)
> +#define MT2712_MUTEX_MOD_DISP_RDMA0 BIT(13)
> +#define MT2712_MUTEX_MOD_DISP_RDMA1 BIT(14)
> +#define MT2712_MUTEX_MOD_DISP_RDMA2 BIT(15)
> +#define MT2712_MUTEX_MOD_DISP_WDMA0 BIT(16)
> +#define MT2712_MUTEX_MOD_DISP_WDMA1 BIT(17)
> +#define MT2712_MUTEX_MOD_DISP_COLOR0 BIT(18)
> +#define MT2712_MUTEX_MOD_DISP_COLOR1 BIT(19)
> +#define MT2712_MUTEX_MOD_DISP_AAL BIT(20)
> +#define MT2712_MUTEX_MOD_DISP_UFOE BIT(22)
> +#define MT2712_MUTEX_MOD_DISP_PWM0 BIT(23)
> +#define MT2712_MUTEX_MOD_DISP_PWM1 BIT(24)
> +#define MT2712_MUTEX_MOD_DISP_PWM2 BIT(10)
> +#define MT2712_MUTEX_MOD_DISP_OD BIT(25)
> +/* modules more than 32, add BIT(31) when using DISP_REG_MUTEX_MOD2 bit */
> +#define MT2712_MUTEX_MOD2_DISP_AAL1 (BIT(1) | BIT(31))
> +#define MT2712_MUTEX_MOD2_DISP_OD1 (BIT(2) | BIT(31))
> +
It looks like that MUTEX_MOD definition varies for each SoC. I think
such definition should be passed from dts to prevent modify driver for
each SoC. For example, the clock definition varies for each SoC, and its
definition is placed in [1]. The dts [2] include the header file and
pass the clock definition to driver.
[1]
https://elixir.bootlin.com/linux/v4.17-rc5/source/include/dt-bindings/clock/mt2712-clk.h
[2]
https://elixir.bootlin.com/linux/v4.17-rc5/source/arch/arm64/boot/dts/mediatek/mt2712e.dtsi
> #define MT2701_MUTEX_MOD_DISP_OVL BIT(3)
> #define MT2701_MUTEX_MOD_DISP_WDMA BIT(6)
> #define MT2701_MUTEX_MOD_DISP_COLOR BIT(7)
> @@ -74,6 +96,7 @@
>
> #define OVL0_MOUT_EN_COLOR0 0x1
> #define OD_MOUT_EN_RDMA0 0x1
> +#define OD1_MOUT_EN_RDMA1 BIT(16)
> #define UFOE_MOUT_EN_DSI0 0x1
> #define COLOR0_SEL_IN_OVL0 0x1
> #define OVL1_MOUT_EN_COLOR1 0x1
> @@ -108,6 +131,26 @@ static const unsigned int mt2701_mutex_mod[DDP_COMPONENT_ID_MAX] = {
> [DDP_COMPONENT_WDMA0] = MT2701_MUTEX_MOD_DISP_WDMA,
> };
>
> +static const unsigned int mt2712_mutex_mod[DDP_COMPONENT_ID_MAX] = {
> + [DDP_COMPONENT_AAL] = MT2712_MUTEX_MOD_DISP_AAL,
> + [DDP_COMPONENT_AAL1] = MT2712_MUTEX_MOD2_DISP_AAL1,
> + [DDP_COMPONENT_COLOR0] = MT2712_MUTEX_MOD_DISP_COLOR0,
> + [DDP_COMPONENT_COLOR1] = MT2712_MUTEX_MOD_DISP_COLOR1,
> + [DDP_COMPONENT_OD] = MT2712_MUTEX_MOD_DISP_OD,
> + [DDP_COMPONENT_OD1] = MT2712_MUTEX_MOD2_DISP_OD1,
> + [DDP_COMPONENT_OVL0] = MT2712_MUTEX_MOD_DISP_OVL0,
> + [DDP_COMPONENT_OVL1] = MT2712_MUTEX_MOD_DISP_OVL1,
> + [DDP_COMPONENT_PWM0] = MT2712_MUTEX_MOD_DISP_PWM0,
> + [DDP_COMPONENT_PWM1] = MT2712_MUTEX_MOD_DISP_PWM1,
> + [DDP_COMPONENT_PWM2] = MT2712_MUTEX_MOD_DISP_PWM2,
> + [DDP_COMPONENT_RDMA0] = MT2712_MUTEX_MOD_DISP_RDMA0,
> + [DDP_COMPONENT_RDMA1] = MT2712_MUTEX_MOD_DISP_RDMA1,
> + [DDP_COMPONENT_RDMA2] = MT2712_MUTEX_MOD_DISP_RDMA2,
> + [DDP_COMPONENT_UFOE] = MT2712_MUTEX_MOD_DISP_UFOE,
> + [DDP_COMPONENT_WDMA0] = MT2712_MUTEX_MOD_DISP_WDMA0,
> + [DDP_COMPONENT_WDMA1] = MT2712_MUTEX_MOD_DISP_WDMA1,
> +};
> +
> static const unsigned int mt8173_mutex_mod[DDP_COMPONENT_ID_MAX] = {
> [DDP_COMPONENT_AAL] = MT8173_MUTEX_MOD_DISP_AAL,
> [DDP_COMPONENT_COLOR0] = MT8173_MUTEX_MOD_DISP_COLOR0,
> @@ -150,6 +193,9 @@ static unsigned int mtk_ddp_mout_en(enum mtk_ddp_comp_id cur,
> } else if (cur == DDP_COMPONENT_GAMMA && next == DDP_COMPONENT_RDMA1) {
> *addr = DISP_REG_CONFIG_DISP_GAMMA_MOUT_EN;
> value = GAMMA_MOUT_EN_RDMA1;
> + } else if (cur == DDP_COMPONENT_OD1 && next == DDP_COMPONENT_RDMA1) {
> + *addr = DISP_REG_CONFIG_DISP_OD_MOUT_EN;
> + value = OD1_MOUT_EN_RDMA1;
I think this should be moved to an independent patch and its title is
'add connection from OD1 to RDMA1'.
> } else if (cur == DDP_COMPONENT_RDMA1 && next == DDP_COMPONENT_DPI0) {
> *addr = DISP_REG_CONFIG_DISP_RDMA1_MOUT_EN;
> value = RDMA1_MOUT_DPI0;
> @@ -278,6 +324,7 @@ void mtk_disp_mutex_add_comp(struct mtk_disp_mutex *mutex,
> struct mtk_ddp *ddp = container_of(mutex, struct mtk_ddp,
> mutex[mutex->id]);
> unsigned int reg;
> + unsigned int offset;
>
> WARN_ON(&ddp->mutex[mutex->id] != mutex);
>
> @@ -292,9 +339,17 @@ void mtk_disp_mutex_add_comp(struct mtk_disp_mutex *mutex,
> reg = MUTEX_SOF_DPI0;
> break;
> default:
> - reg = readl_relaxed(ddp->regs + DISP_REG_MUTEX_MOD(mutex->id));
> - reg |= ddp->mutex_mod[id];
> - writel_relaxed(reg, ddp->regs + DISP_REG_MUTEX_MOD(mutex->id));
> + if (ddp->mutex_mod[id] <= BIT(31)) {
You could reduce one byte if you write as
if (ddp->mutex_mod[id] < BIT(32))
> + offset = DISP_REG_MUTEX_MOD(mutex->id);
> + reg = readl_relaxed(ddp->regs + offset);
> + reg |= ddp->mutex_mod[id];
> + writel_relaxed(reg, ddp->regs + offset);
> + } else {
> + offset = DISP_REG_MUTEX_MOD2(mutex->id);
> + reg = readl_relaxed(ddp->regs + offset);
> + reg |= (ddp->mutex_mod[id] & ~BIT(31));
> + writel_relaxed(reg, ddp->regs + offset);
> + }
I think this should be moved to an independent patch and its title is
'support maximum 64 mutex mod'.
> return;
> }
>
> @@ -307,6 +362,7 @@ void mtk_disp_mutex_remove_comp(struct mtk_disp_mutex *mutex,
> struct mtk_ddp *ddp = container_of(mutex, struct mtk_ddp,
> mutex[mutex->id]);
> unsigned int reg;
> + unsigned int offset;
>
> WARN_ON(&ddp->mutex[mutex->id] != mutex);
>
> @@ -318,9 +374,17 @@ void mtk_disp_mutex_remove_comp(struct mtk_disp_mutex *mutex,
> ddp->regs + DISP_REG_MUTEX_SOF(mutex->id));
> break;
> default:
> - reg = readl_relaxed(ddp->regs + DISP_REG_MUTEX_MOD(mutex->id));
> - reg &= ~(ddp->mutex_mod[id]);
> - writel_relaxed(reg, ddp->regs + DISP_REG_MUTEX_MOD(mutex->id));
> + if (ddp->mutex_mod[id] <= BIT(31)) {
> + offset = DISP_REG_MUTEX_MOD(mutex->id);
> + reg = readl_relaxed(ddp->regs + offset);
> + reg &= ~(ddp->mutex_mod[id]);
> + writel_relaxed(reg, ddp->regs + offset);
> + } else {
> + offset = DISP_REG_MUTEX_MOD2(mutex->id);
> + reg = readl_relaxed(ddp->regs + offset);
> + reg &= ~(ddp->mutex_mod[id] & ~BIT(31));
> + writel_relaxed(reg, ddp->regs + offset);
> + }
> break;
> }
> }
> @@ -407,6 +471,7 @@ static int mtk_ddp_remove(struct platform_device *pdev)
>
> static const struct of_device_id ddp_driver_dt_match[] = {
> { .compatible = "mediatek,mt2701-disp-mutex", .data = mt2701_mutex_mod},
> + { .compatible = "mediatek,mt2712-disp-mutex", .data = mt2712_mutex_mod},
> { .compatible = "mediatek,mt8173-disp-mutex", .data = mt8173_mutex_mod},
> {},
> };
> diff --git a/drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.c b/drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.c
> index 4672317e3ad1..ebe8f0018b31 100644
> --- a/drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.c
> +++ b/drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.c
> @@ -219,6 +219,7 @@ struct mtk_ddp_comp_match {
>
> static const struct mtk_ddp_comp_match mtk_ddp_matches[DDP_COMPONENT_ID_MAX] = {
> [DDP_COMPONENT_AAL] = { MTK_DISP_AAL, 0, &ddp_aal },
> + [DDP_COMPONENT_AAL1] = { MTK_DISP_AAL, 1, &ddp_aal },
For the naming consistency, there are COLOR0 and COLOR1, so after you
add 'AAL1', you should change AAL to AAL0. And this modification should
be an independent patch.
> [DDP_COMPONENT_BLS] = { MTK_DISP_BLS, 0, NULL },
> [DDP_COMPONENT_COLOR0] = { MTK_DISP_COLOR, 0, NULL },
> [DDP_COMPONENT_COLOR1] = { MTK_DISP_COLOR, 1, NULL },
> @@ -227,9 +228,12 @@ static const struct mtk_ddp_comp_match mtk_ddp_matches[DDP_COMPONENT_ID_MAX] = {
> [DDP_COMPONENT_DSI1] = { MTK_DSI, 1, NULL },
> [DDP_COMPONENT_GAMMA] = { MTK_DISP_GAMMA, 0, &ddp_gamma },
> [DDP_COMPONENT_OD] = { MTK_DISP_OD, 0, &ddp_od },
> + [DDP_COMPONENT_OD1] = { MTK_DISP_OD, 1, &ddp_od },
Ditto.
> [DDP_COMPONENT_OVL0] = { MTK_DISP_OVL, 0, NULL },
> [DDP_COMPONENT_OVL1] = { MTK_DISP_OVL, 1, NULL },
> [DDP_COMPONENT_PWM0] = { MTK_DISP_PWM, 0, NULL },
> + [DDP_COMPONENT_PWM1] = { MTK_DISP_PWM, 1, NULL },
> + [DDP_COMPONENT_PWM2] = { MTK_DISP_PWM, 2, NULL },
> [DDP_COMPONENT_RDMA0] = { MTK_DISP_RDMA, 0, NULL },
> [DDP_COMPONENT_RDMA1] = { MTK_DISP_RDMA, 1, NULL },
> [DDP_COMPONENT_RDMA2] = { MTK_DISP_RDMA, 2, NULL },
> diff --git a/drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.h b/drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.h
> index 0828cf8bf85c..43100f9215ae 100644
> --- a/drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.h
> +++ b/drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.h
> @@ -42,6 +42,7 @@ enum mtk_ddp_comp_type {
>
> enum mtk_ddp_comp_id {
> DDP_COMPONENT_AAL,
> + DDP_COMPONENT_AAL1,
> DDP_COMPONENT_BLS,
> DDP_COMPONENT_COLOR0,
> DDP_COMPONENT_COLOR1,
> @@ -50,10 +51,12 @@ enum mtk_ddp_comp_id {
> DDP_COMPONENT_DSI1,
> DDP_COMPONENT_GAMMA,
> DDP_COMPONENT_OD,
> + DDP_COMPONENT_OD1,
> DDP_COMPONENT_OVL0,
> DDP_COMPONENT_OVL1,
> DDP_COMPONENT_PWM0,
> DDP_COMPONENT_PWM1,
> + DDP_COMPONENT_PWM2,
> DDP_COMPONENT_RDMA0,
> DDP_COMPONENT_RDMA1,
> DDP_COMPONENT_RDMA2,
> diff --git a/drivers/gpu/drm/mediatek/mtk_drm_drv.c b/drivers/gpu/drm/mediatek/mtk_drm_drv.c
> index a2ca90fc403c..41baf6653bfc 100644
> --- a/drivers/gpu/drm/mediatek/mtk_drm_drv.c
> +++ b/drivers/gpu/drm/mediatek/mtk_drm_drv.c
> @@ -146,6 +146,32 @@ static const enum mtk_ddp_comp_id mt2701_mtk_ddp_ext[] = {
> DDP_COMPONENT_DPI0,
> };
>
> +static const enum mtk_ddp_comp_id mt2712_mtk_ddp_main[] = {
> + DDP_COMPONENT_OVL0,
> + DDP_COMPONENT_COLOR0,
> + DDP_COMPONENT_AAL,
> + DDP_COMPONENT_OD,
> + DDP_COMPONENT_RDMA0,
> + DDP_COMPONENT_DPI0,
> + DDP_COMPONENT_PWM0,
> +};
> +
> +static const enum mtk_ddp_comp_id mt2712_mtk_ddp_ext[] = {
> + DDP_COMPONENT_OVL1,
> + DDP_COMPONENT_COLOR1,
> + DDP_COMPONENT_AAL1,
> + DDP_COMPONENT_OD1,
> + DDP_COMPONENT_RDMA1,
> + DDP_COMPONENT_DPI1,
> + DDP_COMPONENT_PWM1,
> +};
> +
> +static const enum mtk_ddp_comp_id mt2712_mtk_ddp_third[] = {
> + DDP_COMPONENT_RDMA2,
> + DDP_COMPONENT_DSI2,
> + DDP_COMPONENT_PWM2,
> +};
> +
It looks like that the path definition varies for each SoC. I think such
definition should be passed from dts to prevent modify driver for each
SoC. In [3], it define the partial connection in device tree, but I
would like this to be more general. My idea is:
1. Each component has one or two endpoint in device tree. The first and
the last has one, and the others has two.
2. Remove mtxxxx_mtk_ddp_main[] and mtxxxx_mtk_ddp_ext[], use multiple
link list (One display path has one link list, two display path has two
link list) to generate it by parsing device tree in mtk_drm_probe().
[3] https://patchwork.kernel.org/patch/10397337/
> static const enum mtk_ddp_comp_id mt8173_mtk_ddp_main[] = {
> DDP_COMPONENT_OVL0,
> DDP_COMPONENT_COLOR0,
> @@ -173,6 +199,15 @@ static const struct mtk_mmsys_driver_data mt2701_mmsys_driver_data = {
> .shadow_register = true,
> };
>
> +static const struct mtk_mmsys_driver_data mt2712_mmsys_driver_data = {
> + .main_path = mt2712_mtk_ddp_main,
> + .main_len = ARRAY_SIZE(mt2712_mtk_ddp_main),
> + .ext_path = mt2712_mtk_ddp_ext,
> + .ext_len = ARRAY_SIZE(mt2712_mtk_ddp_ext),
> + .third_path = mt2712_mtk_ddp_third,
> + .third_len = ARRAY_SIZE(mt2712_mtk_ddp_third),
> +};
> +
> static const struct mtk_mmsys_driver_data mt8173_mmsys_driver_data = {
> .main_path = mt8173_mtk_ddp_main,
> .main_len = ARRAY_SIZE(mt8173_mtk_ddp_main),
> @@ -232,6 +267,11 @@ static int mtk_drm_kms_init(struct drm_device *drm)
> if (ret < 0)
> goto err_component_unbind;
>
> + ret = mtk_drm_crtc_create(drm, private->data->third_path,
> + private->data->third_len);
> + if (ret < 0)
> + goto err_component_unbind;
> +
> /* Use OVL device for all DMA memory allocations */
> np = private->comp_node[private->data->main_path[0]] ?:
> private->comp_node[private->data->ext_path[0]];
> @@ -372,8 +412,10 @@ static const struct of_device_id mtk_ddp_comp_dt_ids[] = {
> { .compatible = "mediatek,mt8173-disp-ufoe", .data = (void *)MTK_DISP_UFOE },
> { .compatible = "mediatek,mt2701-dsi", .data = (void *)MTK_DSI },
> { .compatible = "mediatek,mt8173-dsi", .data = (void *)MTK_DSI },
> + { .compatible = "mediatek,mt2712-dpi", .data = (void *)MTK_DPI },
Move this modification to the patch which modify the dpi driver for
mt2712.
> { .compatible = "mediatek,mt8173-dpi", .data = (void *)MTK_DPI },
> { .compatible = "mediatek,mt2701-disp-mutex", .data = (void *)MTK_DISP_MUTEX },
> + { .compatible = "mediatek,mt2712-disp-mutex", .data = (void *)MTK_DISP_MUTEX },
> { .compatible = "mediatek,mt8173-disp-mutex", .data = (void *)MTK_DISP_MUTEX },
> { .compatible = "mediatek,mt2701-disp-pwm", .data = (void *)MTK_DISP_BLS },
> { .compatible = "mediatek,mt8173-disp-pwm", .data = (void *)MTK_DISP_PWM },
> @@ -552,6 +594,8 @@ static SIMPLE_DEV_PM_OPS(mtk_drm_pm_ops, mtk_drm_sys_suspend,
> static const struct of_device_id mtk_drm_of_ids[] = {
> { .compatible = "mediatek,mt2701-mmsys",
> .data = &mt2701_mmsys_driver_data},
> + { .compatible = "mediatek,mt2712-mmsys",
> + .data = &mt2712_mmsys_driver_data},
> { .compatible = "mediatek,mt8173-mmsys",
> .data = &mt8173_mmsys_driver_data},
> { }
> diff --git a/drivers/gpu/drm/mediatek/mtk_drm_drv.h b/drivers/gpu/drm/mediatek/mtk_drm_drv.h
> index c3378c452c0a..e821342bc2d3 100644
> --- a/drivers/gpu/drm/mediatek/mtk_drm_drv.h
> +++ b/drivers/gpu/drm/mediatek/mtk_drm_drv.h
> @@ -17,8 +17,8 @@
> #include <linux/io.h>
> #include "mtk_drm_ddp_comp.h"
>
> -#define MAX_CRTC 2
> -#define MAX_CONNECTOR 2
> +#define MAX_CRTC 3
> +#define MAX_CONNECTOR 3
>
> struct device;
> struct device_node;
> @@ -33,6 +33,9 @@ struct mtk_mmsys_driver_data {
> unsigned int main_len;
> const enum mtk_ddp_comp_id *ext_path;
> unsigned int ext_len;
> + enum mtk_ddp_comp_id *third_path;
> + unsigned int third_len;
> +
> bool shadow_register;
> };
>
Regards,
CK
^ permalink raw reply
* [PATCH] ARM: dts: chromecast: override bad bootloader memory info
From: Jisheng Zhang @ 2018-05-15 2:29 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20180514215645.17592-2-tommyhebb@gmail.com>
Hi,
On Mon, 14 May 2018 17:56:45 -0400 Thomas Hebb wrote:
> On the Chromecast, the bootloader provides us with an ATAG_MEM of
> start=0x01000000 and size=0x3eff8000. This is clearly incorrect, as the
> range given encompasses nearly a GiB but the Chromecast only has 512MiB
> of RAM! Additionally, this causes the kernel to be decompressed at
> 0x00008000, below the claimed beginning of RAM, and so the boot fails.
>
> Since the existing ATAG parsing code runs before the kernel is even
> decompressed and irrevocably patches the device tree, don't even try
This means you enabled ARM_ATAG_DTB_COMPAT. could we disable it instead?
The ATAG is useless when we provide dtb. And IIRC, the ATAG is provided due
to legacy history code.
Thanks
> to bypass it. Instead, use the "linux,usable-memory" property instead
> of the "reg" property to define the real range. The ATAG code only
> overwrites reg, but linux,usable-memory is checked first in the OF
> driver, so the fact that reg gets changed makes no difference.
>
> Signed-off-by: Thomas Hebb <tommyhebb@gmail.com>
> ---
> arch/arm/boot/dts/berlin2cd-google-chromecast.dts | 12 +++++++++++-
> 1 file changed, 11 insertions(+), 1 deletion(-)
>
> diff --git a/arch/arm/boot/dts/berlin2cd-google-chromecast.dts b/arch/arm/boot/dts/berlin2cd-google-chromecast.dts
> index 20f31cdeaf38..54221f55bfa2 100644
> --- a/arch/arm/boot/dts/berlin2cd-google-chromecast.dts
> +++ b/arch/arm/boot/dts/berlin2cd-google-chromecast.dts
> @@ -52,7 +52,17 @@
>
> memory at 0 {
> device_type = "memory";
> - reg = <0x00000000 0x20000000>; /* 512 MB */
> +
> + /*
> + * We're using "linux,usable-memory" instead of "reg" here
> + * because the (signed and encrypted) bootloader that shipped
> + * with this device provides an incorrect memory range in
> + * ATAG_MEM. Linux helpfully overrides the "reg" property with
> + * data from the ATAG, so we can't specify the proper range
> + * normally. Fortunately, this alternate property is checked
> + * first by the OF driver, so we can (ab)use it instead.
> + */
> + linux,usable-memory = <0x00000000 0x20000000>; /* 512 MB */
> };
>
> leds {
^ permalink raw reply
* [PATCH RESEND] clk: uniphier: add LD11/LD20 stream demux system clock
From: Katsuhiro Suzuki @ 2018-05-15 2:14 UTC (permalink / raw)
To: linux-arm-kernel
Add clock for MPEG2 transport stream I/O and demux system (HSC) on
UniPhier LD11/LD20 SoCs.
Signed-off-by: Katsuhiro Suzuki <suzuki.katsuhiro@socionext.com>
Acked-by: Masahiro Yamada <yamada.masahiro@socionext.com>
---
drivers/clk/uniphier/clk-uniphier-sys.c | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/drivers/clk/uniphier/clk-uniphier-sys.c b/drivers/clk/uniphier/clk-uniphier-sys.c
index ebc78ab2df05..4f5ff9fa11fd 100644
--- a/drivers/clk/uniphier/clk-uniphier-sys.c
+++ b/drivers/clk/uniphier/clk-uniphier-sys.c
@@ -51,6 +51,9 @@
#define UNIPHIER_LD11_SYS_CLK_STDMAC(idx) \
UNIPHIER_CLK_GATE("stdmac", (idx), NULL, 0x210c, 8)
+#define UNIPHIER_LD11_SYS_CLK_HSC(idx) \
+ UNIPHIER_CLK_GATE("hsc", (idx), NULL, 0x210c, 9)
+
#define UNIPHIER_PRO4_SYS_CLK_GIO(idx) \
UNIPHIER_CLK_GATE("gio", (idx), NULL, 0x2104, 6)
@@ -182,6 +185,7 @@ const struct uniphier_clk_data uniphier_ld11_sys_clk_data[] = {
/* Index 5 reserved for eMMC PHY */
UNIPHIER_LD11_SYS_CLK_ETHER(6),
UNIPHIER_LD11_SYS_CLK_STDMAC(8), /* HSC, MIO */
+ UNIPHIER_LD11_SYS_CLK_HSC(9),
UNIPHIER_CLK_FACTOR("usb2", -1, "ref", 24, 25),
UNIPHIER_LD11_SYS_CLK_AIO(40),
UNIPHIER_LD11_SYS_CLK_EVEA(41),
@@ -215,6 +219,7 @@ const struct uniphier_clk_data uniphier_ld20_sys_clk_data[] = {
UNIPHIER_LD20_SYS_CLK_SD,
UNIPHIER_LD11_SYS_CLK_ETHER(6),
UNIPHIER_LD11_SYS_CLK_STDMAC(8), /* HSC */
+ UNIPHIER_LD11_SYS_CLK_HSC(9),
/* GIO is always clock-enabled: no function for 0x210c bit5 */
/*
* clock for USB Link is enabled by the logic "OR" of bit 14 and bit 15.
--
2.17.0
^ permalink raw reply related
* [PATCH] media: helene: add I2C device probe function
From: Katsuhiro Suzuki @ 2018-05-15 2:05 UTC (permalink / raw)
To: linux-arm-kernel
This patch adds I2C probe function to use dvb_module_probe()
with this driver.
Signed-off-by: Katsuhiro Suzuki <suzuki.katsuhiro@socionext.com>
---
drivers/media/dvb-frontends/helene.c | 88 ++++++++++++++++++++++++++--
drivers/media/dvb-frontends/helene.h | 2 +
2 files changed, 86 insertions(+), 4 deletions(-)
diff --git a/drivers/media/dvb-frontends/helene.c b/drivers/media/dvb-frontends/helene.c
index a0d0b53c91d7..04033f0c278b 100644
--- a/drivers/media/dvb-frontends/helene.c
+++ b/drivers/media/dvb-frontends/helene.c
@@ -666,7 +666,7 @@ static int helene_set_params_s(struct dvb_frontend *fe)
return 0;
}
-static int helene_set_params(struct dvb_frontend *fe)
+static int helene_set_params_t(struct dvb_frontend *fe)
{
u8 data[MAX_WRITE_REGSIZE];
u32 frequency;
@@ -835,6 +835,19 @@ static int helene_set_params(struct dvb_frontend *fe)
return 0;
}
+static int helene_set_params(struct dvb_frontend *fe)
+{
+ struct dtv_frontend_properties *p = &fe->dtv_property_cache;
+
+ if (p->delivery_system == SYS_DVBT ||
+ p->delivery_system == SYS_DVBT2 ||
+ p->delivery_system == SYS_ISDBT ||
+ p->delivery_system == SYS_DVBC_ANNEX_A)
+ return helene_set_params_t(fe);
+
+ return helene_set_params_s(fe);
+}
+
static int helene_get_frequency(struct dvb_frontend *fe, u32 *frequency)
{
struct helene_priv *priv = fe->tuner_priv;
@@ -843,7 +856,7 @@ static int helene_get_frequency(struct dvb_frontend *fe, u32 *frequency)
return 0;
}
-static const struct dvb_tuner_ops helene_tuner_ops = {
+static const struct dvb_tuner_ops helene_tuner_ops_t = {
.info = {
.name = "Sony HELENE Ter tuner",
.frequency_min = 1000000,
@@ -853,7 +866,7 @@ static const struct dvb_tuner_ops helene_tuner_ops = {
.init = helene_init,
.release = helene_release,
.sleep = helene_sleep,
- .set_params = helene_set_params,
+ .set_params = helene_set_params_t,
.get_frequency = helene_get_frequency,
};
@@ -871,6 +884,20 @@ static const struct dvb_tuner_ops helene_tuner_ops_s = {
.get_frequency = helene_get_frequency,
};
+static const struct dvb_tuner_ops helene_tuner_ops = {
+ .info = {
+ .name = "Sony HELENE Sat/Ter tuner",
+ .frequency_min = 500000,
+ .frequency_max = 1200000000,
+ .frequency_step = 1000,
+ },
+ .init = helene_init,
+ .release = helene_release,
+ .sleep = helene_sleep,
+ .set_params = helene_set_params,
+ .get_frequency = helene_get_frequency,
+};
+
/* power-on tuner
* call once after reset
*/
@@ -1032,7 +1059,7 @@ struct dvb_frontend *helene_attach(struct dvb_frontend *fe,
if (fe->ops.i2c_gate_ctrl)
fe->ops.i2c_gate_ctrl(fe, 0);
- memcpy(&fe->ops.tuner_ops, &helene_tuner_ops,
+ memcpy(&fe->ops.tuner_ops, &helene_tuner_ops_t,
sizeof(struct dvb_tuner_ops));
fe->tuner_priv = priv;
dev_info(&priv->i2c->dev,
@@ -1042,6 +1069,59 @@ struct dvb_frontend *helene_attach(struct dvb_frontend *fe,
}
EXPORT_SYMBOL(helene_attach);
+static int helene_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct helene_config *config = client->dev.platform_data;
+ struct dvb_frontend *fe = config->fe;
+ struct device *dev = &client->dev;
+ struct helene_priv *priv;
+
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ priv->i2c_address = client->addr;
+ priv->i2c = client->adapter;
+ priv->set_tuner_data = config->set_tuner_priv;
+ priv->set_tuner = config->set_tuner_callback;
+ priv->xtal = config->xtal;
+
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 1);
+
+ if (helene_x_pon(priv) != 0)
+ return -EINVAL;
+
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 0);
+
+ memcpy(&fe->ops.tuner_ops, &helene_tuner_ops,
+ sizeof(struct dvb_tuner_ops));
+ fe->tuner_priv = priv;
+ i2c_set_clientdata(client, priv);
+
+ dev_info(dev, "Sony HELENE attached on addr=%x at I2C adapter %p\n",
+ priv->i2c_address, priv->i2c);
+
+ return 0;
+}
+
+static const struct i2c_device_id helene_id[] = {
+ { "helene", },
+ {}
+};
+MODULE_DEVICE_TABLE(i2c, helene_id);
+
+static struct i2c_driver helene_driver = {
+ .driver = {
+ .name = "helene",
+ },
+ .probe = helene_probe,
+ .id_table = helene_id,
+};
+module_i2c_driver(helene_driver);
+
MODULE_DESCRIPTION("Sony HELENE Sat/Ter tuner driver");
MODULE_AUTHOR("Abylay Ospan <aospan@netup.ru>");
MODULE_LICENSE("GPL");
diff --git a/drivers/media/dvb-frontends/helene.h b/drivers/media/dvb-frontends/helene.h
index c9fc81c7e4e7..ceaa283708cb 100644
--- a/drivers/media/dvb-frontends/helene.h
+++ b/drivers/media/dvb-frontends/helene.h
@@ -46,6 +46,8 @@ struct helene_config {
void *set_tuner_priv;
int (*set_tuner_callback)(void *, int);
enum helene_xtal xtal;
+
+ struct dvb_frontend *fe;
};
#if IS_REACHABLE(CONFIG_DVB_HELENE)
--
2.17.0
^ permalink raw reply related
* [PATCH] KVM: arm/arm64: fix unaligned hva start and end in handle_hva_to_gpa
From: Jia He @ 2018-05-15 2:03 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <695beacb-ff51-bb2c-72ef-d268f7d4e59d@arm.com>
Hi Suzuki
I will merge the other thread into this, and add the necessary CC list
That WARN_ON call trace is very easy to reproduce in my armv8a server after I
start 20 guests
and run memhog in the host. Of course, ksm should be enabled
For you question about my inject fault debug patch:
diff --git a/mm/ksm.c b/mm/ksm.c
index e3cbf9a..876bec8 100644
--- a/mm/ksm.c
+++ b/mm/ksm.c
@@ -43,6 +43,8 @@
?#include <asm/tlbflush.h>
?#include "internal.h"
+int trigger_by_ksm = 0;
+
?#ifdef CONFIG_NUMA
?#define NUMA(x)??????????????? (x)
?#define DO_NUMA(x)???? do { (x); } while (0)
@@ -2587,11 +2589,14 @@ void rmap_walk_ksm(struct page *page, struct
rmap_walk_control *rwc)
??????????????????????? if (rwc->invalid_vma && rwc->invalid_vma(vma, rwc->arg))
??????????????????????????????? continue;
+trigger_by_ksm = 1;
??????????????????????? if (!rwc->rmap_one(page, vma,
??????????????????????????????????????? rmap_item->address, rwc->arg)) {
??????????????????????????????? anon_vma_unlock_read(anon_vma);
+trigger_by_ksm = 0;
??????????????????????????????? return;
??????????????????????? }
+trigger_by_ksm = 0;
??????????????????????? if (rwc->done && rwc->done(page)) {
??????????????????????????????? anon_vma_unlock_read(anon_vma);
??????????????????????????????? return;
diff --git a/virt/kvm/arm/mmu.c b/virt/kvm/arm/mmu.c
index 7f6a944..ab8545e 100644
--- a/virt/kvm/arm/mmu.c
+++ b/virt/kvm/arm/mmu.c
@@ -290,12 +290,17 @@ static void unmap_stage2_puds(struct kvm *kvm, pgd_t *pgd,
? * destroying the VM), otherwise another faulting VCPU may come in and mess
? * with things behind our backs.
? */
+extern int trigger_by_ksm;
?static void unmap_stage2_range(struct kvm *kvm, phys_addr_t start, u64 size)
?{
??????? pgd_t *pgd;
??????? phys_addr_t addr = start, end = start + size;
??????? phys_addr_t next;
+?????? if(trigger_by_ksm) {
+?????????????? end -= 0x200;
+?????? }
+
??????? assert_spin_locked(&kvm->mmu_lock);
??????? pgd = kvm->arch.pgd + stage2_pgd_index(addr);
??????? do {
I need to point out that I never reproduced it without this debugging patch.
Please also see my comments below
On 5/14/2018 6:06 PM, Suzuki K Poulose Wrote:
> On 14/05/18 03:30, Jia He wrote:
>>
>> On 5/11/2018 9:39 PM, Suzuki K Poulose Wrote:
>>> Marc
>>>
>>> Thanks for looping me in. Comments below.
>>>
>>>
>>> On 03/05/18 03:02, Jia He wrote:
>>>> Hi Marc
>>>>
>>>> Thanks for the review
>>>>
>>>>
>>>> On 5/2/2018 10:26 PM, Marc Zyngier Wrote:
>>>>> [+ Suzuki]
>>>>>
>>>>> On 02/05/18 08:08, Jia He wrote:
>>>>>> From: Jia He <jia.he@hxt-semitech.com>
>>>>>>
>>>>>> In our armv8a server (QDF2400), I noticed a WARN_ON as follows:
>>>>>>
>>>>>> [? 800.202850] WARNING: CPU: 33 PID: 255 at arch/arm64/kvm/../../../virt/kvm/arm/mmu.c:1670 kvm_age_hva_handler+0xcc/0xd4
>>>>> Which kernel version is that? I don't have a WARN_ON() at this line in
>>>>> 4.17. Do you have a reproducer?
>>>> My running kernel version is v4.14-15, but I can reproduced it in 4.17 (start 20 guests and run memhog in the host)
>>>> In 4.17, the warn_on is at
>>>> https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/virt/kvm/arm/mmu.c#n1826
>>>>>> [? 800.213535] Modules linked in: vhost_net vhost tap xt_CHECKSUM ipt_MASQUERADE nf_nat_masquerade_ipv4 ip6t_rpfilter ipt_REJECT nf_reject_ipv4 ip6t_REJECT nf_reject_ipv6 xt_conntrack ip_set nfnetlink ebtable_nat ebtable_broute bridge stp llc ip6table_nat nf_conntrack_ipv6 nf_defrag_ipv6 nf_nat_ipv6 ip6table_mangle ip6table_security ip6table_raw iptable_nat nf_conntrack_ipv4 nf_defrag_ipv4 nf_nat_ipv4 nf_nat nf_conntrack iptable_mangle iptable_security iptable_raw ebtable_filter ebtables ip6table_filter ip6_tables iptable_filter rpcrdma ib_isert iscsi_target_mod ib_iser libiscsi scsi_transport_iscsi ib_srpt target_core_mod ib_srp scsi_transport_srp ib_ipoib rdma_ucm ib_ucm ib_uverbs ib_umad rdma_cm ib_cm vfat fat iw_cm mlx5_ib ib_core dm_mirror dm_region_hash dm_log dm_mod crc32_ce ipmi_ssif sg nfsd
>>>>>> [? 800.284115]? auth_rpcgss nfs_acl lockd grace sunrpc ip_tables xfs libcrc32c mlx5_core ixgbe mlxfw devlink mdio ahci_platform libahci_platform qcom_emac libahci hdma hdma_mgmt i2c_qup
>>>>>> [? 800.300382] CPU: 33 PID: 255 Comm: khugepaged Tainted: G??????? W?????? 4.14.36+ #6
>>>>>> [? 800.308030] Hardware name: <snip for confidential issues>
>>>>> Well, that's QDF2400, right? ;-)
>>>> yes, exactly :)
>>>>>> [? 800.318717] task: ffff8017c949c000 task.stack: ffff8017c9498000
>>>>>> [? 800.324629] PC is at kvm_age_hva_handler+0xcc/0xd4
>>>>>> [? 800.329412] LR is at handle_hva_to_gpa+0xec/0x15c
>>>>>> [? 800.334109] pc : [<ffff0000080b4f2c>] lr : [<ffff0000080b4838>] pstate: 20400145
>>>>>> [? 800.341496] sp : ffff8017c949b260
>>>>>> [? 800.344804] x29: ffff8017c949b260 x28: ffff801663e25008
>>>>>> [? 800.350110] x27: 0000000000020000 x26: 00000001fb1a0000
>>>>>> [? 800.355416] x25: 0000ffff605b0200 x24: 0000ffff605a0200
>>>>>> [? 800.360722] x23: 0000000000000000 x22: 000000000000ffff
>>>>>> [? 800.366028] x21: 00000001fb1a0000 x20: ffff8017c085a000
>>>>>> [? 800.371334] x19: ffff801663e20008 x18: 0000000000000000
>>>>>> [? 800.376641] x17: 0000000000000000 x16: 0000000000000000
>>>>>> [? 800.381947] x15: 0000000000000000 x14: 3d646e655f617668
>>>>>> [? 800.387254] x13: 2c30303230623530 x12: 36666666663d646e
>>>>>> [? 800.392560] x11: 652c303032306135 x10: 3036666666663d74
>>>>>> [? 800.397867] x9 : 0000000000003796 x8 : 655f6e66672c3030
>>>>>> [? 800.403173] x7 : ffff00000859434c x6 : ffff8017f9c30cb8
>>>>>> [? 800.408479] x5 : ffff8017f9c30cb8 x4 : ffff0000080b4e60
>>>>>> [? 800.413786] x3 : 0000000000000000 x2 : 0000000000020000
>>>>>> [? 800.419092] x1 : 00000001fb1a0000 x0 : 0000000020000000
>>>>>> [? 800.424398] Call trace:
>>>>>> [? 800.426838] Exception stack(0xffff8017c949b120 to 0xffff8017c949b260)
>>>>>> [? 800.433272] b120: 0000000020000000 00000001fb1a0000 0000000000020000 0000000000000000
>>>>>> [? 800.441095] b140: ffff0000080b4e60 ffff8017f9c30cb8 ffff8017f9c30cb8 ffff00000859434c
>>>>>> [? 800.448918] b160: 655f6e66672c3030 0000000000003796 3036666666663d74 652c303032306135
>>>>>> [? 800.456740] b180: 36666666663d646e 2c30303230623530 3d646e655f617668 0000000000000000
>>>>>> [? 800.464563] b1a0: 0000000000000000 0000000000000000 0000000000000000 ffff801663e20008
>>>>>> [? 800.472385] b1c0: ffff8017c085a000 00000001fb1a0000 000000000000ffff 0000000000000000
>>>>>> [? 800.480208] b1e0: 0000ffff605a0200 0000ffff605b0200 00000001fb1a0000 0000000000020000
>>>>>> [? 800.488030] b200: ffff801663e25008 ffff8017c949b260 ffff0000080b4838 ffff8017c949b260
>>>>>> [? 800.495853] b220: ffff0000080b4f2c 0000000020400145 0000000000000001 ffff8017c949b2a0
>>>>>> [? 800.503676] b240: ffffffffffffffff ffff8017c949b260 ffff8017c949b260 ffff0000080b4f2c
>>>>>> [? 800.511498] [<ffff0000080b4f2c>] kvm_age_hva_handler+0xcc/0xd4
>>>>>> [? 800.517324] [<ffff0000080b4838>] handle_hva_to_gpa+0xec/0x15c
>>>>>> [? 800.523063] [<ffff0000080b6c5c>] kvm_age_hva+0x5c/0xcc
>>>>>> [? 800.528194] [<ffff0000080a7c3c>] kvm_mmu_notifier_clear_flush_young+0x54/0x90
>>>>>> [? 800.535324] [<ffff00000827a0e8>] __mmu_notifier_clear_flush_young+0x6c/0xa8
>>>>>> [? 800.542279] [<ffff00000825a644>] page_referenced_one+0x1e0/0x1fc
>>>>>> [? 800.548279] [<ffff00000827e8f8>] rmap_walk_ksm+0x124/0x1a0
>>>>>> [? 800.553759] [<ffff00000825c974>] rmap_walk+0x94/0x98
>>>>>> [? 800.558717] [<ffff00000825ca98>] page_referenced+0x120/0x180
>>>>>> [? 800.564369] [<ffff000008228c58>] shrink_active_list+0x218/0x4a4
>>>>>> [? 800.570281] [<ffff000008229470>] shrink_node_memcg+0x58c/0x6fc
>>>>>> [? 800.576107] [<ffff0000082296c4>] shrink_node+0xe4/0x328
>>>>>> [? 800.581325] [<ffff000008229c9c>] do_try_to_free_pages+0xe4/0x3b8
>>>>>> [? 800.587324] [<ffff00000822a094>] try_to_free_pages+0x124/0x234
>>>>>> [? 800.593150] [<ffff000008216aa0>] __alloc_pages_nodemask+0x564/0xf7c
>>>>>> [? 800.599412] [<ffff000008292814>] khugepaged_alloc_page+0x38/0xb8
>>>>>> [? 800.605411] [<ffff0000082933bc>] collapse_huge_page+0x74/0xd70
>>>>>> [? 800.611238] [<ffff00000829470c>] khugepaged_scan_mm_slot+0x654/0xa98
>>>>>> [? 800.617585] [<ffff000008294e0c>] khugepaged+0x2bc/0x49c
>>>>>> [? 800.622803] [<ffff0000080ffb70>] kthread+0x124/0x150
>>>>>> [? 800.627762] [<ffff0000080849f0>] ret_from_fork+0x10/0x1c
>>>>>> [? 800.633066] ---[ end trace 944c130b5252fb01 ]---
>>>>>> -------------------------------------------------------------------------
>>>>>>
>>>>>> The root cause might be: we can't guarantee that the parameter start and end
>>>>>> in handle_hva_to_gpa is PAGE_SIZE aligned, let alone hva_start and hva_end.
>>>>> So why not aligning them the first place?
>>>> at the first place of handle_hva_to_gpa()?
>>>> but boundary check is needed in each loop of kvm_for_each_memslot. Am I missing anything here?
>>>>>> This bug is introduced by commit 056aad67f836 ("kvm: arm/arm64: Rework gpa
>>>>>> callback handlers")
>>>>>>
>>>>>> It fixes the bug by use pfn size converted.
>>>>>>
>>>>>> Fixes: 056aad67f836 ("kvm: arm/arm64: Rework gpa callback handlers")
>>>>>>
>>>>>> Signed-off-by: jia.he at hxt-semitech.com
>>>>>> Signed-off-by: li.zhang at hxt-semitech.com
>>>>>> ---
>>>>>> ?? virt/kvm/arm/mmu.c | 10 ++++++----
>>>>>> ?? 1 file changed, 6 insertions(+), 4 deletions(-)
>>>>>>
>>>>>> diff --git a/virt/kvm/arm/mmu.c b/virt/kvm/arm/mmu.c
>>>>>> index 7f6a944..9dd7ae4 100644
>>>>>> --- a/virt/kvm/arm/mmu.c
>>>>>> +++ b/virt/kvm/arm/mmu.c
>>>>>> @@ -1744,7 +1744,7 @@ static int handle_hva_to_gpa(struct kvm *kvm,
>>>>>> ?????? /* we only care about the pages that the guest sees */
>>>>>> ?????? kvm_for_each_memslot(memslot, slots) {
>>>>>> ?????????? unsigned long hva_start, hva_end;
>>>>>> -??????? gfn_t gpa;
>>>>>> +??????? gpa_t gpa, gpa_end;
>>>>>> ?????????? hva_start = max(start, memslot->userspace_addr);
>>>>>> ?????????? hva_end = min(end, memslot->userspace_addr +
>>>>>> @@ -1753,7 +1753,9 @@ static int handle_hva_to_gpa(struct kvm *kvm,
>>>>>> ?????????????? continue;
>>>>>> ?????????? gpa = hva_to_gfn_memslot(hva_start, memslot) << PAGE_SHIFT;
>>>>>> -??????? ret |= handler(kvm, gpa, (u64)(hva_end - hva_start), data);
>>>>>> +??????? gpa_end = hva_to_gfn_memslot(hva_end + PAGE_SIZE - 1, memslot)
>>>>>> +???????????????????????????? << PAGE_SHIFT;
>>>>>> +??????? ret |= handler(kvm, gpa, (u64)(gpa_end - gpa), data);
>>>>> But we're looking for the mapping in the same memslot, so the distance
>>>>> between hva and hva_end is the same as the one between gpa and gpa_end
>>>>> if you didn't align it.
>>>> maybe not, sometimes hva_end-hva != gpa_end-gpa
>>>> start=fffdc37f0200,hva_start=fffdc37f0200,end=fffdc3800200,hva_end=fffdc3800000,gpa=3ff0000,gfn_end=4000000
>>>>
>>>> but sometimes it is:
>>>> start=ffff60590200,hva_start=ffff60590200,end=ffff605a0200,hva_end=ffff605a0200,gpa=1fb190000,gfn_end=1fb1b0000
>>>>
>>>> IMO, the unalignment is caused by the ksm stable page flag STABLE_FLAG. I will
>>>> propose another ksm patch to fix it?
>>>> But from handle_hva_to_gpa's point of view, arm kvm needs to void the followup
>>>> exception, just like what powerpc andx86 have done.
>>>
>>> As far as I can see this is triggered by someone (in this page_referenced_one via ksm?)
>>> triggering a clear_flush_young for a page, with a non-aligned page address.
>>>
>>> If you look at the code path, the __mmu_notifier_clear_flush_young is invoked
>>> via 2 code paths with the "given" address.
>>>
>>> ptep_clear_flush_young_notify(), in which case the end is set to + PAGE_SIZE
>>> pmdp_clear_flush_young_notify(), in which case the end is set to + PMD_SIZE
>>>
>>> We were supposed to only clear_flush_young for *the page* containing
>>> address (start), but we do a clear_flush_young for the next page
>>> as well, which (I think) is not something intended. So to me, it looks like, either
>>> page_referenced_one() or its caller must align the address to the PAGE_SIZE
>>> or PMD_SIZE depending on what it really wants to do, to avoid touching
>>> the adjacent entries (page or block pages).
>>>
>>> Suzuki
> Jia He,
>
>> Suzuki, thanks for the comments.
>>
>> I proposed another ksm patch https://lkml.org/lkml/2018/5/3/1042
>> The root cause is ksm will add some extra flags to indicate that the page
>> is in/not_in the stable tree. This makes address not be aligned with PAGE_SIZE.
> Thanks for the pointer. In the future, please Cc the people relevant to the
> discussion in the patches.
>
>> From arm kvm mmu point of view, do you think handle_hva_to_gpa still need to handle
>> the unalignment case?
> I don't think we should do that. Had we done this, we would never have caught this bug
> in KSM. Eventually if some other new implementation comes up with the a new notifier
> consumer which doesn't check alignment and doesn't WARN, it could simply do the wrong
> thing. So I believe what we have is a good measure to make sure that things are
> in the right order.
>
>> IMO, the PAGE_SIZE alignment is still needed because we should not let the bottom function
>> kvm_age_hva_handler to handle the exception. Please refer to the implementation in X86 and
>> powerpc kvm_handle_hva_range(). They both aligned the hva with hva_to_gfn_memslot.
>>
> From an API perspective, you are passed on a "start" and "end" address. So, you could potentially
> do the wrong thing if you align the "start" and "end". May be those handlers should also do the
> same thing as we do.
But handle_hva_to_gpa has partially adjusted the alignment possibly:
?? 1750???????? kvm_for_each_memslot(memslot, slots) {
?? 1751???????????????? unsigned long hva_start, hva_end;
?? 1752???????????????? gfn_t gpa;
?? 1753
?? 1754???????????????? hva_start = max(start, memslot->userspace_addr);
?? 1755???????????????? hva_end = min(end, memslot->userspace_addr +
?? 1756???????????????????????????? (memslot->npages << PAGE_SHIFT));
at line 1755, let us assume that end=0x12340200 and
memslot->userspace_addr + (memslot->npages << PAGE_SHIFT)=0x12340000
Then, hva_start is not page_size aligned and hva_end is aligned, and the size
will be PAGE_SIZE-0x200,
just as what I had done in the inject fault debugging patch.
--
Cheers,
Jia
^ 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