[PATCH v2] usb: serial: option: Fix the lackage for Quectel EG95 LTE modem support

2020-09-02 Thread William Sung
* Add reset-resume callback for resetting USB devices after MCU exits
from suspend/sleep mode.

* Add zero packet support

Fixes: da6902e5b6db ("USB: serial: option: add Quectel EG95 LTE modem"

Signed-off-by: William Sung 
---

 v2: * Add ZLP support
 * Remove redundant code for reserving interface number 4

 drivers/usb/serial/option.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c
index 89b3192af326..ba4dc67dab2f 100644
--- a/drivers/usb/serial/option.c
+++ b/drivers/usb/serial/option.c
@@ -1099,7 +1099,7 @@ static const struct usb_device_id option_ids[] = {
{ USB_DEVICE(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_EC25),
  .driver_info = RSVD(4) },
{ USB_DEVICE(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_EG95),
- .driver_info = RSVD(4) },
+ .driver_info = RSVD(4) | ZLP },
{ USB_DEVICE(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_BG96),
  .driver_info = RSVD(4) },
{ USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, 
QUECTEL_PRODUCT_EP06, 0xff, 0xff, 0xff),
@@ -2069,6 +2069,7 @@ static struct usb_serial_driver option_1port_device = {
 #ifdef CONFIG_PM
.suspend   = usb_wwan_suspend,
.resume= usb_wwan_resume,
+   .reset_resume  = usb_wwan_resume,
 #endif
 };
 
-- 
2.17.1



[PATCH 1/2] usb: serial: option: Fix the lackage for Quectel EG95 LTE modem support

2020-08-31 Thread William Sung
* Add reset-resume callback for resetting USB devices after MCU exits
from suspend/sleep mode.

* Because Quectel EG95 uses USB interface 4 as a USB network device, so
return from option_startup() to prevent being used as a USB serial
device.

Fixes: da6902e5b6db ("USB: serial: option: add Quectel EG95 LTE modem"

Signed-off-by: William Sung 
---
 drivers/usb/serial/option.c | 6 ++
 1 file changed, 6 insertions(+)

diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c
index 89b3192af326..9de8aec47e5e 100644
--- a/drivers/usb/serial/option.c
+++ b/drivers/usb/serial/option.c
@@ -2069,6 +2069,7 @@ static struct usb_serial_driver option_1port_device = {
 #ifdef CONFIG_PM
.suspend   = usb_wwan_suspend,
.resume= usb_wwan_resume,
+   .reset_resume  = usb_wwan_resume,
 #endif
 };
 
@@ -2104,6 +2105,11 @@ static int option_probe(struct usb_serial *serial,
if (device_flags & NUMEP2 && iface_desc->bNumEndpoints != 2)
return -ENODEV;
 
+   /* Quectel EC25 & EC21 & EG91 & EG95 ... interface 4 can be used as USB 
network device */
+   if (serial->dev->descriptor.idVendor == cpu_to_le16(0x2c7c) &&
+   serial->interface->cur_altsetting->desc.bInterfaceNumber >= 4)
+   return -ENODEV;
+
/* Store the device flags so we can use them during attach. */
usb_set_serial_data(serial, (void *)device_flags);
 
-- 
2.17.1



[PATCH 2/2] usb: serial: usb_wwan: Fix lackage for Quectel EG95 LTE modem support

2020-08-31 Thread William Sung
As required by the USB protocol, add zero packet support for Quectel
EG95 LTE modem module.

Fixes: f815dd5cf48b ("net: usb: qmi_wwan: add support for Quectel EG95
LTE modem"

Signed-off-by: William Sung 
---
 drivers/usb/serial/usb_wwan.c | 12 ++--
 1 file changed, 10 insertions(+), 2 deletions(-)

diff --git a/drivers/usb/serial/usb_wwan.c b/drivers/usb/serial/usb_wwan.c
index 4b9845807bee..75495c939ac6 100644
--- a/drivers/usb/serial/usb_wwan.c
+++ b/drivers/usb/serial/usb_wwan.c
@@ -467,6 +467,7 @@ static struct urb *usb_wwan_setup_urb(struct 
usb_serial_port *port,
struct usb_serial *serial = port->serial;
struct usb_wwan_intf_private *intfdata = usb_get_serial_data(serial);
struct urb *urb;
+   struct usb_device_descriptor *desc = >dev->descriptor;
 
urb = usb_alloc_urb(0, GFP_KERNEL); /* No ISO */
if (!urb)
@@ -476,8 +477,15 @@ static struct urb *usb_wwan_setup_urb(struct 
usb_serial_port *port,
  usb_sndbulkpipe(serial->dev, endpoint) | dir,
  buf, len, callback, ctx);
 
-   if (intfdata->use_zlp && dir == USB_DIR_OUT)
-   urb->transfer_flags |= URB_ZERO_PACKET;
+   if (dir == USB_DIR_OUT) {
+   if (intfdata->use_zlp)
+   urb->transfer_flags |= URB_ZERO_PACKET;
+
+   /* Add zero packet support for Quectel EG95 */
+   if (desc->idVendor == cpu_to_le16(0x2C7C) &&
+   desc->idProduct == cpu_to_le16(0x0195))
+   urb->transfer_flags |= URB_ZERO_PACKET;
+   }
 
return urb;
 }
-- 
2.17.1



[PATCH v2] iio: dac: ad5593r: Dynamically set AD5593R channel modes

2020-08-25 Thread William Sung
To use ad5593r more flexibly, we use the module parameter to setting the
channel modes dynamically whenever the module probe up. Users can pass
the channel modes to the module parameter for allocating the
functionality of channels as desired.

For example:
* Use in the kernel command line:
Users can add the module parameter in the kernel command line such as

"ad5593r.ch_mode=88001122"

"88001122" means the channel mode setting for each channel. The most
left side indicates the mode of channel 7, and the most right side
indicates the mode of channel 0.

* Use when manually probe the module:
Similar to the kernel command line usage, users can enter

"modprobe ad5593r ch_mode=88001122"

to start the ad5593r module with the desired channel mode setting.

v2: Fix the patch description and remove redundant for loop

Signed-off-by: William Sung 
---
 drivers/iio/dac/ad5592r-base.c | 33 ---
 drivers/iio/dac/ad5592r-base.h |  4 +++
 drivers/iio/dac/ad5593r.c  | 48 ++
 3 files changed, 82 insertions(+), 3 deletions(-)

diff --git a/drivers/iio/dac/ad5592r-base.c b/drivers/iio/dac/ad5592r-base.c
index cc4875660a69..cd69a34fa21e 100644
--- a/drivers/iio/dac/ad5592r-base.c
+++ b/drivers/iio/dac/ad5592r-base.c
@@ -21,6 +21,10 @@
 
 #include "ad5592r-base.h"
 
+/* Parameters for dynamic channel mode setting */
+static u8 update_channel_mode;
+static u8 new_channel_modes[AD559XR_CHANNEL_NR];
+
 static int ad5592r_gpio_get(struct gpio_chip *chip, unsigned offset)
 {
struct ad5592r_state *st = gpiochip_get_data(chip);
@@ -132,7 +136,7 @@ static int ad5592r_gpio_init(struct ad5592r_state *st)
 
st->gpiochip.label = dev_name(st->dev);
st->gpiochip.base = -1;
-   st->gpiochip.ngpio = 8;
+   st->gpiochip.ngpio = AD559XR_CHANNEL_NR;
st->gpiochip.parent = st->dev;
st->gpiochip.can_sleep = true;
st->gpiochip.direction_input = ad5592r_gpio_direction_input;
@@ -287,6 +291,14 @@ static int ad5592r_set_channel_modes(struct ad5592r_state 
*st)
return ret;
 }
 
+static void ad5592r_set_def_channel_modes(struct ad5592r_state *st)
+{
+   int i;
+
+   for (i = 0; i < ARRAY_SIZE(st->channel_modes); i++)
+   st->channel_modes[i] = new_channel_modes[i];
+}
+
 static int ad5592r_reset_channel_modes(struct ad5592r_state *st)
 {
int i;
@@ -532,6 +544,10 @@ static int ad5592r_alloc_channels(struct iio_dev *iio_dev)
st->channel_offstate[reg] = tmp;
}
 
+   /* Update default channel modes set by external module */
+   if (update_channel_mode == 1)
+   ad5592r_set_def_channel_modes(st);
+
channels = devm_kcalloc(st->dev,
1 + 2 * num_channels, sizeof(*channels),
GFP_KERNEL);
@@ -567,7 +583,7 @@ static int ad5592r_alloc_channels(struct iio_dev *iio_dev)
}
 
channels[curr_channel].type = IIO_TEMP;
-   channels[curr_channel].channel = 8;
+   channels[curr_channel].channel = AD559XR_CHANNEL_NR;
channels[curr_channel].info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
   BIT(IIO_CHAN_INFO_SCALE) |
   BIT(IIO_CHAN_INFO_OFFSET);
@@ -589,6 +605,17 @@ static void ad5592r_init_scales(struct ad5592r_state *st, 
int vref_mV)
div_s64_rem(tmp * 2, 10LL, >scale_avail[1][1]);
 }
 
+void ad5592r_update_default_channel_modes(u8 *new_modes)
+{
+   int idx = 0;
+
+   update_channel_mode = 1;
+   for (idx = 0; idx < AD559XR_CHANNEL_NR; idx++)
+   new_channel_modes[idx] = new_modes[idx];
+
+}
+EXPORT_SYMBOL_GPL(ad5592r_update_default_channel_modes);
+
 int ad5592r_probe(struct device *dev, const char *name,
const struct ad5592r_rw_ops *ops)
 {
@@ -603,7 +630,7 @@ int ad5592r_probe(struct device *dev, const char *name,
st = iio_priv(iio_dev);
st->dev = dev;
st->ops = ops;
-   st->num_channels = 8;
+   st->num_channels = AD559XR_CHANNEL_NR;
dev_set_drvdata(dev, iio_dev);
 
st->reg = devm_regulator_get_optional(dev, "vref");
diff --git a/drivers/iio/dac/ad5592r-base.h b/drivers/iio/dac/ad5592r-base.h
index 23dac2f1ff8a..40ad6369e660 100644
--- a/drivers/iio/dac/ad5592r-base.h
+++ b/drivers/iio/dac/ad5592r-base.h
@@ -39,6 +39,9 @@ enum ad5592r_registers {
 #define AD5592R_REG_CTRL_ADC_RANGE BIT(5)
 #define AD5592R_REG_CTRL_DAC_RANGE BIT(4)
 
+/* Define quantity of channels of AD5592R/AD5593R */
+#define AD559XR_CHANNEL_NR 8
+
 struct ad5592r_rw_ops {
int (*write_dac)(struct ad5592r_state *st, unsigned chan, u16 value);
int (*read_adc)(struct ad5592r_state *st, unsigned chan, u16 *value);
@@ -69,6 +72,7 @@ struct ad5592r_state {
__be16 spi_msg_nop;
 };
 
+

[PATCH] iio: dac: ad5593r: Dynamically set AD5593R channel modes

2020-08-23 Thread William Sung
To use ad5593r more flexibly, we use the module parameter to setting the
channel modes dynamically whenever the module probe up. Users can pass
the channel modes to the module parameter for allocating the
functionality of channels as desired.

For example:
* Use in the kernel command line:
Users can add the module parameter in the kernel command line such as

"ad5593r.ch_mode_cmdline=88001122"

"88001122" means the channel mode setting for each channel. The most
left side indicates the mode of channel 7, and the most right side
indicates the mode of channel 0.

* Use when manually probe the module:
Similar to the kernel command line usage, users can enter

"modprobe ad5593r ch_mode_probe=88001122"

to start the ad5593r module with the desired channel mode setting.

Signed-off-by: William Sung 
---
 drivers/iio/dac/ad5592r-base.c | 33 ++--
 drivers/iio/dac/ad5592r-base.h |  4 +++
 drivers/iio/dac/ad5593r.c  | 55 ++
 3 files changed, 89 insertions(+), 3 deletions(-)

diff --git a/drivers/iio/dac/ad5592r-base.c b/drivers/iio/dac/ad5592r-base.c
index cc4875660a69..cd69a34fa21e 100644
--- a/drivers/iio/dac/ad5592r-base.c
+++ b/drivers/iio/dac/ad5592r-base.c
@@ -21,6 +21,10 @@
 
 #include "ad5592r-base.h"
 
+/* Parameters for dynamic channel mode setting */
+static u8 update_channel_mode;
+static u8 new_channel_modes[AD559XR_CHANNEL_NR];
+
 static int ad5592r_gpio_get(struct gpio_chip *chip, unsigned offset)
 {
struct ad5592r_state *st = gpiochip_get_data(chip);
@@ -132,7 +136,7 @@ static int ad5592r_gpio_init(struct ad5592r_state *st)
 
st->gpiochip.label = dev_name(st->dev);
st->gpiochip.base = -1;
-   st->gpiochip.ngpio = 8;
+   st->gpiochip.ngpio = AD559XR_CHANNEL_NR;
st->gpiochip.parent = st->dev;
st->gpiochip.can_sleep = true;
st->gpiochip.direction_input = ad5592r_gpio_direction_input;
@@ -287,6 +291,14 @@ static int ad5592r_set_channel_modes(struct ad5592r_state 
*st)
return ret;
 }
 
+static void ad5592r_set_def_channel_modes(struct ad5592r_state *st)
+{
+   int i;
+
+   for (i = 0; i < ARRAY_SIZE(st->channel_modes); i++)
+   st->channel_modes[i] = new_channel_modes[i];
+}
+
 static int ad5592r_reset_channel_modes(struct ad5592r_state *st)
 {
int i;
@@ -532,6 +544,10 @@ static int ad5592r_alloc_channels(struct iio_dev *iio_dev)
st->channel_offstate[reg] = tmp;
}
 
+   /* Update default channel modes set by external module */
+   if (update_channel_mode == 1)
+   ad5592r_set_def_channel_modes(st);
+
channels = devm_kcalloc(st->dev,
1 + 2 * num_channels, sizeof(*channels),
GFP_KERNEL);
@@ -567,7 +583,7 @@ static int ad5592r_alloc_channels(struct iio_dev *iio_dev)
}
 
channels[curr_channel].type = IIO_TEMP;
-   channels[curr_channel].channel = 8;
+   channels[curr_channel].channel = AD559XR_CHANNEL_NR;
channels[curr_channel].info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
   BIT(IIO_CHAN_INFO_SCALE) |
   BIT(IIO_CHAN_INFO_OFFSET);
@@ -589,6 +605,17 @@ static void ad5592r_init_scales(struct ad5592r_state *st, 
int vref_mV)
div_s64_rem(tmp * 2, 10LL, >scale_avail[1][1]);
 }
 
+void ad5592r_update_default_channel_modes(u8 *new_modes)
+{
+   int idx = 0;
+
+   update_channel_mode = 1;
+   for (idx = 0; idx < AD559XR_CHANNEL_NR; idx++)
+   new_channel_modes[idx] = new_modes[idx];
+
+}
+EXPORT_SYMBOL_GPL(ad5592r_update_default_channel_modes);
+
 int ad5592r_probe(struct device *dev, const char *name,
const struct ad5592r_rw_ops *ops)
 {
@@ -603,7 +630,7 @@ int ad5592r_probe(struct device *dev, const char *name,
st = iio_priv(iio_dev);
st->dev = dev;
st->ops = ops;
-   st->num_channels = 8;
+   st->num_channels = AD559XR_CHANNEL_NR;
dev_set_drvdata(dev, iio_dev);
 
st->reg = devm_regulator_get_optional(dev, "vref");
diff --git a/drivers/iio/dac/ad5592r-base.h b/drivers/iio/dac/ad5592r-base.h
index 23dac2f1ff8a..40ad6369e660 100644
--- a/drivers/iio/dac/ad5592r-base.h
+++ b/drivers/iio/dac/ad5592r-base.h
@@ -39,6 +39,9 @@ enum ad5592r_registers {
 #define AD5592R_REG_CTRL_ADC_RANGE BIT(5)
 #define AD5592R_REG_CTRL_DAC_RANGE BIT(4)
 
+/* Define quantity of channels of AD5592R/AD5593R */
+#define AD559XR_CHANNEL_NR 8
+
 struct ad5592r_rw_ops {
int (*write_dac)(struct ad5592r_state *st, unsigned chan, u16 value);
int (*read_adc)(struct ad5592r_state *st, unsigned chan, u16 *value);
@@ -69,6 +72,7 @@ struct ad5592r_state {
__be16 spi_msg_nop;
 };
 
+void ad5592r_update_default_channel_modes(u8 *new_modes);