[PATCH v4 5/8] ehci-atmel: Power down during suspend is normal

2017-09-28 Thread Romain Izard
When an Atmel SoC is suspended with the backup mode, the USB bus will be
powered down. As this is expected, do not return an error to the driver
core when ehci_resume detects it.

Signed-off-by: Romain Izard 
Acked-by: Nicolas Ferre 
---
 drivers/usb/host/ehci-atmel.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/drivers/usb/host/ehci-atmel.c b/drivers/usb/host/ehci-atmel.c
index 7440722bfbf0..2a8b9bdc0e57 100644
--- a/drivers/usb/host/ehci-atmel.c
+++ b/drivers/usb/host/ehci-atmel.c
@@ -205,7 +205,8 @@ static int __maybe_unused ehci_atmel_drv_resume(struct 
device *dev)
struct atmel_ehci_priv *atmel_ehci = hcd_to_atmel_ehci_priv(hcd);
 
atmel_start_clock(atmel_ehci);
-   return ehci_resume(hcd, false);
+   ehci_resume(hcd, false);
+   return 0;
 }
 
 #ifdef CONFIG_OF
-- 
2.11.0



[PATCH v4 1/8] clk: at91: pmc: Wait for clocks when resuming

2017-09-28 Thread Romain Izard
Wait for the syncronization of all clocks when resuming, not only the
UPLL clock. Do not use regmap_read_poll_timeout, as it will call BUG()
when interrupts are masked, which is the case in here.

Signed-off-by: Romain Izard 
Acked-by: Ludovic Desroches 
Acked-by: Nicolas Ferre 
---
 drivers/clk/at91/pmc.c | 24 
 1 file changed, 16 insertions(+), 8 deletions(-)

diff --git a/drivers/clk/at91/pmc.c b/drivers/clk/at91/pmc.c
index 775af473fe11..5c2b26de303e 100644
--- a/drivers/clk/at91/pmc.c
+++ b/drivers/clk/at91/pmc.c
@@ -107,10 +107,20 @@ static int pmc_suspend(void)
return 0;
 }
 
+static bool pmc_ready(unsigned int mask)
+{
+   unsigned int status;
+
+   regmap_read(pmcreg, AT91_PMC_SR, );
+
+   return ((status & mask) == mask) ? 1 : 0;
+}
+
 static void pmc_resume(void)
 {
-   int i, ret = 0;
+   int i;
u32 tmp;
+   u32 mask = AT91_PMC_MCKRDY | AT91_PMC_LOCKA;
 
regmap_read(pmcreg, AT91_PMC_MCKR, );
if (pmc_cache.mckr != tmp)
@@ -134,13 +144,11 @@ static void pmc_resume(void)
 AT91_PMC_PCR_CMD);
}
 
