Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 270e5a74 authored by Fabrice Gasnier's avatar Fabrice Gasnier Committed by Greg Kroah-Hartman
Browse files

serial: stm32: add wakeup mechanism



Add support for wake-up from low power modes. This extends stm32f7.
Introduce new compatible for stm32h7 to manage wake-up capability.

Signed-off-by: default avatarFabrice Gasnier <fabrice.gasnier@st.com>
Signed-off-by: default avatarBich Hemon <bich.hemon@st.com>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent 20e4cfc9
Loading
Loading
Loading
Loading
+89 −1
Original line number Diff line number Diff line
@@ -26,6 +26,7 @@
#include <linux/of_platform.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/pm_wakeirq.h>
#include <linux/serial_core.h>
#include <linux/serial.h>
#include <linux/spinlock.h>
@@ -326,6 +327,10 @@ static irqreturn_t stm32_interrupt(int irq, void *ptr)

	sr = readl_relaxed(port->membase + ofs->isr);

	if ((sr & USART_SR_WUF) && (ofs->icr != UNDEF_REG))
		writel_relaxed(USART_ICR_WUCF,
			       port->membase + ofs->icr);

	if ((sr & USART_SR_RXNE) && !(stm32_port->rx_ch))
		stm32_receive_chars(port, false);

@@ -442,6 +447,7 @@ static int stm32_startup(struct uart_port *port)
{
	struct stm32_port *stm32_port = to_stm32_port(port);
	struct stm32_usart_offsets *ofs = &stm32_port->info->ofs;
	struct stm32_usart_config *cfg = &stm32_port->info->cfg;
	const char *name = to_platform_device(port->dev)->name;
	u32 val;
	int ret;
@@ -452,6 +458,15 @@ static int stm32_startup(struct uart_port *port)
	if (ret)
		return ret;

	if (cfg->has_wakeup && stm32_port->wakeirq >= 0) {
		ret = dev_pm_set_dedicated_wake_irq(port->dev,
						    stm32_port->wakeirq);
		if (ret) {
			free_irq(port->irq, port);
			return ret;
		}
	}

	val = USART_CR1_RXNEIE | USART_CR1_TE | USART_CR1_RE;
	stm32_set_bits(port, ofs->cr1, val);

@@ -469,6 +484,7 @@ static void stm32_shutdown(struct uart_port *port)
	val |= BIT(cfg->uart_enable_bit);
	stm32_clr_bits(port, ofs->cr1, val);

	dev_pm_clear_wake_irq(port->dev);
	free_irq(port->irq, port);
}

@@ -659,6 +675,7 @@ static int stm32_init_port(struct stm32_port *stm32port,
	port->ops	= &stm32_uart_ops;
	port->dev	= &pdev->dev;
	port->irq	= platform_get_irq(pdev, 0);
	stm32port->wakeirq = platform_get_irq(pdev, 1);

	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
	port->membase = devm_ioremap_resource(&pdev->dev, res);
@@ -716,6 +733,8 @@ static const struct of_device_id stm32_match[] = {
	{ .compatible = "st,stm32-uart", .data = &stm32f4_info},
	{ .compatible = "st,stm32f7-usart", .data = &stm32f7_info},
	{ .compatible = "st,stm32f7-uart", .data = &stm32f7_info},
	{ .compatible = "st,stm32h7-usart", .data = &stm32h7_info},
	{ .compatible = "st,stm32h7-uart", .data = &stm32h7_info},
	{},
};

@@ -865,9 +884,15 @@ static int stm32_serial_probe(struct platform_device *pdev)
	if (ret)
		return ret;

	ret = uart_add_one_port(&stm32_usart_driver, &stm32port->port);
	if (stm32port->info->cfg.has_wakeup && stm32port->wakeirq >= 0) {
		ret = device_init_wakeup(&pdev->dev, true);
		if (ret)
			goto err_uninit;
	}

	ret = uart_add_one_port(&stm32_usart_driver, &stm32port->port);
	if (ret)
		goto err_nowup;

	ret = stm32_of_dma_rx_probe(stm32port, pdev);
	if (ret)
@@ -881,6 +906,10 @@ static int stm32_serial_probe(struct platform_device *pdev)

	return 0;

err_nowup:
	if (stm32port->info->cfg.has_wakeup && stm32port->wakeirq >= 0)
		device_init_wakeup(&pdev->dev, false);

err_uninit:
	clk_disable_unprepare(stm32port->clk);

@@ -892,6 +921,7 @@ static int stm32_serial_remove(struct platform_device *pdev)
	struct uart_port *port = platform_get_drvdata(pdev);
	struct stm32_port *stm32_port = to_stm32_port(port);
	struct stm32_usart_offsets *ofs = &stm32_port->info->ofs;
	struct stm32_usart_config *cfg = &stm32_port->info->cfg;

	stm32_clr_bits(port, ofs->cr3, USART_CR3_DMAR);

@@ -913,6 +943,9 @@ static int stm32_serial_remove(struct platform_device *pdev)
				  TX_BUF_L, stm32_port->tx_buf,
				  stm32_port->tx_dma_buf);

	if (cfg->has_wakeup && stm32_port->wakeirq >= 0)
		device_init_wakeup(&pdev->dev, false);

	clk_disable_unprepare(stm32_port->clk);

	return uart_remove_one_port(&stm32_usart_driver, port);
@@ -1018,11 +1051,66 @@ static struct uart_driver stm32_usart_driver = {
	.cons		= STM32_SERIAL_CONSOLE,
};

#ifdef CONFIG_PM_SLEEP
static void stm32_serial_enable_wakeup(struct uart_port *port, bool enable)
{
	struct stm32_port *stm32_port = to_stm32_port(port);
	struct stm32_usart_offsets *ofs = &stm32_port->info->ofs;
	struct stm32_usart_config *cfg = &stm32_port->info->cfg;
	u32 val;

	if (!cfg->has_wakeup || stm32_port->wakeirq < 0)
		return;

	if (enable) {
		stm32_clr_bits(port, ofs->cr1, BIT(cfg->uart_enable_bit));
		stm32_set_bits(port, ofs->cr1, USART_CR1_UESM);
		val = readl_relaxed(port->membase + ofs->cr3);
		val &= ~USART_CR3_WUS_MASK;
		/* Enable Wake up interrupt from low power on start bit */
		val |= USART_CR3_WUS_START_BIT | USART_CR3_WUFIE;
		writel_relaxed(val, port->membase + ofs->cr3);
		stm32_set_bits(port, ofs->cr1, BIT(cfg->uart_enable_bit));
	} else {
		stm32_clr_bits(port, ofs->cr1, USART_CR1_UESM);
	}
}

static int stm32_serial_suspend(struct device *dev)
{
	struct uart_port *port = dev_get_drvdata(dev);

	uart_suspend_port(&stm32_usart_driver, port);

	if (device_may_wakeup(dev))
		stm32_serial_enable_wakeup(port, true);
	else
		stm32_serial_enable_wakeup(port, false);

	return 0;
}

static int stm32_serial_resume(struct device *dev)
{
	struct uart_port *port = dev_get_drvdata(dev);

	if (device_may_wakeup(dev))
		stm32_serial_enable_wakeup(port, false);

	return uart_resume_port(&stm32_usart_driver, port);
}
#endif /* CONFIG_PM_SLEEP */

static const struct dev_pm_ops stm32_serial_pm_ops = {
	SET_SYSTEM_SLEEP_PM_OPS(stm32_serial_suspend, stm32_serial_resume)
};

static struct platform_driver stm32_serial_driver = {
	.probe		= stm32_serial_probe,
	.remove		= stm32_serial_remove,
	.driver	= {
		.name	= DRIVER_NAME,
		.pm	= &stm32_serial_pm_ops,
		.of_match_table = of_match_ptr(stm32_match),
	},
};
+29 −0
Original line number Diff line number Diff line
@@ -25,6 +25,7 @@ struct stm32_usart_offsets {
struct stm32_usart_config {
	u8 uart_enable_bit; /* USART_CR1_UE */
	bool has_7bits_data;
	bool has_wakeup;
};

struct stm32_usart_info {
@@ -75,6 +76,27 @@ struct stm32_usart_info stm32f7_info = {
	}
};

struct stm32_usart_info stm32h7_info = {
	.ofs = {
		.cr1	= 0x00,
		.cr2	= 0x04,
		.cr3	= 0x08,
		.brr	= 0x0c,
		.gtpr	= 0x10,
		.rtor	= 0x14,
		.rqr	= 0x18,
		.isr	= 0x1c,
		.icr	= 0x20,
		.rdr	= 0x24,
		.tdr	= 0x28,
	},
	.cfg = {
		.uart_enable_bit = 0,
		.has_7bits_data = true,
		.has_wakeup = true,
	}
};

/* USART_SR (F4) / USART_ISR (F7) */
#define USART_SR_PE		BIT(0)
#define USART_SR_FE		BIT(1)
@@ -94,6 +116,7 @@ struct stm32_usart_info stm32f7_info = {
#define USART_SR_BUSY		BIT(16)		/* F7 */
#define USART_SR_CMF		BIT(17)		/* F7 */
#define USART_SR_SBKF		BIT(18)		/* F7 */
#define USART_SR_WUF		BIT(20)		/* H7 */
#define USART_SR_TEACK		BIT(21)		/* F7 */
#define USART_SR_ERR_MASK	(USART_SR_LBD | USART_SR_ORE | \
				 USART_SR_FE | USART_SR_PE)
@@ -114,6 +137,7 @@ struct stm32_usart_info stm32f7_info = {
/* USART_CR1 */
#define USART_CR1_SBK		BIT(0)
#define USART_CR1_RWU		BIT(1)		/* F4 */
#define USART_CR1_UESM		BIT(1)		/* H7 */
#define USART_CR1_RE		BIT(2)
#define USART_CR1_TE		BIT(3)
#define USART_CR1_IDLEIE	BIT(4)
@@ -176,6 +200,9 @@ struct stm32_usart_info stm32f7_info = {
#define USART_CR3_DEM		BIT(14)		/* F7 */
#define USART_CR3_DEP		BIT(15)		/* F7 */
#define USART_CR3_SCARCNT_MASK	GENMASK(19, 17)	/* F7 */
#define USART_CR3_WUS_MASK	GENMASK(21, 20)	/* H7 */
#define USART_CR3_WUS_START_BIT	BIT(21)		/* H7 */
#define USART_CR3_WUFIE		BIT(22)		/* H7 */

/* USART_GTPR */
#define USART_GTPR_PSC_MASK	GENMASK(7, 0)
@@ -204,6 +231,7 @@ struct stm32_usart_info stm32f7_info = {
#define USART_ICR_RTOCF		BIT(11)		/* F7 */
#define USART_ICR_EOBCF		BIT(12)		/* F7 */
#define USART_ICR_CMCF		BIT(17)		/* F7 */
#define USART_ICR_WUCF		BIT(20)		/* H7 */

#define STM32_SERIAL_NAME "ttyS"
#define STM32_MAX_PORTS 8
@@ -225,6 +253,7 @@ struct stm32_port {
	int last_res;
	bool tx_dma_busy;	 /* dma tx busy               */
	bool hw_flow_control;
	int wakeirq;
};

static struct stm32_port stm32_ports[STM32_MAX_PORTS];