Message ID | c53ab7a3ca68f8b9f802c1ea799d72e1cb04a1eb.1445018913.git.hns@goldelico.com |
---|---|
State | Under Review, archived |
Headers | show |
Am 16.10.2015 um 20:08 schrieb H. Nikolaus Schaller <hns@goldelico.com>: > Add driver for Wi2Wi W2SG0004/84 GPS module connected through uart. > Use uart slave + notification hooks to glue with tty and turn on/off the > module. Detect if the module is turned on (sends data) but should be off, > e.g. if already turned on during boot. > > Additionally, rfkill block/unblock can be used to control an external LNA > (and power down the module if not needed). > > The driver concept is based on code developed by NeilBrown <neilb@suse.de> > but simplified and adapted to use the serial slave API. > > Signed-off-by: H. Nikolaus Schaller <hns@goldelico.com> > --- > .../devicetree/bindings/misc/wi2wi,w2sg0004.txt | 18 + > .../devicetree/bindings/vendor-prefixes.txt | 1 + > drivers/misc/Kconfig | 18 + > drivers/misc/Makefile | 1 + > drivers/misc/w2sg0004.c | 443 +++++++++++++++++++++ > drivers/tty/serial/serial_core.c | 25 +- ^^^sorry this change is garbage from patch editing^^^ > include/linux/w2sg0004.h | 27 ++ > 7 files changed, 522 insertions(+), 11 deletions(-) > create mode 100644 Documentation/devicetree/bindings/misc/wi2wi,w2sg0004.txt > create mode 100644 drivers/misc/w2sg0004.c > create mode 100644 include/linux/w2sg0004.h > > diff --git a/Documentation/devicetree/bindings/misc/wi2wi,w2sg0004.txt b/Documentation/devicetree/bindings/misc/wi2wi,w2sg0004.txt > new file mode 100644 > index 0000000..ef0d6d5 > --- /dev/null > +++ b/Documentation/devicetree/bindings/misc/wi2wi,w2sg0004.txt > @@ -0,0 +1,18 @@ > +Wi2Wi GPS module connected through UART > + > +Required properties: > +- compatible: wi2wi,w2sg0004 or wi2wi,w2sg0084 > +- on-off-gpio: the GPIO that controls the module's on-off toggle input > +- uart: the uart we are connected to (provides DTR for power control) > + > +Optional properties: > +- lna-suppy: an (optional) LNA regulator that is enabled together with the GPS receiver > + > +example: > + > + gps_receiver: w2sg0004 { > + compatible = "wi2wi,w2sg0004"; > + lna-supply = <&vsim>; /* LNA regulator */ > + on-off-gpio = <&gpio5 17 0>; /* GPIO_145: trigger for turning on/off w2sg0004 */ > + uart = <&uart1>; /* we are a slave of uart1 */ > + } > diff --git a/Documentation/devicetree/bindings/vendor-prefixes.txt b/Documentation/devicetree/bindings/vendor-prefixes.txt > index 82d2ac9..a778eb5 100644 > --- a/Documentation/devicetree/bindings/vendor-prefixes.txt > +++ b/Documentation/devicetree/bindings/vendor-prefixes.txt > @@ -230,6 +230,7 @@ via VIA Technologies, Inc. > virtio Virtual I/O Device Specification, developed by the OASIS consortium > voipac Voipac Technologies s.r.o. > wexler Wexler > +wi2wi Wi2Wi, Inc. > winbond Winbond Electronics corp. > wlf Wolfson Microelectronics > wm Wondermedia Technologies, Inc. > diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig > index ccccc29..1279faf 100644 > --- a/drivers/misc/Kconfig > +++ b/drivers/misc/Kconfig > @@ -537,4 +537,22 @@ source "drivers/misc/mic/Kconfig" > source "drivers/misc/genwqe/Kconfig" > source "drivers/misc/echo/Kconfig" > source "drivers/misc/cxl/Kconfig" > + > +menu "GTA04 misc hardware support" > + > +config W2SG0004 > + tristate "W2SG0004 on/off control" > + depends on GPIOLIB > + help > + Enable on/off control of W2SG0004 GPS to allow powering up/down if > + the /dev/tty$n is opened/closed. > + It also provides a rfkill gps node to control the LNA power. > + > +config W2SG0004_DEBUG > + bool "W2SG0004 on/off debugging" > + depends on W2SG0004 > + help > + Enable driver debugging mode of W2SG0004 GPS. > + > +endmenu > endmenu > diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile > index 537d7f3..a153a89 100644 > --- a/drivers/misc/Makefile > +++ b/drivers/misc/Makefile > @@ -54,5 +54,6 @@ obj-$(CONFIG_SRAM) += sram.o > obj-y += mic/ > obj-$(CONFIG_GENWQE) += genwqe/ > obj-$(CONFIG_ECHO) += echo/ > +obj-$(CONFIG_W2SG0004) += w2sg0004.o > obj-$(CONFIG_VEXPRESS_SYSCFG) += vexpress-syscfg.o > obj-$(CONFIG_CXL_BASE) += cxl/ > diff --git a/drivers/misc/w2sg0004.c b/drivers/misc/w2sg0004.c > new file mode 100644 > index 0000000..6aadf44 > --- /dev/null > +++ b/drivers/misc/w2sg0004.c > @@ -0,0 +1,443 @@ > +/* > + * w2sg0004.c > + * Driver for power controlling the w2sg0004/w2sg0084 GPS receiver. > + * > + * This receiver has an ON/OFF pin which must be toggled to > + * turn the device 'on' of 'off'. A high->low->high toggle > + * will switch the device on if it is off, and off if it is on. > + * > + * To enable receiving on/off requests we register with the > + * UART power management notifications. > + * > + * It is not possible to directly detect the state of the device. > + * However when it is on it will send characters on a UART line > + * regularly. > + * > + * To detect that the power state is out of sync (e.g. if GPS > + * was enabled before a reboot), we register for UART data received > + * notifications. > + * > + * In addition we register as a rfkill client so that we can > + * control the LNA power. > + * > + */ > + > +#include <linux/module.h> > +#include <linux/interrupt.h> > +#include <linux/sched.h> > +#include <linux/irq.h> > +#include <linux/slab.h> > +#include <linux/err.h> > +#include <linux/delay.h> > +#include <linux/of.h> > +#include <linux/of_irq.h> > +#include <linux/of_gpio.h> > +#include <linux/platform_device.h> > +#include <linux/w2sg0004.h> > +#include <linux/workqueue.h> > +#include <linux/rfkill.h> > +#include <linux/serial_core.h> > + > +#ifdef CONFIG_W2SG0004_DEBUG > +#undef pr_debug > +#define pr_debug printk > +#endif > + > +/* > + * There seems to be restrictions on how quickly we can toggle the > + * on/off line. data sheets says "two rtc ticks", whatever that means. > + * If we do it too soon it doesn't work. > + * So we have a state machine which uses the common work queue to ensure > + * clean transitions. > + * When a change is requested we record that request and only act on it > + * once the previous change has completed. > + * A change involves a 10ms low pulse, and a 990ms raised level, so only > + * one change per second. > + */ > + > +enum w2sg_state { > + W2SG_IDLE, /* is not changing state */ > + W2SG_PULSE, /* activate on/off impulse */ > + W2SG_NOPULSE /* deactivate on/off impulse */ > +}; > + > +struct w2sg_data { > + struct rfkill *rf_kill; > + struct regulator *lna_regulator; > + int lna_blocked; /* rfkill block gps active */ > + int lna_is_off; /* LNA is currently off */ > + int is_on; /* current state (0/1) */ > + unsigned long last_toggle; > + unsigned long backoff; /* time to wait since last_toggle */ > + int on_off_gpio; /* the on-off gpio number */ > + struct uart_port *uart; /* the drvdata of the uart or tty */ > + enum w2sg_state state; > + int requested; /* requested state (0/1) */ > + int suspended; > + spinlock_t lock; > + struct delayed_work work; > +}; > + > +static int w2sg_data_set_lna_power(struct w2sg_data *data) > +{ > + int ret = 0; > + int off = data->suspended || !data->requested || data->lna_blocked; > + > + pr_debug("%s: %s\n", __func__, off ? "off" : "on"); > + > + if (off != data->lna_is_off) { > + data->lna_is_off = off; > + if (!IS_ERR_OR_NULL(data->lna_regulator)) { > + if (off) > + regulator_disable(data->lna_regulator); > + else > + ret = regulator_enable(data->lna_regulator); > + } > + } > + > + return ret; > +} > + > +static void w2sg_data_set_power(void *pdata, int val) > +{ > + struct w2sg_data *data = (struct w2sg_data *) pdata; > + unsigned long flags; > + > + pr_debug("%s to %d (%d)\n", __func__, val, data->requested); > + > + spin_lock_irqsave(&data->lock, flags); > + > + if (val && !data->requested) { > + data->requested = true; > + } else if (!val && data->requested) { > + data->backoff = HZ; > + data->requested = false; > + } else > + goto unlock; > + > + pr_debug("w2sg scheduled for %d\n", data->requested); > + > + if (!data->suspended) > + schedule_delayed_work(&data->work, 0); > +unlock: > + spin_unlock_irqrestore(&data->lock, flags); > +} > + > +/* called each time data is received by the host (i.e. sent by the w2sg0004) */ > + > +static int rx_notification(void *pdata, unsigned int *c) > +{ > + struct w2sg_data *data = (struct w2sg_data *) pdata; > + unsigned long flags; > + > + if (!data->requested && !data->is_on) { > + /* we have received a RX signal while GPS should be off */ > + pr_debug("%02x!", *c); > + > + if ((data->state == W2SG_IDLE) && > + time_after(jiffies, > + data->last_toggle + data->backoff)) { > + /* Should be off by now, time to toggle again */ > + pr_debug("w2sg has sent data although it should be off!\n"); > + data->is_on = true; > + data->backoff *= 2; > + spin_lock_irqsave(&data->lock, flags); > + if (!data->suspended) > + schedule_delayed_work(&data->work, 0); > + spin_unlock_irqrestore(&data->lock, flags); > + } > + } > + return 0; /* forward to tty */ > +} > + > +/* called by uart modem control line changes (DTR) */ > + > +static int w2sg_mctrl(void *pdata, int val) > +{ > + pr_debug("%s(...,%x)\n", __func__, val); > + val = (val & TIOCM_DTR) != 0; /* DTR controls power on/off */ > + w2sg_data_set_power((struct w2sg_data *) pdata, val); > + return 0; > +} > + > +/* try to toggle the power state by sending a pulse to the on-off GPIO */ > + > +static void toggle_work(struct work_struct *work) > +{ > + struct w2sg_data *data = container_of(work, struct w2sg_data, > + work.work); > + > + switch (data->state) { > + case W2SG_IDLE: > + spin_lock_irq(&data->lock); > + if (data->requested == data->is_on) { > + spin_unlock_irq(&data->lock); > + return; > + } > + spin_unlock_irq(&data->lock); > + w2sg_data_set_lna_power(data); /* update LNA power state */ > + gpio_set_value_cansleep(data->on_off_gpio, 0); > + data->state = W2SG_PULSE; > + > + pr_debug("w2sg: power gpio ON\n"); > + > + schedule_delayed_work(&data->work, > + msecs_to_jiffies(10)); > + break; > + > + case W2SG_PULSE: > + gpio_set_value_cansleep(data->on_off_gpio, 1); > + data->last_toggle = jiffies; > + data->state = W2SG_NOPULSE; > + data->is_on = !data->is_on; > + > + pr_debug("w2sg: power gpio OFF\n"); > + > + schedule_delayed_work(&data->work, > + msecs_to_jiffies(10)); > + break; > + > + case W2SG_NOPULSE: > + data->state = W2SG_IDLE; > + pr_debug("w2sg: idle\n"); > + > + break; > + > + } > +} > + > +static int w2sg_data_rfkill_set_block(void *pdata, bool blocked) > +{ > + struct w2sg_data *data = pdata; > + > + pr_debug("%s: blocked: %d\n", __func__, blocked); > + > + data->lna_blocked = blocked; > + > + return w2sg_data_set_lna_power(data); > +} > + > +static struct rfkill_ops w2sg_data0004_rfkill_ops = { > + .set_block = w2sg_data_rfkill_set_block, > +}; > + > +static int w2sg_data_probe(struct platform_device *pdev) > +{ > + struct w2sg_pdata *pdata = dev_get_platdata(&pdev->dev); > + struct w2sg_data *data; > + struct rfkill *rf_kill; > + int err; > + > + pr_debug("%s()\n", __func__); > + > + if (pdev->dev.of_node) { > + struct device *dev = &pdev->dev; > + enum of_gpio_flags flags; > + > + pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL); > + if (!pdata) > + return -ENOMEM; > + > + pdata->on_off_gpio = of_get_named_gpio_flags(dev->of_node, > + "on-off-gpio", 0, > + &flags); > + > + if (pdata->on_off_gpio == -EPROBE_DEFER) > + return -EPROBE_DEFER; /* defer until we have all gpios */ > + > + pdata->lna_regulator = devm_regulator_get_optional(dev, "lna"); > + > + pr_debug("%s() lna_regulator = %p\n", __func__, pdata->lna_regulator); > + > + pdev->dev.platform_data = pdata; > + } > + > + data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); > + if (data == NULL) > + return -ENOMEM; > + > + data->lna_regulator = pdata->lna_regulator; > + data->lna_blocked = true; > + data->lna_is_off = true; > + > + data->on_off_gpio = pdata->on_off_gpio; > + > + data->is_on = false; > + data->requested = false; > + data->state = W2SG_IDLE; > + data->last_toggle = jiffies; > + data->backoff = HZ; > + > +#ifdef CONFIG_OF > + data->uart = devm_serial_get_uart_by_phandle(&pdev->dev, "uart", 0); > + if (IS_ERR(data->uart)) { > + if (PTR_ERR(data->uart) == -EPROBE_DEFER) > + return -EPROBE_DEFER; /* we can't probe yet */ > + data->uart = NULL; /* no UART */ > + } > +#endif > + > + INIT_DELAYED_WORK(&data->work, toggle_work); > + spin_lock_init(&data->lock); > + > + err = devm_gpio_request(&pdev->dev, data->on_off_gpio, "w2sg0004-on-off"); > + if (err < 0) > + goto out; > + > + gpio_direction_output(data->on_off_gpio, false); > + > + if (data->uart) { > + struct ktermios termios = { > + .c_iflag = IGNBRK, > + .c_oflag = 0, > + .c_cflag = B9600 | CS8, > + .c_lflag = 0, > + .c_line = 0, > + .c_ispeed = 9600, > + .c_ospeed = 9600 > + }; > + uart_register_slave(data->uart, data); > + uart_register_mctrl_notification(data->uart, w2sg_mctrl); > + uart_register_rx_notification(data->uart, rx_notification, > + &termios); > + } > + > + rf_kill = rfkill_alloc("GPS", &pdev->dev, RFKILL_TYPE_GPS, > + &w2sg_data0004_rfkill_ops, data); > + if (rf_kill == NULL) { > + err = -ENOMEM; > + goto err_rfkill; > + } > + > + err = rfkill_register(rf_kill); > + if (err) { > + dev_err(&pdev->dev, "Cannot register rfkill device\n"); > + goto err_rfkill; > + } > + > + data->rf_kill = rf_kill; > + > + platform_set_drvdata(pdev, data); > + > + pr_debug("w2sg0004 probed\n"); > + > +#ifdef CONFIG_W2SG0004_DEBUG > + /* turn on for debugging rx notifications */ > + pr_debug("w2sg power gpio ON\n"); > + gpio_set_value_cansleep(data->on_off_gpio, 1); > + mdelay(100); > + pr_debug("w2sg power gpio OFF\n"); > + gpio_set_value_cansleep(data->on_off_gpio, 0); > + mdelay(300); > +#endif > + > + /* if we won't receive mctrl notifications, turn on. > + * otherwise keep off until DTR is asserted through mctrl. > + */ > + > + w2sg_data_set_power(data, !data->uart); > + > + return 0; > + > +err_rfkill: > + rfkill_destroy(rf_kill); > +out: > + return err; > +} > + > +static int w2sg_data_remove(struct platform_device *pdev) > +{ > + struct w2sg_data *data = platform_get_drvdata(pdev); > + > + cancel_delayed_work_sync(&data->work); > + > + if (data->uart) { > + uart_register_rx_notification(data->uart, NULL, NULL); > + uart_register_slave(data->uart, NULL); > + } > + return 0; > +} > + > +static int w2sg_data_suspend(struct device *dev) > +{ > + struct w2sg_data *data = dev_get_drvdata(dev); > + > + spin_lock_irq(&data->lock); > + data->suspended = true; > + spin_unlock_irq(&data->lock); > + > + cancel_delayed_work_sync(&data->work); > + > + w2sg_data_set_lna_power(data); /* shuts down if needed */ > + > + if (data->state == W2SG_PULSE) { > + msleep(10); > + gpio_set_value_cansleep(data->on_off_gpio, 1); > + data->last_toggle = jiffies; > + data->is_on = !data->is_on; > + data->state = W2SG_NOPULSE; > + } > + > + if (data->state == W2SG_NOPULSE) { > + msleep(10); > + data->state = W2SG_IDLE; > + } > + > + if (data->is_on) { > + pr_info("GPS off for suspend %d %d %d\n", data->requested, > + data->is_on, data->lna_is_off); > + > + gpio_set_value_cansleep(data->on_off_gpio, 0); > + msleep(10); > + gpio_set_value_cansleep(data->on_off_gpio, 1); > + data->is_on = 0; > + } > + > + return 0; > +} > + > +static int w2sg_data_resume(struct device *dev) > +{ > + struct w2sg_data *data = dev_get_drvdata(dev); > + > + spin_lock_irq(&data->lock); > + data->suspended = false; > + spin_unlock_irq(&data->lock); > + > + pr_info("GPS resuming %d %d %d\n", data->requested, > + data->is_on, data->lna_is_off); > + > + schedule_delayed_work(&data->work, 0); /* enables LNA if needed */ > + > + return 0; > +} > + > +#if defined(CONFIG_OF) > +static const struct of_device_id w2sg0004_of_match[] = { > + { .compatible = "wi2wi,w2sg0004" }, > + { .compatible = "wi2wi,w2sg0084" }, > + {}, > +}; > +MODULE_DEVICE_TABLE(of, w2sg0004_of_match); > +#endif > + > +SIMPLE_DEV_PM_OPS(w2sg_pm_ops, w2sg_data_suspend, w2sg_data_resume); > + > +static struct platform_driver w2sg_data_driver = { > + .probe = w2sg_data_probe, > + .remove = w2sg_data_remove, > + .driver = { > + .name = "w2sg0004", > + .owner = THIS_MODULE, > + .pm = &w2sg_pm_ops, > + .of_match_table = of_match_ptr(w2sg0004_of_match) > + }, > +}; > + > +module_platform_driver(w2sg_data_driver); > + > +MODULE_ALIAS("w2sg0004"); > + > +MODULE_AUTHOR("NeilBrown <neilb@suse.de>"); > +MODULE_DESCRIPTION("w2sg0004 GPS power management driver"); > +MODULE_LICENSE("GPL v2"); VVVsorry this change is garbage from patch editingVVV > diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c > index b731100..c8ae0dd 100644 > --- a/drivers/tty/serial/serial_core.c > +++ b/drivers/tty/serial/serial_core.c > @@ -177,7 +177,7 @@ void uart_register_slave(struct uart_port *uport, void *slave) > EXPORT_SYMBOL_GPL(uart_register_slave); > > void uart_register_mctrl_notification(struct uart_port *uport, > - int (*function)(void *slave, int state)) > + int (*function)(void *slave, int state)) > { > uport->mctrl_notification = function; > } > @@ -208,14 +208,14 @@ void uart_register_rx_notification(struct uart_port *uport, > retval = uport->ops->startup(uport); > if (retval == 0 && termios) { > int hw_stopped; > - /* > - * Initialise the hardware port settings. > - */ > +/* > + * Initialise the hardware port settings. > + */ > uport->ops->set_termios(uport, termios, NULL); > > - /* > - * Set modem status enables based on termios cflag > - */ > +/* > + * Set modem status enables based on termios cflag > + */ > spin_lock_irq(&uport->lock); > if (termios->c_cflag & CRTSCTS) > uport->status |= UPSTAT_CTS_ENABLE; > @@ -227,11 +227,13 @@ void uart_register_rx_notification(struct uart_port *uport, > else > uport->status |= UPSTAT_DCD_ENABLE; > > - /* reset sw-assisted CTS flow control based on (possibly) new mode */ > +/* > + * reset sw-assisted CTS flow control based on (possibly) new mode > + */ > hw_stopped = uport->hw_stopped; > uport->hw_stopped = uart_softcts_mode(uport) && > - !(uport->ops->get_mctrl(uport) > - & TIOCM_CTS); > + !(uport->ops->get_mctrl(uport) & > + TIOCM_CTS); > if (uport->hw_stopped) { > if (!hw_stopped) > uport->ops->stop_tx(uport); > @@ -432,7 +434,8 @@ static void uart_shutdown(struct tty_struct *tty, struct uart_state *state) > uart_clear_mctrl(uport, TIOCM_DTR | TIOCM_RTS); > /* > * if we have a slave that has registered for rx_notifications > - * we do not shut down the uart port to be able to monitor the device > + * we do not shut down the uart port to be able to monitor the > + * device > */ > if (!uport->rx_notification) > uart_port_shutdown(port); ^^^sorry this change is garbage from patch editing^^^ > diff --git a/include/linux/w2sg0004.h b/include/linux/w2sg0004.h > new file mode 100644 > index 0000000..ad0c4a1 > --- /dev/null > +++ b/include/linux/w2sg0004.h > @@ -0,0 +1,27 @@ > +/* > + * UART slave to allow ON/OFF control of w2sg0004 GPS receiver. > + * > + * Copyright (C) 2011 Neil Brown <neil@brown.name> > + * > + * This program is free software; you can redistribute it and/or > + * modify it under the terms of the GNU General Public License > + * version 2 as published by the Free Software Foundation. > + * > + * This program is distributed in the hope that it will be useful, but > + * WITHOUT ANY WARRANTY; without even the implied warranty of > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU > + * General Public License for more details. > + */ > + > + > + > +#ifndef __LINUX_W2SG0004_H > +#define __LINUX_W2SG0004_H > + > +#include <linux/regulator/consumer.h> > + > +struct w2sg_pdata { > + struct regulator *lna_regulator; /* enable LNA power */ > + int on_off_gpio; /* on-off input of the GPS module */ > +}; > +#endif /* __LINUX_W2SG0004_H */ > -- > 2.5.1 > -- To unsubscribe from this list: send the line "unsubscribe devicetree" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
On Friday 16 October 2015 20:08:35 H. Nikolaus Schaller wrote: > + > +static int w2sg_data_probe(struct platform_device *pdev) > +{ > + struct w2sg_pdata *pdata = dev_get_platdata(&pdev->dev); > + struct w2sg_data *data; > + struct rfkill *rf_kill; > + int err; > + > + pr_debug("%s()\n", __func__); > + > + if (pdev->dev.of_node) { > + struct device *dev = &pdev->dev; > + enum of_gpio_flags flags; > + > + pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL); > + if (!pdata) > Why is this a platform_device and not a serio_device? Arnd -- To unsubscribe from this list: send the line "unsubscribe devicetree" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
Am 16.10.2015 um 21:06 schrieb Arnd Bergmann <arnd@arndb.de>: > On Friday 16 October 2015 20:08:35 H. Nikolaus Schaller wrote: >> + >> +static int w2sg_data_probe(struct platform_device *pdev) >> +{ >> + struct w2sg_pdata *pdata = dev_get_platdata(&pdev->dev); >> + struct w2sg_data *data; >> + struct rfkill *rf_kill; >> + int err; >> + >> + pr_debug("%s()\n", __func__); >> + >> + if (pdev->dev.of_node) { >> + struct device *dev = &pdev->dev; >> + enum of_gpio_flags flags; >> + >> + pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL); >> + if (!pdata) >> > > Why is this a platform_device and not a serio_device? I can't find a struct serio_device. What is that? BR and thanks, Nikolaus -- To unsubscribe from this list: send the line "unsubscribe devicetree" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
On Friday 16 October 2015 21:27:11 H. Nikolaus Schaller wrote: > Am 16.10.2015 um 21:06 schrieb Arnd Bergmann <arnd@arndb.de>: > > > On Friday 16 October 2015 20:08:35 H. Nikolaus Schaller wrote: > >> + > >> +static int w2sg_data_probe(struct platform_device *pdev) > >> +{ > >> + struct w2sg_pdata *pdata = dev_get_platdata(&pdev->dev); > >> + struct w2sg_data *data; > >> + struct rfkill *rf_kill; > >> + int err; > >> + > >> + pr_debug("%s()\n", __func__); > >> + > >> + if (pdev->dev.of_node) { > >> + struct device *dev = &pdev->dev; > >> + enum of_gpio_flags flags; > >> + > >> + pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL); > >> + if (!pdata) > >> > > > > Why is this a platform_device and not a serio_device? > > I can't find a struct serio_device. What is that? > Sorry, I meant 'struct serio', see drivers/input/serio/ This is an existing infrastructure that is used for devices attached to a dumb serial device (rs232 or 8042/psaux usually). They have a user interface for connecting a driver to a port, but you should be able to do it all in the kernel as well if DT has the information what device is connected. Arnd -- To unsubscribe from this list: send the line "unsubscribe devicetree" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
Am 16.10.2015 um 21:38 schrieb Arnd Bergmann <arnd@arndb.de>: > On Friday 16 October 2015 21:27:11 H. Nikolaus Schaller wrote: >> Am 16.10.2015 um 21:06 schrieb Arnd Bergmann <arnd@arndb.de>: >> >>> On Friday 16 October 2015 20:08:35 H. Nikolaus Schaller wrote: >>>> + >>>> +static int w2sg_data_probe(struct platform_device *pdev) >>>> +{ >>>> + struct w2sg_pdata *pdata = dev_get_platdata(&pdev->dev); >>>> + struct w2sg_data *data; >>>> + struct rfkill *rf_kill; >>>> + int err; >>>> + >>>> + pr_debug("%s()\n", __func__); >>>> + >>>> + if (pdev->dev.of_node) { >>>> + struct device *dev = &pdev->dev; >>>> + enum of_gpio_flags flags; >>>> + >>>> + pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL); >>>> + if (!pdata) >>>> >>> >>> Why is this a platform_device and not a serio_device? >> >> I can't find a struct serio_device. What is that? >> > > Sorry, I meant 'struct serio', see drivers/input/serio/ > > This is an existing infrastructure that is used for devices attached > to a dumb serial device (rs232 or 8042/psaux usually). They have > a user interface for connecting a driver to a port, but you should > be able to do it all in the kernel as well if DT has the information > what device is connected. Ah, I understand. But it is for a different purpose. E.g. making a serial device (mouse/touch) an input device. So it is a driver sitting "on top" of tty/uart drivers. The problem to be solved here is a different one. The only task for the driver is to do power control of the device. I.e. turn it on by open("/dev/ttyX") or asserting DTR. So we are on a much lower level. Please see also the patch 0/3 I have resent (the BLURB defined by git --edit-description was apparently eaten by my git send-email). BR and thanks, Nikolaus -- To unsubscribe from this list: send the line "unsubscribe devicetree" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
Hi Nikolaus, [auto build test WARNING on tty/tty-next -- if it's inappropriate base, please suggest rules for selecting the more suitable base] url: https://github.com/0day-ci/linux/commits/H-Nikolaus-Schaller/UART-slave-device-support-goldelico-version/20151017-021238 config: xtensa-allmodconfig (attached as .config) reproduce: wget https://git.kernel.org/cgit/linux/kernel/git/wfg/lkp-tests.git/plain/sbin/make.cross -O ~/bin/make.cross chmod +x ~/bin/make.cross # save the attached .config to linux build tree make.cross ARCH=xtensa All warnings (new ones prefixed by >>): >> drivers/misc/w2sg0004.c:361:12: warning: 'w2sg_data_suspend' defined but not used [-Wunused-function] static int w2sg_data_suspend(struct device *dev) ^ >> drivers/misc/w2sg0004.c:399:12: warning: 'w2sg_data_resume' defined but not used [-Wunused-function] static int w2sg_data_resume(struct device *dev) ^ vim +/w2sg_data_suspend +361 drivers/misc/w2sg0004.c 355 uart_register_rx_notification(data->uart, NULL, NULL); 356 uart_register_slave(data->uart, NULL); 357 } 358 return 0; 359 } 360 > 361 static int w2sg_data_suspend(struct device *dev) 362 { 363 struct w2sg_data *data = dev_get_drvdata(dev); 364 365 spin_lock_irq(&data->lock); 366 data->suspended = true; 367 spin_unlock_irq(&data->lock); 368 369 cancel_delayed_work_sync(&data->work); 370 371 w2sg_data_set_lna_power(data); /* shuts down if needed */ 372 373 if (data->state == W2SG_PULSE) { 374 msleep(10); 375 gpio_set_value_cansleep(data->on_off_gpio, 1); 376 data->last_toggle = jiffies; 377 data->is_on = !data->is_on; 378 data->state = W2SG_NOPULSE; 379 } 380 381 if (data->state == W2SG_NOPULSE) { 382 msleep(10); 383 data->state = W2SG_IDLE; 384 } 385 386 if (data->is_on) { 387 pr_info("GPS off for suspend %d %d %d\n", data->requested, 388 data->is_on, data->lna_is_off); 389 390 gpio_set_value_cansleep(data->on_off_gpio, 0); 391 msleep(10); 392 gpio_set_value_cansleep(data->on_off_gpio, 1); 393 data->is_on = 0; 394 } 395 396 return 0; 397 } 398 > 399 static int w2sg_data_resume(struct device *dev) 400 { 401 struct w2sg_data *data = dev_get_drvdata(dev); 402 --- 0-DAY kernel test infrastructure Open Source Technology Center https://lists.01.org/pipermail/kbuild-all Intel Corporation
diff --git a/Documentation/devicetree/bindings/misc/wi2wi,w2sg0004.txt b/Documentation/devicetree/bindings/misc/wi2wi,w2sg0004.txt new file mode 100644 index 0000000..ef0d6d5 --- /dev/null +++ b/Documentation/devicetree/bindings/misc/wi2wi,w2sg0004.txt @@ -0,0 +1,18 @@ +Wi2Wi GPS module connected through UART + +Required properties: +- compatible: wi2wi,w2sg0004 or wi2wi,w2sg0084 +- on-off-gpio: the GPIO that controls the module's on-off toggle input +- uart: the uart we are connected to (provides DTR for power control) + +Optional properties: +- lna-suppy: an (optional) LNA regulator that is enabled together with the GPS receiver + +example: + + gps_receiver: w2sg0004 { + compatible = "wi2wi,w2sg0004"; + lna-supply = <&vsim>; /* LNA regulator */ + on-off-gpio = <&gpio5 17 0>; /* GPIO_145: trigger for turning on/off w2sg0004 */ + uart = <&uart1>; /* we are a slave of uart1 */ + } diff --git a/Documentation/devicetree/bindings/vendor-prefixes.txt b/Documentation/devicetree/bindings/vendor-prefixes.txt index 82d2ac9..a778eb5 100644 --- a/Documentation/devicetree/bindings/vendor-prefixes.txt +++ b/Documentation/devicetree/bindings/vendor-prefixes.txt @@ -230,6 +230,7 @@ via VIA Technologies, Inc. virtio Virtual I/O Device Specification, developed by the OASIS consortium voipac Voipac Technologies s.r.o. wexler Wexler +wi2wi Wi2Wi, Inc. winbond Winbond Electronics corp. wlf Wolfson Microelectronics wm Wondermedia Technologies, Inc. diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index ccccc29..1279faf 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -537,4 +537,22 @@ source "drivers/misc/mic/Kconfig" source "drivers/misc/genwqe/Kconfig" source "drivers/misc/echo/Kconfig" source "drivers/misc/cxl/Kconfig" + +menu "GTA04 misc hardware support" + +config W2SG0004 + tristate "W2SG0004 on/off control" + depends on GPIOLIB + help + Enable on/off control of W2SG0004 GPS to allow powering up/down if + the /dev/tty$n is opened/closed. + It also provides a rfkill gps node to control the LNA power. + +config W2SG0004_DEBUG + bool "W2SG0004 on/off debugging" + depends on W2SG0004 + help + Enable driver debugging mode of W2SG0004 GPS. + +endmenu endmenu diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile index 537d7f3..a153a89 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile @@ -54,5 +54,6 @@ obj-$(CONFIG_SRAM) += sram.o obj-y += mic/ obj-$(CONFIG_GENWQE) += genwqe/ obj-$(CONFIG_ECHO) += echo/ +obj-$(CONFIG_W2SG0004) += w2sg0004.o obj-$(CONFIG_VEXPRESS_SYSCFG) += vexpress-syscfg.o obj-$(CONFIG_CXL_BASE) += cxl/ diff --git a/drivers/misc/w2sg0004.c b/drivers/misc/w2sg0004.c new file mode 100644 index 0000000..6aadf44 --- /dev/null +++ b/drivers/misc/w2sg0004.c @@ -0,0 +1,443 @@ +/* + * w2sg0004.c + * Driver for power controlling the w2sg0004/w2sg0084 GPS receiver. + * + * This receiver has an ON/OFF pin which must be toggled to + * turn the device 'on' of 'off'. A high->low->high toggle + * will switch the device on if it is off, and off if it is on. + * + * To enable receiving on/off requests we register with the + * UART power management notifications. + * + * It is not possible to directly detect the state of the device. + * However when it is on it will send characters on a UART line + * regularly. + * + * To detect that the power state is out of sync (e.g. if GPS + * was enabled before a reboot), we register for UART data received + * notifications. + * + * In addition we register as a rfkill client so that we can + * control the LNA power. + * + */ + +#include <linux/module.h> +#include <linux/interrupt.h> +#include <linux/sched.h> +#include <linux/irq.h> +#include <linux/slab.h> +#include <linux/err.h> +#include <linux/delay.h> +#include <linux/of.h> +#include <linux/of_irq.h> +#include <linux/of_gpio.h> +#include <linux/platform_device.h> +#include <linux/w2sg0004.h> +#include <linux/workqueue.h> +#include <linux/rfkill.h> +#include <linux/serial_core.h> + +#ifdef CONFIG_W2SG0004_DEBUG +#undef pr_debug +#define pr_debug printk +#endif + +/* + * There seems to be restrictions on how quickly we can toggle the + * on/off line. data sheets says "two rtc ticks", whatever that means. + * If we do it too soon it doesn't work. + * So we have a state machine which uses the common work queue to ensure + * clean transitions. + * When a change is requested we record that request and only act on it + * once the previous change has completed. + * A change involves a 10ms low pulse, and a 990ms raised level, so only + * one change per second. + */ + +enum w2sg_state { + W2SG_IDLE, /* is not changing state */ + W2SG_PULSE, /* activate on/off impulse */ + W2SG_NOPULSE /* deactivate on/off impulse */ +}; + +struct w2sg_data { + struct rfkill *rf_kill; + struct regulator *lna_regulator; + int lna_blocked; /* rfkill block gps active */ + int lna_is_off; /* LNA is currently off */ + int is_on; /* current state (0/1) */ + unsigned long last_toggle; + unsigned long backoff; /* time to wait since last_toggle */ + int on_off_gpio; /* the on-off gpio number */ + struct uart_port *uart; /* the drvdata of the uart or tty */ + enum w2sg_state state; + int requested; /* requested state (0/1) */ + int suspended; + spinlock_t lock; + struct delayed_work work; +}; + +static int w2sg_data_set_lna_power(struct w2sg_data *data) +{ + int ret = 0; + int off = data->suspended || !data->requested || data->lna_blocked; + + pr_debug("%s: %s\n", __func__, off ? "off" : "on"); + + if (off != data->lna_is_off) { + data->lna_is_off = off; + if (!IS_ERR_OR_NULL(data->lna_regulator)) { + if (off) + regulator_disable(data->lna_regulator); + else + ret = regulator_enable(data->lna_regulator); + } + } + + return ret; +} + +static void w2sg_data_set_power(void *pdata, int val) +{ + struct w2sg_data *data = (struct w2sg_data *) pdata; + unsigned long flags; + + pr_debug("%s to %d (%d)\n", __func__, val, data->requested); + + spin_lock_irqsave(&data->lock, flags); + + if (val && !data->requested) { + data->requested = true; + } else if (!val && data->requested) { + data->backoff = HZ; + data->requested = false; + } else + goto unlock; + + pr_debug("w2sg scheduled for %d\n", data->requested); + + if (!data->suspended) + schedule_delayed_work(&data->work, 0); +unlock: + spin_unlock_irqrestore(&data->lock, flags); +} + +/* called each time data is received by the host (i.e. sent by the w2sg0004) */ + +static int rx_notification(void *pdata, unsigned int *c) +{ + struct w2sg_data *data = (struct w2sg_data *) pdata; + unsigned long flags; + + if (!data->requested && !data->is_on) { + /* we have received a RX signal while GPS should be off */ + pr_debug("%02x!", *c); + + if ((data->state == W2SG_IDLE) && + time_after(jiffies, + data->last_toggle + data->backoff)) { + /* Should be off by now, time to toggle again */ + pr_debug("w2sg has sent data although it should be off!\n"); + data->is_on = true; + data->backoff *= 2; + spin_lock_irqsave(&data->lock, flags); + if (!data->suspended) + schedule_delayed_work(&data->work, 0); + spin_unlock_irqrestore(&data->lock, flags); + } + } + return 0; /* forward to tty */ +} + +/* called by uart modem control line changes (DTR) */ + +static int w2sg_mctrl(void *pdata, int val) +{ + pr_debug("%s(...,%x)\n", __func__, val); + val = (val & TIOCM_DTR) != 0; /* DTR controls power on/off */ + w2sg_data_set_power((struct w2sg_data *) pdata, val); + return 0; +} + +/* try to toggle the power state by sending a pulse to the on-off GPIO */ + +static void toggle_work(struct work_struct *work) +{ + struct w2sg_data *data = container_of(work, struct w2sg_data, + work.work); + + switch (data->state) { + case W2SG_IDLE: + spin_lock_irq(&data->lock); + if (data->requested == data->is_on) { + spin_unlock_irq(&data->lock); + return; + } + spin_unlock_irq(&data->lock); + w2sg_data_set_lna_power(data); /* update LNA power state */ + gpio_set_value_cansleep(data->on_off_gpio, 0); + data->state = W2SG_PULSE; + + pr_debug("w2sg: power gpio ON\n"); + + schedule_delayed_work(&data->work, + msecs_to_jiffies(10)); + break; + + case W2SG_PULSE: + gpio_set_value_cansleep(data->on_off_gpio, 1); + data->last_toggle = jiffies; + data->state = W2SG_NOPULSE; + data->is_on = !data->is_on; + + pr_debug("w2sg: power gpio OFF\n"); + + schedule_delayed_work(&data->work, + msecs_to_jiffies(10)); + break; + + case W2SG_NOPULSE: + data->state = W2SG_IDLE; + pr_debug("w2sg: idle\n"); + + break; + + } +} + +static int w2sg_data_rfkill_set_block(void *pdata, bool blocked) +{ + struct w2sg_data *data = pdata; + + pr_debug("%s: blocked: %d\n", __func__, blocked); + + data->lna_blocked = blocked; + + return w2sg_data_set_lna_power(data); +} + +static struct rfkill_ops w2sg_data0004_rfkill_ops = { + .set_block = w2sg_data_rfkill_set_block, +}; + +static int w2sg_data_probe(struct platform_device *pdev) +{ + struct w2sg_pdata *pdata = dev_get_platdata(&pdev->dev); + struct w2sg_data *data; + struct rfkill *rf_kill; + int err; + + pr_debug("%s()\n", __func__); + + if (pdev->dev.of_node) { + struct device *dev = &pdev->dev; + enum of_gpio_flags flags; + + pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL); + if (!pdata) + return -ENOMEM; + + pdata->on_off_gpio = of_get_named_gpio_flags(dev->of_node, + "on-off-gpio", 0, + &flags); + + if (pdata->on_off_gpio == -EPROBE_DEFER) + return -EPROBE_DEFER; /* defer until we have all gpios */ + + pdata->lna_regulator = devm_regulator_get_optional(dev, "lna"); + + pr_debug("%s() lna_regulator = %p\n", __func__, pdata->lna_regulator); + + pdev->dev.platform_data = pdata; + } + + data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); + if (data == NULL) + return -ENOMEM; + + data->lna_regulator = pdata->lna_regulator; + data->lna_blocked = true; + data->lna_is_off = true; + + data->on_off_gpio = pdata->on_off_gpio; + + data->is_on = false; + data->requested = false; + data->state = W2SG_IDLE; + data->last_toggle = jiffies; + data->backoff = HZ; + +#ifdef CONFIG_OF + data->uart = devm_serial_get_uart_by_phandle(&pdev->dev, "uart", 0); + if (IS_ERR(data->uart)) { + if (PTR_ERR(data->uart) == -EPROBE_DEFER) + return -EPROBE_DEFER; /* we can't probe yet */ + data->uart = NULL; /* no UART */ + } +#endif + + INIT_DELAYED_WORK(&data->work, toggle_work); + spin_lock_init(&data->lock); + + err = devm_gpio_request(&pdev->dev, data->on_off_gpio, "w2sg0004-on-off"); + if (err < 0) + goto out; + + gpio_direction_output(data->on_off_gpio, false); + + if (data->uart) { + struct ktermios termios = { + .c_iflag = IGNBRK, + .c_oflag = 0, + .c_cflag = B9600 | CS8, + .c_lflag = 0, + .c_line = 0, + .c_ispeed = 9600, + .c_ospeed = 9600 + }; + uart_register_slave(data->uart, data); + uart_register_mctrl_notification(data->uart, w2sg_mctrl); + uart_register_rx_notification(data->uart, rx_notification, + &termios); + } + + rf_kill = rfkill_alloc("GPS", &pdev->dev, RFKILL_TYPE_GPS, + &w2sg_data0004_rfkill_ops, data); + if (rf_kill == NULL) { + err = -ENOMEM; + goto err_rfkill; + } + + err = rfkill_register(rf_kill); + if (err) { + dev_err(&pdev->dev, "Cannot register rfkill device\n"); + goto err_rfkill; + } + + data->rf_kill = rf_kill; + + platform_set_drvdata(pdev, data); + + pr_debug("w2sg0004 probed\n"); + +#ifdef CONFIG_W2SG0004_DEBUG + /* turn on for debugging rx notifications */ + pr_debug("w2sg power gpio ON\n"); + gpio_set_value_cansleep(data->on_off_gpio, 1); + mdelay(100); + pr_debug("w2sg power gpio OFF\n"); + gpio_set_value_cansleep(data->on_off_gpio, 0); + mdelay(300); +#endif + + /* if we won't receive mctrl notifications, turn on. + * otherwise keep off until DTR is asserted through mctrl. + */ + + w2sg_data_set_power(data, !data->uart); + + return 0; + +err_rfkill: + rfkill_destroy(rf_kill); +out: + return err; +} + +static int w2sg_data_remove(struct platform_device *pdev) +{ + struct w2sg_data *data = platform_get_drvdata(pdev); + + cancel_delayed_work_sync(&data->work); + + if (data->uart) { + uart_register_rx_notification(data->uart, NULL, NULL); + uart_register_slave(data->uart, NULL); + } + return 0; +} + +static int w2sg_data_suspend(struct device *dev) +{ + struct w2sg_data *data = dev_get_drvdata(dev); + + spin_lock_irq(&data->lock); + data->suspended = true; + spin_unlock_irq(&data->lock); + + cancel_delayed_work_sync(&data->work); + + w2sg_data_set_lna_power(data); /* shuts down if needed */ + + if (data->state == W2SG_PULSE) { + msleep(10); + gpio_set_value_cansleep(data->on_off_gpio, 1); + data->last_toggle = jiffies; + data->is_on = !data->is_on; + data->state = W2SG_NOPULSE; + } + + if (data->state == W2SG_NOPULSE) { + msleep(10); + data->state = W2SG_IDLE; + } + + if (data->is_on) { + pr_info("GPS off for suspend %d %d %d\n", data->requested, + data->is_on, data->lna_is_off); + + gpio_set_value_cansleep(data->on_off_gpio, 0); + msleep(10); + gpio_set_value_cansleep(data->on_off_gpio, 1); + data->is_on = 0; + } + + return 0; +} + +static int w2sg_data_resume(struct device *dev) +{ + struct w2sg_data *data = dev_get_drvdata(dev); + + spin_lock_irq(&data->lock); + data->suspended = false; + spin_unlock_irq(&data->lock); + + pr_info("GPS resuming %d %d %d\n", data->requested, + data->is_on, data->lna_is_off); + + schedule_delayed_work(&data->work, 0); /* enables LNA if needed */ + + return 0; +} + +#if defined(CONFIG_OF) +static const struct of_device_id w2sg0004_of_match[] = { + { .compatible = "wi2wi,w2sg0004" }, + { .compatible = "wi2wi,w2sg0084" }, + {}, +}; +MODULE_DEVICE_TABLE(of, w2sg0004_of_match); +#endif + +SIMPLE_DEV_PM_OPS(w2sg_pm_ops, w2sg_data_suspend, w2sg_data_resume); + +static struct platform_driver w2sg_data_driver = { + .probe = w2sg_data_probe, + .remove = w2sg_data_remove, + .driver = { + .name = "w2sg0004", + .owner = THIS_MODULE, + .pm = &w2sg_pm_ops, + .of_match_table = of_match_ptr(w2sg0004_of_match) + }, +}; + +module_platform_driver(w2sg_data_driver); + +MODULE_ALIAS("w2sg0004"); + +MODULE_AUTHOR("NeilBrown <neilb@suse.de>"); +MODULE_DESCRIPTION("w2sg0004 GPS power management driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c index b731100..c8ae0dd 100644 --- a/drivers/tty/serial/serial_core.c +++ b/drivers/tty/serial/serial_core.c @@ -177,7 +177,7 @@ void uart_register_slave(struct uart_port *uport, void *slave) EXPORT_SYMBOL_GPL(uart_register_slave); void uart_register_mctrl_notification(struct uart_port *uport, - int (*function)(void *slave, int state)) + int (*function)(void *slave, int state)) { uport->mctrl_notification = function; } @@ -208,14 +208,14 @@ void uart_register_rx_notification(struct uart_port *uport, retval = uport->ops->startup(uport); if (retval == 0 && termios) { int hw_stopped; - /* - * Initialise the hardware port settings. - */ +/* + * Initialise the hardware port settings. + */ uport->ops->set_termios(uport, termios, NULL); - /* - * Set modem status enables based on termios cflag - */ +/* + * Set modem status enables based on termios cflag + */ spin_lock_irq(&uport->lock); if (termios->c_cflag & CRTSCTS) uport->status |= UPSTAT_CTS_ENABLE; @@ -227,11 +227,13 @@ void uart_register_rx_notification(struct uart_port *uport, else uport->status |= UPSTAT_DCD_ENABLE; - /* reset sw-assisted CTS flow control based on (possibly) new mode */ +/* + * reset sw-assisted CTS flow control based on (possibly) new mode + */ hw_stopped = uport->hw_stopped; uport->hw_stopped = uart_softcts_mode(uport) && - !(uport->ops->get_mctrl(uport) - & TIOCM_CTS); + !(uport->ops->get_mctrl(uport) & + TIOCM_CTS); if (uport->hw_stopped) { if (!hw_stopped) uport->ops->stop_tx(uport); @@ -432,7 +434,8 @@ static void uart_shutdown(struct tty_struct *tty, struct uart_state *state) uart_clear_mctrl(uport, TIOCM_DTR | TIOCM_RTS); /* * if we have a slave that has registered for rx_notifications - * we do not shut down the uart port to be able to monitor the device + * we do not shut down the uart port to be able to monitor the + * device */ if (!uport->rx_notification) uart_port_shutdown(port); diff --git a/include/linux/w2sg0004.h b/include/linux/w2sg0004.h new file mode 100644 index 0000000..ad0c4a1 --- /dev/null +++ b/include/linux/w2sg0004.h @@ -0,0 +1,27 @@ +/* + * UART slave to allow ON/OFF control of w2sg0004 GPS receiver. + * + * Copyright (C) 2011 Neil Brown <neil@brown.name> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + + + +#ifndef __LINUX_W2SG0004_H +#define __LINUX_W2SG0004_H + +#include <linux/regulator/consumer.h> + +struct w2sg_pdata { + struct regulator *lna_regulator; /* enable LNA power */ + int on_off_gpio; /* on-off input of the GPS module */ +}; +#endif /* __LINUX_W2SG0004_H */
Add driver for Wi2Wi W2SG0004/84 GPS module connected through uart. Use uart slave + notification hooks to glue with tty and turn on/off the module. Detect if the module is turned on (sends data) but should be off, e.g. if already turned on during boot. Additionally, rfkill block/unblock can be used to control an external LNA (and power down the module if not needed). The driver concept is based on code developed by NeilBrown <neilb@suse.de> but simplified and adapted to use the serial slave API. Signed-off-by: H. Nikolaus Schaller <hns@goldelico.com> --- .../devicetree/bindings/misc/wi2wi,w2sg0004.txt | 18 + .../devicetree/bindings/vendor-prefixes.txt | 1 + drivers/misc/Kconfig | 18 + drivers/misc/Makefile | 1 + drivers/misc/w2sg0004.c | 443 +++++++++++++++++++++ drivers/tty/serial/serial_core.c | 25 +- include/linux/w2sg0004.h | 27 ++ 7 files changed, 522 insertions(+), 11 deletions(-) create mode 100644 Documentation/devicetree/bindings/misc/wi2wi,w2sg0004.txt create mode 100644 drivers/misc/w2sg0004.c create mode 100644 include/linux/w2sg0004.h