u-boot/drivers/serial/serial_s3c24x0.c
Anatolij Gustschin fbb0030e38 serial: struct serial_device: add uninit() entry for drivers
Subsequent patch extends mpc512x serial driver to support
multiple PSC ports. The driver will provide an uninit()
function to stop the serial controller and to disable the
controller's clock. Adding uninit() entry to struct serial_device
allows disabling the serial controller after usage of
a stdio serial device.

This patch adds uninit() entry to the struct serial_device
and fixes initialization of this structure in the code
accordingly.

Signed-off-by: Anatolij Gustschin <agust@denx.de>
2010-04-24 21:34:07 +02:00

319 lines
6.6 KiB
C

/*
* (C) Copyright 2002
* Gary Jennejohn, DENX Software Engineering, <garyj@denx.de>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#include <common.h>
#include <asm/arch/s3c24x0_cpu.h>
DECLARE_GLOBAL_DATA_PTR;
#ifdef CONFIG_SERIAL1
#define UART_NR S3C24X0_UART0
#elif defined(CONFIG_SERIAL2)
# if defined(CONFIG_TRAB)
# error "TRAB supports only CONFIG_SERIAL1"
# endif
#define UART_NR S3C24X0_UART1
#elif defined(CONFIG_SERIAL3)
# if defined(CONFIG_TRAB)
# error "TRAB supports only CONFIG_SERIAL1"
# endif
#define UART_NR S3C24X0_UART2
#else
#error "Bad: you didn't configure serial ..."
#endif
#include <asm/io.h>
#if defined(CONFIG_SERIAL_MULTI)
#include <serial.h>
/* Multi serial device functions */
#define DECLARE_S3C_SERIAL_FUNCTIONS(port) \
int s3serial##port##_init(void) \
{ \
return serial_init_dev(port); \
} \
void s3serial##port##_setbrg(void) \
{ \
serial_setbrg_dev(port); \
} \
int s3serial##port##_getc(void) \
{ \
return serial_getc_dev(port); \
} \
int s3serial##port##_tstc(void) \
{ \
return serial_tstc_dev(port); \
} \
void s3serial##port##_putc(const char c) \
{ \
serial_putc_dev(port, c); \
} \
void s3serial##port##_puts(const char *s) \
{ \
serial_puts_dev(port, s); \
}
#define INIT_S3C_SERIAL_STRUCTURE(port, name, bus) { \
name, \
bus, \
s3serial##port##_init, \
NULL,\
s3serial##port##_setbrg, \
s3serial##port##_getc, \
s3serial##port##_tstc, \
s3serial##port##_putc, \
s3serial##port##_puts, \
}
#endif /* CONFIG_SERIAL_MULTI */
#ifdef CONFIG_HWFLOW
static int hwflow;
#endif
void _serial_setbrg(const int dev_index)
{
struct s3c24x0_uart *uart = s3c24x0_get_base_uart(dev_index);
unsigned int reg = 0;
int i;
/* value is calculated so : (int)(PCLK/16./baudrate) -1 */
reg = get_PCLK() / (16 * gd->baudrate) - 1;
writel(reg, &uart->UBRDIV);
for (i = 0; i < 100; i++)
/* Delay */ ;
}
#if defined(CONFIG_SERIAL_MULTI)
static inline void serial_setbrg_dev(unsigned int dev_index)
{
_serial_setbrg(dev_index);
}
#else
void serial_setbrg(void)
{
_serial_setbrg(UART_NR);
}
#endif
/* Initialise the serial port. The settings are always 8 data bits, no parity,
* 1 stop bit, no start bits.
*/
static int serial_init_dev(const int dev_index)
{
struct s3c24x0_uart *uart = s3c24x0_get_base_uart(dev_index);
#ifdef CONFIG_HWFLOW
hwflow = 0; /* turned off by default */
#endif
/* FIFO enable, Tx/Rx FIFO clear */
writel(0x07, &uart->UFCON);
writel(0x0, &uart->UMCON);
/* Normal,No parity,1 stop,8 bit */
writel(0x3, &uart->ULCON);
/*
* tx=level,rx=edge,disable timeout int.,enable rx error int.,
* normal,interrupt or polling
*/
writel(0x245, &uart->UCON);
#ifdef CONFIG_HWFLOW
writel(0x1, &uart->UMCON); /* RTS up */
#endif
/* FIXME: This is sooooooooooooooooooo ugly */
#if defined(CONFIG_ARCH_GTA02_v1) || defined(CONFIG_ARCH_GTA02_v2)
/* we need auto hw flow control on the gsm and gps port */
if (dev_index == 0 || dev_index == 1)
writel(0x10, &uart->UMCON);
#endif
_serial_setbrg(dev_index);
return (0);
}
#if !defined(CONFIG_SERIAL_MULTI)
/* Initialise the serial port. The settings are always 8 data bits, no parity,
* 1 stop bit, no start bits.
*/
int serial_init(void)
{
return serial_init_dev(UART_NR);
}
#endif
/*
* Read a single byte from the serial port. Returns 1 on success, 0
* otherwise. When the function is succesfull, the character read is
* written into its argument c.
*/
int _serial_getc(const int dev_index)
{
struct s3c24x0_uart *uart = s3c24x0_get_base_uart(dev_index);
while (!(readl(&uart->UTRSTAT) & 0x1))
/* wait for character to arrive */ ;
return readb(&uart->URXH) & 0xff;
}
#if defined(CONFIG_SERIAL_MULTI)
static inline int serial_getc_dev(unsigned int dev_index)
{
return _serial_getc(dev_index);
}
#else
int serial_getc(void)
{
return _serial_getc(UART_NR);
}
#endif
#ifdef CONFIG_HWFLOW
int hwflow_onoff(int on)
{
switch (on) {
case 0:
default:
break; /* return current */
case 1:
hwflow = 1; /* turn on */
break;
case -1:
hwflow = 0; /* turn off */
break;
}
return hwflow;
}
#endif
#ifdef CONFIG_MODEM_SUPPORT
static int be_quiet = 0;
void disable_putc(void)
{
be_quiet = 1;
}
void enable_putc(void)
{
be_quiet = 0;
}
#endif
/*
* Output a single byte to the serial port.
*/
void _serial_putc(const char c, const int dev_index)
{
struct s3c24x0_uart *uart = s3c24x0_get_base_uart(dev_index);
#ifdef CONFIG_MODEM_SUPPORT
if (be_quiet)
return;
#endif
while (!(readl(&uart->UTRSTAT) & 0x2))
/* wait for room in the tx FIFO */ ;
#ifdef CONFIG_HWFLOW
while (hwflow && !(readl(&uart->UMSTAT) & 0x1))
/* Wait for CTS up */ ;
#endif
writeb(c, &uart->UTXH);
/* If \n, also do \r */
if (c == '\n')
serial_putc('\r');
}
#if defined(CONFIG_SERIAL_MULTI)
static inline void serial_putc_dev(unsigned int dev_index, const char c)
{
_serial_putc(c, dev_index);
}
#else
void serial_putc(const char c)
{
_serial_putc(c, UART_NR);
}
#endif
/*
* Test whether a character is in the RX buffer
*/
int _serial_tstc(const int dev_index)
{
struct s3c24x0_uart *uart = s3c24x0_get_base_uart(dev_index);
return readl(&uart->UTRSTAT) & 0x1;
}
#if defined(CONFIG_SERIAL_MULTI)
static inline int serial_tstc_dev(unsigned int dev_index)
{
return _serial_tstc(dev_index);
}
#else
int serial_tstc(void)
{
return _serial_tstc(UART_NR);
}
#endif
void _serial_puts(const char *s, const int dev_index)
{
while (*s) {
_serial_putc(*s++, dev_index);
}
}
#if defined(CONFIG_SERIAL_MULTI)
static inline void serial_puts_dev(int dev_index, const char *s)
{
_serial_puts(s, dev_index);
}
#else
void serial_puts(const char *s)
{
_serial_puts(s, UART_NR);
}
#endif
#if defined(CONFIG_SERIAL_MULTI)
DECLARE_S3C_SERIAL_FUNCTIONS(0);
struct serial_device s3c24xx_serial0_device =
INIT_S3C_SERIAL_STRUCTURE(0, "s3ser0", "S3UART1");
DECLARE_S3C_SERIAL_FUNCTIONS(1);
struct serial_device s3c24xx_serial1_device =
INIT_S3C_SERIAL_STRUCTURE(1, "s3ser1", "S3UART2");
DECLARE_S3C_SERIAL_FUNCTIONS(2);
struct serial_device s3c24xx_serial2_device =
INIT_S3C_SERIAL_STRUCTURE(2, "s3ser2", "S3UART3");
#endif /* CONFIG_SERIAL_MULTI */