devicetree.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: NeilBrown <neilb-l3A5Bk7waGM@public.gmane.org>
To: "Dr. H. Nikolaus Schaller" <hns-xXXSsgcRVICgSpxsJD1C4w@public.gmane.org>
Cc: List for communicating with real GTA04 owners
	<gta04-owner-xXXSsgcRVICgSpxsJD1C4w@public.gmane.org>,
	Mark Rutland <mark.rutland-5wv7dgnIgG8@public.gmane.org>,
	One Thousand Gnomes
	<gnomes-qBU/x9rampVanCEyBjwyrvXRex20P6io@public.gmane.org>,
	Peter Hurley
	<peter-WaGBZJeGNqdsbIuE7sb01tBPR1lH4CV8@public.gmane.org>,
	Arnd Bergmann <arnd-r2nGTMty4D4@public.gmane.org>,
	Greg Kroah-Hartman
	<gregkh-hQyY1W1yCW8ekmWlsbkhG0B+6BGkLq7r@public.gmane.org>,
	Sebastian Reichel <sre-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>,
	Pavel Machek <pavel-+ZI9xUNit7I@public.gmane.org>,
	Grant Likely
	<grant.likely-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>,
	Jiri Slaby <jslaby-AlSwsSmVLrQ@public.gmane.org>,
	devicetree-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
	lkml <linux-kernel-u79uwXL29TY76Z2rM5mHXA@public.gmane.org>
Subject: Re: [Gta04-owner] [PATCH 3/3] tty/slaves: add a driver to power on/off UART attached devices.
Date: Fri, 20 Mar 2015 19:54:51 +1100	[thread overview]
Message-ID: <20150320195451.146a7915@notabene.brown> (raw)
In-Reply-To: <C53A3E5D-5AEA-4F0F-88A9-F9BC8CB0D0D6-xXXSsgcRVICgSpxsJD1C4w@public.gmane.org>

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

On Fri, 20 Mar 2015 08:54:38 +0100 "Dr. H. Nikolaus Schaller"
<hns-xXXSsgcRVICgSpxsJD1C4w@public.gmane.org> wrote:

> 
> Am 18.03.2015 um 06:58 schrieb NeilBrown <neil-+NVA1uvv1dVBDLzU/O5InQ@public.gmane.org>:
> 
> > If a platform has a particular device permanently attached to a UART,
> > there may be out-of-band signaling necessary to power the device
> > on and off.
> > 
> > This driver controls that signalling for a number of different devices.
> > It can
> > - enable/disable a regulator
> > - toggle a GPIO
> > - register an 'rfkill' which can force the device to be off.
> > 
> > When the rfkill is absent or unblocked, the device will be on when the
> > associated tty device is open, and closed otherwise.
> > 
> > Signed-off-by: NeilBrown <neil-+NVA1uvv1dVBDLzU/O5InQ@public.gmane.org>
> > ---
> > .../bindings/tty_slave/wi2wi,w2cbw003.txt          |   19 +
> > .../bindings/tty_slave/wi2wi,w2sg0004.txt          |   37 +
> > .../devicetree/bindings/vendor-prefixes.txt        |    1 
> > drivers/tty/slave/Kconfig                          |   14 +
> > drivers/tty/slave/Makefile                         |    2 
> > drivers/tty/slave/serial-power-manager.c           |  510 ++++++++++++++++++++
> > 6 files changed, 583 insertions(+)
> > create mode 100644 Documentation/devicetree/bindings/tty_slave/wi2wi,w2cbw003.txt
> > create mode 100644 Documentation/devicetree/bindings/tty_slave/wi2wi,w2sg0004.txt
> > create mode 100644 drivers/tty/slave/serial-power-manager.c
> > 
> > diff --git a/Documentation/devicetree/bindings/tty_slave/wi2wi,w2cbw003.txt b/Documentation/devicetree/bindings/tty_slave/wi2wi,w2cbw003.txt
> > new file mode 100644
> > index 000000000000..cfe6ee5e01e9
> > --- /dev/null
> > +++ b/Documentation/devicetree/bindings/tty_slave/wi2wi,w2cbw003.txt
> > @@ -0,0 +1,19 @@
> > +wi2wi bluetooth module
> > +
> > +This is accessed via a serial port and is largely controlled via that
> > +link.  Extra configuration is needed to enable power on/off
> > +
> > +Required properties:
> > +- compatible: "wi2wi,w2cbw003"
> > +- vdd-supply: regulator used to power the device.
> > +
> > +The node for this device must be the child of a UART.
> > +
> > +Example:
> > +
> > +&uart1 {
> > +       bluetooth {
> > +               compatible = "wi2wi,w2cbw003";
> > +               vdd-supply = <&vaux4>;
> > +       };
> > +};
> 
> Wouldn’t it be easier to simply write
> 
> &uart1 {
> 	vdd-suppy = <&vaux4>;
> }

Easier to write: certainly.
Easier to justify? No.
Easier to get merged upstream?  Definitely not.
After all, the uart itself doesn't require a power supply.
It is the device connected to the uart which requires the power supply.


