#include <linux/of.h>
#include <linux/of_platform.h>
#include <linux/clk.h>
+#include <linux/slab.h>
#include <asm/mpc52xx.h>
#include <asm/mpc52xx_psc.h>
irqreturn_t (*handle_irq)(struct uart_port *port);
};
+struct mpc52xx_private_data {
+ int mode; // Determine wether to use port in RS-232 (=0) or RS-485 (=1) mode
+};
+
/* setting the prescaler and divisor reg is common for all chips */
static inline void mpc52xx_set_divisor(struct mpc52xx_psc __iomem *psc,
u16 prescaler, unsigned int divisor)
static void mpc52xx_psc_start_tx(struct uart_port *port)
{
+ struct mpc52xx_private_data *private_data = (struct mpc52xx_private_data *) port->private_data;
+
port->read_status_mask |= MPC52xx_PSC_IMR_TXRDY;
+ if (private_data->mode == 1)
+ port->read_status_mask |= MPC52xx_PSC_IMR_TXEMP;
out_be16(&PSC(port)->mpc52xx_psc_imr, port->read_status_mask);
}
psc_fifoc = of_iomap(np, 0);
if (!psc_fifoc) {
pr_err("%s: Can't map FIFOC\n", __func__);
+ of_node_put(np);
return -ENODEV;
}
{
/* port->lock taken by caller */
psc_ops->start_tx(port);
+// if(!uart_console(port))
+// printk("mpc52xx: start_tx\n");
}
static void
{
/* port->lock taken by caller */
psc_ops->stop_rx(port);
+// if(!uart_console(port))
+// printk("mpc52xx: stop_tx\n");
}
static void
}
if (new->c_cflag & PARENB) {
- mr1 |= (new->c_cflag & PARODD) ?
- MPC52xx_PSC_MODE_PARODD : MPC52xx_PSC_MODE_PAREVEN;
+ if (new->c_cflag & CMSPAR) {
+ mr1 |= MPC52xx_PSC_MODE_PARFORCE;
+ mr1 |= (new->c_cflag & PARODD) ?
+ MPC52xx_PSC_MODE_PARODD : MPC52xx_PSC_MODE_PAREVEN;
+ } else {
+ mr1 |= (new->c_cflag & PARODD) ?
+ MPC52xx_PSC_MODE_PARODD : MPC52xx_PSC_MODE_PAREVEN;
+ }
} else
mr1 |= MPC52xx_PSC_MODE_PARNONE;
-
mr2 = 0;
if (new->c_cflag & CSTOPB)
out_8(&psc->command, MPC52xx_PSC_TX_ENABLE);
out_8(&psc->command, MPC52xx_PSC_RX_ENABLE);
+ /* RS-485 direction control - Switch to receiving TODO: if in 485 mode*/
+ out_8(&PSC(port)->op1, 0x1);
+
/* We're all set, release the lock */
spin_unlock_irqrestore(&port->lock, flags);
}
static irqreturn_t
mpc5xxx_uart_process_int(struct uart_port *port)
{
+ struct mpc52xx_private_data *private_data = (struct mpc52xx_private_data *) port->private_data;
unsigned long pass = ISR_PASS_LIMIT;
unsigned int keepgoing;
+ struct circ_buf *xmit = &port->state->xmit;
u8 status;
/* While we have stuff to do, we continue */
if (psc_ops->rx_rdy(port))
keepgoing |= mpc52xx_uart_int_rx_chars(port);
+ /* RS-485 direction control */
+ if (private_data->mode == 1) {
+ if (!uart_circ_empty(xmit)) {
+ out_8(&PSC(port)->op0, 0x1);
+ }
+ }
+
psc_ops->tx_clr_irq(port);
if (psc_ops->tx_rdy(port))
keepgoing |= mpc52xx_uart_int_tx_chars(port);
+ /* RS-485 direction control */
+ if (private_data->mode == 1) {
+ //if ( ( status & MPC52xx_PSC_IMR_TXEMP) && ( (status & MPC52xx_PSC_IMR_TXRDY) == 0 ) && (uart_circ_empty(xmit)) ) {
+ if ( (psc_ops->tx_empty(port)) && (psc_ops->raw_tx_rdy(port) != 0) && (uart_circ_empty(xmit)) ) {
+ out_8(&PSC(port)->op1, 0x1);
+
+ port->read_status_mask &= ~MPC52xx_PSC_IMR_TXEMP;
+ out_be16(&PSC(port)->mpc52xx_psc_imr, port->read_status_mask);
+
+ keepgoing = 0;
+ }
+ }
+
status = in_8(&PSC(port)->mpc52xx_psc_ipcr);
if (status & MPC52xx_PSC_D_DCD)
uart_handle_dcd_change(port, !(status & MPC52xx_PSC_DCD));
struct uart_port *port = NULL;
struct resource res;
int ret;
+ struct mpc52xx_private_data *private_data = NULL;
+ const char *port_mode = NULL;
+ int port_mode_len;
dev_dbg(&op->dev, "mpc52xx_uart_probe(op=%p, match=%p)\n", op, match);
dev_dbg(&op->dev, "Could not get irq\n");
return -EINVAL;
}
+
+ private_data = (struct mpc52xx_private_data *) kzalloc(sizeof(struct mpc52xx_private_data), GFP_KERNEL);
+ if (private_data) {
+ port_mode = of_get_property(op->dev.of_node, "uart_mode", &port_mode_len);
+ if (port_mode) {
+ if (strcmp(port_mode, "RS-485") == 0) {
+ private_data->mode = 1;
+ }
+ } else {
+ private_data->mode = 0;
+ }
+ port->private_data = (void *) private_data;
+ } else {
+ printk(KERN_ERR "Could not allocate private data for PSC\n");
+ return -EINVAL;
+ }
dev_dbg(&op->dev, "mpc52xx-psc uart at %p, irq=%x, freq=%i\n",
(void *)port->mapbase, port->irq, port->uartclk);
dev_set_drvdata(&op->dev, NULL);
if (port)
+ if (port->private_data) {
+ kfree(port->private_data);
+ }
uart_remove_one_port(&mpc52xx_uart_driver, port);
return 0;