[RFC 3/3] misc: Add w2cbw003 (wifi/bluetooth) power control driver
Add driver for Wi2Wi W2CBW003 WiFi and Bluetooth module where the Bluetooth interface is connected through uart. Uses the new serdev API to glue with tty and turn on/off the module if the tty port (/dev/ttyBTn) is opened. Note that this is only for the Bluetooth side. The WLAN (libertas) sdio driver should be abe to enable the same vdd regulator. So that the device is powered on if either WiFi or Bluetooth is activated (or both) and powered down if neither is in use. Signed-off-by: H. Nikolaus Schaller--- drivers/misc/Kconfig | 7 + drivers/misc/Makefile | 1 + drivers/misc/w2cbw003-bluetooth.c | 390 ++ 3 files changed, 398 insertions(+) create mode 100644 drivers/misc/w2cbw003-bluetooth.c diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index 7f97ef8fb6cd..90ce23bef77b 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -525,4 +525,11 @@ config W2SG0004_DEBUG help Enable driver debugging mode of W2SG0004 GPS. +config W2CBW003 + tristate "W2CBW003 power on/off control" + depends on GPIOLIB + help + Enable on/off power control of W2CBW003 if the /dev/tty$n for + Bluetooth is opened. + endmenu diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile index 0e88e06e5ee0..f6d3f096c5e0 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile @@ -51,6 +51,7 @@ obj-y += mic/ obj-$(CONFIG_GENWQE) += genwqe/ obj-$(CONFIG_ECHO) += echo/ obj-$(CONFIG_W2SG0004) += w2sg0004.o +obj-$(CONFIG_W2CBW003) += w2cbw003-bluetooth.o obj-$(CONFIG_VEXPRESS_SYSCFG) += vexpress-syscfg.o obj-$(CONFIG_CXL_BASE) += cxl/ obj-$(CONFIG_ASPEED_LPC_CTRL) += aspeed-lpc-ctrl.o diff --git a/drivers/misc/w2cbw003-bluetooth.c b/drivers/misc/w2cbw003-bluetooth.c new file mode 100644 index ..3bcfd51bbf09 --- /dev/null +++ b/drivers/misc/w2cbw003-bluetooth.c @@ -0,0 +1,390 @@ +/* + * w2scbw003.c + * Driver for power controlling the w2cbw003 WiFi/Bluetooth chip. + * + * powers on the chip if the tty port associated/connected + * to the bluetooth subsystem is opened (e.g. hciattach /dev/ttyBT0) + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#undef pr_debug +#define pr_debug printk + +struct w2cbw_data { + struct regulator *vdd_regulator; + struct serdev_device *uart;/* the uart connected to the chip */ + struct tty_driver *tty_drv;/* this is the user space tty */ + struct device *dev;/* returned by tty_port_register_device() */ + struct tty_port port; + int open_count; /* how often we were opened */ +}; + +static struct w2cbw_data *w2cbw_by_minor[1]; + +static void w2cbw_set_power(void *pdata, int val) +{ + struct w2cbw_data *data = (struct w2cbw_data *) pdata; + + pr_debug("%s(...,%x)\n", __func__, val); + + if (val != 0) + WARN_ON(regulator_enable(data->vdd_regulator)); + else + regulator_disable(data->vdd_regulator); +} + +/* called each time data is received by the UART (i.e. sent by the w2cbw003) */ + +static int w2cbw_uart_receive_buf(struct serdev_device *serdev, const unsigned char *rxdata, + size_t count) +{ + struct w2cbw_data *data = (struct w2cbw_data *) serdev_device_get_drvdata(serdev); + +// pr_debug("%s() characters\n", __func__, count); + + if (data->open_count > 0) { + int n; + + pr_debug("w2cbw003: uart->tty %d chars\n", count); + n = tty_insert_flip_string(>port, rxdata, count); /* pass to user-space */ + if (n != count) + pr_debug("w2cbw003: did loose %d characters\n", count - n); + tty_flip_buffer_push(>port); + return n; + } + + /* nobody is listenig - ignore incoming data */ + return count; +} + +static struct serdev_device_ops serdev_ops = { + .receive_buf = w2cbw_uart_receive_buf, +#if 0 + .write_wakeup = w2cbw_uart_wakeup, +#endif +}; + +static struct w2cbw_data *w2cbw_get_by_minor(unsigned int minor) +{ + return w2cbw_by_minor[minor]; +} + +static int w2cbw_tty_install(struct tty_driver *driver, struct tty_struct *tty) +{ + struct w2cbw_data *data; + int retval; + + pr_debug("%s() tty = %p\n", __func__, tty); + + data = w2cbw_get_by_minor(tty->index); + pr_debug("%s() data = %p\n", __func__, data); + + if (!data) + return -ENODEV; + + retval = tty_standard_install(driver, tty); + if (retval) + goto error_init_termios; + + tty->driver_data = data; + + return 0; + +error_init_termios: + tty_port_put(>port); + return retval; +} + +static int w2cbw_tty_open(struct
[RFC 3/3] misc: Add w2cbw003 (wifi/bluetooth) power control driver
Add driver for Wi2Wi W2CBW003 WiFi and Bluetooth module where the Bluetooth interface is connected through uart. Uses the new serdev API to glue with tty and turn on/off the module if the tty port (/dev/ttyBTn) is opened. Note that this is only for the Bluetooth side. The WLAN (libertas) sdio driver should be abe to enable the same vdd regulator. So that the device is powered on if either WiFi or Bluetooth is activated (or both) and powered down if neither is in use. Signed-off-by: H. Nikolaus Schaller --- drivers/misc/Kconfig | 7 + drivers/misc/Makefile | 1 + drivers/misc/w2cbw003-bluetooth.c | 390 ++ 3 files changed, 398 insertions(+) create mode 100644 drivers/misc/w2cbw003-bluetooth.c diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index 7f97ef8fb6cd..90ce23bef77b 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -525,4 +525,11 @@ config W2SG0004_DEBUG help Enable driver debugging mode of W2SG0004 GPS. +config W2CBW003 + tristate "W2CBW003 power on/off control" + depends on GPIOLIB + help + Enable on/off power control of W2CBW003 if the /dev/tty$n for + Bluetooth is opened. + endmenu diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile index 0e88e06e5ee0..f6d3f096c5e0 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile @@ -51,6 +51,7 @@ obj-y += mic/ obj-$(CONFIG_GENWQE) += genwqe/ obj-$(CONFIG_ECHO) += echo/ obj-$(CONFIG_W2SG0004) += w2sg0004.o +obj-$(CONFIG_W2CBW003) += w2cbw003-bluetooth.o obj-$(CONFIG_VEXPRESS_SYSCFG) += vexpress-syscfg.o obj-$(CONFIG_CXL_BASE) += cxl/ obj-$(CONFIG_ASPEED_LPC_CTRL) += aspeed-lpc-ctrl.o diff --git a/drivers/misc/w2cbw003-bluetooth.c b/drivers/misc/w2cbw003-bluetooth.c new file mode 100644 index ..3bcfd51bbf09 --- /dev/null +++ b/drivers/misc/w2cbw003-bluetooth.c @@ -0,0 +1,390 @@ +/* + * w2scbw003.c + * Driver for power controlling the w2cbw003 WiFi/Bluetooth chip. + * + * powers on the chip if the tty port associated/connected + * to the bluetooth subsystem is opened (e.g. hciattach /dev/ttyBT0) + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#undef pr_debug +#define pr_debug printk + +struct w2cbw_data { + struct regulator *vdd_regulator; + struct serdev_device *uart;/* the uart connected to the chip */ + struct tty_driver *tty_drv;/* this is the user space tty */ + struct device *dev;/* returned by tty_port_register_device() */ + struct tty_port port; + int open_count; /* how often we were opened */ +}; + +static struct w2cbw_data *w2cbw_by_minor[1]; + +static void w2cbw_set_power(void *pdata, int val) +{ + struct w2cbw_data *data = (struct w2cbw_data *) pdata; + + pr_debug("%s(...,%x)\n", __func__, val); + + if (val != 0) + WARN_ON(regulator_enable(data->vdd_regulator)); + else + regulator_disable(data->vdd_regulator); +} + +/* called each time data is received by the UART (i.e. sent by the w2cbw003) */ + +static int w2cbw_uart_receive_buf(struct serdev_device *serdev, const unsigned char *rxdata, + size_t count) +{ + struct w2cbw_data *data = (struct w2cbw_data *) serdev_device_get_drvdata(serdev); + +// pr_debug("%s() characters\n", __func__, count); + + if (data->open_count > 0) { + int n; + + pr_debug("w2cbw003: uart->tty %d chars\n", count); + n = tty_insert_flip_string(>port, rxdata, count); /* pass to user-space */ + if (n != count) + pr_debug("w2cbw003: did loose %d characters\n", count - n); + tty_flip_buffer_push(>port); + return n; + } + + /* nobody is listenig - ignore incoming data */ + return count; +} + +static struct serdev_device_ops serdev_ops = { + .receive_buf = w2cbw_uart_receive_buf, +#if 0 + .write_wakeup = w2cbw_uart_wakeup, +#endif +}; + +static struct w2cbw_data *w2cbw_get_by_minor(unsigned int minor) +{ + return w2cbw_by_minor[minor]; +} + +static int w2cbw_tty_install(struct tty_driver *driver, struct tty_struct *tty) +{ + struct w2cbw_data *data; + int retval; + + pr_debug("%s() tty = %p\n", __func__, tty); + + data = w2cbw_get_by_minor(tty->index); + pr_debug("%s() data = %p\n", __func__, data); + + if (!data) + return -ENODEV; + + retval = tty_standard_install(driver, tty); + if (retval) + goto error_init_termios; + + tty->driver_data = data; + + return 0; + +error_init_termios: + tty_port_put(>port); + return retval; +} + +static int w2cbw_tty_open(struct tty_struct *tty, struct