> 
> > diff --git a/Documentation/devicetree/bindings/tty_slave/wi2wi,w2sg0004.txt b/Documentation/devicetree/bindings/tty_slave/wi2wi,w2sg0004.txt
> > new file mode 100644
> > index 000000000000..fdc52cf56533
> > --- /dev/null
> > +++ b/Documentation/devicetree/bindings/tty_slave/wi2wi,w2sg0004.txt
> > @@ -0,0 +1,37 @@
> > +wi2wi GPS device
> > +
> > +This is accessed via a serial port and is largely controlled via that
> > +link.  Extra configuration is needed to enable power on/off
> > +
> > +Required properties:
> > +- compatible: "wi2wi,w2sg0004"
> > +- gpios: gpios used to toggle 'on/off' pin
> > +- interrupts: interrupt generated by RX pin when device
> > +      should be off
> > +
> > +Optional properties:
> > +- vdd-supply: regulator used to power antenna
> > +- pinctrl: "default", "off"
> > +      if "off" setting is provided it is imposed when device should
> > +      be off.  This can route the RX pin to a GPIO interrupt.
> > +
> > +The w2sg0004 uses a pin-toggle both to power-on and to
> > +power-off, so the driver needs to detect what state it is in.
> > +It does this by detecting characters on the RX line.
> > +When it should be off, these can optionally be detected by a GPIO.
> > +
> > +The node for this device must be the child of a UART.
> > +
> > +Example:
> > +&uart2 {
> > +       gps {
> > +               compatible = "wi2iw,w2sg0004";
> > +               vdd-supply = <&vsim>;
> > +               gpios = <&gpio5 17 0>; /* GPIO_145 */
> > +               interrupts-extended = <&gpio5 19 0>; /* GPIO_147 */
> > +               /* When off, switch RX to be an interrupt */
> > +               pinctrl-names = "default", "off";
> > +               pinctrl-0 = <&uart2_pins>;
> > +               pinctrl-1 = <&uart2_pins_rx_gpio>;
> > +       };
> > +};
> 
> If the wi2wi driver is a regulator driver one would write
> 
> / {
>        gps-regulator: gps {
>                compatible = "wi2iw,w2sg0004";
>                vdd-supply = <&vsim>;
>                gpios = <&gpio5 17 0>; /* GPIO_145 */
>                interrupts-extended = <&gpio5 19 0>; /* GPIO_147 */
>                /* When off, switch RX to be an interrupt */
>                pinctrl-names = "default", "off";
>                pinctrl-0 = <&uart2_pins>;
>                pinctrl-1 = <&uart2_pins_rx_gpio>;
>        };
> }
> 
> &uart2 {
> 	vdd-suppy = <&gps-regulator>;
> };
> 
> Which IMHO better describes that the uart controls power of a separate driver.

But the uart doesn't control the power.
An 'open' on the tty causes one driver to turn on a regulator, and another
driver to activate a uart so that the device represented by the tty can be
communicated with.

> 
> And this pattern for writing a DT would IMHO be more flexible because you
> can „connect“ to any regulator, e.g. a regulator for a RS232 level shifter.

I'm sure there are lots of ways we could find to make DT more flexible, only
then it wouldn't be DT any more - it would be something similar but different.

There needs to be one device-node for each device, and that device-node needs
to be a child of the device-node for the device which is the primary
connection to the child device.  That is how devicetree is structured - for
better or worse.



> 
> 
> > diff --git a/Documentation/devicetree/bindings/vendor-prefixes.txt b/Documentation/devicetree/bindings/vendor-prefixes.txt
> > index 389ca1347a77..81d259303710 100644
> > --- a/Documentation/devicetree/bindings/vendor-prefixes.txt
> > +++ b/Documentation/devicetree/bindings/vendor-prefixes.txt
> > @@ -189,6 +189,7 @@ variscite	Variscite Ltd.
> > via	VIA Technologies, Inc.
> > virtio	Virtual I/O Device Specification, developed by the OASIS consortium
> > voipac	Voipac Technologies s.r.o.
> > +wi2wi	wi2wi Inc.  http://www.wi2wi.com/
> > winbond Winbond Electronics corp.
> > wlf	Wolfson Microelectronics
> > wm	Wondermedia Technologies, Inc.
> > diff --git a/drivers/tty/slave/Kconfig b/drivers/tty/slave/Kconfig
> > index 3976760c2e28..05c5d966ae57 100644
> > --- a/drivers/tty/slave/Kconfig
> > +++ b/drivers/tty/slave/Kconfig
> > @@ -5,3 +5,17 @@ menuconfig TTY_SLAVE
> > 	  Devices which attach via a uart, but need extra
> > 	  driver support for power management etc.
> > 
> > +if TTY_SLAVE
> > +
> > +config SERIAL_POWER_MANAGER
> > +	tristate "Power Management controller for serial-attached devices"
> > +	default n
> > +	help
> > +	  Some devices permanently attached via a UART can benefit from
> > +	  being power-managed when the tty device is opened or closed.
> > +	  This driver can support several such devices with simple
> > +	  power requirements such as enabling a regulator.
> > +
> > +	  If in doubt, say 'N'
> > +
> > +endif
> > diff --git a/drivers/tty/slave/Makefile b/drivers/tty/slave/Makefile
> > index 65669acb392e..a2f7d2847319 100644
> > --- a/drivers/tty/slave/Makefile
> > +++ b/drivers/tty/slave/Makefile
> > @@ -1,2 +1,4 @@
> > 
> > obj-$(CONFIG_TTY_SLAVE) += tty_slave_core.o
> > +
> > +obj-$(CONFIG_SERIAL_POWER_MANAGER) += serial-power-manager.o
> > diff --git a/drivers/tty/slave/serial-power-manager.c b/drivers/tty/slave/serial-power-manager.c
> > new file mode 100644
> > index 000000000000..662a526d8630
> > --- /dev/null
> > +++ b/drivers/tty/slave/serial-power-manager.c
> > @@ -0,0 +1,510 @@
> > +/*
> > + * Serial-power-manager
> > + * tty-slave device that intercepts open/close events on the tty,
> > + * and turns power on/off for the device which is connected.
> > + *
> > + * Currently supported devices:
> > + *  wi2wi,w2sg0004 - GPS with on/off toggle on a GPIO
> > + *  wi2wi,w2cbw003 - bluetooth port; powered by regulator.
> > + *
> > + * When appropriate, an RFKILL will be registered which
> > + * can power-down the device even when it is open.
> > + *
> > + * Device can be turned on either by
> > + *  - enabling a regulator.  Disable to turn off
> > + *  - toggling a GPIO.  Toggle again to turn off.  This requires
> > + *     that we know the current state.  It is assumed to be 'off'
> > + *     at boot, however if an interrupt can be generated when on,
> > + *     such as by connecting RX to a GPIO, that can be used to detect
> > + *     if the device is on when it should be off.
> 
> Why does this driver mix both things? 
> 
> The only thing they have in common is that both are uart slaves
> and that they have a serial interface. But power control is very
> different.

Because if I wrote two drivers, they would have more code in common than they
would have differences.


> 
> One driver per fundamentally different chip...
> 
> > + */
> > +
> > +#include <linux/module.h>
> > +#include <linux/slab.h>
> > +#include <linux/err.h>
> > +#include <linux/regulator/consumer.h>
> > +#include <linux/platform_device.h>
> > +#include <linux/of_device.h>
> > +#include <linux/tty.h>
> > +#include <linux/gpio.h>
> > +#include <linux/of_gpio.h>
> > +#include <linux/of_irq.h>
> > +#include <linux/interrupt.h>
> > +#include <linux/delay.h>
> > +#include <linux/rfkill.h>
> > +
> > +#include <linux/tty_slave.h>
> > +
> > +/* This is used for testing. Setting this module parameter
> > + * will simulate booting with the device "on"
> > + */
> > +static bool toggle_on_probe = false;
> > +module_param(toggle_on_probe, bool, 0);
> > +MODULE_PARM_DESC(toggle_on_probe, "simulate power-on with devices active");
> > +
> > +struct spm_config {
> > +	int	rfkill_type;		/* type of rfkill to register */
> > +	int	toggle_time;		/* msec to pulse GPIO for on/off */
> > +	int	toggle_gap;		/* min msecs between toggles */
> > +	bool	off_in_suspend;
> > +}
> > +	simple_config = {
> > +		.off_in_suspend = true,
> > +	},
> > +	w2sg_config = {
> > +		.rfkill_type = RFKILL_TYPE_GPS,
> 
> The driver pretends to be generic by its name but incorporates a lot of specific
> knowledge about the w2sg chip, e.g. that it is a GPS chip.
> 
> > +		.toggle_time = 10,
> > +		.toggle_gap = 500,
> > +		.off_in_suspend = true,
> > +	};
> > +
> > +const static struct of_device_id spm_dt_ids[] = {
> > +       { .compatible = "wi2wi,w2sg0004", .data = &w2sg_config},
> > +       { .compatible = "wi2wi,w2cbw003", .data = &simple_config},
> 
> Well, how large will this table become if other uart slave device types
> are added?

When that becomes a problem it can trivially be solved.  While it is not a
problem there is no value in solving it.


> 
> > +       {}
> > +};
> > +
> > +struct spm_data {
> > +	const struct spm_config *config;
> > +	struct gpio_desc *gpiod;
> > +	int		irq;	/* irq line from RX pin when pinctrl
> > +				 * set to 'idle' */
> > +	struct regulator *reg;
> > +
> > +	unsigned long	toggle_time;
> > +	unsigned long	toggle_gap;
> > +	unsigned long	last_toggle;	/* jiffies when last toggle completed. */
> > +	unsigned long	backoff;	/* jiffies since last_toggle when
> > +					 * we try again
> > +					 */
> > +	enum {Idle, Down, Up} state;	/* state-machine state. */
> > +
> > +	int		open_cnt;
> > +	bool		requested, is_on;
> > +	bool		suspended;
> > +	bool		reg_enabled;
> > +
> > +	struct pinctrl	*pins;
> > +	struct pinctrl_state *pins_off;
> > +
> > +	struct delayed_work work;
> > +	spinlock_t	lock;
> > +	struct device	*dev;
> > +
> > +	struct rfkill	*rfkill;
> > +
> > +	int (*old_open)(struct tty_struct * tty, struct file * filp);
> > +	void (*old_close)(struct tty_struct * tty, struct file * filp);
> > +
> > +};
> > +
> > +/* When a device is powered on/off by toggling a GPIO we perform
> > + * all the toggling via a workqueue to ensure only one toggle happens
> > + * at a time and to allow easy timing.
> > + * This is managed as a state machine which transitions
> > + *  Idle -> Down -> Up -> Idle
> > + * The GPIO is held down for toggle_time and then up for toggle_time,
> > + * and then we assume the device has changed state.
> > + * We never toggle until at least toggle_gap has passed since the
> > + * last toggle.
> > + */
> > +static void toggle_work(struct work_struct *work)
> > +{
> > +	struct spm_data *data = container_of(
> > +		work, struct spm_data, work.work);
> > +
> > +	if (data->gpiod == NULL)
> > +		return;
> > +
> > +	spin_lock_irq(&data->lock);
> > +	switch (data->state) {
> > +	case Up:
> > +		data->state = Idle;
> > +		if (data->requested == data->is_on)
> > +			break;
> > +		if (!data->requested)
> > +			/* Assume it is off unless activity is detected */
> > +			break;
> > +		/* Try again in a while unless we get some activity */
> > +		dev_dbg(data->dev, "Wait %dusec until retry\n",
> > +			jiffies_to_msecs(data->backoff));
> > +		schedule_delayed_work(&data->work, data->backoff);
> > +		break;
> > +	case Idle:
> > +		if (data->requested == data->is_on)
> > +			break;
> > +
> > +		/* Time to toggle */
> > +		dev_dbg(data->dev, "Starting toggle to turn %s\n",
> > +			data->requested ? "on" : "off");
> > +		data->state = Down;
> > +		spin_unlock_irq(&data->lock);
> > +		gpiod_set_value_cansleep(data->gpiod, 1);
> > +		schedule_delayed_work(&data->work, data->toggle_time);
> > +
> > +		return;
> > +
> > +	case Down:
> > +		data->state = Up;
> > +		data->last_toggle = jiffies;
> > +		dev_dbg(data->dev, "Toggle completed, should be %s now.\n",
> > +			data->is_on ? "off" : "on");
> > +		data->is_on = ! data->is_on;
> > +		spin_unlock_irq(&data->lock);
> > +
> > +		gpiod_set_value_cansleep(data->gpiod, 0);
> > +		schedule_delayed_work(&data->work, data->toggle_time);
> > +
> > +		return;
> > +	}
> > +	spin_unlock_irq(&data->lock);
> > +}
> > +
> > +static irqreturn_t spm_isr(int irq, void *dev_id)
> > +{
> > +	struct spm_data *data = dev_id;
> > +	unsigned long flags;
> > +
> > +	spin_lock_irqsave(&data->lock, flags);
> > +	if (!data->requested && !data->is_on && data->state == Idle &&
> > +	    time_after(jiffies, data->last_toggle + data->backoff)) {
> > +		data->is_on = 1;
> > +		data->backoff *= 2;
> > +		dev_dbg(data->dev, "Received data, must be on. Try to turn off\n");
> > +		if (!data->suspended)
> > +			schedule_delayed_work(&data->work, 0);
> > +	}
> > +	spin_unlock_irqrestore(&data->lock, flags);
> > +	return IRQ_HANDLED;
> > +}
> > +
> > +static void spm_on(struct spm_data *data)
> > +{
> > +	if (!data->rfkill || !rfkill_blocked(data->rfkill)) {
> > +		unsigned long flags;
> > +
> > +		if (!data->reg_enabled &&
> > +		    data->reg &&
> > +		    regulator_enable(data->reg) == 0)
> > +			data->reg_enabled = true;
> > +
> > +		spin_lock_irqsave(&data->lock, flags);
> > +		if (!data->requested) {
> > +			dev_dbg(data->dev, "TTY open - turn device on\n");
> > +			data->requested = true;
> > +			data->backoff = data->toggle_gap;
> > +			if (data->irq > 0) {
> > +				disable_irq(data->irq);
> > +				pinctrl_pm_select_default_state(data->dev);
> > +			}
> > +			if (!data->suspended && data->state == Idle)
> > +				schedule_delayed_work(&data->work, 0);
> > +		}
> > +		spin_unlock_irqrestore(&data->lock, flags);
> > +	}
> > +}
> > +
> > +static int spm_open(struct tty_struct *tty, struct file *filp)
> > +{
> > +	struct spm_data *data = dev_get_drvdata(tty->dev->parent);
> > +
> > +	data->open_cnt++;
> > +	spm_on(data);
> > +	if (data->old_open)
> > +		return data->old_open(tty, filp);
> > +}
> > +
> > +static void spm_off(struct spm_data *data)
> > +{
> > +	unsigned long flags;
> > +
> > +	if (data->reg && data->reg_enabled)
> > +		if (regulator_disable(data->reg) == 0)
> > +			data->reg_enabled = false;
> > +
> > +	spin_lock_irqsave(&data->lock, flags);
> > +	if (data->requested) {
> > +		data->requested = false;
> > +		data->backoff = data->toggle_gap;
> > +		if (data->pins_off) {
> > +			pinctrl_select_state(data->pins,
> > +					     data->pins_off);
> > +			enable_irq(data->irq);
> > +		}
> > +		if (!data->suspended && data->state == Idle)
> > +			schedule_delayed_work(&data->work, 0);
> > +	}
> > +	spin_unlock_irqrestore(&data->lock, flags);
> > +}
> > +
> > +static void spm_close(struct tty_struct *tty, struct file *filp)
> > +{
> > +	struct spm_data *data = dev_get_drvdata(tty->dev->parent);
> > +
> > +	data->open_cnt--;
> > +	if (!data->open_cnt) {
> > +		dev_dbg(data->dev, "TTY closed - turn device off\n");
> > +		spm_off(data);
> > +	}
> > +
> > +	if (data->old_close)
> > +		data->old_close(tty, filp);
> > +}
> > +
> > +static int spm_rfkill_set_block(void *vdata, bool blocked)
> > +{
> > +	struct spm_data *data = vdata;
> > +
> > +	dev_dbg(data->dev, "rfkill_set_blocked %d\n", blocked);
> > +	if (blocked)
> > +		spm_off(data);
> > +
> > +	if (!blocked &&
> > +	    data->open_cnt)
> > +		spm_on(data);
> > +
> > +	return 0;
> > +}
> > +
> > +static struct rfkill_ops spm_rfkill_ops = {
> > +	.set_block = spm_rfkill_set_block,
> > +};
> > +
> > +static int spm_suspend(struct device *dev)
> > +{
> > +	/* Ignore incoming data and just turn device off.
> > +	 * we cannot really wait for a separate thread to
> > +	 * do things, so we disable that and do it all
> > +	 * here
> > +	 */
> > +	struct spm_data *data = dev_get_drvdata(dev);
> > +
> > +	spin_lock_irq(&data->lock);
> > +	data->suspended = true;
> > +	spin_unlock_irq(&data->lock);
> > +	if (!data->config->off_in_suspend)
> > +		return 0;
> > +
> > +	if (data->gpiod) {
> > +
> > +		cancel_delayed_work_sync(&data->work);
> > +		if (data->state == Down) {
> > +			dev_dbg(data->dev, "Suspending while GPIO down - raising\n");
> > +			msleep(data->config->toggle_time);
> > +			gpiod_set_value_cansleep(data->gpiod, 0);
> > +			data->last_toggle = jiffies;
> > +			data->is_on = !data->is_on;
> > +			data->state = Up;
> > +		}
> > +		if (data->state == Up) {
> > +			msleep(data->config->toggle_time);
> > +			data->state = Idle;
> > +		}
> > +		if (data->is_on) {
> > +			dev_dbg(data->dev, "Suspending while device on: toggling\n");
> > +			gpiod_set_value_cansleep(data->gpiod, 1);
> > +			msleep(data->config->toggle_time);
> > +			gpiod_set_value_cansleep(data->gpiod, 0);
> > +			data->is_on = 0;
> > +		}
> > +	}
> > +
> > +	if (data->reg && data->reg_enabled)
> > +		if (regulator_disable(data->reg) == 0)
> > +			data->reg_enabled = false;
> > +
> > +	return 0;
> > +}
> > +
> > +static int spm_resume(struct device *dev)
> > +{
> > +	struct spm_data *data = dev_get_drvdata(dev);
> > +
> > +	spin_lock_irq(&data->lock);
> > +	data->suspended = false;
> > +	spin_unlock_irq(&data->lock);
> > +	schedule_delayed_work(&data->work, 0);
> > +
> > +	if (data->open_cnt &&
> > +	    (!data->rfkill || !rfkill_blocked(data->rfkill))) {
> > +		if (!data->reg_enabled &&
> > +		    data->reg &&
> > +		    regulator_enable(data->reg) == 0)
> > +			data->reg_enabled = true;
> > +	}
> > +	return 0;
> > +}
> > +
> > +static const struct dev_pm_ops spm_pm_ops = {
> > +	SET_SYSTEM_SLEEP_PM_OPS(spm_suspend, spm_resume)
> > +};
> > +
> > +static int spm_probe(struct device *dev)
> > +{
> > +	struct tty_slave *slave = container_of(dev, struct tty_slave, dev);
> > +	struct spm_data *data;
> > +	struct regulator *reg;
> > +	int err;
> > +	const struct of_device_id *id;
> > +	const char *name;
> > +
> > +	if (dev->parent == NULL)
> > +		return -ENODEV;
> > +
> > +	id = of_match_device(spm_dt_ids, dev);
> > +	if (!id)
> > +		return -ENODEV;
> > +
> > +	if (dev->of_node && dev->of_node->name)
> > +		name = dev->of_node->name;
> > +	else
> > +		name = "serial-power-manager";
> > +
> > +	data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
> > +	if (!data)
> > +		return -ENOMEM;
> > +
> > +	data->config = id->data;
> > +	data->toggle_time = msecs_to_jiffies(data->config->toggle_time) + 1;
> > +	data->toggle_gap = msecs_to_jiffies(data->config->toggle_gap) + 1;
> > +	data->last_toggle = jiffies;
> > +	data->backoff = data->toggle_gap;
> > +	data->state = Idle;
> > +	spin_lock_init(&data->lock);
> > +	INIT_DELAYED_WORK(&data->work, toggle_work);
> > +
> > +	/* If a regulator is provided, it is enabled on 'open'
> > +	 * and disabled on 'release'
> > +	 */
> > +	reg = devm_regulator_get(dev, "vdd");
> > +	if (IS_ERR(reg)) {
> > +		err = PTR_ERR(reg);
> > +		if (err != -ENODEV)
> > +			goto out;
> > +	} else
> > +		data->reg = reg;
> > +
> > +	/* If an irq is provided, any transitions are taken as
> > +	 * indication that the device is currently "on"
> > +	 */
> > +	data->irq = of_irq_get(dev->of_node, 0);
> > +	if (data->irq < 0) {
> > +		err = data->irq;
> > +		if (err != -EINVAL)
> > +			goto out;
> > +	} else {
> > +		dev_dbg(dev, "IRQ configured: %d\n", data->irq);
> > +
> > +		irq_set_status_flags(data->irq, IRQ_NOAUTOEN);
> > +		err = devm_request_irq(dev, data->irq, spm_isr,
> > +				       IRQF_TRIGGER_FALLING,
> > +				       name, data);
> > +
> > +		if (err)
> > +			goto out;
> > +
> > +	}
> 
> Up to here it is generic and makes no assumptions about a specific
> device.
> 
> > +
> > +	/* If a gpio is provided, then it is used to turn the device
> > +	 * on/off.
> 
> You have a compatible record (for two well defined chips), but you
> do control which functions are really used by providing/not providing
> some DT parameters.
> 
> Either one is redundant.
> 
> > +	 * If toggle_time is zero, then the GPIO directly controls
> > +	 * the device.
> 
> Very hidden and non-obvoius functionality.

Yes, that should probably be documented more clearly, or removed.


> 
> >  If non-zero, then the GPIO must be toggled to
> > +	 * change the state of the device.
> 
> All the following is very special logic for the w2sg0004 chip which should be
> divided out into a separate driver.
> 
> Marek and me already had proposed such a chip specific driver (to be located
> in drivers/misc) some months ago. It would encapsulate everything w2sg0004
> specific and present itself as a regulator (because that is its main purpose:
> control the LDO regulator inside the w2sg0004 chip).

Presenting itself as a regulator would be wrong because it isn't a regulator.


Thanks,
NeilBrown


> 
> We can resubmit it as soon as this driver stabilizes and finds acceptance.
> 
> > +	 */
> > +	data->gpiod = devm_gpiod_get(dev, NULL, GPIOD_OUT_LOW);
> > +	if (IS_ERR(data->gpiod)) {
> > +		err = PTR_ERR(data->gpiod);
> > +		if (err != -ENOENT)
> > +			goto out;
> > +		data->gpiod = NULL;
> > +	} else
> > +		dev_dbg(dev, "GPIO configured: %d\n",
> > +			desc_to_gpio(data->gpiod));
> > +
> > +	/* If an 'off' pinctrl state is defined, we apply that
> > +	 * when the device is assumed to be off.  This is expected to
> > +	 * route the 'rx' line to the 'irq' interrupt.
> > +	 */
> > +	data->pins = devm_pinctrl_get(dev);
> > +	if (data->pins && data->irq > 0) {
> > +		data->pins_off = pinctrl_lookup_state(data->pins, "off");
> > +		if (IS_ERR(data->pins_off))
> > +			data->pins_off = NULL;
> > +	}
> > +
> > +	if (data->config->rfkill_type) {
> > +		data->rfkill = rfkill_alloc(name, dev,
> > +					    data->config->rfkill_type,
> > +					    &spm_rfkill_ops, data);
> > +		if (!data->rfkill) {
> > +			err = -ENOMEM;
> > +			goto out;
> > +		}
> > +		err = rfkill_register(data->rfkill);
> > +		if (err) {
> > +			dev_err(dev, "Cannot register rfkill device");
> > +			rfkill_destroy(data->rfkill);
> > +			goto out;
> > +		}
> > +	}
> > +	dev_set_drvdata(dev, data);
> > +	data->dev = dev;
> > +	data->old_open = slave->ops.open;
> > +	data->old_close = slave->ops.close;
> > +	slave->ops.open = spm_open;
> > +	slave->ops.close = spm_close;
> > +	tty_slave_finalize(slave);
> > +
> > +	if (data->pins_off)
> > +		pinctrl_select_state(data->pins, data->pins_off);
> > +	if (data->irq > 0)
> > +		enable_irq(data->irq);
> > +
> > +	if (toggle_on_probe && data->gpiod) {
> > +		dev_dbg(data->dev, "Performing initial toggle\n");
> > +		gpiod_set_value_cansleep(data->gpiod, 1);
> > +		msleep(data->config->toggle_time);
> > +		gpiod_set_value_cansleep(data->gpiod, 0);
> > +		msleep(data->config->toggle_time);
> > +	}
> > +	err = 0;
> > +out:
> > +	dev_dbg(data->dev, "Probed: err=%d\n", err);
> > +	return err;
> > +}
> > +
> > +static int spm_remove(struct device *dev)
> > +{
> > +       struct spm_data *data = dev_get_drvdata(dev);
> > +
> > +       if (data->rfkill) {
> > +               rfkill_unregister(data->rfkill);
> > +               rfkill_destroy(data->rfkill);
> > +       }
> > +       return 0;
> > +}
> > +
> > +static struct device_driver spm_driver = {
> > +	.name		= "serial-power-manager",
> > +	.owner		= THIS_MODULE,
> > +	.of_match_table	= spm_dt_ids,
> > +	.probe		= spm_probe,
> > +	.remove		= spm_remove,
> > +};
> > +
> > +static int __init spm_init(void)
> > +{
> > +       return tty_slave_driver_register(&spm_driver);
> > +}
> > +module_init(spm_init);
> > +
> > +static void __exit spm_exit(void)
> > +{
> > +	driver_unregister(&spm_driver);
> > +}
> > +module_exit(spm_exit);
> > +
> > +MODULE_AUTHOR("NeilBrown <neil-+NVA1uvv1dVBDLzU/O5InQ@public.gmane.org>");
> > +MODULE_DEVICE_TABLE(of, spm_dt_ids);
> > +MODULE_DESCRIPTION("Power management for Serial-attached device.");
> > +MODULE_LICENSE("GPL v2");
> > 
> > 
> > _______________________________________________
> > Gta04-owner mailing list
> > Gta04-owner-xXXSsgcRVICgSpxsJD1C4w@public.gmane.org
> > http://lists.goldelico.com/mailman/listinfo.cgi/gta04-owner
> 
> --
> To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
> the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
> Please read the FAQ at  http://www.tux.org/lkml/


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 811 bytes --]

  parent reply	other threads:[~2015-03-20  8:54 UTC|newest]