-   if (pmc_cache.uckr & AT91_PMC_UPLLEN) {
-   ret = regmap_read_poll_timeout(pmcreg, AT91_PMC_SR, tmp,
-  !(tmp & AT91_PMC_LOCKU),
-  10, 5000);
-   if (ret)
-   pr_crit("USB PLL didn't lock when resuming\n");
-   }
+   if (pmc_cache.uckr & AT91_PMC_UPLLEN)
+   mask |= AT91_PMC_LOCKU;
+
+   while (!pmc_ready(mask))
+   cpu_relax();
 }
 
 static struct syscore_ops pmc_syscore_ops = {
-- 
2.11.0



[PATCH v4 8/8] tty/serial: atmel: Prevent a warning on suspend

2017-09-28 Thread Romain Izard
The atmel serial port driver reported the following warning on suspend:
atmel_usart f802.serial: ttyS1: Unable to drain transmitter

As the ATMEL_US_TXEMPTY status bit in ATMEL_US_CSR is always cleared
when the transmitter is disabled, we need to know the transmitter's
state to return the real fifo state. And as ATMEL_US_CR is write-only,
it is necessary to save the state of the transmitter in a local
variable, and update the variable when TXEN and TXDIS is written in
ATMEL_US_CR.

After those changes, atmel_tx_empty can return "empty" on suspend, the
warning in uart_suspend_port disappears, and suspending is 20ms shorter
for each enabled Atmel serial port.

Signed-off-by: Romain Izard 
Tested-by: Nicolas Ferre 
Acked-by: Nicolas Ferre 
Acked-by: Richard Genoud 
---
 drivers/tty/serial/atmel_serial.c | 13 +
 1 file changed, 13 insertions(+)

diff --git a/drivers/tty/serial/atmel_serial.c 
b/drivers/tty/serial/atmel_serial.c
index 7551cab438ff..ce45b4ada0bf 100644
--- a/drivers/tty/serial/atmel_serial.c
+++ b/drivers/tty/serial/atmel_serial.c
@@ -171,6 +171,7 @@ struct atmel_uart_port {
boolhas_hw_timer;
struct timer_list   uart_timer;
 
+   booltx_stopped;
boolsuspended;
unsigned intpending;
unsigned intpending_status;
@@ -380,6 +381,10 @@ static int atmel_config_rs485(struct uart_port *port,
  */
 static u_int atmel_tx_empty(struct uart_port *port)
 {
+   struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
+
+   if (atmel_port->tx_stopped)
+   return TIOCSER_TEMT;
return (atmel_uart_readl(port, ATMEL_US_CSR) & ATMEL_US_TXEMPTY) ?
TIOCSER_TEMT :
0;
@@ -485,6 +490,7 @@ static void atmel_stop_tx(struct uart_port *port)
 * is fully transmitted.
 */
atmel_uart_writel(port, ATMEL_US_CR, ATMEL_US_TXDIS);
+   atmel_port->tx_stopped = true;
 
/* Disable interrupts */
atmel_uart_writel(port, ATMEL_US_IDR, atmel_port->tx_done_mask);
@@ -521,6 +527,7 @@ static void atmel_start_tx(struct uart_port *port)
 
/* re-enable the transmitter */
atmel_uart_writel(port, ATMEL_US_CR, ATMEL_US_TXEN);
+   atmel_port->tx_stopped = false;
 }
 
 /*
@@ -1866,6 +1873,7 @@ static int atmel_startup(struct uart_port *port)
atmel_uart_writel(port, ATMEL_US_CR, ATMEL_US_RSTSTA | ATMEL_US_RSTRX);
/* enable xmit & rcvr */
atmel_uart_writel(port, ATMEL_US_CR, ATMEL_US_TXEN | ATMEL_US_RXEN);
+   atmel_port->tx_stopped = false;
 
setup_timer(_port->uart_timer,
atmel_uart_timer_callback,
@@ -2122,6 +2130,7 @@ static void atmel_set_termios(struct uart_port *port, 
struct ktermios *termios,
 
/* disable receiver and transmitter */
atmel_uart_writel(port, ATMEL_US_CR, ATMEL_US_TXDIS | ATMEL_US_RXDIS);
+   atmel_port->tx_stopped = true;
 
/* mode */
if (port->rs485.flags & SER_RS485_ENABLED) {
@@ -2207,6 +2216,7 @@ static void atmel_set_termios(struct uart_port *port, 
struct ktermios *termios,
atmel_uart_writel(port, ATMEL_US_BRGR, quot);
atmel_uart_writel(port, ATMEL_US_CR, ATMEL_US_RSTSTA | ATMEL_US_RSTRX);
atmel_uart_writel(port, ATMEL_US_CR, ATMEL_US_TXEN | ATMEL_US_RXEN);
+   atmel_port->tx_stopped = false;
 
/* restore interrupts */
atmel_uart_writel(port, ATMEL_US_IER, imr);
@@ -2450,6 +2460,7 @@ static void atmel_console_write(struct console *co, const 
char *s, u_int count)
 
/* Make sure that tx path is actually able to send characters */
atmel_uart_writel(port, ATMEL_US_CR, ATMEL_US_TXEN);
+   atmel_port->tx_stopped = false;
 
uart_console_write(port, s, count, atmel_console_putchar);
 
@@ -2511,6 +2522,7 @@ static int __init atmel_console_setup(struct console *co, 
char *options)
 {
int ret;
struct uart_port *port = _ports[co->index].uart;
+   struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
int baud = 115200;
int bits = 8;
int parity = 'n';
@@ -2528,6 +2540,7 @@ static int __init atmel_console_setup(struct console *co, 
char *options)
atmel_uart_writel(port, ATMEL_US_IDR, -1);
atmel_uart_writel(port, ATMEL_US_CR, ATMEL_US_RSTSTA | ATMEL_US_RSTRX);
atmel_uart_writel(port, ATMEL_US_CR, ATMEL_US_TXEN | ATMEL_US_RXEN);
+   atmel_port->tx_stopped = false;
 
if (options)
uart_parse_options(options, , , , );
-- 
2.11.0



[PATCH v4 6/8] pwm: atmel-tcb: Support backup mode

2017-09-28 Thread Romain Izard
Save and restore registers for the PWM on suspend and resume, which
makes hibernation and backup modes possible.

Signed-off-by: Romain Izard <romain.izard@gmail.com>
Acked-by: Nicolas Ferre <nicolas.fe...@microchip.com>
---
 drivers/pwm/pwm-atmel-tcb.c | 63 +++--
 1 file changed, 61 insertions(+), 2 deletions(-)

diff --git a/drivers/pwm/pwm-atmel-tcb.c b/drivers/pwm/pwm-atmel-tcb.c
index 75db585a2a94..acd3ce8ecf3f 100644
--- a/drivers/pwm/pwm-atmel-tcb.c
+++ b/drivers/pwm/pwm-atmel-tcb.c
@@ -37,11 +37,20 @@ struct atmel_tcb_pwm_device {
unsigned period;/* PWM period expressed in clk cycles */
 };
 
+struct atmel_tcb_channel {
+   u32 enabled;
+   u32 cmr;
+   u32 ra;
+   u32 rb;
+   u32 rc;
+};
+
 struct atmel_tcb_pwm_chip {
struct pwm_chip chip;
spinlock_t lock;
struct atmel_tc *tc;
struct atmel_tcb_pwm_device *pwms[NPWM];
+   struct atmel_tcb_channel bkup[NPWM / 2];
 };
 
 static inline struct atmel_tcb_pwm_chip *to_tcb_chip(struct pwm_chip *chip)
@@ -175,12 +184,15 @@ static void atmel_tcb_pwm_disable(struct pwm_chip *chip, 
struct pwm_device *pwm)
 * Use software trigger to apply the new setting.
 * If both PWM devices in this group are disabled we stop the clock.
 */
-   if (!(cmr & (ATMEL_TC_ACPC | ATMEL_TC_BCPC)))
+   if (!(cmr & (ATMEL_TC_ACPC | ATMEL_TC_BCPC))) {
__raw_writel(ATMEL_TC_SWTRG | ATMEL_TC_CLKDIS,
 regs + ATMEL_TC_REG(group, CCR));
-   else
+   tcbpwmc->bkup[group].enabled = 1;
+   } else {
__raw_writel(ATMEL_TC_SWTRG, regs +
 ATMEL_TC_REG(group, CCR));
+   tcbpwmc->bkup[group].enabled = 0;
+   }
 
spin_unlock(>lock);
 }
@@ -263,6 +275,7 @@ static int atmel_tcb_pwm_enable(struct pwm_chip *chip, 
struct pwm_device *pwm)
/* Use software trigger to apply the new setting */
__raw_writel(ATMEL_TC_CLKEN | ATMEL_TC_SWTRG,
 regs + ATMEL_TC_REG(group, CCR));
+   tcbpwmc->bkup[group].enabled = 1;
spin_unlock(>lock);
return 0;
 }
@@ -445,10 +458,56 @@ static const struct of_device_id atmel_tcb_pwm_dt_ids[] = 
{
 };
 MODULE_DEVICE_TABLE(of, atmel_tcb_pwm_dt_ids);
 
+#ifdef CONFIG_PM_SLEEP
+static int atmel_tcb_pwm_suspend(struct device *dev)
+{
+   struct platform_device *pdev = to_platform_device(dev);
+   struct atmel_tcb_pwm_chip *tcbpwm = platform_get_drvdata(pdev);
+   void __iomem *base = tcbpwm->tc->regs;
+   int i;
+
+   for (i = 0; i < (NPWM / 2); i++) {
+   struct atmel_tcb_channel *chan = >bkup[i];
+
+   chan->cmr = readl(base + ATMEL_TC_REG(i, CMR));
+   chan->ra = readl(base + ATMEL_TC_REG(i, RA));
+   chan->rb = readl(base + ATMEL_TC_REG(i, RB));
+   chan->rc = readl(base + ATMEL_TC_REG(i, RC));
+   }
+   return 0;
+}
+
+static int atmel_tcb_pwm_resume(struct device *dev)
+{
+   struct platform_device *pdev = to_platform_device(dev);
+   struct atmel_tcb_pwm_chip *tcbpwm = platform_get_drvdata(pdev);
+   void __iomem *base = tcbpwm->tc->regs;
+   int i;
+
+   for (i = 0; i < (NPWM / 2); i++) {
+   struct atmel_tcb_channel *chan = >bkup[i];
+
+   writel(chan->cmr, base + ATMEL_TC_REG(i, CMR));
+   writel(chan->ra, base + ATMEL_TC_REG(i, RA));
+   writel(chan->rb, base + ATMEL_TC_REG(i, RB));
+   writel(chan->rc, base + ATMEL_TC_REG(i, RC));
+   if (chan->enabled) {
+   writel(ATMEL_TC_CLKEN | ATMEL_TC_SWTRG,
+   base + ATMEL_TC_REG(i, CCR));
+   }
+   }
+   return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(atmel_tcb_pwm_pm_ops, atmel_tcb_pwm_suspend,
+atmel_tcb_pwm_resume);
+
 static struct platform_driver atmel_tcb_pwm_driver = {
.driver = {
.name = "atmel-tcb-pwm",
.of_match_table = atmel_tcb_pwm_dt_ids,
+   .pm = _tcb_pwm_pm_ops,
},
.probe = atmel_tcb_pwm_probe,
.remove = atmel_tcb_pwm_remove,
-- 
2.11.0



[PATCH v4 6/8] pwm: atmel-tcb: Support backup mode

2017-09-28 Thread Romain Izard
Save and restore registers for the PWM on suspend and resume, which
makes hibernation and backup modes possible.

Signed-off-by: Romain Izard 
Acked-by: Nicolas Ferre 
---
 drivers/pwm/pwm-atmel-tcb.c | 63 +++--
 1 file changed, 61 insertions(+), 2 deletions(-)

diff --git a/drivers/pwm/pwm-atmel-tcb.c b/drivers/pwm/pwm-atmel-tcb.c
index 75db585a2a94..acd3ce8ecf3f 100644
--- a/drivers/pwm/pwm-atmel-tcb.c
+++ b/drivers/pwm/pwm-atmel-tcb.c
@@ -37,11 +37,20 @@ struct atmel_tcb_pwm_device {
unsigned period;/* PWM period expressed in clk cycles */
 };
 
+struct atmel_tcb_channel {
+   u32 enabled;
+   u32 cmr;
+   u32 ra;
+   u32 rb;
+   u32 rc;
+};
+
 struct atmel_tcb_pwm_chip {
struct pwm_chip chip;
spinlock_t lock;
struct atmel_tc *tc;
struct atmel_tcb_pwm_device *pwms[NPWM];
+   struct atmel_tcb_channel bkup[NPWM / 2];
 };
 
 static inline struct atmel_tcb_pwm_chip *to_tcb_chip(struct pwm_chip *chip)
@@ -175,12 +184,15 @@ static void atmel_tcb_pwm_disable(struct pwm_chip *chip, 
struct pwm_device *pwm)
 * Use software trigger to apply the new setting.
 * If both PWM devices in this group are disabled we stop the clock.
 */
-   if (!(cmr & (ATMEL_TC_ACPC | ATMEL_TC_BCPC)))
+   if (!(cmr & (ATMEL_TC_ACPC | ATMEL_TC_BCPC))) {
__raw_writel(ATMEL_TC_SWTRG | ATMEL_TC_CLKDIS,
 regs + ATMEL_TC_REG(group, CCR));
-   else
+   tcbpwmc->bkup[group].enabled = 1;
+   } else {
__raw_writel(ATMEL_TC_SWTRG, regs +
 ATMEL_TC_REG(group, CCR));
+   tcbpwmc->bkup[group].enabled = 0;
+   }
 
spin_unlock(>lock);
 }
@@ -263,6 +275,7 @@ static int atmel_tcb_pwm_enable(struct pwm_chip *chip, 
struct pwm_device *pwm)
/* Use software trigger to apply the new setting */
__raw_writel(ATMEL_TC_CLKEN | ATMEL_TC_SWTRG,
 regs + ATMEL_TC_REG(group, CCR));
+   tcbpwmc->bkup[group].enabled = 1;
spin_unlock(>lock);
return 0;
 }
@@ -445,10 +458,56 @@ static const struct of_device_id atmel_tcb_pwm_dt_ids[] = 
{
 };
 MODULE_DEVICE_TABLE(of, atmel_tcb_pwm_dt_ids);
 
+#ifdef CONFIG_PM_SLEEP
+static int atmel_tcb_pwm_suspend(struct device *dev)
+{
+   struct platform_device *pdev = to_platform_device(dev);
+   struct atmel_tcb_pwm_chip *tcbpwm = platform_get_drvdata(pdev);
+   void __iomem *base = tcbpwm->tc->regs;
+   int i;
+
+   for (i = 0; i < (NPWM / 2); i++) {
+   struct atmel_tcb_channel *chan = >bkup[i];
+
+   chan->cmr = readl(base + ATMEL_TC_REG(i, CMR));
+   chan->ra = readl(base + ATMEL_TC_REG(i, RA));
+   chan->rb = readl(base + ATMEL_TC_REG(i, RB));
+   chan->rc = readl(base + ATMEL_TC_REG(i, RC));
+   }
+   return 0;
+}
+
+static int atmel_tcb_pwm_resume(struct device *dev)
+{
+   struct platform_device *pdev = to_platform_device(dev);
+   struct atmel_tcb_pwm_chip *tcbpwm = platform_get_drvdata(pdev);
+   void __iomem *base = tcbpwm->tc->regs;
+   int i;
+
+   for (i = 0; i < (NPWM / 2); i++) {
+   struct atmel_tcb_channel *chan = >bkup[i];
+
+   writel(chan->cmr, base + ATMEL_TC_REG(i, CMR));
+   writel(chan->ra, base + ATMEL_TC_REG(i, RA));
+   writel(chan->rb, base + ATMEL_TC_REG(i, RB));
+   writel(chan->rc, base + ATMEL_TC_REG(i, RC));
+   if (chan->enabled) {
+   writel(ATMEL_TC_CLKEN | ATMEL_TC_SWTRG,
+   base + ATMEL_TC_REG(i, CCR));
+   }
+   }
+   return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(atmel_tcb_pwm_pm_ops, atmel_tcb_pwm_suspend,
+atmel_tcb_pwm_resume);
+
 static struct platform_driver atmel_tcb_pwm_driver = {
.driver = {
.name = "atmel-tcb-pwm",
.of_match_table = atmel_tcb_pwm_dt_ids,
+   .pm = _tcb_pwm_pm_ops,
},
.probe = atmel_tcb_pwm_probe,
.remove = atmel_tcb_pwm_remove,
-- 
2.11.0



Re: [PATCH v3 4/8] mtd: nand: atmel: Avoid ECC errors when leaving backup mode

2017-09-27 Thread Romain Izard
2017-09-27 17:08 GMT+02:00 Boris Brezillon <boris.brezil...@free-electrons.com>:
> On Wed, 27 Sep 2017 10:35:51 +0200
> Romain Izard <romain.izard@gmail.com> wrote:
>
>> During backup mode, the contents of all registers will be cleared as the
>> SoC will be completely powered down. For a product that boots on NAND
>> Flash memory, the bootloader will obviously use the related controller
>> to read the Flash and correct any detected error in the memory, before
>> handling back control to the kernel's resuming entry point.
>>
>> But it does not clean the NAND controller registers after use and on its
>> side the kernel driver expects the error locator to be powered down and
>> in a clean state. Add a resume hook for the PMECC error locator, and
>> reset its registers.
>>
>> Signed-off-by: Romain Izard <romain.izard@gmail.com>
>> ---
>> Change in v3:
>> * keep the PMECC disabled when not in use, and use atmel_pmecc_resume to
>>   reset the controller after the bootloader has left it enabled.
>>
>>  drivers/mtd/nand/atmel/nand-controller.c |  3 +++
>>  drivers/mtd/nand/atmel/pmecc.c   | 22 ++
>>  drivers/mtd/nand/atmel/pmecc.h   |  1 +
>>  3 files changed, 18 insertions(+), 8 deletions(-)
>>
>> diff --git a/drivers/mtd/nand/atmel/nand-controller.c 
>> b/drivers/mtd/nand/atmel/nand-controller.c
>> index f25eca79f4e5..86c2199380c2 100644
>> --- a/drivers/mtd/nand/atmel/nand-controller.c
>> +++ b/drivers/mtd/nand/atmel/nand-controller.c
>> @@ -2530,6 +2530,9 @@ static __maybe_unused int 
>> atmel_nand_controller_resume(struct device *dev)
>>   struct atmel_nand_controller *nc = dev_get_drvdata(dev);
>>   struct atmel_nand *nand;
>>
>> + if (nand->pmecc)
>> + atmel_pmecc_resume(nand->pmecc);
>> +
>
> nand is used uninitialized here, and atmel_pmecc_resume() should be
> passed a atmel_pmecc object not a atmel_pmecc_user.
>
> if (nc->pmecc)
> atmel_pmecc_resume(nc->pmecc);
>
And yet I thought I correctly tested this code...

:(

>>   list_for_each_entry(nand, >chips, node) {
>>   int i;
>>
>> diff --git a/drivers/mtd/nand/atmel/pmecc.c b/drivers/mtd/nand/atmel/pmecc.c
>> index 146af8218314..ff09c0f25dd4 100644
>> --- a/drivers/mtd/nand/atmel/pmecc.c
>> +++ b/drivers/mtd/nand/atmel/pmecc.c
>> @@ -765,6 +765,12 @@ void atmel_pmecc_get_generated_eccbytes(struct 
>> atmel_pmecc_user *user,
>>  }
>>  EXPORT_SYMBOL_GPL(atmel_pmecc_get_generated_eccbytes);
>>
>> +void atmel_pmecc_reset(struct atmel_pmecc *pmecc)
>> +{
>> + writel(PMECC_CTRL_RST, pmecc->regs.base + ATMEL_PMECC_CTRL);
>> + writel(PMECC_CTRL_DISABLE, pmecc->regs.base + ATMEL_PMECC_CTRL);
>> +}
>
> It's not used outside of this file, so it should have a static
> specifier. Anyway, I wonder why you don't expose atmel_pmecc_reset()
> directly instead of creating this atmel_pmecc_resume() wrapper.

I will fix this...

-- 
Romain Izard


Re: [PATCH v3 4/8] mtd: nand: atmel: Avoid ECC errors when leaving backup mode

2017-09-27 Thread Romain Izard
2017-09-27 17:08 GMT+02:00 Boris Brezillon :
> On Wed, 27 Sep 2017 10:35:51 +0200
> Romain Izard  wrote:
>
>> During backup mode, the contents of all registers will be cleared as the
>> SoC will be completely powered down. For a product that boots on NAND
>> Flash memory, the bootloader will obviously use the related controller
>> to read the Flash and correct any detected error in the memory, before
>> handling back control to the kernel's resuming entry point.
>>
>> But it does not clean the NAND controller registers after use and on its
>> side the kernel driver expects the error locator to be powered down and
>> in a clean state. Add a resume hook for the PMECC error locator, and
>> reset its registers.
>>
>> Signed-off-by: Romain Izard 
>> ---
>> Change in v3:
>> * keep the PMECC disabled when not in use, and use atmel_pmecc_resume to
>>   reset the controller after the bootloader has left it enabled.
>>
>>  drivers/mtd/nand/atmel/nand-controller.c |  3 +++
>>  drivers/mtd/nand/atmel/pmecc.c   | 22 ++
>>  drivers/mtd/nand/atmel/pmecc.h   |  1 +
>>  3 files changed, 18 insertions(+), 8 deletions(-)
>>
>> diff --git a/drivers/mtd/nand/atmel/nand-controller.c 
>> b/drivers/mtd/nand/atmel/nand-controller.c
>> index f25eca79f4e5..86c2199380c2 100644
>> --- a/drivers/mtd/nand/atmel/nand-controller.c
>> +++ b/drivers/mtd/nand/atmel/nand-controller.c
>> @@ -2530,6 +2530,9 @@ static __maybe_unused int 
>> atmel_nand_controller_resume(struct device *dev)
>>   struct atmel_nand_controller *nc = dev_get_drvdata(dev);
>>   struct atmel_nand *nand;
>>
>> + if (nand->pmecc)
>> + atmel_pmecc_resume(nand->pmecc);
>> +
>
> nand is used uninitialized here, and atmel_pmecc_resume() should be
> passed a atmel_pmecc object not a atmel_pmecc_user.
>
> if (nc->pmecc)
> atmel_pmecc_resume(nc->pmecc);
>
And yet I thought I correctly tested this code...

:(

>>   list_for_each_entry(nand, >chips, node) {
>>   int i;
>>
>> diff --git a/drivers/mtd/nand/atmel/pmecc.c b/drivers/mtd/nand/atmel/pmecc.c
>> index 146af8218314..ff09c0f25dd4 100644
>> --- a/drivers/mtd/nand/atmel/pmecc.c
>> +++ b/drivers/mtd/nand/atmel/pmecc.c
>> @@ -765,6 +765,12 @@ void atmel_pmecc_get_generated_eccbytes(struct 
>> atmel_pmecc_user *user,
>>  }
>>  EXPORT_SYMBOL_GPL(atmel_pmecc_get_generated_eccbytes);
>>
>> +void atmel_pmecc_reset(struct atmel_pmecc *pmecc)
>> +{
>> + writel(PMECC_CTRL_RST, pmecc->regs.base + ATMEL_PMECC_CTRL);
>> + writel(PMECC_CTRL_DISABLE, pmecc->regs.base + ATMEL_PMECC_CTRL);
>> +}
>
> It's not used outside of this file, so it should have a static
> specifier. Anyway, I wonder why you don't expose atmel_pmecc_reset()
> directly instead of creating this atmel_pmecc_resume() wrapper.

I will fix this...

-- 
Romain Izard


[PATCH v3 3/8] clk: at91: pmc: Support backup for programmable clocks

2017-09-27 Thread Romain Izard
From: Romain Izard <romain.iz...@mobile-devices.fr>

When an AT91 programmable clock is declared in the device tree, register
it into the Power Management Controller driver. On entering suspend mode,
the driver saves and restores the Programmable Clock registers to support
the backup mode for these clocks.

Signed-off-by: Romain Izard <romain.izard@gmail.com>
Acked-by: Nicolas Ferre <nicolas.fe...@microchip.com>
---
Changes in v2:
* register PCKs on clock startup
Chnages in v3:
* improve comments on hanling 0 in pmc_register_id and pmc_register_pck
* declare local variables earlier for checkpatch

 drivers/clk/at91/clk-programmable.c |  2 ++
 drivers/clk/at91/pmc.c  | 35 +++
 drivers/clk/at91/pmc.h  |  2 ++
 3 files changed, 39 insertions(+)

diff --git a/drivers/clk/at91/clk-programmable.c 
b/drivers/clk/at91/clk-programmable.c
index 85a449cf61e3..0e6aab1252fc 100644
--- a/drivers/clk/at91/clk-programmable.c
+++ b/drivers/clk/at91/clk-programmable.c
@@ -204,6 +204,8 @@ at91_clk_register_programmable(struct regmap *regmap,
if (ret) {
kfree(prog);
hw = ERR_PTR(ret);
+   } else {
+   pmc_register_pck(id);
}
 
return hw;
diff --git a/drivers/clk/at91/pmc.c b/drivers/clk/at91/pmc.c
index 07dc2861ad3f..1fa27f4ea538 100644
--- a/drivers/clk/at91/pmc.c
+++ b/drivers/clk/at91/pmc.c
@@ -22,6 +22,7 @@
 #include "pmc.h"
 
 #define PMC_MAX_IDS 128
+#define PMC_MAX_PCKS 8
 
 int of_at91_get_clk_range(struct device_node *np, const char *propname,
  struct clk_range *range)
@@ -50,6 +51,7 @@ EXPORT_SYMBOL_GPL(of_at91_get_clk_range);
 static struct regmap *pmcreg;
 
 static u8 registered_ids[PMC_MAX_IDS];
+static u8 registered_pcks[PMC_MAX_PCKS];
 
 static struct
 {
@@ -66,8 +68,13 @@ static struct
u32 pcr[PMC_MAX_IDS];
u32 audio_pll0;
u32 audio_pll1;
+   u32 pckr[PMC_MAX_PCKS];
 } pmc_cache;
 
+/*
+ * As Peripheral ID 0 is invalid on AT91 chips, the identifier is stored
+ * without alteration in the table, and 0 is for unused clocks.
+ */
 void pmc_register_id(u8 id)
 {
int i;
@@ -82,9 +89,28 @@ void pmc_register_id(u8 id)
}
 }
 
+/*
+ * As Programmable Clock 0 is valid on AT91 chips, there is an offset
+ * of 1 between the stored value and the real clock ID.
+ */
+void pmc_register_pck(u8 pck)
+{
+   int i;
+
+   for (i = 0; i < PMC_MAX_PCKS; i++) {
+   if (registered_pcks[i] == 0) {
+   registered_pcks[i] = pck + 1;
+   break;
+   }
+   if (registered_pcks[i] == (pck + 1))
+   break;
+   }
+}
+
 static int pmc_suspend(void)
 {
int i;
+   u8 num;
 
regmap_read(pmcreg, AT91_PMC_SCSR, _cache.scsr);
regmap_read(pmcreg, AT91_PMC_PCSR, _cache.pcsr0);
@@ -103,6 +129,10 @@ static int pmc_suspend(void)
regmap_read(pmcreg, AT91_PMC_PCR,
_cache.pcr[registered_ids[i]]);
}
+   for (i = 0; registered_pcks[i]; i++) {
+   num = registered_pcks[i] - 1;
+   regmap_read(pmcreg, AT91_PMC_PCKR(num), _cache.pckr[num]);
+   }
 
return 0;
 }
@@ -119,6 +149,7 @@ static bool pmc_ready(unsigned int mask)
 static void pmc_resume(void)
 {
int i;
+   u8 num;
u32 tmp;
u32 mask = AT91_PMC_MCKRDY | AT91_PMC_LOCKA;
 
@@ -143,6 +174,10 @@ static void pmc_resume(void)
 pmc_cache.pcr[registered_ids[i]] |
 AT91_PMC_PCR_CMD);
}
+   for (i = 0; registered_pcks[i]; i++) {
+   num = registered_pcks[i] - 1;
+   regmap_write(pmcreg, AT91_PMC_PCKR(num), pmc_cache.pckr[num]);
+   }
 
if (pmc_cache.uckr & AT91_PMC_UPLLEN)
mask |= AT91_PMC_LOCKU;
diff --git a/drivers/clk/at91/pmc.h b/drivers/clk/at91/pmc.h
index 858e8ef7e8db..d22b1fa9ecdc 100644
--- a/drivers/clk/at91/pmc.h
+++ b/drivers/clk/at91/pmc.h
@@ -31,8 +31,10 @@ int of_at91_get_clk_range(struct device_node *np, const char 
*propname,
 
 #ifdef CONFIG_PM
 void pmc_register_id(u8 id);
+void pmc_register_pck(u8 pck);
 #else
 static inline void pmc_register_id(u8 id) {}
+static inline void pmc_register_pck(u8 pck) {}
 #endif
 
 #endif /* __PMC_H_ */
-- 
2.11.0



[PATCH v3 3/8] clk: at91: pmc: Support backup for programmable clocks

2017-09-27 Thread Romain Izard
From: Romain Izard 

When an AT91 programmable clock is declared in the device tree, register
it into the Power Management Controller driver. On entering suspend mode,
the driver saves and restores the Programmable Clock registers to support
the backup mode for these clocks.

Signed-off-by: Romain Izard 
Acked-by: Nicolas Ferre 
---
Changes in v2:
* register PCKs on clock startup
Chnages in v3:
* improve comments on hanling 0 in pmc_register_id and pmc_register_pck
* declare local variables earlier for checkpatch

 drivers/clk/at91/clk-programmable.c |  2 ++
 drivers/clk/at91/pmc.c  | 35 +++
 drivers/clk/at91/pmc.h  |  2 ++
 3 files changed, 39 insertions(+)

diff --git a/drivers/clk/at91/clk-programmable.c 
b/drivers/clk/at91/clk-programmable.c
index 85a449cf61e3..0e6aab1252fc 100644
--- a/drivers/clk/at91/clk-programmable.c
+++ b/drivers/clk/at91/clk-programmable.c
@@ -204,6 +204,8 @@ at91_clk_register_programmable(struct regmap *regmap,
if (ret) {
kfree(prog);
hw = ERR_PTR(ret);
+   } else {
+   pmc_register_pck(id);
}
 
return hw;
diff --git a/drivers/clk/at91/pmc.c b/drivers/clk/at91/pmc.c
index 07dc2861ad3f..1fa27f4ea538 100644
--- a/drivers/clk/at91/pmc.c
+++ b/drivers/clk/at91/pmc.c
@@ -22,6 +22,7 @@
 #include "pmc.h"
 
 #define PMC_MAX_IDS 128
+#define PMC_MAX_PCKS 8
 
 int of_at91_get_clk_range(struct device_node *np, const char *propname,
  struct clk_range *range)
@@ -50,6 +51,7 @@ EXPORT_SYMBOL_GPL(of_at91_get_clk_range);
 static struct regmap *pmcreg;
 
 static u8 registered_ids[PMC_MAX_IDS];
+static u8 registered_pcks[PMC_MAX_PCKS];
 
 static struct
 {
@@ -66,8 +68,13 @@ static struct
u32 pcr[PMC_MAX_IDS];
u32 audio_pll0;
u32 audio_pll1;
+   u32 pckr[PMC_MAX_PCKS];
 } pmc_cache;
 
+/*
+ * As Peripheral ID 0 is invalid on AT91 chips, the identifier is stored
+ * without alteration in the table, and 0 is for unused clocks.
+ */
 void pmc_register_id(u8 id)
 {
int i;
@@ -82,9 +89,28 @@ void pmc_register_id(u8 id)
}
 }
 
+/*
+ * As Programmable Clock 0 is valid on AT91 chips, there is an offset
+ * of 1 between the stored value and the real clock ID.
+ */
+void pmc_register_pck(u8 pck)
+{
+   int i;
+
+   for (i = 0; i < PMC_MAX_PCKS; i++) {
+   if (registered_pcks[i] == 0) {
+   registered_pcks[i] = pck + 1;
+   break;
+   }
+   if (registered_pcks[i] == (pck + 1))
+   break;
+   }
+}
+
 static int pmc_suspend(void)
 {
int i;
+   u8 num;
 
regmap_read(pmcreg, AT91_PMC_SCSR, _cache.scsr);
regmap_read(pmcreg, AT91_PMC_PCSR, _cache.pcsr0);
@@ -103,6 +129,10 @@ static int pmc_suspend(void)
regmap_read(pmcreg, AT91_PMC_PCR,
_cache.pcr[registered_ids[i]]);
}
+   for (i = 0; registered_pcks[i]; i++) {
+   num = registered_pcks[i] - 1;
+   regmap_read(pmcreg, AT91_PMC_PCKR(num), _cache.pckr[num]);
+   }
 
return 0;
 }
@@ -119,6 +149,7 @@ static bool pmc_ready(unsigned int mask)
 static void pmc_resume(void)
 {
int i;
+   u8 num;
u32 tmp;
u32 mask = AT91_PMC_MCKRDY | AT91_PMC_LOCKA;
 
@@ -143,6 +174,10 @@ static void pmc_resume(void)
 pmc_cache.pcr[registered_ids[i]] |
 AT91_PMC_PCR_CMD);
}
+   for (i = 0; registered_pcks[i]; i++) {
+   num = registered_pcks[i] - 1;
+   regmap_write(pmcreg, AT91_PMC_PCKR(num), pmc_cache.pckr[num]);
+   }
 
if (pmc_cache.uckr & AT91_PMC_UPLLEN)
mask |= AT91_PMC_LOCKU;
diff --git a/drivers/clk/at91/pmc.h b/drivers/clk/at91/pmc.h
index 858e8ef7e8db..d22b1fa9ecdc 100644
--- a/drivers/clk/at91/pmc.h
+++ b/drivers/clk/at91/pmc.h
@@ -31,8 +31,10 @@ int of_at91_get_clk_range(struct device_node *np, const char 
*propname,
 
 #ifdef CONFIG_PM
 void pmc_register_id(u8 id);
+void pmc_register_pck(u8 pck);
 #else
 static inline void pmc_register_id(u8 id) {}
+static inline void pmc_register_pck(u8 pck) {}
 #endif
 
 #endif /* __PMC_H_ */
-- 
2.11.0



[PATCH v3 2/8] clk: at91: pmc: Save SCSR during suspend

2017-09-27 Thread Romain Izard
The contents of the System Clock Status Register (SCSR) needs to be
restored into the System Clock Enable Register (SCER).

As the bootloader will restore some clocks by itself, the issue can be
missed as only the USB controller, the LCD controller, the Image Sensor
controller and the programmable clocks will be impacted.

Fix the obvious typo in the suspend/resume code, as the IMR register
does not need to be saved twice.

Signed-off-by: Romain Izard <romain.izard@gmail.com>
Acked-by: Nicolas Ferre <nicolas.fe...@microchip.com>
---
 drivers/clk/at91/pmc.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/drivers/clk/at91/pmc.c b/drivers/clk/at91/pmc.c
index 5c2b26de303e..07dc2861ad3f 100644
--- a/drivers/clk/at91/pmc.c
+++ b/drivers/clk/at91/pmc.c
@@ -86,7 +86,7 @@ static int pmc_suspend(void)
 {
int i;
 
-   regmap_read(pmcreg, AT91_PMC_IMR, _cache.scsr);
+   regmap_read(pmcreg, AT91_PMC_SCSR, _cache.scsr);
regmap_read(pmcreg, AT91_PMC_PCSR, _cache.pcsr0);
regmap_read(pmcreg, AT91_CKGR_UCKR, _cache.uckr);
regmap_read(pmcreg, AT91_CKGR_MOR, _cache.mor);
@@ -129,7 +129,7 @@ static void pmc_resume(void)
if (pmc_cache.pllar != tmp)
pr_warn("PLLAR was not configured properly by the firmware\n");
 
-   regmap_write(pmcreg, AT91_PMC_IMR, pmc_cache.scsr);
+   regmap_write(pmcreg, AT91_PMC_SCER, pmc_cache.scsr);
regmap_write(pmcreg, AT91_PMC_PCER, pmc_cache.pcsr0);
regmap_write(pmcreg, AT91_CKGR_UCKR, pmc_cache.uckr);
regmap_write(pmcreg, AT91_CKGR_MOR, pmc_cache.mor);
-- 
2.11.0



[PATCH v3 2/8] clk: at91: pmc: Save SCSR during suspend

2017-09-27 Thread Romain Izard
The contents of the System Clock Status Register (SCSR) needs to be
restored into the System Clock Enable Register (SCER).

As the bootloader will restore some clocks by itself, the issue can be
missed as only the USB controller, the LCD controller, the Image Sensor
controller and the programmable clocks will be impacted.

Fix the obvious typo in the suspend/resume code, as the IMR register
does not need to be saved twice.

Signed-off-by: Romain Izard 
Acked-by: Nicolas Ferre 
---
 drivers/clk/at91/pmc.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/drivers/clk/at91/pmc.c b/drivers/clk/at91/pmc.c
index 5c2b26de303e..07dc2861ad3f 100644
--- a/drivers/clk/at91/pmc.c
+++ b/drivers/clk/at91/pmc.c
@@ -86,7 +86,7 @@ static int pmc_suspend(void)
 {
int i;
 
-   regmap_read(pmcreg, AT91_PMC_IMR, _cache.scsr);
+   regmap_read(pmcreg, AT91_PMC_SCSR, _cache.scsr);
regmap_read(pmcreg, AT91_PMC_PCSR, _cache.pcsr0);
regmap_read(pmcreg, AT91_CKGR_UCKR, _cache.uckr);
regmap_read(pmcreg, AT91_CKGR_MOR, _cache.mor);
@@ -129,7 +129,7 @@ static void pmc_resume(void)
if (pmc_cache.pllar != tmp)
pr_warn("PLLAR was not configured properly by the firmware\n");
 
-   regmap_write(pmcreg, AT91_PMC_IMR, pmc_cache.scsr);
+   regmap_write(pmcreg, AT91_PMC_SCER, pmc_cache.scsr);
regmap_write(pmcreg, AT91_PMC_PCER, pmc_cache.pcsr0);
regmap_write(pmcreg, AT91_CKGR_UCKR, pmc_cache.uckr);
regmap_write(pmcreg, AT91_CKGR_MOR, pmc_cache.mor);
-- 
2.11.0



[PATCH v3 0/8] Various patches for SAMA5D2 backup mode

2017-09-27 Thread Romain Izard
While the core of the backup mode for SAMA5D2 has been integrated in
v4.13, it is far from complete. Individual controllers in the chip have
drivers that do not support the reset of the registers during suspend,
and they need to be adapted to handle it.

The first patch uses the clock wakeup code from the prototype backup
mode instead of the version integrated in the mainline, as the mainline
version is not stable. During a test loop with two-second backup
suspend, the mainline version will hang in less than one day, whereas
the prototype version has been running the same test for more than a
week without hanging.

While all these patches are provided in a series, the clock, mtd,
usb, pwm and mfd patch do not depend on each other.

Changes in v2:
* drop the IIO patch duplicating existing code
* determine the number of programmable clocks to save dynamically
* declare a required local variable in the tty/serial patch

Changes in v3:
* drop dev_printk changes for PMECC
* rework the resume code for PMECC
* improve comments on PMC clock handling

Romain Izard (8):
  clk: at91: pmc: Wait for clocks when resuming
  clk: at91: pmc: Save SCSR during suspend
  clk: at91: pmc: Support backup for programmable clocks
  mtd: nand: atmel: Avoid ECC errors when leaving backup mode
  ehci-atmel: Power down during suspend is normal
  pwm: atmel-tcb: Support backup mode
  atmel_flexcom: Support backup mode
  tty/serial: atmel: Prevent a warning on suspend

 drivers/clk/at91/clk-programmable.c  |  2 +
 drivers/clk/at91/pmc.c   | 63 ++-
 drivers/clk/at91/pmc.h   |  2 +
 drivers/mfd/atmel-flexcom.c  | 65 
 drivers/mtd/nand/atmel/nand-controller.c |  3 ++
 drivers/mtd/nand/atmel/pmecc.c   | 22 +++
 drivers/mtd/nand/atmel/pmecc.h   |  1 +
 drivers/pwm/pwm-atmel-tcb.c  | 63 ++-
 drivers/tty/serial/atmel_serial.c| 13 +++
 drivers/usb/host/ehci-atmel.c|  3 +-
 10 files changed, 201 insertions(+), 36 deletions(-)

-- 
2.11.0



[PATCH v3 0/8] Various patches for SAMA5D2 backup mode

2017-09-27 Thread Romain Izard
While the core of the backup mode for SAMA5D2 has been integrated in
v4.13, it is far from complete. Individual controllers in the chip have
drivers that do not support the reset of the registers during suspend,
and they need to be adapted to handle it.

The first patch uses the clock wakeup code from the prototype backup
mode instead of the version integrated in the mainline, as the mainline
version is not stable. During a test loop with two-second backup
suspend, the mainline version will hang in less than one day, whereas
the prototype version has been running the same test for more than a
week without hanging.

While all these patches are provided in a series, the clock, mtd,
usb, pwm and mfd patch do not depend on each other.

Changes in v2:
* drop the IIO patch duplicating existing code
* determine the number of programmable clocks to save dynamically
* declare a required local variable in the tty/serial patch

Changes in v3:
* drop dev_printk changes for PMECC
* rework the resume code for PMECC
* improve comments on PMC clock handling

Romain Izard (8):
  clk: at91: pmc: Wait for clocks when resuming
  clk: at91: pmc: Save SCSR during suspend
  clk: at91: pmc: Support backup for programmable clocks
  mtd: nand: atmel: Avoid ECC errors when leaving backup mode
  ehci-atmel: Power down during suspend is normal
  pwm: atmel-tcb: Support backup mode
  atmel_flexcom: Support backup mode
  tty/serial: atmel: Prevent a warning on suspend

 drivers/clk/at91/clk-programmable.c  |  2 +
 drivers/clk/at91/pmc.c   | 63 ++-
 drivers/clk/at91/pmc.h   |  2 +
 drivers/mfd/atmel-flexcom.c  | 65 
 drivers/mtd/nand/atmel/nand-controller.c |  3 ++
 drivers/mtd/nand/atmel/pmecc.c   | 22 +++
 drivers/mtd/nand/atmel/pmecc.h   |  1 +
 drivers/pwm/pwm-atmel-tcb.c  | 63 ++-
 drivers/tty/serial/atmel_serial.c| 13 +++
 drivers/usb/host/ehci-atmel.c|  3 +-
 10 files changed, 201 insertions(+), 36 deletions(-)

-- 
2.11.0



[PATCH v3 5/8] ehci-atmel: Power down during suspend is normal

2017-09-27 Thread Romain Izard
When an Atmel SoC is suspended with the backup mode, the USB bus will be
powered down. As this is expected, do not return an error to the driver
core when ehci_resume detects it.

Signed-off-by: Romain Izard <romain.izard@gmail.com>
Acked-by: Nicolas Ferre <nicolas.fe...@microchip.com>
---
 drivers/usb/host/ehci-atmel.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/drivers/usb/host/ehci-atmel.c b/drivers/usb/host/ehci-atmel.c
index 7440722bfbf0..2a8b9bdc0e57 100644
--- a/drivers/usb/host/ehci-atmel.c
+++ b/drivers/usb/host/ehci-atmel.c
@@ -205,7 +205,8 @@ static int __maybe_unused ehci_atmel_drv_resume(struct 
device *dev)
struct atmel_ehci_priv *atmel_ehci = hcd_to_atmel_ehci_priv(hcd);
 
atmel_start_clock(atmel_ehci);
-   return ehci_resume(hcd, false);
+   ehci_resume(hcd, false);
+   return 0;
 }
 
 #ifdef CONFIG_OF
-- 
2.11.0



[PATCH v3 5/8] ehci-atmel: Power down during suspend is normal

2017-09-27 Thread Romain Izard
When an Atmel SoC is suspended with the backup mode, the USB bus will be
powered down. As this is expected, do not return an error to the driver
core when ehci_resume detects it.

Signed-off-by: Romain Izard 
Acked-by: Nicolas Ferre 
---
 drivers/usb/host/ehci-atmel.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/drivers/usb/host/ehci-atmel.c b/drivers/usb/host/ehci-atmel.c
index 7440722bfbf0..2a8b9bdc0e57 100644
--- a/drivers/usb/host/ehci-atmel.c
+++ b/drivers/usb/host/ehci-atmel.c
@@ -205,7 +205,8 @@ static int __maybe_unused ehci_atmel_drv_resume(struct 
device *dev)
struct atmel_ehci_priv *atmel_ehci = hcd_to_atmel_ehci_priv(hcd);
 
atmel_start_clock(atmel_ehci);
-   return ehci_resume(hcd, false);
+   ehci_resume(hcd, false);
+   return 0;
 }
 
 #ifdef CONFIG_OF
-- 
2.11.0



[PATCH v3 4/8] mtd: nand: atmel: Avoid ECC errors when leaving backup mode

2017-09-27 Thread Romain Izard
During backup mode, the contents of all registers will be cleared as the
SoC will be completely powered down. For a product that boots on NAND
Flash memory, the bootloader will obviously use the related controller
to read the Flash and correct any detected error in the memory, before
handling back control to the kernel's resuming entry point.

But it does not clean the NAND controller registers after use and on its
side the kernel driver expects the error locator to be powered down and
in a clean state. Add a resume hook for the PMECC error locator, and
reset its registers.

Signed-off-by: Romain Izard <romain.izard@gmail.com>
---
Change in v3:
* keep the PMECC disabled when not in use, and use atmel_pmecc_resume to
  reset the controller after the bootloader has left it enabled.

 drivers/mtd/nand/atmel/nand-controller.c |  3 +++
 drivers/mtd/nand/atmel/pmecc.c   | 22 ++
 drivers/mtd/nand/atmel/pmecc.h   |  1 +
 3 files changed, 18 insertions(+), 8 deletions(-)

diff --git a/drivers/mtd/nand/atmel/nand-controller.c 
b/drivers/mtd/nand/atmel/nand-controller.c
index f25eca79f4e5..86c2199380c2 100644
--- a/drivers/mtd/nand/atmel/nand-controller.c
+++ b/drivers/mtd/nand/atmel/nand-controller.c
@@ -2530,6 +2530,9 @@ static __maybe_unused int 
atmel_nand_controller_resume(struct device *dev)
struct atmel_nand_controller *nc = dev_get_drvdata(dev);
struct atmel_nand *nand;
 
+   if (nand->pmecc)
+   atmel_pmecc_resume(nand->pmecc);
+
list_for_each_entry(nand, >chips, node) {
int i;
 
diff --git a/drivers/mtd/nand/atmel/pmecc.c b/drivers/mtd/nand/atmel/pmecc.c
index 146af8218314..ff09c0f25dd4 100644
--- a/drivers/mtd/nand/atmel/pmecc.c
+++ b/drivers/mtd/nand/atmel/pmecc.c
@@ -765,6 +765,12 @@ void atmel_pmecc_get_generated_eccbytes(struct 
atmel_pmecc_user *user,
 }
 EXPORT_SYMBOL_GPL(atmel_pmecc_get_generated_eccbytes);
 
+void atmel_pmecc_reset(struct atmel_pmecc *pmecc)
+{
+   writel(PMECC_CTRL_RST, pmecc->regs.base + ATMEL_PMECC_CTRL);
+   writel(PMECC_CTRL_DISABLE, pmecc->regs.base + ATMEL_PMECC_CTRL);
+}
+
 int atmel_pmecc_enable(struct atmel_pmecc_user *user, int op)
 {
struct atmel_pmecc *pmecc = user->pmecc;
@@ -797,14 +803,17 @@ EXPORT_SYMBOL_GPL(atmel_pmecc_enable);
 
 void atmel_pmecc_disable(struct atmel_pmecc_user *user)
 {
-   struct atmel_pmecc *pmecc = user->pmecc;
-
-   writel(PMECC_CTRL_RST, pmecc->regs.base + ATMEL_PMECC_CTRL);
-   writel(PMECC_CTRL_DISABLE, pmecc->regs.base + ATMEL_PMECC_CTRL);
+   atmel_pmecc_reset(user->pmecc);
mutex_unlock(>pmecc->lock);
 }
 EXPORT_SYMBOL_GPL(atmel_pmecc_disable);
 
+void atmel_pmecc_resume(struct atmel_pmecc_user *user)
+{
+   atmel_pmecc_reset(user->pmecc);
+}
+EXPORT_SYMBOL_GPL(atmel_pmecc_resume);
+
 int atmel_pmecc_wait_rdy(struct atmel_pmecc_user *user)
 {
struct atmel_pmecc *pmecc = user->pmecc;
@@ -855,10 +864,7 @@ static struct atmel_pmecc *atmel_pmecc_create(struct 
platform_device *pdev,
 
/* Disable all interrupts before registering the PMECC handler. */
writel(0x, pmecc->regs.base + ATMEL_PMECC_IDR);
-
-   /* Reset the ECC engine */
-   writel(PMECC_CTRL_RST, pmecc->regs.base + ATMEL_PMECC_CTRL);
-   writel(PMECC_CTRL_DISABLE, pmecc->regs.base + ATMEL_PMECC_CTRL);
+   atmel_pmecc_reset(pmecc);
 
return pmecc;
 }
diff --git a/drivers/mtd/nand/atmel/pmecc.h b/drivers/mtd/nand/atmel/pmecc.h
index a8ddbfca2ea5..488a90f1965d 100644
--- a/drivers/mtd/nand/atmel/pmecc.h
+++ b/drivers/mtd/nand/atmel/pmecc.h
@@ -63,6 +63,7 @@ void atmel_pmecc_destroy_user(struct atmel_pmecc_user *user);
 
 int atmel_pmecc_enable(struct atmel_pmecc_user *user, int op);
 void atmel_pmecc_disable(struct atmel_pmecc_user *user);
+void atmel_pmecc_resume(struct atmel_pmecc_user *user);
 int atmel_pmecc_wait_rdy(struct atmel_pmecc_user *user);
 int atmel_pmecc_correct_sector(struct atmel_pmecc_user *user, int sector,
   void *data, void *ecc);
-- 
2.11.0



[PATCH v3 4/8] mtd: nand: atmel: Avoid ECC errors when leaving backup mode

2017-09-27 Thread Romain Izard
During backup mode, the contents of all registers will be cleared as the
SoC will be completely powered down. For a product that boots on NAND
Flash memory, the bootloader will obviously use the related controller
to read the Flash and correct any detected error in the memory, before
handling back control to the kernel's resuming entry point.

But it does not clean the NAND controller registers after use and on its
side the kernel driver expects the error locator to be powered down and
in a clean state. Add a resume hook for the PMECC error locator, and
reset its registers.

Signed-off-by: Romain Izard 
---
Change in v3:
* keep the PMECC disabled when not in use, and use atmel_pmecc_resume to
  reset the controller after the bootloader has left it enabled.

 drivers/mtd/nand/atmel/nand-controller.c |  3 +++
 drivers/mtd/nand/atmel/pmecc.c   | 22 ++
 drivers/mtd/nand/atmel/pmecc.h   |  1 +
 3 files changed, 18 insertions(+), 8 deletions(-)

diff --git a/drivers/mtd/nand/atmel/nand-controller.c 
b/drivers/mtd/nand/atmel/nand-controller.c
index f25eca79f4e5..86c2199380c2 100644
--- a/drivers/mtd/nand/atmel/nand-controller.c
+++ b/drivers/mtd/nand/atmel/nand-controller.c
@@ -2530,6 +2530,9 @@ static __maybe_unused int 
atmel_nand_controller_resume(struct device *dev)
struct atmel_nand_controller *nc = dev_get_drvdata(dev);
struct atmel_nand *nand;
 
+   if (nand->pmecc)
+   atmel_pmecc_resume(nand->pmecc);
+
list_for_each_entry(nand, >chips, node) {
int i;
 
diff --git a/drivers/mtd/nand/atmel/pmecc.c b/drivers/mtd/nand/atmel/pmecc.c
index 146af8218314..ff09c0f25dd4 100644
--- a/drivers/mtd/nand/atmel/pmecc.c
+++ b/drivers/mtd/nand/atmel/pmecc.c
@@ -765,6 +765,12 @@ void atmel_pmecc_get_generated_eccbytes(struct 
atmel_pmecc_user *user,
 }
 EXPORT_SYMBOL_GPL(atmel_pmecc_get_generated_eccbytes);
 
+void atmel_pmecc_reset(struct atmel_pmecc *pmecc)
+{
+   writel(PMECC_CTRL_RST, pmecc->regs.base + ATMEL_PMECC_CTRL);
+   writel(PMECC_CTRL_DISABLE, pmecc->regs.base + ATMEL_PMECC_CTRL);
+}
+
 int atmel_pmecc_enable(struct atmel_pmecc_user *user, int op)
 {
struct atmel_pmecc *pmecc = user->pmecc;
@@ -797,14 +803,17 @@ EXPORT_SYMBOL_GPL(atmel_pmecc_enable);
 
 void atmel_pmecc_disable(struct atmel_pmecc_user *user)
 {
-   struct atmel_pmecc *pmecc = user->pmecc;
-
-   writel(PMECC_CTRL_RST, pmecc->regs.base + ATMEL_PMECC_CTRL);
-   writel(PMECC_CTRL_DISABLE, pmecc->regs.base + ATMEL_PMECC_CTRL);
+   atmel_pmecc_reset(user->pmecc);
mutex_unlock(>pmecc->lock);
 }
 EXPORT_SYMBOL_GPL(atmel_pmecc_disable);
 
+void atmel_pmecc_resume(struct atmel_pmecc_user *user)
+{
+   atmel_pmecc_reset(user->pmecc);
+}
+EXPORT_SYMBOL_GPL(atmel_pmecc_resume);
+
 int atmel_pmecc_wait_rdy(struct atmel_pmecc_user *user)
 {
struct atmel_pmecc *pmecc = user->pmecc;
@@ -855,10 +864,7 @@ static struct atmel_pmecc *atmel_pmecc_create(struct 
platform_device *pdev,
 
/* Disable all interrupts before registering the PMECC handler. */
writel(0x, pmecc->regs.base + ATMEL_PMECC_IDR);
-
-   /* Reset the ECC engine */
-   writel(PMECC_CTRL_RST, pmecc->regs.base + ATMEL_PMECC_CTRL);
-   writel(PMECC_CTRL_DISABLE, pmecc->regs.base + ATMEL_PMECC_CTRL);
+   atmel_pmecc_reset(pmecc);
 
return pmecc;
 }
diff --git a/drivers/mtd/nand/atmel/pmecc.h b/drivers/mtd/nand/atmel/pmecc.h
index a8ddbfca2ea5..488a90f1965d 100644
--- a/drivers/mtd/nand/atmel/pmecc.h
+++ b/drivers/mtd/nand/atmel/pmecc.h
@@ -63,6 +63,7 @@ void atmel_pmecc_destroy_user(struct atmel_pmecc_user *user);
 
 int atmel_pmecc_enable(struct atmel_pmecc_user *user, int op);
 void atmel_pmecc_disable(struct atmel_pmecc_user *user);
+void atmel_pmecc_resume(struct atmel_pmecc_user *user);
 int atmel_pmecc_wait_rdy(struct atmel_pmecc_user *user);
 int atmel_pmecc_correct_sector(struct atmel_pmecc_user *user, int sector,
   void *data, void *ecc);
-- 
2.11.0



[PATCH v3 6/8] pwm: atmel-tcb: Support backup mode

2017-09-27 Thread Romain Izard
Save and restore registers for the PWM on suspend and resume, which
makes hibernation and backup modes possible.

Signed-off-by: Romain Izard <romain.izard@gmail.com>
Acked-by: Nicolas Ferre <nicolas.fe...@microchip.com>
---
 drivers/pwm/pwm-atmel-tcb.c | 63 +++--
 1 file changed, 61 insertions(+), 2 deletions(-)

diff --git a/drivers/pwm/pwm-atmel-tcb.c b/drivers/pwm/pwm-atmel-tcb.c
index 75db585a2a94..acd3ce8ecf3f 100644
--- a/drivers/pwm/pwm-atmel-tcb.c
+++ b/drivers/pwm/pwm-atmel-tcb.c
@@ -37,11 +37,20 @@ struct atmel_tcb_pwm_device {
unsigned period;/* PWM period expressed in clk cycles */
 };
 
+struct atmel_tcb_channel {
+   u32 enabled;
+   u32 cmr;
+   u32 ra;
+   u32 rb;
+   u32 rc;
+};
+
 struct atmel_tcb_pwm_chip {
struct pwm_chip chip;
spinlock_t lock;
struct atmel_tc *tc;
struct atmel_tcb_pwm_device *pwms[NPWM];
+   struct atmel_tcb_channel bkup[NPWM / 2];
 };
 
 static inline struct atmel_tcb_pwm_chip *to_tcb_chip(struct pwm_chip *chip)
@@ -175,12 +184,15 @@ static void atmel_tcb_pwm_disable(struct pwm_chip *chip, 
struct pwm_device *pwm)
 * Use software trigger to apply the new setting.
 * If both PWM devices in this group are disabled we stop the clock.
 */
-   if (!(cmr & (ATMEL_TC_ACPC | ATMEL_TC_BCPC)))
+   if (!(cmr & (ATMEL_TC_ACPC | ATMEL_TC_BCPC))) {
__raw_writel(ATMEL_TC_SWTRG | ATMEL_TC_CLKDIS,
 regs + ATMEL_TC_REG(group, CCR));
-   else
+   tcbpwmc->bkup[group].enabled = 1;
+   } else {
__raw_writel(ATMEL_TC_SWTRG, regs +
 ATMEL_TC_REG(group, CCR));
+   tcbpwmc->bkup[group].enabled = 0;
+   }
 
spin_unlock(>lock);
 }
@@ -263,6 +275,7 @@ static int atmel_tcb_pwm_enable(struct pwm_chip *chip, 
struct pwm_device *pwm)
/* Use software trigger to apply the new setting */
__raw_writel(ATMEL_TC_CLKEN | ATMEL_TC_SWTRG,
 regs + ATMEL_TC_REG(group, CCR));
+   tcbpwmc->bkup[group].enabled = 1;
spin_unlock(>lock);
return 0;
 }
@@ -445,10 +458,56 @@ static const struct of_device_id atmel_tcb_pwm_dt_ids[] = 
{
 };
 MODULE_DEVICE_TABLE(of, atmel_tcb_pwm_dt_ids);
 
+#ifdef CONFIG_PM_SLEEP
+static int atmel_tcb_pwm_suspend(struct device *dev)
+{
+   struct platform_device *pdev = to_platform_device(dev);
+   struct atmel_tcb_pwm_chip *tcbpwm = platform_get_drvdata(pdev);
+   void __iomem *base = tcbpwm->tc->regs;
+   int i;
+
+   for (i = 0; i < (NPWM / 2); i++) {
+   struct atmel_tcb_channel *chan = >bkup[i];
+
+   chan->cmr = readl(base + ATMEL_TC_REG(i, CMR));
+   chan->ra = readl(base + ATMEL_TC_REG(i, RA));
+   chan->rb = readl(base + ATMEL_TC_REG(i, RB));
+   chan->rc = readl(base + ATMEL_TC_REG(i, RC));
+   }
+   return 0;
+}
+
+static int atmel_tcb_pwm_resume(struct device *dev)
+{
+   struct platform_device *pdev = to_platform_device(dev);
+   struct atmel_tcb_pwm_chip *tcbpwm = platform_get_drvdata(pdev);
+   void __iomem *base = tcbpwm->tc->regs;
+   int i;
+
+   for (i = 0; i < (NPWM / 2); i++) {
+   struct atmel_tcb_channel *chan = >bkup[i];
+
+   writel(chan->cmr, base + ATMEL_TC_REG(i, CMR));
+   writel(chan->ra, base + ATMEL_TC_REG(i, RA));
+   writel(chan->rb, base + ATMEL_TC_REG(i, RB));
+   writel(chan->rc, base + ATMEL_TC_REG(i, RC));
+   if (chan->enabled) {
+   writel(ATMEL_TC_CLKEN | ATMEL_TC_SWTRG,
+   base + ATMEL_TC_REG(i, CCR));
+   }
+   }
+   return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(atmel_tcb_pwm_pm_ops, atmel_tcb_pwm_suspend,
+atmel_tcb_pwm_resume);
+
 static struct platform_driver atmel_tcb_pwm_driver = {
.driver = {
.name = "atmel-tcb-pwm",
.of_match_table = atmel_tcb_pwm_dt_ids,
+   .pm = _tcb_pwm_pm_ops,
},
.probe = atmel_tcb_pwm_probe,
.remove = atmel_tcb_pwm_remove,
-- 
2.11.0



[PATCH v3 6/8] pwm: atmel-tcb: Support backup mode

2017-09-27 Thread Romain Izard
Save and restore registers for the PWM on suspend and resume, which
makes hibernation and backup modes possible.

Signed-off-by: Romain Izard 
Acked-by: Nicolas Ferre 
---
 drivers/pwm/pwm-atmel-tcb.c | 63 +++--
 1 file changed, 61 insertions(+), 2 deletions(-)

diff --git a/drivers/pwm/pwm-atmel-tcb.c b/drivers/pwm/pwm-atmel-tcb.c
index 75db585a2a94..acd3ce8ecf3f 100644
--- a/drivers/pwm/pwm-atmel-tcb.c
+++ b/drivers/pwm/pwm-atmel-tcb.c
@@ -37,11 +37,20 @@ struct atmel_tcb_pwm_device {
unsigned period;/* PWM period expressed in clk cycles */
 };
 
+struct atmel_tcb_channel {
+   u32 enabled;
+   u32 cmr;
+   u32 ra;
+   u32 rb;
+   u32 rc;
+};
+
 struct atmel_tcb_pwm_chip {
struct pwm_chip chip;
spinlock_t lock;
struct atmel_tc *tc;
struct atmel_tcb_pwm_device *pwms[NPWM];
+   struct atmel_tcb_channel bkup[NPWM / 2];
 };
 
 static inline struct atmel_tcb_pwm_chip *to_tcb_chip(struct pwm_chip *chip)
@@ -175,12 +184,15 @@ static void atmel_tcb_pwm_disable(struct pwm_chip *chip, 
struct pwm_device *pwm)
 * Use software trigger to apply the new setting.
 * If both PWM devices in this group are disabled we stop the clock.
 */
-   if (!(cmr & (ATMEL_TC_ACPC | ATMEL_TC_BCPC)))
+   if (!(cmr & (ATMEL_TC_ACPC | ATMEL_TC_BCPC))) {
__raw_writel(ATMEL_TC_SWTRG | ATMEL_TC_CLKDIS,
 regs + ATMEL_TC_REG(group, CCR));
-   else
+   tcbpwmc->bkup[group].enabled = 1;
+   } else {
__raw_writel(ATMEL_TC_SWTRG, regs +
 ATMEL_TC_REG(group, CCR));
+   tcbpwmc->bkup[group].enabled = 0;
+   }
 
spin_unlock(>lock);
 }
@@ -263,6 +275,7 @@ static int atmel_tcb_pwm_enable(struct pwm_chip *chip, 
struct pwm_device *pwm)
/* Use software trigger to apply the new setting */
__raw_writel(ATMEL_TC_CLKEN | ATMEL_TC_SWTRG,
 regs + ATMEL_TC_REG(group, CCR));
+   tcbpwmc->bkup[group].enabled = 1;
spin_unlock(>lock);
return 0;
 }
@@ -445,10 +458,56 @@ static const struct of_device_id atmel_tcb_pwm_dt_ids[] = 
{
 };
 MODULE_DEVICE_TABLE(of, atmel_tcb_pwm_dt_ids);
 
+#ifdef CONFIG_PM_SLEEP
+static int atmel_tcb_pwm_suspend(struct device *dev)
+{
+   struct platform_device *pdev = to_platform_device(dev);
+   struct atmel_tcb_pwm_chip *tcbpwm = platform_get_drvdata(pdev);
+   void __iomem *base = tcbpwm->tc->regs;
+   int i;
+
+   for (i = 0; i < (NPWM / 2); i++) {
+   struct atmel_tcb_channel *chan = >bkup[i];
+
+   chan->cmr = readl(base + ATMEL_TC_REG(i, CMR));
+   chan->ra = readl(base + ATMEL_TC_REG(i, RA));
+   chan->rb = readl(base + ATMEL_TC_REG(i, RB));
+   chan->rc = readl(base + ATMEL_TC_REG(i, RC));
+   }
+   return 0;
+}
+
+static int atmel_tcb_pwm_resume(struct device *dev)
+{
+   struct platform_device *pdev = to_platform_device(dev);
+   struct atmel_tcb_pwm_chip *tcbpwm = platform_get_drvdata(pdev);
+   void __iomem *base = tcbpwm->tc->regs;
+   int i;
+
+   for (i = 0; i < (NPWM / 2); i++) {
+   struct atmel_tcb_channel *chan = >bkup[i];
+
+   writel(chan->cmr, base + ATMEL_TC_REG(i, CMR));
+   writel(chan->ra, base + ATMEL_TC_REG(i, RA));
+   writel(chan->rb, base + ATMEL_TC_REG(i, RB));
+   writel(chan->rc, base + ATMEL_TC_REG(i, RC));
+   if (chan->enabled) {
+   writel(ATMEL_TC_CLKEN | ATMEL_TC_SWTRG,
+   base + ATMEL_TC_REG(i, CCR));
+   }
+   }
+   return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(atmel_tcb_pwm_pm_ops, atmel_tcb_pwm_suspend,
+atmel_tcb_pwm_resume);
+
 static struct platform_driver atmel_tcb_pwm_driver = {
.driver = {
.name = "atmel-tcb-pwm",
.of_match_table = atmel_tcb_pwm_dt_ids,
+   .pm = _tcb_pwm_pm_ops,
},
.probe = atmel_tcb_pwm_probe,
.remove = atmel_tcb_pwm_remove,
-- 
2.11.0



[PATCH v3 8/8] tty/serial: atmel: Prevent a warning on suspend

2017-09-27 Thread Romain Izard
The atmel serial port driver reported the following warning on suspend:
atmel_usart f802.serial: ttyS1: Unable to drain transmitter

As the ATMEL_US_TXEMPTY status bit in ATMEL_US_CSR is always cleared
when the transmitter is disabled, we need to know the transmitter's
state to return the real fifo state. And as ATMEL_US_CR is write-only,
it is necessary to save the state of the transmitter in a local
variable, and update the variable when TXEN and TXDIS is written in
ATMEL_US_CR.

After those changes, atmel_tx_empty can return "empty" on suspend, the
warning in uart_suspend_port disappears, and suspending is 20ms shorter
for each enabled Atmel serial port.

Signed-off-by: Romain Izard <romain.izard@gmail.com>
Tested-by: Nicolas Ferre <nicolas.fe...@microchip.com>
Acked-by: Nicolas Ferre <nicolas.fe...@microchip.com>
Acked-by: Richard Genoud <richard.gen...@gmail.com>
---
 drivers/tty/serial/atmel_serial.c | 13 +
 1 file changed, 13 insertions(+)

diff --git a/drivers/tty/serial/atmel_serial.c 
b/drivers/tty/serial/atmel_serial.c
index 7551cab438ff..ce45b4ada0bf 100644
--- a/drivers/tty/serial/atmel_serial.c
+++ b/drivers/tty/serial/atmel_serial.c
@@ -171,6 +171,7 @@ struct atmel_uart_port {
boolhas_hw_timer;
struct timer_list   uart_timer;
 
+   booltx_stopped;
boolsuspended;
unsigned intpending;
unsigned intpending_status;
@@ -380,6 +381,10 @@ static int atmel_config_rs485(struct uart_port *port,
  */
 static u_int atmel_tx_empty(struct uart_port *port)
 {
+   struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
+
+   if (atmel_port->tx_stopped)
+   return TIOCSER_TEMT;
return (atmel_uart_readl(port, ATMEL_US_CSR) & ATMEL_US_TXEMPTY) ?
TIOCSER_TEMT :
0;
@@ -485,6 +490,7 @@ static void atmel_stop_tx(struct uart_port *port)
 * is fully transmitted.
 */
atmel_uart_writel(port, ATMEL_US_CR, ATMEL_US_TXDIS);
+   atmel_port->tx_stopped = true;
 
/* Disable interrupts */
atmel_uart_writel(port, ATMEL_US_IDR, atmel_port->tx_done_mask);
@@ -521,6 +527,7 @@ static void atmel_start_tx(struct uart_port *port)
 
/* re-enable the transmitter */
atmel_uart_writel(port, ATMEL_US_CR, ATMEL_US_TXEN);
+   atmel_port->tx_stopped = false;
 }
 
 /*
@@ -1866,6 +1873,7 @@ static int atmel_startup(struct uart_port *port)
atmel_uart_writel(port, ATMEL_US_CR, ATMEL_US_RSTSTA | ATMEL_US_RSTRX);
/* enable xmit & rcvr */
atmel_uart_writel(port, ATMEL_US_CR, ATMEL_US_TXEN | ATMEL_US_RXEN);
+   atmel_port->tx_stopped = false;
 
setup_timer(_port->uart_timer,
atmel_uart_timer_callback,
@@ -2122,6 +2130,7 @@ static void atmel_set_termios(struct uart_port *port, 
struct ktermios *termios,
 
/* disable receiver and transmitter */
atmel_uart_writel(port, ATMEL_US_CR, ATMEL_US_TXDIS | ATMEL_US_RXDIS);
+   atmel_port->tx_stopped = true;
 
/* mode */
if (port->rs485.flags & SER_RS485_ENABLED) {
@@ -2207,6 +2216,7 @@ static void atmel_set_termios(struct uart_port *port, 
struct ktermios *termios,
atmel_uart_writel(port, ATMEL_US_BRGR, quot);
atmel_uart_writel(port, ATMEL_US_CR, ATMEL_US_RSTSTA | ATMEL_US_RSTRX);
atmel_uart_writel(port, ATMEL_US_CR, ATMEL_US_TXEN | ATMEL_US_RXEN);
+   atmel_port->tx_stopped = false;
 
/* restore interrupts */
atmel_uart_writel(port, ATMEL_US_IER, imr);
@@ -2450,6 +2460,7 @@ static void atmel_console_write(struct console *co, const 
char *s, u_int count)
 
/* Make sure that tx path is actually able to send characters */
atmel_uart_writel(port, ATMEL_US_CR, ATMEL_US_TXEN);
+   atmel_port->tx_stopped = false;
 
uart_console_write(port, s, count, atmel_console_putchar);
 
@@ -2511,6 +2522,7 @@ static int __init atmel_console_setup(struct console *co, 
char *options)
 {
int ret;
struct uart_port *port = _ports[co->index].uart;
+   struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
int baud = 115200;
int bits = 8;
int parity = 'n';
@@ -2528,6 +2540,7 @@ static int __init atmel_console_setup(struct console *co, 
char *options)
atmel_uart_writel(port, ATMEL_US_IDR, -1);
atmel_uart_writel(port, ATMEL_US_CR, ATMEL_US_RSTSTA | ATMEL_US_RSTRX);
atmel_uart_writel(port, ATMEL_US_CR, ATMEL_US_TXEN | ATMEL_US_RXEN);
+   atmel_port->tx_stopped = false;
 
if (options)
uart_parse_options(options, , , , );
-- 
2.11.0



[PATCH v3 7/8] atmel_flexcom: Support backup mode

2017-09-27 Thread Romain Izard
The controller used by a flexcom module is configured at boot, and left
alone after this. As the configuration will be lost after backup mode,
restore the state of the flexcom driver on resume.

Signed-off-by: Romain Izard <romain.izard@gmail.com>
Acked-by: Nicolas Ferre <nicolas.fe...@microchip.com>
Tested-by: Nicolas Ferre <nicolas.fe...@microchip.com>
---
 drivers/mfd/atmel-flexcom.c | 65 ++---
 1 file changed, 50 insertions(+), 15 deletions(-)

diff --git a/drivers/mfd/atmel-flexcom.c b/drivers/mfd/atmel-flexcom.c
index 064bde9cff5a..ef1235c4a179 100644
--- a/drivers/mfd/atmel-flexcom.c
+++ b/drivers/mfd/atmel-flexcom.c
@@ -39,34 +39,44 @@
 #define FLEX_MR_OPMODE(opmode) (((opmode) << FLEX_MR_OPMODE_OFFSET) &  \
 FLEX_MR_OPMODE_MASK)
 
+struct atmel_flexcom {
+   void __iomem *base;
+   u32 opmode;
+   struct clk *clk;
+};
 
 static int atmel_flexcom_probe(struct platform_device *pdev)
 {
struct device_node *np = pdev->dev.of_node;
-   struct clk *clk;
struct resource *res;
-   void __iomem *base;
-   u32 opmode;
+   struct atmel_flexcom *afc;
int err;
+   u32 val;
+
+   afc = devm_kzalloc(>dev, sizeof(*afc), GFP_KERNEL);
+   if (!afc)
+   return -ENOMEM;
 
-   err = of_property_read_u32(np, "atmel,flexcom-mode", );
+   platform_set_drvdata(pdev, afc);
+
+   err = of_property_read_u32(np, "atmel,flexcom-mode", >opmode);
if (err)
return err;
 
-   if (opmode < ATMEL_FLEXCOM_MODE_USART ||
-   opmode > ATMEL_FLEXCOM_MODE_TWI)
+   if (afc->opmode < ATMEL_FLEXCOM_MODE_USART ||
+   afc->opmode > ATMEL_FLEXCOM_MODE_TWI)
return -EINVAL;
 
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-   base = devm_ioremap_resource(>dev, res);
-   if (IS_ERR(base))
-   return PTR_ERR(base);
+   afc->base = devm_ioremap_resource(>dev, res);
+   if (IS_ERR(afc->base))
+   return PTR_ERR(afc->base);
 
-   clk = devm_clk_get(>dev, NULL);
-   if (IS_ERR(clk))
-   return PTR_ERR(clk);
+   afc->clk = devm_clk_get(>dev, NULL);
+   if (IS_ERR(afc->clk))
+   return PTR_ERR(afc->clk);
 
-   err = clk_prepare_enable(clk);
+   err = clk_prepare_enable(afc->clk);
if (err)
return err;
 
@@ -76,9 +86,10 @@ static int atmel_flexcom_probe(struct platform_device *pdev)
 * inaccessible and are read as zero. Also the external I/O lines of the
 * Flexcom are muxed to reach the selected device.
 */
-   writel(FLEX_MR_OPMODE(opmode), base + FLEX_MR);
+   val = FLEX_MR_OPMODE(afc->opmode);
+   writel(val, afc->base + FLEX_MR);
 
-   clk_disable_unprepare(clk);
+   clk_disable_unprepare(afc->clk);
 
return devm_of_platform_populate(>dev);
 }
@@ -89,10 +100,34 @@ static const struct of_device_id atmel_flexcom_of_match[] 
= {
 };
 MODULE_DEVICE_TABLE(of, atmel_flexcom_of_match);
 
+#ifdef CONFIG_PM_SLEEP
+static int atmel_flexcom_resume(struct device *dev)
+{
+   struct atmel_flexcom *afc = dev_get_drvdata(dev);
+   int err;
+   u32 val;
+
+   err = clk_prepare_enable(afc->clk);
+   if (err)
+   return err;
+
+   val = FLEX_MR_OPMODE(afc->opmode),
+   writel(val, afc->base + FLEX_MR);
+
+   clk_disable_unprepare(afc->clk);
+
+   return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(atmel_flexcom_pm_ops, NULL,
+atmel_flexcom_resume);
+
 static struct platform_driver atmel_flexcom_driver = {
.probe  = atmel_flexcom_probe,
.driver = {
.name   = "atmel_flexcom",
+   .pm = _flexcom_pm_ops,
.of_match_table = atmel_flexcom_of_match,
},
 };
-- 
2.11.0



[PATCH v3 8/8] tty/serial: atmel: Prevent a warning on suspend

2017-09-27 Thread Romain Izard
The atmel serial port driver reported the following warning on suspend:
atmel_usart f802.serial: ttyS1: Unable to drain transmitter

As the ATMEL_US_TXEMPTY status bit in ATMEL_US_CSR is always cleared
when the transmitter is disabled, we need to know the transmitter's
state to return the real fifo state. And as ATMEL_US_CR is write-only,
it is necessary to save the state of the transmitter in a local
variable, and update the variable when TXEN and TXDIS is written in
ATMEL_US_CR.

After those changes, atmel_tx_empty can return "empty" on suspend, the
warning in uart_suspend_port disappears, and suspending is 20ms shorter
for each enabled Atmel serial port.

Signed-off-by: Romain Izard 
Tested-by: Nicolas Ferre 
Acked-by: Nicolas Ferre 
Acked-by: Richard Genoud 
---
 drivers/tty/serial/atmel_serial.c | 13 +
 1 file changed, 13 insertions(+)

diff --git a/drivers/tty/serial/atmel_serial.c 
b/drivers/tty/serial/atmel_serial.c
index 7551cab438ff..ce45b4ada0bf 100644
--- a/drivers/tty/serial/atmel_serial.c
+++ b/drivers/tty/serial/atmel_serial.c
@@ -171,6 +171,7 @@ struct atmel_uart_port {
boolhas_hw_timer;
struct timer_list   uart_timer;
 
+   booltx_stopped;
boolsuspended;
unsigned intpending;
unsigned intpending_status;
@@ -380,6 +381,10 @@ static int atmel_config_rs485(struct uart_port *port,
  */
 static u_int atmel_tx_empty(struct uart_port *port)
 {
+   struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
+
+   if (atmel_port->tx_stopped)
+   return TIOCSER_TEMT;
return (atmel_uart_readl(port, ATMEL_US_CSR) & ATMEL_US_TXEMPTY) ?
TIOCSER_TEMT :
0;
@@ -485,6 +490,7 @@ static void atmel_stop_tx(struct uart_port *port)
 * is fully transmitted.
 */
atmel_uart_writel(port, ATMEL_US_CR, ATMEL_US_TXDIS);
+   atmel_port->tx_stopped = true;
 
/* Disable interrupts */
atmel_uart_writel(port, ATMEL_US_IDR, atmel_port->tx_done_mask);
@@ -521,6 +527,7 @@ static void atmel_start_tx(struct uart_port *port)
 
/* re-enable the transmitter */
atmel_uart_writel(port, ATMEL_US_CR, ATMEL_US_TXEN);
+   atmel_port->tx_stopped = false;
 }
 
 /*
@@ -1866,6 +1873,7 @@ static int atmel_startup(struct uart_port *port)
atmel_uart_writel(port, ATMEL_US_CR, ATMEL_US_RSTSTA | ATMEL_US_RSTRX);
/* enable xmit & rcvr */
atmel_uart_writel(port, ATMEL_US_CR, ATMEL_US_TXEN | ATMEL_US_RXEN);
+   atmel_port->tx_stopped = false;
 
setup_timer(_port->uart_timer,
atmel_uart_timer_callback,
@@ -2122,6 +2130,7 @@ static void atmel_set_termios(struct uart_port *port, 
struct ktermios *termios,
 
/* disable receiver and transmitter */
atmel_uart_writel(port, ATMEL_US_CR, ATMEL_US_TXDIS | ATMEL_US_RXDIS);
+   atmel_port->tx_stopped = true;
 
/* mode */
if (port->rs485.flags & SER_RS485_ENABLED) {
@@ -2207,6 +2216,7 @@ static void atmel_set_termios(struct uart_port *port, 
struct ktermios *termios,
atmel_uart_writel(port, ATMEL_US_BRGR, quot);
atmel_uart_writel(port, ATMEL_US_CR, ATMEL_US_RSTSTA | ATMEL_US_RSTRX);
atmel_uart_writel(port, ATMEL_US_CR, ATMEL_US_TXEN | ATMEL_US_RXEN);
+   atmel_port->tx_stopped = false;
 
/* restore interrupts */
atmel_uart_writel(port, ATMEL_US_IER, imr);
@@ -2450,6 +2460,7 @@ static void atmel_console_write(struct console *co, const 
char *s, u_int count)
 
/* Make sure that tx path is actually able to send characters */
atmel_uart_writel(port, ATMEL_US_CR, ATMEL_US_TXEN);
+   atmel_port->tx_stopped = false;
 
uart_console_write(port, s, count, atmel_console_putchar);
 
@@ -2511,6 +2522,7 @@ static int __init atmel_console_setup(struct console *co, 
char *options)
 {
int ret;
struct uart_port *port = _ports[co->index].uart;
+   struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
int baud = 115200;
int bits = 8;
int parity = 'n';
@@ -2528,6 +2540,7 @@ static int __init atmel_console_setup(struct console *co, 
char *options)
atmel_uart_writel(port, ATMEL_US_IDR, -1);
atmel_uart_writel(port, ATMEL_US_CR, ATMEL_US_RSTSTA | ATMEL_US_RSTRX);
atmel_uart_writel(port, ATMEL_US_CR, ATMEL_US_TXEN | ATMEL_US_RXEN);
+   atmel_port->tx_stopped = false;
 
if (options)
uart_parse_options(options, , , , );
-- 
2.11.0



[PATCH v3 7/8] atmel_flexcom: Support backup mode

2017-09-27 Thread Romain Izard
The controller used by a flexcom module is configured at boot, and left
alone after this. As the configuration will be lost after backup mode,
restore the state of the flexcom driver on resume.

Signed-off-by: Romain Izard 
Acked-by: Nicolas Ferre 
Tested-by: Nicolas Ferre 
---
 drivers/mfd/atmel-flexcom.c | 65 ++---
 1 file changed, 50 insertions(+), 15 deletions(-)

diff --git a/drivers/mfd/atmel-flexcom.c b/drivers/mfd/atmel-flexcom.c
index 064bde9cff5a..ef1235c4a179 100644
--- a/drivers/mfd/atmel-flexcom.c
+++ b/drivers/mfd/atmel-flexcom.c
@@ -39,34 +39,44 @@
 #define FLEX_MR_OPMODE(opmode) (((opmode) << FLEX_MR_OPMODE_OFFSET) &  \
 FLEX_MR_OPMODE_MASK)
 
+struct atmel_flexcom {
+   void __iomem *base;
+   u32 opmode;
+   struct clk *clk;
+};
 
 static int atmel_flexcom_probe(struct platform_device *pdev)
 {
struct device_node *np = pdev->dev.of_node;
-   struct clk *clk;
struct resource *res;
-   void __iomem *base;
-   u32 opmode;
+   struct atmel_flexcom *afc;
int err;
+   u32 val;
+
+   afc = devm_kzalloc(>dev, sizeof(*afc), GFP_KERNEL);
+   if (!afc)
+   return -ENOMEM;
 
-   err = of_property_read_u32(np, "atmel,flexcom-mode", );
+   platform_set_drvdata(pdev, afc);
+
+   err = of_property_read_u32(np, "atmel,flexcom-mode", >opmode);
if (err)
return err;
 
-   if (opmode < ATMEL_FLEXCOM_MODE_USART ||
-   opmode > ATMEL_FLEXCOM_MODE_TWI)
+   if (afc->opmode < ATMEL_FLEXCOM_MODE_USART ||
+   afc->opmode > ATMEL_FLEXCOM_MODE_TWI)
return -EINVAL;
 
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-   base = devm_ioremap_resource(>dev, res);
-   if (IS_ERR(base))
-   return PTR_ERR(base);
+   afc->base = devm_ioremap_resource(>dev, res);
+   if (IS_ERR(afc->base))
+   return PTR_ERR(afc->base);
 
-   clk = devm_clk_get(>dev, NULL);
-   if (IS_ERR(clk))
-   return PTR_ERR(clk);
+   afc->clk = devm_clk_get(>dev, NULL);
+   if (IS_ERR(afc->clk))
+   return PTR_ERR(afc->clk);
 
-   err = clk_prepare_enable(clk);
+   err = clk_prepare_enable(afc->clk);
if (err)
return err;
 
@@ -76,9 +86,10 @@ static int atmel_flexcom_probe(struct platform_device *pdev)
 * inaccessible and are read as zero. Also the external I/O lines of the
 * Flexcom are muxed to reach the selected device.
 */
-   writel(FLEX_MR_OPMODE(opmode), base + FLEX_MR);
+   val = FLEX_MR_OPMODE(afc->opmode);
+   writel(val, afc->base + FLEX_MR);
 
-   clk_disable_unprepare(clk);
+   clk_disable_unprepare(afc->clk);
 
return devm_of_platform_populate(>dev);
 }
@@ -89,10 +100,34 @@ static const struct of_device_id atmel_flexcom_of_match[] 
= {
 };
 MODULE_DEVICE_TABLE(of, atmel_flexcom_of_match);
 
+#ifdef CONFIG_PM_SLEEP
+static int atmel_flexcom_resume(struct device *dev)
+{
+   struct atmel_flexcom *afc = dev_get_drvdata(dev);
+   int err;
+   u32 val;
+
+   err = clk_prepare_enable(afc->clk);
+   if (err)
+   return err;
+
+   val = FLEX_MR_OPMODE(afc->opmode),
+   writel(val, afc->base + FLEX_MR);
+
+   clk_disable_unprepare(afc->clk);
+
+   return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(atmel_flexcom_pm_ops, NULL,
+atmel_flexcom_resume);
+
 static struct platform_driver atmel_flexcom_driver = {
.probe  = atmel_flexcom_probe,
.driver = {
.name   = "atmel_flexcom",
+   .pm = _flexcom_pm_ops,
.of_match_table = atmel_flexcom_of_match,
},
 };
-- 
2.11.0



[PATCH v3 1/8] clk: at91: pmc: Wait for clocks when resuming

2017-09-27 Thread Romain Izard
Wait for the syncronization of all clocks when resuming, not only the
UPLL clock. Do not use regmap_read_poll_timeout, as it will call BUG()
when interrupts are masked, which is the case in here.

Signed-off-by: Romain Izard <romain.izard@gmail.com>
Acked-by: Ludovic Desroches <ludovic.desroc...@microchip.com>
Acked-by: Nicolas Ferre <nicolas.fe...@microchip.com>
---
 drivers/clk/at91/pmc.c | 24 
 1 file changed, 16 insertions(+), 8 deletions(-)

diff --git a/drivers/clk/at91/pmc.c b/drivers/clk/at91/pmc.c
index 775af473fe11..5c2b26de303e 100644
--- a/drivers/clk/at91/pmc.c
+++ b/drivers/clk/at91/pmc.c
@@ -107,10 +107,20 @@ static int pmc_suspend(void)
return 0;
 }
 
+static bool pmc_ready(unsigned int mask)
+{
+   unsigned int status;
+
+   regmap_read(pmcreg, AT91_PMC_SR, );
+
+   return ((status & mask) == mask) ? 1 : 0;
+}
+
 static void pmc_resume(void)
 {
-   int i, ret = 0;
+   int i;
u32 tmp;
+   u32 mask = AT91_PMC_MCKRDY | AT91_PMC_LOCKA;
 
regmap_read(pmcreg, AT91_PMC_MCKR, );
if (pmc_cache.mckr != tmp)
@@ -134,13 +144,11 @@ static void pmc_resume(void)
 AT91_PMC_PCR_CMD);
}
 
-   if (pmc_cache.uckr & AT91_PMC_UPLLEN) {
-   ret = regmap_read_poll_timeout(pmcreg, AT91_PMC_SR, tmp,
-  !(tmp & AT91_PMC_LOCKU),
-  10, 5000);
-   if (ret)
-   pr_crit("USB PLL didn't lock when resuming\n");
-   }
+   if (pmc_cache.uckr & AT91_PMC_UPLLEN)
+   mask |= AT91_PMC_LOCKU;
+
+   while (!pmc_ready(mask))
+   cpu_relax();
 }
 
 static struct syscore_ops pmc_syscore_ops = {
-- 
2.11.0



[PATCH v3 1/8] clk: at91: pmc: Wait for clocks when resuming

2017-09-27 Thread Romain Izard
Wait for the syncronization of all clocks when resuming, not only the
UPLL clock. Do not use regmap_read_poll_timeout, as it will call BUG()
when interrupts are masked, which is the case in here.

Signed-off-by: Romain Izard 
Acked-by: Ludovic Desroches 
Acked-by: Nicolas Ferre 
---
 drivers/clk/at91/pmc.c | 24 
 1 file changed, 16 insertions(+), 8 deletions(-)

diff --git a/drivers/clk/at91/pmc.c b/drivers/clk/at91/pmc.c
index 775af473fe11..5c2b26de303e 100644
--- a/drivers/clk/at91/pmc.c
+++ b/drivers/clk/at91/pmc.c
@@ -107,10 +107,20 @@ static int pmc_suspend(void)
return 0;
 }
 
+static bool pmc_ready(unsigned int mask)
+{
+   unsigned int status;
+
+   regmap_read(pmcreg, AT91_PMC_SR, );
+
+   return ((status & mask) == mask) ? 1 : 0;
+}
+
 static void pmc_resume(void)
 {
-   int i, ret = 0;
+   int i;
u32 tmp;
+   u32 mask = AT91_PMC_MCKRDY | AT91_PMC_LOCKA;
 
regmap_read(pmcreg, AT91_PMC_MCKR, );
if (pmc_cache.mckr != tmp)
@@ -134,13 +144,11 @@ static void pmc_resume(void)
 AT91_PMC_PCR_CMD);
}
 
-   if (pmc_cache.uckr & AT91_PMC_UPLLEN) {
-   ret = regmap_read_poll_timeout(pmcreg, AT91_PMC_SR, tmp,
-  !(tmp & AT91_PMC_LOCKU),
-  10, 5000);
-   if (ret)
-   pr_crit("USB PLL didn't lock when resuming\n");
-   }
+   if (pmc_cache.uckr & AT91_PMC_UPLLEN)
+   mask |= AT91_PMC_LOCKU;
+
+   while (!pmc_ready(mask))
+   cpu_relax();
 }
 
 static struct syscore_ops pmc_syscore_ops = {
-- 
2.11.0



Re: [PATCH v2 3/9] clk: at91: pmc: Support backup for programmable clocks

2017-09-25 Thread Romain Izard
2017-09-22 12:31 GMT+02:00 Nicolas Ferre <nicolas.fe...@microchip.com>:
> On 15/09/2017 at 16:04, Romain Izard wrote:
>> From: Romain Izard <romain.iz...@mobile-devices.fr>
>>
>> When an AT91 programmable clock is declared in the device tree, register
>> it into the Power Management Controller driver. On entering suspend mode,
>> the driver saves and restores the Programmable Clock registers to support
>> the backup mode for these clocks.
>>
>> Signed-off-by: Romain Izard <romain.izard@gmail.com>
>
> Romain,
>
> Some nitpicking and one comment. But on the overall patch, here is my:
> Acked-by: Nicolas Ferre <nicolas.fe...@microchip.com>
>
> See below:
>
>> ---
>> Changes in v2:
>> * register PCKs on clock startup
>>
>>  drivers/clk/at91/clk-programmable.c |  2 ++
>>  drivers/clk/at91/pmc.c  | 27 +++
>>  drivers/clk/at91/pmc.h  |  2 ++
>>  3 files changed, 31 insertions(+)
>>
>> diff --git a/drivers/clk/at91/clk-programmable.c 
>> b/drivers/clk/at91/clk-programmable.c
>> index 85a449cf61e3..0e6aab1252fc 100644
>> --- a/drivers/clk/at91/clk-programmable.c
>> +++ b/drivers/clk/at91/clk-programmable.c
>> @@ -204,6 +204,8 @@ at91_clk_register_programmable(struct regmap *regmap,
>>   if (ret) {
>>   kfree(prog);
>>   hw = ERR_PTR(ret);
>
> Nit: "else" not needed.
>
This is a shared idiom in all the atmel clock drivers, so I prefer to keep
it this way.

>> + } else {
>> + pmc_register_pck(id);
>>   }
>>
>>   return hw;
>> diff --git a/drivers/clk/at91/pmc.c b/drivers/clk/at91/pmc.c
>> index 07dc2861ad3f..3910b7537152 100644
>> --- a/drivers/clk/at91/pmc.c
>> +++ b/drivers/clk/at91/pmc.c
>> @@ -22,6 +22,7 @@
>>  #include "pmc.h"
>>
>>  #define PMC_MAX_IDS 128
>> +#define PMC_MAX_PCKS 8
>>
>>  int of_at91_get_clk_range(struct device_node *np, const char *propname,
>> struct clk_range *range)
>> @@ -50,6 +51,7 @@ EXPORT_SYMBOL_GPL(of_at91_get_clk_range);
>>  static struct regmap *pmcreg;
>>
>>  static u8 registered_ids[PMC_MAX_IDS];
>> +static u8 registered_pcks[PMC_MAX_PCKS];
>>
>>  static struct
>>  {
>> @@ -66,8 +68,10 @@ static struct
>>   u32 pcr[PMC_MAX_IDS];
>>   u32 audio_pll0;
>>   u32 audio_pll1;
>> + u32 pckr[PMC_MAX_PCKS];
>>  } pmc_cache;
>>
>> +/* Clock ID 0 is invalid */
>
> (read: so we can use the 0 value as an indicator that this place in the
> table hasn't been filled, so unused)
>
>>  void pmc_register_id(u8 id)
>>  {
>>   int i;
>> @@ -82,6 +86,21 @@ void pmc_register_id(u8 id)
>>   }
>>  }
>>
>> +/* Programmable Clock 0 is valid */
>
> I understand the rationale behind these ^^ two comments, but I would
> like that it's more explicit. Saying that you will store the pck id as
> (id + 1) and that you would have to invert this operation while using
> the stored id.
> Maybe add a comment about this transformation to the struct definition
> as well...
>

I will improve the comments for the next revision.

>
>> +void pmc_register_pck(u8 pck)
>> +{
>> + int i;
>> +
>> + for (i = 0; i < PMC_MAX_PCKS; i++) {
>> + if (registered_pcks[i] == 0) {
>> + registered_pcks[i] = pck + 1;
>> + break;
>> + }
>> + if (registered_pcks[i] == (pck + 1))
>> + break;
>> + }
>> +}
>> +
>>  static int pmc_suspend(void)
>>  {
>>   int i;
>> @@ -103,6 +122,10 @@ static int pmc_suspend(void)
>>   regmap_read(pmcreg, AT91_PMC_PCR,
>>   _cache.pcr[registered_ids[i]]);
>>   }
>> + for (i = 0; registered_pcks[i]; i++) {
>> + u8 num = registered_pcks[i] - 1;
>
> Nit: declaration are better made at the beginning of the function. This
> lead to a checkpatch warning:
> "WARNING: Missing a blank line after declarations"
>

I'll fix this as well.

>> + regmap_read(pmcreg, AT91_PMC_PCKR(num), _cache.pckr[num]);
>> + }
>>
>>   return 0;
>>  }
>> @@ -143,6 +166,10 @@ static void pmc_resume(void)
>>pmc_cache.pcr[registered_ids[i]] |
>>AT91_PMC_PCR_CMD);
>>   }
>> + for (i = 0; registered_pcks[i]; i++) {
>

Re: [PATCH v2 3/9] clk: at91: pmc: Support backup for programmable clocks

2017-09-25 Thread Romain Izard
2017-09-22 12:31 GMT+02:00 Nicolas Ferre :
> On 15/09/2017 at 16:04, Romain Izard wrote:
>> From: Romain Izard 
>>
>> When an AT91 programmable clock is declared in the device tree, register
>> it into the Power Management Controller driver. On entering suspend mode,
>> the driver saves and restores the Programmable Clock registers to support
>> the backup mode for these clocks.
>>
>> Signed-off-by: Romain Izard 
>
> Romain,
>
> Some nitpicking and one comment. But on the overall patch, here is my:
> Acked-by: Nicolas Ferre 
>
> See below:
>
>> ---
>> Changes in v2:
>> * register PCKs on clock startup
>>
>>  drivers/clk/at91/clk-programmable.c |  2 ++
>>  drivers/clk/at91/pmc.c  | 27 +++
>>  drivers/clk/at91/pmc.h  |  2 ++
>>  3 files changed, 31 insertions(+)
>>
>> diff --git a/drivers/clk/at91/clk-programmable.c 
>> b/drivers/clk/at91/clk-programmable.c
>> index 85a449cf61e3..0e6aab1252fc 100644
>> --- a/drivers/clk/at91/clk-programmable.c
>> +++ b/drivers/clk/at91/clk-programmable.c
>> @@ -204,6 +204,8 @@ at91_clk_register_programmable(struct regmap *regmap,
>>   if (ret) {
>>   kfree(prog);
>>   hw = ERR_PTR(ret);
>
> Nit: "else" not needed.
>
This is a shared idiom in all the atmel clock drivers, so I prefer to keep
it this way.

>> + } else {
>> + pmc_register_pck(id);
>>   }
>>
>>   return hw;
>> diff --git a/drivers/clk/at91/pmc.c b/drivers/clk/at91/pmc.c
>> index 07dc2861ad3f..3910b7537152 100644
>> --- a/drivers/clk/at91/pmc.c
>> +++ b/drivers/clk/at91/pmc.c
>> @@ -22,6 +22,7 @@
>>  #include "pmc.h"
>>
>>  #define PMC_MAX_IDS 128
>> +#define PMC_MAX_PCKS 8
>>
>>  int of_at91_get_clk_range(struct device_node *np, const char *propname,
>> struct clk_range *range)
>> @@ -50,6 +51,7 @@ EXPORT_SYMBOL_GPL(of_at91_get_clk_range);
>>  static struct regmap *pmcreg;
>>
>>  static u8 registered_ids[PMC_MAX_IDS];
>> +static u8 registered_pcks[PMC_MAX_PCKS];
>>
>>  static struct
>>  {
>> @@ -66,8 +68,10 @@ static struct
>>   u32 pcr[PMC_MAX_IDS];
>>   u32 audio_pll0;
>>   u32 audio_pll1;
>> + u32 pckr[PMC_MAX_PCKS];
>>  } pmc_cache;
>>
>> +/* Clock ID 0 is invalid */
>
> (read: so we can use the 0 value as an indicator that this place in the
> table hasn't been filled, so unused)
>
>>  void pmc_register_id(u8 id)
>>  {
>>   int i;
>> @@ -82,6 +86,21 @@ void pmc_register_id(u8 id)
>>   }
>>  }
>>
>> +/* Programmable Clock 0 is valid */
>
> I understand the rationale behind these ^^ two comments, but I would
> like that it's more explicit. Saying that you will store the pck id as
> (id + 1) and that you would have to invert this operation while using
> the stored id.
> Maybe add a comment about this transformation to the struct definition
> as well...
>

I will improve the comments for the next revision.

>
>> +void pmc_register_pck(u8 pck)
>> +{
>> + int i;
>> +
>> + for (i = 0; i < PMC_MAX_PCKS; i++) {
>> + if (registered_pcks[i] == 0) {
>> + registered_pcks[i] = pck + 1;
>> + break;
>> + }
>> + if (registered_pcks[i] == (pck + 1))
>> + break;
>> + }
>> +}
>> +
>>  static int pmc_suspend(void)
>>  {
>>   int i;
>> @@ -103,6 +122,10 @@ static int pmc_suspend(void)
>>   regmap_read(pmcreg, AT91_PMC_PCR,
>>   _cache.pcr[registered_ids[i]]);
>>   }
>> + for (i = 0; registered_pcks[i]; i++) {
>> + u8 num = registered_pcks[i] - 1;
>
> Nit: declaration are better made at the beginning of the function. This
> lead to a checkpatch warning:
> "WARNING: Missing a blank line after declarations"
>

I'll fix this as well.

>> + regmap_read(pmcreg, AT91_PMC_PCKR(num), _cache.pckr[num]);
>> + }
>>
>>   return 0;
>>  }
>> @@ -143,6 +166,10 @@ static void pmc_resume(void)
>>pmc_cache.pcr[registered_ids[i]] |
>>AT91_PMC_PCR_CMD);
>>   }
>> + for (i = 0; registered_pcks[i]; i++) {
>> + u8 num = registered_pcks[i] - 1;
>
> Ditto
>
>> + regmap_write(pmcreg, AT91_PMC_PCKR(num), pmc_cache.pckr[num]);
>> + }
>>
>>   if (pmc_cache.uckr & AT91_PMC_UPLLEN)
>>   mask |= AT91_PMC_LOCKU;
>> diff --git a/drivers/clk/at91/pmc.h b/drivers/clk/at91/pmc.h
>> index 858e8ef7e8db..d22b1fa9ecdc 100644
>> --- a/drivers/clk/at91/pmc.h
>> +++ b/drivers/clk/at91/pmc.h
>> @@ -31,8 +31,10 @@ int of_at91_get_clk_range(struct device_node *np, const 
>> char *propname,
>>
>>  #ifdef CONFIG_PM
>>  void pmc_register_id(u8 id);
>> +void pmc_register_pck(u8 pck);
>>  #else
>>  static inline void pmc_register_id(u8 id) {}
>> +static inline void pmc_register_pck(u8 pck) {}
>>  #endif
>>
>>  #endif /* __PMC_H_ */
>>

-- 
Romain Izard


Re: [PATCH v2 5/9] mtd: nand: atmel: Report PMECC failures as errors

2017-09-21 Thread Romain Izard
2017-09-18 12:00 GMT+02:00 Boris Brezillon <boris.brezil...@free-electrons.com>:
> Hi Romain,
>
> On Fri, 15 Sep 2017 16:04:07 +0200
> Romain Izard <romain.izard@gmail.com> wrote:
>
>> It is not normal for the PMECC to fail when trying to fix ECC errors.
>> Report these cases as errors.
>
> I'm not sure we want to have ECC error messages at this level. ECC
> errors are rather unusual but not impossible, and sometimes it's even
> not a real error (I'm thinking of bitflips in erased pages for
> example, which are not necessarily detected/fixed in hardware).
>
> If we decide to print error messages when unfixable bitflips are
> detected, it should be done in the nand-controller driver (somewhere
> along those lines [1]).
>
> Regards,
>
> Boris
>
> [1]http://elixir.free-electrons.com/linux/latest/source/drivers/mtd/nand/atmel/nand-controller.c#L827
>
>>
>> Signed-off-by: Romain Izard <romain.izard@gmail.com>
>> ---
>>  drivers/mtd/nand/atmel/pmecc.c | 4 +++-
>>  1 file changed, 3 insertions(+), 1 deletion(-)
>>
>> diff --git a/drivers/mtd/nand/atmel/pmecc.c b/drivers/mtd/nand/atmel/pmecc.c
>> index 8d1208f38025..2a23f1ff945f 100644
>> --- a/drivers/mtd/nand/atmel/pmecc.c
>> +++ b/drivers/mtd/nand/atmel/pmecc.c
>> @@ -687,6 +687,8 @@ static int atmel_pmecc_err_location(struct 
>> atmel_pmecc_user *user)
>>* Number of roots does not match the degree of smu
>>* unable to correct error.
>>*/
>> + dev_err(pmecc->dev,
>> + "PMECC: Impossible to calculate error location.\n");
>>   return -EBADMSG;
>>  }
>>
>> @@ -729,7 +731,7 @@ int atmel_pmecc_correct_sector(struct atmel_pmecc_user 
>> *user, int sector,
>>   ptr = ecc + byte - sectorsize;
>>   area = "ECC";
>>   } else {
>> - dev_dbg(pmecc->dev,
>> + dev_err(pmecc->dev,
>>   "Invalid errpos value (%d, max is %d)\n",
>>   errpos, (sectorsize + eccbytes) * 8);
>>   return -EINVAL;
>

Ok, I will drop this patch.


Re: [PATCH v2 5/9] mtd: nand: atmel: Report PMECC failures as errors

2017-09-21 Thread Romain Izard
2017-09-18 12:00 GMT+02:00 Boris Brezillon :
> Hi Romain,
>
> On Fri, 15 Sep 2017 16:04:07 +0200
> Romain Izard  wrote:
>
>> It is not normal for the PMECC to fail when trying to fix ECC errors.
>> Report these cases as errors.
>
> I'm not sure we want to have ECC error messages at this level. ECC
> errors are rather unusual but not impossible, and sometimes it's even
> not a real error (I'm thinking of bitflips in erased pages for
> example, which are not necessarily detected/fixed in hardware).
>
> If we decide to print error messages when unfixable bitflips are
> detected, it should be done in the nand-controller driver (somewhere
> along those lines [1]).
>
> Regards,
>
> Boris
>
> [1]http://elixir.free-electrons.com/linux/latest/source/drivers/mtd/nand/atmel/nand-controller.c#L827
>
>>
>> Signed-off-by: Romain Izard 
>> ---
>>  drivers/mtd/nand/atmel/pmecc.c | 4 +++-
>>  1 file changed, 3 insertions(+), 1 deletion(-)
>>
>> diff --git a/drivers/mtd/nand/atmel/pmecc.c b/drivers/mtd/nand/atmel/pmecc.c
>> index 8d1208f38025..2a23f1ff945f 100644
>> --- a/drivers/mtd/nand/atmel/pmecc.c
>> +++ b/drivers/mtd/nand/atmel/pmecc.c
>> @@ -687,6 +687,8 @@ static int atmel_pmecc_err_location(struct 
>> atmel_pmecc_user *user)
>>* Number of roots does not match the degree of smu
>>* unable to correct error.
>>*/
>> + dev_err(pmecc->dev,
>> + "PMECC: Impossible to calculate error location.\n");
>>   return -EBADMSG;
>>  }
>>
>> @@ -729,7 +731,7 @@ int atmel_pmecc_correct_sector(struct atmel_pmecc_user 
>> *user, int sector,
>>   ptr = ecc + byte - sectorsize;
>>   area = "ECC";
>>   } else {
>> - dev_dbg(pmecc->dev,
>> + dev_err(pmecc->dev,
>>   "Invalid errpos value (%d, max is %d)\n",
>>   errpos, (sectorsize + eccbytes) * 8);
>>   return -EINVAL;
>

Ok, I will drop this patch.


[PATCH] ARM: unaligned.h: Use an arch-specific version

2017-09-20 Thread Romain Izard
For the 32-bit ARM architecture, unaligned access support is variable.
On a chip without a MMU, an unaligned access returns a rotated data word
and must be avoided.

When a MMU is available, it can be trapped. On ARMv6 or ARMv7, it can also
be handled by the hardware, depending on the type of access instruction.
Unaligned access of 32 bits or less are corrected, while larger access
provoke a trap.

Unfortunately, the compiler is able to merge two 32-bit access that
would generate a LDR instruction, that works on unaligned access, into a
single LDM access that will not work. This is not a common situation,
but it has been observed in the LZ4 decompression code.

To prevent this type of optimization, it is necessary to change the
explicit accessors for unaligned addresses from those defined in the
access_ok.h header, to those defined in the packed_struct.h header.

Add an arch-specific header to ARM, to retain other optimizations that
rely on HAVE_EFFICIENT_UNALIGNED_ACCESS, while making sure that access
that explicitly rely on the unaligned accessors are correctly handled by
the compiler.

Signed-off-by: Romain Izard <romain.izard@gmail.com>
---

This is a follow-up to this discussion:
HAVE_EFFICIENT_UNALIGNED_ACCESS on ARM32
https://lkml.org/lkml/2017/9/4/359

 arch/arm/include/asm/Kbuild  |  1 -
 arch/arm/include/asm/unaligned.h | 22 ++
 2 files changed, 22 insertions(+), 1 deletion(-)
 create mode 100644 arch/arm/include/asm/unaligned.h

diff --git a/arch/arm/include/asm/Kbuild b/arch/arm/include/asm/Kbuild
index 721ab5ecfb9b..0f2c8a2a8131 100644
--- a/arch/arm/include/asm/Kbuild
+++ b/arch/arm/include/asm/Kbuild
@@ -20,7 +20,6 @@ generic-y += simd.h
 generic-y += sizes.h
 generic-y += timex.h
 generic-y += trace_clock.h
-generic-y += unaligned.h
 
 generated-y += mach-types.h
 generated-y += unistd-nr.h
diff --git a/arch/arm/include/asm/unaligned.h b/arch/arm/include/asm/unaligned.h
new file mode 100644
index ..394227f24b77
--- /dev/null
+++ b/arch/arm/include/asm/unaligned.h
@@ -0,0 +1,22 @@
+#ifndef __ASM_ARM_UNALIGNED_H
+#define __ASM_ARM_UNALIGNED_H
+
+#include 
+
+#if defined(__LITTLE_ENDIAN)
+#include 
+#include 
+#include 
+#define get_unaligned  __get_unaligned_le
+#define put_unaligned  __put_unaligned_le
+#elif defined(__BIG_ENDIAN)
+#include 
:q
+#include 
+#include 
+#define get_unaligned  __get_unaligned_be
+#define put_unaligned  __put_unaligned_be
+#else
+#error need to define endianness
+#endif
+
+#endif /* __ASM_ARM_UNALIGNED_H */
-- 
2.11.0


[PATCH] ARM: unaligned.h: Use an arch-specific version

2017-09-20 Thread Romain Izard
For the 32-bit ARM architecture, unaligned access support is variable.
On a chip without a MMU, an unaligned access returns a rotated data word
and must be avoided.

When a MMU is available, it can be trapped. On ARMv6 or ARMv7, it can also
be handled by the hardware, depending on the type of access instruction.
Unaligned access of 32 bits or less are corrected, while larger access
provoke a trap.

Unfortunately, the compiler is able to merge two 32-bit access that
would generate a LDR instruction, that works on unaligned access, into a
single LDM access that will not work. This is not a common situation,
but it has been observed in the LZ4 decompression code.

To prevent this type of optimization, it is necessary to change the
explicit accessors for unaligned addresses from those defined in the
access_ok.h header, to those defined in the packed_struct.h header.

Add an arch-specific header to ARM, to retain other optimizations that
rely on HAVE_EFFICIENT_UNALIGNED_ACCESS, while making sure that access
that explicitly rely on the unaligned accessors are correctly handled by
the compiler.

Signed-off-by: Romain Izard 
---

This is a follow-up to this discussion:
HAVE_EFFICIENT_UNALIGNED_ACCESS on ARM32
https://lkml.org/lkml/2017/9/4/359

 arch/arm/include/asm/Kbuild  |  1 -
 arch/arm/include/asm/unaligned.h | 22 ++
 2 files changed, 22 insertions(+), 1 deletion(-)
 create mode 100644 arch/arm/include/asm/unaligned.h

diff --git a/arch/arm/include/asm/Kbuild b/arch/arm/include/asm/Kbuild
index 721ab5ecfb9b..0f2c8a2a8131 100644
--- a/arch/arm/include/asm/Kbuild
+++ b/arch/arm/include/asm/Kbuild
@@ -20,7 +20,6 @@ generic-y += simd.h
 generic-y += sizes.h
 generic-y += timex.h
 generic-y += trace_clock.h
-generic-y += unaligned.h
 
 generated-y += mach-types.h
 generated-y += unistd-nr.h
diff --git a/arch/arm/include/asm/unaligned.h b/arch/arm/include/asm/unaligned.h
new file mode 100644
index ..394227f24b77
--- /dev/null
+++ b/arch/arm/include/asm/unaligned.h
@@ -0,0 +1,22 @@
+#ifndef __ASM_ARM_UNALIGNED_H
+#define __ASM_ARM_UNALIGNED_H
+
+#include 
+
+#if defined(__LITTLE_ENDIAN)
+#include 
+#include 
+#include 
+#define get_unaligned  __get_unaligned_le
+#define put_unaligned  __put_unaligned_le
+#elif defined(__BIG_ENDIAN)
+#include 
:q
+#include 
+#include 
+#define get_unaligned  __get_unaligned_be
+#define put_unaligned  __put_unaligned_be
+#else
+#error need to define endianness
+#endif
+
+#endif /* __ASM_ARM_UNALIGNED_H */
-- 
2.11.0


Re: [PATCH v2 8/9] atmel_flexcom: Support backup mode

2017-09-20 Thread Romain Izard
2017-09-19 17:25 GMT+02:00 Lee Jones <lee.jo...@linaro.org>:
> On Tue, 19 Sep 2017, Nicolas Ferre wrote:
>
>> On 15/09/2017 at 16:04, Romain Izard wrote:
>> > The controller used by a flexcom module is configured at boot, and left
>> > alone after this. As the configuration will be lost after backup mode,
>> > restore the state of the flexcom driver on resume.
>> >
>> > Signed-off-by: Romain Izard <romain.izard@gmail.com>
>>
>> Tested-by: Nicolas Ferre <nicolas.fe...@microchip.com>
>> On sama5d2 Xplained board (i2c0 from flexcom 4).
>> and obviously:
>> Acked-by: Nicolas Ferre <nicolas.fe...@microchip.com>
>>
>> Thanks Romain!
>>
>> Regards,
>>
>> > ---
>> >  drivers/mfd/atmel-flexcom.c | 65 
>> > ++---
>> >  1 file changed, 50 insertions(+), 15 deletions(-)
>
> This is the first time I've seen this patch.  Why's that?
>

As the patchset covers many subsystems, get_maintainers.pl provided a
very long list of both developpers and mailing lists (28). I thought it
was a good idea to shorten it a little. Bad idea. Sorry.

Best regards,
-- 
Romain Izard


Re: [PATCH v2 8/9] atmel_flexcom: Support backup mode

2017-09-20 Thread Romain Izard
2017-09-19 17:25 GMT+02:00 Lee Jones :
> On Tue, 19 Sep 2017, Nicolas Ferre wrote:
>
>> On 15/09/2017 at 16:04, Romain Izard wrote:
>> > The controller used by a flexcom module is configured at boot, and left
>> > alone after this. As the configuration will be lost after backup mode,
>> > restore the state of the flexcom driver on resume.
>> >
>> > Signed-off-by: Romain Izard 
>>
>> Tested-by: Nicolas Ferre 
>> On sama5d2 Xplained board (i2c0 from flexcom 4).
>> and obviously:
>> Acked-by: Nicolas Ferre 
>>
>> Thanks Romain!
>>
>> Regards,
>>
>> > ---
>> >  drivers/mfd/atmel-flexcom.c | 65 
>> > ++---
>> >  1 file changed, 50 insertions(+), 15 deletions(-)
>
> This is the first time I've seen this patch.  Why's that?
>

As the patchset covers many subsystems, get_maintainers.pl provided a
very long list of both developpers and mailing lists (28). I thought it
was a good idea to shorten it a little. Bad idea. Sorry.

Best regards,
-- 
Romain Izard


[PATCH v2 1/9] clk: at91: pmc: Wait for clocks when resuming

2017-09-15 Thread Romain Izard
Wait for the syncronization of all clocks when resuming, not only the
UPLL clock. Do not use regmap_read_poll_timeout, as it will call BUG()
when interrupts are masked, which is the case in here.

Signed-off-by: Romain Izard <romain.izard@gmail.com>
---
 drivers/clk/at91/pmc.c | 24 
 1 file changed, 16 insertions(+), 8 deletions(-)

diff --git a/drivers/clk/at91/pmc.c b/drivers/clk/at91/pmc.c
index 775af473fe11..5c2b26de303e 100644
--- a/drivers/clk/at91/pmc.c
+++ b/drivers/clk/at91/pmc.c
@@ -107,10 +107,20 @@ static int pmc_suspend(void)
return 0;
 }
 
+static bool pmc_ready(unsigned int mask)
+{
+   unsigned int status;
+
+   regmap_read(pmcreg, AT91_PMC_SR, );
+
+   return ((status & mask) == mask) ? 1 : 0;
+}
+
 static void pmc_resume(void)
 {
-   int i, ret = 0;
+   int i;
u32 tmp;
+   u32 mask = AT91_PMC_MCKRDY | AT91_PMC_LOCKA;
 
regmap_read(pmcreg, AT91_PMC_MCKR, );
if (pmc_cache.mckr != tmp)
@@ -134,13 +144,11 @@ static void pmc_resume(void)
 AT91_PMC_PCR_CMD);
}
 
-   if (pmc_cache.uckr & AT91_PMC_UPLLEN) {
-   ret = regmap_read_poll_timeout(pmcreg, AT91_PMC_SR, tmp,
-  !(tmp & AT91_PMC_LOCKU),
-  10, 5000);
-   if (ret)
-   pr_crit("USB PLL didn't lock when resuming\n");
-   }
+   if (pmc_cache.uckr & AT91_PMC_UPLLEN)
+   mask |= AT91_PMC_LOCKU;
+
+   while (!pmc_ready(mask))
+   cpu_relax();
 }
 
 static struct syscore_ops pmc_syscore_ops = {
-- 
2.11.0



[PATCH v2 1/9] clk: at91: pmc: Wait for clocks when resuming

2017-09-15 Thread Romain Izard
Wait for the syncronization of all clocks when resuming, not only the
UPLL clock. Do not use regmap_read_poll_timeout, as it will call BUG()
when interrupts are masked, which is the case in here.

Signed-off-by: Romain Izard 
---
 drivers/clk/at91/pmc.c | 24 
 1 file changed, 16 insertions(+), 8 deletions(-)

diff --git a/drivers/clk/at91/pmc.c b/drivers/clk/at91/pmc.c
index 775af473fe11..5c2b26de303e 100644
--- a/drivers/clk/at91/pmc.c
+++ b/drivers/clk/at91/pmc.c
@@ -107,10 +107,20 @@ static int pmc_suspend(void)
return 0;
 }
 
+static bool pmc_ready(unsigned int mask)
+{
+   unsigned int status;
+
+   regmap_read(pmcreg, AT91_PMC_SR, );
+
+   return ((status & mask) == mask) ? 1 : 0;
+}
+
 static void pmc_resume(void)
 {
-   int i, ret = 0;
+   int i;
u32 tmp;
+   u32 mask = AT91_PMC_MCKRDY | AT91_PMC_LOCKA;
 
regmap_read(pmcreg, AT91_PMC_MCKR, );
if (pmc_cache.mckr != tmp)
@@ -134,13 +144,11 @@ static void pmc_resume(void)
 AT91_PMC_PCR_CMD);
}
 
-   if (pmc_cache.uckr & AT91_PMC_UPLLEN) {
-   ret = regmap_read_poll_timeout(pmcreg, AT91_PMC_SR, tmp,
-  !(tmp & AT91_PMC_LOCKU),
-  10, 5000);
-   if (ret)
-   pr_crit("USB PLL didn't lock when resuming\n");
-   }
+   if (pmc_cache.uckr & AT91_PMC_UPLLEN)
+   mask |= AT91_PMC_LOCKU;
+
+   while (!pmc_ready(mask))
+   cpu_relax();
 }
 
 static struct syscore_ops pmc_syscore_ops = {
-- 
2.11.0



[PATCH v2 2/9] clk: at91: pmc: Save SCSR during suspend

2017-09-15 Thread Romain Izard
The contents of the System Clock Status Register (SCSR) needs to be
restored into the System Clock Enable Register (SCER).

As the bootloader will restore some clocks by itself, the issue can be
missed as only the USB controller, the LCD controller, the Image Sensor
controller and the programmable clocks will be impacted.

Fix the obvious typo in the suspend/resume code, as the IMR register
does not need to be saved twice.

Signed-off-by: Romain Izard <romain.izard@gmail.com>
Acked-by: Nicolas Ferre <nicolas.fe...@microchip.com>
---
 drivers/clk/at91/pmc.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/drivers/clk/at91/pmc.c b/drivers/clk/at91/pmc.c
index 5c2b26de303e..07dc2861ad3f 100644
--- a/drivers/clk/at91/pmc.c
+++ b/drivers/clk/at91/pmc.c
@@ -86,7 +86,7 @@ static int pmc_suspend(void)
 {
int i;
 
-   regmap_read(pmcreg, AT91_PMC_IMR, _cache.scsr);
+   regmap_read(pmcreg, AT91_PMC_SCSR, _cache.scsr);
regmap_read(pmcreg, AT91_PMC_PCSR, _cache.pcsr0);
regmap_read(pmcreg, AT91_CKGR_UCKR, _cache.uckr);
regmap_read(pmcreg, AT91_CKGR_MOR, _cache.mor);
@@ -129,7 +129,7 @@ static void pmc_resume(void)
if (pmc_cache.pllar != tmp)
pr_warn("PLLAR was not configured properly by the firmware\n");
 
-   regmap_write(pmcreg, AT91_PMC_IMR, pmc_cache.scsr);
+   regmap_write(pmcreg, AT91_PMC_SCER, pmc_cache.scsr);
regmap_write(pmcreg, AT91_PMC_PCER, pmc_cache.pcsr0);
regmap_write(pmcreg, AT91_CKGR_UCKR, pmc_cache.uckr);
regmap_write(pmcreg, AT91_CKGR_MOR, pmc_cache.mor);
-- 
2.11.0



[PATCH v2 2/9] clk: at91: pmc: Save SCSR during suspend

2017-09-15 Thread Romain Izard
The contents of the System Clock Status Register (SCSR) needs to be
restored into the System Clock Enable Register (SCER).

As the bootloader will restore some clocks by itself, the issue can be
missed as only the USB controller, the LCD controller, the Image Sensor
controller and the programmable clocks will be impacted.

Fix the obvious typo in the suspend/resume code, as the IMR register
does not need to be saved twice.

Signed-off-by: Romain Izard 
Acked-by: Nicolas Ferre 
---
 drivers/clk/at91/pmc.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/drivers/clk/at91/pmc.c b/drivers/clk/at91/pmc.c
index 5c2b26de303e..07dc2861ad3f 100644
--- a/drivers/clk/at91/pmc.c
+++ b/drivers/clk/at91/pmc.c
@@ -86,7 +86,7 @@ static int pmc_suspend(void)
 {
int i;
 
-   regmap_read(pmcreg, AT91_PMC_IMR, _cache.scsr);
+   regmap_read(pmcreg, AT91_PMC_SCSR, _cache.scsr);
regmap_read(pmcreg, AT91_PMC_PCSR, _cache.pcsr0);
regmap_read(pmcreg, AT91_CKGR_UCKR, _cache.uckr);
regmap_read(pmcreg, AT91_CKGR_MOR, _cache.mor);
@@ -129,7 +129,7 @@ static void pmc_resume(void)
if (pmc_cache.pllar != tmp)
pr_warn("PLLAR was not configured properly by the firmware\n");
 
-   regmap_write(pmcreg, AT91_PMC_IMR, pmc_cache.scsr);
+   regmap_write(pmcreg, AT91_PMC_SCER, pmc_cache.scsr);
regmap_write(pmcreg, AT91_PMC_PCER, pmc_cache.pcsr0);
regmap_write(pmcreg, AT91_CKGR_UCKR, pmc_cache.uckr);
regmap_write(pmcreg, AT91_CKGR_MOR, pmc_cache.mor);
-- 
2.11.0



[PATCH v2 3/9] clk: at91: pmc: Support backup for programmable clocks

2017-09-15 Thread Romain Izard
From: Romain Izard <romain.iz...@mobile-devices.fr>

When an AT91 programmable clock is declared in the device tree, register
it into the Power Management Controller driver. On entering suspend mode,
the driver saves and restores the Programmable Clock registers to support
the backup mode for these clocks.

Signed-off-by: Romain Izard <romain.izard@gmail.com>
---
Changes in v2:
* register PCKs on clock startup

 drivers/clk/at91/clk-programmable.c |  2 ++
 drivers/clk/at91/pmc.c  | 27 +++
 drivers/clk/at91/pmc.h  |  2 ++
 3 files changed, 31 insertions(+)

diff --git a/drivers/clk/at91/clk-programmable.c 
b/drivers/clk/at91/clk-programmable.c
index 85a449cf61e3..0e6aab1252fc 100644
--- a/drivers/clk/at91/clk-programmable.c
+++ b/drivers/clk/at91/clk-programmable.c
@@ -204,6 +204,8 @@ at91_clk_register_programmable(struct regmap *regmap,
if (ret) {
kfree(prog);
hw = ERR_PTR(ret);
+   } else {
+   pmc_register_pck(id);
}
 
return hw;
diff --git a/drivers/clk/at91/pmc.c b/drivers/clk/at91/pmc.c
index 07dc2861ad3f..3910b7537152 100644
--- a/drivers/clk/at91/pmc.c
+++ b/drivers/clk/at91/pmc.c
@@ -22,6 +22,7 @@
 #include "pmc.h"
 
 #define PMC_MAX_IDS 128
+#define PMC_MAX_PCKS 8
 
 int of_at91_get_clk_range(struct device_node *np, const char *propname,
  struct clk_range *range)
@@ -50,6 +51,7 @@ EXPORT_SYMBOL_GPL(of_at91_get_clk_range);
 static struct regmap *pmcreg;
 
 static u8 registered_ids[PMC_MAX_IDS];
+static u8 registered_pcks[PMC_MAX_PCKS];
 
 static struct
 {
@@ -66,8 +68,10 @@ static struct
u32 pcr[PMC_MAX_IDS];
u32 audio_pll0;
u32 audio_pll1;
+   u32 pckr[PMC_MAX_PCKS];
 } pmc_cache;
 
+/* Clock ID 0 is invalid */
 void pmc_register_id(u8 id)
 {
int i;
@@ -82,6 +86,21 @@ void pmc_register_id(u8 id)
}
 }
 
+/* Programmable Clock 0 is valid */
+void pmc_register_pck(u8 pck)
+{
+   int i;
+
+   for (i = 0; i < PMC_MAX_PCKS; i++) {
+   if (registered_pcks[i] == 0) {
+   registered_pcks[i] = pck + 1;
+   break;
+   }
+   if (registered_pcks[i] == (pck + 1))
+   break;
+   }
+}
+
 static int pmc_suspend(void)
 {
int i;
@@ -103,6 +122,10 @@ static int pmc_suspend(void)
regmap_read(pmcreg, AT91_PMC_PCR,
_cache.pcr[registered_ids[i]]);
}
+   for (i = 0; registered_pcks[i]; i++) {
+   u8 num = registered_pcks[i] - 1;
+   regmap_read(pmcreg, AT91_PMC_PCKR(num), _cache.pckr[num]);
+   }
 
return 0;
 }
@@ -143,6 +166,10 @@ static void pmc_resume(void)
 pmc_cache.pcr[registered_ids[i]] |
 AT91_PMC_PCR_CMD);
}
+   for (i = 0; registered_pcks[i]; i++) {
+   u8 num = registered_pcks[i] - 1;
+   regmap_write(pmcreg, AT91_PMC_PCKR(num), pmc_cache.pckr[num]);
+   }
 
if (pmc_cache.uckr & AT91_PMC_UPLLEN)
mask |= AT91_PMC_LOCKU;
diff --git a/drivers/clk/at91/pmc.h b/drivers/clk/at91/pmc.h
index 858e8ef7e8db..d22b1fa9ecdc 100644
--- a/drivers/clk/at91/pmc.h
+++ b/drivers/clk/at91/pmc.h
@@ -31,8 +31,10 @@ int of_at91_get_clk_range(struct device_node *np, const char 
*propname,
 
 #ifdef CONFIG_PM
 void pmc_register_id(u8 id);
+void pmc_register_pck(u8 pck);
 #else
 static inline void pmc_register_id(u8 id) {}
+static inline void pmc_register_pck(u8 pck) {}
 #endif
 
 #endif /* __PMC_H_ */
-- 
2.11.0



[PATCH v2 3/9] clk: at91: pmc: Support backup for programmable clocks

2017-09-15 Thread Romain Izard
From: Romain Izard 

When an AT91 programmable clock is declared in the device tree, register
it into the Power Management Controller driver. On entering suspend mode,
the driver saves and restores the Programmable Clock registers to support
the backup mode for these clocks.

Signed-off-by: Romain Izard 
---
Changes in v2:
* register PCKs on clock startup

 drivers/clk/at91/clk-programmable.c |  2 ++
 drivers/clk/at91/pmc.c  | 27 +++
 drivers/clk/at91/pmc.h  |  2 ++
 3 files changed, 31 insertions(+)

diff --git a/drivers/clk/at91/clk-programmable.c 
b/drivers/clk/at91/clk-programmable.c
index 85a449cf61e3..0e6aab1252fc 100644
--- a/drivers/clk/at91/clk-programmable.c
+++ b/drivers/clk/at91/clk-programmable.c
@@ -204,6 +204,8 @@ at91_clk_register_programmable(struct regmap *regmap,
if (ret) {
kfree(prog);
hw = ERR_PTR(ret);
+   } else {
+   pmc_register_pck(id);
}
 
return hw;
diff --git a/drivers/clk/at91/pmc.c b/drivers/clk/at91/pmc.c
index 07dc2861ad3f..3910b7537152 100644
--- a/drivers/clk/at91/pmc.c
+++ b/drivers/clk/at91/pmc.c
@@ -22,6 +22,7 @@
 #include "pmc.h"
 
 #define PMC_MAX_IDS 128
+#define PMC_MAX_PCKS 8
 
 int of_at91_get_clk_range(struct device_node *np, const char *propname,
  struct clk_range *range)
@@ -50,6 +51,7 @@ EXPORT_SYMBOL_GPL(of_at91_get_clk_range);
 static struct regmap *pmcreg;
 
 static u8 registered_ids[PMC_MAX_IDS];
+static u8 registered_pcks[PMC_MAX_PCKS];
 
 static struct
 {
@@ -66,8 +68,10 @@ static struct
u32 pcr[PMC_MAX_IDS];
u32 audio_pll0;
u32 audio_pll1;
+   u32 pckr[PMC_MAX_PCKS];
 } pmc_cache;
 
+/* Clock ID 0 is invalid */
 void pmc_register_id(u8 id)
 {
int i;
@@ -82,6 +86,21 @@ void pmc_register_id(u8 id)
}
 }
 
+/* Programmable Clock 0 is valid */
+void pmc_register_pck(u8 pck)
+{
+   int i;
+
+   for (i = 0; i < PMC_MAX_PCKS; i++) {
+   if (registered_pcks[i] == 0) {
+   registered_pcks[i] = pck + 1;
+   break;
+   }
+   if (registered_pcks[i] == (pck + 1))
+   break;
+   }
+}
+
 static int pmc_suspend(void)
 {
int i;
@@ -103,6 +122,10 @@ static int pmc_suspend(void)
regmap_read(pmcreg, AT91_PMC_PCR,
_cache.pcr[registered_ids[i]]);
}
+   for (i = 0; registered_pcks[i]; i++) {
+   u8 num = registered_pcks[i] - 1;
+   regmap_read(pmcreg, AT91_PMC_PCKR(num), _cache.pckr[num]);
+   }
 
return 0;
 }
@@ -143,6 +166,10 @@ static void pmc_resume(void)
 pmc_cache.pcr[registered_ids[i]] |
 AT91_PMC_PCR_CMD);
}
+   for (i = 0; registered_pcks[i]; i++) {
+   u8 num = registered_pcks[i] - 1;
+   regmap_write(pmcreg, AT91_PMC_PCKR(num), pmc_cache.pckr[num]);
+   }
 
if (pmc_cache.uckr & AT91_PMC_UPLLEN)
mask |= AT91_PMC_LOCKU;
diff --git a/drivers/clk/at91/pmc.h b/drivers/clk/at91/pmc.h
index 858e8ef7e8db..d22b1fa9ecdc 100644
--- a/drivers/clk/at91/pmc.h
+++ b/drivers/clk/at91/pmc.h
@@ -31,8 +31,10 @@ int of_at91_get_clk_range(struct device_node *np, const char 
*propname,
 
 #ifdef CONFIG_PM
 void pmc_register_id(u8 id);
+void pmc_register_pck(u8 pck);
 #else
 static inline void pmc_register_id(u8 id) {}
+static inline void pmc_register_pck(u8 pck) {}
 #endif
 
 #endif /* __PMC_H_ */
-- 
2.11.0



[PATCH v2 7/9] pwm: atmel-tcb: Support backup mode

2017-09-15 Thread Romain Izard
Save and restore registers for the PWM on suspend and resume, which
makes hibernation and backup modes possible.

Signed-off-by: Romain Izard <romain.izard@gmail.com>
---
 drivers/pwm/pwm-atmel-tcb.c | 63 +++--
 1 file changed, 61 insertions(+), 2 deletions(-)

diff --git a/drivers/pwm/pwm-atmel-tcb.c b/drivers/pwm/pwm-atmel-tcb.c
index 75db585a2a94..acd3ce8ecf3f 100644
--- a/drivers/pwm/pwm-atmel-tcb.c
+++ b/drivers/pwm/pwm-atmel-tcb.c
@@ -37,11 +37,20 @@ struct atmel_tcb_pwm_device {
unsigned period;/* PWM period expressed in clk cycles */
 };
 
+struct atmel_tcb_channel {
+   u32 enabled;
+   u32 cmr;
+   u32 ra;
+   u32 rb;
+   u32 rc;
+};
+
 struct atmel_tcb_pwm_chip {
struct pwm_chip chip;
spinlock_t lock;
struct atmel_tc *tc;
struct atmel_tcb_pwm_device *pwms[NPWM];
+   struct atmel_tcb_channel bkup[NPWM / 2];
 };
 
 static inline struct atmel_tcb_pwm_chip *to_tcb_chip(struct pwm_chip *chip)
@@ -175,12 +184,15 @@ static void atmel_tcb_pwm_disable(struct pwm_chip *chip, 
struct pwm_device *pwm)
 * Use software trigger to apply the new setting.
 * If both PWM devices in this group are disabled we stop the clock.
 */
-   if (!(cmr & (ATMEL_TC_ACPC | ATMEL_TC_BCPC)))
+   if (!(cmr & (ATMEL_TC_ACPC | ATMEL_TC_BCPC))) {
__raw_writel(ATMEL_TC_SWTRG | ATMEL_TC_CLKDIS,
 regs + ATMEL_TC_REG(group, CCR));
-   else
+   tcbpwmc->bkup[group].enabled = 1;
+   } else {
__raw_writel(ATMEL_TC_SWTRG, regs +
 ATMEL_TC_REG(group, CCR));
+   tcbpwmc->bkup[group].enabled = 0;
+   }
 
spin_unlock(>lock);
 }
@@ -263,6 +275,7 @@ static int atmel_tcb_pwm_enable(struct pwm_chip *chip, 
struct pwm_device *pwm)
/* Use software trigger to apply the new setting */
__raw_writel(ATMEL_TC_CLKEN | ATMEL_TC_SWTRG,
 regs + ATMEL_TC_REG(group, CCR));
+   tcbpwmc->bkup[group].enabled = 1;
spin_unlock(>lock);
return 0;
 }
@@ -445,10 +458,56 @@ static const struct of_device_id atmel_tcb_pwm_dt_ids[] = 
{
 };
 MODULE_DEVICE_TABLE(of, atmel_tcb_pwm_dt_ids);
 
+#ifdef CONFIG_PM_SLEEP
+static int atmel_tcb_pwm_suspend(struct device *dev)
+{
+   struct platform_device *pdev = to_platform_device(dev);
+   struct atmel_tcb_pwm_chip *tcbpwm = platform_get_drvdata(pdev);
+   void __iomem *base = tcbpwm->tc->regs;
+   int i;
+
+   for (i = 0; i < (NPWM / 2); i++) {
+   struct atmel_tcb_channel *chan = >bkup[i];
+
+   chan->cmr = readl(base + ATMEL_TC_REG(i, CMR));
+   chan->ra = readl(base + ATMEL_TC_REG(i, RA));
+   chan->rb = readl(base + ATMEL_TC_REG(i, RB));
+   chan->rc = readl(base + ATMEL_TC_REG(i, RC));
+   }
+   return 0;
+}
+
+static int atmel_tcb_pwm_resume(struct device *dev)
+{
+   struct platform_device *pdev = to_platform_device(dev);
+   struct atmel_tcb_pwm_chip *tcbpwm = platform_get_drvdata(pdev);
+   void __iomem *base = tcbpwm->tc->regs;
+   int i;
+
+   for (i = 0; i < (NPWM / 2); i++) {
+   struct atmel_tcb_channel *chan = >bkup[i];
+
+   writel(chan->cmr, base + ATMEL_TC_REG(i, CMR));
+   writel(chan->ra, base + ATMEL_TC_REG(i, RA));
+   writel(chan->rb, base + ATMEL_TC_REG(i, RB));
+   writel(chan->rc, base + ATMEL_TC_REG(i, RC));
+   if (chan->enabled) {
+   writel(ATMEL_TC_CLKEN | ATMEL_TC_SWTRG,
+   base + ATMEL_TC_REG(i, CCR));
+   }
+   }
+   return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(atmel_tcb_pwm_pm_ops, atmel_tcb_pwm_suspend,
+atmel_tcb_pwm_resume);
+
 static struct platform_driver atmel_tcb_pwm_driver = {
.driver = {
.name = "atmel-tcb-pwm",
.of_match_table = atmel_tcb_pwm_dt_ids,
+   .pm = _tcb_pwm_pm_ops,
},
.probe = atmel_tcb_pwm_probe,
.remove = atmel_tcb_pwm_remove,
-- 
2.11.0



[PATCH v2 7/9] pwm: atmel-tcb: Support backup mode

2017-09-15 Thread Romain Izard
Save and restore registers for the PWM on suspend and resume, which
makes hibernation and backup modes possible.

Signed-off-by: Romain Izard 
---
 drivers/pwm/pwm-atmel-tcb.c | 63 +++--
 1 file changed, 61 insertions(+), 2 deletions(-)

diff --git a/drivers/pwm/pwm-atmel-tcb.c b/drivers/pwm/pwm-atmel-tcb.c
index 75db585a2a94..acd3ce8ecf3f 100644
--- a/drivers/pwm/pwm-atmel-tcb.c
+++ b/drivers/pwm/pwm-atmel-tcb.c
@@ -37,11 +37,20 @@ struct atmel_tcb_pwm_device {
unsigned period;/* PWM period expressed in clk cycles */
 };
 
+struct atmel_tcb_channel {
+   u32 enabled;
+   u32 cmr;
+   u32 ra;
+   u32 rb;
+   u32 rc;
+};
+
 struct atmel_tcb_pwm_chip {
struct pwm_chip chip;
spinlock_t lock;
struct atmel_tc *tc;
struct atmel_tcb_pwm_device *pwms[NPWM];
+   struct atmel_tcb_channel bkup[NPWM / 2];
 };
 
 static inline struct atmel_tcb_pwm_chip *to_tcb_chip(struct pwm_chip *chip)
@@ -175,12 +184,15 @@ static void atmel_tcb_pwm_disable(struct pwm_chip *chip, 
struct pwm_device *pwm)
 * Use software trigger to apply the new setting.
 * If both PWM devices in this group are disabled we stop the clock.
 */
-   if (!(cmr & (ATMEL_TC_ACPC | ATMEL_TC_BCPC)))
+   if (!(cmr & (ATMEL_TC_ACPC | ATMEL_TC_BCPC))) {
__raw_writel(ATMEL_TC_SWTRG | ATMEL_TC_CLKDIS,
 regs + ATMEL_TC_REG(group, CCR));
-   else
+   tcbpwmc->bkup[group].enabled = 1;
+   } else {
__raw_writel(ATMEL_TC_SWTRG, regs +
 ATMEL_TC_REG(group, CCR));
+   tcbpwmc->bkup[group].enabled = 0;
+   }
 
spin_unlock(>lock);
 }
@@ -263,6 +275,7 @@ static int atmel_tcb_pwm_enable(struct pwm_chip *chip, 
struct pwm_device *pwm)
/* Use software trigger to apply the new setting */
__raw_writel(ATMEL_TC_CLKEN | ATMEL_TC_SWTRG,
 regs + ATMEL_TC_REG(group, CCR));
+   tcbpwmc->bkup[group].enabled = 1;
spin_unlock(>lock);
return 0;
 }
@@ -445,10 +458,56 @@ static const struct of_device_id atmel_tcb_pwm_dt_ids[] = 
{
 };
 MODULE_DEVICE_TABLE(of, atmel_tcb_pwm_dt_ids);
 
+#ifdef CONFIG_PM_SLEEP
+static int atmel_tcb_pwm_suspend(struct device *dev)
+{
+   struct platform_device *pdev = to_platform_device(dev);
+   struct atmel_tcb_pwm_chip *tcbpwm = platform_get_drvdata(pdev);
+   void __iomem *base = tcbpwm->tc->regs;
+   int i;
+
+   for (i = 0; i < (NPWM / 2); i++) {
+   struct atmel_tcb_channel *chan = >bkup[i];
+
+   chan->cmr = readl(base + ATMEL_TC_REG(i, CMR));
+   chan->ra = readl(base + ATMEL_TC_REG(i, RA));
+   chan->rb = readl(base + ATMEL_TC_REG(i, RB));
+   chan->rc = readl(base + ATMEL_TC_REG(i, RC));
+   }
+   return 0;
+}
+
+static int atmel_tcb_pwm_resume(struct device *dev)
+{
+   struct platform_device *pdev = to_platform_device(dev);
+   struct atmel_tcb_pwm_chip *tcbpwm = platform_get_drvdata(pdev);
+   void __iomem *base = tcbpwm->tc->regs;
+   int i;
+
+   for (i = 0; i < (NPWM / 2); i++) {
+   struct atmel_tcb_channel *chan = >bkup[i];
+
+   writel(chan->cmr, base + ATMEL_TC_REG(i, CMR));
+   writel(chan->ra, base + ATMEL_TC_REG(i, RA));
+   writel(chan->rb, base + ATMEL_TC_REG(i, RB));
+   writel(chan->rc, base + ATMEL_TC_REG(i, RC));
+   if (chan->enabled) {
+   writel(ATMEL_TC_CLKEN | ATMEL_TC_SWTRG,
+   base + ATMEL_TC_REG(i, CCR));
+   }
+   }
+   return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(atmel_tcb_pwm_pm_ops, atmel_tcb_pwm_suspend,
+atmel_tcb_pwm_resume);
+
 static struct platform_driver atmel_tcb_pwm_driver = {
.driver = {
.name = "atmel-tcb-pwm",
.of_match_table = atmel_tcb_pwm_dt_ids,
+   .pm = _tcb_pwm_pm_ops,
},
.probe = atmel_tcb_pwm_probe,
.remove = atmel_tcb_pwm_remove,
-- 
2.11.0



[PATCH v2 8/9] atmel_flexcom: Support backup mode

2017-09-15 Thread Romain Izard
The controller used by a flexcom module is configured at boot, and left
alone after this. As the configuration will be lost after backup mode,
restore the state of the flexcom driver on resume.

Signed-off-by: Romain Izard <romain.izard@gmail.com>
---
 drivers/mfd/atmel-flexcom.c | 65 ++---
 1 file changed, 50 insertions(+), 15 deletions(-)

diff --git a/drivers/mfd/atmel-flexcom.c b/drivers/mfd/atmel-flexcom.c
index 064bde9cff5a..ef1235c4a179 100644
--- a/drivers/mfd/atmel-flexcom.c
+++ b/drivers/mfd/atmel-flexcom.c
@@ -39,34 +39,44 @@
 #define FLEX_MR_OPMODE(opmode) (((opmode) << FLEX_MR_OPMODE_OFFSET) &  \
 FLEX_MR_OPMODE_MASK)
 
+struct atmel_flexcom {
+   void __iomem *base;
+   u32 opmode;
+   struct clk *clk;
+};
 
 static int atmel_flexcom_probe(struct platform_device *pdev)
 {
struct device_node *np = pdev->dev.of_node;
-   struct clk *clk;
struct resource *res;
-   void __iomem *base;
-   u32 opmode;
+   struct atmel_flexcom *afc;
int err;
+   u32 val;
+
+   afc = devm_kzalloc(>dev, sizeof(*afc), GFP_KERNEL);
+   if (!afc)
+   return -ENOMEM;
 
-   err = of_property_read_u32(np, "atmel,flexcom-mode", );
+   platform_set_drvdata(pdev, afc);
+
+   err = of_property_read_u32(np, "atmel,flexcom-mode", >opmode);
if (err)
return err;
 
-   if (opmode < ATMEL_FLEXCOM_MODE_USART ||
-   opmode > ATMEL_FLEXCOM_MODE_TWI)
+   if (afc->opmode < ATMEL_FLEXCOM_MODE_USART ||
+   afc->opmode > ATMEL_FLEXCOM_MODE_TWI)
return -EINVAL;
 
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-   base = devm_ioremap_resource(>dev, res);
-   if (IS_ERR(base))
-   return PTR_ERR(base);
+   afc->base = devm_ioremap_resource(>dev, res);
+   if (IS_ERR(afc->base))
+   return PTR_ERR(afc->base);
 
-   clk = devm_clk_get(>dev, NULL);
-   if (IS_ERR(clk))
-   return PTR_ERR(clk);
+   afc->clk = devm_clk_get(>dev, NULL);
+   if (IS_ERR(afc->clk))
+   return PTR_ERR(afc->clk);
 
-   err = clk_prepare_enable(clk);
+   err = clk_prepare_enable(afc->clk);
if (err)
return err;
 
@@ -76,9 +86,10 @@ static int atmel_flexcom_probe(struct platform_device *pdev)
 * inaccessible and are read as zero. Also the external I/O lines of the
 * Flexcom are muxed to reach the selected device.
 */
-   writel(FLEX_MR_OPMODE(opmode), base + FLEX_MR);
+   val = FLEX_MR_OPMODE(afc->opmode);
+   writel(val, afc->base + FLEX_MR);
 
-   clk_disable_unprepare(clk);
+   clk_disable_unprepare(afc->clk);
 
return devm_of_platform_populate(>dev);
 }
@@ -89,10 +100,34 @@ static const struct of_device_id atmel_flexcom_of_match[] 
= {
 };
 MODULE_DEVICE_TABLE(of, atmel_flexcom_of_match);
 
+#ifdef CONFIG_PM_SLEEP
+static int atmel_flexcom_resume(struct device *dev)
+{
+   struct atmel_flexcom *afc = dev_get_drvdata(dev);
+   int err;
+   u32 val;
+
+   err = clk_prepare_enable(afc->clk);
+   if (err)
+   return err;
+
+   val = FLEX_MR_OPMODE(afc->opmode),
+   writel(val, afc->base + FLEX_MR);
+
+   clk_disable_unprepare(afc->clk);
+
+   return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(atmel_flexcom_pm_ops, NULL,
+atmel_flexcom_resume);
+
 static struct platform_driver atmel_flexcom_driver = {
.probe  = atmel_flexcom_probe,
.driver = {
.name   = "atmel_flexcom",
+   .pm = _flexcom_pm_ops,
.of_match_table = atmel_flexcom_of_match,
},
 };
-- 
2.11.0



[PATCH v2 9/9] tty/serial: atmel: Prevent a warning on suspend

2017-09-15 Thread Romain Izard
The atmel serial port driver reported the following warning on suspend:
atmel_usart f802.serial: ttyS1: Unable to drain transmitter

As the ATMEL_US_TXEMPTY status bit in ATMEL_US_CSR is always cleared
when the transmitter is disabled, we need to know the transmitter's
state to return the real fifo state. And as ATMEL_US_CR is write-only,
it is necessary to save the state of the transmitter in a local
variable, and update the variable when TXEN and TXDIS is written in
ATMEL_US_CR.

After those changes, atmel_tx_empty can return "empty" on suspend, the
warning in uart_suspend_port disappears, and suspending is 20ms shorter
for each enabled Atmel serial port.

Signed-off-by: Romain Izard <romain.izard@gmail.com>
---
 drivers/tty/serial/atmel_serial.c | 14 ++
 1 file changed, 14 insertions(+)

diff --git a/drivers/tty/serial/atmel_serial.c 
b/drivers/tty/serial/atmel_serial.c
index 7551cab438ff..783af6648be0 100644
--- a/drivers/tty/serial/atmel_serial.c
+++ b/drivers/tty/serial/atmel_serial.c
@@ -171,6 +171,7 @@ struct atmel_uart_port {
boolhas_hw_timer;
struct timer_list   uart_timer;
 
+   booltx_stopped;
boolsuspended;
unsigned intpending;
unsigned intpending_status;
@@ -380,6 +381,10 @@ static int atmel_config_rs485(struct uart_port *port,
  */
 static u_int atmel_tx_empty(struct uart_port *port)
 {
+   struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
+
+   if (atmel_port->tx_stopped)
+   return TIOCSER_TEMT;
return (atmel_uart_readl(port, ATMEL_US_CSR) & ATMEL_US_TXEMPTY) ?
TIOCSER_TEMT :
0;
@@ -485,6 +490,7 @@ static void atmel_stop_tx(struct uart_port *port)
 * is fully transmitted.
 */
atmel_uart_writel(port, ATMEL_US_CR, ATMEL_US_TXDIS);
+   atmel_port->tx_stopped = true;
 
/* Disable interrupts */
atmel_uart_writel(port, ATMEL_US_IDR, atmel_port->tx_done_mask);
@@ -492,6 +498,7 @@ static void atmel_stop_tx(struct uart_port *port)
if ((port->rs485.flags & SER_RS485_ENABLED) &&
!(port->rs485.flags & SER_RS485_RX_DURING_TX))
atmel_start_rx(port);
+
 }
 
 /*
@@ -521,6 +528,7 @@ static void atmel_start_tx(struct uart_port *port)
 
/* re-enable the transmitter */
atmel_uart_writel(port, ATMEL_US_CR, ATMEL_US_TXEN);
+   atmel_port->tx_stopped = false;
 }
 
 /*
@@ -1866,6 +1874,7 @@ static int atmel_startup(struct uart_port *port)
atmel_uart_writel(port, ATMEL_US_CR, ATMEL_US_RSTSTA | ATMEL_US_RSTRX);
/* enable xmit & rcvr */
atmel_uart_writel(port, ATMEL_US_CR, ATMEL_US_TXEN | ATMEL_US_RXEN);
+   atmel_port->tx_stopped = false;
 
setup_timer(_port->uart_timer,
atmel_uart_timer_callback,
@@ -2122,6 +2131,7 @@ static void atmel_set_termios(struct uart_port *port, 
struct ktermios *termios,
 
/* disable receiver and transmitter */
atmel_uart_writel(port, ATMEL_US_CR, ATMEL_US_TXDIS | ATMEL_US_RXDIS);
+   atmel_port->tx_stopped = true;
 
/* mode */
if (port->rs485.flags & SER_RS485_ENABLED) {
@@ -2207,6 +2217,7 @@ static void atmel_set_termios(struct uart_port *port, 
struct ktermios *termios,
atmel_uart_writel(port, ATMEL_US_BRGR, quot);
atmel_uart_writel(port, ATMEL_US_CR, ATMEL_US_RSTSTA | ATMEL_US_RSTRX);
atmel_uart_writel(port, ATMEL_US_CR, ATMEL_US_TXEN | ATMEL_US_RXEN);
+   atmel_port->tx_stopped = false;
 
/* restore interrupts */
atmel_uart_writel(port, ATMEL_US_IER, imr);
@@ -2450,6 +2461,7 @@ static void atmel_console_write(struct console *co, const 
char *s, u_int count)
 
/* Make sure that tx path is actually able to send characters */
atmel_uart_writel(port, ATMEL_US_CR, ATMEL_US_TXEN);
+   atmel_port->tx_stopped = false;
 
uart_console_write(port, s, count, atmel_console_putchar);
 
@@ -2511,6 +2523,7 @@ static int __init atmel_console_setup(struct console *co, 
char *options)
 {
int ret;
struct uart_port *port = _ports[co->index].uart;
+   struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
int baud = 115200;
int bits = 8;
int parity = 'n';
@@ -2528,6 +2541,7 @@ static int __init atmel_console_setup(struct console *co, 
char *options)
atmel_uart_writel(port, ATMEL_US_IDR, -1);
atmel_uart_writel(port, ATMEL_US_CR, ATMEL_US_RSTSTA | ATMEL_US_RSTRX);
atmel_uart_writel(port, ATMEL_US_CR, ATMEL_US_TXEN | ATMEL_US_RXEN);
+   atmel_port->tx_stopped = false;
 
if (options)
uart_parse_options(options, , , , );
-- 
2.11.0



[PATCH v2 6/9] ehci-atmel: Power down during suspend is normal

2017-09-15 Thread Romain Izard
When an Atmel SoC is suspended with the backup mode, the USB bus will be
powered down. As this is expected, do not return an error to the driver
core when ehci_resume detects it.

Signed-off-by: Romain Izard <romain.izard@gmail.com>
---
 drivers/usb/host/ehci-atmel.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/drivers/usb/host/ehci-atmel.c b/drivers/usb/host/ehci-atmel.c
index 7440722bfbf0..2a8b9bdc0e57 100644
--- a/drivers/usb/host/ehci-atmel.c
+++ b/drivers/usb/host/ehci-atmel.c
@@ -205,7 +205,8 @@ static int __maybe_unused ehci_atmel_drv_resume(struct 
device *dev)
struct atmel_ehci_priv *atmel_ehci = hcd_to_atmel_ehci_priv(hcd);
 
atmel_start_clock(atmel_ehci);
-   return ehci_resume(hcd, false);
+   ehci_resume(hcd, false);
+   return 0;
 }
 
 #ifdef CONFIG_OF
-- 
2.11.0



[PATCH v2 9/9] tty/serial: atmel: Prevent a warning on suspend

2017-09-15 Thread Romain Izard
The atmel serial port driver reported the following warning on suspend:
atmel_usart f802.serial: ttyS1: Unable to drain transmitter

As the ATMEL_US_TXEMPTY status bit in ATMEL_US_CSR is always cleared
when the transmitter is disabled, we need to know the transmitter's
state to return the real fifo state. And as ATMEL_US_CR is write-only,
it is necessary to save the state of the transmitter in a local
variable, and update the variable when TXEN and TXDIS is written in
ATMEL_US_CR.

After those changes, atmel_tx_empty can return "empty" on suspend, the
warning in uart_suspend_port disappears, and suspending is 20ms shorter
for each enabled Atmel serial port.

Signed-off-by: Romain Izard 
---
 drivers/tty/serial/atmel_serial.c | 14 ++
 1 file changed, 14 insertions(+)

diff --git a/drivers/tty/serial/atmel_serial.c 
b/drivers/tty/serial/atmel_serial.c
index 7551cab438ff..783af6648be0 100644
--- a/drivers/tty/serial/atmel_serial.c
+++ b/drivers/tty/serial/atmel_serial.c
@@ -171,6 +171,7 @@ struct atmel_uart_port {
boolhas_hw_timer;
struct timer_list   uart_timer;
 
+   booltx_stopped;
boolsuspended;
unsigned intpending;
unsigned intpending_status;
@@ -380,6 +381,10 @@ static int atmel_config_rs485(struct uart_port *port,
  */
 static u_int atmel_tx_empty(struct uart_port *port)
 {
+   struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
+
+   if (atmel_port->tx_stopped)
+   return TIOCSER_TEMT;
return (atmel_uart_readl(port, ATMEL_US_CSR) & ATMEL_US_TXEMPTY) ?
TIOCSER_TEMT :
0;
@@ -485,6 +490,7 @@ static void atmel_stop_tx(struct uart_port *port)
 * is fully transmitted.
 */
atmel_uart_writel(port, ATMEL_US_CR, ATMEL_US_TXDIS);
+   atmel_port->tx_stopped = true;
 
/* Disable interrupts */
atmel_uart_writel(port, ATMEL_US_IDR, atmel_port->tx_done_mask);
@@ -492,6 +498,7 @@ static void atmel_stop_tx(struct uart_port *port)
if ((port->rs485.flags & SER_RS485_ENABLED) &&
!(port->rs485.flags & SER_RS485_RX_DURING_TX))
atmel_start_rx(port);
+
 }
 
 /*
@@ -521,6 +528,7 @@ static void atmel_start_tx(struct uart_port *port)
 
/* re-enable the transmitter */
atmel_uart_writel(port, ATMEL_US_CR, ATMEL_US_TXEN);
+   atmel_port->tx_stopped = false;
 }
 
 /*
@@ -1866,6 +1874,7 @@ static int atmel_startup(struct uart_port *port)
atmel_uart_writel(port, ATMEL_US_CR, ATMEL_US_RSTSTA | ATMEL_US_RSTRX);
/* enable xmit & rcvr */
atmel_uart_writel(port, ATMEL_US_CR, ATMEL_US_TXEN | ATMEL_US_RXEN);
+   atmel_port->tx_stopped = false;
 
setup_timer(_port->uart_timer,
atmel_uart_timer_callback,
@@ -2122,6 +2131,7 @@ static void atmel_set_termios(struct uart_port *port, 
struct ktermios *termios,
 
/* disable receiver and transmitter */
atmel_uart_writel(port, ATMEL_US_CR, ATMEL_US_TXDIS | ATMEL_US_RXDIS);
+   atmel_port->tx_stopped = true;
 
/* mode */
if (port->rs485.flags & SER_RS485_ENABLED) {
@@ -2207,6 +2217,7 @@ static void atmel_set_termios(struct uart_port *port, 
struct ktermios *termios,
atmel_uart_writel(port, ATMEL_US_BRGR, quot);
atmel_uart_writel(port, ATMEL_US_CR, ATMEL_US_RSTSTA | ATMEL_US_RSTRX);
atmel_uart_writel(port, ATMEL_US_CR, ATMEL_US_TXEN | ATMEL_US_RXEN);
+   atmel_port->tx_stopped = false;
 
/* restore interrupts */
atmel_uart_writel(port, ATMEL_US_IER, imr);
@@ -2450,6 +2461,7 @@ static void atmel_console_write(struct console *co, const 
char *s, u_int count)
 
/* Make sure that tx path is actually able to send characters */
atmel_uart_writel(port, ATMEL_US_CR, ATMEL_US_TXEN);
+   atmel_port->tx_stopped = false;
 
uart_console_write(port, s, count, atmel_console_putchar);
 
@@ -2511,6 +2523,7 @@ static int __init atmel_console_setup(struct console *co, 
char *options)
 {
int ret;
struct uart_port *port = _ports[co->index].uart;
+   struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
int baud = 115200;
int bits = 8;
int parity = 'n';
@@ -2528,6 +2541,7 @@ static int __init atmel_console_setup(struct console *co, 
char *options)
atmel_uart_writel(port, ATMEL_US_IDR, -1);
atmel_uart_writel(port, ATMEL_US_CR, ATMEL_US_RSTSTA | ATMEL_US_RSTRX);
atmel_uart_writel(port, ATMEL_US_CR, ATMEL_US_TXEN | ATMEL_US_RXEN);
+   atmel_port->tx_stopped = false;
 
if (options)
uart_parse_options(options, , , , );
-- 
2.11.0



[PATCH v2 8/9] atmel_flexcom: Support backup mode

2017-09-15 Thread Romain Izard
The controller used by a flexcom module is configured at boot, and left
alone after this. As the configuration will be lost after backup mode,
restore the state of the flexcom driver on resume.

Signed-off-by: Romain Izard 
---
 drivers/mfd/atmel-flexcom.c | 65 ++---
 1 file changed, 50 insertions(+), 15 deletions(-)

diff --git a/drivers/mfd/atmel-flexcom.c b/drivers/mfd/atmel-flexcom.c
index 064bde9cff5a..ef1235c4a179 100644
--- a/drivers/mfd/atmel-flexcom.c
+++ b/drivers/mfd/atmel-flexcom.c
@@ -39,34 +39,44 @@
 #define FLEX_MR_OPMODE(opmode) (((opmode) << FLEX_MR_OPMODE_OFFSET) &  \
 FLEX_MR_OPMODE_MASK)
 
+struct atmel_flexcom {
+   void __iomem *base;
+   u32 opmode;
+   struct clk *clk;
+};
 
 static int atmel_flexcom_probe(struct platform_device *pdev)
 {
struct device_node *np = pdev->dev.of_node;
-   struct clk *clk;
struct resource *res;
-   void __iomem *base;
-   u32 opmode;
+   struct atmel_flexcom *afc;
int err;
+   u32 val;
+
+   afc = devm_kzalloc(>dev, sizeof(*afc), GFP_KERNEL);
+   if (!afc)
+   return -ENOMEM;
 
-   err = of_property_read_u32(np, "atmel,flexcom-mode", );
+   platform_set_drvdata(pdev, afc);
+
+   err = of_property_read_u32(np, "atmel,flexcom-mode", >opmode);
if (err)
return err;
 
-   if (opmode < ATMEL_FLEXCOM_MODE_USART ||
-   opmode > ATMEL_FLEXCOM_MODE_TWI)
+   if (afc->opmode < ATMEL_FLEXCOM_MODE_USART ||
+   afc->opmode > ATMEL_FLEXCOM_MODE_TWI)
return -EINVAL;
 
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-   base = devm_ioremap_resource(>dev, res);
-   if (IS_ERR(base))
-   return PTR_ERR(base);
+   afc->base = devm_ioremap_resource(>dev, res);
+   if (IS_ERR(afc->base))
+   return PTR_ERR(afc->base);
 
-   clk = devm_clk_get(>dev, NULL);
-   if (IS_ERR(clk))
-   return PTR_ERR(clk);
+   afc->clk = devm_clk_get(>dev, NULL);
+   if (IS_ERR(afc->clk))
+   return PTR_ERR(afc->clk);
 
-   err = clk_prepare_enable(clk);
+   err = clk_prepare_enable(afc->clk);
if (err)
return err;
 
@@ -76,9 +86,10 @@ static int atmel_flexcom_probe(struct platform_device *pdev)
 * inaccessible and are read as zero. Also the external I/O lines of the
 * Flexcom are muxed to reach the selected device.
 */
-   writel(FLEX_MR_OPMODE(opmode), base + FLEX_MR);
+   val = FLEX_MR_OPMODE(afc->opmode);
+   writel(val, afc->base + FLEX_MR);
 
-   clk_disable_unprepare(clk);
+   clk_disable_unprepare(afc->clk);
 
return devm_of_platform_populate(>dev);
 }
@@ -89,10 +100,34 @@ static const struct of_device_id atmel_flexcom_of_match[] 
= {
 };
 MODULE_DEVICE_TABLE(of, atmel_flexcom_of_match);
 
+#ifdef CONFIG_PM_SLEEP
+static int atmel_flexcom_resume(struct device *dev)
+{
+   struct atmel_flexcom *afc = dev_get_drvdata(dev);
+   int err;
+   u32 val;
+
+   err = clk_prepare_enable(afc->clk);
+   if (err)
+   return err;
+
+   val = FLEX_MR_OPMODE(afc->opmode),
+   writel(val, afc->base + FLEX_MR);
+
+   clk_disable_unprepare(afc->clk);
+
+   return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(atmel_flexcom_pm_ops, NULL,
+atmel_flexcom_resume);
+
 static struct platform_driver atmel_flexcom_driver = {
.probe  = atmel_flexcom_probe,
.driver = {
.name   = "atmel_flexcom",
+   .pm = _flexcom_pm_ops,
.of_match_table = atmel_flexcom_of_match,
},
 };
-- 
2.11.0



[PATCH v2 6/9] ehci-atmel: Power down during suspend is normal

2017-09-15 Thread Romain Izard
When an Atmel SoC is suspended with the backup mode, the USB bus will be
powered down. As this is expected, do not return an error to the driver
core when ehci_resume detects it.

Signed-off-by: Romain Izard 
---
 drivers/usb/host/ehci-atmel.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/drivers/usb/host/ehci-atmel.c b/drivers/usb/host/ehci-atmel.c
index 7440722bfbf0..2a8b9bdc0e57 100644
--- a/drivers/usb/host/ehci-atmel.c
+++ b/drivers/usb/host/ehci-atmel.c
@@ -205,7 +205,8 @@ static int __maybe_unused ehci_atmel_drv_resume(struct 
device *dev)
struct atmel_ehci_priv *atmel_ehci = hcd_to_atmel_ehci_priv(hcd);
 
atmel_start_clock(atmel_ehci);
-   return ehci_resume(hcd, false);
+   ehci_resume(hcd, false);
+   return 0;
 }
 
 #ifdef CONFIG_OF
-- 
2.11.0



[PATCH v2 5/9] mtd: nand: atmel: Report PMECC failures as errors

2017-09-15 Thread Romain Izard
It is not normal for the PMECC to fail when trying to fix ECC errors.
Report these cases as errors.

Signed-off-by: Romain Izard <romain.izard@gmail.com>
---
 drivers/mtd/nand/atmel/pmecc.c | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/drivers/mtd/nand/atmel/pmecc.c b/drivers/mtd/nand/atmel/pmecc.c
index 8d1208f38025..2a23f1ff945f 100644
--- a/drivers/mtd/nand/atmel/pmecc.c
+++ b/drivers/mtd/nand/atmel/pmecc.c
@@ -687,6 +687,8 @@ static int atmel_pmecc_err_location(struct atmel_pmecc_user 
*user)
 * Number of roots does not match the degree of smu
 * unable to correct error.
 */
+   dev_err(pmecc->dev,
+   "PMECC: Impossible to calculate error location.\n");
return -EBADMSG;
 }
 
@@ -729,7 +731,7 @@ int atmel_pmecc_correct_sector(struct atmel_pmecc_user 
*user, int sector,
ptr = ecc + byte - sectorsize;
area = "ECC";
} else {
-   dev_dbg(pmecc->dev,
+   dev_err(pmecc->dev,
"Invalid errpos value (%d, max is %d)\n",
errpos, (sectorsize + eccbytes) * 8);
return -EINVAL;
-- 
2.11.0



[PATCH v2 5/9] mtd: nand: atmel: Report PMECC failures as errors

2017-09-15 Thread Romain Izard
It is not normal for the PMECC to fail when trying to fix ECC errors.
Report these cases as errors.

Signed-off-by: Romain Izard 
---
 drivers/mtd/nand/atmel/pmecc.c | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/drivers/mtd/nand/atmel/pmecc.c b/drivers/mtd/nand/atmel/pmecc.c
index 8d1208f38025..2a23f1ff945f 100644
--- a/drivers/mtd/nand/atmel/pmecc.c
+++ b/drivers/mtd/nand/atmel/pmecc.c
@@ -687,6 +687,8 @@ static int atmel_pmecc_err_location(struct atmel_pmecc_user 
*user)
 * Number of roots does not match the degree of smu
 * unable to correct error.
 */
+   dev_err(pmecc->dev,
+   "PMECC: Impossible to calculate error location.\n");
return -EBADMSG;
 }
 
@@ -729,7 +731,7 @@ int atmel_pmecc_correct_sector(struct atmel_pmecc_user 
*user, int sector,
ptr = ecc + byte - sectorsize;
area = "ECC";
} else {
-   dev_dbg(pmecc->dev,
+   dev_err(pmecc->dev,
"Invalid errpos value (%d, max is %d)\n",
errpos, (sectorsize + eccbytes) * 8);
return -EINVAL;
-- 
2.11.0



[PATCH v2 4/9] mtd: nand: atmel: Avoid ECC errors when leaving backup mode

2017-09-15 Thread Romain Izard
During backup mode, the contents of all registers will be cleared as the
SoC will be completely powered down. For a product that boots on NAND
Flash memory, the bootloader will obviously use the related controller
to read the Flash and correct any detected error in the memory, before
handling back control to the kernel's resuming entry point.

In normal devices, it is up to the driver's suspend/resume code to
restore the registers in a valid state. But the PMECC is not a regular
device in the driver model when used with the legacy device tree binding
for the Atmel NAND controller, and suspend/resume code is not called.

As in my case the bootloader leaves the PMECC controller in a programmed
state, and the controller is only reset at boot or after a NAND access,
the first NAND Flash access with the Atmel controller will report
uncorrectable ECC errors.

To avoid this, systematically reset the PMECC controller before using
it.

Signed-off-by: Romain Izard <romain.izard@gmail.com>
---
 drivers/mtd/nand/atmel/pmecc.c | 11 +++
 1 file changed, 3 insertions(+), 8 deletions(-)

diff --git a/drivers/mtd/nand/atmel/pmecc.c b/drivers/mtd/nand/atmel/pmecc.c
index 8c210a5776bc..8d1208f38025 100644
--- a/drivers/mtd/nand/atmel/pmecc.c
+++ b/drivers/mtd/nand/atmel/pmecc.c
@@ -777,6 +777,9 @@ int atmel_pmecc_enable(struct atmel_pmecc_user *user, int 
op)
 
mutex_lock(>pmecc->lock);
 
+   writel(PMECC_CTRL_RST, pmecc->regs.base + ATMEL_PMECC_CTRL);
+   writel(PMECC_CTRL_DISABLE, pmecc->regs.base + ATMEL_PMECC_CTRL);
+
cfg = user->cache.cfg;
if (op == NAND_ECC_WRITE)
cfg |= PMECC_CFG_WRITE_OP;
@@ -797,10 +800,6 @@ EXPORT_SYMBOL_GPL(atmel_pmecc_enable);
 
 void atmel_pmecc_disable(struct atmel_pmecc_user *user)
 {
-   struct atmel_pmecc *pmecc = user->pmecc;
-
-   writel(PMECC_CTRL_RST, pmecc->regs.base + ATMEL_PMECC_CTRL);
-   writel(PMECC_CTRL_DISABLE, pmecc->regs.base + ATMEL_PMECC_CTRL);
mutex_unlock(>pmecc->lock);
 }
 EXPORT_SYMBOL_GPL(atmel_pmecc_disable);
@@ -856,10 +855,6 @@ static struct atmel_pmecc *atmel_pmecc_create(struct 
platform_device *pdev,
/* Disable all interrupts before registering the PMECC handler. */
writel(0x, pmecc->regs.base + ATMEL_PMECC_IDR);
 
-   /* Reset the ECC engine */
-   writel(PMECC_CTRL_RST, pmecc->regs.base + ATMEL_PMECC_CTRL);
-   writel(PMECC_CTRL_DISABLE, pmecc->regs.base + ATMEL_PMECC_CTRL);
-
return pmecc;
 }
 
-- 
2.11.0



[PATCH v2 4/9] mtd: nand: atmel: Avoid ECC errors when leaving backup mode

2017-09-15 Thread Romain Izard
During backup mode, the contents of all registers will be cleared as the
SoC will be completely powered down. For a product that boots on NAND
Flash memory, the bootloader will obviously use the related controller
to read the Flash and correct any detected error in the memory, before
handling back control to the kernel's resuming entry point.

In normal devices, it is up to the driver's suspend/resume code to
restore the registers in a valid state. But the PMECC is not a regular
device in the driver model when used with the legacy device tree binding
for the Atmel NAND controller, and suspend/resume code is not called.

As in my case the bootloader leaves the PMECC controller in a programmed
state, and the controller is only reset at boot or after a NAND access,
the first NAND Flash access with the Atmel controller will report
uncorrectable ECC errors.

To avoid this, systematically reset the PMECC controller before using
it.

Signed-off-by: Romain Izard 
---
 drivers/mtd/nand/atmel/pmecc.c | 11 +++
 1 file changed, 3 insertions(+), 8 deletions(-)

diff --git a/drivers/mtd/nand/atmel/pmecc.c b/drivers/mtd/nand/atmel/pmecc.c
index 8c210a5776bc..8d1208f38025 100644
--- a/drivers/mtd/nand/atmel/pmecc.c
+++ b/drivers/mtd/nand/atmel/pmecc.c
@@ -777,6 +777,9 @@ int atmel_pmecc_enable(struct atmel_pmecc_user *user, int 
op)
 
mutex_lock(>pmecc->lock);
 
+   writel(PMECC_CTRL_RST, pmecc->regs.base + ATMEL_PMECC_CTRL);
+   writel(PMECC_CTRL_DISABLE, pmecc->regs.base + ATMEL_PMECC_CTRL);
+
cfg = user->cache.cfg;
if (op == NAND_ECC_WRITE)
cfg |= PMECC_CFG_WRITE_OP;
@@ -797,10 +800,6 @@ EXPORT_SYMBOL_GPL(atmel_pmecc_enable);
 
 void atmel_pmecc_disable(struct atmel_pmecc_user *user)
 {
-   struct atmel_pmecc *pmecc = user->pmecc;
-
-   writel(PMECC_CTRL_RST, pmecc->regs.base + ATMEL_PMECC_CTRL);
-   writel(PMECC_CTRL_DISABLE, pmecc->regs.base + ATMEL_PMECC_CTRL);
mutex_unlock(>pmecc->lock);
 }
 EXPORT_SYMBOL_GPL(atmel_pmecc_disable);
@@ -856,10 +855,6 @@ static struct atmel_pmecc *atmel_pmecc_create(struct 
platform_device *pdev,
/* Disable all interrupts before registering the PMECC handler. */
writel(0x, pmecc->regs.base + ATMEL_PMECC_IDR);
 
-   /* Reset the ECC engine */
-   writel(PMECC_CTRL_RST, pmecc->regs.base + ATMEL_PMECC_CTRL);
-   writel(PMECC_CTRL_DISABLE, pmecc->regs.base + ATMEL_PMECC_CTRL);
-
return pmecc;
 }
 
-- 
2.11.0



[PATCH v2 0/9] Various patches for SAMA5D2 backup mode

2017-09-15 Thread Romain Izard
While the core of the backup mode for SAMA5D2 has been integrated in
v4.13, it is far from complete. Individual controllers in the chip have
drivers that do not support the reset of the registers during suspend,
and they need to be adapted to handle it.

The first patch uses the clock wakeup code from the prototype backup
mode instead of the version integrated in the mainline, as the mainline
version is not stable. During a test loop with two-second backup
suspend, the mainline version will hang in less than one day, whereas
the prototype version has been running the same test for more than a
week without hanging.

Changes in v2:
* drop the IIO patch duplicating existing code
* determine the number of programmable clocks to save dynamically
* declare a required local variable in the tty/serial patch

Romain Izard (9):
  clk: at91: pmc: Wait for clocks when resuming
  clk: at91: pmc: Save SCSR during suspend
  clk: at91: pmc: Support backup for programmable clocks
  mtd: nand: atmel: Avoid ECC errors when leaving backup mode
  mtd: nand: atmel: Report PMECC failures as errors
  ehci-atmel: Power down during suspend is normal
  pwm: atmel-tcb: Support backup mode
  atmel_flexcom: Support backup mode
  tty/serial: atmel: Prevent a warning on suspend

 drivers/clk/at91/clk-programmable.c |  2 ++
 drivers/clk/at91/pmc.c  | 55 +--
 drivers/clk/at91/pmc.h  |  2 ++
 drivers/mfd/atmel-flexcom.c | 65 -
 drivers/mtd/nand/atmel/pmecc.c  | 15 -
 drivers/pwm/pwm-atmel-tcb.c | 63 +--
 drivers/tty/serial/atmel_serial.c   | 14 
 drivers/usb/host/ehci-atmel.c   |  3 +-
 8 files changed, 182 insertions(+), 37 deletions(-)

-- 
2.11.0



[PATCH v2 0/9] Various patches for SAMA5D2 backup mode

2017-09-15 Thread Romain Izard
While the core of the backup mode for SAMA5D2 has been integrated in
v4.13, it is far from complete. Individual controllers in the chip have
drivers that do not support the reset of the registers during suspend,
and they need to be adapted to handle it.

The first patch uses the clock wakeup code from the prototype backup
mode instead of the version integrated in the mainline, as the mainline
version is not stable. During a test loop with two-second backup
suspend, the mainline version will hang in less than one day, whereas
the prototype version has been running the same test for more than a
week without hanging.

Changes in v2:
* drop the IIO patch duplicating existing code
* determine the number of programmable clocks to save dynamically
* declare a required local variable in the tty/serial patch

Romain Izard (9):
  clk: at91: pmc: Wait for clocks when resuming
  clk: at91: pmc: Save SCSR during suspend
  clk: at91: pmc: Support backup for programmable clocks
  mtd: nand: atmel: Avoid ECC errors when leaving backup mode
  mtd: nand: atmel: Report PMECC failures as errors
  ehci-atmel: Power down during suspend is normal
  pwm: atmel-tcb: Support backup mode
  atmel_flexcom: Support backup mode
  tty/serial: atmel: Prevent a warning on suspend

 drivers/clk/at91/clk-programmable.c |  2 ++
 drivers/clk/at91/pmc.c  | 55 +--
 drivers/clk/at91/pmc.h  |  2 ++
 drivers/mfd/atmel-flexcom.c | 65 -
 drivers/mtd/nand/atmel/pmecc.c  | 15 -
 drivers/pwm/pwm-atmel-tcb.c | 63 +--
 drivers/tty/serial/atmel_serial.c   | 14 
 drivers/usb/host/ehci-atmel.c   |  3 +-
 8 files changed, 182 insertions(+), 37 deletions(-)

-- 
2.11.0



Re: [PATCH v1 01/10] clk: at91: pmc: Wait for clocks when resuming

2017-09-14 Thread Romain Izard
2017-09-13 14:15 GMT+02:00 Nicolas Ferre <nicolas.fe...@microchip.com>:
> On 08/09/2017 at 17:35, Romain Izard wrote:
>> Wait for the syncronization of all clocks when resuming, not only the
>> UPLL clock. Do not use regmap_read_poll_timeout, as it will call BUG()
>> when interrupts are masked, which is the case in here.
>>
>> Signed-off-by: Romain Izard <romain.izard@gmail.com>
>> ---
>>  drivers/clk/at91/pmc.c | 24 
>>  1 file changed, 16 insertions(+), 8 deletions(-)
>>
>> diff --git a/drivers/clk/at91/pmc.c b/drivers/clk/at91/pmc.c
>> index 775af473fe11..5c2b26de303e 100644
>> --- a/drivers/clk/at91/pmc.c
>> +++ b/drivers/clk/at91/pmc.c
>> @@ -107,10 +107,20 @@ static int pmc_suspend(void)
>>   return 0;
>>  }
>>
>> +static bool pmc_ready(unsigned int mask)
>> +{
>> + unsigned int status;
>> +
>> + regmap_read(pmcreg, AT91_PMC_SR, );
>> +
>> + return ((status & mask) == mask) ? 1 : 0;
>> +}
>> +
>>  static void pmc_resume(void)
>>  {
>> - int i, ret = 0;
>> + int i;
>>   u32 tmp;
>> + u32 mask = AT91_PMC_MCKRDY | AT91_PMC_LOCKA;
>>
>>   regmap_read(pmcreg, AT91_PMC_MCKR, );
>>   if (pmc_cache.mckr != tmp)
>> @@ -134,13 +144,11 @@ static void pmc_resume(void)
>>AT91_PMC_PCR_CMD);
>>   }
>>
>> - if (pmc_cache.uckr & AT91_PMC_UPLLEN) {
>> - ret = regmap_read_poll_timeout(pmcreg, AT91_PMC_SR, tmp,
>> -!(tmp & AT91_PMC_LOCKU),
>> -10, 5000);
>> - if (ret)
>> - pr_crit("USB PLL didn't lock when resuming\n");
>> - }
>> + if (pmc_cache.uckr & AT91_PMC_UPLLEN)
>> + mask |= AT91_PMC_LOCKU;
>> +
>> + while (!pmc_ready(mask))
>> + cpu_relax();
>
> Okay, but I would prefer to keep the timeout property in it. So we may
> need to re-implement a timeout way-out here.
>

We need to have a reference clock to measure the timeout delay. If we use
the kernel's timekeeping, it relies on the clocks that we are configuring in
this code. Moreover, my experience with the mainline code is that when
something goes wrong, nothing will work. No oops or panic will be reported,
the device will just stop working.

In my case, I had obvious failures (it just stopped working unless I removed
USB wakeup or activated the console during suspend) but also very rare
failures, that occurred in the bootloader. Those issues were detected when
testing repeated suspend cycles for a night: the memory controller would
never enter the self-refresh mode during the resume sequence.

This led me to question the bootloader's code first, and I set up 4 boards
with the backup prototype code on v4.9 to verify that it was stable on
suspend. I've reached 1.5 million sleep cycles over 3 weeks without
failure, so this hinted towards the difference between the prototype and the
backup code provided for v4.12 (which contained the patch that got in
v4.13). Once I integrated this patch, I've run the v4.12 code for 2 weeks
without issue as well.

In the end, I don't want to touch this code if I do not have to, as checking
that it does not regress is really cumbersome.

-- 
Romain Izard


Re: [PATCH v1 01/10] clk: at91: pmc: Wait for clocks when resuming

2017-09-14 Thread Romain Izard
2017-09-13 14:15 GMT+02:00 Nicolas Ferre :
> On 08/09/2017 at 17:35, Romain Izard wrote:
>> Wait for the syncronization of all clocks when resuming, not only the
>> UPLL clock. Do not use regmap_read_poll_timeout, as it will call BUG()
>> when interrupts are masked, which is the case in here.
>>
>> Signed-off-by: Romain Izard 
>> ---
>>  drivers/clk/at91/pmc.c | 24 
>>  1 file changed, 16 insertions(+), 8 deletions(-)
>>
>> diff --git a/drivers/clk/at91/pmc.c b/drivers/clk/at91/pmc.c
>> index 775af473fe11..5c2b26de303e 100644
>> --- a/drivers/clk/at91/pmc.c
>> +++ b/drivers/clk/at91/pmc.c
>> @@ -107,10 +107,20 @@ static int pmc_suspend(void)
>>   return 0;
>>  }
>>
>> +static bool pmc_ready(unsigned int mask)
>> +{
>> + unsigned int status;
>> +
>> + regmap_read(pmcreg, AT91_PMC_SR, );
>> +
>> + return ((status & mask) == mask) ? 1 : 0;
>> +}
>> +
>>  static void pmc_resume(void)
>>  {
>> - int i, ret = 0;
>> + int i;
>>   u32 tmp;
>> + u32 mask = AT91_PMC_MCKRDY | AT91_PMC_LOCKA;
>>
>>   regmap_read(pmcreg, AT91_PMC_MCKR, );
>>   if (pmc_cache.mckr != tmp)
>> @@ -134,13 +144,11 @@ static void pmc_resume(void)
>>AT91_PMC_PCR_CMD);
>>   }
>>
>> - if (pmc_cache.uckr & AT91_PMC_UPLLEN) {
>> - ret = regmap_read_poll_timeout(pmcreg, AT91_PMC_SR, tmp,
>> -!(tmp & AT91_PMC_LOCKU),
>> -10, 5000);
>> - if (ret)
>> - pr_crit("USB PLL didn't lock when resuming\n");
>> - }
>> + if (pmc_cache.uckr & AT91_PMC_UPLLEN)
>> + mask |= AT91_PMC_LOCKU;
>> +
>> + while (!pmc_ready(mask))
>> + cpu_relax();
>
> Okay, but I would prefer to keep the timeout property in it. So we may
> need to re-implement a timeout way-out here.
>

We need to have a reference clock to measure the timeout delay. If we use
the kernel's timekeeping, it relies on the clocks that we are configuring in
this code. Moreover, my experience with the mainline code is that when
something goes wrong, nothing will work. No oops or panic will be reported,
the device will just stop working.

In my case, I had obvious failures (it just stopped working unless I removed
USB wakeup or activated the console during suspend) but also very rare
failures, that occurred in the bootloader. Those issues were detected when
testing repeated suspend cycles for a night: the memory controller would
never enter the self-refresh mode during the resume sequence.

This led me to question the bootloader's code first, and I set up 4 boards
with the backup prototype code on v4.9 to verify that it was stable on
suspend. I've reached 1.5 million sleep cycles over 3 weeks without
failure, so this hinted towards the difference between the prototype and the
backup code provided for v4.12 (which contained the patch that got in
v4.13). Once I integrated this patch, I've run the v4.12 code for 2 weeks
without issue as well.

In the end, I don't want to touch this code if I do not have to, as checking
that it does not regress is really cumbersome.

-- 
Romain Izard


Re: [PATCH v1 03/10] clk: at91: pmc: Support backup for programmable clocks

2017-09-14 Thread romain izard
2017-09-13 19:03 GMT+02:00 Alexandre Belloni
<alexandre.bell...@free-electrons.com>:
> On 13/09/2017 at 14:29:35 +0200, Nicolas Ferre wrote:
>> On 08/09/2017 at 17:35, Romain Izard wrote:
>> > From: Romain Izard <romain.iz...@mobile-devices.fr>
>> >
>> > Save and restore the System Clock and Programmable Clock register for
>> > the backup use case.
>>
>> "System Clock" seems to be handled in another patch.
>>
>> > Signed-off-by: Romain Izard <romain.izard@gmail.com>
>> > ---
>> >  drivers/clk/at91/pmc.c | 5 +
>> >  1 file changed, 5 insertions(+)
>> >
>> > diff --git a/drivers/clk/at91/pmc.c b/drivers/clk/at91/pmc.c
>> > index 07dc2861ad3f..5421b03553ec 100644
>> > --- a/drivers/clk/at91/pmc.c
>> > +++ b/drivers/clk/at91/pmc.c
>> > @@ -66,6 +66,7 @@ static struct
>> > u32 pcr[PMC_MAX_IDS];
>> > u32 audio_pll0;
>> > u32 audio_pll1;
>> > +   u32 pckr[3];
>>
>> Some products have different numbers of PCK (only 2 on at91sam9x5 for
>> instance)...
>>
>
> My opinion is that it will be time to change that when multiple SoCs will
> need to save their registers.
>
For the next version, I'll add a #define. But as this code requires a
device tree node with the compatible string "atmel,sama5d2-pmc", I believe
that we can ignore other chips for now.


-- 
Romain Izard


Re: [PATCH v1 03/10] clk: at91: pmc: Support backup for programmable clocks

2017-09-14 Thread romain izard
2017-09-13 19:03 GMT+02:00 Alexandre Belloni
:
> On 13/09/2017 at 14:29:35 +0200, Nicolas Ferre wrote:
>> On 08/09/2017 at 17:35, Romain Izard wrote:
>> > From: Romain Izard 
>> >
>> > Save and restore the System Clock and Programmable Clock register for
>> > the backup use case.
>>
>> "System Clock" seems to be handled in another patch.
>>
>> > Signed-off-by: Romain Izard 
>> > ---
>> >  drivers/clk/at91/pmc.c | 5 +
>> >  1 file changed, 5 insertions(+)
>> >
>> > diff --git a/drivers/clk/at91/pmc.c b/drivers/clk/at91/pmc.c
>> > index 07dc2861ad3f..5421b03553ec 100644
>> > --- a/drivers/clk/at91/pmc.c
>> > +++ b/drivers/clk/at91/pmc.c
>> > @@ -66,6 +66,7 @@ static struct
>> > u32 pcr[PMC_MAX_IDS];
>> > u32 audio_pll0;
>> > u32 audio_pll1;
>> > +   u32 pckr[3];
>>
>> Some products have different numbers of PCK (only 2 on at91sam9x5 for
>> instance)...
>>
>
> My opinion is that it will be time to change that when multiple SoCs will
> need to save their registers.
>
For the next version, I'll add a #define. But as this code requires a
device tree node with the compatible string "atmel,sama5d2-pmc", I believe
that we can ignore other chips for now.


-- 
Romain Izard


Re: [PATCH v1 10/10] tty/serial: atmel: Prevent a warning on suspend

2017-09-11 Thread Romain Izard
2017-09-08 17:36 GMT+02:00 Romain Izard <romain.izard@gmail.com>:
> The atmel serial port driver reported the following warning on suspend:
> atmel_usart f802.serial: ttyS1: Unable to drain transmitter
>
> As the ATMEL_US_TXEMPTY status bit in ATMEL_US_CSR is always cleared
> when the transmitter is disabled, we need to know the transmitter's
> state to return the real fifo state. And as ATMEL_US_CR is write-only,
> it is necessary to save the state of the transmitter in a local
> variable, and update the variable when TXEN and TXDIS is written in
> ATMEL_US_CR.
>
> After those changes, atmel_tx_empty can return "empty" on suspend, the
> warning in uart_suspend_port disappears, and suspending is 20ms shorter
> for each enabled Atmel serial port.
>
> Signed-off-by: Romain Izard <romain.izard@gmail.com>
> ---
>  drivers/tty/serial/atmel_serial.c | 13 +
>  1 file changed, 13 insertions(+)
>
> diff --git a/drivers/tty/serial/atmel_serial.c 
> b/drivers/tty/serial/atmel_serial.c
> index 7551cab438ff..195c0d1b594e 100644
> --- a/drivers/tty/serial/atmel_serial.c
> +++ b/drivers/tty/serial/atmel_serial.c
> @@ -171,6 +171,7 @@ struct atmel_uart_port {
> boolhas_hw_timer;
> struct timer_list   uart_timer;
>
> +   booltx_stopped;
> boolsuspended;
> unsigned intpending;
> unsigned intpending_status;
> @@ -380,6 +381,10 @@ static int atmel_config_rs485(struct uart_port *port,
>   */
>  static u_int atmel_tx_empty(struct uart_port *port)
>  {
> +   struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
> +
> +   if (atmel_port->tx_stopped)
> +   return TIOCSER_TEMT;
> return (atmel_uart_readl(port, ATMEL_US_CSR) & ATMEL_US_TXEMPTY) ?
> TIOCSER_TEMT :
> 0;
> @@ -485,6 +490,7 @@ static void atmel_stop_tx(struct uart_port *port)
>  * is fully transmitted.
>  */
> atmel_uart_writel(port, ATMEL_US_CR, ATMEL_US_TXDIS);
> +   atmel_port->tx_stopped = true;
>
> /* Disable interrupts */
> atmel_uart_writel(port, ATMEL_US_IDR, atmel_port->tx_done_mask);
> @@ -492,6 +498,7 @@ static void atmel_stop_tx(struct uart_port *port)
> if ((port->rs485.flags & SER_RS485_ENABLED) &&
> !(port->rs485.flags & SER_RS485_RX_DURING_TX))
> atmel_start_rx(port);
> +
>  }
>
>  /*
> @@ -521,6 +528,7 @@ static void atmel_start_tx(struct uart_port *port)
>
> /* re-enable the transmitter */
> atmel_uart_writel(port, ATMEL_US_CR, ATMEL_US_TXEN);
> +   atmel_port->tx_stopped = false;
>  }
>
>  /*
> @@ -1866,6 +1874,7 @@ static int atmel_startup(struct uart_port *port)
> atmel_uart_writel(port, ATMEL_US_CR, ATMEL_US_RSTSTA | 
> ATMEL_US_RSTRX);
> /* enable xmit & rcvr */
> atmel_uart_writel(port, ATMEL_US_CR, ATMEL_US_TXEN | ATMEL_US_RXEN);
> +   atmel_port->tx_stopped = false;
>
> setup_timer(_port->uart_timer,
> atmel_uart_timer_callback,
> @@ -2122,6 +2131,7 @@ static void atmel_set_termios(struct uart_port *port, 
> struct ktermios *termios,
>
> /* disable receiver and transmitter */
> atmel_uart_writel(port, ATMEL_US_CR, ATMEL_US_TXDIS | ATMEL_US_RXDIS);
> +   atmel_port->tx_stopped = true;
>
> /* mode */
> if (port->rs485.flags & SER_RS485_ENABLED) {
> @@ -2207,6 +2217,7 @@ static void atmel_set_termios(struct uart_port *port, 
> struct ktermios *termios,
> atmel_uart_writel(port, ATMEL_US_BRGR, quot);
> atmel_uart_writel(port, ATMEL_US_CR, ATMEL_US_RSTSTA | 
> ATMEL_US_RSTRX);
> atmel_uart_writel(port, ATMEL_US_CR, ATMEL_US_TXEN | ATMEL_US_RXEN);
> +   atmel_port->tx_stopped = false;
>
> /* restore interrupts */
> atmel_uart_writel(port, ATMEL_US_IER, imr);
> @@ -2450,6 +2461,7 @@ static void atmel_console_write(struct console *co, 
> const char *s, u_int count)
>
> /* Make sure that tx path is actually able to send characters */
> atmel_uart_writel(port, ATMEL_US_CR, ATMEL_US_TXEN);
> +   atmel_port->tx_stopped = false;
>
> uart_console_write(port, s, count, atmel_console_putchar);
>
> @@ -2528,6 +2540,7 @@ static int __init atmel_console_setup(struct console 
> *co, char *options)
> atmel_uart_writel(port, ATMEL_US_IDR, -1);
> atmel_uart_writel(port, ATMEL_US_CR, ATMEL_US_RSTSTA | 
> ATMEL_US_RSTRX);
> atmel_uart_writel(port, 

Re: [PATCH v1 10/10] tty/serial: atmel: Prevent a warning on suspend

2017-09-11 Thread Romain Izard
2017-09-08 17:36 GMT+02:00 Romain Izard :
> The atmel serial port driver reported the following warning on suspend:
> atmel_usart f802.serial: ttyS1: Unable to drain transmitter
>
> As the ATMEL_US_TXEMPTY status bit in ATMEL_US_CSR is always cleared
> when the transmitter is disabled, we need to know the transmitter's
> state to return the real fifo state. And as ATMEL_US_CR is write-only,
> it is necessary to save the state of the transmitter in a local
> variable, and update the variable when TXEN and TXDIS is written in
> ATMEL_US_CR.
>
> After those changes, atmel_tx_empty can return "empty" on suspend, the
> warning in uart_suspend_port disappears, and suspending is 20ms shorter
> for each enabled Atmel serial port.
>
> Signed-off-by: Romain Izard 
> ---
>  drivers/tty/serial/atmel_serial.c | 13 +
>  1 file changed, 13 insertions(+)
>
> diff --git a/drivers/tty/serial/atmel_serial.c 
> b/drivers/tty/serial/atmel_serial.c
> index 7551cab438ff..195c0d1b594e 100644
> --- a/drivers/tty/serial/atmel_serial.c
> +++ b/drivers/tty/serial/atmel_serial.c
> @@ -171,6 +171,7 @@ struct atmel_uart_port {
> boolhas_hw_timer;
> struct timer_list   uart_timer;
>
> +   booltx_stopped;
> boolsuspended;
> unsigned intpending;
> unsigned intpending_status;
> @@ -380,6 +381,10 @@ static int atmel_config_rs485(struct uart_port *port,
>   */
>  static u_int atmel_tx_empty(struct uart_port *port)
>  {
> +   struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
> +
> +   if (atmel_port->tx_stopped)
> +   return TIOCSER_TEMT;
> return (atmel_uart_readl(port, ATMEL_US_CSR) & ATMEL_US_TXEMPTY) ?
> TIOCSER_TEMT :
> 0;
> @@ -485,6 +490,7 @@ static void atmel_stop_tx(struct uart_port *port)
>  * is fully transmitted.
>  */
> atmel_uart_writel(port, ATMEL_US_CR, ATMEL_US_TXDIS);
> +   atmel_port->tx_stopped = true;
>
> /* Disable interrupts */
> atmel_uart_writel(port, ATMEL_US_IDR, atmel_port->tx_done_mask);
> @@ -492,6 +498,7 @@ static void atmel_stop_tx(struct uart_port *port)
> if ((port->rs485.flags & SER_RS485_ENABLED) &&
> !(port->rs485.flags & SER_RS485_RX_DURING_TX))
> atmel_start_rx(port);
> +
>  }
>
>  /*
> @@ -521,6 +528,7 @@ static void atmel_start_tx(struct uart_port *port)
>
> /* re-enable the transmitter */
> atmel_uart_writel(port, ATMEL_US_CR, ATMEL_US_TXEN);
> +   atmel_port->tx_stopped = false;
>  }
>
>  /*
> @@ -1866,6 +1874,7 @@ static int atmel_startup(struct uart_port *port)
> atmel_uart_writel(port, ATMEL_US_CR, ATMEL_US_RSTSTA | 
> ATMEL_US_RSTRX);
> /* enable xmit & rcvr */
> atmel_uart_writel(port, ATMEL_US_CR, ATMEL_US_TXEN | ATMEL_US_RXEN);
> +   atmel_port->tx_stopped = false;
>
> setup_timer(_port->uart_timer,
> atmel_uart_timer_callback,
> @@ -2122,6 +2131,7 @@ static void atmel_set_termios(struct uart_port *port, 
> struct ktermios *termios,
>
> /* disable receiver and transmitter */
> atmel_uart_writel(port, ATMEL_US_CR, ATMEL_US_TXDIS | ATMEL_US_RXDIS);
> +   atmel_port->tx_stopped = true;
>
> /* mode */
> if (port->rs485.flags & SER_RS485_ENABLED) {
> @@ -2207,6 +2217,7 @@ static void atmel_set_termios(struct uart_port *port, 
> struct ktermios *termios,
> atmel_uart_writel(port, ATMEL_US_BRGR, quot);
> atmel_uart_writel(port, ATMEL_US_CR, ATMEL_US_RSTSTA | 
> ATMEL_US_RSTRX);
> atmel_uart_writel(port, ATMEL_US_CR, ATMEL_US_TXEN | ATMEL_US_RXEN);
> +   atmel_port->tx_stopped = false;
>
> /* restore interrupts */
> atmel_uart_writel(port, ATMEL_US_IER, imr);
> @@ -2450,6 +2461,7 @@ static void atmel_console_write(struct console *co, 
> const char *s, u_int count)
>
> /* Make sure that tx path is actually able to send characters */
> atmel_uart_writel(port, ATMEL_US_CR, ATMEL_US_TXEN);
> +   atmel_port->tx_stopped = false;
>
> uart_console_write(port, s, count, atmel_console_putchar);
>
> @@ -2528,6 +2540,7 @@ static int __init atmel_console_setup(struct console 
> *co, char *options)
> atmel_uart_writel(port, ATMEL_US_IDR, -1);
> atmel_uart_writel(port, ATMEL_US_CR, ATMEL_US_RSTSTA | 
> ATMEL_US_RSTRX);
> atmel_uart_writel(port, ATMEL_US_CR, ATMEL_US_TXEN | ATMEL_US_RXEN);
> +   atmel_port->tx_stopped = false;
>
> if (options)
> uart_parse_options(options, , , , );
> --
> 2.11.0
>

Unfortunately this patch was broken when I reported it from my branch to
the v4.13, as it does not build because of the missing declaration of
'atmel_port' in 'atmel_console_setup'.

I'll send a corrected version for v2.

-- 
Romain Izard


Re: [PATCH v1 07/10] iio:adc:at91-sama5d2: Support backup mode

2017-09-08 Thread Romain Izard
2017-09-08 18:03 GMT+02:00 Nicolas Ferre <nicolas.fe...@microchip.com>:
> On 08/09/2017 at 17:36, Romain Izard wrote:
>> Support the backup mode for platform suspend, by restoring the hardware
>> registers on resume.
>>
>> Signed-off-by: Romain Izard <romain.izard@gmail.com>
>
> Romain,
>
> Thanks for your series: definitively some of your patches need to be
> integrated (I've merged some of them in our current linux-4.9-at91 branch.
> However, It seems that some of your additions have already been
> submitted and/or accepted by maintainers.
> For instance an equivalent of this one seems already in Linus' tree:
> 500a2eefd6b16ba141a8fb777ea6962d2eb65e3b ("iio: adc: at91-sama5d2_adc:
> add support for suspend/resume functionality").
>
> Please tell us if it fits what your observed on this driver (or others).
>
> Regards,
>
>> ---
>>  drivers/iio/adc/at91-sama5d2_adc.c | 71 
>> --
>>  1 file changed, 61 insertions(+), 10 deletions(-)
>>
>> diff --git a/drivers/iio/adc/at91-sama5d2_adc.c 
>> b/drivers/iio/adc/at91-sama5d2_adc.c
>> index e10dca3ed74b..f9718c863363 100644
>> --- a/drivers/iio/adc/at91-sama5d2_adc.c
>> +++ b/drivers/iio/adc/at91-sama5d2_adc.c
>> @@ -200,6 +200,7 @@ struct at91_adc_state {
>>   u32 conversion_value;
>>   struct at91_adc_soc_infosoc_info;
>>   wait_queue_head_t   wq_data_available;
>> + unsigned intcurrent_rate;
>>   /*
>>* lock to prevent concurrent 'single conversion' requests through
>>* sysfs.
>> @@ -269,6 +270,8 @@ static void at91_adc_setup_samp_freq(struct 
>> at91_adc_state *st, unsigned freq)
>>   mr |= AT91_SAMA5D2_MR_PRESCAL(prescal);
>>   at91_adc_writel(st, AT91_SAMA5D2_MR, mr);
>>
>> + st->current_rate = freq;
>> +
>>   dev_dbg(_dev->dev, "freq: %u, startup: %u, prescal: %u\n",
>>   freq, startup, prescal);
>>  }
>> @@ -375,7 +378,9 @@ static int at91_adc_write_raw(struct iio_dev *indio_dev,
>>   val > st->soc_info.max_sample_rate)
>>   return -EINVAL;
>>
>> + mutex_lock(>lock);
>>   at91_adc_setup_samp_freq(st, val);
>> + mutex_unlock(>lock);
>>
>>   return 0;
>>  }
>> @@ -386,6 +391,21 @@ static const struct iio_info at91_adc_info = {
>>   .driver_module = THIS_MODULE,
>>  };
>>
>> +static void at91_adc_init_hw(struct at91_adc_state *st, unsigned int freq)
>> +{
>> + at91_adc_writel(st, AT91_SAMA5D2_CR, AT91_SAMA5D2_CR_SWRST);
>> + at91_adc_writel(st, AT91_SAMA5D2_IDR, 0x);
>> + /*
>> +  * Transfer field must be set to 2 according to the datasheet and
>> +  * allows different analog settings for each channel.
>> +  */
>> + at91_adc_writel(st, AT91_SAMA5D2_MR,
>> + AT91_SAMA5D2_MR_TRANSFER(2) | AT91_SAMA5D2_MR_ANACH);
>> +
>> + at91_adc_setup_samp_freq(st, freq);
>> +
>> +}
>> +
>>  static int at91_adc_probe(struct platform_device *pdev)
>>  {
>>   struct iio_dev *indio_dev;
>> @@ -482,16 +502,7 @@ static int at91_adc_probe(struct platform_device *pdev)
>>   goto vref_disable;
>>   }
>>
>> - at91_adc_writel(st, AT91_SAMA5D2_CR, AT91_SAMA5D2_CR_SWRST);
>> - at91_adc_writel(st, AT91_SAMA5D2_IDR, 0x);
>> - /*
>> -  * Transfer field must be set to 2 according to the datasheet and
>> -  * allows different analog settings for each channel.
>> -  */
>> - at91_adc_writel(st, AT91_SAMA5D2_MR,
>> - AT91_SAMA5D2_MR_TRANSFER(2) | AT91_SAMA5D2_MR_ANACH);
>> -
>> - at91_adc_setup_samp_freq(st, st->soc_info.min_sample_rate);
>> + at91_adc_init_hw(st, st->soc_info.min_sample_rate);
>>
>>   ret = clk_prepare_enable(st->per_clk);
>>   if (ret)
>> @@ -541,12 +552,52 @@ static const struct of_device_id at91_adc_dt_match[] = 
>> {
>>  };
>>  MODULE_DEVICE_TABLE(of, at91_adc_dt_match);
>>
>> +#ifdef CONFIG_PM_SLEEP
>> +static int at91_adc_suspend(struct device *dev)
>> +{
>> + struct platform_device *pdev = to_platform_device(dev);
>> + struct iio_dev *indio_dev = platform_get_drvdata(pdev);
>> + struct at91_adc_state *st = iio_priv(indio_dev);
>> +
>> + clk_disable_unprepare(st->per_clk);
>> +
>> + regulator_disable(st-

Re: [PATCH v1 07/10] iio:adc:at91-sama5d2: Support backup mode

2017-09-08 Thread Romain Izard
2017-09-08 18:03 GMT+02:00 Nicolas Ferre :
> On 08/09/2017 at 17:36, Romain Izard wrote:
>> Support the backup mode for platform suspend, by restoring the hardware
>> registers on resume.
>>
>> Signed-off-by: Romain Izard 
>
> Romain,
>
> Thanks for your series: definitively some of your patches need to be
> integrated (I've merged some of them in our current linux-4.9-at91 branch.
> However, It seems that some of your additions have already been
> submitted and/or accepted by maintainers.
> For instance an equivalent of this one seems already in Linus' tree:
> 500a2eefd6b16ba141a8fb777ea6962d2eb65e3b ("iio: adc: at91-sama5d2_adc:
> add support for suspend/resume functionality").
>
> Please tell us if it fits what your observed on this driver (or others).
>
> Regards,
>
>> ---
>>  drivers/iio/adc/at91-sama5d2_adc.c | 71 
>> --
>>  1 file changed, 61 insertions(+), 10 deletions(-)
>>
>> diff --git a/drivers/iio/adc/at91-sama5d2_adc.c 
>> b/drivers/iio/adc/at91-sama5d2_adc.c
>> index e10dca3ed74b..f9718c863363 100644
>> --- a/drivers/iio/adc/at91-sama5d2_adc.c
>> +++ b/drivers/iio/adc/at91-sama5d2_adc.c
>> @@ -200,6 +200,7 @@ struct at91_adc_state {
>>   u32 conversion_value;
>>   struct at91_adc_soc_infosoc_info;
>>   wait_queue_head_t   wq_data_available;
>> + unsigned intcurrent_rate;
>>   /*
>>* lock to prevent concurrent 'single conversion' requests through
>>* sysfs.
>> @@ -269,6 +270,8 @@ static void at91_adc_setup_samp_freq(struct 
>> at91_adc_state *st, unsigned freq)
>>   mr |= AT91_SAMA5D2_MR_PRESCAL(prescal);
>>   at91_adc_writel(st, AT91_SAMA5D2_MR, mr);
>>
>> + st->current_rate = freq;
>> +
>>   dev_dbg(_dev->dev, "freq: %u, startup: %u, prescal: %u\n",
>>   freq, startup, prescal);
>>  }
>> @@ -375,7 +378,9 @@ static int at91_adc_write_raw(struct iio_dev *indio_dev,
>>   val > st->soc_info.max_sample_rate)
>>   return -EINVAL;
>>
>> + mutex_lock(>lock);
>>   at91_adc_setup_samp_freq(st, val);
>> + mutex_unlock(>lock);
>>
>>   return 0;
>>  }
>> @@ -386,6 +391,21 @@ static const struct iio_info at91_adc_info = {
>>   .driver_module = THIS_MODULE,
>>  };
>>
>> +static void at91_adc_init_hw(struct at91_adc_state *st, unsigned int freq)
>> +{
>> + at91_adc_writel(st, AT91_SAMA5D2_CR, AT91_SAMA5D2_CR_SWRST);
>> + at91_adc_writel(st, AT91_SAMA5D2_IDR, 0x);
>> + /*
>> +  * Transfer field must be set to 2 according to the datasheet and
>> +  * allows different analog settings for each channel.
>> +  */
>> + at91_adc_writel(st, AT91_SAMA5D2_MR,
>> + AT91_SAMA5D2_MR_TRANSFER(2) | AT91_SAMA5D2_MR_ANACH);
>> +
>> + at91_adc_setup_samp_freq(st, freq);
>> +
>> +}
>> +
>>  static int at91_adc_probe(struct platform_device *pdev)
>>  {
>>   struct iio_dev *indio_dev;
>> @@ -482,16 +502,7 @@ static int at91_adc_probe(struct platform_device *pdev)
>>   goto vref_disable;
>>   }
>>
>> - at91_adc_writel(st, AT91_SAMA5D2_CR, AT91_SAMA5D2_CR_SWRST);
>> - at91_adc_writel(st, AT91_SAMA5D2_IDR, 0x);
>> - /*
>> -  * Transfer field must be set to 2 according to the datasheet and
>> -  * allows different analog settings for each channel.
>> -  */
>> - at91_adc_writel(st, AT91_SAMA5D2_MR,
>> - AT91_SAMA5D2_MR_TRANSFER(2) | AT91_SAMA5D2_MR_ANACH);
>> -
>> - at91_adc_setup_samp_freq(st, st->soc_info.min_sample_rate);
>> + at91_adc_init_hw(st, st->soc_info.min_sample_rate);
>>
>>   ret = clk_prepare_enable(st->per_clk);
>>   if (ret)
>> @@ -541,12 +552,52 @@ static const struct of_device_id at91_adc_dt_match[] = 
>> {
>>  };
>>  MODULE_DEVICE_TABLE(of, at91_adc_dt_match);
>>
>> +#ifdef CONFIG_PM_SLEEP
>> +static int at91_adc_suspend(struct device *dev)
>> +{
>> + struct platform_device *pdev = to_platform_device(dev);
>> + struct iio_dev *indio_dev = platform_get_drvdata(pdev);
>> + struct at91_adc_state *st = iio_priv(indio_dev);
>> +
>> + clk_disable_unprepare(st->per_clk);
>> +
>> + regulator_disable(st->vref);
>> + regulator_disable(st->reg);
>>

[PATCH v1 02/10] clk: at91: pmc: Save SCSR during suspend

2017-09-08 Thread Romain Izard
The contents of the System Clock Status Register (SCSR) needs to be
restored into the System Clock Enable Register (SCER).

As the bootloader will restore some clocks by itself, the issue can be
missed as only the USB controller, the LCD controller, the Image Sensor
controller and the programmable clocks will be impacted.

Fix the obvious typo in the suspend/resume code, as the IMR register
does not need to be saved twice.

Signed-off-by: Romain Izard <romain.izard@gmail.com>
---
 drivers/clk/at91/pmc.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/drivers/clk/at91/pmc.c b/drivers/clk/at91/pmc.c
index 5c2b26de303e..07dc2861ad3f 100644
--- a/drivers/clk/at91/pmc.c
+++ b/drivers/clk/at91/pmc.c
@@ -86,7 +86,7 @@ static int pmc_suspend(void)
 {
int i;
 
-   regmap_read(pmcreg, AT91_PMC_IMR, _cache.scsr);
+   regmap_read(pmcreg, AT91_PMC_SCSR, _cache.scsr);
regmap_read(pmcreg, AT91_PMC_PCSR, _cache.pcsr0);
regmap_read(pmcreg, AT91_CKGR_UCKR, _cache.uckr);
regmap_read(pmcreg, AT91_CKGR_MOR, _cache.mor);
@@ -129,7 +129,7 @@ static void pmc_resume(void)
if (pmc_cache.pllar != tmp)
pr_warn("PLLAR was not configured properly by the firmware\n");
 
-   regmap_write(pmcreg, AT91_PMC_IMR, pmc_cache.scsr);
+   regmap_write(pmcreg, AT91_PMC_SCER, pmc_cache.scsr);
regmap_write(pmcreg, AT91_PMC_PCER, pmc_cache.pcsr0);
regmap_write(pmcreg, AT91_CKGR_UCKR, pmc_cache.uckr);
regmap_write(pmcreg, AT91_CKGR_MOR, pmc_cache.mor);
-- 
2.11.0



[PATCH v1 02/10] clk: at91: pmc: Save SCSR during suspend

2017-09-08 Thread Romain Izard
The contents of the System Clock Status Register (SCSR) needs to be
restored into the System Clock Enable Register (SCER).

As the bootloader will restore some clocks by itself, the issue can be
missed as only the USB controller, the LCD controller, the Image Sensor
controller and the programmable clocks will be impacted.

Fix the obvious typo in the suspend/resume code, as the IMR register
does not need to be saved twice.

Signed-off-by: Romain Izard 
---
 drivers/clk/at91/pmc.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/drivers/clk/at91/pmc.c b/drivers/clk/at91/pmc.c
index 5c2b26de303e..07dc2861ad3f 100644
--- a/drivers/clk/at91/pmc.c
+++ b/drivers/clk/at91/pmc.c
@@ -86,7 +86,7 @@ static int pmc_suspend(void)
 {
int i;
 
-   regmap_read(pmcreg, AT91_PMC_IMR, _cache.scsr);
+   regmap_read(pmcreg, AT91_PMC_SCSR, _cache.scsr);
regmap_read(pmcreg, AT91_PMC_PCSR, _cache.pcsr0);
regmap_read(pmcreg, AT91_CKGR_UCKR, _cache.uckr);
regmap_read(pmcreg, AT91_CKGR_MOR, _cache.mor);
@@ -129,7 +129,7 @@ static void pmc_resume(void)
if (pmc_cache.pllar != tmp)
pr_warn("PLLAR was not configured properly by the firmware\n");
 
-   regmap_write(pmcreg, AT91_PMC_IMR, pmc_cache.scsr);
+   regmap_write(pmcreg, AT91_PMC_SCER, pmc_cache.scsr);
regmap_write(pmcreg, AT91_PMC_PCER, pmc_cache.pcsr0);
regmap_write(pmcreg, AT91_CKGR_UCKR, pmc_cache.uckr);
regmap_write(pmcreg, AT91_CKGR_MOR, pmc_cache.mor);
-- 
2.11.0



[PATCH v1 03/10] clk: at91: pmc: Support backup for programmable clocks

2017-09-08 Thread Romain Izard
From: Romain Izard <romain.iz...@mobile-devices.fr>

Save and restore the System Clock and Programmable Clock register for
the backup use case.

Signed-off-by: Romain Izard <romain.izard@gmail.com>
---
 drivers/clk/at91/pmc.c | 5 +
 1 file changed, 5 insertions(+)

diff --git a/drivers/clk/at91/pmc.c b/drivers/clk/at91/pmc.c
index 07dc2861ad3f..5421b03553ec 100644
--- a/drivers/clk/at91/pmc.c
+++ b/drivers/clk/at91/pmc.c
@@ -66,6 +66,7 @@ static struct
u32 pcr[PMC_MAX_IDS];
u32 audio_pll0;
u32 audio_pll1;
+   u32 pckr[3];
 } pmc_cache;
 
 void pmc_register_id(u8 id)
@@ -103,6 +104,8 @@ static int pmc_suspend(void)
regmap_read(pmcreg, AT91_PMC_PCR,
_cache.pcr[registered_ids[i]]);
}
+   for (i = 0; i < 3; i++)
+   regmap_read(pmcreg, AT91_PMC_PCKR(i), _cache.pckr[i]);
 
return 0;
 }
@@ -143,6 +146,8 @@ static void pmc_resume(void)
 pmc_cache.pcr[registered_ids[i]] |
 AT91_PMC_PCR_CMD);
}
+   for (i = 0; i < 3; i++)
+   regmap_write(pmcreg, AT91_PMC_PCKR(i), pmc_cache.pckr[i]);
 
if (pmc_cache.uckr & AT91_PMC_UPLLEN)
mask |= AT91_PMC_LOCKU;
-- 
2.11.0



[PATCH v1 03/10] clk: at91: pmc: Support backup for programmable clocks

2017-09-08 Thread Romain Izard
From: Romain Izard 

Save and restore the System Clock and Programmable Clock register for
the backup use case.

Signed-off-by: Romain Izard 
---
 drivers/clk/at91/pmc.c | 5 +
 1 file changed, 5 insertions(+)

diff --git a/drivers/clk/at91/pmc.c b/drivers/clk/at91/pmc.c
index 07dc2861ad3f..5421b03553ec 100644
--- a/drivers/clk/at91/pmc.c
+++ b/drivers/clk/at91/pmc.c
@@ -66,6 +66,7 @@ static struct
u32 pcr[PMC_MAX_IDS];
u32 audio_pll0;
u32 audio_pll1;
+   u32 pckr[3];
 } pmc_cache;
 
 void pmc_register_id(u8 id)
@@ -103,6 +104,8 @@ static int pmc_suspend(void)
regmap_read(pmcreg, AT91_PMC_PCR,
_cache.pcr[registered_ids[i]]);
}
+   for (i = 0; i < 3; i++)
+   regmap_read(pmcreg, AT91_PMC_PCKR(i), _cache.pckr[i]);
 
return 0;
 }
@@ -143,6 +146,8 @@ static void pmc_resume(void)
 pmc_cache.pcr[registered_ids[i]] |
 AT91_PMC_PCR_CMD);
}
+   for (i = 0; i < 3; i++)
+   regmap_write(pmcreg, AT91_PMC_PCKR(i), pmc_cache.pckr[i]);
 
if (pmc_cache.uckr & AT91_PMC_UPLLEN)
mask |= AT91_PMC_LOCKU;
-- 
2.11.0



[PATCH v1 01/10] clk: at91: pmc: Wait for clocks when resuming

2017-09-08 Thread Romain Izard
Wait for the syncronization of all clocks when resuming, not only the
UPLL clock. Do not use regmap_read_poll_timeout, as it will call BUG()
when interrupts are masked, which is the case in here.

Signed-off-by: Romain Izard <romain.izard@gmail.com>
---
 drivers/clk/at91/pmc.c | 24 
 1 file changed, 16 insertions(+), 8 deletions(-)

diff --git a/drivers/clk/at91/pmc.c b/drivers/clk/at91/pmc.c
index 775af473fe11..5c2b26de303e 100644
--- a/drivers/clk/at91/pmc.c
+++ b/drivers/clk/at91/pmc.c
@@ -107,10 +107,20 @@ static int pmc_suspend(void)
return 0;
 }
 
+static bool pmc_ready(unsigned int mask)
+{
+   unsigned int status;
+
+   regmap_read(pmcreg, AT91_PMC_SR, );
+
+   return ((status & mask) == mask) ? 1 : 0;
+}
+
 static void pmc_resume(void)
 {
-   int i, ret = 0;
+   int i;
u32 tmp;
+   u32 mask = AT91_PMC_MCKRDY | AT91_PMC_LOCKA;
 
regmap_read(pmcreg, AT91_PMC_MCKR, );
if (pmc_cache.mckr != tmp)
@@ -134,13 +144,11 @@ static void pmc_resume(void)
 AT91_PMC_PCR_CMD);
}
 
-   if (pmc_cache.uckr & AT91_PMC_UPLLEN) {
-   ret = regmap_read_poll_timeout(pmcreg, AT91_PMC_SR, tmp,
-  !(tmp & AT91_PMC_LOCKU),
-  10, 5000);
-   if (ret)
-   pr_crit("USB PLL didn't lock when resuming\n");
-   }
+   if (pmc_cache.uckr & AT91_PMC_UPLLEN)
+   mask |= AT91_PMC_LOCKU;
+
+   while (!pmc_ready(mask))
+   cpu_relax();
 }
 
 static struct syscore_ops pmc_syscore_ops = {
-- 
2.11.0



[PATCH v1 01/10] clk: at91: pmc: Wait for clocks when resuming

2017-09-08 Thread Romain Izard
Wait for the syncronization of all clocks when resuming, not only the
UPLL clock. Do not use regmap_read_poll_timeout, as it will call BUG()
when interrupts are masked, which is the case in here.

Signed-off-by: Romain Izard 
---
 drivers/clk/at91/pmc.c | 24 
 1 file changed, 16 insertions(+), 8 deletions(-)

diff --git a/drivers/clk/at91/pmc.c b/drivers/clk/at91/pmc.c
index 775af473fe11..5c2b26de303e 100644
--- a/drivers/clk/at91/pmc.c
+++ b/drivers/clk/at91/pmc.c
@@ -107,10 +107,20 @@ static int pmc_suspend(void)
return 0;
 }
 
+static bool pmc_ready(unsigned int mask)
+{
+   unsigned int status;
+
+   regmap_read(pmcreg, AT91_PMC_SR, );
+
+   return ((status & mask) == mask) ? 1 : 0;
+}
+
 static void pmc_resume(void)
 {
-   int i, ret = 0;
+   int i;
u32 tmp;
+   u32 mask = AT91_PMC_MCKRDY | AT91_PMC_LOCKA;
 
regmap_read(pmcreg, AT91_PMC_MCKR, );
if (pmc_cache.mckr != tmp)
@@ -134,13 +144,11 @@ static void pmc_resume(void)
 AT91_PMC_PCR_CMD);
}
 
-   if (pmc_cache.uckr & AT91_PMC_UPLLEN) {
-   ret = regmap_read_poll_timeout(pmcreg, AT91_PMC_SR, tmp,
-  !(tmp & AT91_PMC_LOCKU),
-  10, 5000);
-   if (ret)
-   pr_crit("USB PLL didn't lock when resuming\n");
-   }
+   if (pmc_cache.uckr & AT91_PMC_UPLLEN)
+   mask |= AT91_PMC_LOCKU;
+
+   while (!pmc_ready(mask))
+   cpu_relax();
 }
 
 static struct syscore_ops pmc_syscore_ops = {
-- 
2.11.0



[PATCH v1 05/10] mtd: nand: atmel: Report PMECC failures as errors

2017-09-08 Thread Romain Izard
It is not normal for the PMECC to fail when trying to fix ECC errors.
Report these cases as errors.

Signed-off-by: Romain Izard <romain.izard@gmail.com>
---
 drivers/mtd/nand/atmel/pmecc.c | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/drivers/mtd/nand/atmel/pmecc.c b/drivers/mtd/nand/atmel/pmecc.c
index 8d1208f38025..2a23f1ff945f 100644
--- a/drivers/mtd/nand/atmel/pmecc.c
+++ b/drivers/mtd/nand/atmel/pmecc.c
@@ -687,6 +687,8 @@ static int atmel_pmecc_err_location(struct atmel_pmecc_user 
*user)
 * Number of roots does not match the degree of smu
 * unable to correct error.
 */
+   dev_err(pmecc->dev,
+   "PMECC: Impossible to calculate error location.\n");
return -EBADMSG;
 }
 
@@ -729,7 +731,7 @@ int atmel_pmecc_correct_sector(struct atmel_pmecc_user 
*user, int sector,
ptr = ecc + byte - sectorsize;
area = "ECC";
} else {
-   dev_dbg(pmecc->dev,
+   dev_err(pmecc->dev,
"Invalid errpos value (%d, max is %d)\n",
errpos, (sectorsize + eccbytes) * 8);
return -EINVAL;
-- 
2.11.0



[PATCH v1 05/10] mtd: nand: atmel: Report PMECC failures as errors

2017-09-08 Thread Romain Izard
It is not normal for the PMECC to fail when trying to fix ECC errors.
Report these cases as errors.

Signed-off-by: Romain Izard 
---
 drivers/mtd/nand/atmel/pmecc.c | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/drivers/mtd/nand/atmel/pmecc.c b/drivers/mtd/nand/atmel/pmecc.c
index 8d1208f38025..2a23f1ff945f 100644
--- a/drivers/mtd/nand/atmel/pmecc.c
+++ b/drivers/mtd/nand/atmel/pmecc.c
@@ -687,6 +687,8 @@ static int atmel_pmecc_err_location(struct atmel_pmecc_user 
*user)
 * Number of roots does not match the degree of smu
 * unable to correct error.
 */
+   dev_err(pmecc->dev,
+   "PMECC: Impossible to calculate error location.\n");
return -EBADMSG;
 }
 
@@ -729,7 +731,7 @@ int atmel_pmecc_correct_sector(struct atmel_pmecc_user 
*user, int sector,
ptr = ecc + byte - sectorsize;
area = "ECC";
} else {
-   dev_dbg(pmecc->dev,
+   dev_err(pmecc->dev,
"Invalid errpos value (%d, max is %d)\n",
errpos, (sectorsize + eccbytes) * 8);
return -EINVAL;
-- 
2.11.0



[PATCH v1 06/10] ehci-atmel: Power down during suspend is normal

2017-09-08 Thread Romain Izard
When an Atmel SoC is suspended with the backup mode, the USB bus will be
powered down. As this is expected, do not return an error to the driver
core when ehci_resume detects it.

Signed-off-by: Romain Izard <romain.izard@gmail.com>
---
 drivers/usb/host/ehci-atmel.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/drivers/usb/host/ehci-atmel.c b/drivers/usb/host/ehci-atmel.c
index 7440722bfbf0..2a8b9bdc0e57 100644
--- a/drivers/usb/host/ehci-atmel.c
+++ b/drivers/usb/host/ehci-atmel.c
@@ -205,7 +205,8 @@ static int __maybe_unused ehci_atmel_drv_resume(struct 
device *dev)
struct atmel_ehci_priv *atmel_ehci = hcd_to_atmel_ehci_priv(hcd);
 
atmel_start_clock(atmel_ehci);
-   return ehci_resume(hcd, false);
+   ehci_resume(hcd, false);
+   return 0;
 }
 
 #ifdef CONFIG_OF
-- 
2.11.0



[PATCH v1 06/10] ehci-atmel: Power down during suspend is normal

2017-09-08 Thread Romain Izard
When an Atmel SoC is suspended with the backup mode, the USB bus will be
powered down. As this is expected, do not return an error to the driver
core when ehci_resume detects it.

Signed-off-by: Romain Izard 
---
 drivers/usb/host/ehci-atmel.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/drivers/usb/host/ehci-atmel.c b/drivers/usb/host/ehci-atmel.c
index 7440722bfbf0..2a8b9bdc0e57 100644
--- a/drivers/usb/host/ehci-atmel.c
+++ b/drivers/usb/host/ehci-atmel.c
@@ -205,7 +205,8 @@ static int __maybe_unused ehci_atmel_drv_resume(struct 
device *dev)
struct atmel_ehci_priv *atmel_ehci = hcd_to_atmel_ehci_priv(hcd);
 
atmel_start_clock(atmel_ehci);
-   return ehci_resume(hcd, false);
+   ehci_resume(hcd, false);
+   return 0;
 }
 
 #ifdef CONFIG_OF
-- 
2.11.0



[PATCH v1 10/10] tty/serial: atmel: Prevent a warning on suspend

2017-09-08 Thread Romain Izard
The atmel serial port driver reported the following warning on suspend:
atmel_usart f802.serial: ttyS1: Unable to drain transmitter

As the ATMEL_US_TXEMPTY status bit in ATMEL_US_CSR is always cleared
when the transmitter is disabled, we need to know the transmitter's
state to return the real fifo state. And as ATMEL_US_CR is write-only,
it is necessary to save the state of the transmitter in a local
variable, and update the variable when TXEN and TXDIS is written in
ATMEL_US_CR.

After those changes, atmel_tx_empty can return "empty" on suspend, the
warning in uart_suspend_port disappears, and suspending is 20ms shorter
for each enabled Atmel serial port.

Signed-off-by: Romain Izard <romain.izard@gmail.com>
---
 drivers/tty/serial/atmel_serial.c | 13 +
 1 file changed, 13 insertions(+)

diff --git a/drivers/tty/serial/atmel_serial.c 
b/drivers/tty/serial/atmel_serial.c
index 7551cab438ff..195c0d1b594e 100644
--- a/drivers/tty/serial/atmel_serial.c
+++ b/drivers/tty/serial/atmel_serial.c
@@ -171,6 +171,7 @@ struct atmel_uart_port {
boolhas_hw_timer;
struct timer_list   uart_timer;
 
+   booltx_stopped;
boolsuspended;
unsigned intpending;
unsigned intpending_status;
@@ -380,6 +381,10 @@ static int atmel_config_rs485(struct uart_port *port,
  */
 static u_int atmel_tx_empty(struct uart_port *port)
 {
+   struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
+
+   if (atmel_port->tx_stopped)
+   return TIOCSER_TEMT;
return (atmel_uart_readl(port, ATMEL_US_CSR) & ATMEL_US_TXEMPTY) ?
TIOCSER_TEMT :
0;
@@ -485,6 +490,7 @@ static void atmel_stop_tx(struct uart_port *port)
 * is fully transmitted.
 */
atmel_uart_writel(port, ATMEL_US_CR, ATMEL_US_TXDIS);
+   atmel_port->tx_stopped = true;
 
/* Disable interrupts */
atmel_uart_writel(port, ATMEL_US_IDR, atmel_port->tx_done_mask);
@@ -492,6 +498,7 @@ static void atmel_stop_tx(struct uart_port *port)
if ((port->rs485.flags & SER_RS485_ENABLED) &&
!(port->rs485.flags & SER_RS485_RX_DURING_TX))
atmel_start_rx(port);
+
 }
 
 /*
@@ -521,6 +528,7 @@ static void atmel_start_tx(struct uart_port *port)
 
/* re-enable the transmitter */
atmel_uart_writel(port, ATMEL_US_CR, ATMEL_US_TXEN);
+   atmel_port->tx_stopped = false;
 }
 
 /*
@@ -1866,6 +1874,7 @@ static int atmel_startup(struct uart_port *port)
atmel_uart_writel(port, ATMEL_US_CR, ATMEL_US_RSTSTA | ATMEL_US_RSTRX);
/* enable xmit & rcvr */
atmel_uart_writel(port, ATMEL_US_CR, ATMEL_US_TXEN | ATMEL_US_RXEN);
+   atmel_port->tx_stopped = false;
 
setup_timer(_port->uart_timer,
atmel_uart_timer_callback,
@@ -2122,6 +2131,7 @@ static void atmel_set_termios(struct uart_port *port, 
struct ktermios *termios,
 
/* disable receiver and transmitter */
atmel_uart_writel(port, ATMEL_US_CR, ATMEL_US_TXDIS | ATMEL_US_RXDIS);
+   atmel_port->tx_stopped = true;
 
/* mode */
if (port->rs485.flags & SER_RS485_ENABLED) {
@@ -2207,6 +2217,7 @@ static void atmel_set_termios(struct uart_port *port, 
struct ktermios *termios,
atmel_uart_writel(port, ATMEL_US_BRGR, quot);
atmel_uart_writel(port, ATMEL_US_CR, ATMEL_US_RSTSTA | ATMEL_US_RSTRX);
atmel_uart_writel(port, ATMEL_US_CR, ATMEL_US_TXEN | ATMEL_US_RXEN);
+   atmel_port->tx_stopped = false;
 
/* restore interrupts */
atmel_uart_writel(port, ATMEL_US_IER, imr);
@@ -2450,6 +2461,7 @@ static void atmel_console_write(struct console *co, const 
char *s, u_int count)
 
/* Make sure that tx path is actually able to send characters */
atmel_uart_writel(port, ATMEL_US_CR, ATMEL_US_TXEN);
+   atmel_port->tx_stopped = false;
 
uart_console_write(port, s, count, atmel_console_putchar);
 
@@ -2528,6 +2540,7 @@ static int __init atmel_console_setup(struct console *co, 
char *options)
atmel_uart_writel(port, ATMEL_US_IDR, -1);
atmel_uart_writel(port, ATMEL_US_CR, ATMEL_US_RSTSTA | ATMEL_US_RSTRX);
atmel_uart_writel(port, ATMEL_US_CR, ATMEL_US_TXEN | ATMEL_US_RXEN);
+   atmel_port->tx_stopped = false;
 
if (options)
uart_parse_options(options, , , , );
-- 
2.11.0



[PATCH v1 10/10] tty/serial: atmel: Prevent a warning on suspend

2017-09-08 Thread Romain Izard
The atmel serial port driver reported the following warning on suspend:
atmel_usart f802.serial: ttyS1: Unable to drain transmitter

As the ATMEL_US_TXEMPTY status bit in ATMEL_US_CSR is always cleared
when the transmitter is disabled, we need to know the transmitter's
state to return the real fifo state. And as ATMEL_US_CR is write-only,
it is necessary to save the state of the transmitter in a local
variable, and update the variable when TXEN and TXDIS is written in
ATMEL_US_CR.

After those changes, atmel_tx_empty can return "empty" on suspend, the
warning in uart_suspend_port disappears, and suspending is 20ms shorter
for each enabled Atmel serial port.

Signed-off-by: Romain Izard 
---
 drivers/tty/serial/atmel_serial.c | 13 +
 1 file changed, 13 insertions(+)

diff --git a/drivers/tty/serial/atmel_serial.c 
b/drivers/tty/serial/atmel_serial.c
index 7551cab438ff..195c0d1b594e 100644
--- a/drivers/tty/serial/atmel_serial.c
+++ b/drivers/tty/serial/atmel_serial.c
@@ -171,6 +171,7 @@ struct atmel_uart_port {
boolhas_hw_timer;
struct timer_list   uart_timer;
 
+   booltx_stopped;
boolsuspended;
unsigned intpending;
unsigned intpending_status;
@@ -380,6 +381,10 @@ static int atmel_config_rs485(struct uart_port *port,
  */
 static u_int atmel_tx_empty(struct uart_port *port)
 {
+   struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
+
+   if (atmel_port->tx_stopped)
+   return TIOCSER_TEMT;
return (atmel_uart_readl(port, ATMEL_US_CSR) & ATMEL_US_TXEMPTY) ?
TIOCSER_TEMT :
0;
@@ -485,6 +490,7 @@ static void atmel_stop_tx(struct uart_port *port)
 * is fully transmitted.
 */
atmel_uart_writel(port, ATMEL_US_CR, ATMEL_US_TXDIS);
+   atmel_port->tx_stopped = true;
 
/* Disable interrupts */
atmel_uart_writel(port, ATMEL_US_IDR, atmel_port->tx_done_mask);
@@ -492,6 +498,7 @@ static void atmel_stop_tx(struct uart_port *port)
if ((port->rs485.flags & SER_RS485_ENABLED) &&
!(port->rs485.flags & SER_RS485_RX_DURING_TX))
atmel_start_rx(port);
+
 }
 
 /*
@@ -521,6 +528,7 @@ static void atmel_start_tx(struct uart_port *port)
 
/* re-enable the transmitter */
atmel_uart_writel(port, ATMEL_US_CR, ATMEL_US_TXEN);
+   atmel_port->tx_stopped = false;
 }
 
 /*
@@ -1866,6 +1874,7 @@ static int atmel_startup(struct uart_port *port)
atmel_uart_writel(port, ATMEL_US_CR, ATMEL_US_RSTSTA | ATMEL_US_RSTRX);
/* enable xmit & rcvr */
atmel_uart_writel(port, ATMEL_US_CR, ATMEL_US_TXEN | ATMEL_US_RXEN);
+   atmel_port->tx_stopped = false;
 
setup_timer(_port->uart_timer,
atmel_uart_timer_callback,
@@ -2122,6 +2131,7 @@ static void atmel_set_termios(struct uart_port *port, 
struct ktermios *termios,
 
/* disable receiver and transmitter */
atmel_uart_writel(port, ATMEL_US_CR, ATMEL_US_TXDIS | ATMEL_US_RXDIS);
+   atmel_port->tx_stopped = true;
 
/* mode */
if (port->rs485.flags & SER_RS485_ENABLED) {
@@ -2207,6 +2217,7 @@ static void atmel_set_termios(struct uart_port *port, 
struct ktermios *termios,
atmel_uart_writel(port, ATMEL_US_BRGR, quot);
atmel_uart_writel(port, ATMEL_US_CR, ATMEL_US_RSTSTA | ATMEL_US_RSTRX);
atmel_uart_writel(port, ATMEL_US_CR, ATMEL_US_TXEN | ATMEL_US_RXEN);
+   atmel_port->tx_stopped = false;
 
/* restore interrupts */
atmel_uart_writel(port, ATMEL_US_IER, imr);
@@ -2450,6 +2461,7 @@ static void atmel_console_write(struct console *co, const 
char *s, u_int count)
 
/* Make sure that tx path is actually able to send characters */
atmel_uart_writel(port, ATMEL_US_CR, ATMEL_US_TXEN);
+   atmel_port->tx_stopped = false;
 
uart_console_write(port, s, count, atmel_console_putchar);
 
@@ -2528,6 +2540,7 @@ static int __init atmel_console_setup(struct console *co, 
char *options)
atmel_uart_writel(port, ATMEL_US_IDR, -1);
atmel_uart_writel(port, ATMEL_US_CR, ATMEL_US_RSTSTA | ATMEL_US_RSTRX);
atmel_uart_writel(port, ATMEL_US_CR, ATMEL_US_TXEN | ATMEL_US_RXEN);
+   atmel_port->tx_stopped = false;
 
if (options)
uart_parse_options(options, , , , );
-- 
2.11.0



[PATCH v1 08/10] pwm: atmel-tcb: Support backup mode

2017-09-08 Thread Romain Izard
Save and restore registers for the PWM on suspend and resume, which
makes hibernation and backup modes possible.

Signed-off-by: Romain Izard <romain.izard@gmail.com>
---
 drivers/pwm/pwm-atmel-tcb.c | 63 +++--
 1 file changed, 61 insertions(+), 2 deletions(-)

diff --git a/drivers/pwm/pwm-atmel-tcb.c b/drivers/pwm/pwm-atmel-tcb.c
index 75db585a2a94..acd3ce8ecf3f 100644
--- a/drivers/pwm/pwm-atmel-tcb.c
+++ b/drivers/pwm/pwm-atmel-tcb.c
@@ -37,11 +37,20 @@ struct atmel_tcb_pwm_device {
unsigned period;/* PWM period expressed in clk cycles */
 };
 
+struct atmel_tcb_channel {
+   u32 enabled;
+   u32 cmr;
+   u32 ra;
+   u32 rb;
+   u32 rc;
+};
+
 struct atmel_tcb_pwm_chip {
struct pwm_chip chip;
spinlock_t lock;
struct atmel_tc *tc;
struct atmel_tcb_pwm_device *pwms[NPWM];
+   struct atmel_tcb_channel bkup[NPWM / 2];
 };
 
 static inline struct atmel_tcb_pwm_chip *to_tcb_chip(struct pwm_chip *chip)
@@ -175,12 +184,15 @@ static void atmel_tcb_pwm_disable(struct pwm_chip *chip, 
struct pwm_device *pwm)
 * Use software trigger to apply the new setting.
 * If both PWM devices in this group are disabled we stop the clock.
 */
-   if (!(cmr & (ATMEL_TC_ACPC | ATMEL_TC_BCPC)))
+   if (!(cmr & (ATMEL_TC_ACPC | ATMEL_TC_BCPC))) {
__raw_writel(ATMEL_TC_SWTRG | ATMEL_TC_CLKDIS,
 regs + ATMEL_TC_REG(group, CCR));
-   else
+   tcbpwmc->bkup[group].enabled = 1;
+   } else {
__raw_writel(ATMEL_TC_SWTRG, regs +
 ATMEL_TC_REG(group, CCR));
+   tcbpwmc->bkup[group].enabled = 0;
+   }
 
spin_unlock(>lock);
 }
@@ -263,6 +275,7 @@ static int atmel_tcb_pwm_enable(struct pwm_chip *chip, 
struct pwm_device *pwm)
/* Use software trigger to apply the new setting */
__raw_writel(ATMEL_TC_CLKEN | ATMEL_TC_SWTRG,
 regs + ATMEL_TC_REG(group, CCR));
+   tcbpwmc->bkup[group].enabled = 1;
spin_unlock(>lock);
return 0;
 }
@@ -445,10 +458,56 @@ static const struct of_device_id atmel_tcb_pwm_dt_ids[] = 
{
 };
 MODULE_DEVICE_TABLE(of, atmel_tcb_pwm_dt_ids);
 
+#ifdef CONFIG_PM_SLEEP
+static int atmel_tcb_pwm_suspend(struct device *dev)
+{
+   struct platform_device *pdev = to_platform_device(dev);
+   struct atmel_tcb_pwm_chip *tcbpwm = platform_get_drvdata(pdev);
+   void __iomem *base = tcbpwm->tc->regs;
+   int i;
+
+   for (i = 0; i < (NPWM / 2); i++) {
+   struct atmel_tcb_channel *chan = >bkup[i];
+
+   chan->cmr = readl(base + ATMEL_TC_REG(i, CMR));
+   chan->ra = readl(base + ATMEL_TC_REG(i, RA));
+   chan->rb = readl(base + ATMEL_TC_REG(i, RB));
+   chan->rc = readl(base + ATMEL_TC_REG(i, RC));
+   }
+   return 0;
+}
+
+static int atmel_tcb_pwm_resume(struct device *dev)
+{
+   struct platform_device *pdev = to_platform_device(dev);
+   struct atmel_tcb_pwm_chip *tcbpwm = platform_get_drvdata(pdev);
+   void __iomem *base = tcbpwm->tc->regs;
+   int i;
+
+   for (i = 0; i < (NPWM / 2); i++) {
+   struct atmel_tcb_channel *chan = >bkup[i];
+
+   writel(chan->cmr, base + ATMEL_TC_REG(i, CMR));
+   writel(chan->ra, base + ATMEL_TC_REG(i, RA));
+   writel(chan->rb, base + ATMEL_TC_REG(i, RB));
+   writel(chan->rc, base + ATMEL_TC_REG(i, RC));
+   if (chan->enabled) {
+   writel(ATMEL_TC_CLKEN | ATMEL_TC_SWTRG,
+   base + ATMEL_TC_REG(i, CCR));
+   }
+   }
+   return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(atmel_tcb_pwm_pm_ops, atmel_tcb_pwm_suspend,
+atmel_tcb_pwm_resume);
+
 static struct platform_driver atmel_tcb_pwm_driver = {
.driver = {
.name = "atmel-tcb-pwm",
.of_match_table = atmel_tcb_pwm_dt_ids,
+   .pm = _tcb_pwm_pm_ops,
},
.probe = atmel_tcb_pwm_probe,
.remove = atmel_tcb_pwm_remove,
-- 
2.11.0



[PATCH v1 08/10] pwm: atmel-tcb: Support backup mode

2017-09-08 Thread Romain Izard
Save and restore registers for the PWM on suspend and resume, which
makes hibernation and backup modes possible.

Signed-off-by: Romain Izard 
---
 drivers/pwm/pwm-atmel-tcb.c | 63 +++--
 1 file changed, 61 insertions(+), 2 deletions(-)

diff --git a/drivers/pwm/pwm-atmel-tcb.c b/drivers/pwm/pwm-atmel-tcb.c
index 75db585a2a94..acd3ce8ecf3f 100644
--- a/drivers/pwm/pwm-atmel-tcb.c
+++ b/drivers/pwm/pwm-atmel-tcb.c
@@ -37,11 +37,20 @@ struct atmel_tcb_pwm_device {
unsigned period;/* PWM period expressed in clk cycles */
 };
 
+struct atmel_tcb_channel {
+   u32 enabled;
+   u32 cmr;
+   u32 ra;
+   u32 rb;
+   u32 rc;
+};
+
 struct atmel_tcb_pwm_chip {
struct pwm_chip chip;
spinlock_t lock;
struct atmel_tc *tc;
struct atmel_tcb_pwm_device *pwms[NPWM];
+   struct atmel_tcb_channel bkup[NPWM / 2];
 };
 
 static inline struct atmel_tcb_pwm_chip *to_tcb_chip(struct pwm_chip *chip)
@@ -175,12 +184,15 @@ static void atmel_tcb_pwm_disable(struct pwm_chip *chip, 
struct pwm_device *pwm)
 * Use software trigger to apply the new setting.
 * If both PWM devices in this group are disabled we stop the clock.
 */
-   if (!(cmr & (ATMEL_TC_ACPC | ATMEL_TC_BCPC)))
+   if (!(cmr & (ATMEL_TC_ACPC | ATMEL_TC_BCPC))) {
__raw_writel(ATMEL_TC_SWTRG | ATMEL_TC_CLKDIS,
 regs + ATMEL_TC_REG(group, CCR));
-   else
+   tcbpwmc->bkup[group].enabled = 1;
+   } else {
__raw_writel(ATMEL_TC_SWTRG, regs +
 ATMEL_TC_REG(group, CCR));
+   tcbpwmc->bkup[group].enabled = 0;
+   }
 
spin_unlock(>lock);
 }
@@ -263,6 +275,7 @@ static int atmel_tcb_pwm_enable(struct pwm_chip *chip, 
struct pwm_device *pwm)
/* Use software trigger to apply the new setting */
__raw_writel(ATMEL_TC_CLKEN | ATMEL_TC_SWTRG,
 regs + ATMEL_TC_REG(group, CCR));
+   tcbpwmc->bkup[group].enabled = 1;
spin_unlock(>lock);
return 0;
 }
@@ -445,10 +458,56 @@ static const struct of_device_id atmel_tcb_pwm_dt_ids[] = 
{
 };
 MODULE_DEVICE_TABLE(of, atmel_tcb_pwm_dt_ids);
 
+#ifdef CONFIG_PM_SLEEP
+static int atmel_tcb_pwm_suspend(struct device *dev)
+{
+   struct platform_device *pdev = to_platform_device(dev);
+   struct atmel_tcb_pwm_chip *tcbpwm = platform_get_drvdata(pdev);
+   void __iomem *base = tcbpwm->tc->regs;
+   int i;
+
+   for (i = 0; i < (NPWM / 2); i++) {
+   struct atmel_tcb_channel *chan = >bkup[i];
+
+   chan->cmr = readl(base + ATMEL_TC_REG(i, CMR));
+   chan->ra = readl(base + ATMEL_TC_REG(i, RA));
+   chan->rb = readl(base + ATMEL_TC_REG(i, RB));
+   chan->rc = readl(base + ATMEL_TC_REG(i, RC));
+   }
+   return 0;
+}
+
+static int atmel_tcb_pwm_resume(struct device *dev)
+{
+   struct platform_device *pdev = to_platform_device(dev);
+   struct atmel_tcb_pwm_chip *tcbpwm = platform_get_drvdata(pdev);
+   void __iomem *base = tcbpwm->tc->regs;
+   int i;
+
+   for (i = 0; i < (NPWM / 2); i++) {
+   struct atmel_tcb_channel *chan = >bkup[i];
+
+   writel(chan->cmr, base + ATMEL_TC_REG(i, CMR));
+   writel(chan->ra, base + ATMEL_TC_REG(i, RA));
+   writel(chan->rb, base + ATMEL_TC_REG(i, RB));
+   writel(chan->rc, base + ATMEL_TC_REG(i, RC));
+   if (chan->enabled) {
+   writel(ATMEL_TC_CLKEN | ATMEL_TC_SWTRG,
+   base + ATMEL_TC_REG(i, CCR));
+   }
+   }
+   return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(atmel_tcb_pwm_pm_ops, atmel_tcb_pwm_suspend,
+atmel_tcb_pwm_resume);
+
 static struct platform_driver atmel_tcb_pwm_driver = {
.driver = {
.name = "atmel-tcb-pwm",
.of_match_table = atmel_tcb_pwm_dt_ids,
+   .pm = _tcb_pwm_pm_ops,
},
.probe = atmel_tcb_pwm_probe,
.remove = atmel_tcb_pwm_remove,
-- 
2.11.0



[PATCH v1 04/10] mtd: nand: atmel: Avoid ECC errors when leaving backup mode

2017-09-08 Thread Romain Izard
During backup mode, the contents of all registers will be cleared as the
SoC will be completely powered down. For a product that boots on NAND
Flash memory, the bootloader will obviously use the related controller
to read the Flash and correct any detected error in the memory, before
handling back control to the kernel's resuming entry point.

In normal devices, it is up to the driver's suspend/resume code to
restore the registers in a valid state. But the PMECC is not a regular
device in the driver model when used with the legacy device tree binding
for the Atmel NAND controller, and suspend/resume code is not called.

As in my case the bootloader leaves the PMECC controller in a programmed
state, and the controller is only reset at boot or after a NAND access,
the first NAND Flash access with the Atmel controller will report
uncorrectable ECC errors.

To avoid this, systematically reset the PMECC controller before using
it.

Signed-off-by: Romain Izard <romain.izard@gmail.com>
---
 drivers/mtd/nand/atmel/pmecc.c | 11 +++
 1 file changed, 3 insertions(+), 8 deletions(-)

diff --git a/drivers/mtd/nand/atmel/pmecc.c b/drivers/mtd/nand/atmel/pmecc.c
index 8c210a5776bc..8d1208f38025 100644
--- a/drivers/mtd/nand/atmel/pmecc.c
+++ b/drivers/mtd/nand/atmel/pmecc.c
@@ -777,6 +777,9 @@ int atmel_pmecc_enable(struct atmel_pmecc_user *user, int 
op)
 
mutex_lock(>pmecc->lock);
 
+   writel(PMECC_CTRL_RST, pmecc->regs.base + ATMEL_PMECC_CTRL);
+   writel(PMECC_CTRL_DISABLE, pmecc->regs.base + ATMEL_PMECC_CTRL);
+
cfg = user->cache.cfg;
if (op == NAND_ECC_WRITE)
cfg |= PMECC_CFG_WRITE_OP;
@@ -797,10 +800,6 @@ EXPORT_SYMBOL_GPL(atmel_pmecc_enable);
 
 void atmel_pmecc_disable(struct atmel_pmecc_user *user)
 {
-   struct atmel_pmecc *pmecc = user->pmecc;
-
-   writel(PMECC_CTRL_RST, pmecc->regs.base + ATMEL_PMECC_CTRL);
-   writel(PMECC_CTRL_DISABLE, pmecc->regs.base + ATMEL_PMECC_CTRL);
mutex_unlock(>pmecc->lock);
 }
 EXPORT_SYMBOL_GPL(atmel_pmecc_disable);
@@ -856,10 +855,6 @@ static struct atmel_pmecc *atmel_pmecc_create(struct 
platform_device *pdev,
/* Disable all interrupts before registering the PMECC handler. */
writel(0x, pmecc->regs.base + ATMEL_PMECC_IDR);
 
-   /* Reset the ECC engine */
-   writel(PMECC_CTRL_RST, pmecc->regs.base + ATMEL_PMECC_CTRL);
-   writel(PMECC_CTRL_DISABLE, pmecc->regs.base + ATMEL_PMECC_CTRL);
-
return pmecc;
 }
 
-- 
2.11.0



[PATCH v1 04/10] mtd: nand: atmel: Avoid ECC errors when leaving backup mode

2017-09-08 Thread Romain Izard
During backup mode, the contents of all registers will be cleared as the
SoC will be completely powered down. For a product that boots on NAND
Flash memory, the bootloader will obviously use the related controller
to read the Flash and correct any detected error in the memory, before
handling back control to the kernel's resuming entry point.

In normal devices, it is up to the driver's suspend/resume code to
restore the registers in a valid state. But the PMECC is not a regular
device in the driver model when used with the legacy device tree binding
for the Atmel NAND controller, and suspend/resume code is not called.

As in my case the bootloader leaves the PMECC controller in a programmed
state, and the controller is only reset at boot or after a NAND access,
the first NAND Flash access with the Atmel controller will report
uncorrectable ECC errors.

To avoid this, systematically reset the PMECC controller before using
it.

Signed-off-by: Romain Izard 
---
 drivers/mtd/nand/atmel/pmecc.c | 11 +++
 1 file changed, 3 insertions(+), 8 deletions(-)

diff --git a/drivers/mtd/nand/atmel/pmecc.c b/drivers/mtd/nand/atmel/pmecc.c
index 8c210a5776bc..8d1208f38025 100644
--- a/drivers/mtd/nand/atmel/pmecc.c
+++ b/drivers/mtd/nand/atmel/pmecc.c
@@ -777,6 +777,9 @@ int atmel_pmecc_enable(struct atmel_pmecc_user *user, int 
op)
 
mutex_lock(>pmecc->lock);
 
+   writel(PMECC_CTRL_RST, pmecc->regs.base + ATMEL_PMECC_CTRL);
+   writel(PMECC_CTRL_DISABLE, pmecc->regs.base + ATMEL_PMECC_CTRL);
+
cfg = user->cache.cfg;
if (op == NAND_ECC_WRITE)
cfg |= PMECC_CFG_WRITE_OP;
@@ -797,10 +800,6 @@ EXPORT_SYMBOL_GPL(atmel_pmecc_enable);
 
 void atmel_pmecc_disable(struct atmel_pmecc_user *user)
 {
-   struct atmel_pmecc *pmecc = user->pmecc;
-
-   writel(PMECC_CTRL_RST, pmecc->regs.base + ATMEL_PMECC_CTRL);
-   writel(PMECC_CTRL_DISABLE, pmecc->regs.base + ATMEL_PMECC_CTRL);
mutex_unlock(>pmecc->lock);
 }
 EXPORT_SYMBOL_GPL(atmel_pmecc_disable);
@@ -856,10 +855,6 @@ static struct atmel_pmecc *atmel_pmecc_create(struct 
platform_device *pdev,
/* Disable all interrupts before registering the PMECC handler. */
writel(0x, pmecc->regs.base + ATMEL_PMECC_IDR);
 
-   /* Reset the ECC engine */
-   writel(PMECC_CTRL_RST, pmecc->regs.base + ATMEL_PMECC_CTRL);
-   writel(PMECC_CTRL_DISABLE, pmecc->regs.base + ATMEL_PMECC_CTRL);
-
return pmecc;
 }
 
-- 
2.11.0



[PATCH v1 09/10] atmel_flexcom: Support backup mode

2017-09-08 Thread Romain Izard
The controller used by a flexcom module is configured at boot, and left
alone after this. As the configuration will be lost after backup mode,
restore the state of the flexcom driver on resume.

Signed-off-by: Romain Izard <romain.izard@gmail.com>
---
 drivers/mfd/atmel-flexcom.c | 65 ++---
 1 file changed, 50 insertions(+), 15 deletions(-)

diff --git a/drivers/mfd/atmel-flexcom.c b/drivers/mfd/atmel-flexcom.c
index 064bde9cff5a..ef1235c4a179 100644
--- a/drivers/mfd/atmel-flexcom.c
+++ b/drivers/mfd/atmel-flexcom.c
@@ -39,34 +39,44 @@
 #define FLEX_MR_OPMODE(opmode) (((opmode) << FLEX_MR_OPMODE_OFFSET) &  \
 FLEX_MR_OPMODE_MASK)
 
+struct atmel_flexcom {
+   void __iomem *base;
+   u32 opmode;
+   struct clk *clk;
+};
 
 static int atmel_flexcom_probe(struct platform_device *pdev)
 {
struct device_node *np = pdev->dev.of_node;
-   struct clk *clk;
struct resource *res;
-   void __iomem *base;
-   u32 opmode;
+   struct atmel_flexcom *afc;
int err;
+   u32 val;
+
+   afc = devm_kzalloc(>dev, sizeof(*afc), GFP_KERNEL);
+   if (!afc)
+   return -ENOMEM;
 
-   err = of_property_read_u32(np, "atmel,flexcom-mode", );
+   platform_set_drvdata(pdev, afc);
+
+   err = of_property_read_u32(np, "atmel,flexcom-mode", >opmode);
if (err)
return err;
 
-   if (opmode < ATMEL_FLEXCOM_MODE_USART ||
-   opmode > ATMEL_FLEXCOM_MODE_TWI)
+   if (afc->opmode < ATMEL_FLEXCOM_MODE_USART ||
+   afc->opmode > ATMEL_FLEXCOM_MODE_TWI)
return -EINVAL;
 
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-   base = devm_ioremap_resource(>dev, res);
-   if (IS_ERR(base))
-   return PTR_ERR(base);
+   afc->base = devm_ioremap_resource(>dev, res);
+   if (IS_ERR(afc->base))
+   return PTR_ERR(afc->base);
 
-   clk = devm_clk_get(>dev, NULL);
-   if (IS_ERR(clk))
-   return PTR_ERR(clk);
+   afc->clk = devm_clk_get(>dev, NULL);
+   if (IS_ERR(afc->clk))
+   return PTR_ERR(afc->clk);
 
-   err = clk_prepare_enable(clk);
+   err = clk_prepare_enable(afc->clk);
if (err)
return err;
 
@@ -76,9 +86,10 @@ static int atmel_flexcom_probe(struct platform_device *pdev)
 * inaccessible and are read as zero. Also the external I/O lines of the
 * Flexcom are muxed to reach the selected device.
 */
-   writel(FLEX_MR_OPMODE(opmode), base + FLEX_MR);
+   val = FLEX_MR_OPMODE(afc->opmode);
+   writel(val, afc->base + FLEX_MR);
 
-   clk_disable_unprepare(clk);
+   clk_disable_unprepare(afc->clk);
 
return devm_of_platform_populate(>dev);
 }
@@ -89,10 +100,34 @@ static const struct of_device_id atmel_flexcom_of_match[] 
= {
 };
 MODULE_DEVICE_TABLE(of, atmel_flexcom_of_match);
 
+#ifdef CONFIG_PM_SLEEP
+static int atmel_flexcom_resume(struct device *dev)
+{
+   struct atmel_flexcom *afc = dev_get_drvdata(dev);
+   int err;
+   u32 val;
+
+   err = clk_prepare_enable(afc->clk);
+   if (err)
+   return err;
+
+   val = FLEX_MR_OPMODE(afc->opmode),
+   writel(val, afc->base + FLEX_MR);
+
+   clk_disable_unprepare(afc->clk);
+
+   return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(atmel_flexcom_pm_ops, NULL,
+atmel_flexcom_resume);
+
 static struct platform_driver atmel_flexcom_driver = {
.probe  = atmel_flexcom_probe,
.driver = {
.name   = "atmel_flexcom",
+   .pm = _flexcom_pm_ops,
.of_match_table = atmel_flexcom_of_match,
},
 };
-- 
2.11.0



[PATCH v1 09/10] atmel_flexcom: Support backup mode

2017-09-08 Thread Romain Izard
The controller used by a flexcom module is configured at boot, and left
alone after this. As the configuration will be lost after backup mode,
restore the state of the flexcom driver on resume.

Signed-off-by: Romain Izard 
---
 drivers/mfd/atmel-flexcom.c | 65 ++---
 1 file changed, 50 insertions(+), 15 deletions(-)

diff --git a/drivers/mfd/atmel-flexcom.c b/drivers/mfd/atmel-flexcom.c
index 064bde9cff5a..ef1235c4a179 100644
--- a/drivers/mfd/atmel-flexcom.c
+++ b/drivers/mfd/atmel-flexcom.c
@@ -39,34 +39,44 @@
 #define FLEX_MR_OPMODE(opmode) (((opmode) << FLEX_MR_OPMODE_OFFSET) &  \
 FLEX_MR_OPMODE_MASK)
 
+struct atmel_flexcom {
+   void __iomem *base;
+   u32 opmode;
+   struct clk *clk;
+};
 
 static int atmel_flexcom_probe(struct platform_device *pdev)
 {
struct device_node *np = pdev->dev.of_node;
-   struct clk *clk;
struct resource *res;
-   void __iomem *base;
-   u32 opmode;
+   struct atmel_flexcom *afc;
int err;
+   u32 val;
+
+   afc = devm_kzalloc(>dev, sizeof(*afc), GFP_KERNEL);
+   if (!afc)
+   return -ENOMEM;
 
-   err = of_property_read_u32(np, "atmel,flexcom-mode", );
+   platform_set_drvdata(pdev, afc);
+
+   err = of_property_read_u32(np, "atmel,flexcom-mode", >opmode);
if (err)
return err;
 
-   if (opmode < ATMEL_FLEXCOM_MODE_USART ||
-   opmode > ATMEL_FLEXCOM_MODE_TWI)
+   if (afc->opmode < ATMEL_FLEXCOM_MODE_USART ||
+   afc->opmode > ATMEL_FLEXCOM_MODE_TWI)
return -EINVAL;
 
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-   base = devm_ioremap_resource(>dev, res);
-   if (IS_ERR(base))
-   return PTR_ERR(base);
+   afc->base = devm_ioremap_resource(>dev, res);
+   if (IS_ERR(afc->base))
+   return PTR_ERR(afc->base);
 
-   clk = devm_clk_get(>dev, NULL);
-   if (IS_ERR(clk))
-   return PTR_ERR(clk);
+   afc->clk = devm_clk_get(>dev, NULL);
+   if (IS_ERR(afc->clk))
+   return PTR_ERR(afc->clk);
 
-   err = clk_prepare_enable(clk);
+   err = clk_prepare_enable(afc->clk);
if (err)
return err;
 
@@ -76,9 +86,10 @@ static int atmel_flexcom_probe(struct platform_device *pdev)
 * inaccessible and are read as zero. Also the external I/O lines of the
 * Flexcom are muxed to reach the selected device.
 */
-   writel(FLEX_MR_OPMODE(opmode), base + FLEX_MR);
+   val = FLEX_MR_OPMODE(afc->opmode);
+   writel(val, afc->base + FLEX_MR);
 
-   clk_disable_unprepare(clk);
+   clk_disable_unprepare(afc->clk);
 
return devm_of_platform_populate(>dev);
 }
@@ -89,10 +100,34 @@ static const struct of_device_id atmel_flexcom_of_match[] 
= {
 };
 MODULE_DEVICE_TABLE(of, atmel_flexcom_of_match);
 
+#ifdef CONFIG_PM_SLEEP
+static int atmel_flexcom_resume(struct device *dev)
+{
+   struct atmel_flexcom *afc = dev_get_drvdata(dev);
+   int err;
+   u32 val;
+
+   err = clk_prepare_enable(afc->clk);
+   if (err)
+   return err;
+
+   val = FLEX_MR_OPMODE(afc->opmode),
+   writel(val, afc->base + FLEX_MR);
+
+   clk_disable_unprepare(afc->clk);
+
+   return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(atmel_flexcom_pm_ops, NULL,
+atmel_flexcom_resume);
+
 static struct platform_driver atmel_flexcom_driver = {
.probe  = atmel_flexcom_probe,
.driver = {
.name   = "atmel_flexcom",
+   .pm = _flexcom_pm_ops,
.of_match_table = atmel_flexcom_of_match,
},
 };
-- 
2.11.0



[PATCH v1 07/10] iio:adc:at91-sama5d2: Support backup mode

2017-09-08 Thread Romain Izard
Support the backup mode for platform suspend, by restoring the hardware
registers on resume.

Signed-off-by: Romain Izard <romain.izard@gmail.com>
---
 drivers/iio/adc/at91-sama5d2_adc.c | 71 --
 1 file changed, 61 insertions(+), 10 deletions(-)

diff --git a/drivers/iio/adc/at91-sama5d2_adc.c 
b/drivers/iio/adc/at91-sama5d2_adc.c
index e10dca3ed74b..f9718c863363 100644
--- a/drivers/iio/adc/at91-sama5d2_adc.c
+++ b/drivers/iio/adc/at91-sama5d2_adc.c
@@ -200,6 +200,7 @@ struct at91_adc_state {
u32 conversion_value;
struct at91_adc_soc_infosoc_info;
wait_queue_head_t   wq_data_available;
+   unsigned intcurrent_rate;
/*
 * lock to prevent concurrent 'single conversion' requests through
 * sysfs.
@@ -269,6 +270,8 @@ static void at91_adc_setup_samp_freq(struct at91_adc_state 
*st, unsigned freq)
mr |= AT91_SAMA5D2_MR_PRESCAL(prescal);
at91_adc_writel(st, AT91_SAMA5D2_MR, mr);
 
+   st->current_rate = freq;
+
dev_dbg(_dev->dev, "freq: %u, startup: %u, prescal: %u\n",
freq, startup, prescal);
 }
@@ -375,7 +378,9 @@ static int at91_adc_write_raw(struct iio_dev *indio_dev,
val > st->soc_info.max_sample_rate)
return -EINVAL;
 
+   mutex_lock(>lock);
at91_adc_setup_samp_freq(st, val);
+   mutex_unlock(>lock);
 
return 0;
 }
@@ -386,6 +391,21 @@ static const struct iio_info at91_adc_info = {
.driver_module = THIS_MODULE,
 };
 
+static void at91_adc_init_hw(struct at91_adc_state *st, unsigned int freq)
+{
+   at91_adc_writel(st, AT91_SAMA5D2_CR, AT91_SAMA5D2_CR_SWRST);
+   at91_adc_writel(st, AT91_SAMA5D2_IDR, 0x);
+   /*
+* Transfer field must be set to 2 according to the datasheet and
+* allows different analog settings for each channel.
+*/
+   at91_adc_writel(st, AT91_SAMA5D2_MR,
+   AT91_SAMA5D2_MR_TRANSFER(2) | AT91_SAMA5D2_MR_ANACH);
+
+   at91_adc_setup_samp_freq(st, freq);
+
+}
+
 static int at91_adc_probe(struct platform_device *pdev)
 {
struct iio_dev *indio_dev;
@@ -482,16 +502,7 @@ static int at91_adc_probe(struct platform_device *pdev)
goto vref_disable;
}
 
-   at91_adc_writel(st, AT91_SAMA5D2_CR, AT91_SAMA5D2_CR_SWRST);
-   at91_adc_writel(st, AT91_SAMA5D2_IDR, 0x);
-   /*
-* Transfer field must be set to 2 according to the datasheet and
-* allows different analog settings for each channel.
-*/
-   at91_adc_writel(st, AT91_SAMA5D2_MR,
-   AT91_SAMA5D2_MR_TRANSFER(2) | AT91_SAMA5D2_MR_ANACH);
-
-   at91_adc_setup_samp_freq(st, st->soc_info.min_sample_rate);
+   at91_adc_init_hw(st, st->soc_info.min_sample_rate);
 
ret = clk_prepare_enable(st->per_clk);
if (ret)
@@ -541,12 +552,52 @@ static const struct of_device_id at91_adc_dt_match[] = {
 };
 MODULE_DEVICE_TABLE(of, at91_adc_dt_match);
 
+#ifdef CONFIG_PM_SLEEP
+static int at91_adc_suspend(struct device *dev)
+{
+   struct platform_device *pdev = to_platform_device(dev);
+   struct iio_dev *indio_dev = platform_get_drvdata(pdev);
+   struct at91_adc_state *st = iio_priv(indio_dev);
+
+   clk_disable_unprepare(st->per_clk);
+
+   regulator_disable(st->vref);
+   regulator_disable(st->reg);
+
+   return 0;
+}
+
+static int at91_adc_resume(struct device *dev)
+{
+   struct platform_device *pdev = to_platform_device(dev);
+   struct iio_dev *indio_dev = platform_get_drvdata(pdev);
+   struct at91_adc_state *st = iio_priv(indio_dev);
+   int err;
+
+   err = regulator_enable(st->reg);
+   if (err)
+   return err;
+
+   err = regulator_enable(st->vref);
+   if (err)
+   return err;
+
+   at91_adc_init_hw(st, st->current_rate);
+
+   err = clk_prepare_enable(st->per_clk);
+   return err;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(at91_adc_pm_ops, at91_adc_suspend, at91_adc_resume);
+
 static struct platform_driver at91_adc_driver = {
.probe = at91_adc_probe,
.remove = at91_adc_remove,
.driver = {
.name = "at91-sama5d2_adc",
.of_match_table = at91_adc_dt_match,
+   .pm = _adc_pm_ops,
},
 };
 module_platform_driver(at91_adc_driver)
-- 
2.11.0



[PATCH v1 07/10] iio:adc:at91-sama5d2: Support backup mode

2017-09-08 Thread Romain Izard
Support the backup mode for platform suspend, by restoring the hardware
registers on resume.

Signed-off-by: Romain Izard 
---
 drivers/iio/adc/at91-sama5d2_adc.c | 71 --
 1 file changed, 61 insertions(+), 10 deletions(-)

diff --git a/drivers/iio/adc/at91-sama5d2_adc.c 
b/drivers/iio/adc/at91-sama5d2_adc.c
index e10dca3ed74b..f9718c863363 100644
--- a/drivers/iio/adc/at91-sama5d2_adc.c
+++ b/drivers/iio/adc/at91-sama5d2_adc.c
@@ -200,6 +200,7 @@ struct at91_adc_state {
u32 conversion_value;
struct at91_adc_soc_infosoc_info;
wait_queue_head_t   wq_data_available;
+   unsigned intcurrent_rate;
/*
 * lock to prevent concurrent 'single conversion' requests through
 * sysfs.
@@ -269,6 +270,8 @@ static void at91_adc_setup_samp_freq(struct at91_adc_state 
*st, unsigned freq)
mr |= AT91_SAMA5D2_MR_PRESCAL(prescal);
at91_adc_writel(st, AT91_SAMA5D2_MR, mr);
 
+   st->current_rate = freq;
+
dev_dbg(_dev->dev, "freq: %u, startup: %u, prescal: %u\n",
freq, startup, prescal);
 }
@@ -375,7 +378,9 @@ static int at91_adc_write_raw(struct iio_dev *indio_dev,
val > st->soc_info.max_sample_rate)
return -EINVAL;
 
+   mutex_lock(>lock);
at91_adc_setup_samp_freq(st, val);
+   mutex_unlock(>lock);
 
return 0;
 }
@@ -386,6 +391,21 @@ static const struct iio_info at91_adc_info = {
.driver_module = THIS_MODULE,
 };
 
+static void at91_adc_init_hw(struct at91_adc_state *st, unsigned int freq)
+{
+   at91_adc_writel(st, AT91_SAMA5D2_CR, AT91_SAMA5D2_CR_SWRST);
+   at91_adc_writel(st, AT91_SAMA5D2_IDR, 0x);
+   /*
+* Transfer field must be set to 2 according to the datasheet and
+* allows different analog settings for each channel.
+*/
+   at91_adc_writel(st, AT91_SAMA5D2_MR,
+   AT91_SAMA5D2_MR_TRANSFER(2) | AT91_SAMA5D2_MR_ANACH);
+
+   at91_adc_setup_samp_freq(st, freq);
+
+}
+
 static int at91_adc_probe(struct platform_device *pdev)
 {
struct iio_dev *indio_dev;
@@ -482,16 +502,7 @@ static int at91_adc_probe(struct platform_device *pdev)
goto vref_disable;
}
 
-   at91_adc_writel(st, AT91_SAMA5D2_CR, AT91_SAMA5D2_CR_SWRST);
-   at91_adc_writel(st, AT91_SAMA5D2_IDR, 0x);
-   /*
-* Transfer field must be set to 2 according to the datasheet and
-* allows different analog settings for each channel.
-*/
-   at91_adc_writel(st, AT91_SAMA5D2_MR,
-   AT91_SAMA5D2_MR_TRANSFER(2) | AT91_SAMA5D2_MR_ANACH);
-
-   at91_adc_setup_samp_freq(st, st->soc_info.min_sample_rate);
+   at91_adc_init_hw(st, st->soc_info.min_sample_rate);
 
ret = clk_prepare_enable(st->per_clk);
if (ret)
@@ -541,12 +552,52 @@ static const struct of_device_id at91_adc_dt_match[] = {
 };
 MODULE_DEVICE_TABLE(of, at91_adc_dt_match);
 
+#ifdef CONFIG_PM_SLEEP
+static int at91_adc_suspend(struct device *dev)
+{
+   struct platform_device *pdev = to_platform_device(dev);
+   struct iio_dev *indio_dev = platform_get_drvdata(pdev);
+   struct at91_adc_state *st = iio_priv(indio_dev);
+
+   clk_disable_unprepare(st->per_clk);
+
+   regulator_disable(st->vref);
+   regulator_disable(st->reg);
+
+   return 0;
+}
+
+static int at91_adc_resume(struct device *dev)
+{
+   struct platform_device *pdev = to_platform_device(dev);
+   struct iio_dev *indio_dev = platform_get_drvdata(pdev);
+   struct at91_adc_state *st = iio_priv(indio_dev);
+   int err;
+
+   err = regulator_enable(st->reg);
+   if (err)
+   return err;
+
+   err = regulator_enable(st->vref);
+   if (err)
+   return err;
+
+   at91_adc_init_hw(st, st->current_rate);
+
+   err = clk_prepare_enable(st->per_clk);
+   return err;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(at91_adc_pm_ops, at91_adc_suspend, at91_adc_resume);
+
 static struct platform_driver at91_adc_driver = {
.probe = at91_adc_probe,
.remove = at91_adc_remove,
.driver = {
.name = "at91-sama5d2_adc",
.of_match_table = at91_adc_dt_match,
+   .pm = _adc_pm_ops,
},
 };
 module_platform_driver(at91_adc_driver)
-- 
2.11.0



[PATCH v1 00/10] Various patches for SAMA5D2 backup mode

2017-09-08 Thread Romain Izard
While the core of the backup mode for SAMA5D2 has been integrated in
v4.13, it is far from complete. Individual controllers in the chip have
drivers that do not support the reset of the registers during suspend,
and they need to be adapted to handle it.

The first patch uses the clock wakeup code from the prototype backup
mode instead of the version integrated in the mainline, as the mainline
version is not stable. During a test loop with two-second backup
suspend, the mainline version will hang in less than one day, whereas
the prototype version has been running the same test for more than a
week without hanging.

Romain Izard (10):
  clk: at91: pmc: Wait for clocks when resuming
  clk: at91: pmc: Save SCSR during suspend
  clk: at91: pmc: Support backup for programmable clocks
  mtd: nand: atmel: Avoid ECC errors when leaving backup mode
  mtd: nand: atmel: Report PMECC failures as errors
  ehci-atmel: Power down during suspend is normal
  iio:adc:at91-sama5d2: Support backup mode
  pwm: atmel-tcb: Support backup mode
  atmel_flexcom: Support backup mode
  tty/serial: atmel: Prevent a warning on suspend

 drivers/clk/at91/pmc.c | 33 --
 drivers/iio/adc/at91-sama5d2_adc.c | 71 --
 drivers/mfd/atmel-flexcom.c| 65 ++
 drivers/mtd/nand/atmel/pmecc.c | 15 
 drivers/pwm/pwm-atmel-tcb.c| 63 +++--
 drivers/tty/serial/atmel_serial.c  | 13 +++
 drivers/usb/host/ehci-atmel.c  |  3 +-
 7 files changed, 216 insertions(+), 47 deletions(-)

-- 
2.11.0



[PATCH v1 00/10] Various patches for SAMA5D2 backup mode

2017-09-08 Thread Romain Izard
While the core of the backup mode for SAMA5D2 has been integrated in
v4.13, it is far from complete. Individual controllers in the chip have
drivers that do not support the reset of the registers during suspend,
and they need to be adapted to handle it.

The first patch uses the clock wakeup code from the prototype backup
mode instead of the version integrated in the mainline, as the mainline
version is not stable. During a test loop with two-second backup
suspend, the mainline version will hang in less than one day, whereas
the prototype version has been running the same test for more than a
week without hanging.

Romain Izard (10):
  clk: at91: pmc: Wait for clocks when resuming
  clk: at91: pmc: Save SCSR during suspend
  clk: at91: pmc: Support backup for programmable clocks
  mtd: nand: atmel: Avoid ECC errors when leaving backup mode
  mtd: nand: atmel: Report PMECC failures as errors
  ehci-atmel: Power down during suspend is normal
  iio:adc:at91-sama5d2: Support backup mode
  pwm: atmel-tcb: Support backup mode
  atmel_flexcom: Support backup mode
  tty/serial: atmel: Prevent a warning on suspend

 drivers/clk/at91/pmc.c | 33 --
 drivers/iio/adc/at91-sama5d2_adc.c | 71 --
 drivers/mfd/atmel-flexcom.c| 65 ++
 drivers/mtd/nand/atmel/pmecc.c | 15 
 drivers/pwm/pwm-atmel-tcb.c| 63 +++--
 drivers/tty/serial/atmel_serial.c  | 13 +++
 drivers/usb/host/ehci-atmel.c  |  3 +-
 7 files changed, 216 insertions(+), 47 deletions(-)

-- 
2.11.0



HAVE_EFFICIENT_UNALIGNED_ACCESS on ARM32 (was: Alignment issues in zImage with Linux 4.12, LZ4 and GCC5.3)

2017-09-04 Thread Romain Izard
2017-07-24 13:07 GMT+02:00 Ard Biesheuvel <ard.biesheu...@linaro.org>:
> On 24 July 2017 at 11:57, Romain Izard <romain.izard@gmail.com> wrote:
>>
>> While upgrading the kernel from 4.9 to 4.12 for a custom board with a
>> Cortex-A5 based CPU, I have encountered a compilation issue that leads to
>> a data abort during the execution of the LZ4 decompression code in
>> zImage.
>>
>> [...]
>>
>> The compilation options are a little different between both cases:
>> The library is built with -O3, whereas the zImage decompressor is built
>> with -O2, -DDISABLE_BRANCH_PROFILING, -fpic, -mno-single-pic-base,
>> -fno-builtin. All other compilation options are shared in both cases.
>>

This is a red herring: the critical option here is '-fno-builtin'. If it is
not set, the bug disappears. It also disappears if we replace it with
'-fno-builtin-putc'. But it only changes the optimizations applied by
the compiler itself, and cannot explain the issue.

Before updating the LZ4 decompressor, the LZ4 header contained specific
code for handling alignment issues, which has been changed.

>> For Linux 4.9, the LZ4 decompressor code is completely different, which
>> explains why the issue appeared when changing kernel versions.
>>
>
> I see some void* to u32* casts in the new code, which makes me think
> that it is perhaps not valid C, and has maybe not been tested on an
> architecture that has stricter alignment requirements than x86?
>

I can reproduce it easily on v4.13 with GCC6.3:
- Configure with allnoconfig
- Enable CONFIG_MMU, CONFIG_KERNEL_LZ4
- Check the generated assembly for arch/arm/boot/compressed/decompress.o:
In the LZ4_decompress_fast function, the memory access after the third
branch uses ldm and stm. This is invalid, as the addresses can be unaligned.

With this configuration, HAVE_EFFICIENT_UNALIGNED_ACCESS is set, but this is
wrong. On 32-bit ARM, the compiler is free to generate LDM or LDRD access
that will always fail on unaligned addresses. In this case, we have two
LDR/STR access to adjascent addresses that appear in inline code. The
get_unaligned functions in "include/linux/unaligned/access_ok.h" cast the
pointers directly as regular 32-bit access, and as those are by default
aligned, the compiler will optimise and combine the access.

If we use the functions from "include/linux/unaligned/le_struct.h", the
get_unaligned() function correctly tells the compiler that the access is
special, and that it should not merge memory access. But we do not fall back
to byte-by-byte access, as the compiler itself knows how to use 32-bit
access when -funaligned-access is set (by default for ARMv7).

The issue is probably hidden by the kernel fault handler in normal kernel
code, but for this case it does nothing as we are working in the boot
decompressor, that cannot use the fault handler. But it should have a
performance inpact.

As a result, this means that HAVE_EFFICIENT_UNALIGNED_ACCESS should not
be set at least in the context of "include/asm-generic/unaligned.h". But
as this option is also used in other places, where it is not related to
the get_unaligned functions, it is not possible to remove it on ARM 32-bit
without further study.

-- 
Romain Izard


HAVE_EFFICIENT_UNALIGNED_ACCESS on ARM32 (was: Alignment issues in zImage with Linux 4.12, LZ4 and GCC5.3)

2017-09-04 Thread Romain Izard
2017-07-24 13:07 GMT+02:00 Ard Biesheuvel :
> On 24 July 2017 at 11:57, Romain Izard  wrote:
>>
>> While upgrading the kernel from 4.9 to 4.12 for a custom board with a
>> Cortex-A5 based CPU, I have encountered a compilation issue that leads to
>> a data abort during the execution of the LZ4 decompression code in
>> zImage.
>>
>> [...]
>>
>> The compilation options are a little different between both cases:
>> The library is built with -O3, whereas the zImage decompressor is built
>> with -O2, -DDISABLE_BRANCH_PROFILING, -fpic, -mno-single-pic-base,
>> -fno-builtin. All other compilation options are shared in both cases.
>>

This is a red herring: the critical option here is '-fno-builtin'. If it is
not set, the bug disappears. It also disappears if we replace it with
'-fno-builtin-putc'. But it only changes the optimizations applied by
the compiler itself, and cannot explain the issue.

Before updating the LZ4 decompressor, the LZ4 header contained specific
code for handling alignment issues, which has been changed.

>> For Linux 4.9, the LZ4 decompressor code is completely different, which
>> explains why the issue appeared when changing kernel versions.
>>
>
> I see some void* to u32* casts in the new code, which makes me think
> that it is perhaps not valid C, and has maybe not been tested on an
> architecture that has stricter alignment requirements than x86?
>

I can reproduce it easily on v4.13 with GCC6.3:
- Configure with allnoconfig
- Enable CONFIG_MMU, CONFIG_KERNEL_LZ4
- Check the generated assembly for arch/arm/boot/compressed/decompress.o:
In the LZ4_decompress_fast function, the memory access after the third
branch uses ldm and stm. This is invalid, as the addresses can be unaligned.

With this configuration, HAVE_EFFICIENT_UNALIGNED_ACCESS is set, but this is
wrong. On 32-bit ARM, the compiler is free to generate LDM or LDRD access
that will always fail on unaligned addresses. In this case, we have two
LDR/STR access to adjascent addresses that appear in inline code. The
get_unaligned functions in "include/linux/unaligned/access_ok.h" cast the
pointers directly as regular 32-bit access, and as those are by default
aligned, the compiler will optimise and combine the access.

If we use the functions from "include/linux/unaligned/le_struct.h", the
get_unaligned() function correctly tells the compiler that the access is
special, and that it should not merge memory access. But we do not fall back
to byte-by-byte access, as the compiler itself knows how to use 32-bit
access when -funaligned-access is set (by default for ARMv7).

The issue is probably hidden by the kernel fault handler in normal kernel
code, but for this case it does nothing as we are working in the boot
decompressor, that cannot use the fault handler. But it should have a
performance inpact.

As a result, this means that HAVE_EFFICIENT_UNALIGNED_ACCESS should not
be set at least in the context of "include/asm-generic/unaligned.h". But
as this option is also used in other places, where it is not related to
the get_unaligned functions, it is not possible to remove it on ARM 32-bit
without further study.

-- 
Romain Izard


Re: [PATCH 1/3] ARM: at91: pm: Add sama5d2 backup mode

2017-04-27 Thread Romain Izard
essed during the at91_sramc_self_refresh, but .pmc_base
may need to be loaded in the TLB as well.

>
> /* Active the self-refresh mode */
> mov r0, #SRAMC_SELF_FRESH_ACTIVE
> bl  at91_sramc_self_refresh
>
> ldr r0, .pm_mode
> -   tst r0, #AT91_PM_SLOW_CLOCK
> -   beq skip_disable_main_clock
> +   cmp r0, #AT91_PM_SLOW_CLOCK
> +   beq slow_clock
> +#if defined(BACKUP_MODE)
> +   cmp r0, #AT91_PM_BACKUP
> +   beq backup_mode
> +#endif
>
> +   /* Wait for interrupt */
> +   ldr pmc, .pmc_base
> +   at91_cpu_idle
> +   b   exit_suspend
> +
> +slow_clock:
> +   bl  at91_slowck_mode
> +   b   exit_suspend
> +#if defined(BACKUP_MODE)
> +backup_mode:
> +   bl  at91_backup_mode
> +   b   exit_suspend
> +#endif
> +
> +exit_suspend:
> +   /* Exit the self-refresh mode */
> +   mov r0, #SRAMC_SELF_FRESH_EXIT
> +   bl  at91_sramc_self_refresh
> +
> +   /* Restore registers, and return */
> +   ldmfd   sp!, {r4 - r12, pc}
> +ENDPROC(at91_pm_suspend_in_sram)
> +
> +#if defined(BACKUP_MODE)
> +ENTRY(at91_backup_mode)
> +   #if 0
> +   /* Read LPR */
> +   ldr r2, .sramc_base
> +   ldr r3, [r2, #AT91_DDRSDRC_LPR]
> +   #endif
> +

Do we need to keep this commented code ?

> +   /*BUMEN*/
> +   ldr r0, .sfr
> +   mov tmp1, #(0x1)

We don't need any parenthesis here

> +   str tmp1, [r0, #0x10]
> +
> +   /* Shutdown */
> +   ldr r0, .shdwc
> +   movwtmp1, #0x1
> +   movttmp1, #0xA500

I believe the following assembly should do the same thing
without using v6+ instructions.

movtmp1, #0xA500
addtmp1, tmp1, #0x1

> +   str tmp1, [r0, #0]
> +ENDPROC(at91_backup_mode)
> +#endif
> +
> +ENTRY(at91_slowck_mode)
> ldr pmc, .pmc_base
>
> /* Save Master clock setting */
> @@ -134,18 +193,9 @@ ENTRY(at91_pm_suspend_in_sram)
> orr tmp1, tmp1, #AT91_PMC_KEY
> str tmp1, [pmc, #AT91_CKGR_MOR]
>
> -skip_disable_main_clock:
> -   ldr pmc, .pmc_base
> -
> /* Wait for interrupt */
> at91_cpu_idle
>
> -   ldr r0, .pm_mode
> -   tst r0, #AT91_PM_SLOW_CLOCK
> -   beq skip_enable_main_clock
> -
> -   ldr pmc, .pmc_base
> -
> /* Turn on the main oscillator */
> ldr tmp1, [pmc, #AT91_CKGR_MOR]
> orr tmp1, tmp1, #AT91_PMC_MOSCEN
> @@ -174,14 +224,8 @@ skip_disable_main_clock:
>
> wait_mckrdy
>
> -skip_enable_main_clock:
> -   /* Exit the self-refresh mode */
> -   mov r0, #SRAMC_SELF_FRESH_EXIT
> -   bl  at91_sramc_self_refresh
> -
> -   /* Restore registers, and return */
> -   ldmfd   sp!, {r4 - r12, pc}
> -ENDPROC(at91_pm_suspend_in_sram)
> +   mov pc, lr
> +ENDPROC(at91_slowck_mode)
>
>  /*
>   * void at91_sramc_self_refresh(unsigned int is_active)
> @@ -314,6 +358,10 @@ ENDPROC(at91_sramc_self_refresh)
> .word 0
>  .sramc1_base:
>     .word 0
> +.shdwc:
> +   .word 0
> +.sfr:
> +   .word 0
>  .memtype:
> .word 0
>  .pm_mode:
> diff --git a/arch/arm/mach-at91/sama5.c b/arch/arm/mach-at91/sama5.c
> index 6d157d0ead8e..3d0bf95a56ae 100644
> --- a/arch/arm/mach-at91/sama5.c
> +++ b/arch/arm/mach-at91/sama5.c
> @@ -34,7 +34,6 @@ DT_MACHINE_START(sama5_dt, "Atmel SAMA5")
>  MACHINE_END
>
>  static const char *const sama5_alt_dt_board_compat[] __initconst = {
> -   "atmel,sama5d2",
> "atmel,sama5d4",
> NULL
>  };
> @@ -45,3 +44,21 @@ DT_MACHINE_START(sama5_alt_dt, "Atmel SAMA5")
> .dt_compat  = sama5_alt_dt_board_compat,
> .l2c_aux_mask   = ~0UL,
>  MACHINE_END
> +
> +static void __init sama5d2_init(void)
> +{
> +   of_platform_default_populate(NULL, NULL, NULL);
> +   sama5d2_pm_init();
> +}
> +
> +static const char *const sama5d2_compat[] __initconst = {
> +   "atmel,sama5d2",
> +   NULL
> +};
> +
> +DT_MACHINE_START(sama5d2, "Atmel SAMA5")
> +   /* Maintainer: Atmel */
> +   .init_machine   = sama5d2_init,
> +   .dt_compat  = sama5d2_compat,
> +   .l2c_aux_mask   = ~0UL,
> +MACHINE_END

Best regards,
-- 
Romain Izard


Re: [PATCH 1/3] ARM: at91: pm: Add sama5d2 backup mode

2017-04-27 Thread Romain Izard
 in the TLB as well.

>
> /* Active the self-refresh mode */
> mov r0, #SRAMC_SELF_FRESH_ACTIVE
> bl  at91_sramc_self_refresh
>
> ldr r0, .pm_mode
> -   tst r0, #AT91_PM_SLOW_CLOCK
> -   beq skip_disable_main_clock
> +   cmp r0, #AT91_PM_SLOW_CLOCK
> +   beq slow_clock
> +#if defined(BACKUP_MODE)
> +   cmp r0, #AT91_PM_BACKUP
> +   beq backup_mode
> +#endif
>
> +   /* Wait for interrupt */
> +   ldr pmc, .pmc_base
> +   at91_cpu_idle
> +   b   exit_suspend
> +
> +slow_clock:
> +   bl  at91_slowck_mode
> +   b   exit_suspend
> +#if defined(BACKUP_MODE)
> +backup_mode:
> +   bl  at91_backup_mode
> +   b   exit_suspend
> +#endif
> +
> +exit_suspend:
> +   /* Exit the self-refresh mode */
> +   mov r0, #SRAMC_SELF_FRESH_EXIT
> +   bl  at91_sramc_self_refresh
> +
> +   /* Restore registers, and return */
> +   ldmfd   sp!, {r4 - r12, pc}
> +ENDPROC(at91_pm_suspend_in_sram)
> +
> +#if defined(BACKUP_MODE)
> +ENTRY(at91_backup_mode)
> +   #if 0
> +   /* Read LPR */
> +   ldr r2, .sramc_base
> +   ldr r3, [r2, #AT91_DDRSDRC_LPR]
> +   #endif
> +

Do we need to keep this commented code ?

> +   /*BUMEN*/
> +   ldr r0, .sfr
> +   mov tmp1, #(0x1)

We don't need any parenthesis here

> +   str tmp1, [r0, #0x10]
> +
> +   /* Shutdown */
> +   ldr r0, .shdwc
> +   movwtmp1, #0x1
> +   movttmp1, #0xA500

I believe the following assembly should do the same thing
without using v6+ instructions.

movtmp1, #0xA500
addtmp1, tmp1, #0x1

> +   str tmp1, [r0, #0]
> +ENDPROC(at91_backup_mode)
> +#endif
> +
> +ENTRY(at91_slowck_mode)
> ldr pmc, .pmc_base
>
> /* Save Master clock setting */
> @@ -134,18 +193,9 @@ ENTRY(at91_pm_suspend_in_sram)
> orr tmp1, tmp1, #AT91_PMC_KEY
> str tmp1, [pmc, #AT91_CKGR_MOR]
>
> -skip_disable_main_clock:
> -   ldr pmc, .pmc_base
> -
> /* Wait for interrupt */
> at91_cpu_idle
>
> -   ldr r0, .pm_mode
> -   tst r0, #AT91_PM_SLOW_CLOCK
> -   beq skip_enable_main_clock
> -
> -   ldr pmc, .pmc_base
> -
> /* Turn on the main oscillator */
> ldr tmp1, [pmc, #AT91_CKGR_MOR]
> orr tmp1, tmp1, #AT91_PMC_MOSCEN
> @@ -174,14 +224,8 @@ skip_disable_main_clock:
>
> wait_mckrdy
>
> -skip_enable_main_clock:
> -   /* Exit the self-refresh mode */
> -   mov r0, #SRAMC_SELF_FRESH_EXIT
> -   bl  at91_sramc_self_refresh
> -
> -   /* Restore registers, and return */
> -   ldmfd   sp!, {r4 - r12, pc}
> -ENDPROC(at91_pm_suspend_in_sram)
> +   mov pc, lr
> +ENDPROC(at91_slowck_mode)
>
>  /*
>   * void at91_sramc_self_refresh(unsigned int is_active)
> @@ -314,6 +358,10 @@ ENDPROC(at91_sramc_self_refresh)
> .word 0
>  .sramc1_base:
> .word 0
> +.shdwc:
> +   .word 0
> +.sfr:
> +   .word 0
>  .memtype:
> .word 0
>  .pm_mode:
> diff --git a/arch/arm/mach-at91/sama5.c b/arch/arm/mach-at91/sama5.c
> index 6d157d0ead8e..3d0bf95a56ae 100644
> --- a/arch/arm/mach-at91/sama5.c
> +++ b/arch/arm/mach-at91/sama5.c
> @@ -34,7 +34,6 @@ DT_MACHINE_START(sama5_dt, "Atmel SAMA5")
>  MACHINE_END
>
>  static const char *const sama5_alt_dt_board_compat[] __initconst = {
> -   "atmel,sama5d2",
> "atmel,sama5d4",
> NULL
>  };
> @@ -45,3 +44,21 @@ DT_MACHINE_START(sama5_alt_dt, "Atmel SAMA5")
> .dt_compat  = sama5_alt_dt_board_compat,
> .l2c_aux_mask   = ~0UL,
>  MACHINE_END
> +
> +static void __init sama5d2_init(void)
> +{
> +   of_platform_default_populate(NULL, NULL, NULL);
> +   sama5d2_pm_init();
> +}
> +
> +static const char *const sama5d2_compat[] __initconst = {
> +   "atmel,sama5d2",
> +   NULL
> +};
> +
> +DT_MACHINE_START(sama5d2, "Atmel SAMA5")
> +   /* Maintainer: Atmel */
> +   .init_machine   = sama5d2_init,
> +   .dt_compat  = sama5d2_compat,
> +   .l2c_aux_mask   = ~0UL,
> +MACHINE_END

Best regards,
-- 
Romain Izard


Re: [PATCH v1] usb: gadget: udc: atmel: Remove an unused local variable

2017-03-10 Thread Romain Izard
2017-03-09 18:02 GMT+01:00 Romain Izard <romain.izard@gmail.com>:
> The local variable ept_cfg is not used anymore in usba_ep_enable.
> Use ep->ept_cfg in the debug function to remove a warning when building
> with dynamic debug enabled.
>
> Signed-off-by: Romain Izard <romain.izard@gmail.com>
> Fixes: 741d2558bf0a ("usb: gadget: udc: atmel: Update endpoint allocation 
> scheme")

Sorry for the noise, 4242820277b5 ("usb: gadget: udc: atmel: fix debug
output") in Greg KH's tree already fixed this.

-- 
Romain Izard


Re: [PATCH v1] usb: gadget: udc: atmel: Remove an unused local variable

2017-03-10 Thread Romain Izard
2017-03-09 18:02 GMT+01:00 Romain Izard :
> The local variable ept_cfg is not used anymore in usba_ep_enable.
> Use ep->ept_cfg in the debug function to remove a warning when building
> with dynamic debug enabled.
>
> Signed-off-by: Romain Izard 
> Fixes: 741d2558bf0a ("usb: gadget: udc: atmel: Update endpoint allocation 
> scheme")

Sorry for the noise, 4242820277b5 ("usb: gadget: udc: atmel: fix debug
output") in Greg KH's tree already fixed this.

-- 
Romain Izard


[PATCH v3 2/2] usb: gadget: reword configuration choices

2017-03-10 Thread Romain Izard
As USB_CONFIGFS is not a part of the "USB Gadget Drivers" choice
anymore, the name for the option and its attached description needs to
be more descriptive. It appears one level higher in the configuration
menu, and without the context provided by the comments for the choice
entry, it needs to make sense on its own.

Conversely, the "USB Gadget Drivers" entry now only introduces the
legacy drivers, where one or more functions are combined in a single
driver. As the configfs option can be used as a full-fledged
alternative, rename the choice entry to show that it is not the only
way to provice service as an USB gadget.

Signed-off-by: Romain Izard <romain.izard@gmail.com>
---
Changes in v3:
 - split from the functional patch

 drivers/usb/gadget/Kconfig | 10 --
 1 file changed, 8 insertions(+), 2 deletions(-)

diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig
index f3ee80ece682..e157e9aa4f3d 100644
--- a/drivers/usb/gadget/Kconfig
+++ b/drivers/usb/gadget/Kconfig
@@ -212,7 +212,7 @@ config USB_F_TCM
 # this first set of drivers all depend on bulk-capable hardware.
 
 config USB_CONFIGFS
-   tristate "USB functions configurable through configfs"
+   tristate "USB Gadget functions configurable through configfs"
select USB_LIBCOMPOSITE
help
  A Linux USB "gadget" can be set up through configfs.
@@ -458,7 +458,7 @@ config USB_CONFIGFS_F_TCM
  UAS utilizes the USB 3.0 feature called streams support.
 
 choice
-   tristate "USB Gadget Drivers"
+   tristate "USB Gadget precomposed configurations"
default USB_ETH
optional
help
@@ -477,6 +477,12 @@ choice
  not be able work with that controller, or might need to implement
  a less common variant of a device class protocol.
 
+ The available choices each represent a single precomposed USB
+ gadget configuration. In the device model, each option contains
+ both the device instanciation as a child for a USB gadget
+ controller, and the relevant drivers for each function declared
+ by the device.
+
 source "drivers/usb/gadget/legacy/Kconfig"
 
 endchoice
-- 
2.9.3



[PATCH v3 2/2] usb: gadget: reword configuration choices

2017-03-10 Thread Romain Izard
As USB_CONFIGFS is not a part of the "USB Gadget Drivers" choice
anymore, the name for the option and its attached description needs to
be more descriptive. It appears one level higher in the configuration
menu, and without the context provided by the comments for the choice
entry, it needs to make sense on its own.

Conversely, the "USB Gadget Drivers" entry now only introduces the
legacy drivers, where one or more functions are combined in a single
driver. As the configfs option can be used as a full-fledged
alternative, rename the choice entry to show that it is not the only
way to provice service as an USB gadget.

Signed-off-by: Romain Izard 
---
Changes in v3:
 - split from the functional patch

 drivers/usb/gadget/Kconfig | 10 --
 1 file changed, 8 insertions(+), 2 deletions(-)

diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig
index f3ee80ece682..e157e9aa4f3d 100644
--- a/drivers/usb/gadget/Kconfig
+++ b/drivers/usb/gadget/Kconfig
@@ -212,7 +212,7 @@ config USB_F_TCM
 # this first set of drivers all depend on bulk-capable hardware.
 
 config USB_CONFIGFS
-   tristate "USB functions configurable through configfs"
+   tristate "USB Gadget functions configurable through configfs"
select USB_LIBCOMPOSITE
help
  A Linux USB "gadget" can be set up through configfs.
@@ -458,7 +458,7 @@ config USB_CONFIGFS_F_TCM
  UAS utilizes the USB 3.0 feature called streams support.
 
 choice
-   tristate "USB Gadget Drivers"
+   tristate "USB Gadget precomposed configurations"
default USB_ETH
optional
help
@@ -477,6 +477,12 @@ choice
  not be able work with that controller, or might need to implement
  a less common variant of a device class protocol.
 
+ The available choices each represent a single precomposed USB
+ gadget configuration. In the device model, each option contains
+ both the device instanciation as a child for a USB gadget
+ controller, and the relevant drivers for each function declared
+ by the device.
+
 source "drivers/usb/gadget/legacy/Kconfig"
 
 endchoice
-- 
2.9.3



[PATCH v3 0/2] Various cleanups for USB Gadget Kconfig

2017-03-10 Thread Romain Izard
Fix some issues left when the configuration was updated to support
a built-in USB gadget configfs together with modular legacy gadget
drivers.

Changes in v2:
 - Reword description
Changes in v3:
 - Split functional and comment changes

Romain Izard (2):
  usb: gadget: legacy gadgets are optional
  usb: gadget: reword configuration choices

 drivers/usb/gadget/Kconfig | 11 +--
 1 file changed, 9 insertions(+), 2 deletions(-)

-- 
2.9.3



[PATCH v3 0/2] Various cleanups for USB Gadget Kconfig

2017-03-10 Thread Romain Izard
Fix some issues left when the configuration was updated to support
a built-in USB gadget configfs together with modular legacy gadget
drivers.

Changes in v2:
 - Reword description
Changes in v3:
 - Split functional and comment changes

Romain Izard (2):
  usb: gadget: legacy gadgets are optional
  usb: gadget: reword configuration choices

 drivers/usb/gadget/Kconfig | 11 +--
 1 file changed, 9 insertions(+), 2 deletions(-)

-- 
2.9.3



[PATCH v3 1/2] usb: gadget: legacy gadgets are optional

2017-03-10 Thread Romain Izard
With commit bc49d1d17dcf ("usb: gadget: don't couple configfs to legacy
gadgets"),it is possible to build a modular kernel with both built-in
configfs support and modular legacy gadget drivers.

But when building a kernel without modules, it is also necessary to be
able to build with configfs but without any legacy gadget driver. This
was a possible configuration when the USB_CONFIGFS was a part of the
choice options, but not anymore.

Mark the choice for legacy gadget drivers as optional restores this.

Fixes: bc49d1d17dcf ("usb: gadget: don't couple configfs to legacy gadgets")
Cc: <sta...@vger.kernel.org> # 4.9+
Signed-off-by: Romain Izard <romain.izard@gmail.com>
---
Changes in v2:
 - Reword description
Changes in v3:
 - Remove comment changes

 drivers/usb/gadget/Kconfig | 1 +
 1 file changed, 1 insertion(+)

diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig
index 8ad203296079..f3ee80ece682 100644
--- a/drivers/usb/gadget/Kconfig
+++ b/drivers/usb/gadget/Kconfig
@@ -460,6 +460,7 @@ config USB_CONFIGFS_F_TCM
 choice
tristate "USB Gadget Drivers"
default USB_ETH
+   optional
help
  A Linux "Gadget Driver" talks to the USB Peripheral Controller
  driver through the abstract "gadget" API.  Some other operating
-- 
2.9.3



[PATCH v3 1/2] usb: gadget: legacy gadgets are optional

2017-03-10 Thread Romain Izard
With commit bc49d1d17dcf ("usb: gadget: don't couple configfs to legacy
gadgets"),it is possible to build a modular kernel with both built-in
configfs support and modular legacy gadget drivers.

But when building a kernel without modules, it is also necessary to be
able to build with configfs but without any legacy gadget driver. This
was a possible configuration when the USB_CONFIGFS was a part of the
choice options, but not anymore.

Mark the choice for legacy gadget drivers as optional restores this.

Fixes: bc49d1d17dcf ("usb: gadget: don't couple configfs to legacy gadgets")
Cc:  # 4.9+
Signed-off-by: Romain Izard 
---
Changes in v2:
 - Reword description
Changes in v3:
 - Remove comment changes

 drivers/usb/gadget/Kconfig | 1 +
 1 file changed, 1 insertion(+)

diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig
index 8ad203296079..f3ee80ece682 100644
--- a/drivers/usb/gadget/Kconfig
+++ b/drivers/usb/gadget/Kconfig
@@ -460,6 +460,7 @@ config USB_CONFIGFS_F_TCM
 choice
tristate "USB Gadget Drivers"
default USB_ETH
+   optional
help
  A Linux "Gadget Driver" talks to the USB Peripheral Controller
  driver through the abstract "gadget" API.  Some other operating
-- 
2.9.3



Re: [PATCH v2] usb: gadget: legacy gadgets are optional

2017-03-10 Thread Romain Izard
Hello Felipe,

2017-03-10 10:15 GMT+01:00 Felipe Balbi <ba...@kernel.org>:
>
> Hi,
>
> Romain Izard <romain.izard@gmail.com> writes:
>> With commit "usb: gadget: don't couple configfs to legacy gadgets"
>> it is possible to build a modular kernel with both built-in configfs
>> support and modular legacy gadget drivers.
>>
>> But when building a kernel without modules, it is also necessary to be
>> able to build with configfs but without any legacy gadget driver.
>>
>> Mark the choice for legacy gadget drivers as optional.
>>
>> Fixes: bc49d1d17dcf ("usb: gadget: don't couple configfs to legacy gadgets")
>> Cc: <sta...@vger.kernel.org> # 4.9+
>
> this is *NOT* a fix since this requirement didn't exist before.

It worked in 4.1, as a non-modular kernel would only have a single entry
from the USB gadget driver choice option, and USB_CONFIGFS was one of
those.

When I moved on to 4.9, this configuration could not be selected anymore.

>
>> Signed-off-by: Romain Izard <romain.izard@gmail.com>
>> ---
>> changes in v2:
>>  - Reword description
>>
>>  drivers/usb/gadget/Kconfig | 11 +--
>>  1 file changed, 9 insertions(+), 2 deletions(-)
>>
>> diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig
>> index 8ad203296079..e157e9aa4f3d 100644
>> --- a/drivers/usb/gadget/Kconfig
>> +++ b/drivers/usb/gadget/Kconfig
>> @@ -212,7 +212,7 @@ config USB_F_TCM
>>  # this first set of drivers all depend on bulk-capable hardware.
>>
>>  config USB_CONFIGFS
>> - tristate "USB functions configurable through configfs"
>> + tristate "USB Gadget functions configurable through configfs"
>
> unrelated change
>
>>   select USB_LIBCOMPOSITE
>>   help
>> A Linux USB "gadget" can be set up through configfs.
>> @@ -458,8 +458,9 @@ config USB_CONFIGFS_F_TCM
>> UAS utilizes the USB 3.0 feature called streams support.
>>
>>  choice
>> - tristate "USB Gadget Drivers"
>> + tristate "USB Gadget precomposed configurations"
>
> unrelated change
>
>>   default USB_ETH
>> + optional
>>   help
>> A Linux "Gadget Driver" talks to the USB Peripheral Controller
>> driver through the abstract "gadget" API.  Some other operating
>> @@ -476,6 +477,12 @@ choice
>> not be able work with that controller, or might need to implement
>> a less common variant of a device class protocol.
>>
>> +   The available choices each represent a single precomposed USB
>> +   gadget configuration. In the device model, each option contains
>> +   both the device instanciation as a child for a USB gadget
>> +   controller, and the relevant drivers for each function declared
>> +   by the device.
>
> unrelated change

I'll split this.

Best regards,
-- 
Romain Izard


Re: [PATCH v2] usb: gadget: legacy gadgets are optional

2017-03-10 Thread Romain Izard
Hello Felipe,

2017-03-10 10:15 GMT+01:00 Felipe Balbi :
>
> Hi,
>
> Romain Izard  writes:
>> With commit "usb: gadget: don't couple configfs to legacy gadgets"
>> it is possible to build a modular kernel with both built-in configfs
>> support and modular legacy gadget drivers.
>>
>> But when building a kernel without modules, it is also necessary to be
>> able to build with configfs but without any legacy gadget driver.
>>
>> Mark the choice for legacy gadget drivers as optional.
>>
>> Fixes: bc49d1d17dcf ("usb: gadget: don't couple configfs to legacy gadgets")
>> Cc:  # 4.9+
>
> this is *NOT* a fix since this requirement didn't exist before.

It worked in 4.1, as a non-modular kernel would only have a single entry
from the USB gadget driver choice option, and USB_CONFIGFS was one of
those.

When I moved on to 4.9, this configuration could not be selected anymore.

>
>> Signed-off-by: Romain Izard 
>> ---
>> changes in v2:
>>  - Reword description
>>
>>  drivers/usb/gadget/Kconfig | 11 +--
>>  1 file changed, 9 insertions(+), 2 deletions(-)
>>
>> diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig
>> index 8ad203296079..e157e9aa4f3d 100644
>> --- a/drivers/usb/gadget/Kconfig
>> +++ b/drivers/usb/gadget/Kconfig
>> @@ -212,7 +212,7 @@ config USB_F_TCM
>>  # this first set of drivers all depend on bulk-capable hardware.
>>
>>  config USB_CONFIGFS
>> - tristate "USB functions configurable through configfs"
>> + tristate "USB Gadget functions configurable through configfs"
>
> unrelated change
>
>>   select USB_LIBCOMPOSITE
>>   help
>> A Linux USB "gadget" can be set up through configfs.
>> @@ -458,8 +458,9 @@ config USB_CONFIGFS_F_TCM
>> UAS utilizes the USB 3.0 feature called streams support.
>>
>>  choice
>> - tristate "USB Gadget Drivers"
>> + tristate "USB Gadget precomposed configurations"
>
> unrelated change
>
>>   default USB_ETH
>> + optional
>>   help
>> A Linux "Gadget Driver" talks to the USB Peripheral Controller
>> driver through the abstract "gadget" API.  Some other operating
>> @@ -476,6 +477,12 @@ choice
>> not be able work with that controller, or might need to implement
>> a less common variant of a device class protocol.
>>
>> +   The available choices each represent a single precomposed USB
>> +   gadget configuration. In the device model, each option contains
>> +   both the device instanciation as a child for a USB gadget
>> +   controller, and the relevant drivers for each function declared
>> +   by the device.
>
> unrelated change

I'll split this.

Best regards,
-- 
Romain Izard


[PATCH v1] usb: gadget: udc: atmel: Remove an unused local variable

2017-03-09 Thread Romain Izard
The local variable ept_cfg is not used anymore in usba_ep_enable.
Use ep->ept_cfg in the debug function to remove a warning when building
with dynamic debug enabled.

Signed-off-by: Romain Izard <romain.izard@gmail.com>
Fixes: 741d2558bf0a ("usb: gadget: udc: atmel: Update endpoint allocation 
scheme")
---
 drivers/usb/gadget/udc/atmel_usba_udc.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/drivers/usb/gadget/udc/atmel_usba_udc.c 
b/drivers/usb/gadget/udc/atmel_usba_udc.c
index 11bbce28bc23..2035906b8ced 100644
--- a/drivers/usb/gadget/udc/atmel_usba_udc.c
+++ b/drivers/usb/gadget/udc/atmel_usba_udc.c
@@ -610,7 +610,7 @@ usba_ep_enable(struct usb_ep *_ep, const struct 
usb_endpoint_descriptor *desc)
 {
struct usba_ep *ep = to_usba_ep(_ep);
struct usba_udc *udc = ep->udc;
-   unsigned long flags, ept_cfg, maxpacket;
+   unsigned long flags, maxpacket;
unsigned int nr_trans;
 
DBG(DBG_GADGET, "%s: ep_enable: desc=%p\n", ep->ep.name, desc);
@@ -630,7 +630,7 @@ usba_ep_enable(struct usb_ep *_ep, const struct 
usb_endpoint_descriptor *desc)
ep->is_in = 0;
 
DBG(DBG_ERR, "%s: EPT_CFG = 0x%lx (maxpacket = %lu)\n",
-   ep->ep.name, ept_cfg, maxpacket);
+   ep->ep.name, ep->ept_cfg, maxpacket);
 
if (usb_endpoint_dir_in(desc)) {
ep->is_in = 1;
-- 
2.9.3



[PATCH v1] usb: gadget: udc: atmel: Remove an unused local variable

2017-03-09 Thread Romain Izard
The local variable ept_cfg is not used anymore in usba_ep_enable.
Use ep->ept_cfg in the debug function to remove a warning when building
with dynamic debug enabled.

Signed-off-by: Romain Izard 
Fixes: 741d2558bf0a ("usb: gadget: udc: atmel: Update endpoint allocation 
scheme")
---
 drivers/usb/gadget/udc/atmel_usba_udc.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/drivers/usb/gadget/udc/atmel_usba_udc.c 
b/drivers/usb/gadget/udc/atmel_usba_udc.c
index 11bbce28bc23..2035906b8ced 100644
--- a/drivers/usb/gadget/udc/atmel_usba_udc.c
+++ b/drivers/usb/gadget/udc/atmel_usba_udc.c
@@ -610,7 +610,7 @@ usba_ep_enable(struct usb_ep *_ep, const struct 
usb_endpoint_descriptor *desc)
 {
struct usba_ep *ep = to_usba_ep(_ep);
struct usba_udc *udc = ep->udc;
-   unsigned long flags, ept_cfg, maxpacket;
+   unsigned long flags, maxpacket;
unsigned int nr_trans;
 
DBG(DBG_GADGET, "%s: ep_enable: desc=%p\n", ep->ep.name, desc);
@@ -630,7 +630,7 @@ usba_ep_enable(struct usb_ep *_ep, const struct 
usb_endpoint_descriptor *desc)
ep->is_in = 0;
 
DBG(DBG_ERR, "%s: EPT_CFG = 0x%lx (maxpacket = %lu)\n",
-   ep->ep.name, ept_cfg, maxpacket);
+   ep->ep.name, ep->ept_cfg, maxpacket);
 
if (usb_endpoint_dir_in(desc)) {
ep->is_in = 1;
-- 
2.9.3



[PATCH v1] Revert "clocksource/drivers/tcb_clksrc: Use 32 bit tcb as sched_clock"

2017-03-09 Thread Romain Izard
This reverts commit 7b9f1d16e6d1 ("clocksource/drivers/tcb_clksrc: Use
32 bit tcb as sched_clock"). In the current state, the kernel warns
against a late registration of the new sched_clock, the printk clock
resets after only a few minutes, and it seems that scheduling can be
affected as well.

Signed-off-by: Romain Izard <romain.izard@gmail.com>
---
 drivers/clocksource/tcb_clksrc.c | 16 +---
 1 file changed, 1 insertion(+), 15 deletions(-)

diff --git a/drivers/clocksource/tcb_clksrc.c b/drivers/clocksource/tcb_clksrc.c
index 745844ee973e..d4ca9962a759 100644
--- a/drivers/clocksource/tcb_clksrc.c
+++ b/drivers/clocksource/tcb_clksrc.c
@@ -10,7 +10,6 @@
 #include 
 #include 
 #include 
-#include 
 
 
 /*
@@ -57,14 +56,9 @@ static u64 tc_get_cycles(struct clocksource *cs)
return (upper << 16) | lower;
 }
 
-static u32 tc_get_cv32(void)
-{
-   return __raw_readl(tcaddr + ATMEL_TC_REG(0, CV));
-}
-
 static u64 tc_get_cycles32(struct clocksource *cs)
 {
-   return tc_get_cv32();
+   return __raw_readl(tcaddr + ATMEL_TC_REG(0, CV));
 }
 
 static struct clocksource clksrc = {
@@ -75,11 +69,6 @@ static struct clocksource clksrc = {
.flags  = CLOCK_SOURCE_IS_CONTINUOUS,
 };
 
-static u64 notrace tc_read_sched_clock(void)
-{
-   return tc_get_cv32();
-}
-
 #ifdef CONFIG_GENERIC_CLOCKEVENTS
 
 struct tc_clkevt_device {
@@ -350,9 +339,6 @@ static int __init tcb_clksrc_init(void)
clksrc.read = tc_get_cycles32;
/* setup ony channel 0 */
tcb_setup_single_chan(tc, best_divisor_idx);
-
-   /* register sched_clock on chips with single 32 bit counter */
-   sched_clock_register(tc_read_sched_clock, 32, divided_rate);
} else {
/* tclib will give us three clocks no matter what the
 * underlying platform supports.
-- 
2.9.3



<    1   2   3   >