Devicetree
 help / color / mirror / Atom feed
* Re: [PATCH] leds: as3645a: Fix checkpatch warnings
From: Dan Murphy @ 2017-12-13 20:49 UTC (permalink / raw)
  To: Jacek Anaszewski, robh+dt, mark.rutland, rpurdie, pavel,
	sakari.ailus, laurent.pinchart
  Cc: devicetree, linux-kernel, linux-leds
In-Reply-To: <98df06ef-9ee4-02b1-6d6b-f10b911a8d71@gmail.com>

Pavel and Laurent

On 12/13/2017 02:43 PM, Jacek Anaszewski wrote:
> Dan,
> 
> On 12/13/2017 09:41 PM, Dan Murphy wrote:
>> Jacek
>>
>> On 12/13/2017 02:29 PM, Jacek Anaszewski wrote:
>>> Hi Dan,
>>>
>>> checkpatch.pl doesn't want to be mentioned in the patch subject :-)
>>>
>>
>> Ack
>>
>>> "WARNING: A patch subject line should describe the change not the tool
>>> that found it"
>>>
>>> Preferably I'd see two separate patches.
>>>
>>
>> So you want me to split them up? I have no issue with that.
> 
> Yeah, it will be easier to come up with concise but meaningful
> subjects.
> 

When I split these up can I add your Acked-by to each patch or would you prefer to resend your
Acked-by for each patch?

Dan

>>> Also, line length limit for the commit description is 75 characters.
>>> Please use whole available space.
>>>
>>
>>
> 


-- 
------------------
Dan Murphy

^ permalink raw reply

* Re: [PATCH] leds: as3645a: Fix checkpatch warnings
From: Jacek Anaszewski @ 2017-12-13 20:43 UTC (permalink / raw)
  To: Dan Murphy, robh+dt, mark.rutland, rpurdie, pavel, sakari.ailus,
	laurent.pinchart
  Cc: devicetree, linux-kernel, linux-leds
In-Reply-To: <bf955d16-9af0-7776-354a-dc2a71b525f1@ti.com>

Dan,

On 12/13/2017 09:41 PM, Dan Murphy wrote:
> Jacek
> 
> On 12/13/2017 02:29 PM, Jacek Anaszewski wrote:
>> Hi Dan,
>>
>> checkpatch.pl doesn't want to be mentioned in the patch subject :-)
>>
> 
> Ack
> 
>> "WARNING: A patch subject line should describe the change not the tool
>> that found it"
>>
>> Preferably I'd see two separate patches.
>>
> 
> So you want me to split them up? I have no issue with that.

Yeah, it will be easier to come up with concise but meaningful
subjects.

>> Also, line length limit for the commit description is 75 characters.
>> Please use whole available space.
>>
> 
> 

-- 
Best regards,
Jacek Anaszewski

^ permalink raw reply

* Re: [PATCH] leds: as3645a: Fix checkpatch warnings
From: Dan Murphy @ 2017-12-13 20:41 UTC (permalink / raw)
  To: Jacek Anaszewski, robh+dt, mark.rutland, rpurdie, pavel,
	sakari.ailus, laurent.pinchart
  Cc: devicetree, linux-kernel, linux-leds
In-Reply-To: <ac821766-4bb8-d5d4-feaf-6750e8ceb894@gmail.com>

Jacek

On 12/13/2017 02:29 PM, Jacek Anaszewski wrote:
> Hi Dan,
> 
> checkpatch.pl doesn't want to be mentioned in the patch subject :-)
> 

Ack

> "WARNING: A patch subject line should describe the change not the tool
> that found it"
> 
> Preferably I'd see two separate patches.
> 

So you want me to split them up? I have no issue with that.

> Also, line length limit for the commit description is 75 characters.
> Please use whole available space.
> 


-- 
------------------
Dan Murphy

^ permalink raw reply

* [PATCH 4/4] PM / OPP: Add ti-opp-supply driver
From: Dave Gerlach @ 2017-12-13 20:33 UTC (permalink / raw)
  To: Viresh Kumar, Rob Herring, Rafael J . Wysocki
  Cc: linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	linux-omap-u79uwXL29TY76Z2rM5mHXA,
	linux-pm-u79uwXL29TY76Z2rM5mHXA,
	devicetree-u79uwXL29TY76Z2rM5mHXA, Tony Lindgren, Nishanth Menon,
	Dave Gerlach
In-Reply-To: <20171213203358.20839-1-d-gerlach-l0cyMroinI0@public.gmane.org>

Introduce a ti-opp-supply driver that will use new multiple regulator
support that is part of the OPP core This is needed on TI platforms like
DRA7/AM57 in order to control both CPU regulator and Adaptive Body Bias
(ABB) regulator. These regulators must be scaled in sequence during an
OPP transition depending on whether or not the frequency is being scaled
up or down.

This driver also implements AVS Class0 for these parts by looking up the
required values from registers in the SoC and programming adjusted
optimal voltage values for each OPP.

Signed-off-by: Dave Gerlach <d-gerlach-l0cyMroinI0@public.gmane.org>
---
 drivers/opp/Makefile        |   1 +
 drivers/opp/ti-opp-supply.c | 428 ++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 429 insertions(+)
 create mode 100644 drivers/opp/ti-opp-supply.c

diff --git a/drivers/opp/Makefile b/drivers/opp/Makefile
index e70ceb406fe9..6ce6aefacc81 100644
--- a/drivers/opp/Makefile
+++ b/drivers/opp/Makefile
@@ -2,3 +2,4 @@ ccflags-$(CONFIG_DEBUG_DRIVER)	:= -DDEBUG
 obj-y				+= core.o cpu.o
 obj-$(CONFIG_OF)		+= of.o
 obj-$(CONFIG_DEBUG_FS)		+= debugfs.o
+obj-$(CONFIG_ARM_TI_CPUFREQ)	+= ti-opp-supply.o
diff --git a/drivers/opp/ti-opp-supply.c b/drivers/opp/ti-opp-supply.c
new file mode 100644
index 000000000000..73d795c90b79
--- /dev/null
+++ b/drivers/opp/ti-opp-supply.c
@@ -0,0 +1,428 @@
+/*
+ * Copyright (C) 2016-2017 Texas Instruments Incorporated - http://www.ti.com/
+ *	Nishanth Menon <nm-l0cyMroinI0@public.gmane.org>
+ *	Dave Gerlach <d-gerlach-l0cyMroinI0@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.
+ *
+ * TI OPP supply driver that provides override into the regulator control
+ * for generic opp core to handle devices with ABB regulator and/or
+ * SmartReflex Class0.
+ */
+#include <linux/clk.h>
+#include <linux/cpufreq.h>
+#include <linux/device.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/notifier.h>
+#include <linux/of_device.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/pm_opp.h>
+#include <linux/regulator/consumer.h>
+#include <linux/slab.h>
+
+/**
+ * struct ti_opp_supply_optimum_voltage_table - optimized voltage table
+ * @reference_uv:	reference voltage (usually Nominal voltage)
+ * @optimized_uv:	Optimized voltage from efuse
+ */
+struct ti_opp_supply_optimum_voltage_table {
+	unsigned int reference_uv;
+	unsigned int optimized_uv;
+};
+
+/**
+ * struct ti_opp_supply_data - OMAP specific opp supply data
+ * @vdd_table:	Optimized voltage mapping table
+ * @num_vdd_table: number of entries in vdd_table
+ * @vdd_absolute_max_voltage_uv: absolute maximum voltage in UV for the supply
+ */
+struct ti_opp_supply_data {
+	struct ti_opp_supply_optimum_voltage_table *vdd_table;
+	u32 num_vdd_table;
+	u32 vdd_absolute_max_voltage_uv;
+};
+
+static struct ti_opp_supply_data opp_data;
+
+/**
+ * struct ti_opp_supply_of_data - device tree match data
+ * @flags:	specific type of opp supply
+ * @efuse_voltage_mask: mask required for efuse register representing voltage
+ * @efuse_voltage_uv: Are the efuse entries in micro-volts? if not, assume
+ *		milli-volts.
+ */
+struct ti_opp_supply_of_data {
+#define OPPDM_EFUSE_CLASS0_OPTIMIZED_VOLTAGE	BIT(1)
+#define OPPDM_HAS_NO_ABB			BIT(2)
+	const u8 flags;
+	const u32 efuse_voltage_mask;
+	const bool efuse_voltage_uv;
+};
+
+/**
+ * _store_optimized_voltages() - store optimized voltages
+ * @dev:	ti opp supply device for which we need to store info
+ * @data:	data specific to the device
+ *
+ * Picks up efuse based optimized voltages for VDD unique per device and
+ * stores it in internal data structure for use during transition requests.
+ *
+ * Return: If successful, 0, else appropriate error value.
+ */
+static int _store_optimized_voltages(struct device *dev,
+				     struct ti_opp_supply_data *data)
+{
+	void __iomem *base;
+	struct property *prop;
+	struct resource *res;
+	const __be32 *val;
+	int proplen, i;
+	int ret = 0;
+	struct ti_opp_supply_optimum_voltage_table *table;
+	const struct ti_opp_supply_of_data *of_data = dev_get_drvdata(dev);
+
+	/* pick up Efuse based voltages */
+	res = platform_get_resource(to_platform_device(dev), IORESOURCE_MEM, 0);
+	if (!res) {
+		dev_err(dev, "Unable to get IO resource\n");
+		ret = -ENODEV;
+		goto out_map;
+	}
+
+	base = ioremap_nocache(res->start, resource_size(res));
+	if (!base) {
+		dev_err(dev, "Unable to map Efuse registers\n");
+		ret = -ENOMEM;
+		goto out_map;
+	}
+
+	/* Fetch efuse-settings. */
+	prop = of_find_property(dev->of_node, "ti,efuse-settings", NULL);
+	if (!prop) {
+		dev_err(dev, "No 'ti,efuse-settings' property found\n");
+		ret = -EINVAL;
+		goto out;
+	}
+
+	proplen = prop->length / sizeof(int);
+	data->num_vdd_table = proplen / 2;
+	/* Verify for corrupted OPP entries in dt */
+	if (data->num_vdd_table * 2 * sizeof(int) != prop->length) {
+		dev_err(dev, "Invalid 'ti,efuse-settings'\n");
+		ret = -EINVAL;
+		goto out;
+	}
+
+	ret = of_property_read_u32(dev->of_node, "ti,absolute-max-voltage-uv",
+				   &data->vdd_absolute_max_voltage_uv);
+	if (ret) {
+		dev_err(dev, "ti,absolute-max-voltage-uv is missing\n");
+		ret = -EINVAL;
+		goto out;
+	}
+
+	table = kzalloc(sizeof(*data->vdd_table) *
+				  data->num_vdd_table, GFP_KERNEL);
+	if (!table) {
+		ret = -ENOMEM;
+		goto out;
+	}
+	data->vdd_table = table;
+
+	val = prop->value;
+	for (i = 0; i < data->num_vdd_table; i++, table++) {
+		u32 efuse_offset;
+		u32 tmp;
+
+		table->reference_uv = be32_to_cpup(val++);
+		efuse_offset = be32_to_cpup(val++);
+
+		tmp = readl(base + efuse_offset);
+		tmp &= of_data->efuse_voltage_mask;
+		tmp >>= __ffs(of_data->efuse_voltage_mask);
+
+		table->optimized_uv = of_data->efuse_voltage_uv ? tmp :
+					tmp * 1000;
+
+		dev_dbg(dev, "[%d] efuse=0x%08x volt_table=%d vset=%d\n",
+			i, efuse_offset, table->reference_uv,
+			table->optimized_uv);
+
+		/*
+		 * Some older samples might not have optimized efuse
+		 * Use reference voltage for those - just add debug message
+		 * for them.
+		 */
+		if (!table->optimized_uv) {
+			dev_dbg(dev, "[%d] efuse=0x%08x volt_table=%d:vset0\n",
+				i, efuse_offset, table->reference_uv);
+			table->optimized_uv = table->reference_uv;
+		}
+	}
+out:
+	iounmap(base);
+out_map:
+	return ret;
+}
+
+/**
+ * _free_optimized_voltages() - free resources for optvoltages
+ * @dev:	device for which we need to free info
+ * @data:	data specific to the device
+ */
+static void _free_optimized_voltages(struct device *dev,
+				     struct ti_opp_supply_data *data)
+{
+	kfree(data->vdd_table);
+	data->vdd_table = NULL;
+	data->num_vdd_table = 0;
+}
+
+/**
+ * _get_optimal_vdd_voltage() - Finds optimal voltage for the supply
+ * @dev:	device for which we need to find info
+ * @data:	data specific to the device
+ * @reference_uv:	reference voltage (OPP voltage) for which we need value
+ *
+ * Return: if a match is found, return optimized voltage, else return
+ * reference_uv, also return reference_uv if no optimization is needed.
+ */
+static int _get_optimal_vdd_voltage(struct device *dev,
+				    struct ti_opp_supply_data *data,
+				    int reference_uv)
+{
+	int i;
+	struct ti_opp_supply_optimum_voltage_table *table;
+
+	if (!data->num_vdd_table)
+		return reference_uv;
+
+	table = data->vdd_table;
+	if (!table)
+		return -EINVAL;
+
+	/* Find a exact match - this list is usually very small */
+	for (i = 0; i < data->num_vdd_table; i++, table++)
+		if (table->reference_uv == reference_uv)
+			return table->optimized_uv;
+
+	/* IF things are screwed up, we'd make a mess on console.. ratelimit */
+	dev_err_ratelimited(dev, "%s: Failed optimized voltage match for %d\n",
+			    __func__, reference_uv);
+	return reference_uv;
+}
+
+static int _opp_set_voltage(struct device *dev,
+			    struct dev_pm_opp_supply *supply,
+			    int new_target_uv, struct regulator *reg,
+			    char *reg_name)
+{
+	int ret;
+	unsigned long vdd_uv, uv_max;
+
+	if (new_target_uv)
+		vdd_uv = new_target_uv;
+	else
+		vdd_uv = supply->u_volt;
+
+	/*
+	 * If we do have an absolute max voltage specified, then we should
+	 * use that voltage instead to allow for cases where the voltage rails
+	 * are ganged (example if we set the max for an opp as 1.12v, and
+	 * the absolute max is 1.5v, for another rail to get 1.25v, it cannot
+	 * be achieved if the regulator is constrainted to max of 1.12v, even
+	 * if it can function at 1.25v
+	 */
+	if (opp_data.vdd_absolute_max_voltage_uv)
+		uv_max = opp_data.vdd_absolute_max_voltage_uv;
+	else
+		uv_max = supply->u_volt_max;
+
+	if (vdd_uv > uv_max ||
+	    vdd_uv < supply->u_volt_min ||
+	    supply->u_volt_min > uv_max) {
+		dev_warn(dev,
+			 "Invalid range voltages [Min:%lu target:%lu Max:%lu]\n",
+			 supply->u_volt_min, vdd_uv, uv_max);
+		return -EINVAL;
+	}
+
+	dev_dbg(dev, "%s scaling to %luuV[min %luuV max %luuV]\n", reg_name,
+		vdd_uv, supply->u_volt_min,
+		uv_max);
+
+	ret = regulator_set_voltage_triplet(reg,
+					    supply->u_volt_min,
+					    vdd_uv,
+					    uv_max);
+	if (ret) {
+		dev_err(dev, "%s failed for %luuV[min %luuV max %luuV]\n",
+			reg_name, vdd_uv, supply->u_volt_min,
+			uv_max);
+		return ret;
+	}
+
+	return 0;
+}
+
+/**
+ * ti_opp_supply_set_opp() - do the opp supply transition
+ * @data:	information on regulators and new and old opps provided by
+ *		opp core to use in transition
+ *
+ * Return: If successful, 0, else appropriate error value.
+ */
+int ti_opp_supply_set_opp(struct dev_pm_set_opp_data *data)
+{
+	struct dev_pm_opp_supply *old_supply_vdd = &data->old_opp.supplies[0];
+	struct dev_pm_opp_supply *old_supply_vbb = &data->old_opp.supplies[1];
+	struct dev_pm_opp_supply *new_supply_vdd = &data->new_opp.supplies[0];
+	struct dev_pm_opp_supply *new_supply_vbb = &data->new_opp.supplies[1];
+	struct device *dev = data->dev;
+	unsigned long old_freq = data->old_opp.rate, freq = data->new_opp.rate;
+	struct clk *clk = data->clk;
+	struct regulator *vdd_reg = data->regulators[0];
+	struct regulator *vbb_reg = data->regulators[1];
+	int vdd_uv;
+	int ret;
+
+	vdd_uv = _get_optimal_vdd_voltage(dev, &opp_data,
+					  new_supply_vbb->u_volt);
+
+	/* Scaling up? Scale voltage before frequency */
+	if (freq > old_freq) {
+		ret = _opp_set_voltage(dev, new_supply_vdd, vdd_uv, vdd_reg,
+				       "vdd");
+		if (ret)
+			goto restore_voltage;
+
+		ret = _opp_set_voltage(dev, new_supply_vbb, 0, vbb_reg, "vbb");
+		if (ret)
+			goto restore_voltage;
+	}
+
+	/* Change frequency */
+	dev_dbg(dev, "%s: switching OPP: %lu Hz --> %lu Hz\n",
+		__func__, old_freq, freq);
+
+	ret = clk_set_rate(clk, freq);
+	if (ret) {
+		dev_err(dev, "%s: failed to set clock rate: %d\n", __func__,
+			ret);
+		goto restore_voltage;
+	}
+
+	/* Scaling down? Scale voltage after frequency */
+	if (freq < old_freq) {
+		ret = _opp_set_voltage(dev, new_supply_vbb, 0, vbb_reg, "vbb");
+		if (ret)
+			goto restore_freq;
+
+		ret = _opp_set_voltage(dev, new_supply_vdd, vdd_uv, vdd_reg,
+				       "vdd");
+		if (ret)
+			goto restore_freq;
+	}
+
+	return 0;
+
+restore_freq:
+	ret = clk_set_rate(clk, old_freq);
+	if (ret)
+		dev_err(dev, "%s: failed to restore old-freq (%lu Hz)\n",
+			__func__, old_freq);
+restore_voltage:
+	/* This shouldn't harm even if the voltages weren't updated earlier */
+	if (old_supply_vdd->u_volt) {
+		ret = _opp_set_voltage(dev, old_supply_vbb, 0, vbb_reg, "vbb");
+		if (ret)
+			return ret;
+
+		ret = _opp_set_voltage(dev, old_supply_vdd, 0, vdd_reg,
+				       "vdd");
+		if (ret)
+			return ret;
+	}
+
+	return ret;
+}
+
+static const struct ti_opp_supply_of_data omap_generic_of_data = {
+};
+
+static const struct ti_opp_supply_of_data omap_omap5_of_data = {
+	.flags = OPPDM_EFUSE_CLASS0_OPTIMIZED_VOLTAGE,
+	.efuse_voltage_mask = 0xFFF,
+	.efuse_voltage_uv = false,
+};
+
+static const struct ti_opp_supply_of_data omap_omap5core_of_data = {
+	.flags = OPPDM_EFUSE_CLASS0_OPTIMIZED_VOLTAGE | OPPDM_HAS_NO_ABB,
+	.efuse_voltage_mask = 0xFFF,
+	.efuse_voltage_uv = false,
+};
+
+static const struct of_device_id ti_opp_supply_of_match[] = {
+	{.compatible = "ti,omap-opp-supply", .data = &omap_generic_of_data},
+	{.compatible = "ti,omap5-opp-supply", .data = &omap_omap5_of_data},
+	{.compatible = "ti,omap5-core-opp-supply",
+	 .data = &omap_omap5core_of_data},
+	{},
+};
+MODULE_DEVICE_TABLE(of, ti_opp_supply_of_match);
+
+static int ti_opp_supply_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct device *cpu_dev = get_cpu_device(0);
+	const struct of_device_id *match;
+	const struct ti_opp_supply_of_data *of_data;
+	int ret = 0;
+
+	match = of_match_device(ti_opp_supply_of_match, dev);
+	if (!match) {
+		/* We do not expect this to happen */
+		dev_err(dev, "%s: Unable to match device\n", __func__);
+		return -ENODEV;
+	}
+	if (!match->data) {
+		/* Again, unlikely.. but mistakes do happen */
+		dev_err(dev, "%s: Bad data in match\n", __func__);
+		return -EINVAL;
+	}
+	of_data = match->data;
+
+	dev_set_drvdata(dev, (void *)of_data);
+
+	/* If we need optimized voltage */
+	if (of_data->flags & OPPDM_EFUSE_CLASS0_OPTIMIZED_VOLTAGE) {
+		ret = _store_optimized_voltages(dev, &opp_data);
+		if (ret)
+			return ret;
+	}
+
+	ret = PTR_ERR_OR_ZERO(dev_pm_opp_register_set_opp_helper(cpu_dev,
+								 ti_opp_supply_set_opp));
+	if (ret)
+		_free_optimized_voltages(dev, &opp_data);
+
+	return ret;
+}
+
+static struct platform_driver ti_opp_supply_driver = {
+	.probe = ti_opp_supply_probe,
+	.driver = {
+		   .name = "ti_opp_supply",
+		   .owner = THIS_MODULE,
+		   .of_match_table = of_match_ptr(ti_opp_supply_of_match),
+		   },
+};
+module_platform_driver(ti_opp_supply_driver);
+
+MODULE_DESCRIPTION("Texas Instruments OMAP OPP Supply driver");
+MODULE_AUTHOR("Texas Instruments Inc.");
+MODULE_LICENSE("GPL v2");
-- 
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 3/4] dt-bindings: opp: Introduce ti-opp-supply bindings
From: Dave Gerlach @ 2017-12-13 20:33 UTC (permalink / raw)
  To: Viresh Kumar, Rob Herring, Rafael J . Wysocki
  Cc: linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	linux-omap-u79uwXL29TY76Z2rM5mHXA,
	linux-pm-u79uwXL29TY76Z2rM5mHXA,
	devicetree-u79uwXL29TY76Z2rM5mHXA, Tony Lindgren, Nishanth Menon,
	Dave Gerlach
In-Reply-To: <20171213203358.20839-1-d-gerlach-l0cyMroinI0@public.gmane.org>

Document the devicetree bindings that describe Texas Instruments
opp-supply which allow a platform to describe multiple regulators and
additional information, such as registers containing data needed to
program aforementioned regulators.

Signed-off-by: Dave Gerlach <d-gerlach-l0cyMroinI0@public.gmane.org>
---
 .../bindings/opp/ti-omap5-opp-supply.txt           | 63 ++++++++++++++++++++++
 1 file changed, 63 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/opp/ti-omap5-opp-supply.txt

diff --git a/Documentation/devicetree/bindings/opp/ti-omap5-opp-supply.txt b/Documentation/devicetree/bindings/opp/ti-omap5-opp-supply.txt
new file mode 100644
index 000000000000..832346e489a3
--- /dev/null
+++ b/Documentation/devicetree/bindings/opp/ti-omap5-opp-supply.txt
@@ -0,0 +1,63 @@
+Texas Instruments OMAP compatible OPP supply description
+
+OMAP5, DRA7, and AM57 family of SoCs have Class0 AVS eFuse registers which
+contain data that can be used to adjust voltages programmed for some of their
+supplies for more efficient operation. This binding provides the information
+needed to read these values and use them to program the main regulator during
+an OPP transitions.
+
+Also, some supplies may have an associated vbb-supply which is an Adaptive Body
+Bias regulator which much be transitioned in a specific sequence with regards
+to the vdd-supply and clk when making an OPP transition. By supplying two
+regulators to the device that will undergo OPP transitions we can make use
+of the multi regulator binding that is part of the OPP core described here [1]
+to describe both regulators needed by the platform.
+
+[1] Documentation/devicetree/bindings/opp/opp.txt
+
+Required Properties for Device Node:
+- vdd-supply: phandle to regulator controlling VDD supply
+- vbb-supply: phandle to regulator controlling Body Bias supply
+	      (Usually Adaptive Body Bias regulator)
+
+Required Properties for opp-supply node:
+- compatible: Should be one of:
+	"ti,omap-opp-supply" - basic OPP supply controlling VDD and VBB
+	"ti,omap5-opp-supply" - OMAP5+ optimized voltages in efuse(class0)VDD
+			    along with VBB
+	"ti,omap5-core-opp-supply" - OMAP5+ optimized voltages in efuse(class0) VDD
+			    but no VBB.
+- reg: Address and length of the efuse register set for the device (mandatory
+	only for "ti,omap5-opp-supply")
+- ti,efuse-settings: An array of u32 tuple items providing information about
+	optimized efuse configuration. Each item consists of the following:
+	volt: voltage in uV - reference voltage (OPP voltage)
+	efuse_offseet: efuse offset from reg where the optimized voltage is stored.
+- ti,absolute-max-voltage-uv: absolute maximum voltage for the OPP supply.
+
+Example:
+
+/* Device Node (CPU)  */
+cpus {
+	cpu0: cpu@0 {
+		device_type = "cpu";
+
+		...
+
+		vdd-supply = <&vcc>;
+		vbb-supply = <&abb_mpu>;
+	};
+};
+
+/* OMAP OPP Supply with Class0 registers */
+opp_supply_mpu: opp_supply@4a003b20 {
+	compatible = "ti,omap5-opp-supply";
+	reg = <0x4a003b20 0x8>;
+	ti,efuse-settings = <
+	/* uV   offset */
+	1060000 0x0
+	1160000 0x4
+	1210000 0x8
+	>;
+	ti,absolute-max-voltage-uv = <1500000>;
+};
-- 
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 2/4] cpufreq: ti-cpufreq: Add support for multiple regulators
From: Dave Gerlach @ 2017-12-13 20:33 UTC (permalink / raw)
  To: Viresh Kumar, Rob Herring, Rafael J . Wysocki
  Cc: linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	linux-omap-u79uwXL29TY76Z2rM5mHXA,
	linux-pm-u79uwXL29TY76Z2rM5mHXA,
	devicetree-u79uwXL29TY76Z2rM5mHXA, Tony Lindgren, Nishanth Menon,
	Dave Gerlach
In-Reply-To: <20171213203358.20839-1-d-gerlach-l0cyMroinI0@public.gmane.org>

Some platforms, like those in the DRA7 and AM57 families, require the
scaling of multiple regulators in order to properly support higher OPPs.
Let the ti-cpufreq driver determine when this is required and pass the
appropriate regulator names to the OPP core so that they can be properly
managed.

Signed-off-by: Dave Gerlach <d-gerlach-l0cyMroinI0@public.gmane.org>
---
 drivers/cpufreq/ti-cpufreq.c | 28 ++++++++++++++++++++++++----
 1 file changed, 24 insertions(+), 4 deletions(-)

diff --git a/drivers/cpufreq/ti-cpufreq.c b/drivers/cpufreq/ti-cpufreq.c
index b1c230a1e2aa..a099b7bf74cd 100644
--- a/drivers/cpufreq/ti-cpufreq.c
+++ b/drivers/cpufreq/ti-cpufreq.c
@@ -51,6 +51,7 @@ struct ti_cpufreq_soc_data {
 	unsigned long efuse_mask;
 	unsigned long efuse_shift;
 	unsigned long rev_offset;
+	bool multi_regulator;
 };
 
 struct ti_cpufreq_data {
@@ -58,6 +59,7 @@ struct ti_cpufreq_data {
 	struct device_node *opp_node;
 	struct regmap *syscon;
 	const struct ti_cpufreq_soc_data *soc_data;
+	struct opp_table *opp_table;
 };
 
 static unsigned long amx3_efuse_xlate(struct ti_cpufreq_data *opp_data,
@@ -96,6 +98,7 @@ static struct ti_cpufreq_soc_data am3x_soc_data = {
 	.efuse_offset = 0x07fc,
 	.efuse_mask = 0x1fff,
 	.rev_offset = 0x600,
+	.multi_regulator = false,
 };
 
 static struct ti_cpufreq_soc_data am4x_soc_data = {
@@ -104,6 +107,7 @@ static struct ti_cpufreq_soc_data am4x_soc_data = {
 	.efuse_offset = 0x0610,
 	.efuse_mask = 0x3f,
 	.rev_offset = 0x600,
+	.multi_regulator = false,
 };
 
 static struct ti_cpufreq_soc_data dra7_soc_data = {
@@ -112,6 +116,7 @@ static struct ti_cpufreq_soc_data dra7_soc_data = {
 	.efuse_mask = 0xf80000,
 	.efuse_shift = 19,
 	.rev_offset = 0x204,
+	.multi_regulator = true,
 };
 
 /**
@@ -201,7 +206,9 @@ static int ti_cpufreq_probe(struct platform_device *pdev)
 	u32 version[VERSION_COUNT];
 	struct device_node *np;
 	const struct of_device_id *match;
+	struct opp_table *ti_opp_table;
 	struct ti_cpufreq_data *opp_data;
+	const char * const reg_names[] = {"vdd", "vbb"};
 	int ret;
 
 	np = of_find_node_by_path("/");
@@ -248,16 +255,29 @@ static int ti_cpufreq_probe(struct platform_device *pdev)
 	if (ret)
 		goto fail_put_node;
 
-	ret = PTR_ERR_OR_ZERO(dev_pm_opp_set_supported_hw(opp_data->cpu_dev,
-							  version, VERSION_COUNT));
-	if (ret) {
+	ti_opp_table = dev_pm_opp_set_supported_hw(opp_data->cpu_dev,
+						   version, VERSION_COUNT);
+	if (IS_ERR(ti_opp_table)) {
 		dev_err(opp_data->cpu_dev,
 			"Failed to set supported hardware\n");
+		ret = PTR_ERR(ti_opp_table);
 		goto fail_put_node;
 	}
 
-	of_node_put(opp_data->opp_node);
+	opp_data->opp_table = ti_opp_table;
+
+	if (opp_data->soc_data->multi_regulator) {
+		ti_opp_table = dev_pm_opp_set_regulators(opp_data->cpu_dev,
+							 reg_names,
+							 ARRAY_SIZE(reg_names));
+		if (IS_ERR(ti_opp_table)) {
+			dev_pm_opp_put_supported_hw(opp_data->opp_table);
+			ret =  PTR_ERR(ti_opp_table);
+			goto fail_put_node;
+		}
+	}
 
+	of_node_put(opp_data->opp_node);
 register_cpufreq_dt:
 	platform_device_register_simple("cpufreq-dt", -1, NULL, 0);
 
-- 
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/4] cpufreq: ti-cpufreq: Convert to module_platform_driver
From: Dave Gerlach @ 2017-12-13 20:33 UTC (permalink / raw)
  To: Viresh Kumar, Rob Herring, Rafael J . Wysocki
  Cc: linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	linux-omap-u79uwXL29TY76Z2rM5mHXA,
	linux-pm-u79uwXL29TY76Z2rM5mHXA,
	devicetree-u79uwXL29TY76Z2rM5mHXA, Tony Lindgren, Nishanth Menon,
	Dave Gerlach
In-Reply-To: <20171213203358.20839-1-d-gerlach-l0cyMroinI0@public.gmane.org>

ti-cpufreq will be responsible for calling dev_pm_opp_set_regulators on
platforms that require AVS and ABB regulator support so we must be
able to defer probe if regulators are not yet available, so change
ti-cpufreq to be a module_platform_driver to allow for probe defer.

Signed-off-by: Dave Gerlach <d-gerlach-l0cyMroinI0@public.gmane.org>
---
 drivers/cpufreq/ti-cpufreq.c | 23 +++++++++++++++++++++--
 1 file changed, 21 insertions(+), 2 deletions(-)

diff --git a/drivers/cpufreq/ti-cpufreq.c b/drivers/cpufreq/ti-cpufreq.c
index 923317f03b4b..b1c230a1e2aa 100644
--- a/drivers/cpufreq/ti-cpufreq.c
+++ b/drivers/cpufreq/ti-cpufreq.c
@@ -17,6 +17,7 @@
 #include <linux/cpu.h>
 #include <linux/io.h>
 #include <linux/mfd/syscon.h>
+#include <linux/module.h>
 #include <linux/init.h>
 #include <linux/of.h>
 #include <linux/of_platform.h>
@@ -195,7 +196,7 @@ static const struct of_device_id ti_cpufreq_of_match[] = {
 	{},
 };
 
-static int ti_cpufreq_init(void)
+static int ti_cpufreq_probe(struct platform_device *pdev)
 {
 	u32 version[VERSION_COUNT];
 	struct device_node *np;
@@ -269,4 +270,22 @@ static int ti_cpufreq_init(void)
 
 	return ret;
 }
-device_initcall(ti_cpufreq_init);
+
+static int ti_cpufreq_init(void)
+{
+	platform_device_register_simple("ti-cpufreq", -1, NULL, 0);
+	return 0;
+}
+module_init(ti_cpufreq_init);
+
+static struct platform_driver ti_cpufreq_driver = {
+	.probe = ti_cpufreq_probe,
+	.driver = {
+		.name = "ti-cpufreq",
+	},
+};
+module_platform_driver(ti_cpufreq_driver);
+
+MODULE_DESCRIPTION("TI CPUFreq/OPP hw-supported driver");
+MODULE_AUTHOR("Dave Gerlach <d-gerlach-l0cyMroinI0@public.gmane.org>");
+MODULE_LICENSE("GPL v2");
-- 
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/4] PM / OPP: Introduce ti-opp-supply driver
From: Dave Gerlach @ 2017-12-13 20:33 UTC (permalink / raw)
  To: Viresh Kumar, Rob Herring, Rafael J . Wysocki
  Cc: linux-arm-kernel, linux-omap, linux-pm, devicetree, Tony Lindgren,
	Nishanth Menon, Dave Gerlach

Some SoCs, such as Texas Instruments DRA7 family, have complex requirements for
scaling voltages and supplies when doing things like DVFS. For example, for
cpufreq on dra7xx, the cpu-supply must be scaled as is normally done however
there is also an Adaptive Body Bias (ABB) regulator that should be scaled at
the same time as the main supply in sequence depending on the transition,
explained earlier by Nishanth Menon here [1]. In addition to this, each
possible operating point has a corresponding optimized voltage value stored in
a register (Adaptive Voltage Scaling Class 0) that can be used instead of the
nominal value.

The OPP framework is now able to to handle DVFS transitions through the
provided dev_pm_opp_set_rate API and cpufreq-dt is doing this. Viresh Kumar
extended this to allow platforms to register a transition helper through the
use of dev_pm_opp_register_set_opp_helper and has also extended the OPP core to
support registering multiple regulators. By providing a TI platform specific
opp_helper function and registering the proepr regulators with the OPP core we
can meet the above requirements for properly changing DVFS state of the cpu
device.

This series introduces a ti-opp-supply driver that overrides the standard
single regulator DVFS transition handler to handle scaling the ABB regulator in
sequence with the normal supply and programing AVS voltages through a custom
opp_helper that is registered. The ti-cpufreq driver is extended to allow
registering the proper regulators needed for the CPU if the platform supports
multi regulators and is also changed to a regular driver so that it can defer
probe if needed as it now may have to if the regulators are not ready.

This series only contains driver changes and binding docs, DT patches will be
sent later but I have pushed them all here for anyone curious [2].

This series is required to enable the highest 1.5GHz OPP on dra7/am57 platforms
that support it but all dra7/am57 platforms will make use of this for all OPPs.

Regards,
Dave

[1] https://marc.info/?l=linux-pm&m=145684495832764&w=2
[2] https://github.com/dgerlach/linux-pm/tree/upstream/v4.15/ti-multireg-support

Dave Gerlach (4):
  cpufreq: ti-cpufreq: Convert to module_platform_driver
  cpufreq: ti-cpufreq: Add support for multiple regulators
  dt-bindings: opp: Introduce ti-opp-supply bindings
  PM / OPP: Add ti-opp-supply driver

 .../bindings/opp/ti-omap5-opp-supply.txt           |  63 +++
 drivers/cpufreq/ti-cpufreq.c                       |  51 ++-
 drivers/opp/Makefile                               |   1 +
 drivers/opp/ti-opp-supply.c                        | 428 +++++++++++++++++++++
 4 files changed, 537 insertions(+), 6 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/opp/ti-omap5-opp-supply.txt
 create mode 100644 drivers/opp/ti-opp-supply.c

-- 
2.15.1

^ permalink raw reply

* Re: [PATCH] leds: as3645a: Fix checkpatch warnings
From: Jacek Anaszewski @ 2017-12-13 20:29 UTC (permalink / raw)
  To: Dan Murphy, robh+dt-DgEjT+Ai2ygdnm+yROfE0A,
	mark.rutland-5wv7dgnIgG8, rpurdie-Fm38FmjxZ/leoWH0uzbU5w,
	pavel-+ZI9xUNit7I, sakari.ailus-X3B1VOXEql0,
	laurent.pinchart-ryLnwIuWjnjg/C1BVhZhaw
  Cc: devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	linux-leds-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <20171212215011.30066-1-dmurphy-l0cyMroinI0@public.gmane.org>

Hi Dan,

checkpatch.pl doesn't want to be mentioned in the patch subject :-)

"WARNING: A patch subject line should describe the change not the tool
that found it"

Preferably I'd see two separate patches.

Also, line length limit for the commit description is 75 characters.
Please use whole available space.

-- 
Best regards,
Jacek Anaszewski

On 12/12/2017 10:50 PM, Dan Murphy wrote:
> Fix two checkpatch warnings for 80 char
> length and for a quoted string across multiple
> line warnings.
> 
> Signed-off-by: Dan Murphy <dmurphy-l0cyMroinI0@public.gmane.org>
> ---
>  drivers/leds/leds-as3645a.c | 7 ++++---
>  1 file changed, 4 insertions(+), 3 deletions(-)
> 
> diff --git a/drivers/leds/leds-as3645a.c b/drivers/leds/leds-as3645a.c
> index 9a257f969300..f883616d9e60 100644
> --- a/drivers/leds/leds-as3645a.c
> +++ b/drivers/leds/leds-as3645a.c
> @@ -360,7 +360,8 @@ static int as3645a_set_flash_brightness(struct led_classdev_flash *fled,
>  {
>  	struct as3645a *flash = fled_to_as3645a(fled);
>  
> -	flash->flash_current = as3645a_current_to_reg(flash, true, brightness_ua);
> +	flash->flash_current = as3645a_current_to_reg(flash, true,
> +						      brightness_ua);
>  
>  	return as3645a_set_current(flash);
>  }
> @@ -455,8 +456,8 @@ static int as3645a_detect(struct as3645a *flash)
>  
>  	/* Verify the chip model and version. */
>  	if (model != 0x01 || rfu != 0x00) {
> -		dev_err(dev, "AS3645A not detected "
> -			"(model %d rfu %d)\n", model, rfu);
> +		dev_err(dev, "AS3645A not detected (model %d rfu %d)\n",
> +			model, rfu);
>  		return -ENODEV;
>  	}
>  
> 

--
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 v9 2/2] media: i2c: Add the ov7740 image sensor driver
From: Sakari Ailus @ 2017-12-13 20:06 UTC (permalink / raw)
  To: Wenyou Yang, Mauro Carvalho Chehab, Rob Herring, Mark Rutland
  Cc: linux-kernel-u79uwXL29TY76Z2rM5mHXA, Nicolas Ferre,
	devicetree-u79uwXL29TY76Z2rM5mHXA, Jonathan Corbet, Hans Verkuil,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	Linux Media Mailing List, Songjun Wu
In-Reply-To: <20171211013146.2497-3-wenyou.yang-UWL1GkI3JZL3oGB3hsPCZA@public.gmane.org>

Hi Wenyou,

Wenyou Yang wrote:
...
> +static int ov7740_start_streaming(struct ov7740 *ov7740)
> +{
> +	int ret;
> +
> +	if (ov7740->fmt) {
> +		ret = regmap_multi_reg_write(ov7740->regmap,
> +					     ov7740->fmt->regs,
> +					     ov7740->fmt->reg_num);
> +		if (ret)
> +			return ret;
> +	}
> +
> +	if (ov7740->frmsize) {
> +		ret = regmap_multi_reg_write(ov7740->regmap,
> +					     ov7740->frmsize->regs,
> +					     ov7740->frmsize->reg_num);
> +		if (ret)
> +			return ret;
> +	}
> +
> +	return __v4l2_ctrl_handler_setup(ov7740->subdev.ctrl_handler);

I believe you're still setting the controls after starting streaming.

-- 
Sakari Ailus
sakari.ailus-X3B1VOXEql0@public.gmane.org

--
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] of_mdio / mdiobus: ensure mdio devices have fwnode correctly populated
From: David Miller @ 2017-12-13 20:02 UTC (permalink / raw)
  To: rmk+kernel-I+IVW8TIWO2tmTQ+vhA3Yw
  Cc: andrew-g2DYL2Zd6BY, f.fainelli-Re5JQEeQqe8AvxtiuMwx3w,
	robh+dt-DgEjT+Ai2ygdnm+yROfE0A,
	frowand.list-Re5JQEeQqe8AvxtiuMwx3w,
	netdev-u79uwXL29TY76Z2rM5mHXA, devicetree-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <E1eOi7f-0002Rs-K7-eh5Bv4kxaXIk46pC+1QYvQNdhmdF6hFW@public.gmane.org>

From: Russell King <rmk+kernel-I+IVW8TIWO2tmTQ+vhA3Yw@public.gmane.org>
Date: Tue, 12 Dec 2017 10:49:15 +0000

> 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-I+IVW8TIWO2tmTQ+vhA3Yw@public.gmane.org>

Applied.
--
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 2/2] ARM: dts: vf610-zii-dev: use XAUI for DSA link ports
From: David Miller @ 2017-12-13 19:59 UTC (permalink / raw)
  To: rmk+kernel
  Cc: andrew, f.fainelli, devicetree, linux-arm-kernel, mark.rutland,
	netdev, robh+dt, kernel, shawnguo, stefan, vivien.didelot
In-Reply-To: <E1eOgsp-0001Ab-9x@rmk-PC.armlinux.org.uk>

From: Russell King <rmk+kernel@armlinux.org.uk>
Date: Tue, 12 Dec 2017 09:29:51 +0000

> Use XAUI rather than XGMII for DSA link ports, as this is the interface
> mode that the switches actually use. XAUI is the 4 lane bus with clock
> per direction, whereas XGMII is a 32 bit bus with clock.
> 
> Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk>
> ---
> This must be applied along with patch 1 to avoid breakage.

Applied.

^ permalink raw reply

* Re: [PATCH 1/2] net: dsa: allow XAUI phy interface mode
From: David Miller @ 2017-12-13 19:59 UTC (permalink / raw)
  To: rmk+kernel-I+IVW8TIWO2tmTQ+vhA3Yw
  Cc: andrew-g2DYL2Zd6BY, f.fainelli-Re5JQEeQqe8AvxtiuMwx3w,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	mark.rutland-5wv7dgnIgG8, netdev-u79uwXL29TY76Z2rM5mHXA,
	robh+dt-DgEjT+Ai2ygdnm+yROfE0A, kernel-bIcnvbaLZ9MEGnE8C9+IrQ,
	shawnguo-DgEjT+Ai2ygdnm+yROfE0A, stefan-XLVq0VzYD2Y,
	vivien.didelot-4ysUXcep3aM1wj+D4I0NRVaTQe2KTcn/
In-Reply-To: <E1eOgsk-0001AU-5z-eh5Bv4kxaXIk46pC+1QYvQNdhmdF6hFW@public.gmane.org>

From: Russell King <rmk+kernel-I+IVW8TIWO2tmTQ+vhA3Yw@public.gmane.org>
Date: Tue, 12 Dec 2017 09:29:46 +0000

> XGMII is a 32-bit bus plus two clock signals per direction.  XAUI is
> four serial lanes per direction.  The 88e6190 supports XAUI but not
> XGMII as it doesn't have enough pins.  The same is true of 88e6176.
> 
> Match on PHY_INTERFACE_MODE_XAUI for the XAUI port type, but keep
> accepting XGMII for backwards compatibility.
> 
> Signed-off-by: Russell King <rmk+kernel-I+IVW8TIWO2tmTQ+vhA3Yw@public.gmane.org>

Applied.
--
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 0/6] Updated LP8860 driver series
From: Jacek Anaszewski @ 2017-12-13 19:55 UTC (permalink / raw)
  To: Dan Murphy, robh+dt, mark.rutland, rpurdie, pavel
  Cc: devicetree, linux-kernel, linux-leds
In-Reply-To: <20171212220143.31210-1-dmurphy@ti.com>

Dan,

Patch set applied, thanks.

-- 
Best regards,
Jacek Anaszewski

On 12/12/2017 11:01 PM, Dan Murphy wrote:
> All
> 
> v4 - Fix checkpatch warning for code indentation on the
> leds: lp8860: Update the dt parsing for LED labeling patch.
> 
> v3 - Made changes to the patch set to address concerns on DT node naming conventions
> based on discussion in the RFC patch https://patchwork.kernel.org/patch/10089047/
> also made requested changes to the DT and driver based on feedback.  Patchworks
> links in each patch.
> 
> v2 - Added an initial patch to bring the DT binding up to standard prior to adding
> the changes for the label and triggers.
> 
> v1 Cover letter repeat below
> 
> After creating a new LED driver for the LM3692x device I went back to the
> LP8860 driver that I authored and found some updates that need to be applied.
> 
> First the way the LP8860 retrieved the label from the DT was incorrect as the
> label should have been from a child node as opposed to the parent.  This is now
> fixed with this series.
> 
> Second, since that device can be used to as either a backlight driver or as a
> string agnostic driver a trigger to the backlight needed to be added.
> 
> Finally there are changes to the driver that need to be made as either
> unnoticed bugs or updates to the driver to align with the current LED
> framework.  For instance moving to the devm LED class registration, destroying
> the mutex upon driver removal and removing the in driver dependency on CONFIG_OF
> and moving it to the Kconfig.
> 
> With these changes this should at least bring the driver into a better shape.
> 
> There are additional changes coming for this driver but I wanted to get the
> driver up to snuff before adding a feature to it.
> 
> Dan
> 
> Dan Murphy (6):
>   dt: bindings: lp8860: Update bindings for lp8860
>   dt: bindings: lp8860: Update DT label binding
>   leds: lp8860: Update the dt parsing for LED labeling
>   dt: bindings: lp8860: Add trigger binding to the lp8860
>   leds: lp8860: Add DT parsing to retrieve the trigger node
>   leds: lp8860: Various fixes to align with LED framework
> 
>  .../devicetree/bindings/leds/leds-lp8860.txt       | 32 ++++++++++++-----
>  drivers/leds/Kconfig                               |  2 +-
>  drivers/leds/leds-lp8860.c                         | 40 ++++++++++++----------
>  3 files changed, 46 insertions(+), 28 deletions(-)
> 

^ permalink raw reply

* Re: [PATCH v2 3/6] ARM: sun4i: Convert to CCU
From: Kevin Hilman @ 2017-12-13 19:46 UTC (permalink / raw)
  To: Priit Laes
  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: <20171213171358.oocp24c2mdon45o5-q/aMd4JkU83YtjvyW6yDsg@public.gmane.org>

On Wed, Dec 13, 2017 at 9:13 AM, Priit Laes <plaes-q/aMd4JkU83YtjvyW6yDsg@public.gmane.org> wrote:
> 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-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))
>
> 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/

Can you share a full boot-log (including all the u-boot output etc.)
so I can see exactly how the kernel is being loaded?    Especially the
u-boot version?

As $SUBJECT patch seems to be changing clocks around, perhaps this is
an issue where some u-boot dependency is uncovered, and older versions
of u-boot don't play well with this change.

Kevin

^ permalink raw reply

* [PATCH 2/2] arm: dts: sun8i: a83t: Add an unit address to the memory node
From: Corentin Labbe @ 2017-12-13 19:37 UTC (permalink / raw)
  To: linux-I+IVW8TIWO2tmTQ+vhA3Yw, mark.rutland-5wv7dgnIgG8,
	maxime.ripard-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8,
	robh+dt-DgEjT+Ai2ygdnm+yROfE0A, wens-jdAy2FN1RRM
  Cc: devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	linux-sunxi-/JYPxA39Uh5TLH3MbocFFw, Corentin Labbe
In-Reply-To: <20171213193715.31039-1-clabbe.montjoie-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>

This will fix the following warning:
Warning (unit_address_vs_reg): Node /memory has a reg or ranges property, but no unit name

Signed-off-by: Corentin Labbe <clabbe.montjoie-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
---
 arch/arm/boot/dts/sun8i-a83t.dtsi | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/arch/arm/boot/dts/sun8i-a83t.dtsi b/arch/arm/boot/dts/sun8i-a83t.dtsi
index 715719f9ea2e..bddde0141f24 100644
--- a/arch/arm/boot/dts/sun8i-a83t.dtsi
+++ b/arch/arm/boot/dts/sun8i-a83t.dtsi
@@ -160,7 +160,7 @@
 		};
 	};
 
-	memory {
+	memory@40000000 {
 		reg = <0x40000000 0x80000000>;
 		device_type = "memory";
 	};
-- 
2.13.6

^ permalink raw reply related

* [PATCH 1/2] arm: dts: sun8i: a83t: remove leading zero from cpucfg node address
From: Corentin Labbe @ 2017-12-13 19:37 UTC (permalink / raw)
  To: linux-I+IVW8TIWO2tmTQ+vhA3Yw, mark.rutland-5wv7dgnIgG8,
	maxime.ripard-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8,
	robh+dt-DgEjT+Ai2ygdnm+yROfE0A, wens-jdAy2FN1RRM
  Cc: devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	linux-sunxi-/JYPxA39Uh5TLH3MbocFFw, Corentin Labbe

This will fix the following warning:
Warning (simple_bus_reg): Node /soc/cpucfg@01700000 simple-bus unit address format error, expected "1700000"

Signed-off-by: Corentin Labbe <clabbe.montjoie-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
---
 arch/arm/boot/dts/sun8i-a83t.dtsi | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/arch/arm/boot/dts/sun8i-a83t.dtsi b/arch/arm/boot/dts/sun8i-a83t.dtsi
index 7b372acf4bf6..715719f9ea2e 100644
--- a/arch/arm/boot/dts/sun8i-a83t.dtsi
+++ b/arch/arm/boot/dts/sun8i-a83t.dtsi
@@ -377,7 +377,7 @@
 			#reset-cells = <1>;
 		};
 
-		cpucfg@01700000 {
+		cpucfg@1700000 {
 			compatible = "allwinner,sun9i-a80-cpucfg";
 			reg = <0x01700000 0x100>;
 		};
-- 
2.13.6

--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply related

* Re: [PATCH v4 00/12] [dt-bindings] [media] Add document file and driver for Sony CXD2880 DVB-T2/T tuner + demodulator
From: Mauro Carvalho Chehab @ 2017-12-13 19:36 UTC (permalink / raw)
  To: Yasunari.Takiguchi-7U/KSKJipcs
  Cc: akpm-de/tnXTf+JLsfHDXvbKv3WD2FQJk+8+b,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-media-u79uwXL29TY76Z2rM5mHXA,
	tbird20d-Re5JQEeQqe8AvxtiuMwx3w,
	frowand.list-Re5JQEeQqe8AvxtiuMwx3w,
	Masayuki.Yamamoto-7U/KSKJipcs, Hideki.Nozawa-7U/KSKJipcs,
	Kota.Yonezawa-7U/KSKJipcs, Toshihiko.Matsumoto-7U/KSKJipcs,
	Satoshi.C.Watanabe-7U/KSKJipcs, Bird, Timothy
In-Reply-To: <20171013054635.20946-1-Yasunari.Takiguchi-7U/KSKJipcs@public.gmane.org>

Em Fri, 13 Oct 2017 14:46:35 +0900
<Yasunari.Takiguchi-7U/KSKJipcs@public.gmane.org> escreveu:

> From: Yasunari Takiguchi <Yasunari.Takiguchi-7U/KSKJipcs@public.gmane.org>
> 
> Hi,
> 
> This is the patch series (version 4) of Sony CXD2880 DVB-T2/T tuner + 
> demodulator driver.The driver supports DVB-API and interfaces through 
> SPI.
> 
> We have tested the driver on Raspberry Pi 3 and got picture and sound 
> from a media player.
> 
> The change history of this patch series is as below.

Finally had time to review this patch series. Sorry for taking so long.
4Q is usually very busy.

I answered each patch with comments. There ones I didn't comment
(patches 1, 4 and 8-12) is because I didn't see anything wrong,
except for the notes I did on other patches, mainly:
	- they lack SPDX license text;
	- the error codes need to review.

Additionally, on patches 8-11, I found weird to not found any
64 bits division, but I noticed some code there that it seemed
to actually be doing such division using some algorithm to make
them work on 32 bits machine. If so, please replace them by
do_div64(), as it makes clearer about what's been doing, and it
provides better performance when compiled on 64 bit Kernels (as
they become just a DIV asm operation).

Regards,
Mauro

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 07/12] [media] cxd2880: Add top level of the driver
From: Mauro Carvalho Chehab @ 2017-12-13 19:25 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: <20171013060934.21612-1-Yasunari.Takiguchi-7U/KSKJipcs@public.gmane.org>

Em Fri, 13 Oct 2017 15:09:34 +0900
<Yasunari.Takiguchi-7U/KSKJipcs@public.gmane.org> escreveu:

> From: Yasunari Takiguchi <Yasunari.Takiguchi-7U/KSKJipcs@public.gmane.org>
> 
> This provides the main dvb frontend operation functions
> for 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_top.c
>       -modified typo "inavlid" to "invalid" at pr_err
>       -removed unnecessary initialization at variable declaration
>       -removed unnecessary brace {}
>       -changed to use cxd2880_dvbt_tune and cxd2880_dvbt2_tune 
>        instead of cxd2880_integ_dvbt_tune and cxd2880_integ_dvbt2_tune
>        (because we changed it so that demodulator does not 
>          wait for locking the signal.) 
> 
> Changes in V3
>    drivers/media/dvb-frontends/cxd2880/cxd2880_top.c
>       -adjusted indent spaces
>       -modified debugging code
>       -removed unnecessary cast
>       -modified return code
>       -modified coding style of if() 
>       -modified about measurement period of PER/BER.
>       -changed hexadecimal code to lower case. 
> 
>  drivers/media/dvb-frontends/cxd2880/cxd2880_top.c | 2019 +++++++++++++++++++++
>  1 file changed, 2019 insertions(+)
>  create mode 100644 drivers/media/dvb-frontends/cxd2880/cxd2880_top.c
> 
> diff --git a/drivers/media/dvb-frontends/cxd2880/cxd2880_top.c b/drivers/media/dvb-frontends/cxd2880/cxd2880_top.c
> new file mode 100644
> index 000000000000..c82aaf0c1597
> --- /dev/null
> +++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_top.c
> @@ -0,0 +1,2019 @@
> +/*
> + * cxd2880_top.c
> + * Sony CXD2880 DVB-T2/T tuner + demodulator driver
> + *
> + * 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 pr_fmt(fmt) KBUILD_MODNAME ": %s: " fmt, __func__

Same comments as on other patches: use SPDX and dev_foo() for printing 
messages.

> +
> +#include <linux/spi/spi.h>
> +
> +#include "dvb_frontend.h"
> +#include "dvb_math.h"
> +
> +#include "cxd2880.h"
> +#include "cxd2880_tnrdmd_mon.h"
> +#include "cxd2880_tnrdmd_dvbt2_mon.h"
> +#include "cxd2880_tnrdmd_dvbt_mon.h"
> +#include "cxd2880_integ.h"
> +#include "cxd2880_tnrdmd_dvbt2.h"
> +#include "cxd2880_tnrdmd_dvbt.h"
> +#include "cxd2880_devio_spi.h"
> +#include "cxd2880_spi_device.h"
> +#include "cxd2880_tnrdmd_driver_version.h"
> +
> +struct cxd2880_priv {
> +	struct cxd2880_tnrdmd tnrdmd;
> +	struct spi_device *spi;
> +	struct cxd2880_io regio;
> +	struct cxd2880_spi_device spi_device;
> +	struct cxd2880_spi cxd2880_spi;
> +	struct cxd2880_dvbt_tune_param dvbt_tune_param;
> +	struct cxd2880_dvbt2_tune_param dvbt2_tune_param;
> +	struct mutex *spi_mutex; /* For SPI access exclusive control */
> +	unsigned long pre_ber_update;
> +	unsigned long pre_ber_interval;
> +	unsigned long post_ber_update;
> +	unsigned long post_ber_interval;
> +	unsigned long ucblock_update;
> +	unsigned long ucblock_interval;
> +	enum fe_status s;
> +};
> +
> +static int cxd2880_pre_bit_err_t(
> +		struct cxd2880_tnrdmd *tnrdmd, u32 *pre_bit_err,
> +		u32 *pre_bit_count)
> +{
> +	u8 rdata[2];
> +	int ret;
> +
> +	if ((!tnrdmd) || (!pre_bit_err) || (!pre_bit_count))
> +		return -EINVAL;
> +
> +	if (tnrdmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
> +		return -EINVAL;
> +
> +	if (tnrdmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
> +		return -EPERM;
> +
> +	if (tnrdmd->sys != CXD2880_DTV_SYS_DVBT)
> +		return -EPERM;

As pointed previously, please check the returned values on this
patch.

> +
> +	ret = slvt_freeze_reg(tnrdmd);
> +	if (ret)
> +		return ret;
> +
> +	ret = tnrdmd->io->write_reg(tnrdmd->io,
> +				    CXD2880_IO_TGT_DMD,
> +				    0x00, 0x10);
> +	if (ret) {
> +		slvt_unfreeze_reg(tnrdmd);
> +		return ret;
> +	}
> +
> +	ret = tnrdmd->io->read_regs(tnrdmd->io,
> +				    CXD2880_IO_TGT_DMD,
> +				    0x39, rdata, 1);
> +	if (ret) {
> +		slvt_unfreeze_reg(tnrdmd);
> +		return ret;
> +	}
> +
> +	if ((rdata[0] & 0x01) == 0) {
> +		slvt_unfreeze_reg(tnrdmd);
> +		return -EBUSY;
> +	}
> +
> +	ret = tnrdmd->io->read_regs(tnrdmd->io,
> +				    CXD2880_IO_TGT_DMD,
> +				    0x22, rdata, 2);
> +	if (ret) {
> +		slvt_unfreeze_reg(tnrdmd);
> +		return ret;
> +	}
> +
> +	*pre_bit_err = (rdata[0] << 8) | rdata[1];
> +
> +	ret = tnrdmd->io->read_regs(tnrdmd->io,
> +				    CXD2880_IO_TGT_DMD,
> +				    0x6f, rdata, 1);
> +	if (ret) {
> +		slvt_unfreeze_reg(tnrdmd);
> +		return ret;
> +	}
> +
> +	slvt_unfreeze_reg(tnrdmd);
> +
> +	*pre_bit_count = ((rdata[0] & 0x07) == 0) ?
> +			 256 : (0x1000 << (rdata[0] & 0x07));
> +
> +	return 0;
> +}
> +
> +static int cxd2880_pre_bit_err_t2(struct cxd2880_tnrdmd *tnrdmd,
> +				  u32 *pre_bit_err,
> +				  u32 *pre_bit_count)
> +{
> +	u32 period_exp = 0;
> +	u32 n_ldpc = 0;
> +	u8 data[5];
> +	int ret;
> +
> +	if ((!tnrdmd) || (!pre_bit_err) || (!pre_bit_count))
> +		return -EINVAL;
> +
> +	if (tnrdmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
> +		return -EINVAL;
> +
> +	if (tnrdmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
> +		return -EPERM;
> +
> +	if (tnrdmd->sys != CXD2880_DTV_SYS_DVBT2)
> +		return -EPERM;
> +
> +	ret = slvt_freeze_reg(tnrdmd);
> +	if (ret)
> +		return ret;
> +
> +	ret = tnrdmd->io->write_reg(tnrdmd->io,
> +				    CXD2880_IO_TGT_DMD,
> +				    0x00, 0x0b);
> +	if (ret) {
> +		slvt_unfreeze_reg(tnrdmd);
> +		return ret;
> +	}
> +
> +	ret = tnrdmd->io->read_regs(tnrdmd->io,
> +				    CXD2880_IO_TGT_DMD,
> +				    0x3c, data, sizeof(data));
> +	if (ret) {
> +		slvt_unfreeze_reg(tnrdmd);
> +		return ret;
> +	}
> +
> +	if (!(data[0] & 0x01)) {
> +		slvt_unfreeze_reg(tnrdmd);
> +		return -EBUSY;
> +	}
> +	*pre_bit_err =
> +	((data[1] & 0x0f) << 24) | (data[2] << 16) | (data[3] << 8) | data[4];
> +
> +	ret = tnrdmd->io->read_regs(tnrdmd->io,
> +				    CXD2880_IO_TGT_DMD,
> +				    0xa0, data, 1);
> +	if (ret) {
> +		slvt_unfreeze_reg(tnrdmd);
> +		return ret;
> +	}
> +
> +	if (((enum cxd2880_dvbt2_plp_fec)(data[0] & 0x03)) ==
> +	    CXD2880_DVBT2_FEC_LDPC_16K)
> +		n_ldpc = 16200;
> +	else
> +		n_ldpc = 64800;
> +	slvt_unfreeze_reg(tnrdmd);
> +
> +	ret = tnrdmd->io->write_reg(tnrdmd->io,
> +				    CXD2880_IO_TGT_DMD,
> +				    0x00, 0x20);
> +	if (ret)
> +		return ret;
> +
> +	ret = tnrdmd->io->read_regs(tnrdmd->io,
> +				    CXD2880_IO_TGT_DMD,
> +				    0x6f, data, 1);
> +	if (ret)
> +		return ret;
> +
> +	period_exp = data[0] & 0x0f;
> +
> +	*pre_bit_count = (1U << period_exp) * n_ldpc;
> +
> +	return 0;
> +}
> +
> +static int cxd2880_post_bit_err_t(struct cxd2880_tnrdmd *tnrdmd,
> +				  u32 *post_bit_err,
> +				  u32 *post_bit_count)
> +{
> +	u8 rdata[3];
> +	u32 bit_error = 0;
> +	u32 period_exp = 0;
> +	int ret;
> +
> +	if ((!tnrdmd) || (!post_bit_err) || (!post_bit_count))
> +		return -EINVAL;
> +
> +	if (tnrdmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
> +		return -EINVAL;
> +
> +	if (tnrdmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
> +		return -EPERM;
> +
> +	if (tnrdmd->sys != CXD2880_DTV_SYS_DVBT)
> +		return -EPERM;
> +
> +	ret = tnrdmd->io->write_reg(tnrdmd->io,
> +				    CXD2880_IO_TGT_DMD,
> +				    0x00, 0x0d);
> +	if (ret)
> +		return ret;
> +
> +	ret = tnrdmd->io->read_regs(tnrdmd->io,
> +				    CXD2880_IO_TGT_DMD,
> +				    0x15, rdata, 3);
> +	if (ret)
> +		return ret;
> +
> +	if ((rdata[0] & 0x40) == 0)
> +		return -EBUSY;
> +
> +	*post_bit_err = ((rdata[0] & 0x3f) << 16) | (rdata[1] << 8) | rdata[2];
> +
> +	ret = tnrdmd->io->write_reg(tnrdmd->io,
> +				    CXD2880_IO_TGT_DMD,
> +				    0x00, 0x10);
> +	if (ret)
> +		return ret;
> +
> +	ret = tnrdmd->io->read_regs(tnrdmd->io,
> +				    CXD2880_IO_TGT_DMD,
> +				    0x60, rdata, 1);
> +	if (ret)
> +		return ret;
> +
> +	period_exp = (rdata[0] & 0x1f);
> +
> +	if ((period_exp <= 11) && (bit_error > (1U << period_exp) * 204 * 8))
> +		return -EBUSY;
> +
> +	if (period_exp == 11)
> +		*post_bit_count = 3342336;
> +	else
> +		*post_bit_count = (1U << period_exp) * 204 * 81;
> +
> +	return 0;
> +}
> +
> +static int cxd2880_post_bit_err_t2(struct cxd2880_tnrdmd *tnrdmd,
> +				   u32 *post_bit_err,
> +				   u32 *post_bit_count)
> +{
> +	u32 period_exp = 0;
> +	u32 n_bch = 0;
> +	u8 data[3];
> +	enum cxd2880_dvbt2_plp_fec plp_fec_type =
> +		CXD2880_DVBT2_FEC_LDPC_16K;
> +	enum cxd2880_dvbt2_plp_code_rate plp_code_rate =
> +		CXD2880_DVBT2_R1_2;
> +	int ret;
> +	static const u16 n_bch_bits_lookup[2][8] = {
> +		{7200, 9720, 10800, 11880, 12600, 13320, 5400, 6480},
> +		{32400, 38880, 43200, 48600, 51840, 54000, 21600, 25920}
> +	};
> +
> +	if ((!tnrdmd) || (!post_bit_err) || (!post_bit_count))
> +		return -EINVAL;
> +
> +	if (tnrdmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
> +		return -EINVAL;
> +
> +	if (tnrdmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
> +		return -EPERM;
> +
> +	if (tnrdmd->sys != CXD2880_DTV_SYS_DVBT2)
> +		return -EPERM;
> +
> +	ret = slvt_freeze_reg(tnrdmd);
> +	if (ret)
> +		return ret;
> +
> +	ret = tnrdmd->io->write_reg(tnrdmd->io,
> +				    CXD2880_IO_TGT_DMD,
> +				    0x00, 0x0b);
> +	if (ret) {
> +		slvt_unfreeze_reg(tnrdmd);
> +		return ret;
> +	}
> +
> +	ret = tnrdmd->io->read_regs(tnrdmd->io,
> +				    CXD2880_IO_TGT_DMD,
> +				    0x15, data, 3);
> +	if (ret) {
> +		slvt_unfreeze_reg(tnrdmd);
> +		return ret;
> +	}
> +
> +	if (!(data[0] & 0x40)) {
> +		slvt_unfreeze_reg(tnrdmd);
> +		return -EBUSY;
> +	}
> +
> +	*post_bit_err =
> +		((data[0] & 0x3f) << 16) | (data[1] << 8) | data[2];
> +
> +	ret = tnrdmd->io->read_regs(tnrdmd->io,
> +				    CXD2880_IO_TGT_DMD,
> +				    0x9d, data, 1);
> +	if (ret) {
> +		slvt_unfreeze_reg(tnrdmd);
> +		return ret;
> +	}
> +
> +	plp_code_rate =
> +	(enum cxd2880_dvbt2_plp_code_rate)(data[0] & 0x07);
> +
> +	ret = tnrdmd->io->read_regs(tnrdmd->io,
> +				    CXD2880_IO_TGT_DMD,
> +				    0xa0, data, 1);
> +	if (ret) {
> +		slvt_unfreeze_reg(tnrdmd);
> +		return ret;
> +	}
> +
> +	plp_fec_type = (enum cxd2880_dvbt2_plp_fec)(data[0] & 0x03);
> +
> +	slvt_unfreeze_reg(tnrdmd);
> +
> +	ret = tnrdmd->io->write_reg(tnrdmd->io,
> +				    CXD2880_IO_TGT_DMD,
> +				    0x00, 0x20);
> +	if (ret)
> +		return ret;
> +
> +	ret = tnrdmd->io->read_regs(tnrdmd->io,
> +				    CXD2880_IO_TGT_DMD,
> +				    0x72, data, 1);
> +	if (ret)
> +		return ret;
> +
> +	period_exp = data[0] & 0x0f;
> +
> +	if ((plp_fec_type > CXD2880_DVBT2_FEC_LDPC_64K) ||
> +	    (plp_code_rate > CXD2880_DVBT2_R2_5))
> +		return -EBUSY;
> +
> +	n_bch = n_bch_bits_lookup[plp_fec_type][plp_code_rate];
> +
> +	if (*post_bit_err > ((1U << period_exp) * n_bch))
> +		return -EBUSY;
> +
> +	*post_bit_count = (1U << period_exp) * n_bch;
> +
> +	return 0;
> +}
> +
> +static int cxd2880_read_block_err_t(struct cxd2880_tnrdmd *tnrdmd,
> +				    u32 *block_err,
> +				    u32 *block_count)
> +{
> +	u8 rdata[3];
> +	int ret;
> +
> +	if ((!tnrdmd) || (!block_err) || (!block_count))
> +		return -EINVAL;
> +
> +	if (tnrdmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
> +		return -EINVAL;
> +
> +	if (tnrdmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
> +		return -EPERM;
> +
> +	if (tnrdmd->sys != CXD2880_DTV_SYS_DVBT)
> +		return -EPERM;
> +
> +	ret = tnrdmd->io->write_reg(tnrdmd->io,
> +				    CXD2880_IO_TGT_DMD,
> +				    0x00, 0x0d);
> +	if (ret)
> +		return ret;
> +
> +	ret = tnrdmd->io->read_regs(tnrdmd->io,
> +				    CXD2880_IO_TGT_DMD,
> +				    0x18, rdata, 3);
> +	if (ret)
> +		return ret;
> +
> +	if ((rdata[0] & 0x01) == 0)
> +		return -EBUSY;
> +
> +	*block_err = (rdata[1] << 8) | rdata[2];
> +
> +	ret = tnrdmd->io->write_reg(tnrdmd->io,
> +				    CXD2880_IO_TGT_DMD,
> +				    0x00, 0x10);
> +	if (ret)
> +		return ret;
> +
> +	ret = tnrdmd->io->read_regs(tnrdmd->io,
> +				    CXD2880_IO_TGT_DMD,
> +				    0x5c, rdata, 1);
> +	if (ret)
> +		return ret;
> +
> +	*block_count = 1U << (rdata[0] & 0x0f);
> +
> +	if ((*block_count == 0) || (*block_err > *block_count))
> +		return -EBUSY;
> +
> +	return 0;
> +}
> +
> +static int cxd2880_read_block_err_t2(struct cxd2880_tnrdmd *tnrdmd,
> +				     u32 *block_err,
> +				     u32 *block_count)
> +{
> +	u8 rdata[3];
> +	int ret;
> +
> +	if ((!tnrdmd) || (!block_err) || (!block_count))
> +		return -EINVAL;
> +
> +	if (tnrdmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
> +		return -EINVAL;
> +
> +	if (tnrdmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
> +		return -EPERM;
> +	if (tnrdmd->sys != CXD2880_DTV_SYS_DVBT2)
> +		return -EPERM;
> +
> +	ret = tnrdmd->io->write_reg(tnrdmd->io,
> +				    CXD2880_IO_TGT_DMD,
> +				    0x00, 0x0b);
> +	if (ret)
> +		return ret;
> +
> +	ret = tnrdmd->io->read_regs(tnrdmd->io,
> +				    CXD2880_IO_TGT_DMD,
> +				    0x18, rdata, 3);
> +	if (ret)
> +		return ret;
> +
> +	if ((rdata[0] & 0x01) == 0)
> +		return -EBUSY;
> +
> +	*block_err = (rdata[1] << 8) | rdata[2];
> +
> +	ret = tnrdmd->io->write_reg(tnrdmd->io,
> +				    CXD2880_IO_TGT_DMD,
> +				    0x00, 0x24);
> +	if (ret)
> +		return ret;
> +
> +	ret = tnrdmd->io->read_regs(tnrdmd->io,
> +				    CXD2880_IO_TGT_DMD,
> +				    0xdc, rdata, 1);
> +	if (ret)
> +		return ret;
> +
> +	*block_count = 1U << (rdata[0] & 0x0f);
> +
> +	if ((*block_count == 0) || (*block_err > *block_count))
> +		return -EBUSY;
> +
> +	return 0;
> +}
> +
> +static void cxd2880_release(struct dvb_frontend *fe)
> +{
> +	struct cxd2880_priv *priv = NULL;
> +
> +	if (!fe) {
> +		pr_err("invalid arg.\n");
> +		return;
> +	}
> +	priv = fe->demodulator_priv;
> +	kfree(priv);
> +}
> +
> +static int cxd2880_init(struct dvb_frontend *fe)
> +{
> +	int ret;
> +	struct cxd2880_priv *priv = NULL;
> +	struct cxd2880_tnrdmd_create_param create_param;
> +
> +	if (!fe) {
> +		pr_err("invalid arg.\n");
> +		return -EINVAL;
> +	}
> +
> +	priv = fe->demodulator_priv;
> +
> +	create_param.ts_output_if = CXD2880_TNRDMD_TSOUT_IF_SPI;
> +	create_param.xtal_share_type = CXD2880_TNRDMD_XTAL_SHARE_NONE;
> +	create_param.en_internal_ldo = 1;
> +	create_param.xosc_cap = 18;
> +	create_param.xosc_i = 8;
> +	create_param.stationary_use = 1;
> +
> +	mutex_lock(priv->spi_mutex);
> +	if (priv->tnrdmd.io != &priv->regio) {
> +		ret = cxd2880_tnrdmd_create(&priv->tnrdmd,
> +					    &priv->regio, &create_param);
> +		if (ret) {
> +			mutex_unlock(priv->spi_mutex);
> +			pr_info("cxd2880 tnrdmd create failed %d\n", ret);
> +			return ret;
> +		}
> +	}
> +	ret = cxd2880_integ_init(&priv->tnrdmd);
> +	if (ret) {
> +		mutex_unlock(priv->spi_mutex);
> +		pr_err("cxd2880 integ init failed %d\n", ret);
> +		return ret;
> +	}
> +	mutex_unlock(priv->spi_mutex);
> +
> +	pr_debug("OK.\n");
> +
> +	return ret;
> +}
> +
> +static int cxd2880_sleep(struct dvb_frontend *fe)
> +{
> +	int ret;
> +	struct cxd2880_priv *priv = NULL;
> +
> +	if (!fe) {
> +		pr_err("invalid arg\n");
> +		return -EINVAL;
> +	}
> +
> +	priv = fe->demodulator_priv;
> +
> +	mutex_lock(priv->spi_mutex);
> +	ret = cxd2880_tnrdmd_sleep(&priv->tnrdmd);
> +	mutex_unlock(priv->spi_mutex);
> +
> +	pr_debug("tnrdmd_sleep ret %d\n", ret);
> +
> +	return ret;
> +}
> +
> +static int cxd2880_read_signal_strength(struct dvb_frontend *fe,
> +					u16 *strength)
> +{
> +	int ret;
> +	struct cxd2880_priv *priv = NULL;
> +	struct dtv_frontend_properties *c = NULL;
> +	int level = 0;
> +
> +	if ((!fe) || (!strength)) {
> +		pr_err("invalid arg\n");
> +		return -EINVAL;
> +	}
> +
> +	priv = fe->demodulator_priv;
> +	c = &fe->dtv_property_cache;
> +
> +	mutex_lock(priv->spi_mutex);
> +	if ((c->delivery_system == SYS_DVBT) ||
> +	    (c->delivery_system == SYS_DVBT2)) {
> +		ret = cxd2880_tnrdmd_mon_rf_lvl(&priv->tnrdmd, &level);
> +	} else {
> +		pr_debug("invalid system\n");
> +		mutex_unlock(priv->spi_mutex);
> +		return -EINVAL;
> +	}
> +	mutex_unlock(priv->spi_mutex);
> +
> +	level /= 125;
> +	/*
> +	 * level should be between -105dBm and -30dBm.
> +	 * E.g. they should be between:
> +	 * -105000/125 = -840 and -30000/125 = -240
> +	 */
> +	level = clamp(level, -840, -240);
> +	/* scale value to 0x0000-0xffff */
> +	*strength = (u16)(((level + 840) * 0xffff) / (-240 + 840));
> +
> +	if (ret)
> +		pr_debug("ret = %d\n", ret);
> +
> +	return ret;
> +}
> +
> +static int cxd2880_read_snr(struct dvb_frontend *fe, u16 *snr)
> +{
> +	int ret;
> +	int snrvalue = 0;
> +	struct cxd2880_priv *priv = NULL;
> +	struct dtv_frontend_properties *c = NULL;
> +
> +	if ((!fe) || (!snr)) {
> +		pr_err("invalid arg\n");
> +		return -EINVAL;
> +	}
> +
> +	priv = fe->demodulator_priv;
> +	c = &fe->dtv_property_cache;
> +
> +	mutex_lock(priv->spi_mutex);
> +	if (c->delivery_system == SYS_DVBT) {
> +		ret = cxd2880_tnrdmd_dvbt_mon_snr(&priv->tnrdmd,
> +						  &snrvalue);
> +	} else if (c->delivery_system == SYS_DVBT2) {
> +		ret = cxd2880_tnrdmd_dvbt2_mon_snr(&priv->tnrdmd,
> +						   &snrvalue);
> +	} else {
> +		pr_err("invalid system\n");
> +		mutex_unlock(priv->spi_mutex);
> +		return -EINVAL;
> +	}
> +	mutex_unlock(priv->spi_mutex);
> +
> +	if (snrvalue < 0)
> +		snrvalue = 0;
> +	*snr = (u16)snrvalue;
> +
> +	if (ret)
> +		pr_debug("ret = %d\n", ret);
> +
> +	return ret;
> +}
> +
> +static int cxd2880_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks)
> +{
> +	int ret;
> +	struct cxd2880_priv *priv = NULL;
> +	struct dtv_frontend_properties *c = NULL;
> +
> +	if ((!fe) || (!ucblocks)) {
> +		pr_err("invalid arg\n");
> +		return -EINVAL;
> +	}
> +
> +	priv = fe->demodulator_priv;
> +	c = &fe->dtv_property_cache;
> +
> +	mutex_lock(priv->spi_mutex);
> +	if (c->delivery_system == SYS_DVBT) {
> +		ret = cxd2880_tnrdmd_dvbt_mon_packet_error_number(
> +								&priv->tnrdmd,
> +								ucblocks);
> +	} else if (c->delivery_system == SYS_DVBT2) {
> +		ret = cxd2880_tnrdmd_dvbt2_mon_packet_error_number(
> +								&priv->tnrdmd,
> +								ucblocks);
> +	} else {
> +		pr_err("invalid system\n");
> +		mutex_unlock(priv->spi_mutex);
> +		return -EINVAL;
> +	}
> +	mutex_unlock(priv->spi_mutex);
> +
> +	if (ret)
> +		pr_debug("ret = %d\n", ret);
> +
> +	return ret;
> +}
> +
> +static int cxd2880_read_ber(struct dvb_frontend *fe, u32 *ber)
> +{
> +	int ret;
> +	struct cxd2880_priv *priv = NULL;
> +	struct dtv_frontend_properties *c = NULL;
> +
> +	if ((!fe) || (!ber)) {
> +		pr_err("invalid arg\n");
> +		return -EINVAL;
> +	}
> +
> +	priv = fe->demodulator_priv;
> +	c = &fe->dtv_property_cache;
> +
> +	mutex_lock(priv->spi_mutex);
> +	if (c->delivery_system == SYS_DVBT) {
> +		ret = cxd2880_tnrdmd_dvbt_mon_pre_rsber(&priv->tnrdmd,
> +							ber);
> +		/* x100 to change unit.(10^7 -> 10^9) */
> +		*ber *= 100;
> +	} else if (c->delivery_system == SYS_DVBT2) {
> +		ret = cxd2880_tnrdmd_dvbt2_mon_pre_bchber(&priv->tnrdmd,
> +							  ber);
> +	} else {
> +		pr_err("invalid system\n");
> +		mutex_unlock(priv->spi_mutex);
> +		return -EINVAL;
> +	}
> +	mutex_unlock(priv->spi_mutex);
> +
> +	if (ret)
> +		pr_debug("ret = %d\n", ret);
> +
> +	return ret;
> +}
> +
> +static int cxd2880_set_ber_per_period_t(struct dvb_frontend *fe)
> +{
> +	int ret;
> +	struct dtv_frontend_properties *c;
> +	struct cxd2880_priv *priv;
> +	struct cxd2880_dvbt_tpsinfo info;
> +	enum cxd2880_dtv_bandwidth bw = CXD2880_DTV_BW_1_7_MHZ;
> +	u32 pre_ber_rate = 0;
> +	u32 post_ber_rate = 0;
> +	u32 ucblock_rate = 0;
> +	u32 mes_exp = 0;
> +	static const int cr_table[5] = {31500, 42000, 47250, 52500, 55125};
> +	static const int denominator_tbl[4] = {125664, 129472, 137088, 152320};
> +
> +	if (!fe) {
> +		pr_err("invalid arg\n");
> +		return -EINVAL;
> +	}
> +
> +	priv = fe->demodulator_priv;
> +	c = &fe->dtv_property_cache;
> +	bw = priv->dvbt_tune_param.bandwidth;
> +
> +	ret = cxd2880_tnrdmd_dvbt_mon_tps_info(&priv->tnrdmd,
> +					       &info);
> +	if (ret) {
> +		pr_err("tps monitor error ret = %d\n", ret);
> +		info.hierarchy = CXD2880_DVBT_HIERARCHY_NON;
> +		info.constellation = CXD2880_DVBT_CONSTELLATION_QPSK;
> +		info.guard = CXD2880_DVBT_GUARD_1_4;
> +		info.rate_hp = CXD2880_DVBT_CODERATE_1_2;
> +		info.rate_lp = CXD2880_DVBT_CODERATE_1_2;
> +	}
> +
> +	if (info.hierarchy == CXD2880_DVBT_HIERARCHY_NON) {
> +		pre_ber_rate = 63000000 * bw * (info.constellation * 2 + 2) /
> +			       denominator_tbl[info.guard];
> +
> +		post_ber_rate =	1000 * cr_table[info.rate_hp] * bw *
> +				(info.constellation * 2 + 2) /
> +				denominator_tbl[info.guard];
> +
> +		ucblock_rate = 875 * cr_table[info.rate_hp] * bw *
> +			       (info.constellation * 2 + 2) /
> +			       denominator_tbl[info.guard];
> +	} else {
> +		u8 data = 0;
> +		struct cxd2880_tnrdmd *tnrdmd = &priv->tnrdmd;
> +
> +		ret = tnrdmd->io->write_reg(tnrdmd->io,
> +					    CXD2880_IO_TGT_DMD,
> +					    0x00, 0x10);
> +		if (!ret) {
> +			ret = tnrdmd->io->read_regs(tnrdmd->io,
> +						    CXD2880_IO_TGT_DMD,
> +						    0x67, &data, 1);
> +			if (ret)
> +				data = 0x00;
> +		} else {
> +			data = 0x00;
> +		}
> +
> +		if (data & 0x01) { /* Low priority */
> +			pre_ber_rate =
> +				63000000 * bw * (info.constellation * 2 + 2) /
> +				denominator_tbl[info.guard];
> +
> +			post_ber_rate = 1000 * cr_table[info.rate_lp] * bw *
> +					(info.constellation * 2 + 2) /
> +					denominator_tbl[info.guard];
> +
> +			ucblock_rate = (1000 * 7 / 8) *	cr_table[info.rate_lp] *
> +				       bw * (info.constellation * 2 + 2) /
> +				       denominator_tbl[info.guard];
> +		} else { /* High priority */
> +			pre_ber_rate =
> +				63000000 * bw * 2 / denominator_tbl[info.guard];
> +
> +			post_ber_rate = 1000 * cr_table[info.rate_hp] * bw * 2 /
> +					denominator_tbl[info.guard];
> +
> +			ucblock_rate = (1000 * 7 / 8) * cr_table[info.rate_hp] *
> +					bw * 2 / denominator_tbl[info.guard];
> +		}
> +	}
> +
> +	mes_exp = pre_ber_rate < 8192 ? 8 : intlog2(pre_ber_rate) >> 24;
> +	priv->pre_ber_interval =
> +		((1U << mes_exp) * 1000 + (pre_ber_rate / 2)) /
> +		pre_ber_rate;
> +	cxd2880_tnrdmd_set_cfg(&priv->tnrdmd,
> +			       CXD2880_TNRDMD_CFG_DVBT_VBER_PERIOD,
> +			       mes_exp == 8 ? 0 : mes_exp - 12);
> +
> +	mes_exp = intlog2(post_ber_rate) >> 24;
> +	priv->post_ber_interval =
> +		((1U << mes_exp) * 1000 + (post_ber_rate / 2)) /
> +		post_ber_rate;
> +	cxd2880_tnrdmd_set_cfg(&priv->tnrdmd,
> +			       CXD2880_TNRDMD_CFG_DVBT_BERN_PERIOD,
> +			       mes_exp);
> +
> +	mes_exp = intlog2(ucblock_rate) >> 24;
> +	priv->ucblock_interval =
> +		((1U << mes_exp) * 1000 + (ucblock_rate / 2)) /
> +		ucblock_rate;
> +	cxd2880_tnrdmd_set_cfg(&priv->tnrdmd,
> +			       CXD2880_TNRDMD_CFG_DVBT_PER_MES,
> +			       mes_exp);
> +
> +	return 0;
> +}
> +
> +static int cxd2880_set_ber_per_period_t2(struct dvb_frontend *fe)
> +{
> +	int ret;
> +	struct dtv_frontend_properties *c;
> +	struct cxd2880_priv *priv;
> +	struct cxd2880_dvbt2_l1pre l1pre;
> +	struct cxd2880_dvbt2_l1post l1post;
> +	struct cxd2880_dvbt2_plp plp;
> +	struct cxd2880_dvbt2_bbheader bbheader;
> +	enum cxd2880_dtv_bandwidth bw = CXD2880_DTV_BW_1_7_MHZ;
> +	u32 pre_ber_rate = 0;
> +	u32 post_ber_rate = 0;
> +	u32 ucblock_rate = 0;
> +	u32 mes_exp = 0;
> +	u32 term_a = 0;
> +	u32 term_b = 0;
> +	u32 denominator = 0;
> +	static const u32 gi_tbl[7] = {32, 64, 128, 256, 8, 152, 76};
> +	static const u8 n_tbl[6] = {8, 2, 4, 16, 1, 1};
> +	static const u8 mode_tbl[6] = {2, 8, 4, 1, 16, 32};
> +	static const u32 kbch_tbl[2][8] = {
> +		{6952, 9472, 10552, 11632, 12352, 13072, 5152, 6232},
> +		{32128, 38608, 42960, 48328, 51568, 53760, 0, 0}
> +	};
> +
> +	if (!fe) {
> +		pr_err("invalid arg\n");
> +		return -EINVAL;
> +	}
> +
> +	priv = fe->demodulator_priv;
> +	c = &fe->dtv_property_cache;
> +	bw = priv->dvbt2_tune_param.bandwidth;
> +
> +	ret = cxd2880_tnrdmd_dvbt2_mon_l1_pre(&priv->tnrdmd, &l1pre);
> +	if (ret) {
> +		pr_info("l1 pre error\n");
> +		goto error_ber_setting;
> +	}
> +
> +	ret = cxd2880_tnrdmd_dvbt2_mon_active_plp(&priv->tnrdmd,
> +						  CXD2880_DVBT2_PLP_DATA, &plp);
> +	if (ret) {
> +		pr_info("plp info error\n");
> +		goto error_ber_setting;
> +	}
> +
> +	ret = cxd2880_tnrdmd_dvbt2_mon_l1_post(&priv->tnrdmd, &l1post);
> +	if (ret) {
> +		pr_info("l1 post error\n");
> +		goto error_ber_setting;
> +	}
> +
> +	term_a =
> +		(mode_tbl[l1pre.fft_mode] * (1024 + gi_tbl[l1pre.gi])) *
> +		(l1pre.num_symbols + n_tbl[l1pre.fft_mode]) + 2048;
> +
> +	if (l1pre.mixed && l1post.fef_intvl) {
> +		term_b = (l1post.fef_length + (l1post.fef_intvl / 2)) /
> +			 l1post.fef_intvl;
> +	} else {
> +		term_b = 0;
> +	}
> +
> +	switch (bw) {
> +	case CXD2880_DTV_BW_1_7_MHZ:
> +		denominator = ((term_a + term_b) * 71 + (131 / 2)) / 131;
> +		break;
> +	case CXD2880_DTV_BW_5_MHZ:
> +		denominator = ((term_a + term_b) * 7 + 20) / 40;
> +		break;
> +	case CXD2880_DTV_BW_6_MHZ:
> +		denominator = ((term_a + term_b) * 7 + 24) / 48;
> +		break;
> +	case CXD2880_DTV_BW_7_MHZ:
> +		denominator = ((term_a + term_b) + 4) / 8;
> +		break;
> +	case CXD2880_DTV_BW_8_MHZ:
> +	default:
> +		denominator = ((term_a + term_b) * 7 + 32) / 64;
> +		break;
> +	}
> +
> +	if (plp.til_type && plp.til_len) {
> +		pre_ber_rate =
> +			(plp.num_blocks_max * 1000000 + (denominator / 2)) /
> +			denominator;
> +		pre_ber_rate = (pre_ber_rate + (plp.til_len / 2)) /
> +			       plp.til_len;
> +	} else {
> +		pre_ber_rate =
> +			(plp.num_blocks_max * 1000000 + (denominator / 2)) /
> +			denominator;
> +	}
> +
> +	post_ber_rate = pre_ber_rate;
> +
> +	mes_exp = intlog2(pre_ber_rate) >> 24;
> +	priv->pre_ber_interval =
> +		((1U << mes_exp) * 1000 + (pre_ber_rate / 2)) /
> +		pre_ber_rate;
> +	cxd2880_tnrdmd_set_cfg(&priv->tnrdmd,
> +			       CXD2880_TNRDMD_CFG_DVBT2_LBER_MES,
> +			       mes_exp);
> +
> +	mes_exp = intlog2(post_ber_rate) >> 24;
> +	priv->post_ber_interval =
> +		((1U << mes_exp) * 1000 + (post_ber_rate / 2)) /
> +		post_ber_rate;
> +	cxd2880_tnrdmd_set_cfg(&priv->tnrdmd,
> +			       CXD2880_TNRDMD_CFG_DVBT2_BBER_MES,
> +			       mes_exp);
> +
> +	ret = cxd2880_tnrdmd_dvbt2_mon_bbheader(&priv->tnrdmd,
> +						CXD2880_DVBT2_PLP_DATA,
> +						&bbheader);
> +	if (ret) {
> +		pr_info("bb header error\n");
> +		goto error_ucblock_setting;
> +	}
> +
> +	if (bbheader.plp_mode == CXD2880_DVBT2_PLP_MODE_NM) {
> +		if (!bbheader.issy_indicator) {
> +			ucblock_rate =
> +				(pre_ber_rate * kbch_tbl[plp.fec][plp.plp_cr] +
> +				752) / 1504;
> +		} else {
> +			ucblock_rate =
> +				(pre_ber_rate * kbch_tbl[plp.fec][plp.plp_cr] +
> +				764) / 1528;
> +		}
> +	} else if (bbheader.plp_mode == CXD2880_DVBT2_PLP_MODE_HEM) {
> +		ucblock_rate =
> +			(pre_ber_rate * kbch_tbl[plp.fec][plp.plp_cr] + 748) /
> +			1496;
> +	} else {
> +		pr_info("plp mode is not Normal or HEM\n");
> +		goto error_ucblock_setting;
> +	}
> +
> +	mes_exp = intlog2(ucblock_rate) >> 24;
> +	priv->ucblock_interval =
> +		((1U << mes_exp) * 1000 + (ucblock_rate / 2)) /
> +		ucblock_rate;
> +	cxd2880_tnrdmd_set_cfg(&priv->tnrdmd,
> +			       CXD2880_TNRDMD_CFG_DVBT2_PER_MES,
> +			       mes_exp);
> +
> +	return 0;
> +
> +error_ber_setting:
> +	priv->pre_ber_interval = 1000;
> +	cxd2880_tnrdmd_set_cfg(&priv->tnrdmd,
> +				     CXD2880_TNRDMD_CFG_DVBT2_LBER_MES, 0);
> +
> +	priv->post_ber_interval = 1000;
> +	cxd2880_tnrdmd_set_cfg(&priv->tnrdmd,
> +			       CXD2880_TNRDMD_CFG_DVBT2_BBER_MES, 0);
> +
> +error_ucblock_setting:
> +	priv->ucblock_interval = 1000;
> +	cxd2880_tnrdmd_set_cfg(&priv->tnrdmd,
> +			       CXD2880_TNRDMD_CFG_DVBT2_PER_MES, 8);
> +
> +	return 0;
> +}
> +
> +static int cxd2880_dvbt_tune(struct cxd2880_tnrdmd *tnr_dmd,
> +			     struct cxd2880_dvbt_tune_param
> +			     *tune_param)
> +{
> +	int ret;
> +
> +	if ((!tnr_dmd) || (!tune_param))
> +		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;
> +
> +	atomic_set(&tnr_dmd->cancel, 0);
> +
> +	if ((tune_param->bandwidth != CXD2880_DTV_BW_5_MHZ) &&
> +	    (tune_param->bandwidth != CXD2880_DTV_BW_6_MHZ) &&
> +	    (tune_param->bandwidth != CXD2880_DTV_BW_7_MHZ) &&
> +	    (tune_param->bandwidth != CXD2880_DTV_BW_8_MHZ)) {
> +		return -EOPNOTSUPP;
> +	}
> +
> +	ret = cxd2880_tnrdmd_dvbt_tune1(tnr_dmd, tune_param);
> +	if (ret)
> +		return ret;
> +
> +	usleep_range(CXD2880_TNRDMD_WAIT_AGC_STABLE * 10000,
> +		     CXD2880_TNRDMD_WAIT_AGC_STABLE * 10000 + 1000);
> +
> +	ret = cxd2880_tnrdmd_dvbt_tune2(tnr_dmd, tune_param);
> +	if (ret)
> +		return ret;
> +
> +	return 0;
> +}
> +
> +static int cxd2880_dvbt2_tune(struct cxd2880_tnrdmd *tnr_dmd,
> +			      struct cxd2880_dvbt2_tune_param
> +			      *tune_param)
> +{
> +	int ret;
> +
> +	if ((!tnr_dmd) || (!tune_param))
> +		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;
> +
> +	atomic_set(&tnr_dmd->cancel, 0);
> +
> +	if ((tune_param->bandwidth != CXD2880_DTV_BW_1_7_MHZ) &&
> +	    (tune_param->bandwidth != CXD2880_DTV_BW_5_MHZ) &&
> +	    (tune_param->bandwidth != CXD2880_DTV_BW_6_MHZ) &&
> +	    (tune_param->bandwidth != CXD2880_DTV_BW_7_MHZ) &&
> +	    (tune_param->bandwidth != CXD2880_DTV_BW_8_MHZ)) {
> +		return -EOPNOTSUPP;
> +	}
> +
> +	if ((tune_param->profile != CXD2880_DVBT2_PROFILE_BASE) &&
> +	    (tune_param->profile != CXD2880_DVBT2_PROFILE_LITE))
> +		return -EINVAL;
> +
> +	ret = cxd2880_tnrdmd_dvbt2_tune1(tnr_dmd, tune_param);
> +	if (ret)
> +		return ret;
> +
> +	usleep_range(CXD2880_TNRDMD_WAIT_AGC_STABLE * 10000,
> +		     CXD2880_TNRDMD_WAIT_AGC_STABLE * 10000 + 1000);
> +
> +	ret = cxd2880_tnrdmd_dvbt2_tune2(tnr_dmd, tune_param);
> +	if (ret)
> +		return ret;
> +
> +	return ret;
> +}
> +
> +static int cxd2880_set_frontend(struct dvb_frontend *fe)
> +{
> +	int ret;
> +	struct dtv_frontend_properties *c;
> +	struct cxd2880_priv *priv;
> +	enum cxd2880_dtv_bandwidth bw = CXD2880_DTV_BW_1_7_MHZ;
> +
> +	if (!fe) {
> +		pr_err("invalid arg\n");
> +		return -EINVAL;
> +	}
> +
> +	priv = fe->demodulator_priv;
> +	c = &fe->dtv_property_cache;
> +
> +	c->pre_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
> +	c->pre_bit_error.stat[0].uvalue = 0;
> +	c->pre_bit_error.len = 1;
> +	c->pre_bit_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
> +	c->pre_bit_count.stat[0].uvalue = 0;
> +	c->pre_bit_count.len = 1;
> +	c->post_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
> +	c->post_bit_error.stat[0].uvalue = 0;
> +	c->post_bit_error.len = 1;
> +	c->post_bit_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
> +	c->post_bit_count.stat[0].uvalue = 0;
> +	c->post_bit_count.len = 1;
> +	c->block_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
> +	c->block_error.stat[0].uvalue = 0;
> +	c->block_error.len = 1;
> +	c->block_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
> +	c->block_count.stat[0].uvalue = 0;
> +	c->block_count.len = 1;
> +
> +	switch (c->bandwidth_hz) {
> +	case 1712000:
> +		bw = CXD2880_DTV_BW_1_7_MHZ;
> +		break;
> +	case 5000000:
> +		bw = CXD2880_DTV_BW_5_MHZ;
> +		break;
> +	case 6000000:
> +		bw = CXD2880_DTV_BW_6_MHZ;
> +		break;
> +	case 7000000:
> +		bw = CXD2880_DTV_BW_7_MHZ;
> +		break;
> +	case 8000000:
> +		bw = CXD2880_DTV_BW_8_MHZ;
> +		break;
> +	default:
> +		return -EINVAL;
> +	}
> +
> +	priv->s = 0;
> +
> +	pr_info("sys:%d freq:%d bw:%d\n",
> +		c->delivery_system, c->frequency, bw);
> +	mutex_lock(priv->spi_mutex);
> +	if (c->delivery_system == SYS_DVBT) {
> +		priv->tnrdmd.sys = CXD2880_DTV_SYS_DVBT;
> +		priv->dvbt_tune_param.center_freq_khz = c->frequency / 1000;
> +		priv->dvbt_tune_param.bandwidth = bw;
> +		priv->dvbt_tune_param.profile = CXD2880_DVBT_PROFILE_HP;
> +		ret = cxd2880_dvbt_tune(&priv->tnrdmd,
> +					&priv->dvbt_tune_param);
> +	} else if (c->delivery_system == SYS_DVBT2) {
> +		priv->tnrdmd.sys = CXD2880_DTV_SYS_DVBT2;
> +		priv->dvbt2_tune_param.center_freq_khz = c->frequency / 1000;
> +		priv->dvbt2_tune_param.bandwidth = bw;
> +		priv->dvbt2_tune_param.data_plp_id = (u16)c->stream_id;
> +		priv->dvbt2_tune_param.profile = CXD2880_DVBT2_PROFILE_BASE;
> +		ret = cxd2880_dvbt2_tune(&priv->tnrdmd,
> +					 &priv->dvbt2_tune_param);
> +	} else {
> +		pr_err("invalid system\n");
> +		mutex_unlock(priv->spi_mutex);
> +		return -EINVAL;
> +	}
> +	mutex_unlock(priv->spi_mutex);
> +
> +	pr_info("tune result %d\n", ret);
> +
> +	return ret;
> +}
> +
> +static int cxd2880_get_stats(struct dvb_frontend *fe,
> +			     enum fe_status status)
> +{
> +	struct cxd2880_priv *priv = NULL;
> +	struct dtv_frontend_properties *c = NULL;
> +	u32 pre_bit_err = 0, pre_bit_count = 0;
> +	u32 post_bit_err = 0, post_bit_count = 0;
> +	u32 block_err = 0, block_count = 0;
> +	int ret;
> +
> +	if (!fe) {
> +		pr_err("invalid arg\n");
> +		return -EINVAL;
> +	}
> +
> +	priv = fe->demodulator_priv;
> +	c = &fe->dtv_property_cache;
> +
> +	if (!(status & FE_HAS_LOCK)) {
> +		c->pre_bit_error.len = 1;
> +		c->pre_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
> +		c->pre_bit_count.len = 1;
> +		c->pre_bit_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
> +		c->post_bit_error.len = 1;
> +		c->post_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
> +		c->post_bit_count.len = 1;
> +		c->post_bit_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
> +		c->block_error.len = 1;
> +		c->block_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
> +		c->block_count.len = 1;
> +		c->block_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
> +
> +		return 0;
> +	}
> +
> +	if (time_after(jiffies, priv->pre_ber_update)) {
> +		priv->pre_ber_update =
> +			 jiffies + msecs_to_jiffies(priv->pre_ber_interval);
> +		if (c->delivery_system == SYS_DVBT) {
> +			mutex_lock(priv->spi_mutex);
> +			ret = cxd2880_pre_bit_err_t(&priv->tnrdmd,
> +						    &pre_bit_err,
> +						    &pre_bit_count);
> +			mutex_unlock(priv->spi_mutex);
> +		} else if (c->delivery_system == SYS_DVBT2) {
> +			mutex_lock(priv->spi_mutex);
> +			ret = cxd2880_pre_bit_err_t2(&priv->tnrdmd,
> +						     &pre_bit_err,
> +						     &pre_bit_count);
> +			mutex_unlock(priv->spi_mutex);
> +		} else {
> +			return -EINVAL;
> +		}
> +
> +		if (!ret) {
> +			c->pre_bit_error.len = 1;
> +			c->pre_bit_error.stat[0].scale = FE_SCALE_COUNTER;
> +			c->pre_bit_error.stat[0].uvalue += pre_bit_err;
> +			c->pre_bit_count.len = 1;
> +			c->pre_bit_count.stat[0].scale = FE_SCALE_COUNTER;
> +			c->pre_bit_count.stat[0].uvalue += pre_bit_count;
> +		} else {
> +			c->pre_bit_error.len = 1;
> +			c->pre_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
> +			c->pre_bit_count.len = 1;
> +			c->pre_bit_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
> +			pr_debug("pre_bit_error_t failed %d\n", ret);
> +		}
> +	}
> +
> +	if (time_after(jiffies, priv->post_ber_update)) {
> +		priv->post_ber_update =
> +			jiffies + msecs_to_jiffies(priv->post_ber_interval);
> +		if (c->delivery_system == SYS_DVBT) {
> +			mutex_lock(priv->spi_mutex);
> +			ret = cxd2880_post_bit_err_t(&priv->tnrdmd,
> +						     &post_bit_err,
> +						     &post_bit_count);
> +			mutex_unlock(priv->spi_mutex);
> +		} else if (c->delivery_system == SYS_DVBT2) {
> +			mutex_lock(priv->spi_mutex);
> +			ret = cxd2880_post_bit_err_t2(&priv->tnrdmd,
> +						      &post_bit_err,
> +						      &post_bit_count);
> +			mutex_unlock(priv->spi_mutex);
> +		} else {
> +			return -EINVAL;
> +		}
> +
> +		if (!ret) {
> +			c->post_bit_error.len = 1;
> +			c->post_bit_error.stat[0].scale = FE_SCALE_COUNTER;
> +			c->post_bit_error.stat[0].uvalue += post_bit_err;
> +			c->post_bit_count.len = 1;
> +			c->post_bit_count.stat[0].scale = FE_SCALE_COUNTER;
> +			c->post_bit_count.stat[0].uvalue += post_bit_count;
> +		} else {
> +			c->post_bit_error.len = 1;
> +			c->post_bit_error.stat[0].scale =
> +							FE_SCALE_NOT_AVAILABLE;
> +			c->post_bit_count.len = 1;
> +			c->post_bit_count.stat[0].scale =
> +							FE_SCALE_NOT_AVAILABLE;
> +			pr_debug("post_bit_err_t %d\n", ret);
> +		}
> +	}
> +
> +	if (time_after(jiffies, priv->ucblock_update)) {
> +		priv->ucblock_update =
> +			jiffies + msecs_to_jiffies(priv->ucblock_interval);
> +		if (c->delivery_system == SYS_DVBT) {
> +			mutex_lock(priv->spi_mutex);
> +			ret = cxd2880_read_block_err_t(&priv->tnrdmd,
> +						       &block_err,
> +						       &block_count);
> +			mutex_unlock(priv->spi_mutex);
> +		} else if (c->delivery_system == SYS_DVBT2) {
> +			mutex_lock(priv->spi_mutex);
> +			ret = cxd2880_read_block_err_t2(&priv->tnrdmd,
> +							&block_err,
> +							&block_count);
> +			mutex_unlock(priv->spi_mutex);
> +		} else {
> +			return -EINVAL;
> +		}
> +		if (!ret) {
> +			c->block_error.len = 1;
> +			c->block_error.stat[0].scale = FE_SCALE_COUNTER;
> +			c->block_error.stat[0].uvalue += block_err;
> +			c->block_count.len = 1;
> +			c->block_count.stat[0].scale = FE_SCALE_COUNTER;
> +			c->block_count.stat[0].uvalue += block_count;
> +		} else {
> +			c->block_error.len = 1;
> +			c->block_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
> +			c->block_count.len = 1;
> +			c->block_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
> +			pr_debug("read_block_err_t  %d\n", ret);
> +		}
> +	}
> +
> +	return 0;
> +}
> +
> +static int cxd2880_check_l1post_plp(struct dvb_frontend *fe)
> +{
> +	u8 valid = 0;
> +	u8 plp_not_found;
> +	int ret;
> +	struct cxd2880_priv *priv = NULL;
> +
> +	if (!fe) {
> +		pr_err("invalid arg\n");
> +		return -EINVAL;
> +	}
> +
> +	priv = fe->demodulator_priv;
> +
> +	ret = cxd2880_tnrdmd_dvbt2_check_l1post_valid(&priv->tnrdmd,
> +						      &valid);
> +	if (ret)
> +		return ret;
> +
> +	if (!valid)
> +		return -EAGAIN;
> +
> +	ret = cxd2880_tnrdmd_dvbt2_mon_data_plp_error(&priv->tnrdmd,
> +						      &plp_not_found);
> +	if (ret)
> +		return ret;
> +
> +	if (plp_not_found) {
> +		priv->dvbt2_tune_param.tune_info =
> +			CXD2880_TNRDMD_DVBT2_TUNE_INFO_INVALID_PLP_ID;
> +	} else {
> +		priv->dvbt2_tune_param.tune_info =
> +			CXD2880_TNRDMD_DVBT2_TUNE_INFO_OK;
> +	}
> +
> +	return 0;
> +}
> +
> +static int cxd2880_read_status(struct dvb_frontend *fe,
> +			       enum fe_status *status)
> +{
> +	int ret;
> +	u8 sync = 0;
> +	u8 lock = 0;
> +	u8 unlock = 0;
> +	struct cxd2880_priv *priv = NULL;
> +	struct dtv_frontend_properties *c = NULL;
> +
> +	if ((!fe) || (!status)) {
> +		pr_err("invalid arg\n");
> +		return -EINVAL;
> +	}
> +
> +	priv = fe->demodulator_priv;
> +	c = &fe->dtv_property_cache;
> +	*status = 0;
> +
> +	if (priv->tnrdmd.state == CXD2880_TNRDMD_STATE_ACTIVE) {
> +		mutex_lock(priv->spi_mutex);
> +		if (c->delivery_system == SYS_DVBT) {
> +			ret = cxd2880_tnrdmd_dvbt_mon_sync_stat(
> +							&priv->tnrdmd,
> +							&sync,
> +							&lock,
> +							&unlock);
> +		} else if (c->delivery_system == SYS_DVBT2) {
> +			ret = cxd2880_tnrdmd_dvbt2_mon_sync_stat(
> +							&priv->tnrdmd,
> +							&sync,
> +							&lock,
> +							&unlock);
> +		} else {
> +			pr_err("invalid system");
> +			mutex_unlock(priv->spi_mutex);
> +			return -EINVAL;
> +		}
> +
> +		mutex_unlock(priv->spi_mutex);
> +		if (ret) {
> +			pr_err("failed. sys = %d\n", priv->tnrdmd.sys);
> +			return  ret;
> +		}
> +
> +		if (sync == 6) {
> +			*status = FE_HAS_SIGNAL |
> +				  FE_HAS_CARRIER;
> +		}
> +		if (lock)
> +			*status |= FE_HAS_VITERBI |
> +				   FE_HAS_SYNC |
> +				   FE_HAS_LOCK;
> +	}
> +
> +	pr_debug("status %d\n", *status);
> +
> +	if (priv->s == 0 && (*status & FE_HAS_LOCK)) {
> +		mutex_lock(priv->spi_mutex);
> +		if (c->delivery_system == SYS_DVBT) {
> +			ret = cxd2880_set_ber_per_period_t(fe);
> +			priv->s = *status;
> +		} else if (c->delivery_system == SYS_DVBT2) {
> +			ret = cxd2880_check_l1post_plp(fe);
> +			if (!ret) {
> +				ret = cxd2880_set_ber_per_period_t2(fe);
> +				priv->s = *status;
> +			}
> +		} else {
> +			pr_err("invalid system\n");
> +			mutex_unlock(priv->spi_mutex);
> +			return -EINVAL;
> +		}
> +		mutex_unlock(priv->spi_mutex);
> +	}
> +
> +	cxd2880_get_stats(fe, *status);
> +	return  0;
> +}
> +
> +static int cxd2880_tune(struct dvb_frontend *fe,
> +			bool retune,
> +			unsigned int mode_flags,
> +			unsigned int *delay,
> +			enum fe_status *status)
> +{
> +	int ret;
> +
> +	if ((!fe) || (!delay) || (!status)) {
> +		pr_err("invalid arg.");
> +		return -EINVAL;
> +	}
> +
> +	if (retune) {
> +		ret = cxd2880_set_frontend(fe);
> +		if (ret) {
> +			pr_err("cxd2880_set_frontend failed %d\n", ret);
> +			return ret;
> +		}
> +	}
> +
> +	*delay = HZ / 5;
> +
> +	return cxd2880_read_status(fe, status);
> +}
> +
> +static int cxd2880_get_frontend_t(struct dvb_frontend *fe,
> +				  struct dtv_frontend_properties *c)
> +{
> +	int ret;
> +	struct cxd2880_priv *priv = NULL;
> +	enum cxd2880_dvbt_mode mode = CXD2880_DVBT_MODE_2K;
> +	enum cxd2880_dvbt_guard guard = CXD2880_DVBT_GUARD_1_32;
> +	struct cxd2880_dvbt_tpsinfo tps;
> +	enum cxd2880_tnrdmd_spectrum_sense sense;
> +	u16 snr = 0;
> +	int strength = 0;
> +
> +	if ((!fe) || (!c)) {
> +		pr_err("invalid arg\n");
> +		return -EINVAL;
> +	}
> +
> +	priv = fe->demodulator_priv;
> +
> +	mutex_lock(priv->spi_mutex);
> +	ret = cxd2880_tnrdmd_dvbt_mon_mode_guard(&priv->tnrdmd,
> +						 &mode, &guard);
> +	mutex_unlock(priv->spi_mutex);
> +	if (!ret) {
> +		switch (mode) {
> +		case CXD2880_DVBT_MODE_2K:
> +			c->transmission_mode = TRANSMISSION_MODE_2K;
> +			break;
> +		case CXD2880_DVBT_MODE_8K:
> +			c->transmission_mode = TRANSMISSION_MODE_8K;
> +			break;
> +		default:
> +			c->transmission_mode = TRANSMISSION_MODE_2K;
> +			pr_debug("transmission mode is invalid %d\n", mode);
> +			break;
> +		}
> +		switch (guard) {
> +		case CXD2880_DVBT_GUARD_1_32:
> +			c->guard_interval = GUARD_INTERVAL_1_32;
> +			break;
> +		case CXD2880_DVBT_GUARD_1_16:
> +			c->guard_interval = GUARD_INTERVAL_1_16;
> +			break;
> +		case CXD2880_DVBT_GUARD_1_8:
> +			c->guard_interval = GUARD_INTERVAL_1_8;
> +			break;
> +		case CXD2880_DVBT_GUARD_1_4:
> +			c->guard_interval = GUARD_INTERVAL_1_4;
> +			break;
> +		default:
> +			c->guard_interval = GUARD_INTERVAL_1_32;
> +			pr_debug("guard interval is invalid %d\n",
> +				 guard);
> +			break;
> +		}
> +	} else {
> +		c->transmission_mode = TRANSMISSION_MODE_2K;
> +		c->guard_interval = GUARD_INTERVAL_1_32;
> +		pr_debug("ModeGuard err %d\n", ret);
> +	}
> +
> +	mutex_lock(priv->spi_mutex);
> +	ret = cxd2880_tnrdmd_dvbt_mon_tps_info(&priv->tnrdmd, &tps);
> +	mutex_unlock(priv->spi_mutex);
> +	if (!ret) {
> +		switch (tps.hierarchy) {
> +		case CXD2880_DVBT_HIERARCHY_NON:
> +			c->hierarchy = HIERARCHY_NONE;
> +			break;
> +		case CXD2880_DVBT_HIERARCHY_1:
> +			c->hierarchy = HIERARCHY_1;
> +			break;
> +		case CXD2880_DVBT_HIERARCHY_2:
> +			c->hierarchy = HIERARCHY_2;
> +			break;
> +		case CXD2880_DVBT_HIERARCHY_4:
> +			c->hierarchy = HIERARCHY_4;
> +			break;
> +		default:
> +			c->hierarchy = HIERARCHY_NONE;
> +			pr_debug("TPSInfo hierarchy is invalid %d\n",
> +				 tps.hierarchy);
> +			break;
> +		}
> +
> +		switch (tps.rate_hp) {
> +		case CXD2880_DVBT_CODERATE_1_2:
> +			c->code_rate_HP = FEC_1_2;
> +			break;
> +		case CXD2880_DVBT_CODERATE_2_3:
> +			c->code_rate_HP = FEC_2_3;
> +			break;
> +		case CXD2880_DVBT_CODERATE_3_4:
> +			c->code_rate_HP = FEC_3_4;
> +			break;
> +		case CXD2880_DVBT_CODERATE_5_6:
> +			c->code_rate_HP = FEC_5_6;
> +			break;
> +		case CXD2880_DVBT_CODERATE_7_8:
> +			c->code_rate_HP = FEC_7_8;
> +			break;
> +		default:
> +			c->code_rate_HP = FEC_NONE;
> +			pr_debug("TPSInfo rateHP is invalid %d\n",
> +				 tps.rate_hp);
> +			break;
> +		}
> +		switch (tps.rate_lp) {
> +		case CXD2880_DVBT_CODERATE_1_2:
> +			c->code_rate_LP = FEC_1_2;
> +			break;
> +		case CXD2880_DVBT_CODERATE_2_3:
> +			c->code_rate_LP = FEC_2_3;
> +			break;
> +		case CXD2880_DVBT_CODERATE_3_4:
> +			c->code_rate_LP = FEC_3_4;
> +			break;
> +		case CXD2880_DVBT_CODERATE_5_6:
> +			c->code_rate_LP = FEC_5_6;
> +			break;
> +		case CXD2880_DVBT_CODERATE_7_8:
> +			c->code_rate_LP = FEC_7_8;
> +			break;
> +		default:
> +			c->code_rate_LP = FEC_NONE;
> +			pr_debug("TPSInfo rateLP is invalid %d\n",
> +				 tps.rate_lp);
> +			break;
> +		}
> +		switch (tps.constellation) {
> +		case CXD2880_DVBT_CONSTELLATION_QPSK:
> +			c->modulation = QPSK;
> +			break;
> +		case CXD2880_DVBT_CONSTELLATION_16QAM:
> +			c->modulation = QAM_16;
> +			break;
> +		case CXD2880_DVBT_CONSTELLATION_64QAM:
> +			c->modulation = QAM_64;
> +			break;
> +		default:
> +			c->modulation = QPSK;
> +			pr_debug("TPSInfo constellation is invalid %d\n",
> +				 tps.constellation);
> +			break;
> +		}
> +	} else {
> +		c->hierarchy = HIERARCHY_NONE;
> +		c->code_rate_HP = FEC_NONE;
> +		c->code_rate_LP = FEC_NONE;
> +		c->modulation = QPSK;
> +		pr_debug("TPS info err %d\n", ret);
> +	}
> +
> +	mutex_lock(priv->spi_mutex);
> +	ret = cxd2880_tnrdmd_dvbt_mon_spectrum_sense(&priv->tnrdmd, &sense);
> +	mutex_unlock(priv->spi_mutex);
> +	if (!ret) {
> +		switch (sense) {
> +		case CXD2880_TNRDMD_SPECTRUM_NORMAL:
> +			c->inversion = INVERSION_OFF;
> +			break;
> +		case CXD2880_TNRDMD_SPECTRUM_INV:
> +			c->inversion = INVERSION_ON;
> +			break;
> +		default:
> +			c->inversion = INVERSION_OFF;
> +			pr_debug("spectrum sense is invalid %d\n", sense);
> +			break;
> +		}
> +	} else {
> +		c->inversion = INVERSION_OFF;
> +		pr_debug("spectrum_sense %d\n", ret);
> +	}
> +
> +	mutex_lock(priv->spi_mutex);
> +	ret = cxd2880_tnrdmd_mon_rf_lvl(&priv->tnrdmd, &strength);
> +	mutex_unlock(priv->spi_mutex);
> +	if (!ret) {
> +		c->strength.len = 1;
> +		c->strength.stat[0].scale = FE_SCALE_DECIBEL;
> +		c->strength.stat[0].svalue = strength;
> +	} else {
> +		c->strength.len = 1;
> +		c->strength.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
> +		pr_debug("mon_rf_lvl %d\n", ret);
> +	}
> +
> +	ret = cxd2880_read_snr(fe, &snr);
> +	if (!ret) {
> +		c->cnr.len = 1;
> +		c->cnr.stat[0].scale = FE_SCALE_DECIBEL;
> +		c->cnr.stat[0].svalue = snr;
> +	} else {
> +		c->cnr.len = 1;
> +		c->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
> +		pr_debug("read_snr %d\n", ret);
> +	}
> +
> +	return 0;
> +}
> +
> +static int cxd2880_get_frontend_t2(struct dvb_frontend *fe,
> +				   struct dtv_frontend_properties *c)
> +{
> +	int ret;
> +	struct cxd2880_priv *priv = NULL;
> +	struct cxd2880_dvbt2_l1pre l1pre;
> +	enum cxd2880_dvbt2_plp_code_rate coderate;
> +	enum cxd2880_dvbt2_plp_constell qam;
> +	enum cxd2880_tnrdmd_spectrum_sense sense;
> +	u16 snr = 0;
> +	int strength = 0;
> +
> +	if ((!fe) || (!c)) {
> +		pr_err("invalid arg.\n");
> +		return -EINVAL;
> +	}
> +
> +	priv = fe->demodulator_priv;
> +
> +	mutex_lock(priv->spi_mutex);
> +	ret = cxd2880_tnrdmd_dvbt2_mon_l1_pre(&priv->tnrdmd, &l1pre);
> +	mutex_unlock(priv->spi_mutex);
> +	if (!ret) {
> +		switch (l1pre.fft_mode) {
> +		case CXD2880_DVBT2_M2K:
> +			c->transmission_mode = TRANSMISSION_MODE_2K;
> +			break;
> +		case CXD2880_DVBT2_M8K:
> +			c->transmission_mode = TRANSMISSION_MODE_8K;
> +			break;
> +		case CXD2880_DVBT2_M4K:
> +			c->transmission_mode = TRANSMISSION_MODE_4K;
> +			break;
> +		case CXD2880_DVBT2_M1K:
> +			c->transmission_mode = TRANSMISSION_MODE_1K;
> +			break;
> +		case CXD2880_DVBT2_M16K:
> +			c->transmission_mode = TRANSMISSION_MODE_16K;
> +			break;
> +		case CXD2880_DVBT2_M32K:
> +			c->transmission_mode = TRANSMISSION_MODE_32K;
> +			break;
> +		default:
> +			c->transmission_mode = TRANSMISSION_MODE_2K;
> +			pr_debug("L1Pre fft_mode is invalid %d\n",
> +				 l1pre.fft_mode);
> +			break;
> +		}
> +		switch (l1pre.gi) {
> +		case CXD2880_DVBT2_G1_32:
> +			c->guard_interval = GUARD_INTERVAL_1_32;
> +			break;
> +		case CXD2880_DVBT2_G1_16:
> +			c->guard_interval = GUARD_INTERVAL_1_16;
> +			break;
> +		case CXD2880_DVBT2_G1_8:
> +			c->guard_interval = GUARD_INTERVAL_1_8;
> +			break;
> +		case CXD2880_DVBT2_G1_4:
> +			c->guard_interval = GUARD_INTERVAL_1_4;
> +			break;
> +		case CXD2880_DVBT2_G1_128:
> +			c->guard_interval = GUARD_INTERVAL_1_128;
> +			break;
> +		case CXD2880_DVBT2_G19_128:
> +			c->guard_interval = GUARD_INTERVAL_19_128;
> +			break;
> +		case CXD2880_DVBT2_G19_256:
> +			c->guard_interval = GUARD_INTERVAL_19_256;
> +			break;
> +		default:
> +			c->guard_interval = GUARD_INTERVAL_1_32;
> +			pr_debug("L1Pre guard interval is invalid %d\n",
> +				 l1pre.gi);
> +			break;
> +		}
> +	} else {
> +		c->transmission_mode = TRANSMISSION_MODE_2K;
> +		c->guard_interval = GUARD_INTERVAL_1_32;
> +		pr_debug("L1Pre err %d\n", ret);
> +	}
> +
> +	mutex_lock(priv->spi_mutex);
> +	ret = cxd2880_tnrdmd_dvbt2_mon_code_rate(&priv->tnrdmd,
> +						 CXD2880_DVBT2_PLP_DATA,
> +						 &coderate);
> +	mutex_unlock(priv->spi_mutex);
> +	if (!ret) {
> +		switch (coderate) {
> +		case CXD2880_DVBT2_R1_2:
> +			c->fec_inner = FEC_1_2;
> +			break;
> +		case CXD2880_DVBT2_R3_5:
> +			c->fec_inner = FEC_3_5;
> +			break;
> +		case CXD2880_DVBT2_R2_3:
> +			c->fec_inner = FEC_2_3;
> +			break;
> +		case CXD2880_DVBT2_R3_4:
> +			c->fec_inner = FEC_3_4;
> +			break;
> +		case CXD2880_DVBT2_R4_5:
> +			c->fec_inner = FEC_4_5;
> +			break;
> +		case CXD2880_DVBT2_R5_6:
> +			c->fec_inner = FEC_5_6;
> +			break;
> +		default:
> +			c->fec_inner = FEC_NONE;
> +			pr_debug("CodeRate is invalid %d\n", coderate);
> +			break;
> +		}
> +	} else {
> +		c->fec_inner = FEC_NONE;
> +		pr_debug("CodeRate %d\n", ret);
> +	}
> +
> +	mutex_lock(priv->spi_mutex);
> +	ret = cxd2880_tnrdmd_dvbt2_mon_qam(&priv->tnrdmd,
> +					   CXD2880_DVBT2_PLP_DATA,
> +					   &qam);
> +	mutex_unlock(priv->spi_mutex);
> +	if (!ret) {
> +		switch (qam) {
> +		case CXD2880_DVBT2_QPSK:
> +			c->modulation = QPSK;
> +			break;
> +		case CXD2880_DVBT2_QAM16:
> +			c->modulation = QAM_16;
> +			break;
> +		case CXD2880_DVBT2_QAM64:
> +			c->modulation = QAM_64;
> +			break;
> +		case CXD2880_DVBT2_QAM256:
> +			c->modulation = QAM_256;
> +			break;
> +		default:
> +			c->modulation = QPSK;
> +			pr_debug("QAM is invalid %d\n", qam);
> +			break;
> +		}
> +	} else {
> +		c->modulation = QPSK;
> +		pr_debug("QAM %d\n", ret);
> +	}
> +
> +	mutex_lock(priv->spi_mutex);
> +	ret = cxd2880_tnrdmd_dvbt2_mon_spectrum_sense(&priv->tnrdmd, &sense);
> +	mutex_unlock(priv->spi_mutex);
> +	if (!ret) {
> +		switch (sense) {
> +		case CXD2880_TNRDMD_SPECTRUM_NORMAL:
> +			c->inversion = INVERSION_OFF;
> +			break;
> +		case CXD2880_TNRDMD_SPECTRUM_INV:
> +			c->inversion = INVERSION_ON;
> +			break;
> +		default:
> +			c->inversion = INVERSION_OFF;
> +			pr_debug("spectrum sense is invalid %d\n", sense);
> +			break;
> +		}
> +	} else {
> +		c->inversion = INVERSION_OFF;
> +		pr_debug("SpectrumSense %d\n", ret);
> +	}
> +
> +	mutex_lock(priv->spi_mutex);
> +	ret = cxd2880_tnrdmd_mon_rf_lvl(&priv->tnrdmd, &strength);
> +	mutex_unlock(priv->spi_mutex);
> +	if (!ret) {
> +		c->strength.len = 1;
> +		c->strength.stat[0].scale = FE_SCALE_DECIBEL;
> +		c->strength.stat[0].svalue = strength;
> +	} else {
> +		c->strength.len = 1;
> +		c->strength.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
> +		pr_debug("mon_rf_lvl %d\n", ret);
> +	}
> +
> +	ret = cxd2880_read_snr(fe, &snr);
> +	if (!ret) {
> +		c->cnr.len = 1;
> +		c->cnr.stat[0].scale = FE_SCALE_DECIBEL;
> +		c->cnr.stat[0].svalue = snr;
> +	} else {
> +		c->cnr.len = 1;
> +		c->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
> +		pr_debug("read_snr %d\n", ret);
> +	}
> +
> +	return 0;
> +}
> +
> +static int cxd2880_get_frontend(struct dvb_frontend *fe,
> +				struct dtv_frontend_properties *props)
> +{
> +	struct cxd2880_priv *priv = NULL;
> +	int ret;
> +
> +	if ((!fe) || (!props)) {
> +		pr_err("invalid arg.");
> +		return -EINVAL;
> +	}
> +
> +	priv = fe->demodulator_priv;
> +
> +	pr_debug("system=%d\n", fe->dtv_property_cache.delivery_system);
> +	switch (fe->dtv_property_cache.delivery_system) {
> +	case SYS_DVBT:
> +		ret = cxd2880_get_frontend_t(fe, props);
> +		break;
> +	case SYS_DVBT2:
> +		ret = cxd2880_get_frontend_t2(fe, props);
> +		break;
> +	default:
> +		ret = -EINVAL;
> +		break;
> +	}
> +
> +	return ret;
> +}
> +
> +static enum dvbfe_algo cxd2880_get_frontend_algo(struct dvb_frontend *fe)
> +{
> +	return DVBFE_ALGO_HW;
> +}
> +
> +static struct dvb_frontend_ops cxd2880_dvbt_t2_ops;
> +
> +struct dvb_frontend *cxd2880_attach(struct dvb_frontend *fe,
> +				    struct cxd2880_config *cfg)
> +{
> +	int ret;
> +	enum cxd2880_tnrdmd_chip_id chipid =
> +					CXD2880_TNRDMD_CHIP_ID_UNKNOWN;
> +	static struct cxd2880_priv *priv;
> +	u8 data = 0;
> +
> +	if (!fe) {
> +		pr_err("invalid arg.\n");
> +		return NULL;
> +	}
> +
> +	priv = kzalloc(sizeof(struct cxd2880_priv), GFP_KERNEL);
> +	if (!priv)
> +		return NULL;
> +
> +	priv->spi = cfg->spi;
> +	priv->spi_mutex = cfg->spi_mutex;
> +	priv->spi_device.spi = cfg->spi;
> +
> +	memcpy(&fe->ops, &cxd2880_dvbt_t2_ops,
> +	       sizeof(struct dvb_frontend_ops));
> +
> +	ret = cxd2880_spi_device_initialize(&priv->spi_device,
> +					    CXD2880_SPI_MODE_0,
> +					    55000000);
> +	if (ret) {
> +		pr_err("spi_device_initialize failed. %d\n", ret);
> +		kfree(priv);
> +		return NULL;
> +	}
> +
> +	ret = cxd2880_spi_device_create_spi(&priv->cxd2880_spi,
> +					    &priv->spi_device);
> +	if (ret) {
> +		pr_err("spi_device_create_spi failed. %d\n", ret);
> +		kfree(priv);
> +		return NULL;
> +	}
> +
> +	ret = cxd2880_io_spi_create(&priv->regio, &priv->cxd2880_spi, 0);
> +	if (ret) {
> +		pr_err("io_spi_create failed. %d\n", ret);
> +		kfree(priv);
> +		return NULL;
> +	}
> +	ret = priv->regio.write_reg(&priv->regio,
> +				    CXD2880_IO_TGT_SYS, 0x00, 0x00);
> +	if (ret) {
> +		pr_err("set bank to 0x00 failed.\n");
> +		kfree(priv);
> +		return NULL;
> +	}
> +	ret = priv->regio.read_regs(&priv->regio,
> +				    CXD2880_IO_TGT_SYS, 0xfd, &data, 1);
> +	if (ret) {
> +		pr_err("read chip id failed.\n");
> +		kfree(priv);
> +		return NULL;
> +	}
> +
> +	chipid = (enum cxd2880_tnrdmd_chip_id)data;
> +	if ((chipid != CXD2880_TNRDMD_CHIP_ID_CXD2880_ES1_0X) &&
> +	    (chipid != CXD2880_TNRDMD_CHIP_ID_CXD2880_ES1_11)) {
> +		pr_err("chip id invalid.\n");
> +		kfree(priv);
> +		return NULL;
> +	}
> +
> +	fe->demodulator_priv = priv;
> +	pr_info("CXD2880 driver version: Ver %s\n",
> +		CXD2880_TNRDMD_DRIVER_VERSION);
> +
> +	return fe;
> +}
> +EXPORT_SYMBOL(cxd2880_attach);
> +
> +static struct dvb_frontend_ops cxd2880_dvbt_t2_ops = {
> +	.info = {
> +		.name = "Sony CXD2880",
> +		.frequency_min =  174000000,
> +		.frequency_max = 862000000,
> +		.frequency_stepsize = 1000,
> +		.caps = FE_CAN_INVERSION_AUTO |
> +				FE_CAN_FEC_1_2 |
> +				FE_CAN_FEC_2_3 |
> +				FE_CAN_FEC_3_4 |
> +				FE_CAN_FEC_4_5 |
> +				FE_CAN_FEC_5_6	|
> +				FE_CAN_FEC_7_8	|
> +				FE_CAN_FEC_AUTO |
> +				FE_CAN_QPSK |
> +				FE_CAN_QAM_16 |
> +				FE_CAN_QAM_32 |
> +				FE_CAN_QAM_64 |
> +				FE_CAN_QAM_128 |
> +				FE_CAN_QAM_256 |
> +				FE_CAN_QAM_AUTO |
> +				FE_CAN_TRANSMISSION_MODE_AUTO |
> +				FE_CAN_GUARD_INTERVAL_AUTO |
> +				FE_CAN_2G_MODULATION |
> +				FE_CAN_RECOVER |
> +				FE_CAN_MUTE_TS,
> +	},
> +	.delsys = { SYS_DVBT, SYS_DVBT2 },
> +
> +	.release = cxd2880_release,
> +	.init = cxd2880_init,
> +	.sleep = cxd2880_sleep,
> +	.tune = cxd2880_tune,
> +	.set_frontend = cxd2880_set_frontend,
> +	.get_frontend = cxd2880_get_frontend,
> +	.read_status = cxd2880_read_status,
> +	.read_ber = cxd2880_read_ber,
> +	.read_signal_strength = cxd2880_read_signal_strength,
> +	.read_snr = cxd2880_read_snr,
> +	.read_ucblocks = cxd2880_read_ucblocks,
> +	.get_frontend_algo = cxd2880_get_frontend_algo,
> +};
> +
> +MODULE_DESCRIPTION(
> +"Sony CXD2880 DVB-T2/T tuner + demodulator 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

* Re: [PATCH v4 06/12] [media] cxd2880: Add integration layer for the driver
From: Mauro Carvalho Chehab @ 2017-12-13 19:13 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: <20171013060834.21526-1-Yasunari.Takiguchi@sony.com>

Em Fri, 13 Oct 2017 15:08:34 +0900
<Yasunari.Takiguchi@sony.com> escreveu:

> From: Yasunari Takiguchi <Yasunari.Takiguchi@sony.com>
> 
> These functions monitor the driver and watch for task completion.
> This is part of the Sony CXD2880 DVB-T2/T tuner + demodulator driver.

If I understand well, the goal here is to have thread that would
be waking up from time to time, right? Just use the infrastructure
that the Kernel has for it, like a kthread, or timer_setup() & friends.

Take a look at include/linux/timer.h, and just use what's already
defined.


> 
> 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_integ.c
>       -removed unnecessary initialization at variable declaration
> 
> Changes in V3
>    drivers/media/dvb-frontends/cxd2880/cxd2880_integ.c
>       -changed cxd2880_atomic_read to atomic_read
>       -changed cxd2880_atomic_set to atomic_set
>       -modified return code
>       -modified coding style of if() 
>    drivers/media/dvb-frontends/cxd2880/cxd2880_integ.h
>       -modified return code
> 
>  .../media/dvb-frontends/cxd2880/cxd2880_integ.c    | 98 ++++++++++++++++++++++
>  .../media/dvb-frontends/cxd2880/cxd2880_integ.h    | 44 ++++++++++
>  2 files changed, 142 insertions(+)
>  create mode 100644 drivers/media/dvb-frontends/cxd2880/cxd2880_integ.c
>  create mode 100644 drivers/media/dvb-frontends/cxd2880/cxd2880_integ.h
> 
> diff --git a/drivers/media/dvb-frontends/cxd2880/cxd2880_integ.c b/drivers/media/dvb-frontends/cxd2880/cxd2880_integ.c
> new file mode 100644
> index 000000000000..7264fc355d6b
> --- /dev/null
> +++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_integ.c
> @@ -0,0 +1,98 @@
> +/*
> + * cxd2880_integ.c
> + * Sony CXD2880 DVB-T2/T tuner + demodulator driver
> + * integration layer 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_tnrdmd.h"
> +#include "cxd2880_tnrdmd_mon.h"
> +#include "cxd2880_integ.h"
> +
> +int cxd2880_integ_init(struct cxd2880_tnrdmd *tnr_dmd)
> +{
> +	int ret;
> +	struct cxd2880_stopwatch timer;
> +	unsigned int elapsed_time = 0;
> +	u8 cpu_task_completed = 0;
> +
> +	if (!tnr_dmd)
> +		return -EINVAL;
> +
> +	ret = cxd2880_tnrdmd_init1(tnr_dmd);
> +	if (ret)
> +		return ret;
> +
> +	ret = cxd2880_stopwatch_start(&timer);
> +	if (ret)
> +		return ret;
> +
> +	while (1) {
> +		ret = cxd2880_stopwatch_elapsed(&timer, &elapsed_time);
> +		if (ret)
> +			return ret;
> +
> +		ret =
> +		    cxd2880_tnrdmd_check_internal_cpu_status(tnr_dmd,
> +						     &cpu_task_completed);
> +		if (ret)
> +			return ret;
> +
> +		if (cpu_task_completed)
> +			break;
> +
> +		if (elapsed_time > CXD2880_TNRDMD_WAIT_INIT_TIMEOUT)
> +			return -ETIME;
> +		ret =
> +		    cxd2880_stopwatch_sleep(&timer,
> +					    CXD2880_TNRDMD_WAIT_INIT_INTVL);
> +		if (ret)
> +			return ret;
> +	}
> +
> +	ret = cxd2880_tnrdmd_init2(tnr_dmd);
> +	if (ret)
> +		return ret;
> +
> +	return 0;
> +}
> +
> +int cxd2880_integ_cancel(struct cxd2880_tnrdmd *tnr_dmd)
> +{
> +	if (!tnr_dmd)
> +		return -EINVAL;
> +
> +	atomic_set(&tnr_dmd->cancel, 1);
> +
> +	return 0;
> +}
> +
> +int cxd2880_integ_check_cancellation(struct cxd2880_tnrdmd *tnr_dmd)
> +{
> +	if (!tnr_dmd)
> +		return -EINVAL;
> +
> +	if (atomic_read(&tnr_dmd->cancel) != 0)
> +		return -ECANCELED;
> +
> +	return 0;
> +}
> diff --git a/drivers/media/dvb-frontends/cxd2880/cxd2880_integ.h b/drivers/media/dvb-frontends/cxd2880/cxd2880_integ.h
> new file mode 100644
> index 000000000000..2b4fe5c3743b
> --- /dev/null
> +++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_integ.h
> @@ -0,0 +1,44 @@
> +/*
> + * cxd2880_integ.h
> + * Sony CXD2880 DVB-T2/T tuner + demodulator driver
> + * integration layer common 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_INTEG_H
> +#define CXD2880_INTEG_H
> +
> +#include "cxd2880_tnrdmd.h"
> +
> +#define CXD2880_TNRDMD_WAIT_INIT_TIMEOUT	500
> +#define CXD2880_TNRDMD_WAIT_INIT_INTVL	10
> +
> +#define CXD2880_TNRDMD_WAIT_AGC_STABLE		100
> +
> +int cxd2880_integ_init(struct cxd2880_tnrdmd *tnr_dmd);
> +
> +int cxd2880_integ_cancel(struct cxd2880_tnrdmd *tnr_dmd);
> +
> +int cxd2880_integ_check_cancellation(struct cxd2880_tnrdmd
> +				     *tnr_dmd);
> +
> +#endif



Thanks,
Mauro

^ permalink raw reply

* [PATCH 4/4] spi: sh-msiof: Document hardware limitations related to chip selects
From: Geert Uytterhoeven @ 2017-12-13 19:05 UTC (permalink / raw)
  To: Mark Brown, Rob Herring, Mark Rutland
  Cc: linux-spi, devicetree, linux-renesas-soc, Geert Uytterhoeven
In-Reply-To: <1513191913-10612-1-git-send-email-geert+renesas@glider.be>

Guide users to maintain the proper balance between native and GPIO chip
selects.

Signed-off-by: Geert Uytterhoeven <geert+renesas@glider.be>
---
 Documentation/devicetree/bindings/spi/sh-msiof.txt | 10 ++++++++++
 1 file changed, 10 insertions(+)

diff --git a/Documentation/devicetree/bindings/spi/sh-msiof.txt b/Documentation/devicetree/bindings/spi/sh-msiof.txt
index bc8c16a6cfc82685..80710f0f04489174 100644
--- a/Documentation/devicetree/bindings/spi/sh-msiof.txt
+++ b/Documentation/devicetree/bindings/spi/sh-msiof.txt
@@ -41,6 +41,16 @@ Optional properties:
 			   0: MSIOF_SYNC
 			   1: MSIOF_SS1
 			   2: MSIOF_SS2
+			 Hardware limitations related to chip selects:
+			   - Native chip selects are always deasserted in
+			     between transfers that are part of the same
+			     message.  Use cs-gpios to work around this.
+			   - All slaves using native chip selects must use the
+			     same spi-cs-high configuration.  Use cs-gpios to
+			     work around this.
+			   - When using GPIO chip selects, at least one native
+			     chip select must be left unused, as it will be
+			     driven anyway.
 - dmas                 : Must contain a list of two references to DMA
 			 specifiers, one for transmission, and one for
 			 reception.
-- 
2.7.4

^ permalink raw reply related

* [PATCH 3/4] spi: sh-msiof: Implement cs-gpios configuration
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
In-Reply-To: <1513191913-10612-1-git-send-email-geert+renesas-gXvu3+zWzMSzQB+pC5nmwQ@public.gmane.org>

The current support for GPIO chip selects assumes the GPIOs have been
configured by platform code or the boot loader.  This includes pinmux
setup and GPIO direction.  Hence it does not work as expected when just
described in DT using the "cs-gpios" property.

Fix this by:
  1. using devm_gpiod_get_index() to request the GPIO, and thus
     configure pinmux, if needed,
  2. configuring the GPIO direction is the spi_master.setup() callback.

Use gpio_is_valid() instead of a check on positive numbers.

Note that when using GPIO chip selects, at least one native chip select
must be left unused, as that native chip select will be driven anyway,
and (global) native chip select polarity must be taken into account.

Signed-off-by: Geert Uytterhoeven <geert+renesas-gXvu3+zWzMSzQB+pC5nmwQ@public.gmane.org>
---
 drivers/spi/spi-sh-msiof.c | 66 +++++++++++++++++++++++++++++++++++++++++-----
 1 file changed, 59 insertions(+), 7 deletions(-)

diff --git a/drivers/spi/spi-sh-msiof.c b/drivers/spi/spi-sh-msiof.c
index 9bdc292aa050cb16..8aa5c7b910d9ca93 100644
--- a/drivers/spi/spi-sh-msiof.c
+++ b/drivers/spi/spi-sh-msiof.c
@@ -19,6 +19,7 @@
 #include <linux/dmaengine.h>
 #include <linux/err.h>
 #include <linux/gpio.h>
+#include <linux/gpio/consumer.h>
 #include <linux/interrupt.h>
 #include <linux/io.h>
 #include <linux/kernel.h>
@@ -55,6 +56,7 @@ struct sh_msiof_spi_priv {
 	void *rx_dma_page;
 	dma_addr_t tx_dma_addr;
 	dma_addr_t rx_dma_addr;
+	unsigned short unused_ss;
 	bool native_cs_inited;
 	bool native_cs_high;
 	bool slave_aborted;
@@ -547,8 +549,8 @@ static int sh_msiof_spi_setup(struct spi_device *spi)
 		spi->cs_gpio = (uintptr_t)spi->controller_data;
 	}
 
-	if (spi->cs_gpio >= 0) {
-		gpio_set_value(spi->cs_gpio, !(spi->mode & SPI_CS_HIGH));
+	if (gpio_is_valid(spi->cs_gpio)) {
+		gpio_direction_output(spi->cs_gpio, !(spi->mode & SPI_CS_HIGH));
 		return 0;
 	}
 
@@ -580,14 +582,20 @@ static int sh_msiof_prepare_message(struct spi_master *master,
 {
 	struct sh_msiof_spi_priv *p = spi_master_get_devdata(master);
 	const struct spi_device *spi = msg->spi;
+	u32 ss, cs_high;
 
 	/* Configure pins before asserting CS */
-	sh_msiof_spi_set_pin_regs(p, spi->chip_select,
-				  !!(spi->mode & SPI_CPOL),
+	if (gpio_is_valid(spi->cs_gpio)) {
+		ss = p->unused_ss;
+		cs_high = p->native_cs_high;
+	} else {
+		ss = spi->chip_select;
+		cs_high = !!(spi->mode & SPI_CS_HIGH);
+	}
+	sh_msiof_spi_set_pin_regs(p, ss, !!(spi->mode & SPI_CPOL),
 				  !!(spi->mode & SPI_CPHA),
 				  !!(spi->mode & SPI_3WIRE),
-				  !!(spi->mode & SPI_LSB_FIRST),
-				  !!(spi->mode & SPI_CS_HIGH));
+				  !!(spi->mode & SPI_LSB_FIRST), cs_high);
 	return 0;
 }
 
@@ -1091,6 +1099,45 @@ static struct sh_msiof_spi_info *sh_msiof_spi_parse_dt(struct device *dev)
 }
 #endif
 
+static int sh_msiof_get_cs_gpios(struct sh_msiof_spi_priv *p)
+{
+	struct device *dev = &p->pdev->dev;
+	unsigned int used_ss_mask = 0;
+	unsigned int cs_gpios = 0;
+	unsigned int num_cs, i;
+	int ret;
+
+	ret = gpiod_count(dev, "cs");
+	if (ret <= 0)
+		return 0;
+
+	num_cs = max_t(unsigned int, ret, p->master->num_chipselect);
+	for (i = 0; i < num_cs; i++) {
+		struct gpio_desc *gpiod;
+
+		gpiod = devm_gpiod_get_index(dev, "cs", i, GPIOD_ASIS);
+		if (!IS_ERR(gpiod)) {
+			cs_gpios++;
+			continue;
+		}
+
+		if (PTR_ERR(gpiod) != -ENOENT)
+			return PTR_ERR(gpiod);
+
+		if (i >= MAX_SS) {
+			dev_err(dev, "Invalid native chip select %d\n", i);
+			return -EINVAL;
+		}
+		used_ss_mask |= BIT(i);
+	}
+	p->unused_ss = ffz(used_ss_mask);
+	if (cs_gpios && p->unused_ss >= MAX_SS) {
+		dev_err(dev, "No unused native chip select available\n");
+		return -EINVAL;
+	}
+	return 0;
+}
+
 static struct dma_chan *sh_msiof_request_dma_chan(struct device *dev,
 	enum dma_transfer_direction dir, unsigned int id, dma_addr_t port_addr)
 {
@@ -1304,13 +1351,18 @@ static int sh_msiof_spi_probe(struct platform_device *pdev)
 	if (p->info->rx_fifo_override)
 		p->rx_fifo_size = p->info->rx_fifo_override;
 
+	/* Setup GPIO chip selects */
+	master->num_chipselect = p->info->num_chipselect;
+	ret = sh_msiof_get_cs_gpios(p);
+	if (ret)
+		goto err1;
+
 	/* init master code */
 	master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH;
 	master->mode_bits |= SPI_LSB_FIRST | SPI_3WIRE;
 	master->flags = chipdata->master_flags;
 	master->bus_num = pdev->id;
 	master->dev.of_node = pdev->dev.of_node;
-	master->num_chipselect = p->info->num_chipselect;
 	master->setup = sh_msiof_spi_setup;
 	master->prepare_message = sh_msiof_prepare_message;
 	master->slave_abort = sh_msiof_slave_abort;
-- 
2.7.4

--
To unsubscribe from this list: send the line "unsubscribe linux-spi" 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 2/4] spi: sh-msiof: Extend support to 3 native chip selects
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
In-Reply-To: <1513191913-10612-1-git-send-email-geert+renesas-gXvu3+zWzMSzQB+pC5nmwQ@public.gmane.org>

Currently only the MSIOF_SYNC signal can be used as a native chip
select.  Extend support to up to 3 native chipselects using the
MSIOF_SS1 and MSIOF_SS2 signals.

Inspired by a patch in the BSP by Hiromitsu Yamasaki.

Signed-off-by: Geert Uytterhoeven <geert+renesas-gXvu3+zWzMSzQB+pC5nmwQ@public.gmane.org>
---
 Documentation/devicetree/bindings/spi/sh-msiof.txt |  6 +++++-
 drivers/spi/spi-sh-msiof.c                         | 18 +++++++++++++-----
 2 files changed, 18 insertions(+), 6 deletions(-)

diff --git a/Documentation/devicetree/bindings/spi/sh-msiof.txt b/Documentation/devicetree/bindings/spi/sh-msiof.txt
index bdd83959019c7883..bc8c16a6cfc82685 100644
--- a/Documentation/devicetree/bindings/spi/sh-msiof.txt
+++ b/Documentation/devicetree/bindings/spi/sh-msiof.txt
@@ -36,7 +36,11 @@ Required properties:
 
 Optional properties:
 - clocks               : Must contain a reference to the functional clock.
-- num-cs               : Total number of chip-selects (default is 1)
+- num-cs               : Total number of chip selects (default is 1).
+			 Up to 3 native chip selects are supported:
+			   0: MSIOF_SYNC
+			   1: MSIOF_SS1
+			   2: MSIOF_SS2
 - dmas                 : Must contain a list of two references to DMA
 			 specifiers, one for transmission, and one for
 			 reception.
diff --git a/drivers/spi/spi-sh-msiof.c b/drivers/spi/spi-sh-msiof.c
index 2704abb11ea41fd0..9bdc292aa050cb16 100644
--- a/drivers/spi/spi-sh-msiof.c
+++ b/drivers/spi/spi-sh-msiof.c
@@ -60,6 +60,8 @@ struct sh_msiof_spi_priv {
 	bool slave_aborted;
 };
 
+#define MAX_SS	3	/* Maximum number of native chip selects */
+
 #define TMDR1	0x00	/* Transmit Mode Register 1 */
 #define TMDR2	0x04	/* Transmit Mode Register 2 */
 #define TMDR3	0x08	/* Transmit Mode Register 3 */
@@ -93,6 +95,8 @@ struct sh_msiof_spi_priv {
 #define MDR1_XXSTP	 0x00000001 /* Transmission/Reception Stop on FIFO */
 /* TMDR1 */
 #define TMDR1_PCON	 0x40000000 /* Transfer Signal Connection */
+#define TMDR1_SYNCCH_MASK 0xc000000 /* Synchronization Signal Channel Select */
+#define TMDR1_SYNCCH_SHIFT	 26 /* 0=MSIOF_SYNC, 1=MSIOF_SS1, 2=MSIOF_SS2 */
 
 /* TMDR2 and RMDR2 */
 #define MDR2_BITLEN1(i)	(((i) - 1) << 24) /* Data Size (8-32 bits) */
@@ -326,7 +330,7 @@ static u32 sh_msiof_spi_get_dtdl_and_syncdl(struct sh_msiof_spi_priv *p)
 	return val;
 }
 
-static void sh_msiof_spi_set_pin_regs(struct sh_msiof_spi_priv *p,
+static void sh_msiof_spi_set_pin_regs(struct sh_msiof_spi_priv *p, u32 ss,
 				      u32 cpol, u32 cpha,
 				      u32 tx_hi_z, u32 lsb_first, u32 cs_high)
 {
@@ -344,10 +348,13 @@ static void sh_msiof_spi_set_pin_regs(struct sh_msiof_spi_priv *p,
 	tmp |= !cs_high << MDR1_SYNCAC_SHIFT;
 	tmp |= lsb_first << MDR1_BITLSB_SHIFT;
 	tmp |= sh_msiof_spi_get_dtdl_and_syncdl(p);
-	if (spi_controller_is_slave(p->master))
+	if (spi_controller_is_slave(p->master)) {
 		sh_msiof_write(p, TMDR1, tmp | TMDR1_PCON);
-	else
-		sh_msiof_write(p, TMDR1, tmp | MDR1_TRMD | TMDR1_PCON);
+	} else {
+		sh_msiof_write(p, TMDR1,
+			       tmp | MDR1_TRMD | TMDR1_PCON |
+			       (ss < MAX_SS ? ss : 0) << TMDR1_SYNCCH_SHIFT);
+	}
 	if (p->master->flags & SPI_MASTER_MUST_TX) {
 		/* These bits are reserved if RX needs TX */
 		tmp &= ~0x0000ffff;
@@ -575,7 +582,8 @@ static int sh_msiof_prepare_message(struct spi_master *master,
 	const struct spi_device *spi = msg->spi;
 
 	/* Configure pins before asserting CS */
-	sh_msiof_spi_set_pin_regs(p, !!(spi->mode & SPI_CPOL),
+	sh_msiof_spi_set_pin_regs(p, spi->chip_select,
+				  !!(spi->mode & SPI_CPOL),
 				  !!(spi->mode & SPI_CPHA),
 				  !!(spi->mode & SPI_3WIRE),
 				  !!(spi->mode & SPI_LSB_FIRST),
-- 
2.7.4

--
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/4] spi: sh-msiof: Avoid writing to registers from spi_master.setup()
From: Geert Uytterhoeven @ 2017-12-13 19:05 UTC (permalink / raw)
  To: Mark Brown, Rob Herring, Mark Rutland
  Cc: linux-spi, devicetree, linux-renesas-soc, Geert Uytterhoeven
In-Reply-To: <1513191913-10612-1-git-send-email-geert+renesas@glider.be>

The spi_master.setup() callback must not change configuration registers,
as that could corrupt I/O that is in progress for other SPI slaves.

The only exception is the configuration of the native chip select
polarity in SPI master mode, as a wrong chip select polarity will cause
havoc during all future transfers to any other SPI slave.

Hence stop writing to registers in sh_msiof_spi_setup(), unless it is
the first call for a controller using a native chip select, or unless
native chip select polarity has changed (note that you'll loose anyway
if I/O is in progress).  Even then, only do what is strictly necessary,
instead of calling sh_msiof_spi_set_pin_regs().

Signed-off-by: Geert Uytterhoeven <geert+renesas@glider.be>
---
 drivers/spi/spi-sh-msiof.c | 35 ++++++++++++++++++++++++-----------
 1 file changed, 24 insertions(+), 11 deletions(-)

diff --git a/drivers/spi/spi-sh-msiof.c b/drivers/spi/spi-sh-msiof.c
index 81a9144f5442cdb4..2704abb11ea41fd0 100644
--- a/drivers/spi/spi-sh-msiof.c
+++ b/drivers/spi/spi-sh-msiof.c
@@ -55,6 +55,8 @@ struct sh_msiof_spi_priv {
 	void *rx_dma_page;
 	dma_addr_t tx_dma_addr;
 	dma_addr_t rx_dma_addr;
+	bool native_cs_inited;
+	bool native_cs_high;
 	bool slave_aborted;
 };
 
@@ -528,8 +530,7 @@ static int sh_msiof_spi_setup(struct spi_device *spi)
 {
 	struct device_node	*np = spi->master->dev.of_node;
 	struct sh_msiof_spi_priv *p = spi_master_get_devdata(spi->master);
-
-	pm_runtime_get_sync(&p->pdev->dev);
+	u32 clr, set, tmp;
 
 	if (!np) {
 		/*
@@ -539,19 +540,31 @@ static int sh_msiof_spi_setup(struct spi_device *spi)
 		spi->cs_gpio = (uintptr_t)spi->controller_data;
 	}
 
-	/* Configure pins before deasserting CS */
-	sh_msiof_spi_set_pin_regs(p, !!(spi->mode & SPI_CPOL),
-				  !!(spi->mode & SPI_CPHA),
-				  !!(spi->mode & SPI_3WIRE),
-				  !!(spi->mode & SPI_LSB_FIRST),
-				  !!(spi->mode & SPI_CS_HIGH));
-
-	if (spi->cs_gpio >= 0)
+	if (spi->cs_gpio >= 0) {
 		gpio_set_value(spi->cs_gpio, !(spi->mode & SPI_CS_HIGH));
+		return 0;
+	}
 
+	if (spi_controller_is_slave(p->master))
+		return 0;
 
-	pm_runtime_put(&p->pdev->dev);
+	if (p->native_cs_inited &&
+	    (p->native_cs_high == !!(spi->mode & SPI_CS_HIGH)))
+		return 0;
 
+	/* Configure native chip select mode/polarity early */
+	clr = MDR1_SYNCMD_MASK;
+	set = MDR1_TRMD | TMDR1_PCON | MDR1_SYNCMD_SPI;
+	if (spi->mode & SPI_CS_HIGH)
+		clr |= BIT(MDR1_SYNCAC_SHIFT);
+	else
+		set |= BIT(MDR1_SYNCAC_SHIFT);
+	pm_runtime_get_sync(&p->pdev->dev);
+	tmp = sh_msiof_read(p, TMDR1) & ~clr;
+	sh_msiof_write(p, TMDR1, tmp | set);
+	pm_runtime_put(&p->pdev->dev);
+	p->native_cs_high = spi->mode & SPI_CS_HIGH;
+	p->native_cs_inited = true;
 	return 0;
 }
 
-- 
2.7.4

^ permalink raw reply related

* [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


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox