Devicetree
 help / color / mirror / Atom feed
* [PATCH v3 0/4] tty/serial: meson_uart: add support for core clock handling
From: Helmut Klein @ 2017-03-31 16:54 UTC (permalink / raw)
  Cc: Helmut Klein, linux-clk, linux-serial, linux-amlogic,
	linux-arm-kernel, linux-kernel, devicetree

To be able to use the three none AO uarts of the meson gx SoCs (uart_A,
uart_B & uart_C), the core clock has to be enabled (see chapter 22.3 of
the public s905 data sheet).
At least the u-boot of my s905 based media player (netxeon minimx-g)
doesn't do this. so the driver must enable the clock.

This patch set does:
- exposes the UART clock ids to the dtb
- adds documentation for the dt-bindings of meson_uart
- adds the core clock handling to the driver
- adds the core clock handling to meson-gxbb.dtsi and meson-gxl.dtsi

The patchset is based on the branch "master" of the repository in [1]

Changes since v2
- mail subjects reworked
- add clocks/clock-names to the documentation
- add core clock handling to meson-gxbb.dtsi & meson-gxl.dtsi

Changes since v1
- use git to produce the patch set
- added the clock ids for uart_B and uart_C

[1] git://git.kernel.org/pub/scm/linux/kernel/git/khilman/linux-amlogic.git

Helmut Klein (4):
  clk: meson: gxbb: expose CLKID_UARTx
  dt-bindings: meson_uart: add documentation for meson UARTs
  tty/serial: meson_uart: add the core clock handling to the driver
  ARM64: dts: meson-gx: add core clock support for uart_A, uart_B and
    uart_C

 .../bindings/serial/amlogic,meson_uart.txt         | 30 ++++++++++++++++++++++
 arch/arm64/boot/dts/amlogic/meson-gxbb.dtsi        | 15 +++++++++++
 arch/arm64/boot/dts/amlogic/meson-gxl.dtsi         | 15 +++++++++++
 drivers/clk/meson/gxbb.h                           |  6 ++---
 drivers/tty/serial/meson_uart.c                    | 10 ++++++++
 include/dt-bindings/clock/gxbb-clkc.h              |  3 +++
 6 files changed, 76 insertions(+), 3 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/serial/amlogic,meson_uart.txt

--
2.11.0


^ permalink raw reply

* Re: [PATCH v2 2/2] ARM: dts: sun7i: Add CAN node and can0_pins_a pinctrl settings
From: Patrick Menschel @ 2017-03-31 16:51 UTC (permalink / raw)
  To: Maxime Ripard
  Cc: robh+dt, mark.rutland, linux, wens, devicetree, linux-arm-kernel,
	linux-kernel, linux-can
In-Reply-To: <20170327064625.we7ydn2oapwizncb@lukather>

