[PATCH v2 tty-next 3/3] 8250: microchip: pci1xxxx: Add power management functions to quad-uart driver.

Kumaravel Thiagarajan posted 3 patches 3 years, 4 months ago
[PATCH v2 tty-next 3/3] 8250: microchip: pci1xxxx: Add power management functions to quad-uart driver.
Posted by Kumaravel Thiagarajan 3 years, 4 months ago
pci1xxxx's quad-uart function has the capability to wake up the host from
suspend state. Enable wakeup before entering into suspend and disable
wakeup on resume.

Signed-off-by: Kumaravel Thiagarajan <kumaravel.thiagarajan@microchip.com>
---
Changes in v2:
- Use DEFINE_SIMPLE_DEV_PM_OPS instead of SIMPLE_DEV_PM_OPS.
- Use pm_sleep_ptr instead of CONFIG_PM_SLEEP.
- Change the return data type of pci1xxxx_port_suspend to bool from int.
---
 drivers/tty/serial/8250/8250_pci1xxxx.c | 112 ++++++++++++++++++++++++
 1 file changed, 112 insertions(+)

diff --git a/drivers/tty/serial/8250/8250_pci1xxxx.c b/drivers/tty/serial/8250/8250_pci1xxxx.c
index 999e5a284266..0a0459f66177 100644
--- a/drivers/tty/serial/8250/8250_pci1xxxx.c
+++ b/drivers/tty/serial/8250/8250_pci1xxxx.c
@@ -352,6 +352,112 @@ static void pci1xxxx_irq_assign(struct pci1xxxx_8250 *priv,
 	}
 }
 
+static bool pci1xxxx_port_suspend(int line)
+{
+	struct uart_8250_port *up = serial8250_get_port(line);
+	struct uart_port *port = &up->port;
+	unsigned long flags;
+	u8 wakeup_mask;
+	bool ret = false;
+
+	if (port->suspended == 0 && port->dev) {
+		wakeup_mask = readb(up->port.membase + UART_WAKE_MASK_REG);
+
+		spin_lock_irqsave(&port->lock, flags);
+		port->mctrl &= ~TIOCM_OUT2;
+		port->ops->set_mctrl(port, port->mctrl);
+		spin_unlock_irqrestore(&port->lock, flags);
+
+		if ((wakeup_mask & UART_WAKE_SRCS) != UART_WAKE_SRCS)
+			ret = true;
+	}
+
+	writeb(UART_WAKE_SRCS, port->membase + UART_WAKE_REG);
+
+	return ret;
+}
+
+static void pci1xxxx_port_resume(int line)
+{
+	struct uart_8250_port *up = serial8250_get_port(line);
+	struct uart_port *port = &up->port;
+	unsigned long flags;
+
+	writeb(UART_WAKE_SRCS, port->membase + UART_WAKE_REG);
+
+	if (port->suspended == 0) {
+		spin_lock_irqsave(&port->lock, flags);
+		port->mctrl |= TIOCM_OUT2;
+		port->ops->set_mctrl(port, port->mctrl);
+		spin_unlock_irqrestore(&port->lock, flags);
+	}
+}
+
+static int pci1xxxx_suspend(struct device *dev)
+{
+	struct pci1xxxx_8250 *priv = dev_get_drvdata(dev);
+	struct pci_dev *pcidev = to_pci_dev(dev);
+	unsigned int data;
+	void __iomem *p;
+	bool wakeup = false;
+	int i;
+
+	for (i = 0; i < priv->nr; i++) {
+		if (priv->line[i] >= 0) {
+			serial8250_suspend_port(priv->line[i]);
+			wakeup |= pci1xxxx_port_suspend(priv->line[i]);
+		}
+	}
+
+	p = pci_ioremap_bar(pcidev, 0);
+	if (!p) {
+		dev_err(dev, "remapping of bar 0 memory failed");
+		return -ENOMEM;
+	}
+
+	data = readl(p + UART_RESET_REG);
+	writel(data | UART_RESET_D3_RESET_DISABLE, p + UART_RESET_REG);
+
+	if (wakeup)
+		writeb(UART_PCI_CTRL_D3_CLK_ENABLE, (p + UART_PCI_CTRL_REG));
+
+	iounmap(p);
+
+	device_set_wakeup_enable(dev, true);
+
+	pci_wake_from_d3(pcidev, true);
+
+	return 0;
+}
+
+static int pci1xxxx_resume(struct device *dev)
+{
+	struct pci1xxxx_8250 *priv = dev_get_drvdata(dev);
+	struct pci_dev *pcidev = to_pci_dev(dev);
+	unsigned int data;
+	void __iomem *p;
+	int i;
+
+	p = pci_ioremap_bar(pcidev, 0);
+	if (!p) {
+		dev_err(dev, "remapping of bar 0 memory failed");
+		return -ENOMEM;
+	}
+
+	data = readl(p + UART_RESET_REG);
+	writel(data & ~UART_RESET_D3_RESET_DISABLE, p + UART_RESET_REG);
+	iounmap(p);
+
+	for (i = 0; i < priv->nr; i++) {
+		if (priv->line[i] >= 0) {
+			pci1xxxx_port_resume(priv->line[i]);
+			serial8250_resume_port(priv->line[i]);
+		}
+	}
+
+	return 0;
+}
+
 static int pci1xxxx_serial_probe(struct pci_dev *dev,
 				 const struct pci_device_id *ent)
 {
@@ -427,6 +533,9 @@ static void pci1xxxx_serial_remove(struct pci_dev *dev)
 	}
 }
 
+static DEFINE_SIMPLE_DEV_PM_OPS(pci1xxxx_pm_ops, pci1xxxx_suspend,
+				pci1xxxx_resume);
+
 static const struct pci_device_id pci1xxxx_pci_tbl[] = {
 	{ PCI_DEVICE(PCI_VENDOR_ID_MCHP_PCI1XXXX, PCI_DEVICE_ID_MCHP_PCI11010) },
 	{ PCI_DEVICE(PCI_VENDOR_ID_MCHP_PCI1XXXX, PCI_DEVICE_ID_MCHP_PCI11101) },
@@ -441,6 +550,9 @@ static struct pci_driver pci1xxxx_pci_driver = {
 	.name		= "pci1xxxx serial",
 	.probe		= pci1xxxx_serial_probe,
 	.remove		= pci1xxxx_serial_remove,
+	.driver	= {
+		.pm     = pm_sleep_ptr(&pci1xxxx_pm_ops),
+	},
 	.id_table	= pci1xxxx_pci_tbl,
 };
 
-- 
2.25.1
Re: [PATCH v2 tty-next 3/3] 8250: microchip: pci1xxxx: Add power management functions to quad-uart driver.
Posted by Ilpo Järvinen 3 years, 4 months ago
On Sat, 1 Oct 2022, Kumaravel Thiagarajan wrote:

> pci1xxxx's quad-uart function has the capability to wake up the host from
> suspend state. Enable wakeup before entering into suspend and disable
> wakeup on resume.
> 
> Signed-off-by: Kumaravel Thiagarajan <kumaravel.thiagarajan@microchip.com>
> ---
> Changes in v2:
> - Use DEFINE_SIMPLE_DEV_PM_OPS instead of SIMPLE_DEV_PM_OPS.
> - Use pm_sleep_ptr instead of CONFIG_PM_SLEEP.
> - Change the return data type of pci1xxxx_port_suspend to bool from int.
> ---
>  drivers/tty/serial/8250/8250_pci1xxxx.c | 112 ++++++++++++++++++++++++
>  1 file changed, 112 insertions(+)
> 
> diff --git a/drivers/tty/serial/8250/8250_pci1xxxx.c b/drivers/tty/serial/8250/8250_pci1xxxx.c
> index 999e5a284266..0a0459f66177 100644
> --- a/drivers/tty/serial/8250/8250_pci1xxxx.c
> +++ b/drivers/tty/serial/8250/8250_pci1xxxx.c
> @@ -352,6 +352,112 @@ static void pci1xxxx_irq_assign(struct pci1xxxx_8250 *priv,
>  	}
>  }
>  
> +static bool pci1xxxx_port_suspend(int line)
> +{
> +	struct uart_8250_port *up = serial8250_get_port(line);
> +	struct uart_port *port = &up->port;
> +	unsigned long flags;
> +	u8 wakeup_mask;
> +	bool ret = false;
> +
> +	if (port->suspended == 0 && port->dev) {
> +		wakeup_mask = readb(up->port.membase + UART_WAKE_MASK_REG);
> +
> +		spin_lock_irqsave(&port->lock, flags);
> +		port->mctrl &= ~TIOCM_OUT2;
> +		port->ops->set_mctrl(port, port->mctrl);
> +		spin_unlock_irqrestore(&port->lock, flags);
> +
> +		if ((wakeup_mask & UART_WAKE_SRCS) != UART_WAKE_SRCS)
> +			ret = true;
> +	}
> +
> +	writeb(UART_WAKE_SRCS, port->membase + UART_WAKE_REG);
> +
> +	return ret;
> +}
> +
> +static void pci1xxxx_port_resume(int line)
> +{
> +	struct uart_8250_port *up = serial8250_get_port(line);
> +	struct uart_port *port = &up->port;
> +	unsigned long flags;
> +
> +	writeb(UART_WAKE_SRCS, port->membase + UART_WAKE_REG);
> +
> +	if (port->suspended == 0) {

Is this check the right way around?

> +		spin_lock_irqsave(&port->lock, flags);
> +		port->mctrl |= TIOCM_OUT2;
> +		port->ops->set_mctrl(port, port->mctrl);
> +		spin_unlock_irqrestore(&port->lock, flags);
> +	}
> +}
> +
> +static int pci1xxxx_suspend(struct device *dev)
> +{
> +	struct pci1xxxx_8250 *priv = dev_get_drvdata(dev);
> +	struct pci_dev *pcidev = to_pci_dev(dev);
> +	unsigned int data;
> +	void __iomem *p;
> +	bool wakeup = false;
> +	int i;
> +
> +	for (i = 0; i < priv->nr; i++) {
> +		if (priv->line[i] >= 0) {
> +			serial8250_suspend_port(priv->line[i]);
> +			wakeup |= pci1xxxx_port_suspend(priv->line[i]);

So first serial8250_suspend_port() calls into uart_suspend_port() that
sets port->suspended to 1, then pci1xxxx_port_suspend() checks if it's 0.
Is this intentional?


-- 
 i.
RE: [PATCH v2 tty-next 3/3] 8250: microchip: pci1xxxx: Add power management functions to quad-uart driver.
Posted by Kumaravel.Thiagarajan@microchip.com 3 years, 4 months ago
> -----Original Message-----
> From: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
> Sent: Monday, October 3, 2022 3:21 PM
> To: Kumaravel Thiagarajan - I21417 <Kumaravel.Thiagarajan@microchip.com>
> Subject: Re: [PATCH v2 tty-next 3/3] 8250: microchip: pci1xxxx: Add power
> management functions to quad-uart driver.
>  
> On Sat, 1 Oct 2022, Kumaravel Thiagarajan wrote:
> 
> > pci1xxxx's quad-uart function has the capability to wake up the host
> > from suspend state. Enable wakeup before entering into suspend and
> > disable wakeup on resume.
> >
> > Signed-off-by: Kumaravel Thiagarajan
> > <kumaravel.thiagarajan@microchip.com>
> > ---
> > Changes in v2:
> > - Use DEFINE_SIMPLE_DEV_PM_OPS instead of SIMPLE_DEV_PM_OPS.
> > - Use pm_sleep_ptr instead of CONFIG_PM_SLEEP.
> > - Change the return data type of pci1xxxx_port_suspend to bool from int.
> > ---
> >  drivers/tty/serial/8250/8250_pci1xxxx.c | 112
> > ++++++++++++++++++++++++
> >  1 file changed, 112 insertions(+)
> >
> > diff --git a/drivers/tty/serial/8250/8250_pci1xxxx.c
> > b/drivers/tty/serial/8250/8250_pci1xxxx.c
> > index 999e5a284266..0a0459f66177 100644
> > --- a/drivers/tty/serial/8250/8250_pci1xxxx.c
> > +++ b/drivers/tty/serial/8250/8250_pci1xxxx.c
> > @@ -352,6 +352,112 @@ static void pci1xxxx_irq_assign(struct
> pci1xxxx_8250 *priv,
> >       }
> >  }
> >
> > +static bool pci1xxxx_port_suspend(int line) {
> > +     struct uart_8250_port *up = serial8250_get_port(line);
> > +     struct uart_port *port = &up->port;
> > +     unsigned long flags;
> > +     u8 wakeup_mask;
> > +     bool ret = false;
> > +
> > +     if (port->suspended == 0 && port->dev) {
> > +             wakeup_mask = readb(up->port.membase +
> > + UART_WAKE_MASK_REG);
> > +
> > +             spin_lock_irqsave(&port->lock, flags);
> > +             port->mctrl &= ~TIOCM_OUT2;
> > +             port->ops->set_mctrl(port, port->mctrl);
> > +             spin_unlock_irqrestore(&port->lock, flags);
> > +
> > +             if ((wakeup_mask & UART_WAKE_SRCS) != UART_WAKE_SRCS)
> > +                     ret = true;
> > +     }
> > +
> > +     writeb(UART_WAKE_SRCS, port->membase + UART_WAKE_REG);
> > +
> > +     return ret;
> > +}
> > +
> > +static void pci1xxxx_port_resume(int line) {
> > +     struct uart_8250_port *up = serial8250_get_port(line);
> > +     struct uart_port *port = &up->port;
> > +     unsigned long flags;
> > +
> > +     writeb(UART_WAKE_SRCS, port->membase + UART_WAKE_REG);
> > +
> > +     if (port->suspended == 0) {
> 
> Is this check the right way around?
Yes. I think port->suspended is not set for wake-up capable ports and the code in this if block gets executed for those ports.
I will check this again.
> 
> > +             spin_lock_irqsave(&port->lock, flags);
> > +             port->mctrl |= TIOCM_OUT2;
> > +             port->ops->set_mctrl(port, port->mctrl);
> > +             spin_unlock_irqrestore(&port->lock, flags);
> > +     }
> > +}
> > +
> > +static int pci1xxxx_suspend(struct device *dev) {
> > +     struct pci1xxxx_8250 *priv = dev_get_drvdata(dev);
> > +     struct pci_dev *pcidev = to_pci_dev(dev);
> > +     unsigned int data;
> > +     void __iomem *p;
> > +     bool wakeup = false;
> > +     int i;
> > +
> > +     for (i = 0; i < priv->nr; i++) {
> > +             if (priv->line[i] >= 0) {
> > +                     serial8250_suspend_port(priv->line[i]);
> > +                     wakeup |= pci1xxxx_port_suspend(priv->line[i]);
> 
> So first serial8250_suspend_port() calls into uart_suspend_port() that sets
> port->suspended to 1, then pci1xxxx_port_suspend() checks if it's 0.
> Is this intentional?
Yes. I think port->suspended does not seem to be set for wake-up capable ports and only 
for those ports, inside pci1xxxx_port_suspend, TIOCM_OUT2 is cleared.
But I must check for the race condition as Andy had pointed out.
Please let me know if there are any questions.

Thank You.

Regards,
Kumar
Re: [PATCH v2 tty-next 3/3] 8250: microchip: pci1xxxx: Add power management functions to quad-uart driver.
Posted by Andy Shevchenko 3 years, 4 months ago
On Sat, Oct 1, 2022 at 9:15 AM Kumaravel Thiagarajan
<kumaravel.thiagarajan@microchip.com> wrote:
>
> pci1xxxx's quad-uart function has the capability to wake up the host from
> suspend state. Enable wakeup before entering into suspend and disable
> wakeup on resume.

...

> +       port->suspended == 0

How is this check race-protected?

...

> +static void pci1xxxx_port_resume(int line)
> +{

> +       if (port->suspended == 0) {

Ditto.

> +       }
> +}

...

If you have similarities with 8250_pci, probably you need to split it
to 8250_pcilib.c and share. (See how 8250_dw /8250_lpss are done in
that sense.)

--
With Best Regards,
Andy Shevchenko
RE: [PATCH v2 tty-next 3/3] 8250: microchip: pci1xxxx: Add power management functions to quad-uart driver.
Posted by Tharunkumar.Pasumarthi@microchip.com 3 years, 3 months ago
> From: Andy Shevchenko <andy.shevchenko@gmail.com>
> Sent: Monday, October 3, 2022 2:57 PM
> To: Kumaravel Thiagarajan - I21417
> <Kumaravel.Thiagarajan@microchip.com>
> Subject: Re: [PATCH v2 tty-next 3/3] 8250: microchip: pci1xxxx: Add power
> management functions to quad-uart driver.
> 
> EXTERNAL EMAIL: Do not click links or open attachments unless you know the
> content is safe
>  
> If you have similarities with 8250_pci, probably you need to split it to
> 8250_pcilib.c and share. (See how 8250_dw /8250_lpss are done in that
> sense.)

Hi Andy,
All the functions used in 8250_pci1xxxx.c  that have similarity with 8250_pci use registers
that are specific to our IP.  The only function that can be moved to common library is the 
setup_port. But, for that the first argument of setup_port must be changed to 
'struct pci_dev *dev' (priv->dev).  Do you suggest doing this?

Thanks,
Tharun Kumar P
Re: [PATCH v2 tty-next 3/3] 8250: microchip: pci1xxxx: Add power management functions to quad-uart driver.
Posted by Andy Shevchenko 3 years, 3 months ago
On Fri, Nov 4, 2022 at 12:23 PM <Tharunkumar.Pasumarthi@microchip.com> wrote:
> > From: Andy Shevchenko <andy.shevchenko@gmail.com>
> > Sent: Monday, October 3, 2022 2:57 PM

> > If you have similarities with 8250_pci, probably you need to split it to
> > 8250_pcilib.c and share. (See how 8250_dw /8250_lpss are done in that
> > sense.)
>
> All the functions used in 8250_pci1xxxx.c  that have similarity with 8250_pci use registers
> that are specific to our IP.  The only function that can be moved to common library is the
> setup_port. But, for that the first argument of setup_port must be changed to
> 'struct pci_dev *dev' (priv->dev).  Do you suggest doing this?

So, you can create a common serial8250_setup_port(struct pci_dev *dev,
...) and call it from the static setup_port() inside 8250_pci.c. This
way you won't introduce too many churn.

-- 
With Best Regards,
Andy Shevchenko
RE: [PATCH v2 tty-next 3/3] 8250: microchip: pci1xxxx: Add power management functions to quad-uart driver.
Posted by Tharunkumar.Pasumarthi@microchip.com 3 years, 3 months ago
> -----Original Message-----
> From: Andy Shevchenko <andy.shevchenko@gmail.com>
> Sent: Monday, October 3, 2022 2:57 PM
> To: Kumaravel Thiagarajan - I21417
> <Kumaravel.Thiagarajan@microchip.com>
> Subject: Re: [PATCH v2 tty-next 3/3] 8250: microchip: pci1xxxx: Add power
> management functions to quad-uart driver.
> 
> EXTERNAL EMAIL: Do not click links or open attachments unless you know the
> content is safe
>  
> If you have similarities with 8250_pci, probably you need to split it to
> 8250_pcilib.c and share. (See how 8250_dw /8250_lpss are done in that
> sense.)

Functions related to suspend and resume use registers specific to our IP which other drivers cannot use. I will move functions like pci1xxxx_set_divisor, pci1xxxx_get_divisor, pci1xxxx_setup_port and pci1xxxx_rs485_config to a new file.

Thanks,
Tharun Kumar P
Re: [PATCH v2 tty-next 3/3] 8250: microchip: pci1xxxx: Add power management functions to quad-uart driver.
Posted by Tharunkumar.Pasumarthi@microchip.com 3 years, 3 months ago
On Mon, 2022-10-03 at 12:26 +0300, Andy Shevchenko wrote:
> EXTERNAL EMAIL: Do not click links or open attachments unless you know the
> content is safe
> > +       port->suspended == 0
> 
> How is this check race-protected?

I will use mutex_lock to handle race condition.


Thanks,
Tharun Kumar P
RE: [PATCH v2 tty-next 3/3] 8250: microchip: pci1xxxx: Add power management functions to quad-uart driver.
Posted by Kumaravel.Thiagarajan@microchip.com 3 years, 4 months ago
> -----Original Message-----
> From: Andy Shevchenko <andy.shevchenko@gmail.com>
> Sent: Monday, October 3, 2022 2:57 PM
> To: Kumaravel Thiagarajan - I21417 <Kumaravel.Thiagarajan@microchip.com>
> Subject: Re: [PATCH v2 tty-next 3/3] 8250: microchip: pci1xxxx: Add power
> management functions to quad-uart driver.
> 
> EXTERNAL EMAIL: Do not click links or open attachments unless you know the
> content is safe
> 
> On Sat, Oct 1, 2022 at 9:15 AM Kumaravel Thiagarajan
> <kumaravel.thiagarajan@microchip.com> wrote:
> >
> > pci1xxxx's quad-uart function has the capability to wake up the host
> > from suspend state. Enable wakeup before entering into suspend and
> > disable wakeup on resume.
> 
> ...
> 
> > +       port->suspended == 0
> 
> How is this check race-protected?
> 
> ...
> 
> > +static void pci1xxxx_port_resume(int line) {
> 
> > +       if (port->suspended == 0) {
> 
> Ditto.
> 
> > +       }
> > +}
> 
> ...
> 
> If you have similarities with 8250_pci, probably you need to split it to
> 8250_pcilib.c and share. (See how 8250_dw /8250_lpss are done in that
> sense.)
I will review my code against all the above comments and get back to you.

Thank You.

Regards,
Kumar