Thread overview: 80+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2015-03-18  5:58 [PATCH 0/3] tty slave device support - version 3 NeilBrown
2015-03-18  5:58 ` [PATCH 2/3] TTY: add support for tty_slave devices NeilBrown
2015-03-18  9:11   ` Paul Bolle
2015-03-22  3:32     ` NeilBrown
2015-03-20 19:41   ` Pavel Machek
2015-03-22  3:42     ` [Gta04-owner] " NeilBrown
     [not found]       ` <20150322144209.14abc603-wvvUuzkyo1EYVZTmpyfIwg@public.gmane.org>
2015-03-22  7:58         ` Pavel Machek
2015-03-24 10:31   ` Jiri Slaby
2015-03-30 23:45     ` NeilBrown
2015-03-25 16:30   ` Peter Hurley
2015-03-25 21:17     ` [Gta04-owner] " NeilBrown
2015-03-27 11:09       ` Peter Hurley
2015-03-31  0:33         ` NeilBrown
2015-03-18  5:58 ` [PATCH 1/3] TTY: use class_find_device to find port in uart_suspend/resume NeilBrown
     [not found]   ` <20150318055831.21025.27864.stgit-wvvUuzkyo1EYVZTmpyfIwg@public.gmane.org>
2015-03-25 16:20     ` Peter Hurley
2015-03-29 21:49       ` [Gta04-owner] " NeilBrown
2015-03-18  5:58 ` [PATCH 3/3] tty/slaves: add a driver to power on/off UART attached devices NeilBrown
     [not found]   ` <20150318055831.21025.33670.stgit-wvvUuzkyo1EYVZTmpyfIwg@public.gmane.org>
2015-03-20  7:54     ` [Gta04-owner] " Dr. H. Nikolaus Schaller
     [not found]       ` <C53A3E5D-5AEA-4F0F-88A9-F9BC8CB0D0D6-xXXSsgcRVICgSpxsJD1C4w@public.gmane.org>
2015-03-20  8:54         ` NeilBrown [this message]
2015-03-20  9:34           ` Dr. H. Nikolaus Schaller
     [not found]             ` <14FB51CF-9568-4BF4-B917-2C019D992FDB-xXXSsgcRVICgSpxsJD1C4w@public.gmane.org>
2015-03-20 19:50               ` Pavel Machek
2015-03-20 23:31             ` NeilBrown
2015-03-24 17:58               ` Dr. H. Nikolaus Schaller
2015-03-25  1:45                 ` Sebastian Reichel
2015-03-25  7:59                   ` Dr. H. Nikolaus Schaller
2015-03-25 15:21                     ` Sebastian Reichel
2015-03-25 16:44                       ` Dr. H. Nikolaus Schaller
2015-03-26 18:08                         ` Sebastian Reichel
2015-03-27  9:22                           ` Dr. H. Nikolaus Schaller
2015-03-27 16:31                             ` Sebastian Reichel
2015-03-27 17:11                               ` Dr. H. Nikolaus Schaller
2015-04-27 20:35                                 ` Pavel Machek
2015-04-29  6:56                                   ` Dr. H. Nikolaus Schaller
2015-03-25 20:53                     ` Pavel Machek
2015-03-25 21:25                       ` Dr. H. Nikolaus Schaller
     [not found]                         ` <1F3B5E32-1330-457C-AE25-791319293C38-xXXSsgcRVICgSpxsJD1C4w@public.gmane.org>
2015-03-26  5:56                           ` Pavel Machek
2015-03-26  6:44                             ` Dr. H. Nikolaus Schaller
     [not found]                               ` <365B2C25-1782-4ADE-B620-190A4CC5E8E3-xXXSsgcRVICgSpxsJD1C4w@public.gmane.org>
2015-04-04  7:52                                 ` Pavel Machek
2015-03-25 20:42                 ` Pavel Machek
2015-03-25 21:22                   ` Dr. H. Nikolaus Schaller
2015-03-20  7:54 ` [Gta04-owner] [PATCH 0/3] tty slave device support - version 3 Dr. H. Nikolaus Schaller
     [not found]   ` <80D7E742-4633-4CCD-B754-221387D82922-xXXSsgcRVICgSpxsJD1C4w@public.gmane.org>
2015-03-20  8:43     ` NeilBrown
2015-03-20  8:54       ` Dr. H. Nikolaus Schaller
2015-03-20 13:08         ` Sebastian Reichel
2015-03-20 13:57           ` Dr. H. Nikolaus Schaller
     [not found]             ` <72268D51-F993-497A-B4F9-707C8DB7155C-xXXSsgcRVICgSpxsJD1C4w@public.gmane.org>
2015-03-20 17:14               ` Sebastian Reichel
2015-03-20 19:31 ` Pavel Machek
     [not found] ` <20150318055437.21025.13990.stgit-wvvUuzkyo1EYVZTmpyfIwg@public.gmane.org>
2015-05-05 19:54   ` Peter Hurley
2015-05-05 20:46     ` NeilBrown
     [not found]     ` <55492001.30806-WaGBZJeGNqdsbIuE7sb01tBPR1lH4CV8@public.gmane.org>
2015-05-06  5:19       ` [Gta04-owner] " Dr. H. Nikolaus Schaller
     [not found]         ` <DE60E7AA-1BF4-4A68-9638-E09F85FD5C72-xXXSsgcRVICgSpxsJD1C4w@public.gmane.org>
2015-05-06  9:27           ` Pavel Machek
2015-05-06 11:50             ` Dr. H. Nikolaus Schaller
2015-05-06 12:05               ` Peter Hurley
2015-05-06 12:27                 ` Dr. H. Nikolaus Schaller
2015-05-06 12:36                   ` Mark Rutland
2015-05-06 13:28                     ` Dr. H. Nikolaus Schaller
2015-05-06 14:15                       ` Mark Rutland
2015-05-06 16:09                         ` Dr. H. Nikolaus Schaller
2015-05-06 17:18                           ` Mark Rutland
2015-05-07 12:46                             ` Dr. H. Nikolaus Schaller
2015-05-07 14:30                               ` Peter Hurley
2015-05-07 15:11                                 ` Dr. H. Nikolaus Schaller
2015-05-07 16:18                                   ` Peter Hurley
2015-05-07 16:57                                     ` Dr. H. Nikolaus Schaller
     [not found]                               ` <B4D487C5-C260-497B-A441-6C381D53616C-xXXSsgcRVICgSpxsJD1C4w@public.gmane.org>
2015-05-07 14:56                                 ` Peter Hurley
     [not found]                                   ` <554B7D33.602-WaGBZJeGNqdsbIuE7sb01tBPR1lH4CV8@public.gmane.org>
2015-05-07 15:34                                     ` Dr. H. Nikolaus Schaller
2015-05-07 15:51                                       ` Peter Hurley
2015-05-07 16:46                                         ` Dr. H. Nikolaus Schaller
2015-05-13  8:09                                           ` Dr. H. Nikolaus Schaller
2015-06-03 11:49                                             ` [PATCH RFC 0/3] UART slave device support Dr. H. Nikolaus Schaller
     [not found]                                               ` <56C579D3-E8F8-4B5A-B6E8-993B113F3461-xXXSsgcRVICgSpxsJD1C4w@public.gmane.org>
2015-06-06 13:09                                                 ` Pavel Machek
     [not found]                                             ` <463356C5-E3C6-432C-A1C5-71F0287F1FEE@goldelico.com>
2015-06-03 12:09                                               ` [Gta04-owner] [PATCH RFC 3/3] misc: Add w2g0004 gps receiver driver Christ van Willegen
2015-05-07 15:37                       ` [Gta04-owner] [PATCH 0/3] tty slave device support - version 3 Peter Hurley
     [not found]               ` <A2594559-CA92-40C2-A06A-AC2483E85CB1-xXXSsgcRVICgSpxsJD1C4w@public.gmane.org>
2015-05-06 14:28                 ` Pavel Machek
2015-05-06 16:12                   ` Dr. H. Nikolaus Schaller
     [not found]                     ` <C68A105A-7147-4143-9B91-1A239726A780-xXXSsgcRVICgSpxsJD1C4w@public.gmane.org>
2015-06-06 13:09                       ` Pavel Machek
2015-06-06 18:53                         ` Belisko Marek
2015-06-06 18:55                           ` Belisko Marek
2015-05-07 15:48           ` Rob Herring
2015-05-06 11:10         ` Peter Hurley

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20150320195451.146a7915@notabene.brown \
    --to=neilb-l3a5bk7wagm@public.gmane.org \
    --cc=arnd-r2nGTMty4D4@public.gmane.org \
    --cc=devicetree-u79uwXL29TY76Z2rM5mHXA@public.gmane.org \
    --cc=gnomes-qBU/x9rampVanCEyBjwyrvXRex20P6io@public.gmane.org \
    --cc=grant.likely-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org \
    --cc=gregkh-hQyY1W1yCW8ekmWlsbkhG0B+6BGkLq7r@public.gmane.org \
    --cc=gta04-owner-xXXSsgcRVICgSpxsJD1C4w@public.gmane.org \
    --cc=hns-xXXSsgcRVICgSpxsJD1C4w@public.gmane.org \
    --cc=jslaby-AlSwsSmVLrQ@public.gmane.org \
    --cc=linux-kernel-u79uwXL29TY76Z2rM5mHXA@public.gmane.org \
    --cc=mark.rutland-5wv7dgnIgG8@public.gmane.org \
    --cc=pavel-+ZI9xUNit7I@public.gmane.org \
    --cc=peter-WaGBZJeGNqdsbIuE7sb01tBPR1lH4CV8@public.gmane.org \
    --cc=sre-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).