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

Commit ada8618f authored by Alexandre TORGUE's avatar Alexandre TORGUE Committed by Greg Kroah-Hartman
Browse files

serial: stm32: adding support for stm32f7



Register offset management rework to support both
stm32f4 (default) and stm32f7. Driver rework to
ensure same functional level on both stm32f4 and
stm32f7: no new feature in this version yet.

Signed-off-by: default avatarGerald Baeza <gerald.baeza@st.com>
Signed-off-by: default avatarAlexandre TORGUE <alexandre.torgue@st.com>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent 27b17ae0
Loading
Loading
Loading
Loading
+219 −50
Original line number Diff line number Diff line
/*
 * Copyright (C) Maxime Coquelin 2015
 * Author:  Maxime Coquelin <mcoquelin.stm32@gmail.com>
 * Authors:  Maxime Coquelin <mcoquelin.stm32@gmail.com>
 *	     Gerald Baeza <gerald.baeza@st.com>
 * License terms:  GNU General Public License (GPL), version 2
 *
 * Inspired by st-asc.c from STMicroelectronics (c)
@@ -29,16 +30,74 @@

#define DRIVER_NAME "stm32-usart"

struct stm32_usart_offsets {
	u8 cr1;
	u8 cr2;
	u8 cr3;
	u8 brr;
	u8 gtpr;
	u8 rtor;
	u8 rqr;
	u8 isr;
	u8 icr;
	u8 rdr;
	u8 tdr;
};

struct stm32_usart_config {
	u8 uart_enable_bit; /* USART_CR1_UE */
	bool has_7bits_data;
};

struct stm32_usart_info {
	struct stm32_usart_offsets ofs;
	struct stm32_usart_config cfg;
};

#define UNDEF_REG ~0

/* Register offsets */
#define USART_SR		0x00
#define USART_DR		0x04
#define USART_BRR		0x08
#define USART_CR1		0x0c
#define USART_CR2		0x10
#define USART_CR3		0x14
#define USART_GTPR		0x18

/* USART_SR */
struct stm32_usart_info stm32f4_info = {
	.ofs = {
		.isr	= 0x00,
		.rdr	= 0x04,
		.tdr	= 0x04,
		.brr	= 0x08,
		.cr1	= 0x0c,
		.cr2	= 0x10,
		.cr3	= 0x14,
		.gtpr	= 0x18,
		.rtor	= UNDEF_REG,
		.rqr	= UNDEF_REG,
		.icr	= UNDEF_REG,
	},
	.cfg = {
		.uart_enable_bit = 13,
		.has_7bits_data = false,
	}
};

struct stm32_usart_info stm32f7_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,
	}
};

/* USART_SR (F4) / USART_ISR (F7) */
#define USART_SR_PE		BIT(0)
#define USART_SR_FE		BIT(1)
#define USART_SR_NF		BIT(2)
@@ -48,7 +107,16 @@
#define USART_SR_TC		BIT(6)
#define USART_SR_TXE		BIT(7)
#define USART_SR_LBD		BIT(8)
#define USART_SR_CTS		BIT(9)
#define USART_SR_CTSIF		BIT(9)
#define USART_SR_CTS		BIT(10)		/* F7 */
#define USART_SR_RTOF		BIT(11)		/* F7 */
#define USART_SR_EOBF		BIT(12)		/* F7 */
#define USART_SR_ABRE		BIT(14)		/* F7 */
#define USART_SR_ABRF		BIT(15)		/* F7 */
#define USART_SR_BUSY		BIT(16)		/* F7 */
#define USART_SR_CMF		BIT(17)		/* F7 */
#define USART_SR_SBKF		BIT(18)		/* F7 */
#define USART_SR_TEACK		BIT(21)		/* F7 */
#define USART_SR_ERR_MASK	(USART_SR_LBD | USART_SR_ORE | \
				 USART_SR_FE | USART_SR_PE)
/* Dummy bits */
@@ -64,7 +132,7 @@

/* USART_CR1 */
#define USART_CR1_SBK		BIT(0)
#define USART_CR1_RWU		BIT(1)
#define USART_CR1_RWU		BIT(1)		/* F4 */
#define USART_CR1_RE		BIT(2)
#define USART_CR1_TE		BIT(3)
#define USART_CR1_IDLEIE	BIT(4)
@@ -76,12 +144,20 @@
#define USART_CR1_PCE		BIT(10)
#define USART_CR1_WAKE		BIT(11)
#define USART_CR1_M		BIT(12)
#define USART_CR1_UE		BIT(13)
#define USART_CR1_M0		BIT(12)		/* F7 */
#define USART_CR1_MME		BIT(13)		/* F7 */
#define USART_CR1_CMIE		BIT(14)		/* F7 */
#define USART_CR1_OVER8		BIT(15)
#define USART_CR1_IE_MASK	GENMASK(8, 4)
#define USART_CR1_DEDT_MASK	GENMASK(20, 16)	/* F7 */
#define USART_CR1_DEAT_MASK	GENMASK(25, 21)	/* F7 */
#define USART_CR1_RTOIE		BIT(26)		/* F7 */
#define USART_CR1_EOBIE		BIT(27)		/* F7 */
#define USART_CR1_M1		BIT(28)		/* F7 */
#define USART_CR1_IE_MASK	(GENMASK(8, 4) | BIT(14) | BIT(26) | BIT(27))

/* USART_CR2 */
#define USART_CR2_ADD_MASK	GENMASK(3, 0)
#define USART_CR2_ADD_MASK	GENMASK(3, 0)	/* F4 */
#define USART_CR2_ADDM7		BIT(4)		/* F7 */
#define USART_CR2_LBDL		BIT(5)
#define USART_CR2_LBDIE		BIT(6)
#define USART_CR2_LBCL		BIT(8)
@@ -91,6 +167,15 @@
#define USART_CR2_STOP_2B	BIT(13)
#define USART_CR2_STOP_MASK	GENMASK(13, 12)
#define USART_CR2_LINEN		BIT(14)
#define USART_CR2_SWAP		BIT(15)		/* F7 */
#define USART_CR2_RXINV		BIT(16)		/* F7 */
#define USART_CR2_TXINV		BIT(17)		/* F7 */
#define USART_CR2_DATAINV	BIT(18)		/* F7 */
#define USART_CR2_MSBFIRST	BIT(19)		/* F7 */
#define USART_CR2_ABREN		BIT(20)		/* F7 */
#define USART_CR2_ABRMOD_MASK	GENMASK(22, 21)	/* F7 */
#define USART_CR2_RTOEN		BIT(23)		/* F7 */
#define USART_CR2_ADD_F7_MASK	GENMASK(31, 24)	/* F7 */

/* USART_CR3 */
#define USART_CR3_EIE		BIT(0)
@@ -105,18 +190,47 @@
#define USART_CR3_CTSE		BIT(9)
#define USART_CR3_CTSIE		BIT(10)
#define USART_CR3_ONEBIT	BIT(11)
#define USART_CR3_OVRDIS	BIT(12)		/* F7 */
#define USART_CR3_DDRE		BIT(13)		/* F7 */
#define USART_CR3_DEM		BIT(14)		/* F7 */
#define USART_CR3_DEP		BIT(15)		/* F7 */
#define USART_CR3_SCARCNT_MASK	GENMASK(19, 17)	/* F7 */

/* USART_GTPR */
#define USART_GTPR_PSC_MASK	GENMASK(7, 0)
#define USART_GTPR_GT_MASK	GENMASK(15, 8)

#define DRIVER_NAME "stm32-usart"
/* USART_RTOR */
#define USART_RTOR_RTO_MASK	GENMASK(23, 0)	/* F7 */
#define USART_RTOR_BLEN_MASK	GENMASK(31, 24)	/* F7 */

/* USART_RQR */
#define USART_RQR_ABRRQ		BIT(0)		/* F7 */
#define USART_RQR_SBKRQ		BIT(1)		/* F7 */
#define USART_RQR_MMRQ		BIT(2)		/* F7 */
#define USART_RQR_RXFRQ		BIT(3)		/* F7 */
#define USART_RQR_TXFRQ		BIT(4)		/* F7 */

