* [PATCH 0/4] spi: sh-msiof: Multi-slave enhancements
From: Geert Uytterhoeven @ 2017-12-13 19:05 UTC (permalink / raw)
To: Mark Brown, Rob Herring, Mark Rutland
Cc: linux-spi-u79uwXL29TY76Z2rM5mHXA,
devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-renesas-soc-u79uwXL29TY76Z2rM5mHXA, Geert Uytterhoeven
Hi Mark², Rob,
This patch series enhances the Renesas MSIOF SPI controller driver for
multiple SPI slave devices.
The first patch fixes the classical pitfall of writing to configuration
registers from the spi_master.setup() callback, where possible.
The second patch extends support from 1 to 3 native chip selects,
The third patch fixes the use of GPIOs as chip selects on modern
platform using DT instead of board code.
The last patch documents hardware limitations related to chip selects,
to guide board designers and DTS writers.
This has been tested with:
- Three 25LC040 SPI EEPROMs, using GPIO chip selects due to MSIOF
hardware limitations (verified with a logic analyzer),
- Multiple 74HC595 shift registers feeding lots of blinkenlights,
using only native chipselects, only GPIO chip selects, and a healthy
mix of both.
Thanks for your comments!
Geert Uytterhoeven (4):
spi: sh-msiof: Avoid writing to registers from spi_master.setup()
spi: sh-msiof: Extend support to 3 native chip selects
spi: sh-msiof: Implement cs-gpios configuration
spi: sh-msiof: Document hardware limitations related to chip selects
Documentation/devicetree/bindings/spi/sh-msiof.txt | 16 ++-
drivers/spi/spi-sh-msiof.c | 111 +++++++++++++++++----
2 files changed, 107 insertions(+), 20 deletions(-)
--
2.7.4
Gr{oetje,eeting}s,
Geert
--
Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert-Td1EMuHUCqxL1ZNQvxDV9g@public.gmane.org
In personal conversations with technical people, I call myself a hacker. But
when I'm talking to journalists I just say "programmer" or something like that.
-- Linus Torvalds
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* [PATCH 3/3] clocksource/drivers: integrator-ap: parse the chosen node
From: Alexandre Belloni @ 2017-12-13 18:53 UTC (permalink / raw)
To: Rob Herring, Daniel Lezcano
Cc: Nicolas Ferre, Mark Rutland, Thomas Gleixner, devicetree,
linux-kernel, linux-arm-kernel, Alexandre Belloni
In-Reply-To: <20171213185313.20017-1-alexandre.belloni@free-electrons.com>
The driver currently uses aliases to know whether the timer is the
clocksource or the clockevent. Add the /chosen/linux,clocksource and
/chosen/linux,clockevent parsing while keeping backward compatibility.
Signed-off-by: Alexandre Belloni <alexandre.belloni@free-electrons.com>
---
drivers/clocksource/Kconfig | 1 +
drivers/clocksource/timer-integrator-ap.c | 11 +++++++++++
2 files changed, 12 insertions(+)
diff --git a/drivers/clocksource/Kconfig b/drivers/clocksource/Kconfig
index 9a22d1048fcf..053aca99caf7 100644
--- a/drivers/clocksource/Kconfig
+++ b/drivers/clocksource/Kconfig
@@ -212,6 +212,7 @@ config KEYSTONE_TIMER
config INTEGRATOR_AP_TIMER
bool "Integrator-ap timer driver" if COMPILE_TEST
select CLKSRC_MMIO
+ select TIMER_OF
help
Enables support for the Integrator-ap timer.
diff --git a/drivers/clocksource/timer-integrator-ap.c b/drivers/clocksource/timer-integrator-ap.c
index 62d24690ba02..8f38be724aff 100644
--- a/drivers/clocksource/timer-integrator-ap.c
+++ b/drivers/clocksource/timer-integrator-ap.c
@@ -197,6 +197,17 @@ static int __init integrator_ap_timer_init_of(struct device_node *node)
rate = clk_get_rate(clk);
writel(0, base + TIMER_CTRL);
+ if (timer_of_is_clocksource(node))
+ /* The primary timer lacks IRQ, use as clocksource */
+ return integrator_clocksource_init(rate, base);
+
+ if (timer_of_is_clockevent(node)) {
+ /* The secondary timer will drive the clock event */
+ irq = irq_of_parse_and_map(node, 0);
+ return integrator_clockevent_init(rate, base, irq);
+ }
+
+ /* DT ABI compatibility below */
err = of_property_read_string(of_aliases,
"arm,timer-primary", &path);
if (err) {
--
2.15.1
^ permalink raw reply related
* [PATCH 2/3] clocksource/drivers: timer-of: parse the chosen node
From: Alexandre Belloni @ 2017-12-13 18:53 UTC (permalink / raw)
To: Rob Herring, Daniel Lezcano
Cc: Nicolas Ferre, Mark Rutland, Thomas Gleixner,
devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-kernel-u79uwXL29TY76Z2rM5mHXA,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
Alexandre Belloni
In-Reply-To: <20171213185313.20017-1-alexandre.belloni-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org>
Add a way for drivers to know whether the timer they are currently handling
is a clocksource or a clockevent.
Signed-off-by: Alexandre Belloni <alexandre.belloni-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org>
---
drivers/clocksource/timer-of.c | 22 ++++++++++++++++++++++
drivers/clocksource/timer-of.h | 3 +++
2 files changed, 25 insertions(+)
diff --git a/drivers/clocksource/timer-of.c b/drivers/clocksource/timer-of.c
index a31990408153..71680eacd390 100644
--- a/drivers/clocksource/timer-of.c
+++ b/drivers/clocksource/timer-of.c
@@ -195,3 +195,25 @@ void __init timer_of_cleanup(struct timer_of *to)
if (to->flags & TIMER_OF_BASE)
timer_base_exit(&to->of_base);
}
+
+int __init timer_of_is_type(struct device_node *np, char *type)
+{
+ struct device_node *node, *timer;
+
+ if (!of_chosen)
+ return 0;
+
+ node = of_get_child_by_name(of_chosen, type);
+ if (!node)
+ return 0;
+
+ timer = of_parse_phandle(node, "timer", 0);
+ of_node_put(node);
+ if (!timer)
+ return 0;
+
+ if (timer == np)
+ return 1;
+
+ return 0;
+}
diff --git a/drivers/clocksource/timer-of.h b/drivers/clocksource/timer-of.h
index 3f708f1be43d..24923cfe748d 100644
--- a/drivers/clocksource/timer-of.h
+++ b/drivers/clocksource/timer-of.h
@@ -70,4 +70,7 @@ extern int __init timer_of_init(struct device_node *np,
extern void __init timer_of_cleanup(struct timer_of *to);
+extern int __init timer_of_is_type(struct device_node *np, char *type);
+#define timer_of_is_clocksource(np) timer_of_is_type(np, "linux,clocksource")
+#define timer_of_is_clockevent(np) timer_of_is_type(np, "linux,clockevent")
#endif
--
2.15.1
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply related
* [PATCH 1/3] dt-bindings: chosen: Add clocksource and clockevent selection
From: Alexandre Belloni @ 2017-12-13 18:53 UTC (permalink / raw)
To: Rob Herring, Daniel Lezcano
Cc: Nicolas Ferre, Mark Rutland, Thomas Gleixner,
devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-kernel-u79uwXL29TY76Z2rM5mHXA,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
Alexandre Belloni
In-Reply-To: <20171213185313.20017-1-alexandre.belloni-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org>
The clocksource and clockevent timer are probed early in the boot process.
At that time it is difficult for linux to know whether a particular timer
can be used as the clocksource or the clockevent or by another driver,
especially when they are all identical or have similar features.
Until now, multiple strategies have been used to solve that:
- use Kconfig option as MXC_USE_EPIT or ATMEL_TCB_CLKSRC_BLOCK
- use a kernel parameter as the "clocksource" early_param in mach-omap2
- registering the first seen timer as a clockevent and the second one as
a clocksource as in rk_timer_init or dw_apb_timer_init
Add a linux,clocksource and a linux,clockevent node in chosen with a timer
property pointing to the timer to use. Other properties, like the targeted
precision may be added later.
Signed-off-by: Alexandre Belloni <alexandre.belloni-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org>
---
Documentation/devicetree/bindings/chosen.txt | 20 ++++++++++++++++++++
1 file changed, 20 insertions(+)
diff --git a/Documentation/devicetree/bindings/chosen.txt b/Documentation/devicetree/bindings/chosen.txt
index e3b13ea7d2ae..c7ee3ecb5276 100644
--- a/Documentation/devicetree/bindings/chosen.txt
+++ b/Documentation/devicetree/bindings/chosen.txt
@@ -120,3 +120,23 @@ e.g.
While this property does not represent a real hardware, the address
and the size are expressed in #address-cells and #size-cells,
respectively, of the root node.
+
+linux,clocksource and linux,clockevent
+--------------------------------------
+
+Those nodes have a timer property. This property is a phandle to the timer to be
+chosen as the clocksource or clockevent. This is only useful when the platform
+has multiple identical timers and it is not possible to let linux make the
+correct choice.
+
+/ {
+ chosen {
+ linux,clocksource {
+ timer = <&timer0>;
+ };
+
+ linux,clockevent {
+ timer = <&timer1>;
+ };
+ };
+};
--
2.15.1
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply related
* [PATCH 0/3] clocksource/drivers: introduce DT based selection
From: Alexandre Belloni @ 2017-12-13 18:53 UTC (permalink / raw)
To: Rob Herring, Daniel Lezcano
Cc: Nicolas Ferre, Mark Rutland, Thomas Gleixner,
devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-kernel-u79uwXL29TY76Z2rM5mHXA,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
Alexandre Belloni
Hi,
Currently, many drivers implement their own strategy when trying to find
which timer to use as the clocksource or the clockevent.
The main issue is that this selection is happen early in the boot
process and the kernel doesn't always have all the information to take
that decision.
So we end up with suboptimal solutions, especially in a multiplatform
kernel setting, as the MXC_USE_EPIT or ATMEL_TCB_CLKSRC_BLOCK kernel
config option.
There is also the clocksource kernel parameter only implemented in
mach-omap2.
Other drivers are registering the first seen timer as a clockevent, the
other one as a clocksource.
Also, this will help in the goal of separating clocksource and
clockevent drivers (see 376bc27150f180d9f5eddec6a14117780177589d)
Patch 1 documents the binding, patch 2 implements the parsing of the
chosen node and finally, patch 3 makes use of the parsing in a driver to
give an overview of how it is working.
Alexandre Belloni (3):
dt-bindings: chosen: Add clocksource and clockevent selection
clocksource/drivers: timer-of: parse the chosen node
clocksource/drivers: integrator-ap: parse the chosen node
Documentation/devicetree/bindings/chosen.txt | 20 ++++++++++++++++++++
drivers/clocksource/Kconfig | 1 +
drivers/clocksource/timer-integrator-ap.c | 11 +++++++++++
drivers/clocksource/timer-of.c | 22 ++++++++++++++++++++++
drivers/clocksource/timer-of.h | 3 +++
5 files changed, 57 insertions(+)
--
2.15.1
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* Re: [PATCH v4 05/12] [media] cxd2880: Add tuner part of the driver
From: Mauro Carvalho Chehab @ 2017-12-13 18:40 UTC (permalink / raw)
To: Yasunari.Takiguchi-7U/KSKJipcs
Cc: linux-kernel-u79uwXL29TY76Z2rM5mHXA,
devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-media-u79uwXL29TY76Z2rM5mHXA,
tbird20d-Re5JQEeQqe8AvxtiuMwx3w,
frowand.list-Re5JQEeQqe8AvxtiuMwx3w, Masayuki Yamamoto,
Hideki Nozawa, Kota Yonezawa, Toshihiko Matsumoto,
Satoshi Watanabe
In-Reply-To: <20171013060725.21439-1-Yasunari.Takiguchi-7U/KSKJipcs@public.gmane.org>
Em Fri, 13 Oct 2017 15:07:25 +0900
<Yasunari.Takiguchi-7U/KSKJipcs@public.gmane.org> escreveu:
> From: Yasunari Takiguchi <Yasunari.Takiguchi-7U/KSKJipcs@public.gmane.org>
>
> This part of the driver has the main routines to handle
> the tuner and demodulator functionality. The tnrdmd_mon.* files
> have monitor functions for the driver.
> This is part of the Sony CXD2880 DVB-T2/T tuner + demodulator driver.
>
> Signed-off-by: Yasunari Takiguchi <Yasunari.Takiguchi-7U/KSKJipcs@public.gmane.org>
> Signed-off-by: Masayuki Yamamoto <Masayuki.Yamamoto-7U/KSKJipcs@public.gmane.org>
> Signed-off-by: Hideki Nozawa <Hideki.Nozawa-7U/KSKJipcs@public.gmane.org>
> Signed-off-by: Kota Yonezawa <Kota.Yonezawa-7U/KSKJipcs@public.gmane.org>
> Signed-off-by: Toshihiko Matsumoto <Toshihiko.Matsumoto-7U/KSKJipcs@public.gmane.org>
> Signed-off-by: Satoshi Watanabe <Satoshi.C.Watanabe-7U/KSKJipcs@public.gmane.org>
> ---
>
> [Change list]
> Changes in V4
> drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd.c
> -used over 80 columns limit, it makes fine to read codes
> -removed unnecessary initialization at variable declaration
> -modified how to write consecutive registers
> -removed unnecessary brace {}
> drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd.h
> -adjusted of indent spaces of macro
> drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_driver_version.h
> -updated version information
> drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_mon.c
> -removed unnecessary brace {}
>
> Changes in V3
> drivers/media/dvb-frontends/cxd2880/cxd2880_dtv.h
> -removed code relevant to ISDB-T
> drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd.c
> -removed unnecessary cast
> -removed code relevant to ISDB-T
> -changed CXD2880_SLEEP to usleep_range
> -changed cxd2880_memset to memset
> -changed cxd2880_atomic_set to atomic_set
> -modified return code
> -modified coding style of if()
> -changed to use const values at writing a lot of registers
> with a command.
> -changed hexadecimal code to lower case.
> -adjusted of indent spaces
> drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd.h
> -removed code relevant to ISDB-T
> -changed cxd2880_atomic struct to atomic_t
> -modified return code
> -changed hexadecimal code to lower case.
> drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_driver_version.h
> -updated version information
> drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_mon.c
> -changed CXD2880_SLEEP to usleep_range
> -removed unnecessary cast
> -modified return code
> -modified coding style of if()
> -changed hexadecimal code to lower case.
> drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_mon.h
> -modified return code
>
> Changes in V2
> drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_driver_version.h
> -updated version information
>
> drivers/media/dvb-frontends/cxd2880/cxd2880_dtv.h | 46 +
> .../media/dvb-frontends/cxd2880/cxd2880_tnrdmd.c | 3687 ++++++++++++++++++++
> .../media/dvb-frontends/cxd2880/cxd2880_tnrdmd.h | 391 +++
> .../cxd2880/cxd2880_tnrdmd_driver_version.h | 29 +
> .../dvb-frontends/cxd2880/cxd2880_tnrdmd_mon.c | 218 ++
> .../dvb-frontends/cxd2880/cxd2880_tnrdmd_mon.h | 52 +
> 6 files changed, 4423 insertions(+)
> create mode 100644 drivers/media/dvb-frontends/cxd2880/cxd2880_dtv.h
> create mode 100644 drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd.c
> create mode 100644 drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd.h
> create mode 100644 drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_driver_version.h
> create mode 100644 drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_mon.c
> create mode 100644 drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_mon.h
>
> diff --git a/drivers/media/dvb-frontends/cxd2880/cxd2880_dtv.h b/drivers/media/dvb-frontends/cxd2880/cxd2880_dtv.h
> new file mode 100644
> index 000000000000..2d35d3990060
> --- /dev/null
> +++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_dtv.h
> @@ -0,0 +1,46 @@
> +/*
> + * cxd2880_dtv.h
> + * Sony CXD2880 DVB-T2/T tuner + demodulator driver
> + * DTV related definitions
> + *
> + * Copyright (C) 2016, 2017 Sony Semiconductor Solutions Corporation
> + *
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms of the GNU General Public License as published by the
> + * Free Software Foundation; version 2 of the License.
> + *
> + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
> + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
> + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
> + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
> + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
> + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
> + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
> + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
> + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
> + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
> + *
> + * You should have received a copy of the GNU General Public License along
> + * with this program; if not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +#ifndef CXD2880_DTV_H
> +#define CXD2880_DTV_H
> +
> +enum cxd2880_dtv_sys {
> + CXD2880_DTV_SYS_UNKNOWN,
> + CXD2880_DTV_SYS_DVBT,
> + CXD2880_DTV_SYS_DVBT2,
> + CXD2880_DTV_SYS_ANY
> +};
> +
> +enum cxd2880_dtv_bandwidth {
> + CXD2880_DTV_BW_UNKNOWN = 0,
> + CXD2880_DTV_BW_1_7_MHZ = 1,
> + CXD2880_DTV_BW_5_MHZ = 5,
> + CXD2880_DTV_BW_6_MHZ = 6,
> + CXD2880_DTV_BW_7_MHZ = 7,
> + CXD2880_DTV_BW_8_MHZ = 8
> +};
> +
> +#endif
> diff --git a/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd.c b/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd.c
> new file mode 100644
> index 000000000000..17fd0bc6249c
> --- /dev/null
> +++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd.c
> @@ -0,0 +1,3687 @@
> +/*
> + * cxd2880_tnrdmd.c
> + * Sony CXD2880 DVB-T2/T tuner + demodulator driver
> + * common control functions
> + *
> + * Copyright (C) 2016, 2017 Sony Semiconductor Solutions Corporation
> + *
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms of the GNU General Public License as published by the
> + * Free Software Foundation; version 2 of the License.
> + *
> + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
> + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
> + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
> + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
> + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
> + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
> + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
> + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
> + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
> + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
> + *
> + * You should have received a copy of the GNU General Public License along
> + * with this program; if not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +#include "dvb_frontend.h"
> +#include "cxd2880_common.h"
> +#include "cxd2880_tnrdmd.h"
> +#include "cxd2880_tnrdmd_mon.h"
> +#include "cxd2880_tnrdmd_dvbt.h"
> +#include "cxd2880_tnrdmd_dvbt2.h"
> +
> +static const struct cxd2880_reg_value p_init1_seq[] = {
> + {0x11, 0x16}, {0x00, 0x10},
> +};
> +
> +static const struct cxd2880_reg_value rf_init1_seq1[] = {
> + {0x4f, 0x18}, {0x61, 0x00}, {0x71, 0x00}, {0x9d, 0x01},
> + {0x7d, 0x02}, {0x8f, 0x01}, {0x8b, 0xc6}, {0x9a, 0x03},
> + {0x1c, 0x00},
> +};
> +
> +static const struct cxd2880_reg_value rf_init1_seq2[] = {
> + {0xb9, 0x07}, {0x33, 0x01}, {0xc1, 0x01}, {0xc4, 0x1e},
> +};
> +
> +static const struct cxd2880_reg_value rf_init1_seq3[] = {
> + {0x00, 0x10}, {0x51, 0x01}, {0xc5, 0x07}, {0x00, 0x11},
> + {0x70, 0xe9}, {0x76, 0x0a}, {0x78, 0x32}, {0x7a, 0x46},
> + {0x7c, 0x86}, {0x7e, 0xa4}, {0x00, 0x10}, {0xe1, 0x01},
> +};
> +
> +static const struct cxd2880_reg_value rf_init1_seq4[] = {
> + {0x15, 0x00}, {0x00, 0x16}
> +};
> +
> +static const struct cxd2880_reg_value rf_init1_seq5[] = {
> + {0x00, 0x00}, {0x25, 0x00}
> +};
> +
> +static const struct cxd2880_reg_value rf_init1_seq6[] = {
> + {0x02, 0x00}, {0x00, 0x00}, {0x21, 0x01}, {0x00, 0xe1},
> + {0x8f, 0x16}, {0x67, 0x60}, {0x6a, 0x0f}, {0x6c, 0x17}
> +};
> +
> +static const struct cxd2880_reg_value rf_init1_seq7[] = {
> + {0x00, 0xe2}, {0x41, 0xa0}, {0x4b, 0x68}, {0x00, 0x00},
> + {0x21, 0x00}, {0x10, 0x01},
> +};
> +
> +static const struct cxd2880_reg_value rf_init1_seq8[] = {
> + {0x00, 0x10}, {0x25, 0x01},
> +};
> +
> +static const struct cxd2880_reg_value rf_init1_seq9[] = {
> + {0x00, 0x10}, {0x14, 0x01}, {0x00, 0x00}, {0x26, 0x00},
> +};
> +
> +static const struct cxd2880_reg_value rf_init2_seq1[] = {
> + {0x00, 0x14}, {0x1b, 0x01},
> +};
> +
> +static const struct cxd2880_reg_value rf_init2_seq2[] = {
> + {0x00, 0x00}, {0x21, 0x01}, {0x00, 0xe1}, {0xd3, 0x00},
> + {0x00, 0x00}, {0x21, 0x00},
> +};
> +
> +static const struct cxd2880_reg_value x_tune1_seq1[] = {
> + {0x00, 0x00}, {0x10, 0x01},
> +};
> +
> +static const struct cxd2880_reg_value x_tune1_seq2[] = {
> + {0x62, 0x00}, {0x00, 0x15},
> +};
> +
> +static const struct cxd2880_reg_value x_tune2_seq1[] = {
> + {0x00, 0x1a}, {0x29, 0x01},
> +};
> +
> +static const struct cxd2880_reg_value x_tune2_seq2[] = {
> + {0x62, 0x01}, {0x00, 0x11}, {0x2d, 0x00}, {0x2f, 0x00},
> +};
> +
> +static const struct cxd2880_reg_value x_tune2_seq3[] = {
> + {0x00, 0x00}, {0x10, 0x00}, {0x21, 0x01},
> +};
> +
> +static const struct cxd2880_reg_value x_tune2_seq4[] = {
> + {0x00, 0xe1}, {0x8a, 0x87},
> +};
> +
> +static const struct cxd2880_reg_value x_tune2_seq5[] = {
> + {0x00, 0x00}, {0x21, 0x00},
> +};
> +
> +static const struct cxd2880_reg_value x_tune3_seq[] = {
> + {0x00, 0x00}, {0x21, 0x01}, {0x00, 0xe2}, {0x41, 0xa0},
> + {0x00, 0x00}, {0x21, 0x00}, {0xfe, 0x01},
> +};
> +
> +static const struct cxd2880_reg_value x_tune4_seq[] = {
> + {0x00, 0x00}, {0xfe, 0x01},
> +};
> +
> +static const struct cxd2880_reg_value x_sleep1_seq[] = {
> + {0x00, 0x00}, {0x57, 0x03},
> +};
> +
> +static const struct cxd2880_reg_value x_sleep2_seq1[] = {
> + {0x00, 0x2d}, {0xb1, 0x01},
> +};
> +
> +static const struct cxd2880_reg_value x_sleep2_seq2[] = {
> + {0x00, 0x10}, {0xf4, 0x00}, {0xf3, 0x00}, {0xf2, 0x00},
> + {0xf1, 0x00}, {0xf0, 0x00}, {0xef, 0x00},
> +};
> +
> +static const struct cxd2880_reg_value x_sleep3_seq[] = {
> + {0x00, 0x00}, {0xfd, 0x00},
> +};
> +
> +static const struct cxd2880_reg_value x_sleep4_seq[] = {
> + {0x00, 0x00}, {0x21, 0x01}, {0x00, 0xe2}, {0x41, 0x00},
> + {0x00, 0x00}, {0x21, 0x00},
> +};
> +
> +static const struct cxd2880_reg_value spll_reset_seq1[] = {
> + {0x00, 0x10}, {0x29, 0x01}, {0x28, 0x01}, {0x27, 0x01},
> + {0x26, 0x01},
> +};
> +
> +static const struct cxd2880_reg_value spll_reset_seq2[] = {
> + {0x00, 0x00}, {0x10, 0x00},
> +};
> +
> +static const struct cxd2880_reg_value spll_reset_seq3[] = {
> + {0x00, 0x00}, {0x27, 0x00}, {0x22, 0x01},
> +};
> +
> +static const struct cxd2880_reg_value spll_reset_seq4[] = {
> + {0x00, 0x00}, {0x27, 0x01},
> +};
> +
> +static const struct cxd2880_reg_value spll_reset_seq5[] = {
> + {0x00, 0x00}, {0x10, 0x01},
> +};
> +
> +static const struct cxd2880_reg_value t_power_x_seq1[] = {
> + {0x00, 0x10}, {0x29, 0x01}, {0x28, 0x01}, {0x27, 0x01},
> +};
> +
> +static const struct cxd2880_reg_value t_power_x_seq2[] = {
> + {0x00, 0x00}, {0x10, 0x00},
> +};
> +
> +static const struct cxd2880_reg_value t_power_x_seq3[] = {
> + {0x00, 0x00}, {0x27, 0x00}, {0x25, 0x01},
> +};
> +
> +static const struct cxd2880_reg_value t_power_x_seq4[] = {
> + {0x00, 0x00}, {0x2a, 0x00},
> +};
> +
> +static const struct cxd2880_reg_value t_power_x_seq5[] = {
> + {0x00, 0x00}, {0x25, 0x00},
> +};
> +
> +static const struct cxd2880_reg_value t_power_x_seq6[] = {
> + {0x00, 0x00}, {0x27, 0x01},
> +};
> +
> +static const struct cxd2880_reg_value t_power_x_seq7[] = {
> + {0x00, 0x00}, {0x10, 0x01},
> +};
> +
> +static const struct cxd2880_reg_value set_ts_pin_seq[] = {
> + {0x50, 0x3f}, {0x52, 0x1f},
> +
> +};
> +
> +static const struct cxd2880_reg_value set_ts_output_seq1[] = {
> + {0x00, 0x00}, {0x52, 0x00},
> +};
> +
> +static const struct cxd2880_reg_value set_ts_output_seq2[] = {
> + {0x00, 0x00}, {0xc3, 0x00},
> +
> +};
> +
> +static const struct cxd2880_reg_value set_ts_output_seq3[] = {
> + {0x00, 0x00}, {0xc3, 0x01},
> +
> +};
> +
> +static const struct cxd2880_reg_value set_ts_output_seq4[] = {
> + {0x00, 0x00}, {0x52, 0x1f},
> +
> +};
> +
> +static int p_init1(struct cxd2880_tnrdmd *tnr_dmd)
> +{
> + u8 data = 0;
> + int ret;
> +
> + if (!tnr_dmd)
> + return -EINVAL;
> +
> + ret = tnr_dmd->io->write_reg(tnr_dmd->io,
> + CXD2880_IO_TGT_SYS,
> + 0x00, 0x00);
> + if (ret)
> + return ret;
> +
> + if ((tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SINGLE) ||
> + (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN)) {
> + switch (tnr_dmd->create_param.ts_output_if) {
> + case CXD2880_TNRDMD_TSOUT_IF_TS:
> + data = 0x00;
> + break;
> + case CXD2880_TNRDMD_TSOUT_IF_SPI:
> + data = 0x01;
> + break;
> + case CXD2880_TNRDMD_TSOUT_IF_SDIO:
> + data = 0x02;
> + break;
> + default:
> + return -EINVAL;
> + }
> + ret = tnr_dmd->io->write_reg(tnr_dmd->io,
> + CXD2880_IO_TGT_SYS,
> + 0x10, data);
> + if (ret)
> + return ret;
> + }
> +
> + ret = cxd2880_io_write_multi_regs(tnr_dmd->io,
> + CXD2880_IO_TGT_SYS,
> + p_init1_seq,
> + ARRAY_SIZE(p_init1_seq));
> + if (ret)
> + return ret;
> +
> + switch (tnr_dmd->chip_id) {
> + case CXD2880_TNRDMD_CHIP_ID_CXD2880_ES1_0X:
> + data = 0x1a;
> + break;
> + case CXD2880_TNRDMD_CHIP_ID_CXD2880_ES1_11:
> + data = 0x16;
> + break;
> + default:
> + return -EOPNOTSUPP;
Hmm... this error code is used on DVB to indicate that an ioctl
is not supported. If a parameter is not valid, the right error
code is -EINVAL.
Please see:
https://linuxtv.org/downloads/v4l-dvb-apis-new/uapi/gen-errors.html
for a (non-complete) list of error codes. The same note applies to other
similar checks.
> + }
> +
> + ret = tnr_dmd->io->write_reg(tnr_dmd->io,
> + CXD2880_IO_TGT_SYS,
> + 0x10, data);
> + if (ret)
> + return ret;
> +
> + if (tnr_dmd->create_param.en_internal_ldo)
> + data = 0x01;
> + else
> + data = 0x00;
> +
> + ret = tnr_dmd->io->write_reg(tnr_dmd->io,
> + CXD2880_IO_TGT_SYS,
> + 0x11, data);
> + if (ret)
> + return ret;
> + ret = tnr_dmd->io->write_reg(tnr_dmd->io,
> + CXD2880_IO_TGT_SYS,
> + 0x13, data);
> + if (ret)
> + return ret;
> +
> + ret = tnr_dmd->io->write_reg(tnr_dmd->io,
> + CXD2880_IO_TGT_SYS,
> + 0x00, 0x00);
> + if (ret)
> + return ret;
> + ret = tnr_dmd->io->write_reg(tnr_dmd->io,
> + CXD2880_IO_TGT_SYS,
> + 0x12, data);
> + if (ret)
> + return ret;
> +
> + ret = tnr_dmd->io->write_reg(tnr_dmd->io,
> + CXD2880_IO_TGT_SYS,
> + 0x00, 0x10);
> + if (ret)
> + return ret;
> +
> + switch (tnr_dmd->chip_id) {
> + case CXD2880_TNRDMD_CHIP_ID_CXD2880_ES1_0X:
> + data = 0x01;
> + break;
> + case CXD2880_TNRDMD_CHIP_ID_CXD2880_ES1_11:
> + data = 0x00;
> + break;
> + default:
> + return -EOPNOTSUPP;
> + }
> +
> + ret = tnr_dmd->io->write_reg(tnr_dmd->io,
> + CXD2880_IO_TGT_SYS,
> + 0x69, data);
> + if (ret)
> + return ret;
> +
> + return 0;
> +}
> +
> +static int p_init2(struct cxd2880_tnrdmd *tnr_dmd)
> +{
> + u8 data[6] = { 0 };
> + int ret;
> +
> + if (!tnr_dmd)
> + return -EINVAL;
> +
> + ret = tnr_dmd->io->write_reg(tnr_dmd->io,
> + CXD2880_IO_TGT_SYS,
> + 0x00, 0x00);
> + if (ret)
> + return ret;
> + data[0] = tnr_dmd->create_param.xosc_cap;
> + data[1] = tnr_dmd->create_param.xosc_i;
> + switch (tnr_dmd->create_param.xtal_share_type) {
> + case CXD2880_TNRDMD_XTAL_SHARE_NONE:
> + data[2] = 0x01;
> + data[3] = 0x00;
> + break;
> + case CXD2880_TNRDMD_XTAL_SHARE_EXTREF:
> + data[2] = 0x00;
> + data[3] = 0x00;
> + break;
> + case CXD2880_TNRDMD_XTAL_SHARE_MASTER:
> + data[2] = 0x01;
> + data[3] = 0x01;
> + break;
> + case CXD2880_TNRDMD_XTAL_SHARE_SLAVE:
> + data[2] = 0x00;
> + data[3] = 0x01;
> + break;
> + default:
> + return -EINVAL;
> + }
> + data[4] = 0x06;
> + data[5] = 0x00;
> + ret = tnr_dmd->io->write_regs(tnr_dmd->io,
> + CXD2880_IO_TGT_SYS,
> + 0x13,
> + data,
> + 6);
> + if (ret)
> + return ret;
> +
> + return 0;
> +}
> +
> +static int p_init3(struct cxd2880_tnrdmd *tnr_dmd)
> +{
> + u8 data[2] = { 0 };
> + int ret;
> +
> + if (!tnr_dmd)
> + return -EINVAL;
> +
> + ret = tnr_dmd->io->write_reg(tnr_dmd->io,
> + CXD2880_IO_TGT_SYS,
> + 0x00, 0x00);
> + if (ret)
> + return ret;
> +
> + switch (tnr_dmd->diver_mode) {
> + case CXD2880_TNRDMD_DIVERMODE_SINGLE:
> + data[0] = 0x00;
> + break;
> + case CXD2880_TNRDMD_DIVERMODE_MAIN:
> + data[0] = 0x03;
> + break;
> + case CXD2880_TNRDMD_DIVERMODE_SUB:
> + data[0] = 0x02;
> + break;
> + default:
> + return -EINVAL;
> + }
> +
> + data[1] = 0x01;
> + ret = tnr_dmd->io->write_regs(tnr_dmd->io,
> + CXD2880_IO_TGT_SYS,
> + 0x1f, data, 2);
> + if (ret)
> + return ret;
> +
> + return 0;
> +}
> +
> +static int rf_init1(struct cxd2880_tnrdmd *tnr_dmd)
> +{
> + u8 data[8] = { 0 };
> + static const u8 rf_init1_cdata1[40] = {
> + 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
> + 0x05, 0x05, 0x04, 0x04, 0x04, 0x03, 0x03,
> + 0x03, 0x04, 0x04, 0x05, 0x05, 0x05, 0x02,
> + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
> + 0x02, 0x03, 0x02, 0x01, 0x01, 0x01, 0x02,
> + 0x02, 0x03, 0x04, 0x04, 0x04
> + };
> +
> + static const u8 rf_init1_cdata2[5] = {0xff, 0x00, 0x00, 0x00, 0x00};
> + static const u8 rf_init1_cdata3[80] = {
> + 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00,
> + 0x01, 0x00, 0x02, 0x00, 0x63, 0x00, 0x00,
> + 0x00, 0x03, 0x00, 0x04, 0x00, 0x04, 0x00,
> + 0x06, 0x00, 0x06, 0x00, 0x08, 0x00, 0x09,
> + 0x00, 0x0b, 0x00, 0x0b, 0x00, 0x0d, 0x00,
> + 0x0d, 0x00, 0x0f, 0x00, 0x0f, 0x00, 0x0f,
> + 0x00, 0x10, 0x00, 0x79, 0x00, 0x00, 0x00,
> + 0x02, 0x00, 0x00, 0x00, 0x03, 0x00, 0x01,
> + 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00,
> + 0x04, 0x00, 0x04, 0x00, 0x06, 0x00, 0x05,
> + 0x00, 0x07, 0x00, 0x07, 0x00, 0x08, 0x00,
> + 0x0a, 0x03, 0xe0
> + };
> +
> + static const u8 rf_init1_cdata4[8] = {
> + 0x20, 0x20, 0x30, 0x41, 0x50, 0x5f, 0x6f, 0x80
> + };
> +
> + static const u8 rf_init1_cdata5[50] = {
> + 0x00, 0x09, 0x00, 0x08, 0x00, 0x07, 0x00,
> + 0x06, 0x00, 0x05, 0x00, 0x03, 0x00, 0x02,
> + 0x00, 0x00, 0x00, 0x78, 0x00, 0x00, 0x00,
> + 0x06, 0x00, 0x08, 0x00, 0x08, 0x00, 0x0c,
> + 0x00, 0x0c, 0x00, 0x0d, 0x00, 0x0f, 0x00,
> + 0x0e, 0x00, 0x0e, 0x00, 0x10, 0x00, 0x0f,
> + 0x00, 0x0e, 0x00, 0x10, 0x00, 0x0f, 0x00,
> + 0x0e
> + };
> +
> + u8 addr = 0;
> + int ret;
> +
> + if (!tnr_dmd)
> + return -EINVAL;
> +
> + ret = tnr_dmd->io->write_reg(tnr_dmd->io,
> + CXD2880_IO_TGT_SYS,
> + 0x00, 0x00);
> + if (ret)
> + return ret;
> + data[0] = 0x01;
> + data[1] = 0x00;
> + data[2] = 0x01;
> + ret = tnr_dmd->io->write_regs(tnr_dmd->io,
> + CXD2880_IO_TGT_SYS,
> + 0x21, data, 3);
> + if (ret)
> + return ret;
> +
> + ret = tnr_dmd->io->write_reg(tnr_dmd->io,
> + CXD2880_IO_TGT_SYS,
> + 0x00, 0x10);
> + if (ret)
> + return ret;
> + data[0] = 0x01;
> + data[1] = 0x01;
> + ret = tnr_dmd->io->write_regs(tnr_dmd->io,
> + CXD2880_IO_TGT_SYS,
> + 0x17, data, 2);
> + if (ret)
> + return ret;
> +
> + if (tnr_dmd->create_param.stationary_use) {
> + ret = tnr_dmd->io->write_reg(tnr_dmd->io,
> + CXD2880_IO_TGT_SYS,
> + 0x1a, 0x06);
> + if (ret)
> + return ret;
> + }
> +
> + ret = cxd2880_io_write_multi_regs(tnr_dmd->io,
> + CXD2880_IO_TGT_SYS,
> + rf_init1_seq1,
> + ARRAY_SIZE(rf_init1_seq1));
> + if (ret)
> + return ret;
> +
> + data[0] = 0x00;
> + if ((tnr_dmd->create_param.is_cxd2881gg) &&
> + (tnr_dmd->create_param.xtal_share_type ==
> + CXD2880_TNRDMD_XTAL_SHARE_SLAVE))
> + data[1] = 0x00;
> + else
> + data[1] = 0x1f;
> + data[2] = 0x0a;
> + ret = tnr_dmd->io->write_regs(tnr_dmd->io,
> + CXD2880_IO_TGT_SYS,
> + 0xb5, data, 3);
> + if (ret)
> + return ret;
> +
> + ret = cxd2880_io_write_multi_regs(tnr_dmd->io,
> + CXD2880_IO_TGT_SYS,
> + rf_init1_seq2,
> + ARRAY_SIZE(rf_init1_seq2));
> + if (ret)
> + return ret;
> +
> + if (tnr_dmd->chip_id == CXD2880_TNRDMD_CHIP_ID_CXD2880_ES1_0X) {
> + data[0] = 0x34;
> + data[1] = 0x2c;
> + } else {
> + data[0] = 0x2f;
> + data[1] = 0x25;
> + }
> + data[2] = 0x15;
> + data[3] = 0x19;
> + data[4] = 0x1b;
> + data[5] = 0x15;
> + data[6] = 0x19;
> + data[7] = 0x1b;
> + ret = tnr_dmd->io->write_regs(tnr_dmd->io,
> + CXD2880_IO_TGT_SYS,
> + 0xd9, data, 8);
> + if (ret)
> + return ret;
> +
> + ret = tnr_dmd->io->write_reg(tnr_dmd->io,
> + CXD2880_IO_TGT_SYS,
> + 0x00, 0x11);
> + if (ret)
> + return ret;
> + data[0] = 0x6c;
> + data[1] = 0x10;
> + data[2] = 0xa6;
> + ret = tnr_dmd->io->write_regs(tnr_dmd->io,
> + CXD2880_IO_TGT_SYS,
> + 0x44, data, 3);
> + if (ret)
> + return ret;
> + data[0] = 0x16;
> + data[1] = 0xa8;
> + ret = tnr_dmd->io->write_regs(tnr_dmd->io,
> + CXD2880_IO_TGT_SYS,
> + 0x50, data, 2);
> + if (ret)
> + return ret;
> + data[0] = 0x00;
> + data[1] = 0x22;
> + data[2] = 0x00;
> + data[3] = 0x88;
> + ret = tnr_dmd->io->write_regs(tnr_dmd->io,
> + CXD2880_IO_TGT_SYS,
> + 0x62, data, 4);
> + if (ret)
> + return ret;
> + ret = tnr_dmd->io->write_reg(tnr_dmd->io,
> + CXD2880_IO_TGT_SYS,
> + 0x74, 0x75);
> + if (ret)
> + return ret;
> + ret = tnr_dmd->io->write_regs(tnr_dmd->io,
> + CXD2880_IO_TGT_SYS,
> + 0x7f, rf_init1_cdata1, 40);
> + if (ret)
> + return ret;
> +
> + ret = tnr_dmd->io->write_reg(tnr_dmd->io,
> + CXD2880_IO_TGT_SYS,
> + 0x00, 0x16);
> + if (ret)
> + return ret;
> + data[0] = 0x00;
> + data[1] = 0x71;
> + ret = tnr_dmd->io->write_regs(tnr_dmd->io,
> + CXD2880_IO_TGT_SYS,
> + 0x10, data, 2);
> + if (ret)
> + return ret;
> + ret = tnr_dmd->io->write_reg(tnr_dmd->io,
> + CXD2880_IO_TGT_SYS,
> + 0x23, 0x89);
> + if (ret)
> + return ret;
> +
> + ret = tnr_dmd->io->write_regs(tnr_dmd->io,
> + CXD2880_IO_TGT_SYS,
> + 0x27, rf_init1_cdata2, 5);
> + if (ret)
> + return ret;
> +
> + ret = tnr_dmd->io->write_regs(tnr_dmd->io,
> + CXD2880_IO_TGT_SYS,
> + 0x3a, rf_init1_cdata3, 80);
> + if (ret)
> + return ret;
> +
> + data[0] = 0x03;
> + data[1] = 0xe0;
> + ret = tnr_dmd->io->write_regs(tnr_dmd->io,
> + CXD2880_IO_TGT_SYS,
> + 0xbc, data, 2);
> + if (ret)
> + return ret;
> +
> + ret = cxd2880_io_write_multi_regs(tnr_dmd->io,
> + CXD2880_IO_TGT_SYS,
> + rf_init1_seq3,
> + ARRAY_SIZE(rf_init1_seq3));
> + if (ret)
> + return ret;
> +
> + if (tnr_dmd->create_param.stationary_use) {
> + data[0] = 0x06;
> + data[1] = 0x07;
> + data[2] = 0x1a;
> + } else {
> + data[0] = 0x00;
> + data[1] = 0x08;
> + data[2] = 0x19;
> + }
> + data[3] = 0x0e;
> + data[4] = 0x09;
> + data[5] = 0x0e;
> +
> + ret = tnr_dmd->io->write_reg(tnr_dmd->io,
> + CXD2880_IO_TGT_SYS,
> + 0x00, 0x12);
> + if (ret)
> + return ret;
> + for (addr = 0x10; addr < 0x9f; addr += 6) {
> + if (tnr_dmd->lna_thrs_tbl_air) {
> + u8 idx = 0;
> +
> + idx = (addr - 0x10) / 6;
> + data[0] =
> + tnr_dmd->lna_thrs_tbl_air->thrs[idx].off_on;
> + data[1] =
> + tnr_dmd->lna_thrs_tbl_air->thrs[idx].on_off;
> + }
> + ret = tnr_dmd->io->write_regs(tnr_dmd->io,
> + CXD2880_IO_TGT_SYS,
> + addr, data, 6);
> + if (ret)
> + return ret;
> + }
> +
> + data[0] = 0x00;
> + data[1] = 0x08;
> + if (tnr_dmd->create_param.stationary_use)
> + data[2] = 0x1a;
> + else
> + data[2] = 0x19;
> + data[3] = 0x0e;
> + data[4] = 0x09;
> + data[5] = 0x0e;
> +
> + ret = tnr_dmd->io->write_reg(tnr_dmd->io,
> + CXD2880_IO_TGT_SYS,
> + 0x00, 0x13);
> + if (ret)
> + return ret;
> + for (addr = 0x10; addr < 0xcf; addr += 6) {
> + if (tnr_dmd->lna_thrs_tbl_cable) {
> + u8 idx = 0;
> +
> + idx = (addr - 0x10) / 6;
> + data[0] =
> + tnr_dmd->lna_thrs_tbl_cable->thrs[idx].off_on;
> + data[1] =
> + tnr_dmd->lna_thrs_tbl_cable->thrs[idx].on_off;
> + }
> + ret = tnr_dmd->io->write_regs(tnr_dmd->io,
> + CXD2880_IO_TGT_SYS,
> + addr, data, 6);
> + if (ret)
> + return ret;
> + }
> +
> + ret = tnr_dmd->io->write_reg(tnr_dmd->io,
> + CXD2880_IO_TGT_SYS,
> + 0x00, 0x11);
> + if (ret)
> + return ret;
> + data[0] = 0x08;
> + data[1] = 0x09;
> + ret = tnr_dmd->io->write_regs(tnr_dmd->io,
> + CXD2880_IO_TGT_SYS,
> + 0xbd, data, 2);
> + if (ret)
> + return ret;
> + data[0] = 0x08;
> + data[1] = 0x09;
> + ret = tnr_dmd->io->write_regs(tnr_dmd->io,
> + CXD2880_IO_TGT_SYS,
> + 0xc4, data, 2);
> + if (ret)
> + return ret;
> +
> + ret = tnr_dmd->io->write_regs(tnr_dmd->io,
> + CXD2880_IO_TGT_SYS,
> + 0xc9, rf_init1_cdata4, 8);
> + if (ret)
> + return ret;
> +
> + ret = tnr_dmd->io->write_reg(tnr_dmd->io,
> + CXD2880_IO_TGT_SYS,
> + 0x00, 0x14);
> + if (ret)
> + return ret;
> + data[0] = 0x15;
> + data[1] = 0x18;
> + data[2] = 0x00;
> + ret = tnr_dmd->io->write_regs(tnr_dmd->io,
> + CXD2880_IO_TGT_SYS,
> + 0x10, data, 3);
> + if (ret)
> + return ret;
> +
> + ret = cxd2880_io_write_multi_regs(tnr_dmd->io,
> + CXD2880_IO_TGT_SYS,
> + rf_init1_seq4,
> + ARRAY_SIZE(rf_init1_seq4));
> + if (ret)
> + return ret;
> +
> + ret = tnr_dmd->io->write_regs(tnr_dmd->io,
> + CXD2880_IO_TGT_SYS,
> + 0x12, rf_init1_cdata5, 50);
> + if (ret)
> + return ret;
> +
> + usleep_range(1000, 2000);
> +
> + ret = tnr_dmd->io->write_reg(tnr_dmd->io,
> + CXD2880_IO_TGT_SYS,
> + 0x00, 0x0a);
> + if (ret)
> + return ret;
> + ret = tnr_dmd->io->read_regs(tnr_dmd->io,
> + CXD2880_IO_TGT_SYS,
> + 0x10, data, 1);
> + if (ret)
> + return ret;
> + if ((data[0] & 0x01) == 0x00)
> + return -EBUSY;
I don't know anything about this hardware, but it sounds weird to return
-EBUSY here, except if the hardware reached a permanent busy condition,
and would require some sort of reset to work again.
As this is in the middle of lots of things, I *suspect* that this is
not the case.
If I'm right, and this is just a transitory solution that could happen
for a limited amount of time, e. g. if what's there at data[0] is a flag
saying that the device didn't finish the last operation yet, maybe the
best would be to do something like:
for (i = 0; i < 10; i++) {
ret = tnr_dmd->io->read_regs(tnr_dmd->io,
CXD2880_IO_TGT_SYS,
0x10, data, 1);
if (ret)
return ret;
if (data[0] & 0x01)
break;
msleep(10);
}
if (!(data[0] & 0x01))
return -EBUSY;
> +
> + ret = cxd2880_io_write_multi_regs(tnr_dmd->io,
> + CXD2880_IO_TGT_SYS,
> + rf_init1_seq5,
> + ARRAY_SIZE(rf_init1_seq5));
> + if (ret)
> + return ret;
> +
> + usleep_range(1000, 2000);
> +
> + ret = tnr_dmd->io->write_reg(tnr_dmd->io,
> + CXD2880_IO_TGT_SYS,
> + 0x00, 0x0a);
> + if (ret)
> + return ret;
> + ret = tnr_dmd->io->read_regs(tnr_dmd->io,
> + CXD2880_IO_TGT_SYS,
> + 0x11, data, 1);
> + if (ret)
> + return ret;
> + if ((data[0] & 0x01) == 0x00)
> + return -EBUSY;
Same here and on similar places.
> +
> + ret = cxd2880_io_write_multi_regs(tnr_dmd->io,
> + CXD2880_IO_TGT_DMD,
> + rf_init1_seq6,
> + ARRAY_SIZE(rf_init1_seq6));
> + if (ret)
> + return ret;
> +
> + data[0] = 0x00;
> + data[1] = 0xfe;
> + data[2] = 0xee;
> + ret = tnr_dmd->io->write_regs(tnr_dmd->io,
> + CXD2880_IO_TGT_DMD,
> + 0x6e, data, 3);
> + if (ret)
> + return ret;
> + data[0] = 0xa1;
> + data[1] = 0x8b;
> + ret = tnr_dmd->io->write_regs(tnr_dmd->io,
> + CXD2880_IO_TGT_DMD,
> + 0x8d, data, 2);
> + if (ret)
> + return ret;
> + data[0] = 0x08;
> + data[1] = 0x09;
> + ret = tnr_dmd->io->write_regs(tnr_dmd->io,
> + CXD2880_IO_TGT_DMD,
> + 0x77, data, 2);
> + if (ret)
> + return ret;
> +
> + if (tnr_dmd->create_param.stationary_use) {
> + ret = tnr_dmd->io->write_reg(tnr_dmd->io,
> + CXD2880_IO_TGT_DMD,
> + 0x80, 0xaa);
> + if (ret)
> + return ret;
> + }
> +
> + ret = cxd2880_io_write_multi_regs(tnr_dmd->io,
> + CXD2880_IO_TGT_DMD,
> + rf_init1_seq7,
> + ARRAY_SIZE(rf_init1_seq7));
> + if (ret)
> + return ret;
> +
> + ret = cxd2880_io_write_multi_regs(tnr_dmd->io,
> + CXD2880_IO_TGT_SYS,
> + rf_init1_seq8,
> + ARRAY_SIZE(rf_init1_seq8));
> + if (ret)
> + return ret;
> +
> + usleep_range(1000, 2000);
> +
> + ret = tnr_dmd->io->write_reg(tnr_dmd->io,
> + CXD2880_IO_TGT_SYS,
> + 0x00, 0x1a);
> + if (ret)
> + return ret;
> + ret = tnr_dmd->io->read_regs(tnr_dmd->io,
> + CXD2880_IO_TGT_SYS,
> + 0x10, data, 1);
> + if (ret)
> + return ret;
> + if ((data[0] & 0x01) == 0x00)
> + return -EBUSY;
> +
> + ret = cxd2880_io_write_multi_regs(tnr_dmd->io,
> + CXD2880_IO_TGT_SYS,
> + rf_init1_seq9,
> + ARRAY_SIZE(rf_init1_seq9));
> + if (ret)
> + return ret;
> +
> + return 0;
> +}
> +
> +static int rf_init2(struct cxd2880_tnrdmd *tnr_dmd)
> +{
> + u8 data[5] = { 0 };
> + int ret;
> +
> + if (!tnr_dmd)
> + return -EINVAL;
> +
> + ret = tnr_dmd->io->write_reg(tnr_dmd->io,
> + CXD2880_IO_TGT_SYS,
> + 0x00, 0x10);
> + if (ret)
> + return ret;
> + data[0] = 0x40;
> + data[1] = 0x40;
> + ret = tnr_dmd->io->write_regs(tnr_dmd->io,
> + CXD2880_IO_TGT_SYS,
> + 0xea, data, 2);
> + if (ret)
> + return ret;
> +
> + usleep_range(1000, 2000);
> +
> + data[0] = 0x00;
> + if (tnr_dmd->chip_id == CXD2880_TNRDMD_CHIP_ID_CXD2880_ES1_0X)
> + data[1] = 0x00;
> + else
> + data[1] = 0x01;
> + data[2] = 0x01;
> + data[3] = 0x03;
> + ret = tnr_dmd->io->write_regs(tnr_dmd->io,
> + CXD2880_IO_TGT_SYS,
> + 0x30, data, 4);
> + if (ret)
> + return ret;
> +
> + ret = cxd2880_io_write_multi_regs(tnr_dmd->io,
> + CXD2880_IO_TGT_SYS,
> + rf_init2_seq1,
> + ARRAY_SIZE(rf_init2_seq1));
> + if (ret)
> + return ret;
> +
> + ret = cxd2880_io_write_multi_regs(tnr_dmd->io,
> + CXD2880_IO_TGT_DMD,
> + rf_init2_seq2,
> + ARRAY_SIZE(rf_init2_seq2));
> + if (ret)
> + return ret;
> +
> + return 0;
> +}
> +
> +static int x_tune1(struct cxd2880_tnrdmd *tnr_dmd,
> + enum cxd2880_dtv_sys sys, u32 freq_khz,
> + enum cxd2880_dtv_bandwidth bandwidth,
> + u8 is_cable, int shift_frequency_khz)
> +{
> + u8 data[11] = { 0 };
> + int ret;
> +
> + if (!tnr_dmd)
> + return -EINVAL;
> +
> + ret = cxd2880_io_write_multi_regs(tnr_dmd->io,
> + CXD2880_IO_TGT_DMD,
> + x_tune1_seq1,
> + ARRAY_SIZE(x_tune1_seq1));
> + if (ret)
> + return ret;
> +
> + ret = tnr_dmd->io->write_reg(tnr_dmd->io,
> + CXD2880_IO_TGT_SYS,
> + 0x00, 0x10);
> + if (ret)
> + return ret;
> +
> + data[0] = 0x00;
> + data[1] = 0x00;
The data struct was already zeroed when it was created with { 0 }.
So, you only need to fill the values that aren't zero.
> + data[2] = 0x0e;
> + data[3] = 0x00;
> + data[4] = 0x03;
> + ret = tnr_dmd->io->write_regs(tnr_dmd->io,
> + CXD2880_IO_TGT_SYS,
> + 0xe7, data, 5);
> + if (ret)
> + return ret;
> +
> + data[0] = 0x1f;
> + data[1] = 0x80;
> + data[2] = 0x18;
> + data[3] = 0x00;
> + data[4] = 0x07;
> + ret = tnr_dmd->io->write_regs(tnr_dmd->io,
> + CXD2880_IO_TGT_SYS,
> + 0xe7, data, 5);
> + if (ret)
> + return ret;
> +
> + usleep_range(1000, 2000);
> +
> + data[0] = 0x72;
> + data[1] = 0x81;
> + data[3] = 0x1d;
> + data[4] = 0x6f;
> + data[5] = 0x7e;
> + data[7] = 0x1c;
> + switch (sys) {
> + case CXD2880_DTV_SYS_DVBT:
> + data[2] = 0x94;
> + data[6] = 0x91;
> + break;
> + case CXD2880_DTV_SYS_DVBT2:
> + data[2] = 0x96;
> + data[6] = 0x93;
> + break;
> + default:
> + return -EINVAL;
> + }
> + ret = tnr_dmd->io->write_regs(tnr_dmd->io,
> + CXD2880_IO_TGT_SYS,
> + 0x44, data, 8);
> + if (ret)
> + return ret;
> +
> + ret = cxd2880_io_write_multi_regs(tnr_dmd->io,
> + CXD2880_IO_TGT_SYS,
> + x_tune1_seq2,
> + ARRAY_SIZE(x_tune1_seq2));
> + if (ret)
> + return ret;
> +
> + data[0] = 0x03;
> + data[1] = 0xe2;
> + ret = tnr_dmd->io->write_regs(tnr_dmd->io,
> + CXD2880_IO_TGT_SYS,
> + 0x1e, data, 2);
> + if (ret)
> + return ret;
> +
> + ret = tnr_dmd->io->write_reg(tnr_dmd->io,
> + CXD2880_IO_TGT_SYS,
> + 0x00, 0x10);
> + if (ret)
> + return ret;
> +
> + data[0] = is_cable ? 0x01 : 0x00;
> + data[1] = 0x00;
> + data[2] = 0x6b;
> + data[3] = 0x4d;
> +
> + switch (bandwidth) {
> + case CXD2880_DTV_BW_1_7_MHZ:
> + data[4] = 0x03;
> + break;
> + case CXD2880_DTV_BW_5_MHZ:
> + case CXD2880_DTV_BW_6_MHZ:
> + data[4] = 0x00;
> + break;
> + case CXD2880_DTV_BW_7_MHZ:
> + data[4] = 0x01;
> + break;
> + case CXD2880_DTV_BW_8_MHZ:
> + data[4] = 0x02;
> + break;
> + default:
> + return -EINVAL;
> + }
> +
> + data[5] = 0x00;
> +
> + freq_khz += shift_frequency_khz;
> +
> + data[6] = (freq_khz >> 16) & 0x0f;
> + data[7] = (freq_khz >> 8) & 0xff;
> + data[8] = freq_khz & 0xff;
> + data[9] = 0xff;
> + data[10] = 0xfe;
> + ret = tnr_dmd->io->write_regs(tnr_dmd->io,
> + CXD2880_IO_TGT_SYS,
> + 0x52, data, 11);
> + if (ret)
> + return ret;
> +
> + return 0;
> +}
> +
> +static int x_tune2(struct cxd2880_tnrdmd *tnr_dmd,
> + enum cxd2880_dtv_bandwidth bandwidth,
> + enum cxd2880_tnrdmd_clockmode clk_mode,
> + int shift_frequency_khz)
> +{
> + u8 data[3] = { 0 };
> + int ret;
> +
> + if (!tnr_dmd)
> + return -EINVAL;
> +
> + ret = tnr_dmd->io->write_reg(tnr_dmd->io,
> + CXD2880_IO_TGT_SYS,
> + 0x00, 0x11);
> + if (ret)
> + return ret;
> +
> + data[0] = 0x01;
> + data[1] = 0x0e;
> + data[2] = 0x01;
> + ret = tnr_dmd->io->write_regs(tnr_dmd->io,
> + CXD2880_IO_TGT_SYS,
> + 0x2d, data, 3);
> + if (ret)
> + return ret;
> +
> + ret = cxd2880_io_write_multi_regs(tnr_dmd->io,
> + CXD2880_IO_TGT_SYS,
> + x_tune2_seq1,
> + ARRAY_SIZE(x_tune2_seq1));
> + if (ret)
> + return ret;
> +
> + ret = tnr_dmd->io->read_regs(tnr_dmd->io,
> + CXD2880_IO_TGT_SYS,
> + 0x2c, data, 1);
> + if (ret)
> + return ret;
> +
> + ret = tnr_dmd->io->write_reg(tnr_dmd->io,
> + CXD2880_IO_TGT_SYS,
> + 0x00, 0x10);
> + if (ret)
> + return ret;
> +
> + ret = tnr_dmd->io->write_reg(tnr_dmd->io,
> + CXD2880_IO_TGT_SYS,
> + 0x60, data[0]);
> + if (ret)
> + return ret;
> +
> + ret = cxd2880_io_write_multi_regs(tnr_dmd->io,
> + CXD2880_IO_TGT_SYS,
> + x_tune2_seq2,
> + ARRAY_SIZE(x_tune2_seq2));
> + if (ret)
> + return ret;
> +
> + ret = cxd2880_io_write_multi_regs(tnr_dmd->io,
> + CXD2880_IO_TGT_DMD,
> + x_tune2_seq3,
> + ARRAY_SIZE(x_tune2_seq3));
> + if (ret)
> + return ret;
> +
> + if (shift_frequency_khz != 0) {
> + int shift_freq = 0;
> +
> + ret = tnr_dmd->io->write_reg(tnr_dmd->io,
> + CXD2880_IO_TGT_DMD,
> + 0x00, 0xe1);
> + if (ret)
> + return ret;
> +
> + ret = tnr_dmd->io->read_regs(tnr_dmd->io,
> + CXD2880_IO_TGT_DMD,
> + 0x60, data, 2);
> + if (ret)
> + return ret;
> +
> + shift_freq = shift_frequency_khz * 1000;
> +
> + switch (clk_mode) {
> + case CXD2880_TNRDMD_CLOCKMODE_A:
> + case CXD2880_TNRDMD_CLOCKMODE_C:
> + default:
> + if (shift_freq >= 0)
> + shift_freq = (shift_freq + 183 / 2) / 183;
> + else
> + shift_freq = (shift_freq - 183 / 2) / 183;
> + break;
> + case CXD2880_TNRDMD_CLOCKMODE_B:
> + if (shift_freq >= 0)
> + shift_freq = (shift_freq + 178 / 2) / 178;
> + else
> + shift_freq = (shift_freq - 178 / 2) / 178;
> + break;
> + }
> +
> + shift_freq +=
> + cxd2880_convert2s_complement((data[0] << 8) | data[1], 16);
> +
> + if (shift_freq > 32767)
> + shift_freq = 32767;
> + else if (shift_freq < -32768)
> + shift_freq = -32768;
> +
> + data[0] = (shift_freq >> 8) & 0xff;
> + data[1] = shift_freq & 0xff;
> +
> + ret = tnr_dmd->io->write_regs(tnr_dmd->io,
> + CXD2880_IO_TGT_DMD,
> + 0x60, data, 2);
> + if (ret)
> + return ret;
> +
> + ret = tnr_dmd->io->read_regs(tnr_dmd->io,
> + CXD2880_IO_TGT_DMD,
> + 0x69, data, 1);
> + if (ret)
> + return ret;
> +
> + shift_freq = -shift_frequency_khz;
> +
> + if (bandwidth == CXD2880_DTV_BW_1_7_MHZ) {
> + switch (clk_mode) {
> + case CXD2880_TNRDMD_CLOCKMODE_A:
> + case CXD2880_TNRDMD_CLOCKMODE_C:
> + default:
> + if (shift_freq >= 0)
> + shift_freq =
> + (shift_freq * 1000 +
> + 17578 / 2) / 17578;
> + else
> + shift_freq =
> + (shift_freq * 1000 -
> + 17578 / 2) / 17578;
> + break;
> + case CXD2880_TNRDMD_CLOCKMODE_B:
> + if (shift_freq >= 0)
> + shift_freq =
> + (shift_freq * 1000 +
> + 17090 / 2) / 17090;
> + else
> + shift_freq =
> + (shift_freq * 1000 -
> + 17090 / 2) / 17090;
> + break;
> + }
> + } else {
> + switch (clk_mode) {
> + case CXD2880_TNRDMD_CLOCKMODE_A:
> + case CXD2880_TNRDMD_CLOCKMODE_C:
> + default:
> + if (shift_freq >= 0)
> + shift_freq =
> + (shift_freq * 1000 +
> + 35156 / 2) / 35156;
> + else
> + shift_freq =
> + (shift_freq * 1000 -
> + 35156 / 2) / 35156;
> + break;
> + case CXD2880_TNRDMD_CLOCKMODE_B:
> + if (shift_freq >= 0)
> + shift_freq =
> + (shift_freq * 1000 +
> + 34180 / 2) / 34180;
> + else
> + shift_freq =
> + (shift_freq * 1000 -
> + 34180 / 2) / 34180;
> + break;
> + }
> + }
> +
> + shift_freq += cxd2880_convert2s_complement(data[0], 8);
> +
> + if (shift_freq > 127)
> + shift_freq = 127;
> + else if (shift_freq < -128)
> + shift_freq = -128;
> +
> + data[0] = shift_freq & 0xff;
> +
> + ret = tnr_dmd->io->write_reg(tnr_dmd->io,
> + CXD2880_IO_TGT_DMD,
> + 0x69, data[0]);
> + if (ret)
> + return ret;
> + }
> +
> + if (tnr_dmd->create_param.stationary_use) {
> + ret = cxd2880_io_write_multi_regs(tnr_dmd->io,
> + CXD2880_IO_TGT_DMD,
> + x_tune2_seq4,
> + ARRAY_SIZE(x_tune2_seq4));
> + if (ret)
> + return ret;
> + }
> +
> + ret = cxd2880_io_write_multi_regs(tnr_dmd->io,
> + CXD2880_IO_TGT_DMD,
> + x_tune2_seq5,
> + ARRAY_SIZE(x_tune2_seq5));
> + if (ret)
> + return ret;
> +
> + return 0;
> +}
> +
> +static int x_tune3(struct cxd2880_tnrdmd *tnr_dmd,
> + enum cxd2880_dtv_sys sys,
> + u8 en_fef_intmtnt_ctrl)
> +{
> + u8 data[6] = { 0 };
> + int ret;
> +
> + if (!tnr_dmd)
> + return -EINVAL;
> +
> + ret = cxd2880_io_write_multi_regs(tnr_dmd->io,
> + CXD2880_IO_TGT_DMD,
> + x_tune3_seq,
> + ARRAY_SIZE(x_tune3_seq));
> + if (ret)
> + return ret;
> +
> + ret = tnr_dmd->io->write_reg(tnr_dmd->io,
> + CXD2880_IO_TGT_SYS,
> + 0x00, 0x10);
> + if (ret)
> + return ret;
> +
> + if ((sys == CXD2880_DTV_SYS_DVBT2) && en_fef_intmtnt_ctrl)
> + memset(data, 0x01, sizeof(data));
> + else
> + memset(data, 0x00, sizeof(data));
> +
> + ret = tnr_dmd->io->write_regs(tnr_dmd->io,
> + CXD2880_IO_TGT_SYS,
> + 0xef, data, 6);
> + if (ret)
> + return ret;
> +
> + ret = tnr_dmd->io->write_reg(tnr_dmd->io,
> + CXD2880_IO_TGT_DMD,
> + 0x00, 0x2d);
> + if (ret)
> + return ret;
> + if ((sys == CXD2880_DTV_SYS_DVBT2) && en_fef_intmtnt_ctrl)
> + data[0] = 0x00;
> + else
> + data[0] = 0x01;
> +
> + ret = tnr_dmd->io->write_reg(tnr_dmd->io,
> + CXD2880_IO_TGT_DMD,
> + 0xb1, data[0]);
> + if (ret)
> + return ret;
> +
> + return 0;
> +}
> +
> +static int x_tune4(struct cxd2880_tnrdmd *tnr_dmd)
> +{
> + u8 data[2] = { 0 };
> + int ret;
> +
> + if (!tnr_dmd)
> + return -EINVAL;
> +
> + if (tnr_dmd->diver_mode != CXD2880_TNRDMD_DIVERMODE_MAIN)
> + return -EINVAL;
> +
> + ret = tnr_dmd->diver_sub->io->write_reg(tnr_dmd->diver_sub->io,
> + CXD2880_IO_TGT_SYS,
> + 0x00, 0x00);
> + if (ret)
> + return ret;
> + data[0] = 0x14;
> + data[1] = 0x00;
> + ret = tnr_dmd->diver_sub->io->write_regs(tnr_dmd->diver_sub->io,
> + CXD2880_IO_TGT_SYS,
> + 0x55, data, 2);
> + if (ret)
> + return ret;
> +
> + ret = tnr_dmd->io->write_reg(tnr_dmd->io,
> + CXD2880_IO_TGT_SYS,
> + 0x00, 0x00);
> + if (ret)
> + return ret;
> + data[0] = 0x0b;
> + data[1] = 0xff;
> + ret = tnr_dmd->io->write_regs(tnr_dmd->io,
> + CXD2880_IO_TGT_SYS,
> + 0x53, data, 2);
> + if (ret)
> + return ret;
> + ret = tnr_dmd->io->write_reg(tnr_dmd->io,
> + CXD2880_IO_TGT_SYS,
> + 0x57, 0x01);
> + if (ret)
> + return ret;
> + data[0] = 0x0b;
> + data[1] = 0xff;
> + ret = tnr_dmd->io->write_regs(tnr_dmd->io,
> + CXD2880_IO_TGT_SYS,
> + 0x55, data, 2);
> + if (ret)
> + return ret;
> +
> + ret = tnr_dmd->diver_sub->io->write_reg(tnr_dmd->diver_sub->io,
> + CXD2880_IO_TGT_SYS,
> + 0x00, 0x00);
> + if (ret)
> + return ret;
> + data[0] = 0x14;
> + data[1] = 0x00;
> + ret = tnr_dmd->diver_sub->io->write_regs(tnr_dmd->diver_sub->io,
> + CXD2880_IO_TGT_SYS,
> + 0x53, data, 2);
> + if (ret)
> + return ret;
> + ret = tnr_dmd->diver_sub->io->write_reg(tnr_dmd->diver_sub->io,
> + CXD2880_IO_TGT_SYS,
> + 0x57, 0x02);
> + if (ret)
> + return ret;
> +
> + ret = cxd2880_io_write_multi_regs(tnr_dmd->io,
> + CXD2880_IO_TGT_DMD,
> + x_tune4_seq,
> + ARRAY_SIZE(x_tune4_seq));
> + if (ret)
> + return ret;
> +
> + ret = cxd2880_io_write_multi_regs(tnr_dmd->diver_sub->io,
> + CXD2880_IO_TGT_DMD,
> + x_tune4_seq,
> + ARRAY_SIZE(x_tune4_seq));
> + if (ret)
> + return ret;
> +
> + return 0;
> +}
> +
> +static int x_sleep1(struct cxd2880_tnrdmd *tnr_dmd)
> +{
> + u8 data[3] = { 0 };
> + int ret;
> +
> + if (!tnr_dmd)
> + return -EINVAL;
> +
> + if (tnr_dmd->diver_mode != CXD2880_TNRDMD_DIVERMODE_MAIN)
> + return -EINVAL;
> +
> + ret = cxd2880_io_write_multi_regs(tnr_dmd->io,
> + CXD2880_IO_TGT_SYS,
> + x_sleep1_seq,
> + ARRAY_SIZE(x_sleep1_seq));
> + if (ret)
> + return ret;
> +
> + data[0] = 0x00;
> + data[1] = 0x00;
> + ret = tnr_dmd->io->write_regs(tnr_dmd->io,
> + CXD2880_IO_TGT_SYS,
> + 0x53, data, 2);
> + if (ret)
> + return ret;
> +
> + ret = tnr_dmd->diver_sub->io->write_reg(tnr_dmd->diver_sub->io,
> + CXD2880_IO_TGT_SYS,
> + 0x00, 0x00);
> + if (ret)
> + return ret;
> + data[0] = 0x1f;
> + data[1] = 0xff;
> + data[2] = 0x03;
> + ret = tnr_dmd->diver_sub->io->write_regs(tnr_dmd->diver_sub->io,
> + CXD2880_IO_TGT_SYS,
> + 0x55, data, 3);
> + if (ret)
> + return ret;
> + data[0] = 0x00;
> + data[1] = 0x00;
> + ret = tnr_dmd->diver_sub->io->write_regs(tnr_dmd->diver_sub->io,
> + CXD2880_IO_TGT_SYS,
> + 0x53, data, 2);
> + if (ret)
> + return ret;
> +
> + ret = tnr_dmd->io->write_reg(tnr_dmd->io,
> + CXD2880_IO_TGT_SYS,
> + 0x00, 0x00);
> + if (ret)
> + return ret;
> + data[0] = 0x1f;
> + data[1] = 0xff;
> + ret = tnr_dmd->io->write_regs(tnr_dmd->io,
> + CXD2880_IO_TGT_SYS,
> + 0x55, data, 2);
> + if (ret)
> + return ret;
> +
> + return 0;
> +}
> +
> +static int x_sleep2(struct cxd2880_tnrdmd *tnr_dmd)
> +{
> + u8 data = 0;
> + int ret;
> +
> + if (!tnr_dmd)
> + return -EINVAL;
> +
> + ret = cxd2880_io_write_multi_regs(tnr_dmd->io,
> + CXD2880_IO_TGT_DMD,
> + x_sleep2_seq1,
> + ARRAY_SIZE(x_sleep2_seq1));
> + if (ret)
> + return ret;
> +
> + usleep_range(1000, 2000);
> +
> + ret = tnr_dmd->io->read_regs(tnr_dmd->io,
> + CXD2880_IO_TGT_DMD,
> + 0xb2, &data, 1);
> + if (ret)
> + return ret;
> +
> + if ((data & 0x01) == 0x00)
> + return -EBUSY;
> +
> + ret = cxd2880_io_write_multi_regs(tnr_dmd->io,
> + CXD2880_IO_TGT_SYS,
> + x_sleep2_seq2,
> + ARRAY_SIZE(x_sleep2_seq2));
> + if (ret)
> + return ret;
> +
> + return 0;
> +}
> +
> +static int x_sleep3(struct cxd2880_tnrdmd *tnr_dmd)
> +{
> + int ret;
> +
> + if (!tnr_dmd)
> + return -EINVAL;
> +
> + ret = cxd2880_io_write_multi_regs(tnr_dmd->io,
> + CXD2880_IO_TGT_DMD,
> + x_sleep3_seq,
> + ARRAY_SIZE(x_sleep3_seq));
> + if (ret)
> + return ret;
> +
> + return 0;
> +}
> +
> +static int x_sleep4(struct cxd2880_tnrdmd *tnr_dmd)
> +{
> + int ret;
> +
> + if (!tnr_dmd)
> + return -EINVAL;
> +
> + ret = cxd2880_io_write_multi_regs(tnr_dmd->io,
> + CXD2880_IO_TGT_DMD,
> + x_sleep4_seq,
> + ARRAY_SIZE(x_sleep4_seq));
> + if (ret)
> + return ret;
> +
> + return 0;
> +}
> +
> +static int spll_reset(struct cxd2880_tnrdmd *tnr_dmd,
> + enum cxd2880_tnrdmd_clockmode clockmode)
> +{
> + u8 data[4] = { 0 };
> + int ret;
> +
> + if (!tnr_dmd)
> + return -EINVAL;
> +
> + ret = cxd2880_io_write_multi_regs(tnr_dmd->io,
> + CXD2880_IO_TGT_SYS,
> + spll_reset_seq1,
> + ARRAY_SIZE(spll_reset_seq1));
> + if (ret)
> + return ret;
> +
> + ret = cxd2880_io_write_multi_regs(tnr_dmd->io,
> + CXD2880_IO_TGT_DMD,
> + spll_reset_seq2,
> + ARRAY_SIZE(spll_reset_seq2));
> + if (ret)
> + return ret;
> +
> + ret = cxd2880_io_write_multi_regs(tnr_dmd->io,
> + CXD2880_IO_TGT_SYS,
> + spll_reset_seq3,
> + ARRAY_SIZE(spll_reset_seq3));
> + if (ret)
> + return ret;
> +
> + switch (clockmode) {
> + case CXD2880_TNRDMD_CLOCKMODE_A:
> + data[0] = 0x00;
> + break;
> +
> + case CXD2880_TNRDMD_CLOCKMODE_B:
> + data[0] = 0x01;
> + break;
> +
> + case CXD2880_TNRDMD_CLOCKMODE_C:
> + data[0] = 0x02;
> + break;
> +
> + default:
> + return -EINVAL;
> + }
> + ret = tnr_dmd->io->write_reg(tnr_dmd->io,
> + CXD2880_IO_TGT_SYS,
> + 0x30, data[0]);
> + if (ret)
> + return ret;
> + ret = tnr_dmd->io->write_reg(tnr_dmd->io,
> + CXD2880_IO_TGT_SYS,
> + 0x22, 0x00);
> + if (ret)
> + return ret;
> +
> + usleep_range(2000, 3000);
> +
> + ret = tnr_dmd->io->write_reg(tnr_dmd->io,
> + CXD2880_IO_TGT_SYS,
> + 0x00, 0x0a);
> + if (ret)
> + return ret;
> + ret = tnr_dmd->io->read_regs(tnr_dmd->io,
> + CXD2880_IO_TGT_SYS,
> + 0x10, data, 1);
> + if (ret)
> + return ret;
> + if ((data[0] & 0x01) == 0x00)
> + return -EBUSY;
> +
> + ret = cxd2880_io_write_multi_regs(tnr_dmd->io,
> + CXD2880_IO_TGT_SYS,
> + spll_reset_seq4,
> + ARRAY_SIZE(spll_reset_seq4));
> + if (ret)
> + return ret;
> +
> + usleep_range(1000, 2000);
> +
> + ret = cxd2880_io_write_multi_regs(tnr_dmd->io,
> + CXD2880_IO_TGT_DMD,
> + spll_reset_seq5,
> + ARRAY_SIZE(spll_reset_seq5));
> + if (ret)
> + return ret;
> +
> + ret = tnr_dmd->io->write_reg(tnr_dmd->io,
> + CXD2880_IO_TGT_SYS,
> + 0x00, 0x10);
> + if (ret)
> + return ret;
> +
> + memset(data, 0x00, sizeof(data));
> + ret = tnr_dmd->io->write_regs(tnr_dmd->io,
> + CXD2880_IO_TGT_SYS,
> + 0x26, data, 4);
> + if (ret)
> + return ret;
> +
> + return 0;
> +}
> +
> +static int t_power_x(struct cxd2880_tnrdmd *tnr_dmd, u8 on)
> +{
> + u8 data[3] = { 0 };
> + int ret;
> +
> + if (!tnr_dmd)
> + return -EINVAL;
> +
> + ret = cxd2880_io_write_multi_regs(tnr_dmd->io,
> + CXD2880_IO_TGT_SYS,
> + t_power_x_seq1,
> + ARRAY_SIZE(t_power_x_seq1));
> + if (ret)
> + return ret;
> +
> + ret = cxd2880_io_write_multi_regs(tnr_dmd->io,
> + CXD2880_IO_TGT_DMD,
> + t_power_x_seq2,
> + ARRAY_SIZE(t_power_x_seq2));
> + if (ret)
> + return ret;
> +
> + ret = cxd2880_io_write_multi_regs(tnr_dmd->io,
> + CXD2880_IO_TGT_SYS,
> + t_power_x_seq3,
> + ARRAY_SIZE(t_power_x_seq3));
> + if (ret)
> + return ret;
> +
> + if (on) {
> + ret = tnr_dmd->io->write_reg(tnr_dmd->io,
> + CXD2880_IO_TGT_SYS,
> + 0x2b, 0x01);
> + if (ret)
> + return ret;
> +
> + usleep_range(1000, 2000);
> +
> + ret = tnr_dmd->io->write_reg(tnr_dmd->io,
> + CXD2880_IO_TGT_SYS,
> + 0x00, 0x0a);
> + if (ret)
> + return ret;
> + ret = tnr_dmd->io->read_regs(tnr_dmd->io,
> + CXD2880_IO_TGT_SYS,
> + 0x12, data, 1);
> + if (ret)
> + return ret;
> + if ((data[0] & 0x01) == 0)
> + return -EBUSY;
> +
> + ret = cxd2880_io_write_multi_regs(tnr_dmd->io,
> + CXD2880_IO_TGT_SYS,
> + t_power_x_seq4,
> + ARRAY_SIZE(t_power_x_seq4));
> + if (ret)
> + return ret;
> + } else {
> + data[0] = 0x03;
> + data[1] = 0x00;
> + ret = tnr_dmd->io->write_regs(tnr_dmd->io,
> + CXD2880_IO_TGT_SYS,
> + 0x2a, data, 2);
> + if (ret)
> + return ret;
> +
> + usleep_range(1000, 2000);
> +
> + ret = tnr_dmd->io->write_reg(tnr_dmd->io,
> + CXD2880_IO_TGT_SYS,
> + 0x00, 0x0a);
> + if (ret)
> + return ret;
> + ret = tnr_dmd->io->read_regs(tnr_dmd->io,
> + CXD2880_IO_TGT_SYS,
> + 0x13, data, 1);
> + if (ret)
> + return ret;
> + if ((data[0] & 0x01) == 0)
> + return -EBUSY;
> + }
> +
> + ret = cxd2880_io_write_multi_regs(tnr_dmd->io,
> + CXD2880_IO_TGT_SYS,
> + t_power_x_seq5,
> + ARRAY_SIZE(t_power_x_seq5));
> + if (ret)
> + return ret;
> +
> + usleep_range(1000, 2000);
> +
> + ret = tnr_dmd->io->write_reg(tnr_dmd->io,
> + CXD2880_IO_TGT_SYS,
> + 0x00, 0x0a);
> + if (ret)
> + return ret;
> + ret = tnr_dmd->io->read_regs(tnr_dmd->io,
> + CXD2880_IO_TGT_SYS,
> + 0x11, data, 1);
> + if (ret)
> + return ret;
> + if ((data[0] & 0x01) == 0)
> + return -EBUSY;
> +
> + ret = cxd2880_io_write_multi_regs(tnr_dmd->io,
> + CXD2880_IO_TGT_SYS,
> + t_power_x_seq6,
> + ARRAY_SIZE(t_power_x_seq6));
> + if (ret)
> + return ret;
> +
> + usleep_range(1000, 2000);
> +
> + ret = cxd2880_io_write_multi_regs(tnr_dmd->io,
> + CXD2880_IO_TGT_DMD,
> + t_power_x_seq7,
> + ARRAY_SIZE(t_power_x_seq7));
> + if (ret)
> + return ret;
> +
> + ret = tnr_dmd->io->write_reg(tnr_dmd->io,
> + CXD2880_IO_TGT_SYS,
> + 0x00, 0x10);
> + if (ret)
> + return ret;
> +
> + memset(data, 0x00, sizeof(data));
> + ret = tnr_dmd->io->write_regs(tnr_dmd->io,
> + CXD2880_IO_TGT_SYS,
> + 0x27, data, 3);
> + if (ret)
> + return ret;
> +
> + return 0;
> +}
> +
> +struct cxd2880_tnrdmd_ts_clk_cfg {
> + u8 srl_clk_mode;
> + u8 srl_duty_mode;
> + u8 ts_clk_period;
> +};
> +
> +static int set_ts_clk_mode_and_freq(struct cxd2880_tnrdmd *tnr_dmd,
> + enum cxd2880_dtv_sys sys)
> +{
> + int ret;
> + u8 backwards_compatible = 0;
> + struct cxd2880_tnrdmd_ts_clk_cfg ts_clk_cfg;
> + u8 ts_rate_ctrl_off = 0;
> + u8 ts_in_off = 0;
> + u8 ts_clk_manaul_on = 0;
> + u8 data = 0;
> +
> + static const struct cxd2880_tnrdmd_ts_clk_cfg srl_ts_clk_stgs[2][2] = {
> + {
> + {3, 1, 8,},
> + {0, 2, 16,}
> + }, {
> + {1, 1, 8,},
> + {2, 2, 16,}
> + }
> + };
> +
> + if (!tnr_dmd)
> + return -EINVAL;
> +
> + ret = tnr_dmd->io->write_reg(tnr_dmd->io,
> + CXD2880_IO_TGT_DMD,
> + 0x00, 0x00);
> + if (ret)
> + return ret;
> +
> + if (tnr_dmd->is_ts_backwards_compatible_mode) {
> + backwards_compatible = 1;
> + ts_rate_ctrl_off = 1;
> + ts_in_off = 1;
> + } else {
> + backwards_compatible = 0;
> + ts_rate_ctrl_off = 0;
> + ts_in_off = 0;
> + }
> +
> + if (tnr_dmd->ts_byte_clk_manual_setting) {
> + ts_clk_manaul_on = 1;
> + ts_rate_ctrl_off = 0;
> + }
> +
> + ret = cxd2880_io_set_reg_bits(tnr_dmd->io,
> + CXD2880_IO_TGT_DMD,
> + 0xd3, ts_rate_ctrl_off, 0x01);
> + if (ret)
> + return ret;
> +
> + ret = cxd2880_io_set_reg_bits(tnr_dmd->io,
> + CXD2880_IO_TGT_DMD,
> + 0xde, ts_in_off, 0x01);
> + if (ret)
> + return ret;
> +
> + ret = cxd2880_io_set_reg_bits(tnr_dmd->io,
> + CXD2880_IO_TGT_DMD,
> + 0xda, ts_clk_manaul_on, 0x01);
> + if (ret)
> + return ret;
> +
> + ts_clk_cfg = srl_ts_clk_stgs[tnr_dmd->srl_ts_clk_mod_cnts]
> + [tnr_dmd->srl_ts_clk_frq];
> +
> + if (tnr_dmd->ts_byte_clk_manual_setting)
> + ts_clk_cfg.ts_clk_period = tnr_dmd->ts_byte_clk_manual_setting;
> +
> + ret = cxd2880_io_set_reg_bits(tnr_dmd->io,
> + CXD2880_IO_TGT_DMD,
> + 0xc4, ts_clk_cfg.srl_clk_mode, 0x03);
> + if (ret)
> + return ret;
> +
> + ret = cxd2880_io_set_reg_bits(tnr_dmd->io,
> + CXD2880_IO_TGT_DMD,
> + 0xd1, ts_clk_cfg.srl_duty_mode, 0x03);
> + if (ret)
> + return ret;
> +
> + ret = tnr_dmd->io->write_reg(tnr_dmd->io,
> + CXD2880_IO_TGT_DMD, 0xd9,
> + ts_clk_cfg.ts_clk_period);
> + if (ret)
> + return ret;
> +
> + data = backwards_compatible ? 0x00 : 0x01;
> +
> + if (sys == CXD2880_DTV_SYS_DVBT) {
> + ret = tnr_dmd->io->write_reg(tnr_dmd->io,
> + CXD2880_IO_TGT_DMD,
> + 0x00, 0x10);
> + if (ret)
> + return ret;
> +
> + ret =
> + cxd2880_io_set_reg_bits(tnr_dmd->io,
> + CXD2880_IO_TGT_DMD,
> + 0x66, data, 0x01);
> + if (ret)
> + return ret;
> + }
> +
> + return ret;
> +}
> +
> +static int pid_ftr_setting(struct cxd2880_tnrdmd *tnr_dmd,
> + struct cxd2880_tnrdmd_pid_ftr_cfg
> + *pid_ftr_cfg)
> +{
> + int i;
> + int ret;
> +
> + if (!tnr_dmd)
> + return -EINVAL;
> +
> + ret = tnr_dmd->io->write_reg(tnr_dmd->io,
> + CXD2880_IO_TGT_DMD,
> + 0x00, 0x00);
> + if (ret)
> + return ret;
> +
> + if (!pid_ftr_cfg) {
> + ret = tnr_dmd->io->write_reg(tnr_dmd->io,
> + CXD2880_IO_TGT_DMD,
> + 0x50, 0x02);
> + if (ret)
> + return ret;
Just do:
return ret;
as there's nothing else to be done in this case.
> + } else {
With the above change, you can remove this else, with removes one
indentation from the code, making easier to read.
> + u8 data[65];
> +
> + data[0] = pid_ftr_cfg->is_negative ? 0x01 : 0x00;
> +
> + for (i = 0; i < 32; i++) {
> + if (pid_ftr_cfg->pid_cfg[i].is_en) {
> + data[1 + (i * 2)] = (pid_ftr_cfg->pid_cfg[i].pid >> 8) | 0x20;
> + data[2 + (i * 2)] = pid_ftr_cfg->pid_cfg[i].pid & 0xff;
> + } else {
> + data[1 + (i * 2)] = 0x00;
> + data[2 + (i * 2)] = 0x00;
> + }
> + }
> + ret = tnr_dmd->io->write_regs(tnr_dmd->io,
> + CXD2880_IO_TGT_DMD,
> + 0x50, data, 65);
> + if (ret)
> + return ret;
Again, just
return ret;
is enough.
> + }
> +
> + return 0;
> +}
> +
> +static int load_cfg_mem(struct cxd2880_tnrdmd *tnr_dmd)
> +{
> + int ret;
> + u8 i;
> +
> + if (!tnr_dmd)
> + return -EINVAL;
> +
> + for (i = 0; i < tnr_dmd->cfg_mem_last_entry; i++) {
> + ret = tnr_dmd->io->write_reg(tnr_dmd->io,
> + tnr_dmd->cfg_mem[i].tgt,
> + 0x00, tnr_dmd->cfg_mem[i].bank);
> + if (ret)
> + return ret;
> +
> + ret = cxd2880_io_set_reg_bits(tnr_dmd->io,
> + tnr_dmd->cfg_mem[i].tgt,
> + tnr_dmd->cfg_mem[i].address,
> + tnr_dmd->cfg_mem[i].value,
> + tnr_dmd->cfg_mem[i].bit_mask);
> + if (ret)
> + return ret;
> + }
> +
> + return ret;
better to use:
return 0;
as otherwise, gcc may complain about the usage of an uninitialized var, when
compiling with W=1.
> +}
> +
> +static int set_cfg_mem(struct cxd2880_tnrdmd *tnr_dmd,
> + enum cxd2880_io_tgt tgt,
> + u8 bank, u8 address, u8 value, u8 bit_mask)
> +{
> + u8 i;
> + u8 value_stored = 0;
> +
> + if (!tnr_dmd)
> + return -EINVAL;
> +
> + for (i = 0; i < tnr_dmd->cfg_mem_last_entry; i++) {
> + if ((value_stored == 0) &&
> + (tnr_dmd->cfg_mem[i].tgt == tgt) &&
> + (tnr_dmd->cfg_mem[i].bank == bank) &&
> + (tnr_dmd->cfg_mem[i].address == address)) {
> + tnr_dmd->cfg_mem[i].value &= ~bit_mask;
> + tnr_dmd->cfg_mem[i].value |= (value & bit_mask);
> +
> + tnr_dmd->cfg_mem[i].bit_mask |= bit_mask;
> +
> + value_stored = 1;
> + }
> + }
> +
> + if (value_stored == 0) {
Better to invert the condition here and remove one indent:
if (value_stored)
return 0;
> + if (tnr_dmd->cfg_mem_last_entry <
> + CXD2880_TNRDMD_MAX_CFG_MEM_COUNT) {
> + tnr_dmd->cfg_mem[tnr_dmd->cfg_mem_last_entry].tgt = tgt;
> + tnr_dmd->cfg_mem[tnr_dmd->cfg_mem_last_entry].bank = bank;
> + tnr_dmd->cfg_mem[tnr_dmd->cfg_mem_last_entry].address = address;
> + tnr_dmd->cfg_mem[tnr_dmd->cfg_mem_last_entry].value = (value & bit_mask);
> + tnr_dmd->cfg_mem[tnr_dmd->cfg_mem_last_entry].bit_mask = bit_mask;
> + tnr_dmd->cfg_mem_last_entry++;
> + } else {
> + return -EOVERFLOW;
Same here: if you invert the condition, checking first for overflow,
you can reduce another indent, preventing 80 cols warnings and making
simpler to review.
> + }
> + }
> +
> + return 0;
> +}
> +
> +int cxd2880_tnrdmd_create(struct cxd2880_tnrdmd *tnr_dmd,
> + struct cxd2880_io *io,
> + struct cxd2880_tnrdmd_create_param
> + *create_param)
> +{
> + if ((!tnr_dmd) || (!io) || (!create_param))
> + return -EINVAL;
> +
> + memset(tnr_dmd, 0, sizeof(struct cxd2880_tnrdmd));
> +
> + tnr_dmd->io = io;
> + tnr_dmd->create_param = *create_param;
> +
> + tnr_dmd->diver_mode = CXD2880_TNRDMD_DIVERMODE_SINGLE;
> + tnr_dmd->diver_sub = NULL;
> +
> + tnr_dmd->srl_ts_clk_mod_cnts = 1;
> + tnr_dmd->en_fef_intmtnt_base = 1;
> + tnr_dmd->en_fef_intmtnt_lite = 1;
> + tnr_dmd->rf_lvl_cmpstn = NULL;
> + tnr_dmd->lna_thrs_tbl_air = NULL;
> + tnr_dmd->lna_thrs_tbl_cable = NULL;
> + atomic_set(&tnr_dmd->cancel, 0);
> +
> + return 0;
> +}
> +
> +int cxd2880_tnrdmd_diver_create(struct cxd2880_tnrdmd
> + *tnr_dmd_main,
> + struct cxd2880_io *io_main,
> + struct cxd2880_tnrdmd *tnr_dmd_sub,
> + struct cxd2880_io *io_sub,
> + struct
> + cxd2880_tnrdmd_diver_create_param
> + *create_param)
> +{
> + struct cxd2880_tnrdmd_create_param *main_param, *sub_param;
> +
> + if ((!tnr_dmd_main) || (!io_main) || (!tnr_dmd_sub) || (!io_sub) ||
> + (!create_param))
> + return -EINVAL;
> +
> + memset(tnr_dmd_main, 0, sizeof(struct cxd2880_tnrdmd));
> + memset(tnr_dmd_sub, 0, sizeof(struct cxd2880_tnrdmd));
> +
> + main_param = &tnr_dmd_main->create_param;
> + sub_param = &tnr_dmd_sub->create_param;
> +
> + tnr_dmd_main->io = io_main;
> + tnr_dmd_main->diver_mode = CXD2880_TNRDMD_DIVERMODE_MAIN;
> + tnr_dmd_main->diver_sub = tnr_dmd_sub;
> + tnr_dmd_main->create_param.en_internal_ldo =
> + create_param->en_internal_ldo;
> +
> + main_param->ts_output_if = create_param->ts_output_if;
> + main_param->xtal_share_type = CXD2880_TNRDMD_XTAL_SHARE_MASTER;
> + main_param->xosc_cap = create_param->xosc_cap_main;
> + main_param->xosc_i = create_param->xosc_i_main;
> + main_param->is_cxd2881gg = create_param->is_cxd2881gg;
> + main_param->stationary_use = create_param->stationary_use;
> +
> + tnr_dmd_sub->io = io_sub;
> + tnr_dmd_sub->diver_mode = CXD2880_TNRDMD_DIVERMODE_SUB;
> + tnr_dmd_sub->diver_sub = NULL;
> +
> + sub_param->en_internal_ldo = create_param->en_internal_ldo;
> + sub_param->ts_output_if = create_param->ts_output_if;
> + sub_param->xtal_share_type = CXD2880_TNRDMD_XTAL_SHARE_SLAVE;
> + sub_param->xosc_cap = 0;
> + sub_param->xosc_i = create_param->xosc_i_sub;
> + sub_param->is_cxd2881gg = create_param->is_cxd2881gg;
> + sub_param->stationary_use = create_param->stationary_use;
> +
> + tnr_dmd_main->srl_ts_clk_mod_cnts = 1;
> + tnr_dmd_main->en_fef_intmtnt_base = 1;
> + tnr_dmd_main->en_fef_intmtnt_lite = 1;
> + tnr_dmd_main->rf_lvl_cmpstn = NULL;
> + tnr_dmd_main->lna_thrs_tbl_air = NULL;
> + tnr_dmd_main->lna_thrs_tbl_cable = NULL;
> +
> + tnr_dmd_sub->srl_ts_clk_mod_cnts = 1;
> + tnr_dmd_sub->en_fef_intmtnt_base = 1;
> + tnr_dmd_sub->en_fef_intmtnt_lite = 1;
> + tnr_dmd_sub->rf_lvl_cmpstn = NULL;
> + tnr_dmd_sub->lna_thrs_tbl_air = NULL;
> + tnr_dmd_sub->lna_thrs_tbl_cable = NULL;
> +
> + return 0;
> +}
> +
> +int cxd2880_tnrdmd_init1(struct cxd2880_tnrdmd *tnr_dmd)
> +{
> + int ret;
> +
> + if ((!tnr_dmd) || (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB))
> + return -EINVAL;
> +
> + tnr_dmd->chip_id = CXD2880_TNRDMD_CHIP_ID_UNKNOWN;
> + tnr_dmd->state = CXD2880_TNRDMD_STATE_UNKNOWN;
> + tnr_dmd->clk_mode = CXD2880_TNRDMD_CLOCKMODE_UNKNOWN;
> + tnr_dmd->frequency_khz = 0;
> + tnr_dmd->sys = CXD2880_DTV_SYS_UNKNOWN;
> + tnr_dmd->bandwidth = CXD2880_DTV_BW_UNKNOWN;
> + tnr_dmd->scan_mode = 0;
> + atomic_set(&tnr_dmd->cancel, 0);
> +
> + if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) {
> + tnr_dmd->diver_sub->chip_id = CXD2880_TNRDMD_CHIP_ID_UNKNOWN;
> + tnr_dmd->diver_sub->state = CXD2880_TNRDMD_STATE_UNKNOWN;
> + tnr_dmd->diver_sub->clk_mode = CXD2880_TNRDMD_CLOCKMODE_UNKNOWN;
> + tnr_dmd->diver_sub->frequency_khz = 0;
> + tnr_dmd->diver_sub->sys = CXD2880_DTV_SYS_UNKNOWN;
> + tnr_dmd->diver_sub->bandwidth = CXD2880_DTV_BW_UNKNOWN;
> + tnr_dmd->diver_sub->scan_mode = 0;
> + atomic_set(&tnr_dmd->diver_sub->cancel, 0);
> + }
> +
> + ret = cxd2880_tnrdmd_chip_id(tnr_dmd, &tnr_dmd->chip_id);
> + if (ret)
> + return ret;
> +
> + if (!CXD2880_TNRDMD_CHIP_ID_VALID(tnr_dmd->chip_id))
> + return -EOPNOTSUPP;
> +
> + if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) {
> + ret =
> + cxd2880_tnrdmd_chip_id(tnr_dmd->diver_sub,
> + &tnr_dmd->diver_sub->chip_id);
> + if (ret)
> + return ret;
> +
> + if (!CXD2880_TNRDMD_CHIP_ID_VALID(tnr_dmd->diver_sub->chip_id))
> + return -EOPNOTSUPP;
> + }
> +
> + ret = p_init1(tnr_dmd);
> + if (ret)
> + return ret;
> +
> + if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) {
> + ret = p_init1(tnr_dmd->diver_sub);
> + if (ret)
> + return ret;
> + }
> +
> + usleep_range(1000, 2000);
> +
> + if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) {
> + ret = p_init2(tnr_dmd->diver_sub);
> + if (ret)
> + return ret;
> + }
> +
> + ret = p_init2(tnr_dmd);
> + if (ret)
> + return ret;
> +
> + usleep_range(5000, 6000);
> +
> + ret = p_init3(tnr_dmd);
> + if (ret)
> + return ret;
> +
> + if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) {
> + ret = p_init3(tnr_dmd->diver_sub);
> + if (ret)
> + return ret;
> + }
> +
> + ret = rf_init1(tnr_dmd);
> + if (ret)
> + return ret;
> +
> + if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) {
> + ret = rf_init1(tnr_dmd->diver_sub);
> + if (ret)
> + return ret;
> + }
> +
> + return ret;
> +}
> +
> +int cxd2880_tnrdmd_init2(struct cxd2880_tnrdmd *tnr_dmd)
> +{
> + u8 cpu_task_completed;
> + int ret;
> +
> + if (!tnr_dmd)
> + return -EINVAL;
> +
> + if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
> + return -EINVAL;
> +
> + ret = cxd2880_tnrdmd_check_internal_cpu_status(tnr_dmd,
> + &cpu_task_completed);
> + if (ret)
> + return ret;
> +
> + if (!cpu_task_completed)
> + return -EBUSY;
> +
> + ret = rf_init2(tnr_dmd);
> + if (ret)
> + return ret;
> +
> + if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) {
> + ret = rf_init2(tnr_dmd->diver_sub);
> + if (ret)
> + return ret;
> + }
> +
> + ret = load_cfg_mem(tnr_dmd);
> + if (ret)
> + return ret;
> +
> + if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) {
> + ret = load_cfg_mem(tnr_dmd->diver_sub);
> + if (ret)
> + return ret;
> + }
> +
> + tnr_dmd->state = CXD2880_TNRDMD_STATE_SLEEP;
> +
> + if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN)
> + tnr_dmd->diver_sub->state = CXD2880_TNRDMD_STATE_SLEEP;
> +
> + return ret;
> +}
> +
> +int cxd2880_tnrdmd_check_internal_cpu_status(struct cxd2880_tnrdmd
> + *tnr_dmd,
> + u8 *task_completed)
> +{
> + u16 cpu_status = 0;
> + int ret;
> +
> + if ((!tnr_dmd) || (!task_completed))
> + return -EINVAL;
> +
> + if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
> + return -EINVAL;
> +
> + ret = cxd2880_tnrdmd_mon_internal_cpu_status(tnr_dmd, &cpu_status);
> + if (ret)
> + return ret;
> +
> + if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SINGLE) {
> + if (cpu_status == 0)
> + *task_completed = 1;
> + else
> + *task_completed = 0;
> +
> + return ret;
> + }
> + if (cpu_status != 0) {
> + *task_completed = 0;
> + return ret;
> + }
> +
> + ret = cxd2880_tnrdmd_mon_internal_cpu_status_sub(tnr_dmd, &cpu_status);
> + if (ret)
> + return ret;
> +
> + if (cpu_status == 0)
> + *task_completed = 1;
> + else
> + *task_completed = 0;
> +
> + return ret;
> +}
> +
> +int cxd2880_tnrdmd_common_tune_setting1(struct cxd2880_tnrdmd *tnr_dmd,
> + enum cxd2880_dtv_sys sys,
> + u32 frequency_khz,
> + enum cxd2880_dtv_bandwidth
> + bandwidth, u8 one_seg_opt,
> + u8 one_seg_opt_shft_dir)
> +{
> + u8 data;
> + enum cxd2880_tnrdmd_clockmode new_clk_mode =
> + CXD2880_TNRDMD_CLOCKMODE_A;
> + int shift_frequency_khz;
> + u8 cpu_task_completed;
> + int ret;
> +
> + if (!tnr_dmd)
> + return -EINVAL;
> +
> + if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
> + return -EINVAL;
> +
> + if ((tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP) &&
> + (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE))
> + return -EPERM;
> +
> + if (frequency_khz < 4000)
> + return -ERANGE;
> +
> + ret = cxd2880_tnrdmd_sleep(tnr_dmd);
> + if (ret)
> + return ret;
> +
> + ret = tnr_dmd->io->write_reg(tnr_dmd->io,
> + CXD2880_IO_TGT_SYS,
> + 0x00,
> + 0x00);
> + if (ret)
> + return ret;
> +
> + ret = tnr_dmd->io->read_regs(tnr_dmd->io,
> + CXD2880_IO_TGT_SYS,
> + 0x2b,
> + &data,
> + 1);
> + if (ret)
> + return ret;
> +
> + switch (sys) {
> + case CXD2880_DTV_SYS_DVBT:
> + if (data == 0x00) {
> + ret = t_power_x(tnr_dmd, 1);
> + if (ret)
> + return ret;
> +
> + if (tnr_dmd->diver_mode ==
> + CXD2880_TNRDMD_DIVERMODE_MAIN) {
> + ret = t_power_x(tnr_dmd->diver_sub, 1);
> + if (ret)
> + return ret;
> + }
> + }
> + break;
> +
> + case CXD2880_DTV_SYS_DVBT2:
> + if (data == 0x01) {
> + ret = t_power_x(tnr_dmd, 0);
> + if (ret)
> + return ret;
> +
> + if (tnr_dmd->diver_mode ==
> + CXD2880_TNRDMD_DIVERMODE_MAIN) {
> + ret = t_power_x(tnr_dmd->diver_sub, 0);
> + if (ret)
> + return ret;
> + }
> + }
> + break;
> +
> + default:
> + return -EINVAL;
> + }
> +
> + ret = spll_reset(tnr_dmd, new_clk_mode);
> + if (ret)
> + return ret;
> +
> + tnr_dmd->clk_mode = new_clk_mode;
> +
> + if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) {
> + ret = spll_reset(tnr_dmd->diver_sub, new_clk_mode);
> + if (ret)
> + return ret;
> +
> + tnr_dmd->diver_sub->clk_mode = new_clk_mode;
> + }
> +
> + ret = load_cfg_mem(tnr_dmd);
> + if (ret)
> + return ret;
> +
> + if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) {
> + ret = load_cfg_mem(tnr_dmd->diver_sub);
> + if (ret)
> + return ret;
> + }
> +
> + if (one_seg_opt) {
> + if (tnr_dmd->diver_mode ==
> + CXD2880_TNRDMD_DIVERMODE_MAIN) {
> + shift_frequency_khz = 350;
> + } else {
> + if (one_seg_opt_shft_dir)
> + shift_frequency_khz = 350;
> + else
> + shift_frequency_khz = -350;
> +
> + if (tnr_dmd->create_param.xtal_share_type ==
> + CXD2880_TNRDMD_XTAL_SHARE_SLAVE)
> + shift_frequency_khz *= -1;
> + }
> + } else {
> + if (tnr_dmd->diver_mode ==
> + CXD2880_TNRDMD_DIVERMODE_MAIN) {
> + shift_frequency_khz = 150;
> + } else {
> + switch (tnr_dmd->create_param.xtal_share_type) {
> + case CXD2880_TNRDMD_XTAL_SHARE_NONE:
> + case CXD2880_TNRDMD_XTAL_SHARE_EXTREF:
> + default:
> + shift_frequency_khz = 0;
> + break;
> + case CXD2880_TNRDMD_XTAL_SHARE_MASTER:
> + shift_frequency_khz = 150;
> + break;
> + case CXD2880_TNRDMD_XTAL_SHARE_SLAVE:
> + shift_frequency_khz = -150;
> + break;
> + }
> + }
> + }
> +
> + ret =
> + x_tune1(tnr_dmd, sys, frequency_khz, bandwidth,
> + tnr_dmd->is_cable_input, shift_frequency_khz);
> + if (ret)
> + return ret;
> +
> + if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) {
> + ret =
> + x_tune1(tnr_dmd->diver_sub, sys, frequency_khz,
> + bandwidth, tnr_dmd->is_cable_input,
> + -shift_frequency_khz);
> + if (ret)
> + return ret;
> + }
> +
> + usleep_range(10000, 11000);
> +
> + ret =
> + cxd2880_tnrdmd_check_internal_cpu_status(tnr_dmd,
> + &cpu_task_completed);
> + if (ret)
> + return ret;
> +
> + if (!cpu_task_completed)
> + return -EBUSY;
> +
> + ret =
> + x_tune2(tnr_dmd, bandwidth, tnr_dmd->clk_mode,
> + shift_frequency_khz);
> + if (ret)
> + return ret;
> +
> + if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) {
> + ret =
> + x_tune2(tnr_dmd->diver_sub, bandwidth,
> + tnr_dmd->diver_sub->clk_mode,
> + -shift_frequency_khz);
> + if (ret)
> + return ret;
> + }
> +
> + if (tnr_dmd->create_param.ts_output_if == CXD2880_TNRDMD_TSOUT_IF_TS) {
> + ret = set_ts_clk_mode_and_freq(tnr_dmd, sys);
> + if (ret)
> + return ret;
> + } else {
> + struct cxd2880_tnrdmd_pid_ftr_cfg *pid_ftr_cfg;
> +
> + if (tnr_dmd->pid_ftr_cfg_en)
> + pid_ftr_cfg = &tnr_dmd->pid_ftr_cfg;
> + else
> + pid_ftr_cfg = NULL;
> +
> + ret = pid_ftr_setting(tnr_dmd, pid_ftr_cfg);
> + if (ret)
> + return ret;
> + }
> +
> + return ret;
> +}
> +
> +int cxd2880_tnrdmd_common_tune_setting2(struct cxd2880_tnrdmd
> + *tnr_dmd,
> + enum cxd2880_dtv_sys sys,
> + u8 en_fef_intmtnt_ctrl)
> +{
> + int ret;
> +
> + if (!tnr_dmd)
> + return -EINVAL;
> +
> + if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
> + return -EINVAL;
> +
> + if ((tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP) &&
> + (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE))
> + return -EPERM;
> +
> + ret = x_tune3(tnr_dmd, sys, en_fef_intmtnt_ctrl);
> + if (ret)
> + return ret;
> +
> + if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) {
> + ret = x_tune3(tnr_dmd->diver_sub, sys, en_fef_intmtnt_ctrl);
> + if (ret)
> + return ret;
> + }
> +
> + if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) {
> + ret = x_tune4(tnr_dmd);
> + if (ret)
> + return ret;
> + }
Hmm... both ifs are checking the same thing. Just merge them.
> +
> + ret = cxd2880_tnrdmd_set_ts_output(tnr_dmd, 1);
> + if (ret)
> + return ret;
> +
> + return ret;
> +}
> +
> +int cxd2880_tnrdmd_sleep(struct cxd2880_tnrdmd *tnr_dmd)
> +{
> + int ret;
> +
> + if (!tnr_dmd)
> + return -EINVAL;
> +
> + if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
> + return -EINVAL;
> +
> + if (tnr_dmd->state == CXD2880_TNRDMD_STATE_SLEEP)
> + return 0;
> +
> + if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
> + return -EPERM;
-EPERM? What a state has to do with Linux permissions? This should
very likely be -EINVAL or -EBUSY. -EPERM is usually reserved to check
if the user has permissions to open/read/write (or to execute
some other syscall).
Very likely, the same applies to all places where this driver is
returning -EPERM.
> +
> + ret = cxd2880_tnrdmd_set_ts_output(tnr_dmd, 0);
> + if (ret)
> + return ret;
> +
> + if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) {
> + ret = x_sleep1(tnr_dmd);
> + if (ret)
> + return ret;
> + }
> +
> + ret = x_sleep2(tnr_dmd);
> + if (ret)
> + return ret;
> +
> + if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) {
> + ret = x_sleep2(tnr_dmd->diver_sub);
> + if (ret)
> + return ret;
> + }
> +
> + switch (tnr_dmd->sys) {
> + case CXD2880_DTV_SYS_DVBT:
> + ret = cxd2880_tnrdmd_dvbt_sleep_setting(tnr_dmd);
> + if (ret)
> + return ret;
> + break;
> +
> + case CXD2880_DTV_SYS_DVBT2:
> + ret = cxd2880_tnrdmd_dvbt2_sleep_setting(tnr_dmd);
> + if (ret)
> + return ret;
> + break;
> +
> + default:
> + return -EPERM;
> + }
> +
> + ret = x_sleep3(tnr_dmd);
> + if (ret)
> + return ret;
> +
> + if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) {
> + ret = x_sleep3(tnr_dmd->diver_sub);
> + if (ret)
> + return ret;
> + }
> +
> + ret = x_sleep4(tnr_dmd);
> + if (ret)
> + return ret;
> +
> + if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) {
> + ret = x_sleep4(tnr_dmd->diver_sub);
> + if (ret)
> + return ret;
> + }
> +
> + tnr_dmd->state = CXD2880_TNRDMD_STATE_SLEEP;
> + tnr_dmd->frequency_khz = 0;
> + tnr_dmd->sys = CXD2880_DTV_SYS_UNKNOWN;
> + tnr_dmd->bandwidth = CXD2880_DTV_BW_UNKNOWN;
> +
> + if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) {
> + tnr_dmd->diver_sub->state = CXD2880_TNRDMD_STATE_SLEEP;
> + tnr_dmd->diver_sub->frequency_khz = 0;
> + tnr_dmd->diver_sub->sys = CXD2880_DTV_SYS_UNKNOWN;
> + tnr_dmd->diver_sub->bandwidth = CXD2880_DTV_BW_UNKNOWN;
> + }
> +
> + return 0;
> +}
> +
> +int cxd2880_tnrdmd_set_cfg(struct cxd2880_tnrdmd *tnr_dmd,
> + enum cxd2880_tnrdmd_cfg_id id,
> + int value)
> +{
> + int ret;
> + u8 data[2] = { 0 };
> + u8 need_sub_setting = 0;
> +
> + if (!tnr_dmd)
> + return -EINVAL;
> +
> + if ((tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP) &&
> + (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE))
> + return -EPERM;
> +
> + switch (id) {
> + case CXD2880_TNRDMD_CFG_OUTPUT_SEL_MSB:
> + if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP)
> + return -EPERM;
> +
> + ret =
> + cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd,
> + CXD2880_IO_TGT_DMD,
> + 0x00, 0xc4,
> + value ? 0x00 : 0x10,
> + 0x10);
> + if (ret)
> + return ret;
> + break;
> +
> + case CXD2880_TNRDMD_CFG_TSVALID_ACTIVE_HI:
> + if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP)
> + return -EPERM;
> +
> + ret =
> + cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd,
> + CXD2880_IO_TGT_DMD,
> + 0x00, 0xc5,
> + value ? 0x00 : 0x02,
> + 0x02);
> + if (ret)
> + return ret;
> + break;
> +
> + case CXD2880_TNRDMD_CFG_TSSYNC_ACTIVE_HI:
> + if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP)
> + return -EPERM;
> +
> + ret =
> + cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd,
> + CXD2880_IO_TGT_DMD,
> + 0x00, 0xc5,
> + value ? 0x00 : 0x04,
> + 0x04);
> + if (ret)
> + return ret;
> + break;
> +
> + case CXD2880_TNRDMD_CFG_TSERR_ACTIVE_HI:
> + if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP)
> + return -EPERM;
> +
> + ret =
> + cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd,
> + CXD2880_IO_TGT_DMD,
> + 0x00, 0xcb,
> + value ? 0x00 : 0x01,
> + 0x01);
> + if (ret)
> + return ret;
> + break;
> +
> + case CXD2880_TNRDMD_CFG_LATCH_ON_POSEDGE:
> + if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP)
> + return -EPERM;
> +
> + ret =
> + cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd,
> + CXD2880_IO_TGT_DMD,
> + 0x00, 0xc5,
> + value ? 0x01 : 0x00,
> + 0x01);
> + if (ret)
> + return ret;
> + break;
> +
> + case CXD2880_TNRDMD_CFG_TSCLK_CONT:
> + if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP)
> + return -EPERM;
> +
> + tnr_dmd->srl_ts_clk_mod_cnts = value ? 0x01 : 0x00;
> + break;
> +
> + case CXD2880_TNRDMD_CFG_TSCLK_MASK:
> + if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP)
> + return -EPERM;
> +
> + if ((value < 0) || (value > 0x1f))
> + return -ERANGE;
> +
> + ret =
> + cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd,
> + CXD2880_IO_TGT_DMD,
> + 0x00, 0xc6, value,
> + 0x1f);
> + if (ret)
> + return ret;
> + break;
> +
> + case CXD2880_TNRDMD_CFG_TSVALID_MASK:
> + if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP)
> + return -EPERM;
> +
> + if ((value < 0) || (value > 0x1f))
> + return -ERANGE;
> +
> + ret =
> + cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd,
> + CXD2880_IO_TGT_DMD,
> + 0x00, 0xc8, value,
> + 0x1f);
> + if (ret)
> + return ret;
> + break;
> +
> + case CXD2880_TNRDMD_CFG_TSERR_MASK:
> + if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP)
> + return -EPERM;
> +
> + if ((value < 0) || (value > 0x1f))
> + return -ERANGE;
> +
> + ret =
> + cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd,
> + CXD2880_IO_TGT_DMD,
> + 0x00, 0xc9, value,
> + 0x1f);
> + if (ret)
> + return ret;
> + break;
> +
> + case CXD2880_TNRDMD_CFG_TSERR_VALID_DIS:
> + if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP)
> + return -EPERM;
> +
> + ret =
> + cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd,
> + CXD2880_IO_TGT_DMD,
> + 0x00, 0x91,
> + value ? 0x01 : 0x00,
> + 0x01);
> + if (ret)
> + return ret;
> + break;
> +
> + case CXD2880_TNRDMD_CFG_TSPIN_CURRENT:
> + if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP)
> + return -EPERM;
> +
> + ret =
> + cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd,
> + CXD2880_IO_TGT_SYS,
> + 0x00, 0x51, value,
> + 0x3f);
> + if (ret)
> + return ret;
> + break;
> +
> + case CXD2880_TNRDMD_CFG_TSPIN_PULLUP_MANUAL:
> + if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP)
> + return -EPERM;
> +
> + ret =
> + cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd,
> + CXD2880_IO_TGT_SYS,
> + 0x00, 0x50,
> + value ? 0x80 : 0x00,
> + 0x80);
> + if (ret)
> + return ret;
> + break;
> +
> + case CXD2880_TNRDMD_CFG_TSPIN_PULLUP:
> + if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP)
> + return -EPERM;
> +
> + ret =
> + cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd,
> + CXD2880_IO_TGT_SYS,
> + 0x00, 0x50, value,
> + 0x3f);
> + if (ret)
> + return ret;
> + break;
> +
> + case CXD2880_TNRDMD_CFG_TSCLK_FREQ:
> + if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP)
> + return -EPERM;
> +
> + if ((value < 0) || (value > 1))
> + return -ERANGE;
> +
> + tnr_dmd->srl_ts_clk_frq =
> + (enum cxd2880_tnrdmd_serial_ts_clk)value;
> + break;
> +
> + case CXD2880_TNRDMD_CFG_TSBYTECLK_MANUAL:
> + if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP)
> + return -EPERM;
> +
> + if ((value < 0) || (value > 0xff))
> + return -ERANGE;
> +
> + tnr_dmd->ts_byte_clk_manual_setting = value;
> +
> + break;
> +
> + case CXD2880_TNRDMD_CFG_TS_PACKET_GAP:
> + if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP)
> + return -EPERM;
> +
> + if ((value < 0) || (value > 7))
> + return -ERANGE;
> +
> + ret =
> + cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd,
> + CXD2880_IO_TGT_DMD,
> + 0x00, 0xd6, value,
> + 0x07);
> + if (ret)
> + return ret;
> +
> + break;
> +
> + case CXD2880_TNRDMD_CFG_TS_BACKWARDS_COMPATIBLE:
> + if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP)
> + return -EPERM;
> +
> + tnr_dmd->is_ts_backwards_compatible_mode = value ? 1 : 0;
> +
> + break;
> +
> + case CXD2880_TNRDMD_CFG_PWM_VALUE:
> + if ((value < 0) || (value > 0x1000))
> + return -ERANGE;
> +
> + ret =
> + cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd,
> + CXD2880_IO_TGT_DMD,
> + 0x00, 0x22,
> + value ? 0x01 : 0x00,
> + 0x01);
> + if (ret)
> + return ret;
> +
> + data[0] = (value >> 8) & 0x1f;
> + data[1] = value & 0xff;
> +
> + ret =
> + cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd,
> + CXD2880_IO_TGT_DMD,
> + 0x00, 0x23,
> + data[0], 0x1f);
> + if (ret)
> + return ret;
> +
> + ret =
> + cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd,
> + CXD2880_IO_TGT_DMD,
> + 0x00, 0x24,
> + data[1], 0xff);
> + if (ret)
> + return ret;
> +
> + break;
> +
> + case CXD2880_TNRDMD_CFG_INTERRUPT:
> + data[0] = (value >> 8) & 0xff;
> + data[1] = value & 0xff;
> + ret =
> + cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd,
> + CXD2880_IO_TGT_SYS,
> + 0x00, 0x48, data[0],
> + 0xff);
> + if (ret)
> + return ret;
> + ret =
> + cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd,
> + CXD2880_IO_TGT_SYS,
> + 0x00, 0x49, data[1],
> + 0xff);
> + if (ret)
> + return ret;
> + break;
> +
> + case CXD2880_TNRDMD_CFG_INTERRUPT_LOCK_SEL:
> + data[0] = value & 0x07;
> + ret =
> + cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd,
> + CXD2880_IO_TGT_SYS,
> + 0x00, 0x4a, data[0],
> + 0x07);
> + if (ret)
> + return ret;
> + break;
> +
> + case CXD2880_TNRDMD_CFG_INTERRUPT_INV_LOCK_SEL:
> + data[0] = (value & 0x07) << 3;
> + ret =
> + cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd,
> + CXD2880_IO_TGT_SYS,
> + 0x00, 0x4a, data[0],
> + 0x38);
> + if (ret)
> + return ret;
> + break;
> +
> + case CXD2880_TNRDMD_CFG_FIXED_CLOCKMODE:
> + if ((value < CXD2880_TNRDMD_CLOCKMODE_UNKNOWN) ||
> + (value > CXD2880_TNRDMD_CLOCKMODE_C))
> + return -ERANGE;
> + tnr_dmd->fixed_clk_mode = (enum cxd2880_tnrdmd_clockmode)value;
> + break;
> +
> + case CXD2880_TNRDMD_CFG_CABLE_INPUT:
> + tnr_dmd->is_cable_input = value ? 1 : 0;
> + break;
> +
> + case CXD2880_TNRDMD_CFG_DVBT2_FEF_INTERMITTENT_BASE:
> + tnr_dmd->en_fef_intmtnt_base = value ? 1 : 0;
> + break;
> +
> + case CXD2880_TNRDMD_CFG_DVBT2_FEF_INTERMITTENT_LITE:
> + tnr_dmd->en_fef_intmtnt_lite = value ? 1 : 0;
> + break;
> +
> + case CXD2880_TNRDMD_CFG_TS_BUF_ALMOST_EMPTY_THRS:
> + data[0] = (value >> 8) & 0x07;
> + data[1] = value & 0xff;
> + ret =
> + cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd,
> + CXD2880_IO_TGT_DMD,
> + 0x00, 0x99, data[0],
> + 0x07);
> + if (ret)
> + return ret;
> + ret =
> + cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd,
> + CXD2880_IO_TGT_DMD,
> + 0x00, 0x9a, data[1],
> + 0xff);
> + if (ret)
> + return ret;
> + break;
> +
> + case CXD2880_TNRDMD_CFG_TS_BUF_ALMOST_FULL_THRS:
> + data[0] = (value >> 8) & 0x07;
> + data[1] = value & 0xff;
> + ret =
> + cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd,
> + CXD2880_IO_TGT_DMD,
> + 0x00, 0x9b, data[0],
> + 0x07);
> + if (ret)
> + return ret;
> + ret =
> + cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd,
> + CXD2880_IO_TGT_DMD,
> + 0x00, 0x9c, data[1],
> + 0xff);
> + if (ret)
> + return ret;
> + break;
> +
> + case CXD2880_TNRDMD_CFG_TS_BUF_RRDY_THRS:
> + data[0] = (value >> 8) & 0x07;
> + data[1] = value & 0xff;
> + ret =
> + cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd,
> + CXD2880_IO_TGT_DMD,
> + 0x00, 0x9d, data[0],
> + 0x07);
> + if (ret)
> + return ret;
> + ret =
> + cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd,
> + CXD2880_IO_TGT_DMD,
> + 0x00, 0x9e, data[1],
> + 0xff);
> + if (ret)
> + return ret;
> + break;
> +
> + case CXD2880_TNRDMD_CFG_BLINDTUNE_DVBT2_FIRST:
> + tnr_dmd->blind_tune_dvbt2_first = value ? 1 : 0;
> + break;
> +
> + case CXD2880_TNRDMD_CFG_DVBT_BERN_PERIOD:
> + if ((value < 0) || (value > 31))
> + return -ERANGE;
> +
> + ret =
> + cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd,
> + CXD2880_IO_TGT_DMD,
> + 0x10, 0x60,
> + value & 0x1f, 0x1f);
> + if (ret)
> + return ret;
> + break;
> +
> + case CXD2880_TNRDMD_CFG_DVBT_VBER_PERIOD:
> + if ((value < 0) || (value > 7))
> + return -ERANGE;
> +
> + ret =
> + cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd,
> + CXD2880_IO_TGT_DMD,
> + 0x10, 0x6f,
> + value & 0x07, 0x07);
> + if (ret)
> + return ret;
> + break;
> +
> + case CXD2880_TNRDMD_CFG_DVBT2_BBER_MES:
> + if ((value < 0) || (value > 15))
> + return -ERANGE;
> +
> + ret =
> + cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd,
> + CXD2880_IO_TGT_DMD,
> + 0x20, 0x72,
> + value & 0x0f, 0x0f);
> + if (ret)
> + return ret;
> + break;
> +
> + case CXD2880_TNRDMD_CFG_DVBT2_LBER_MES:
> + if ((value < 0) || (value > 15))
> + return -ERANGE;
> +
> + ret =
> + cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd,
> + CXD2880_IO_TGT_DMD,
> + 0x20, 0x6f,
> + value & 0x0f, 0x0f);
> + if (ret)
> + return ret;
> + break;
> +
> + case CXD2880_TNRDMD_CFG_DVBT_PER_MES:
> + if ((value < 0) || (value > 15))
> + return -ERANGE;
> +
> + ret =
> + cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd,
> + CXD2880_IO_TGT_DMD,
> + 0x10, 0x5c,
> + value & 0x0f, 0x0f);
> + if (ret)
> + return ret;
> + break;
> +
> + case CXD2880_TNRDMD_CFG_DVBT2_PER_MES:
> + if ((value < 0) || (value > 15))
> + return -ERANGE;
> +
> + ret =
> + cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd,
> + CXD2880_IO_TGT_DMD,
> + 0x24, 0xdc,
> + value & 0x0f, 0x0f);
> + if (ret)
> + return ret;
> + break;
> +
> + default:
> + return -EINVAL;
> + }
> +
> + if (need_sub_setting &&
> + (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN)) {
> + ret = cxd2880_tnrdmd_set_cfg(tnr_dmd->diver_sub, id, value);
> + if (ret)
> + return ret;
> + }
> +
> + return 0;
> +}
> +
> +int cxd2880_tnrdmd_gpio_set_cfg(struct cxd2880_tnrdmd *tnr_dmd,
> + u8 id,
> + u8 en,
> + enum cxd2880_tnrdmd_gpio_mode mode,
> + u8 open_drain, u8 invert)
> +{
> + int ret;
> +
> + if (!tnr_dmd)
> + return -EINVAL;
> +
> + if (id > 2)
> + return -EINVAL;
> +
> + if (mode > CXD2880_TNRDMD_GPIO_MODE_EEW)
> + return -EINVAL;
> +
> + if ((tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP) &&
> + (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE))
> + return -EPERM;
> +
> + ret =
> + cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd, CXD2880_IO_TGT_SYS,
> + 0x00, 0x40 + id, mode,
> + 0x0f);
> + if (ret)
> + return ret;
> +
> + ret =
> + cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd, CXD2880_IO_TGT_SYS,
> + 0x00, 0x43,
> + open_drain ? (1 << id) : 0,
> + 1 << id);
> + if (ret)
> + return ret;
> +
> + ret =
> + cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd, CXD2880_IO_TGT_SYS,
> + 0x00, 0x44,
> + invert ? (1 << id) : 0,
> + 1 << id);
> + if (ret)
> + return ret;
> +
> + ret =
> + cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd, CXD2880_IO_TGT_SYS,
> + 0x00, 0x45,
> + en ? 0 : (1 << id),
> + 1 << id);
> + if (ret)
> + return ret;
> +
> + return 0;
> +}
> +
> +int cxd2880_tnrdmd_gpio_set_cfg_sub(struct cxd2880_tnrdmd *tnr_dmd,
> + u8 id,
> + u8 en,
> + enum cxd2880_tnrdmd_gpio_mode
> + mode, u8 open_drain, u8 invert)
> +{
> + int ret;
> +
> + if (!tnr_dmd)
> + return -EINVAL;
> +
> + if (tnr_dmd->diver_mode != CXD2880_TNRDMD_DIVERMODE_MAIN)
> + return -EINVAL;
> +
> + ret =
> + cxd2880_tnrdmd_gpio_set_cfg(tnr_dmd->diver_sub, id, en, mode,
> + open_drain, invert);
> +
> + return ret;
> +}
> +
> +int cxd2880_tnrdmd_gpio_read(struct cxd2880_tnrdmd *tnr_dmd,
> + u8 id, u8 *value)
> +{
> + u8 data = 0;
> + int ret;
> +
> + if ((!tnr_dmd) || (!value))
> + return -EINVAL;
> +
> + if (id > 2)
> + return -EINVAL;
> +
> + if ((tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP) &&
> + (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE))
> + return -EPERM;
> +
> + ret = tnr_dmd->io->write_reg(tnr_dmd->io,
> + CXD2880_IO_TGT_SYS,
> + 0x00, 0x0a);
> + if (ret)
> + return ret;
> + ret = tnr_dmd->io->read_regs(tnr_dmd->io,
> + CXD2880_IO_TGT_SYS,
> + 0x20, &data, 1);
> + if (ret)
> + return ret;
> +
> + *value = (data >> id) & 0x01;
> +
> + return 0;
> +}
> +
> +int cxd2880_tnrdmd_gpio_read_sub(struct cxd2880_tnrdmd *tnr_dmd,
> + u8 id, u8 *value)
> +{
> + int ret;
> +
> + if (!tnr_dmd)
> + return -EINVAL;
> +
> + if (tnr_dmd->diver_mode != CXD2880_TNRDMD_DIVERMODE_MAIN)
> + return -EINVAL;
> +
> + ret = cxd2880_tnrdmd_gpio_read(tnr_dmd->diver_sub, id, value);
> +
> + return ret;
> +}
> +
> +int cxd2880_tnrdmd_gpio_write(struct cxd2880_tnrdmd *tnr_dmd,
> + u8 id, u8 value)
> +{
> + int ret;
> +
> + if (!tnr_dmd)
> + return -EINVAL;
> +
> + if (id > 2)
> + return -EINVAL;
> +
> + if ((tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP) &&
> + (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE))
> + return -EPERM;
> +
> + ret = cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd,
> + CXD2880_IO_TGT_SYS,
> + 0x00, 0x46,
> + value ? (1 << id) : 0,
> + 1 << id);
> + if (ret)
> + return ret;
> +
> + return 0;
> +}
> +
> +int cxd2880_tnrdmd_gpio_write_sub(struct cxd2880_tnrdmd *tnr_dmd,
> + u8 id, u8 value)
> +{
> + int ret;
> +
> + if (!tnr_dmd)
> + return -EINVAL;
> +
> + if (tnr_dmd->diver_mode != CXD2880_TNRDMD_DIVERMODE_MAIN)
> + return -EINVAL;
> +
> + ret = cxd2880_tnrdmd_gpio_write(tnr_dmd->diver_sub, id, value);
> +
> + return ret;
> +}
> +
> +int cxd2880_tnrdmd_interrupt_read(struct cxd2880_tnrdmd *tnr_dmd,
> + u16 *value)
> +{
> + int ret;
> + u8 data[2] = { 0 };
> +
> + if ((!tnr_dmd) || (!value))
> + return -EINVAL;
> +
> + if ((tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP) &&
> + (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE))
> + return -EPERM;
> +
> + ret = tnr_dmd->io->write_reg(tnr_dmd->io,
> + CXD2880_IO_TGT_SYS,
> + 0x00, 0x0a);
> + if (ret)
> + return ret;
> + ret = tnr_dmd->io->read_regs(tnr_dmd->io,
> + CXD2880_IO_TGT_SYS,
> + 0x15, data, 2);
> + if (ret)
> + return ret;
> +
> + *value = (data[0] << 8) | data[1];
> +
> + return 0;
> +}
> +
> +int cxd2880_tnrdmd_interrupt_clear(struct cxd2880_tnrdmd *tnr_dmd,
> + u16 value)
> +{
> + int ret;
> + u8 data[2] = { 0 };
> +
> + if (!tnr_dmd)
> + return -EINVAL;
> +
> + if ((tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP) &&
> + (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE))
> + return -EPERM;
> +
> + ret = tnr_dmd->io->write_reg(tnr_dmd->io,
> + CXD2880_IO_TGT_SYS,
> + 0x00, 0x00);
> + if (ret)
> + return ret;
> +
> + data[0] = (value >> 8) & 0xff;
> + data[1] = value & 0xff;
> + ret = tnr_dmd->io->write_regs(tnr_dmd->io,
> + CXD2880_IO_TGT_SYS,
> + 0x3c, data, 2);
> + if (ret)
> + return ret;
> +
> + return 0;
> +}
> +
> +int cxd2880_tnrdmd_ts_buf_clear(struct cxd2880_tnrdmd *tnr_dmd,
> + u8 clear_overflow_flag,
> + u8 clear_underflow_flag,
> + u8 clear_buf)
> +{
> + int ret;
> + u8 data[2] = { 0 };
> +
> + if (!tnr_dmd)
> + return -EINVAL;
> +
> + if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
> + return -EINVAL;
> +
> + if ((tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP) &&
> + (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE))
> + return -EPERM;
> +
> + ret = tnr_dmd->io->write_reg(tnr_dmd->io,
> + CXD2880_IO_TGT_DMD,
> + 0x00, 0x00);
> + if (ret)
> + return ret;
> +
> + data[0] = clear_overflow_flag ? 0x02 : 0x00;
> + data[0] |= clear_underflow_flag ? 0x01 : 0x00;
> + data[1] = clear_buf ? 0x01 : 0x00;
> + ret = tnr_dmd->io->write_regs(tnr_dmd->io,
> + CXD2880_IO_TGT_DMD,
> + 0x9f, data, 2);
> + if (ret)
> + return ret;
> +
> + return 0;
> +}
> +
> +int cxd2880_tnrdmd_chip_id(struct cxd2880_tnrdmd *tnr_dmd,
> + enum cxd2880_tnrdmd_chip_id *chip_id)
> +{
> + int ret;
> + u8 data = 0;
> +
> + if ((!tnr_dmd) || (!chip_id))
> + return -EINVAL;
> +
> + ret = tnr_dmd->io->write_reg(tnr_dmd->io,
> + CXD2880_IO_TGT_SYS,
> + 0x00, 0x00);
> + if (ret)
> + return ret;
> + ret = tnr_dmd->io->read_regs(tnr_dmd->io,
> + CXD2880_IO_TGT_SYS,
> + 0xfd, &data, 1);
> + if (ret)
> + return ret;
> +
> + *chip_id = (enum cxd2880_tnrdmd_chip_id)data;
> +
> + return 0;
> +}
> +
> +int cxd2880_tnrdmd_set_and_save_reg_bits(struct cxd2880_tnrdmd
> + *tnr_dmd,
> + enum cxd2880_io_tgt tgt,
> + u8 bank, u8 address,
> + u8 value, u8 bit_mask)
> +{
> + int ret;
> +
> + if (!tnr_dmd)
> + return -EINVAL;
> +
> + if ((tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP) &&
> + (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE))
> + return -EPERM;
> +
> + ret = tnr_dmd->io->write_reg(tnr_dmd->io, tgt, 0x00, bank);
> + if (ret)
> + return ret;
> +
> + ret = cxd2880_io_set_reg_bits(tnr_dmd->io,
> + tgt, address, value, bit_mask);
> + if (ret)
> + return ret;
> +
> + ret = set_cfg_mem(tnr_dmd, tgt, bank, address, value, bit_mask);
> + if (ret)
> + return ret;
> +
> + return ret;
> +}
> +
> +int cxd2880_tnrdmd_set_scan_mode(struct cxd2880_tnrdmd *tnr_dmd,
> + enum cxd2880_dtv_sys sys,
> + u8 scan_mode_end)
> +{
> + if (!tnr_dmd)
> + return -EINVAL;
> +
> + if ((tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP) &&
> + (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE))
> + return -EPERM;
> +
> + CXD2880_ARG_UNUSED(sys);
> +
> + tnr_dmd->scan_mode = scan_mode_end;
> +
> + if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) {
> + int ret;
> +
> + ret =
> + cxd2880_tnrdmd_set_scan_mode(tnr_dmd->diver_sub, sys,
> + scan_mode_end);
> + if (ret)
> + return ret;
> + }
> +
> + return 0;
> +}
> +
> +int cxd2880_tnrdmd_set_pid_ftr(struct cxd2880_tnrdmd *tnr_dmd,
> + struct cxd2880_tnrdmd_pid_ftr_cfg
> + *pid_ftr_cfg)
> +{
> + int ret;
> +
> + if (!tnr_dmd)
> + return -EINVAL;
> +
> + if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
> + return -EINVAL;
> +
> + if ((tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP) &&
> + (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE))
> + return -EPERM;
> +
> + if (tnr_dmd->create_param.ts_output_if == CXD2880_TNRDMD_TSOUT_IF_TS)
> + return -EOPNOTSUPP;
> +
> + if (pid_ftr_cfg) {
> + tnr_dmd->pid_ftr_cfg = *pid_ftr_cfg;
> + tnr_dmd->pid_ftr_cfg_en = 1;
> + } else {
> + tnr_dmd->pid_ftr_cfg_en = 0;
> + }
> +
> + if (tnr_dmd->state == CXD2880_TNRDMD_STATE_ACTIVE) {
> + ret = pid_ftr_setting(tnr_dmd, pid_ftr_cfg);
> + if (ret)
> + return ret;
> + }
> +
> + return 0;
> +}
> +
> +int cxd2880_tnrdmd_set_rf_lvl_cmpstn(struct cxd2880_tnrdmd
> + *tnr_dmd,
> + int (*rf_lvl_cmpstn)
> + (struct cxd2880_tnrdmd *,
> + int *))
> +{
> + if (!tnr_dmd)
> + return -EINVAL;
> +
> + tnr_dmd->rf_lvl_cmpstn = rf_lvl_cmpstn;
> +
> + return 0;
> +}
> +
> +int cxd2880_tnrdmd_set_rf_lvl_cmpstn_sub(struct cxd2880_tnrdmd
> + *tnr_dmd,
> + int (*rf_lvl_cmpstn)
> + (struct cxd2880_tnrdmd *,
> + int *))
> +{
> + int ret;
> +
> + if (!tnr_dmd)
> + return -EINVAL;
> +
> + if (tnr_dmd->diver_mode != CXD2880_TNRDMD_DIVERMODE_MAIN)
> + return -EINVAL;
> +
> + ret = cxd2880_tnrdmd_set_rf_lvl_cmpstn(tnr_dmd->diver_sub,
> + rf_lvl_cmpstn);
> +
> + return ret;
> +}
> +
> +int cxd2880_tnrdmd_set_lna_thrs(struct cxd2880_tnrdmd *tnr_dmd,
> + struct cxd2880_tnrdmd_lna_thrs_tbl_air
> + *tbl_air,
> + struct cxd2880_tnrdmd_lna_thrs_tbl_cable
> + *tbl_cable)
> +{
> + if (!tnr_dmd)
> + return -EINVAL;
> +
> + tnr_dmd->lna_thrs_tbl_air = tbl_air;
> + tnr_dmd->lna_thrs_tbl_cable = tbl_cable;
> +
> + return 0;
> +}
> +
> +int cxd2880_tnrdmd_set_lna_thrs_sub(struct cxd2880_tnrdmd *tnr_dmd,
> + struct
> + cxd2880_tnrdmd_lna_thrs_tbl_air
> + *tbl_air,
> + struct cxd2880_tnrdmd_lna_thrs_tbl_cable
> + *tbl_cable)
> +{
> + int ret;
> +
> + if (!tnr_dmd)
> + return -EINVAL;
> +
> + if (tnr_dmd->diver_mode != CXD2880_TNRDMD_DIVERMODE_MAIN)
> + return -EINVAL;
> +
> + ret = cxd2880_tnrdmd_set_lna_thrs(tnr_dmd->diver_sub,
> + tbl_air, tbl_cable);
> +
> + return ret;
> +}
> +
> +int cxd2880_tnrdmd_set_ts_pin_high_low(struct cxd2880_tnrdmd
> + *tnr_dmd, u8 en, u8 value)
> +{
> + int ret;
> +
> + if (!tnr_dmd)
> + return -EINVAL;
> +
> + if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
> + return -EINVAL;
> +
> + if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP)
> + return -EPERM;
> +
> + if (tnr_dmd->create_param.ts_output_if != CXD2880_TNRDMD_TSOUT_IF_TS)
> + return -EOPNOTSUPP;
Again, EOPNOTSUPP is wrong. You should review all error codes, as there
are some very weird things with regards to it all over this patch.
> +
> + ret = tnr_dmd->io->write_reg(tnr_dmd->io,
> + CXD2880_IO_TGT_SYS,
> + 0x00, 0x00);
> + if (ret)
> + return ret;
> +
> + if (en) {
> + ret = tnr_dmd->io->write_reg(tnr_dmd->io,
> + CXD2880_IO_TGT_SYS,
> + 0x50, ((value & 0x1f) | 0x80));
> + if (ret)
> + return ret;
> +
> + ret = tnr_dmd->io->write_reg(tnr_dmd->io,
> + CXD2880_IO_TGT_SYS,
> + 0x52, (value & 0x1f));
> + if (ret)
> + return ret;
> + } else {
> + ret = cxd2880_io_write_multi_regs(tnr_dmd->io,
> + CXD2880_IO_TGT_SYS,
> + set_ts_pin_seq,
> + ARRAY_SIZE(set_ts_pin_seq));
> + if (ret)
> + return ret;
> +
> + ret = load_cfg_mem(tnr_dmd);
> + if (ret)
> + return ret;
> + }
> +
> + return 0;
> +}
> +
> +int cxd2880_tnrdmd_set_ts_output(struct cxd2880_tnrdmd *tnr_dmd,
> + u8 en)
> +{
> + int ret;
> +
> + if (!tnr_dmd)
> + return -EINVAL;
> +
> + if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
> + return -EINVAL;
> +
> + if ((tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP) &&
> + (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE))
> + return -EPERM;
> +
> + switch (tnr_dmd->create_param.ts_output_if) {
> + case CXD2880_TNRDMD_TSOUT_IF_TS:
> + if (en) {
> + ret = cxd2880_io_write_multi_regs(tnr_dmd->io,
> + CXD2880_IO_TGT_SYS,
> + set_ts_output_seq1,
> + ARRAY_SIZE(set_ts_output_seq1));
> + if (ret)
> + return ret;
> +
> + ret = cxd2880_io_write_multi_regs(tnr_dmd->io,
> + CXD2880_IO_TGT_DMD,
> + set_ts_output_seq2,
> + ARRAY_SIZE(set_ts_output_seq2));
> + if (ret)
> + return ret;
> + } else {
> + ret = cxd2880_io_write_multi_regs(tnr_dmd->io,
> + CXD2880_IO_TGT_DMD,
> + set_ts_output_seq3,
> + ARRAY_SIZE(set_ts_output_seq3));
> + if (ret)
> + return ret;
> +
> + ret = cxd2880_io_write_multi_regs(tnr_dmd->io,
> + CXD2880_IO_TGT_SYS,
> + set_ts_output_seq4,
> + ARRAY_SIZE(set_ts_output_seq4));
> + if (ret)
> + return ret;
> + }
> + break;
> +
> + case CXD2880_TNRDMD_TSOUT_IF_SPI:
> + break;
> +
> + case CXD2880_TNRDMD_TSOUT_IF_SDIO:
> + break;
> +
> + default:
> + return -EPERM;
> + }
> +
> + return 0;
> +}
> +
> +int slvt_freeze_reg(struct cxd2880_tnrdmd *tnr_dmd)
> +{
> + u8 data;
> + int ret;
> +
> + if (!tnr_dmd)
> + return -EINVAL;
> +
> + switch (tnr_dmd->create_param.ts_output_if) {
> + case CXD2880_TNRDMD_TSOUT_IF_SPI:
> + case CXD2880_TNRDMD_TSOUT_IF_SDIO:
> +
> + ret = tnr_dmd->io->read_regs(tnr_dmd->io,
> + CXD2880_IO_TGT_DMD,
> + 0x00, &data, 1);
> + if (ret)
> + return ret;
> +
> + break;
> + case CXD2880_TNRDMD_TSOUT_IF_TS:
> + default:
> + break;
> + }
> +
> + ret = tnr_dmd->io->write_reg(tnr_dmd->io,
> + CXD2880_IO_TGT_DMD,
> + 0x01, 0x01);
> + if (ret)
> + return ret;
> +
> + return ret;
> +}
> diff --git a/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd.h b/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd.h
> new file mode 100644
> index 000000000000..d740da0ff868
> --- /dev/null
> +++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd.h
> @@ -0,0 +1,391 @@
> +/*
> + * cxd2880_tnrdmd.h
> + * Sony CXD2880 DVB-T2/T tuner + demodulator driver
> + * common control interface
> + *
> + * Copyright (C) 2016, 2017 Sony Semiconductor Solutions Corporation
> + *
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms of the GNU General Public License as published by the
> + * Free Software Foundation; version 2 of the License.
> + *
> + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
> + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
> + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
> + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
> + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
> + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
> + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
> + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
> + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
> + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
> + *
> + * You should have received a copy of the GNU General Public License along
> + * with this program; if not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +#ifndef CXD2880_TNRDMD_H
> +#define CXD2880_TNRDMD_H
> +
> +#include <linux/atomic.h>
> +
> +#include "cxd2880_common.h"
> +#include "cxd2880_io.h"
> +#include "cxd2880_dtv.h"
> +#include "cxd2880_dvbt.h"
> +#include "cxd2880_dvbt2.h"
> +
> +#define CXD2880_TNRDMD_MAX_CFG_MEM_COUNT 100
> +
> +#define slvt_unfreeze_reg(tnr_dmd) ((void)((tnr_dmd)->io->write_reg\
> +((tnr_dmd)->io, CXD2880_IO_TGT_DMD, 0x01, 0x00)))
> +
> +#define CXD2880_TNRDMD_INTERRUPT_TYPE_BUF_UNDERFLOW 0x0001
> +#define CXD2880_TNRDMD_INTERRUPT_TYPE_BUF_OVERFLOW 0x0002
> +#define CXD2880_TNRDMD_INTERRUPT_TYPE_BUF_ALMOST_EMPTY 0x0004
> +#define CXD2880_TNRDMD_INTERRUPT_TYPE_BUF_ALMOST_FULL 0x0008
> +#define CXD2880_TNRDMD_INTERRUPT_TYPE_BUF_RRDY 0x0010
> +#define CXD2880_TNRDMD_INTERRUPT_TYPE_ILLEGAL_COMMAND 0x0020
> +#define CXD2880_TNRDMD_INTERRUPT_TYPE_ILLEGAL_ACCESS 0x0040
> +#define CXD2880_TNRDMD_INTERRUPT_TYPE_CPU_ERROR 0x0100
> +#define CXD2880_TNRDMD_INTERRUPT_TYPE_LOCK 0x0200
> +#define CXD2880_TNRDMD_INTERRUPT_TYPE_INV_LOCK 0x0400
> +#define CXD2880_TNRDMD_INTERRUPT_TYPE_NOOFDM 0x0800
> +#define CXD2880_TNRDMD_INTERRUPT_TYPE_EWS 0x1000
> +#define CXD2880_TNRDMD_INTERRUPT_TYPE_EEW 0x2000
> +#define CXD2880_TNRDMD_INTERRUPT_TYPE_FEC_FAIL 0x4000
> +
> +#define CXD2880_TNRDMD_INTERRUPT_LOCK_SEL_L1POST_OK 0x01
> +#define CXD2880_TNRDMD_INTERRUPT_LOCK_SEL_DMD_LOCK 0x02
> +#define CXD2880_TNRDMD_INTERRUPT_LOCK_SEL_TS_LOCK 0x04
> +
> +enum cxd2880_tnrdmd_chip_id {
> + CXD2880_TNRDMD_CHIP_ID_UNKNOWN = 0x00,
> + CXD2880_TNRDMD_CHIP_ID_CXD2880_ES1_0X = 0x62,
> + CXD2880_TNRDMD_CHIP_ID_CXD2880_ES1_11 = 0x6a
> +};
> +
> +#define CXD2880_TNRDMD_CHIP_ID_VALID(chip_id) \
> + (((chip_id) == CXD2880_TNRDMD_CHIP_ID_CXD2880_ES1_0X) || \
> + ((chip_id) == CXD2880_TNRDMD_CHIP_ID_CXD2880_ES1_11))
> +
> +enum cxd2880_tnrdmd_state {
> + CXD2880_TNRDMD_STATE_UNKNOWN,
> + CXD2880_TNRDMD_STATE_SLEEP,
> + CXD2880_TNRDMD_STATE_ACTIVE,
> + CXD2880_TNRDMD_STATE_INVALID
> +};
> +
> +enum cxd2880_tnrdmd_divermode {
> + CXD2880_TNRDMD_DIVERMODE_SINGLE,
> + CXD2880_TNRDMD_DIVERMODE_MAIN,
> + CXD2880_TNRDMD_DIVERMODE_SUB
> +};
> +
> +enum cxd2880_tnrdmd_clockmode {
> + CXD2880_TNRDMD_CLOCKMODE_UNKNOWN,
> + CXD2880_TNRDMD_CLOCKMODE_A,
> + CXD2880_TNRDMD_CLOCKMODE_B,
> + CXD2880_TNRDMD_CLOCKMODE_C
> +};
> +
> +enum cxd2880_tnrdmd_tsout_if {
> + CXD2880_TNRDMD_TSOUT_IF_TS,
> + CXD2880_TNRDMD_TSOUT_IF_SPI,
> + CXD2880_TNRDMD_TSOUT_IF_SDIO
> +};
> +
> +enum cxd2880_tnrdmd_xtal_share {
> + CXD2880_TNRDMD_XTAL_SHARE_NONE,
> + CXD2880_TNRDMD_XTAL_SHARE_EXTREF,
> + CXD2880_TNRDMD_XTAL_SHARE_MASTER,
> + CXD2880_TNRDMD_XTAL_SHARE_SLAVE
> +};
> +
> +enum cxd2880_tnrdmd_spectrum_sense {
> + CXD2880_TNRDMD_SPECTRUM_NORMAL,
> + CXD2880_TNRDMD_SPECTRUM_INV
> +};
> +
> +enum cxd2880_tnrdmd_cfg_id {
> + CXD2880_TNRDMD_CFG_OUTPUT_SEL_MSB,
> + CXD2880_TNRDMD_CFG_TSVALID_ACTIVE_HI,
> + CXD2880_TNRDMD_CFG_TSSYNC_ACTIVE_HI,
> + CXD2880_TNRDMD_CFG_TSERR_ACTIVE_HI,
> + CXD2880_TNRDMD_CFG_LATCH_ON_POSEDGE,
> + CXD2880_TNRDMD_CFG_TSCLK_CONT,
> + CXD2880_TNRDMD_CFG_TSCLK_MASK,
> + CXD2880_TNRDMD_CFG_TSVALID_MASK,
> + CXD2880_TNRDMD_CFG_TSERR_MASK,
> + CXD2880_TNRDMD_CFG_TSERR_VALID_DIS,
> + CXD2880_TNRDMD_CFG_TSPIN_CURRENT,
> + CXD2880_TNRDMD_CFG_TSPIN_PULLUP_MANUAL,
> + CXD2880_TNRDMD_CFG_TSPIN_PULLUP,
> + CXD2880_TNRDMD_CFG_TSCLK_FREQ,
> + CXD2880_TNRDMD_CFG_TSBYTECLK_MANUAL,
> + CXD2880_TNRDMD_CFG_TS_PACKET_GAP,
> + CXD2880_TNRDMD_CFG_TS_BACKWARDS_COMPATIBLE,
> + CXD2880_TNRDMD_CFG_PWM_VALUE,
> + CXD2880_TNRDMD_CFG_INTERRUPT,
> + CXD2880_TNRDMD_CFG_INTERRUPT_LOCK_SEL,
> + CXD2880_TNRDMD_CFG_INTERRUPT_INV_LOCK_SEL,
> + CXD2880_TNRDMD_CFG_TS_BUF_ALMOST_EMPTY_THRS,
> + CXD2880_TNRDMD_CFG_TS_BUF_ALMOST_FULL_THRS,
> + CXD2880_TNRDMD_CFG_TS_BUF_RRDY_THRS,
> + CXD2880_TNRDMD_CFG_FIXED_CLOCKMODE,
> + CXD2880_TNRDMD_CFG_CABLE_INPUT,
> + CXD2880_TNRDMD_CFG_DVBT2_FEF_INTERMITTENT_BASE,
> + CXD2880_TNRDMD_CFG_DVBT2_FEF_INTERMITTENT_LITE,
> + CXD2880_TNRDMD_CFG_BLINDTUNE_DVBT2_FIRST,
> + CXD2880_TNRDMD_CFG_DVBT_BERN_PERIOD,
> + CXD2880_TNRDMD_CFG_DVBT_VBER_PERIOD,
> + CXD2880_TNRDMD_CFG_DVBT_PER_MES,
> + CXD2880_TNRDMD_CFG_DVBT2_BBER_MES,
> + CXD2880_TNRDMD_CFG_DVBT2_LBER_MES,
> + CXD2880_TNRDMD_CFG_DVBT2_PER_MES,
> +};
> +
> +enum cxd2880_tnrdmd_lock_result {
> + CXD2880_TNRDMD_LOCK_RESULT_NOTDETECT,
> + CXD2880_TNRDMD_LOCK_RESULT_LOCKED,
> + CXD2880_TNRDMD_LOCK_RESULT_UNLOCKED
> +};
> +
> +enum cxd2880_tnrdmd_gpio_mode {
> + CXD2880_TNRDMD_GPIO_MODE_OUTPUT = 0x00,
> + CXD2880_TNRDMD_GPIO_MODE_INPUT = 0x01,
> + CXD2880_TNRDMD_GPIO_MODE_INT = 0x02,
> + CXD2880_TNRDMD_GPIO_MODE_FEC_FAIL = 0x03,
> + CXD2880_TNRDMD_GPIO_MODE_PWM = 0x04,
> + CXD2880_TNRDMD_GPIO_MODE_EWS = 0x05,
> + CXD2880_TNRDMD_GPIO_MODE_EEW = 0x06
> +};
> +
> +enum cxd2880_tnrdmd_serial_ts_clk {
> + CXD2880_TNRDMD_SERIAL_TS_CLK_FULL,
> + CXD2880_TNRDMD_SERIAL_TS_CLK_HALF
> +};
> +
> +struct cxd2880_tnrdmd_cfg_mem {
> + enum cxd2880_io_tgt tgt;
> + u8 bank;
> + u8 address;
> + u8 value;
> + u8 bit_mask;
> +};
> +
> +struct cxd2880_tnrdmd_pid_cfg {
> + u8 is_en;
> + u16 pid;
> +};
> +
> +struct cxd2880_tnrdmd_pid_ftr_cfg {
> + u8 is_negative;
> + struct cxd2880_tnrdmd_pid_cfg pid_cfg[32];
> +};
> +
> +struct cxd2880_tnrdmd_ts_buf_info {
> + u8 read_ready;
> + u8 almost_full;
> + u8 almost_empty;
> + u8 overflow;
> + u8 underflow;
> + u16 packet_num;
> +};
> +
> +struct cxd2880_tnrdmd_lna_thrs {
> + u8 off_on;
> + u8 on_off;
> +};
> +
> +struct cxd2880_tnrdmd_lna_thrs_tbl_air {
> + struct cxd2880_tnrdmd_lna_thrs thrs[24];
> +};
> +
> +struct cxd2880_tnrdmd_lna_thrs_tbl_cable {
> + struct cxd2880_tnrdmd_lna_thrs thrs[32];
> +};
> +
> +struct cxd2880_tnrdmd_create_param {
> + enum cxd2880_tnrdmd_tsout_if ts_output_if;
> + u8 en_internal_ldo;
> + enum cxd2880_tnrdmd_xtal_share xtal_share_type;
> + u8 xosc_cap;
> + u8 xosc_i;
> + u8 is_cxd2881gg;
> + u8 stationary_use;
> +};
> +
> +struct cxd2880_tnrdmd_diver_create_param {
> + enum cxd2880_tnrdmd_tsout_if ts_output_if;
> + u8 en_internal_ldo;
> + u8 xosc_cap_main;
> + u8 xosc_i_main;
> + u8 xosc_i_sub;
> + u8 is_cxd2881gg;
> + u8 stationary_use;
> +};
> +
> +struct cxd2880_tnrdmd {
> + struct cxd2880_tnrdmd *diver_sub;
> + struct cxd2880_io *io;
> + struct cxd2880_tnrdmd_create_param create_param;
> + enum cxd2880_tnrdmd_divermode diver_mode;
> + enum cxd2880_tnrdmd_clockmode fixed_clk_mode;
> + u8 is_cable_input;
> + u8 en_fef_intmtnt_base;
> + u8 en_fef_intmtnt_lite;
> + u8 blind_tune_dvbt2_first;
> + int (*rf_lvl_cmpstn)(struct cxd2880_tnrdmd *tnr_dmd,
> + int *rf_lvl_db);
> + struct cxd2880_tnrdmd_lna_thrs_tbl_air *lna_thrs_tbl_air;
> + struct cxd2880_tnrdmd_lna_thrs_tbl_cable *lna_thrs_tbl_cable;
> + u8 srl_ts_clk_mod_cnts;
> + enum cxd2880_tnrdmd_serial_ts_clk srl_ts_clk_frq;
> + u8 ts_byte_clk_manual_setting;
> + u8 is_ts_backwards_compatible_mode;
> + struct cxd2880_tnrdmd_cfg_mem cfg_mem[CXD2880_TNRDMD_MAX_CFG_MEM_COUNT];
> + u8 cfg_mem_last_entry;
> + struct cxd2880_tnrdmd_pid_ftr_cfg pid_ftr_cfg;
> + u8 pid_ftr_cfg_en;
> + void *user;
> + enum cxd2880_tnrdmd_chip_id chip_id;
> + enum cxd2880_tnrdmd_state state;
> + enum cxd2880_tnrdmd_clockmode clk_mode;
> + u32 frequency_khz;
> + enum cxd2880_dtv_sys sys;
> + enum cxd2880_dtv_bandwidth bandwidth;
> + u8 scan_mode;
> + atomic_t cancel;
> +};
> +
> +int cxd2880_tnrdmd_create(struct cxd2880_tnrdmd *tnr_dmd,
> + struct cxd2880_io *io,
> + struct cxd2880_tnrdmd_create_param
> + *create_param);
> +
> +int cxd2880_tnrdmd_diver_create(struct cxd2880_tnrdmd
> + *tnr_dmd_main,
> + struct cxd2880_io *io_main,
> + struct cxd2880_tnrdmd *tnr_dmd_sub,
> + struct cxd2880_io *io_sub,
> + struct
> + cxd2880_tnrdmd_diver_create_param
> + *create_param);
> +
> +int cxd2880_tnrdmd_init1(struct cxd2880_tnrdmd *tnr_dmd);
> +
> +int cxd2880_tnrdmd_init2(struct cxd2880_tnrdmd *tnr_dmd);
> +
> +int cxd2880_tnrdmd_check_internal_cpu_status(struct cxd2880_tnrdmd
> + *tnr_dmd,
> + u8 *task_completed);
> +
> +int cxd2880_tnrdmd_common_tune_setting1(struct cxd2880_tnrdmd
> + *tnr_dmd,
> + enum cxd2880_dtv_sys sys,
> + u32 frequency_khz,
> + enum cxd2880_dtv_bandwidth
> + bandwidth, u8 one_seg_opt,
> + u8 one_seg_opt_shft_dir);
> +
> +int cxd2880_tnrdmd_common_tune_setting2(struct cxd2880_tnrdmd
> + *tnr_dmd,
> + enum cxd2880_dtv_sys sys,
> + u8 en_fef_intmtnt_ctrl);
> +
> +int cxd2880_tnrdmd_sleep(struct cxd2880_tnrdmd *tnr_dmd);
> +
> +int cxd2880_tnrdmd_set_cfg(struct cxd2880_tnrdmd *tnr_dmd,
> + enum cxd2880_tnrdmd_cfg_id id,
> + int value);
> +
> +int cxd2880_tnrdmd_gpio_set_cfg(struct cxd2880_tnrdmd *tnr_dmd,
> + u8 id,
> + u8 en,
> + enum cxd2880_tnrdmd_gpio_mode mode,
> + u8 open_drain, u8 invert);
> +
> +int cxd2880_tnrdmd_gpio_set_cfg_sub(struct cxd2880_tnrdmd *tnr_dmd,
> + u8 id,
> + u8 en,
> + enum cxd2880_tnrdmd_gpio_mode
> + mode, u8 open_drain,
> + u8 invert);
> +
> +int cxd2880_tnrdmd_gpio_read(struct cxd2880_tnrdmd *tnr_dmd,
> + u8 id, u8 *value);
> +
> +int cxd2880_tnrdmd_gpio_read_sub(struct cxd2880_tnrdmd *tnr_dmd,
> + u8 id, u8 *value);
> +
> +int cxd2880_tnrdmd_gpio_write(struct cxd2880_tnrdmd *tnr_dmd,
> + u8 id, u8 value);
> +
> +int cxd2880_tnrdmd_gpio_write_sub(struct cxd2880_tnrdmd *tnr_dmd,
> + u8 id, u8 value);
> +
> +int cxd2880_tnrdmd_interrupt_read(struct cxd2880_tnrdmd *tnr_dmd,
> + u16 *value);
> +
> +int cxd2880_tnrdmd_interrupt_clear(struct cxd2880_tnrdmd *tnr_dmd,
> + u16 value);
> +
> +int cxd2880_tnrdmd_ts_buf_clear(struct cxd2880_tnrdmd *tnr_dmd,
> + u8 clear_overflow_flag,
> + u8 clear_underflow_flag,
> + u8 clear_buf);
> +
> +int cxd2880_tnrdmd_chip_id(struct cxd2880_tnrdmd *tnr_dmd,
> + enum cxd2880_tnrdmd_chip_id *chip_id);
> +
> +int cxd2880_tnrdmd_set_and_save_reg_bits(struct cxd2880_tnrdmd
> + *tnr_dmd,
> + enum cxd2880_io_tgt tgt,
> + u8 bank, u8 address,
> + u8 value, u8 bit_mask);
> +
> +int cxd2880_tnrdmd_set_scan_mode(struct cxd2880_tnrdmd *tnr_dmd,
> + enum cxd2880_dtv_sys sys,
> + u8 scan_mode_end);
> +
> +int cxd2880_tnrdmd_set_pid_ftr(struct cxd2880_tnrdmd *tnr_dmd,
> + struct cxd2880_tnrdmd_pid_ftr_cfg
> + *pid_ftr_cfg);
> +
> +int cxd2880_tnrdmd_set_rf_lvl_cmpstn(struct cxd2880_tnrdmd
> + *tnr_dmd,
> + int (*rf_lvl_cmpstn)
> + (struct cxd2880_tnrdmd *,
> + int *));
> +
> +int cxd2880_tnrdmd_set_rf_lvl_cmpstn_sub(struct cxd2880_tnrdmd *tnr_dmd,
> + int (*rf_lvl_cmpstn)
> + (struct cxd2880_tnrdmd *,
> + int *));
> +
> +int cxd2880_tnrdmd_set_lna_thrs(struct cxd2880_tnrdmd *tnr_dmd,
> + struct
> + cxd2880_tnrdmd_lna_thrs_tbl_air
> + *tbl_air,
> + struct
> + cxd2880_tnrdmd_lna_thrs_tbl_cable
> + *tbl_cable);
> +
> +int cxd2880_tnrdmd_set_lna_thrs_sub(struct cxd2880_tnrdmd *tnr_dmd,
> + struct
> + cxd2880_tnrdmd_lna_thrs_tbl_air
> + *tbl_air,
> + struct
> + cxd2880_tnrdmd_lna_thrs_tbl_cable
> + *tbl_cable);
> +
> +int cxd2880_tnrdmd_set_ts_pin_high_low(struct cxd2880_tnrdmd
> + *tnr_dmd, u8 en, u8 value);
> +
> +int cxd2880_tnrdmd_set_ts_output(struct cxd2880_tnrdmd *tnr_dmd,
> + u8 en);
> +
> +int slvt_freeze_reg(struct cxd2880_tnrdmd *tnr_dmd);
> +
> +#endif
> diff --git a/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_driver_version.h b/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_driver_version.h
> new file mode 100644
> index 000000000000..ff29e747d5b7
> --- /dev/null
> +++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_driver_version.h
> @@ -0,0 +1,29 @@
> +/*
> + * cxd2880_tnrdmd_driver_version.h
> + * Sony CXD2880 DVB-T2/T tuner + demodulator driver
> + * version information
> + *
> + * Copyright (C) 2016, 2017 Sony Semiconductor Solutions Corporation
> + *
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms of the GNU General Public License as published by the
> + * Free Software Foundation; version 2 of the License.
> + *
> + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
> + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
> + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
> + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
> + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
> + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
> + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
> + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
> + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
> + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
> + *
> + * You should have received a copy of the GNU General Public License along
> + * with this program; if not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +#define CXD2880_TNRDMD_DRIVER_VERSION "1.4.1 - 1.0.3"
> +
> +#define CXD2880_TNRDMD_DRIVER_RELEASE_DATE "2017-10-03"
> diff --git a/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_mon.c b/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_mon.c
> new file mode 100644
> index 000000000000..2b40b25ee2b8
> --- /dev/null
> +++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_mon.c
> @@ -0,0 +1,218 @@
> +/*
> + * cxd2880_tnrdmd_mon.c
> + * Sony CXD2880 DVB-T2/T tuner + demodulator driver
> + * common monitor functions
> + *
> + * Copyright (C) 2016, 2017 Sony Semiconductor Solutions Corporation
> + *
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms of the GNU General Public License as published by the
> + * Free Software Foundation; version 2 of the License.
> + *
> + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
> + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
> + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
> + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
> + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
> + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
> + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
> + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
> + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
> + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
> + *
> + * You should have received a copy of the GNU General Public License along
> + * with this program; if not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +#include "cxd2880_common.h"
> +#include "cxd2880_tnrdmd_mon.h"
> +
> +static const u8 rf_lvl_seq[2] = {
> + 0x80, 0x00,
> +};
> +
> +int cxd2880_tnrdmd_mon_rf_lvl(struct cxd2880_tnrdmd *tnr_dmd,
> + int *rf_lvl_db)
> +{
> + u8 rdata[2];
> + int ret;
> +
> + if ((!tnr_dmd) || (!rf_lvl_db))
> + return -EINVAL;
> +
> + if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
> + return -EPERM;
> +
> + ret = tnr_dmd->io->write_reg(tnr_dmd->io,
> + CXD2880_IO_TGT_DMD,
> + 0x00, 0x00);
> + if (ret)
> + return ret;
> +
> + ret = tnr_dmd->io->write_reg(tnr_dmd->io,
> + CXD2880_IO_TGT_DMD,
> + 0x10, 0x01);
> + if (ret)
> + return ret;
> +
> + ret = tnr_dmd->io->write_reg(tnr_dmd->io,
> + CXD2880_IO_TGT_SYS,
> + 0x00, 0x10);
> + if (ret)
> + return ret;
> +
> + ret = tnr_dmd->io->write_regs(tnr_dmd->io,
> + CXD2880_IO_TGT_SYS,
> + 0x5b, rf_lvl_seq, 2);
> + if (ret)
> + return ret;
> +
> + usleep_range(2000, 3000);
> +
> + ret = tnr_dmd->io->write_reg(tnr_dmd->io,
> + CXD2880_IO_TGT_SYS,
> + 0x00, 0x1a);
> + if (ret)
> + return ret;
> +
> + ret = tnr_dmd->io->read_regs(tnr_dmd->io,
> + CXD2880_IO_TGT_SYS,
> + 0x15, rdata, 2);
> + if (ret)
> + return ret;
> +
> + if ((rdata[0] != 0) || (rdata[1] != 0))
> + return -EPERM;
> +
> + ret = tnr_dmd->io->read_regs(tnr_dmd->io,
> + CXD2880_IO_TGT_SYS,
> + 0x11, rdata, 2);
> + if (ret)
> + return ret;
> +
> + *rf_lvl_db =
> + cxd2880_convert2s_complement((rdata[0] << 3) |
> + ((rdata[1] & 0xe0) >> 5), 11);
> +
> + *rf_lvl_db *= 125;
> +
> + ret = tnr_dmd->io->write_reg(tnr_dmd->io,
> + CXD2880_IO_TGT_DMD,
> + 0x00, 0x00);
> + if (ret)
> + return ret;
> +
> + ret = tnr_dmd->io->write_reg(tnr_dmd->io,
> + CXD2880_IO_TGT_DMD,
> + 0x10, 0x00);
> + if (ret)
> + return ret;
> +
> + if (tnr_dmd->rf_lvl_cmpstn) {
> + ret = tnr_dmd->rf_lvl_cmpstn(tnr_dmd, rf_lvl_db);
> + if (ret)
> + return ret;
> + }
> +
> + return ret;
> +}
> +
> +int cxd2880_tnrdmd_mon_rf_lvl_sub(struct cxd2880_tnrdmd *tnr_dmd,
> + int *rf_lvl_db)
> +{
> + int ret;
> +
> + if ((!tnr_dmd) || (!rf_lvl_db))
> + return -EINVAL;
> +
> + if (tnr_dmd->diver_mode != CXD2880_TNRDMD_DIVERMODE_MAIN)
> + return -EINVAL;
> +
> + ret = cxd2880_tnrdmd_mon_rf_lvl(tnr_dmd->diver_sub, rf_lvl_db);
> +
> + return ret;
> +}
> +
> +int cxd2880_tnrdmd_mon_internal_cpu_status(struct cxd2880_tnrdmd
> + *tnr_dmd, u16 *status)
> +{
> + u8 data[2] = { 0 };
> + int ret;
> +
> + if ((!tnr_dmd) || (!status))
> + return -EINVAL;
> +
> + ret = tnr_dmd->io->write_reg(tnr_dmd->io,
> + CXD2880_IO_TGT_SYS,
> + 0x00, 0x1a);
> + if (ret)
> + return ret;
> + ret = tnr_dmd->io->read_regs(tnr_dmd->io,
> + CXD2880_IO_TGT_SYS,
> + 0x15, data, 2);
> + if (ret)
> + return ret;
> +
> + *status = (data[0] << 8) | data[1];
> +
> + return 0;
> +}
> +
> +int cxd2880_tnrdmd_mon_internal_cpu_status_sub(struct
> + cxd2880_tnrdmd
> + *tnr_dmd,
> + u16 *status)
> +{
> + int ret;
> +
> + if ((!tnr_dmd) || (!status))
> + return -EINVAL;
> +
> + if (tnr_dmd->diver_mode != CXD2880_TNRDMD_DIVERMODE_MAIN)
> + return -EINVAL;
> +
> + ret =
> + cxd2880_tnrdmd_mon_internal_cpu_status(tnr_dmd->diver_sub, status);
> +
> + return ret;
> +}
> +
> +int cxd2880_tnrdmd_mon_ts_buf_info(struct cxd2880_tnrdmd *tnr_dmd,
> + struct
> + cxd2880_tnrdmd_ts_buf_info
> + *info)
> +{
> + u8 data[3] = { 0 };
> + int ret;
> +
> + if ((!tnr_dmd) || (!info))
> + return -EINVAL;
> +
> + if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
> + return -EINVAL;
> +
> + if ((tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP) &&
> + (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE))
> + return -EPERM;
> +
> + ret = tnr_dmd->io->write_reg(tnr_dmd->io,
> + CXD2880_IO_TGT_DMD,
> + 0x00, 0x0a);
> + if (ret)
> + return ret;
> + ret = tnr_dmd->io->read_regs(tnr_dmd->io,
> + CXD2880_IO_TGT_DMD,
> + 0x50, data, 3);
> + if (ret)
> + return ret;
> +
> + info->read_ready = (data[0] & 0x10) ? 0x01 : 0x00;
> + info->almost_full = (data[0] & 0x08) ? 0x01 : 0x00;
> + info->almost_empty = (data[0] & 0x04) ? 0x01 : 0x00;
> + info->overflow = (data[0] & 0x02) ? 0x01 : 0x00;
> + info->underflow = (data[0] & 0x01) ? 0x01 : 0x00;
> +
> + info->packet_num = ((data[1] & 0x07) << 8) | data[2];
> +
> + return ret;
> +}
> diff --git a/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_mon.h b/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_mon.h
> new file mode 100644
> index 000000000000..a4c34f56a7a1
> --- /dev/null
> +++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_mon.h
> @@ -0,0 +1,52 @@
> +/*
> + * cxd2880_tnrdmd_mon.h
> + * Sony CXD2880 DVB-T2/T tuner + demodulator driver
> + * common monitor interface
> + *
> + * Copyright (C) 2016, 2017 Sony Semiconductor Solutions Corporation
> + *
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms of the GNU General Public License as published by the
> + * Free Software Foundation; version 2 of the License.
> + *
> + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
> + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
> + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
> + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
> + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
> + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
> + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
> + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
> + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
> + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
> + *
> + * You should have received a copy of the GNU General Public License along
> + * with this program; if not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +#ifndef CXD2880_TNRDMD_MON_H
> +#define CXD2880_TNRDMD_MON_H
> +
> +#include "cxd2880_common.h"
> +#include "cxd2880_tnrdmd.h"
> +
> +int cxd2880_tnrdmd_mon_rf_lvl(struct cxd2880_tnrdmd *tnr_dmd,
> + int *rf_lvl_db);
> +
> +int cxd2880_tnrdmd_mon_rf_lvl_sub(struct cxd2880_tnrdmd *tnr_dmd,
> + int *rf_lvl_db);
> +
> +int cxd2880_tnrdmd_mon_internal_cpu_status(struct cxd2880_tnrdmd
> + *tnr_dmd, u16 *status);
> +
> +int cxd2880_tnrdmd_mon_internal_cpu_status_sub(struct
> + cxd2880_tnrdmd
> + *tnr_dmd,
> + u16 *status);
> +
> +int cxd2880_tnrdmd_mon_ts_buf_info(struct cxd2880_tnrdmd *tnr_dmd,
> + struct
> + cxd2880_tnrdmd_ts_buf_info
> + *info);
> +
> +#endif
Thanks,
Mauro
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* Re: [PATCH v4 03/12] [media] cxd2880: Add common files for the driver
From: Mauro Carvalho Chehab @ 2017-12-13 18:02 UTC (permalink / raw)
To: Yasunari.Takiguchi
Cc: linux-kernel, devicetree, linux-media, tbird20d, frowand.list,
Masayuki Yamamoto, Hideki Nozawa, Kota Yonezawa,
Toshihiko Matsumoto, Satoshi Watanabe
In-Reply-To: <20171013060259.21221-1-Yasunari.Takiguchi@sony.com>
Em Fri, 13 Oct 2017 15:02:59 +0900
<Yasunari.Takiguchi@sony.com> escreveu:
> From: Yasunari Takiguchi <Yasunari.Takiguchi@sony.com>
>
> These are common files for the driver for the
> Sony CXD2880 DVB-T2/T tuner + demodulator.
> These contains helper functions for the driver.
>
> Signed-off-by: Yasunari Takiguchi <Yasunari.Takiguchi@sony.com>
> Signed-off-by: Masayuki Yamamoto <Masayuki.Yamamoto@sony.com>
> Signed-off-by: Hideki Nozawa <Hideki.Nozawa@sony.com>
> Signed-off-by: Kota Yonezawa <Kota.Yonezawa@sony.com>
> Signed-off-by: Toshihiko Matsumoto <Toshihiko.Matsumoto@sony.com>
> Signed-off-by: Satoshi Watanabe <Satoshi.C.Watanabe@sony.com>
> ---
>
> [Change list]
> Changes in V4
> drivers/media/dvb-frontends/cxd2880/cxd2880_io.c
> -removed unnecessary initialization at variable declaration
> -modified how to write consecutive registers
>
> Changes in V3
> drivers/media/dvb-frontends/cxd2880/cxd2880.h
> -no change
> drivers/media/dvb-frontends/cxd2880/cxd2880_common.c
> -changed MASKUPPER/MASKLOWER with GENMASK
> drivers/media/dvb-frontends/cxd2880/cxd2880_common.h
> -removed definition NULL and SONY_SLEEP
> -changed CXD2880_SLEEP to usleep_range
> -changed cxd2880_atomic_set to atomic_set
> -removed cxd2880_atomic struct and cxd2880_atomic_read
> -changed stop-watch function
> -modified return code
> drivers/media/dvb-frontends/cxd2880/cxd2880_io.c
> -removed unnecessary cast
> -modified return code
> -changed hexadecimal code to lower case.
> drivers/media/dvb-frontends/cxd2880/cxd2880_io.h
> -modified return code
> drivers/media/dvb-frontends/cxd2880/cxd2880_stopwatch_port.c
> -changed CXD2880_SLEEP to usleep_range
> -changed stop-watch function
> -modified return code
> #drivers/media/dvb-frontends/cxd2880/cxd2880_stdlib.h
> -cxd2880_stdlib.h file was removed from V3.
>
> drivers/media/dvb-frontends/cxd2880/cxd2880.h | 46 +++++++++++
> .../media/dvb-frontends/cxd2880/cxd2880_common.c | 38 +++++++++
> .../media/dvb-frontends/cxd2880/cxd2880_common.h | 50 ++++++++++++
> drivers/media/dvb-frontends/cxd2880/cxd2880_io.c | 89 ++++++++++++++++++++++
> drivers/media/dvb-frontends/cxd2880/cxd2880_io.h | 71 +++++++++++++++++
> .../dvb-frontends/cxd2880/cxd2880_stopwatch_port.c | 60 +++++++++++++++
> 6 files changed, 354 insertions(+)
> create mode 100644 drivers/media/dvb-frontends/cxd2880/cxd2880.h
> create mode 100644 drivers/media/dvb-frontends/cxd2880/cxd2880_common.c
> create mode 100644 drivers/media/dvb-frontends/cxd2880/cxd2880_common.h
> create mode 100644 drivers/media/dvb-frontends/cxd2880/cxd2880_io.c
> create mode 100644 drivers/media/dvb-frontends/cxd2880/cxd2880_io.h
> create mode 100644 drivers/media/dvb-frontends/cxd2880/cxd2880_stopwatch_port.c
>
> diff --git a/drivers/media/dvb-frontends/cxd2880/cxd2880.h b/drivers/media/dvb-frontends/cxd2880/cxd2880.h
> new file mode 100644
> index 000000000000..281f9a784eb5
> --- /dev/null
> +++ b/drivers/media/dvb-frontends/cxd2880/cxd2880.h
> @@ -0,0 +1,46 @@
> +/*
> + * cxd2880.h
> + * Sony CXD2880 DVB-T2/T tuner + demodulator driver public definitions
> + *
> + * Copyright (C) 2016, 2017 Sony Semiconductor Solutions Corporation
> + *
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms of the GNU General Public License as published by the
> + * Free Software Foundation; version 2 of the License.
> + *
> + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
> + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
> + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
> + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
> + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
> + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
> + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
> + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
> + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
> + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
> + *
> + * You should have received a copy of the GNU General Public License along
> + * with this program; if not, see <http://www.gnu.org/licenses/>.
> + */
Same comment made on patch 2 applies to this one and to the entire
series, with regards to SPDX.
> +
> +#ifndef CXD2880_H
> +#define CXD2880_H
> +
> +struct cxd2880_config {
> + struct spi_device *spi;
> + struct mutex *spi_mutex; /* For SPI access exclusive control */
> +};
> +
> +#if IS_REACHABLE(CONFIG_DVB_CXD2880)
> +extern struct dvb_frontend *cxd2880_attach(struct dvb_frontend *fe,
> + struct cxd2880_config *cfg);
> +#else
> +static inline struct dvb_frontend *cxd2880_attach(struct dvb_frontend *fe,
> + struct cxd2880_config *cfg)
> +{
> + pr_warn("%s: driver disabled by Kconfig\n", __func__);
> + return NULL;
> +}
> +#endif /* CONFIG_DVB_CXD2880 */
> +
> +#endif /* CXD2880_H */
> diff --git a/drivers/media/dvb-frontends/cxd2880/cxd2880_common.c b/drivers/media/dvb-frontends/cxd2880/cxd2880_common.c
> new file mode 100644
> index 000000000000..ffaa140bb8cb
> --- /dev/null
> +++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_common.c
> @@ -0,0 +1,38 @@
> +/*
> + * cxd2880_common.c
> + * Sony CXD2880 DVB-T2/T tuner + demodulator driver
> + * common functions
> + *
> + * Copyright (C) 2016, 2017 Sony Semiconductor Solutions Corporation
> + *
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms of the GNU General Public License as published by the
> + * Free Software Foundation; version 2 of the License.
> + *
> + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
> + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
> + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
> + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
> + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
> + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
> + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
> + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
> + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
> + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
> + *
> + * You should have received a copy of the GNU General Public License along
> + * with this program; if not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +#include "cxd2880_common.h"
> +
> +int cxd2880_convert2s_complement(u32 value, u32 bitlen)
> +{
> + if ((bitlen == 0) || (bitlen >= 32))
> + return (int)value;
> +
> + if (value & (u32)(1 << (bitlen - 1)))
> + return (int)(GENMASK(31, bitlen) | value);
> + else
> + return (int)(GENMASK(bitlen - 1, 0) & value);
> +}
> diff --git a/drivers/media/dvb-frontends/cxd2880/cxd2880_common.h b/drivers/media/dvb-frontends/cxd2880/cxd2880_common.h
> new file mode 100644
> index 000000000000..cf6b800809ee
> --- /dev/null
> +++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_common.h
> @@ -0,0 +1,50 @@
> +/*
> + * cxd2880_common.h
> + * Sony CXD2880 DVB-T2/T tuner + demodulator driver common definitions
> + *
> + * Copyright (C) 2016, 2017 Sony Semiconductor Solutions Corporation
> + *
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms of the GNU General Public License as published by the
> + * Free Software Foundation; version 2 of the License.
> + *
> + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
> + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
> + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
> + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
> + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
> + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
> + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
> + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
> + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
> + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
> + *
> + * You should have received a copy of the GNU General Public License along
> + * with this program; if not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +#ifndef CXD2880_COMMON_H
> +#define CXD2880_COMMON_H
> +
> +#include <linux/types.h>
> +#include <linux/errno.h>
> +#include <linux/delay.h>
> +#include <linux/string.h>
> +
> +#define CXD2880_ARG_UNUSED(arg) ((void)(arg))
Huh??? Why this is needed?
> +
> +int cxd2880_convert2s_complement(u32 value, u32 bitlen);
> +
> +struct cxd2880_stopwatch {
> + unsigned long start_time;
> +};
> +
> +int cxd2880_stopwatch_start(struct cxd2880_stopwatch *stopwatch);
> +
> +int cxd2880_stopwatch_sleep(struct cxd2880_stopwatch *stopwatch,
> + u32 ms);
> +
> +int cxd2880_stopwatch_elapsed(struct cxd2880_stopwatch *stopwatch,
> + unsigned int *elapsed);
> +
> +#endif
> diff --git a/drivers/media/dvb-frontends/cxd2880/cxd2880_io.c b/drivers/media/dvb-frontends/cxd2880/cxd2880_io.c
> new file mode 100644
> index 000000000000..bdb0b7982401
> --- /dev/null
> +++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_io.c
> @@ -0,0 +1,89 @@
> +/*
> + * cxd2880_io.c
> + * Sony CXD2880 DVB-T2/T tuner + demodulator driver
> + * register I/O interface functions
> + *
> + * Copyright (C) 2016, 2017 Sony Semiconductor Solutions Corporation
> + *
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms of the GNU General Public License as published by the
> + * Free Software Foundation; version 2 of the License.
> + *
> + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
> + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
> + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
> + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
> + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
> + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
> + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
> + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
> + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
> + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
> + *
> + * You should have received a copy of the GNU General Public License along
> + * with this program; if not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +#include "cxd2880_io.h"
> +
> +int cxd2880_io_common_write_one_reg(struct cxd2880_io *io,
> + enum cxd2880_io_tgt tgt,
> + u8 sub_address, u8 data)
> +{
> + int ret;
> +
> + if (!io)
> + return -EINVAL;
> +
> + ret = io->write_regs(io, tgt, sub_address, &data, 1);
> +
> + return ret;
> +}
> +
> +int cxd2880_io_set_reg_bits(struct cxd2880_io *io,
> + enum cxd2880_io_tgt tgt,
> + u8 sub_address, u8 data, u8 mask)
> +{
> + int ret;
> +
> + if (!io)
> + return -EINVAL;
> +
> + if (mask == 0x00)
> + return 0;
> +
> + if (mask != 0xff) {
> + u8 rdata = 0x00;
> +
> + ret = io->read_regs(io, tgt, sub_address, &rdata, 1);
> + if (ret)
> + return ret;
> +
> + data = (data & mask) | (rdata & (mask ^ 0xff));
> + }
> +
> + ret = io->write_reg(io, tgt, sub_address, data);
> +
> + return ret;
> +}
> +
> +int cxd2880_io_write_multi_regs(struct cxd2880_io *io,
> + enum cxd2880_io_tgt tgt,
> + const struct cxd2880_reg_value reg_value[],
> + u8 size)
> +{
> + int ret;
> + int i;
> +
> + if (!io)
> + return -EINVAL;
> +
> + for (i = 0; i < size ; i++) {
> + ret = io->write_reg(io, tgt, reg_value[i].addr,
> + reg_value[i].value);
> + if (ret)
> + return ret;
> + }
> +
> + return 0;
> +}
> diff --git a/drivers/media/dvb-frontends/cxd2880/cxd2880_io.h b/drivers/media/dvb-frontends/cxd2880/cxd2880_io.h
> new file mode 100644
> index 000000000000..f7aee6c6480e
> --- /dev/null
> +++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_io.h
> @@ -0,0 +1,71 @@
> +/*
> + * cxd2880_io.h
> + * Sony CXD2880 DVB-T2/T tuner + demodulator driver
> + * register I/O interface definitions
> + *
> + * Copyright (C) 2016, 2017 Sony Semiconductor Solutions Corporation
> + *
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms of the GNU General Public License as published by the
> + * Free Software Foundation; version 2 of the License.
> + *
> + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
> + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
> + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
> + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
> + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
> + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
> + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
> + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
> + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
> + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
> + *
> + * You should have received a copy of the GNU General Public License along
> + * with this program; if not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +#ifndef CXD2880_IO_H
> +#define CXD2880_IO_H
> +
> +#include "cxd2880_common.h"
> +
> +enum cxd2880_io_tgt {
> + CXD2880_IO_TGT_SYS,
> + CXD2880_IO_TGT_DMD
> +};
> +
> +struct cxd2880_reg_value {
> + u8 addr;
> + u8 value;
> +};
> +
> +struct cxd2880_io {
> + int (*read_regs)(struct cxd2880_io *io,
> + enum cxd2880_io_tgt tgt, u8 sub_address,
> + u8 *data, u32 size);
> + int (*write_regs)(struct cxd2880_io *io,
> + enum cxd2880_io_tgt tgt, u8 sub_address,
> + const u8 *data, u32 size);
> + int (*write_reg)(struct cxd2880_io *io,
> + enum cxd2880_io_tgt tgt, u8 sub_address,
> + u8 data);
> + void *if_object;
> + u8 i2c_address_sys;
> + u8 i2c_address_demod;
> + u8 slave_select;
> + void *user;
> +};
> +
> +int cxd2880_io_common_write_one_reg(struct cxd2880_io *io,
> + enum cxd2880_io_tgt tgt,
> + u8 sub_address, u8 data);
> +
> +int cxd2880_io_set_reg_bits(struct cxd2880_io *io,
> + enum cxd2880_io_tgt tgt,
> + u8 sub_address, u8 data, u8 mask);
> +
> +int cxd2880_io_write_multi_regs(struct cxd2880_io *io,
> + enum cxd2880_io_tgt tgt,
> + const struct cxd2880_reg_value reg_value[],
> + u8 size);
> +#endif
> diff --git a/drivers/media/dvb-frontends/cxd2880/cxd2880_stopwatch_port.c b/drivers/media/dvb-frontends/cxd2880/cxd2880_stopwatch_port.c
> new file mode 100644
> index 000000000000..a4a1e29de653
> --- /dev/null
> +++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_stopwatch_port.c
> @@ -0,0 +1,60 @@
> +/*
> + * cxd2880_stopwatch_port.c
> + * Sony CXD2880 DVB-T2/T tuner + demodulator driver
> + * time measurement functions
> + *
> + * Copyright (C) 2016, 2017 Sony Semiconductor Solutions Corporation
> + *
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms of the GNU General Public License as published by the
> + * Free Software Foundation; version 2 of the License.
> + *
> + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
> + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
> + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
> + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
> + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
> + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
> + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
> + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
> + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
> + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
> + *
> + * You should have received a copy of the GNU General Public License along
> + * with this program; if not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +#include <linux/jiffies.h>
> +
> +#include "cxd2880_common.h"
> +
> +int cxd2880_stopwatch_start(struct cxd2880_stopwatch *stopwatch)
> +{
> + if (!stopwatch)
> + return -EINVAL;
> +
> + stopwatch->start_time = jiffies;
> +
> + return 0;
> +}
> +
> +int cxd2880_stopwatch_sleep(struct cxd2880_stopwatch *stopwatch,
> + u32 ms)
> +{
> + if (!stopwatch)
> + return -EINVAL;
> + CXD2880_ARG_UNUSED(*stopwatch);
Huh? This macro does nothing. If this argument is not needed, then
just don't declare it...
> + usleep_range(ms * 10000, ms * 10000 + 1000);
> +
> + return 0;
> +}
... at the end, this code evaluates to just:
usleep_range(ms * 10000, ms * 10000 + 1000);
Why don't you just use usleep_range() directly, instead of adding
a function that just encapsulates it?
> +
> +int cxd2880_stopwatch_elapsed(struct cxd2880_stopwatch *stopwatch,
> + unsigned int *elapsed)
> +{
> + if (!stopwatch || !elapsed)
> + return -EINVAL;
> + *elapsed = jiffies_to_msecs(jiffies - stopwatch->start_time);
> +
> + return 0;
> +}
It seems that this entire C file is just adding another layer on the
top of jiffies. Please, don't do that, as it makes a lot harder to
understand what you're trying to do.
Thanks,
Mauro
^ permalink raw reply
* Re: [PATCH v4 02/12] [media] cxd2880-spi: Add support for CXD2880 SPI interface
From: Mauro Carvalho Chehab @ 2017-12-13 17:54 UTC (permalink / raw)
To: Yasunari.Takiguchi-7U/KSKJipcs
Cc: linux-kernel-u79uwXL29TY76Z2rM5mHXA,
devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-media-u79uwXL29TY76Z2rM5mHXA,
tbird20d-Re5JQEeQqe8AvxtiuMwx3w,
frowand.list-Re5JQEeQqe8AvxtiuMwx3w, Masayuki Yamamoto,
Hideki Nozawa, Kota Yonezawa, Toshihiko Matsumoto,
Satoshi Watanabe
In-Reply-To: <20171013055928.21132-1-Yasunari.Takiguchi-7U/KSKJipcs@public.gmane.org>
Em Fri, 13 Oct 2017 14:59:28 +0900
<Yasunari.Takiguchi-7U/KSKJipcs@public.gmane.org> escreveu:
> From: Yasunari Takiguchi <Yasunari.Takiguchi-7U/KSKJipcs@public.gmane.org>
>
> This is the SPI adapter part of the driver for the
> Sony CXD2880 DVB-T2/T tuner + demodulator.
>
> Signed-off-by: Yasunari Takiguchi <Yasunari.Takiguchi-7U/KSKJipcs@public.gmane.org>
> Signed-off-by: Masayuki Yamamoto <Masayuki.Yamamoto-7U/KSKJipcs@public.gmane.org>
> Signed-off-by: Hideki Nozawa <Hideki.Nozawa-7U/KSKJipcs@public.gmane.org>
> Signed-off-by: Kota Yonezawa <Kota.Yonezawa-7U/KSKJipcs@public.gmane.org>
> Signed-off-by: Toshihiko Matsumoto <Toshihiko.Matsumoto-7U/KSKJipcs@public.gmane.org>
> Signed-off-by: Satoshi Watanabe <Satoshi.C.Watanabe-7U/KSKJipcs@public.gmane.org>
> ---
>
> [Change list]
> Changes in V4
> drivers/media/spi/cxd2880-spi.c
> -removed Camel case
> -removed unnecessary initialization at variable declaration
> -removed unnecessary brace {}
>
> Changes in V3
> drivers/media/spi/cxd2880-spi.c
> -adjusted of indent spaces
> -removed unnecessary cast
> -changed debugging code
> -changed timeout method
> -modified coding style of if()
> -changed hexadecimal code to lower case.
>
> Changes in V2
> drivers/media/spi/cxd2880-spi.c
> -Modified PID filter setting.
>
> drivers/media/spi/cxd2880-spi.c | 695 ++++++++++++++++++++++++++++++++++++++++
> 1 file changed, 695 insertions(+)
> create mode 100644 drivers/media/spi/cxd2880-spi.c
>
> diff --git a/drivers/media/spi/cxd2880-spi.c b/drivers/media/spi/cxd2880-spi.c
> new file mode 100644
> index 000000000000..387cb32f90b8
> --- /dev/null
> +++ b/drivers/media/spi/cxd2880-spi.c
> @@ -0,0 +1,695 @@
> +/*
> + * cxd2880-spi.c
> + * Sony CXD2880 DVB-T2/T tuner + demodulator driver
> + * SPI adapter
> + *
> + * Copyright (C) 2016, 2017 Sony Semiconductor Solutions Corporation
> + *
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms of the GNU General Public License as published by the
> + * Free Software Foundation; version 2 of the License.
> + *
> + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
> + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
> + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
> + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
> + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
> + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
> + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
> + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
> + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
> + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
> + *
> + * You should have received a copy of the GNU General Public License along
> + * with this program; if not, see <http://www.gnu.org/licenses/>.
> + */
A minor issue (that it is not a show-stopper): we're now using SPDX
instead of repeating the copyright notes everywhere. Please look at
those articles for more details:
https://blogs.s-osg.org/linux-kernel-license-practices-revisited-spdx/
https://lwn.net/Articles/739183/
> +
> +#define pr_fmt(fmt) KBUILD_MODNAME ": %s: " fmt, __func__
It would be better to use dev_foo() debug macros instead of
pr_foo() ones.
> +
> +#include <linux/spi/spi.h>
> +#include <linux/jiffies.h>
> +
> +#include "dvb_demux.h"
> +#include "dmxdev.h"
> +#include "dvb_frontend.h"
> +#include "cxd2880.h"
> +
> +#define CXD2880_MAX_FILTER_SIZE 32
> +#define BURST_WRITE_MAX 128
> +#define MAX_TRANS_PACKET 300
> +
> +struct cxd2880_ts_buf_info {
> + u8 read_ready;
> + u8 almost_full;
> + u8 almost_empty;
> + u8 overflow;
> + u8 underflow;
Hmm... those seem to be booleans. The best is to declare them as:
u8 read_ready:1;
u8 almost_full:1;
u8 almost_empty:1;
u8 overflow:1;
u8 underflow:1;
or to use bool, in order to make it clearer.
> + u16 packet_num;
> +};
> +
> +struct cxd2880_pid_config {
> + u8 is_enable;
> + u16 pid;
> +};
> +
> +struct cxd2880_pid_filter_config {
> + u8 is_negative;
> + struct cxd2880_pid_config pid_config[CXD2880_MAX_FILTER_SIZE];
> +};
> +
> +struct cxd2880_dvb_spi {
> + struct dvb_frontend dvb_fe;
> + struct dvb_adapter adapter;
> + struct dvb_demux demux;
> + struct dmxdev dmxdev;
> + struct dmx_frontend dmx_fe;
> + struct task_struct *cxd2880_ts_read_thread;
> + struct spi_device *spi;
> + struct mutex spi_mutex; /* For SPI access exclusive control */
> + int feed_count;
> + int all_pid_feed_count;
> + u8 *ts_buf;
> + struct cxd2880_pid_filter_config filter_config;
> +};
> +
> +DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
> +
> +static int cxd2880_write_spi(struct spi_device *spi, u8 *data, u32 size)
> +{
> + struct spi_message msg;
> + struct spi_transfer tx;
> + int ret;
> +
> + if ((!spi) || (!data)) {
> + pr_err("invalid arg\n");
> + return -EINVAL;
> + }
> +
> + memset(&tx, 0, sizeof(tx));
> + tx.tx_buf = data;
> + tx.len = size;
> +
> + spi_message_init(&msg);
> + spi_message_add_tail(&tx, &msg);
> + ret = spi_sync(spi, &msg);
> +
> + return ret;
> +}
> +
> +static int cxd2880_write_reg(struct spi_device *spi,
> + u8 sub_address, const u8 *data, u32 size)
> +{
> + u8 send_data[BURST_WRITE_MAX + 4];
> + const u8 *write_data_top = NULL;
> + int ret = 0;
> +
> + if ((!spi) || (!data)) {
> + pr_err("invalid arg\n");
> + return -EINVAL;
> + }
> + if (size > BURST_WRITE_MAX) {
> + pr_err("data size > WRITE_MAX\n");
> + return -EINVAL;
> + }
> +
> + if (sub_address + size > 0x100) {
> + pr_err("out of range\n");
> + return -EINVAL;
> + }
> +
> + send_data[0] = 0x0e;
> + write_data_top = data;
> +
> + while (size > 0) {
> + send_data[1] = sub_address;
> + if (size > 255)
> + send_data[2] = 255;
> + else
> + send_data[2] = (u8)size;
> +
> + memcpy(&send_data[3], write_data_top, send_data[2]);
> +
> + ret = cxd2880_write_spi(spi, send_data, send_data[2] + 3);
> + if (ret) {
> + pr_err("write spi failed %d\n", ret);
> + break;
> + }
> + sub_address += send_data[2];
> + write_data_top += send_data[2];
> + size -= send_data[2];
> + }
> +
> + return ret;
> +}
> +
> +static int cxd2880_spi_read_ts(struct spi_device *spi,
> + u8 *read_data,
> + u32 packet_num)
> +{
> + int ret;
> + u8 data[3];
> + struct spi_message message;
> + struct spi_transfer transfer[2];
> +
> + if ((!spi) || (!read_data) || (!packet_num)) {
> + pr_err("invalid arg\n");
> + return -EINVAL;
> + }
> + if (packet_num > 0xffff) {
> + pr_err("packet num > 0xffff\n");
> + return -EINVAL;
> + }
> +
> + data[0] = 0x10;
> + data[1] = (packet_num >> 8) & 0xff;
> + data[2] = packet_num & 0xff;
The & 0xff aren't needed, as the size of each data[] element is 8 bits.
> +
> + spi_message_init(&message);
> + memset(transfer, 0, sizeof(transfer));
> +
> + transfer[0].len = 3;
> + transfer[0].tx_buf = data;
> + spi_message_add_tail(&transfer[0], &message);
> + transfer[1].len = packet_num * 188;
> + transfer[1].rx_buf = read_data;
> + spi_message_add_tail(&transfer[1], &message);
> +
> + ret = spi_sync(spi, &message);
> + if (ret)
> + pr_err("spi_write_then_read failed\n");
> +
> + return ret;
> +}
> +
> +static int cxd2880_spi_read_ts_buffer_info(struct spi_device *spi,
> + struct cxd2880_ts_buf_info *info)
> +{
> + u8 send_data = 0x20;
> + u8 recv_data[2];
> + int ret;
> +
> + if ((!spi) || (!info)) {
> + pr_err("invalid arg\n");
> + return -EINVAL;
> + }
> +
> + ret = spi_write_then_read(spi, &send_data, 1,
> + recv_data, sizeof(recv_data));
> + if (ret)
> + pr_err("spi_write_then_read failed\n");
> +
> + info->read_ready = (recv_data[0] & 0x80) ? 1 : 0;
> + info->almost_full = (recv_data[0] & 0x40) ? 1 : 0;
> + info->almost_empty = (recv_data[0] & 0x20) ? 1 : 0;
> + info->overflow = (recv_data[0] & 0x10) ? 1 : 0;
> + info->underflow = (recv_data[0] & 0x08) ? 1 : 0;
See my comment above. if you use bool, instead of 1/0, you would need
to use true/false here.
> + info->packet_num = ((recv_data[0] & 0x07) << 8) | recv_data[1];
> +
> + return ret;
> +}
> +
> +static int cxd2880_spi_clear_ts_buffer(struct spi_device *spi)
> +{
> + u8 data = 0x03;
> + int ret;
> +
> + ret = cxd2880_write_spi(spi, &data, 1);
> +
> + if (ret)
> + pr_err("write spi failed\n");
> +
> + return ret;
> +}
> +
> +static int cxd2880_set_pid_filter(struct spi_device *spi,
> + struct cxd2880_pid_filter_config *cfg)
> +{
> + u8 data[65];
> + int i;
> + u16 pid = 0;
> + int ret;
> +
> + if (!spi) {
> + pr_err("ivnalid arg\n");
typo.
> + return -EINVAL;
> + }
> +
> + data[0] = 0x00;
> + ret = cxd2880_write_reg(spi, 0x00, &data[0], 1);
> + if (ret)
> + return ret;
> + if (!cfg) {
> + data[0] = 0x02;
> + ret = cxd2880_write_reg(spi, 0x50, &data[0], 1);
> + if (ret)
> + return ret;
> + } else {
> + data[0] = cfg->is_negative ? 0x01 : 0x00;
> +
> + for (i = 0; i < CXD2880_MAX_FILTER_SIZE; i++) {
> + pid = cfg->pid_config[i].pid;
> + if (cfg->pid_config[i].is_enable) {
> + data[1 + (i * 2)] = (pid >> 8) | 0x20;
> + data[2 + (i * 2)] = pid & 0xff;
> + } else {
> + data[1 + (i * 2)] = 0x00;
> + data[2 + (i * 2)] = 0x00;
> + }
> + }
> + ret = cxd2880_write_reg(spi, 0x50, data, 65);
> + if (ret)
> + return ret;
> + }
> +
> + return 0;
> +}
> +
> +static int cxd2880_update_pid_filter(struct cxd2880_dvb_spi *dvb_spi,
> + struct cxd2880_pid_filter_config *cfg,
> + bool is_all_pid_filter)
> +{
> + int ret;
> +
> + if ((!dvb_spi) || (!cfg)) {
> + pr_err("invalid arg.\n");
> + return -EINVAL;
> + }
> +
> + mutex_lock(&dvb_spi->spi_mutex);
> + if (is_all_pid_filter) {
> + struct cxd2880_pid_filter_config tmpcfg;
> +
> + memset(&tmpcfg, 0, sizeof(tmpcfg));
> + tmpcfg.is_negative = 1;
> + tmpcfg.pid_config[0].is_enable = 1;
> + tmpcfg.pid_config[0].pid = 0x1fff;
> +
> + ret = cxd2880_set_pid_filter(dvb_spi->spi, &tmpcfg);
> + } else {
> + ret = cxd2880_set_pid_filter(dvb_spi->spi, cfg);
> + }
> + mutex_unlock(&dvb_spi->spi_mutex);
> +
> + if (ret)
> + pr_err("set_pid_filter failed\n");
> +
> + return ret;
> +}
> +
> +static int cxd2880_ts_read(void *arg)
> +{
> + struct cxd2880_dvb_spi *dvb_spi = NULL;
> + struct cxd2880_ts_buf_info info;
> + unsigned int elapsed = 0;
> + unsigned long start_time = 0;
> + u32 i;
> + int ret;
> +
> + dvb_spi = arg;
> + if (!dvb_spi) {
> + pr_err("invalid arg\n");
> + return -EINVAL;
> + }
> +
> + ret = cxd2880_spi_clear_ts_buffer(dvb_spi->spi);
> + if (ret) {
> + pr_err("set_clear_ts_buffer failed\n");
> + return ret;
> + }
> + start_time = jiffies;
> + while (!kthread_should_stop()) {
> + elapsed = jiffies_to_msecs(jiffies - start_time);
> + ret = cxd2880_spi_read_ts_buffer_info(dvb_spi->spi,
> + &info);
> + if (ret) {
> + pr_err("spi_read_ts_buffer_info error\n");
> + return ret;
> + }
> +
> + if (info.packet_num > MAX_TRANS_PACKET) {
> + for (i = 0; i < info.packet_num / MAX_TRANS_PACKET;
> + i++) {
Please fix intend or place the for on a single line.
> + cxd2880_spi_read_ts(dvb_spi->spi,
> + dvb_spi->ts_buf,
> + MAX_TRANS_PACKET);
> + dvb_dmx_swfilter(&dvb_spi->demux,
> + dvb_spi->ts_buf,
> + MAX_TRANS_PACKET * 188);
> + }
> + start_time = jiffies;
> + } else if ((info.packet_num > 0) && (elapsed >= 500)) {
> + cxd2880_spi_read_ts(dvb_spi->spi,
> + dvb_spi->ts_buf,
> + info.packet_num);
> + dvb_dmx_swfilter(&dvb_spi->demux,
> + dvb_spi->ts_buf,
> + info.packet_num * 188);
> + start_time = jiffies;
> + } else {
> + usleep_range(10000, 11000);
> + }
> + }
> +
> + return 0;
> +}
> +
> +static int cxd2880_start_feed(struct dvb_demux_feed *feed)
> +{
> + int ret = 0;
> + int i = 0;
> + struct dvb_demux *demux = NULL;
> + struct cxd2880_dvb_spi *dvb_spi = NULL;
> +
> + if (!feed) {
> + pr_err("invalid arg\n");
> + return -EINVAL;
> + }
> +
> + demux = feed->demux;
> + if (!demux) {
> + pr_err("feed->demux is NULL\n");
> + return -EINVAL;
> + }
> + dvb_spi = demux->priv;
> +
> + if (dvb_spi->feed_count == CXD2880_MAX_FILTER_SIZE) {
> + pr_err("Exceeded maximum PID count (32).");
> + pr_err("Selected PID cannot be enabled.\n");
> + return -EBUSY;
> + }
> +
> + if (feed->pid == 0x2000) {
> + if (dvb_spi->all_pid_feed_count == 0) {
> + ret = cxd2880_update_pid_filter(dvb_spi,
> + &dvb_spi->filter_config,
> + true);
> + if (ret) {
> + pr_err("update pid filter failed\n");
> + return ret;
> + }
> + }
> + dvb_spi->all_pid_feed_count++;
> +
> + pr_debug("all PID feed (count = %d)\n",
> + dvb_spi->all_pid_feed_count);
> + } else {
> + struct cxd2880_pid_filter_config cfgtmp;
> +
> + cfgtmp = dvb_spi->filter_config;
> +
> + for (i = 0; i < CXD2880_MAX_FILTER_SIZE; i++) {
> + if (cfgtmp.pid_config[i].is_enable == 0) {
> + cfgtmp.pid_config[i].is_enable = 1;
> + cfgtmp.pid_config[i].pid = feed->pid;
> + pr_debug("store PID %d to #%d\n",
> + feed->pid, i);
> + break;
> + }
> + }
> + if (i == CXD2880_MAX_FILTER_SIZE) {
> + pr_err("PID filter is full. Assumed bug.\n");
> + return -EBUSY;
> + }
> + if (!dvb_spi->all_pid_feed_count)
> + ret = cxd2880_update_pid_filter(dvb_spi,
> + &cfgtmp,
> + false);
> + if (ret)
> + return ret;
> +
> + dvb_spi->filter_config = cfgtmp;
> + }
> +
> + if (dvb_spi->feed_count == 0) {
> + dvb_spi->ts_buf =
> + kmalloc(MAX_TRANS_PACKET * 188,
> + GFP_KERNEL | GFP_DMA);
> + if (!dvb_spi->ts_buf) {
> + pr_err("ts buffer allocate failed\n");
> + memset(&dvb_spi->filter_config, 0,
> + sizeof(dvb_spi->filter_config));
> + dvb_spi->all_pid_feed_count = 0;
> + return -ENOMEM;
> + }
> + dvb_spi->cxd2880_ts_read_thread = kthread_run(cxd2880_ts_read,
> + dvb_spi,
> + "cxd2880_ts_read");
> + if (IS_ERR(dvb_spi->cxd2880_ts_read_thread)) {
> + pr_err("kthread_run failed/\n");
> + kfree(dvb_spi->ts_buf);
> + dvb_spi->ts_buf = NULL;
> + memset(&dvb_spi->filter_config, 0,
> + sizeof(dvb_spi->filter_config));
> + dvb_spi->all_pid_feed_count = 0;
> + return PTR_ERR(dvb_spi->cxd2880_ts_read_thread);
> + }
> + }
> +
> + dvb_spi->feed_count++;
> +
> + pr_debug("start feed (count %d)\n", dvb_spi->feed_count);
> + return 0;
> +}
> +
> +static int cxd2880_stop_feed(struct dvb_demux_feed *feed)
> +{
> + int i = 0;
> + int ret;
> + struct dvb_demux *demux = NULL;
> + struct cxd2880_dvb_spi *dvb_spi = NULL;
> +
> + if (!feed) {
> + pr_err("invalid arg\n");
> + return -EINVAL;
> + }
> +
> + demux = feed->demux;
> + if (!demux) {
> + pr_err("feed->demux is NULL\n");
> + return -EINVAL;
> + }
> + dvb_spi = demux->priv;
> +
> + if (!dvb_spi->feed_count) {
> + pr_err("no feed is started\n");
> + return -EINVAL;
> + }
> +
> + if (feed->pid == 0x2000) {
> + /*
> + * Special PID case.
> + * Number of 0x2000 feed request was stored
> + * in dvb_spi->all_pid_feed_count.
> + */
> + if (dvb_spi->all_pid_feed_count <= 0) {
> + pr_err("PID %d not found.\n", feed->pid);
> + return -EINVAL;
> + }
> + dvb_spi->all_pid_feed_count--;
> + } else {
> + struct cxd2880_pid_filter_config cfgtmp;
> +
> + cfgtmp = dvb_spi->filter_config;
> +
> + for (i = 0; i < CXD2880_MAX_FILTER_SIZE; i++) {
> + if (feed->pid == cfgtmp.pid_config[i].pid &&
> + cfgtmp.pid_config[i].is_enable != 0) {
> + cfgtmp.pid_config[i].is_enable = 0;
> + cfgtmp.pid_config[i].pid = 0;
> + pr_debug("removed PID %d from #%d\n",
> + feed->pid, i);
> + break;
> + }
> + }
> + dvb_spi->filter_config = cfgtmp;
> +
> + if (i == CXD2880_MAX_FILTER_SIZE) {
> + pr_err("PID %d not found\n", feed->pid);
> + return -EINVAL;
> + }
> + }
> +
> + ret = cxd2880_update_pid_filter(dvb_spi,
> + &dvb_spi->filter_config,
> + dvb_spi->all_pid_feed_count > 0);
> + dvb_spi->feed_count--;
> +
> + if (dvb_spi->feed_count == 0) {
> + int ret_stop = 0;
> +
> + ret_stop = kthread_stop(dvb_spi->cxd2880_ts_read_thread);
> + if (ret_stop) {
> + pr_err("'kthread_stop failed. (%d)\n", ret_stop);
> + ret = ret_stop;
> + }
> + kfree(dvb_spi->ts_buf);
> + dvb_spi->ts_buf = NULL;
> + }
> +
> + pr_debug("stop feed ok.(count %d)\n", dvb_spi->feed_count);
> +
> + return ret;
> +}
I have the feeling that I've seen the code above before at the dvb core.
Any reason why don't use the already-existing code at dvb_demux.c & friends?
> +
> +static const struct of_device_id cxd2880_spi_of_match[] = {
> + { .compatible = "sony,cxd2880" },
> + { /* sentinel */ }
> +};
> +
> +MODULE_DEVICE_TABLE(of, cxd2880_spi_of_match);
> +
> +static int
> +cxd2880_spi_probe(struct spi_device *spi)
> +{
> + int ret;
> + struct cxd2880_dvb_spi *dvb_spi = NULL;
> + struct cxd2880_config config;
> +
> + if (!spi) {
> + pr_err("invalid arg.\n");
> + return -EINVAL;
> + }
> +
> + dvb_spi = kzalloc(sizeof(struct cxd2880_dvb_spi), GFP_KERNEL);
> + if (!dvb_spi)
> + return -ENOMEM;
> +
> + dvb_spi->spi = spi;
> + mutex_init(&dvb_spi->spi_mutex);
> + dev_set_drvdata(&spi->dev, dvb_spi);
> + config.spi = spi;
> + config.spi_mutex = &dvb_spi->spi_mutex;
> +
> + ret = dvb_register_adapter(&dvb_spi->adapter,
> + "CXD2880",
> + THIS_MODULE,
> + &spi->dev,
> + adapter_nr);
> + if (ret < 0) {
> + pr_err("dvb_register_adapter() failed\n");
> + goto fail_adapter;
> + }
> +
> + if (!dvb_attach(cxd2880_attach, &dvb_spi->dvb_fe, &config)) {
> + pr_err("cxd2880_attach failed\n");
> + goto fail_attach;
> + }
> +
> + ret = dvb_register_frontend(&dvb_spi->adapter,
> + &dvb_spi->dvb_fe);
> + if (ret < 0) {
> + pr_err("dvb_register_frontend() failed\n");
> + goto fail_frontend;
> + }
> +
> + dvb_spi->demux.dmx.capabilities = DMX_TS_FILTERING;
> + dvb_spi->demux.priv = dvb_spi;
> + dvb_spi->demux.filternum = CXD2880_MAX_FILTER_SIZE;
> + dvb_spi->demux.feednum = CXD2880_MAX_FILTER_SIZE;
> + dvb_spi->demux.start_feed = cxd2880_start_feed;
> + dvb_spi->demux.stop_feed = cxd2880_stop_feed;
> +
> + ret = dvb_dmx_init(&dvb_spi->demux);
> + if (ret < 0) {
> + pr_err("dvb_dmx_init() failed\n");
> + goto fail_dmx;
> + }
> +
> + dvb_spi->dmxdev.filternum = CXD2880_MAX_FILTER_SIZE;
> + dvb_spi->dmxdev.demux = &dvb_spi->demux.dmx;
> + dvb_spi->dmxdev.capabilities = 0;
> + ret = dvb_dmxdev_init(&dvb_spi->dmxdev,
> + &dvb_spi->adapter);
> + if (ret < 0) {
> + pr_err("dvb_dmxdev_init() failed\n");
> + goto fail_dmxdev;
> + }
> +
> + dvb_spi->dmx_fe.source = DMX_FRONTEND_0;
> + ret = dvb_spi->demux.dmx.add_frontend(&dvb_spi->demux.dmx,
> + &dvb_spi->dmx_fe);
> + if (ret < 0) {
> + pr_err("add_frontend() failed\n");
> + goto fail_dmx_fe;
> + }
> +
> + ret = dvb_spi->demux.dmx.connect_frontend(&dvb_spi->demux.dmx,
> + &dvb_spi->dmx_fe);
> + if (ret < 0) {
> + pr_err("dvb_register_frontend() failed\n");
> + goto fail_fe_conn;
> + }
> +
> + pr_info("Sony CXD2880 has successfully attached.\n");
> +
> + return 0;
> +
> +fail_fe_conn:
> + dvb_spi->demux.dmx.remove_frontend(&dvb_spi->demux.dmx,
> + &dvb_spi->dmx_fe);
> +fail_dmx_fe:
> + dvb_dmxdev_release(&dvb_spi->dmxdev);
> +fail_dmxdev:
> + dvb_dmx_release(&dvb_spi->demux);
> +fail_dmx:
> + dvb_unregister_frontend(&dvb_spi->dvb_fe);
> +fail_frontend:
> + dvb_frontend_detach(&dvb_spi->dvb_fe);
> +fail_attach:
> + dvb_unregister_adapter(&dvb_spi->adapter);
> +fail_adapter:
> + kfree(dvb_spi);
> + return ret;
> +}
> +
> +static int
> +cxd2880_spi_remove(struct spi_device *spi)
> +{
> + struct cxd2880_dvb_spi *dvb_spi;
> +
> + if (!spi) {
> + pr_err("invalid arg\n");
> + return -EINVAL;
> + }
> +
> + dvb_spi = dev_get_drvdata(&spi->dev);
> +
> + if (!dvb_spi) {
> + pr_err("failed\n");
> + return -EINVAL;
> + }
> + dvb_spi->demux.dmx.remove_frontend(&dvb_spi->demux.dmx,
> + &dvb_spi->dmx_fe);
> + dvb_dmxdev_release(&dvb_spi->dmxdev);
> + dvb_dmx_release(&dvb_spi->demux);
> + dvb_unregister_frontend(&dvb_spi->dvb_fe);
> + dvb_frontend_detach(&dvb_spi->dvb_fe);
> + dvb_unregister_adapter(&dvb_spi->adapter);
> +
> + kfree(dvb_spi);
> + pr_info("cxd2880_spi remove ok.\n");
> +
> + return 0;
> +}
> +
> +static const struct spi_device_id cxd2880_spi_id[] = {
> + { "cxd2880", 0 },
> + { /* sentinel */ }
> +};
> +MODULE_DEVICE_TABLE(spi, cxd2880_spi_id);
> +
> +static struct spi_driver cxd2880_spi_driver = {
> + .driver = {
> + .name = "cxd2880",
> + .of_match_table = cxd2880_spi_of_match,
> + },
> + .id_table = cxd2880_spi_id,
> + .probe = cxd2880_spi_probe,
> + .remove = cxd2880_spi_remove,
> +};
> +module_spi_driver(cxd2880_spi_driver);
> +
> +MODULE_DESCRIPTION(
> +"Sony CXD2880 DVB-T2/T tuner + demodulator drvier SPI adapter");
Just put it on a single line.
There's a typo there too: drvier
> +MODULE_AUTHOR("Sony Semiconductor Solutions Corporation");
> +MODULE_LICENSE("GPL v2");
Thanks,
Mauro
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* [PATCH v3 4/4] arm64: dts: marvell: armada-37xx: add nodes allowing cpufreq support
From: Gregory CLEMENT @ 2017-12-13 17:51 UTC (permalink / raw)
To: Rafael J. Wysocki, Viresh Kumar, linux-pm-u79uwXL29TY76Z2rM5mHXA
Cc: Jason Cooper, Andrew Lunn, Sebastian Hesselbarth, Gregory CLEMENT,
Rob Herring, devicetree-u79uwXL29TY76Z2rM5mHXA, Thomas Petazzoni,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, Antoine Tenart,
Miquèl Raynal, Nadav Haklai, Victor Gu, Marcin Wojtas,
Wilson Ding, Hua Jing, Neta Zur Hershkovits, Evan Wang,
Andre Heider
In-Reply-To: <20171213175119.9441-1-gregory.clement-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org>
In order to be able to use cpu freq, we need to associate a clock to each
CPU and to expose the power management registers.
Signed-off-by: Gregory CLEMENT <gregory.clement-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org>
---
arch/arm64/boot/dts/marvell/armada-372x.dtsi | 1 +
arch/arm64/boot/dts/marvell/armada-37xx.dtsi | 7 +++++++
2 files changed, 8 insertions(+)
diff --git a/arch/arm64/boot/dts/marvell/armada-372x.dtsi b/arch/arm64/boot/dts/marvell/armada-372x.dtsi
index 59d7557d3b1b..2554e0baea6b 100644
--- a/arch/arm64/boot/dts/marvell/armada-372x.dtsi
+++ b/arch/arm64/boot/dts/marvell/armada-372x.dtsi
@@ -56,6 +56,7 @@
device_type = "cpu";
compatible = "arm,cortex-a53","arm,armv8";
reg = <0x1>;
+ clocks = <&nb_periph_clk 16>;
enable-method = "psci";
};
};
diff --git a/arch/arm64/boot/dts/marvell/armada-37xx.dtsi b/arch/arm64/boot/dts/marvell/armada-37xx.dtsi
index 90c26d616a54..3056d7168e0b 100644
--- a/arch/arm64/boot/dts/marvell/armada-37xx.dtsi
+++ b/arch/arm64/boot/dts/marvell/armada-37xx.dtsi
@@ -65,6 +65,7 @@
device_type = "cpu";
compatible = "arm,cortex-a53", "arm,armv8";
reg = <0>;
+ clocks = <&nb_periph_clk 16>;
enable-method = "psci";
};
};
@@ -234,6 +235,12 @@
};
};
+ nb_pm: syscon@14000 {
+ compatible = "marvell,armada-3700-nb-pm",
+ "syscon";
+ reg = <0x14000 0x60>;
+ };
+
pinctrl_sb: pinctrl@18800 {
compatible = "marvell,armada3710-sb-pinctrl",
"syscon", "simple-mfd";
--
2.15.1
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply related
* [PATCH v3 3/4] cpufreq: Add DVFS support for Armada 37xx
From: Gregory CLEMENT @ 2017-12-13 17:51 UTC (permalink / raw)
To: Rafael J. Wysocki, Viresh Kumar, linux-pm
Cc: Jason Cooper, Andrew Lunn, Sebastian Hesselbarth, Gregory CLEMENT,
Rob Herring, devicetree, Thomas Petazzoni, linux-arm-kernel,
Antoine Tenart, Miquèl Raynal, Nadav Haklai, Victor Gu,
Marcin Wojtas, Wilson Ding, Hua Jing, Neta Zur Hershkovits,
Evan Wang, Andre Heider
In-Reply-To: <20171213175119.9441-1-gregory.clement@free-electrons.com>
This patch adds DVFS support for the Armada 37xx SoCs
There are up to four CPU frequency loads for Armada 37xx controlled by
the hardware.
This driver associates the CPU load level to a frequency, then the
hardware will switch while selecting a load level.
The hardware also can associate a voltage for each level (AVS support)
but it is not yet supported
Tested-by: Andre Heider <a.heider@gmail.com>
Acked-by: Viresh Kumar <viresh.kumar@linaro.org>
Signed-off-by: Gregory CLEMENT <gregory.clement@free-electrons.com>
---
drivers/cpufreq/Kconfig.arm | 7 +
drivers/cpufreq/Makefile | 1 +
drivers/cpufreq/armada-37xx-cpufreq.c | 241 ++++++++++++++++++++++++++++++++++
3 files changed, 249 insertions(+)
create mode 100644 drivers/cpufreq/armada-37xx-cpufreq.c
diff --git a/drivers/cpufreq/Kconfig.arm b/drivers/cpufreq/Kconfig.arm
index beb8826afbb1..3a88e33b0cfe 100644
--- a/drivers/cpufreq/Kconfig.arm
+++ b/drivers/cpufreq/Kconfig.arm
@@ -18,6 +18,13 @@ config ACPI_CPPC_CPUFREQ
If in doubt, say N.
+config ARM_ARMADA_37XX_CPUFREQ
+ tristate "Armada 37xx CPUFreq support"
+ depends on ARCH_MVEBU
+ help
+ This adds the CPUFreq driver support for Marvell Armada 37xx SoCs.
+ The Armada 37xx PMU supports 4 frequency and VDD levels.
+
# big LITTLE core layer and glue drivers
config ARM_BIG_LITTLE_CPUFREQ
tristate "Generic ARM big LITTLE CPUfreq driver"
diff --git a/drivers/cpufreq/Makefile b/drivers/cpufreq/Makefile
index d762e76887e7..e07715ce8844 100644
--- a/drivers/cpufreq/Makefile
+++ b/drivers/cpufreq/Makefile
@@ -52,6 +52,7 @@ obj-$(CONFIG_ARM_BIG_LITTLE_CPUFREQ) += arm_big_little.o
# LITTLE drivers, so that it is probed last.
obj-$(CONFIG_ARM_DT_BL_CPUFREQ) += arm_big_little_dt.o
+obj-$(CONFIG_ARM_ARMADA_37XX_CPUFREQ) += armada-37xx-cpufreq.o
obj-$(CONFIG_ARM_BRCMSTB_AVS_CPUFREQ) += brcmstb-avs-cpufreq.o
obj-$(CONFIG_ACPI_CPPC_CPUFREQ) += cppc_cpufreq.o
obj-$(CONFIG_ARCH_DAVINCI) += davinci-cpufreq.o
diff --git a/drivers/cpufreq/armada-37xx-cpufreq.c b/drivers/cpufreq/armada-37xx-cpufreq.c
new file mode 100644
index 000000000000..b819e5159a4b
--- /dev/null
+++ b/drivers/cpufreq/armada-37xx-cpufreq.c
@@ -0,0 +1,241 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * CPU frequency scaling support for Armada 37xx platform.
+ *
+ * Copyright (C) 2017 Marvell
+ *
+ * Gregory CLEMENT <gregory.clement@free-electrons.com>
+ */
+
+#include <linux/clk.h>
+#include <linux/cpu.h>
+#include <linux/cpufreq.h>
+#include <linux/err.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/mfd/syscon.h>
+#include <linux/module.h>
+#include <linux/of_address.h>
+#include <linux/of_device.h>
+#include <linux/of_irq.h>
+#include <linux/platform_device.h>
+#include <linux/pm_opp.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+
+/* Power management in North Bridge register set */
+#define ARMADA_37XX_NB_L0L1 0x18
+#define ARMADA_37XX_NB_L2L3 0x1C
+#define ARMADA_37XX_NB_TBG_DIV_OFF 13
+#define ARMADA_37XX_NB_TBG_DIV_MASK 0x7
+#define ARMADA_37XX_NB_CLK_SEL_OFF 11
+#define ARMADA_37XX_NB_CLK_SEL_MASK 0x1
+#define ARMADA_37XX_NB_CLK_SEL_TBG 0x1
+#define ARMADA_37XX_NB_TBG_SEL_OFF 9
+#define ARMADA_37XX_NB_TBG_SEL_MASK 0x3
+#define ARMADA_37XX_NB_VDD_SEL_OFF 6
+#define ARMADA_37XX_NB_VDD_SEL_MASK 0x3
+#define ARMADA_37XX_NB_CONFIG_SHIFT 16
+#define ARMADA_37XX_NB_DYN_MOD 0x24
+#define ARMADA_37XX_NB_CLK_SEL_EN BIT(26)
+#define ARMADA_37XX_NB_TBG_EN BIT(28)
+#define ARMADA_37XX_NB_DIV_EN BIT(29)
+#define ARMADA_37XX_NB_VDD_EN BIT(30)
+#define ARMADA_37XX_NB_DFS_EN BIT(31)
+#define ARMADA_37XX_NB_CPU_LOAD 0x30
+#define ARMADA_37XX_NB_CPU_LOAD_MASK 0x3
+#define ARMADA_37XX_DVFS_LOAD_0 0
+#define ARMADA_37XX_DVFS_LOAD_1 1
+#define ARMADA_37XX_DVFS_LOAD_2 2
+#define ARMADA_37XX_DVFS_LOAD_3 3
+
+/*
+ * On Armada 37xx the Power management manages 4 level of CPU load,
+ * each level can be associated with a CPU clock source, a CPU
+ * divider, a VDD level, etc...
+ */
+#define LOAD_LEVEL_NR 4
+
+struct armada_37xx_dvfs {
+ u32 cpu_freq_max;
+ u8 divider[LOAD_LEVEL_NR];
+};
+
+static struct armada_37xx_dvfs armada_37xx_dvfs[] = {
+ {.cpu_freq_max = 1200*1000*1000, .divider = {1, 2, 4, 6} },
+ {.cpu_freq_max = 1000*1000*1000, .divider = {1, 2, 4, 5} },
+ {.cpu_freq_max = 800*1000*1000, .divider = {1, 2, 3, 4} },
+ {.cpu_freq_max = 600*1000*1000, .divider = {2, 4, 5, 6} },
+};
+
+static struct armada_37xx_dvfs *armada_37xx_cpu_freq_info_get(u32 freq)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(armada_37xx_dvfs); i++) {
+ if (freq == armada_37xx_dvfs[i].cpu_freq_max)
+ return &armada_37xx_dvfs[i];
+ }
+
+ pr_err("Unsupported CPU frequency %d MHz\n", freq/1000000);
+ return NULL;
+}
+
+/*
+ * Setup the four level managed by the hardware. Once the four level
+ * will be configured then the DVFS will be enabled.
+ */
+static void __init armada37xx_cpufreq_dvfs_setup(struct regmap *base,
+ struct clk *clk, u8 *divider)
+{
+ int load_lvl;
+ struct clk *parent;
+
+ for (load_lvl = 0; load_lvl < LOAD_LEVEL_NR; load_lvl++) {
+ unsigned int reg, mask, val, offset = 0;
+
+ if (load_lvl <= ARMADA_37XX_DVFS_LOAD_1)
+ reg = ARMADA_37XX_NB_L0L1;
+ else
+ reg = ARMADA_37XX_NB_L2L3;
+
+ if (load_lvl == ARMADA_37XX_DVFS_LOAD_0 ||
+ load_lvl == ARMADA_37XX_DVFS_LOAD_2)
+ offset += ARMADA_37XX_NB_CONFIG_SHIFT;
+
+ /* Set cpu clock source, for all the level we use TBG */
+ val = ARMADA_37XX_NB_CLK_SEL_TBG << ARMADA_37XX_NB_CLK_SEL_OFF;
+ mask = (ARMADA_37XX_NB_CLK_SEL_MASK
+ << ARMADA_37XX_NB_CLK_SEL_OFF);
+
+ /*
+ * Set cpu divider based on the pre-computed array in
+ * order to have balanced step.
+ */
+ val |= divider[load_lvl] << ARMADA_37XX_NB_TBG_DIV_OFF;
+ mask |= (ARMADA_37XX_NB_TBG_DIV_MASK
+ << ARMADA_37XX_NB_TBG_DIV_OFF);
+
+ /* Set VDD divider which is actually the load level. */
+ val |= load_lvl << ARMADA_37XX_NB_VDD_SEL_OFF;
+ mask |= (ARMADA_37XX_NB_VDD_SEL_MASK
+ << ARMADA_37XX_NB_VDD_SEL_OFF);
+
+ val <<= offset;
+ mask <<= offset;
+
+ regmap_update_bits(base, reg, mask, val);
+ }
+
+ /*
+ * Set cpu clock source, for all the level we keep the same
+ * clock source that the one already configured. For this one
+ * we need to use the clock framework
+ */
+ parent = clk_get_parent(clk);
+ clk_set_parent(clk, parent);
+}
+
+static void __init armada37xx_cpufreq_disable_dvfs(struct regmap *base)
+{
+ unsigned int reg = ARMADA_37XX_NB_DYN_MOD,
+ mask = ARMADA_37XX_NB_DFS_EN;
+
+ regmap_update_bits(base, reg, mask, 0);
+}
+
+static void __init armada37xx_cpufreq_enable_dvfs(struct regmap *base)
+{
+ unsigned int val, reg = ARMADA_37XX_NB_CPU_LOAD,
+ mask = ARMADA_37XX_NB_CPU_LOAD_MASK;
+
+ /* Start with the highest load (0) */
+ val = ARMADA_37XX_DVFS_LOAD_0;
+ regmap_update_bits(base, reg, mask, val);
+
+ /* Now enable DVFS for the CPUs */
+ reg = ARMADA_37XX_NB_DYN_MOD;
+ mask = ARMADA_37XX_NB_CLK_SEL_EN | ARMADA_37XX_NB_TBG_EN |
+ ARMADA_37XX_NB_DIV_EN | ARMADA_37XX_NB_VDD_EN |
+ ARMADA_37XX_NB_DFS_EN;
+
+ regmap_update_bits(base, reg, mask, mask);
+}
+
+static int __init armada37xx_cpufreq_driver_init(void)
+{
+ struct armada_37xx_dvfs *dvfs;
+ struct platform_device *pdev;
+ unsigned int cur_frequency;
+ struct regmap *nb_pm_base;
+ struct device *cpu_dev;
+ int load_lvl, ret;
+ struct clk *clk;
+
+ nb_pm_base =
+ syscon_regmap_lookup_by_compatible("marvell,armada-3700-nb-pm");
+
+ if (IS_ERR(nb_pm_base))
+ return -ENODEV;
+
+ /* Before doing any configuration on the DVFS first, disable it */
+ armada37xx_cpufreq_disable_dvfs(nb_pm_base);
+
+ /*
+ * On CPU 0 register the operating points supported (which are
+ * the nominal CPU frequency and full integer divisions of
+ * it).
+ */
+ cpu_dev = get_cpu_device(0);
+ if (!cpu_dev) {
+ dev_err(cpu_dev, "Cannot get CPU\n");
+ return -ENODEV;
+ }
+
+ clk = clk_get(cpu_dev, 0);
+ if (IS_ERR(clk)) {
+ dev_err(cpu_dev, "Cannot get clock for CPU0\n");
+ return PTR_ERR(clk);
+ }
+
+ /* Get nominal (current) CPU frequency */
+ cur_frequency = clk_get_rate(clk);
+ if (!cur_frequency) {
+ dev_err(cpu_dev, "Failed to get clock rate for CPU\n");
+ return -EINVAL;
+ }
+
+ dvfs = armada_37xx_cpu_freq_info_get(cur_frequency);
+ if (!dvfs)
+ return -EINVAL;
+
+ armada37xx_cpufreq_dvfs_setup(nb_pm_base, clk, dvfs->divider);
+
+ for (load_lvl = ARMADA_37XX_DVFS_LOAD_0; load_lvl < LOAD_LEVEL_NR;
+ load_lvl++) {
+ unsigned long freq = cur_frequency / dvfs->divider[load_lvl];
+
+ ret = dev_pm_opp_add(cpu_dev, freq, 0);
+ if (ret) {
+ /* clean-up the already added opp before leaving */
+ while (load_lvl-- > ARMADA_37XX_DVFS_LOAD_0) {
+ freq = cur_frequency / dvfs->divider[load_lvl];
+ dev_pm_opp_remove(cpu_dev, freq);
+ }
+ return ret;
+ }
+ }
+
+ /* Now that everything is setup, enable the DVFS at hardware level */
+ armada37xx_cpufreq_enable_dvfs(nb_pm_base);
+
+ pdev = platform_device_register_simple("cpufreq-dt", -1, NULL, 0);
+
+ return PTR_ERR_OR_ZERO(pdev);
+}
+/* late_initcall, to guarantee the driver is loaded after A37xx clock driver */
+late_initcall(armada37xx_cpufreq_driver_init);
+
+MODULE_AUTHOR("Gregory CLEMENT <gregory.clement@free-electrons.com>");
+MODULE_DESCRIPTION("Armada 37xx cpufreq driver");
+MODULE_LICENSE("GPL");
--
2.15.1
^ permalink raw reply related
* [PATCH v3 2/4] MAINTAINERS: add new entries for Armada 37xx cpufreq driver
From: Gregory CLEMENT @ 2017-12-13 17:51 UTC (permalink / raw)
To: Rafael J. Wysocki, Viresh Kumar, linux-pm
Cc: Jason Cooper, Andrew Lunn, Sebastian Hesselbarth, Gregory CLEMENT,
Rob Herring, devicetree, Thomas Petazzoni, linux-arm-kernel,
Antoine Tenart, Miquèl Raynal, Nadav Haklai, Victor Gu,
Marcin Wojtas, Wilson Ding, Hua Jing, Neta Zur Hershkovits,
Evan Wang, Andre Heider
In-Reply-To: <20171213175119.9441-1-gregory.clement@free-electrons.com>
This new driver belongs to the mvebu family, update the MAINTAINER file
to document it.
Acked-by: Viresh Kumar <viresh.kumar@linaro.org>
Signed-off-by: Gregory CLEMENT <gregory.clement@free-electrons.com>
---
MAINTAINERS | 1 +
1 file changed, 1 insertion(+)
diff --git a/MAINTAINERS b/MAINTAINERS
index aa71ab52fd76..98dcee849481 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1582,6 +1582,7 @@ F: arch/arm/boot/dts/kirkwood*
F: arch/arm/configs/mvebu_*_defconfig
F: arch/arm/mach-mvebu/
F: arch/arm64/boot/dts/marvell/armada*
+F: drivers/cpufreq/armada-37xx-cpufreq.c
F: drivers/cpufreq/mvebu-cpufreq.c
F: drivers/irqchip/irq-armada-370-xp.c
F: drivers/irqchip/irq-mvebu-*
--
2.15.1
^ permalink raw reply related
* [PATCH v3 1/4] dt-bindings: marvell: Add documentation for the North Bridge PM on Armada 37xx
From: Gregory CLEMENT @ 2017-12-13 17:51 UTC (permalink / raw)
To: Rafael J. Wysocki, Viresh Kumar, linux-pm
Cc: Jason Cooper, Andrew Lunn, Sebastian Hesselbarth, Gregory CLEMENT,
Rob Herring, devicetree, Thomas Petazzoni, linux-arm-kernel,
Antoine Tenart, Miquèl Raynal, Nadav Haklai, Victor Gu,
Marcin Wojtas, Wilson Ding, Hua Jing, Neta Zur Hershkovits,
Evan Wang, Andre Heider
In-Reply-To: <20171213175119.9441-1-gregory.clement@free-electrons.com>
Extend the documentation of the Armada 37xx SoC with the the North
Bridge Power Management component.
Reviewed-by: Rob Herring <robh@kernel.org>
Signed-off-by: Gregory CLEMENT <gregory.clement@free-electrons.com>
---
.../devicetree/bindings/arm/marvell/armada-37xx.txt | 19 +++++++++++++++++++
1 file changed, 19 insertions(+)
diff --git a/Documentation/devicetree/bindings/arm/marvell/armada-37xx.txt b/Documentation/devicetree/bindings/arm/marvell/armada-37xx.txt
index 51336e5fc761..35c3c3460d17 100644
--- a/Documentation/devicetree/bindings/arm/marvell/armada-37xx.txt
+++ b/Documentation/devicetree/bindings/arm/marvell/armada-37xx.txt
@@ -14,3 +14,22 @@ following property before the previous one:
Example:
compatible = "marvell,armada-3720-db", "marvell,armada3720", "marvell,armada3710";
+
+
+Power management
+----------------
+
+For power management (particularly DVFS and AVS), the North Bridge
+Power Management component is needed:
+
+Required properties:
+- compatible : should contain "marvell,armada-3700-nb-pm", "syscon";
+- reg : the register start and length for the North Bridge
+ Power Management
+
+Example:
+
+nb_pm: syscon@14000 {
+ compatible = "marvell,armada-3700-nb-pm", "syscon";
+ reg = <0x14000 0x60>;
+}
--
2.15.1
^ permalink raw reply related
* [PATCH v3 0/4] Add CPU Frequency scaling support on Armada 37xx
From: Gregory CLEMENT @ 2017-12-13 17:51 UTC (permalink / raw)
To: Rafael J. Wysocki, Viresh Kumar, linux-pm
Cc: Jason Cooper, Andrew Lunn, Sebastian Hesselbarth, Gregory CLEMENT,
Rob Herring, devicetree, Thomas Petazzoni, linux-arm-kernel,
Antoine Tenart, Miquèl Raynal, Nadav Haklai, Victor Gu,
Marcin Wojtas, Wilson Ding, Hua Jing, Neta Zur Hershkovits,
Evan Wang, Andre Heider
Hi,
This is the third version of a series adding the CPU Frequency support
on Armada 37xx using DVFS. It is based on the initial work of Evan
Wang and Victor Gu.
As requested all the patches not directly related to the Armada 37xx
support had been sent in separate series.
The only other changes is replacing tab by space in the define as it
should have be already done in the v2.
The last patch is for arm-soc the arm-soc subsystem through mvebu and
update the device tree to support the CPU frequency scaling.
An update on the CPU clock driver is needed in order to take into
account the DVFS setting. It's the purpose of an other series already
sent, but is no dependencies between the series (for building or at
runtime).
Thanks,
Gregory
Changelog:
v1 -> v2:
- using syscon instead of nb_pm for the binding of the North bridge
power management unit: reported by Rob Herring
- fix sorting inside the big LITTLE section for the Kconfig: reported
by Viresh Kumar
- fix the bogus freq calculation in armada37xx_cpufreq_driver_init,
bug reported by Andre Heider
- use dev_pm_opp_remove() on the previous opp if dev_pm_opp_add()
failed, reported by Viresh Kumar
- add the Tested-by flag from Andre Heider on "cpufreq: Add DVFS
support for Armada 37xx" patch
v2 -> v3:
- move patches "cpufreq: ARM: sort the Kconfig menu", " cpufreq:
sort the drivers in ARM part", "cpufreq: mvebu: Use
dev_pm_opp_remove()" in separate series
- add reviewed-by and acked-by flags on the commits
- use space instead of tab in the #define in the armada-37xx-cpufreq.c file.
Gregory CLEMENT (4):
dt-bindings: marvell: Add documentation for the North Bridge PM on
Armada 37xx
MAINTAINERS: add new entries for Armada 37xx cpufreq driver
cpufreq: Add DVFS support for Armada 37xx
arm64: dts: marvell: armada-37xx: add nodes allowing cpufreq support
.../bindings/arm/marvell/armada-37xx.txt | 19 ++
MAINTAINERS | 1 +
arch/arm64/boot/dts/marvell/armada-372x.dtsi | 1 +
arch/arm64/boot/dts/marvell/armada-37xx.dtsi | 7 +
drivers/cpufreq/Kconfig.arm | 7 +
drivers/cpufreq/Makefile | 1 +
drivers/cpufreq/armada-37xx-cpufreq.c | 241 +++++++++++++++++++++
7 files changed, 277 insertions(+)
create mode 100644 drivers/cpufreq/armada-37xx-cpufreq.c
--
2.15.1
^ permalink raw reply
* Re: [PATCH] of_mdio / mdiobus: ensure mdio devices have fwnode correctly populated
From: Andrew Lunn @ 2017-12-13 17:15 UTC (permalink / raw)
To: Russell King
Cc: Florian Fainelli, Rob Herring, Frank Rowand, netdev, devicetree
In-Reply-To: <E1eOi7f-0002Rs-K7@rmk-PC.armlinux.org.uk>
On Tue, Dec 12, 2017 at 10:49:15AM +0000, Russell King wrote:
> Ensure that all mdio devices populate the struct device fwnode pointer
> as well as the of_node pointer to allow drivers that wish to use
> fwnode APIs to work.
>
> Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk>
Reviewed-by: Andrew Lunn <andrew@lunn.ch>
Andrew
^ permalink raw reply
* Re: [linux-sunxi] [PATCH v2 3/6] ARM: sun4i: Convert to CCU
From: Priit Laes @ 2017-12-13 17:13 UTC (permalink / raw)
To: Kevin Hilman
Cc: Chen-Yu Tsai, Maxime Ripard, lkml, linux-arm-kernel, devicetree,
linux-clk, linux-sunxi, Icenowy Zheng, Russell King, Mark Rutland,
Rob Herring, Stephen Boyd, Michael Turquette, Philipp Zabel,
Olof Johansson
In-Reply-To: <20171213170933.qyfn3hgnrvjpmod7@plaes.org>
On Wed, Dec 13, 2017 at 05:09:33PM +0000, Priit Laes wrote:
> On Tue, Dec 12, 2017 at 01:24:52PM -0800, Kevin Hilman wrote:
> > On Tue, Dec 12, 2017 at 9:26 AM, Priit Laes <plaes@plaes.org> wrote:
> > > On Mon, Dec 11, 2017 at 02:22:30PM -0800, Kevin Hilman wrote:
> > >> On Sun, Mar 26, 2017 at 10:20 AM, Priit Laes <plaes@plaes.org> wrote:
> > >> > Convert sun4i-a10.dtsi to new CCU driver.
> > >> >
> > >> > Signed-off-by: Priit Laes <plaes@plaes.org>
> > >>
> > >> I finally got around to bisecting a mainline boot failure on
> > >> sun4i-a10-cubieboard that's been happening for quite a while. Based
> > >> on on kernelci.org, it showed up sometime during the v4.15 merge
> > >> window[1]. It bisected down to this commit (in mainline as commit
> > >> 41193869f2bdb585ce09bfdd16d9482aadd560ad).
> > >>
> > >> When it fails, there is no output on the serial console, so I don't
> > >> know exactly how it's failing, just that it no longer boots.
> > >
> > > We tried out latest 4.15 with various compilers and it works:
> > > - gcc version 7.1.1 20170622 (Red Hat Cross 7.1.1-3) (GCC) - A10 Gemei G9 tablet
> > > - gcc 7.2.0-debian - A10 Cubieboard
> >
> > And you can reproduce the bug with gcc5 or gcc6?
>
> Tried following commits on Gemei G9 (A10 tablet):
> * 4.15.0-rc3-00037-gd39a01eff9af - latest master
> * 4.14.0-rc1-00002-g41193869f2bd - the exact commit, causing the issue.
>
> With the same Linaro toolchain:
> (gcc version 5.3.1 20160412 (Linaro GCC 5.3-2016.05))
And I also tried the same dtb and zImage from kernelci page [1] and it works with
that too...
https://storage.kernelci.org/mainline/master/v4.15-rc3/arm/sunxi_defconfig/
>
> >
> > Very strange that a DT only patch would cause a gcc related regression
> > and if it does, it should be investigated. I don't think requiring
> > gcc7 is an appropriate solution.
> >
> > @Chen-Yu, @Maxime: are you guys OK with requiring gcc7 for working
> > upstream boot for A10?
> >
> > Kevin
> >
> > --
> > You received this message because you are subscribed to the Google Groups "linux-sunxi" group.
> > To unsubscribe from this group and stop receiving emails from it, send an email to linux-sunxi+unsubscribe@googlegroups.com.
> > For more options, visit https://groups.google.com/d/optout.
>
> --
> You received this message because you are subscribed to the Google Groups "linux-sunxi" group.
> To unsubscribe from this group and stop receiving emails from it, send an email to linux-sunxi+unsubscribe@googlegroups.com.
> For more options, visit https://groups.google.com/d/optout.
^ permalink raw reply
* Re: [PATCH v2 3/6] ARM: sun4i: Convert to CCU
From: Priit Laes @ 2017-12-13 17:09 UTC (permalink / raw)
To: Kevin Hilman
Cc: Chen-Yu Tsai, Maxime Ripard, lkml, linux-arm-kernel, devicetree,
linux-clk-u79uwXL29TY76Z2rM5mHXA,
linux-sunxi-/JYPxA39Uh5TLH3MbocFFw, Icenowy Zheng, Russell King,
Mark Rutland, Rob Herring, Stephen Boyd, Michael Turquette,
Philipp Zabel, Olof Johansson
In-Reply-To: <CAOi56cUeRrKQnJ-akJPB160-60aBzy64bDgFeoqMpHRJSCnDeQ-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
On Tue, Dec 12, 2017 at 01:24:52PM -0800, Kevin Hilman wrote:
> On Tue, Dec 12, 2017 at 9:26 AM, Priit Laes <plaes-q/aMd4JkU83YtjvyW6yDsg@public.gmane.org> wrote:
> > On Mon, Dec 11, 2017 at 02:22:30PM -0800, Kevin Hilman wrote:
> >> On Sun, Mar 26, 2017 at 10:20 AM, Priit Laes <plaes-q/aMd4JkU83YtjvyW6yDsg@public.gmane.org> wrote:
> >> > Convert sun4i-a10.dtsi to new CCU driver.
> >> >
> >> > Signed-off-by: Priit Laes <plaes-q/aMd4JkU83YtjvyW6yDsg@public.gmane.org>
> >>
> >> I finally got around to bisecting a mainline boot failure on
> >> sun4i-a10-cubieboard that's been happening for quite a while. Based
> >> on on kernelci.org, it showed up sometime during the v4.15 merge
> >> window[1]. It bisected down to this commit (in mainline as commit
> >> 41193869f2bdb585ce09bfdd16d9482aadd560ad).
> >>
> >> When it fails, there is no output on the serial console, so I don't
> >> know exactly how it's failing, just that it no longer boots.
> >
> > We tried out latest 4.15 with various compilers and it works:
> > - gcc version 7.1.1 20170622 (Red Hat Cross 7.1.1-3) (GCC) - A10 Gemei G9 tablet
> > - gcc 7.2.0-debian - A10 Cubieboard
>
> And you can reproduce the bug with gcc5 or gcc6?
Tried following commits on Gemei G9 (A10 tablet):
* 4.15.0-rc3-00037-gd39a01eff9af - latest master
* 4.14.0-rc1-00002-g41193869f2bd - the exact commit, causing the issue.
With the same Linaro toolchain:
(gcc version 5.3.1 20160412 (Linaro GCC 5.3-2016.05))
>
> Very strange that a DT only patch would cause a gcc related regression
> and if it does, it should be investigated. I don't think requiring
> gcc7 is an appropriate solution.
>
> @Chen-Yu, @Maxime: are you guys OK with requiring gcc7 for working
> upstream boot for A10?
>
> Kevin
>
> --
> You received this message because you are subscribed to the Google Groups "linux-sunxi" group.
> To unsubscribe from this group and stop receiving emails from it, send an email to linux-sunxi+unsubscribe-/JYPxA39Uh5TLH3MbocFF+G/Ez6ZCGd0@public.gmane.org
> For more options, visit https://groups.google.com/d/optout.
^ permalink raw reply
* [PATCH v2 2/2] cpufreq: sort the drivers in ARM part
From: Gregory CLEMENT @ 2017-12-13 17:05 UTC (permalink / raw)
To: Rafael J. Wysocki, Viresh Kumar, linux-pm
Cc: Jason Cooper, Andrew Lunn, Sebastian Hesselbarth, Gregory CLEMENT,
Rob Herring, devicetree, Thomas Petazzoni, linux-arm-kernel,
Antoine Tenart, Miquèl Raynal, Nadav Haklai, Victor Gu,
Marcin Wojtas, Wilson Ding, Hua Jing, Neta Zur Hershkovits,
Evan Wang, Andre Heider
In-Reply-To: <20171213170536.28238-1-gregory.clement@free-electrons.com>
Keep the driver files alphabetically sorted.
Acked-by: Viresh Kumar <viresh.kumar@linaro.org>
Signed-off-by: Gregory CLEMENT <gregory.clement@free-electrons.com>
---
drivers/cpufreq/Makefile | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/drivers/cpufreq/Makefile b/drivers/cpufreq/Makefile
index 812f9e0d01a3..d762e76887e7 100644
--- a/drivers/cpufreq/Makefile
+++ b/drivers/cpufreq/Makefile
@@ -53,22 +53,24 @@ obj-$(CONFIG_ARM_BIG_LITTLE_CPUFREQ) += arm_big_little.o
obj-$(CONFIG_ARM_DT_BL_CPUFREQ) += arm_big_little_dt.o
obj-$(CONFIG_ARM_BRCMSTB_AVS_CPUFREQ) += brcmstb-avs-cpufreq.o
+obj-$(CONFIG_ACPI_CPPC_CPUFREQ) += cppc_cpufreq.o
obj-$(CONFIG_ARCH_DAVINCI) += davinci-cpufreq.o
obj-$(CONFIG_ARM_EXYNOS5440_CPUFREQ) += exynos5440-cpufreq.o
obj-$(CONFIG_ARM_HIGHBANK_CPUFREQ) += highbank-cpufreq.o
obj-$(CONFIG_ARM_IMX6Q_CPUFREQ) += imx6q-cpufreq.o
obj-$(CONFIG_ARM_KIRKWOOD_CPUFREQ) += kirkwood-cpufreq.o
obj-$(CONFIG_ARM_MEDIATEK_CPUFREQ) += mediatek-cpufreq.o
+obj-$(CONFIG_MACH_MVEBU_V7) += mvebu-cpufreq.o
obj-$(CONFIG_ARM_OMAP2PLUS_CPUFREQ) += omap-cpufreq.o
obj-$(CONFIG_ARM_PXA2xx_CPUFREQ) += pxa2xx-cpufreq.o
obj-$(CONFIG_PXA3xx) += pxa3xx-cpufreq.o
-obj-$(CONFIG_ARM_S3C24XX_CPUFREQ) += s3c24xx-cpufreq.o
-obj-$(CONFIG_ARM_S3C24XX_CPUFREQ_DEBUGFS) += s3c24xx-cpufreq-debugfs.o
obj-$(CONFIG_ARM_S3C2410_CPUFREQ) += s3c2410-cpufreq.o
obj-$(CONFIG_ARM_S3C2412_CPUFREQ) += s3c2412-cpufreq.o
obj-$(CONFIG_ARM_S3C2416_CPUFREQ) += s3c2416-cpufreq.o
obj-$(CONFIG_ARM_S3C2440_CPUFREQ) += s3c2440-cpufreq.o
obj-$(CONFIG_ARM_S3C64XX_CPUFREQ) += s3c64xx-cpufreq.o
+obj-$(CONFIG_ARM_S3C24XX_CPUFREQ) += s3c24xx-cpufreq.o
+obj-$(CONFIG_ARM_S3C24XX_CPUFREQ_DEBUGFS) += s3c24xx-cpufreq-debugfs.o
obj-$(CONFIG_ARM_S5PV210_CPUFREQ) += s5pv210-cpufreq.o
obj-$(CONFIG_ARM_SA1100_CPUFREQ) += sa1100-cpufreq.o
obj-$(CONFIG_ARM_SA1110_CPUFREQ) += sa1110-cpufreq.o
@@ -81,8 +83,6 @@ obj-$(CONFIG_ARM_TEGRA124_CPUFREQ) += tegra124-cpufreq.o
obj-$(CONFIG_ARM_TEGRA186_CPUFREQ) += tegra186-cpufreq.o
obj-$(CONFIG_ARM_TI_CPUFREQ) += ti-cpufreq.o
obj-$(CONFIG_ARM_VEXPRESS_SPC_CPUFREQ) += vexpress-spc-cpufreq.o
-obj-$(CONFIG_ACPI_CPPC_CPUFREQ) += cppc_cpufreq.o
-obj-$(CONFIG_MACH_MVEBU_V7) += mvebu-cpufreq.o
##################################################################################
--
2.15.1
^ permalink raw reply related
* [PATCH v2 1/2] cpufreq: ARM: sort the Kconfig menu
From: Gregory CLEMENT @ 2017-12-13 17:05 UTC (permalink / raw)
To: Rafael J. Wysocki, Viresh Kumar, linux-pm
Cc: Jason Cooper, Andrew Lunn, Sebastian Hesselbarth, Gregory CLEMENT,
Rob Herring, devicetree, Thomas Petazzoni, linux-arm-kernel,
Antoine Tenart, Miquèl Raynal, Nadav Haklai, Victor Gu,
Marcin Wojtas, Wilson Ding, Hua Jing, Neta Zur Hershkovits,
Evan Wang, Andre Heider
In-Reply-To: <20171213170536.28238-1-gregory.clement@free-electrons.com>
Group all the related big LITTLE configuration together and sort the
other entries in alphabetic order.
Also fixing tab vs space issue while mofifying these entries.
Acked-by: Viresh Kumar <viresh.kumar@linaro.org>
Signed-off-by: Gregory CLEMENT <gregory.clement@free-electrons.com>
---
drivers/cpufreq/Kconfig.arm | 81 ++++++++++++++++++++++-----------------------
1 file changed, 40 insertions(+), 41 deletions(-)
diff --git a/drivers/cpufreq/Kconfig.arm b/drivers/cpufreq/Kconfig.arm
index bdce4488ded1..beb8826afbb1 100644
--- a/drivers/cpufreq/Kconfig.arm
+++ b/drivers/cpufreq/Kconfig.arm
@@ -2,6 +2,22 @@
# ARM CPU Frequency scaling drivers
#
+config ACPI_CPPC_CPUFREQ
+ tristate "CPUFreq driver based on the ACPI CPPC spec"
+ depends on ACPI_PROCESSOR
+ select ACPI_CPPC_LIB
+ help
+ This adds a CPUFreq driver which uses CPPC methods
+ as described in the ACPIv5.1 spec. CPPC stands for
+ Collaborative Processor Performance Controls. It
+ is based on an abstract continuous scale of CPU
+ performance values which allows the remote power
+ processor to flexibly optimize for power and
+ performance. CPPC relies on power management firmware
+ support for its operation.
+
+ If in doubt, say N.
+
# big LITTLE core layer and glue drivers
config ARM_BIG_LITTLE_CPUFREQ
tristate "Generic ARM big LITTLE CPUfreq driver"
@@ -12,6 +28,30 @@ config ARM_BIG_LITTLE_CPUFREQ
help
This enables the Generic CPUfreq driver for ARM big.LITTLE platforms.
+config ARM_DT_BL_CPUFREQ
+ tristate "Generic probing via DT for ARM big LITTLE CPUfreq driver"
+ depends on ARM_BIG_LITTLE_CPUFREQ && OF
+ help
+ This enables probing via DT for Generic CPUfreq driver for ARM
+ big.LITTLE platform. This gets frequency tables from DT.
+
+config ARM_SCPI_CPUFREQ
+ tristate "SCPI based CPUfreq driver"
+ depends on ARM_BIG_LITTLE_CPUFREQ && ARM_SCPI_PROTOCOL && COMMON_CLK_SCPI
+ help
+ This adds the CPUfreq driver support for ARM big.LITTLE platforms
+ using SCPI protocol for CPU power management.
+
+ This driver uses SCPI Message Protocol driver to interact with the
+ firmware providing the CPU DVFS functionality.
+
+config ARM_VEXPRESS_SPC_CPUFREQ
+ tristate "Versatile Express SPC based CPUfreq driver"
+ depends on ARM_BIG_LITTLE_CPUFREQ && ARCH_VEXPRESS_SPC
+ help
+ This add the CPUfreq driver support for Versatile Express
+ big.LITTLE platforms using SPC for power management.
+
config ARM_BRCMSTB_AVS_CPUFREQ
tristate "Broadcom STB AVS CPUfreq driver"
depends on ARCH_BRCMSTB || COMPILE_TEST
@@ -33,20 +73,6 @@ config ARM_BRCMSTB_AVS_CPUFREQ_DEBUG
If in doubt, say N.
-config ARM_DT_BL_CPUFREQ
- tristate "Generic probing via DT for ARM big LITTLE CPUfreq driver"
- depends on ARM_BIG_LITTLE_CPUFREQ && OF
- help
- This enables probing via DT for Generic CPUfreq driver for ARM
- big.LITTLE platform. This gets frequency tables from DT.
-
-config ARM_VEXPRESS_SPC_CPUFREQ
- tristate "Versatile Express SPC based CPUfreq driver"
- depends on ARM_BIG_LITTLE_CPUFREQ && ARCH_VEXPRESS_SPC
- help
- This add the CPUfreq driver support for Versatile Express
- big.LITTLE platforms using SPC for power management.
-
config ARM_EXYNOS5440_CPUFREQ
tristate "SAMSUNG EXYNOS5440"
depends on SOC_EXYNOS5440
@@ -205,16 +231,6 @@ config ARM_SA1100_CPUFREQ
config ARM_SA1110_CPUFREQ
bool
-config ARM_SCPI_CPUFREQ
- tristate "SCPI based CPUfreq driver"
- depends on ARM_BIG_LITTLE_CPUFREQ && ARM_SCPI_PROTOCOL && COMMON_CLK_SCPI
- help
- This adds the CPUfreq driver support for ARM big.LITTLE platforms
- using SCPI protocol for CPU power management.
-
- This driver uses SCPI Message Protocol driver to interact with the
- firmware providing the CPU DVFS functionality.
-
config ARM_SPEAR_CPUFREQ
bool "SPEAr CPUFreq support"
depends on PLAT_SPEAR
@@ -275,20 +291,3 @@ config ARM_PXA2xx_CPUFREQ
This add the CPUFreq driver support for Intel PXA2xx SOCs.
If in doubt, say N.
-
-config ACPI_CPPC_CPUFREQ
- tristate "CPUFreq driver based on the ACPI CPPC spec"
- depends on ACPI_PROCESSOR
- select ACPI_CPPC_LIB
- default n
- help
- This adds a CPUFreq driver which uses CPPC methods
- as described in the ACPIv5.1 spec. CPPC stands for
- Collaborative Processor Performance Controls. It
- is based on an abstract continuous scale of CPU
- performance values which allows the remote power
- processor to flexibly optimize for power and
- performance. CPPC relies on power management firmware
- support for its operation.
-
- If in doubt, say N.
--
2.15.1
^ permalink raw reply related
* [PATCH v2 0/2] cpufreq: Sort Kconfig and Makefile
From: Gregory CLEMENT @ 2017-12-13 17:05 UTC (permalink / raw)
To: Rafael J. Wysocki, Viresh Kumar, linux-pm
Cc: Jason Cooper, Andrew Lunn, Sebastian Hesselbarth, Gregory CLEMENT,
Rob Herring, devicetree, Thomas Petazzoni, linux-arm-kernel,
Antoine Tenart, Miquèl Raynal, Nadav Haklai, Victor Gu,
Marcin Wojtas, Wilson Ding, Hua Jing, Neta Zur Hershkovits,
Evan Wang, Andre Heider
Hi,
The patch of this series was originally part of the series "Add CPU
Frequency scaling support on Armada 37xx" [1].
As requested by Rafael J. Wysocki, these 2 patches are extracted in a
independent series, in the meantime, Viresh Kumar gave his acked-by
that I added to the patches.
In this second version only the 1st patch had been modified, see the
changelog for the details.
Thanks,
Gregory
[1]: http://lists.infradead.org/pipermail/linux-arm-kernel/2017-December/546709.html
Changelog:
v1 -> v2:
- Fixed tab vs space issue in KConfig, suggested by Randy Dunlap
- Removed unneeded "default n" in KConfig, suggested by Randy Dunlap
Gregory CLEMENT (2):
cpufreq: ARM: sort the Kconfig menu
cpufreq: sort the drivers in ARM part
drivers/cpufreq/Kconfig.arm | 81 ++++++++++++++++++++++-----------------------
drivers/cpufreq/Makefile | 8 ++---
2 files changed, 44 insertions(+), 45 deletions(-)
--
2.15.1
^ permalink raw reply
* Re: [RFC 2/5] i3c: Add core I3C infrastructure
From: Greg Kroah-Hartman @ 2017-12-13 16:51 UTC (permalink / raw)
To: Boris Brezillon
Cc: Wolfram Sang, linux-i2c-u79uwXL29TY76Z2rM5mHXA, Jonathan Corbet,
linux-doc-u79uwXL29TY76Z2rM5mHXA, Arnd Bergmann, Przemyslaw Sroka,
Arkadiusz Golec, Alan Douglas, Bartosz Folta, Damian Kos,
Alicja Jurasik-Urbaniak, Jan Kotas, Cyprian Wronka,
Alexandre Belloni, Thomas Petazzoni, Nishanth Menon, Rob Herring,
Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala
In-Reply-To: <20171213172043.24e4d4bc@bbrezillon>
On Wed, Dec 13, 2017 at 05:20:43PM +0100, Boris Brezillon wrote:
> Hi Greg,
>
> On Tue, 1 Aug 2017 19:13:27 -0700
> Greg Kroah-Hartman <gregkh-hQyY1W1yCW8ekmWlsbkhG0B+6BGkLq7r@public.gmane.org> wrote:
>
> > > > > Unless you see a good reason to not use a R/W lock, I'd like to keep it
> > > > > this way because master IPs are likely to implement advanced queuing
> > > > > mechanism (allows one to queue new transfers even if the master is
> > > > > already busy processing other requests), and serializing things at the
> > > > > framework level will just prevent us from using this kind of
> > > > > optimization.
> > > >
> > > > Unless you can prove otherwise, using a rw lock is almost always worse
> > > > than just a mutex.
> > >
> > > Is it still true when it's taken in non-exclusive mode most of the
> > > time, and the time you spend in the critical section is non-negligible?
> > >
> > > I won't pretend I know better than you do what is preferable, it's just
> > > that the RW lock seemed appropriate to me for the situation I tried to
> > > described here.
> >
> > Again, measure it. If you can't measure it, then don't use it. Use a
> > simple lock instead. Seriously, don't make it more complex until you
> > really have to. It sounds like you didn't measure it at all, which
> > isn't good, please do so.
> >
>
> I'm resurrecting this thread because I finally had the time to implement
> message queuing in Cadence I3C master driver. So I did a test with 2
> I3C devices on the bus, and their drivers sending as much SDR messages
> as they can in 10s. Here are the results:
>
> | mutex | rwsem |
> ---------------------------------------
> dev1 | 19087 | 29532 |
> dev2 | 19341 | 29118 |
> =======================================
> total | 38428 | 58650 |
> msg/sec | ~3843 | ~5865 |
>
>
> The results I'm obtaining here are not so surprising since all normal
> transfers are taking the lock in read mode, so there's no contention.
> I didn't measure the impact on performances when there's one
> maintenance operation taking the lock in write mode and several normal
> transfers waiting for this lock, but really, maintenance operations are
> infrequent, and that's not where performance matters in our use case.
>
> I also did the same test with only one device doing transfers on the
> bus, and this time the mutex wins, but there's not a huge difference.
>
> | mutex | rwsem |
> ---------------------------------------
> total | 67116 | 66561 |
> msg/sec | ~6712 | ~6656 |
>
> Let me know if you want more information on the test procedure.
Nice, thanks for testing, so it is a real win here, good!
greg k-h
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* Re: [PATCH 5/5] PCI: cadence: add EndPoint Controller driver for Cadence PCIe controller
From: Cyrille Pitchen @ 2017-12-13 16:50 UTC (permalink / raw)
To: Kishon Vijay Abraham I, Lorenzo Pieralisi
Cc: bhelgaas-hpIqsD4AKlfQT0dZR+AlfA, linux-pci-u79uwXL29TY76Z2rM5mHXA,
adouglas-vna1KIf7WgpBDgjK7y7TUQ, stelford-vna1KIf7WgpBDgjK7y7TUQ,
dgary-vna1KIf7WgpBDgjK7y7TUQ, kgopi-vna1KIf7WgpBDgjK7y7TUQ,
eandrews-vna1KIf7WgpBDgjK7y7TUQ,
thomas.petazzoni-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8,
sureshp-vna1KIf7WgpBDgjK7y7TUQ, nsekhar-l0cyMroinI0,
linux-kernel-u79uwXL29TY76Z2rM5mHXA, robh-DgEjT+Ai2ygdnm+yROfE0A,
devicetree-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <ed6d811e-608e-e6aa-7b07-2f2d2d68adbd-l0cyMroinI0@public.gmane.org>
Hi Kishon,
Le 05/12/2017 à 10:19, Kishon Vijay Abraham I a écrit :
> Hi,
>
> On Friday 01 December 2017 05:50 PM, Lorenzo Pieralisi wrote:
>> On Thu, Nov 23, 2017 at 04:01:50PM +0100, Cyrille Pitchen wrote:
>>> This patch adds support to the Cadence PCIe controller in endpoint mode.
>>
>> Please add a brief description to the log to describe the most salient
>> features.
>>
>>> Signed-off-by: Cyrille Pitchen <cyrille.pitchen-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org>
>>> ---
>>> drivers/pci/cadence/Kconfig | 9 +
>>> drivers/pci/cadence/Makefile | 1 +
>>> drivers/pci/cadence/pcie-cadence-ep.c | 553 ++++++++++++++++++++++++++++++++++
>>> 3 files changed, 563 insertions(+)
>>> create mode 100644 drivers/pci/cadence/pcie-cadence-ep.c
>>>
>>> diff --git a/drivers/pci/cadence/Kconfig b/drivers/pci/cadence/Kconfig
>>> index 120306cae2aa..b2e6af71f39e 100644
>>> --- a/drivers/pci/cadence/Kconfig
>>> +++ b/drivers/pci/cadence/Kconfig
>>> @@ -21,4 +21,13 @@ config PCIE_CADENCE_HOST
>>> mode. This PCIe controller may be embedded into many different vendors
>>> SoCs.
>>>
>>> +config PCIE_CADENCE_EP
>>> + bool "Cadence PCIe endpoint controller"
>>> + depends on PCI_ENDPOINT
>>> + select PCIE_CADENCE
>>> + help
>>> + Say Y here if you want to support the Cadence PCIe controller in
>>> + endpoint mode. This PCIe controller may be embedded into many
>>> + different vendors SoCs.
>>> +
>>> endif # PCI_CADENCE
>>> diff --git a/drivers/pci/cadence/Makefile b/drivers/pci/cadence/Makefile
>>> index d57d192d2595..61e9c8d6839d 100644
>>> --- a/drivers/pci/cadence/Makefile
>>> +++ b/drivers/pci/cadence/Makefile
>>> @@ -1,2 +1,3 @@
>>> obj-$(CONFIG_PCIE_CADENCE) += pcie-cadence.o
>>> obj-$(CONFIG_PCIE_CADENCE_HOST) += pcie-cadence-host.o
>>> +obj-$(CONFIG_PCIE_CADENCE_EP) += pcie-cadence-ep.o
>>> diff --git a/drivers/pci/cadence/pcie-cadence-ep.c b/drivers/pci/cadence/pcie-cadence-ep.c
>>> new file mode 100644
>>> index 000000000000..a1d761101a9c
>>> --- /dev/null
>>> +++ b/drivers/pci/cadence/pcie-cadence-ep.c
>>> @@ -0,0 +1,553 @@
>>> +/*
>>> + * Cadence PCIe host controller driver.
>>
>> You should update this comment.
>>
>>> + *
>>> + * Copyright (c) 2017 Cadence
>>> + *
>>> + * Author: Cyrille Pitchen <cyrille.pitchen-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org>
>>> + *
>>> + * This program is free software; you can redistribute it and/or modify
>>> + * it under the terms of the GNU General Public License version 2 as
>>> + * published by the Free Software Foundation.
>>> + *
>>> + * This program is distributed in the hope that it will be useful,
>>> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
>>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
>>> + * GNU General Public License for more details.
>>> + *
>>> + * You should have received a copy of the GNU General Public License along with
>>> + * this program. If not, see <http://www.gnu.org/licenses/>.
>>> + */
>>> +#include <linux/kernel.h>
>>> +#include <linux/of.h>
>>> +#include <linux/pci-epc.h>
>>> +#include <linux/platform_device.h>
>>> +#include <linux/pm_runtime.h>
>>> +#include <linux/sizes.h>
>>> +#include <linux/delay.h>
>>
>> Nit: alphabetical order.
>>
>>> +#include "pcie-cadence.h"
>>> +
>>> +#define CDNS_PCIE_EP_MIN_APERTURE 128 /* 128 bytes */
>>> +#define CDNS_PCIE_EP_IRQ_PCI_ADDR_NONE 0x1
>>> +#define CDNS_PCIE_EP_IRQ_PCI_ADDR_LEGACY 0x3
>>> +
>>> +/**
>>> + * struct cdns_pcie_ep_data - hardware specific data
>>> + * @max_regions: maximum nmuber of regions supported by hardware
>>
>> s/nmuber/number
>>
>>> + */
>>> +struct cdns_pcie_ep_data {
>>> + size_t max_regions;
>>> +};
>>> +
>>> +/**
>>> + * struct cdns_pcie_ep - private data for this PCIe endpoint controller driver
>>> + * @pcie: Cadence PCIe controller
>>> + * @data: pointer to a 'struct cdns_pcie_data'
>>> + */
>>> +struct cdns_pcie_ep {
>>> + struct cdns_pcie pcie;
>>> + const struct cdns_pcie_ep_data *data;
>>> + struct pci_epc *epc;
>>> + unsigned long ob_region_map;
>>> + phys_addr_t *ob_addr;
>>> + phys_addr_t irq_phys_addr;
>>> + void __iomem *irq_cpu_addr;
>>> + u64 irq_pci_addr;
>>> + u8 irq_pending;
>>> +};
>>> +
>>> +static int cdns_pcie_ep_write_header(struct pci_epc *epc,
>>> + struct pci_epf_header *hdr)
>>> +{
>>> + struct cdns_pcie_ep *ep = epc_get_drvdata(epc);
>>> + struct cdns_pcie *pcie = &ep->pcie;
>>> + u8 fn = 0;
>>> +
>>> + if (fn == 0) {
>>
>> I think there is some code to retrieve fn missing here.
>
> hmm.. the endpoint core has to send the function number which right now it's
> not doing though it has the function number info in pci_epf.
Would it be OK if I add a new patch in the next series adding a
'struct pcie_epf *epf' as a 2nd argument to all handlers in the
'struct pcie_epc_ops'? This way I could have access to epf->func_no as needed.
Best regards,
Cyrille
>>
>>> + u32 id = CDNS_PCIE_LM_ID_VENDOR(hdr->vendorid) |
>>> + CDNS_PCIE_LM_ID_SUBSYS(hdr->subsys_vendor_id);
>>> + cdns_pcie_writel(pcie, CDNS_PCIE_LM_ID, id);
>>> + }
>>> + cdns_pcie_ep_fn_writew(pcie, fn, PCI_DEVICE_ID, hdr->deviceid);
>>> + cdns_pcie_ep_fn_writeb(pcie, fn, PCI_REVISION_ID, hdr->revid);
>>> + cdns_pcie_ep_fn_writeb(pcie, fn, PCI_CLASS_PROG, hdr->progif_code);
>>> + cdns_pcie_ep_fn_writew(pcie, fn, PCI_CLASS_DEVICE,
>>> + hdr->subclass_code | hdr->baseclass_code << 8);
>>> + cdns_pcie_ep_fn_writeb(pcie, fn, PCI_CACHE_LINE_SIZE,
>>> + hdr->cache_line_size);
>>> + cdns_pcie_ep_fn_writew(pcie, fn, PCI_SUBSYSTEM_ID, hdr->subsys_id);
>>> + cdns_pcie_ep_fn_writeb(pcie, fn, PCI_INTERRUPT_PIN, hdr->interrupt_pin);
>>> +
>>> + return 0;
>>> +}
>>> +
>>> +static int cdns_pcie_ep_set_bar(struct pci_epc *epc, enum pci_barno bar,
>>> + dma_addr_t bar_phys, size_t size, int flags)
>>> +{
>>> + struct cdns_pcie_ep *ep = epc_get_drvdata(epc);
>>> + struct cdns_pcie *pcie = &ep->pcie;
>>> + u32 addr0, addr1, reg, cfg, b, aperture, ctrl;
>>> + u8 fn = 0;
>
> Here too endpoint core should send the function number..
>>> + u64 sz;
>>> +
>>> + /* BAR size is 2^(aperture + 7) */
>>> + sz = max_t(size_t, size, CDNS_PCIE_EP_MIN_APERTURE);
>>> + sz = 1ULL << fls64(sz - 1);
>>> + aperture = ilog2(sz) - 7; /* 128B -> 0, 256B -> 1, 512B -> 2, ... */
>>> +
>>> + if ((flags & PCI_BASE_ADDRESS_SPACE) == PCI_BASE_ADDRESS_SPACE_IO) {
>>> + ctrl = CDNS_PCIE_LM_BAR_CFG_CTRL_IO_32BITS;
>>> + } else {
>>> + bool is_prefetch = !!(flags & PCI_BASE_ADDRESS_MEM_PREFETCH);
>>> + bool is_64bits = sz > SZ_2G;
>>> +
>>> + if (is_64bits && (bar & 1))
>>> + return -EINVAL;
>>> +
>>> + switch (is_64bits << 1 | is_prefetch) {
>>
>> I would not mind implementing this as a nested if-else, I am not a big
>> fan of using bool this way.
>>
>>> + case 0:
>>> + ctrl = CDNS_PCIE_LM_BAR_CFG_CTRL_MEM_32BITS;
>>> + break;
>>> +
>>> + case 1:
>>> + ctrl = CDNS_PCIE_LM_BAR_CFG_CTRL_PREFETCH_MEM_32BITS;
>>> + break;
>>> +
>>> + case 2:
>>> + ctrl = CDNS_PCIE_LM_BAR_CFG_CTRL_MEM_64BITS;
>>> + break;
>>> +
>>> + case 3:
>>> + ctrl = CDNS_PCIE_LM_BAR_CFG_CTRL_PREFETCH_MEM_64BITS;
>>> + break;
>>> + }
>>> + }
>>> +
>>> + addr0 = lower_32_bits(bar_phys);
>>> + addr1 = upper_32_bits(bar_phys);
>>> + cdns_pcie_writel(pcie, CDNS_PCIE_AT_IB_EP_FUNC_BAR_ADDR0(fn, bar),
>>> + addr0);
>
> It would be nice if you can have defines for CDNS_PCIE_AT_IB_EP_FUNC_BAR_ADDR0
> included in this patch rather than "PCI: cadence: Add host driver for Cadence
> PCIe controller". All EP specific functions in header file should be included here.
>>> + cdns_pcie_writel(pcie, CDNS_PCIE_AT_IB_EP_FUNC_BAR_ADDR1(fn, bar),
>>> + addr1);
>>
>> Is fn always 0 ?
>>
>>> + if (bar < BAR_4) {
>>> + reg = CDNS_PCIE_LM_EP_FUNC_BAR_CFG0(fn);
>>> + b = bar;
>>> + } else {
>>> + reg = CDNS_PCIE_LM_EP_FUNC_BAR_CFG1(fn);
>>> + b = bar - BAR_4;
>>> + }
>>> +
>>> + cfg = cdns_pcie_readl(pcie, reg);
>>> + cfg &= ~(CDNS_PCIE_LM_EP_FUNC_BAR_CFG_BAR_APERTURE_MASK(b) |
>>> + CDNS_PCIE_LM_EP_FUNC_BAR_CFG_BAR_CTRL_MASK(b));
>>> + cfg |= (CDNS_PCIE_LM_EP_FUNC_BAR_CFG_BAR_APERTURE(b, aperture) |
>>> + CDNS_PCIE_LM_EP_FUNC_BAR_CFG_BAR_CTRL(b, ctrl));
>>> + cdns_pcie_writel(pcie, reg, cfg);
>>> +
>>> + return 0;
>>> +}
>>> +
>>> +static void cdns_pcie_ep_clear_bar(struct pci_epc *epc, enum pci_barno bar)
>>> +{
>>> + struct cdns_pcie_ep *ep = epc_get_drvdata(epc);
>>> + struct cdns_pcie *pcie = &ep->pcie;
>>> + u32 reg, cfg, b, ctrl;
>>> + u8 fn = 0;
>
> Here too endpoint core should send the function number..
>>> +
>>> + if (bar < BAR_4) {
>>> + reg = CDNS_PCIE_LM_EP_FUNC_BAR_CFG0(fn);
>>> + b = bar;
>>> + } else {
>>> + reg = CDNS_PCIE_LM_EP_FUNC_BAR_CFG1(fn);
>>> + b = bar - BAR_4;
>>> + }
>>> +
>>> + ctrl = CDNS_PCIE_LM_BAR_CFG_CTRL_DISABLED;
>>> + cfg = cdns_pcie_readl(pcie, reg);
>>> + cfg &= ~(CDNS_PCIE_LM_EP_FUNC_BAR_CFG_BAR_APERTURE_MASK(b) |
>>> + CDNS_PCIE_LM_EP_FUNC_BAR_CFG_BAR_CTRL_MASK(b));
>>> + cfg |= CDNS_PCIE_LM_EP_FUNC_BAR_CFG_BAR_CTRL_MASK(ctrl);
>>> + cdns_pcie_writel(pcie, reg, cfg);
>>> +
>>> + cdns_pcie_writel(pcie, CDNS_PCIE_AT_IB_EP_FUNC_BAR_ADDR0(fn, bar), 0);
>>> + cdns_pcie_writel(pcie, CDNS_PCIE_AT_IB_EP_FUNC_BAR_ADDR1(fn, bar), 0);
>>> +}
>>> +
>>> +static int cdns_pcie_ep_map_addr(struct pci_epc *epc, phys_addr_t addr,
>>> + u64 pci_addr, size_t size)
>>> +{
>>> + struct cdns_pcie_ep *ep = epc_get_drvdata(epc);
>>> + struct cdns_pcie *pcie = &ep->pcie;
>>> + u32 r;
>>> +
>>> + r = find_first_zero_bit(&ep->ob_region_map, sizeof(ep->ob_region_map));
>>
>> Second argument must be in bits not bytes.
>>
>> https://marc.info/?l=linux-pci&m=151179781225513&w=2
>>
>>> + if (r >= ep->data->max_regions - 1) {
>>> + dev_err(&epc->dev, "no free outbound region\n");
>>> + return -EINVAL;
>>> + }
>>> +
>>> + cdns_pcie_set_outbound_region(pcie, r, false, addr, pci_addr, size);
>>> +
>>> + set_bit(r, &ep->ob_region_map);
>>> + ep->ob_addr[r] = addr;
>>> +
>>> + return 0;
>>> +}
>>> +
>>> +static void cdns_pcie_ep_unmap_addr(struct pci_epc *epc, phys_addr_t addr)
>>> +{
>>> + struct cdns_pcie_ep *ep = epc_get_drvdata(epc);
>>> + struct cdns_pcie *pcie = &ep->pcie;
>>> + u32 r;
>>> +
>>> + for (r = 0; r < ep->data->max_regions - 1; r++)
>>> + if (ep->ob_addr[r] == addr)
>>> + break;
>>> +
>>> + if (r >= ep->data->max_regions - 1)
>>
>> == ?
>>
>>> + return;
>>> +
>>> + cdns_pcie_reset_outbound_region(pcie, r);
>>> +
>>> + ep->ob_addr[r] = 0;
>>> + clear_bit(r, &ep->ob_region_map);
>>> +}
>>> +
>>> +static int cdns_pcie_ep_set_msi(struct pci_epc *epc, u8 mmc)
>>> +{
>>> + struct cdns_pcie_ep *ep = epc_get_drvdata(epc);
>>> + struct cdns_pcie *pcie = &ep->pcie;
>>> + u32 cap = CDNS_PCIE_EP_FUNC_MSI_CAP_OFFSET;
>>> + u16 flags;
>>> + u8 fn = 0;
>>> +
>>> + /* Validate the ID of the MSI Capability structure. */
>>> + if (cdns_pcie_ep_fn_readb(pcie, fn, cap) != PCI_CAP_ID_MSI)
>>> + return -EINVAL;
>>> +
>>> + /*
>>> + * Set the Multiple Message Capable bitfield into the Message Control
>>> + * register.
>>> + */
>>> + flags = cdns_pcie_ep_fn_readw(pcie, fn, cap + PCI_MSI_FLAGS);
>>> + flags = (flags & ~PCI_MSI_FLAGS_QMASK) | (mmc << 1);
>>> + flags |= PCI_MSI_FLAGS_64BIT;
>>> + flags &= ~PCI_MSI_FLAGS_MASKBIT;
>
> Any reason why "Per-vector masking capable" is reset?
>>> + cdns_pcie_ep_fn_writew(pcie, fn, cap + PCI_MSI_FLAGS, flags);
>>> +
>>> + return 0;
>>> +}
>>> +
>>> +static int cdns_pcie_ep_get_msi(struct pci_epc *epc)
>>> +{
>>> + struct cdns_pcie_ep *ep = epc_get_drvdata(epc);
>>> + struct cdns_pcie *pcie = &ep->pcie;
>>> + u32 cap = CDNS_PCIE_EP_FUNC_MSI_CAP_OFFSET;
>>> + u16 flags, mmc, mme;
>>> + u8 fn = 0;
>>> +
>>> + /* Validate the ID of the MSI Capability structure. */
>>> + if (cdns_pcie_ep_fn_readb(pcie, fn, cap) != PCI_CAP_ID_MSI)
>>> + return -EINVAL;
>>> +
>>> + /* Validate that the MSI feature is actually enabled. */
>>> + flags = cdns_pcie_ep_fn_readw(pcie, fn, cap + PCI_MSI_FLAGS);
>>> + if (!(flags & PCI_MSI_FLAGS_ENABLE))
>>> + return -EINVAL;
>>> +
>>> + /*
>>> + * Get the Multiple Message Enable bitfield from the Message Control
>>> + * register.
>>> + */
>>> + mmc = (flags & PCI_MSI_FLAGS_QMASK) >> 1;
>>> + mme = (flags & PCI_MSI_FLAGS_QSIZE) >> 4;
>>> + if (mme > mmc)
>>> + mme = mmc;
>>> + if (mme > 5)
>>> + mme = 5;
>
> I'm not sure if both these above checks are required..
>>
>> You should comment on what this 5 means and why it is fine to cap mme.
>>
>>> +
>>> + return mme;
>>> +}
>>> +
>>> +static void cdns_pcie_ep_assert_intx(struct cdns_pcie_ep *ep, u8 fn,
>>> + u8 intx, bool is_asserted)
>>> +{
>>> + struct cdns_pcie *pcie = &ep->pcie;
>>> + u32 r = ep->data->max_regions - 1;
>>> + u32 offset;
>>> + u16 status;
>>> + u8 msg_code;
>>> +
>>> + intx &= 3;
>>> +
>>> + /* Set the outbound region if needed. */
>>> + if (unlikely(ep->irq_pci_addr != CDNS_PCIE_EP_IRQ_PCI_ADDR_LEGACY)) {
>>> + /* Last region was reserved for IRQ writes. */
>>> + cdns_pcie_set_outbound_region_for_normal_msg(pcie, r,
>>> + ep->irq_phys_addr);
>>> + ep->irq_pci_addr = CDNS_PCIE_EP_IRQ_PCI_ADDR_LEGACY;
>>> + }
>>> +
>>> + if (is_asserted) {
>>> + ep->irq_pending |= BIT(intx);
>>> + msg_code = MSG_CODE_ASSERT_INTA + intx;
>>> + } else {
>>> + ep->irq_pending &= ~BIT(intx);
>>> + msg_code = MSG_CODE_DEASSERT_INTA + intx;
>>> + }
>>> +
>>> + status = cdns_pcie_ep_fn_readw(pcie, fn, PCI_STATUS);
>>> + if (((status & PCI_STATUS_INTERRUPT) != 0) ^ (ep->irq_pending != 0)) {
>>> + status ^= PCI_STATUS_INTERRUPT;
>>> + cdns_pcie_ep_fn_writew(pcie, fn, PCI_STATUS, status);
>>> + }
>
> here you are setting the PCI_STATUS_INTERRUPT even before sending the ASSERT
> message.
>>> +
>>> + offset = CDNS_PCIE_NORMAL_MSG_ROUTING(MSG_ROUTING_LOCAL) |
>>> + CDNS_PCIE_NORMAL_MSG_CODE(msg_code) |
>>> + CDNS_PCIE_MSG_NO_DATA;
>>> + writel(0, ep->irq_cpu_addr + offset);
>>> +}
>>> +
>>> +static int cdns_pcie_ep_send_legacy_irq(struct cdns_pcie_ep *ep, u8 fn, u8 intx)
>>> +{
>>> + u16 cmd;
>>> +
>>> + cmd = cdns_pcie_ep_fn_readw(&ep->pcie, fn, PCI_COMMAND);
>>> + if (cmd & PCI_COMMAND_INTX_DISABLE)
>>> + return -EINVAL;
>>> +
>>> + cdns_pcie_ep_assert_intx(ep, fn, intx, true);
>>> + mdelay(1);
>>
>> Add a comment please to explain the mdelay value.
>>
>>> + cdns_pcie_ep_assert_intx(ep, fn, intx, false);
>>> + return 0;
>>> +}
>>> +
>>> +static int cdns_pcie_ep_raise_irq(struct pci_epc *epc,
>>> + enum pci_epc_irq_type type, u8 interrupt_num)
>>> +{
>>> + struct cdns_pcie_ep *ep = epc_get_drvdata(epc);
>>> + struct cdns_pcie *pcie = &ep->pcie;
>>> + u32 cap = CDNS_PCIE_EP_FUNC_MSI_CAP_OFFSET;
>>> + u16 flags, mmc, mme, data, data_mask;
>>> + u8 msi_count;
>>> + u64 pci_addr, pci_addr_mask = 0xff;
>>> + u8 fn = 0;
>>> +
>>> + /* Handle legacy IRQ. */
>>> + if (type == PCI_EPC_IRQ_LEGACY)
>>> + return cdns_pcie_ep_send_legacy_irq(ep, fn, 0);
>>> +
>>> + /* Otherwise MSI. */
>>> + if (type != PCI_EPC_IRQ_MSI)
>>> + return -EINVAL;
>
> MSI-X?
>>> +
>>> + /* Check whether the MSI feature has been enabled by the PCI host. */
>>> + flags = cdns_pcie_ep_fn_readw(pcie, fn, cap + PCI_MSI_FLAGS);
>>> + if (!(flags & PCI_MSI_FLAGS_ENABLE))
>>> + return -EINVAL;
>>> +
>>> + /* Get the number of enabled MSIs */
>>> + mmc = (flags & PCI_MSI_FLAGS_QMASK) >> 1;
>>> + mme = (flags & PCI_MSI_FLAGS_QSIZE) >> 4;
>>> + if (mme > mmc)
>>> + mme = mmc;
>>> + if (mme > 5)
>>> + mme = 5;
>>
>> Same comment as above.
>>
>>> + msi_count = 1 << mme;
>>> + if (!interrupt_num || interrupt_num > msi_count)
>>> + return -EINVAL;
>>> +
>>> + /* Compute the data value to be written. */
>>> + data_mask = msi_count - 1;
>>> + data = cdns_pcie_ep_fn_readw(pcie, fn, cap + PCI_MSI_DATA_64);
>>> + data = (data & ~data_mask) | ((interrupt_num - 1) & data_mask);
>>> +
>>> + /* Get the PCI address where to write the data into. */
>>> + pci_addr = cdns_pcie_ep_fn_readl(pcie, fn, cap + PCI_MSI_ADDRESS_HI);
>>> + pci_addr <<= 32;
>>> + pci_addr |= cdns_pcie_ep_fn_readl(pcie, fn, cap + PCI_MSI_ADDRESS_LO);
>>> + pci_addr &= GENMASK_ULL(63, 2);
>>> +
>>> + /* Set the outbound region if needed. */
>>> + if (unlikely(ep->irq_pci_addr != pci_addr)) {
>>> + /* Last region was reserved for IRQ writes. */
>>> + cdns_pcie_set_outbound_region(pcie, ep->data->max_regions - 1,
>>> + false,
>>> + ep->irq_phys_addr,
>>> + pci_addr & ~pci_addr_mask,
>>> + pci_addr_mask + 1);
>>> + ep->irq_pci_addr = pci_addr;
>>> + }
>>> + writew(data, ep->irq_cpu_addr + (pci_addr & pci_addr_mask));
>>> +
>>> + return 0;
>>> +}
>>> +
>>> +static int cdns_pcie_ep_start(struct pci_epc *epc)
>>> +{
>>> + struct cdns_pcie_ep *ep = epc_get_drvdata(epc);
>>> + struct cdns_pcie *pcie = &ep->pcie;
>>> + struct pci_epf *epf;
>>> + u32 cfg;
>>> + u8 fn = 0;
>>> +
>>> + /* Enable this endpoint function. */
>>> + cfg = cdns_pcie_readl(pcie, CDNS_PCIE_LM_EP_FUNC_CFG);
>>> + cfg |= BIT(fn);
>>> + cdns_pcie_writel(pcie, CDNS_PCIE_LM_EP_FUNC_CFG, cfg);
>>> +
>>> + /*
>>> + * Already linked-up: don't call directly pci_epc_linkup() because we've
>>> + * already locked the epc->lock.
>>> + */
>
> Not sure what you mean by linked-up here?
>>> + list_for_each_entry(epf, &epc->pci_epf, list)
>>> + pci_epf_linkup(epf);
>
>>> +
>>> + return 0;
>>> +}
>>> +
>>> +static void cdns_pcie_ep_stop(struct pci_epc *epc)
>>> +{
>>> + struct cdns_pcie_ep *ep = epc_get_drvdata(epc);
>>> + struct cdns_pcie *pcie = &ep->pcie;
>>> + u32 cfg;
>>> + u8 fn = 0;
>>> +
>>> + /* Disable this endpoint function (function 0 can't be disabled). */
>>
>> I do not understand this comment and how it applies to the code,
>> in other words fn is always 0 here (so it can't be disabled)
>> I do not understand what this code is there for.
>>
>>> + cfg = cdns_pcie_readl(pcie, CDNS_PCIE_LM_EP_FUNC_CFG);
>>> + cfg &= ~BIT(fn);
>>> + cdns_pcie_writel(pcie, CDNS_PCIE_LM_EP_FUNC_CFG, cfg);
>>> +}
>>> +
>>> +static const struct pci_epc_ops cdns_pcie_epc_ops = {
>>> + .write_header = cdns_pcie_ep_write_header,
>>> + .set_bar = cdns_pcie_ep_set_bar,
>>> + .clear_bar = cdns_pcie_ep_clear_bar,
>>> + .map_addr = cdns_pcie_ep_map_addr,
>>> + .unmap_addr = cdns_pcie_ep_unmap_addr,
>>> + .set_msi = cdns_pcie_ep_set_msi,
>>> + .get_msi = cdns_pcie_ep_get_msi,
>>> + .raise_irq = cdns_pcie_ep_raise_irq,
>>> + .start = cdns_pcie_ep_start,
>>> + .stop = cdns_pcie_ep_stop,
>>> +};
>>> +
>>> +static const struct cdns_pcie_ep_data cdns_pcie_ep_data = {
>>> + .max_regions = 16,
>>> +};
>>
>> As I mentioned in patch 3, should this be set-up with DT ?
>>
>> Thanks,
>> Lorenzo
>>
>>> +
>>> +static const struct of_device_id cdns_pcie_ep_of_match[] = {
>>> + { .compatible = "cdns,cdns-pcie-ep",
>>> + .data = &cdns_pcie_ep_data },
>>> +
>>> + { },
>>> +};
>>> +
>>> +static int cdns_pcie_ep_probe(struct platform_device *pdev)
>>> +{
>>> + struct device *dev = &pdev->dev;
>>> + struct device_node *np = dev->of_node;
>>> + const struct of_device_id *of_id;
>>> + struct cdns_pcie_ep *ep;
>>> + struct cdns_pcie *pcie;
>>> + struct pci_epc *epc;
>>> + struct resource *res;
>>> + size_t max_regions;
>>> + int ret;
>>> +
>>> + ep = devm_kzalloc(dev, sizeof(*ep), GFP_KERNEL);
>>> + if (!ep)
>>> + return -ENOMEM;
>>> +
>>> + platform_set_drvdata(pdev, ep);
>>> +
>>> + pcie = &ep->pcie;
>>> + pcie->is_rc = false;
>>> +
>>> + of_id = of_match_node(cdns_pcie_ep_of_match, np);
>>> + ep->data = (const struct cdns_pcie_ep_data *)of_id->data;
>>> +
>>> + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "reg");
>>> + pcie->reg_base = devm_ioremap_resource(dev, res);
>>> + if (IS_ERR(pcie->reg_base)) {
>>> + dev_err(dev, "missing \"reg\"\n");
>>> + return PTR_ERR(pcie->reg_base);
>>> + }
>>> +
>>> + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "mem");
>>> + if (!res) {
>>> + dev_err(dev, "missing \"mem\"\n");
>>> + return -EINVAL;
>>> + }
>>> + pcie->mem_res = res;
>>> +
>>> + max_regions = ep->data->max_regions;
>>> + ep->ob_addr = devm_kzalloc(dev, max_regions * sizeof(*ep->ob_addr),
>>> + GFP_KERNEL);
>>> + if (!ep->ob_addr)
>>> + return -ENOMEM;
>>> +
>>> + pm_runtime_enable(dev);
>>> + ret = pm_runtime_get_sync(dev);
>>> + if (ret < 0) {
>>> + dev_err(dev, "pm_runtime_get_sync() failed\n");
>>> + goto err_get_sync;
>>> + }
>>> +
>>> + /* Disable all but function 0 (anyway BIT(0) is hardwired to 1). */
>>> + cdns_pcie_writel(pcie, CDNS_PCIE_LM_EP_FUNC_CFG, BIT(0));
>
> why disable all functions?
>>> +
>>> + epc = devm_pci_epc_create(dev, &cdns_pcie_epc_ops);
>>> + if (IS_ERR(epc)) {
>>> + dev_err(dev, "failed to create epc device\n");
>>> + ret = PTR_ERR(epc);
>>> + goto err_init;
>>> + }
>>> +
>>> + ep->epc = epc;
>>> + epc_set_drvdata(epc, ep);
>>> +
>>> + ret = of_property_read_u8(np, "max-functions", &epc->max_functions);
>>> + if (ret < 0)
>>> + epc->max_functions = 1;
>>> +
>>> + ret = pci_epc_mem_init(epc, pcie->mem_res->start,
>>> + resource_size(pcie->mem_res));
>>> + if (ret < 0) {
>>> + dev_err(dev, "failed to initialize the memory space\n");
>>> + goto err_init;
>>> + }
>>> +
>>> + ep->irq_cpu_addr = pci_epc_mem_alloc_addr(epc, &ep->irq_phys_addr,
>>> + SZ_128K);
>
> Any reason why you chose SZ_128K?
>
> Thanks
> Kishon
>
--
Cyrille Pitchen, Free Electrons
Embedded Linux and Kernel engineering
http://free-electrons.com
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* Re: [PATCH 1/2] cpufreq: ARM: sort the Kconfig menu
From: Gregory CLEMENT @ 2017-12-13 16:49 UTC (permalink / raw)
To: Randy Dunlap
Cc: Rafael J. Wysocki, Viresh Kumar, linux-pm-u79uwXL29TY76Z2rM5mHXA,
Jason Cooper, Andrew Lunn, Sebastian Hesselbarth, Rob Herring,
devicetree-u79uwXL29TY76Z2rM5mHXA, Thomas Petazzoni,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, Antoine Tenart,
Miquèl Raynal, Nadav Haklai, Victor Gu, Marcin Wojtas,
Wilson Ding, Hua Jing, Neta Zur Hershkovits, Evan Wang,
Andre Heider
In-Reply-To: <97091943-dcb8-47ab-0e55-831d35b1f73e-wEGCiKHe2LqWVfeAwA7xHQ@public.gmane.org>
Hi Randy,
On mar., déc. 12 2017, Randy Dunlap <rdunlap-wEGCiKHe2LqWVfeAwA7xHQ@public.gmane.org> wrote:
> On 12/12/2017 08:54 AM, Gregory CLEMENT wrote:
>> Group all the related big LITTLE configuration together and sort the
>> other entries in alphabetic order.
>>
>> Acked-by: Viresh Kumar <viresh.kumar-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>
>> Signed-off-by: Gregory CLEMENT <gregory.clement-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org>
>> ---
>> drivers/cpufreq/Kconfig.arm | 82 ++++++++++++++++++++++-----------------------
>> 1 file changed, 41 insertions(+), 41 deletions(-)
>>
>> diff --git a/drivers/cpufreq/Kconfig.arm b/drivers/cpufreq/Kconfig.arm
>> index bdce4488ded1..0baf43837b51 100644
>> --- a/drivers/cpufreq/Kconfig.arm
>> +++ b/drivers/cpufreq/Kconfig.arm
>> @@ -2,6 +2,23 @@
>> # ARM CPU Frequency scaling drivers
>> #
>>
>> +config ACPI_CPPC_CPUFREQ
>> + tristate "CPUFreq driver based on the ACPI CPPC spec"
>> + depends on ACPI_PROCESSOR
>> + select ACPI_CPPC_LIB
>> + default n
>
> Drop "default n" since that is the default default.
This lines and as weel as all you pointed was already here, I've jsute
move things. But OK why not fixing it too.
Gregory
>
>> + help
>> + This adds a CPUFreq driver which uses CPPC methods
>> + as described in the ACPIv5.1 spec. CPPC stands for
>> + Collaborative Processor Performance Controls. It
>> + is based on an abstract continuous scale of CPU
>> + performance values which allows the remote power
>> + processor to flexibly optimize for power and
>> + performance. CPPC relies on power management firmware
>> + support for its operation.
>> +
>> + If in doubt, say N.
>> +
>> # big LITTLE core layer and glue drivers
>> config ARM_BIG_LITTLE_CPUFREQ
>> tristate "Generic ARM big LITTLE CPUfreq driver"
>> @@ -12,6 +29,30 @@ config ARM_BIG_LITTLE_CPUFREQ
>> help
>> This enables the Generic CPUfreq driver for ARM big.LITTLE platforms.
>>
>> +config ARM_DT_BL_CPUFREQ
>> + tristate "Generic probing via DT for ARM big LITTLE CPUfreq driver"
>> + depends on ARM_BIG_LITTLE_CPUFREQ && OF
>> + help
>> + This enables probing via DT for Generic CPUfreq driver for ARM
>> + big.LITTLE platform. This gets frequency tables from DT.
>> +
>> +config ARM_SCPI_CPUFREQ
>> + tristate "SCPI based CPUfreq driver"
>> + depends on ARM_BIG_LITTLE_CPUFREQ && ARM_SCPI_PROTOCOL && COMMON_CLK_SCPI
>> + help
>
> Fix the help and tristate lines -- use tab instead of spaces.
>
>> + This adds the CPUfreq driver support for ARM big.LITTLE platforms
>> + using SCPI protocol for CPU power management.
>> +
>> + This driver uses SCPI Message Protocol driver to interact with the
>> + firmware providing the CPU DVFS functionality.
>> +
>> +config ARM_VEXPRESS_SPC_CPUFREQ
>> + tristate "Versatile Express SPC based CPUfreq driver"
>> + depends on ARM_BIG_LITTLE_CPUFREQ && ARCH_VEXPRESS_SPC
>> + help
>
> Use tab instead of spaces above. Oh, and one line below.
>
>> + This add the CPUfreq driver support for Versatile Express
>> + big.LITTLE platforms using SPC for power management.
>> +
>> config ARM_BRCMSTB_AVS_CPUFREQ
>> tristate "Broadcom STB AVS CPUfreq driver"
>> depends on ARCH_BRCMSTB || COMPILE_TEST
>
>
> --
> ~Randy
--
Gregory Clement, Free Electrons
Kernel, drivers, real-time and embedded Linux
development, consulting, training and support.
http://free-electrons.com
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* Re: [PATCH v2 6/7] cpufreq: Add DVFS support for Armada 37xx
From: Gregory CLEMENT @ 2017-12-13 16:45 UTC (permalink / raw)
To: Viresh Kumar
Cc: Rafael J. Wysocki, linux-pm, Jason Cooper, Andrew Lunn,
Sebastian Hesselbarth, Rob Herring, devicetree, Thomas Petazzoni,
linux-arm-kernel, Antoine Tenart, Miquèl Raynal,
Nadav Haklai, Victor Gu, Marcin Wojtas, Wilson Ding, Hua Jing,
Neta Zur Hershkovits, Evan Wang, Andre Heider
In-Reply-To: <20171212072426.GK25177@vireshk-i7>
Hi Viresh,
On mar., déc. 12 2017, Viresh Kumar <viresh.kumar@linaro.org> wrote:
> On 07-12-17, 14:56, Gregory CLEMENT wrote:
>> +/* Power management in North Bridge register set */
>> +#define ARMADA_37XX_NB_L0L1 0x18
>> +#define ARMADA_37XX_NB_L2L3 0x1C
>> +#define ARMADA_37XX_NB_TBG_DIV_OFF 13
>> +#define ARMADA_37XX_NB_TBG_DIV_MASK 0x7
>> +#define ARMADA_37XX_NB_CLK_SEL_OFF 11
>> +#define ARMADA_37XX_NB_CLK_SEL_MASK 0x1
>> +#define ARMADA_37XX_NB_CLK_SEL_TBG 0x1
>> +#define ARMADA_37XX_NB_TBG_SEL_OFF 9
>> +#define ARMADA_37XX_NB_TBG_SEL_MASK 0x3
>> +#define ARMADA_37XX_NB_VDD_SEL_OFF 6
>> +#define ARMADA_37XX_NB_VDD_SEL_MASK 0x3
>> +#define ARMADA_37XX_NB_CONFIG_SHIFT 16
>> +#define ARMADA_37XX_NB_DYN_MOD 0x24
>> +#define ARMADA_37XX_NB_CLK_SEL_EN BIT(26)
>> +#define ARMADA_37XX_NB_TBG_EN BIT(28)
>> +#define ARMADA_37XX_NB_DIV_EN BIT(29)
>> +#define ARMADA_37XX_NB_VDD_EN BIT(30)
>> +#define ARMADA_37XX_NB_DFS_EN BIT(31)
>> +#define ARMADA_37XX_NB_CPU_LOAD 0x30
>> +#define ARMADA_37XX_NB_CPU_LOAD_MASK 0x3
>> +#define ARMADA_37XX_DVFS_LOAD_0 0
>> +#define ARMADA_37XX_DVFS_LOAD_1 1
>> +#define ARMADA_37XX_DVFS_LOAD_2 2
>> +#define ARMADA_37XX_DVFS_LOAD_3 3
>
> I thought you agreed to using space instead of tab after #define ?
Me too! Actually I did it, and I don't know what happened with this
patch.
However it will be part of the next version and I will double check it.
>
> Looks fine otherwise. You can add below after fixing above tab/space thing:
>
> Acked-by: Viresh Kumar <viresh.kumar@linaro.org>
Thanks,
Gregory
>
> --
> viresh
--
Gregory Clement, Free Electrons
Kernel, drivers, real-time and embedded Linux
development, consulting, training and support.
http://free-electrons.com
^ permalink raw reply
* Re: [PATCH 3/5] PCI: cadence: Add host driver for Cadence PCIe controller
From: Cyrille Pitchen @ 2017-12-13 16:42 UTC (permalink / raw)
To: Lorenzo Pieralisi, Ard Biesheuvel
Cc: Bjorn Helgaas, kishon, linux-pci, adouglas, Scott Telford, dgary,
kgopi, eandrews, Thomas Petazzoni, sureshp, nsekhar,
linux-kernel@vger.kernel.org, Rob Herring,
devicetree@vger.kernel.org
In-Reply-To: <20171206113214.GA27582@red-moon>
Hi all,
Le 06/12/2017 à 12:32, Lorenzo Pieralisi a écrit :
> On Mon, Dec 04, 2017 at 06:49:12PM +0000, Ard Biesheuvel wrote:
>
> [...]
>
>>>>>> +static int cdns_pcie_host_init_root_port(struct cdns_pcie_rc *rc)
>>>>>> +{
>>>>>> + const struct cdns_pcie_rc_data *data = rc->data;
>>>>>> + struct cdns_pcie *pcie = &rc->pcie;
>>>>>> + u8 pbn, sbn, subn;
>>>>>> + u32 value, ctrl;
>>>>>> +
>>>>>> + /*
>>>>>> + * Set the root complex BAR configuration register:
>>>>>> + * - disable both BAR0 and BAR1.
>>>>>> + * - enable Prefetchable Memory Base and Limit registers in type 1
>>>>>> + * config space (64 bits).
>>>>>> + * - enable IO Base and Limit registers in type 1 config
>>>>>> + * space (32 bits).
>>>>>> + */
>>>>>> + ctrl = CDNS_PCIE_LM_BAR_CFG_CTRL_DISABLED;
>>>>>> + value = CDNS_PCIE_LM_RC_BAR_CFG_BAR0_CTRL(ctrl) |
>>>>>> + CDNS_PCIE_LM_RC_BAR_CFG_BAR1_CTRL(ctrl) |
>>>>>> + CDNS_PCIE_LM_RC_BAR_CFG_PREFETCH_MEM_ENABLE |
>>>>>> + CDNS_PCIE_LM_RC_BAR_CFG_PREFETCH_MEM_64BITS |
>>>>>> + CDNS_PCIE_LM_RC_BAR_CFG_IO_ENABLE |
>>>>>> + CDNS_PCIE_LM_RC_BAR_CFG_IO_32BITS;
>>>>>> + cdns_pcie_writel(pcie, CDNS_PCIE_LM_RC_BAR_CFG, value);
>>>>>> +
>>>>>> + /* Set root port configuration space */
>>>>>> + if (data->vendor_id != 0xffff)
>>>>>> + cdns_pcie_rp_writew(pcie, PCI_VENDOR_ID, data->vendor_id);
>>>>>> + if (data->device_id != 0xffff)
>>>>>> + cdns_pcie_rp_writew(pcie, PCI_DEVICE_ID, data->device_id);
>>>>>> +
>>>>>> + cdns_pcie_rp_writeb(pcie, PCI_CLASS_REVISION, 0);
>>>>>> + cdns_pcie_rp_writeb(pcie, PCI_CLASS_PROG, 0);
>>>>>> + cdns_pcie_rp_writew(pcie, PCI_CLASS_DEVICE, PCI_CLASS_BRIDGE_PCI);
>>>>>> +
>>>>>> + pbn = rc->bus_range->start;
>>>>>> + sbn = pbn + 1; /* Single root port. */
>>>>>> + subn = rc->bus_range->end;
>>>>>> + cdns_pcie_rp_writeb(pcie, PCI_PRIMARY_BUS, pbn);
>>>>>> + cdns_pcie_rp_writeb(pcie, PCI_SECONDARY_BUS, sbn);
>>>>>> + cdns_pcie_rp_writeb(pcie, PCI_SUBORDINATE_BUS, subn);
>>>>>
>>>>> Again - I do not have the datasheet for this device therefore I would
>>>>> kindly ask you how this works; it seems to me that what you are doing
>>>>> here is done through normal configuration cycles in an ECAM compliant
>>>>> system to program the RP PRIMARY/SECONDARY/SUBORDINATE bus - I would
>>>>> like to understand why this code is needed.
>>>>>
>>>>
>>>> I will test without those lines to test whether I can remove them.
>>>>
>>>> At first, the PCIe controller was tested by Cadence team: there was code
>>>> in their bootloader to initialize the hardware (building the AXI <-> PCIe
>>>> mappings, ...): the bootloader used to set the primary, secondary and
>>>> subordinate bus numbers in the root port PCI config space.
>>>>
>>>> Also there was a hardware trick to redirect accesses of the lowest
>>>> addresses in the AXI bus to the APB bus so the PCI configuration space of
>>>> the root port could have been accessed from the AXI bus too.
>>>>
>>>> The AXI <-> PCIe mapping being done by the bootloader and the root port
>>>> config space being accessible from the AXI bus, it was possible to use
>>>> the pci-host-generic driver.
>>>
>>> That's what I was getting at. Ard (CC'ed) implemented a firmware set-up
>>> (even though it was for a different IP but maybe it applies here) that
>>> allows the kernel to use the pci-host-generic driver to initialize the
>>> PCI controller:
>>>
>>> https://marc.info/?l=linux-pci&m=150360022626351&w=2
>>>
>>> I want to understand if there is an IP initialization sequence whereby
>>> this IP can be made to work in an ECAM compliant way and therefore
>>> reuse (most of) the pci-host-generic driver code.
>>>
>>
>> I think the Synopsys case is probably very similar. There are some
>> registers that look like the config space of a root port, but in
>> reality, every memory access that hits a live host bridge window is
>> forwarded onto the link, regardless of the values of the bridge BARs.
>> That is why in the quoted case, we can get away with ignoring the root
>> port altogether, rather than jumping through hoops to make the IP
>> block's PCI config space registers appear at B/D/F 0/0/0, while still
>> having to filter type 0 config TLPs going onto the link (which is
>> arguably the job of the root port to begin with)
>>
>> So if this IP does implement a proper root port (i.e., one where the
>> bridge BARs are actually taken into account, and where type 0 config
>> TLPs are in fact filtered), I strongly recommend mapping its config
>> space registers in an ECAM compliant matter, which implies no
>> accessors in the OS.
>>
>> However, given the observation above, this IP does not appear to
>> filter type 0 config TLPs to devfn > 0 downstream of the root port
>> either.
>
> Unfortunately that matches my understanding too, let's wait for
> Cyrille's reply on my query.
>
>>>> However, the hardware trick won't be included in the final design since
>>>> Cadence now wants to perform all PCI configuration space accesses through
>>>> a small 4KB window at a fixed address on the AXI bus.
>>>
>>> I would like to understand what the HW "trick" (if you can disclose it)
>>> was, because if there is a chance to reuse the pci-host-generic driver
>>> for this IP I want to take it (yes it may entail some firmware set-up in
>>> the bootloader) - was it a HW trick or a specific IP SW configuration ?
>>>
I will have to ask for details to Cadence designers if needed but when I
asked them about it, they explained me that AXI bus accesses in a small
window (I guess 4KB width) were redirected to the APB bus where lay the
registers for the root port PCI configuration space.
I was some hardware trick which won't be included in the final design, so
we can't enable or disable it by software.
Actually, this is requirement from the Cadence's customer that the host
driver can access the PCI config space of any device in sub ordinates
buses through a small memory area on the AXI bus. For some reason, they
don't want an ECAM compliant controller.
I don't know the reason but my guess is that they don't want to waste to
much space allocated to the PCIe controller on the AXI bus, likely on a
32-bit platform. As I said, this is such an assumption.
>>>> Also, we now want all initialisations to be done by the linux driver
>>>> instead of the bootloader.
>>>
>>> That's a choice, I do not necessarily agree with it and I think we
>>> should aim for more standardization on the PCI host bridge set-up
>>> at firmware->kernel handover on DT platforms.
>>>
It was another requirement of Cadence's customer that the PCIe host
controller initialization is done by the Linux driver rather than by
some boot loader.
Best regards,
Cyrille
>>
>> Well, for one, it means this IP will never be supported by ACPI, which
>> seems like a huge downside to me.
>
> Yes it is - that's exactly where my comments were heading.
>
> Thanks,
> Lorenzo
>
--
Cyrille Pitchen, Free Electrons
Embedded Linux and Kernel engineering
http://free-electrons.com
^ permalink raw reply
* Re: [RFC PATCH 1/2] dt: bindings: as3645a: Update dt node example with standard
From: Laurent Pinchart @ 2017-12-13 16:29 UTC (permalink / raw)
To: Dan Murphy
Cc: robh+dt, mark.rutland, rpurdie, jacek.anaszewski, pavel,
sakari.ailus, devicetree, linux-kernel, linux-leds
In-Reply-To: <90a1c908-3170-6956-8983-f9d88234cc07@ti.com>
Hi Dan,
On Wednesday, 13 December 2017 14:55:03 EET Dan Murphy wrote:
> On 12/13/2017 02:09 AM, Laurent Pinchart wrote:
> > On Tuesday, 12 December 2017 23:50:23 EET Dan Murphy wrote:
> >> Update the DT binding to remove the device name from
> >> the DT parent node as well as removing the device
> >> name from the label. The LED label will be generated
> >> based off the id name stored in the local driver so
> >> the LED function can be indicated in the label DT
> >> entry.
> >>
> >> Also removed the indentation on the example.
> >
> > This makes the patch a bit harder to review and seems to be a matter of
> > style.
>
> I debated whether to remove the extra tabs. The changes below came from
> comments from a recent LED driver I submitted.
>
> >> Signed-off-by: Dan Murphy <dmurphy@ti.com>
> >> ---
> >>
> >> .../devicetree/bindings/leds/ams,as3645a.txt | 36 +++++++++-------
> >> 1 file changed, 18 insertions(+), 18 deletions(-)
> >>
> >> diff --git a/Documentation/devicetree/bindings/leds/ams,as3645a.txt
> >> b/Documentation/devicetree/bindings/leds/ams,as3645a.txt index
> >> fc7f5f9f234c..122aa7165cf3 100644
> >> --- a/Documentation/devicetree/bindings/leds/ams,as3645a.txt
> >> +++ b/Documentation/devicetree/bindings/leds/ams,as3645a.txt
> >> @@ -58,22 +58,22 @@ label : The label of the indicator LED.
> >
> > I believe you should expand the documentation of the label property to
> > detail how it should be formed. It's nice to update the example, but the
> > bindings should be understandable without it.
>
> OK. I will add a reference to
> Documentation/devicetree/bindings/leds/common.txt
>
> label formation will be undergoing some changes. I wanted to make sure
> there were some good examples in the LED tree for other developers to
> reference.
>
> >> Example
> >> =======
> >>
> >> - as3645a@30 {
> >> - compatible = "ams,as3645a";
> >> - #address-cells = <1>;
> >> - #size-cells = <0>;
> >> - reg = <0x30>;
> >> - flash@0 {
> >> - reg = <0x0>;
> >> - flash-timeout-us = <150000>;
> >> - flash-max-microamp = <320000>;
> >> - led-max-microamp = <60000>;
> >> - ams,input-max-microamp = <1750000>;
> >> - label = "as3645a:flash";
> >> - };
> >> - indicator@1 {
> >> - reg = <0x1>;
> >> - led-max-microamp = <10000>;
> >> - label = "as3645a:indicator";
> >> - };
> >> +led-controller@30 {
> >
> > This change looks fine to me.
> >
> >> + compatible = "ams,as3645a";
> >> + #address-cells = <1>;
> >> + #size-cells = <0>;
> >> + reg = <0x30>;
> >> + led@0 {
> >
> > What's the rationale for changing the node name here ? It should be
> > explained in the commit message, and in the DT bindings documentation.
>
> In my patch to the DT maintainers Rob H indicated
>
> "Actually, it should be led-controller and led or leds be used for the
> LED child nodes (and gpio-led or pwd-led bindings)"
>
> Here is the patch that the node naming conventions took place
>
> https://patchwork.kernel.org/patch/10093757
OK, that makes sense to me.
Acked-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
> >> + reg = <0x0>;
> >> + flash-timeout-us = <150000>;
> >> + flash-max-microamp = <320000>;
> >> + led-max-microamp = <60000>;
> >> + ams,input-max-microamp = <1750000>;
> >> + label = "flash";
> >>
> >> };
> >>
> >> + led@1 {
> >> + reg = <0x1>;
> >> + led-max-microamp = <10000>;
> >> + label = "indicator";
> >> + };
> >> +};
--
Regards,
Laurent Pinchart
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox