Linux MIPS Architecture development
 help / color / mirror / Atom feed
* Re: [PATCH] SC26XX: New serial driver for SC2681 uarts
From: Andrew Morton @ 2007-12-05  3:27 UTC (permalink / raw)
  To: Thomas Bogendoerfer; +Cc: linux-kernel, linux-mips, Andy Whitcroft, Alan Cox
In-Reply-To: <20071204234112.GA12352@alpha.franken.de>

On Wed, 5 Dec 2007 00:41:12 +0100 tsbogend@alpha.franken.de (Thomas Bogendoerfer) wrote:

> On Mon, Dec 03, 2007 at 03:53:17PM -0800, Andrew Morton wrote:
> > On Sun,  2 Dec 2007 20:43:46 +0100 (CET)
> > Thomas Bogendoerfer <tsbogend@alpha.franken.de> wrote:
> > 
> > > New serial driver for SC2681/SC2691 uarts. Older SNI RM400 machines are
> > > using these chips for onboard serial ports.
> > > 
> > 
> > Little things...
> 
> here is an updated version.
> 
> Changes:
>    - use container_of
>    - remove not needed locking
>    - remove inlines
>    - fix macros with double argument reference
> 
> Thomas.
> --
> 
> New serial driver for SC2681/SC2691 uarts. Older SNI RM400 machines are
> using these chips for onboard serial ports.
> 

grumble.

These:

> +#define READ_SC(p, r)        readb((p)->membase + RD_##r)
> +#define WRITE_SC(p, r, v)    writeb((v), (p)->membase + WR_##r)

and these:

> +#define READ_SC_PORT(p, r)     read_sc_port(p, RD_PORT_##r)
> +#define WRITE_SC_PORT(p, r, v) write_sc_port(p, WR_PORT_##r, v)

really don't need to exist.  All they do is make the code harder to read.

Think of the poor reader who sees this:

		status = READ_SC_PORT(port, SR);

and then goes madly searching for "SR".  After a while, our confused reader
might think to go look at the definition of READ_SC_PORT, after which our
reader will emulate a C preprocessor in wetware and will eventually construct
then hunt down RD_PORT_SR and will then hopefully remember what the heck he was
trying to do in the first place.

This sucks.

Code is written once and is read a thousand times.  Please optimise for
reading.

^ permalink raw reply

* Re: [PATCH] SC26XX: New serial driver for SC2681 uarts
From: Andrew Morton @ 2007-12-05  3:27 UTC (permalink / raw)
  To: Thomas Bogendoerfer; +Cc: linux-kernel, linux-mips, Andy Whitcroft, Alan Cox
In-Reply-To: <20071204234112.GA12352@alpha.franken.de>

On Wed, 5 Dec 2007 00:41:12 +0100 tsbogend@alpha.franken.de (Thomas Bogendoerfer) wrote:

> On Mon, Dec 03, 2007 at 03:53:17PM -0800, Andrew Morton wrote:
> > On Sun,  2 Dec 2007 20:43:46 +0100 (CET)
> > Thomas Bogendoerfer <tsbogend@alpha.franken.de> wrote:
> > 
> > > New serial driver for SC2681/SC2691 uarts. Older SNI RM400 machines are
> > > using these chips for onboard serial ports.
> > > 
> > 
> > Little things...
> 
> here is an updated version.
> 
> Changes:
>    - use container_of
>    - remove not needed locking
>    - remove inlines
>    - fix macros with double argument reference
> 
> Thomas.
> --
> 
> New serial driver for SC2681/SC2691 uarts. Older SNI RM400 machines are
> using these chips for onboard serial ports.
> 

grumble.

These:

> +#define READ_SC(p, r)        readb((p)->membase + RD_##r)
> +#define WRITE_SC(p, r, v)    writeb((v), (p)->membase + WR_##r)

and these:

> +#define READ_SC_PORT(p, r)     read_sc_port(p, RD_PORT_##r)
> +#define WRITE_SC_PORT(p, r, v) write_sc_port(p, WR_PORT_##r, v)

really don't need to exist.  All they do is make the code harder to read.

Think of the poor reader who sees this:

		status = READ_SC_PORT(port, SR);

and then goes madly searching for "SR".  After a while, our confused reader
might think to go look at the definition of READ_SC_PORT, after which our
reader will emulate a C preprocessor in wetware and will eventually construct
then hunt down RD_PORT_SR and will then hopefully remember what the heck he was
trying to do in the first place.

This sucks.

Code is written once and is read a thousand times.  Please optimise for
reading.

^ permalink raw reply

* Re: [PATCH] SC26XX: New serial driver for SC2681 uarts
From: Thomas Bogendoerfer @ 2007-12-04 23:57 UTC (permalink / raw)
  To: Alan Cox
  Cc: Arjan van de Ven, Andrew Morton, linux-kernel, linux-mips,
	Andy Whitcroft
In-Reply-To: <20071204124516.0120266a@the-village.bc.nu>

On Tue, Dec 04, 2007 at 12:45:16PM +0000, Alan Cox wrote:
> > I tried to numbers several months ago and didn't get any response 
> 
> Please raise it with the relevant people. If our LANANA administrator is
> not doing their job then the Linuxfoundation need to find a replacement,
> or hand control of it over to someone outside.

I just sent another request for registration. I'll keep you updated.

Thomas.

-- 
Crap can work. Given enough thrust pigs will fly, but it's not necessary a
good idea.                                                [ RFC1925, 2.3 ]

^ permalink raw reply

* Re: [PATCH] SC26XX: New serial driver for SC2681 uarts
From: Thomas Bogendoerfer @ 2007-12-04 23:41 UTC (permalink / raw)
  To: Andrew Morton; +Cc: linux-kernel, linux-mips, Andy Whitcroft, Alan Cox
In-Reply-To: <20071203155317.772231f9.akpm@linux-foundation.org>

On Mon, Dec 03, 2007 at 03:53:17PM -0800, Andrew Morton wrote:
> On Sun,  2 Dec 2007 20:43:46 +0100 (CET)
> Thomas Bogendoerfer <tsbogend@alpha.franken.de> wrote:
> 
> > New serial driver for SC2681/SC2691 uarts. Older SNI RM400 machines are
> > using these chips for onboard serial ports.
> > 
> 
> Little things...

here is an updated version.

Changes:
   - use container_of
   - remove not needed locking
   - remove inlines
   - fix macros with double argument reference

Thomas.
--

New serial driver for SC2681/SC2691 uarts. Older SNI RM400 machines are
using these chips for onboard serial ports.

Signed-off-by: Thomas Bogendoerfer <tsbogend@alpha.franken.de>
---

 drivers/serial/Kconfig  |   15 +
 drivers/serial/Makefile |    1 +
 drivers/serial/sc26xx.c |  755 +++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 771 insertions(+), 0 deletions(-)

diff --git a/drivers/serial/Kconfig b/drivers/serial/Kconfig
index d7e1996..2ea55d0 100644
--- a/drivers/serial/Kconfig
+++ b/drivers/serial/Kconfig
@@ -1284,4 +1284,19 @@ config SERIAL_OF_PLATFORM
 	  Currently, only 8250 compatible ports are supported, but
 	  others can easily be added.
 
+config SERIAL_SC26XX
+	tristate "SC2681/SC2692 serial port support"
+	depends on SNI_RM
+	select SERIAL_CORE
+	help
+	  This is a driver for the onboard serial ports of 
+	  older RM400 machines.
+
+config SERIAL_SC26XX_CONSOLE
+	bool "Console on SC2681/SC2692 serial port"
+	depends on SERIAL_SC26XX
+	select SERIAL_CORE_CONSOLE
+	help
+	  Support for Console on SC2681/SC2692 serial ports.
+
 endmenu
diff --git a/drivers/serial/Makefile b/drivers/serial/Makefile
index af6377d..87d09b6 100644
--- a/drivers/serial/Makefile
+++ b/drivers/serial/Makefile
@@ -64,3 +64,4 @@ obj-$(CONFIG_SERIAL_UARTLITE) += uartlite.o
 obj-$(CONFIG_SERIAL_NETX) += netx-serial.o
 obj-$(CONFIG_SERIAL_OF_PLATFORM) += of_serial.o
 obj-$(CONFIG_SERIAL_KS8695) += serial_ks8695.o
+obj-$(CONFIG_SERIAL_SC26XX) += sc26xx.o
diff --git a/drivers/serial/sc26xx.c b/drivers/serial/sc26xx.c
new file mode 100644
index 0000000..a350b6d
--- /dev/null
+++ b/drivers/serial/sc26xx.c
@@ -0,0 +1,755 @@
+/*
+ * SC268xx.c: Serial driver for Philiphs SC2681/SC2692 devices.
+ *
+ * Copyright (C) 2006,2007 Thomas Bogendörfer (tsbogend@alpha.franken.de)
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+#include <linux/major.h>
+#include <linux/circ_buf.h>
+#include <linux/serial.h>
+#include <linux/sysrq.h>
+#include <linux/console.h>
+#include <linux/spinlock.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/irq.h>
+
+#if defined(CONFIG_MAGIC_SYSRQ)
+#define SUPPORT_SYSRQ
+#endif
+
+#include <linux/serial_core.h>
+
+#define SC26XX_MAJOR         204
+#define SC26XX_MINOR_START   205
+#define SC26XX_NR            2
+
+struct uart_sc26xx_port {
+	struct uart_port      port[2];
+	u8     dsr_mask[2];
+	u8     cts_mask[2];
+	u8     dcd_mask[2];
+	u8     ri_mask[2];
+	u8     dtr_mask[2];
+	u8     rts_mask[2];
+	u8     imr;
+};
+
+/* register common to both ports */
+#define RD_ISR      0x14
+#define RD_IPR      0x34
+
+#define WR_ACR      0x10
+#define WR_IMR      0x14
+#define WR_OPCR     0x34
+#define WR_OPR_SET  0x38
+#define WR_OPR_CLR  0x3C
+
+/* access common register */
+#define READ_SC(p, r)        readb((p)->membase + RD_##r)
+#define WRITE_SC(p, r, v)    writeb((v), (p)->membase + WR_##r)
+
+/* register per port */
+#define RD_PORT_MRx 0x00
+#define RD_PORT_SR  0x04
+#define RD_PORT_RHR 0x0c
+
+#define WR_PORT_MRx 0x00
+#define WR_PORT_CSR 0x04
+#define WR_PORT_CR  0x08
+#define WR_PORT_THR 0x0c
+
+/* SR bits */
+#define SR_BREAK    (1 << 7)
+#define SR_FRAME    (1 << 6)
+#define SR_PARITY   (1 << 5)
+#define SR_OVERRUN  (1 << 4)
+#define SR_TXRDY    (1 << 2)
+#define SR_RXRDY    (1 << 0)
+
+#define CR_RES_MR   (1 << 4)
+#define CR_RES_RX   (2 << 4)
+#define CR_RES_TX   (3 << 4)
+#define CR_STRT_BRK (6 << 4)
+#define CR_STOP_BRK (7 << 4)
+#define CR_DIS_TX   (1 << 3)
+#define CR_ENA_TX   (1 << 2)
+#define CR_DIS_RX   (1 << 1)
+#define CR_ENA_RX   (1 << 0)
+
+/* ISR bits */
+#define ISR_RXRDYB  (1 << 5)
+#define ISR_TXRDYB  (1 << 4)
+#define ISR_RXRDYA  (1 << 1)
+#define ISR_TXRDYA  (1 << 0)
+
+/* IMR bits */
+#define IMR_RXRDY   (1 << 1)
+#define IMR_TXRDY   (1 << 0)
+
+/* access port register */
+static inline u8 read_sc_port(struct uart_port *p, u8 reg)
+{
+	return readb(p->membase + p->line * 0x20 + reg);
+}
+
+static inline void write_sc_port(struct uart_port *p, u8 reg, u8 val)
+{
+	writeb(val, p->membase + p->line * 0x20 + reg);
+}
+
+#define READ_SC_PORT(p, r)     read_sc_port(p, RD_PORT_##r)
+#define WRITE_SC_PORT(p, r, v) write_sc_port(p, WR_PORT_##r, v)
+
+static void sc26xx_enable_irq(struct uart_port *port, int mask)
+{
+	struct uart_sc26xx_port *up;
+	int line = port->line;
+
+	port -= line;
+	up = container_of(port, struct uart_sc26xx_port, port[0]);
+
+	up->imr |= mask << (line * 4);
+	WRITE_SC(port, IMR, up->imr);
+}
+
+static void sc26xx_disable_irq(struct uart_port *port, int mask)
+{
+	struct uart_sc26xx_port *up;
+	int line = port->line;
+
+	port -= line;
+	up = container_of(port, struct uart_sc26xx_port, port[0]);
+
+	up->imr &= ~(mask << (line * 4));
+	WRITE_SC(port, IMR, up->imr);
+}
+
+static struct tty_struct *receive_chars(struct uart_port *port)
+{
+	struct tty_struct *tty = NULL;
+	int limit = 10000;
+	unsigned char ch;
+	char flag;
+	u8 status;
+
+	if (port->info != NULL)		/* Unopened serial console */
+		tty = port->info->tty;
+
+	while (limit-- > 0) {
+		status = READ_SC_PORT(port, SR);
+		if (!(status & SR_RXRDY))
+			break;
+		ch = READ_SC_PORT(port, RHR);
+
+		flag = TTY_NORMAL;
+		port->icount.rx++;
+
+		if (unlikely(status & (SR_BREAK | SR_FRAME |
+				       SR_PARITY | SR_OVERRUN))) {
+			if (status & SR_BREAK) {
+				status &= ~(SR_PARITY | SR_FRAME);
+				port->icount.brk++;
+				if (uart_handle_break(port))
+					continue;
+			} else if (status & SR_PARITY)
+				port->icount.parity++;
+			else if (status & SR_FRAME)
+				port->icount.frame++;
+			if (status & SR_OVERRUN)
+				port->icount.overrun++;
+
+			status &= port->read_status_mask;
+			if (status & SR_BREAK)
+				flag = TTY_BREAK;
+			else if (status & SR_PARITY)
+				flag = TTY_PARITY;
+			else if (status & SR_FRAME)
+				flag = TTY_FRAME;
+		}
+
+		if (uart_handle_sysrq_char(port, ch))
+			continue;
+
+		if (status & port->ignore_status_mask)
+			continue;
+
+		tty_insert_flip_char(tty, ch, flag);
+	}
+	return tty;
+}
+
+static void transmit_chars(struct uart_port *port)
+{
+	struct circ_buf *xmit;
+
+	if (!port->info)
+		return;
+
+	xmit = &port->info->xmit;
+	if (uart_circ_empty(xmit) || uart_tx_stopped(port)) {
+		sc26xx_disable_irq(port, IMR_TXRDY);
+		return;
+	}
+	while (!uart_circ_empty(xmit)) {
+		if (!(READ_SC_PORT(port, SR) & SR_TXRDY))
+			break;
+
+		WRITE_SC_PORT(port, THR, xmit->buf[xmit->tail]);
+		xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
+		port->icount.tx++;
+	}
+	if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+		uart_write_wakeup(port);
+}
+
+static irqreturn_t sc26xx_interrupt(int irq, void *dev_id)
+{
+	struct uart_sc26xx_port *up = dev_id;
+	struct tty_struct *tty;
+	unsigned long flags;
+	u8 isr;
+
+	spin_lock_irqsave(&up->port[0].lock, flags);
+
+	tty = NULL;
+	isr = READ_SC(&up->port[0], ISR);
+	if (isr & ISR_TXRDYA)
+	    transmit_chars(&up->port[0]);
+	if (isr & ISR_RXRDYA)
+	    tty = receive_chars(&up->port[0]);
+
+	spin_unlock(&up->port[0].lock);
+
+	if (tty)
+		tty_flip_buffer_push(tty);
+
+	spin_lock(&up->port[1].lock);
+
+	tty = NULL;
+	if (isr & ISR_TXRDYB)
+	    transmit_chars(&up->port[1]);
+	if (isr & ISR_RXRDYB)
+	    tty = receive_chars(&up->port[1]);
+
+	spin_unlock_irqrestore(&up->port[1].lock, flags);
+
+	if (tty)
+		tty_flip_buffer_push(tty);
+
+	return IRQ_HANDLED;
+}
+
+/* port->lock is not held.  */
+static unsigned int sc26xx_tx_empty(struct uart_port *port)
+{
+	return (READ_SC_PORT(port, SR) & SR_TXRDY) ? TIOCSER_TEMT : 0;
+}
+
+/* port->lock held by caller.  */
+static void sc26xx_set_mctrl(struct uart_port *port, unsigned int mctrl)
+{
+	struct uart_sc26xx_port *up;
+	int line = port->line;
+
+	port -= line;
+	up = container_of(port, struct uart_sc26xx_port, port[0]);
+
+	if (up->dtr_mask[line]) {
+		if (mctrl & TIOCM_DTR)
+			WRITE_SC(port, OPR_SET, up->dtr_mask[line]);
+		else
+			WRITE_SC(port, OPR_CLR, up->dtr_mask[line]);
+	}
+	if (up->rts_mask[line]) {
+		if (mctrl & TIOCM_RTS)
+			WRITE_SC(port, OPR_SET, up->rts_mask[line]);
+		else
+			WRITE_SC(port, OPR_CLR, up->rts_mask[line]);
+	}
+}
+
+/* port->lock is held by caller and interrupts are disabled.  */
+static unsigned int sc26xx_get_mctrl(struct uart_port *port)
+{
+	struct uart_sc26xx_port *up;
+	int line = port->line;
+	unsigned int mctrl = TIOCM_DSR | TIOCM_CTS | TIOCM_CAR;
+	u8 ipr;
+
+	port -= line;
+	up = container_of(port, struct uart_sc26xx_port, port[0]);
+	ipr = READ_SC(port, IPR) ^ 0xff;
+
+	if (up->dsr_mask[line]) {
+		mctrl &= ~TIOCM_DSR;
+		mctrl |= ipr & up->dsr_mask[line] ? TIOCM_DSR : 0;
+	}
+	if (up->cts_mask[line]) {
+		mctrl &= ~TIOCM_CTS;
+		mctrl |= ipr & up->cts_mask[line] ? TIOCM_CTS : 0;
+	}
+	if (up->dcd_mask[line]) {
+		mctrl &= ~TIOCM_CAR;
+		mctrl |= ipr & up->dcd_mask[line] ? TIOCM_CAR : 0;
+	}
+	if (up->ri_mask[line]) {
+		mctrl &= ~TIOCM_RNG;
+		mctrl |= ipr & up->ri_mask[line] ? TIOCM_RNG : 0;
+	}
+	return mctrl;
+}
+
+/* port->lock held by caller.  */
+static void sc26xx_stop_tx(struct uart_port *port)
+{
+	return;
+}
+
+/* port->lock held by caller.  */
+static void sc26xx_start_tx(struct uart_port *port)
+{
+	struct circ_buf *xmit = &port->info->xmit;
+
+	while (!uart_circ_empty(xmit)) {
+		if (!(READ_SC_PORT(port, SR) & SR_TXRDY)) {
+			sc26xx_enable_irq(port, IMR_TXRDY);
+			break;
+		}
+		WRITE_SC_PORT(port, THR, xmit->buf[xmit->tail]);
+		xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
+		port->icount.tx++;
+	}
+}
+
+/* port->lock held by caller.  */
+static void sc26xx_stop_rx(struct uart_port *port)
+{
+}
+
+/* port->lock held by caller.  */
+static void sc26xx_enable_ms(struct uart_port *port)
+{
+}
+
+/* port->lock is not held.  */
+static void sc26xx_break_ctl(struct uart_port *port, int break_state)
+{
+	if (break_state == -1)
+		WRITE_SC_PORT(port, CR, CR_STRT_BRK);
+	else
+		WRITE_SC_PORT(port, CR, CR_STOP_BRK);
+}
+
+/* port->lock is not held.  */
+static int sc26xx_startup(struct uart_port *port)
+{
+	sc26xx_disable_irq(port, IMR_TXRDY | IMR_RXRDY);
+	WRITE_SC(port, OPCR, 0);
+
+	/* reset tx and rx */
+	WRITE_SC_PORT(port, CR, CR_RES_RX);
+	WRITE_SC_PORT(port, CR, CR_RES_TX);
+
+	/* start rx/tx */
+	WRITE_SC_PORT(port, CR, CR_ENA_TX | CR_ENA_RX);
+
+	/* enable irqs */
+	sc26xx_enable_irq(port, IMR_RXRDY);
+	return 0;
+}
+
+/* port->lock is not held.  */
+static void sc26xx_shutdown(struct uart_port *port)
+{
+	/* disable interrupst */
+	sc26xx_disable_irq(port, IMR_TXRDY | IMR_RXRDY);
+
+	/* stop tx/rx */
+	WRITE_SC_PORT(port, CR, CR_DIS_TX | CR_DIS_RX);
+}
+
+/* port->lock is not held.  */
+static void sc26xx_set_termios(struct uart_port *port, struct ktermios *termios,
+			      struct ktermios *old)
+{
+	unsigned int baud = uart_get_baud_rate(port, termios, old, 0, 4000000);
+	unsigned int quot = uart_get_divisor(port, baud);
+	unsigned int iflag, cflag;
+	unsigned long flags;
+	u8 mr1, mr2, csr;
+
+	spin_lock_irqsave(&port->lock, flags);
+
+	while ((READ_SC_PORT(port, SR) & ((1 << 3) | (1 << 2))) != 0xc)
+		udelay(2);
+
+	WRITE_SC_PORT(port, CR, CR_DIS_TX | CR_DIS_RX);
+
+	iflag = termios->c_iflag;
+	cflag = termios->c_cflag;
+
+	port->read_status_mask = SR_OVERRUN;
+	if (iflag & INPCK)
+		port->read_status_mask |= SR_PARITY | SR_FRAME;
+	if (iflag & (BRKINT | PARMRK))
+		port->read_status_mask |= SR_BREAK;
+
+	port->ignore_status_mask = 0;
+	if (iflag & IGNBRK)
+		port->ignore_status_mask |= SR_BREAK;
+	if ((cflag & CREAD) == 0)
+		port->ignore_status_mask |= SR_BREAK | SR_FRAME |
+					    SR_PARITY | SR_OVERRUN;
+
+	switch (cflag & CSIZE) {
+	case CS5:
+		mr1 = 0x00;
+		break;
+	case CS6:
+		mr1 = 0x01;
+		break;
+	case CS7:
+		mr1 = 0x02;
+		break;
+	default:
+	case CS8:
+		mr1 = 0x03;
+		break;
+	}
+	mr2 = 0x07;
+	if (cflag & CSTOPB)
+		mr2 = 0x0f;
+	if (cflag & PARENB) {
+		if (cflag & PARODD)
+			mr1 |= (1 << 2);
+	} else
+		mr1 |= (2 << 3);
+
+	switch (baud) {
+	case 50:
+		csr = 0x00;
+		break;
+	case 110:
+		csr = 0x11;
+		break;
+	case 134:
+		csr = 0x22;
+		break;
+	case 200:
+		csr = 0x33;
+		break;
+	case 300:
+		csr = 0x44;
+		break;
+	case 600:
+		csr = 0x55;
+		break;
+	case 1200:
+		csr = 0x66;
+		break;
+	case 2400:
+		csr = 0x88;
+		break;
+	case 4800:
+		csr = 0x99;
+		break;
+	default:
+	case 9600:
+		csr = 0xbb;
+		break;
+	case 19200:
+		csr = 0xcc;
+		break;
+	}
+
+	WRITE_SC_PORT(port, CR, CR_RES_MR);
+	WRITE_SC_PORT(port, MRx, mr1);
+	WRITE_SC_PORT(port, MRx, mr2);
+
+	WRITE_SC(port, ACR, 0x80);
+	WRITE_SC_PORT(port, CSR, csr);
+
+	/* reset tx and rx */
+	WRITE_SC_PORT(port, CR, CR_RES_RX);
+	WRITE_SC_PORT(port, CR, CR_RES_TX);
+
+	WRITE_SC_PORT(port, CR, CR_ENA_TX | CR_ENA_RX);
+	while ((READ_SC_PORT(port, SR) & ((1 << 3) | (1 << 2))) != 0xc)
+		udelay(2);
+
+	/* XXX */
+	uart_update_timeout(port, cflag,
+			    (port->uartclk / (16 * quot)));
+
+	spin_unlock_irqrestore(&port->lock, flags);
+}
+
+static const char *sc26xx_type(struct uart_port *port)
+{
+	return "SC26XX";
+}
+
+static void sc26xx_release_port(struct uart_port *port)
+{
+}
+
+static int sc26xx_request_port(struct uart_port *port)
+{
+	return 0;
+}
+
+static void sc26xx_config_port(struct uart_port *port, int flags)
+{
+}
+
+static int sc26xx_verify_port(struct uart_port *port, struct serial_struct *ser)
+{
+	return -EINVAL;
+}
+
+static struct uart_ops sc26xx_ops = {
+	.tx_empty	= sc26xx_tx_empty,
+	.set_mctrl	= sc26xx_set_mctrl,
+	.get_mctrl	= sc26xx_get_mctrl,
+	.stop_tx	= sc26xx_stop_tx,
+	.start_tx	= sc26xx_start_tx,
+	.stop_rx	= sc26xx_stop_rx,
+	.enable_ms	= sc26xx_enable_ms,
+	.break_ctl	= sc26xx_break_ctl,
+	.startup	= sc26xx_startup,
+	.shutdown	= sc26xx_shutdown,
+	.set_termios	= sc26xx_set_termios,
+	.type		= sc26xx_type,
+	.release_port	= sc26xx_release_port,
+	.request_port	= sc26xx_request_port,
+	.config_port	= sc26xx_config_port,
+	.verify_port	= sc26xx_verify_port,
+};
+
+static struct uart_port *sc26xx_port;
+
+#ifdef CONFIG_SERIAL_SC26XX_CONSOLE
+static void sc26xx_console_putchar(struct uart_port *port, char c)
+{
+	unsigned long flags;
+	int limit = 1000000;
+
+	spin_lock_irqsave(&port->lock, flags);
+
+	while (limit-- > 0) {
+		if (READ_SC_PORT(port, SR) & SR_TXRDY) {
+			WRITE_SC_PORT(port, THR, c);
+			break;
+		}
+		udelay(2);
+	}
+
+	spin_unlock_irqrestore(&port->lock, flags);
+}
+
+static void sc26xx_console_write(struct console *con, const char *s, unsigned n)
+{
+	struct uart_port *port = sc26xx_port;
+	int i;
+
+	for (i = 0; i < n; i++) {
+		if (*s == '\n')
+			sc26xx_console_putchar(port, '\r');
+		sc26xx_console_putchar(port, *s++);
+	}
+}
+
+static int __init sc26xx_console_setup(struct console *con, char *options)
+{
+	struct uart_port *port = sc26xx_port;
+	int baud = 9600;
+	int bits = 8;
+	int parity = 'n';
+	int flow = 'n';
+
+	if (port->type != PORT_SC26XX)
+		return -1;
+
+	printk(KERN_INFO "Console: ttySC%d (SC26XX)\n", con->index);
+	if (options)
+		uart_parse_options(options, &baud, &parity, &bits, &flow);
+
+	return uart_set_options(port, con, baud, parity, bits, flow);
+}
+
+static struct uart_driver sc26xx_reg;
+static struct console sc26xx_console = {
+	.name	=	"ttySC",
+	.write	=	sc26xx_console_write,
+	.device	=	uart_console_device,
+	.setup  =       sc26xx_console_setup,
+	.flags	=	CON_PRINTBUFFER,
+	.index	=	-1,
+	.data	=	&sc26xx_reg,
+};
+#define SC26XX_CONSOLE   &sc26xx_console
+#else
+#define SC26XX_CONSOLE   NULL
+#endif
+
+static struct uart_driver sc26xx_reg = {
+	.owner			= THIS_MODULE,
+	.driver_name		= "SC26xx",
+	.dev_name		= "ttySC",
+	.major			= SC26XX_MAJOR,
+	.minor			= SC26XX_MINOR_START,
+	.nr			= SC26XX_NR,
+	.cons                   = SC26XX_CONSOLE,
+};
+
+static u8 sc26xx_flags2mask(unsigned int flags, unsigned int bitpos)
+{
+	unsigned int bit = (flags >> bitpos) & 15;
+
+	return bit ? (1 << (bit - 1)) : 0;
+}
+
+static void __devinit sc26xx_init_masks(struct uart_sc26xx_port *up,
+					int line, unsigned int data)
+{
+	up->dtr_mask[line] = sc26xx_flags2mask(data,  0);
+	up->rts_mask[line] = sc26xx_flags2mask(data,  4);
+	up->dsr_mask[line] = sc26xx_flags2mask(data,  8);
+	up->cts_mask[line] = sc26xx_flags2mask(data, 12);
+	up->dcd_mask[line] = sc26xx_flags2mask(data, 16);
+	up->ri_mask[line]  = sc26xx_flags2mask(data, 20);
+}
+
+static int __devinit sc26xx_probe(struct platform_device *dev)
+{
+	struct resource *res;
+	struct uart_sc26xx_port *up;
+	unsigned int *sc26xx_data = dev->dev.platform_data;
+	int err;
+
+	res = platform_get_resource(dev, IORESOURCE_MEM, 0);
+	if (!res)
+		return -ENODEV;
+
+	up = kzalloc(sizeof *up, GFP_KERNEL);
+	if (unlikely(!up))
+		return -ENOMEM;
+
+	up->port[0].line = 0;
+	up->port[0].ops = &sc26xx_ops;
+	up->port[0].type = PORT_SC26XX;
+	up->port[0].uartclk = (29491200 / 16); /* arbitrary */
+
+	up->port[0].mapbase = res->start;
+	up->port[0].membase = ioremap_nocache(up->port[0].mapbase, 0x40);
+	up->port[0].iotype = UPIO_MEM;
+	up->port[0].irq = platform_get_irq(dev, 0);
+
+	up->port[0].dev = &dev->dev;
+
+	sc26xx_init_masks(up, 0, sc26xx_data[0]);
+
+	sc26xx_port = &up->port[0];
+
+	up->port[1].line = 1;
+	up->port[1].ops = &sc26xx_ops;
+	up->port[1].type = PORT_SC26XX;
+	up->port[1].uartclk = (29491200 / 16); /* arbitrary */
+
+	up->port[1].mapbase = up->port[0].mapbase;
+	up->port[1].membase = up->port[0].membase;
+	up->port[1].iotype = UPIO_MEM;
+	up->port[1].irq = up->port[0].irq;
+
+	up->port[1].dev = &dev->dev;
+
+	sc26xx_init_masks(up, 1, sc26xx_data[1]);
+
+	err = uart_register_driver(&sc26xx_reg);
+	if (err)
+		goto out_free_port;
+
+	sc26xx_reg.tty_driver->name_base = sc26xx_reg.minor;
+
+	err = uart_add_one_port(&sc26xx_reg, &up->port[0]);
+	if (err)
+		goto out_unregister_driver;
+
+	err = uart_add_one_port(&sc26xx_reg, &up->port[1]);
+	if (err)
+		goto out_remove_port0;
+
+	err = request_irq(up->port[0].irq, sc26xx_interrupt, 0, "sc26xx", up);
+	if (err)
+		goto out_remove_ports;
+
+	dev_set_drvdata(&dev->dev, up);
+	return 0;
+
+out_remove_ports:
+	uart_remove_one_port(&sc26xx_reg, &up->port[1]);
+out_remove_port0:
+	uart_remove_one_port(&sc26xx_reg, &up->port[0]);
+
+out_unregister_driver:
+	uart_unregister_driver(&sc26xx_reg);
+
+out_free_port:
+	kfree(up);
+	sc26xx_port = NULL;
+	return err;
+}
+
+
+static int __exit sc26xx_driver_remove(struct platform_device *dev)
+{
+	struct uart_sc26xx_port *up = dev_get_drvdata(&dev->dev);
+
+	free_irq(up->port[0].irq, up);
+
+	uart_remove_one_port(&sc26xx_reg, &up->port[0]);
+	uart_remove_one_port(&sc26xx_reg, &up->port[1]);
+
+	uart_unregister_driver(&sc26xx_reg);
+
+	kfree(up);
+	sc26xx_port = NULL;
+
+	dev_set_drvdata(&dev->dev, NULL);
+	return 0;
+}
+
+static struct platform_driver sc26xx_driver = {
+	.probe	= sc26xx_probe,
+	.remove	= __devexit_p(sc26xx_driver_remove),
+	.driver	= {
+		.name	= "SC26xx",
+	},
+};
+
+static int __init sc26xx_init(void)
+{
+	return platform_driver_register(&sc26xx_driver);
+}
+
+static void __exit sc26xx_exit(void)
+{
+	platform_driver_unregister(&sc26xx_driver);
+}
+
+module_init(sc26xx_init);
+module_exit(sc26xx_exit);
+
+
+MODULE_AUTHOR("Thomas Bogendörfer");
+MODULE_DESCRIPTION("SC681/SC2692 serial driver");
+MODULE_VERSION("1.0");
+MODULE_LICENSE("GPL");

^ permalink raw reply related

* Re: [UPDATED PATCH] SGISEEQ: use cached memory access to make driver work on IP28
From: Ralf Baechle @ 2007-12-04 21:05 UTC (permalink / raw)
  To: Jeff Garzik; +Cc: Thomas Bogendoerfer, netdev, linux-mips
In-Reply-To: <4755B536.8070003@pobox.com>

On Tue, Dec 04, 2007 at 03:14:46PM -0500, Jeff Garzik wrote:

>> Changes to last version:
>> - Use inline functions for dma_sync_* instead of macros (suggested by Ralf)
>> - added Kconfig change to make selection for similair SGI boxes easier
>
> Has Ralf ACK'd this updated version?

Acked-by: Ralf Baechle <ralf@linux-mips.org>

> This is for 2.6.25 (i.e. not a bug fix for 2.6.24-rc) I presume?

Yes.  IP28 support it scheduled to be merged for 2.6.25.

  Ralf

^ permalink raw reply

* Re: [UPDATED PATCH] SGISEEQ: use cached memory access to make driver work on IP28
From: Jeff Garzik @ 2007-12-04 20:14 UTC (permalink / raw)
  To: Thomas Bogendoerfer; +Cc: netdev, linux-mips, ralf
In-Reply-To: <20071202103312.75E51C2EB5@solo.franken.de>

Thomas Bogendoerfer wrote:
> SGI IP28 machines would need special treatment (enable adding addtional
> wait states) when accessing memory uncached. To avoid this pain I changed
> the driver to use only cached access to memory.
> 
> Signed-off-by: Thomas Bogendoerfer <tsbogend@alpha.franken.de>
> ---
> 
> Changes to last version:
> - Use inline functions for dma_sync_* instead of macros (suggested by Ralf)
> - added Kconfig change to make selection for similair SGI boxes easier

Has Ralf ACK'd this updated version?

This is for 2.6.25 (i.e. not a bug fix for 2.6.24-rc) I presume?

^ permalink raw reply

* [PATCH] Add support for SB1 hardware watchdog, try#2
From: Andrew Sharp @ 2007-12-04 19:03 UTC (permalink / raw)
  To: linux-mips; +Cc: Ralf Baechle, akpm, wim

This patch is for watchdog timers built into SiByte MIPS SoCs.

Resend with changes from comments.


Signed-off-by: Andy Sharp <andy.sharp@onstor.com>
---
 drivers/watchdog/Kconfig   |   13 ++
 drivers/watchdog/Makefile  |    1 +
 drivers/watchdog/sb_wdog.c |  362 ++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 376 insertions(+), 0 deletions(-)

diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig
index 2792bc1..9057362 100644
--- a/drivers/watchdog/Kconfig
+++ b/drivers/watchdog/Kconfig
@@ -616,6 +616,19 @@ config AR7_WDT
 	help
 	  Hardware driver for the TI AR7 Watchdog Timer.
 
+config SIBYTE_WDOG
+	tristate "Sibyte SoC hardware watchdog"
+	depends on CPU_SB1
+	help
+	  Watchdog driver for the built in watchdog hardware in Sibyte
+	  SoC processors.  There are apparently two watchdog timers
+	  on such processors; this driver supports only the first one,
+	  because currently Linux only supports exporting one watchdog
+	  to userspace.
+
+	  To compile this driver as a loadable module, choose M here.
+	  The module will be called sb_wdog.
+
 # PARISC Architecture
 
 # POWERPC Architecture
diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile
index 7d9e573..eb3822b 100644
--- a/drivers/watchdog/Makefile
+++ b/drivers/watchdog/Makefile
@@ -91,6 +91,7 @@ obj-$(CONFIG_INDYDOG) += indydog.o
 obj-$(CONFIG_WDT_MTX1)	+= mtx-1_wdt.o
 obj-$(CONFIG_WDT_RM9K_GPI) += rm9k_wdt.o
 obj-$(CONFIG_AR7_WDT) += ar7_wdt.o
+obj-$(CONFIG_SIBYTE_WDOG) += sb_wdog.o
 
 # PARISC Architecture
 
diff --git a/drivers/watchdog/sb_wdog.c b/drivers/watchdog/sb_wdog.c
new file mode 100644
index 0000000..d2a0886
--- /dev/null
+++ b/drivers/watchdog/sb_wdog.c
@@ -0,0 +1,362 @@
+/*
+ * Watchdog driver for SiByte SB1 SoCs
+ *
+ * Copyright (C) 2007 OnStor, Inc. * Andrew Sharp <andy.sharp@onstor.com>
+ *
+ * This driver is intended to make the second of two hardware watchdogs
+ * on the Sibyte 12XX and 11XX SoCs available to the user.  There are two
+ * such devices available on the SoC, but it seems that there isn't an
+ * enumeration class for watchdogs in Linux like there is for RTCs.
+ * The second is used rather than the first because it uses IRQ 1,
+ * thereby avoiding all that IRQ 0 problematic nonsense.
+ *
+ * I have not tried this driver on a 1480 processor; it might work
+ * just well enough to really screw things up.
+ *
+ * It is a simple timer, and there is an interrupt that is raised the
+ * first time the timer expires.  The second time it expires, the chip
+ * is reset and there is no way to redirect that NMI.  Which could
+ * be problematic in some cases where this chip is sitting on the HT
+ * bus and has just taken responsibility for providing a cache block.
+ * Since the reset can't be redirected to the external reset pin, it is
+ * possible that other HT connected processors might hang and not reset.
+ * For Linux, a soft reset would probably be even worse than a hard reset.
+ * There you have it.
+ *
+ * The timer takes 23 bits of a 64 bit register (?) as a count value,
+ * and decrements the count every microsecond, for a max value of
+ * 0x7fffff usec or about 8.3ish seconds.
+ *
+ * This watchdog borrows some user semantics from the softdog driver,
+ * in that if you close the fd, it leaves the watchdog running, unless
+ * you previously wrote a 'V' to the fd, in which case it disables
+ * the watchdog when you close the fd like some other drivers.
+ *
+ * Based on various other watchdog drivers, which are probably all
+ * loosely based on something Alan Cox wrote years ago.
+ *
+ *	(c) Copyright 1996 Alan Cox <alan@redhat.com>, All Rights Reserved.
+ *				http://www.redhat.com
+ *
+ *	This program is free software; you can redistribute it and/or
+ *	modify it under the terms of the GNU General Public License
+ *	version 1 or 2 as published by the Free Software Foundation.
+ *
+ */
+#include <linux/module.h>
+#include <linux/io.h>
+#include <linux/uaccess.h>
+#include <linux/fs.h>
+#include <linux/reboot.h>
+#include <linux/miscdevice.h>
+#include <linux/watchdog.h>
+#include <linux/interrupt.h>
+
+#include <asm/sibyte/sb1250.h>
+#include <asm/sibyte/sb1250_regs.h>
+#include <asm/sibyte/sb1250_int.h>
+#include <asm/sibyte/sb1250_scd.h>
+
+
+/*
+ * set the initial count value of a timer
+ *
+ * wdog is the iomem address of the cfg register
+ */
+ void
+sbwdog_set(char __iomem *wdog, unsigned long t)
+{
+	__raw_writeb(0, wdog - 0x10);
+	__raw_writeq(t & 0x7fffffUL, wdog);
+}
+
+/*
+ * cause the timer to [re]load it's initial count and start counting
+ * all over again
+ *
+ * wdog is the iomem address of the cfg register
+ */
+ void
+sbwdog_pet(char __iomem *wdog)
+{
+	__raw_writeb(__raw_readb(wdog) | 1, wdog);
+}
+
+static unsigned long sbwdog_gate; /* keeps it to one thread only */
+static char __iomem *kern_dog = (char __iomem *)(IO_BASE + (A_SCD_WDOG_CFG_0));
+static char __iomem *user_dog = (char __iomem *)(IO_BASE + (A_SCD_WDOG_CFG_1));
+static unsigned long timeout = 0x7fffffUL;	/* useconds: 8.3ish secs. */
+static int expect_close;
+
+static struct watchdog_info ident = {
+	.options	= WDIOF_CARDRESET | WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING,
+	.identity	= "SiByte Watchdog",
+};
+
+/*
+ * Allow only a single thread to walk the dog
+ */
+ static int
+sbwdog_open(struct inode *inode, struct file *file)
+{
+	nonseekable_open(inode, file);
+	if (test_and_set_bit(0, &sbwdog_gate)) {
+		return -EBUSY;
+	}
+	__module_get(THIS_MODULE);
+
+	/*
+	 * Activate the timer
+	 */
+	sbwdog_set(user_dog, timeout);
+	__raw_writeb(1, user_dog);
+
+	return 0;
+}
+
+/*
+ * Put the dog back in the kennel.
+ */
+ static int
+sbwdog_release(struct inode *inode, struct file *file)
+{
+	if (expect_close == 42) {
+		__raw_writeb(0, user_dog);
+		module_put(THIS_MODULE);
+	} else {
+		printk(KERN_CRIT "%s: Unexpected close, not stopping watchdog!\n",
+			ident.identity);
+		sbwdog_pet(user_dog);
+	}
+	clear_bit(0, &sbwdog_gate);
+	expect_close = 0;
+
+	return 0;
+}
+
+/*
+ * 42 - the answer
+ */
+ static ssize_t
+sbwdog_write(struct file *file, const char __user *data, size_t len,
+	loff_t *ppos)
+{
+	int i;
+
+	if (len) {
+		/*
+		 * restart the timer
+		 */
+		expect_close = 0;
+
+		for (i = 0; i != len; i++) {
+			char c;
+
+			if (get_user(c, data + i)) {
+				return -EFAULT;
+			}
+			if (c == 'V') {
+				expect_close = 42;
+			}
+		}
+		sbwdog_pet(user_dog);
+	}
+
+	return len;
+}
+
+ static int
+sbwdog_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
+	unsigned long arg)
+{
+	int ret = -ENOTTY;
+	unsigned long time;
+	void __user *argp = (void __user *)arg;
+	int __user *p = argp;
+
+	switch (cmd) {
+	case WDIOC_GETSUPPORT:
+		ret = copy_to_user(argp, &ident, sizeof(ident)) ? -EFAULT : 0;
+		break;
+
+	case WDIOC_GETSTATUS:
+	case WDIOC_GETBOOTSTATUS:
+		ret = put_user(0, p);
+		break;
+
+	case WDIOC_SETTIMEOUT:
+		ret = get_user(time, p);
+		if (ret) {
+			break;
+		}
+
+		time *= 1000000;
+		if (time > 0x7fffffUL) {
+			ret = -EINVAL;
+			break;
+		}
+		timeout = time;
+		sbwdog_set(user_dog, timeout);
+		sbwdog_pet(user_dog);
+
+	case WDIOC_GETTIMEOUT:
+		/*
+		 * get the remaining count from the ... count register
+		 * which is 1*8 before the config register
+		 */
+		ret = put_user(__raw_readq(user_dog - 8) / 1000000, p);
+		break;
+
+	case WDIOC_KEEPALIVE:
+		sbwdog_pet(user_dog);
+		ret = 0;
+		break;
+	}
+	return ret;
+}
+
+/*
+ *	Notifier for system down
+ */
+ static int
+sbwdog_notify_sys(struct notifier_block *this, unsigned long code, void *erf)
+{
+	if (code == SYS_DOWN || code == SYS_HALT) {
+		/*
+		 * sit and sit
+		 */
+		__raw_writeb(0, user_dog);
+		__raw_writeb(0, kern_dog);
+	}
+
+	return NOTIFY_DONE;
+}
+
+static const struct file_operations sbwdog_fops =
+{
+	.owner		= THIS_MODULE,
+	.llseek		= no_llseek,
+	.write		= sbwdog_write,
+	.ioctl		= sbwdog_ioctl,
+	.open		= sbwdog_open,
+	.release	= sbwdog_release,
+};
+
+static struct miscdevice sbwdog_miscdev =
+{
+	.minor		= WATCHDOG_MINOR,
+	.name		= "watchdog",
+	.fops		= &sbwdog_fops,
+};
+
+static struct notifier_block sbwdog_notifier = {
+	.notifier_call	= sbwdog_notify_sys,
+};
+
+/*
+ * interrupt handler
+ *
+ * doesn't do a whole lot for user, but oh so cleverly written so kernel
+ * code can use it to re-up the watchdog, thereby saving the kernel from
+ * having to create and maintain a timer, just to tickle another timer,
+ * which is just so wrong.
+ */
+ irqreturn_t
+sbwdog_interrupt(int irq, void *addr)
+{
+	unsigned long wd_init;
+	char *wd_cfg_reg = (char *)addr;
+	u8 cfg;
+
+	cfg = __raw_readb(wd_cfg_reg);
+	wd_init = __raw_readq(wd_cfg_reg - 8) & 0x7fffff;
+
+	/*
+	 * if it's the second watchdog timer, it's for those users
+	 */
+	if (wd_cfg_reg == user_dog) {
+		printk(KERN_CRIT
+			"%s in danger of initiating system reset in %ld.%01ld seconds\n",
+			ident.identity, wd_init / 1000000, (wd_init / 100000) % 10);
+	} else {
+		cfg |= 1;
+	}
+
+	__raw_writeb(cfg, wd_cfg_reg);
+
+	return IRQ_HANDLED;
+}
+
+ static int __init
+sbwdog_init(void)
+{
+	int ret;
+
+	/*
+	 * register a reboot notifier
+	 */
+	ret = register_reboot_notifier(&sbwdog_notifier);
+	if (ret) {
+		printk (KERN_ERR "%s: cannot register reboot notifier (err=%d)\n",
+			ident.identity, ret);
+		return ret;
+	}
+
+	/*
+	 * get the resources
+	 */
+	ret = misc_register(&sbwdog_miscdev);
+	if (ret == 0) {
+		printk(KERN_INFO "%s: timeout is %ld.%ld secs\n", ident.identity,
+			timeout / 1000000, (timeout / 100000) % 10);
+	}
+
+	ret = request_irq(1, sbwdog_interrupt, IRQF_DISABLED | IRQF_SHARED,
+		ident.identity, (void *)user_dog);
+	if (ret) {
+		printk(KERN_ERR "%s: failed to request irq 1 - %d\n", ident.identity,
+			ret);
+		misc_deregister(&sbwdog_miscdev);
+	}
+
+	return ret;
+}
+
+ static void __exit
+sbwdog_exit(void)
+{
+	misc_deregister(&sbwdog_miscdev);
+}
+
+module_init(sbwdog_init);
+module_exit(sbwdog_exit);
+
+MODULE_AUTHOR("Andrew Sharp <andy.sharp@onstor.com>");
+MODULE_DESCRIPTION("SiByte Watchdog");
+
+module_param(timeout, ulong, 0);
+MODULE_PARM_DESC(timeout,
+	"Watchdog timeout in microseconds (max/default 8388607 or 8.3ish secs)");
+
+MODULE_LICENSE("GPL");
+MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
+
+/*
+ * example code that can be put in a platform code area to utilize the
+ * first watchdog timer for the kernels own purpose.
+
+ void
+platform_wd_setup(void)
+{
+	int ret;
+
+	ret = request_irq(0, sbwdog_interrupt, IRQF_DISABLED | IRQF_SHARED,
+		"Kernel Watchdog", IOADDR(A_SCD_WDOG_CFG_0));
+	if (ret) {
+		printk(KERN_CRIT "Watchdog IRQ zero(0) failed to be requested - %d\n",
+			ret);
+	}
+}
+
+
+ */