/* USART_ICR */
#define USART_ICR_PECF		BIT(0)		/* F7 */
#define USART_ICR_FFECF		BIT(1)		/* F7 */
#define USART_ICR_NCF		BIT(2)		/* F7 */
#define USART_ICR_ORECF		BIT(3)		/* F7 */
#define USART_ICR_IDLECF	BIT(4)		/* F7 */
#define USART_ICR_TCCF		BIT(6)		/* F7 */
#define USART_ICR_LBDCF		BIT(8)		/* F7 */
#define USART_ICR_CTSCF		BIT(9)		/* F7 */
#define USART_ICR_RTOCF		BIT(11)		/* F7 */
#define USART_ICR_EOBCF		BIT(12)		/* F7 */
#define USART_ICR_CMCF		BIT(17)		/* F7 */

#define STM32_SERIAL_NAME "ttyS"
#define STM32_MAX_PORTS 6

struct stm32_port {
	struct uart_port port;
	struct clk *clk;
	struct stm32_usart_info *info;
	bool hw_flow_control;
};

@@ -151,6 +265,8 @@ static void stm32_clr_bits(struct uart_port *port, u32 reg, u32 bits)
static void stm32_receive_chars(struct uart_port *port)
{
	struct tty_port *tport = &port->state->port;
	struct stm32_port *stm32_port = to_stm32_port(port);
	struct stm32_usart_offsets *ofs = &stm32_port->info->ofs;
	unsigned long c;
	u32 sr;
	char flag;
@@ -158,9 +274,9 @@ static void stm32_receive_chars(struct uart_port *port)
	if (port->irq_wake)
		pm_wakeup_event(tport->tty->dev, 0);

	while ((sr = readl_relaxed(port->membase + USART_SR)) & USART_SR_RXNE) {
	while ((sr = readl_relaxed(port->membase + ofs->isr)) & USART_SR_RXNE) {
		sr |= USART_SR_DUMMY_RX;
		c = readl_relaxed(port->membase + USART_DR);
		c = readl_relaxed(port->membase + ofs->rdr);
		flag = TTY_NORMAL;
		port->icount.rx++;

@@ -170,6 +286,10 @@ static void stm32_receive_chars(struct uart_port *port)
				if (uart_handle_break(port))
					continue;
			} else if (sr & USART_SR_ORE) {
				if (ofs->icr != UNDEF_REG)
					writel_relaxed(USART_ICR_ORECF,
						       port->membase +
						       ofs->icr);
				port->icount.overrun++;
			} else if (sr & USART_SR_PE) {
				port->icount.parity++;
@@ -199,10 +319,12 @@ static void stm32_receive_chars(struct uart_port *port)

static void stm32_transmit_chars(struct uart_port *port)
{
	struct stm32_port *stm32_port = to_stm32_port(port);
	struct stm32_usart_offsets *ofs = &stm32_port->info->ofs;
	struct circ_buf *xmit = &port->state->xmit;

	if (port->x_char) {
		writel_relaxed(port->x_char, port->membase + USART_DR);
		writel_relaxed(port->x_char, port->membase + ofs->tdr);
		port->x_char = 0;
		port->icount.tx++;
		return;
@@ -218,7 +340,7 @@ static void stm32_transmit_chars(struct uart_port *port)
		return;
	}

	writel_relaxed(xmit->buf[xmit->tail], port->membase + USART_DR);
	writel_relaxed(xmit->buf[xmit->tail], port->membase + ofs->tdr);
	xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
	port->icount.tx++;

@@ -232,11 +354,13 @@ static void stm32_transmit_chars(struct uart_port *port)
static irqreturn_t stm32_interrupt(int irq, void *ptr)
{
	struct uart_port *port = ptr;
	struct stm32_port *stm32_port = to_stm32_port(port);
	struct stm32_usart_offsets *ofs = &stm32_port->info->ofs;
	u32 sr;

	spin_lock(&port->lock);

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

	if (sr & USART_SR_RXNE)
		stm32_receive_chars(port);
@@ -251,15 +375,21 @@ static irqreturn_t stm32_interrupt(int irq, void *ptr)

static unsigned int stm32_tx_empty(struct uart_port *port)
{
	return readl_relaxed(port->membase + USART_SR) & USART_SR_TXE;
	struct stm32_port *stm32_port = to_stm32_port(port);
	struct stm32_usart_offsets *ofs = &stm32_port->info->ofs;

	return readl_relaxed(port->membase + ofs->isr) & USART_SR_TXE;
}

static void stm32_set_mctrl(struct uart_port *port, unsigned int mctrl)
{
	struct stm32_port *stm32_port = to_stm32_port(port);
	struct stm32_usart_offsets *ofs = &stm32_port->info->ofs;

	if ((mctrl & TIOCM_RTS) && (port->status & UPSTAT_AUTORTS))
		stm32_set_bits(port, USART_CR3, USART_CR3_RTSE);
		stm32_set_bits(port, ofs->cr3, USART_CR3_RTSE);
	else
		stm32_clr_bits(port, USART_CR3, USART_CR3_RTSE);
		stm32_clr_bits(port, ofs->cr3, USART_CR3_RTSE);
}

static unsigned int stm32_get_mctrl(struct uart_port *port)
@@ -271,44 +401,56 @@ static unsigned int stm32_get_mctrl(struct uart_port *port)
/* Transmit stop */
static void stm32_stop_tx(struct uart_port *port)
{
	stm32_clr_bits(port, USART_CR1, USART_CR1_TXEIE);
	struct stm32_port *stm32_port = to_stm32_port(port);
	struct stm32_usart_offsets *ofs = &stm32_port->info->ofs;

	stm32_clr_bits(port, ofs->cr1, USART_CR1_TXEIE);
}

/* There are probably characters waiting to be transmitted. */
static void stm32_start_tx(struct uart_port *port)
{
	struct stm32_port *stm32_port = to_stm32_port(port);
	struct stm32_usart_offsets *ofs = &stm32_port->info->ofs;
	struct circ_buf *xmit = &port->state->xmit;

	if (uart_circ_empty(xmit))
		return;

	stm32_set_bits(port, USART_CR1, USART_CR1_TXEIE | USART_CR1_TE);
	stm32_set_bits(port, ofs->cr1, USART_CR1_TXEIE | USART_CR1_TE);
}

/* Throttle the remote when input buffer is about to overflow. */
static void stm32_throttle(struct uart_port *port)
{
	struct stm32_port *stm32_port = to_stm32_port(port);
	struct stm32_usart_offsets *ofs = &stm32_port->info->ofs;
	unsigned long flags;

	spin_lock_irqsave(&port->lock, flags);
	stm32_clr_bits(port, USART_CR1, USART_CR1_RXNEIE);
	stm32_clr_bits(port, ofs->cr1, USART_CR1_RXNEIE);
	spin_unlock_irqrestore(&port->lock, flags);
}

/* Unthrottle the remote, the input buffer can now accept data. */
static void stm32_unthrottle(struct uart_port *port)
{
	struct stm32_port *stm32_port = to_stm32_port(port);
	struct stm32_usart_offsets *ofs = &stm32_port->info->ofs;
	unsigned long flags;

	spin_lock_irqsave(&port->lock, flags);
	stm32_set_bits(port, USART_CR1, USART_CR1_RXNEIE);
	stm32_set_bits(port, ofs->cr1, USART_CR1_RXNEIE);
	spin_unlock_irqrestore(&port->lock, flags);
}

/* Receive stop */
static void stm32_stop_rx(struct uart_port *port)
{
	stm32_clr_bits(port, USART_CR1, USART_CR1_RXNEIE);
	struct stm32_port *stm32_port = to_stm32_port(port);
	struct stm32_usart_offsets *ofs = &stm32_port->info->ofs;

	stm32_clr_bits(port, ofs->cr1, USART_CR1_RXNEIE);
}

/* Handle breaks - ignored by us */
@@ -318,6 +460,8 @@ static void stm32_break_ctl(struct uart_port *port, int break_state)

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;
	const char *name = to_platform_device(port->dev)->name;
	u32 val;
	int ret;
@@ -327,17 +471,19 @@ static int stm32_startup(struct uart_port *port)
		return ret;

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

	return 0;
}

static void stm32_shutdown(struct uart_port *port)
{
	struct stm32_port *stm32_port = to_stm32_port(port);
	struct stm32_usart_offsets *ofs = &stm32_port->info->ofs;
	u32 val;

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

	free_irq(port->irq, port);
}
@@ -346,6 +492,8 @@ static void stm32_set_termios(struct uart_port *port, struct ktermios *termios,
			    struct ktermios *old)
{
	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;
	unsigned int baud;
	u32 usartdiv, mantissa, fraction, oversampling;
	tcflag_t cflag = termios->c_cflag;
@@ -360,9 +508,10 @@ static void stm32_set_termios(struct uart_port *port, struct ktermios *termios,
	spin_lock_irqsave(&port->lock, flags);

	/* Stop serial port and reset value */
	writel_relaxed(0, port->membase + USART_CR1);
	writel_relaxed(0, port->membase + ofs->cr1);

	cr1 = USART_CR1_TE | USART_CR1_RE | USART_CR1_UE | USART_CR1_RXNEIE;
	cr1 = USART_CR1_TE | USART_CR1_RE | USART_CR1_RXNEIE;
	cr1 |= BIT(cfg->uart_enable_bit);
	cr2 = 0;
	cr3 = 0;

@@ -371,9 +520,13 @@ static void stm32_set_termios(struct uart_port *port, struct ktermios *termios,

	if (cflag & PARENB) {
		cr1 |= USART_CR1_PCE;
		if ((cflag & CSIZE) == CS8)
		if ((cflag & CSIZE) == CS8) {
			if (cfg->has_7bits_data)
				cr1 |= USART_CR1_M0;
			else
				cr1 |= USART_CR1_M;
		}
	}

	if (cflag & PARODD)
		cr1 |= USART_CR1_PS;
@@ -394,15 +547,15 @@ static void stm32_set_termios(struct uart_port *port, struct ktermios *termios,
	 */
	if (usartdiv < 16) {
		oversampling = 8;
		stm32_set_bits(port, USART_CR1, USART_CR1_OVER8);
		stm32_set_bits(port, ofs->cr1, USART_CR1_OVER8);
	} else {
		oversampling = 16;
		stm32_clr_bits(port, USART_CR1, USART_CR1_OVER8);
		stm32_clr_bits(port, ofs->cr1, USART_CR1_OVER8);
	}

	mantissa = (usartdiv / oversampling) << USART_BRR_DIV_M_SHIFT;
	fraction = usartdiv % oversampling;
	writel_relaxed(mantissa | fraction, port->membase + USART_BRR);
	writel_relaxed(mantissa | fraction, port->membase + ofs->brr);

	uart_update_timeout(port, cflag, baud);

@@ -430,9 +583,9 @@ static void stm32_set_termios(struct uart_port *port, struct ktermios *termios,
	if ((termios->c_cflag & CREAD) == 0)
		port->ignore_status_mask |= USART_SR_DUMMY_RX;

	writel_relaxed(cr3, port->membase + USART_CR3);
	writel_relaxed(cr2, port->membase + USART_CR2);
	writel_relaxed(cr1, port->membase + USART_CR1);
	writel_relaxed(cr3, port->membase + ofs->cr3);
	writel_relaxed(cr2, port->membase + ofs->cr2);
	writel_relaxed(cr1, port->membase + ofs->cr1);

	spin_unlock_irqrestore(&port->lock, flags);
}
@@ -469,6 +622,8 @@ static void stm32_pm(struct uart_port *port, unsigned int state,
{
	struct stm32_port *stm32port = container_of(port,
			struct stm32_port, port);
	struct stm32_usart_offsets *ofs = &stm32port->info->ofs;
	struct stm32_usart_config *cfg = &stm32port->info->cfg;
	unsigned long flags = 0;

	switch (state) {
@@ -477,7 +632,7 @@ static void stm32_pm(struct uart_port *port, unsigned int state,
		break;
	case UART_PM_STATE_OFF:
		spin_lock_irqsave(&port->lock, flags);
		stm32_clr_bits(port, USART_CR1, USART_CR1_UE);
		stm32_clr_bits(port, ofs->cr1, BIT(cfg->uart_enable_bit));
		spin_unlock_irqrestore(&port->lock, flags);
		clk_disable_unprepare(stm32port->clk);
		break;
@@ -567,8 +722,10 @@ static struct stm32_port *stm32_of_get_stm32_port(struct platform_device *pdev)

#ifdef CONFIG_OF
static const struct of_device_id stm32_match[] = {
	{ .compatible = "st,stm32-usart", },
	{ .compatible = "st,stm32-uart", },
	{ .compatible = "st,stm32-usart", .data = &stm32f4_info},
	{ .compatible = "st,stm32-uart", .data = &stm32f4_info},
	{ .compatible = "st,stm32f7-usart", .data = &stm32f7_info},
	{ .compatible = "st,stm32f7-uart", .data = &stm32f7_info},
	{},
};

@@ -577,13 +734,20 @@ MODULE_DEVICE_TABLE(of, stm32_match);

static int stm32_serial_probe(struct platform_device *pdev)
{
	int ret;
	const struct of_device_id *match;
	struct stm32_port *stm32port;
	int ret;

	stm32port = stm32_of_get_stm32_port(pdev);
	if (!stm32port)
		return -ENODEV;

	match = of_match_device(stm32_match, &pdev->dev);
	if (match && match->data)
		stm32port->info = (struct stm32_usart_info *)match->data;
	else
		return -EINVAL;

	ret = stm32_init_port(stm32port, pdev);
	if (ret)
		return ret;
@@ -608,15 +772,20 @@ static int stm32_serial_remove(struct platform_device *pdev)
#ifdef CONFIG_SERIAL_STM32_CONSOLE
static void stm32_console_putchar(struct uart_port *port, int ch)
{
	while (!(readl_relaxed(port->membase + USART_SR) & USART_SR_TXE))
	struct stm32_port *stm32_port = to_stm32_port(port);
	struct stm32_usart_offsets *ofs = &stm32_port->info->ofs;

	while (!(readl_relaxed(port->membase + ofs->isr) & USART_SR_TXE))
		cpu_relax();

	writel_relaxed(ch, port->membase + USART_DR);
	writel_relaxed(ch, port->membase + ofs->tdr);
}

static void stm32_console_write(struct console *co, const char *s, unsigned cnt)
{
	struct uart_port *port = &stm32_ports[co->index].port;
	struct stm32_port *stm32_port = to_stm32_port(port);
	struct stm32_usart_offsets *ofs = &stm32_port->info->ofs;
	unsigned long flags;
	u32 old_cr1, new_cr1;
	int locked = 1;
@@ -630,14 +799,14 @@ static void stm32_console_write(struct console *co, const char *s, unsigned cnt)
		spin_lock(&port->lock);

	/* Save and disable interrupts */
	old_cr1 = readl_relaxed(port->membase + USART_CR1);
	old_cr1 = readl_relaxed(port->membase + ofs->cr1);
	new_cr1 = old_cr1 & ~USART_CR1_IE_MASK;
	writel_relaxed(new_cr1, port->membase + USART_CR1);
	writel_relaxed(new_cr1, port->membase + ofs->cr1);

	uart_console_write(port, s, cnt, stm32_console_putchar);

	/* Restore interrupt state */
	writel_relaxed(old_cr1, port->membase + USART_CR1);
	writel_relaxed(old_cr1, port->membase + ofs->cr1);

	if (locked)
		spin_unlock(&port->lock);