Here my rewritten patch for rs485 support for kernel 2.6.24.7 Read/write direction ot differential bus transceiver will be changed by /RTS with output port register 0p0 and 0p1; To switch uart to rs485 mode, a 1 has to be written to /sys/dev/f00000000.soc5200/f0002x000.serial/uartmode. *void private_data in struct uart_port will point t a variable for each port to mark mode. A uart console can not be switched to rs485 mode
Maybe this is usefull for somebody Criticism is welcome cheers diff -uprN linux-2.6.24.7-el392-rt11-can751_org/drivers/serial/Kconfig linux-2.6.24.7-el392-rt11-can751/drivers/serial/Kconfig --- linux-2.6.24.7-el392-rt11-can751_org/drivers/serial/Kconfig 2008-10-16 13:35:31.000000000 +0200 +++ linux-2.6.24.7-el392-rt11-can751/drivers/serial/Kconfig 2008-11-13 15:44:19.000000000 +0100 @@ -1123,6 +1123,13 @@ config SERIAL_MPC52xx for use as console, it must be included in kernel and not as a module. +config SERIAL_RS485 + bool "RS485 support" + depends on SERIAL_MPC52xx + help + This drivers support serial RS485 ports. If you like + to use them, answer Y to this option. + config SERIAL_MPC52xx_CONSOLE bool "Console on a Freescale MPC52xx family PSC serial port" depends on SERIAL_MPC52xx=y diff -uprN linux-2.6.24.7-el392-rt11-can751_org/drivers/serial/mpc52xx_uart.c linux-2.6.24.7-el392-rt11-can751/drivers/serial/mpc52xx_uart.c --- linux-2.6.24.7-el392-rt11-can751_org/drivers/serial/mpc52xx_uart.c 2008-10-16 13:40:15.000000000 +0200 +++ linux-2.6.24.7-el392-rt11-can751/drivers/serial/mpc52xx_uart.c 2008-11-17 15:55:07.000000000 +0100 @@ -21,11 +21,25 @@ * Copyright (C) 2004-2006 Sylvain Munaut <[EMAIL PROTECTED]> * Copyright (C) 2003 MontaVista, Software, Inc. * + * RS485 support is written by Hans Lehmann + * <[EMAIL PROTECTED]> + * * This file is licensed under the terms of the GNU General Public License * version 2. This program is licensed "as is" without any warranty of any * kind, whether express or implied. */ +/* RS485 Usage: + * The driver will autmaticly handle a uart port in RS485 mode if you set uartmode + * in /sys/device/f00000000.soc5200/f0002x000.serial to 1 + * "echo 1 > /sys/device/f00000000.soc5200/f0002x000.serial/uartmode + * We misuse void pointer private->data in struct uart_port + * to determinate handle of port. + * RTS bit is used to set direction of differential bus transceiver + * Set mpc52xx_psc_op1 asserts output port /RTS: Write + * Set mpc52xx_psc_op0 negate output port /RTS: Read +*/ + /* Platform device Usage : * * Since PSCs can have multiple function, the correct driver for each one @@ -94,6 +108,12 @@ #define ISR_PASS_LIMIT 256 /* Max number of iteration in the interrupt */ +#define RS485_SET_RTS 0x01 /* for ouput port 1 bit set */ +#define RS485_CLR_RTS RS485_SET_RTS /* for ouput port 0 bit set */ + +#define RS485 1 +#define RS232 0 + static struct uart_port mpc52xx_uart_ports[MPC52xx_PSC_MAXNUM]; /* Rem: - We use the read_status_mask as a shadow of @@ -132,6 +152,12 @@ static struct of_device_id mpc52xx_uart_ }; #endif +/* Simple variable to set private->data + * private data will be used to test, how to handle port +*/ +#ifdef CONFIG_SERIAL_RS485 +int uartmode = RS232; +#endif /* CONFIG_SERIAL_RS485 */ /* ======================================================================== */ /* UART operations */ @@ -144,6 +170,20 @@ mpc52xx_uart_tx_empty(struct uart_port * return (status & MPC52xx_PSC_SR_TXEMP) ? TIOCSER_TEMT : 0; } +#ifdef CONFIG_SERIAL_RS485 +static void +mpc52xx_uart_rs485_rts_clear(struct uart_port *port) +{ + /* In RS485 mode negate output port /RTS after + * TX empty (underrun) IRQ, to be able to receive data, directly + */ + out_8(&PSC(port)->mpc52xx_psc_op0, RS485_SET_RTS); + port->read_status_mask &= ~MPC52xx_PSC_IMR_TXEMP + & ~MPC52xx_PSC_IMR_TXRDY; + out_be16(&PSC(port)->mpc52xx_psc_imr,port->read_status_mask); +} +#endif /* CONFIG_SERIAL_RS485 */ + static void mpc52xx_uart_set_mctrl(struct uart_port *port, unsigned int mctrl) { @@ -168,8 +208,18 @@ mpc52xx_uart_stop_tx(struct uart_port *p static void mpc52xx_uart_start_tx(struct uart_port *port) { - /* port->lock taken by caller */ - port->read_status_mask |= MPC52xx_PSC_IMR_TXRDY; + /* port->lock taken by caller */ +#ifdef CONFIG_SERIAL_RS485 + if (*(int *)port->private_data){ + /* Set /RTS immidiate before start transaction + * Make sure tx empty interrupt is on + */ + port->read_status_mask |= MPC52xx_PSC_IMR_TXEMP + | MPC52xx_PSC_IMR_TXRDY; + out_8(&PSC(port)->mpc52xx_psc_op1, RS485_CLR_RTS); + } else +#endif /* CONFIG_SERIAL_RS485 */ + port->read_status_mask |= MPC52xx_PSC_IMR_TXRDY; out_be16(&PSC(port)->mpc52xx_psc_imr,port->read_status_mask); } @@ -513,6 +563,14 @@ mpc52xx_uart_int_tx_chars(struct uart_po { struct circ_buf *xmit = &port->info->xmit; +#ifdef CONFIG_SERIAL_RS485 + /* Tx Empty in RS485 Mode */ + if (uart_circ_empty(xmit) && mpc52xx_uart_tx_empty(port)){ + mpc52xx_uart_rs485_rts_clear(port); + return 0; + } +#endif /* CONFIG_SERIAL_RS485 */ + /* Process out of band chars */ if (port->x_char) { out_8(&PSC(port)->mpc52xx_psc_buffer_8, port->x_char); @@ -578,6 +636,15 @@ mpc52xx_uart_int(int irq, void *dev_id) if ( status & MPC52xx_PSC_IMR_TXRDY ) keepgoing |= mpc52xx_uart_int_tx_chars(port); +#ifdef CONFIG_SERIAL_RS485 + /* Do we need to send chars in RS485 mode ? */ + /* For this, TX must be ready, TX and TX empty + * interrupt enabled + */ + if ((*(int *)port->private_data) && (status & MPC52xx_PSC_IMR_TXEMP)) + keepgoing |= mpc52xx_uart_int_tx_chars(port); +#endif /* CONFIG_SERIAL_RS485 */ + /* Limit number of iteration */ if ( !(--pass) ) keepgoing = 0; @@ -805,6 +872,44 @@ console_initcall(mpc52xx_console_init); #define MPC52xx_PSC_CONSOLE NULL #endif +#ifdef CONFIG_SERIAL_RS485 +/* ======================================================================== */ +/* SYSFS operations */ +/* ======================================================================== */ + +static ssize_t read_uartmode(struct device *dev, + struct device_attribute *attr, + const char *buf) +{ + struct uart_port *port = (struct uart_port *) dev_get_drvdata(dev); + + printk("Type ttyPSC%i: %i\n", port->line,*(int *)port->private_data); + return strlen(buf)+1; +} + +static ssize_t write_uartmode(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct uart_port *port = (struct uart_port *) dev_get_drvdata(dev); + + uartmode = simple_strtoul(buf, NULL, 0); + + if (!uart_console(port)){ + if (uartmode || *(int *)port->private_data) + *(int *)port->private_data = uartmode; + printk("Type ttyPSC%i: %i\n", port->line, + *(int *)port->private_data); + } else + printk("ttyPSC%i is console\n",port->line); + + uartmode = RS232; + + return strlen (buf) + 1; +} + +static DEVICE_ATTR(uartmode, S_IRUGO|S_IWUGO, read_uartmode, write_uartmode); +#endif /* CONFIG_SERIAL_RS485 */ /* ======================================================================== */ /* UART Driver */ @@ -969,6 +1074,19 @@ mpc52xx_uart_of_probe(struct of_device * port->ops = &mpc52xx_uart_ops; port->dev = &op->dev; + /* to change uart mode, we need a entry point */ +#ifdef CONFIG_SERIAL_RS485 + device_create_file(&op->dev, &dev_attr_uartmode); + + /* for each port we have to allocate a variable */ + port->private_data = kmalloc(sizeof(int), GFP_USER); + if (!port->private_data) + return -ENOMEM; + + *(int *)port->private_data = RS232; + +#endif /* CONFIG_SERIAL_RS485 */ + /* Search for IRQ and mapbase */ if ((ret = of_address_to_resource(op->node, 0, &res)) != 0) return ret; @@ -997,6 +1115,13 @@ mpc52xx_uart_of_remove(struct of_device { struct uart_port *port = dev_get_drvdata(&op->dev); dev_set_drvdata(&op->dev, NULL); + +#ifdef CONFIG_SERIAL_RS485 + device_remove_file(&op->dev, &dev_attr_uartmode); + + /* Don't forget to free memory */ + kfree(port->private_data); +#endif /* CONFIG_SERIAL_RS485 */ if (port) { uart_remove_one_port(&mpc52xx_uart_driver, port); @@ -1132,6 +1257,7 @@ mpc52xx_uart_init(void) return ret; } #else + ret = platform_driver_register(&mpc52xx_uart_platform_driver); if (ret) { printk(KERN_ERR "%s: platform_driver_register failed (%i)\n", diff -uprN linux-2.6.24.7-el392-rt11-can751_org/include/asm-powerpc/mpc52xx_psc.h linux-2.6.24.7-el392-rt11-can751/include/asm-powerpc/mpc52xx_psc.h --- linux-2.6.24.7-el392-rt11-can751_org/include/asm-powerpc/mpc52xx_psc.h 2008-10-16 13:35:05.000000000 +0200 +++ linux-2.6.24.7-el392-rt11-can751/include/asm-powerpc/mpc52xx_psc.h 2008-11-14 11:40:56.000000000 +0100 @@ -27,6 +27,7 @@ /* Max number of PSCs */ #define MPC52xx_PSC_MAXNUM 6 + /* Programmable Serial Controller (PSC) status register bits */ #define MPC52xx_PSC_SR_CDE 0x0080 #define MPC52xx_PSC_SR_RXRDY 0x0100 @@ -64,6 +65,7 @@ #define MPC52xx_PSC_IMR_TXRDY 0x0100 #define MPC52xx_PSC_IMR_RXRDY 0x0200 #define MPC52xx_PSC_IMR_DB 0x0400 +#define MPC52xx_PSC_IMR_TXEMP MPC52xx_PSC_SR_TXEMP #define MPC52xx_PSC_IMR_IPC 0x8000 /* PSC input port change bit */ @@ -139,8 +141,10 @@ struct mpc52xx_psc { u8 ip; /* PSC + 0x34 */ u8 reserved9[3]; u8 op1; /* PSC + 0x38 */ +#define mpc52xx_psc_op1 op1 u8 reserved10[3]; u8 op0; /* PSC + 0x3c */ +#define mpc52xx_psc_op0 op0 u8 reserved11[3]; u32 sicr; /* PSC + 0x40 */ u8 ircr1; /* PSC + 0x44 */ Mit freundlichen Grüßen Hans Lehmann Softwareentwicklung Telefon +49 (0)2191-67-2520 Fax +49 (0)2191-67-703408 e-mail [EMAIL PROTECTED] Ritter Elektronik GmbH Leverkuser Straße 65 D-42897 Remscheid www.ritter-elektronik.de Geschäftsführer: Manfred A. Wagner, Dr. Uwe Baader Sitz der Gesellschaft: Oberhausen HRB 17168 Duisburg / USt-ID DE 814009849 -----Ursprüngliche Nachricht----- Von: Wolfram Sang [mailto:[EMAIL PROTECTED] Gesendet: Donnerstag, 13. November 2008 19:42 An: Lehmann, Hans (Ritter Elektronik) Cc: linuxppc-dev@ozlabs.org; Grant Likely Betreff: Re: [PATCH v1] RS485 support for MPC5200x_psc_uart Hello Hans, On Thu, Nov 13, 2008 at 04:48:30PM +0100, Lehmann, Hans (Ritter Elektronik) wrote: > Adds rs485 support for MPC52xx_psc_uart Please be more specific. What exactly was done/modified to have RS485-support. Please also read SubmittingPatches and CodingStyle in the Documentation directory and adapt your patch accordingly. For example: Send patches inlined, this makes reviewing a lot easier. Don't use magic numbers. Use spaces around operators. All this helps maintaining your code in the future. > COM1 is console and not selectable. COM1 is rarely selectable in Linux ;) Kind regards, Wolfram -- Dipl.-Ing. Wolfram Sang | http://www.pengutronix.de Pengutronix - Linux Solutions for Science and Industry _______________________________________________ Linuxppc-dev mailing list Linuxppc-dev@ozlabs.org https://ozlabs.org/mailman/listinfo/linuxppc-dev