andy git 06/15 suspend/resume observations
Sean McNeil
sean at mcneil.com
Thu Jun 19 23:39:32 CEST 2008
Please excuse my ignorance (once again), but I'm unclear about the
manipulation required for GSM in the serial driver. Is this because of
multiplexing? Isn't the GSM simply a uart with hw flow control and RTS
causing an interrupt when in suspend? I noticed several places that test
for GSM_PORT and thought this might be able to be generic across all of
the ports.
Mike (mwester) wrote:
> This patch that cleans up the GSM suspend/resume handling code.
>
> Specifically, this patch removes:
> - the delayed GSM flow-control release code (work queue)
> - the code that enabled/disabled suspended flow-control via user-space
> manipulation of the RTS modem control signal via the tty driver
> - the GTA01-specific code to read the first data after resume by polling
>
> The patch adds some paranoid tests to ensure that none of the remaining
> GSM suspend/resume handling code dereferences pointers without testing
> for NULL.
>
> This leaves the existing sysfs mechanism to forcibly enable/disable RTS
> in place as the preferred means for user-space to manage putting the GSM
> "to bed" during suspend, with the fall-back of having the neo1973_pm_gsm
> driver forcibly enable and disable RTS in the event that user-space has
> failed to do so.
>
> No special code exists for the GTA01 any longer as this special code
> merely made overruns less likely to occur, but did not actually fix
> the problem.
>
> Signed-off-by: Mike Westerhof (mwester at dls.net)
>
> arch/arm/plat-s3c24xx/neo1973_pm_gsm.c | 52 +-------
> drivers/serial/s3c2410.c | 204 +++-----------------------------
> 2 files changed, 24 insertions(+), 232 deletions(-)
>
> diff --git a/arch/arm/plat-s3c24xx/neo1973_pm_gsm.c b/arch/arm/plat-s3c24xx/neo1973_pm_gsm.c
> index 3d086bc..7443d48 100644
> --- a/arch/arm/plat-s3c24xx/neo1973_pm_gsm.c
> +++ b/arch/arm/plat-s3c24xx/neo1973_pm_gsm.c
> @@ -31,19 +31,10 @@
> #endif
>
> #include <linux/neospy.h>
> -#include <linux/workqueue.h>
> -#include <linux/delay.h>
> -
> -extern void s3c24xx_fake_rx_interrupt(int l);
> -
> -static struct work_struct gsmwork;
>
> /* flag set if we flow-controlled the GSM in our suspend routine */
> int gsm_auto_flowcontrolled;
>
> -/* msecs to delay the auto-unlock after resume to minimize overruns */
> -int gsm_autounlock_delay = 750;
> -
> struct neospy_logdata neospy;
> EXPORT_SYMBOL(neospy);
> int nspy_showctrl;
> @@ -103,8 +94,6 @@ static ssize_t gsm_read(struct device *dev, struct device_attribute *attr,
> }
> } else if (!strcmp(attr->attr.name, "gsm_interrupts")) {
> return snprintf(buf, PAGE_SIZE, "%d\n", gta_gsm_interrupts);
> - } else if (!strcmp(attr->attr.name, "autounlock_delay")) {
> - return snprintf(buf, PAGE_SIZE, "%d\n", gsm_autounlock_delay);
> } else if (!strcmp(attr->attr.name, "flowcontrolled")) {
> if (s3c2410_gpio_getcfg(S3C2410_GPH1) == S3C2410_GPIO_OUTPUT)
> goto out_1;
> @@ -361,13 +350,6 @@ static ssize_t gsm_write(struct device *dev, struct device_attribute *attr,
> }
> } else if (!strcmp(attr->attr.name, "gsm_interrupts")) {
> gta_gsm_interrupts = 0;
> - } else if (!strcmp(attr->attr.name, "autounlock_delay")) {
> - sscanf(buf, "%d", &gsm_autounlock_delay);
> - /* constrain to within reasonable bounds */
> - if ((gsm_autounlock_delay > 0) && (gsm_autounlock_delay < 250))
> - gsm_autounlock_delay = 250;
> - else if (gsm_autounlock_delay > 5000)
> - gsm_autounlock_delay = 5000;
> } else if (!strcmp(attr->attr.name, "flowcontrolled")) {
> if (on) {
> gta_gsm_interrupts = 0;
> @@ -405,7 +387,6 @@ static DEVICE_ATTR(power_on, 0644, gsm_read, gsm_write);
> static DEVICE_ATTR(reset, 0644, gsm_read, gsm_write);
> static DEVICE_ATTR(download, 0644, gsm_read, gsm_write);
> static DEVICE_ATTR(gsm_interrupts, 0644, gsm_read, gsm_write);
> -static DEVICE_ATTR(autounlock_delay, 0644, gsm_read, gsm_write);
> static DEVICE_ATTR(flowcontrolled, 0644, gsm_read, gsm_write);
> static DEVICE_ATTR(nspy_enable, 0644, gsm_read, gsm_write);
> static DEVICE_ATTR(nspy_buffer, 0644, gsm_read, gsm_write);
> @@ -414,23 +395,6 @@ static DEVICE_ATTR(nspy_linewrap, 0644, gsm_read, gsm_write);
> static DEVICE_ATTR(nspy_jifdelta, 0644, gsm_read, gsm_write);
>
> #ifdef CONFIG_PM
> -static void gsm_resume_work(struct work_struct *w)
> -{
> - printk(KERN_INFO "%s: waiting...\n", __FUNCTION__);
> - nspy_add(NSPY_TYPE_RESUME, 'W', jiffies);
> - if (gsm_autounlock_delay)
> - msleep(gsm_autounlock_delay);
> - if (gsm_auto_flowcontrolled) {
> - nspy_add(NSPY_TYPE_SPECIAL, '+', jiffies);
> - if (machine_is_neo1973_gta01())
> - s3c24xx_fake_rx_interrupt(10000);
> - s3c2410_gpio_cfgpin(S3C2410_GPH1, S3C2410_GPH1_nRTS0);
> - gsm_auto_flowcontrolled = 0;
> - }
> - nspy_add(NSPY_TYPE_RESUME, 'Z', jiffies);
> - printk(KERN_INFO "%s: done.\n", __FUNCTION__);
> -}
> -
> static int gta01_gsm_suspend(struct platform_device *pdev, pm_message_t state)
> {
> /* GPIO state is saved/restored by S3C2410 core GPIO driver, so we
> @@ -499,12 +463,14 @@ static int gta01_gsm_resume(struct platform_device *pdev)
> if (machine_is_neo1973_gta02())
> s3c2410_gpio_setpin(GTA02_GPIO_nDL_GSM, gta01_gsm.gpio_ndl_gsm);
>
> - /* We must defer the auto flowcontrol because we resume before
> - * the serial driver */
> - if (!schedule_work(&gsmwork))
> - dev_err(&pdev->dev,
> - "Unable to schedule GSM wakeup work\n");
> + /* If we forced flow-control on the GSM, we should also release it */
> + if (gsm_auto_flowcontrolled) {
> + nspy_add(NSPY_TYPE_SPECIAL, '+', jiffies);
> + s3c2410_gpio_cfgpin(S3C2410_GPH1, S3C2410_GPH1_nRTS0);
> + gsm_auto_flowcontrolled = 0;
> + }
>
> + nspy_add(NSPY_TYPE_RESUME, 'Z', jiffies);
> return 0;
> }
> #else
> @@ -518,7 +484,6 @@ static struct attribute *gta01_gsm_sysfs_entries[] = {
> &dev_attr_reset.attr,
> &dev_attr_download.attr,
> &dev_attr_gsm_interrupts.attr,
> - &dev_attr_autounlock_delay.attr,
> &dev_attr_flowcontrolled.attr,
> &dev_attr_nspy_enable.attr,
> &dev_attr_nspy_buffer.attr,
> @@ -612,9 +577,6 @@ static struct platform_driver gta01_gsm_driver = {
> static int __devinit gta01_gsm_init(void)
> {
> nspy_init();
> - INIT_WORK(&gsmwork, gsm_resume_work);
> - if (machine_is_neo1973_gta02())
> - gsm_autounlock_delay = 0;
> return platform_driver_register(>a01_gsm_driver);
> }
>
> diff --git a/drivers/serial/s3c2410.c b/drivers/serial/s3c2410.c
> index 3315fca..4fe1325 100644
> --- a/drivers/serial/s3c2410.c
> +++ b/drivers/serial/s3c2410.c
> @@ -83,6 +83,7 @@
>
> #include <linux/neospy.h>
> extern struct neospy_logdata neospy;
> +#define GSM_PORT 0
>
> extern int gta_gsm_interrupts;
>
> @@ -372,7 +373,7 @@ s3c24xx_serial_rx_chars(int irq, void *dev_id)
> /* check for break */
> if (uerstat & S3C2410_UERSTAT_BREAK) {
> dbg("break!\n");
> - if (cfg->hwport == 0)
> + if (cfg && (cfg->hwport == GSM_PORT))
> nspy_add(NSPY_TYPE_CERROR,
> 'B', jiffies);
> port->icount.brk++;
> @@ -386,13 +387,13 @@ s3c24xx_serial_rx_chars(int irq, void *dev_id)
> port->icount.overrun++;
>
> if ((uerstat & S3C2410_UERSTAT_FRAME) &&
> - (cfg->hwport == 0))
> + cfg && (cfg->hwport == GSM_PORT))
> nspy_add(NSPY_TYPE_CERROR, 'F', jiffies);
> if ((uerstat & S3C2410_UERSTAT_OVERRUN) &&
> - (cfg->hwport == 0))
> + cfg && (cfg->hwport == GSM_PORT))
> nspy_add(NSPY_TYPE_CERROR, 'O', jiffies);
> if ((uerstat & S3C2410_UERSTAT_PARITY) &&
> - (cfg->hwport == 0))
> + cfg && (cfg->hwport == GSM_PORT))
> nspy_add(NSPY_TYPE_CERROR, 'P', jiffies);
>
> uerstat &= port->read_status_mask;
> @@ -411,7 +412,7 @@ s3c24xx_serial_rx_chars(int irq, void *dev_id)
>
> uart_insert_char(port, uerstat, S3C2410_UERSTAT_OVERRUN, ch,
> flag);
> - if (cfg->hwport == 0) {
> + if (cfg && (cfg->hwport == GSM_PORT)) {
> if (afc_is_enabled)
> nspy_add(NSPY_TYPE_CIN, ch, jiffies);
> else
> @@ -427,126 +428,6 @@ s3c24xx_serial_rx_chars(int irq, void *dev_id)
> return IRQ_HANDLED;
> }
>
> -struct s3c24xx_uart_port *gta01_devptr;
> -#define FAKE_BSIZE 64
> -void s3c24xx_fake_rx_interrupt(int l)
> -{
> - struct s3c24xx_uart_port *ourport;
> - struct uart_port *port;
> - struct tty_struct *tty;
> - struct s3c2410_uartcfg *cfg;
> - unsigned int ulcon, ucon, ufcon, umcon, ufstat, umstat;
> - unsigned int ch, uerstat, flag, u;
> - int n, c, d;
> - unsigned int cbuf[FAKE_BSIZE], ubuf[FAKE_BSIZE];
> - int i, top, lctr;
> -
> - ourport = gta01_devptr;
> - if (!ourport)
> - return;
> -
> - port = &ourport->port;
> - if (!port)
> - return;
> -
> - tty = port->info->tty;
> - if (!tty)
> - return;
> -
> - cfg = s3c24xx_port_to_cfg(port);
> - if (!cfg)
> - return;
> -
> - disable_irq(RX_IRQ(port));
> -
> - ulcon = rd_regl(port, S3C2410_ULCON);
> - ucon = rd_regl(port, S3C2410_UCON),
> - ufcon = rd_regl(port, S3C2410_UFCON);
> - umcon = rd_regl(port, S3C2410_UMCON);
> - ufstat = rd_regl(port, S3C2410_UFSTAT);
> - umstat = rd_regl(port, S3C2410_UMSTAT);
> -
> - printk(KERN_ERR "%s @ entry: ulcon=0x%08x, ucon=0x%08x, ufcon=0x%08x\n",
> - __FUNCTION__, ulcon, ucon, ufcon);
> - printk(KERN_ERR
> - "%s @ entry: umcon=0x%08x, ufstat=0x%08x, umstat=0x%08x\n",
> - __FUNCTION__, umcon, ufstat, umstat);
> -
> - n = s3c24xx_serial_rx_fifocnt(ourport, ufstat);
> - if (ufstat & 0x100)
> - printk(KERN_ERR "%s @ entry: FIFO full...\n", __FUNCTION__);
> - else
> - printk(KERN_ERR "%s @ entry: %d characters await...\n",
> - __FUNCTION__, (ufstat & 0xf));
> -
> - nspy_add(NSPY_TYPE_SPECIAL, '[', jiffies);
> -
> - if (l < 100)
> - l = 10000;
> -
> - top = 0;
> - d = FAKE_BSIZE;
> - lctr = 0;
> - if (!(umcon & S3C2410_UMCOM_AFC)) {
> - wr_regl(port, S3C2410_UMCON, (umcon | S3C2410_UMCOM_AFC));
> - printk(KERN_ERR "%s @ entry: set AFC.\n", __FUNCTION__);
> - }
> - s3c2410_gpio_cfgpin(S3C2410_GPH1, S3C2410_GPH1_nRTS0);
> - while (d-- > 0) {
> -
> - u = rd_regl(port, S3C2410_UFSTAT) & 0x10f;
> - if (u == 0) {
> - c = l;
> - while ((c-- > 0) && (u == 0))
> - u = rd_regl(port, S3C2410_UFSTAT) & 0x10f;
> -
> - if (u) {
> - i = l - c;
> - if (i > lctr)
> - lctr = i;
> - }
> - }
> -
> - if (u & 0x100)
> - n = 16;
> - else
> - n = u & 0xf;
> -
> - while ((top < FAKE_BSIZE) && (n-- > 0)) {
> - ubuf[top] = rd_regl(port, S3C2410_UERSTAT);
> - cbuf[top] = rd_regb(port, S3C2410_URXH);
> - top++;
> - }
> - }
> -
> - for (i = 0; i < top; i++) {
> - uerstat = ubuf[i];
> - ch = cbuf[i];
> - flag = TTY_NORMAL;
> - port->icount.rx++;
> - if (uerstat & S3C2410_UERSTAT_OVERRUN) {
> - port->icount.overrun++;
> - nspy_add(NSPY_TYPE_CERROR, 'O', jiffies);
> - uerstat &= port->read_status_mask;
> - if (uerstat & S3C2410_UERSTAT_OVERRUN)
> - flag = TTY_FRAME;
> - }
> - uart_insert_char(port, uerstat, S3C2410_UERSTAT_OVERRUN, ch,
> - flag);
> - if (umcon & S3C2410_UMCOM_AFC)
> - nspy_add(NSPY_TYPE_CIN, ch, jiffies);
> - else
> - nspy_add(NSPY_TYPE_CIN_NOAFC, ch, jiffies);
> - }
> - tty_flip_buffer_push(tty);
> -
> - nspy_add(NSPY_TYPE_SPECIAL, ']', jiffies);
> - enable_irq(RX_IRQ(port));
> - printk(KERN_ERR "%s @ exit: max loop counter was %d...\n", __FUNCTION__,
> - lctr);
> -}
> -EXPORT_SYMBOL(s3c24xx_fake_rx_interrupt);
> -
> static irqreturn_t s3c24xx_serial_tx_chars(int irq, void *id)
> {
> struct s3c24xx_uart_port *ourport = id;
> @@ -557,7 +438,7 @@ static irqreturn_t s3c24xx_serial_tx_chars(int irq, void *id)
>
> if (port->x_char) {
> wr_regb(port, S3C2410_UTXH, port->x_char);
> - if (cfg->hwport == 0)
> + if (cfg && (cfg->hwport == GSM_PORT))
> nspy_add(NSPY_TYPE_COUT, port->x_char, jiffies);
> port->icount.tx++;
> port->x_char = 0;
> @@ -580,7 +461,7 @@ static irqreturn_t s3c24xx_serial_tx_chars(int irq, void *id)
> break;
>
> wr_regb(port, S3C2410_UTXH, xmit->buf[xmit->tail]);
> - if (cfg->hwport == 0)
> + if (cfg && (cfg->hwport == GSM_PORT))
> nspy_add(NSPY_TYPE_COUT, xmit->buf[xmit->tail],
> jiffies);
> xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
> @@ -625,51 +506,9 @@ static unsigned int s3c24xx_serial_get_mctrl(struct uart_port *port)
> return TIOCM_CAR | TIOCM_DSR;
> }
>
> -/* set during suspend/resume to ensure only user-space changes GSM mctrl */
> -static int doing_serial_suspend_resume;
> -/* flag to ensure that we only re-assert if, in fact, we did the de-assert */
> -static int gsm_mctrl_flowcontrolled;
> -
> static void s3c24xx_serial_set_mctrl(struct uart_port *port, unsigned int mctrl)
> {
> - struct s3c2410_uartcfg *cfg = s3c24xx_port_to_cfg(port);
> - if (cfg == NULL)
> - return;
> -
> - /* Special handling is only on port 0 of the GTA0x devices (GSM port) */
> - if (cfg->hwport != 0)
> - return;
> -
> - printk(KERN_ERR "s3c24xx_serial_set_mctrl: GSM mctrl=0x%08x\n", mctrl);
> - nspy_add(NSPY_TYPE_SPECIAL, 'R', jiffies);
> -
> - /* Ignore the request if this is a side-effect of suspend/resume */
> - if (doing_serial_suspend_resume)
> - return;
> -
> - if (mctrl & TIOCM_RTS) {
> - /* Return RTS to the UART - but only if
> - * we de-asserted it originally
> - */
> - if (gsm_mctrl_flowcontrolled) {
> - nspy_add(NSPY_TYPE_SPECIAL, '+', jiffies);
> - s3c2410_gpio_cfgpin(S3C2410_GPH1, S3C2410_GPH1_nRTS0);
> - gsm_mctrl_flowcontrolled = 0;
> - }
> - } else {
> - /* Hold RTS off by turning it into a GPIO,
> - * if not already done
> - */
> - if (s3c2410_gpio_getcfg(S3C2410_GPH1) == S3C2410_GPIO_OUTPUT)
> - gsm_mctrl_flowcontrolled = 0;
> - else {
> - gta_gsm_interrupts = 0;
> - s3c2410_gpio_setpin(S3C2410_GPH1, 1);
> - s3c2410_gpio_cfgpin(S3C2410_GPH1, S3C2410_GPH1_OUTP);
> - gsm_mctrl_flowcontrolled = 1;
> - nspy_add(NSPY_TYPE_SPECIAL, '-', jiffies);
> - }
> - }
> + /* todo - possibly remove AFC and do manual CTS */
> }
>
> static void s3c24xx_serial_break_ctl(struct uart_port *port, int break_state)
> @@ -1005,7 +844,7 @@ static void s3c24xx_serial_set_termios(struct uart_port *port,
>
> umcon = (termios->c_cflag & CRTSCTS) ? S3C2410_UMCOM_AFC : 0;
>
> - if (cfg->hwport == 0) {
> + if (cfg && (cfg->hwport == GSM_PORT)) {
> if (umcon & S3C2410_UMCOM_AFC)
> nspy_add(NSPY_TYPE_SPECIAL, 'M', jiffies);
> else
> @@ -1337,14 +1176,13 @@ static int s3c24xx_serial_suspend(struct platform_device *dev,
>
> if (port) {
> cfg = s3c24xx_port_to_cfg(port);
> - if (cfg->hwport == 0) {
> + if (cfg && (cfg->hwport == GSM_PORT)) {
> nspy_add(NSPY_TYPE_SUSPEND, 'S', jiffies);
> if ((s3c2410_gpio_getcfg(S3C2410_GPH1) ==
> S3C2410_GPIO_OUTPUT) && gta_gsm_interrupts) {
> nspy_add(NSPY_TYPE_SUSPEND, '!', jiffies);
> goto busy;
> }
> - doing_serial_suspend_resume = 1;
> nspy_add(NSPY_TYPE_SUSPEND, '{', jiffies);
> umcon = rd_regl(port, S3C2410_UMCON);
> if (umcon & S3C2410_UMCOM_AFC)
> @@ -1353,10 +1191,8 @@ static int s3c24xx_serial_suspend(struct platform_device *dev,
> nspy_add(NSPY_TYPE_SUSPEND, 'i', jiffies);
> }
> uart_suspend_port(&s3c24xx_uart_drv, port);
> - if (cfg->hwport == 0) {
> + if (cfg && (cfg->hwport == GSM_PORT))
> nspy_add(NSPY_TYPE_SUSPEND, '}', jiffies);
> - doing_serial_suspend_resume = 0;
> - }
> }
>
> return 0;
> @@ -1374,8 +1210,7 @@ static int s3c24xx_serial_resume(struct platform_device *dev)
>
> if (port) {
> cfg = s3c24xx_port_to_cfg(port);
> - if (cfg->hwport == 0) {
> - doing_serial_suspend_resume = 1;
> + if (cfg && (cfg->hwport == GSM_PORT)) {
> nspy_add(NSPY_TYPE_RESUME, 'S', jiffies);
> nspy_add(NSPY_TYPE_RESUME, '{', jiffies);
> umcon = rd_regl(port, S3C2410_UMCON);
> @@ -1390,18 +1225,13 @@ static int s3c24xx_serial_resume(struct platform_device *dev)
>
> uart_resume_port(&s3c24xx_uart_drv, port);
>
> - if (cfg->hwport == 0) {
> + if (cfg && (cfg->hwport == GSM_PORT)) {
> umcon = rd_regl(port, S3C2410_UMCON);
> if (umcon & S3C2410_UMCOM_AFC)
> nspy_add(NSPY_TYPE_RESUME, 'J', jiffies);
> else
> nspy_add(NSPY_TYPE_RESUME, 'j', jiffies);
> nspy_add(NSPY_TYPE_RESUME, '}', jiffies);
> - /* stash away the port info
> - * for the fake RX IRQ routine
> - */
> - gta01_devptr = ourport;
> - doing_serial_suspend_resume = 0;
> }
> }
>
> @@ -1551,7 +1381,7 @@ static int s3c2410_serial_resetport(struct uart_port *port,
> struct s3c2410_uartcfg *cfg)
> {
> unsigned int umcon = rd_regl(port, S3C2410_UMCON);
> - if (cfg->hwport == 0) {
> + if (cfg && (cfg->hwport == GSM_PORT)) {
> if (umcon & S3C2410_UMCOM_AFC)
> nspy_add(NSPY_TYPE_SPECIAL, 'R', jiffies);
> else
> @@ -1716,7 +1546,7 @@ static int s3c2440_serial_resetport(struct uart_port *port,
> {
> unsigned long ucon = rd_regl(port, S3C2410_UCON);
> unsigned int umcon = rd_regl(port, S3C2410_UMCON);
> - if (cfg->hwport == 0) {
> + if (cfg && (cfg->hwport == GSM_PORT)) {
> if (umcon & S3C2410_UMCOM_AFC)
> nspy_add(NSPY_TYPE_SPECIAL, 'R', jiffies);
> else
> @@ -2015,7 +1845,7 @@ s3c24xx_serial_console_putchar(struct uart_port *port, int ch)
> while (!s3c24xx_serial_console_txrdy(port, ufcon))
> barrier();
> wr_regb(cons_uart, S3C2410_UTXH, ch);
> - if (cfg->hwport == 0)
> + if (cfg && (cfg->hwport == GSM_PORT))
> nspy_add(NSPY_TYPE_CONSOLE, ch, jiffies);
>
> if (umcon & S3C2410_UMCOM_AFC)
>
>
>
More information about the openmoko-kernel
mailing list