[-- Attachment #1: Type: text/plain, Size: 901 bytes --]

Hello Maxime,

Am 27.03.2017 um 08:46 schrieb Maxime Ripard:
>> @@ -1582,6 +1587,14 @@
>>  			#size-cells = <0>;
>>  		};
>>  
>> +		can0: can@01c2bc00 {
>> +			compatible = "allwinner,sun4i-a10-can";
> 
> Sorry for not spotting this earlier, but this would need an A20
> compatible too, to deal with the case where it turns out not to be
> compatible.
> 
> you can do something like this:
> compatible = "allwinner,sun7i-a20-can", "allwinner,sun4i-a10-can";

Adding "sun7i-a20-can" to the compatible list results in a checkpatch
warning, that the "driver" is not documented.

 WARNING: DT compatible string "allwinner,sun7i-a20-can" appears
un-documented -- check ./Documentation/devicetree/bindings/
#27: FILE: arch/arm/boot/dts/sun7i-a20.dtsi:1586:
+			compatible = "allwinner,sun7i-a20-can",

Is it really OK to add a driver that doesn't exist yet?

Thanks,
Patrick


[-- Attachment #2: S/MIME Cryptographic Signature --]
[-- Type: application/pkcs7-signature, Size: 3709 bytes --]

^ permalink raw reply

* Re: [PATCH] ARM: dts: armada-38x: label USB and SATA nodes
From: Andrew Lunn @ 2017-03-31 16:50 UTC (permalink / raw)
  To: Ralph Sennhauser
  Cc: linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, Jason Cooper,
	Gregory Clement, Sebastian Hesselbarth, Rob Herring, Mark Rutland,
	Russell King, devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <20170331074115.8111-1-ralph.sennhauser-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>

> -			sata@a8000 {
> +			satac0: sata@a8000 {

Hi Ralph

Why the c in satac0?

>  
> -			usb3@f0000 {
> +			usb3_0: usb3@f0000 {
>  				compatible = "marvell,armada-380-xhci";
>  				reg = <0xf0000 0x4000>,<0xf4000 0x4000>;
>  				interrupts = <GIC_SPI 16 IRQ_TYPE_LEVEL_HIGH>;
> @@ -598,7 +598,7 @@
>  				status = "disabled";
>  			};
>  
> -			usb3@f8000 {
> +			usb3_1: usb3@f8000 {
>  				compatible = "marvell,armada-380-xhci";
>  				reg = <0xf8000 0x4000>,<0xfc000 0x4000>;
>  				interrupts = <GIC_SPI 17 IRQ_TYPE_LEVEL_HIGH>;

I can understand what you are saying. But does anybody else care? Are
there other .dtsi files differentiating between USB 1.1, 2 and 3?

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

^ permalink raw reply

* Re: [PATCH v2,1/3] meson_uart: expose CLKID_UARTx
From: Jerome Brunet @ 2017-03-31 15:37 UTC (permalink / raw)
  To: Kevin Hilman, Martin Blumenstingl
  Cc: Helmut Klein, mturquette, sboyd, devicetree, linux-clk,
	linux-kernel, linux-arm-kernel, linux-amlogic
In-Reply-To: <m2y3vn6dhk.fsf@baylibre.com>

On Wed, 2017-03-29 at 13:21 -0700, Kevin Hilman wrote:
> Martin Blumenstingl <martin.blumenstingl@googlemail.com> writes:
> 
> [...]
> 
> > for Helmut this would mean that instead of dropping this patch (or
> > dropping CLKID_UART1 and CLKID_UART2 from this patch) he would rather
> > have to *add* another patch (for meson-gxbb.dtsi and meson-gxl.dtsi)
> > which passes the core clocks to the corresponding UART controllers
> > (similar to the CLKID_SD_EMMC_ clocks).
> 
> Yes, this is what I would like to see.
> 
> If a new CLKID is exposed, I want to see the users of it at the same
> time.

Helmut,

If you send another version of these patches, considering the feedback of Kevin
and Martin, could you please change the subject of this patch to:

dt-bindings: clock: gxbb: expose UART clocks

Thx
Jerome

> 
> Kevin

^ permalink raw reply

* Re: [PATCH] ARM: dts: armada-385-linksys: disk-activity trigger for all
From: Andrew Lunn @ 2017-03-31 15:35 UTC (permalink / raw)
  To: Ralph Sennhauser
  Cc: linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, Jason Cooper,
	Gregory Clement, Sebastian Hesselbarth, Rob Herring, Mark Rutland,
	Russell King, devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <20170330165405.14540-1-ralph.sennhauser-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>

On Thu, Mar 30, 2017 at 06:54:04PM +0200, Ralph Sennhauser wrote:
> Commit a4ee7e18d808 ("ARM: dts: armada: Add default trigger for sata
> led") adds the default trigger to individual boards, move it to
> armada-385-linksys.dtsi which effectively enables the definition for
> the WRT1900ACS (Shelby) as well as for future boards.
> 
> Signed-off-by: Ralph Sennhauser <ralph.sennhauser-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>

Centralising this makes sense.

Reviewed-by: Andrew Lunn <andrew-g2DYL2Zd6BY@public.gmane.org>

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

^ permalink raw reply

* Re: [PATCH v2] i2c/muxes/i2c-mux-ltc4306: LTC4306 and LTC4305 I2C multiplexer/switch
From: Peter Rosin @ 2017-03-31 15:29 UTC (permalink / raw)
  To: michael.hennerich, wsa, robh+dt, mark.rutland, linus.walleij
  Cc: linux-i2c, devicetree, linux-gpio, linux-kernel
In-Reply-To: <1490782541-7832-1-git-send-email-michael.hennerich@analog.com>

Hi!

Sorry for my incremental reviewing...

There's a question for the gpio people below that I would like
to have some confirmation on. Thanks!

On 2017-03-29 12:15, michael.hennerich@analog.com wrote:
> From: Michael Hennerich <michael.hennerich@analog.com>
> 
> This patch adds support for the Analog Devices / Linear Technology
> LTC4306 and LTC4305 4/2 Channel I2C Bus Multiplexer/Switches.
> The LTC4306 optionally provides two general purpose input/output pins
> (GPIOs) that can be configured as logic inputs, opendrain outputs or
> push-pull outputs via the generic GPIOLIB framework.
> 
> Signed-off-by: Michael Hennerich <michael.hennerich@analog.com>
> 
> ---
> 
> Changes since v1:
> 
>  - Sort makefile entries
>  - Sort driver includes
>  - Use proper defines
>  - Miscellaneous coding style fixups
>  - Rename mux select callback
>  - Revise i2c-mux-idle-disconnect handling
>  - Add ENABLE GPIO handling on error and device removal.
>  - Remove surplus of_match_device call.
> ---
>  .../devicetree/bindings/i2c/i2c-mux-ltc4306.txt    |  61 ++++
>  MAINTAINERS                                        |   8 +
>  drivers/i2c/muxes/Kconfig                          |  10 +
>  drivers/i2c/muxes/Makefile                         |   1 +
>  drivers/i2c/muxes/i2c-mux-ltc4306.c                | 367 +++++++++++++++++++++
>  5 files changed, 447 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/i2c/i2c-mux-ltc4306.txt
>  create mode 100644 drivers/i2c/muxes/i2c-mux-ltc4306.c
> 
> diff --git a/Documentation/devicetree/bindings/i2c/i2c-mux-ltc4306.txt b/Documentation/devicetree/bindings/i2c/i2c-mux-ltc4306.txt
> new file mode 100644
> index 0000000..1e98c6b
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/i2c/i2c-mux-ltc4306.txt
> @@ -0,0 +1,61 @@
> +* Linear Technology / Analog Devices I2C bus switch
> +
> +Required Properties:
> +
> +  - compatible: Must contain one of the following.
> +    "lltc,ltc4305", "lltc,ltc4306"
> +  - reg: The I2C address of the device.
> +
> +  The following required properties are defined externally:
> +
> +  - Standard I2C mux properties. See i2c-mux.txt in this directory.
> +  - I2C child bus nodes. See i2c-mux.txt in this directory.
> +
> +Optional Properties:
> +
> +  - enable-gpios: Reference to the GPIO connected to the enable input.
> +  - i2c-mux-idle-disconnect: Boolean; if defined, forces mux to disconnect all
> +    children in idle state. This is necessary for example, if there are several
> +    multiplexers on the bus and the devices behind them use same I2C addresses.
> +  - gpio-controller: Marks the device node as a GPIO Controller.
> +  - #gpio-cells: Should be two.  The first cell is the pin number and
> +	the second cell is used to specify flags.
> +	See ../gpio/gpio.txt for more information.
> +  - ltc,downstream-accelerators-enable: Enables the rise time accelerators
> +	on the downstream port.
> +  - ltc,upstream-accelerators-enable: Enables the rise time accelerators
> +	on the upstream port.
> +
> +Example:
> +
> +	ltc4306: i2c-mux@4a {
> +		compatible = "lltc,ltc4306";
> +		#address-cells = <1>;
> +		#size-cells = <0>;
> +		reg = <0x4a>;
> +
> +		gpio-controller;
> +		#gpio-cells = <2>;
> +
> +		i2c@0 {
> +			#address-cells = <1>;
> +			#size-cells = <0>;
> +			reg = <0>;
> +
> +			eeprom@50 {
> +				compatible = "at,24c02";
> +				reg = <0x50>;
> +			};
> +		};
> +
> +		i2c@1 {
> +			#address-cells = <1>;
> +			#size-cells = <0>;
> +			reg = <1>;
> +
> +			eeprom@50 {
> +				compatible = "at,24c02";
> +				reg = <0x50>;
> +			};
> +		};
> +	};
> diff --git a/MAINTAINERS b/MAINTAINERS
> index c776906..9a27a19 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -7698,6 +7698,14 @@ S:	Maintained
>  F:	Documentation/hwmon/ltc4261
>  F:	drivers/hwmon/ltc4261.c
>  
> +LTC4306 I2C MULTIPLEXER DRIVER
> +M:	Michael Hennerich <michael.hennerich@analog.com>
> +W:	http://ez.analog.com/community/linux-device-drivers
> +L:	linux-i2c@vger.kernel.org
> +S:	Supported
> +F:	drivers/i2c/muxes/i2c-mux-ltc4306.c
> +F:	Documentation/devicetree/bindings/i2c/i2c-mux-ltc4306.txt
> +
>  LTP (Linux Test Project)
>  M:	Mike Frysinger <vapier@gentoo.org>
>  M:	Cyril Hrubis <chrubis@suse.cz>
> diff --git a/drivers/i2c/muxes/Kconfig b/drivers/i2c/muxes/Kconfig
> index 10b3d17..f501b3b 100644
> --- a/drivers/i2c/muxes/Kconfig
> +++ b/drivers/i2c/muxes/Kconfig
> @@ -30,6 +30,16 @@ config I2C_MUX_GPIO
>  	  This driver can also be built as a module.  If so, the module
>  	  will be called i2c-mux-gpio.
>  
> +config I2C_MUX_LTC4306
> +	tristate "LTC LTC4306/5 I2C multiplexer"
> +	select GPIOLIB
> +	help
> +	  If you say yes here you get support for the LTC LTC4306 or LTC4305
> +	  I2C mux/switch devices.
> +
> +	  This driver can also be built as a module.  If so, the module
> +	  will be called i2c-mux-ltc4306.
> +
>  config I2C_MUX_PCA9541
>  	tristate "NXP PCA9541 I2C Master Selector"
>  	help
> diff --git a/drivers/i2c/muxes/Makefile b/drivers/i2c/muxes/Makefile
> index 9948fa4..ff7618c 100644
> --- a/drivers/i2c/muxes/Makefile
> +++ b/drivers/i2c/muxes/Makefile
> @@ -6,6 +6,7 @@ obj-$(CONFIG_I2C_ARB_GPIO_CHALLENGE)	+= i2c-arb-gpio-challenge.o
>  obj-$(CONFIG_I2C_DEMUX_PINCTRL)		+= i2c-demux-pinctrl.o
>  
>  obj-$(CONFIG_I2C_MUX_GPIO)	+= i2c-mux-gpio.o
> +obj-$(CONFIG_I2C_MUX_LTC4306)	+= i2c-mux-ltc4306.o
>  obj-$(CONFIG_I2C_MUX_MLXCPLD)	+= i2c-mux-mlxcpld.o
>  obj-$(CONFIG_I2C_MUX_PCA9541)	+= i2c-mux-pca9541.o
>  obj-$(CONFIG_I2C_MUX_PCA954x)	+= i2c-mux-pca954x.o
> diff --git a/drivers/i2c/muxes/i2c-mux-ltc4306.c b/drivers/i2c/muxes/i2c-mux-ltc4306.c
> new file mode 100644
> index 0000000..c15a8a4
> --- /dev/null
> +++ b/drivers/i2c/muxes/i2c-mux-ltc4306.c
> @@ -0,0 +1,367 @@
> +/*
> + * Linear Technology LTC4306 and LTC4305 I2C multiplexer/switch
> + *
> + * Copyright (C) 2017 Analog Devices Inc.
> + *
> + * Licensed under the GPL-2.
> + *
> + * Based on: i2c-mux-pca954x.c
> + *
> + * Datasheet: http://cds.linear.com/docs/en/datasheet/4306.pdf
> + */
> +
> +#include <linux/device.h>
> +#include <linux/gpio.h>
> +#include <linux/gpio/consumer.h>
> +#include <linux/gpio/driver.h>
> +#include <linux/i2c-mux.h>
> +#include <linux/i2c.h>
> +#include <linux/module.h>
> +#include <linux/of.h>
> +#include <linux/of_device.h>
> +#include <linux/slab.h>
> +
> +#define LTC4305_MAX_NCHANS 2
> +#define LTC4306_MAX_NCHANS 4
> +
> +#define LTC_REG_STATUS	0x0
> +#define LTC_REG_CONFIG	0x1
> +#define LTC_REG_MODE	0x2
> +#define LTC_REG_SWITCH	0x3
> +
> +#define LTC_DOWNSTREAM_ACCL_EN BIT(6)
> +#define LTC_UPSTREAM_ACCL_EN BIT(7)

Maybe align the BIT(x) parts with a tab, like you do above...

> +
> +#define LTC_GPIO_ALL_INPUT	0xC0
> +
> +enum ltc_type {
> +	ltc_4305,
> +	ltc_4306,
> +};
> +
> +struct chip_desc {
> +	u8 nchans;
> +	u8 num_gpios;
> +};
> +
> +struct ltc4306 {
> +	struct i2c_client *client;
> +	struct gpio_desc *en_gpio;
> +	struct gpio_chip gpiochip;
> +	const struct chip_desc *chip;
> +	u8 regs[LTC_REG_SWITCH + 1];
> +};
> +
> +/* Provide specs for the PCA954x types we know about */
> +static const struct chip_desc chips[] = {
> +	[ltc_4305] = {
> +		.nchans = LTC4305_MAX_NCHANS,
> +	},
> +	[ltc_4306] = {
> +		.nchans = LTC4306_MAX_NCHANS,
> +		.num_gpios = 2,
> +	},
> +};
> +
> +static int ltc4306_gpio_get(struct gpio_chip *chip, unsigned int offset)
> +{
> +	struct ltc4306 *data = gpiochip_get_data(chip);
> +	int ret = 0;

This assignment is not needed.

> +
> +	if (gpiochip_line_is_open_drain(chip, offset) ||
> +	    (data->regs[LTC_REG_MODE] & BIT(7 - offset))) {

I wonder about this open-coded register cache. So, gpio people, is there
a guarantee from gpiolib that only one gpio_chip operation is in flight
concurrently? Because I don't see any evidence of that. With that in
mind, I think some locking is needed?

> +		/* Open Drain or Input */
> +		ret = i2c_smbus_read_byte_data(data->client, LTC_REG_CONFIG);
> +		if (ret < 0)
> +			return ret;
> +
> +		return !!(ret & BIT(1 - offset));
> +	} else {
> +		return !!(data->regs[LTC_REG_CONFIG] & BIT(5 - offset));
> +	}
> +}
> +
> +static void ltc4306_gpio_set(struct gpio_chip *chip, unsigned int offset,
> +			     int value)
> +{
> +	struct ltc4306 *data = gpiochip_get_data(chip);
> +
> +	if (value)
> +		data->regs[LTC_REG_CONFIG] |= BIT(5 - offset);
> +	else
> +		data->regs[LTC_REG_CONFIG] &= ~BIT(5 - offset);
> +
> +	i2c_smbus_write_byte_data(data->client, LTC_REG_CONFIG,
> +				  data->regs[LTC_REG_CONFIG]);
> +}
> +
> +static int ltc4306_gpio_direction_input(struct gpio_chip *chip,
> +					unsigned int offset)
> +{
> +	struct ltc4306 *data = gpiochip_get_data(chip);
> +
> +	data->regs[LTC_REG_MODE] |= BIT(7 - offset);
> +
> +	return i2c_smbus_write_byte_data(data->client, LTC_REG_MODE,
> +					 data->regs[LTC_REG_MODE]);
> +}
> +
> +static int ltc4306_gpio_direction_output(struct gpio_chip *chip,
> +					 unsigned int offset, int value)
> +{
> +	struct ltc4306 *data = gpiochip_get_data(chip);
> +
> +	ltc4306_gpio_set(chip, offset, value);
> +	data->regs[LTC_REG_MODE] &= ~BIT(7 - offset);
> +
> +	return i2c_smbus_write_byte_data(data->client, LTC_REG_MODE,
> +					 data->regs[LTC_REG_MODE]);
> +}
> +
> +static int ltc4306_gpio_set_config(struct gpio_chip *chip,
> +				   unsigned int offset, unsigned long config)
> +{
> +	struct ltc4306 *data = gpiochip_get_data(chip);
> +
> +	switch (pinconf_to_config_param(config)) {
> +	case PIN_CONFIG_DRIVE_OPEN_DRAIN:
> +		data->regs[LTC_REG_MODE] &= ~BIT(4 - offset);
> +		break;
> +	case PIN_CONFIG_DRIVE_PUSH_PULL:
> +		data->regs[LTC_REG_MODE] |= BIT(4 - offset);
> +		break;
> +	default:
> +		return -ENOTSUPP;
> +	}
> +
> +	return i2c_smbus_write_byte_data(data->client, LTC_REG_MODE,
> +					 data->regs[LTC_REG_MODE]);
> +}
> +
> +static int ltc4306_gpio_init(struct ltc4306 *data)
> +{
> +	if (!data->chip->num_gpios)
> +		return 0;
> +
> +	data->gpiochip.label = dev_name(&data->client->dev);
> +	data->gpiochip.base = -1;
> +	data->gpiochip.ngpio = data->chip->num_gpios;
> +	data->gpiochip.parent = &data->client->dev;
> +	data->gpiochip.can_sleep = true;
> +	data->gpiochip.direction_input = ltc4306_gpio_direction_input;
> +	data->gpiochip.direction_output = ltc4306_gpio_direction_output;
> +	data->gpiochip.get = ltc4306_gpio_get;
> +	data->gpiochip.set = ltc4306_gpio_set;
> +	data->gpiochip.set_config = ltc4306_gpio_set_config;
> +	data->gpiochip.owner = THIS_MODULE;
> +
> +	/* gpiolib assumes all GPIOs default input */
> +	data->regs[LTC_REG_MODE] |= LTC_GPIO_ALL_INPUT;
> +	i2c_smbus_write_byte_data(data->client, LTC_REG_MODE,
> +				  data->regs[LTC_REG_MODE]);
> +
> +	return devm_gpiochip_add_data(&data->client->dev,
> +				      &data->gpiochip, data);
> +}
> +
> +/*
> + * Write to chip register. Don't use i2c_transfer()/i2c_smbus_xfer()
> + * as they will try to lock the adapter a second time.
> + */
> +static int ltc4306_reg_write(struct i2c_adapter *adap,
> +			     struct i2c_client *client, u8 reg, u8 val)
> +{
> +	int ret;
> +
> +	if (adap->algo->master_xfer) {
> +		struct i2c_msg msg;
> +		char buf[2];
> +
> +		msg.addr = client->addr;
> +		msg.flags = 0;
> +		msg.len = 2;
> +		buf[0] = reg;
> +		buf[1] = val;
> +		msg.buf = buf;
> +		ret = __i2c_transfer(adap, &msg, 1);
> +	} else {
> +		union i2c_smbus_data data;
> +
> +		data.byte = val;
> +		ret = adap->algo->smbus_xfer(adap, client->addr,
> +					     client->flags,
> +					     I2C_SMBUS_WRITE,
> +					     reg,
> +					     I2C_SMBUS_BYTE_DATA, &data);
> +	}
> +
> +	return ret;
> +}
> +
> +static int ltc4306_select_mux(struct i2c_mux_core *muxc, u32 chan)
> +{
> +	struct ltc4306 *data = i2c_mux_priv(muxc);
> +	struct i2c_client *client = data->client;
> +	u8 regval;
> +	int ret = 0;
> +
> +	regval = BIT(7 - chan);
> +
> +	/* Only select the channel if its different from the last channel */
> +	if (data->regs[LTC_REG_SWITCH] != regval) {
> +		ret = ltc4306_reg_write(muxc->parent, client,
> +					LTC_REG_SWITCH, regval);
> +		data->regs[LTC_REG_SWITCH] = ret < 0 ? 0 : regval;
> +	}
> +
> +	return ret;
> +}
> +
> +static int ltc4306_deselect_mux(struct i2c_mux_core *muxc, u32 chan)
> +{
> +	struct ltc4306 *data = i2c_mux_priv(muxc);
> +	struct i2c_client *client = data->client;
> +
> +	/* Deselect all channels */
> +	data->regs[LTC_REG_SWITCH] = 0;
> +
> +	return ltc4306_reg_write(muxc->parent, client,
> +				 LTC_REG_SWITCH, data->regs[LTC_REG_SWITCH]);
> +}
> +
> +static const struct i2c_device_id ltc4306_id[] = {
> +	{ "ltc4305", ltc_4305 },
> +	{ "ltc4306", ltc_4306 },
> +	{ }
> +};
> +MODULE_DEVICE_TABLE(i2c, ltc4306_id);
> +
> +static const struct of_device_id ltc4306_of_match[] = {
> +	{ .compatible = "lltc,ltc4305", .data = &chips[ltc_4305] },
> +	{ .compatible = "lltc,ltc4306", .data = &chips[ltc_4306] },
> +	{ }
> +};
> +MODULE_DEVICE_TABLE(of, ltc4306_of_match);
> +
> +static int ltc4306_probe(struct i2c_client *client,
> +			 const struct i2c_device_id *id)
> +{
> +	struct i2c_adapter *adap = to_i2c_adapter(client->dev.parent);
> +	struct device_node *of_node = client->dev.of_node;
> +	bool idle_disconnect_dt = false;
> +	struct i2c_mux_core *muxc;
> +	struct ltc4306 *data;
> +	int num, ret;
> +
> +	if (!i2c_check_functionality(adap, I2C_FUNC_SMBUS_BYTE_DATA))
> +		return -ENODEV;
> +
> +	if (of_node) {
> +		idle_disconnect_dt =
> +		of_property_read_bool(of_node,
> +				      "i2c-mux-idle-disconnect");

If you rename the variable "disconnect" or something similar and
shorter, you can avoid the interesting indentation.

> +	}
> +
> +	muxc = i2c_mux_alloc(adap, &client->dev,
> +			     LTC4306_MAX_NCHANS, sizeof(*data), 0,
> +			     ltc4306_select_mux, idle_disconnect_dt ?
> +			     ltc4306_deselect_mux : NULL);
> +	if (!muxc)
> +		return -ENOMEM;
> +	data = i2c_mux_priv(muxc);
> +
> +	i2c_set_clientdata(client, muxc);
> +	data->client = client;
> +
> +	/* Enable the mux if an enable GPIO is specified. */
> +	data->en_gpio = devm_gpiod_get_optional(&client->dev, "enable",
> +						GPIOD_OUT_HIGH);
> +	if (IS_ERR(data->en_gpio))
> +		return PTR_ERR(data->en_gpio);
> +
> +	/*
> +	 * Write the mux register at addr to verify
> +	 * that the mux is in fact present. This also
> +	 * initializes the mux to disconnected state.
> +	 */
> +	if (i2c_smbus_write_byte_data(client, LTC_REG_SWITCH, 0) < 0) {
> +		dev_warn(&client->dev, "probe failed\n");
> +		ret = -ENODEV;
> +		goto gpio_default;
> +	}
> +
> +	if (of_node) {
> +		data->chip = of_device_get_match_data(&client->dev);
> +
> +		if (of_property_read_bool(of_node,
> +					  "ltc,downstream-accelerators-enable"))
> +			data->regs[LTC_REG_CONFIG] |= LTC_DOWNSTREAM_ACCL_EN;
> +
> +		if (of_property_read_bool(of_node,
> +					  "ltc,upstream-accelerators-enable"))
> +			data->regs[LTC_REG_CONFIG] |= LTC_UPSTREAM_ACCL_EN;
> +
> +		if (i2c_smbus_write_byte_data(client, LTC_REG_CONFIG,
> +					      data->regs[LTC_REG_CONFIG]) < 0) {
> +			dev_warn(&client->dev, "probe failed\n");
> +			ret = -ENODEV;
> +			goto gpio_default;
> +		}
> +	} else {
> +		data->chip = &chips[id->driver_data];
> +	}
> +
> +	ret = ltc4306_gpio_init(data);
> +	if (ret < 0)
> +		goto gpio_default;
> +
> +	/* Now create an adapter for each channel */
> +	for (num = 0; num < data->chip->nchans; num++) {
> +		ret = i2c_mux_add_adapter(muxc, 0, num, 0);
> +		if (ret) {
> +			dev_err(&client->dev,
> +				"failed to register multiplexed adapter %d\n",
> +				num);
> +			goto add_adapter_failed;
> +		}
> +	}
> +
> +	dev_info(&client->dev,
> +		 "registered %d multiplexed busses for I2C switch %s\n",
> +		 num, client->name);
> +
> +	return 0;
> +
> +add_adapter_failed:
> +	i2c_mux_del_adapters(muxc);
> +gpio_default:
> +	gpiod_direction_input(data->en_gpio);

This was actually not what I had in mind when I asked about it in v1, and
this looks a bit strange. You have no way of knowing if the pin was
configured as input when probe was called, and I don't see code like this
all over the place. Maybe it's is ok to not disable the chip over
suspend/resume, I was just asking because it looked a bit strange to grab
a pin and then forget about it. Now that I think about it some more, it's
probably ok to do just that since it is perhaps not possible to make the
chip draw less power by deasserting enable, but what do I know?

However, it might be a good idea to toggle enable and deliberately reset
the chip in probe?

Cheers,
peda

> +	return ret;
> +}
> +
> +static int ltc4306_remove(struct i2c_client *client)
> +{
> +	struct i2c_mux_core *muxc = i2c_get_clientdata(client);
> +	struct ltc4306 *data = i2c_mux_priv(muxc);
> +
> +	i2c_mux_del_adapters(muxc);
> +	gpiod_direction_input(data->en_gpio);
> +
> +	return 0;
> +}
> +
> +static struct i2c_driver ltc4306_driver = {
> +	.driver		= {
> +		.name	= "ltc4306",
> +		.of_match_table = of_match_ptr(ltc4306_of_match),
> +	},
> +	.probe		= ltc4306_probe,
> +	.remove		= ltc4306_remove,
> +	.id_table	= ltc4306_id,
> +};
> +
> +module_i2c_driver(ltc4306_driver);
> +
> +MODULE_AUTHOR("Michael Hennerich <michael.hennerich@analog.com>");
> +MODULE_DESCRIPTION("Linear Technology LTC4306, LTC4305 I2C mux/switch driver");
> +MODULE_LICENSE("GPL v2");
> 

^ permalink raw reply

* Re: [PATCH] OF: mark released devices as no longer populated
From: Horia Geantă @ 2017-03-31 15:23 UTC (permalink / raw)
  To: Russell King - ARM Linux, Rob Herring, Fabio Estevam
  Cc: Frank Rowand, devicetree-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r@public.gmane.org,
	linux-crypto-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
	Dan Douglass
In-Reply-To: <20170331103950.GA18376@n2100.armlinux.org.uk>

On 3/31/2017 1:40 PM, Russell King - ARM Linux wrote:
> Ping, this issue still exists with 4.11-rc4 - and there's been no
> reaction from the alleged CAAM maintainers.
> 
Sorry, this somehow slipped through (Cc vs. To, no linux-crypto).

> On Tue, Aug 09, 2016 at 11:48:38AM -0500, Rob Herring wrote:
>> On Tue, Aug 9, 2016 at 4:33 AM, Russell King <rmk+kernel-I+IVW8TIWO2tmTQ+vhA3Yw@public.gmane.org> wrote:
>>> When a Linux device is released and cleaned up, we left the OF device
>>> node marked as populated.  This causes the Freescale CAAM driver
>>> (drivers/crypto/caam) problems when the module is removed and re-
>>> inserted:
>>>
>>> JR0 Platform device creation error
>>> JR0 Platform device creation error
>>> caam 2100000.caam: no queues configured, terminating
>>> caam: probe of 2100000.caam failed with error -12
>>>
>>> The reason is that CAAM creates platform devices for each job ring:
>>>
>>>         for_each_available_child_of_node(nprop, np)
>>>                 if (of_device_is_compatible(np, "fsl,sec-v4.0-job-ring") ||
>>>                     of_device_is_compatible(np, "fsl,sec4.0-job-ring")) {
>>>                         ctrlpriv->jrpdev[ring] =
>>>                                 of_platform_device_create(np, NULL, dev);
>>>
>>> which sets OF_POPULATED on the device node, but then it cleans these
>>> up:
>>>
>>>         /* Remove platform devices for JobRs */
>>>         for (ring = 0; ring < ctrlpriv->total_jobrs; ring++) {
>>>                 if (ctrlpriv->jrpdev[ring])
>>>                         of_device_unregister(ctrlpriv->jrpdev[ring]);
>>
>> This looks a bit asymmetrical to me with a of_platform_device_* call
>> and a of_device_* call.
>>
>> I think you could use of_platform_{de}populate here instead. That
>> would simplify things in the driver a bit too as you wouldn't need to
>> store jrpdev. It wouldn't work if there are other child nodes with
Indeed, this would clean-up the driver a bit. However, the driver needs
to know how many of the devices probed successfully - to print the
number and more importantly to exit in case total_jobrs = 0.

Thus, I would keep the one-by-one probing of the devices.
What options are there in this case?
Should a function symmetric to of_platform_device_create() be added - to
replace of_device_unregister() - or rely on an open-coded solution?

Thanks,
Horia

>> compatible strings which you don't want devices created.
>>
>>>         }
>>>
>>> which leaves OF_POPULATED set.
>>>
>>> Arrange for platform devices with a device node to clear the
>>> OF_POPULATED bit when they are released.
>>>
>>> Signed-off-by: Russell King <rmk+kernel-I+IVW8TIWO2tmTQ+vhA3Yw@public.gmane.org>
>>> ---
>>> Please check this carefully - it may have issues where an of_node
>>> pointer is copied from one platform device to another, but IMHO
>>> doing that is itself buggy behaviour.
>>
>> Agreed, that is wrong.
>>
>>>
>>> Resending due to wrong list address, sorry.
>>>
>>>  include/linux/of_device.h | 1 +
>>>  1 file changed, 1 insertion(+)
>>>
>>> diff --git a/include/linux/of_device.h b/include/linux/of_device.h
>>> index cc7dd687a89d..7a8362d0c6d2 100644
>>> --- a/include/linux/of_device.h
>>> +++ b/include/linux/of_device.h
>>> @@ -43,6 +43,7 @@ extern int of_device_uevent_modalias(struct device *dev, struct kobj_uevent_env
>>>
>>>  static inline void of_device_node_put(struct device *dev)
>>>  {
>>> +       of_node_clear_flag(dev->of_node, OF_POPULATED);
>>
>> This would result in clearing the flag twice in the
>> of_platform_populate/of_platform_depopulate case. It would do the same
>> for other bus types like i2c as well. That doesn't really hurt
>> anything that I can think of, but just not the best implementation. I
>> think adding a of_platform_device_unregister() call that wraps
>> of_platform_device_destroy would be more balanced.
>>
>> I looked thru all the callers of of_platform_device_create. The only
>> other ones affected by this are:
>>
>> drivers/macintosh/ams/ams-core.c
>> drivers/macintosh/therm_adt746x.c
>> drivers/macintosh/therm_windtunnel.c
>>
>> The others either have no remove path or a buggy remove path.
>>
>> Rob
> 
--
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 4/4] dt-bindings: input: Add Atmel PTC subsystem bindings
From: Ludovic Desroches @ 2017-03-31 15:22 UTC (permalink / raw)
  To: linux-input, linux-arm-kernel, devicetree
  Cc: dmitry.torokhov, nicolas.ferre, alexandre.belloni, linux-kernel,
	Ludovic Desroches
In-Reply-To: <20170331152250.12758-1-ludovic.desroches@microchip.com>

Add description of the Atmel PTC subsystem bindings.

Signed-off-by: Ludovic Desroches <ludovic.desroches@microchip.com>
---
 .../devicetree/bindings/input/atmel,ptc.txt        | 67 ++++++++++++++++++++++
 1 file changed, 67 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/input/atmel,ptc.txt

diff --git a/Documentation/devicetree/bindings/input/atmel,ptc.txt b/Documentation/devicetree/bindings/input/atmel,ptc.txt
new file mode 100644
index 0000000..a183fd5
--- /dev/null
+++ b/Documentation/devicetree/bindings/input/atmel,ptc.txt
@@ -0,0 +1,67 @@
+Atmel PTC Subsystem
+
+The Atmel Peripheral Touch Controller subsystem offers built-in hardware
+for capacitive touch measurement on sensors that function as buttons, sliders
+and wheels.
+
+1) PTC Subsystem node
+
+Required properties:
+- compatible: 		Must be "atmel,sama5d2-ptc"
+- reg: 			Address, length of the shared memory and ppp registers location
+			and length.
+- clocks: 		Phandlers to the clocks.
+- clock-names: 		Must be "ptc_clk", "ptc_int_osc", "slow_clk".
+- #address-cells:	Must be one. The cell is the button or scroller id.
+- #size-cells: 		Must be zero.
+
+Example:
+	ptc@fc060000 {
+		compatible = "atmel,sama5d2-ptc";
+		reg = <0x00800000 0x10000
+		       0xfc060000 0xcf>;
+		interrupts = <58 IRQ_TYPE_LEVEL_HIGH 7>;
+		clocks = <&ptc_clk>, <&main>, <&clk32k>;
+		clock-names = "ptc_clk", "ptc_int_osc", "slow_clk";
+		#address-cells = <1>;
+		#size-cells = <0>;
+
+		[ child node definitions... ]
+	};
+
+2) Scroller / buttons subnodes
+
+Subnodes describe the kind of sensors the customer want to use. They have to be
+named according to their function: button, slider or wheel.
+
+2.1) Scroller subnodes
+
+Required properties:
+- reg:	Id of the scroller, each id must be different.
+
+Example:
+	slider@0 {
+		reg = <0>;
+	};
+
+	wheel@1 {
+		reg = <1>;
+	};
+
+2.2) Button subnodes
+
+Required properties:
+- reg:			Id of node used for the button, each id must be
+			different.
+- linux,keycode: 	Key code of the button.
+
+Example:
+		button@8 {
+			reg = <8>;
+			linux,keycode = <2>;
+		};
+
+		button@9 {
+			reg = <9>;
+			linux,keycode = <3>;
+		};
-- 
2.9.0


^ permalink raw reply related

* [PATCH 3/4] MAINTAINERS: add Atmel PTC entries
From: Ludovic Desroches @ 2017-03-31 15:22 UTC (permalink / raw)
  To: linux-input, linux-arm-kernel, devicetree
  Cc: dmitry.torokhov, nicolas.ferre, alexandre.belloni, linux-kernel,
	Ludovic Desroches
In-Reply-To: <20170331152250.12758-1-ludovic.desroches@microchip.com>

Add entries for the Atmel PTC Subsystem.

Signed-off-by: Ludovic Desroches <ludovic.desroches@microchip.com>
---
 MAINTAINERS | 7 +++++++
 1 file changed, 7 insertions(+)

diff --git a/MAINTAINERS b/MAINTAINERS
index 57634d0..afb2645 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -2292,6 +2292,13 @@ F:	Documentation/devicetree/bindings/input/atmel,maxtouch.txt
 F:	drivers/input/touchscreen/atmel_mxt_ts.c
 F:	include/linux/platform_data/atmel_mxt_ts.h
 
+ATMEL PTC SUBSYSTEM DRIVER
+M:	Ludovic Desroches <ludovic.desroches@microchip.com>
+L:	linux-input@vger.kernel.org
+S:	Supported
+F:	drivers/input/misc/atmel_ptc.c
+F:	include/uapi/linux/atmel_ptc.h
+
 ATTO EXPRESSSAS SAS/SATA RAID SCSI DRIVER
 M:	Bradley Grove <linuxdrivers@attotech.com>
 L:	linux-scsi@vger.kernel.org
-- 
2.9.0

^ permalink raw reply related

* [PATCH 2/4] ARM: dts: at91: sama5d2: add PTC subsystem device
From: Ludovic Desroches @ 2017-03-31 15:22 UTC (permalink / raw)
  To: linux-input, linux-arm-kernel, devicetree
  Cc: dmitry.torokhov, nicolas.ferre, alexandre.belloni, linux-kernel,
	Ludovic Desroches
In-Reply-To: <20170331152250.12758-1-ludovic.desroches@microchip.com>

From: Ludovic Desroches <ludovic.desroches@atmel.com>

Add the Atmel Peripheral Touch Controller subsystem.

Signed-off-by: Ludovic Desroches <ludovic.desroches@atmel.com>
---
 arch/arm/boot/dts/sama5d2.dtsi | 16 ++++++++++++++++
 1 file changed, 16 insertions(+)

diff --git a/arch/arm/boot/dts/sama5d2.dtsi b/arch/arm/boot/dts/sama5d2.dtsi
index 468ef9c..a958456 100644
--- a/arch/arm/boot/dts/sama5d2.dtsi
+++ b/arch/arm/boot/dts/sama5d2.dtsi
@@ -762,6 +762,12 @@
 						atmel,clk-output-range = <0 83000000>;
 					};
 
+					ptc_clk: ptc_clk {
+						#clock-cells = <0>;
+						reg = <58>;
+						atmel,clk-output-range = <0 83000000>;
+					};
+
 					classd_clk: classd_clk {
 						#clock-cells = <0>;
 						reg = <59>;
@@ -1310,6 +1316,16 @@
 				reg = <0xfc05c000 0x20>;
 			};
 
+			ptc@fc060000 {
+				compatible = "atmel,sama5d2-ptc";
+				reg = <0x00800000 0x10000
+				       0xfc060000 0xcf>;
+				interrupts = <58 IRQ_TYPE_LEVEL_HIGH 7>;
+				clocks = <&ptc_clk>, <&main>, <&clk32k>;
+				clock-names = "ptc_clk", "ptc_int_osc", "slow_clk";
+				status = "disabled";
+			};
+
 			chipid@fc069000 {
 				compatible = "atmel,sama5d2-chipid";
 				reg = <0xfc069000 0x8>;
-- 
2.9.0

^ permalink raw reply related

* [PATCH 1/4] input: misc: introduce Atmel PTC driver
From: Ludovic Desroches @ 2017-03-31 15:22 UTC (permalink / raw)
  To: linux-input-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	devicetree-u79uwXL29TY76Z2rM5mHXA
  Cc: dmitry.torokhov-Re5JQEeQqe8AvxtiuMwx3w,
	nicolas.ferre-UWL1GkI3JZL3oGB3hsPCZA,
	alexandre.belloni-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA, Ludovic Desroches
In-Reply-To: <20170331152250.12758-1-ludovic.desroches-UWL1GkI3JZL3oGB3hsPCZA@public.gmane.org>

From: Ludovic Desroches <ludovic.desroches-AIFe0yeh4nAAvxtiuMwx3w@public.gmane.org>

Signed-off-by: Ludovic Desroches <ludovic.desroches-AIFe0yeh4nAAvxtiuMwx3w@public.gmane.org>
---
 drivers/input/misc/Kconfig     |  12 +
 drivers/input/misc/Makefile    |   1 +
 drivers/input/misc/atmel_ptc.c | 651 +++++++++++++++++++++++++++++++++++++++++
 include/uapi/linux/atmel_ptc.h | 355 ++++++++++++++++++++++
 4 files changed, 1019 insertions(+)
 create mode 100644 drivers/input/misc/atmel_ptc.c
 create mode 100644 include/uapi/linux/atmel_ptc.h

diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig
index 5b6c522..aec7b8f 100644
--- a/drivers/input/misc/Kconfig
+++ b/drivers/input/misc/Kconfig
@@ -96,6 +96,18 @@ config INPUT_ATMEL_CAPTOUCH
 	  To compile this driver as a module, choose M here: the
 	  module will be called atmel_captouch.
 
+config INPUT_ATMEL_PTC
+	tristate "Atmel PTC Driver"
+	depends on OF || COMPILE_TEST
+	depends on SOC_SAMA5D2
+	help
+	  Say Y to enable support for the Atmel PTC Subsystem.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called atmel_ptc.
+	  If you compile it as a built-in driver, you have to build the
+	  firmware into the kernel or to use an initrd.
+
 config INPUT_BMA150
 	tristate "BMA150/SMB380 acceleration sensor support"
 	depends on I2C
diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile
index b10523f..9470ec7 100644
--- a/drivers/input/misc/Makefile
+++ b/drivers/input/misc/Makefile
@@ -18,6 +18,7 @@ obj-$(CONFIG_INPUT_ARIZONA_HAPTICS)	+= arizona-haptics.o
 obj-$(CONFIG_INPUT_ATI_REMOTE2)		+= ati_remote2.o
 obj-$(CONFIG_INPUT_ATLAS_BTNS)		+= atlas_btns.o
 obj-$(CONFIG_INPUT_ATMEL_CAPTOUCH)	+= atmel_captouch.o
+obj-$(CONFIG_INPUT_ATMEL_PTC)		+= atmel_ptc.o
 obj-$(CONFIG_INPUT_BFIN_ROTARY)		+= bfin_rotary.o
 obj-$(CONFIG_INPUT_BMA150)		+= bma150.o
 obj-$(CONFIG_INPUT_CM109)		+= cm109.o
diff --git a/drivers/input/misc/atmel_ptc.c b/drivers/input/misc/atmel_ptc.c
new file mode 100644
index 0000000..44df1bf
--- /dev/null
+++ b/drivers/input/misc/atmel_ptc.c
@@ -0,0 +1,651 @@
+/*
+ * Atmel PTC subsystem driver for SAMA5D2 devices and compatible.
+ *
+ * Copyright (C) 2017 Microchip,
+ *               2017 Ludovic Desroches <ludovic.desroches-UWL1GkI3JZL3oGB3hsPCZA@public.gmane.org>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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/cdev.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/firmware.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+#include <linux/uaccess.h>
+
+#include <uapi/linux/atmel_ptc.h>
+
+#define ATMEL_QTM_MB_OFFSET	0x4000
+
+/* ----- PPP REGISTERS ----- */
+#define ATMEL_PPP_CONFIG	0x20
+#define ATMEL_PPP_CTRL		0x24
+#define ATMEL_PPP_CMD		0x28
+#define		ATMEL_PPP_CMD_STOP		0x1
+#define		ATMEL_PPP_CMD_RESET		0x2
+#define		ATMEL_PPP_CMD_RESTART		0x3
+#define		ATMEL_PPP_CMD_ABORT		0x4
+#define		ATMEL_PPP_CMD_RUN		0x5
+#define		ATMEL_PPP_CMD_RUN_LOCKED	0x6
+#define		ATMEL_PPP_CMD_RUN_OCD		0x7
+#define		ATMEL_PPP_CMD_UNLOCK		0x8
+#define		ATMEL_PPP_CMD_NMI		0x9
+#define		ATMEL_PPP_CMD_HOST_OCD_RESUME	0xB
+#define ATMEL_PPP_ISR		0x33
+#define		ATMEL_PPP_IRQ_MASK	GENMASK(7, 4)
+#define		ATMEL_PPP_IRQ0		BIT(4)
+#define		ATMEL_PPP_IRQ1		BIT(5)
+#define		ATMEL_PPP_IRQ2		BIT(6)
+#define		ATMEL_PPP_IRQ3		BIT(7)
+#define		ATMEL_PPP_NOTIFY_MASK	GENMASK(3, 0)
+#define		ATMEL_PPP_NOTIFY0	BIT(0)
+#define		ATMEL_PPP_NOTIFY1	BIT(1)
+#define		ATMEL_PPP_NOTIFY2	BIT(2)
+#define		ATMEL_PPP_NOTIFY3	BIT(3)
+#define ATMEL_PPP_IDR		0x34
+#define ATMEL_PPP_IER		0x35
+
+#define PPP_FIRMWARE_NAME	"atmel_ptc.bin"
+#define QTM_CONF_NAME		"atmel_ptc.conf"
+
+#define atmel_ppp_readb(ptc, reg)	readb_relaxed(ptc->ppp_regs + reg)
+#define atmel_ppp_writeb(ptc, reg, val)	writeb_relaxed(val, ptc->ppp_regs + reg)
+#define atmel_ppp_readl(ptc, reg)	readl_relaxed(ptc->ppp_regs + reg)
+#define atmel_ppp_writel(ptc, reg, val)	writel_relaxed(val, ptc->ppp_regs + reg)
+
+#define get_scroller_resolution(ptc, scroller_id) \
+	(1 << (ptc->qtm_mb->scroller_config[i].resol_deadband >> 4))
+
+struct atmel_ptc {
+	void __iomem		*ppp_regs;
+	void __iomem		*firmware;
+	int			irq;
+	uint8_t			imr;
+	volatile struct atmel_qtm_mailbox __iomem	*qtm_mb;
+	struct clk		*clk_per;
+	struct clk		*clk_int_osc;
+	struct clk		*clk_slow;
+	struct device		*dev;
+	struct completion	ppp_ack;
+	unsigned int		button_keycode[ATMEL_PTC_MAX_NODES];
+	struct input_dev	*buttons_input;
+	struct input_dev	*scroller_input[ATMEL_PTC_MAX_SCROLLERS];
+	bool			buttons_registered;
+	bool			scroller_registered[ATMEL_PTC_MAX_SCROLLERS];
+	uint32_t		button_event[ATMEL_PTC_MAX_NODES/32];
+	uint32_t		button_state[ATMEL_PTC_MAX_NODES/32];
+	uint32_t		scroller_event;
+	uint32_t		scroller_state;
+};
+
+static void atmel_ppp_irq_enable(struct atmel_ptc *ptc, uint8_t mask)
+{
+	ptc->imr |= mask;
+	atmel_ppp_writeb(ptc, ATMEL_PPP_IER, mask & ATMEL_PPP_IRQ_MASK);
+}
+
+static void atmel_ppp_irq_disable(struct atmel_ptc *ptc, uint8_t mask)
+{
+	ptc->imr &= ~mask;
+	atmel_ppp_writeb(ptc, ATMEL_PPP_IDR, mask & ATMEL_PPP_IRQ_MASK);
+}
+
+static void atmel_ppp_notify(struct atmel_ptc *ptc, uint8_t mask)
+{
+	if (mask & ATMEL_PPP_NOTIFY_MASK) {
+		uint8_t notify = atmel_ppp_readb(ptc, ATMEL_PPP_ISR)
+			    | (mask & ATMEL_PPP_NOTIFY_MASK);
+
+		atmel_ppp_writeb(ptc, ATMEL_PPP_ISR, notify);
+	}
+}
+
+static void atmel_ppp_irq_pending_clr(struct atmel_ptc *ptc, uint8_t mask)
+{
+	if (mask & ATMEL_PPP_IRQ_MASK) {
+		uint8_t irq = atmel_ppp_readb(ptc, ATMEL_PPP_ISR) & ~mask;
+
+		atmel_ppp_writeb(ptc, ATMEL_PPP_ISR, irq);
+	}
+}
+
+static void atmel_ppp_cmd_send(struct atmel_ptc *ptc, uint32_t cmd)
+{
+	atmel_ppp_writel(ptc, ATMEL_PPP_CMD, cmd);
+}
+
+static void atmel_ppp_irq_scroller_event(struct atmel_ptc *ptc)
+{
+	int i;
+
+	if (!ptc->scroller_event)
+		return;
+
+	for (i = 0; i < ATMEL_PTC_MAX_SCROLLERS; i++) {
+		uint32_t mask = 1 << i;
+		uint8_t status;
+		uint16_t position;
+
+		if (!(ptc->scroller_event & mask))
+			continue;
+
+		status = ptc->qtm_mb->scroller_data[i].status;
+		position = ptc->qtm_mb->scroller_data[i].position;
+
+		if (ptc->qtm_mb->scroller_config[i].type ==
+			    QTM_SCROLLER_TYPE_WHEEL)
+			input_report_abs(ptc->scroller_input[i],
+					 ABS_WHEEL, position);
+		else
+			input_report_abs(ptc->scroller_input[i],
+					 ABS_X, position);
+
+		input_report_key(ptc->scroller_input[i], BTN_TOUCH, status & 0x1);
+		input_sync(ptc->scroller_input[i]);
+	}
+}
+
+static void atmel_ppp_irq_button_event(struct atmel_ptc *ptc)
+{
+	int i, j;
+
+	for (i = 0; i < ATMEL_PTC_MAX_NODES / 32; i++) {
+		if (!ptc->button_event[i])
+			continue;
+
+		for (j = 0; j < 32; j++) {
+			uint32_t mask = 1 << j;
+			uint32_t state = ptc->button_state[i] & mask;
+			unsigned int key_id = i * 32 + j;
+
+			if (!(ptc->button_event[i] & mask))
+				continue;
+
+			input_report_key(ptc->buttons_input,
+					 ptc->button_keycode[key_id], !!state);
+			input_sync(ptc->buttons_input);
+		}
+	}
+}
+
+static void atmel_ppp_irq_touch_event(struct atmel_ptc *ptc)
+{
+	atmel_ppp_irq_scroller_event(ptc);
+	atmel_ppp_irq_button_event(ptc);
+}
+
+static irqreturn_t atmel_ppp_irq_handler(int irq, void *data)
+{
+	struct atmel_ptc *ptc = data;
+	uint32_t isr = atmel_ppp_readb(ptc, ATMEL_PPP_ISR) & ptc->imr;
+
+	/* QTM CMD acknowledgment */
+	if (isr & ATMEL_PPP_IRQ0) {
+		atmel_ppp_irq_disable(ptc, ATMEL_PPP_IRQ0);
+		atmel_ppp_irq_pending_clr(ptc, ATMEL_PPP_IRQ0);
+		complete(&ptc->ppp_ack);
+	}
+	/* QTM touch event */
+	if (isr & ATMEL_PPP_IRQ1) {
+		int i;
+
+		for (i = 0; i < ATMEL_PTC_MAX_NODES / 32; i++) {
+			ptc->button_event[i] = ptc->qtm_mb->touch_events.key_event_id[i];
+			ptc->button_state[i] = ptc->qtm_mb->touch_events.key_enable_state[i];
+		}
+		ptc->scroller_event = ptc->qtm_mb->touch_events.scroller_event_id;
+		ptc->scroller_state = ptc->qtm_mb->touch_events.scroller_event_state;
+
+		atmel_ppp_irq_pending_clr(ptc, ATMEL_PPP_IRQ1);
+
+		atmel_ppp_irq_touch_event(ptc);
+	}
+	/* Debug event */
+	if (isr & ATMEL_PPP_IRQ2) {
+		atmel_ppp_irq_pending_clr(ptc, ATMEL_PPP_IRQ2);
+	}
+
+	return IRQ_HANDLED;
+}
+
+uint32_t atmel_qtm_cmd_send(struct atmel_ptc *ptc, struct atmel_qtm_cmd *cmd)
+{
+	int i, ret;
+
+	dev_dbg(ptc->dev, "%s: cmd=0x%x, addr=0x%x, data=0x%x\n",
+		__func__, cmd->id, cmd->addr, cmd->data);
+
+	ptc->qtm_mb->cmd.id = cmd->id;
+	ptc->qtm_mb->cmd.addr = cmd->addr;
+	ptc->qtm_mb->cmd.data = cmd->data;
+
+	/* Once command performed, we'll get an IRQ. */
+	atmel_ppp_irq_enable(ptc, ATMEL_PPP_IRQ0);
+	/* Notify PPP that we have sent a command. */
+	atmel_ppp_notify(ptc, ATMEL_PPP_NOTIFY0);
+	/* Wait for IRQ from PPP. */
+	wait_for_completion(&ptc->ppp_ack);
+
+	/*
+	 * Register input devices only when QTM is started since we need some
+	 * information from the QTM configuration.
+	 */
+	if (cmd->id == QTM_CMD_RUN) {
+		if (ptc->buttons_input && !ptc->buttons_registered) {
+			ret = input_register_device(ptc->buttons_input);
+			if (ret)
+				dev_err(ptc->dev, "can't register input button device.\n");
+			else
+				ptc->buttons_registered = true;
+		}
+
+		for (i = 0; i < ATMEL_PTC_MAX_SCROLLERS; i++) {
+			struct input_dev *scroller = ptc->scroller_input[i];
+
+			if (!scroller || ptc->scroller_registered[i])
+				continue;
+			if (ptc->qtm_mb->scroller_config[i].type ==
+			    QTM_SCROLLER_TYPE_SLIDER) {
+				unsigned int max = get_scroller_resolution(ptc, i);
+
+				input_set_abs_params(scroller, 0, 0, max, 0, 0);
+			}
+			ret = input_register_device(scroller);
+			if (ret)
+				dev_err(ptc->dev, "can't register input scroller device.\n");
+			else
+				ptc->scroller_registered[i] = true;
+		}
+	}
+
+	return ptc->qtm_mb->cmd.data;
+}
+
+static inline struct atmel_ptc *kobj_to_atmel_ptc(struct kobject *kobj)
+{
+	struct device *dev = kobj_to_dev(kobj);
+
+	return dev->driver_data;
+}
+
+static ssize_t atmel_qtm_mb_read(struct file *filp, struct kobject *kobj,
+				 struct bin_attribute *attr,
+				 char *buf, loff_t off, size_t count)
+{
+	struct atmel_ptc *ptc = kobj_to_atmel_ptc(kobj);
+	char *qtm_mb = (char *)ptc->qtm_mb;
+
+	dev_dbg(ptc->dev, "%s: off=0x%llx, count=%zu\n", __func__, off, count);
+
+	memcpy(buf, qtm_mb + off, count);
+
+	return count;
+}
+
+static ssize_t atmel_qtm_mb_write(struct file *filp, struct kobject *kobj,
+				  struct bin_attribute *attr,
+				  char *buf, loff_t off, size_t count)
+{
+	struct atmel_ptc *ptc = kobj_to_atmel_ptc(kobj);
+	char *qtm_mb = (char *)ptc->qtm_mb;
+
+	dev_dbg(ptc->dev, "%s: off=0x%llx, count=%zu\n", __func__, off, count);
+
+	if (off == 0 && count == sizeof(struct atmel_qtm_cmd))
+		atmel_qtm_cmd_send(ptc, (struct atmel_qtm_cmd *)buf);
+	else
+		memcpy(qtm_mb + off, buf, count);
+
+	return count;
+}
+
+static struct bin_attribute atmel_ptc_qtm_mb_attr = {
+	.attr = {
+		.name = "qtm_mb",
+		.mode = 0644,
+	},
+	.size = sizeof(struct atmel_qtm_mailbox),
+	.read = atmel_qtm_mb_read,
+	.write = atmel_qtm_mb_write,
+};
+
+/*
+ * From QTM MB configuration, we can't retrieve all the information needed
+ * to setup correctly input devices: buttons key codes and slider axis are
+ * missing.
+ */
+static int atmel_ptc_of_parse(struct atmel_ptc *ptc)
+{
+	struct device_node *sensor;
+	bool first_button = true;
+
+	/* Parse sensors. */
+	for_each_child_of_node(ptc->dev->of_node, sensor) {
+		if (!strcmp(sensor->name, "button")) {
+			uint32_t key_id, keycode;
+			struct input_dev *buttons = ptc->buttons_input;
+
+			if (of_property_read_u32(sensor, "reg", &key_id)) {
+				dev_err(ptc->dev, "reg is missing (%s)\n",
+					sensor->full_name);
+				return -EINVAL;
+			}
+
+			if (of_property_read_u32(sensor, "linux,keycode", &keycode)) {
+				dev_err(ptc->dev, "linux,keycode is missing (%s)\n",
+					sensor->full_name);
+				return -EINVAL;
+			}
+			ptc->button_keycode[key_id] = keycode;
+
+			/* All buttons are put together in a keyboard device. */
+			if (first_button) {
+				buttons = devm_input_allocate_device(ptc->dev);
+				if (!buttons)
+					return -ENOMEM;
+				buttons->name = "atmel_ptc_buttons";
+				buttons->dev.parent = ptc->dev;
+				buttons->keycode = ptc->button_keycode;
+				buttons->keycodesize = sizeof(ptc->button_keycode[0]);
+				buttons->keycodemax = ATMEL_PTC_MAX_NODES;
+				ptc->buttons_input = buttons;
+				first_button = false;
+			}
+
+			input_set_capability(buttons, EV_KEY, keycode);
+		} else if (!strcmp(sensor->name, "slider") ||
+			   !strcmp(sensor->name, "wheel")) {
+			uint32_t scroller_id;
+			struct input_dev *scroller;
+
+			if (of_property_read_u32(sensor, "reg", &scroller_id)) {
+				dev_err(ptc->dev, "reg is missing (%s)\n",
+					sensor->full_name);
+				return -EINVAL;
+			}
+
+			if (scroller_id > ATMEL_PTC_MAX_SCROLLERS - 1) {
+				dev_err(ptc->dev, "wrong scroller id (%s)\n",
+					sensor->full_name);
+				return -EINVAL;
+			}
+
+			scroller = devm_input_allocate_device(ptc->dev);
+			if (!scroller)
+				return -ENOMEM;
+
+			scroller->dev.parent = ptc->dev;
+			ptc->scroller_input[scroller_id] = scroller;
+
+			if (!strcmp(sensor->name, "slider")) {
+				scroller->name = "atmel_ptc_slider";
+				input_set_capability(scroller, EV_ABS, ABS_X);
+				input_set_capability(scroller, EV_KEY, BTN_TOUCH);
+			} else {
+				scroller->name = "atmel_ptc_wheel";
+				input_set_capability(scroller, EV_ABS, ABS_WHEEL);
+				input_set_capability(scroller, EV_KEY, BTN_TOUCH);
+			}
+		} else {
+			dev_err(ptc->dev, "%s is not supported\n", sensor->name);
+			return -EINVAL;
+		}
+	}
+
+	return 0;
+}
+
+static void atmel_qtm_conf_callback(const struct firmware *conf, void *context)
+{
+	struct atmel_ptc *ptc = context;
+	unsigned int qtm_conf_size = sizeof(struct atmel_qtm_mailbox)
+		- offsetof(struct atmel_qtm_mailbox, node_group_config);
+	struct atmel_qtm_cmd qtm_cmd;
+	char *dst;
+
+	if (!conf) {
+		dev_err(ptc->dev, "cannot load QTM configuration, "
+			"it has to be set manually.\n");
+		return;
+	}
+
+	if (!conf->size || conf->size != qtm_conf_size) {
+		dev_err(ptc->dev, "incorrect QTM configuration file (size must be %u bytes), "
+			"configuration has to be set manually.\n", qtm_conf_size);
+		return;
+	}
+
+	atmel_ppp_irq_enable(ptc, ATMEL_PPP_IRQ1);
+	atmel_ppp_irq_disable(ptc, ATMEL_PPP_IRQ2 | ATMEL_PPP_IRQ3);
+
+	qtm_cmd.id = QTM_CMD_STOP;
+	atmel_qtm_cmd_send(ptc, &qtm_cmd);
+
+	/* Load QTM configuration. */
+	dst = (char *)ptc->qtm_mb +
+		offsetof(struct atmel_qtm_mailbox, node_group_config);
+	_memcpy_toio(dst, conf->data, qtm_conf_size);
+	release_firmware(conf);
+
+	if (atmel_ptc_of_parse(ptc))
+		dev_err(ptc->dev, "ptc_of_parse failed\n");
+
+	/* Start QTM. */
+	qtm_cmd.id = QTM_CMD_INIT;
+	qtm_cmd.data = ptc->qtm_mb->node_group_config.count;
+	atmel_qtm_cmd_send(ptc, &qtm_cmd);
+	qtm_cmd.id = QTM_CMD_SET_ACQ_MODE_TIMER;
+	qtm_cmd.data = 20;
+	atmel_qtm_cmd_send(ptc, &qtm_cmd);
+	qtm_cmd.id = QTM_CMD_RUN;
+	qtm_cmd.data = ptc->qtm_mb->node_group_config.count;
+	atmel_qtm_cmd_send(ptc, &qtm_cmd);
+}
+
+static void atmel_ppp_fw_callback(const struct firmware *fw, void *context)
+{
+	struct atmel_ptc *ptc = context;
+	int ret;
+	uint32_t firm_version;
+	struct atmel_qtm_cmd cmd;
+
+	if (!fw || !fw->size) {
+		dev_err(ptc->dev, "cannot load firmware.\n");
+		release_firmware(fw);
+		device_release_driver(ptc->dev);
+		return;
+	}
+
+	/* Command sequence to start from a clean state. */
+	atmel_ppp_cmd_send(ptc, ATMEL_PPP_CMD_ABORT);
+	atmel_ppp_irq_pending_clr(ptc, ATMEL_PPP_IRQ_MASK);
+	atmel_ppp_cmd_send(ptc, ATMEL_PPP_CMD_RESET);
+
+	_memcpy_toio(ptc->firmware, fw->data, fw->size);
+	release_firmware(fw);
+
+	atmel_ppp_cmd_send(ptc, ATMEL_PPP_CMD_RUN);
+
+	cmd.id = QTM_CMD_FIRM_VERSION;
+	firm_version = atmel_qtm_cmd_send(ptc, &cmd);
+	dev_info(ptc->dev, "firmware version: %u\n", firm_version);
+
+	/* PPP is running, it's time to load the QTM configuration. */
+	ret = request_firmware_nowait(THIS_MODULE, 1, QTM_CONF_NAME, ptc->dev,
+				      GFP_KERNEL, ptc, atmel_qtm_conf_callback);
+	if (ret)
+		dev_err(ptc->dev, "QTM configuration loading failed.\n");
+}
+
+static int atmel_ptc_probe(struct platform_device *pdev)
+{
+	struct atmel_ptc *ptc;
+	struct resource	*res;
+	void *shared_memory;
+	int ret;
+
+	ptc = devm_kzalloc(&pdev->dev, sizeof(*ptc), GFP_KERNEL);
+	if (!ptc)
+		return -ENOMEM;
+
+	platform_set_drvdata(pdev, ptc);
+	ptc->dev = &pdev->dev;
+	ptc->dev->driver_data = ptc;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!res)
+		return -ENODEV;
+
+	shared_memory = devm_ioremap_resource(&pdev->dev, res);
+	if (IS_ERR(shared_memory))
+		return PTR_ERR(shared_memory);
+
+	ptc->firmware = shared_memory;
+	ptc->qtm_mb = shared_memory + ATMEL_QTM_MB_OFFSET;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+	if (!res)
+		return -EINVAL;
+
+	ptc->ppp_regs = devm_ioremap_resource(&pdev->dev, res);
+	if (IS_ERR(ptc->ppp_regs))
+		return PTR_ERR(ptc->ppp_regs);
+
+	ptc->irq = platform_get_irq(pdev, 0);
+	if (ptc->irq <= 0) {
+		if (!ptc->irq)
+			ptc->irq = -ENXIO;
+
+		return ptc->irq;
+	}
+
+	ptc->clk_per = devm_clk_get(&pdev->dev, "ptc_clk");
+	if (IS_ERR(ptc->clk_per))
+		return PTR_ERR(ptc->clk_per);
+
+	ptc->clk_int_osc = devm_clk_get(&pdev->dev, "ptc_int_osc");
+	if (IS_ERR(ptc->clk_int_osc))
+		return PTR_ERR(ptc->clk_int_osc);
+
+	ptc->clk_slow = devm_clk_get(&pdev->dev, "slow_clk");
+	if (IS_ERR(ptc->clk_slow))
+		return PTR_ERR(ptc->clk_slow);
+
+	ret = devm_request_irq(&pdev->dev, ptc->irq, atmel_ppp_irq_handler, 0,
+			       pdev->dev.driver->name, ptc);
+	if (ret)
+		return ret;
+
+	ret = clk_prepare_enable(ptc->clk_int_osc);
+	if (ret)
+		return ret;
+
+	ret = clk_prepare_enable(ptc->clk_per);
+	if (ret)
+		goto disable_clk_int_osc;
+
+	ret = clk_prepare_enable(ptc->clk_slow);
+	if (ret)
+		goto disable_clk_per;
+
+	/* Needed to avoid unexpected behaviors. */
+	memset(ptc->firmware, 0, ATMEL_QTM_MB_OFFSET + sizeof(*ptc->qtm_mb));
+	ptc->imr = 0;
+	init_completion(&ptc->ppp_ack);
+
+	/*
+	 * Expose a file to give an access to the QTM mailbox to a user space
+	 * application in order to configure it or to send commands.
+	 */
+	ret = sysfs_create_bin_file(&pdev->dev.kobj, &atmel_ptc_qtm_mb_attr);
+	if (ret)
+		goto disable_clk_slow;
+
+	ret = request_firmware_nowait(THIS_MODULE, 1, PPP_FIRMWARE_NAME,
+				      ptc->dev, GFP_KERNEL, ptc,
+				      atmel_ppp_fw_callback);
+	if (ret) {
+		dev_err(&pdev->dev, "firmware loading failed\n");
+		ret = -EPROBE_DEFER;
+		goto remove_qtm_mb;
+	}
+
+	return 0;
+
+remove_qtm_mb:
+	sysfs_remove_bin_file(&pdev->dev.kobj, &atmel_ptc_qtm_mb_attr);
+disable_clk_slow:
+	clk_disable_unprepare(ptc->clk_slow);
+disable_clk_per:
+	clk_disable_unprepare(ptc->clk_per);
+disable_clk_int_osc:
+	clk_disable_unprepare(ptc->clk_int_osc);
+
+	return ret;
+}
+
+static int atmel_ptc_remove(struct platform_device *pdev)
+{
+	struct atmel_ptc *ptc = platform_get_drvdata(pdev);
+	int i;
+
+	if (ptc->buttons_registered)
+		input_unregister_device(ptc->buttons_input);
+
+	for (i = 0; i < ATMEL_PTC_MAX_SCROLLERS; i++) {
+		struct input_dev *scroller = ptc->scroller_input[i];
+
+		if (!scroller || !ptc->scroller_registered[i])
+			continue;
+		input_unregister_device(scroller);
+	}
+
+	sysfs_remove_bin_file(&pdev->dev.kobj, &atmel_ptc_qtm_mb_attr);
+	clk_disable_unprepare(ptc->clk_slow);
+	clk_disable_unprepare(ptc->clk_per);
+	clk_disable_unprepare(ptc->clk_int_osc);
+
+	return 0;
+}
+
+static const struct of_device_id atmel_ptc_dt_match[] = {
+	{
+		.compatible = "atmel,sama5d2-ptc",
+	}, {
+		/* sentinel */
+	}
+};
+MODULE_DEVICE_TABLE(of, atmel_ptc_dt_match);
+
+static struct platform_driver atmel_ptc_driver = {
+	.probe = atmel_ptc_probe,
+	.remove = atmel_ptc_remove,
+	.driver = {
+		.name = "atmel_ptc",
+		.of_match_table = atmel_ptc_dt_match,
+	},
+};
+module_platform_driver(atmel_ptc_driver)
+
+MODULE_AUTHOR("Ludovic Desroches <ludovic.desroches-UWL1GkI3JZL3oGB3hsPCZA@public.gmane.org>");
+MODULE_DESCRIPTION("Atmel PTC subsystem");
+MODULE_LICENSE("GPL v2");
+MODULE_FIRMWARE(PPP_FIRMWARE_NAME);
+MODULE_FIRMWARE(QTM_CONF_NAME);
diff --git a/include/uapi/linux/atmel_ptc.h b/include/uapi/linux/atmel_ptc.h
new file mode 100644
index 0000000..d15c4df
--- /dev/null
+++ b/include/uapi/linux/atmel_ptc.h
@@ -0,0 +1,355 @@
+/*
+ * Atmel PTC subsystem driver for SAMA5D2 devices and compatible.
+ *
+ * Copyright (C) 2017 Microchip,
+ *               2017 Ludovic Desroches <ludovic.desroches-UWL1GkI3JZL3oGB3hsPCZA@public.gmane.org>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef _LINUX_ATMEL_PTC_H
+#define _LINUX_ATMEL_PTC_H
+
+#ifdef __KERNEL__
+#include <linux/types.h>
+#else
+#include <stdint.h>
+#endif
+
+#define ATMEL_PTC_MAX_NODES	64
+#define ATMEL_PTC_MAX_SCROLLERS	4
+
+enum atmel_qtm_command {
+	QTM_CMD_FIRM_VERSION		= 8,
+	QTM_CMD_ACQ			= 17,
+	QTM_CMD_INIT			= 18,
+	QTM_CMD_RUN			= 19,
+	QTM_CMD_STATUS			= 20,
+	QTM_CMD_STOP			= 21,
+	QTM_CMD_UPDATE_TOUCH_CFG	= 22,
+	QTM_CMD_SET_ACQ_MODE_ON_DEMAND	= 23,
+	QTM_CMD_SET_ACQ_MODE_TIMER	= 24,
+	QTM_CMD_SET_ACQ_MODE_WCOMP	= 25,
+	QTM_CMD_SET_TIMER_INTERVAL	= 26,
+	QTM_CMD_STOP_TIMER		= 27,
+	QTM_CMD_START_TIMER		= 28,
+	QTM_CMD_RESET			= 29,
+};
+
+enum atmel_ptc_type {
+	PTC_TYPE_MUTUAL		= 0x40,
+	PTC_TYPE_SELFCAP	= 0x80,
+};
+
+enum atmel_ptc_scroller_type {
+	QTM_SCROLLER_TYPE_SLIDER,
+	QTM_SCROLLER_TYPE_WHEEL,
+};
+
+enum atmel_ptc_scan {
+	PTC_SCAN_EXTINT_0	= 1,
+	PTC_SCAN_TC2_COMPA	= 2,
+	PTC_SCAN_TC2_OVF	= 3,
+	PTC_SCAN_4MS		= 8,
+	PTC_SCAN_8MS		= 9,
+	PTC_SCAN_16MS		= 10,
+	PTC_SCAN_32MS		= 11,
+	PTC_SCAN_64MS		= 12,
+	PTC_SCAN_128MS		= 13,
+	PTC_SCAN_256MS		= 14,
+};
+
+enum atmel_ptc_cal_charge {
+	PTC_CAL_CHRG_2TAU,
+	PTC_CAL_CHRG_3TAU,
+	PTC_CAL_CHRG_4TAU,
+	PTC_CAL_CHRG_5TAU,
+};
+
+enum atmel_ptc_cal_auto_tune {
+	PTC_CAL_AUTO_TUNE_NONE,
+	PTC_CAL_AUTO_TUNE_RSEL,
+	PTC_CAL_AUTO_TUNE_PRSC,
+	PTC_CAL_AUTO_TUNE_CSD,
+};
+
+enum atmel_qtm_key_state {
+	QTM_KEY_STATE_DISABLE,
+	QTM_KEY_STATE_INIT,
+	QTM_KEY_STATE_CAL,
+	QTM_KEY_STATE_NO_DET,
+	QTM_KEY_STATE_FILT_IN,
+	QTM_KEY_STATE_ANTI_TCH,
+	QTM_KEY_STATE_SUSPEND,
+	QTM_KEY_STATE_DETECT	= 133,
+	QTM_KEY_STATE_FILT_OUT,
+};
+
+/*
+ * PTC acquisition frequency delay setting.
+ *
+ * The PTC acquisition frequency is dependent on the Generic clock
+ * input to PTC and PTC clock prescaler setting. This delay setting
+ * inserts "n" PTC clock cycles between consecutive measurements on
+ * a given sensor, thereby changing the PTC acquisition frequency.
+ * FREQ_HOP_SEL_1 setting inserts 1 PTC clock cycle between consecutive
+ * measurements. FREQ_HOP_SEL_14 setting inserts 14 PTC clock cycles.
+ * Hence, higher delay setting will increase the total time taken for
+ * capacitance measurement on a given sensor as compared to a lower
+ * delay setting.
+ *
+ * A desired setting can be used to avoid noise around the same frequency
+ * as the acquisition frequency.
+ */
+enum atmel_ptc_freq_sel {
+	PTC_FREQ_SEL_0,
+	PTC_FREQ_SEL_1,
+	PTC_FREQ_SEL_2,
+	PTC_FREQ_SEL_3,
+	PTC_FREQ_SEL_4,
+	PTC_FREQ_SEL_5,
+	PTC_FREQ_SEL_6,
+	PTC_FREQ_SEL_7,
+	PTC_FREQ_SEL_8,
+	PTC_FREQ_SEL_9,
+	PTC_FREQ_SEL_10,
+	PTC_FREQ_SEL_11,
+	PTC_FREQ_SEL_12,
+	PTC_FREQ_SEL_13,
+	PTC_FREQ_SEL_14,
+	PTC_FREQ_SEL_15,
+	PTC_FREQ_SEL_SPREAD,
+};
+
+/*
+ * Series resistor setting. For Mutual cap mode, this series
+ * resistor is switched internally on the Y-pin. For Self cap mode,
+ * this series resistor is switched internally on the Sensor pin.
+ */
+enum atmel_ptc_rsel_val {
+	PTC_RSEL_0,
+	PTC_RSEL_20,
+	PTC_RSEL_50,
+	PTC_RSEL_100,
+};
+
+enum atmel_ptc_prsc_div_sel {
+	PTC_PRSC_DIV_1,
+	PTC_PRSC_DIV_2,
+	PTC_PRSC_DIV_4,
+	PTC_PRSC_DIV_8,
+};
+
+enum atmel_ptc_gain {
+	PTC_GAIN_1,
+	PTC_GAIN_2,
+	PTC_GAIN_4,
+	PTC_GAIN_8,
+	PTC_GAIN_16,
+	PTC_GAIN_32,
+};
+
+enum atmel_ptc_filter_level {
+	PTC_FILTER_LEVEL_1,
+	PTC_FILTER_LEVEL_2,
+	PTC_FILTER_LEVEL_4,
+	PTC_FILTER_LEVEL_8,
+	PTC_FILTER_LEVEL_16,
+	PTC_FILTER_LEVEL_32,
+	PTC_FILTER_LEVEL_64,
+};
+
+enum atmel_ptc_aks_group {
+	QTM_KEY_NO_AKS_GROUP,
+	QTM_KEY_AKS_GROUP_1,
+	QTM_KEY_AKS_GROUP_2,
+	QTM_KEY_AKS_GROUP_3,
+	QTM_KEY_AKS_GROUP_4,
+	QTM_KEY_AKS_GROUP_5,
+	QTM_KEY_AKS_GROUP_6,
+	QTM_KEY_AKS_GROUP_7,
+	MAX_QTM_KEY_AKS_GROUP,
+};
+
+enum atmel_ptc_key_hysteresis {
+	QTM_KEY_HYST_50,
+	QTM_KEY_HYST_25,
+	QTM_KEY_HYST_12_5,
+	QTM_KEY_HYST_6_25,
+	MAX_QTM_KEY_HYST,
+};
+
+enum atmel_ptc_key_recal_thr {
+	QTM_KEY_RECAL_THR_100,
+	QTM_KEY_RECAL_THR_50,
+	QTM_KEY_RECAL_THR_25,
+	QTM_KEY_RECAL_THR_12_5,
+	QTM_KEY_RECAL_THR_6_25,
+	MAX_QTM_KEY_RECAL_THR,
+};
+
+/*
+ * Reburst mode:
+ * 0 = none (application calls only)
+ * 1 = Unresolved - i.e. sensors in process of calibration / filter in / filter out and AKS groups
+ * 2 = All keys
+ */
+enum atmel_ptc_key_reburst {
+	QTM_KEY_REBURST_NONE,
+	QTM_KEY_REBURST_UNRESOLVED,
+	QTM_KEY_REBURST_ALL,
+};
+
+struct atmel_qtm_cmd {
+	uint16_t id;
+	uint16_t addr;
+	uint32_t data;
+} __attribute__ ((packed));
+
+struct atmel_qtm_node_group_config {
+	uint16_t count;		/* Number of sensor nodes */
+	uint8_t	ptc_type;	/* Self or mutual sensors */
+	uint8_t	freq_option;	/* SDS or ASDV setting */
+	uint8_t	calib_option;	/* Hardware tuning: XX | TT 3/4/5 Tau | X | XX None/RSEL/PRSC/CSD */
+	uint8_t	unused;
+} __attribute__ ((packed));
+
+struct  atmel_qtm_node_config {
+	uint16_t mask_x;	/* Selects the X Pins for this node (NONE for selfcapacitance configuration) */
+	uint32_t mask_y;	/* Selects the Y Pins for this node */
+	uint8_t	csd;		/* Charge Share Delay */
+	uint8_t	rsel;		/* Resistor */
+	uint8_t	prsc;		/* Prescaler */
+	uint8_t	gain_analog;	/* Analog gain setting (Cint Selection) */
+	uint8_t	gain_digital;	/* Digital gain (Accum / Dec) */
+	uint8_t	oversampling;	/* Accumulator setting */
+} __attribute__ ((packed));
+
+struct atmel_qtm_node_data {
+	uint8_t	status;
+	uint8_t	unused;
+	uint16_t signals;
+	uint16_t comp_caps;
+} __attribute__ ((packed));
+
+struct atmel_qtm_key_config {
+	uint8_t	threshold;	/* Touch detection threshold */
+	uint8_t	hysteresis;	/* Percentage of threshold reduction to exit detect state */
+	uint8_t	aks_group;	/* 0 = None, 1-255 = group number */
+	uint8_t	unused;
+} __attribute__ ((packed));
+
+struct atmel_qtm_key_group_config {
+	uint16_t count;			/* Number of sensors */
+	uint8_t	touch_di;		/* Count in to Detect */
+	uint8_t	max_on_time;		/* Max on duration x 200ms */
+	uint8_t	anti_touch_di;		/* Count in to Anti-touch recal */
+	uint8_t	anti_touch_recal_thr;	/* Anti-touch recal threshold % */
+	uint8_t	touch_drift_rate;	/* One count per <200> ms */
+	uint8_t	anti_touch_drift_rate;	/* One count per <200> ms */
+	uint8_t	drift_hold_time;	/* Drift hold time */
+	uint8_t	reburst_mode;		/* None / Unresolved / All */
+} __attribute__ ((packed));
+
+struct atmel_qtm_key_data {
+	uint8_t	status;			/* Disabled, Off, On, Filter, Cal... */
+	uint8_t	status_counter;		/* State counter */
+	uint16_t node_struct_ptr;	/* Pointer to node struct */
+	uint16_t reference;		/* Reference signal */
+} __attribute__ ((packed));
+
+struct atmel_qtm_auto_scan_config {
+	uint16_t unused;
+	uint16_t node_number;	/* Node Index that will be used */
+	uint8_t	node_threshold;
+	uint8_t	trigger;
+} __attribute__ ((packed));
+
+struct atmel_qtm_scroller_group_config {
+	uint16_t key_data;
+	uint8_t	count;
+	uint8_t	unused;
+} __attribute__ ((packed));
+
+struct atmel_qtm_scroller_config {
+	uint8_t	type;
+	uint8_t	unused;
+	uint16_t key_start;
+	uint8_t	key_count;
+	uint8_t	resol_deadband;
+	uint8_t	position_hysteresis;
+	uint8_t	unused2;
+	uint16_t contact_min_threshold;
+} __attribute__ ((packed));
+
+struct atmel_qtm_scroller_data {
+	uint8_t	status;
+	uint8_t	right_hyst;
+	uint8_t	left_hyst;
+	uint8_t	unused;
+	uint16_t raw_position;
+	uint16_t position;
+	uint16_t contact_size;
+} __attribute__ ((packed));
+
+struct atmel_qtm_fh_autotune_config {
+	uint8_t	count;			/* Number of sensors */
+	uint8_t	num_freqs;
+	uint16_t freq_option_select;
+	uint16_t median_filter_freq;	/* PTC frequencies to be used on the median filter samples */
+	uint8_t	enable_freq_autotune;
+	uint8_t	max_variance_limit;
+	uint8_t	autotune_count_in_limit;
+	uint8_t	unused;
+} __attribute__ ((packed));
+
+struct atmel_qtm_fh_autotune_data {
+	uint8_t	status;			/* Obligatory status byte: Bit 7 = Reburst... */
+	uint8_t	current_freq;		/* PTC Sampling Delay Selection - 0 to 15 PTC CLK cycles */
+	uint16_t filter_buffer;		/* Filter buffer used to store past cycle signal values of sensor */
+	uint16_t acq_node_data;
+	uint16_t freq_tune_count_ins;
+} __attribute__ ((packed));
+
+struct atmel_qtm_fh_freq {
+	uint8_t	freq0;
+	uint8_t	freq1;
+	uint8_t	freq2;
+	uint8_t	unused;
+} __attribute__ ((packed));
+
+struct atmel_qtm_touch_events {
+	uint32_t key_event_id[2];
+	uint32_t key_enable_state[2];
+	uint32_t scroller_event_id;
+	uint32_t scroller_event_state;
+} __attribute__ ((packed));
+
+struct atmel_qtm_mailbox {
+	struct atmel_qtm_cmd			cmd;
+	uint8_t					unused[248];
+	struct atmel_qtm_node_group_config	node_group_config;
+	struct atmel_qtm_node_config		node_config[ATMEL_PTC_MAX_NODES];
+	struct atmel_qtm_node_data		node_data[ATMEL_PTC_MAX_NODES];
+	struct atmel_qtm_key_group_config	key_group_config;
+	struct atmel_qtm_key_config		key_config[ATMEL_PTC_MAX_NODES];
+	struct atmel_qtm_key_data		key_data[ATMEL_PTC_MAX_NODES];
+	struct atmel_qtm_auto_scan_config	auto_scan_config;
+	struct atmel_qtm_scroller_group_config	scroller_group_config;
+	struct atmel_qtm_scroller_config	scroller_config[ATMEL_PTC_MAX_SCROLLERS];
+	struct atmel_qtm_scroller_data		scroller_data[ATMEL_PTC_MAX_SCROLLERS];
+	struct atmel_qtm_fh_autotune_config	fh_autotune_config;
+	struct atmel_qtm_fh_autotune_data	fh_autotune_data;
+	struct atmel_qtm_fh_freq		fh_freq;
+	struct atmel_qtm_touch_events		touch_events;
+} __attribute__ ((packed));
+
+#endif /* _LINUX_ATMEL_PTC_H */
-- 
2.9.0

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

^ permalink raw reply related

* [PATCH 0/4] Introduce the Atmel PTC subsystem
From: Ludovic Desroches @ 2017-03-31 15:22 UTC (permalink / raw)
  To: linux-input, linux-arm-kernel, devicetree
  Cc: dmitry.torokhov, nicolas.ferre, alexandre.belloni, linux-kernel,
	Ludovic Desroches

Hi,

The Atmel Peripheral touch controller subsystem offers built-in hardware for
capacitive touch measurment on sensors that function as buttons, sliders and
wheels. It is available on SAMA5D2.

A firmware and a configuration file describing the topology and the parameters
of the sensor are loaded when probing the driver.

Ludovic Desroches (4):
  input: misc: introduce Atmel PTC driver
  ARM: dts: at91: sama5d2: add PTC subsystem device
  MAINTAINERS: add Atmel PTC entries
  dt-bindings: input: Add Atmel PTC subsystem bindings

 .../devicetree/bindings/input/atmel,ptc.txt        |  67 +++
 MAINTAINERS                                        |   7 +
 arch/arm/boot/dts/sama5d2.dtsi                     |  16 +
 drivers/input/misc/Kconfig                         |  12 +
 drivers/input/misc/Makefile                        |   1 +
 drivers/input/misc/atmel_ptc.c                     | 651 +++++++++++++++++++++
 include/uapi/linux/atmel_ptc.h                     | 355 +++++++++++
 7 files changed, 1109 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/input/atmel,ptc.txt
 create mode 100644 drivers/input/misc/atmel_ptc.c
 create mode 100644 include/uapi/linux/atmel_ptc.h

-- 
2.9.0

^ permalink raw reply

* Re: [PATCH V2 2/2] thermal: broadcom: add Northstar thermal driver
From: Rafał Miłecki @ 2017-03-31 14:49 UTC (permalink / raw)
  To: Jon Mason, Rafał Miłecki
  Cc: Mark Rutland,
	open list:OPEN FIRMWARE AND FLATTENED DEVICE TREE BINDINGS,
	Florian Fainelli, Scott Branden, Stephen Warren, Ray Jui,
	linux-pm, Lee Jones, Eduardo Valentin, Eric Anholt, Rob Herring,
	BCM Kernel Feedback, linux-rpi-kernel, Zhang Rui,
	linux-arm-kernel
In-Reply-To: <CAC3K-4q=npvHBjb2YeWmyCv89r14=vXdUzRW74mcrETs+NRh1Q@mail.gmail.com>

On 03/31/2017 04:23 PM, Jon Mason wrote:
> On Fri, Mar 31, 2017 at 3:03 AM, Rafał Miłecki <zajec5@gmail.com> wrote:
>> On 03/24/2017 03:35 PM, Jon Mason wrote:
>>>
>>> On Thu, Mar 23, 2017 at 11:30:45PM +0100, Rafał Miłecki wrote:
>>>>
>>>> diff --git a/drivers/thermal/broadcom/ns-thermal.c
>>>> b/drivers/thermal/broadcom/ns-thermal.c
>>>> new file mode 100644
>>>> index 000000000000..acf3a4de62a4
>>>> --- /dev/null
>>>> +++ b/drivers/thermal/broadcom/ns-thermal.c
>>>> @@ -0,0 +1,98 @@
>>>> +/*
>>>> + * Copyright (C) 2017 Rafał Miłecki <rafal@milecki.pl>
>>>> + *
>>>> + * 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/module.h>
>>>> +#include <linux/of_address.h>
>>>> +#include <linux/platform_device.h>
>>>> +#include <linux/thermal.h>
>>>> +
>>>> +#define PVTMON_CONTROL0                                        0x00
>>>> +#define PVTMON_CONTROL0_SEL_MASK                       0x0000000e
>>>> +#define PVTMON_CONTROL0_SEL_TEMP_MONITOR               0x00000000
>>>> +#define PVTMON_CONTROL0_SEL_TEST_MODE                  0x0000000e
>>>> +#define PVTMON_STATUS                                  0x08
>>>> +
>>>> +struct ns_thermal {
>>>> +       void __iomem *pvtmon;
>>>> +};
>>>> +
>>>> +static int ns_thermal_get_temp(void *data, int *temp)
>>>> +{
>>>> +       struct ns_thermal *ns_thermal = data;
>>>> +       u32 val;
>>>> +
>>>> +       val = readl(ns_thermal->pvtmon + PVTMON_CONTROL0);
>>>> +       if ((val & PVTMON_CONTROL0_SEL_MASK) !=
>>>> PVTMON_CONTROL0_SEL_TEMP_MONITOR) {
>>>> +               val &= ~PVTMON_CONTROL0_SEL_MASK;
>>>> +               val |= PVTMON_CONTROL0_SEL_TEMP_MONITOR;
>>>
>>>
>>> I think this is slightly confusing here.  If this was off, ORing in 0
>>> will not enable it.  I think we need to flip the #define to make it
>>> PVTMON_CONTROL0_SEL_TEMP_MONITOR_DISABLE (or a less crappy name).
>>> then use this line here to toggle it.  Something like
>>>
>>>                 val &= ~PVTMON_CONTROL0_SEL_TEMP_MONITOR_DISABLE;
>>
>>
>> I don't understand this, can you help me further?
>>
>> OR-ing with 0 is only a cosmetic thing obviously - just to make it clear
>> which
>> value we expect to be set in bits 1:3. The important part is:
>> val &= ~PVTMON_CONTROL0_SEL_MASK;
>
> You are using a side effect of the masking to clear/enable the block.
> I'm simply saying that we should be explicit about enabling it.  My
> concern is that using the side effect hides what is being done and
> could result in a bug somewhere down the line.  I think this is
> improbable based on the code, but wanted to err on the side of
> caution.

Well, I'm clearing current mode selection and "selecting" the mode I want. By
OR-ing PVTMON_CONTROL0_SEL_TEMP_MONITOR I'm clearly indicating I want temp
monitor more.

How else I could make it more obvious? Should I add some comment maybe?

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply

* Re: [PATCH V2 2/2] thermal: broadcom: add Northstar thermal driver
From: Jon Mason @ 2017-03-31 14:23 UTC (permalink / raw)
  To: Rafał Miłecki
  Cc: Zhang Rui, Eduardo Valentin, Rob Herring, Mark Rutland,
	Stephen Warren, Lee Jones, Eric Anholt, Florian Fainelli, Ray Jui,
	Scott Branden, BCM Kernel Feedback,
	linux-pm-u79uwXL29TY76Z2rM5mHXA,
	open list:OPEN FIRMWARE AND FLATTENED DEVICE TREE BINDINGS,
	linux-rpi-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	linux-arm-kernel, Rafał Miłecki
In-Reply-To: <d678d252-a95a-58c0-8098-4401c67040a1-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>

On Fri, Mar 31, 2017 at 3:03 AM, Rafał Miłecki <zajec5-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org> wrote:
> On 03/24/2017 03:35 PM, Jon Mason wrote:
>>
>> On Thu, Mar 23, 2017 at 11:30:45PM +0100, Rafał Miłecki wrote:
>>>
>>> diff --git a/drivers/thermal/broadcom/ns-thermal.c
>>> b/drivers/thermal/broadcom/ns-thermal.c
>>> new file mode 100644
>>> index 000000000000..acf3a4de62a4
>>> --- /dev/null
>>> +++ b/drivers/thermal/broadcom/ns-thermal.c
>>> @@ -0,0 +1,98 @@
>>> +/*
>>> + * Copyright (C) 2017 Rafał Miłecki <rafal-g1n6cQUeyibVItvQsEIGlw@public.gmane.org>
>>> + *
>>> + * This program is free software; you can redistribute it and/or modify
>>> + * it under the terms of the GNU General Public License version 2 as
>>> + * published by the Free Software Foundation.
>>> + */
>>> +
>>> +#include <linux/module.h>
>>> +#include <linux/of_address.h>
>>> +#include <linux/platform_device.h>
>>> +#include <linux/thermal.h>
>>> +
>>> +#define PVTMON_CONTROL0                                        0x00
>>> +#define PVTMON_CONTROL0_SEL_MASK                       0x0000000e
>>> +#define PVTMON_CONTROL0_SEL_TEMP_MONITOR               0x00000000
>>> +#define PVTMON_CONTROL0_SEL_TEST_MODE                  0x0000000e
>>> +#define PVTMON_STATUS                                  0x08
>>> +
>>> +struct ns_thermal {
>>> +       void __iomem *pvtmon;
>>> +};
>>> +
>>> +static int ns_thermal_get_temp(void *data, int *temp)
>>> +{
>>> +       struct ns_thermal *ns_thermal = data;
>>> +       u32 val;
>>> +
>>> +       val = readl(ns_thermal->pvtmon + PVTMON_CONTROL0);
>>> +       if ((val & PVTMON_CONTROL0_SEL_MASK) !=
>>> PVTMON_CONTROL0_SEL_TEMP_MONITOR) {
>>> +               val &= ~PVTMON_CONTROL0_SEL_MASK;
>>> +               val |= PVTMON_CONTROL0_SEL_TEMP_MONITOR;
>>
>>
>> I think this is slightly confusing here.  If this was off, ORing in 0
>> will not enable it.  I think we need to flip the #define to make it
>> PVTMON_CONTROL0_SEL_TEMP_MONITOR_DISABLE (or a less crappy name).
>> then use this line here to toggle it.  Something like
>>
>>                 val &= ~PVTMON_CONTROL0_SEL_TEMP_MONITOR_DISABLE;
>
>
> I don't understand this, can you help me further?
>
> OR-ing with 0 is only a cosmetic thing obviously - just to make it clear
> which
> value we expect to be set in bits 1:3. The important part is:
> val &= ~PVTMON_CONTROL0_SEL_MASK;

You are using a side effect of the masking to clear/enable the block.
I'm simply saying that we should be explicit about enabling it.  My
concern is that using the side effect hides what is being done and
could result in a bug somewhere down the line.  I think this is
improbable based on the code, but wanted to err on the side of
caution.

>
> AFAIU selecting any mode other than TEMP_MONITOR will disable monitor
> access.
> AFAIU even switchint to TEST_MODE will prevent access to temp, so I don't
> see
> how we could use PVTMON_CONTROL0_SEL_TEMP_MONITOR_DISABLE. It could be any
> value other than 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

* Re: [PATCH 2/3] dt-bindings: arm: amlogic: Add SoC information bindings
From: Neil Armstrong @ 2017-03-31 14:10 UTC (permalink / raw)
  To: Arnd Bergmann
  Cc: Kevin Hilman, carlo, linux-amlogic, Linux ARM,
	Linux Kernel Mailing List, devicetree
In-Reply-To: <CAK8P3a199j=7mb=CMgROvaOCVU8=kYda9wkvG3O4rtqweSyC0g@mail.gmail.com>

On 03/31/2017 03:44 PM, Arnd Bergmann wrote:
> On Fri, Mar 31, 2017 at 10:47 AM, Neil Armstrong
> <narmstrong@baylibre.com> wrote:
>> Add bindings for the SoC information register of the Amlogic SoCs.
>>
>> Signed-off-by: Neil Armstrong <narmstrong@baylibre.com>
>> ---
>>  Documentation/devicetree/bindings/arm/amlogic.txt | 20 ++++++++++++++++++++
>>  1 file changed, 20 insertions(+)
>>
>> diff --git a/Documentation/devicetree/bindings/arm/amlogic.txt b/Documentation/devicetree/bindings/arm/amlogic.txt
>> index bfd5b55..b850985 100644
>> --- a/Documentation/devicetree/bindings/arm/amlogic.txt
>> +++ b/Documentation/devicetree/bindings/arm/amlogic.txt
>> @@ -52,3 +52,23 @@ Board compatible values:
>>    - "amlogic,q201" (Meson gxm s912)
>>    - "nexbox,a95x" (Meson gxbb or Meson gxl s905x)
>>    - "nexbox,a1" (Meson gxm s912)
>> +
>> +Amlogic Meson GX SoCs Information
>> +----------------------------------
>> +
>> +The Meson SoCs have a Product Register that allows to retrieve SoC type,
>> +package and revision information. If present, a device node for this register
>> +should be added.
>> +
>> +Required properties:
>> +  - compatible: For Meson GX SoCs, must be "amlogic,meson-gx-socinfo".
>> +  - reg: Base address and length of the register block.
>> +
>> +Examples
>> +--------
>> +
>> +       chipid@220 {
>> +               compatible = "amlogic,meson-gx-socinfo";
>> +               reg = <0x0 0x00220 0x0 0x4>;
>> +       };
>> +
> 
> The register location would hint that this is in the middle of some block of
> random registers, i.e. a syscon or some unrelated device.
> 
> Are you sure that "socinfo" is the actual name of the IP block and that
> it only has a single 32-bit register?
> 
>      Arnd
> 

Hi Arnd,

I'm sorry I did not find any relevant registers in the docs or source code describing
it in a specific block of registers, and no close enough register definitions either.
They may be used by the secure firmware I imagine.

For the register name, Amlogic refers it to "cpu_version" in their code, but it really
gives some details on the whole SoC and package, and socinfo seems better.

Neil

^ permalink raw reply

* Re: [PATCH 2/3] dt-bindings: arm: amlogic: Add SoC information bindings
From: Arnd Bergmann @ 2017-03-31 13:44 UTC (permalink / raw)
  To: Neil Armstrong
  Cc: Kevin Hilman, carlo-KA+7E9HrN00dnm+yROfE0A,
	linux-amlogic-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, Linux ARM,
	Linux Kernel Mailing List, devicetree-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <1490950079-10145-3-git-send-email-narmstrong-rdvid1DuHRBWk0Htik3J/w@public.gmane.org>

On Fri, Mar 31, 2017 at 10:47 AM, Neil Armstrong
<narmstrong-rdvid1DuHRBWk0Htik3J/w@public.gmane.org> wrote:
> Add bindings for the SoC information register of the Amlogic SoCs.
>
> Signed-off-by: Neil Armstrong <narmstrong-rdvid1DuHRBWk0Htik3J/w@public.gmane.org>
> ---
>  Documentation/devicetree/bindings/arm/amlogic.txt | 20 ++++++++++++++++++++
>  1 file changed, 20 insertions(+)
>
> diff --git a/Documentation/devicetree/bindings/arm/amlogic.txt b/Documentation/devicetree/bindings/arm/amlogic.txt
> index bfd5b55..b850985 100644
> --- a/Documentation/devicetree/bindings/arm/amlogic.txt
> +++ b/Documentation/devicetree/bindings/arm/amlogic.txt
> @@ -52,3 +52,23 @@ Board compatible values:
>    - "amlogic,q201" (Meson gxm s912)
>    - "nexbox,a95x" (Meson gxbb or Meson gxl s905x)
>    - "nexbox,a1" (Meson gxm s912)
> +
> +Amlogic Meson GX SoCs Information
> +----------------------------------
> +
> +The Meson SoCs have a Product Register that allows to retrieve SoC type,
> +package and revision information. If present, a device node for this register
> +should be added.
> +
> +Required properties:
> +  - compatible: For Meson GX SoCs, must be "amlogic,meson-gx-socinfo".
> +  - reg: Base address and length of the register block.
> +
> +Examples
> +--------
> +
> +       chipid@220 {
> +               compatible = "amlogic,meson-gx-socinfo";
> +               reg = <0x0 0x00220 0x0 0x4>;
> +       };
> +

The register location would hint that this is in the middle of some block of
random registers, i.e. a syscon or some unrelated device.

Are you sure that "socinfo" is the actual name of the IP block and that
it only has a single 32-bit register?

     Arnd
--
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: [PATCHv3 00/10] Nokia H4+ support
From: Greg Kroah-Hartman @ 2017-03-31 13:33 UTC (permalink / raw)
  To: Marcel Holtmann
  Cc: Rob Herring, Sebastian Reichel, Gustavo F. Padovan, Johan Hedberg,
	Samuel Thibault, Pavel Machek, Tony Lindgren, Jiri Slaby,
	Mark Rutland, open list:BLUETOOTH DRIVERS,
	linux-serial@vger.kernel.org, devicetree@vger.kernel.org,
	linux-kernel@vger.kernel.org, David S. Miller
In-Reply-To: <C1A408F2-1DF8-4F49-B399-B936542DE6BC@holtmann.org>

On Wed, Mar 29, 2017 at 11:33:26PM +0200, Marcel Holtmann wrote:
> Hi Rob,
> 
> >> Here is PATCHv3 for the Nokia bluetooth patchset. I addressed all comments from
> >> Rob and Pavel regarding the serdev patches and dropped the *.dts patches, since
> >> they were queued by Tony. I also changed the patch order, so that the serdev
> >> patches come first. All of them have Acked-by from Rob, so I think it makes
> >> sense to merge them to serdev subsystem (now) and provide an immutable branch
> >> for the bluetooth subsystem.
> > 
> > Greg doesn't read cover letters generally and since the serdev patches
> > are Cc rather than To him, he's probably not planning to pick them up.
> 
> I wonder actually if we should merge all of these via bluetooth-next
> tree with proper Ack from Greg. However it would be good to also get
> buy in from Dave for merging this ultimately through net-next.

I don't really care where it goes.  I can take the whole thing in my
tty/serial tree now if no one objects and I get an ack from the relevant
maintainers {hint...}

thanks,

greg k-h

^ permalink raw reply

* Re: [PATCH 2/2] drivers/serial: Add driver for Aspeed virtual UART
From: Greg Kroah-Hartman @ 2017-03-31 13:31 UTC (permalink / raw)
  To: Joel Stanley
  Cc: Jiri Slaby, Mark Rutland, Rob Herring, Jeremy Kerr,
	linux-serial-u79uwXL29TY76Z2rM5mHXA,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	openbmc-uLR06cmDAlY/bJ5BZ2RsiQ, devicetree-u79uwXL29TY76Z2rM5mHXA,
	Benjamin Herrenschmidt
In-Reply-To: <20170328054458.29341-3-joel-U3u1mxZcP9KHXe+LvDLADg@public.gmane.org>

On Tue, Mar 28, 2017 at 04:14:58PM +1030, Joel Stanley wrote:
> From: Jeremy Kerr <jk-mnsaURCQ41sdnm+yROfE0A@public.gmane.org>
> 
> This change adds a driver for the 16550-based Aspeed virtual UART
> device. We use a similar process to the of_serial driver for device
> probe, but expose some VUART-specific functions through sysfs too.
> 
> OpenPOWER host firmware doesn't like it when the host-side of the
> VUART's FIFO is not drained. This driver only disables host TX discard
> mode when the port is in use. We set the VUART enabled bit when we bind
> to the device, and clear it on unbind.
> 
> We don't want to do this on open/release, as the host may be using this
> bit to configure serial output modes, which is independent of whether
> the devices has been opened by BMC userspace.
> 
> Signed-off-by: Jeremy Kerr <jk-mnsaURCQ41sdnm+yROfE0A@public.gmane.org>
> Signed-off-by: Joel Stanley <joel-U3u1mxZcP9KHXe+LvDLADg@public.gmane.org>
> ---
>  Documentation/devicetree/bindings/serial/8250.txt |   2 +
>  drivers/tty/serial/Kconfig                        |  10 +
>  drivers/tty/serial/Makefile                       |   1 +
>  drivers/tty/serial/aspeed-vuart.c                 | 335 ++++++++++++++++++++++
>  4 files changed, 348 insertions(+)
>  create mode 100644 drivers/tty/serial/aspeed-vuart.c
> 
> diff --git a/Documentation/devicetree/bindings/serial/8250.txt b/Documentation/devicetree/bindings/serial/8250.txt
> index f86bb06c39e9..a12e9277ac5d 100644
> --- a/Documentation/devicetree/bindings/serial/8250.txt
> +++ b/Documentation/devicetree/bindings/serial/8250.txt
> @@ -19,6 +19,8 @@ Required properties:
>  	- "altr,16550-FIFO128"
>  	- "fsl,16550-FIFO64"
>  	- "fsl,ns16550"
> +	- "aspeed,ast2400-vuart"
> +	- "aspeed,ast2500-vuart"
>  	- "serial" if the port type is unknown.
>  - reg : offset and length of the register set for the device.
>  - interrupts : should contain uart interrupt.
> diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig
> index e9cf5b67f1b7..758b69a51078 100644
> --- a/drivers/tty/serial/Kconfig
> +++ b/drivers/tty/serial/Kconfig
> @@ -1129,6 +1129,16 @@ config SERIAL_NETX_CONSOLE
>  	  If you have enabled the serial port on the Hilscher NetX SoC
>  	  you can make it the console by answering Y to this option.
>  
> +config SERIAL_ASPEED_VUART
> +	tristate "Aspeed Virtual UART"
> +	depends on OF
> +	depends on SERIAL_8250
> +	help
> +	  If you want to use the virtual UART (VUART) device on Aspeed
> +	  BMC platforms, enable this option. This enables the 16550A-
> +	  compatible device on the local LPC bus, giving a UART device
> +	  with no physical RS232 connections.
> +
>  config SERIAL_OMAP
>  	tristate "OMAP serial port support"
>  	depends on ARCH_OMAP2PLUS
> diff --git a/drivers/tty/serial/Makefile b/drivers/tty/serial/Makefile
> index 2d6288bc4554..5b97b0fa29e2 100644
> --- a/drivers/tty/serial/Makefile
> +++ b/drivers/tty/serial/Makefile
> @@ -22,6 +22,7 @@ obj-$(CONFIG_SERIAL_8250) += 8250/
>  
>  obj-$(CONFIG_SERIAL_AMBA_PL010) += amba-pl010.o
>  obj-$(CONFIG_SERIAL_AMBA_PL011) += amba-pl011.o
> +obj-$(CONFIG_SERIAL_ASPEED_VUART) += aspeed-vuart.o
>  obj-$(CONFIG_SERIAL_CLPS711X) += clps711x.o
>  obj-$(CONFIG_SERIAL_PXA_NON8250) += pxa.o
>  obj-$(CONFIG_SERIAL_PNX8XXX) += pnx8xxx_uart.o
> diff --git a/drivers/tty/serial/aspeed-vuart.c b/drivers/tty/serial/aspeed-vuart.c
> new file mode 100644
> index 000000000000..fc6fa6d243c8
> --- /dev/null
> +++ b/drivers/tty/serial/aspeed-vuart.c
> @@ -0,0 +1,335 @@
> +/*
> + *  Serial Port driver for Aspeed VUART device
> + *
> + *    Copyright (C) 2016 Jeremy Kerr <jk-mnsaURCQ41sdnm+yROfE0A@public.gmane.org>, IBM Corp.
> + *    Copyright (C) 2006 Arnd Bergmann <arnd-r2nGTMty4D4@public.gmane.org>, IBM Corp.
> + *
> + *  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; either version
> + *  2 of the License, or (at your option) any later version.
> + *
> + */
> +#include <linux/device.h>
> +#include <linux/module.h>
> +#include <linux/of_address.h>
> +#include <linux/of_irq.h>
> +#include <linux/of_platform.h>
> +#include <linux/clk.h>
> +
> +#include "8250/8250.h"
> +
> +#define AST_VUART_GCRA		0x20
> +#define AST_VUART_GCRA_VUART_EN		0x01
> +#define AST_VUART_GCRA_HOST_TX_DISCARD	0x20
> +#define AST_VUART_GCRB		0x24
> +#define AST_VUART_GCRB_HOST_SIRQ_MASK	0xf0
> +#define AST_VUART_GCRB_HOST_SIRQ_SHIFT	4
> +#define AST_VUART_ADDRL		0x28
> +#define AST_VUART_ADDRH		0x2c
> +
> +struct ast_vuart {
> +	struct platform_device *pdev;
> +	void __iomem		*regs;
> +	struct clk		*clk;
> +	int			line;
> +};
> +
> +static ssize_t ast_vuart_show_addr(struct device *dev,
> +		struct device_attribute *attr, char *buf)
> +{
> +	struct ast_vuart *vuart = dev_get_drvdata(dev);
> +	u16 addr;
> +
> +	addr = (readb(vuart->regs + AST_VUART_ADDRH) << 8) |
> +		(readb(vuart->regs + AST_VUART_ADDRL));
> +
> +	return snprintf(buf, PAGE_SIZE - 1, "0x%x\n", addr);
> +}
> +
> +static ssize_t ast_vuart_set_addr(struct device *dev,
> +		struct device_attribute *attr,
> +		const char *buf, size_t count)
> +{
> +	struct ast_vuart *vuart = dev_get_drvdata(dev);
> +	unsigned long val;
> +	int err;
> +
> +	err = kstrtoul(buf, 0, &val);
> +	if (err)
> +		return err;
> +
> +	writeb((val >> 8) & 0xff, vuart->regs + AST_VUART_ADDRH);
> +	writeb((val >> 0) & 0xff, vuart->regs + AST_VUART_ADDRL);
> +
> +	return count;
> +}
> +
> +static DEVICE_ATTR(lpc_address, 0664,
> +		ast_vuart_show_addr, ast_vuart_set_addr);

DEVICE_ATTR_RW()?

And why random sysfs files for a uart?  Where have you documented these?

> +
> +static ssize_t ast_vuart_show_sirq(struct device *dev,
> +		struct device_attribute *attr, char *buf)
> +{
> +	struct ast_vuart *vuart = dev_get_drvdata(dev);
> +	u8 reg;
> +
> +	reg = readb(vuart->regs + AST_VUART_GCRB);
> +	reg &= AST_VUART_GCRB_HOST_SIRQ_MASK;
> +	reg >>= AST_VUART_GCRB_HOST_SIRQ_SHIFT;
> +
> +	return snprintf(buf, PAGE_SIZE - 1, "%u\n", reg);
> +}
> +
> +static ssize_t ast_vuart_set_sirq(struct device *dev,
> +		struct device_attribute *attr,
> +		const char *buf, size_t count)
> +{
> +	struct ast_vuart *vuart = dev_get_drvdata(dev);
> +	unsigned long val;
> +	int err;
> +	u8 reg;
> +
> +	err = kstrtoul(buf, 0, &val);
> +	if (err)
> +		return err;
> +
> +	val <<= AST_VUART_GCRB_HOST_SIRQ_SHIFT;
> +	val &= AST_VUART_GCRB_HOST_SIRQ_MASK;
> +
> +	reg = readb(vuart->regs + AST_VUART_GCRB);
> +	reg &= ~AST_VUART_GCRB_HOST_SIRQ_MASK;
> +	reg |= val;
> +	writeb(reg, vuart->regs + AST_VUART_GCRB);
> +
> +	return count;
> +}
> +
> +static DEVICE_ATTR(sirq, 0664,
> +		ast_vuart_show_sirq, ast_vuart_set_sirq);

DEVICE_ATTR_RW().

> +
> +static void ast_vuart_set_enabled(struct ast_vuart *vuart, bool enabled)
> +{
> +	u8 reg;
> +
> +	reg = readb(vuart->regs + AST_VUART_GCRA);
> +	reg &= ~AST_VUART_GCRA_VUART_EN;
> +	if (enabled)
> +		reg |= AST_VUART_GCRA_VUART_EN;
> +	writeb(reg, vuart->regs + AST_VUART_GCRA);
> +}
> +
> +static void ast_vuart_set_host_tx_discard(struct ast_vuart *vuart, bool discard)
> +{
> +	u8 reg;
> +
> +	reg = readb(vuart->regs + AST_VUART_GCRA);
> +
> +	/* if the HOST_TX_DISCARD bit is set, discard is *disabled* */
> +	reg &= ~AST_VUART_GCRA_HOST_TX_DISCARD;
> +	if (!discard)
> +		reg |= AST_VUART_GCRA_HOST_TX_DISCARD;
> +
> +	writeb(reg, vuart->regs + AST_VUART_GCRA);
> +}
> +
> +static int ast_vuart_startup(struct uart_port *uart_port)
> +{
> +	struct uart_8250_port *uart_8250_port = up_to_u8250p(uart_port);
> +	struct ast_vuart *vuart = uart_8250_port->port.private_data;
> +	int rc;
> +
> +	rc = serial8250_do_startup(uart_port);
> +	if (rc)
> +		return rc;
> +
> +	ast_vuart_set_host_tx_discard(vuart, false);
> +
> +	return 0;
> +}
> +
> +static void ast_vuart_shutdown(struct uart_port *uart_port)
> +{
> +	struct uart_8250_port *uart_8250_port = up_to_u8250p(uart_port);
> +	struct ast_vuart *vuart = uart_8250_port->port.private_data;
> +
> +	ast_vuart_set_host_tx_discard(vuart, true);
> +
> +	serial8250_do_shutdown(uart_port);
> +}
> +
> +
> +/**
> + * The device tree parsing code here is heavily based on that of the of_serial
> + * driver, but we have a few core differences, as we need to use our own
> + * ioremapping for extra register support
> + */
> +static int ast_vuart_probe(struct platform_device *pdev)
> +{
> +	struct uart_8250_port port;
> +	struct resource resource;
> +	struct ast_vuart *vuart;
> +	struct device_node *np;
> +	u32 clk, prop;
> +	int rc;
> +
> +	np = pdev->dev.of_node;
> +
> +	vuart = devm_kzalloc(&pdev->dev, sizeof(*vuart), GFP_KERNEL);
> +	if (!vuart)
> +		return -ENOMEM;
> +
> +	vuart->pdev = pdev;
> +	rc = of_address_to_resource(np, 0, &resource);
> +	if (rc) {
> +		dev_warn(&pdev->dev, "invalid address\n");
> +		return rc;
> +	}
> +
> +	/* create our own mapping for VUART-specific registers */
> +	vuart->regs = devm_ioremap_resource(&pdev->dev, &resource);
> +	if (IS_ERR(vuart->regs)) {
> +		dev_warn(&pdev->dev, "failed to map registers\n");
> +		return PTR_ERR(vuart->regs);
> +	}
> +
> +	memset(&port, 0, sizeof(port));
> +	port.port.private_data = vuart;
> +	port.port.membase = vuart->regs;
> +	port.port.mapbase = resource.start;
> +	port.port.mapsize = resource_size(&resource);
> +	port.port.startup = ast_vuart_startup;
> +	port.port.shutdown = ast_vuart_shutdown;
> +
> +	if (of_property_read_u32(np, "clock-frequency", &clk)) {
> +		vuart->clk = devm_clk_get(&pdev->dev, NULL);
> +		if (IS_ERR(vuart->clk)) {
> +			dev_warn(&pdev->dev,
> +				"clk or clock-frequency not defined\n");
> +			return PTR_ERR(vuart->clk);
> +		}
> +
> +		rc = clk_prepare_enable(vuart->clk);
> +		if (rc < 0)
> +			return rc;
> +
> +		clk = clk_get_rate(vuart->clk);
> +	}
> +
> +	/* If current-speed was set, then try not to change it. */
> +	if (of_property_read_u32(np, "current-speed", &prop) == 0)
> +		port.port.custom_divisor = clk / (16 * prop);
> +
> +	/* Check for shifted address mapping */
> +	if (of_property_read_u32(np, "reg-offset", &prop) == 0)
> +		port.port.mapbase += prop;
> +
> +	/* Check for registers offset within the devices address range */
> +	if (of_property_read_u32(np, "reg-shift", &prop) == 0)
> +		port.port.regshift = prop;
> +
> +	/* Check for fifo size */
> +	if (of_property_read_u32(np, "fifo-size", &prop) == 0)
> +		port.port.fifosize = prop;
> +
> +	/* Check for a fixed line number */
> +	rc = of_alias_get_id(np, "serial");
> +	if (rc >= 0)
> +		port.port.line = rc;
> +
> +	port.port.irq = irq_of_parse_and_map(np, 0);
> +	port.port.irqflags = IRQF_SHARED;
> +	port.port.iotype = UPIO_MEM;
> +	if (of_property_read_u32(np, "reg-io-width", &prop) == 0) {
> +		switch (prop) {
> +		case 1:
> +			port.port.iotype = UPIO_MEM;
> +			break;
> +		case 4:
> +			port.port.iotype = of_device_is_big_endian(np) ?
> +				       UPIO_MEM32BE : UPIO_MEM32;
> +			break;
> +		default:
> +			dev_warn(&pdev->dev, "unsupported reg-io-width (%d)\n",
> +				 prop);
> +			rc = -EINVAL;
> +			goto err_clk_disable;
> +		}
> +	}
> +
> +	port.port.type = PORT_16550A;
> +	port.port.uartclk = clk;
> +	port.port.flags = UPF_SHARE_IRQ | UPF_BOOT_AUTOCONF
> +		| UPF_FIXED_PORT | UPF_FIXED_TYPE | UPF_NO_THRE_TEST;
> +
> +	if (of_find_property(np, "no-loopback-test", NULL))
> +		port.port.flags |= UPF_SKIP_TEST;
> +
> +	port.port.dev = &pdev->dev;
> +
> +	if (port.port.fifosize)
> +		port.capabilities = UART_CAP_FIFO;
> +
> +	if (of_property_read_bool(pdev->dev.of_node,
> +				  "auto-flow-control"))
> +		port.capabilities |= UART_CAP_AFE;
> +
> +	rc = serial8250_register_8250_port(&port);
> +	if (rc < 0)
> +		goto err_clk_disable;
> +
> +
> +	vuart->line = rc;
> +	ast_vuart_set_enabled(vuart, true);
> +	ast_vuart_set_host_tx_discard(vuart, true);
> +	platform_set_drvdata(pdev, vuart);
> +
> +	/* extra sysfs control */
> +	rc = device_create_file(&pdev->dev, &dev_attr_lpc_address);
> +	if (rc)
> +		dev_warn(&pdev->dev, "can't create lpc_address file\n");
> +	rc = device_create_file(&pdev->dev, &dev_attr_sirq);
> +	if (rc)
> +		dev_warn(&pdev->dev, "can't create sirq file\n");

use an attribute group?  You just raced userspace and lost :(

thanks,

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

^ permalink raw reply

* Re: [PATCH 1/3] device property: Move FW type specific functionality to FW specific files
From: Sakari Ailus @ 2017-03-31 13:20 UTC (permalink / raw)
  To: Mika Westerberg
  Cc: linux-acpi-u79uwXL29TY76Z2rM5mHXA,
	devicetree-u79uwXL29TY76Z2rM5mHXA, robh-DgEjT+Ai2ygdnm+yROfE0A,
	sudeep.holla-5wv7dgnIgG8, lorenzo.pieralisi-5wv7dgnIgG8,
	rafael-DgEjT+Ai2ygdnm+yROfE0A, mark.rutland-5wv7dgnIgG8,
	broonie-DgEjT+Ai2ygdnm+yROfE0A, ahs3-H+wXaHxf7aLQT0dZR+AlfA,
	frowand.list-Re5JQEeQqe8AvxtiuMwx3w
In-Reply-To: <20170327113100.GZ2957-3PARRvDOhMZrdx17CPfAsdBPR1lH4CV8@public.gmane.org>

Hi Mika,

Thanks for the review again!

Mika Westerberg wrote:
> On Fri, Mar 24, 2017 at 01:03:50PM +0200, Sakari Ailus wrote:
>> +static int acpi_fwnode_property_read_int_array(
>
> This is really ugly way to split arguments into multiple lines IMHO.
>
>> +	struct fwnode_handle *fwnode, const char *propname,
>> +	unsigned int elem_size, void *val, size_t nval)
>
>
> I would much more see something like:
>
> static int
> acpi_fwnode_property_read_int_array(struct fwnode_handle *fwnode,
> 	const char *propname, unsigned int elem_size, void *val, size_t nval)
>
> or even
>
> static int
> acpi_fwnode_property_read_int_array(struct fwnode_handle *fwnode,
> 				    const char *propname,
> 				    unsigned int elem_size,
> 				    void *val, size_t nval)
>
> ditto for all occurences.
>
> Other than that this patch looks reasonable to me.

I don't think CodingStyle defines any particular points where to wrap 
the lines. Different conventions seem to exist:

$ git grep '^\(static\|const\|inline\|int\|struct\|u\[0-9\).*($' |wc -l
3106

Not all of the occurrences found exhibit such indentation but then again 
the above does not catch all of those that do either.

I can change that if you insist but I'm telling you that it wouldn't be 
alone in the kernel. :-)

-- 
Kind regards,

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

^ permalink raw reply

* Re: [PATCH v3 2/7] UDC: Rename amd5536udc driver file based on IP
From: Raviteja Garimella @ 2017-03-31 13:16 UTC (permalink / raw)
  To: Felipe Balbi
  Cc: Rob Herring, Mark Rutland, Greg Kroah-Hartman, devicetree,
	linux-kernel, BCM Kernel Feedback, linux-usb
In-Reply-To: <87wpb5ixmo.fsf@linux.intel.com>

Hi Felipe,

On Fri, Mar 31, 2017 at 3:19 PM, Felipe Balbi <balbi@kernel.org> wrote:
>
> Hi,
>
> Raviteja Garimella <raviteja.garimella@broadcom.com> writes:
>
>> This patch renames the amd5536udc.c that has the core driver
>> functionality of Synopsys UDC to snps_udc_core.c
>>
>> The symbols exported here can be used by any UDC driver that uses
>> the same Synopsys IP.
>>
>> Signed-off-by: Raviteja Garimella <raviteja.garimella@broadcom.com>
>
> unfortunately, this patch doesn't apply:
>
> Applying: UDC: Rename amd5536udc driver file based on IP
> error: corrupt patch at line 235
> Patch failed at 0001 UDC: Rename amd5536udc driver file based on IP
> The copy of the patch that failed is found in: .git/rebase-apply/patch
> When you have resolved this problem, run "git am --continue".
> If you prefer to skip this patch, run "git am --skip" instead.
> To restore the original branch and stop patching, run "git am --abort".
>
> Can you rebase on testing/next and resend?

I rebased my patches against testing/next branch, resolved the merge conflicts.
Just resent the patches 2/7 to 7/7. Please use them. I verified they are getting
applied without issues now.

Thanks,
Ravi
>
> --
> balbi

^ permalink raw reply

* [RESEND PATCH v3 7/7] UDC: Add Synopsys UDC Platform driver
From: Raviteja Garimella @ 2017-03-31 13:09 UTC (permalink / raw)
  To: Rob Herring, Mark Rutland, Greg Kroah-Hartman, Felipe Balbi
  Cc: devicetree, linux-kernel, bcm-kernel-feedback-list, linux-usb
In-Reply-To: <1490965753-3511-1-git-send-email-raviteja.garimella@broadcom.com>

This patch adds platform driver support for Synopsys UDC.

A new driver file (snps_udc_plat.c) is created for this purpose
where the platform driver registration is done based on OF
node.

Currently, UDC integrated into Broadcom's iProc SoCs (Northstar2
and Cygnus) work with this driver.

New members are added to the UDC data structure for having platform
device support along with extcon and phy support.

Kconfig and Makefiles are modified to select platform driver for
compilation.

Signed-off-by: Raviteja Garimella <raviteja.garimella@broadcom.com>
---
 drivers/usb/gadget/udc/Kconfig         |  16 +-
 drivers/usb/gadget/udc/Makefile        |   1 +
 drivers/usb/gadget/udc/amd5536udc.h    |  14 ++
 drivers/usb/gadget/udc/snps_udc_core.c |  54 ++++--
 drivers/usb/gadget/udc/snps_udc_plat.c | 344 +++++++++++++++++++++++++++++++++
 5 files changed, 409 insertions(+), 20 deletions(-)
 create mode 100644 drivers/usb/gadget/udc/snps_udc_plat.c

diff --git a/drivers/usb/gadget/udc/Kconfig b/drivers/usb/gadget/udc/Kconfig
index 707814d..b788caf 100644
--- a/drivers/usb/gadget/udc/Kconfig
+++ b/drivers/usb/gadget/udc/Kconfig
@@ -255,7 +255,7 @@ config USB_MV_U3D
 	  controller, which support super speed USB peripheral.
 
 config USB_SNP_CORE
-	depends on USB_AMD5536UDC
+	depends on (USB_AMD5536UDC || USB_SNP_UDC_PLAT)
 	tristate
 	help
 	  This enables core driver support for Synopsys USB 2.0 Device
@@ -268,6 +268,20 @@ config USB_SNP_CORE
 	  This IP is different to the High Speed OTG IP that can be enabled
 	  by selecting USB_DWC2 or USB_DWC3 options.
 
+config USB_SNP_UDC_PLAT
+	tristate "Synopsys USB 2.0 Device controller"
+	depends on (USB_GADGET && OF)
+	select USB_GADGET_DUALSPEED
+	select USB_SNP_CORE
+	default ARCH_BCM_IPROC
+	help
+	  This adds Platform Device support for Synopsys Designware core
+	  AHB subsystem USB2.0 Device Controller (UDC).
+
+	  This driver works with UDCs integrated into Broadcom's Northstar2
+	  and Cygnus SoCs.
+
+	  If unsure, say N.
 #
 # Controllers available in both integrated and discrete versions
 #
diff --git a/drivers/usb/gadget/udc/Makefile b/drivers/usb/gadget/udc/Makefile
index 4f4fd62..ea9e1c7 100644
--- a/drivers/usb/gadget/udc/Makefile
+++ b/drivers/usb/gadget/udc/Makefile
@@ -37,4 +37,5 @@ obj-$(CONFIG_USB_FOTG210_UDC)	+= fotg210-udc.o
 obj-$(CONFIG_USB_MV_U3D)	+= mv_u3d_core.o
 obj-$(CONFIG_USB_GR_UDC)	+= gr_udc.o
 obj-$(CONFIG_USB_GADGET_XILINX)	+= udc-xilinx.o
+obj-$(CONFIG_USB_SNP_UDC_PLAT) += snps_udc_plat.o
 obj-$(CONFIG_USB_BDC_UDC)	+= bdc/
diff --git a/drivers/usb/gadget/udc/amd5536udc.h b/drivers/usb/gadget/udc/amd5536udc.h
index c252457..7884281 100644
--- a/drivers/usb/gadget/udc/amd5536udc.h
+++ b/drivers/usb/gadget/udc/amd5536udc.h
@@ -16,6 +16,7 @@
 /* debug control */
 /* #define UDC_VERBOSE */
 
+#include <linux/extcon.h>
 #include <linux/usb/ch9.h>
 #include <linux/usb/gadget.h>
 
@@ -28,6 +29,9 @@
 #define UDC_HSA0_REV 1
 #define UDC_HSB1_REV 2
 
+/* Broadcom chip rev. */
+#define UDC_BCM_REV 10
+
 /*
  * SETUP usb commands
  * needed, because some SETUP's are handled in hw, but must be passed to
@@ -112,6 +116,7 @@
 #define UDC_DEVCTL_BRLEN_MASK			0x00ff0000
 #define UDC_DEVCTL_BRLEN_OFS			16
 
+#define UDC_DEVCTL_SRX_FLUSH			14
 #define UDC_DEVCTL_CSR_DONE			13
 #define UDC_DEVCTL_DEVNAK			12
 #define UDC_DEVCTL_SD				10
@@ -564,7 +569,15 @@ struct udc {
 	u16				cur_intf;
 	u16				cur_alt;
 
+	/* for platform device and extcon support */
 	struct device			*dev;
+	struct phy			*udc_phy;
+	struct extcon_dev		*edev;
+	struct extcon_specific_cable_nb	extcon_nb;
+	struct notifier_block		nb;
+	struct delayed_work		drd_work;
+	struct workqueue_struct		*drd_wq;
+	u32				conn_type;
 };
 
 #define to_amd5536_udc(g)	(container_of((g), struct udc, gadget))
@@ -580,6 +593,7 @@ int udc_enable_dev_setup_interrupts(struct udc *dev);
 int udc_mask_unused_interrupts(struct udc *dev);
 irqreturn_t udc_irq(int irq, void *pdev);
 void gadget_release(struct device *pdev);
+void empty_req_queue(struct udc_ep *ep);
 void udc_basic_init(struct udc *dev);
 void free_dma_pools(struct udc *dev);
 int init_dma_pools(struct udc *dev);
diff --git a/drivers/usb/gadget/udc/snps_udc_core.c b/drivers/usb/gadget/udc/snps_udc_core.c
index d592f77..38a165d 100644
--- a/drivers/usb/gadget/udc/snps_udc_core.c
+++ b/drivers/usb/gadget/udc/snps_udc_core.c
@@ -41,7 +41,6 @@
 #include "amd5536udc.h"
 
 static void udc_tasklet_disconnect(unsigned long);
-static void empty_req_queue(struct udc_ep *);
 static void udc_setup_endpoints(struct udc *dev);
 static void udc_soft_reset(struct udc *dev);
 static struct udc_request *udc_alloc_bna_dummy(struct udc_ep *ep);
@@ -1244,7 +1243,7 @@ udc_queue(struct usb_ep *usbep, struct usb_request *usbreq, gfp_t gfp)
 }
 
 /* Empty request queue of an endpoint; caller holds spinlock */
-static void empty_req_queue(struct udc_ep *ep)
+void empty_req_queue(struct udc_ep *ep)
 {
 	struct udc_request	*req;
 
@@ -1256,6 +1255,7 @@ static void empty_req_queue(struct udc_ep *ep)
 		complete_req(ep, req, -ESHUTDOWN);
 	}
 }
+EXPORT_SYMBOL_GPL(empty_req_queue);
 
 /* Dequeues a request packet, called by gadget driver */
 static int udc_dequeue(struct usb_ep *usbep, struct usb_request *usbreq)
@@ -1623,6 +1623,9 @@ static void udc_setup_endpoints(struct udc *dev)
 /* Bringup after Connect event, initial bringup to be ready for ep0 events */
 static void usb_connect(struct udc *dev)
 {
+	/* Return if already connected */
+	if (dev->connected)
+		return;
 
 	dev_info(dev->dev, "USB Connect\n");
 
@@ -1641,6 +1644,9 @@ static void usb_connect(struct udc *dev)
  */
 static void usb_disconnect(struct udc *dev)
 {
+	/* Return if already disconnected */
+	if (!dev->connected)
+		return;
 
 	dev_info(dev->dev, "USB Disconnect\n");
 
@@ -1715,11 +1721,15 @@ static void udc_soft_reset(struct udc *dev)
 	/* device int. status reset */
 	writel(UDC_DEV_MSK_DISABLE, &dev->regs->irqsts);
 
-	spin_lock_irqsave(&udc_irq_spinlock, flags);
-	writel(AMD_BIT(UDC_DEVCFG_SOFTRESET), &dev->regs->cfg);
-	readl(&dev->regs->cfg);
-	spin_unlock_irqrestore(&udc_irq_spinlock, flags);
-
+	/* Don't do this for Broadcom UDC since this is a reserved
+	 * bit.
+	 */
+	if (dev->chiprev != UDC_BCM_REV) {
+		spin_lock_irqsave(&udc_irq_spinlock, flags);
+		writel(AMD_BIT(UDC_DEVCFG_SOFTRESET), &dev->regs->cfg);
+		readl(&dev->regs->cfg);
+		spin_unlock_irqrestore(&udc_irq_spinlock, flags);
+	}
 }
 
 /* RDE timer callback to set RDE bit */
@@ -3171,21 +3181,27 @@ int udc_probe(struct udc *dev)
 	dev_info(dev->dev, "%s\n", mod_desc);
 
 	snprintf(tmp, sizeof(tmp), "%d", dev->irq);
-	dev_info(dev->dev,
-		 "irq %s, pci mem %08lx, chip rev %02x(Geode5536 %s)\n",
-		 tmp, dev->phys_addr, dev->chiprev,
-		 (dev->chiprev == UDC_HSA0_REV) ? "A0" : "B1");
-	strcpy(tmp, UDC_DRIVER_VERSION_STRING);
-	if (dev->chiprev == UDC_HSA0_REV) {
-		dev_err(dev->dev, "chip revision is A0; too old\n");
-		retval = -ENODEV;
-		goto finished;
+
+	/* Print this device info for AMD chips only*/
+	if (dev->chiprev == UDC_HSA0_REV ||
+	    dev->chiprev == UDC_HSB1_REV) {
+		dev_info(dev->dev, "irq %s, pci mem %08lx, chip rev %02x(Geode5536 %s)\n",
+			 tmp, dev->phys_addr, dev->chiprev,
+			 (dev->chiprev == UDC_HSA0_REV) ?
+			 "A0" : "B1");
+		strcpy(tmp, UDC_DRIVER_VERSION_STRING);
+		if (dev->chiprev == UDC_HSA0_REV) {
+			dev_err(dev->dev, "chip revision is A0; too old\n");
+			retval = -ENODEV;
+			goto finished;
+		}
+		dev_info(dev->dev,
+			 "driver version: %s(for Geode5536 B1)\n", tmp);
 	}
-	dev_info(dev->dev,
-		 "driver version: %s(for Geode5536 B1)\n", tmp);
+
 	udc = dev;
 
-	retval = usb_add_gadget_udc_release(&udc->pdev->dev, &dev->gadget,
+	retval = usb_add_gadget_udc_release(udc->dev, &dev->gadget,
 					    gadget_release);
 	if (retval)
 		goto finished;
diff --git a/drivers/usb/gadget/udc/snps_udc_plat.c b/drivers/usb/gadget/udc/snps_udc_plat.c
new file mode 100644
index 0000000..2e11f19
--- /dev/null
+++ b/drivers/usb/gadget/udc/snps_udc_plat.c
@@ -0,0 +1,344 @@
+/*
+ * snps_udc_plat.c - Synopsys UDC Platform Driver
+ *
+ * Copyright (C) 2016 Broadcom
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation version 2.
+ *
+ * This program is distributed "as is" WITHOUT ANY WARRANTY of any
+ * kind, whether express or implied; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/extcon.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/of_gpio.h>
+#include <linux/platform_device.h>
+#include <linux/phy/phy.h>
+#include <linux/module.h>
+#include <linux/dmapool.h>
+#include <linux/interrupt.h>
+#include <linux/moduleparam.h>
+#include "amd5536udc.h"
+
+/* description */
+#define UDC_MOD_DESCRIPTION     "Synopsys UDC platform driver"
+
+void start_udc(struct udc *udc)
+{
+	if (udc->driver) {
+		dev_info(udc->dev, "Connecting...\n");
+		udc_enable_dev_setup_interrupts(udc);
+		udc_basic_init(udc);
+		udc->connected = 1;
+	}
+}
+
+void stop_udc(struct udc *udc)
+{
+	int tmp;
+	u32 reg;
+
+	spin_lock(&udc->lock);
+
+	/* Flush the receieve fifo */
+	reg = readl(&udc->regs->ctl);
+	reg |= AMD_BIT(UDC_DEVCTL_SRX_FLUSH);
+	writel(reg, &udc->regs->ctl);
+
+	reg = readl(&udc->regs->ctl);
+	reg &= ~(AMD_BIT(UDC_DEVCTL_SRX_FLUSH));
+	writel(reg, &udc->regs->ctl);
+	dev_dbg(udc->dev, "ep rx queue flushed\n");
+
+	/* Mask interrupts. Required more so when the
+	 * UDC is connected to a DRD phy.
+	 */
+	udc_mask_unused_interrupts(udc);
+
+	/* Disconnect gadget driver */
+	if (udc->driver) {
+		spin_unlock(&udc->lock);
+		udc->driver->disconnect(&udc->gadget);
+		spin_lock(&udc->lock);
+
+		/* empty queues */
+		for (tmp = 0; tmp < UDC_EP_NUM; tmp++)
+			empty_req_queue(&udc->ep[tmp]);
+	}
+	udc->connected = 0;
+
+	spin_unlock(&udc->lock);
+	dev_info(udc->dev, "Device disconnected\n");
+}
+
+void udc_drd_work(struct work_struct *work)
+{
+	struct udc *udc;
+
+	udc = container_of(to_delayed_work(work),
+			   struct udc, drd_work);
+
+	if (udc->conn_type) {
+		dev_dbg(udc->dev, "idle -> device\n");
+		start_udc(udc);
+	} else {
+		dev_dbg(udc->dev, "device -> idle\n");
+		stop_udc(udc);
+	}
+}
+
+static int usbd_connect_notify(struct notifier_block *self,
+			       unsigned long event, void *ptr)
+{
+	struct udc *udc = container_of(self, struct udc, nb);
+
+	dev_dbg(udc->dev, "%s: event: %lu\n", __func__, event);
+
+	udc->conn_type = event;
+
+	schedule_delayed_work(&udc->drd_work, 0);
+
+	return NOTIFY_OK;
+}
+
+static int udc_plat_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct resource *res;
+	struct udc *udc;
+	int ret;
+
+	udc = devm_kzalloc(dev, sizeof(*udc), GFP_KERNEL);
+	if (!udc)
+		return -ENOMEM;
+
+	spin_lock_init(&udc->lock);
+	udc->dev = dev;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	udc->virt_addr = devm_ioremap_resource(dev, res);
+	if (IS_ERR(udc->regs))
+		return PTR_ERR(udc->regs);
+
+	/* udc csr registers base */
+	udc->csr = udc->virt_addr + UDC_CSR_ADDR;
+
+	/* dev registers base */
+	udc->regs = udc->virt_addr + UDC_DEVCFG_ADDR;
+
+	/* ep registers base */
+	udc->ep_regs = udc->virt_addr + UDC_EPREGS_ADDR;
+
+	/* fifo's base */
+	udc->rxfifo = (u32 __iomem *)(udc->virt_addr + UDC_RXFIFO_ADDR);
+	udc->txfifo = (u32 __iomem *)(udc->virt_addr + UDC_TXFIFO_ADDR);
+
+	udc->phys_addr = (unsigned long)res->start;
+
+	udc->irq = irq_of_parse_and_map(dev->of_node, 0);
+	if (udc->irq <= 0) {
+		dev_err(dev, "Can't parse and map interrupt\n");
+		return -EINVAL;
+	}
+
+	udc->udc_phy = devm_of_phy_get_by_index(dev, dev->of_node, 0);
+	if (IS_ERR(udc->udc_phy)) {
+		dev_err(dev, "Failed to obtain phy from device tree\n");
+		return PTR_ERR(udc->udc_phy);
+	}
+
+	ret = phy_init(udc->udc_phy);
+	if (ret) {
+		dev_err(dev, "UDC phy init failed");
+		return ret;
+	}
+
+	ret = phy_power_on(udc->udc_phy);
+	if (ret) {
+		dev_err(dev, "UDC phy power on failed");
+		phy_exit(udc->udc_phy);
+		return ret;
+	}
+
+	/* Register for extcon if supported */
+	if (of_get_property(dev->of_node, "extcon", NULL)) {
+		udc->edev = extcon_get_edev_by_phandle(dev, 0);
+		if (IS_ERR(udc->edev)) {
+			if (PTR_ERR(udc->edev) == -EPROBE_DEFER)
+				return -EPROBE_DEFER;
+			dev_err(dev, "Invalid or missing extcon\n");
+			ret = PTR_ERR(udc->edev);
+			goto exit_phy;
+		}
+
+		udc->nb.notifier_call = usbd_connect_notify;
+		ret = extcon_register_notifier(udc->edev, EXTCON_USB,
+					       &udc->nb);
+		if (ret < 0) {
+			dev_err(dev, "Can't register extcon device\n");
+			goto exit_phy;
+		}
+
+		ret = extcon_get_cable_state_(udc->edev, EXTCON_USB);
+		if (ret < 0) {
+			dev_err(dev, "Can't get cable state\n");
+			goto exit_extcon;
+		} else if (ret) {
+			udc->conn_type = ret;
+		}
+		INIT_DELAYED_WORK(&udc->drd_work, udc_drd_work);
+	}
+
+	/* init dma pools */
+	if (use_dma) {
+		ret = init_dma_pools(udc);
+		if (ret != 0)
+			goto exit_extcon;
+	}
+
+	ret = devm_request_irq(dev, udc->irq, udc_irq, IRQF_SHARED,
+			       "snps-udc", udc);
+	if (ret < 0) {
+		dev_err(dev, "Request irq %d failed for UDC\n", udc->irq);
+		goto exit_dma;
+	}
+
+	platform_set_drvdata(pdev, udc);
+	udc->chiprev = UDC_BCM_REV;
+
+	if (udc_probe(udc)) {
+		ret = -ENODEV;
+		goto exit_dma;
+	}
+	dev_info(dev, "Synopsys UDC platform driver probe successful\n");
+
+	return 0;
+
+exit_dma:
+	if (use_dma)
+		free_dma_pools(udc);
+exit_extcon:
+	if (udc->edev)
+		extcon_unregister_notifier(udc->edev, EXTCON_USB, &udc->nb);
+exit_phy:
+	if (udc->udc_phy) {
+		phy_power_off(udc->udc_phy);
+		phy_exit(udc->udc_phy);
+	}
+	return ret;
+}
+
+static int udc_plat_remove(struct platform_device *pdev)
+{
+	struct udc *dev;
+
+	dev = platform_get_drvdata(pdev);
+
+	usb_del_gadget_udc(&dev->gadget);
+	/* gadget driver must not be registered */
+	if (WARN_ON(dev->driver))
+		return 0;
+
+	/* dma pool cleanup */
+	free_dma_pools(dev);
+
+	udc_remove(dev);
+
+	platform_set_drvdata(pdev, NULL);
+
+	if (dev->drd_wq) {
+		flush_workqueue(dev->drd_wq);
+		destroy_workqueue(dev->drd_wq);
+	}
+
+	phy_power_off(dev->udc_phy);
+	phy_exit(dev->udc_phy);
+	extcon_unregister_notifier(dev->edev, EXTCON_USB, &dev->nb);
+
+	dev_info(&pdev->dev, "Synopsys UDC platform driver removed\n");
+
+	return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int udc_plat_suspend(struct device *dev)
+{
+	struct udc *udc;
+
+	udc = dev_get_drvdata(dev);
+	stop_udc(udc);
+
+	if (extcon_get_cable_state_(udc->edev, EXTCON_USB) > 0) {
+		dev_dbg(udc->dev, "device -> idle\n");
+		stop_udc(udc);
+	}
+	phy_power_off(udc->udc_phy);
+	phy_exit(udc->udc_phy);
+
+	return 0;
+}
+
+static int udc_plat_resume(struct device *dev)
+{
+	struct udc *udc;
+	int ret;
+
+	udc = dev_get_drvdata(dev);
+
+	ret = phy_init(udc->udc_phy);
+	if (ret) {
+		dev_err(udc->dev, "UDC phy init failure");
+		return ret;
+	}
+
+	ret = phy_power_on(udc->udc_phy);
+	if (ret) {
+		dev_err(udc->dev, "UDC phy power on failure");
+		phy_exit(udc->udc_phy);
+		return ret;
+	}
+
+	if (extcon_get_cable_state_(udc->edev, EXTCON_USB) > 0) {
+		dev_dbg(udc->dev, "idle -> device\n");
+		start_udc(udc);
+	}
+
+	return 0;
+}
+static const struct dev_pm_ops udc_plat_pm_ops = {
+	.suspend	= udc_plat_suspend,
+	.resume		= udc_plat_resume,
+};
+#endif
+
+#if defined(CONFIG_OF)
+static const struct of_device_id of_udc_match[] = {
+	{ .compatible = "brcm,ns2-udc", },
+	{ .compatible = "brcm,cygnus-udc", },
+	{ .compatible = "brcm,iproc-udc", },
+	{ }
+};
+MODULE_DEVICE_TABLE(of, of_udc_match);
+#endif
+
+static struct platform_driver udc_plat_driver = {
+	.probe		= udc_plat_probe,
+	.remove		= udc_plat_remove,
+	.driver		= {
+		.name	= "snps-udc-plat",
+		.of_match_table = of_match_ptr(of_udc_match),
+#ifdef CONFIG_PM_SLEEP
+		.pm	= &udc_plat_pm_ops,
+#endif
+	},
+};
+module_platform_driver(udc_plat_driver);
+
+MODULE_DESCRIPTION(UDC_MOD_DESCRIPTION);
+MODULE_AUTHOR("Broadcom");
+MODULE_LICENSE("GPL v2");
-- 
2.1.0

^ permalink raw reply related

* [RESEND PATCH v3 6/7] DT bindings documentation for Broadcom IPROC USB Device controller.
From: Raviteja Garimella @ 2017-03-31 13:09 UTC (permalink / raw)
  To: Rob Herring, Mark Rutland, Greg Kroah-Hartman, Felipe Balbi
  Cc: devicetree, linux-kernel, bcm-kernel-feedback-list, linux-usb
In-Reply-To: <1490965753-3511-1-git-send-email-raviteja.garimella@broadcom.com>

The device node is used for UDCs integrated into Broadcom's
iProc family of SoCs'. The UDC is based on Synopsys Designware
Cores AHB Subsystem USB Device Controller IP.

Signed-off-by: Raviteja Garimella <raviteja.garimella@broadcom.com>
---
 Documentation/devicetree/bindings/usb/iproc-udc.txt | 21 +++++++++++++++++++++
 1 file changed, 21 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/usb/iproc-udc.txt

diff --git a/Documentation/devicetree/bindings/usb/iproc-udc.txt b/Documentation/devicetree/bindings/usb/iproc-udc.txt
new file mode 100644
index 0000000..272d7fa
--- /dev/null
+++ b/Documentation/devicetree/bindings/usb/iproc-udc.txt
@@ -0,0 +1,21 @@
+Broadcom IPROC USB Device controller.
+
+The device node is used for UDCs integrated into Broadcom's
+iProc family (Northstar2, Cygnus) of SoCs'. The UDC is based
+on Synopsys Designware Cores AHB Subsystem Device Controller
+IP.
+
+Required properties:
+ - compatible: Add the compatibility strings for supported platforms.
+   For Broadcom NS2 platform, add "brcm,ns2-udc","brcm,iproc-udc".
+   For Broadcom Cygnus platform, add "brcm,cygnus-udc", "brcm,iproc-udc".
+ - reg: Offset and length of UDC register set
+ - interrupts: description of interrupt line
+ - phys: phandle to phy node.
+
+Example:
+	udc_dwc: usb@664e0000 {
+		compatible = "brcm,ns2-udc", "brcm,iproc-udc";
+		reg = <0x664e0000 0x2000>;
+		interrupts = <GIC_SPI 424 IRQ_TYPE_LEVEL_HIGH>;
+		phys = <&usbdrd_phy>;
-- 
2.1.0

^ permalink raw reply related

* [RESEND PATCH v3 5/7] UDC: Use struct dma_pool instead of pci_pool
From: Raviteja Garimella @ 2017-03-31 13:09 UTC (permalink / raw)
  To: Rob Herring, Mark Rutland, Greg Kroah-Hartman, Felipe Balbi
  Cc: devicetree, linux-kernel, bcm-kernel-feedback-list, linux-usb
In-Reply-To: <1490965753-3511-1-git-send-email-raviteja.garimella@broadcom.com>

Using dma_pool instead of pci_pool will make snps_udc_core driver
to be compatible with non-pci platforms.

Signed-off-by: Raviteja Garimella <raviteja.garimella@broadcom.com>
---
 drivers/usb/gadget/udc/snps_udc_core.c | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/drivers/usb/gadget/udc/snps_udc_core.c b/drivers/usb/gadget/udc/snps_udc_core.c
index 5ca8b7b..d592f77 100644
--- a/drivers/usb/gadget/udc/snps_udc_core.c
+++ b/drivers/usb/gadget/udc/snps_udc_core.c
@@ -543,7 +543,7 @@ udc_alloc_request(struct usb_ep *usbep, gfp_t gfp)
 
 	if (ep->dma) {
 		/* ep0 in requests are allocated from data pool here */
-		dma_desc = pci_pool_alloc(ep->dev->data_requests, gfp,
+		dma_desc = dma_pool_alloc(ep->dev->data_requests, gfp,
 						&req->td_phys);
 		if (!dma_desc) {
 			kfree(req);
@@ -582,7 +582,7 @@ static void udc_free_dma_chain(struct udc *dev, struct udc_request *req)
 	for (i = 1; i < req->chain_len; i++) {
 		td = phys_to_virt(addr);
 		addr_next = (dma_addr_t)td->next;
-		pci_pool_free(dev->data_requests, td, addr);
+		dma_pool_free(dev->data_requests, td, addr);
 		addr = addr_next;
 	}
 }
@@ -608,7 +608,7 @@ udc_free_request(struct usb_ep *usbep, struct usb_request *usbreq)
 		if (req->chain_len > 1)
 			udc_free_dma_chain(ep->dev, req);
 
-		pci_pool_free(ep->dev->data_requests, req->td_data,
+		dma_pool_free(ep->dev->data_requests, req->td_data,
 							req->td_phys);
 	}
 	kfree(req);
@@ -803,7 +803,7 @@ static int udc_create_dma_chain(
 	for (i = buf_len; i < bytes; i += buf_len) {
 		/* create or determine next desc. */
 		if (create_new_chain) {
-			td = pci_pool_alloc(ep->dev->data_requests,
+			td = dma_pool_alloc(ep->dev->data_requests,
 					    gfp_flags, &dma_addr);
 			if (!td)
 				return -ENOMEM;
-- 
2.1.0

^ permalink raw reply related

* [RESEND PATCH v3 4/7] UDC: Provide correct arguments for 'dma_pool_create'
From: Raviteja Garimella @ 2017-03-31 13:09 UTC (permalink / raw)
  To: Rob Herring, Mark Rutland, Greg Kroah-Hartman, Felipe Balbi
  Cc: devicetree, linux-kernel, bcm-kernel-feedback-list, linux-usb
In-Reply-To: <1490965753-3511-1-git-send-email-raviteja.garimella@broadcom.com>

Change the argument from NULL to a struct device for the
dma_pool_create call during dma init.

Signed-off-by: Raviteja Garimella <raviteja.garimella@broadcom.com>
---
 drivers/usb/gadget/udc/snps_udc_core.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/drivers/usb/gadget/udc/snps_udc_core.c b/drivers/usb/gadget/udc/snps_udc_core.c
index a35fef9..5ca8b7b 100644
--- a/drivers/usb/gadget/udc/snps_udc_core.c
+++ b/drivers/usb/gadget/udc/snps_udc_core.c
@@ -3097,7 +3097,7 @@ int init_dma_pools(struct udc *dev)
 	}
 
 	/* DMA setup */
-	dev->data_requests = dma_pool_create("data_requests", NULL,
+	dev->data_requests = dma_pool_create("data_requests", dev->dev,
 		sizeof(struct udc_data_dma), 0, 0);
 	if (!dev->data_requests) {
 		DBG(dev, "can't get request data pool\n");
@@ -3108,7 +3108,7 @@ int init_dma_pools(struct udc *dev)
 	dev->ep[UDC_EP0IN_IX].dma = &dev->regs->ctl;
 
 	/* dma desc for setup data */
-	dev->stp_requests = dma_pool_create("setup requests", NULL,
+	dev->stp_requests = dma_pool_create("setup requests", dev->dev,
 		sizeof(struct udc_stp_dma), 0, 0);
 	if (!dev->stp_requests) {
 		DBG(dev, "can't get stp request pool\n");
-- 
2.1.0

^ permalink raw reply related

* [RESEND PATCH v3 3/7] UDC: make debug prints compatible with both pci and platform devices
From: Raviteja Garimella @ 2017-03-31 13:09 UTC (permalink / raw)
  To: Rob Herring, Mark Rutland, Greg Kroah-Hartman, Felipe Balbi
  Cc: devicetree, linux-kernel, bcm-kernel-feedback-list, linux-usb
In-Reply-To: <1490965753-3511-1-git-send-email-raviteja.garimella@broadcom.com>

This patch adds a struct device member to UDC data structure and
makes changes to the arguments of dev_err and dev_dbg calls so that
the debug prints work for both pci and platform devices.

Signed-off-by: Raviteja Garimella <raviteja.garimella@broadcom.com>
---
 drivers/usb/gadget/udc/amd5536udc.h     |  4 +++-
 drivers/usb/gadget/udc/amd5536udc_pci.c |  1 +
 drivers/usb/gadget/udc/snps_udc_core.c  | 28 ++++++++++++++--------------
 3 files changed, 18 insertions(+), 15 deletions(-)

diff --git a/drivers/usb/gadget/udc/amd5536udc.h b/drivers/usb/gadget/udc/amd5536udc.h
index bd2a18e..c252457 100644
--- a/drivers/usb/gadget/udc/amd5536udc.h
+++ b/drivers/usb/gadget/udc/amd5536udc.h
@@ -563,6 +563,8 @@ struct udc {
 	u16				cur_config;
 	u16				cur_intf;
 	u16				cur_alt;
+
+	struct device			*dev;
 };
 
 #define to_amd5536_udc(g)	(container_of((g), struct udc, gadget))
@@ -639,7 +641,7 @@ MODULE_PARM_DESC(use_fullspeed, "true for fullspeed only");
 
 /* debug macros ------------------------------------------------------------*/
 
-#define DBG(udc , args...)	dev_dbg(&(udc)->pdev->dev, args)
+#define DBG(udc , args...)	dev_dbg(udc->dev, args)
 
 #ifdef UDC_VERBOSE
 #define VDBG			DBG
diff --git a/drivers/usb/gadget/udc/amd5536udc_pci.c b/drivers/usb/gadget/udc/amd5536udc_pci.c
index 2a2d0a9..57a13f0 100644
--- a/drivers/usb/gadget/udc/amd5536udc_pci.c
+++ b/drivers/usb/gadget/udc/amd5536udc_pci.c
@@ -168,6 +168,7 @@ static int udc_pci_probe(
 	dev->phys_addr = resource;
 	dev->irq = pdev->irq;
 	dev->pdev = pdev;
+	dev->dev = &pdev->dev;
 
 	/* general probing */
 	if (udc_probe(dev)) {
diff --git a/drivers/usb/gadget/udc/snps_udc_core.c b/drivers/usb/gadget/udc/snps_udc_core.c
index 91d0f1a..a35fef9 100644
--- a/drivers/usb/gadget/udc/snps_udc_core.c
+++ b/drivers/usb/gadget/udc/snps_udc_core.c
@@ -209,18 +209,18 @@ static void print_regs(struct udc *dev)
 	if (use_dma && use_dma_ppb && !use_dma_ppb_du) {
 		DBG(dev, "DMA mode       = PPBNDU (packet per buffer "
 			"WITHOUT desc. update)\n");
-		dev_info(&dev->pdev->dev, "DMA mode (%s)\n", "PPBNDU");
+		dev_info(dev->dev, "DMA mode (%s)\n", "PPBNDU");
 	} else if (use_dma && use_dma_ppb && use_dma_ppb_du) {
 		DBG(dev, "DMA mode       = PPBDU (packet per buffer "
 			"WITH desc. update)\n");
-		dev_info(&dev->pdev->dev, "DMA mode (%s)\n", "PPBDU");
+		dev_info(dev->dev, "DMA mode (%s)\n", "PPBDU");
 	}
 	if (use_dma && use_dma_bufferfill_mode) {
 		DBG(dev, "DMA mode       = BF (buffer fill mode)\n");
-		dev_info(&dev->pdev->dev, "DMA mode (%s)\n", "BF");
+		dev_info(dev->dev, "DMA mode (%s)\n", "BF");
 	}
 	if (!use_dma)
-		dev_info(&dev->pdev->dev, "FIFO mode\n");
+		dev_info(dev->dev, "FIFO mode\n");
 	DBG(dev, "-------------------------------------------------------\n");
 }
 
@@ -1624,7 +1624,7 @@ static void udc_setup_endpoints(struct udc *dev)
 static void usb_connect(struct udc *dev)
 {
 
-	dev_info(&dev->pdev->dev, "USB Connect\n");
+	dev_info(dev->dev, "USB Connect\n");
 
 	dev->connected = 1;
 
@@ -1642,7 +1642,7 @@ static void usb_connect(struct udc *dev)
 static void usb_disconnect(struct udc *dev)
 {
 
-	dev_info(&dev->pdev->dev, "USB Disconnect\n");
+	dev_info(dev->dev, "USB Disconnect\n");
 
 	dev->connected = 0;
 
@@ -2106,7 +2106,7 @@ static irqreturn_t udc_data_out_isr(struct udc *dev, int ep_ix)
 	}
 	/* HE event ? */
 	if (tmp & AMD_BIT(UDC_EPSTS_HE)) {
-		dev_err(&dev->pdev->dev, "HE ep%dout occurred\n", ep->num);
+		dev_err(dev->dev, "HE ep%dout occurred\n", ep->num);
 
 		/* clear HE */
 		writel(tmp | AMD_BIT(UDC_EPSTS_HE), &ep->regs->sts);
@@ -2305,7 +2305,7 @@ static irqreturn_t udc_data_in_isr(struct udc *dev, int ep_ix)
 	if (use_dma) {
 		/* BNA ? */
 		if (epsts & AMD_BIT(UDC_EPSTS_BNA)) {
-			dev_err(&dev->pdev->dev,
+			dev_err(dev->dev,
 				"BNA ep%din occurred - DESPTR = %08lx\n",
 				ep->num,
 				(unsigned long) readl(&ep->regs->desptr));
@@ -2318,7 +2318,7 @@ static irqreturn_t udc_data_in_isr(struct udc *dev, int ep_ix)
 	}
 	/* HE event ? */
 	if (epsts & AMD_BIT(UDC_EPSTS_HE)) {
-		dev_err(&dev->pdev->dev,
+		dev_err(dev->dev,
 			"HE ep%dn occurred - DESPTR = %08lx\n",
 			ep->num, (unsigned long) readl(&ep->regs->desptr));
 
@@ -2956,7 +2956,7 @@ __acquires(dev->lock)
 
 		/* link up all endpoints */
 		udc_setup_endpoints(dev);
-		dev_info(&dev->pdev->dev, "Connect: %s\n",
+		dev_info(dev->dev, "Connect: %s\n",
 			 usb_speed_string(dev->gadget.speed));
 
 		/* init ep 0 */
@@ -3168,20 +3168,20 @@ int udc_probe(struct udc *dev)
 	/* init registers, interrupts, ... */
 	startup_registers(dev);
 
-	dev_info(&dev->pdev->dev, "%s\n", mod_desc);
+	dev_info(dev->dev, "%s\n", mod_desc);
 
 	snprintf(tmp, sizeof(tmp), "%d", dev->irq);
-	dev_info(&dev->pdev->dev,
+	dev_info(dev->dev,
 		 "irq %s, pci mem %08lx, chip rev %02x(Geode5536 %s)\n",
 		 tmp, dev->phys_addr, dev->chiprev,
 		 (dev->chiprev == UDC_HSA0_REV) ? "A0" : "B1");
 	strcpy(tmp, UDC_DRIVER_VERSION_STRING);
 	if (dev->chiprev == UDC_HSA0_REV) {
-		dev_err(&dev->pdev->dev, "chip revision is A0; too old\n");
+		dev_err(dev->dev, "chip revision is A0; too old\n");
 		retval = -ENODEV;
 		goto finished;
 	}
-	dev_info(&dev->pdev->dev,
+	dev_info(dev->dev,
 		 "driver version: %s(for Geode5536 B1)\n", tmp);
 	udc = dev;
 
-- 
2.1.0

^ 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