Devicetree
 help / color / mirror / Atom feed
* [PATCH v2 2/3] dt-bindings: iio: temperature: add MLX90632 device bindings
From: Crt Mori @ 2017-12-04 15:31 UTC (permalink / raw)
  To: Jonathan Cameron
  Cc: linux-iio-u79uwXL29TY76Z2rM5mHXA,
	devicetree-u79uwXL29TY76Z2rM5mHXA, Crt Mori

Add device tree bindings for MLX90632 IR temperature sensor.

Signed-off-by: Crt Mori <cmo-fc6wVz46lShBDgjK7y7TUQ@public.gmane.org>
Reviewed-by: Rob Herring <robh-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>
---
 .../bindings/iio/temperature/mlx90632.txt          | 28 ++++++++++++++++++++++
 1 file changed, 28 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/iio/temperature/mlx90632.txt

diff --git a/Documentation/devicetree/bindings/iio/temperature/mlx90632.txt b/Documentation/devicetree/bindings/iio/temperature/mlx90632.txt
new file mode 100644
index 000000000000..0b05812001f8
--- /dev/null
+++ b/Documentation/devicetree/bindings/iio/temperature/mlx90632.txt
@@ -0,0 +1,28 @@
+* Melexis MLX90632 contactless Infra Red temperature sensor
+
+Link to datasheet: https://www.melexis.com/en/documents/documentation/datasheets/datasheet-mlx90632
+
+There are various applications for the Infra Red contactless temperature sensor
+and MLX90632 is most suitable for consumer applications where measured object
+temperature is in range between -20 to 200 degrees Celsius with relative error
+of measurement below 1 degree Celsius in object temperature range for
+industrial applications. Since it can operate and measure ambient temperature
+in range of -20 to 85 degrees Celsius it is suitable also for outdoor use.
+
+Be aware that electronics surrounding the sensor can increase ambient
+temperature. MLX90632 can be calibrated to reduce the housing effect via
+already existing EEPROM parameters.
+
+Since measured object emissivity effects Infra Red energy emitted, emissivity
+should be set before requesting the object temperature.
+
+Required properties:
+  - compatible: should be "melexis,mlx90632"
+  - reg: the I2C address of the sensor (default 0x3a)
+
+Example:
+
+mlx90632@3a {
+	compatible = "melexis,mlx90632";
+	reg = <0x3a>;
+};
-- 
2.15.0

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

^ permalink raw reply related

* Re: [PATCH v4.14] Add support for bq27521 battery monitor
From: Andrew F. Davis @ 2017-12-04 15:20 UTC (permalink / raw)
  To: Pavel Machek, pali.rohar, sre, kernel list, linux-arm-kernel,
	linux-omap, tony, khilman, aaro.koskinen, ivo.g.dimitrov.75,
	patrikbachan, serge, abcloriens, clayton, martijn, sakari.ailus,
	kernel, devicetree, robh+dt
In-Reply-To: <20171201230649.GA27533@amd>

On 12/01/2017 05:06 PM, Pavel Machek wrote:
> Hi!
> 
>> This adds basic support for BQ27521 battery monitor, used in Nokia N9
>> and N950. In particular, battery voltage is important to be able to
>> tell when the battery is almost empty. Emptying battery on N950 is
>> pretty painful, as flasher needs to be used to recover phone in such
>> case.
> 
> Sebastian, ping? This one should not be too controversial.
> 
> If you could also apply the "shut down when battery is low", that
> would be nice.
> 
> Thanks,
> 								Pavel
> 
>> Signed-off-by: Pavel Machek <pavel@ucw.cz>
>>
>> diff --git a/drivers/power/supply/bq27xxx_battery.c b/drivers/power/supply/bq27xxx_battery.c
>> index 51f0961..f684288 100644
>> --- a/drivers/power/supply/bq27xxx_battery.c
>> +++ b/drivers/power/supply/bq27xxx_battery.c
>> @@ -323,6 +323,30 @@ static u8
>>  		[BQ27XXX_REG_AP] = INVALID_REG_ADDR,
>>  		BQ27XXX_DM_REG_ROWS,
>>  	},
>> +	bq27521_regs[BQ27XXX_REG_MAX] = {
>> +		[BQ27XXX_REG_CTRL] = 0x02,
>> +		[BQ27XXX_REG_TEMP] = 0x0a,
>> +		[BQ27XXX_REG_INT_TEMP] = INVALID_REG_ADDR,
>> +		[BQ27XXX_REG_VOLT] = 0x0c,
>> +		[BQ27XXX_REG_AI] = 0x0e,
>> +		[BQ27XXX_REG_FLAGS] = 0x08,
>> +		[BQ27XXX_REG_TTE] = INVALID_REG_ADDR,
>> +		[BQ27XXX_REG_TTF] = INVALID_REG_ADDR,
>> +		[BQ27XXX_REG_TTES] = INVALID_REG_ADDR,
>> +		[BQ27XXX_REG_TTECP] = INVALID_REG_ADDR,
>> +		[BQ27XXX_REG_NAC] = INVALID_REG_ADDR,
>> +		[BQ27XXX_REG_FCC] = INVALID_REG_ADDR,
>> +		[BQ27XXX_REG_CYCT] = INVALID_REG_ADDR,
>> +		[BQ27XXX_REG_AE] = INVALID_REG_ADDR,
>> +		[BQ27XXX_REG_SOC] = INVALID_REG_ADDR,
>> +		[BQ27XXX_REG_DCAP] = INVALID_REG_ADDR,
>> +		[BQ27XXX_REG_AP] = INVALID_REG_ADDR,
>> +		[BQ27XXX_DM_CTRL] = INVALID_REG_ADDR,
>> +		[BQ27XXX_DM_CLASS] = INVALID_REG_ADDR,
>> +		[BQ27XXX_DM_BLOCK] = INVALID_REG_ADDR,
>> +		[BQ27XXX_DM_DATA] = INVALID_REG_ADDR,
>> +		[BQ27XXX_DM_CKSUM] = INVALID_REG_ADDR,
>> +	},
>>  	bq27530_regs[BQ27XXX_REG_MAX] = {
>>  		[BQ27XXX_REG_CTRL] = 0x00,
>>  		[BQ27XXX_REG_TEMP] = 0x06,
>> @@ -557,6 +581,15 @@ static enum power_supply_property bq27520g4_props[] = {
>>  	POWER_SUPPLY_PROP_MANUFACTURER,
>>  };
>>  
>> +static enum power_supply_property bq27521_props[] = {
>> +	POWER_SUPPLY_PROP_STATUS,
>> +	POWER_SUPPLY_PROP_PRESENT,
>> +	POWER_SUPPLY_PROP_VOLTAGE_NOW,
>> +	POWER_SUPPLY_PROP_CURRENT_NOW,
>> +	POWER_SUPPLY_PROP_TEMP,
>> +	POWER_SUPPLY_PROP_TECHNOLOGY,
>> +};
>> +
>>  static enum power_supply_property bq27530_props[] = {
>>  	POWER_SUPPLY_PROP_STATUS,
>>  	POWER_SUPPLY_PROP_PRESENT,
>> @@ -671,6 +704,7 @@ static struct bq27xxx_dm_reg bq27500_dm_regs[] = {
>>  #define bq27520g2_dm_regs 0
>>  #define bq27520g3_dm_regs 0
>>  #define bq27520g4_dm_regs 0
>> +#define bq27521_dm_regs 0
>>  #define bq27530_dm_regs 0
>>  #define bq27531_dm_regs 0
>>  #define bq27541_dm_regs 0
>> @@ -717,8 +751,8 @@ static struct bq27xxx_dm_reg bq27621_dm_regs[] = {
>>  #endif
>>  
>>  #define BQ27XXX_O_ZERO	0x00000001
>> -#define BQ27XXX_O_OTDC	0x00000002
>> -#define BQ27XXX_O_UTOT  0x00000004
>> +#define BQ27XXX_O_OTDC	0x00000002 /* has OTC/OTD overtemperature flags */
>> +#define BQ27XXX_O_UTOT  0x00000004 /* has OT overtemperature flag */

These should get turned into BIT() macros, but that's not your problem
for this patch,


Acked-by: Andrew F. Davis <afd@ti.com>


>>  #define BQ27XXX_O_CFGUP	0x00000008
>>  #define BQ27XXX_O_RAM	0x00000010
>>  
>> @@ -751,6 +785,7 @@ static struct {
>>  	[BQ27520G2] = BQ27XXX_DATA(bq27520g2, 0         , BQ27XXX_O_OTDC),
>>  	[BQ27520G3] = BQ27XXX_DATA(bq27520g3, 0         , BQ27XXX_O_OTDC),
>>  	[BQ27520G4] = BQ27XXX_DATA(bq27520g4, 0         , BQ27XXX_O_OTDC),
>> +	[BQ27521]   = BQ27XXX_DATA(bq27521,   0         , 0),
>>  	[BQ27530]   = BQ27XXX_DATA(bq27530,   0         , BQ27XXX_O_UTOT),
>>  	[BQ27531]   = BQ27XXX_DATA(bq27531,   0         , BQ27XXX_O_UTOT),
>>  	[BQ27541]   = BQ27XXX_DATA(bq27541,   0         , BQ27XXX_O_OTDC),
>> diff --git a/drivers/power/supply/bq27xxx_battery_i2c.c b/drivers/power/supply/bq27xxx_battery_i2c.c
>> index 0b11ed4..6b25e5f 100644
>> --- a/drivers/power/supply/bq27xxx_battery_i2c.c
>> +++ b/drivers/power/supply/bq27xxx_battery_i2c.c
>> @@ -239,6 +239,7 @@ static const struct i2c_device_id bq27xxx_i2c_id_table[] = {
>>  	{ "bq27520g2", BQ27520G2 },
>>  	{ "bq27520g3", BQ27520G3 },
>>  	{ "bq27520g4", BQ27520G4 },
>> +	{ "bq27521", BQ27521 },
>>  	{ "bq27530", BQ27530 },
>>  	{ "bq27531", BQ27531 },
>>  	{ "bq27541", BQ27541 },
>> @@ -269,6 +270,7 @@ static const struct of_device_id bq27xxx_battery_i2c_of_match_table[] = {
>>  	{ .compatible = "ti,bq27520g2" },
>>  	{ .compatible = "ti,bq27520g3" },
>>  	{ .compatible = "ti,bq27520g4" },
>> +	{ .compatible = "ti,bq27521" },
>>  	{ .compatible = "ti,bq27530" },
>>  	{ .compatible = "ti,bq27531" },
>>  	{ .compatible = "ti,bq27541" },
>> diff --git a/include/linux/mfd/twl.h b/include/linux/mfd/twl.h
>> index 9ad7828..6547d30 100644
>> --- a/include/linux/mfd/twl.h
>> +++ b/include/linux/mfd/twl.h
>> @@ -70,6 +70,7 @@ enum twl4030_module_ids {
>>  	TWL4030_MODULE_INT,
>>  
>>  	TWL5031_MODULE_ACCESSORY,
>> +	TWL5031_MODULE_BCC,
>>  	TWL5031_MODULE_INTERRUPTS,
>>  
>>  	TWL4030_MODULE_LAST,
>> diff --git a/include/linux/power/bq27xxx_battery.h b/include/linux/power/bq27xxx_battery.h
>> index 43194e0..d8d127a 100644
>> --- a/include/linux/power/bq27xxx_battery.h
>> +++ b/include/linux/power/bq27xxx_battery.h
>> @@ -15,6 +15,7 @@ enum bq27xxx_chip {
>>  	BQ27520G2, /* bq27520G2 */
>>  	BQ27520G3, /* bq27520G3 */
>>  	BQ27520G4, /* bq27520G4 */
>> +	BQ27521, /* bq27521 */	
>>  	BQ27530, /* bq27530, bq27531 */
>>  	BQ27531,
>>  	BQ27541, /* bq27541, bq27542, bq27546, bq27742 */
>>
> 
> 
> 

^ permalink raw reply

* Re: [PATCH] of: overlay: fix memory leak of ovcs on error exit path
From: Geert Uytterhoeven @ 2017-12-04 15:15 UTC (permalink / raw)
  To: Dan Carpenter
  Cc: Frank Rowand, Geert Uytterhoeven, Colin King, Pantelis Antoniou,
	Rob Herring, devicetree-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
	kernel-janitors-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
In-Reply-To: <20171130125002.wlnnev5pjih6d5bz@mwanda>

Hi Dan,

On Thu, Nov 30, 2017 at 1:50 PM, Dan Carpenter <dan.carpenter-QHcLZuEGTsvQT0dZR+AlfA@public.gmane.org> wrote:
> On Thu, Nov 30, 2017 at 07:14:45AM -0500, Frank Rowand wrote:
>> On 11/29/17 14:17, Colin King wrote:
>> > From: Colin Ian King <colin.king-Z7WLFzj8eWMS+FvcfC7Uqw@public.gmane.org>
>> >
>> > Currently if the call to of_resolve_phandles fails then then ovcs
>> > is not kfree'd on the error exit path.  Rather than try and make
>> > the clean up exit path more convoluted, fix this by just kfree'ing
>> > ovcs at the point of error detection and exit via the same exit
>> > path.
>> >
>> > Detected by CoverityScan, CID#1462296 ("Resource Leak")
>> >
>> > Fixes: f948d6d8b792 ("of: overlay: avoid race condition between applying multiple overlays")
>
> The Fixes tag is wrong.  It should be:
>
> Fixes: bd80e2555c5c ("of: overlay: Fix cleanup order in of_overlay_apply()")
>
>> > Signed-off-by: Colin Ian King <colin.king-Z7WLFzj8eWMS+FvcfC7Uqw@public.gmane.org>
>> > ---
>> >  drivers/of/overlay.c | 4 +++-
>> >  1 file changed, 3 insertions(+), 1 deletion(-)
>> >
>> > diff --git a/drivers/of/overlay.c b/drivers/of/overlay.c
>> > index 53bc9e3f0b98..6c8efe7d8cbb 100644
>> > --- a/drivers/of/overlay.c
>> > +++ b/drivers/of/overlay.c
>> > @@ -708,8 +708,10 @@ int of_overlay_apply(struct device_node *tree, int *ovcs_id)
>> >     of_overlay_mutex_lock();
>> >
>> >     ret = of_resolve_phandles(tree);
>> > -   if (ret)
>> > +   if (ret) {
>> > +           kfree(ovcs);
>> >             goto err_overlay_unlock;
>> > +   }
>> >
>> >     mutex_lock(&of_mutex);
>> >
>> >
>>
>> False coverity warning.  ovcs is freed in free_overlay_changeset().
>
> You're looking at an older version of the code and Colin is looking at
> linux-next.

Actually Colin was right: the bug was introduced in commit f948d6d8b792:
While free_overlay_changeset() is called in the error path, that function
returns early because of:

        if (!ovcs->cset.entries.next)
                return;

So it never gets to freeing the passed pointer.

I'm fixing it for good (let's hope so ;-)

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

* Re: [PATCH v4] usb: xhci: allow imod-interval to be configurable
From: Timur Tabi @ 2017-12-04 14:58 UTC (permalink / raw)
  To: Adam Wallis, Greg Kroah-Hartman, Rob Herring, Mark Rutland,
	Matthias Brugger, Mathias Nyman, Chunfeng Yun,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	linux-usb-u79uwXL29TY76Z2rM5mHXA,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-mediatek-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r
In-Reply-To: <1512397671-28733-1-git-send-email-awallis-sgV2jX0FEOL9JmXXK+q4OQ@public.gmane.org>

On 12/4/17 8:27 AM, Adam Wallis wrote:
> If no interval is specified, the default of 40,000ns (IMOD=160) will be
> used.

...

> + - imod-interval-ns: default interrupt moderation interval is 5000ns

...

> +  - imod-interval-ns: default interrupt moderation interval is 5000ns

...

> +	xhci->imod_interval = 5000;

...

> +	xhci->imod_interval = 40000;

...

> +	xhci->imod_interval = 40000;

Is the default 5,000 or 40,000?  I can't tell.

-- 
Qualcomm Datacenter Technologies, Inc. as an affiliate of Qualcomm
Technologies, Inc.  Qualcomm Technologies, Inc. is a member of the
Code Aurora Forum, a Linux Foundation Collaborative Project.
--
To unsubscribe from this list: send the line "unsubscribe linux-usb" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply

* Re: [PATCH 5/5] PCI: cadence: add EndPoint Controller driver for Cadence PCIe controller
From: Cyrille Pitchen @ 2017-12-04 14:56 UTC (permalink / raw)
  To: Lorenzo Pieralisi
  Cc: bhelgaas, kishon, linux-pci, adouglas, stelford, dgary, kgopi,
	eandrews, thomas.petazzoni, sureshp, nsekhar, linux-kernel, robh,
	devicetree
In-Reply-To: <20171201122048.GB25010@red-moon>

Hi Lorenzo,

Le 01/12/2017 à 13:20, Lorenzo Pieralisi a écrit :
> On Thu, Nov 23, 2017 at 04:01:50PM +0100, Cyrille Pitchen wrote:
>> This patch adds support to the Cadence PCIe controller in endpoint mode.
> 
> Please add a brief description to the log to describe the most salient
> features.
> 
>> Signed-off-by: Cyrille Pitchen <cyrille.pitchen@free-electrons.com>
>> ---
>>  drivers/pci/cadence/Kconfig           |   9 +
>>  drivers/pci/cadence/Makefile          |   1 +
>>  drivers/pci/cadence/pcie-cadence-ep.c | 553 ++++++++++++++++++++++++++++++++++
>>  3 files changed, 563 insertions(+)
>>  create mode 100644 drivers/pci/cadence/pcie-cadence-ep.c
>>
>> diff --git a/drivers/pci/cadence/Kconfig b/drivers/pci/cadence/Kconfig
>> index 120306cae2aa..b2e6af71f39e 100644
>> --- a/drivers/pci/cadence/Kconfig
>> +++ b/drivers/pci/cadence/Kconfig
>> @@ -21,4 +21,13 @@ config PCIE_CADENCE_HOST
>>  	  mode. This PCIe controller may be embedded into many different vendors
>>  	  SoCs.
>>  
>> +config PCIE_CADENCE_EP
>> +	bool "Cadence PCIe endpoint controller"
>> +	depends on PCI_ENDPOINT
>> +	select PCIE_CADENCE
>> +	help
>> +	  Say Y here if you want to support the Cadence PCIe  controller in
>> +	  endpoint mode. This PCIe controller may be embedded into many
>> +	  different vendors SoCs.
>> +
>>  endif # PCI_CADENCE
>> diff --git a/drivers/pci/cadence/Makefile b/drivers/pci/cadence/Makefile
>> index d57d192d2595..61e9c8d6839d 100644
>> --- a/drivers/pci/cadence/Makefile
>> +++ b/drivers/pci/cadence/Makefile
>> @@ -1,2 +1,3 @@
>>  obj-$(CONFIG_PCIE_CADENCE) += pcie-cadence.o
>>  obj-$(CONFIG_PCIE_CADENCE_HOST) += pcie-cadence-host.o
>> +obj-$(CONFIG_PCIE_CADENCE_EP) += pcie-cadence-ep.o
>> diff --git a/drivers/pci/cadence/pcie-cadence-ep.c b/drivers/pci/cadence/pcie-cadence-ep.c
>> new file mode 100644
>> index 000000000000..a1d761101a9c
>> --- /dev/null
>> +++ b/drivers/pci/cadence/pcie-cadence-ep.c
>> @@ -0,0 +1,553 @@
>> +/*
>> + * Cadence PCIe host controller driver.
> 
> You should update this comment.
> 
>> + *
>> + * Copyright (c) 2017 Cadence
>> + *
>> + * Author: Cyrille Pitchen <cyrille.pitchen@free-electrons.com>
>> + *
>> + * This program is free software; you can redistribute it and/or modify
>> + * it under the terms of the GNU General Public License version 2 as
>> + * published by the Free Software Foundation.
>> + *
>> + * This program is distributed in the hope that it will be useful,
>> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
>> + * GNU General Public License for more details.
>> + *
>> + * You should have received a copy of the GNU General Public License along with
>> + * this program.  If not, see <http://www.gnu.org/licenses/>.
>> + */
>> +#include <linux/kernel.h>
>> +#include <linux/of.h>
>> +#include <linux/pci-epc.h>
>> +#include <linux/platform_device.h>
>> +#include <linux/pm_runtime.h>
>> +#include <linux/sizes.h>
>> +#include <linux/delay.h>
> 
> Nit: alphabetical order.
> 
>> +#include "pcie-cadence.h"
>> +
>> +#define CDNS_PCIE_EP_MIN_APERTURE		128	/* 128 bytes */
>> +#define CDNS_PCIE_EP_IRQ_PCI_ADDR_NONE		0x1
>> +#define CDNS_PCIE_EP_IRQ_PCI_ADDR_LEGACY	0x3
>> +
>> +/**
>> + * struct cdns_pcie_ep_data - hardware specific data
>> + * @max_regions: maximum nmuber of regions supported by hardware
> 
> s/nmuber/number
> 
>> + */
>> +struct cdns_pcie_ep_data {
>> +	size_t				max_regions;
>> +};
>> +
>> +/**
>> + * struct cdns_pcie_ep - private data for this PCIe endpoint controller driver
>> + * @pcie: Cadence PCIe controller
>> + * @data: pointer to a 'struct cdns_pcie_data'
>> + */
>> +struct cdns_pcie_ep {
>> +	struct cdns_pcie		pcie;
>> +	const struct cdns_pcie_ep_data	*data;
>> +	struct pci_epc			*epc;
>> +	unsigned long			ob_region_map;
>> +	phys_addr_t			*ob_addr;
>> +	phys_addr_t			irq_phys_addr;
>> +	void __iomem			*irq_cpu_addr;
>> +	u64				irq_pci_addr;
>> +	u8				irq_pending;
>> +};
>> +
>> +static int cdns_pcie_ep_write_header(struct pci_epc *epc,
>> +				     struct pci_epf_header *hdr)
>> +{
>> +	struct cdns_pcie_ep *ep = epc_get_drvdata(epc);
>> +	struct cdns_pcie *pcie = &ep->pcie;
>> +	u8 fn = 0;
>> +
>> +	if (fn == 0) {
> 
> I think there is some code to retrieve fn missing here.
>

I've used some "u8 fn = 0;" when implementing every handler of
'struct pci_epc_ops' because the current API of the EPC library supports
only one function for now but I guess this will change soon.

So I've implement each handler as if a new 'fn' parameter were added to the
handler prototype. I guess it would make sense to add such a parameter to
the pci_epc_*() functions in pci-epc-core.c.
 
>> +		u32 id = CDNS_PCIE_LM_ID_VENDOR(hdr->vendorid) |
>> +			 CDNS_PCIE_LM_ID_SUBSYS(hdr->subsys_vendor_id);
>> +		cdns_pcie_writel(pcie, CDNS_PCIE_LM_ID, id);
>> +	}
>> +	cdns_pcie_ep_fn_writew(pcie, fn, PCI_DEVICE_ID, hdr->deviceid);
>> +	cdns_pcie_ep_fn_writeb(pcie, fn, PCI_REVISION_ID, hdr->revid);
>> +	cdns_pcie_ep_fn_writeb(pcie, fn, PCI_CLASS_PROG, hdr->progif_code);
>> +	cdns_pcie_ep_fn_writew(pcie, fn, PCI_CLASS_DEVICE,
>> +			       hdr->subclass_code | hdr->baseclass_code << 8);
>> +	cdns_pcie_ep_fn_writeb(pcie, fn, PCI_CACHE_LINE_SIZE,
>> +			       hdr->cache_line_size);
>> +	cdns_pcie_ep_fn_writew(pcie, fn, PCI_SUBSYSTEM_ID, hdr->subsys_id);
>> +	cdns_pcie_ep_fn_writeb(pcie, fn, PCI_INTERRUPT_PIN, hdr->interrupt_pin);
>> +
>> +	return 0;
>> +}
>> +
>> +static int cdns_pcie_ep_set_bar(struct pci_epc *epc, enum pci_barno bar,
>> +				dma_addr_t bar_phys, size_t size, int flags)
>> +{
>> +	struct cdns_pcie_ep *ep = epc_get_drvdata(epc);
>> +	struct cdns_pcie *pcie = &ep->pcie;
>> +	u32 addr0, addr1, reg, cfg, b, aperture, ctrl;
>> +	u8 fn = 0;
>> +	u64 sz;
>> +
>> +	/* BAR size is 2^(aperture + 7) */
>> +	sz = max_t(size_t, size, CDNS_PCIE_EP_MIN_APERTURE);
>> +	sz = 1ULL << fls64(sz - 1);
>> +	aperture = ilog2(sz) - 7; /* 128B -> 0, 256B -> 1, 512B -> 2, ... */
>> +
>> +	if ((flags & PCI_BASE_ADDRESS_SPACE) == PCI_BASE_ADDRESS_SPACE_IO) {
>> +		ctrl = CDNS_PCIE_LM_BAR_CFG_CTRL_IO_32BITS;
>> +	} else {
>> +		bool is_prefetch = !!(flags & PCI_BASE_ADDRESS_MEM_PREFETCH);
>> +		bool is_64bits = sz > SZ_2G;
>> +
>> +		if (is_64bits && (bar & 1))
>> +			return -EINVAL;
>> +
>> +		switch (is_64bits << 1 | is_prefetch) {
> 
> I would not mind implementing this as a nested if-else, I am not a big
> fan of using bool this way.
>

It did it this way to cope with the 80 columns rule but I will try to do it
with nested if-elst statements. The macro names I've chosen are quite long!

>> +		case 0:
>> +			ctrl = CDNS_PCIE_LM_BAR_CFG_CTRL_MEM_32BITS;
>> +			break;
>> +
>> +		case 1:
>> +			ctrl = CDNS_PCIE_LM_BAR_CFG_CTRL_PREFETCH_MEM_32BITS;
>> +			break;
>> +
>> +		case 2:
>> +			ctrl = CDNS_PCIE_LM_BAR_CFG_CTRL_MEM_64BITS;
>> +			break;
>> +
>> +		case 3:
>> +			ctrl = CDNS_PCIE_LM_BAR_CFG_CTRL_PREFETCH_MEM_64BITS;
>> +			break;
>> +		}
>> +	}
>> +
>> +	addr0 = lower_32_bits(bar_phys);
>> +	addr1 = upper_32_bits(bar_phys);
>> +	cdns_pcie_writel(pcie, CDNS_PCIE_AT_IB_EP_FUNC_BAR_ADDR0(fn, bar),
>> +			 addr0);
>> +	cdns_pcie_writel(pcie, CDNS_PCIE_AT_IB_EP_FUNC_BAR_ADDR1(fn, bar),
>> +			 addr1);
> 
> Is fn always 0 ?
>

If the API of the EPC library is updated, fn could be something else but 0
later but for now, fn is actually always 0.

>> +	if (bar < BAR_4) {
>> +		reg = CDNS_PCIE_LM_EP_FUNC_BAR_CFG0(fn);
>> +		b = bar;
>> +	} else {
>> +		reg = CDNS_PCIE_LM_EP_FUNC_BAR_CFG1(fn);
>> +		b = bar - BAR_4;
>> +	}
>> +
>> +	cfg = cdns_pcie_readl(pcie, reg);
>> +	cfg &= ~(CDNS_PCIE_LM_EP_FUNC_BAR_CFG_BAR_APERTURE_MASK(b) |
>> +		 CDNS_PCIE_LM_EP_FUNC_BAR_CFG_BAR_CTRL_MASK(b));
>> +	cfg |= (CDNS_PCIE_LM_EP_FUNC_BAR_CFG_BAR_APERTURE(b, aperture) |
>> +		CDNS_PCIE_LM_EP_FUNC_BAR_CFG_BAR_CTRL(b, ctrl));
>> +	cdns_pcie_writel(pcie, reg, cfg);
>> +
>> +	return 0;
>> +}
>> +
>> +static void cdns_pcie_ep_clear_bar(struct pci_epc *epc, enum pci_barno bar)
>> +{
>> +	struct cdns_pcie_ep *ep = epc_get_drvdata(epc);
>> +	struct cdns_pcie *pcie = &ep->pcie;
>> +	u32 reg, cfg, b, ctrl;
>> +	u8 fn = 0;
>> +
>> +	if (bar < BAR_4) {
>> +		reg = CDNS_PCIE_LM_EP_FUNC_BAR_CFG0(fn);
>> +		b = bar;
>> +	} else {
>> +		reg = CDNS_PCIE_LM_EP_FUNC_BAR_CFG1(fn);
>> +		b = bar - BAR_4;
>> +	}
>> +
>> +	ctrl = CDNS_PCIE_LM_BAR_CFG_CTRL_DISABLED;
>> +	cfg = cdns_pcie_readl(pcie, reg);
>> +	cfg &= ~(CDNS_PCIE_LM_EP_FUNC_BAR_CFG_BAR_APERTURE_MASK(b) |
>> +		 CDNS_PCIE_LM_EP_FUNC_BAR_CFG_BAR_CTRL_MASK(b));
>> +	cfg |= CDNS_PCIE_LM_EP_FUNC_BAR_CFG_BAR_CTRL_MASK(ctrl);
>> +	cdns_pcie_writel(pcie, reg, cfg);
>> +
>> +	cdns_pcie_writel(pcie, CDNS_PCIE_AT_IB_EP_FUNC_BAR_ADDR0(fn, bar), 0);
>> +	cdns_pcie_writel(pcie, CDNS_PCIE_AT_IB_EP_FUNC_BAR_ADDR1(fn, bar), 0);
>> +}
>> +
>> +static int cdns_pcie_ep_map_addr(struct pci_epc *epc, phys_addr_t addr,
>> +				 u64 pci_addr, size_t size)
>> +{
>> +	struct cdns_pcie_ep *ep = epc_get_drvdata(epc);
>> +	struct cdns_pcie *pcie = &ep->pcie;
>> +	u32 r;
>> +
>> +	r = find_first_zero_bit(&ep->ob_region_map, sizeof(ep->ob_region_map));
> 
> Second argument must be in bits not bytes.
> 
> https://marc.info/?l=linux-pci&m=151179781225513&w=2
>

OK, thanks!

>> +	if (r >= ep->data->max_regions - 1) {
>> +		dev_err(&epc->dev, "no free outbound region\n");
>> +		return -EINVAL;
>> +	}
>> +
>> +	cdns_pcie_set_outbound_region(pcie, r, false, addr, pci_addr, size);
>> +
>> +	set_bit(r, &ep->ob_region_map);
>> +	ep->ob_addr[r] = addr;
>> +
>> +	return 0;
>> +}
>> +
>> +static void cdns_pcie_ep_unmap_addr(struct pci_epc *epc, phys_addr_t addr)
>> +{
>> +	struct cdns_pcie_ep *ep = epc_get_drvdata(epc);
>> +	struct cdns_pcie *pcie = &ep->pcie;
>> +	u32 r;
>> +
>> +	for (r = 0; r < ep->data->max_regions - 1; r++)
>> +		if (ep->ob_addr[r] == addr)
>> +			break;
>> +
>> +	if (r >= ep->data->max_regions - 1)
> 
> == ?
>

I will change that.

>> +		return;
>> +
>> +	cdns_pcie_reset_outbound_region(pcie, r);
>> +
>> +	ep->ob_addr[r] = 0;
>> +	clear_bit(r, &ep->ob_region_map);
>> +}
>> +
>> +static int cdns_pcie_ep_set_msi(struct pci_epc *epc, u8 mmc)
>> +{
>> +	struct cdns_pcie_ep *ep = epc_get_drvdata(epc);
>> +	struct cdns_pcie *pcie = &ep->pcie;
>> +	u32 cap = CDNS_PCIE_EP_FUNC_MSI_CAP_OFFSET;
>> +	u16 flags;
>> +	u8 fn = 0;
>> +
>> +	/* Validate the ID of the MSI Capability structure. */
>> +	if (cdns_pcie_ep_fn_readb(pcie, fn, cap) != PCI_CAP_ID_MSI)
>> +		return -EINVAL;
>> +
>> +	/*
>> +	 * Set the Multiple Message Capable bitfield into the Message Control
>> +	 * register.
>> +	 */
>> +	flags = cdns_pcie_ep_fn_readw(pcie, fn, cap + PCI_MSI_FLAGS);
>> +	flags = (flags & ~PCI_MSI_FLAGS_QMASK) | (mmc << 1);
>> +	flags |= PCI_MSI_FLAGS_64BIT;
>> +	flags &= ~PCI_MSI_FLAGS_MASKBIT;
>> +	cdns_pcie_ep_fn_writew(pcie, fn, cap + PCI_MSI_FLAGS, flags);
>> +
>> +	return 0;
>> +}
>> +
>> +static int cdns_pcie_ep_get_msi(struct pci_epc *epc)
>> +{
>> +	struct cdns_pcie_ep *ep = epc_get_drvdata(epc);
>> +	struct cdns_pcie *pcie = &ep->pcie;
>> +	u32 cap = CDNS_PCIE_EP_FUNC_MSI_CAP_OFFSET;
>> +	u16 flags, mmc, mme;
>> +	u8 fn = 0;
>> +
>> +	/* Validate the ID of the MSI Capability structure. */
>> +	if (cdns_pcie_ep_fn_readb(pcie, fn, cap) != PCI_CAP_ID_MSI)
>> +		return -EINVAL;
>> +
>> +	/* Validate that the MSI feature is actually enabled. */
>> +	flags = cdns_pcie_ep_fn_readw(pcie, fn, cap + PCI_MSI_FLAGS);
>> +	if (!(flags & PCI_MSI_FLAGS_ENABLE))
>> +		return -EINVAL;
>> +
>> +	/*
>> +	 * Get the Multiple Message Enable bitfield from the Message Control
>> +	 * register.
>> +	 */
>> +	mmc = (flags & PCI_MSI_FLAGS_QMASK) >> 1;
>> +	mme = (flags & PCI_MSI_FLAGS_QSIZE) >> 4;
>> +	if (mme > mmc)
>> +		mme = mmc;
>> +	if (mme > 5)
>> +		mme = 5;
> 
> You should comment on what this 5 means and why it is fine to cap mme.
> 
>> +
>> +	return mme;
>> +}
>> +
>> +static void cdns_pcie_ep_assert_intx(struct cdns_pcie_ep *ep, u8 fn,
>> +				     u8 intx, bool is_asserted)
>> +{
>> +	struct cdns_pcie *pcie = &ep->pcie;
>> +	u32 r = ep->data->max_regions - 1;
>> +	u32 offset;
>> +	u16 status;
>> +	u8 msg_code;
>> +
>> +	intx &= 3;
>> +
>> +	/* Set the outbound region if needed. */
>> +	if (unlikely(ep->irq_pci_addr != CDNS_PCIE_EP_IRQ_PCI_ADDR_LEGACY)) {
>> +		/* Last region was reserved for IRQ writes. */
>> +		cdns_pcie_set_outbound_region_for_normal_msg(pcie, r,
>> +							     ep->irq_phys_addr);
>> +		ep->irq_pci_addr = CDNS_PCIE_EP_IRQ_PCI_ADDR_LEGACY;
>> +	}
>> +
>> +	if (is_asserted) {
>> +		ep->irq_pending |= BIT(intx);
>> +		msg_code = MSG_CODE_ASSERT_INTA + intx;
>> +	} else {
>> +		ep->irq_pending &= ~BIT(intx);
>> +		msg_code = MSG_CODE_DEASSERT_INTA + intx;
>> +	}
>> +
>> +	status = cdns_pcie_ep_fn_readw(pcie, fn, PCI_STATUS);
>> +	if (((status & PCI_STATUS_INTERRUPT) != 0) ^ (ep->irq_pending != 0)) {
>> +		status ^= PCI_STATUS_INTERRUPT;
>> +		cdns_pcie_ep_fn_writew(pcie, fn, PCI_STATUS, status);
>> +	}
>> +
>> +	offset = CDNS_PCIE_NORMAL_MSG_ROUTING(MSG_ROUTING_LOCAL) |
>> +		 CDNS_PCIE_NORMAL_MSG_CODE(msg_code) |
>> +		 CDNS_PCIE_MSG_NO_DATA;
>> +	writel(0, ep->irq_cpu_addr + offset);
>> +}
>> +
>> +static int cdns_pcie_ep_send_legacy_irq(struct cdns_pcie_ep *ep, u8 fn, u8 intx)
>> +{
>> +	u16 cmd;
>> +
>> +	cmd = cdns_pcie_ep_fn_readw(&ep->pcie, fn, PCI_COMMAND);
>> +	if (cmd & PCI_COMMAND_INTX_DISABLE)
>> +		return -EINVAL;
>> +
>> +	cdns_pcie_ep_assert_intx(ep, fn, intx, true);
>> +	mdelay(1);
> 
> Add a comment please to explain the mdelay value.
>

Honestly, I don't know for this value: I've just adapted the source code
from what was already done by dra7xx_pcie_raise_legacy_irq() from
drivers/pci/dwc/pci-dra7xx.c.

>> +	cdns_pcie_ep_assert_intx(ep, fn, intx, false);
>> +	return 0;
>> +}
>> +
>> +static int cdns_pcie_ep_raise_irq(struct pci_epc *epc,
>> +				  enum pci_epc_irq_type type, u8 interrupt_num)
>> +{
>> +	struct cdns_pcie_ep *ep = epc_get_drvdata(epc);
>> +	struct cdns_pcie *pcie = &ep->pcie;
>> +	u32 cap = CDNS_PCIE_EP_FUNC_MSI_CAP_OFFSET;
>> +	u16 flags, mmc, mme, data, data_mask;
>> +	u8 msi_count;
>> +	u64 pci_addr, pci_addr_mask = 0xff;
>> +	u8 fn = 0;
>> +
>> +	/* Handle legacy IRQ. */
>> +	if (type == PCI_EPC_IRQ_LEGACY)
>> +		return cdns_pcie_ep_send_legacy_irq(ep, fn, 0);
>> +
>> +	/* Otherwise MSI. */
>> +	if (type != PCI_EPC_IRQ_MSI)
>> +		return -EINVAL;
>> +
>> +	/* Check whether the MSI feature has been enabled by the PCI host. */
>> +	flags = cdns_pcie_ep_fn_readw(pcie, fn, cap + PCI_MSI_FLAGS);
>> +	if (!(flags & PCI_MSI_FLAGS_ENABLE))
>> +		return -EINVAL;
>> +
>> +	/* Get the number of enabled MSIs */
>> +	mmc = (flags & PCI_MSI_FLAGS_QMASK) >> 1;
>> +	mme = (flags & PCI_MSI_FLAGS_QSIZE) >> 4;
>> +	if (mme > mmc)
>> +		mme = mmc;
>> +	if (mme > 5)
>> +		mme = 5;
> 
> Same comment as above.
> 
>> +	msi_count = 1 << mme;
>> +	if (!interrupt_num || interrupt_num > msi_count)
>> +		return -EINVAL;
>> +
>> +	/* Compute the data value to be written. */
>> +	data_mask = msi_count - 1;
>> +	data = cdns_pcie_ep_fn_readw(pcie, fn, cap + PCI_MSI_DATA_64);
>> +	data = (data & ~data_mask) | ((interrupt_num - 1) & data_mask);
>> +
>> +	/* Get the PCI address where to write the data into. */
>> +	pci_addr = cdns_pcie_ep_fn_readl(pcie, fn, cap + PCI_MSI_ADDRESS_HI);
>> +	pci_addr <<= 32;
>> +	pci_addr |= cdns_pcie_ep_fn_readl(pcie, fn, cap + PCI_MSI_ADDRESS_LO);
>> +	pci_addr &= GENMASK_ULL(63, 2);
>> +
>> +	/* Set the outbound region if needed. */
>> +	if (unlikely(ep->irq_pci_addr != pci_addr)) {
>> +		/* Last region was reserved for IRQ writes. */
>> +		cdns_pcie_set_outbound_region(pcie, ep->data->max_regions - 1,
>> +					      false,
>> +					      ep->irq_phys_addr,
>> +					      pci_addr & ~pci_addr_mask,
>> +					      pci_addr_mask + 1);
>> +		ep->irq_pci_addr = pci_addr;
>> +	}
>> +	writew(data, ep->irq_cpu_addr + (pci_addr & pci_addr_mask));
>> +
>> +	return 0;
>> +}
>> +
>> +static int cdns_pcie_ep_start(struct pci_epc *epc)
>> +{
>> +	struct cdns_pcie_ep *ep = epc_get_drvdata(epc);
>> +	struct cdns_pcie *pcie = &ep->pcie;
>> +	struct pci_epf *epf;
>> +	u32 cfg;
>> +	u8 fn = 0;
>> +
>> +	/* Enable this endpoint function. */
>> +	cfg = cdns_pcie_readl(pcie, CDNS_PCIE_LM_EP_FUNC_CFG);
>> +	cfg |= BIT(fn);
>> +	cdns_pcie_writel(pcie, CDNS_PCIE_LM_EP_FUNC_CFG, cfg);
>> +
>> +	/*
>> +	 * Already linked-up: don't call directly pci_epc_linkup() because we've
>> +	 * already locked the epc->lock.
>> +	 */
>> +	list_for_each_entry(epf, &epc->pci_epf, list)
>> +		pci_epf_linkup(epf);
>> +
>> +	return 0;
>> +}
>> +
>> +static void cdns_pcie_ep_stop(struct pci_epc *epc)
>> +{
>> +	struct cdns_pcie_ep *ep = epc_get_drvdata(epc);
>> +	struct cdns_pcie *pcie = &ep->pcie;
>> +	u32 cfg;
>> +	u8 fn = 0;
>> +
>> +	/* Disable this endpoint function (function 0 can't be disabled). */
> 
> I do not understand this comment and how it applies to the code,
> in other words fn is always 0 here (so it can't be disabled)
> I do not understand what this code is there for.
>

fn == 0 for the same reason as for other 'struct pci_epc_ops' handlers.
I added this comment above because BIT(0) is hard-wired to 1: in other word
function 0 is the only one that can't be disabled.

So the current code actually does nothing. I've implemented anyway still
hoping that the API would change so function other than 0 could be used.

>> +	cfg = cdns_pcie_readl(pcie, CDNS_PCIE_LM_EP_FUNC_CFG);
>> +	cfg &= ~BIT(fn);
>> +	cdns_pcie_writel(pcie, CDNS_PCIE_LM_EP_FUNC_CFG, cfg);
>> +}
>> +
>> +static const struct pci_epc_ops cdns_pcie_epc_ops = {
>> +	.write_header	= cdns_pcie_ep_write_header,
>> +	.set_bar	= cdns_pcie_ep_set_bar,
>> +	.clear_bar	= cdns_pcie_ep_clear_bar,
>> +	.map_addr	= cdns_pcie_ep_map_addr,
>> +	.unmap_addr	= cdns_pcie_ep_unmap_addr,
>> +	.set_msi	= cdns_pcie_ep_set_msi,
>> +	.get_msi	= cdns_pcie_ep_get_msi,
>> +	.raise_irq	= cdns_pcie_ep_raise_irq,
>> +	.start		= cdns_pcie_ep_start,
>> +	.stop		= cdns_pcie_ep_stop,
>> +};
>> +
>> +static const struct cdns_pcie_ep_data cdns_pcie_ep_data = {
>> +	.max_regions	= 16,
>> +};
> 
> As I mentioned in patch 3, should this be set-up with DT ?
>

OK, I'll move it as a DT property.

Best regards,

Cyrille

> Thanks,
> Lorenzo
> 
>> +
>> +static const struct of_device_id cdns_pcie_ep_of_match[] = {
>> +	{ .compatible = "cdns,cdns-pcie-ep",
>> +	  .data = &cdns_pcie_ep_data },
>> +
>> +	{ },
>> +};
>> +
>> +static int cdns_pcie_ep_probe(struct platform_device *pdev)
>> +{
>> +	struct device *dev = &pdev->dev;
>> +	struct device_node *np = dev->of_node;
>> +	const struct of_device_id *of_id;
>> +	struct cdns_pcie_ep *ep;
>> +	struct cdns_pcie *pcie;
>> +	struct pci_epc *epc;
>> +	struct resource *res;
>> +	size_t max_regions;
>> +	int ret;
>> +
>> +	ep = devm_kzalloc(dev, sizeof(*ep), GFP_KERNEL);
>> +	if (!ep)
>> +		return -ENOMEM;
>> +
>> +	platform_set_drvdata(pdev, ep);
>> +
>> +	pcie = &ep->pcie;
>> +	pcie->is_rc = false;
>> +
>> +	of_id = of_match_node(cdns_pcie_ep_of_match, np);
>> +	ep->data = (const struct cdns_pcie_ep_data *)of_id->data;
>> +
>> +	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "reg");
>> +	pcie->reg_base = devm_ioremap_resource(dev, res);
>> +	if (IS_ERR(pcie->reg_base)) {
>> +		dev_err(dev, "missing \"reg\"\n");
>> +		return PTR_ERR(pcie->reg_base);
>> +	}
>> +
>> +	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "mem");
>> +	if (!res) {
>> +		dev_err(dev, "missing \"mem\"\n");
>> +		return -EINVAL;
>> +	}
>> +	pcie->mem_res = res;
>> +
>> +	max_regions = ep->data->max_regions;
>> +	ep->ob_addr = devm_kzalloc(dev, max_regions * sizeof(*ep->ob_addr),
>> +				   GFP_KERNEL);
>> +	if (!ep->ob_addr)
>> +		return -ENOMEM;
>> +
>> +	pm_runtime_enable(dev);
>> +	ret = pm_runtime_get_sync(dev);
>> +	if (ret < 0) {
>> +		dev_err(dev, "pm_runtime_get_sync() failed\n");
>> +		goto err_get_sync;
>> +	}
>> +
>> +	/* Disable all but function 0 (anyway BIT(0) is hardwired to 1). */
>> +	cdns_pcie_writel(pcie, CDNS_PCIE_LM_EP_FUNC_CFG, BIT(0));
>> +
>> +	epc = devm_pci_epc_create(dev, &cdns_pcie_epc_ops);
>> +	if (IS_ERR(epc)) {
>> +		dev_err(dev, "failed to create epc device\n");
>> +		ret = PTR_ERR(epc);
>> +		goto err_init;
>> +	}
>> +
>> +	ep->epc = epc;
>> +	epc_set_drvdata(epc, ep);
>> +
>> +	ret = of_property_read_u8(np, "max-functions", &epc->max_functions);
>> +	if (ret < 0)
>> +		epc->max_functions = 1;
>> +
>> +	ret = pci_epc_mem_init(epc, pcie->mem_res->start,
>> +			       resource_size(pcie->mem_res));
>> +	if (ret < 0) {
>> +		dev_err(dev, "failed to initialize the memory space\n");
>> +		goto err_init;
>> +	}
>> +
>> +	ep->irq_cpu_addr = pci_epc_mem_alloc_addr(epc, &ep->irq_phys_addr,
>> +						  SZ_128K);
>> +	if (!ep->irq_cpu_addr) {
>> +		dev_err(dev, "failed to reserve memory space for MSI\n");
>> +		ret = -ENOMEM;
>> +		goto free_epc_mem;
>> +	}
>> +	ep->irq_pci_addr = CDNS_PCIE_EP_IRQ_PCI_ADDR_NONE;
>> +
>> +	return 0;
>> +
>> + free_epc_mem:
>> +	pci_epc_mem_exit(epc);
>> +
>> + err_init:
>> +	pm_runtime_put_sync(dev);
>> +
>> + err_get_sync:
>> +	pm_runtime_disable(dev);
>> +
>> +	return ret;
>> +}
>> +
>> +static struct platform_driver cdns_pcie_ep_driver = {
>> +	.driver = {
>> +		.name = "cdns-pcie-ep",
>> +		.of_match_table = cdns_pcie_ep_of_match,
>> +	},
>> +	.probe = cdns_pcie_ep_probe,
>> +};
>> +builtin_platform_driver(cdns_pcie_ep_driver);
>> -- 
>> 2.11.0
>>
> 


-- 
Cyrille Pitchen, Free Electrons
Embedded Linux and Kernel engineering
http://free-electrons.com

^ permalink raw reply

* [PATCH v4] usb: xhci: allow imod-interval to be configurable
From: Adam Wallis @ 2017-12-04 14:27 UTC (permalink / raw)
  To: Greg Kroah-Hartman, Rob Herring, Mark Rutland, Matthias Brugger,
	Mathias Nyman, Chunfeng Yun,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	linux-usb-u79uwXL29TY76Z2rM5mHXA,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-mediatek-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r
  Cc: timur-sgV2jX0FEOL9JmXXK+q4OQ

The xHCI driver currently has the IMOD set to 160, which
translates to an IMOD interval of 40,000ns (160 * 250)ns

Commit 0cbd4b34cda9 ("xhci: mediatek: support MTK xHCI host controller")
introduced a QUIRK for the MTK platform to adjust this interval to 20,
which translates to an IMOD interval of 5,000ns (20 * 250)ns. This is
due to the fact that the MTK controller IMOD interval is 8 times
as much as defined in xHCI spec.

Instead of adding more quirk bits for additional platforms, this patch
introduces the ability for vendors to set the IMOD_INTERVAL as is
optimal for their platform. By using device_property_read_u32() on
"imod-interval-ns", the IMOD INTERVAL can be specified in nano seconds.
If no interval is specified, the default of 40,000ns (IMOD=160) will be
used.

No bounds checking has been implemented due to the fact that a vendor
may have violated the spec and would need to specify a value outside of
the max 8,000 IRQs/second limit specified in the xHCI spec.

Tested-by: Chunfeng Yun <chunfeng.yun-NuS5LvNUpcJWk0Htik3J/w@public.gmane.org>
Signed-off-by: Adam Wallis <awallis-sgV2jX0FEOL9JmXXK+q4OQ@public.gmane.org>
---
changes from v3:
  * Changed imod-interval to imod-interval-ns [Rob Herring/Chunfeng]
  * Changed "modulation" to "moderation" throughout patch [Mathias]
changes from v2:
  * Added PCI default value [Mathias]
  * Removed xhci-mtk.h from xhci-plat.c [Chunfeng Yun]
  * Removed MTK quirk from xhci-plat and moved logic to xhci-mtk [Chunfeng]
  * Updated bindings Documentation to use proper units [Rob Herring]
  * Added imod-interval description and example to MTK binding documentation
changes from v1:
  * Removed device_property_read_u32() per suggestion from greg k-h
  * Used ER_IRQ_INTERVAL_MASK in place of (u16) cast

 Documentation/devicetree/bindings/usb/mediatek,mtk-xhci.txt | 2 ++
 Documentation/devicetree/bindings/usb/usb-xhci.txt          | 1 +
 drivers/usb/host/xhci-mtk.c                                 | 9 +++++++++
 drivers/usb/host/xhci-pci.c                                 | 3 +++
 drivers/usb/host/xhci-plat.c                                | 5 +++++
 drivers/usb/host/xhci.c                                     | 7 ++-----
 drivers/usb/host/xhci.h                                     | 2 ++
 7 files changed, 24 insertions(+), 5 deletions(-)

diff --git a/Documentation/devicetree/bindings/usb/mediatek,mtk-xhci.txt b/Documentation/devicetree/bindings/usb/mediatek,mtk-xhci.txt
index 3059596..9ff5602 100644
--- a/Documentation/devicetree/bindings/usb/mediatek,mtk-xhci.txt
+++ b/Documentation/devicetree/bindings/usb/mediatek,mtk-xhci.txt
@@ -46,6 +46,7 @@ Optional properties:
  - pinctrl-names : a pinctrl state named "default" must be defined
  - pinctrl-0 : pin control group
 	See: Documentation/devicetree/bindings/pinctrl/pinctrl-bindings.txt
+ - imod-interval-ns: default interrupt moderation interval is 5000ns
 
 Example:
 usb30: usb@11270000 {
@@ -66,6 +67,7 @@ usb30: usb@11270000 {
 	usb3-lpm-capable;
 	mediatek,syscon-wakeup = <&pericfg>;
 	mediatek,wakeup-src = <1>;
+	imod-interval-ns = <10000>;
 };
 
 2nd: dual-role mode with xHCI driver
diff --git a/Documentation/devicetree/bindings/usb/usb-xhci.txt b/Documentation/devicetree/bindings/usb/usb-xhci.txt
index ae6e484..969908d 100644
--- a/Documentation/devicetree/bindings/usb/usb-xhci.txt
+++ b/Documentation/devicetree/bindings/usb/usb-xhci.txt
@@ -29,6 +29,7 @@ Optional properties:
   - usb2-lpm-disable: indicate if we don't want to enable USB2 HW LPM
   - usb3-lpm-capable: determines if platform is USB3 LPM capable
   - quirk-broken-port-ped: set if the controller has broken port disable mechanism
+  - imod-interval-ns: default interrupt moderation interval is 5000ns
 
 Example:
 	usb@f0931000 {
diff --git a/drivers/usb/host/xhci-mtk.c b/drivers/usb/host/xhci-mtk.c
index b62a1d2..1cb2a8b 100644
--- a/drivers/usb/host/xhci-mtk.c
+++ b/drivers/usb/host/xhci-mtk.c
@@ -674,6 +674,15 @@ static int xhci_mtk_probe(struct platform_device *pdev)
 
 	xhci = hcd_to_xhci(hcd);
 	xhci->main_hcd = hcd;
+
+	/*
+	 * imod_interval is the interrupt moderation value in nanoseconds.
+	 * The increment interval is 8 times as much as that defined in
+	 * the xHCI spec on MTK's controller.
+	 */
+	xhci->imod_interval = 5000;
+	device_property_read_u32(dev, "imod-interval-ns", &xhci->imod_interval);
+
 	xhci->shared_hcd = usb_create_shared_hcd(driver, dev,
 			dev_name(dev), hcd);
 	if (!xhci->shared_hcd) {
diff --git a/drivers/usb/host/xhci-pci.c b/drivers/usb/host/xhci-pci.c
index 7ef1274..4bcddd4 100644
--- a/drivers/usb/host/xhci-pci.c
+++ b/drivers/usb/host/xhci-pci.c
@@ -234,6 +234,9 @@ static int xhci_pci_setup(struct usb_hcd *hcd)
 	if (!xhci->sbrn)
 		pci_read_config_byte(pdev, XHCI_SBRN_OFFSET, &xhci->sbrn);
 
+	/* imod_interval is the interrupt moderation value in nanoseconds. */
+	xhci->imod_interval = 40000;
+
 	retval = xhci_gen_setup(hcd, xhci_pci_quirks);
 	if (retval)
 		return retval;
diff --git a/drivers/usb/host/xhci-plat.c b/drivers/usb/host/xhci-plat.c
index 09f164f..6f03830 100644
--- a/drivers/usb/host/xhci-plat.c
+++ b/drivers/usb/host/xhci-plat.c
@@ -269,6 +269,11 @@ static int xhci_plat_probe(struct platform_device *pdev)
 	if (device_property_read_bool(&pdev->dev, "quirk-broken-port-ped"))
 		xhci->quirks |= XHCI_BROKEN_PORT_PED;
 
+	/* imod_interval is the interrupt moderation value in nanoseconds. */
+	xhci->imod_interval = 40000;
+	device_property_read_u32(sysdev, "imod-interval-ns",
+				 &xhci->imod_interval);
+
 	hcd->usb_phy = devm_usb_get_phy_by_phandle(sysdev, "usb-phy", 0);
 	if (IS_ERR(hcd->usb_phy)) {
 		ret = PTR_ERR(hcd->usb_phy);
diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c
index 2424d30..0b7755b 100644
--- a/drivers/usb/host/xhci.c
+++ b/drivers/usb/host/xhci.c
@@ -586,11 +586,8 @@ int xhci_run(struct usb_hcd *hcd)
 			"// Set the interrupt modulation register");
 	temp = readl(&xhci->ir_set->irq_control);
 	temp &= ~ER_IRQ_INTERVAL_MASK;
-	/*
-	 * the increment interval is 8 times as much as that defined
-	 * in xHCI spec on MTK's controller
-	 */
-	temp |= (u32) ((xhci->quirks & XHCI_MTK_HOST) ? 20 : 160);
+	temp |= (xhci->imod_interval / 250) & ER_IRQ_INTERVAL_MASK;
+
 	writel(temp, &xhci->ir_set->irq_control);
 
 	/* Set the HCD state before we enable the irqs */
diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h
index 99a014a..2a4177b 100644
--- a/drivers/usb/host/xhci.h
+++ b/drivers/usb/host/xhci.h
@@ -1717,6 +1717,8 @@ struct xhci_hcd {
 	u8		max_interrupters;
 	u8		max_ports;
 	u8		isoc_threshold;
+	/* imod_interval in ns (I * 250ns) */
+	u32		imod_interval;
 	int		event_ring_max;
 	/* 4KB min, 128MB max */
 	int		page_size;
-- 
Qualcomm Datacenter Technologies as an affiliate of Qualcomm Technologies, Inc.
Qualcomm Technologies, Inc. is a member of the
Code Aurora Forum, a Linux Foundation Collaborative Project.

--
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 8/8] ARM: dtsi: sun8i: a711: enable battery power supply subnode
From: Quentin Schulz @ 2017-12-04 14:12 UTC (permalink / raw)
  To: sre-DgEjT+Ai2ygdnm+yROfE0A, robh+dt-DgEjT+Ai2ygdnm+yROfE0A,
	mark.rutland-5wv7dgnIgG8, wens-jdAy2FN1RRM,
	linux-I+IVW8TIWO2tmTQ+vhA3Yw,
	maxime.ripard-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8,
	jic23-DgEjT+Ai2ygdnm+yROfE0A, lee.jones-QSEj5FYQhm4dnm+yROfE0A
  Cc: knaack.h-Mmb7MZpHnFY, lars-Qo5EllUWu/uELgA04lAiVw,
	pmeerw-jW+XmwGofnusTnJN9+BGXg, linux-pm-u79uwXL29TY76Z2rM5mHXA,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	linux-iio-u79uwXL29TY76Z2rM5mHXA, icenowy-h8G6r0blFSE,
	linux-sunxi-/JYPxA39Uh5TLH3MbocFFw,
	thomas.petazzoni-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8, Quentin Schulz
In-Reply-To: <cover.4052f8c517c42db26a3cabe078cad333243d371c.1512396054.git-series.quentin.schulz-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org>

The TBS A711 has an AXP813 PMIC and a soldered battery, thus, we enable
the battery power supply subnode in its Device Tree.

Signed-off-by: Quentin Schulz <quentin.schulz-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org>
---
 arch/arm/boot/dts/sun8i-a83t-tbs-a711.dts | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/arch/arm/boot/dts/sun8i-a83t-tbs-a711.dts b/arch/arm/boot/dts/sun8i-a83t-tbs-a711.dts
index 9871553..806cb17 100644
--- a/arch/arm/boot/dts/sun8i-a83t-tbs-a711.dts
+++ b/arch/arm/boot/dts/sun8i-a83t-tbs-a711.dts
@@ -181,6 +181,10 @@
 
 #include "axp81x.dtsi"
 
+&battery_power_supply {
+	status = "okay";
+};
+
 &reg_aldo1 {
 	regulator-min-microvolt = <1800000>;
 	regulator-max-microvolt = <1800000>;
-- 
git-series 0.9.1

^ permalink raw reply related

* [PATCH 7/8] ARM: dtsi: axp81x: add battery power supply subnode
From: Quentin Schulz @ 2017-12-04 14:12 UTC (permalink / raw)
  To: sre-DgEjT+Ai2ygdnm+yROfE0A, robh+dt-DgEjT+Ai2ygdnm+yROfE0A,
	mark.rutland-5wv7dgnIgG8, wens-jdAy2FN1RRM,
	linux-I+IVW8TIWO2tmTQ+vhA3Yw,
	maxime.ripard-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8,
	jic23-DgEjT+Ai2ygdnm+yROfE0A, lee.jones-QSEj5FYQhm4dnm+yROfE0A
  Cc: knaack.h-Mmb7MZpHnFY, lars-Qo5EllUWu/uELgA04lAiVw,
	pmeerw-jW+XmwGofnusTnJN9+BGXg, linux-pm-u79uwXL29TY76Z2rM5mHXA,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	linux-iio-u79uwXL29TY76Z2rM5mHXA, icenowy-h8G6r0blFSE,
	linux-sunxi-/JYPxA39Uh5TLH3MbocFFw,
	thomas.petazzoni-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8, Quentin Schulz
In-Reply-To: <cover.4052f8c517c42db26a3cabe078cad333243d371c.1512396054.git-series.quentin.schulz-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org>

The X-Powers AXP81X PMIC exposes battery supply various data such as
the battery status (charging, discharging, full, dead), current max
limit, current current, battery capacity (in percentage), voltage max
and min limits, current voltage, and battery capacity (in Ah).

This adds the battery power supply subnode for AXP81X PMIC.

Signed-off-by: Quentin Schulz <quentin.schulz-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org>
---
 arch/arm/boot/dts/axp81x.dtsi | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/arch/arm/boot/dts/axp81x.dtsi b/arch/arm/boot/dts/axp81x.dtsi
index 73b761f..f7401c3 100644
--- a/arch/arm/boot/dts/axp81x.dtsi
+++ b/arch/arm/boot/dts/axp81x.dtsi
@@ -48,6 +48,11 @@
 	interrupt-controller;
 	#interrupt-cells = <1>;
 
+	battery_power_supply: battery-power-supply {
+		compatible = "x-powers,axp813-battery-power-supply";
+		status = "disabled";
+	};
+
 	regulators {
 		/* Default work frequency for buck regulators */
 		x-powers,dcdc-freq = <3000>;
-- 
git-series 0.9.1

^ permalink raw reply related

* [PATCH 6/8] mfd: axp20x: add battery power supply cell for AXP813
From: Quentin Schulz @ 2017-12-04 14:12 UTC (permalink / raw)
  To: sre-DgEjT+Ai2ygdnm+yROfE0A, robh+dt-DgEjT+Ai2ygdnm+yROfE0A,
	mark.rutland-5wv7dgnIgG8, wens-jdAy2FN1RRM,
	linux-I+IVW8TIWO2tmTQ+vhA3Yw,
	maxime.ripard-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8,
	jic23-DgEjT+Ai2ygdnm+yROfE0A, lee.jones-QSEj5FYQhm4dnm+yROfE0A
  Cc: knaack.h-Mmb7MZpHnFY, lars-Qo5EllUWu/uELgA04lAiVw,
	pmeerw-jW+XmwGofnusTnJN9+BGXg, linux-pm-u79uwXL29TY76Z2rM5mHXA,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	linux-iio-u79uwXL29TY76Z2rM5mHXA, icenowy-h8G6r0blFSE,
	linux-sunxi-/JYPxA39Uh5TLH3MbocFFw,
	thomas.petazzoni-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8, Quentin Schulz
In-Reply-To: <cover.4052f8c517c42db26a3cabe078cad333243d371c.1512396054.git-series.quentin.schulz-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org>

As axp20x-battery-power-supply now supports AXP813, add a cell for it.

Signed-off-by: Quentin Schulz <quentin.schulz-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org>
---
 drivers/mfd/axp20x.c | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/drivers/mfd/axp20x.c b/drivers/mfd/axp20x.c
index 42e54d1..7566358 100644
--- a/drivers/mfd/axp20x.c
+++ b/drivers/mfd/axp20x.c
@@ -880,6 +880,9 @@ static struct mfd_cell axp813_cells[] = {
 		.name			= "axp20x-regulator",
 	}, {
 		.name			= "axp813-adc",
+	}, {
+		.name			= "axp20x-battery-power-supply",
+		.of_compatible		= "x-powers,axp813-battery-power-supply",
 	},
 };
 
-- 
git-series 0.9.1

^ permalink raw reply related

* [PATCH 5/8] power: supply: axp20x_battery: add support for AXP813
From: Quentin Schulz @ 2017-12-04 14:12 UTC (permalink / raw)
  To: sre-DgEjT+Ai2ygdnm+yROfE0A, robh+dt-DgEjT+Ai2ygdnm+yROfE0A,
	mark.rutland-5wv7dgnIgG8, wens-jdAy2FN1RRM,
	linux-I+IVW8TIWO2tmTQ+vhA3Yw,
	maxime.ripard-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8,
	jic23-DgEjT+Ai2ygdnm+yROfE0A, lee.jones-QSEj5FYQhm4dnm+yROfE0A
  Cc: knaack.h-Mmb7MZpHnFY, lars-Qo5EllUWu/uELgA04lAiVw,
	pmeerw-jW+XmwGofnusTnJN9+BGXg, linux-pm-u79uwXL29TY76Z2rM5mHXA,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	linux-iio-u79uwXL29TY76Z2rM5mHXA, icenowy-h8G6r0blFSE,
	linux-sunxi-/JYPxA39Uh5TLH3MbocFFw,
	thomas.petazzoni-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8, Quentin Schulz
In-Reply-To: <cover.4052f8c517c42db26a3cabe078cad333243d371c.1512396054.git-series.quentin.schulz-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org>

The X-Powers AXP813 PMIC has got some slight differences from
AXP20X/AXP22X PMICs:
 - the maximum voltage supplied by the PMIC is 4.35 instead of 4.36/4.24
 for AXP20X/AXP22X,
 - the constant charge current formula is different,

It also has a bit to tell whether the battery percentage returned by the
PMIC is valid.

Signed-off-by: Quentin Schulz <quentin.schulz-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org>
---
 drivers/power/supply/axp20x_battery.c | 44 +++++++++++++++++++++++++++-
 1 file changed, 43 insertions(+), 1 deletion(-)

diff --git a/drivers/power/supply/axp20x_battery.c b/drivers/power/supply/axp20x_battery.c
index 7494f0f..cb30302 100644
--- a/drivers/power/supply/axp20x_battery.c
+++ b/drivers/power/supply/axp20x_battery.c
@@ -46,6 +46,8 @@
 #define AXP20X_CHRG_CTRL1_TGT_4_2V	(2 << 5)
 #define AXP20X_CHRG_CTRL1_TGT_4_36V	(3 << 5)
 
+#define AXP813_CHRG_CTRL1_TGT_4_35V	(3 << 5)
+
 #define AXP22X_CHRG_CTRL1_TGT_4_22V	(1 << 5)
 #define AXP22X_CHRG_CTRL1_TGT_4_24V	(3 << 5)
 
@@ -123,10 +125,41 @@ static int axp22x_battery_get_max_voltage(struct axp20x_batt_ps *axp20x_batt,
 	return 0;
 }
 
+static int axp813_battery_get_max_voltage(struct axp20x_batt_ps *axp20x_batt,
+					  int *val)
+{
+	int ret, reg;
+
+	ret = regmap_read(axp20x_batt->regmap, AXP20X_CHRG_CTRL1, &reg);
+	if (ret)
+		return ret;
+
+	switch (reg & AXP20X_CHRG_CTRL1_TGT_VOLT) {
+	case AXP20X_CHRG_CTRL1_TGT_4_1V:
+		*val = 4100000;
+		break;
+	case AXP20X_CHRG_CTRL1_TGT_4_15V:
+		*val = 4150000;
+		break;
+	case AXP20X_CHRG_CTRL1_TGT_4_2V:
+		*val = 4200000;
+		break;
+	case AXP813_CHRG_CTRL1_TGT_4_35V:
+		*val = 4350000;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
 static void raw_to_constant_charge_current(struct axp20x_batt_ps *axp, int *val)
 {
 	if (axp->axp_id == AXP209_ID)
 		*val = *val * 100000 + 300000;
+	else if (axp->axp_id == AXP813_ID)
+		*val = *val * 200000 + 200000;
 	else
 		*val = *val * 150000 + 300000;
 }
@@ -135,6 +168,8 @@ static void constant_charge_current_to_raw(struct axp20x_batt_ps *axp, int *val)
 {
 	if (axp->axp_id == AXP209_ID)
 		*val = (*val - 300000) / 100000;
+	else if (axp->axp_id == AXP813_ID)
+		*val = (*val - 200000) / 200000;
 	else
 		*val = (*val - 300000) / 150000;
 }
@@ -269,7 +304,8 @@ static int axp20x_battery_get_prop(struct power_supply *psy,
 		if (ret)
 			return ret;
 
-		if (axp20x_batt->axp_id == AXP221_ID &&
+		if ((axp20x_batt->axp_id == AXP221_ID ||
+		     axp20x_batt->axp_id == AXP813_ID) &&
 		    !(reg & AXP22X_FG_VALID))
 			return -EINVAL;
 
@@ -284,6 +320,9 @@ static int axp20x_battery_get_prop(struct power_supply *psy,
 		if (axp20x_batt->axp_id == AXP209_ID)
 			return axp20x_battery_get_max_voltage(axp20x_batt,
 							      &val->intval);
+		else if (axp20x_batt->axp_id == AXP813_ID)
+			return axp813_battery_get_max_voltage(axp20x_batt,
+							      &val->intval);
 		return axp22x_battery_get_max_voltage(axp20x_batt,
 						      &val->intval);
 
@@ -467,6 +506,9 @@ static const struct of_device_id axp20x_battery_ps_id[] = {
 	}, {
 		.compatible = "x-powers,axp221-battery-power-supply",
 		.data = (void *)AXP221_ID,
+	}, {
+		.compatible = "x-powers,axp813-battery-power-supply",
+		.data = (void *)AXP813_ID,
 	}, { /* sentinel */ },
 };
 MODULE_DEVICE_TABLE(of, axp20x_battery_ps_id);
-- 
git-series 0.9.1

^ permalink raw reply related

* [PATCH 4/8] dt-bindings: power: supply: axp20x: add AXP813 battery DT binding
From: Quentin Schulz @ 2017-12-04 14:12 UTC (permalink / raw)
  To: sre-DgEjT+Ai2ygdnm+yROfE0A, robh+dt-DgEjT+Ai2ygdnm+yROfE0A,
	mark.rutland-5wv7dgnIgG8, wens-jdAy2FN1RRM,
	linux-I+IVW8TIWO2tmTQ+vhA3Yw,
	maxime.ripard-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8,
	jic23-DgEjT+Ai2ygdnm+yROfE0A, lee.jones-QSEj5FYQhm4dnm+yROfE0A
  Cc: knaack.h-Mmb7MZpHnFY, lars-Qo5EllUWu/uELgA04lAiVw,
	pmeerw-jW+XmwGofnusTnJN9+BGXg, linux-pm-u79uwXL29TY76Z2rM5mHXA,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	linux-iio-u79uwXL29TY76Z2rM5mHXA, icenowy-h8G6r0blFSE,
	linux-sunxi-/JYPxA39Uh5TLH3MbocFFw,
	thomas.petazzoni-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8, Quentin Schulz
In-Reply-To: <cover.4052f8c517c42db26a3cabe078cad333243d371c.1512396054.git-series.quentin.schulz-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org>

The AXP813 can have a battery as power supply, so let's add it to the
list of compatibles.

Signed-off-by: Quentin Schulz <quentin.schulz-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org>
---
 Documentation/devicetree/bindings/power/supply/axp20x_battery.txt | 8 +++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/Documentation/devicetree/bindings/power/supply/axp20x_battery.txt b/Documentation/devicetree/bindings/power/supply/axp20x_battery.txt
index c248866..4614c8e 100644
--- a/Documentation/devicetree/bindings/power/supply/axp20x_battery.txt
+++ b/Documentation/devicetree/bindings/power/supply/axp20x_battery.txt
@@ -4,12 +4,12 @@ Required Properties:
  - compatible, one of:
 			"x-powers,axp209-battery-power-supply"
 			"x-powers,axp221-battery-power-supply"
+			"x-powers,axp813-battery-power-supply"
 
-This node is a subnode of the axp20x/axp22x PMIC.
+This node is a subnode of the axp20x/axp22x/axp81x PMIC.
 
-The AXP20X and AXP22X can read the battery voltage, charge and discharge
-currents of the battery by reading ADC channels from the AXP20X/AXP22X
-ADC.
+The AXP20X, AXP22X and AXP81X can read the battery voltage, charge and
+discharge currents of the battery by reading ADC channels from the ADC.
 
 Example:
 
-- 
git-series 0.9.1

^ permalink raw reply related

* [PATCH 3/8] mfd: axp20x: probe axp20x_adc driver for AXP813
From: Quentin Schulz @ 2017-12-04 14:12 UTC (permalink / raw)
  To: sre-DgEjT+Ai2ygdnm+yROfE0A, robh+dt-DgEjT+Ai2ygdnm+yROfE0A,
	mark.rutland-5wv7dgnIgG8, wens-jdAy2FN1RRM,
	linux-I+IVW8TIWO2tmTQ+vhA3Yw,
	maxime.ripard-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8,
	jic23-DgEjT+Ai2ygdnm+yROfE0A, lee.jones-QSEj5FYQhm4dnm+yROfE0A
  Cc: knaack.h-Mmb7MZpHnFY, lars-Qo5EllUWu/uELgA04lAiVw,
	pmeerw-jW+XmwGofnusTnJN9+BGXg, linux-pm-u79uwXL29TY76Z2rM5mHXA,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	linux-iio-u79uwXL29TY76Z2rM5mHXA, icenowy-h8G6r0blFSE,
	linux-sunxi-/JYPxA39Uh5TLH3MbocFFw,
	thomas.petazzoni-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8, Quentin Schulz
In-Reply-To: <cover.4052f8c517c42db26a3cabe078cad333243d371c.1512396054.git-series.quentin.schulz-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org>

This makes the axp20x_adc driver probe with platform device id
"axp813-adc".

Signed-off-by: Quentin Schulz <quentin.schulz-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org>
---
 drivers/mfd/axp20x.c | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/drivers/mfd/axp20x.c b/drivers/mfd/axp20x.c
index 2468b43..42e54d1 100644
--- a/drivers/mfd/axp20x.c
+++ b/drivers/mfd/axp20x.c
@@ -878,7 +878,9 @@ static struct mfd_cell axp813_cells[] = {
 		.resources		= axp803_pek_resources,
 	}, {
 		.name			= "axp20x-regulator",
-	}
+	}, {
+		.name			= "axp813-adc",
+	},
 };
 
 static struct axp20x_dev *axp20x_pm_power_off;
-- 
git-series 0.9.1

^ permalink raw reply related

* [PATCH 2/8] iio: adc: axp20x_adc: add support for AXP813 ADC
From: Quentin Schulz @ 2017-12-04 14:12 UTC (permalink / raw)
  To: sre-DgEjT+Ai2ygdnm+yROfE0A, robh+dt-DgEjT+Ai2ygdnm+yROfE0A,
	mark.rutland-5wv7dgnIgG8, wens-jdAy2FN1RRM,
	linux-I+IVW8TIWO2tmTQ+vhA3Yw,
	maxime.ripard-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8,
	jic23-DgEjT+Ai2ygdnm+yROfE0A, lee.jones-QSEj5FYQhm4dnm+yROfE0A
  Cc: knaack.h-Mmb7MZpHnFY, lars-Qo5EllUWu/uELgA04lAiVw,
	pmeerw-jW+XmwGofnusTnJN9+BGXg, linux-pm-u79uwXL29TY76Z2rM5mHXA,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	linux-iio-u79uwXL29TY76Z2rM5mHXA, icenowy-h8G6r0blFSE,
	linux-sunxi-/JYPxA39Uh5TLH3MbocFFw,
	thomas.petazzoni-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8, Quentin Schulz
In-Reply-To: <cover.4052f8c517c42db26a3cabe078cad333243d371c.1512396054.git-series.quentin.schulz-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org>

The X-Powers AXP813 PMIC is really close to what is already done for
AXP20X/AXP22X.

There are two pairs of bits to set the rate (one for Voltage and Current
measurements and one for TS/GPIO0 voltage measurements) instead of one.

The register to set the ADC rates is different from the one for
AXP20X/AXP22X.

GPIO0 can be used as an ADC (measuring Volts) unlike for AXP22X.

The scales to apply to the different inputs are unlike the ones from
AXP20X and AXP22X.

Signed-off-by: Quentin Schulz <quentin.schulz-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org>
---
 drivers/iio/adc/axp20x_adc.c | 122 ++++++++++++++++++++++++++++++++++++-
 include/linux/mfd/axp20x.h   |   2 +-
 2 files changed, 124 insertions(+)

diff --git a/drivers/iio/adc/axp20x_adc.c b/drivers/iio/adc/axp20x_adc.c
index 7274f4f..03d489b 100644
--- a/drivers/iio/adc/axp20x_adc.c
+++ b/drivers/iio/adc/axp20x_adc.c
@@ -35,8 +35,13 @@
 #define AXP20X_GPIO10_IN_RANGE_GPIO1_VAL(x)	(((x) & BIT(0)) << 1)
 
 #define AXP20X_ADC_RATE_MASK			GENMASK(7, 6)
+#define AXP813_V_I_ADC_RATE_MASK		GENMASK(5, 4)
+#define AXP813_ADC_RATE_MASK			(AXP20X_ADC_RATE_MASK | AXP813_V_I_ADC_RATE_MASK)
 #define AXP20X_ADC_RATE_HZ(x)			((ilog2((x) / 25) << 6) & AXP20X_ADC_RATE_MASK)
 #define AXP22X_ADC_RATE_HZ(x)			((ilog2((x) / 100) << 6) & AXP20X_ADC_RATE_MASK)
+#define AXP813_TS_GPIO0_ADC_RATE_HZ(x)		AXP20X_ADC_RATE_HZ(x)
+#define AXP813_V_I_ADC_RATE_HZ(x)		((ilog2((x) / 100) << 4) & AXP813_V_I_ADC_RATE_MASK)
+#define AXP813_ADC_RATE_HZ(x)			(AXP20X_ADC_RATE_HZ(x) | AXP813_V_I_ADC_RATE_HZ(x))
 
 #define AXP20X_ADC_CHANNEL(_channel, _name, _type, _reg)	\
 	{							\
@@ -95,6 +100,12 @@ enum axp22x_adc_channel_i {
 	AXP22X_BATT_DISCHRG_I,
 };
 
+enum axp813_adc_channel_v {
+	AXP813_TS_IN = 0,
+	AXP813_GPIO0_V,
+	AXP813_BATT_V,
+};
+
 static struct iio_map axp20x_maps[] = {
 	{
 		.consumer_dev_name = "axp20x-usb-power-supply",
@@ -197,6 +208,25 @@ static const struct iio_chan_spec axp22x_adc_channels[] = {
 			   AXP20X_BATT_DISCHRG_I_H),
 };
 
+static const struct iio_chan_spec axp813_adc_channels[] = {
+	{
+		.type = IIO_TEMP,
+		.address = AXP22X_PMIC_TEMP_H,
+		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
+				      BIT(IIO_CHAN_INFO_SCALE) |
+				      BIT(IIO_CHAN_INFO_OFFSET),
+		.datasheet_name = "pmic_temp",
+	},
+	AXP20X_ADC_CHANNEL(AXP813_GPIO0_V, "gpio0_v", IIO_VOLTAGE,
+			   AXP288_GP_ADC_H),
+	AXP20X_ADC_CHANNEL(AXP813_BATT_V, "batt_v", IIO_VOLTAGE,
+			   AXP20X_BATT_V_H),
+	AXP20X_ADC_CHANNEL(AXP22X_BATT_CHRG_I, "batt_chrg_i", IIO_CURRENT,
+			   AXP20X_BATT_CHRG_I_H),
+	AXP20X_ADC_CHANNEL(AXP22X_BATT_DISCHRG_I, "batt_dischrg_i", IIO_CURRENT,
+			   AXP20X_BATT_DISCHRG_I_H),
+};
+
 static int axp20x_adc_raw(struct iio_dev *indio_dev,
 			  struct iio_chan_spec const *chan, int *val)
 {
@@ -243,6 +273,18 @@ static int axp22x_adc_raw(struct iio_dev *indio_dev,
 	return IIO_VAL_INT;
 }
 
+static int axp813_adc_raw(struct iio_dev *indio_dev,
+			  struct iio_chan_spec const *chan, int *val)
+{
+	struct axp20x_adc_iio *info = iio_priv(indio_dev);
+
+	*val = axp20x_read_variable_width(info->regmap, chan->address, 12);
+	if (*val < 0)
+		return *val;
+
+	return IIO_VAL_INT;
+}
+
 static int axp20x_adc_scale_voltage(int channel, int *val, int *val2)
 {
 	switch (channel) {
@@ -273,6 +315,24 @@ static int axp20x_adc_scale_voltage(int channel, int *val, int *val2)
 	}
 }
 
+static int axp813_adc_scale_voltage(int channel, int *val, int *val2)
+{
+	switch (channel) {
+	case AXP813_GPIO0_V:
+		*val = 0;
+		*val2 = 800000;
+		return IIO_VAL_INT_PLUS_MICRO;
+
+	case AXP813_BATT_V:
+		*val = 1;
+		*val2 = 100000;
+		return IIO_VAL_INT_PLUS_MICRO;
+
+	default:
+		return -EINVAL;
+	}
+}
+
 static int axp20x_adc_scale_current(int channel, int *val, int *val2)
 {
 	switch (channel) {
@@ -342,6 +402,26 @@ static int axp22x_adc_scale(struct iio_chan_spec const *chan, int *val,
 	}
 }
 
+static int axp813_adc_scale(struct iio_chan_spec const *chan, int *val,
+			    int *val2)
+{
+	switch (chan->type) {
+	case IIO_VOLTAGE:
+		return axp813_adc_scale_voltage(chan->channel, val, val2);
+
+	case IIO_CURRENT:
+		*val = 1;
+		return IIO_VAL_INT;
+
+	case IIO_TEMP:
+		*val = 100;
+		return IIO_VAL_INT;
+
+	default:
+		return -EINVAL;
+	}
+}
+
 static int axp20x_adc_offset_voltage(struct iio_dev *indio_dev, int channel,
 				     int *val)
 {
@@ -425,6 +505,26 @@ static int axp22x_read_raw(struct iio_dev *indio_dev,
 	}
 }
 
+static int axp813_read_raw(struct iio_dev *indio_dev,
+			   struct iio_chan_spec const *chan, int *val,
+			   int *val2, long mask)
+{
+	switch (mask) {
+	case IIO_CHAN_INFO_OFFSET:
+		*val = -2667;
+		return IIO_VAL_INT;
+
+	case IIO_CHAN_INFO_SCALE:
+		return axp813_adc_scale(chan, val, val2);
+
+	case IIO_CHAN_INFO_RAW:
+		return axp813_adc_raw(indio_dev, chan, val);
+
+	default:
+		return -EINVAL;
+	}
+}
+
 static int axp20x_write_raw(struct iio_dev *indio_dev,
 			    struct iio_chan_spec const *chan, int val, int val2,
 			    long mask)
@@ -470,6 +570,10 @@ static const struct iio_info axp22x_adc_iio_info = {
 	.read_raw = axp22x_read_raw,
 };
 
+static const struct iio_info axp813_adc_iio_info = {
+	.read_raw = axp813_read_raw,
+};
+
 static int axp20x_adc_rate(struct axp20x_adc_iio *info, int rate)
 {
 	return regmap_update_bits(info->regmap, AXP20X_ADC_RATE,
@@ -484,6 +588,13 @@ static int axp22x_adc_rate(struct axp20x_adc_iio *info, int rate)
 				  AXP22X_ADC_RATE_HZ(rate));
 }
 
+static int axp813_adc_rate(struct axp20x_adc_iio *info, int rate)
+{
+	return regmap_update_bits(info->regmap, AXP813_ADC_RATE,
+				 AXP813_ADC_RATE_MASK,
+				 AXP813_ADC_RATE_HZ(rate));
+}
+
 struct axp_data {
 	const struct iio_info		*iio_info;
 	int				num_channels;
@@ -514,9 +625,20 @@ static const struct axp_data axp22x_data = {
 	.maps = axp22x_maps,
 };
 
+static const struct axp_data axp813_data = {
+	.iio_info = &axp813_adc_iio_info,
+	.num_channels = ARRAY_SIZE(axp813_adc_channels),
+	.channels = axp813_adc_channels,
+	.adc_en1_mask = AXP22X_ADC_EN1_MASK,
+	.adc_rate = axp813_adc_rate,
+	.adc_en2 = false,
+	.maps = axp22x_maps,
+};
+
 static const struct platform_device_id axp20x_adc_id_match[] = {
 	{ .name = "axp20x-adc", .driver_data = (kernel_ulong_t)&axp20x_data, },
 	{ .name = "axp22x-adc", .driver_data = (kernel_ulong_t)&axp22x_data, },
+	{ .name = "axp813-adc", .driver_data = (kernel_ulong_t)&axp813_data, },
 	{ /* sentinel */ },
 };
 MODULE_DEVICE_TABLE(platform, axp20x_adc_id_match);
diff --git a/include/linux/mfd/axp20x.h b/include/linux/mfd/axp20x.h
index 78dc853..ff95414 100644
--- a/include/linux/mfd/axp20x.h
+++ b/include/linux/mfd/axp20x.h
@@ -266,6 +266,8 @@ enum axp20x_variants {
 #define AXP288_RT_BATT_V_H		0xa0
 #define AXP288_RT_BATT_V_L		0xa1
 
+#define AXP813_ADC_RATE			0x85
+
 /* Fuel Gauge */
 #define AXP288_FG_RDC1_REG          0xba
 #define AXP288_FG_RDC0_REG          0xbb
-- 
git-series 0.9.1

^ permalink raw reply related

* [PATCH 1/8] iio: adc: axp20x_adc: put ADC rate setting in a per-variant function
From: Quentin Schulz @ 2017-12-04 14:12 UTC (permalink / raw)
  To: sre-DgEjT+Ai2ygdnm+yROfE0A, robh+dt-DgEjT+Ai2ygdnm+yROfE0A,
	mark.rutland-5wv7dgnIgG8, wens-jdAy2FN1RRM,
	linux-I+IVW8TIWO2tmTQ+vhA3Yw,
	maxime.ripard-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8,
	jic23-DgEjT+Ai2ygdnm+yROfE0A, lee.jones-QSEj5FYQhm4dnm+yROfE0A
  Cc: knaack.h-Mmb7MZpHnFY, lars-Qo5EllUWu/uELgA04lAiVw,
	pmeerw-jW+XmwGofnusTnJN9+BGXg, linux-pm-u79uwXL29TY76Z2rM5mHXA,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	linux-iio-u79uwXL29TY76Z2rM5mHXA, icenowy-h8G6r0blFSE,
	linux-sunxi-/JYPxA39Uh5TLH3MbocFFw,
	thomas.petazzoni-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8, Quentin Schulz
In-Reply-To: <cover.4052f8c517c42db26a3cabe078cad333243d371c.1512396054.git-series.quentin.schulz-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org>

To prepare for a new comer that set a different register with different
values, move rate setting in a function that is specific to each AXP
variant.

Signed-off-by: Quentin Schulz <quentin.schulz-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org>
---
 drivers/iio/adc/axp20x_adc.c | 17 ++++++++++-------
 1 file changed, 10 insertions(+), 7 deletions(-)

diff --git a/drivers/iio/adc/axp20x_adc.c b/drivers/iio/adc/axp20x_adc.c
index a30a972..7274f4f 100644
--- a/drivers/iio/adc/axp20x_adc.c
+++ b/drivers/iio/adc/axp20x_adc.c
@@ -470,14 +470,18 @@ static const struct iio_info axp22x_adc_iio_info = {
 	.read_raw = axp22x_read_raw,
 };
 
-static int axp20x_adc_rate(int rate)
+static int axp20x_adc_rate(struct axp20x_adc_iio *info, int rate)
 {
-	return AXP20X_ADC_RATE_HZ(rate);
+	return regmap_update_bits(info->regmap, AXP20X_ADC_RATE,
+				  AXP20X_ADC_RATE_MASK,
+				  AXP20X_ADC_RATE_HZ(rate));
 }
 
-static int axp22x_adc_rate(int rate)
+static int axp22x_adc_rate(struct axp20x_adc_iio *info, int rate)
 {
-	return AXP22X_ADC_RATE_HZ(rate);
+	return regmap_update_bits(info->regmap, AXP20X_ADC_RATE,
+				  AXP20X_ADC_RATE_MASK,
+				  AXP22X_ADC_RATE_HZ(rate));
 }
 
 struct axp_data {
@@ -485,7 +489,7 @@ struct axp_data {
 	int				num_channels;
 	struct iio_chan_spec const	*channels;
 	unsigned long			adc_en1_mask;
-	int				(*adc_rate)(int rate);
+	int				(*adc_rate)(struct axp20x_adc_iio *info, int rate);
 	bool				adc_en2;
 	struct iio_map			*maps;
 };
@@ -554,8 +558,7 @@ static int axp20x_probe(struct platform_device *pdev)
 				   AXP20X_ADC_EN2_MASK, AXP20X_ADC_EN2_MASK);
 
 	/* Configure ADCs rate */
-	regmap_update_bits(info->regmap, AXP20X_ADC_RATE, AXP20X_ADC_RATE_MASK,
-			   info->data->adc_rate(100));
+	info->data->adc_rate(info, 100);
 
 	ret = iio_map_array_register(indio_dev, info->data->maps);
 	if (ret < 0) {
-- 
git-series 0.9.1

^ permalink raw reply related

* [PATCH 0/8] add support for AXP813 ADC and battery power supply
From: Quentin Schulz @ 2017-12-04 14:12 UTC (permalink / raw)
  To: sre-DgEjT+Ai2ygdnm+yROfE0A, robh+dt-DgEjT+Ai2ygdnm+yROfE0A,
	mark.rutland-5wv7dgnIgG8, wens-jdAy2FN1RRM,
	linux-I+IVW8TIWO2tmTQ+vhA3Yw,
	maxime.ripard-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8,
	jic23-DgEjT+Ai2ygdnm+yROfE0A, lee.jones-QSEj5FYQhm4dnm+yROfE0A
  Cc: knaack.h-Mmb7MZpHnFY, lars-Qo5EllUWu/uELgA04lAiVw,
	pmeerw-jW+XmwGofnusTnJN9+BGXg, linux-pm-u79uwXL29TY76Z2rM5mHXA,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	linux-iio-u79uwXL29TY76Z2rM5mHXA, icenowy-h8G6r0blFSE,
	linux-sunxi-/JYPxA39Uh5TLH3MbocFFw,
	thomas.petazzoni-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8, Quentin Schulz

The AXP813 PMIC is relatively close to the already supported AXP20X and
AXP22X. It provides three different power outputs: battery, AC and USB, and
measures a few different things: temperature, power supply status, current
current and voltage supplied, maximum current limit, battery capacity, min
and max voltage limits.

One of its two GPIOs can be used as an ADC.

There are a few differences with AXP20X/AXP22X PMICs though:
  - a different constant charge current formula,
  - battery temperature, GPIO0 and battery voltages are the only voltages
  measurable,
  - all data are stored on 12 bits (AXP20X/AXP22X had one type of data that
  was stored on 13 bits),
  - different scales and offsets,
  - a different ADC rate formula and register,

This patch series adds support for the PMIC's ADC and battery power supply
in the existing drivers.

Make the axp20x MFD automatically probe the ADC driver, add the battery
power supply node in axp81x node and enable it for the TBS A711 since it
has a soldered battery.

Q: The BananaPi M3 has two solder balls for battery, should the battery
power supply node be enabled for this board as well?

Thanks,
Quentin

Quentin Schulz (8):
  iio: adc: axp20x_adc: put ADC rate setting in a per-variant function
  iio: adc: axp20x_adc: add support for AXP813 ADC
  mfd: axp20x: probe axp20x_adc driver for AXP813
  dt-bindings: power: supply: axp20x: add AXP813 battery DT binding
  power: supply: axp20x_battery: add support for AXP813
  mfd: axp20x: add battery power supply cell for AXP813
  ARM: dtsi: axp81x: add battery power supply subnode
  ARM: dtsi: sun8i: a711: enable battery power supply subnode

 Documentation/devicetree/bindings/power/supply/axp20x_battery.txt |   8 ++--
 arch/arm/boot/dts/axp81x.dtsi                                     |   5 +++-
 arch/arm/boot/dts/sun8i-a83t-tbs-a711.dts                         |   4 ++-
 drivers/iio/adc/axp20x_adc.c                                      | 139 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----
 drivers/mfd/axp20x.c                                              |   7 +++-
 drivers/power/supply/axp20x_battery.c                             |  44 ++++++++++++++++++++++-
 include/linux/mfd/axp20x.h                                        |   2 +-
 7 files changed, 196 insertions(+), 13 deletions(-)

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

^ permalink raw reply

* Re: [PATCH v4 3/5] staging: Introduce NVIDIA Tegra video decoder driver
From: Hans Verkuil @ 2017-12-04 14:04 UTC (permalink / raw)
  To: Dmitry Osipenko, Thierry Reding, Jonathan Hunter,
	Greg Kroah-Hartman, Rob Herring, Mauro Carvalho Chehab,
	Stephen Warren, Vladimir Zapolskiy
  Cc: devel, devicetree, linux-kernel, linux-tegra, Dan Carpenter,
	linux-media
In-Reply-To: <1a3798f337c0097e67d70226ae3ba665fd9156c2.1508448293.git.digetx@gmail.com>

Hi Dmitry,

As you already mention in the TODO, this should become a v4l2 codec driver.

Good existing examples are the coda, qcom/venus and mtk-vcodec drivers.

One thing that is not clear from this code is if the tegra hardware is a
stateful or stateless codec, i.e. does it keep track of the decoder state
in the hardware, or does the application have to keep track of the state and
provide the state information together with the video data?

I ask because at the moment only stateful codecs are supported. Work is ongoing
to support stateless codecs, but we don't support that for now.

Anyway, I'm OK with merging this in staging. Although I think it should go
to staging/media since we want to keep track of it.

Regards,

	Hans

On 10/19/2017 11:34 PM, Dmitry Osipenko wrote:
> NVIDIA Tegra20/30/114/124/132 SoC's have video decoder engine that
> supports standard set of video formats like H.264 / MPEG-4 / WMV / VC1.
> Currently implemented decoding of CAVLC H.264 on Tegra20 only.
> 
> Signed-off-by: Dmitry Osipenko <digetx@gmail.com>
> ---
>  drivers/staging/Kconfig            |    2 +
>  drivers/staging/Makefile           |    1 +
>  drivers/staging/tegra-vde/Kconfig  |    7 +
>  drivers/staging/tegra-vde/Makefile |    1 +
>  drivers/staging/tegra-vde/TODO     |    5 +
>  drivers/staging/tegra-vde/uapi.h   |  101 +++
>  drivers/staging/tegra-vde/vde.c    | 1209 ++++++++++++++++++++++++++++++++++++
>  7 files changed, 1326 insertions(+)
>  create mode 100644 drivers/staging/tegra-vde/Kconfig
>  create mode 100644 drivers/staging/tegra-vde/Makefile
>  create mode 100644 drivers/staging/tegra-vde/TODO
>  create mode 100644 drivers/staging/tegra-vde/uapi.h
>  create mode 100644 drivers/staging/tegra-vde/vde.c
> 
> diff --git a/drivers/staging/Kconfig b/drivers/staging/Kconfig
> index 554683912cff..10c982811093 100644
> --- a/drivers/staging/Kconfig
> +++ b/drivers/staging/Kconfig
> @@ -118,4 +118,6 @@ source "drivers/staging/vboxvideo/Kconfig"
>  
>  source "drivers/staging/pi433/Kconfig"
>  
> +source "drivers/staging/tegra-vde/Kconfig"
> +
>  endif # STAGING
> diff --git a/drivers/staging/Makefile b/drivers/staging/Makefile
> index 8951c37d8d80..c5ef39767f22 100644
> --- a/drivers/staging/Makefile
> +++ b/drivers/staging/Makefile
> @@ -49,3 +49,4 @@ obj-$(CONFIG_BCM2835_VCHIQ)	+= vc04_services/
>  obj-$(CONFIG_CRYPTO_DEV_CCREE)	+= ccree/
>  obj-$(CONFIG_DRM_VBOXVIDEO)	+= vboxvideo/
>  obj-$(CONFIG_PI433)		+= pi433/
> +obj-$(CONFIG_TEGRA_VDE)		+= tegra-vde/
> diff --git a/drivers/staging/tegra-vde/Kconfig b/drivers/staging/tegra-vde/Kconfig
> new file mode 100644
> index 000000000000..ec3ddddebdaa
> --- /dev/null
> +++ b/drivers/staging/tegra-vde/Kconfig
> @@ -0,0 +1,7 @@
> +config TEGRA_VDE
> +	tristate "NVIDIA Tegra Video Decoder Engine driver"
> +	depends on ARCH_TEGRA || COMPILE_TEST
> +	select SRAM
> +	help
> +	    Say Y here to enable support for the NVIDIA Tegra video decoder
> +	    driver.
> diff --git a/drivers/staging/tegra-vde/Makefile b/drivers/staging/tegra-vde/Makefile
> new file mode 100644
> index 000000000000..e7c0df1174bf
> --- /dev/null
> +++ b/drivers/staging/tegra-vde/Makefile
> @@ -0,0 +1 @@
> +obj-$(CONFIG_TEGRA_VDE)	+= vde.o
> diff --git a/drivers/staging/tegra-vde/TODO b/drivers/staging/tegra-vde/TODO
> new file mode 100644
> index 000000000000..e98bbc7b3c19
> --- /dev/null
> +++ b/drivers/staging/tegra-vde/TODO
> @@ -0,0 +1,5 @@
> +TODO:
> +	- Figure out how generic V4L2 API could be utilized by this driver,
> +	  implement it.
> +
> +Contact: Dmitry Osipenko <digetx@gmail.com>
> diff --git a/drivers/staging/tegra-vde/uapi.h b/drivers/staging/tegra-vde/uapi.h
> new file mode 100644
> index 000000000000..8502032b5ee2
> --- /dev/null
> +++ b/drivers/staging/tegra-vde/uapi.h
> @@ -0,0 +1,101 @@
> +/*
> + * Copyright (C) 2016-2017 Dmitry Osipenko <digetx@gmail.com>
> + * All Rights Reserved.
> + *
> + * Permission is hereby granted, free of charge, to any person obtaining a
> + * copy of this software and associated documentation files (the "Software"),
> + * to deal in the Software without restriction, including without limitation
> + * the rights to use, copy, modify, merge, publish, distribute, sublicense,
> + * and/or sell copies of the Software, and to permit persons to whom the
> + * Software is furnished to do so, subject to the following conditions:
> + *
> + * The above copyright notice and this permission notice (including the next
> + * paragraph) shall be included in all copies or substantial portions of the
> + * Software.
> + *
> + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
> + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
> + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
> + * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
> + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
> + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
> + * OTHER DEALINGS IN THE SOFTWARE.
> + */
> +
> +#ifndef _UAPI_TEGRA_VDE_H_
> +#define _UAPI_TEGRA_VDE_H_
> +
> +#include <linux/types.h>
> +#include <asm/ioctl.h>
> +
> +#if defined(__cplusplus)
> +extern "C" {
> +#endif
> +
> +#define FLAG_B_FRAME		(1 << 0)
> +#define FLAG_REFERENCE		(1 << 1)
> +
> +struct tegra_vde_h264_frame {
> +	__s32 y_fd;
> +	__s32 cb_fd;
> +	__s32 cr_fd;
> +	__s32 aux_fd;
> +	__u32 y_offset;
> +	__u32 cb_offset;
> +	__u32 cr_offset;
> +	__u32 aux_offset;
> +	__u32 frame_num;
> +	__u32 flags;
> +
> +	__u32 reserved;
> +} __attribute__((packed));
> +
> +struct tegra_vde_h264_decoder_ctx {
> +	__s32 bitstream_data_fd;
> +	__u32 bitstream_data_offset;
> +
> +	__u64 dpb_frames_ptr;
> +	__u8  dpb_frames_nb;
> +	__u8  dpb_ref_frames_with_earlier_poc_nb;
> +
> +	// SPS
> +	__u8  baseline_profile;
> +	__u8  level_idc;
> +	__u8  log2_max_pic_order_cnt_lsb;
> +	__u8  log2_max_frame_num;
> +	__u8  pic_order_cnt_type;
> +	__u8  direct_8x8_inference_flag;
> +	__u8  pic_width_in_mbs;
> +	__u8  pic_height_in_mbs;
> +
> +	// PPS
> +	__u8  pic_init_qp;
> +	__u8  deblocking_filter_control_present_flag;
> +	__u8  constrained_intra_pred_flag;
> +	__u8  chroma_qp_index_offset;
> +	__u8  pic_order_present_flag;
> +
> +	// Slice header
> +	__u8  num_ref_idx_l0_active_minus1;
> +	__u8  num_ref_idx_l1_active_minus1;
> +
> +	__u32 reserved;
> +} __attribute__((packed));
> +
> +#define VDE_IOCTL_BASE			('v' + 0x20)
> +
> +#define VDE_IO(nr)			_IO(VDE_IOCTL_BASE, nr)
> +#define VDE_IOR(nr, type)		_IOR(VDE_IOCTL_BASE, nr, type)
> +#define VDE_IOW(nr, type)		_IOW(VDE_IOCTL_BASE, nr, type)
> +#define VDE_IOWR(nr, type)		_IOWR(VDE_IOCTL_BASE, nr, type)
> +
> +#define TEGRA_VDE_DECODE_H264		0x00
> +
> +#define TEGRA_VDE_IOCTL_DECODE_H264	\
> +	VDE_IOW(TEGRA_VDE_DECODE_H264, struct tegra_vde_h264_decoder_ctx)
> +
> +#if defined(__cplusplus)
> +}
> +#endif
> +
> +#endif // _UAPI_TEGRA_VDE_H_
> diff --git a/drivers/staging/tegra-vde/vde.c b/drivers/staging/tegra-vde/vde.c
> new file mode 100644
> index 000000000000..9dbe1d08757e
> --- /dev/null
> +++ b/drivers/staging/tegra-vde/vde.c
> @@ -0,0 +1,1209 @@
> +/*
> + * NVIDIA Tegra20 Video decoder driver
> + *
> + * Copyright (C) 2016-2017 Dmitry Osipenko <digetx@gmail.com>
> + *
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms of the GNU General Public License version 2 as published by
> + * the Free Software Foundation.
> + */
> +
> +#include <linux/clk.h>
> +#include <linux/dma-buf.h>
> +#include <linux/genalloc.h>
> +#include <linux/interrupt.h>
> +#include <linux/iopoll.h>
> +#include <linux/miscdevice.h>
> +#include <linux/module.h>
> +#include <linux/platform_device.h>
> +#include <linux/pm_runtime.h>
> +#include <linux/reset.h>
> +#include <linux/slab.h>
> +#include <linux/uaccess.h>
> +
> +#include <soc/tegra/pmc.h>
> +
> +#include "uapi.h"
> +
> +#define ICMDQUE_WR		0x00
> +#define CMDQUE_CONTROL		0x08
> +#define INTR_STATUS		0x18
> +#define BSE_INT_ENB		0x40
> +#define BSE_CONFIG		0x44
> +
> +#define BSE_ICMDQUE_EMPTY	BIT(3)
> +#define BSE_DMA_BUSY		BIT(23)
> +
> +#define VDE_WR(__data, __addr)				\
> +do {							\
> +	dev_dbg(vde->miscdev.parent,			\
> +		"%s: %d: 0x%08X => " #__addr ")\n",	\
> +		__func__, __LINE__, (u32)(__data));	\
> +	writel_relaxed(__data, __addr);			\
> +} while (0)
> +
> +struct video_frame {
> +	struct dma_buf_attachment *y_dmabuf_attachment;
> +	struct dma_buf_attachment *cb_dmabuf_attachment;
> +	struct dma_buf_attachment *cr_dmabuf_attachment;
> +	struct dma_buf_attachment *aux_dmabuf_attachment;
> +	struct sg_table *y_sgt;
> +	struct sg_table *cb_sgt;
> +	struct sg_table *cr_sgt;
> +	struct sg_table *aux_sgt;
> +	dma_addr_t y_addr;
> +	dma_addr_t cb_addr;
> +	dma_addr_t cr_addr;
> +	dma_addr_t aux_addr;
> +	u32 frame_num;
> +	u32 flags;
> +};
> +
> +struct tegra_vde {
> +	void __iomem *sxe;
> +	void __iomem *bsev;
> +	void __iomem *mbe;
> +	void __iomem *ppe;
> +	void __iomem *mce;
> +	void __iomem *tfe;
> +	void __iomem *ppb;
> +	void __iomem *vdma;
> +	void __iomem *frameid;
> +	struct mutex lock;
> +	struct miscdevice miscdev;
> +	struct reset_control *rst;
> +	struct gen_pool *iram_pool;
> +	struct completion decode_completion;
> +	struct clk *clk;
> +	dma_addr_t iram_lists_addr;
> +	u32 *iram;
> +};
> +
> +static void tegra_vde_set_bits(struct tegra_vde *vde,
> +			       u32 mask, void __iomem *regs)
> +{
> +	u32 value = readl_relaxed(regs);
> +
> +	VDE_WR(value | mask, regs);
> +}
> +
> +static int tegra_vde_wait_mbe(struct tegra_vde *vde)
> +{
> +	u32 tmp;
> +
> +	return readl_relaxed_poll_timeout(vde->mbe + 0x8C, tmp,
> +					  (tmp >= 0x10), 1, 100);
> +}
> +
> +static int tegra_vde_setup_mbe_frame_idx(struct tegra_vde *vde,
> +					 unsigned int refs_nb,
> +					 bool setup_refs)
> +{
> +	u32 frame_idx_enb_mask = 0;
> +	u32 value;
> +	unsigned int frame_idx;
> +	unsigned int idx;
> +	int err;
> +
> +	VDE_WR(0xD0000000 | (0 << 23), vde->mbe + 0x80);
> +	VDE_WR(0xD0200000 | (0 << 23), vde->mbe + 0x80);
> +
> +	err = tegra_vde_wait_mbe(vde);
> +	if (err)
> +		return err;
> +
> +	if (!setup_refs)
> +		return 0;
> +
> +	for (idx = 0, frame_idx = 1; idx < refs_nb; idx++, frame_idx++) {
> +		VDE_WR(0xD0000000 | (frame_idx << 23), vde->mbe + 0x80);
> +		VDE_WR(0xD0200000 | (frame_idx << 23), vde->mbe + 0x80);
> +
> +		frame_idx_enb_mask |= frame_idx << (6 * (idx % 4));
> +
> +		if (idx % 4 == 3 || idx == refs_nb - 1) {
> +			value = 0xC0000000;
> +			value |= (idx >> 2) << 24;
> +			value |= frame_idx_enb_mask;
> +
> +			VDE_WR(value, vde->mbe + 0x80);
> +
> +			err = tegra_vde_wait_mbe(vde);
> +			if (err)
> +				return err;
> +
> +			frame_idx_enb_mask = 0;
> +		}
> +	}
> +
> +	return 0;
> +}
> +
> +static void tegra_vde_mbe_set_0xa_reg(struct tegra_vde *vde, int reg, u32 val)
> +{
> +	VDE_WR(0xA0000000 | (reg << 24) | (val & 0xFFFF), vde->mbe + 0x80);
> +	VDE_WR(0xA0000000 | ((reg + 1) << 24) | (val >> 16), vde->mbe + 0x80);
> +}
> +
> +static int tegra_vde_wait_bsev(struct tegra_vde *vde, bool wait_dma)
> +{
> +	struct device *dev = vde->miscdev.parent;
> +	u32 value;
> +	int err;
> +
> +	err = readl_relaxed_poll_timeout(vde->bsev + INTR_STATUS, value,
> +					 !(value & BIT(2)), 1, 100);
> +	if (err) {
> +		dev_err(dev, "BSEV unknown bit timeout\n");
> +		return err;
> +	}
> +
> +	err = readl_relaxed_poll_timeout(vde->bsev + INTR_STATUS, value,
> +					 (value & BSE_ICMDQUE_EMPTY), 1, 100);
> +	if (err) {
> +		dev_err(dev, "BSEV ICMDQUE flush timeout\n");
> +		return err;
> +	}
> +
> +	if (!wait_dma)
> +		return 0;
> +
> +	err = readl_relaxed_poll_timeout(vde->bsev + INTR_STATUS, value,
> +					 !(value & BSE_DMA_BUSY), 1, 100);
> +	if (err) {
> +		dev_err(dev, "BSEV DMA timeout\n");
> +		return err;
> +	}
> +
> +	return 0;
> +}
> +
> +static int tegra_vde_push_to_bsev_icmdqueue(struct tegra_vde *vde,
> +					    u32 value, bool wait_dma)
> +{
> +	VDE_WR(value, vde->bsev + ICMDQUE_WR);
> +
> +	return tegra_vde_wait_bsev(vde, wait_dma);
> +}
> +
> +static void tegra_vde_setup_frameid(struct tegra_vde *vde,
> +				    struct video_frame *frame,
> +				    unsigned int frameid,
> +				    u32 mbs_width, u32 mbs_height)
> +{
> +	u32 y_addr  = frame ? frame->y_addr  : 0xFCDEAD00;
> +	u32 cb_addr = frame ? frame->cb_addr : 0xFCDEAD00;
> +	u32 cr_addr = frame ? frame->cr_addr : 0xFCDEAD00;
> +	u32 value1 = frame ? ((mbs_width << 16) | mbs_height) : 0;
> +	u32 value2 = frame ? ((((mbs_width + 1) >> 1) << 6) | 1) : 0;
> +
> +	VDE_WR(y_addr  >> 8, vde->frameid + 0x000 + frameid * 4);
> +	VDE_WR(cb_addr >> 8, vde->frameid + 0x100 + frameid * 4);
> +	VDE_WR(cr_addr >> 8, vde->frameid + 0x180 + frameid * 4);
> +	VDE_WR(value1,       vde->frameid + 0x080 + frameid * 4);
> +	VDE_WR(value2,       vde->frameid + 0x280 + frameid * 4);
> +}
> +
> +static void tegra_setup_frameidx(struct tegra_vde *vde,
> +				 struct video_frame *frames,
> +				 unsigned int frames_nb,
> +				 u32 mbs_width, u32 mbs_height)
> +{
> +	unsigned int idx;
> +
> +	for (idx = 0; idx < frames_nb; idx++)
> +		tegra_vde_setup_frameid(vde, &frames[idx], idx,
> +					mbs_width, mbs_height);
> +	for (; idx < 17; idx++)
> +		tegra_vde_setup_frameid(vde, NULL, idx, 0, 0);
> +}
> +
> +static void tegra_vde_setup_iram_entry(u32 *iram_tables,
> +				       unsigned int table,
> +				       unsigned int row,
> +				       u32 value1, u32 value2)
> +{
> +	pr_debug("IRAM table %u: row %u: 0x%08X 0x%08X\n",
> +		 table, row, value1, value2);
> +
> +	iram_tables[0x20 * table + row * 2] = value1;
> +	iram_tables[0x20 * table + row * 2 + 1] = value2;
> +}
> +
> +static void tegra_vde_setup_iram_tables(u32 *iram_tables,
> +					struct video_frame *dpb_frames,
> +					unsigned int ref_frames_nb,
> +					unsigned int with_earlier_poc_nb)
> +{
> +	struct video_frame *frame;
> +	u32 value, aux_addr;
> +	int with_later_poc_nb;
> +	unsigned int i, k;
> +
> +	pr_debug("DPB: Frame 0: frame_num = %d\n", dpb_frames[0].frame_num);
> +
> +	pr_debug("REF L0:\n");
> +
> +	for (i = 0; i < 16; i++) {
> +		if (i < ref_frames_nb) {
> +			frame = &dpb_frames[i + 1];
> +
> +			aux_addr = frame->aux_addr;
> +
> +			value  = (i + 1) << 26;
> +			value |= !(frame->flags & FLAG_B_FRAME) << 25;
> +			value |= 1 << 24;
> +			value |= frame->frame_num;
> +
> +			pr_debug("\tFrame %d: frame_num = %d B_frame = %d\n",
> +				 i + 1, frame->frame_num,
> +				 (frame->flags & FLAG_B_FRAME));
> +		} else {
> +			aux_addr = 0xFADEAD00;
> +			value = 0;
> +		}
> +
> +		tegra_vde_setup_iram_entry(iram_tables, 0, i, value, aux_addr);
> +		tegra_vde_setup_iram_entry(iram_tables, 1, i, value, aux_addr);
> +		tegra_vde_setup_iram_entry(iram_tables, 2, i, value, aux_addr);
> +		tegra_vde_setup_iram_entry(iram_tables, 3, i, value, aux_addr);
> +	}
> +
> +	if (!(dpb_frames[0].flags & FLAG_B_FRAME))
> +		return;
> +
> +	if (with_earlier_poc_nb >= ref_frames_nb)
> +		return;
> +
> +	with_later_poc_nb = ref_frames_nb - with_earlier_poc_nb;
> +
> +	pr_debug("REF L1: with_later_poc_nb %d with_earlier_poc_nb %d\n",
> +		 with_later_poc_nb, with_earlier_poc_nb);
> +
> +	for (i = 0, k = with_earlier_poc_nb; i < with_later_poc_nb; i++, k++) {
> +		frame = &dpb_frames[k + 1];
> +
> +		aux_addr = frame->aux_addr;
> +
> +		value  = (k + 1) << 26;
> +		value |= !(frame->flags & FLAG_B_FRAME) << 25;
> +		value |= 1 << 24;
> +		value |= frame->frame_num;
> +
> +		pr_debug("\tFrame %d: frame_num = %d\n",
> +			 k + 1, frame->frame_num);
> +
> +		tegra_vde_setup_iram_entry(iram_tables, 2, i, value, aux_addr);
> +	}
> +
> +	for (k = 0; i < ref_frames_nb; i++, k++) {
> +		frame = &dpb_frames[k + 1];
> +
> +		aux_addr = frame->aux_addr;
> +
> +		value  = (k + 1) << 26;
> +		value |= !(frame->flags & FLAG_B_FRAME) << 25;
> +		value |= 1 << 24;
> +		value |= frame->frame_num;
> +
> +		pr_debug("\tFrame %d: frame_num = %d\n",
> +			 k + 1, frame->frame_num);
> +
> +		tegra_vde_setup_iram_entry(iram_tables, 2, i, value, aux_addr);
> +	}
> +}
> +
> +static int tegra_vde_setup_hw_context(struct tegra_vde *vde,
> +				      struct tegra_vde_h264_decoder_ctx *ctx,
> +				      struct video_frame *dpb_frames,
> +				      dma_addr_t bitstream_data_addr,
> +				      size_t bitstream_data_size,
> +				      unsigned int macroblocks_nb)
> +{
> +	struct device *dev = vde->miscdev.parent;
> +	u32 value;
> +	int err;
> +
> +	tegra_vde_set_bits(vde, 0x000A, vde->sxe + 0xF0);
> +	tegra_vde_set_bits(vde, 0x000B, vde->bsev + CMDQUE_CONTROL);
> +	tegra_vde_set_bits(vde, 0x8002, vde->mbe + 0x50);
> +	tegra_vde_set_bits(vde, 0x000A, vde->mbe + 0xA0);
> +	tegra_vde_set_bits(vde, 0x000A, vde->ppe + 0x14);
> +	tegra_vde_set_bits(vde, 0x000A, vde->ppe + 0x28);
> +	tegra_vde_set_bits(vde, 0x0A00, vde->mce + 0x08);
> +	tegra_vde_set_bits(vde, 0x000A, vde->tfe + 0x00);
> +	tegra_vde_set_bits(vde, 0x0005, vde->vdma + 0x04);
> +
> +	VDE_WR(0x00000000, vde->vdma + 0x1C);
> +	VDE_WR(0x00000000, vde->vdma + 0x00);
> +	VDE_WR(0x00000007, vde->vdma + 0x04);
> +	VDE_WR(0x00000007, vde->frameid + 0x200);
> +	VDE_WR(0x00000005, vde->tfe + 0x04);
> +	VDE_WR(0x00000000, vde->mbe + 0x84);
> +	VDE_WR(0x00000010, vde->sxe + 0x08);
> +	VDE_WR(0x00000150, vde->sxe + 0x54);
> +	VDE_WR(0x0000054C, vde->sxe + 0x58);
> +	VDE_WR(0x00000E34, vde->sxe + 0x5C);
> +	VDE_WR(0x063C063C, vde->mce + 0x10);
> +	VDE_WR(0x0003FC00, vde->bsev + INTR_STATUS);
> +	VDE_WR(0x0000150D, vde->bsev + BSE_CONFIG);
> +	VDE_WR(0x00000100, vde->bsev + BSE_INT_ENB);
> +	VDE_WR(0x00000000, vde->bsev + 0x98);
> +	VDE_WR(0x00000060, vde->bsev + 0x9C);
> +
> +	memset(vde->iram + 128, 0, macroblocks_nb / 2);
> +
> +	tegra_setup_frameidx(vde, dpb_frames, ctx->dpb_frames_nb,
> +			     ctx->pic_width_in_mbs, ctx->pic_height_in_mbs);
> +
> +	tegra_vde_setup_iram_tables(vde->iram, dpb_frames,
> +				    ctx->dpb_frames_nb - 1,
> +				    ctx->dpb_ref_frames_with_earlier_poc_nb);
> +	wmb();
> +
> +	VDE_WR(0x00000000, vde->bsev + 0x8C);
> +	VDE_WR(bitstream_data_addr + bitstream_data_size,
> +	       vde->bsev + 0x54);
> +
> +	value = ctx->pic_width_in_mbs << 11 | ctx->pic_height_in_mbs << 3;
> +
> +	VDE_WR(value, vde->bsev + 0x88);
> +
> +	err = tegra_vde_wait_bsev(vde, false);
> +	if (err)
> +		return err;
> +
> +	err = tegra_vde_push_to_bsev_icmdqueue(vde, 0x800003FC, false);
> +	if (err)
> +		return err;
> +
> +	value = 0x01500000;
> +	value |= ((vde->iram_lists_addr + 512) >> 2) & 0xFFFF;
> +
> +	err = tegra_vde_push_to_bsev_icmdqueue(vde, value, true);
> +	if (err)
> +		return err;
> +
> +	err = tegra_vde_push_to_bsev_icmdqueue(vde, 0x840F054C, false);
> +	if (err)
> +		return err;
> +
> +	err = tegra_vde_push_to_bsev_icmdqueue(vde, 0x80000080, false);
> +	if (err)
> +		return err;
> +
> +	value = 0x0E340000 | ((vde->iram_lists_addr >> 2) & 0xFFFF);
> +
> +	err = tegra_vde_push_to_bsev_icmdqueue(vde, value, true);
> +	if (err)
> +		return err;
> +
> +	value = 0x00800005;
> +	value |= ctx->pic_width_in_mbs << 11;
> +	value |= ctx->pic_height_in_mbs << 3;
> +
> +	VDE_WR(value, vde->sxe + 0x10);
> +
> +	value = !ctx->baseline_profile << 17;
> +	value |= ctx->level_idc << 13;
> +	value |= ctx->log2_max_pic_order_cnt_lsb << 7;
> +	value |= ctx->pic_order_cnt_type << 5;
> +	value |= ctx->log2_max_frame_num;
> +
> +	VDE_WR(value, vde->sxe + 0x40);
> +
> +	value = ctx->pic_init_qp << 25;
> +	value |= !!(ctx->deblocking_filter_control_present_flag) << 2;
> +	value |= !!ctx->pic_order_present_flag;
> +
> +	VDE_WR(value, vde->sxe + 0x44);
> +
> +	value = ctx->chroma_qp_index_offset;
> +	value |= ctx->num_ref_idx_l0_active_minus1 << 5;
> +	value |= ctx->num_ref_idx_l1_active_minus1 << 10;
> +	value |= !!ctx->constrained_intra_pred_flag << 15;
> +
> +	VDE_WR(value, vde->sxe + 0x48);
> +
> +	value = 0x0C000000;
> +	value |= !!(dpb_frames[0].flags & FLAG_B_FRAME) << 24;
> +
> +	VDE_WR(value, vde->sxe + 0x4C);
> +
> +	value = 0x03800000;
> +	value |= min_t(size_t, bitstream_data_size, SZ_1M);
> +
> +	VDE_WR(value, vde->sxe + 0x68);
> +
> +	VDE_WR(bitstream_data_addr, vde->sxe + 0x6C);
> +
> +	value = 0x10000005;
> +	value |= ctx->pic_width_in_mbs << 11;
> +	value |= ctx->pic_height_in_mbs << 3;
> +
> +	VDE_WR(value, vde->mbe + 0x80);
> +
> +	value = 0x26800000;
> +	value |= ctx->level_idc << 4;
> +	value |= !ctx->baseline_profile << 1;
> +	value |= !!ctx->direct_8x8_inference_flag;
> +
> +	VDE_WR(value, vde->mbe + 0x80);
> +
> +	VDE_WR(0xF4000001, vde->mbe + 0x80);
> +	VDE_WR(0x20000000, vde->mbe + 0x80);
> +	VDE_WR(0xF4000101, vde->mbe + 0x80);
> +
> +	value = 0x20000000;
> +	value |= ctx->chroma_qp_index_offset << 8;
> +
> +	VDE_WR(value, vde->mbe + 0x80);
> +
> +	err = tegra_vde_setup_mbe_frame_idx(vde,
> +					    ctx->dpb_frames_nb - 1,
> +					    ctx->pic_order_cnt_type == 0);
> +	if (err) {
> +		dev_err(dev, "MBE frames setup failed\n");
> +		return err;
> +	}
> +
> +	tegra_vde_mbe_set_0xa_reg(vde, 0, 0x000009FC);
> +	tegra_vde_mbe_set_0xa_reg(vde, 2, 0xF1DEAD00);
> +	tegra_vde_mbe_set_0xa_reg(vde, 4, 0xF2DEAD00);
> +	tegra_vde_mbe_set_0xa_reg(vde, 6, 0xF3DEAD00);
> +	tegra_vde_mbe_set_0xa_reg(vde, 8, dpb_frames[0].aux_addr);
> +
> +	value = 0xFC000000;
> +	value |= !!(dpb_frames[0].flags & FLAG_B_FRAME) << 2;
> +
> +	if (!ctx->baseline_profile)
> +		value |= !!(dpb_frames[0].flags & FLAG_REFERENCE) << 1;
> +
> +	VDE_WR(value, vde->mbe + 0x80);
> +
> +	err = tegra_vde_wait_mbe(vde);
> +	if (err) {
> +		dev_err(dev, "MBE programming failed\n");
> +		return err;
> +	}
> +
> +	return 0;
> +}
> +
> +static void tegra_vde_decode_frame(struct tegra_vde *vde,
> +				   unsigned int macroblocks_nb)
> +{
> +	reinit_completion(&vde->decode_completion);
> +
> +	VDE_WR(0x00000001, vde->bsev + 0x8C);
> +	VDE_WR(0x20000000 | (macroblocks_nb - 1), vde->sxe + 0x00);
> +}
> +
> +static void tegra_vde_detach_and_put_dmabuf(struct dma_buf_attachment *a,
> +					    struct sg_table *sgt,
> +					    enum dma_data_direction dma_dir)
> +{
> +	struct dma_buf *dmabuf = a->dmabuf;
> +
> +	dma_buf_unmap_attachment(a, sgt, dma_dir);
> +	dma_buf_detach(dmabuf, a);
> +	dma_buf_put(dmabuf);
> +}
> +
> +static int tegra_vde_attach_dmabuf(struct device *dev,
> +				   int fd,
> +				   unsigned long offset,
> +				   unsigned int min_size,
> +				   struct dma_buf_attachment **a,
> +				   dma_addr_t *addr,
> +				   struct sg_table **s,
> +				   size_t *size,
> +				   enum dma_data_direction dma_dir)
> +{
> +	struct dma_buf_attachment *attachment;
> +	struct dma_buf *dmabuf;
> +	struct sg_table *sgt;
> +	int err;
> +
> +	dmabuf = dma_buf_get(fd);
> +	if (IS_ERR(dmabuf)) {
> +		dev_err(dev, "Invalid dmabuf FD\n");
> +		return PTR_ERR(dmabuf);
> +	}
> +
> +	if ((u64)offset + min_size > dmabuf->size) {
> +		dev_err(dev, "Too small dmabuf size %zu @0x%lX, "
> +			     "should be at least %d\n",
> +			dmabuf->size, offset, min_size);
> +		return -EINVAL;
> +	}
> +
> +	attachment = dma_buf_attach(dmabuf, dev);
> +	if (IS_ERR(attachment)) {
> +		dev_err(dev, "Failed to attach dmabuf\n");
> +		err = PTR_ERR(attachment);
> +		goto err_put;
> +	}
> +
> +	sgt = dma_buf_map_attachment(attachment, dma_dir);
> +	if (IS_ERR(sgt)) {
> +		dev_err(dev, "Failed to get dmabufs sg_table\n");
> +		err = PTR_ERR(sgt);
> +		goto err_detach;
> +	}
> +
> +	if (sgt->nents != 1) {
> +		dev_err(dev, "Sparse DMA region is unsupported\n");
> +		err = -EINVAL;
> +		goto err_unmap;
> +	}
> +
> +	*addr = sg_dma_address(sgt->sgl) + offset;
> +	*a = attachment;
> +	*s = sgt;
> +
> +	if (size)
> +		*size = dmabuf->size - offset;
> +
> +	return 0;
> +
> +err_unmap:
> +	dma_buf_unmap_attachment(attachment, sgt, dma_dir);
> +err_detach:
> +	dma_buf_detach(dmabuf, attachment);
> +err_put:
> +	dma_buf_put(dmabuf);
> +
> +	return err;
> +}
> +
> +static int tegra_vde_attach_dmabufs_to_frame(struct device *dev,
> +					struct video_frame *frame,
> +					struct tegra_vde_h264_frame *source,
> +					enum dma_data_direction dma_dir,
> +					bool baseline_profile,
> +					size_t csize)
> +{
> +	int err;
> +
> +	err = tegra_vde_attach_dmabuf(dev, source->y_fd,
> +				      source->y_offset, csize * 4,
> +				      &frame->y_dmabuf_attachment,
> +				      &frame->y_addr,
> +				      &frame->y_sgt,
> +				      NULL, dma_dir);
> +	if (err)
> +		return err;
> +
> +	err = tegra_vde_attach_dmabuf(dev, source->cb_fd,
> +				      source->cb_offset, csize,
> +				      &frame->cb_dmabuf_attachment,
> +				      &frame->cb_addr,
> +				      &frame->cb_sgt,
> +				      NULL, dma_dir);
> +	if (err)
> +		goto err_release_y;
> +
> +	err = tegra_vde_attach_dmabuf(dev, source->cr_fd,
> +				      source->cr_offset, csize,
> +				      &frame->cr_dmabuf_attachment,
> +				      &frame->cr_addr,
> +				      &frame->cr_sgt,
> +				      NULL, dma_dir);
> +	if (err)
> +		goto err_release_cb;
> +
> +	if (baseline_profile) {
> +		frame->aux_addr = 0xF4DEAD00;
> +	} else {
> +		err = tegra_vde_attach_dmabuf(dev, source->aux_fd,
> +					      source->aux_offset, csize,
> +					      &frame->aux_dmabuf_attachment,
> +					      &frame->aux_addr,
> +					      &frame->aux_sgt,
> +					      NULL, dma_dir);
> +		if (err)
> +			goto err_release_cr;
> +	}
> +
> +	return 0;
> +
> +err_release_cr:
> +	tegra_vde_detach_and_put_dmabuf(frame->cr_dmabuf_attachment,
> +					frame->cr_sgt, dma_dir);
> +err_release_cb:
> +	tegra_vde_detach_and_put_dmabuf(frame->cb_dmabuf_attachment,
> +					frame->cb_sgt, dma_dir);
> +err_release_y:
> +	tegra_vde_detach_and_put_dmabuf(frame->y_dmabuf_attachment,
> +					frame->y_sgt, dma_dir);
> +
> +	return err;
> +}
> +
> +static void tegra_vde_deattach_frame_dmabufs(struct video_frame *frame,
> +					     enum dma_data_direction dma_dir,
> +					     bool baseline_profile)
> +{
> +	if (!baseline_profile)
> +		tegra_vde_detach_and_put_dmabuf(frame->aux_dmabuf_attachment,
> +						frame->aux_sgt, dma_dir);
> +
> +	tegra_vde_detach_and_put_dmabuf(frame->cr_dmabuf_attachment,
> +					frame->cr_sgt, dma_dir);
> +
> +	tegra_vde_detach_and_put_dmabuf(frame->cb_dmabuf_attachment,
> +					frame->cb_sgt, dma_dir);
> +
> +	tegra_vde_detach_and_put_dmabuf(frame->y_dmabuf_attachment,
> +					frame->y_sgt, dma_dir);
> +}
> +
> +static int tegra_vde_validate_frame(struct device *dev,
> +				    struct tegra_vde_h264_frame *frame)
> +{
> +	if (frame->frame_num > 0x7FFFFF) {
> +		dev_err(dev, "Bad frame_num %u\n", frame->frame_num);
> +		return -EINVAL;
> +	}
> +
> +	if (frame->y_offset & 0xFF) {
> +		dev_err(dev, "Bad y_offset 0x%X\n", frame->y_offset);
> +		return -EINVAL;
> +	}
> +
> +	if (frame->cb_offset & 0xFF) {
> +		dev_err(dev, "Bad cb_offset 0x%X\n", frame->cb_offset);
> +		return -EINVAL;
> +	}
> +
> +	if (frame->cr_offset & 0xFF) {
> +		dev_err(dev, "Bad cr_offset 0x%X\n", frame->cr_offset);
> +		return -EINVAL;
> +	}
> +
> +	return 0;
> +}
> +
> +static int tegra_vde_validate_h264_ctx(struct device *dev,
> +				       struct tegra_vde_h264_decoder_ctx *ctx)
> +{
> +	if (ctx->dpb_frames_nb == 0 || ctx->dpb_frames_nb > 17) {
> +		dev_err(dev, "Bad DPB size %u\n", ctx->dpb_frames_nb);
> +		return -EINVAL;
> +	}
> +
> +	if (ctx->level_idc > 15) {
> +		dev_err(dev, "Bad level value %u\n", ctx->level_idc);
> +		return -EINVAL;
> +	}
> +
> +	if (ctx->pic_init_qp > 52) {
> +		dev_err(dev, "Bad pic_init_qp value %u\n", ctx->pic_init_qp);
> +		return -EINVAL;
> +	}
> +
> +	if (ctx->log2_max_pic_order_cnt_lsb > 16) {
> +		dev_err(dev, "Bad log2_max_pic_order_cnt_lsb value %u\n",
> +			ctx->log2_max_pic_order_cnt_lsb);
> +		return -EINVAL;
> +	}
> +
> +	if (ctx->log2_max_frame_num > 16) {
> +		dev_err(dev, "Bad log2_max_frame_num value %u\n",
> +			ctx->log2_max_frame_num);
> +		return -EINVAL;
> +	}
> +
> +	if (ctx->chroma_qp_index_offset > 31) {
> +		dev_err(dev, "Bad chroma_qp_index_offset value %u\n",
> +			ctx->chroma_qp_index_offset);
> +		return -EINVAL;
> +	}
> +
> +	if (ctx->pic_order_cnt_type > 2) {
> +		dev_err(dev, "Bad pic_order_cnt_type value %u\n",
> +			ctx->pic_order_cnt_type);
> +		return -EINVAL;
> +	}
> +
> +	if (ctx->num_ref_idx_l0_active_minus1 > 15) {
> +		dev_err(dev, "Bad num_ref_idx_l0_active_minus1 value %u\n",
> +			ctx->num_ref_idx_l0_active_minus1);
> +		return -EINVAL;
> +	}
> +
> +	if (ctx->num_ref_idx_l1_active_minus1 > 15) {
> +		dev_err(dev, "Bad num_ref_idx_l1_active_minus1 value %u\n",
> +			ctx->num_ref_idx_l1_active_minus1);
> +		return -EINVAL;
> +	}
> +
> +	if (!ctx->pic_width_in_mbs || ctx->pic_width_in_mbs > 127) {
> +		dev_err(dev, "Bad pic_width_in_mbs value %u\n",
> +			ctx->pic_width_in_mbs);
> +		return -EINVAL;
> +	}
> +
> +	if (!ctx->pic_height_in_mbs || ctx->pic_height_in_mbs > 127) {
> +		dev_err(dev, "Bad pic_height_in_mbs value %u\n",
> +			ctx->pic_height_in_mbs);
> +		return -EINVAL;
> +	}
> +
> +	return 0;
> +}
> +
> +static int tegra_vde_ioctl_decode_h264(struct tegra_vde *vde,
> +				       unsigned long vaddr)
> +{
> +	struct device *dev = vde->miscdev.parent;
> +	struct tegra_vde_h264_decoder_ctx ctx;
> +	struct tegra_vde_h264_frame __user *frames_user;
> +	struct tegra_vde_h264_frame *frames;
> +	struct video_frame *dpb_frames;
> +	struct dma_buf_attachment *bitstream_data_dmabuf_attachment;
> +	struct sg_table *bitstream_sgt;
> +	enum dma_data_direction dma_dir;
> +	dma_addr_t bitstream_data_addr;
> +	dma_addr_t bsev_ptr;
> +	size_t bitstream_data_size;
> +	unsigned int macroblocks_nb;
> +	unsigned int read_bytes;
> +	unsigned int i = 0;
> +	bool timeout;
> +	int ret;
> +
> +	if (copy_from_user(&ctx, (void __user *)vaddr, sizeof(ctx)))
> +		return -EFAULT;
> +
> +	ret = tegra_vde_validate_h264_ctx(dev, &ctx);
> +	if (ret)
> +		return -EINVAL;
> +
> +	ret = tegra_vde_attach_dmabuf(dev, ctx.bitstream_data_fd,
> +				      ctx.bitstream_data_offset, 0,
> +				      &bitstream_data_dmabuf_attachment,
> +				      &bitstream_data_addr,
> +				      &bitstream_sgt,
> +				      &bitstream_data_size,
> +				      DMA_TO_DEVICE);
> +	if (ret)
> +		return ret;
> +
> +	dpb_frames = kcalloc(ctx.dpb_frames_nb, sizeof(*dpb_frames),
> +			     GFP_KERNEL);
> +	if (!dpb_frames) {
> +		ret = -ENOMEM;
> +		goto err_release_bitstream_dmabuf;
> +	}
> +
> +	macroblocks_nb = ctx.pic_width_in_mbs * ctx.pic_height_in_mbs;
> +	frames_user = u64_to_user_ptr(ctx.dpb_frames_ptr);
> +
> +	frames = kmalloc_array(ctx.dpb_frames_nb, sizeof(*frames), GFP_KERNEL);
> +	if (!frames) {
> +		ret = -ENOMEM;
> +		goto err_release_dpb_frames;
> +	}
> +
> +	if (copy_from_user(frames, frames_user,
> +			   ctx.dpb_frames_nb * sizeof(*frames))) {
> +		ret = -EFAULT;
> +		goto free_frames;
> +	}
> +
> +	for (i = 0; i < ctx.dpb_frames_nb; i++) {
> +		ret = tegra_vde_validate_frame(dev, &frames[i]);
> +		if (ret)
> +			goto free_frames;
> +
> +		dpb_frames[i].flags = frames[i].flags;
> +		dpb_frames[i].frame_num = frames[i].frame_num;
> +
> +		dma_dir = (i == 0) ? DMA_FROM_DEVICE : DMA_TO_DEVICE;
> +
> +		ret = tegra_vde_attach_dmabufs_to_frame(dev, &dpb_frames[i],
> +							&frames[i], dma_dir,
> +							ctx.baseline_profile,
> +							macroblocks_nb * 64);
> +		if (ret)
> +			goto free_frames;
> +	}
> +
> +free_frames:
> +	kfree(frames);
> +
> +	if (ret)
> +		goto err_release_dpb_frames;
> +
> +	ret = mutex_lock_interruptible(&vde->lock);
> +	if (ret)
> +		goto err_release_dpb_frames;
> +
> +	ret = pm_runtime_get_sync(dev);
> +	if (ret < 0)
> +		goto err_unlock;
> +
> +	/*
> +	 * We rely on the VDE registers reset value, otherwise VDE
> +	 * causes bus lockup.
> +	 */
> +	ret = reset_control_reset(vde->rst);
> +	if (ret) {
> +		dev_err(dev, "Failed to reset HW: %d\n", ret);
> +		goto err_put_runtime_pm;
> +	}
> +
> +	ret = tegra_vde_setup_hw_context(vde, &ctx, dpb_frames,
> +					 bitstream_data_addr,
> +					 bitstream_data_size,
> +					 macroblocks_nb);
> +	if (ret)
> +		goto err_put_runtime_pm;
> +
> +	tegra_vde_decode_frame(vde, macroblocks_nb);
> +
> +	timeout = !wait_for_completion_killable_timeout(&vde->decode_completion,
> +							msecs_to_jiffies(1000));
> +	if (timeout) {
> +		bsev_ptr = readl_relaxed(vde->bsev + 0x10);
> +		macroblocks_nb = readl_relaxed(vde->sxe + 0xC8) & 0x1FFF;
> +		read_bytes = bsev_ptr ? bsev_ptr - bitstream_data_addr : 0;
> +
> +		dev_err(dev, "Decoding failed, "
> +				"read 0x%X bytes : %u macroblocks parsed\n",
> +			read_bytes, macroblocks_nb);
> +
> +		ret = -EIO;
> +	}
> +
> +	reset_control_assert(vde->rst);
> +
> +err_put_runtime_pm:
> +	pm_runtime_mark_last_busy(dev);
> +	pm_runtime_put_autosuspend(dev);
> +
> +err_unlock:
> +	mutex_unlock(&vde->lock);
> +
> +err_release_dpb_frames:
> +	while (i--) {
> +		dma_dir = (i == 0) ? DMA_FROM_DEVICE : DMA_TO_DEVICE;
> +
> +		tegra_vde_deattach_frame_dmabufs(&dpb_frames[i], dma_dir,
> +						 ctx.baseline_profile);
> +	}
> +
> +	kfree(dpb_frames);
> +
> +err_release_bitstream_dmabuf:
> +	tegra_vde_detach_and_put_dmabuf(bitstream_data_dmabuf_attachment,
> +					bitstream_sgt, DMA_TO_DEVICE);
> +
> +	return ret;
> +}
> +
> +static long tegra_vde_unlocked_ioctl(struct file *filp,
> +				     unsigned int cmd, unsigned long arg)
> +{
> +	struct miscdevice *miscdev = filp->private_data;
> +	struct tegra_vde *vde = container_of(miscdev, struct tegra_vde,
> +					     miscdev);
> +
> +	switch (cmd) {
> +	case TEGRA_VDE_IOCTL_DECODE_H264:
> +		return tegra_vde_ioctl_decode_h264(vde, arg);
> +	}
> +
> +	dev_err(miscdev->parent, "Invalid IOCTL command %u\n", cmd);
> +
> +	return -ENOTTY;
> +}
> +
> +static const struct file_operations tegra_vde_fops = {
> +	.owner		= THIS_MODULE,
> +	.unlocked_ioctl	= tegra_vde_unlocked_ioctl,
> +};
> +
> +static irqreturn_t tegra_vde_isr(int irq, void *data)
> +{
> +	struct tegra_vde *vde = data;
> +
> +	tegra_vde_set_bits(vde, 0, vde->frameid + 0x208);
> +	complete(&vde->decode_completion);
> +
> +	return IRQ_HANDLED;
> +}
> +
> +static int tegra_vde_runtime_suspend(struct device *dev)
> +{
> +	struct tegra_vde *vde = dev_get_drvdata(dev);
> +	int err;
> +
> +	err = tegra_powergate_power_off(TEGRA_POWERGATE_VDEC);
> +	if (err) {
> +		dev_err(dev, "Failed to power down HW: %d\n", err);
> +		return err;
> +	}
> +
> +	clk_disable_unprepare(vde->clk);
> +
> +	return 0;
> +}
> +
> +static int tegra_vde_runtime_resume(struct device *dev)
> +{
> +	struct tegra_vde *vde = dev_get_drvdata(dev);
> +	int err;
> +
> +	err = tegra_powergate_sequence_power_up(TEGRA_POWERGATE_VDEC,
> +						vde->clk, vde->rst);
> +	if (err) {
> +		dev_err(dev, "Failed to power up HW : %d\n", err);
> +		return err;
> +	}
> +
> +	return 0;
> +}
> +
> +static int tegra_vde_probe(struct platform_device *pdev)
> +{
> +	struct device *dev = &pdev->dev;
> +	struct resource *regs;
> +	struct tegra_vde *vde;
> +	int irq, err;
> +
> +	vde = devm_kzalloc(dev, sizeof(*vde), GFP_KERNEL);
> +	if (!vde)
> +		return -ENOMEM;
> +
> +	platform_set_drvdata(pdev, vde);
> +
> +	regs = platform_get_resource_byname(pdev, IORESOURCE_MEM, "sxe");
> +	if (!regs)
> +		return -ENODEV;
> +
> +	vde->sxe = devm_ioremap_resource(dev, regs);
> +	if (IS_ERR(vde->sxe))
> +		return PTR_ERR(vde->sxe);
> +
> +	regs = platform_get_resource_byname(pdev, IORESOURCE_MEM, "bsev");
> +	if (!regs)
> +		return -ENODEV;
> +
> +	vde->bsev = devm_ioremap_resource(dev, regs);
> +	if (IS_ERR(vde->bsev))
> +		return PTR_ERR(vde->bsev);
> +
> +	regs = platform_get_resource_byname(pdev, IORESOURCE_MEM, "mbe");
> +	if (!regs)
> +		return -ENODEV;
> +
> +	vde->mbe = devm_ioremap_resource(dev, regs);
> +	if (IS_ERR(vde->mbe))
> +		return PTR_ERR(vde->mbe);
> +
> +	regs = platform_get_resource_byname(pdev, IORESOURCE_MEM, "ppe");
> +	if (!regs)
> +		return -ENODEV;
> +
> +	vde->ppe = devm_ioremap_resource(dev, regs);
> +	if (IS_ERR(vde->ppe))
> +		return PTR_ERR(vde->ppe);
> +
> +	regs = platform_get_resource_byname(pdev, IORESOURCE_MEM, "mce");
> +	if (!regs)
> +		return -ENODEV;
> +
> +	vde->mce = devm_ioremap_resource(dev, regs);
> +	if (IS_ERR(vde->mce))
> +		return PTR_ERR(vde->mce);
> +
> +	regs = platform_get_resource_byname(pdev, IORESOURCE_MEM, "tfe");
> +	if (!regs)
> +		return -ENODEV;
> +
> +	vde->tfe = devm_ioremap_resource(dev, regs);
> +	if (IS_ERR(vde->tfe))
> +		return PTR_ERR(vde->tfe);
> +
> +	regs = platform_get_resource_byname(pdev, IORESOURCE_MEM, "ppb");
> +	if (!regs)
> +		return -ENODEV;
> +
> +	vde->ppb = devm_ioremap_resource(dev, regs);
> +	if (IS_ERR(vde->ppb))
> +		return PTR_ERR(vde->ppb);
> +
> +	regs = platform_get_resource_byname(pdev, IORESOURCE_MEM, "vdma");
> +	if (!regs)
> +		return -ENODEV;
> +
> +	vde->vdma = devm_ioremap_resource(dev, regs);
> +	if (IS_ERR(vde->vdma))
> +		return PTR_ERR(vde->vdma);
> +
> +	regs = platform_get_resource_byname(pdev, IORESOURCE_MEM, "frameid");
> +	if (!regs)
> +		return -ENODEV;
> +
> +	vde->frameid = devm_ioremap_resource(dev, regs);
> +	if (IS_ERR(vde->frameid))
> +		return PTR_ERR(vde->frameid);
> +
> +	vde->clk = devm_clk_get(dev, NULL);
> +	if (IS_ERR(vde->clk)) {
> +		err = PTR_ERR(vde->clk);
> +		dev_err(dev, "Could not get VDE clk %d\n", err);
> +		return err;
> +	}
> +
> +	vde->rst = devm_reset_control_get(dev, NULL);
> +	if (IS_ERR(vde->rst)) {
> +		err = PTR_ERR(vde->rst);
> +		dev_err(dev, "Could not get VDE reset %d\n", err);
> +		return err;
> +	}
> +
> +	irq = platform_get_irq_byname(pdev, "sync-token");
> +	if (irq < 0)
> +		return irq;
> +
> +	err = devm_request_irq(dev, irq, tegra_vde_isr, 0,
> +			       dev_name(dev), vde);
> +	if (err) {
> +		dev_err(dev, "Could not request IRQ %d\n", err);
> +		return err;
> +	}
> +
> +	vde->iram_pool = of_gen_pool_get(dev->of_node, "iram", 0);
> +	if (!vde->iram_pool) {
> +		dev_err(dev, "Could not get IRAM pool\n");
> +		return -EPROBE_DEFER;
> +	}
> +
> +	vde->iram = gen_pool_dma_alloc(vde->iram_pool, 0x3FC00,
> +				       &vde->iram_lists_addr);
> +	if (!vde->iram) {
> +		dev_err(dev, "Could not reserve IRAM\n");
> +		return -ENOMEM;
> +	}
> +
> +	mutex_init(&vde->lock);
> +	init_completion(&vde->decode_completion);
> +
> +	vde->miscdev.minor = MISC_DYNAMIC_MINOR;
> +	vde->miscdev.name = "tegra_vde";
> +	vde->miscdev.fops = &tegra_vde_fops;
> +	vde->miscdev.parent = dev;
> +
> +	err = misc_register(&vde->miscdev);
> +	if (err) {
> +		dev_err(dev, "Failed to register misc device: %d\n", err);
> +		goto err_gen_free;
> +	}
> +
> +	pm_runtime_enable(dev);
> +	pm_runtime_use_autosuspend(dev);
> +	pm_runtime_set_autosuspend_delay(dev, 300);
> +
> +	if (!pm_runtime_enabled(dev)) {
> +		err = tegra_vde_runtime_resume(dev);
> +		if (err)
> +			goto err_misc_unreg;
> +	}
> +
> +	return 0;
> +
> +err_misc_unreg:
> +	misc_deregister(&vde->miscdev);
> +
> +err_gen_free:
> +	gen_pool_free(vde->iram_pool,
> +		      (unsigned long)vde->iram, 0x3FC00);
> +
> +	return err;
> +}
> +
> +static int tegra_vde_remove(struct platform_device *pdev)
> +{
> +	struct tegra_vde *vde = platform_get_drvdata(pdev);
> +	struct device *dev = &pdev->dev;
> +	int err;
> +
> +	if (!pm_runtime_enabled(dev)) {
> +		err = tegra_vde_runtime_suspend(dev);
> +		if (err)
> +			return err;
> +	}
> +
> +	pm_runtime_dont_use_autosuspend(dev);
> +	pm_runtime_disable(dev);
> +
> +	misc_deregister(&vde->miscdev);
> +
> +	gen_pool_free(vde->iram_pool,
> +		      (unsigned long)vde->iram, 0x3FC00);
> +
> +	return 0;
> +}
> +
> +#ifdef CONFIG_PM_SLEEP
> +static int tegra_vde_pm_suspend(struct device *dev)
> +{
> +	struct tegra_vde *vde = dev_get_drvdata(dev);
> +	int err;
> +
> +	mutex_lock(&vde->lock);
> +
> +	err = pm_runtime_force_suspend(dev);
> +	if (err < 0)
> +		return err;
> +
> +	return 0;
> +}
> +
> +static int tegra_vde_pm_resume(struct device *dev)
> +{
> +	struct tegra_vde *vde = dev_get_drvdata(dev);
> +	int err;
> +
> +	err = pm_runtime_force_resume(dev);
> +	if (err < 0)
> +		return err;
> +
> +	mutex_unlock(&vde->lock);
> +
> +	return 0;
> +}
> +#endif
> +
> +static const struct dev_pm_ops tegra_vde_pm_ops = {
> +	SET_RUNTIME_PM_OPS(tegra_vde_runtime_suspend,
> +			   tegra_vde_runtime_resume,
> +			   NULL)
> +	SET_SYSTEM_SLEEP_PM_OPS(tegra_vde_pm_suspend,
> +				tegra_vde_pm_resume)
> +};
> +
> +static const struct of_device_id tegra_vde_of_match[] = {
> +	{ .compatible = "nvidia,tegra20-vde", },
> +	{ },
> +};
> +MODULE_DEVICE_TABLE(of, tegra_vde_of_match);
> +
> +static struct platform_driver tegra_vde_driver = {
> +	.probe		= tegra_vde_probe,
> +	.remove		= tegra_vde_remove,
> +	.driver		= {
> +		.name		= "tegra-vde",
> +		.of_match_table = tegra_vde_of_match,
> +		.pm		= &tegra_vde_pm_ops,
> +	},
> +};
> +module_platform_driver(tegra_vde_driver);
> +
> +MODULE_DESCRIPTION("NVIDIA Tegra20 Video Decoder driver");
> +MODULE_AUTHOR("Dmitry Osipenko");
> +MODULE_LICENSE("GPL");
> 

^ permalink raw reply

* [PATCH v2] ARM: dts: at91: add devicetree for the Axentia Nattis with Natte power
From: Peter Rosin @ 2017-12-04 13:57 UTC (permalink / raw)
  To: linux-kernel-u79uwXL29TY76Z2rM5mHXA
  Cc: Peter Rosin, Rob Herring, Mark Rutland, Russell King,
	Nicolas Ferre, Alexandre Belloni,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	devicetree-u79uwXL29TY76Z2rM5mHXA

The Axentia Nattis is a device designed for presenting departures for
public transport systems. The Natte helper board provides power and
features a battery of battery chargers.

Signed-off-by: Peter Rosin <peda-koto5C5qi+TLoDKTGw+V6w@public.gmane.org>
---

This was part of a two-patch series, but the first patch in that
series was redundant, so I dropped it (there was a dt binding
recently added for the tfa9879 amplifier that I had not noticed).

Changes since v1:    https://lkml.org/lkml/2017/12/1/844
- removed chip-ids from before the @-sign instead naming the nodes
  for the function, e.g sx1502q@20 -> ioexp@20
- added #sound-dai-cells to the amplifier node
- switch to SPDX license tags

Cheers,
Peter

Documentation/devicetree/bindings/arm/axentia.txt |   9 +
 MAINTAINERS                                       |   2 +
 arch/arm/boot/dts/Makefile                        |   1 +
 arch/arm/boot/dts/at91-natte.dtsi                 | 244 ++++++++++++++++++++
 arch/arm/boot/dts/at91-nattis-2-natte-2.dts       | 258 ++++++++++++++++++++++
 5 files changed, 514 insertions(+)
 create mode 100644 arch/arm/boot/dts/at91-natte.dtsi
 create mode 100644 arch/arm/boot/dts/at91-nattis-2-natte-2.dts

diff --git a/Documentation/devicetree/bindings/arm/axentia.txt b/Documentation/devicetree/bindings/arm/axentia.txt
index ea3fb96ae465..de58f2463880 100644
--- a/Documentation/devicetree/bindings/arm/axentia.txt
+++ b/Documentation/devicetree/bindings/arm/axentia.txt
@@ -10,6 +10,15 @@ compatible = "axentia,linea",
 and following the rules from atmel-at91.txt for a sama5d31 SoC.
 
 
+Nattis v2 board with Natte v2 power board
+-----------------------------------------
+
+Required root node properties:
+compatible = "axentia,nattis-2", "axentia,natte-2", "axentia,linea",
+	     "atmel,sama5d31", "atmel,sama5d3", "atmel,sama5";
+and following the rules from above for the axentia,linea CPU module.
+
+
 TSE-850 v3 board
 ----------------
 
diff --git a/MAINTAINERS b/MAINTAINERS
index d4fdcb12616c..ec8a33f7cb01 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -2500,6 +2500,8 @@ L:	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r@public.gmane.org (moderated for non-subscribers)
 S:	Maintained
 F:	Documentation/devicetree/bindings/arm/axentia.txt
 F:	arch/arm/boot/dts/at91-linea.dtsi
+F:	arch/arm/boot/dts/at91-natte.dtsi
+F:	arch/arm/boot/dts/at91-nattis-2-natte-2.dts
 F:	arch/arm/boot/dts/at91-tse850-3.dts
 
 AXENTIA ASOC DRIVERS
diff --git a/arch/arm/boot/dts/Makefile b/arch/arm/boot/dts/Makefile
index d0381e9caf21..d62ee6f0271d 100644
--- a/arch/arm/boot/dts/Makefile
+++ b/arch/arm/boot/dts/Makefile
@@ -47,6 +47,7 @@ dtb-$(CONFIG_SOC_AT91SAM9) += \
 	at91sam9x35ek.dtb
 dtb-$(CONFIG_SOC_SAM_V7) += \
 	at91-kizbox2.dtb \
+	at91-nattis-2-natte-2.dtb \
 	at91-sama5d27_som1_ek.dtb \
 	at91-sama5d2_xplained.dtb \
 	at91-sama5d3_xplained.dtb \
diff --git a/arch/arm/boot/dts/at91-natte.dtsi b/arch/arm/boot/dts/at91-natte.dtsi
new file mode 100644
index 000000000000..49f0a0c46cde
--- /dev/null
+++ b/arch/arm/boot/dts/at91-natte.dtsi
@@ -0,0 +1,244 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * at91-natte.dts - Device Tree include file for the Natte board
+ *
+ * Copyright (C) 2017 Axentia Technologies AB
+ *
+ * Author: Peter Rosin <peda-koto5C5qi+TLoDKTGw+V6w@public.gmane.org>
+ */
+
+/ {
+	mux: mux-controller {
+		compatible = "gpio-mux";
+		#mux-control-cells = <0>;
+
+		mux-gpios = <&ioexp 0 GPIO_ACTIVE_HIGH>,
+			    <&ioexp 1 GPIO_ACTIVE_HIGH>,
+			    <&ioexp 2 GPIO_ACTIVE_HIGH>;
+	};
+
+	batntc-mux {
+		compatible = "io-channel-mux";
+		io-channels = <&adc 5>;
+		io-channel-names = "parent";
+		mux-controls = <&mux>;
+
+		channels =
+			"batntc0", "batntc1", "batntc2", "batntc3",
+			"batntc4", "batntc5", "batntc6", "batntc7";
+	};
+
+	batv-mux {
+		compatible = "io-channel-mux";
+		io-channels = <&adc 6>;
+		io-channel-names = "parent";
+		mux-controls = <&mux>;
+
+		channels =
+			"batv0", "batv1", "batv2", "batv3",
+			"batv4", "batv5", "batv6", "batv7";
+	};
+
+	iout-mux {
+		compatible = "io-channel-mux";
+		io-channels = <&adc 7>;
+		io-channel-names = "parent";
+		mux-controls = <&mux>;
+
+		channels =
+			"iout0", "iout1", "iout2", "iout3",
+			"iout4", "iout5", "iout6", "iout7";
+	};
+
+	i2c-mux {
+		compatible = "i2c-mux";
+		mux-locked;
+		i2c-parent = <&i2c0>;
+		mux-controls = <&mux>;
+
+		#address-cells = <1>;
+		#size-cells = <0>;
+
+		i2c@0 {
+			reg = <0>;
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			charger@9 {
+				compatible = "ti,bq24735";
+				reg = <0x9>;
+
+				ti,charge-current = <2000>;
+				ti,charge-voltage = <16800>;
+
+				poll-interval = <20000>;
+			};
+		};
+
+		i2c@1 {
+			reg = <1>;
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			charger@9 {
+				compatible = "ti,bq24735";
+				reg = <0x9>;
+
+				ti,charge-current = <2000>;
+				ti,charge-voltage = <16800>;
+
+				poll-interval = <20000>;
+			};
+		};
+
+		i2c@2 {
+			reg = <2>;
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			charger@9 {
+				compatible = "ti,bq24735";
+				reg = <0x9>;
+
+				ti,charge-current = <2000>;
+				ti,charge-voltage = <16800>;
+
+				poll-interval = <20000>;
+			};
+		};
+
+		i2c@3 {
+			reg = <3>;
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			charger@9 {
+				compatible = "ti,bq24735";
+				reg = <0x9>;
+
+				ti,charge-current = <2000>;
+				ti,charge-voltage = <16800>;
+
+				poll-interval = <20000>;
+			};
+		};
+
+		i2c@4 {
+			reg = <4>;
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			charger@9 {
+				compatible = "ti,bq24735";
+				reg = <0x9>;
+
+				ti,charge-current = <2000>;
+				ti,charge-voltage = <16800>;
+
+				poll-interval = <20000>;
+			};
+		};
+
+		i2c@5 {
+			reg = <5>;
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			charger@9 {
+				compatible = "ti,bq24735";
+				reg = <0x9>;
+
+				ti,charge-current = <2000>;
+				ti,charge-voltage = <16800>;
+
+				poll-interval = <20000>;
+			};
+		};
+
+		i2c@6 {
+			reg = <6>;
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			charger@9 {
+				compatible = "ti,bq24735";
+				reg = <0x9>;
+
+				ti,charge-current = <2000>;
+				ti,charge-voltage = <16800>;
+
+				poll-interval = <20000>;
+			};
+		};
+
+		i2c@7 {
+			reg = <7>;
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			charger@9 {
+				compatible = "ti,bq24735";
+				reg = <0x9>;
+
+				ti,charge-current = <2000>;
+				ti,charge-voltage = <16800>;
+
+				poll-interval = <20000>;
+			};
+		};
+	};
+};
+
+&i2c0 {
+	status = "okay";
+
+	ioexp: ioexp@20 {
+		#gpio-cells = <2>;
+		compatible = "semtech,sx1502q";
+		reg = <0x20>;
+
+		gpio-controller;
+		ngpios = <8>;
+
+		pinctrl-names = "default";
+		pinctrl-0 = <&gpio3_cfg_pins>;
+
+		gpio3_cfg_pins: gpio3_cfg {
+			pins = "gpio3";
+			bias-pull-up;
+		};
+	};
+
+	adc: adc@48 {
+		compatible = "ti,ads1015";
+		reg = <0x48>;
+		#io-channel-cells = <1>;
+
+		#address-cells = <1>;
+		#size-cells = <0>;
+
+		channel@4 {
+			reg = <4>;
+			ti,gain = <2>;
+			ti,datarate = <4>;
+		};
+
+		channel@5 {
+			reg = <5>;
+			ti,gain = <2>;
+			ti,datarate = <4>;
+		};
+
+		channel@6 {
+			reg = <6>;
+			ti,gain = <1>;
+			ti,datarate = <4>;
+		};
+
+		channel@7 {
+			reg = <7>;
+			ti,gain = <3>;
+			ti,datarate = <4>;
+		};
+	};
+};
diff --git a/arch/arm/boot/dts/at91-nattis-2-natte-2.dts b/arch/arm/boot/dts/at91-nattis-2-natte-2.dts
new file mode 100644
index 000000000000..3ea1d26e1c68
--- /dev/null
+++ b/arch/arm/boot/dts/at91-nattis-2-natte-2.dts
@@ -0,0 +1,258 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * at91-nattis-2-natte-2.dts - Device Tree file for the Linea/Nattis board
+ *
+ * Copyright (C) 2017 Axentia Technologies AB
+ *
+ * Author: Peter Rosin <peda-koto5C5qi+TLoDKTGw+V6w@public.gmane.org>
+ */
+/dts-v1/;
+#include "at91-linea.dtsi"
+#include "sama5d3_lcd.dtsi"
+#include "at91-natte.dtsi"
+
+/ {
+	model = "Axentia Linea-Nattis v2 Natte v2";
+	compatible = "axentia,nattis-2", "axentia,natte-2", "axentia,linea",
+		     "atmel,sama5d31", "atmel,sama5d3", "atmel,sama5";
+
+	ahb {
+		apb {
+			pinctrl@fffff200 {
+				nattis {
+					pinctrl_usba_vbus: usba_vbus {
+						atmel,pins =
+							<AT91_PIOD 28
+							 AT91_PERIPH_GPIO
+							 AT91_PINCTRL_DEGLITCH>;
+					};
+
+					pinctrl_mmc0_cd: mmc0_cd {
+						atmel,pins =
+							<AT91_PIOD 5
+							 AT91_PERIPH_GPIO
+							 AT91_PINCTRL_PULL_UP_DEGLITCH>;
+					};
+
+					pinctrl_lcd_prlud0: lcd_prlud0 {
+						atmel,pins =
+							<AT91_PIOA 21
+							 AT91_PERIPH_GPIO
+							 AT91_PINCTRL_OUTPUT_VAL(0)>;
+					};
+
+					pinctrl_lcd_hipow0: lcd_hipow0 {
+						atmel,pins =
+							<AT91_PIOA 23
+							 AT91_PERIPH_GPIO
+							 AT91_PINCTRL_OUTPUT_VAL(0)>;
+					};
+				};
+			};
+
+			watchdog@fffffe40 {
+				status = "okay";
+			};
+		};
+	};
+
+	gpio-keys {
+		compatible = "gpio-keys";
+
+		wakeup {
+			label = "Wakeup";
+			linux,code = <10>;
+			gpio-key,wakeup;
+			gpios = <&pioB 27 GPIO_ACTIVE_LOW>;
+		};
+	};
+
+	panel_reg: panel-regulator {
+		compatible = "regulator-fixed";
+		regulator-name = "panel-VCC";
+		regulator-min-microvolt = <3300000>;
+		regulator-max-microvolt = <3300000>;
+	};
+
+	bl_reg: backlight-regulator {
+		compatible = "regulator-fixed";
+		regulator-name = "panel-VDD";
+		regulator-min-microvolt = <12000000>;
+		regulator-max-microvolt = <12000000>;
+	};
+
+	panel_bl: backlight {
+		compatible = "pwm-backlight";
+		pwms = <&hlcdc_pwm 0 100000 0>;
+
+		brightness-levels = < 0  1  2  3  4  5  6  7  8  9
+				     10 11 12 13 14 15 16 17 18 19
+				     20 21 22 23 24 25 26 27 28 29
+				     30 31 32 33 34 35 36 37 38 39
+				     40 41 42 43 44 45 46 47 48 49
+				     50 51 52 53 54 55 56 57 58 59
+				     60 61 62 63 64 65 66 67 68 69
+				     70 71 72 73 74 75 76 77 78 79
+				     80 81 82 83 84 85 86 87 88 89
+				     90 91 92 93 94 95 96 97 98 99
+				    100>;
+		default-brightness-level = <40>;
+
+		power-supply = <&bl_reg>;
+		enable-gpios = <&pioA 20 GPIO_ACTIVE_HIGH>;
+	};
+
+	panel: panel {
+		compatible = "sharp,lq150x1lg11";
+		backlight = <&panel_bl>;
+		power-supply = <&panel_reg>;
+
+		port {
+			panel_input: endpoint {
+				remote-endpoint = <&hlcdc_panel_output>;
+			};
+		};
+	};
+
+	sound {
+		compatible = "simple-audio-card";
+
+		simple-audio-card,name = "nattis-tfa9879";
+		simple-audio-card,format = "i2s";
+		simple-audio-card,bitclock-master = <&cpu_dai>;
+		simple-audio-card,frame-master = <&cpu_dai>;
+		simple-audio-card,widgets = "Line", "Line Out Jack";
+		simple-audio-card,routing = "Line Out Jack", "LINEOUT";
+
+		cpu_dai: simple-audio-card,cpu {
+			sound-dai = <&ssc0>;
+		};
+
+		simple-audio-card,codec {
+			sound-dai = <&amp>;
+		};
+	};
+};
+
+&i2c0 {
+	status = "okay";
+
+	clock-frequency = <100000>;
+
+	temp@18 {
+		compatible = "nxp,se97b", "jedec,jc-42.4-temp";
+		reg = <0x18>;
+		smbus-timeout-disable;
+	};
+
+	eeprom@50 {
+		compatible = "nxp,24c02";
+		reg = <0x50>;
+		pagesize = <16>;
+	};
+
+	amp: amplifier@6c {
+		 compatible = "nxp,tfa9879";
+		 reg = <0x6c>;
+		 #sound-dai-cells = <0>;
+	};
+};
+
+&ssc0 {
+	status = "okay";
+
+	atmel,clk-from-rk-pin;
+	#sound-dai-cells = <0>;
+};
+
+&hlcdc {
+	status = "okay";
+
+	hlcdc-display-controller {
+		pinctrl-names = "default";
+		pinctrl-0 = <&pinctrl_lcd_base
+			     &pinctrl_lcd_rgb565
+			     &pinctrl_lcd_prlud0
+			     &pinctrl_lcd_hipow0>;
+
+		port@0 {
+			hlcdc_panel_output: endpoint {
+				remote-endpoint = <&panel_input>;
+			};
+		};
+	};
+};
+
+&mmc0 {
+	status = "okay";
+
+	pinctrl-0 = <&pinctrl_mmc0_clk_cmd_dat0
+		     &pinctrl_mmc0_dat1_3
+		     &pinctrl_mmc0_cd>;
+
+	slot@0 {
+		reg = <0>;
+		bus-width = <4>;
+		cd-gpios = <&pioD 5 GPIO_ACTIVE_HIGH>;
+	};
+};
+
+&usart0 {
+	status = "okay";
+
+	pinctrl-names = "default";
+	pinctrl-0 = <&pinctrl_usart0>;
+	atmel,use-dma-rx;
+};
+
+&nand {
+	partitions {
+		compatible = "fixed-partitions";
+		#address-cells = <1>;
+		#size-cells = <1>;
+
+		at91bootstrap@0 {
+			label = "at91bootstrap";
+			reg = <0x0 0x40000>;
+		};
+
+		bootloader@40000 {
+			label = "bootloader";
+			reg = <0x40000 0x80000>;
+		};
+
+		bootloaderenv@c0000 {
+			label = "bootloader env";
+			reg = <0xc0000 0xc0000>;
+		};
+
+		dtb@180000 {
+			label = "device tree";
+			reg = <0x180000 0x80000>;
+		};
+
+		kernel@200000 {
+			label = "kernel";
+			reg = <0x200000 0x600000>;
+		};
+
+		rootfs@800000 {
+			label = "rootfs";
+			reg = <0x800000 0x0f800000>;
+		};
+	};
+};
+
+&dbgu {
+	status = "okay";
+
+	atmel,use-dma-rx;
+};
+
+&usb0 {
+	status = "okay";
+
+	atmel,vbus-gpio = <&pioD 28 GPIO_ACTIVE_HIGH>;
+	pinctrl-names = "default";
+	pinctrl-0 = <&pinctrl_usba_vbus>;
+};
-- 
2.11.0

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

^ permalink raw reply related

* Re: [PATCH v3] usb: xhci: allow imod-interval to be configurable
From: Adam Wallis @ 2017-12-04 13:38 UTC (permalink / raw)
  To: Chunfeng Yun
  Cc: Mark Rutland, devicetree-u79uwXL29TY76Z2rM5mHXA, Mathias Nyman,
	Greg Kroah-Hartman, timur-sgV2jX0FEOL9JmXXK+q4OQ,
	linux-usb-u79uwXL29TY76Z2rM5mHXA, Rob Herring,
	linux-mediatek-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, Matthias Brugger,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r
In-Reply-To: <1512271374.17567.164.camel@mhfsdcap03>

On 12/2/2017 10:22 PM, Chunfeng Yun wrote:
> On Fri, 2017-12-01 at 10:44 -0500, Adam Wallis wrote:
>> The xHCI driver currently has the IMOD set to 160, which
>> translates to an IMOD interval of 40,000ns (160 * 250)ns
>>
>> Commit 0cbd4b34cda9 ("xhci: mediatek: support MTK xHCI host controller")
>> introduced a QUIRK for the MTK platform to adjust this interval to 20,
>> which translates to an IMOD interval of 5,000ns (20 * 250)ns. This is
>> due to the fact that the MTK controller IMOD interval is 8 times
>> as much as defined in xHCI spec.
>>
>> Instead of adding more quirk bits for additional platforms, this patch
>> introduces the ability for vendors to set the IMOD_INTERVAL as is
>> optimal for their platform. By using device_property_read_u32() on
>> "imod-interval", the IMOD INTERVAL can be specified in nano seconds. If
>> no interval is specified, the default of 40,000ns (IMOD=160) will be
>> used.
>>
>> No bounds checking has been implemented due to the fact that a vendor
>> may have violated the spec and would need to specify a value outside of
>> the max 8,000 IRQs/second limit specified in the xHCI spec.
>>
>> Signed-off-by: Adam Wallis <awallis-sgV2jX0FEOL9JmXXK+q4OQ@public.gmane.org>
>> ---
>> changes from v2:
>>   * Added PCI default value [Mathias]
>>   * Removed xhci-mtk.h from xhci-plat.c [Chunfeng Yun]
>>   * Removed MTK quirk from xhci-plat and moved logic to xhci-mtk [Chunfeng]
>>   * Updated bindings Documentation to use proper units [Rob Herring]
>>   * Added imod-interval description and example to MTK binding documentation
>> changes from v1:
>>   * Removed device_property_read_u32() per suggestion from greg k-h
>>   * Used ER_IRQ_INTERVAL_MASK in place of (u16) cast
>>
>>  Documentation/devicetree/bindings/usb/mediatek,mtk-xhci.txt | 2 ++
>>  Documentation/devicetree/bindings/usb/usb-xhci.txt          | 1 +
>>  drivers/usb/host/xhci-mtk.c                                 | 9 +++++++++
>>  drivers/usb/host/xhci-pci.c                                 | 3 +++
>>  drivers/usb/host/xhci-plat.c                                | 4 ++++
>>  drivers/usb/host/xhci.c                                     | 7 ++-----
>>  drivers/usb/host/xhci.h                                     | 2 ++
>>  7 files changed, 23 insertions(+), 5 deletions(-)
>>
>> diff --git a/Documentation/devicetree/bindings/usb/mediatek,mtk-xhci.txt b/Documentation/devicetree/bindings/usb/mediatek,mtk-xhci.txt
>> index 3059596..45bbf18 100644
>> --- a/Documentation/devicetree/bindings/usb/mediatek,mtk-xhci.txt
>> +++ b/Documentation/devicetree/bindings/usb/mediatek,mtk-xhci.txt
>> @@ -46,6 +46,7 @@ Optional properties:
>>   - pinctrl-names : a pinctrl state named "default" must be defined
>>   - pinctrl-0 : pin control group
>>  	See: Documentation/devicetree/bindings/pinctrl/pinctrl-bindings.txt
>> + - imod-interval: Default interval is 5000ns
> I think, as Rob suggested before, recommend to have a unit suffix
> appended to the property name.
> s/imod-interval/imod-interval-ns

Thanks, I definitely misunderstood his comments the first go around. I will make
the change.

>>  
>>  Example:
>>  usb30: usb@11270000 {
>> @@ -66,6 +67,7 @@ usb30: usb@11270000 {
>>  	usb3-lpm-capable;
>>  	mediatek,syscon-wakeup = <&pericfg>;
>>  	mediatek,wakeup-src = <1>;
>> +	imod-interval = <10000>;
>>  };
>>  
>>  2nd: dual-role mode with xHCI driver
>> diff --git a/Documentation/devicetree/bindings/usb/usb-xhci.txt b/Documentation/devicetree/bindings/usb/usb-xhci.txt
>> index ae6e484..89b68f1 100644
>> --- a/Documentation/devicetree/bindings/usb/usb-xhci.txt
>> +++ b/Documentation/devicetree/bindings/usb/usb-xhci.txt
>> @@ -29,6 +29,7 @@ Optional properties:
>>    - usb2-lpm-disable: indicate if we don't want to enable USB2 HW LPM
>>    - usb3-lpm-capable: determines if platform is USB3 LPM capable
>>    - quirk-broken-port-ped: set if the controller has broken port disable mechanism
>> +  - imod-interval: Default interval is 40000ns
>>  
>>  Example:
>>  	usb@f0931000 {
>> diff --git a/drivers/usb/host/xhci-mtk.c b/drivers/usb/host/xhci-mtk.c
>> index b62a1d2..278ea3b 100644
>> --- a/drivers/usb/host/xhci-mtk.c
>> +++ b/drivers/usb/host/xhci-mtk.c
>> @@ -674,6 +674,15 @@ static int xhci_mtk_probe(struct platform_device *pdev)
>>  
>>  	xhci = hcd_to_xhci(hcd);
>>  	xhci->main_hcd = hcd;
>> +
>> +	/*
>> +	 * imod_interval is the interrupt modulation value in nanoseconds.
>> +	 * The increment interval is 8 times as much as that defined in
>> +	 * the xHCI spec on MTK's controller.
>> +	 */
>> +	xhci->imod_interval = 5000;
>> +	device_property_read_u32(dev, "imod-interval", &xhci->imod_interval);
>> +
>>  	xhci->shared_hcd = usb_create_shared_hcd(driver, dev,
>>  			dev_name(dev), hcd);
>>  	if (!xhci->shared_hcd) {
>> diff --git a/drivers/usb/host/xhci-pci.c b/drivers/usb/host/xhci-pci.c
>> index 7ef1274..efbe57b 100644
>> --- a/drivers/usb/host/xhci-pci.c
>> +++ b/drivers/usb/host/xhci-pci.c
>> @@ -234,6 +234,9 @@ static int xhci_pci_setup(struct usb_hcd *hcd)
>>  	if (!xhci->sbrn)
>>  		pci_read_config_byte(pdev, XHCI_SBRN_OFFSET, &xhci->sbrn);
>>  
>> +	/* imod_interval is the interrupt modulation value in nanoseconds. */
>> +	xhci->imod_interval = 40000;
>> +
>>  	retval = xhci_gen_setup(hcd, xhci_pci_quirks);
>>  	if (retval)
>>  		return retval;
>> diff --git a/drivers/usb/host/xhci-plat.c b/drivers/usb/host/xhci-plat.c
>> index 09f164f..b78be87 100644
>> --- a/drivers/usb/host/xhci-plat.c
>> +++ b/drivers/usb/host/xhci-plat.c
>> @@ -269,6 +269,10 @@ static int xhci_plat_probe(struct platform_device *pdev)
>>  	if (device_property_read_bool(&pdev->dev, "quirk-broken-port-ped"))
>>  		xhci->quirks |= XHCI_BROKEN_PORT_PED;
>>  
>> +	/* imod_interval is the interrupt modulation value in nanoseconds. */
>> +	xhci->imod_interval = 40000;
>> +	device_property_read_u32(sysdev, "imod-interval", &xhci->imod_interval);
>> +
>>  	hcd->usb_phy = devm_usb_get_phy_by_phandle(sysdev, "usb-phy", 0);
>>  	if (IS_ERR(hcd->usb_phy)) {
>>  		ret = PTR_ERR(hcd->usb_phy);
>> diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c
>> index 2424d30..0b7755b 100644
>> --- a/drivers/usb/host/xhci.c
>> +++ b/drivers/usb/host/xhci.c
>> @@ -586,11 +586,8 @@ int xhci_run(struct usb_hcd *hcd)
>>  			"// Set the interrupt modulation register");
>>  	temp = readl(&xhci->ir_set->irq_control);
>>  	temp &= ~ER_IRQ_INTERVAL_MASK;
>> -	/*
>> -	 * the increment interval is 8 times as much as that defined
>> -	 * in xHCI spec on MTK's controller
>> -	 */
>> -	temp |= (u32) ((xhci->quirks & XHCI_MTK_HOST) ? 20 : 160);
>> +	temp |= (xhci->imod_interval / 250) & ER_IRQ_INTERVAL_MASK;
>> +
>>  	writel(temp, &xhci->ir_set->irq_control);
>>  
>>  	/* Set the HCD state before we enable the irqs */
>> diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h
>> index 99a014a..2a4177b 100644
>> --- a/drivers/usb/host/xhci.h
>> +++ b/drivers/usb/host/xhci.h
>> @@ -1717,6 +1717,8 @@ struct xhci_hcd {
>>  	u8		max_interrupters;
>>  	u8		max_ports;
>>  	u8		isoc_threshold;
>> +	/* imod_interval in ns (I * 250ns) */
>> +	u32		imod_interval;
>>  	int		event_ring_max;
>>  	/* 4KB min, 128MB max */
>>  	int		page_size;
> 
> 
> 
> _______________________________________________
> linux-arm-kernel mailing list
> linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r@public.gmane.org
> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
> 


-- 
Adam Wallis
Qualcomm Datacenter Technologies as an affiliate of Qualcomm Technologies, Inc.
Qualcomm Technologies, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project.
--
To unsubscribe from this list: send the line "unsubscribe linux-usb" 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 v3] usb: xhci: allow imod-interval to be configurable
From: Adam Wallis @ 2017-12-04 13:37 UTC (permalink / raw)
  To: Mathias Nyman, Chunfeng Yun
  Cc: Greg Kroah-Hartman, Rob Herring, Mark Rutland, Matthias Brugger,
	Mathias Nyman, linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	linux-usb-u79uwXL29TY76Z2rM5mHXA,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-mediatek-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	timur-sgV2jX0FEOL9JmXXK+q4OQ
In-Reply-To: <82fc73c9-96d5-adc5-e2d1-d777ed861119-VuQAYsv1563Yd54FQh9/CA@public.gmane.org>

On 12/4/2017 3:57 AM, Mathias Nyman wrote:
> On 03.12.2017 05:22, Chunfeng Yun wrote:
>> On Fri, 2017-12-01 at 10:44 -0500, Adam Wallis wrote:
>>> The xHCI driver currently has the IMOD set to 160, which
>>> translates to an IMOD interval of 40,000ns (160 * 250)ns
>>>
>>> Commit 0cbd4b34cda9 ("xhci: mediatek: support MTK xHCI host controller")
>>> introduced a QUIRK for the MTK platform to adjust this interval to 20,
>>> which translates to an IMOD interval of 5,000ns (20 * 250)ns. This is
>>> due to the fact that the MTK controller IMOD interval is 8 times
>>> as much as defined in xHCI spec.
>>>
>>> Instead of adding more quirk bits for additional platforms, this patch
>>> introduces the ability for vendors to set the IMOD_INTERVAL as is
>>> optimal for their platform. By using device_property_read_u32() on
>>> "imod-interval", the IMOD INTERVAL can be specified in nano seconds. If
>>> no interval is specified, the default of 40,000ns (IMOD=160) will be
>>> used.
>>>
>>> No bounds checking has been implemented due to the fact that a vendor
>>> may have violated the spec and would need to specify a value outside of
>>> the max 8,000 IRQs/second limit specified in the xHCI spec.
>>>
>>> Signed-off-by: Adam Wallis <awallis-sgV2jX0FEOL9JmXXK+q4OQ@public.gmane.org>
>>> ---
>>> changes from v2:
>>>    * Added PCI default value [Mathias]
>>>    * Removed xhci-mtk.h from xhci-plat.c [Chunfeng Yun]
>>>    * Removed MTK quirk from xhci-plat and moved logic to xhci-mtk [Chunfeng]
>>>    * Updated bindings Documentation to use proper units [Rob Herring]
>>>    * Added imod-interval description and example to MTK binding documentation
>>> changes from v1:
>>>    * Removed device_property_read_u32() per suggestion from greg k-h
>>>    * Used ER_IRQ_INTERVAL_MASK in place of (u16) cast
>>>
>>>   Documentation/devicetree/bindings/usb/mediatek,mtk-xhci.txt | 2 ++
>>>   Documentation/devicetree/bindings/usb/usb-xhci.txt          | 1 +
>>>   drivers/usb/host/xhci-mtk.c                                 | 9 +++++++++
>>>   drivers/usb/host/xhci-pci.c                                 | 3 +++
>>>   drivers/usb/host/xhci-plat.c                                | 4 ++++
>>>   drivers/usb/host/xhci.c                                     | 7 ++-----
>>>   drivers/usb/host/xhci.h                                     | 2 ++
>>>   7 files changed, 23 insertions(+), 5 deletions(-)
>>>
>>> diff --git a/Documentation/devicetree/bindings/usb/mediatek,mtk-xhci.txt
>>> b/Documentation/devicetree/bindings/usb/mediatek,mtk-xhci.txt
>>> index 3059596..45bbf18 100644
>>> --- a/Documentation/devicetree/bindings/usb/mediatek,mtk-xhci.txt
>>> +++ b/Documentation/devicetree/bindings/usb/mediatek,mtk-xhci.txt
>>> @@ -46,6 +46,7 @@ Optional properties:
>>>    - pinctrl-names : a pinctrl state named "default" must be defined
>>>    - pinctrl-0 : pin control group
>>>       See: Documentation/devicetree/bindings/pinctrl/pinctrl-bindings.txt
>>> + - imod-interval: Default interval is 5000ns
>> I think, as Rob suggested before, recommend to have a unit suffix
>> appended to the property name.
>> s/imod-interval/imod-interval-ns
>>  
>>>     Example:
>>>   usb30: usb@11270000 {
>>> @@ -66,6 +67,7 @@ usb30: usb@11270000 {
>>>       usb3-lpm-capable;
>>>       mediatek,syscon-wakeup = <&pericfg>;
>>>       mediatek,wakeup-src = <1>;
>>> +    imod-interval = <10000>;
>>>   };
>>>     2nd: dual-role mode with xHCI driver
>>> diff --git a/Documentation/devicetree/bindings/usb/usb-xhci.txt
>>> b/Documentation/devicetree/bindings/usb/usb-xhci.txt
>>> index ae6e484..89b68f1 100644
>>> --- a/Documentation/devicetree/bindings/usb/usb-xhci.txt
>>> +++ b/Documentation/devicetree/bindings/usb/usb-xhci.txt
>>> @@ -29,6 +29,7 @@ Optional properties:
>>>     - usb2-lpm-disable: indicate if we don't want to enable USB2 HW LPM
>>>     - usb3-lpm-capable: determines if platform is USB3 LPM capable
>>>     - quirk-broken-port-ped: set if the controller has broken port disable
>>> mechanism
>>> +  - imod-interval: Default interval is 40000ns
>>>     Example:
>>>       usb@f0931000 {
>>> diff --git a/drivers/usb/host/xhci-mtk.c b/drivers/usb/host/xhci-mtk.c
>>> index b62a1d2..278ea3b 100644
>>> --- a/drivers/usb/host/xhci-mtk.c
>>> +++ b/drivers/usb/host/xhci-mtk.c
>>> @@ -674,6 +674,15 @@ static int xhci_mtk_probe(struct platform_device *pdev)
>>>         xhci = hcd_to_xhci(hcd);
>>>       xhci->main_hcd = hcd;
>>> +
>>> +    /*
>>> +     * imod_interval is the interrupt modulation value in nanoseconds.
>>> +     * The increment interval is 8 times as much as that defined in
>>> +     * the xHCI spec on MTK's controller.
>>> +     */
>>> +    xhci->imod_interval = 5000;
>>> +    device_property_read_u32(dev, "imod-interval", &xhci->imod_interval);
>>> +
>>>       xhci->shared_hcd = usb_create_shared_hcd(driver, dev,
>>>               dev_name(dev), hcd);
>>>       if (!xhci->shared_hcd) {
>>> diff --git a/drivers/usb/host/xhci-pci.c b/drivers/usb/host/xhci-pci.c
>>> index 7ef1274..efbe57b 100644
>>> --- a/drivers/usb/host/xhci-pci.c
>>> +++ b/drivers/usb/host/xhci-pci.c
>>> @@ -234,6 +234,9 @@ static int xhci_pci_setup(struct usb_hcd *hcd)
>>>       if (!xhci->sbrn)
>>>           pci_read_config_byte(pdev, XHCI_SBRN_OFFSET, &xhci->sbrn);
>>>   +    /* imod_interval is the interrupt modulation value in nanoseconds. */
>>> +    xhci->imod_interval = 40000;
>>> +
>>>       retval = xhci_gen_setup(hcd, xhci_pci_quirks);
>>>       if (retval)
>>>           return retval;
>>> diff --git a/drivers/usb/host/xhci-plat.c b/drivers/usb/host/xhci-plat.c
>>> index 09f164f..b78be87 100644
>>> --- a/drivers/usb/host/xhci-plat.c
>>> +++ b/drivers/usb/host/xhci-plat.c
>>> @@ -269,6 +269,10 @@ static int xhci_plat_probe(struct platform_device *pdev)
>>>       if (device_property_read_bool(&pdev->dev, "quirk-broken-port-ped"))
>>>           xhci->quirks |= XHCI_BROKEN_PORT_PED;
>>>   +    /* imod_interval is the interrupt modulation value in nanoseconds. */
>>> +    xhci->imod_interval = 40000;
>>> +    device_property_read_u32(sysdev, "imod-interval", &xhci->imod_interval);
>>> +
>>>       hcd->usb_phy = devm_usb_get_phy_by_phandle(sysdev, "usb-phy", 0);
>>>       if (IS_ERR(hcd->usb_phy)) {
>>>           ret = PTR_ERR(hcd->usb_phy);
>>> diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c
>>> index 2424d30..0b7755b 100644
>>> --- a/drivers/usb/host/xhci.c
>>> +++ b/drivers/usb/host/xhci.c
>>> @@ -586,11 +586,8 @@ int xhci_run(struct usb_hcd *hcd)
>>>               "// Set the interrupt modulation register");
> 
> Just noticed the driver has all the time incorrectly used the word "modulation"
> instead
> of "moderation".
> 
> If you do the bindings change that Chunfeng pointed out above could you also change
> the "modulation" to "moderation" in this patch.

Sure thing, I will make the change for this patch.

> 
> Don't worry about changing the old ones. There's a cleanup patch on its way that
> will remove most of them anyway.
> 
> Otherwise the xhci parts look good to me.
> 
> -Mathias
> 
> -- 
> To unsubscribe from this list: send the line "unsubscribe linux-usb" in
> the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html


-- 
Adam Wallis
Qualcomm Datacenter Technologies as an affiliate of Qualcomm Technologies, Inc.
Qualcomm Technologies, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project.
--
To unsubscribe from this list: send the line "unsubscribe linux-usb" 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 v3 2/3] hwrng: exynos - add Samsung Exynos True RNG driver
From: Krzysztof Kozlowski @ 2017-12-04 13:19 UTC (permalink / raw)
  To: Łukasz Stelmach
  Cc: Andrew F . Davis, PrasannaKumar Muralidharan, Rob Herring,
	Matt Mackall, Herbert Xu, Kukjin Kim, devicetree, linux-crypto,
	linux-samsung-soc, linux-kernel, Marek Szyprowski,
	Bartlomiej Zolnierkiewicz
In-Reply-To: <20171204125351.26805-3-l.stelmach@samsung.com>

On Mon, Dec 4, 2017 at 1:53 PM, Łukasz Stelmach <l.stelmach@samsung.com> wrote:
> Add support for True Random Number Generator found in Samsung Exynos
> 5250+ SoCs.
>
> Signed-off-by: Łukasz Stelmach <l.stelmach@samsung.com>
> ---
>  MAINTAINERS                          |   7 +
>  drivers/char/hw_random/Kconfig       |  12 ++
>  drivers/char/hw_random/Makefile      |   1 +
>  drivers/char/hw_random/exynos-trng.c | 245 +++++++++++++++++++++++++++++++++++
>  4 files changed, 265 insertions(+)
>  create mode 100644 drivers/char/hw_random/exynos-trng.c

Reviewed-by: Krzysztof Kozlowski <krzk@kernel.org>

Best regards,
Krzysztof

^ permalink raw reply

* Re: [PATCH v3 1/3] dt-bindings: hwrng: Add Samsung Exynos 5250+ True RNG bindings
From: Krzysztof Kozlowski @ 2017-12-04 13:13 UTC (permalink / raw)
  To: Łukasz Stelmach
  Cc: Andrew F . Davis, PrasannaKumar Muralidharan, Rob Herring,
	Matt Mackall, Herbert Xu, Kukjin Kim, devicetree, linux-crypto,
	linux-samsung-soc, linux-kernel, Marek Szyprowski,
	Bartlomiej Zolnierkiewicz
In-Reply-To: <20171204125351.26805-2-l.stelmach@samsung.com>

On Mon, Dec 4, 2017 at 1:53 PM, Łukasz Stelmach <l.stelmach@samsung.com> wrote:
> Add binding documentation for the True Random Number Generator
> found on Samsung Exynos 5250+ SoCs.
>
> Signed-off-by: Łukasz Stelmach <l.stelmach@samsung.com>
> ---
>  .../devicetree/bindings/rng/samsung,exynos5250-trng.txt | 17 +++++++++++++++++
>  1 file changed, 17 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/rng/samsung,exynos5250-trng.txt
>
> diff --git a/Documentation/devicetree/bindings/rng/samsung,exynos5250-trng.txt b/Documentation/devicetree/bindings/rng/samsung,exynos5250-trng.txt
> new file mode 100644
> index 000000000000..5a613a4ec780
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/rng/samsung,exynos5250-trng.txt
> @@ -0,0 +1,17 @@
> +Exynos True Random Number Generator
> +
> +Required properties:
> +
> +- compatible  : Should be "samsung,exynos5250-trng".
> +- reg         : Specifies base physical address and size of the registers map.
> +- clocks      : Phandle to clock-controller plus clock-specifier pair.
> +- clock-names : "secss" as a clock name.
> +
> +Example:
> +
> +       rng@10830600 {
> +               compatible = "samsung,exynos5250-trng";
> +               reg = <0x10830600 0x100>;
> +               clocks = <&clock CLK_SSS>;
> +               clock-names = "secss";
> +       };
> --
> 2.11.0

Mine and Rob's tags disappeared and I think you did not introduce any
major changes here, right?

Best regards,
Krzysztof

^ permalink raw reply

* Re: [PATCH] dt-bindings: mtd: fsl-quadspi: Pass the qspi clock names
From: Cyrille Pitchen @ 2017-12-04 12:59 UTC (permalink / raw)
  To: Fabio Estevam, boris.brezillon-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8
  Cc: linux-mtd-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	robh+dt-DgEjT+Ai2ygdnm+yROfE0A, devicetree-u79uwXL29TY76Z2rM5mHXA,
	Fabio Estevam
In-Reply-To: <1512340584-26894-1-git-send-email-festevam-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>

Le 03/12/2017 à 23:36, Fabio Estevam a écrit :
> From: Fabio Estevam <fabio.estevam-3arQi8VN3Tc@public.gmane.org>
> 
> In order to improve the bindings documentation, explicitly pass the name
> of the clocks: "qspi_en" and "qspi", which are mandatory.
> 
> Signed-off-by: Fabio Estevam <fabio.estevam-3arQi8VN3Tc@public.gmane.org>

Acked-by: Cyrille Pitchen <cyrille.pitchen-yU5RGvR974pGWvitb5QawA@public.gmane.org>

> ---
>  Documentation/devicetree/bindings/mtd/fsl-quadspi.txt | 2 +-
>  1 file changed, 1 insertion(+), 1 deletion(-)
> 
> diff --git a/Documentation/devicetree/bindings/mtd/fsl-quadspi.txt b/Documentation/devicetree/bindings/mtd/fsl-quadspi.txt
> index c34aa6f..cc3f579 100644
> --- a/Documentation/devicetree/bindings/mtd/fsl-quadspi.txt
> +++ b/Documentation/devicetree/bindings/mtd/fsl-quadspi.txt
> @@ -12,7 +12,7 @@ Required properties:
>    - reg-names: Should contain the reg names "QuadSPI" and "QuadSPI-memory"
>    - interrupts : Should contain the interrupt for the device
>    - clocks : The clocks needed by the QuadSPI controller
> -  - clock-names : the name of the clocks
> +  - clock-names : Should contain the name of the clocks: "qspi_en" and "qspi".
>  
>  Optional properties:
>    - fsl,qspi-has-second-chip: The controller has two buses, bus A and bus B.
> 

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

^ permalink raw reply

* [PATCH v3 3/3] ARM: dts: exynos: Add nodes for True Random Number Generator
From: Łukasz Stelmach @ 2017-12-04 12:53 UTC (permalink / raw)
  To: Andrew F . Davis, PrasannaKumar Muralidharan, Rob Herring,
	Matt Mackall, Herbert Xu, Krzysztof Kozlowski, Kukjin Kim,
	devicetree, linux-crypto, linux-samsung-soc, linux-kernel
  Cc: Łukasz Stelmach, Marek Szyprowski, Bartlomiej Zolnierkiewicz
In-Reply-To: <20171204120429.22892-1-l.stelmach@samsung.com>

Add nodes for the True Random Number Generator found in Samsung Exynos
5250+ SoCs.

Signed-off-by: Łukasz Stelmach <l.stelmach@samsung.com>
---
 arch/arm/boot/dts/exynos5.dtsi    | 5 +++++
 arch/arm/boot/dts/exynos5250.dtsi | 5 +++++
 arch/arm/boot/dts/exynos5410.dtsi | 5 +++++
 arch/arm/boot/dts/exynos5420.dtsi | 5 +++++
 4 files changed, 20 insertions(+)

diff --git a/arch/arm/boot/dts/exynos5.dtsi b/arch/arm/boot/dts/exynos5.dtsi
index 33f929c1dda9..e0c91ff4442c 100644
--- a/arch/arm/boot/dts/exynos5.dtsi
+++ b/arch/arm/boot/dts/exynos5.dtsi
@@ -215,5 +215,10 @@
 		      compatible = "samsung,exynos5250-prng";
 		      reg = <0x10830400 0x200>;
 		};
+
+		trng: rng@10830600 {
+		      compatible = "samsung,exynos5250-trng";
+		      reg = <0x10830600 0x100>;
+		};
 	};
 };
diff --git a/arch/arm/boot/dts/exynos5250.dtsi b/arch/arm/boot/dts/exynos5250.dtsi
index 51aa83ba8c87..38627e8164a0 100644
--- a/arch/arm/boot/dts/exynos5250.dtsi
+++ b/arch/arm/boot/dts/exynos5250.dtsi
@@ -1086,4 +1086,9 @@
 	clock-names = "secss";
 };
 
+&trng {
+	clocks = <&clock CLK_SSS>;
+	clock-names = "secss";
+};
+
 #include "exynos5250-pinctrl.dtsi"
diff --git a/arch/arm/boot/dts/exynos5410.dtsi b/arch/arm/boot/dts/exynos5410.dtsi
index 1604cb1b837d..aa8b14eda662 100644
--- a/arch/arm/boot/dts/exynos5410.dtsi
+++ b/arch/arm/boot/dts/exynos5410.dtsi
@@ -384,6 +384,11 @@
 		  3 0 0x07000000 0x20000>;
 };
 
+&trng {
+	clocks = <&clock CLK_SSS>;
+	clock-names = "secss";
+};
+
 &usbdrd3_0 {
 	clocks = <&clock CLK_USBD300>;
 	clock-names = "usbdrd30";
diff --git a/arch/arm/boot/dts/exynos5420.dtsi b/arch/arm/boot/dts/exynos5420.dtsi
index 31c77ea9123d..6c8cec9d564a 100644
--- a/arch/arm/boot/dts/exynos5420.dtsi
+++ b/arch/arm/boot/dts/exynos5420.dtsi
@@ -1459,6 +1459,11 @@
 	clock-names = "secss";
 };
 
+&trng {
+	clocks = <&clock CLK_SSS>;
+	clock-names = "secss";
+};
+
 &usbdrd3_0 {
 	clocks = <&clock CLK_USBD300>;
 	clock-names = "usbdrd30";
-- 
2.11.0

^ permalink raw reply related

* [PATCH v3 2/3] hwrng: exynos - add Samsung Exynos True RNG driver
From: Łukasz Stelmach @ 2017-12-04 12:53 UTC (permalink / raw)
  To: Andrew F . Davis, PrasannaKumar Muralidharan, Rob Herring,
	Matt Mackall, Herbert Xu, Krzysztof Kozlowski, Kukjin Kim,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-crypto-u79uwXL29TY76Z2rM5mHXA,
	linux-samsung-soc-u79uwXL29TY76Z2rM5mHXA,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA
  Cc: Łukasz Stelmach, Marek Szyprowski, Bartlomiej Zolnierkiewicz
In-Reply-To: <20171204120429.22892-1-l.stelmach-Sze3O3UU22JBDgjK7y7TUQ@public.gmane.org>

Add support for True Random Number Generator found in Samsung Exynos
5250+ SoCs.

Signed-off-by: Łukasz Stelmach <l.stelmach-Sze3O3UU22JBDgjK7y7TUQ@public.gmane.org>
---
 MAINTAINERS                          |   7 +
 drivers/char/hw_random/Kconfig       |  12 ++
 drivers/char/hw_random/Makefile      |   1 +
 drivers/char/hw_random/exynos-trng.c | 245 +++++++++++++++++++++++++++++++++++
 4 files changed, 265 insertions(+)
 create mode 100644 drivers/char/hw_random/exynos-trng.c

diff --git a/MAINTAINERS b/MAINTAINERS
index 2811a211632c..992074cca612 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -11780,6 +11780,13 @@ S:	Maintained
 F:	drivers/crypto/exynos-rng.c
 F:	Documentation/devicetree/bindings/rng/samsung,exynos-rng4.txt
 
+SAMSUNG EXYNOS TRUE RANDOM NUMBER GENERATOR (TRNG) DRIVER
+M:	Łukasz Stelmach <l.stelmach-Sze3O3UU22JBDgjK7y7TUQ@public.gmane.org>
+L:	linux-samsung-soc-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
+S:	Maintained
+F:	drivers/char/hw_random/exynos-trng.c
+F:	Documentation/devicetree/bindings/rng/samsung,exynos5250-trng.txt
+
 SAMSUNG FRAMEBUFFER DRIVER
 M:	Jingoo Han <jingoohan1-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
 L:	linux-fbdev-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
diff --git a/drivers/char/hw_random/Kconfig b/drivers/char/hw_random/Kconfig
index 95a031e9eced..292e6b36d493 100644
--- a/drivers/char/hw_random/Kconfig
+++ b/drivers/char/hw_random/Kconfig
@@ -449,6 +449,18 @@ config HW_RANDOM_S390
 
 	  If unsure, say Y.
 
+config HW_RANDOM_EXYNOS
+	tristate "Samsung Exynos True Random Number Generator support"
+	depends on ARCH_EXYNOS || COMPILE_TEST
+	default HW_RANDOM
+	---help---
+	  This driver provides support for the True Random Number
+	  Generator available in Exynos SoCs.
+
+	  To compile this driver as a module, choose M here: the module
+	  will be called exynos-trng.
+
+	  If unsure, say Y.
 endif # HW_RANDOM
 
 config UML_RANDOM
diff --git a/drivers/char/hw_random/Makefile b/drivers/char/hw_random/Makefile
index f3728d008fff..5595df97da7a 100644
--- a/drivers/char/hw_random/Makefile
+++ b/drivers/char/hw_random/Makefile
@@ -14,6 +14,7 @@ obj-$(CONFIG_HW_RANDOM_GEODE) += geode-rng.o
 obj-$(CONFIG_HW_RANDOM_N2RNG) += n2-rng.o
 n2-rng-y := n2-drv.o n2-asm.o
 obj-$(CONFIG_HW_RANDOM_VIA) += via-rng.o
+obj-$(CONFIG_HW_RANDOM_EXYNOS) += exynos-trng.o
 obj-$(CONFIG_HW_RANDOM_IXP4XX) += ixp4xx-rng.o
 obj-$(CONFIG_HW_RANDOM_OMAP) += omap-rng.o
 obj-$(CONFIG_HW_RANDOM_OMAP3_ROM) += omap3-rom-rng.o
diff --git a/drivers/char/hw_random/exynos-trng.c b/drivers/char/hw_random/exynos-trng.c
new file mode 100644
index 000000000000..971d2fe9d55a
--- /dev/null
+++ b/drivers/char/hw_random/exynos-trng.c
@@ -0,0 +1,245 @@
+/*
+ * RNG driver for Exynos TRNGs
+ *
+ * Author: Łukasz Stelmach <l.stelmach-Sze3O3UU22JBDgjK7y7TUQ@public.gmane.org>
+ *
+ * Copyright 2017 (c) Samsung Electronics Software, Inc.
+ *
+ * Based on the Exynos PRNG driver drivers/crypto/exynos-rng by
+ * Krzysztof Kozłowski <krzk-DgEjT+Ai2ygdnm+yROfE0A@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 as published by
+ * the Free Software Foundation;
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/clk.h>
+#include <linux/crypto.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/hw_random.h>
+#include <linux/io.h>
+#include <linux/iopoll.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+
+#define EXYNOS_TRNG_CLKDIV         (0x0)
+
+#define EXYNOS_TRNG_CTRL           (0x20)
+#define EXYNOS_TRNG_CTRL_RNGEN     BIT(31)
+
+#define EXYNOS_TRNG_POST_CTRL      (0x30)
+#define EXYNOS_TRNG_ONLINE_CTRL    (0x40)
+#define EXYNOS_TRNG_ONLINE_STAT    (0x44)
+#define EXYNOS_TRNG_ONLINE_MAXCHI2 (0x48)
+#define EXYNOS_TRNG_FIFO_CTRL      (0x50)
+#define EXYNOS_TRNG_FIFO_0         (0x80)
+#define EXYNOS_TRNG_FIFO_1         (0x84)
+#define EXYNOS_TRNG_FIFO_2         (0x88)
+#define EXYNOS_TRNG_FIFO_3         (0x8c)
+#define EXYNOS_TRNG_FIFO_4         (0x90)
+#define EXYNOS_TRNG_FIFO_5         (0x94)
+#define EXYNOS_TRNG_FIFO_6         (0x98)
+#define EXYNOS_TRNG_FIFO_7         (0x9c)
+#define EXYNOS_TRNG_FIFO_LEN       (8)
+#define EXYNOS_TRNG_CLOCK_RATE     (500000)
+
+
+struct exynos_trng_dev {
+	struct device    *dev;
+	void __iomem     *mem;
+	struct clk       *clk;
+	struct hwrng rng;
+};
+
+static int exynos_trng_do_read(struct hwrng *rng, void *data, size_t max,
+			       bool wait)
+{
+	struct exynos_trng_dev *trng;
+	u32 val;
+
+	max = min_t(size_t, max, (EXYNOS_TRNG_FIFO_LEN * 4));
+
+	trng = (struct exynos_trng_dev *)rng->priv;
+
+	writel_relaxed(max * 8, trng->mem + EXYNOS_TRNG_FIFO_CTRL);
+	val = readl_poll_timeout(trng->mem + EXYNOS_TRNG_FIFO_CTRL, val,
+				 val == 0, 200, 1000000);
+	if (val < 0)
+		return val;
+
+	memcpy_fromio(data, trng->mem + EXYNOS_TRNG_FIFO_0, max);
+
+	return max;
+}
+
+static int exynos_trng_init(struct hwrng *rng)
+{
+	struct exynos_trng_dev *trng = (struct exynos_trng_dev *)rng->priv;
+	unsigned long sss_rate;
+	u32 val;
+
+	sss_rate = clk_get_rate(trng->clk);
+
+	/*
+	 * For most TRNG circuits the clock frequency of under 500 kHz
+	 * is safe.
+	 */
+	val = sss_rate / (EXYNOS_TRNG_CLOCK_RATE * 2);
+	if (val > 0x7fff) {
+		dev_err(trng->dev, "clock divider too large: %d", val);
+		return -ERANGE;
+	}
+	val = val << 1;
+	writel_relaxed(val, trng->mem + EXYNOS_TRNG_CLKDIV);
+
+	/* Enable the generator. */
+	val = EXYNOS_TRNG_CTRL_RNGEN;
+	writel_relaxed(val, trng->mem + EXYNOS_TRNG_CTRL);
+
+	/*
+	 * Disable post-processing. /dev/hwrng is supposed to deliver
+	 * unprocessed data.
+	 */
+	writel_relaxed(0, trng->mem + EXYNOS_TRNG_POST_CTRL);
+
+	return 0;
+}
+
+static int exynos_trng_probe(struct platform_device *pdev)
+{
+	struct exynos_trng_dev *trng;
+	struct resource *res;
+	int ret = -ENOMEM;
+
+	trng = devm_kzalloc(&pdev->dev, sizeof(*trng), GFP_KERNEL);
+	if (!trng)
+		return ret;
+
+	trng->rng.name = devm_kstrdup(&pdev->dev, dev_name(&pdev->dev),
+				      GFP_KERNEL);
+	if (!trng->rng.name)
+		return ret;
+
+	trng->rng.init = exynos_trng_init;
+	trng->rng.read = exynos_trng_do_read;
+	trng->rng.priv = (unsigned long) trng;
+
+	platform_set_drvdata(pdev, trng);
+	trng->dev = &pdev->dev;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	trng->mem = devm_ioremap_resource(&pdev->dev, res);
+	if (IS_ERR(trng->mem)) {
+		dev_err(&pdev->dev, "Could not map IO resources.\n");
+		return PTR_ERR(trng->mem);
+	}
+
+	pm_runtime_enable(&pdev->dev);
+	ret = pm_runtime_get_sync(&pdev->dev);
+	if (ret < 0) {
+		dev_err(&pdev->dev, "Could not get runtime PM.\n");
+		goto err_pm_get;
+	}
+
+	trng->clk = devm_clk_get(&pdev->dev, "secss");
+	if (IS_ERR(trng->clk)) {
+		ret = PTR_ERR(trng->clk);
+		dev_err(&pdev->dev, "Could not get clock.\n");
+		goto err_clock;
+	}
+
+	ret = clk_prepare_enable(trng->clk);
+	if (ret) {
+		dev_err(&pdev->dev, "Could not enable the clk.\n");
+		goto err_clock;
+	}
+
+	ret = hwrng_register(&trng->rng);
+	if (ret) {
+		dev_err(&pdev->dev, "Could not register hwrng device.\n");
+		goto err_register;
+	}
+
+	dev_info(&pdev->dev, "Exynos True Random Number Generator.\n");
+
+	return 0;
+
+err_register:
+	clk_disable_unprepare(trng->clk);
+
+err_clock:
+	pm_runtime_put_sync(&pdev->dev);
+
+err_pm_get:
+	pm_runtime_disable(&pdev->dev);
+
+	return ret;
+}
+
+static int exynos_trng_remove(struct platform_device *pdev)
+{
+	struct exynos_trng_dev *trng =  platform_get_drvdata(pdev);
+
+	hwrng_unregister(&trng->rng);
+	clk_disable_unprepare(trng->clk);
+
+	pm_runtime_put_sync(&pdev->dev);
+	pm_runtime_disable(&pdev->dev);
+
+	return 0;
+}
+
+static int __maybe_unused exynos_trng_suspend(struct device *dev)
+{
+	pm_runtime_put_sync(dev);
+
+	return 0;
+}
+
+static int __maybe_unused exynos_trng_resume(struct device *dev)
+{
+	int ret;
+
+	ret = pm_runtime_get_sync(dev);
+	if (ret < 0) {
+		dev_err(dev, "Could not get runtime PM.\n");
+		pm_runtime_put_noidle(dev);
+		return ret;
+	}
+
+	return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(exynos_trng_pm_ops, exynos_trng_suspend,
+			 exynos_trng_resume);
+
+static const struct of_device_id exynos_trng_dt_match[] = {
+	{
+		.compatible = "samsung,exynos5250-trng",
+	},
+	{ },
+};
+MODULE_DEVICE_TABLE(of, exynos_rng_dt_match);
+
+static struct platform_driver exynos_trng_driver = {
+	.driver = {
+		.name = "exynos-trng",
+		.pm = &exynos_trng_pm_ops,
+		.of_match_table = exynos_trng_dt_match,
+	},
+	.probe = exynos_trng_probe,
+	.remove = exynos_trng_remove,
+};
+
+module_platform_driver(exynos_trng_driver);
+MODULE_AUTHOR("Łukasz Stelmach");
+MODULE_DESCRIPTION("H/W TRNG driver for Exynos chips");
+MODULE_LICENSE("GPL");
-- 
2.11.0

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

^ permalink raw reply related

* [PATCH v3 1/3] dt-bindings: hwrng: Add Samsung Exynos 5250+ True RNG bindings
From: Łukasz Stelmach @ 2017-12-04 12:53 UTC (permalink / raw)
  To: Andrew F . Davis, PrasannaKumar Muralidharan, Rob Herring,
	Matt Mackall, Herbert Xu, Krzysztof Kozlowski, Kukjin Kim,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-crypto-u79uwXL29TY76Z2rM5mHXA,
	linux-samsung-soc-u79uwXL29TY76Z2rM5mHXA,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA
  Cc: Łukasz Stelmach, Marek Szyprowski, Bartlomiej Zolnierkiewicz
In-Reply-To: <20171204120429.22892-1-l.stelmach-Sze3O3UU22JBDgjK7y7TUQ@public.gmane.org>

Add binding documentation for the True Random Number Generator
found on Samsung Exynos 5250+ SoCs.

Signed-off-by: Łukasz Stelmach <l.stelmach-Sze3O3UU22JBDgjK7y7TUQ@public.gmane.org>
---
 .../devicetree/bindings/rng/samsung,exynos5250-trng.txt | 17 +++++++++++++++++
 1 file changed, 17 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/rng/samsung,exynos5250-trng.txt

diff --git a/Documentation/devicetree/bindings/rng/samsung,exynos5250-trng.txt b/Documentation/devicetree/bindings/rng/samsung,exynos5250-trng.txt
new file mode 100644
index 000000000000..5a613a4ec780
--- /dev/null
+++ b/Documentation/devicetree/bindings/rng/samsung,exynos5250-trng.txt
@@ -0,0 +1,17 @@
+Exynos True Random Number Generator
+
+Required properties:
+
+- compatible  : Should be "samsung,exynos5250-trng".
+- reg         : Specifies base physical address and size of the registers map.
+- clocks      : Phandle to clock-controller plus clock-specifier pair.
+- clock-names : "secss" as a clock name.
+
+Example:
+
+	rng@10830600 {
+		compatible = "samsung,exynos5250-trng";
+		reg = <0x10830600 0x100>;
+		clocks = <&clock CLK_SSS>;
+		clock-names = "secss";
+	};
-- 
2.11.0

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

^ permalink raw reply related


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