^ permalink raw reply related

* Re: [PATCH] SC26XX: New serial driver for SC2681 uarts
From: Alan Cox @ 2007-12-04 12:45 UTC (permalink / raw)
  To: Thomas Bogendoerfer
  Cc: Arjan van de Ven, Andrew Morton, linux-kernel, linux-mips,
	Andy Whitcroft
In-Reply-To: <20071204082534.GA5938@alpha.franken.de>

On Tue, 4 Dec 2007 09:25:35 +0100
tsbogend@alpha.franken.de (Thomas Bogendoerfer) wrote:

> On Mon, Dec 03, 2007 at 03:57:46PM -0800, Arjan van de Ven wrote:
> > > > +#define SC26XX_MAJOR         204
> > > > +#define SC26XX_MINOR_START   205
> > > > +#define SC26XX_NR            2
> > 
> > did lanana assign these numbers officially?
> 
> I tried to numbers several months ago and didn't get any response 

Please raise it with the relevant people. If our LANANA administrator is
not doing their job then the Linuxfoundation need to find a replacement,
or hand control of it over to someone outside.

^ permalink raw reply

* Re: [PATCH] SC26XX: New serial driver for SC2681 uarts
From: Alan Cox @ 2007-12-04 12:45 UTC (permalink / raw)
  To: Thomas Bogendoerfer
  Cc: Arjan van de Ven, Andrew Morton, linux-kernel, linux-mips,
	Andy Whitcroft
In-Reply-To: <20071204082534.GA5938@alpha.franken.de>

On Tue, 4 Dec 2007 09:25:35 +0100
tsbogend@alpha.franken.de (Thomas Bogendoerfer) wrote:

> On Mon, Dec 03, 2007 at 03:57:46PM -0800, Arjan van de Ven wrote:
> > > > +#define SC26XX_MAJOR         204
> > > > +#define SC26XX_MINOR_START   205
> > > > +#define SC26XX_NR            2
> > 
> > did lanana assign these numbers officially?
> 
> I tried to numbers several months ago and didn't get any response 

Please raise it with the relevant people. If our LANANA administrator is
not doing their job then the Linuxfoundation need to find a replacement,
or hand control of it over to someone outside.

^ permalink raw reply

* Re: [PATCH] SC26XX: New serial driver for SC2681 uarts
From: Geert Uytterhoeven @ 2007-12-04 11:01 UTC (permalink / raw)
  To: Thomas Bogendoerfer
  Cc: Arjan van de Ven, Andrew Morton, linux-kernel, linux-mips,
	Andy Whitcroft, Alan Cox
In-Reply-To: <20071204082534.GA5938@alpha.franken.de>

On Tue, 4 Dec 2007, Thomas Bogendoerfer wrote:
> On Mon, Dec 03, 2007 at 03:57:46PM -0800, Arjan van de Ven wrote:
> > > > +#define SC26XX_MAJOR         204
> > > > +#define SC26XX_MINOR_START   205
> > > > +#define SC26XX_NR            2
> > 
> > did lanana assign these numbers officially?
> 
> I tried to numbers several months ago and didn't get any response :-(

What about a dynamic number?

Gr{oetje,eeting}s,

						Geert

--
Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert@linux-m68k.org

In personal conversations with technical people, I call myself a hacker. But
when I'm talking to journalists I just say "programmer" or something like that.
							    -- Linus Torvalds

^ permalink raw reply

* Re: [PATCH] SC26XX: New serial driver for SC2681 uarts
From: Thomas Bogendoerfer @ 2007-12-04  8:25 UTC (permalink / raw)
  To: Arjan van de Ven
  Cc: Andrew Morton, linux-kernel, linux-mips, Andy Whitcroft, Alan Cox
In-Reply-To: <20071203155746.2dc4506d@laptopd505.fenrus.org>

On Mon, Dec 03, 2007 at 03:57:46PM -0800, Arjan van de Ven wrote:
> > > +#define SC26XX_MAJOR         204
> > > +#define SC26XX_MINOR_START   205
> > > +#define SC26XX_NR            2
> 
> did lanana assign these numbers officially?

I tried to numbers several months ago and didn't get any response :-(

Thomas.

-- 
Crap can work. Given enough thrust pigs will fly, but it's not necessary a
good idea.                                                [ RFC1925, 2.3 ]

^ permalink raw reply

* Re: smp8634 add memory at dram1
From: David Kuk @ 2007-12-04  2:22 UTC (permalink / raw)
  To: YH Lin; +Cc: linux-mips
In-Reply-To: <5DF100B598199744B111FCEA5222E78A01CB9F5F@sigma-exch1.sdesigns.com>

[-- Attachment #1: Type: text/plain, Size: 1047 bytes --]

Dear YH

I am sorry i have been disturbed by other issues, the memory problem seems
little bit difficult to me. In our case, i saw that in prom.c under
mips/tango2, the function prom_init , it shows that :
memcfg_t *m=(memcfg_t*)KSEG1ADDR(MEM_BASE_dram_controller_0+FM_MEMCFG);

it's seems the kernel has hard coded to point the starting memory to DRAM
controller 0's starting address, if now, i have remap 64mb memory at DRAM
controller 1 at remap register 4, The problem is come, the kernel will
ignore any memory before dram controller 0's starting address. Even i have
add 0x0c000000--0x10000000 as boot memory, it still think it's first usable
pfn is at 0x10000000.

my solution is 3 steps

1, modify the compiler, let the linux start address moved to 0x0c000000,
2. modify the YAMON, and map the DRAM 1 controller to remap register4 in
YAMON stage
3, modify the linux, to set the two piece of memory to the kernel as boot
memory .

it's this possible, or it have other better solution ?

thx a lot for your kindly help !


best wishes
David

[-- Attachment #2: Type: text/html, Size: 1140 bytes --]

^ permalink raw reply

* Re: [PATCH] SC26XX: New serial driver for SC2681 uarts
From: Arjan van de Ven @ 2007-12-03 23:57 UTC (permalink / raw)
  To: Andrew Morton
  Cc: Thomas Bogendoerfer, linux-kernel, linux-mips, Andy Whitcroft,
	Alan Cox
In-Reply-To: <20071203155317.772231f9.akpm@linux-foundation.org>

On Mon, 3 Dec 2007 15:53:17 -0800
Andrew Morton <akpm@linux-foundation.org> wrote:

> On Sun,  2 Dec 2007 20:43:46 +0100 (CET)
> Thomas Bogendoerfer <tsbogend@alpha.franken.de> wrote:
> 
> > New serial driver for SC2681/SC2691 uarts. Older SNI RM400 machines
> > are using these chips for onboard serial ports.
> > 
> 
> Little things...
> 
> > --- /dev/null
> > +++ b/drivers/serial/sc26xx.c
> > @@ -0,0 +1,757 @@
> > +/*
> > + * SC268xx.c: Serial driver for Philiphs SC2681/SC2692 devices.
> > + *
> > + * Copyright (C) 2006,2007 Thomas Bogend__rfer
> > (tsbogend@alpha.franken.de)
> > + */
> > +
> > +#include <linux/module.h>
> > +#include <linux/kernel.h>
> > +#include <linux/errno.h>
> > +#include <linux/tty.h>
> > +#include <linux/tty_flip.h>
> > +#include <linux/major.h>
> > +#include <linux/circ_buf.h>
> > +#include <linux/serial.h>
> > +#include <linux/sysrq.h>
> > +#include <linux/console.h>
> > +#include <linux/spinlock.h>
> > +#include <linux/slab.h>
> > +#include <linux/delay.h>
> > +#include <linux/init.h>
> > +#include <linux/platform_device.h>
> > +#include <linux/irq.h>
> > +
> > +#if defined(CONFIG_MAGIC_SYSRQ)
> > +#define SUPPORT_SYSRQ
> > +#endif
> > +
> > +#include <linux/serial_core.h>
> > +
> > +#define SC26XX_MAJOR         204
> > +#define SC26XX_MINOR_START   205
> > +#define SC26XX_NR            2

did lanana assign these numbers officially?

^ permalink raw reply

* Re: [PATCH] SC26XX: New serial driver for SC2681 uarts
From: Andrew Morton @ 2007-12-03 23:53 UTC (permalink / raw)
  To: Thomas Bogendoerfer; +Cc: linux-kernel, linux-mips, Andy Whitcroft, Alan Cox
In-Reply-To: <20071202194346.36E3FDE4C4@solo.franken.de>

On Sun,  2 Dec 2007 20:43:46 +0100 (CET)
Thomas Bogendoerfer <tsbogend@alpha.franken.de> wrote:

> New serial driver for SC2681/SC2691 uarts. Older SNI RM400 machines are
> using these chips for onboard serial ports.
> 

Little things...

> --- /dev/null
> +++ b/drivers/serial/sc26xx.c
> @@ -0,0 +1,757 @@
> +/*
> + * SC268xx.c: Serial driver for Philiphs SC2681/SC2692 devices.
> + *
> + * Copyright (C) 2006,2007 Thomas Bogend__rfer (tsbogend@alpha.franken.de)
> + */
> +
> +#include <linux/module.h>
> +#include <linux/kernel.h>
> +#include <linux/errno.h>
> +#include <linux/tty.h>
> +#include <linux/tty_flip.h>
> +#include <linux/major.h>
> +#include <linux/circ_buf.h>
> +#include <linux/serial.h>
> +#include <linux/sysrq.h>
> +#include <linux/console.h>
> +#include <linux/spinlock.h>
> +#include <linux/slab.h>
> +#include <linux/delay.h>
> +#include <linux/init.h>
> +#include <linux/platform_device.h>
> +#include <linux/irq.h>
> +
> +#if defined(CONFIG_MAGIC_SYSRQ)
> +#define SUPPORT_SYSRQ
> +#endif
> +
> +#include <linux/serial_core.h>
> +
> +#define SC26XX_MAJOR         204
> +#define SC26XX_MINOR_START   205
> +#define SC26XX_NR            2
> +
> +struct uart_sc26xx_port {
> +	struct uart_port      port[2];
> +	u8     dsr_mask[2];
> +	u8     cts_mask[2];
> +	u8     dcd_mask[2];
> +	u8     ri_mask[2];
> +	u8     dtr_mask[2];
> +	u8     rts_mask[2];
> +	u8     imr;
> +};
> +
> +/* register common to both ports */
> +#define RD_ISR      0x14
> +#define RD_IPR      0x34
> +
> +#define WR_ACR      0x10
> +#define WR_IMR      0x14
> +#define WR_OPCR     0x34
> +#define WR_OPR_SET  0x38
> +#define WR_OPR_CLR  0x3C
> +
> +/* access common register */
> +#define READ_SC(p, r)        readb ((p)->membase + RD_##r)
> +#define WRITE_SC(p, r, v)    writeb ((v), (p)->membase + WR_##r)

No space before the (.  checkpatch misses this.

> +/* register per port */
> +#define RD_PORT_MRx 0x00
> +#define RD_PORT_SR  0x04
> +#define RD_PORT_RHR 0x0c
> +
> +#define WR_PORT_MRx 0x00
> +#define WR_PORT_CSR 0x04
> +#define WR_PORT_CR  0x08
> +#define WR_PORT_THR 0x0c
> +
> +/* access port register */
> +#define READ_SC_PORT(p, r)     \
> +		readb((p)->membase + (p)->line * 0x20 + RD_PORT_##r)
> +#define WRITE_SC_PORT(p, r, v) \
> +		writeb((v), (p)->membase + (p)->line * 0x20 + WR_PORT_##r)

eww, ugly.  Why not have a nice C function which is passed RD_PORT_SR,
RD_PORT_RHR, etc?

That has the (minor) advantage that it won't malfunction if someone does

	WRITE_SC_PORT(foo++, r, v);

(the macro references an arg more than once)

> +/* SR bits */
> +#define SR_BREAK    (1 << 7)
> +#define SR_FRAME    (1 << 6)
> +#define SR_PARITY   (1 << 5)
> +#define SR_OVERRUN  (1 << 4)
> +#define SR_TXRDY    (1 << 2)
> +#define SR_RXRDY    (1 << 0)
> +
> +#define CR_RES_MR   (1 << 4)
> +#define CR_RES_RX   (2 << 4)
> +#define CR_RES_TX   (3 << 4)
> +#define CR_STRT_BRK (6 << 4)
> +#define CR_STOP_BRK (7 << 4)
> +#define CR_DIS_TX   (1 << 3)
> +#define CR_ENA_TX   (1 << 2)
> +#define CR_DIS_RX   (1 << 1)
> +#define CR_ENA_RX   (1 << 0)
> +
> +/* ISR bits */
> +#define ISR_RXRDYB  (1 << 5)
> +#define ISR_TXRDYB  (1 << 4)
> +#define ISR_RXRDYA  (1 << 1)
> +#define ISR_TXRDYA  (1 << 0)
> +
> +/* IMR bits */
> +#define IMR_RXRDY   (1 << 1)
> +#define IMR_TXRDY   (1 << 0)
> +
> +static void sc26xx_enable_irq(struct uart_port *port, int mask)
> +{
> +	struct uart_sc26xx_port *up;
> +	int line = port->line;
> +
> +	port -= line;
> +	up = (struct uart_sc26xx_port *)port;

Yeah, lots of serial drivers do this and it's old-fashioned.  It would be
clearer to use container_of() rather than the open-coded typecast.  That
way, the uart_port doesn't need to be the first member of uart_sc26xx_port,
too.


> +	up->imr |= mask << (line * 4);
> +	WRITE_SC(port, IMR, up->imr);
> +}
>
> ...
>
> +
> +/* port->lock is not held.  */
> +static unsigned int sc26xx_tx_empty(struct uart_port *port)
> +{
> +	unsigned long flags;
> +	unsigned int ret;
> +
> +	spin_lock_irqsave(&port->lock, flags);
> +	ret = (READ_SC_PORT(port, SR) & SR_TXRDY) ? TIOCSER_TEMT : 0;
> +	spin_unlock_irqrestore(&port->lock, flags);
> +	return ret;
> +}

I suspect the locking here doesn't actually do anything?

> +/* port->lock is not held.  */
> +static void sc26xx_set_termios(struct uart_port *port, struct ktermios *termios,
> +			      struct ktermios *old)

hm, termios stuff.  I'll cc Alan on the commit...

> +static struct uart_port *sc26xx_port;
> +
> +#ifdef CONFIG_SERIAL_SC26XX_CONSOLE
> +static inline void sc26xx_console_putchar(struct uart_port *port, char c)
> +{
> +	unsigned long flags;
> +	int limit = 1000000;
> +
> +	spin_lock_irqsave(&port->lock, flags);
> +
> +	while (limit-- > 0) {
> +		if (READ_SC_PORT(port, SR) & SR_TXRDY) {
> +			WRITE_SC_PORT(port, THR, c);
> +			break;
> +		}
> +		udelay(2);
> +	}
> +
> +	spin_unlock_irqrestore(&port->lock, flags);
> +}

This is far too large to be inlined.

> +static void sc26xx_console_write(struct console *con, const char *s, unsigned n)
> +{
> +	struct uart_port *port = sc26xx_port;
> +	int i;
> +
> +	for (i = 0; i < n; i++) {
> +		if (*s == '\n')
> +			sc26xx_console_putchar(port, '\r');
> +		sc26xx_console_putchar(port, *s++);
> +	}
> +}
> +
> +static int __init sc26xx_console_setup(struct console *con, char *options)
> +{
> +	struct uart_port *port = sc26xx_port;
> +	int baud = 9600;
> +	int bits = 8;
> +	int parity = 'n';
> +	int flow = 'n';
> +
> +	if (port->type != PORT_SC26XX)
> +		return -1;
> +
> +	printk(KERN_INFO "Console: ttySC%d (SC26XX)\n", con->index);
> +	if (options)
> +		uart_parse_options(options, &baud, &parity, &bits, &flow);
> +
> +	return uart_set_options(port, con, baud, parity, bits, flow);
> +}
> +
> +static struct uart_driver sc26xx_reg;
> +static struct console sc26xx_console = {
> +	.name	=	"ttySC",
> +	.write	=	sc26xx_console_write,
> +	.device	=	uart_console_device,
> +	.setup  =       sc26xx_console_setup,
> +	.flags	=	CON_PRINTBUFFER,
> +	.index	=	-1,
> +	.data	=	&sc26xx_reg,
> +};
> +#define SC26XX_CONSOLE   &sc26xx_console
> +#else
> +#define SC26XX_CONSOLE   NULL
> +#endif
> +
> +static struct uart_driver sc26xx_reg = {
> +	.owner			= THIS_MODULE,
> +	.driver_name		= "SC26xx",
> +	.dev_name		= "ttySC",
> +	.major			= SC26XX_MAJOR,
> +	.minor			= SC26XX_MINOR_START,
> +	.nr			= SC26XX_NR,
> +	.cons                   = SC26XX_CONSOLE,
> +};
> +
> +static inline u8 sc26xx_flags2mask(unsigned int flags, unsigned int bitpos)
> +{
> +	unsigned int bit = (flags >> bitpos) & 15;
> +
> +	return bit ? (1 << (bit - 1)) : 0;
> +}

This might be too big as well.  It's less of an issue for __devinit text
though.

^ permalink raw reply

* Re: [PATCH] Add support for SB1 hardware watchdog.
From: Ralf Baechle @ 2007-12-03 23:08 UTC (permalink / raw)
  To: Andrew Sharp; +Cc: linux-mips, akpm, wim
In-Reply-To: <20071203181658.GA26631@onstor.com>

On Mon, Dec 03, 2007 at 10:17:04AM -0800, Andrew Sharp wrote:

> +	  Watchdog driver for the built in watchdog hardware in Sibyte
> +	  SoC processors.  There are apparently two watchdog timers
> +	  on such processors; this driver supports only the first one,
> +	  because currently Linux only supports exporting one watchdog
> +	  to userspace.

And even four watchdogs in the BCM1480.

You'd think they'd trust their hardware more than that ;-)

> + * This driver is intended to make the second of two hardware watchdogs
> + * on the Sibyte 12XX and 11XX SoCs available to the user.  There are two
> + * such devices available on the SoC, but it seems that there isn't an
> + * enumeration class for watchdogs in Linux like there is for RTCs.
> + * The second is used rather than the first because it uses IRQ 1,
> + * thereby avoiding all that IRQ 0 problematic nonsense.
> + *
> + * I have not tried this driver on a 1480 processor; it might work
> + * just well enough to really screw things up.

Four rather similar watchdogs using four interrupts also.

> + * It is a simple timer, and there is an interrupt that is raised the
> + * first time the timer expires.  The second time it expires, the chip
> + * is reset and there is no way to redirect that NMI.  Which could
> + * be problematic in some cases where this chip is sitting on the HT
> + * bus and has just taken responsibility for providing a cache block.
> + * Since the reset can't be redirected to the external reset pin, it is
> + * possible that other HT connected processors might hang and not reset.
> + * For Linux, a soft reset would probably be even worse than a hard reset.
> + * There you have it.

If read requests are never returned eventually the ZB bus HT host bridge will
run out of buffers after the 16th request.  The CPU has four more buffers
so the 21st read will stall the CPU's execution.  About a milisecond later
the machine check exception will make the CPU resume execution.  But at this
stage some registers are marked busy in the register scoreboard and any
reference to those CPU registers will cause the CPU to hang again ... until
the next machine check.  Game over, press button to continue.

> + * The timer takes 23 bits of a 64 bit register (?) as a count value,
> + * and decrements the count every microsecond, for a max value of
> + * 0x7fffff usec or about 8.3ish seconds.

One off - the maximum time is 0x800000µs.

  Ralf

^ permalink raw reply

* Re: [PATCH 2/2] MIPS: vmlinux.lds.S: handle .{init,exit}.bss sections
From: Franck Bui-Huu @ 2007-12-03 21:46 UTC (permalink / raw)
  To: Luck, Tony; +Cc: linux-arch, macro, linux-mips
In-Reply-To: <617E1C2C70743745A92448908E030B2A030FE4C2@scsmsx411.amr.corp.intel.com>

Luck, Tony wrote:
> +	/*
> +	 * We keep init/exit bss sections here to have only one
> +	 * segment to load. Note that .bss.exit is also discarded
> +	 * at runtime for the same reason as above.
> +	 */
> +	.exit.bss : {
> +		*(.bss.exit)
> +	}
> 
> This change would also require an audit of the bootloader or early
> kernel initialization code (whichever handles zeroing of .bss space)

I think the kernel doesn't rely on the bootloader for clearing bss.

> to make sure that it understands that the kernel will now have an
> extra block of memory that needs to be cleared.  Perhaps nothing
> needs to be done if the code already handled the general case of
> loading an ELF binary with some sections that have an in-memory
> size bigger than the on-disk size.  But worth looking at in case
> the code makes an assumption about what needs to be zeroed.
> 

For MIPS case, there is normally no extra block of memory to
clear. [__bss_start, __bss_stop] range still includes the whole memory
block to clear. So there's no need to modify any other code.

		Franck

^ permalink raw reply

* RE: Rename Sibyte duart devices?
From: Kaz Kylheku @ 2007-12-03 21:41 UTC (permalink / raw)
  To: Ralf Baechle, linux-mips, linux-serial
In-Reply-To: <20071203130818.GA6466@linux-mips.org>

Ralf Baechle wrote:
> Devices created by udev have been named duart? instead of the common
> ttyS?.  This is a nuisance because it requires changes to all sorts of
> config files such as /etc/inittab, /etc/securetty etc. to work.
> suggest to kill the problem by the root by something like the below
> patch.  Comments? 

I can see this kind of a change being somewhat sneaky; some Linux users
on SiByte systems have already worked around for the duart naming not by
patching the kernel but by modifying their text configurations to use
the duart naming. After they figure out why, after upgrading to a new
kernel, their system doesn't work any more, they will still have the
problem that the newer and older kernel require different text
configuration in the file system with respect to the tty devices. It's
nice to be able to pull out an older kernel and boot with it, without
having to remember a list of things to tweak.

To be forward and backward compatible, the kernel should perhaps offer
the device under both aliases: /etc/TTYSx and /etc/duartx.

Kaz "Kompatibility Kurmudgeon" Kylheku

^ permalink raw reply

* RE: Rename Sibyte duart devices?
From: Kaz Kylheku @ 2007-12-03 21:41 UTC (permalink / raw)
  To: Ralf Baechle, linux-mips, linux-serial
In-Reply-To: <20071203130818.GA6466@linux-mips.org>

Ralf Baechle wrote:
> Devices created by udev have been named duart? instead of the common
> ttyS?.  This is a nuisance because it requires changes to all sorts of
> config files such as /etc/inittab, /etc/securetty etc. to work.
> suggest to kill the problem by the root by something like the below
> patch.  Comments? 

I can see this kind of a change being somewhat sneaky; some Linux users
on SiByte systems have already worked around for the duart naming not by
patching the kernel but by modifying their text configurations to use
the duart naming. After they figure out why, after upgrading to a new
kernel, their system doesn't work any more, they will still have the
problem that the newer and older kernel require different text
configuration in the file system with respect to the tty devices. It's
nice to be able to pull out an older kernel and boot with it, without
having to remember a list of things to tweak.

To be forward and backward compatible, the kernel should perhaps offer
the device under both aliases: /etc/TTYSx and /etc/duartx.

Kaz "Kompatibility Kurmudgeon" Kylheku

^ permalink raw reply

* Re: [PATCH] Add code to determine the L2 cache size on Sibyte 1250/112x processors.
From: Andrew Sharp @ 2007-12-03 19:36 UTC (permalink / raw)
  To: Ralf Baechle; +Cc: linux-mips
In-Reply-To: <20071203192010.GA14818@linux-mips.org>

On Mon, 3 Dec 2007 19:20:10 +0000 Ralf Baechle <ralf@linux-mips.org>
wrote:

> On Mon, Dec 03, 2007 at 09:56:11AM -0800, Andrew Sharp wrote:
> 
> >  arch/mips/mm/c-sb1.c                 |   70
> > ++++++++++++++++++++++++++++++++++
> 
> c-sb1.c does no longer exist.  The functionality was folded into
> c-r4k.c and at the same time alot of insanity aka pass 1 workarounds
> dropped.

Doh.  I knew that, too.  Mindlessly trying to clear my patch backlog...

^ permalink raw reply

* Re: [PATCH] Add support for SB1 hardware watchdog.
From: Andrew Sharp @ 2007-12-03 19:24 UTC (permalink / raw)
  To: Alan Cox; +Cc: linux-mips, Ralf Baechle, akpm, wim
In-Reply-To: <20071203183419.3213d551@the-village.bc.nu>

On Mon, 3 Dec 2007 18:34:19 +0000 Alan Cox <alan@lxorguk.ukuu.org.uk>
wrote:

> > +	  on such processors; this driver supports only the first
> > one,
> > +	  because currently Linux only supports exporting one
> > watchdog
> > +	  to userspace.
> 
> Yep. Perhaps that should change.

I thought about that just a little; I'm just not sure what it would
mean to have multiple watchdog devices.

> > + * wdog is the iomem address of the cfg register
> > + */
> > + void
> > +sbwdog_set(char __iomem *wdog, unsigned long t)
> > +{
> > +	__raw_writeb(0, wdog - 0x10);
> > +	__raw_writeq(t & 0x7fffffUL, wdog);
> > +}
> 
> What guarantees you don't get a pair of these calls at once or
> interleaving ?

Not much, I suppose, except that opening the device file is exclusive.
A thread could fork (or dup) after that and get crazy in a theoretical
scenario ... are you suggesting this be serialized?

> 
> 
> > +		 * return the bits from the config register
> > +		 */
> > +		ret = put_user(__raw_readb(user_dog), p);
> 
> Should return the translated status bits ?

Don't need this really.  I see a few more things that need cleaning
up so I will do that and submit another patch.  And with changing of
the directory up a level, I also forgot the makefile change.

> 
> 
> Alan

^ permalink raw reply

* Re: [PATCH] Add code to determine the L2 cache size on Sibyte 1250/112x processors.
From: Ralf Baechle @ 2007-12-03 19:20 UTC (permalink / raw)
  To: Andrew Sharp; +Cc: linux-mips
In-Reply-To: <20071203175601.GA26533@onstor.com>

On Mon, Dec 03, 2007 at 09:56:11AM -0800, Andrew Sharp wrote:

>  arch/mips/mm/c-sb1.c                 |   70 ++++++++++++++++++++++++++++++++++

c-sb1.c does no longer exist.  The functionality was folded into c-r4k.c
and at the same time alot of insanity aka pass 1 workarounds dropped.

  Ralf

^ permalink raw reply

* Re: [PATCH] Add support for SB1 hardware watchdog.
From: Alan Cox @ 2007-12-03 18:34 UTC (permalink / raw)
  To: Andrew Sharp; +Cc: linux-mips, Ralf Baechle, akpm, wim
In-Reply-To: <20071203181658.GA26631@onstor.com>

> +	  on such processors; this driver supports only the first one,
> +	  because currently Linux only supports exporting one watchdog
> +	  to userspace.

Yep. Perhaps that should change.

> + * wdog is the iomem address of the cfg register
> + */
> + void
> +sbwdog_set(char __iomem *wdog, unsigned long t)
> +{
> +	__raw_writeb(0, wdog - 0x10);
> +	__raw_writeq(t & 0x7fffffUL, wdog);
> +}

What guarantees you don't get a pair of these calls at once or
interleaving ?



> +		 * return the bits from the config register
> +		 */
> +		ret = put_user(__raw_readb(user_dog), p);

Should return the translated status bits ?



Alan

^ permalink raw reply

* [PATCH] Add support for SB1 hardware watchdog.
From: Andrew Sharp @ 2007-12-03 18:17 UTC (permalink / raw)
  To: linux-mips; +Cc: Ralf Baechle, akpm, wim

This patch is for watchdog timers built into SiByte MIPS SoCs.

Signed-off-by: Andy Sharp <andys@ripper.onstor.net>
---
 drivers/watchdog/Kconfig   |   13 ++
 drivers/watchdog/sb_wdog.c |  369 ++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 382 insertions(+), 0 deletions(-)

diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig
index 2792bc1..9057362 100644
--- a/drivers/watchdog/Kconfig
+++ b/drivers/watchdog/Kconfig
@@ -616,6 +616,19 @@ config AR7_WDT
 	help
 	  Hardware driver for the TI AR7 Watchdog Timer.
 
+config SIBYTE_WDOG
+	tristate "Sibyte SoC hardware watchdog"
+	depends on CPU_SB1
+	help
+	  Watchdog driver for the built in watchdog hardware in Sibyte
+	  SoC processors.  There are apparently two watchdog timers
+	  on such processors; this driver supports only the first one,
+	  because currently Linux only supports exporting one watchdog
+	  to userspace.
+
+	  To compile this driver as a loadable module, choose M here.
+	  The module will be called sb_wdog.
+
 # PARISC Architecture
 
 # POWERPC Architecture
diff --git a/drivers/watchdog/sb_wdog.c b/drivers/watchdog/sb_wdog.c
new file mode 100644
index 0000000..1e80803
--- /dev/null
+++ b/drivers/watchdog/sb_wdog.c
@@ -0,0 +1,369 @@
+/*
+ * Watchdog driver for SiByte SB1 SoCs
+ *
+ * Copyright (C) 2007 OnStor, Inc. * Andrew Sharp <andy.sharp@onstor.com>
+ *
+ * This driver is intended to make the second of two hardware watchdogs
+ * on the Sibyte 12XX and 11XX SoCs available to the user.  There are two
+ * such devices available on the SoC, but it seems that there isn't an
+ * enumeration class for watchdogs in Linux like there is for RTCs.
+ * The second is used rather than the first because it uses IRQ 1,
+ * thereby avoiding all that IRQ 0 problematic nonsense.
+ *
+ * I have not tried this driver on a 1480 processor; it might work
+ * just well enough to really screw things up.
+ *
+ * It is a simple timer, and there is an interrupt that is raised the
+ * first time the timer expires.  The second time it expires, the chip
+ * is reset and there is no way to redirect that NMI.  Which could
+ * be problematic in some cases where this chip is sitting on the HT
+ * bus and has just taken responsibility for providing a cache block.
+ * Since the reset can't be redirected to the external reset pin, it is
+ * possible that other HT connected processors might hang and not reset.
+ * For Linux, a soft reset would probably be even worse than a hard reset.
+ * There you have it.
+ *
+ * The timer takes 23 bits of a 64 bit register (?) as a count value,
+ * and decrements the count every microsecond, for a max value of
+ * 0x7fffff usec or about 8.3ish seconds.
+ *
+ * This watchdog borrows some user semantics from the softdog driver,
+ * in that if you close the fd, it leaves the watchdog running, unless
+ * you previously wrote a 'V' to the fd, in which case it disables
+ * the watchdog when you close the fd like some other drivers.
+ *
+ * Based on various other watchdog drivers, which are probably all
+ * loosely based on something Alan Cox wrote years ago.
+ *
+ *	(c) Copyright 1996 Alan Cox <alan@redhat.com>, All Rights Reserved.
+ *				http://www.redhat.com
+ *
+ *	This program is free software; you can redistribute it and/or
+ *	modify it under the terms of the GNU General Public License
+ *	version 1 or 2 as published by the Free Software Foundation.
+ *
+ */
+#include <linux/module.h>
+#include <linux/io.h>
+#include <linux/uaccess.h>
+#include <linux/fs.h>
+#include <linux/reboot.h>
+#include <linux/miscdevice.h>
+#include <linux/watchdog.h>
+#include <linux/interrupt.h>
+
+#include <asm/sibyte/sb1250.h>
+#include <asm/sibyte/sb1250_regs.h>
+#include <asm/sibyte/sb1250_int.h>
+#include <asm/sibyte/sb1250_scd.h>
+
+
+/*
+ * set the initial count value of a timer
+ *
+ * wdog is the iomem address of the cfg register
+ */
+ void
+sbwdog_set(char __iomem *wdog, unsigned long t)
+{
+	__raw_writeb(0, wdog - 0x10);
+	__raw_writeq(t & 0x7fffffUL, wdog);
+}
+
+/*
+ * cause the timer to [re]load it's initial count and start counting
+ * all over again
+ *
+ * wdog is the iomem address of the cfg register
+ */
+ void
+sbwdog_pet(char __iomem *wdog)
+{
+	__raw_writeb(__raw_readb(wdog) | 1, wdog);
+}
+
+static unsigned long sbwdog_gate; /* keeps it to one thread only */
+static char __iomem *kern_dog = (char __iomem *)(IO_BASE + (A_SCD_WDOG_CFG_0));
+static char __iomem *user_dog = (char __iomem *)(IO_BASE + (A_SCD_WDOG_CFG_1));
+static unsigned long timeout = 0x7fffffUL;	/* useconds: 8.3ish secs. */
+static int expect_close;
+
+static struct watchdog_info ident = {
+	.options	= WDIOF_CARDRESET | WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING,
+	.identity	= "SiByte Watchdog",
+};
+
+/*
+ * Allow only a single thread to walk the dog
+ */
+ static int
+sbwdog_open(struct inode *inode, struct file *file)
+{
+	nonseekable_open(inode, file);
+	if (test_and_set_bit(0, &sbwdog_gate)) {
+		return -EBUSY;
+	}
+
+	/*
+	 * Activate the timer
+	 */
+	sbwdog_set(user_dog, timeout);
+	__raw_writeb(1, user_dog);
+
+	return 0;
+}
+
+/*
+ * Put the dog back in the kennel.
+ */
+ static int
+sbwdog_release(struct inode *inode, struct file *file)
+{
+	if (expect_close == 42) {
+		__raw_writeb(0, user_dog);
+		module_put(THIS_MODULE);
+	} else {
+        /*
+         * ONSTOR change - chassisd does not keep the fd open and we don't want
+         *                 this message everytime the fd is closed.
+         */
+#if !defined(CONFIG_ONSTOR_BOBCAT) && !defined(CONFIG_ONSTOR_COUGAR)
+		printk(KERN_CRIT "%s: Unexpected close, not stopping watchdog!\n",
+			ident.identity);
+#endif
+		sbwdog_pet(user_dog);
+	}
+	clear_bit(0, &sbwdog_gate);
+	expect_close = 0;
+
+	return 0;
+}
+
+/*
+ * 42 - the answer
+ */
+ static ssize_t
+sbwdog_write(struct file *file, const char __user *data, size_t len,
+	loff_t *ppos)
+{
+	int i;
+
+	if (len) {
+		/*
+		 * restart the timer
+		 */
+		expect_close = 0;
+
+		for (i = 0; i != len; i++) {
+			char c;
+
+			if (get_user(c, data + i)) {
+				return -EFAULT;
+			}
+			if (c == 'V') {
+				expect_close = 42;
+			}
+		}
+		sbwdog_pet(user_dog);
+	}
+
+	return len;
+}
+
+ static int
+sbwdog_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
+	unsigned long arg)
+{
+	int ret = -ENOTTY;
+	unsigned long time;
+	void __user *argp = (void __user *)arg;
+	int __user *p = argp;
+
+	switch (cmd) {
+	case WDIOC_GETSUPPORT:
+		ret = copy_to_user(argp, &ident, sizeof(ident)) ? -EFAULT : 0;
+		break;
+
+	case WDIOC_GETSTATUS:
+		/*
+		 * return the bits from the config register
+		 */
+		ret = put_user(__raw_readb(user_dog), p);
+		break;
+
+	case WDIOC_SETTIMEOUT:
+		ret = get_user(time, p);
+		if (ret) {
+			break;
+		}
+
+		time *= 1000000;
+		if (time > 0x7fffffUL) {
+			ret = -EINVAL;
+			break;
+		}
+		timeout = time;
+		sbwdog_set(user_dog, timeout);
+		sbwdog_pet(user_dog);
+
+	case WDIOC_GETTIMEOUT:
+		/*
+		 * get the remaining count from the ... count register
+		 * which is 1*8 before the config register
+		 */
+		ret = put_user(__raw_readq(user_dog - 8) / 1000000, p);
+		break;
+
+	case WDIOC_KEEPALIVE:
+		sbwdog_pet(user_dog);
+		ret = 0;
+		break;
+	}
+	return ret;
+}
+
+/*
+ *	Notifier for system down
+ */
+ static int
+sbwdog_notify_sys(struct notifier_block *this, unsigned long code, void *erf)
+{
+	if (code == SYS_DOWN || code == SYS_HALT) {
+		/*
+		 * sit and sit
+		 */
+		__raw_writeb(0, user_dog);
+		__raw_writeb(0, kern_dog);
+	}
+
+	return NOTIFY_DONE;
+}
+
+static const struct file_operations sbwdog_fops =
+{
+	.owner		= THIS_MODULE,
+	.llseek		= no_llseek,
+	.write		= sbwdog_write,
+	.ioctl		= sbwdog_ioctl,
+	.open		= sbwdog_open,
+	.release	= sbwdog_release,
+};
+
+static struct miscdevice sbwdog_miscdev =
+{
+	.minor		= WATCHDOG_MINOR,
+	.name		= "watchdog",
+	.fops		= &sbwdog_fops,
+};
+
+static struct notifier_block sbwdog_notifier = {
+	.notifier_call	= sbwdog_notify_sys,
+};
+
+/*
+ * interrupt handler
+ *
+ * doesn't do a whole lot for user, but oh so cleverly written so kernel
+ * code can use it to re-up the watchdog, thereby saving the kernel from
+ * having to create and maintain a timer, just to tickle another timer,
+ * which is just so wrong.
+ */
+ irqreturn_t
+sbwdog_interrupt(int irq, void *addr)
+{
+	unsigned long wd_init;
+	char *wd_cfg_reg = (char *)addr;
+	u8 cfg;
+
+	cfg = __raw_readb(wd_cfg_reg);
+	wd_init = __raw_readq(wd_cfg_reg - 8) & 0x7fffff;
+
+	/*
+	 * if it's the second watchdog timer, it's for those users
+	 */
+	if (wd_cfg_reg == user_dog) {
+		printk(KERN_CRIT
+			"%s in danger of initiating system reset in %ld.%01ld seconds\n",
+			ident.identity, wd_init / 1000000, (wd_init / 100000) % 10);
+	} else {
+		cfg |= 1;
+	}
+
+	__raw_writeb(cfg, wd_cfg_reg);
+
+	return IRQ_HANDLED;
+}
+
+ static int __init
+sbwdog_init(void)
+{
+	int ret;
+
+	/*
+	 * register a reboot notifier
+	 */
+	ret = register_reboot_notifier(&sbwdog_notifier);
+	if (ret) {
+		printk (KERN_ERR "%s: cannot register reboot notifier (err=%d)\n",
+			ident.identity, ret);
+		return ret;
+	}
+
+	/*
+	 * get the resources
+	 */
+	ret = misc_register(&sbwdog_miscdev);
+	if (ret == 0) {
+		printk(KERN_INFO "%s: timeout is %ld.%ld secs\n", ident.identity,
+			timeout / 1000000, (timeout / 100000) % 10);
+	}
+
+	ret = request_irq(1, sbwdog_interrupt, IRQF_DISABLED | IRQF_SHARED,
+		ident.identity, (void *)user_dog);
+	if (ret) {
+		printk(KERN_ERR "%s: failed to request irq 1 - %d\n", ident.identity,
+			ret);
+		misc_deregister(&sbwdog_miscdev);
+	}
+
+	return ret;
+}
+
+ static void __exit
+sbwdog_exit(void)
+{
+	misc_deregister(&sbwdog_miscdev);
+}
+
+module_init(sbwdog_init);
+module_exit(sbwdog_exit);
+
+MODULE_AUTHOR("Andrew Sharp <andy.sharp@onstor.com>");
+MODULE_DESCRIPTION("SiByte Watchdog");
+
+module_param(timeout, ulong, 0);
+MODULE_PARM_DESC(timeout,
+	"Watchdog timeout in microseconds (max/default 8388607 or 8.3ish secs)");
+
+MODULE_LICENSE("GPL");
+MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
+
+/*
+ * example code that can be put in a platform code area to utilize the
+ * first watchdog timer for the kernels own purpose.
+
+ void
+platform_wd_setup(void)
+{
+	int ret;
+
+	ret = request_irq(0, sbwdog_interrupt, IRQF_DISABLED | IRQF_SHARED,
+		"Kernel Watchdog", IOADDR(A_SCD_WDOG_CFG_0));
+	if (ret) {
+		printk(KERN_CRIT "Watchdog IRQ zero(0) failed to be requested - %d\n",
+			ret);
+	}
+}
+
+
+ */
-- 
1.4.4.4

^ permalink raw reply related

* [PATCH] Add code to determine the L2 cache size on Sibyte 1250/112x processors.
From: Andrew Sharp @ 2007-12-03 17:56 UTC (permalink / raw)
  To: linux-mips; +Cc: Ralf Baechle

Resend with an actual from header in the email.
Stupid git-send-email...grmbl


Add code to determine the L2 cache size on Sibyte 1250/112x processors.

Signed-off-by: Andrew Sharp <andy.sharp@onstor.com>
---
 arch/mips/mm/c-sb1.c                 |   70 ++++++++++++++++++++++++++++++++++
 include/asm-mips/sibyte/sb1250_l2c.h |    4 ++
 2 files changed, 74 insertions(+), 0 deletions(-)

diff --git a/arch/mips/mm/c-sb1.c b/arch/mips/mm/c-sb1.c
index ca8547e..cbc39f7 100644
--- a/arch/mips/mm/c-sb1.c
+++ b/arch/mips/mm/c-sb1.c
@@ -28,6 +28,9 @@
 #include <asm/mipsregs.h>
 #include <asm/mmu_context.h>
 #include <asm/uaccess.h>
+#include <asm/sibyte/sb1250.h>
+#include <asm/sibyte/sb1250_regs.h>
+#include <asm/sibyte/sb1250_l2c.h>
 
 extern void sb1_dma_init(void);
 
@@ -423,6 +426,68 @@ static unsigned int decode_cache_line_size(unsigned int config_field)
 }
 
 /*
+ * each bit in the 4-bit way-enable field indicates an enabled way
+ */
+
+ static int
+decode_l2_ways(u64 l2_misc)
+{
+	int ways;
+	int size;
+
+	ways = G_L2C_MISC_NO_WAY(l2_misc) ^ 0xf;
+	if (ways) {
+		int i;
+
+		size = L2C_NUM_WAYS;
+		for (i = 0; i < 4; i++) {
+			if (ways & 1) {
+				size -= 1;
+			}
+			ways = ways >> 1;
+		}
+		ways = size;
+	} else {
+		ways = L2C_NUM_WAYS;
+	}
+
+	return ways;
+}
+
+/*
+ * 0          = full size: 512KiB on 1250, 256 on 112x
+ * 1 || 2     = 256KiB
+ * 5, 6, 9, a = 128 KiB
+ */
+ static int
+decode_l2_size(u64 l2_misc)
+{
+	int size = 0;
+	int ways;
+
+	ways = decode_l2_ways(l2_misc);
+
+	switch _SB_GETVALUE(l2_misc, 4, _SB_MAKEMASK(4, 4)) {
+	case 0:
+		/* line size is 32 bytes on l2 cache */
+		size = (L2C_ENTRIES_PER_WAY * ways * 32) / 1024;
+		break;
+	case 1:
+	case 2:
+		size = 256;
+		break;
+	case 5:
+	case 6:
+	case 9:
+	case 0xa:
+		size = 128;
+		break;
+	}
+
+	return size;
+}
+
+/*
  * Relevant bits of the config1 register format (from the MIPS32/MIPS64 specs)
  *
  * 24:22 Icache sets per way
@@ -441,6 +506,7 @@ static char *way_string[] = {
 static __init void probe_cache_sizes(void)
 {
 	u32 config1;
+	u64 l2_misc;
 
 	config1 = read_c0_config1();
 	icache_line_size = decode_cache_line_size((config1 >> 19) & 0x7);
@@ -469,6 +535,10 @@ static __init void probe_cache_sizes(void)
 	printk("Primary data cache %ldkB, %s, linesize %d bytes.\n",
 	       dcache_size >> 10, way_string[dcache_assoc - 1],
 	       dcache_line_size);
+
+	l2_misc = __raw_readq(IOADDR(A_L2_READ_MISC));
+	printk("Secondary cache %dkB, %s, linesize %d bytes.\n",
+		decode_l2_size(l2_misc), way_string[decode_l2_ways(l2_misc) - 1], 32);
 }
 
 /*
diff --git a/include/asm-mips/sibyte/sb1250_l2c.h b/include/asm-mips/sibyte/sb1250_l2c.h
index 842f205..6aa19ad 100644
--- a/include/asm-mips/sibyte/sb1250_l2c.h
+++ b/include/asm-mips/sibyte/sb1250_l2c.h
@@ -102,7 +102,11 @@
 
 #define A_L2C_MGMT_TAG_BASE         0x00D0000000
 
+#ifndef CONFIG_SIBYTE_BCM112X
 #define L2C_ENTRIES_PER_WAY       4096
+#else
+#define L2C_ENTRIES_PER_WAY       2048
+#endif
 #define L2C_NUM_WAYS              4
 
 
-- 
1.4.4.4

^ permalink raw reply related

* RE: [PATCH 2/2] MIPS: vmlinux.lds.S: handle .{init,exit}.bss sections
From: Luck, Tony @ 2007-12-03 17:00 UTC (permalink / raw)
  To: Franck Bui-Huu, linux-arch; +Cc: macro, linux-mips
In-Reply-To: <1196543586-6698-3-git-send-email-fbuihuu@gmail.com>

+	/*
+	 * We keep init/exit bss sections here to have only one
+	 * segment to load. Note that .bss.exit is also discarded
+	 * at runtime for the same reason as above.
+	 */
+	.exit.bss : {
+		*(.bss.exit)
+	}

This change would also require an audit of the bootloader or early
kernel initialization code (whichever handles zeroing of .bss space)
to make sure that it understands that the kernel will now have an
extra block of memory that needs to be cleared.  Perhaps nothing
needs to be done if the code already handled the general case of
loading an ELF binary with some sections that have an in-memory
size bigger than the on-disk size.  But worth looking at in case
the code makes an assumption about what needs to be zeroed.

-Tony

^ permalink raw reply


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox