diff options
Diffstat (limited to 'drivers/tty/serial/sccnxp.c')
| -rw-r--r-- | drivers/tty/serial/sccnxp.c | 54 | 
1 files changed, 46 insertions, 8 deletions
diff --git a/drivers/tty/serial/sccnxp.c b/drivers/tty/serial/sccnxp.c index 339befdd2f4d..68a24a14f6b7 100644 --- a/drivers/tty/serial/sccnxp.c +++ b/drivers/tty/serial/sccnxp.c @@ -12,6 +12,7 @@  #endif  #include <linux/clk.h> +#include <linux/delay.h>  #include <linux/err.h>  #include <linux/module.h>  #include <linux/mod_devicetable.h> @@ -47,7 +48,6 @@  #	define MR2_STOP1		(7 << 0)  #	define MR2_STOP2		(0xf << 0)  #define SCCNXP_SR_REG			(0x01) -#define SCCNXP_CSR_REG			SCCNXP_SR_REG  #	define SR_RXRDY			(1 << 0)  #	define SR_FULL			(1 << 1)  #	define SR_TXRDY			(1 << 2) @@ -56,6 +56,8 @@  #	define SR_PE			(1 << 5)  #	define SR_FE			(1 << 6)  #	define SR_BRK			(1 << 7) +#define SCCNXP_CSR_REG			(SCCNXP_SR_REG) +#	define CSR_TIMER_MODE		(0x0d)  #define SCCNXP_CR_REG			(0x02)  #	define CR_RX_ENABLE		(1 << 0)  #	define CR_RX_DISABLE		(1 << 1) @@ -82,9 +84,12 @@  #	define IMR_RXRDY		(1 << 1)  #	define ISR_TXRDY(x)		(1 << ((x * 4) + 0))  #	define ISR_RXRDY(x)		(1 << ((x * 4) + 1)) +#define SCCNXP_CTPU_REG			(0x06) +#define SCCNXP_CTPL_REG			(0x07)  #define SCCNXP_IPR_REG			(0x0d)  #define SCCNXP_OPCR_REG			SCCNXP_IPR_REG  #define SCCNXP_SOP_REG			(0x0e) +#define SCCNXP_START_COUNTER_REG	SCCNXP_SOP_REG  #define SCCNXP_ROP_REG			(0x0f)  /* Route helpers */ @@ -103,6 +108,8 @@ struct sccnxp_chip {  	unsigned long		freq_max;  	unsigned int		flags;  	unsigned int		fifosize; +	/* Time between read/write cycles */ +	unsigned int		trwd;  };  struct sccnxp_port { @@ -137,6 +144,7 @@ static const struct sccnxp_chip sc2681 = {  	.freq_max	= 4000000,  	.flags		= SCCNXP_HAVE_IO,  	.fifosize	= 3, +	.trwd		= 200,  };  static const struct sccnxp_chip sc2691 = { @@ -147,6 +155,7 @@ static const struct sccnxp_chip sc2691 = {  	.freq_max	= 4000000,  	.flags		= 0,  	.fifosize	= 3, +	.trwd		= 150,  };  static const struct sccnxp_chip sc2692 = { @@ -157,6 +166,7 @@ static const struct sccnxp_chip sc2692 = {  	.freq_max	= 4000000,  	.flags		= SCCNXP_HAVE_IO,  	.fifosize	= 3, +	.trwd		= 30,  };  static const struct sccnxp_chip sc2891 = { @@ -167,6 +177,7 @@ static const struct sccnxp_chip sc2891 = {  	.freq_max	= 8000000,  	.flags		= SCCNXP_HAVE_IO | SCCNXP_HAVE_MR0,  	.fifosize	= 16, +	.trwd		= 27,  };  static const struct sccnxp_chip sc2892 = { @@ -177,6 +188,7 @@ static const struct sccnxp_chip sc2892 = {  	.freq_max	= 8000000,  	.flags		= SCCNXP_HAVE_IO | SCCNXP_HAVE_MR0,  	.fifosize	= 16, +	.trwd		= 17,  };  static const struct sccnxp_chip sc28202 = { @@ -187,6 +199,7 @@ static const struct sccnxp_chip sc28202 = {  	.freq_max	= 50000000,  	.flags		= SCCNXP_HAVE_IO | SCCNXP_HAVE_MR0,  	.fifosize	= 256, +	.trwd		= 10,  };  static const struct sccnxp_chip sc68681 = { @@ -197,6 +210,7 @@ static const struct sccnxp_chip sc68681 = {  	.freq_max	= 4000000,  	.flags		= SCCNXP_HAVE_IO,  	.fifosize	= 3, +	.trwd		= 200,  };  static const struct sccnxp_chip sc68692 = { @@ -207,24 +221,36 @@ static const struct sccnxp_chip sc68692 = {  	.freq_max	= 4000000,  	.flags		= SCCNXP_HAVE_IO,  	.fifosize	= 3, +	.trwd		= 200,  }; -static inline u8 sccnxp_read(struct uart_port *port, u8 reg) +static u8 sccnxp_read(struct uart_port *port, u8 reg)  { -	return readb(port->membase + (reg << port->regshift)); +	struct sccnxp_port *s = dev_get_drvdata(port->dev); +	u8 ret; + +	ret = readb(port->membase + (reg << port->regshift)); + +	ndelay(s->chip->trwd); + +	return ret;  } -static inline void sccnxp_write(struct uart_port *port, u8 reg, u8 v) +static void sccnxp_write(struct uart_port *port, u8 reg, u8 v)  { +	struct sccnxp_port *s = dev_get_drvdata(port->dev); +  	writeb(v, port->membase + (reg << port->regshift)); + +	ndelay(s->chip->trwd);  } -static inline u8 sccnxp_port_read(struct uart_port *port, u8 reg) +static u8 sccnxp_port_read(struct uart_port *port, u8 reg)  {  	return sccnxp_read(port, (port->line << 3) + reg);  } -static inline void sccnxp_port_write(struct uart_port *port, u8 reg, u8 v) +static void sccnxp_port_write(struct uart_port *port, u8 reg, u8 v)  {  	sccnxp_write(port, (port->line << 3) + reg, v);  } @@ -233,7 +259,7 @@ static int sccnxp_update_best_err(int a, int b, int *besterr)  {  	int err = abs(a - b); -	if ((*besterr < 0) || (*besterr > err)) { +	if (*besterr > err) {  		*besterr = err;  		return 0;  	} @@ -281,10 +307,22 @@ static const struct {  static int sccnxp_set_baud(struct uart_port *port, int baud)  {  	struct sccnxp_port *s = dev_get_drvdata(port->dev); -	int div_std, tmp_baud, bestbaud = baud, besterr = -1; +	int div_std, tmp_baud, bestbaud = INT_MAX, besterr = INT_MAX;  	struct sccnxp_chip *chip = s->chip;  	u8 i, acr = 0, csr = 0, mr0 = 0; +	/* Find divisor to load to the timer preset registers */ +	div_std = DIV_ROUND_CLOSEST(port->uartclk, 2 * 16 * baud); +	if ((div_std >= 2) && (div_std <= 0xffff)) { +		bestbaud = DIV_ROUND_CLOSEST(port->uartclk, 2 * 16 * div_std); +		sccnxp_update_best_err(baud, bestbaud, &besterr); +		csr = CSR_TIMER_MODE; +		sccnxp_port_write(port, SCCNXP_CTPU_REG, div_std >> 8); +		sccnxp_port_write(port, SCCNXP_CTPL_REG, div_std); +		/* Issue start timer/counter command */ +		sccnxp_port_read(port, SCCNXP_START_COUNTER_REG); +	} +  	/* Find best baud from table */  	for (i = 0; baud_std[i].baud && besterr; i++) {  		if (baud_std[i].mr0 && !(chip->flags & SCCNXP_HAVE_MR0))  | 
