diff options
author | Juergen Beisert <j.beisert@pengutronix.de> | 2009-06-23 15:46:15 +0200 |
---|---|---|
committer | Juergen Beisert <j.beisert@pengutronix.de> | 2009-07-01 14:36:40 +0200 |
commit | 5139e618b8d732bfbea69d9a9bdf83832de30472 (patch) | |
tree | 530f01f7b25071d0d8ab1a300060e6f9e15aae1b /drivers | |
parent | 5d56a46c0aaaadb7eebc23ef7577a637610926f7 (diff) | |
download | barebox-5139e618b8d732bfbea69d9a9bdf83832de30472.tar.gz barebox-5139e618b8d732bfbea69d9a9bdf83832de30472.tar.xz |
Add basic architecture support for Samsung's S3C2410 and S3C2440 CPU.
It includes a
- driver for the internal UART
- driver for the internal NAND controller
- support to boot from NAND
- PLL handling
- SDRAM initialisation
Signed-off-by: Juergen Beisert <j.beisert@pengutronix.de>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/nand/Kconfig | 7 | ||||
-rw-r--r-- | drivers/nand/Makefile | 1 | ||||
-rw-r--r-- | drivers/nand/nand_s3c2410.c | 528 | ||||
-rw-r--r-- | drivers/serial/Kconfig | 15 | ||||
-rw-r--r-- | drivers/serial/Makefile | 1 | ||||
-rw-r--r-- | drivers/serial/serial_s3c24x0.c | 165 |
6 files changed, 717 insertions, 0 deletions
diff --git a/drivers/nand/Kconfig b/drivers/nand/Kconfig index 4f238dfa6b..2b4d62ab5d 100644 --- a/drivers/nand/Kconfig +++ b/drivers/nand/Kconfig @@ -38,6 +38,13 @@ config NAND_ATMEL prompt "Atmel (AT91SAM9xxx) NAND driver" depends on ARCH_AT91SAM9 +config NAND_S3C24X0 + bool + prompt "Samsung S3C24X0 NAND driver" + depends on ARCH_S3C24xx + help + Add support for processor's NAND device controller. + config MTD_NAND_VERIFY_WRITE bool "Verify NAND page writes" help diff --git a/drivers/nand/Makefile b/drivers/nand/Makefile index af6ac578fe..73f734688e 100644 --- a/drivers/nand/Makefile +++ b/drivers/nand/Makefile @@ -8,4 +8,5 @@ obj-$(CONFIG_MTD_NAND_DISKONCHIP) += diskonchip.o obj-$(CONFIG_NAND_IMX) += nand_imx.o obj-$(CONFIG_NAND_OMAP_GPMC) += nand_omap_gpmc.o obj-$(CONFIG_NAND_ATMEL) += atmel_nand.o +obj-$(CONFIG_NAND_S3C24X0) += nand_s3c2410.o #obj-$(CONFIG_NAND) += nand_util.o diff --git a/drivers/nand/nand_s3c2410.c b/drivers/nand/nand_s3c2410.c new file mode 100644 index 0000000000..699bb34b8c --- /dev/null +++ b/drivers/nand/nand_s3c2410.c @@ -0,0 +1,528 @@ +/* linux/drivers/mtd/nand/s3c2410.c + * + * Copyright (C) 2009 Juergen Beisert, Pengutronix + * + * Copyright © 2004-2008 Simtec Electronics + * http://armlinux.simtec.co.uk/ + * Ben Dooks <ben@simtec.co.uk> + * + * Samsung S3C2410 NAND driver + * + * 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 <config.h> +#include <common.h> +#include <driver.h> +#include <malloc.h> +#include <init.h> +#include <linux/mtd/mtd.h> +#include <linux/mtd/nand.h> +#include <asm/arch/s3c24xx-generic.h> +#include <asm/arch/s3c24x0-iomap.h> +#include <asm/arch/s3c24x0-nand.h> +#include <asm/io.h> +#include <asm-generic/errno.h> + +#ifdef CONFIG_S3C24XX_NAND_BOOT +# define __nand_boot_init __bare_init +# ifndef BOARD_DEFAULT_NAND_TIMING +# define BOARD_DEFAULT_NAND_TIMING 0x0737 +# endif +#else +# define __nand_boot_init +#endif + +/** + * Define this symbol for testing purpose. It will add a command to read an + * image from the NAND like it the boot strap code will do. + */ +#define CONFIG_NAND_S3C24XX_BOOT_DEBUG + +/* NAND controller's register */ + +#define NFCONF 0x00 + +#ifdef CONFIG_CPU_S3C2410 + +#define NFCMD 0x04 +#define NFADDR 0x08 +#define NFDATA 0x0c +#define NFSTAT 0x10 +#define NFECC 0x14 + +/* S3C2410 specific bits */ +#define NFSTAT_BUSY (1) +#define NFCONF_nFCE (1 << 11) +#define NFCONF_INITECC (1 << 12) +#define NFCONF_EN (1 << 15) + +#endif /* CONFIG_CPU_S3C2410 */ + +#ifdef CONFIG_CPU_S3C2440 + +#define NFCONT 0x04 +#define NFCMD 0x08 +#define NFADDR 0x0C +#define NFDATA 0x10 + +#define NFECC 0x1C +#define NFSTAT 0x20 + +/* S3C2440 specific bits */ +#define NFSTAT_BUSY (1) +#define NFCONT_nFCE (1 << 1) +#define NFCONF_INITECC (1 << 12) +#define NFCONT_EN (1) + +#endif /* CONFIG_CPU_S3C2440 */ + + +struct s3c24x0_nand_host { + struct mtd_info mtd; + struct nand_chip nand; + struct mtd_partition *parts; + struct device_d *dev; + + unsigned long base; +}; + +/** + * oob placement block for use with hardware ecc generation + */ +static struct nand_ecclayout nand_hw_eccoob = { + .eccbytes = 3, + .eccpos = { 0, 1, 2}, + .oobfree = { + { + .offset = 8, + .length = 8 + } + } +}; + +/* - Functions shared between the boot strap code and the regular driver - */ + +/** + * Issue the specified command to the NAND device + * @param[in] host Base address of the NAND controller + * @param[in] cmd Command for NAND flash + */ +static void __nand_boot_init send_cmd(unsigned long host, uint8_t cmd) +{ + writeb(cmd, host + NFCMD); +} + +/** + * Issue the specified address to the NAND device + * @param[in] host Base address of the NAND controller + * @param[in] addr Address for the NAND flash + */ +static void __nand_boot_init send_addr(unsigned long host, uint8_t addr) +{ + writeb(addr, host + NFADDR); +} + +/** + * Enable the NAND flash access + * @param[in] host Base address of the NAND controller + */ +static void __nand_boot_init enable_cs(unsigned long host) +{ +#ifdef CONFIG_CPU_S3C2410 + writew(readw(host + NFCONF) & ~NFCONF_nFCE, host + NFCONF); +#endif +#ifdef CONFIG_CPU_S3C2440 + writew(readw(host + NFCONT) & ~NFCONT_nFCE, host + NFCONT); +#endif +} + +/** + * Disable the NAND flash access + * @param[in] host Base address of the NAND controller + */ +static void __nand_boot_init disable_cs(unsigned long host) +{ +#ifdef CONFIG_CPU_S3C2410 + writew(readw(host + NFCONF) | NFCONF_nFCE, host + NFCONF); +#endif +#ifdef CONFIG_CPU_S3C2440 + writew(readw(host + NFCONT) | NFCONT_nFCE, host + NFCONT); +#endif +} + +/** + * Enable the NAND flash controller + * @param[in] host Base address of the NAND controller + * @param[in] timing Timing to access the NAND memory + */ +static void __nand_boot_init enable_nand_controller(unsigned long host, uint32_t timing) +{ +#ifdef CONFIG_CPU_S3C2410 + writew(timing + NFCONF_EN + NFCONF_nFCE, host + NFCONF); +#endif +#ifdef CONFIG_CPU_S3C2440 + writew(NFCONT_EN + NFCONT_nFCE, host + NFCONT); + writew(timing, host + NFCONF); +#endif +} + +/** + * Diable the NAND flash controller + * @param[in] host Base address of the NAND controller + */ +static void __nand_boot_init disable_nand_controller(unsigned long host) +{ +#ifdef CONFIG_CPU_S3C2410 + writew(NFCONF_nFCE, host + NFCONF); +#endif +#ifdef CONFIG_CPU_S3C2440 + writew(NFCONT_nFCE, host + NFCONT); +#endif +} + +/* ----------------------------------------------------------------------- */ + +/** + * Check the ECC and try to repair the data if possible + * @param[in] mtd_info FIXME + * @param[inout] dat Pointer to the data buffer that might contain a bit error + * @param[in] read_ecc ECC data from the OOB space + * @param[in] calc_ecc ECC data calculated from the data + * @return 0 no error, 1 repaired error, -1 no way... + * + * @note: Alsways 512 byte of data + */ +static int s3c2410_nand_correct_data(struct mtd_info *mtd, uint8_t *dat, + uint8_t *read_ecc, uint8_t *calc_ecc) +{ + unsigned int diff0, diff1, diff2; + unsigned int bit, byte; + + diff0 = read_ecc[0] ^ calc_ecc[0]; + diff1 = read_ecc[1] ^ calc_ecc[1]; + diff2 = read_ecc[2] ^ calc_ecc[2]; + + if (diff0 == 0 && diff1 == 0 && diff2 == 0) + return 0; /* ECC is ok */ + + /* sometimes people do not think about using the ECC, so check + * to see if we have an 0xff,0xff,0xff read ECC and then ignore + * the error, on the assumption that this is an un-eccd page. + */ + if (read_ecc[0] == 0xff && read_ecc[1] == 0xff && read_ecc[2] == 0xff + /* && info->platform->ignore_unset_ecc */) + return 0; + + /* Can we correct this ECC (ie, one row and column change). + * Note, this is similar to the 256 error code on smartmedia */ + + if (((diff0 ^ (diff0 >> 1)) & 0x55) == 0x55 && + ((diff1 ^ (diff1 >> 1)) & 0x55) == 0x55 && + ((diff2 ^ (diff2 >> 1)) & 0x55) == 0x55) { + /* calculate the bit position of the error */ + + bit = ((diff2 >> 3) & 1) | + ((diff2 >> 4) & 2) | + ((diff2 >> 5) & 4); + + /* calculate the byte position of the error */ + + byte = ((diff2 << 7) & 0x100) | + ((diff1 << 0) & 0x80) | + ((diff1 << 1) & 0x40) | + ((diff1 << 2) & 0x20) | + ((diff1 << 3) & 0x10) | + ((diff0 >> 4) & 0x08) | + ((diff0 >> 3) & 0x04) | + ((diff0 >> 2) & 0x02) | + ((diff0 >> 1) & 0x01); + + dat[byte] ^= (1 << bit); + return 1; + } + + /* if there is only one bit difference in the ECC, then + * one of only a row or column parity has changed, which + * means the error is most probably in the ECC itself */ + + diff0 |= (diff1 << 8); + diff0 |= (diff2 << 16); + + if ((diff0 & ~(1<<fls(diff0))) == 0) + return 1; + + return -1; +} + +static void s3c2410_nand_enable_hwecc(struct mtd_info *mtd, int mode) +{ + struct nand_chip *nand_chip = mtd->priv; + struct s3c24x0_nand_host *host = nand_chip->priv; + + writel(readl(host->base + NFCONF) | NFCONF_INITECC , host->base + NFCONF); +} + +static int s3c2410_nand_calculate_ecc(struct mtd_info *mtd, const uint8_t *dat, uint8_t *ecc_code) +{ + struct nand_chip *nand_chip = mtd->priv; + struct s3c24x0_nand_host *host = nand_chip->priv; + + ecc_code[0] = readb(host->base + NFECC); + ecc_code[1] = readb(host->base + NFECC + 1); + ecc_code[2] = readb(host->base + NFECC + 2); + + return 0; +} + +static void s3c24x0_nand_select_chip(struct mtd_info *mtd, int chip) +{ + struct nand_chip *nand_chip = mtd->priv; + struct s3c24x0_nand_host *host = nand_chip->priv; + + if (chip == -1) + disable_cs(host->base); + else + enable_cs(host->base); +} + +static int s3c24x0_nand_devready(struct mtd_info *mtd) +{ + struct nand_chip *nand_chip = mtd->priv; + struct s3c24x0_nand_host *host = nand_chip->priv; + + return readw(host->base + NFSTAT) & NFSTAT_BUSY; +} + +static void s3c24x0_nand_hwcontrol(struct mtd_info *mtd, int cmd, + unsigned int ctrl) +{ + struct nand_chip *nand_chip = mtd->priv; + struct s3c24x0_nand_host *host = nand_chip->priv; + + if (cmd == NAND_CMD_NONE) + return; + /* + * If the CLE should be active, this call is a NAND command + */ + if (ctrl & NAND_CLE) + send_cmd(host->base, cmd); + /* + * If the ALE should be active, this call is a NAND address + */ + if (ctrl & NAND_ALE) + send_addr(host->base, cmd); +} + +static int s3c24x0_nand_inithw(struct s3c24x0_nand_host *host) +{ + struct s3c24x0_nand_platform_data *pdata = host->dev->platform_data; + uint32_t tmp; + + /* reset the NAND controller */ + disable_nand_controller(host->base); + + if (pdata != NULL) + tmp = pdata->nand_timing; + else + /* else slowest possible timing */ + tmp = CALC_NFCONF_TIMING(4, 8, 8); + + /* reenable the NAND controller */ + enable_nand_controller(host->base, tmp); + + return 0; +} + +static int s3c24x0_nand_probe(struct device_d *dev) +{ + struct nand_chip *chip; + struct mtd_info *mtd; + struct s3c24x0_nand_host *host; + int ret; + + /* Allocate memory for MTD device structure and private data */ + host = kzalloc(sizeof(struct s3c24x0_nand_host), GFP_KERNEL); + if (!host) + return -ENOMEM; + + host->dev = dev; + host->base = dev->map_base; + + /* structures must be linked */ + chip = &host->nand; + mtd = &host->mtd; + mtd->priv = chip; + + /* init the default settings */ +#if 0 + /* TODO: Will follow later */ + init_nand_chip_bw8(chip); +#endif + /* 50 us command delay time */ + chip->chip_delay = 50; + chip->priv = host; + + chip->IO_ADDR_R = chip->IO_ADDR_W = (void*)(dev->map_base + NFDATA); + + chip->cmd_ctrl = s3c24x0_nand_hwcontrol; + chip->dev_ready = s3c24x0_nand_devready; + chip->select_chip = s3c24x0_nand_select_chip; + + /* we are using the hardware ECC feature of this device */ + chip->ecc.calculate = s3c2410_nand_calculate_ecc; + chip->ecc.correct = s3c2410_nand_correct_data; + chip->ecc.hwctl = s3c2410_nand_enable_hwecc; + chip->ecc.calculate = s3c2410_nand_calculate_ecc; + + /* our hardware capabilities */ + chip->ecc.mode = NAND_ECC_HW; + chip->ecc.size = 512; + chip->ecc.bytes = 3; + chip->ecc.layout = &nand_hw_eccoob; + + ret = s3c24x0_nand_inithw(host); + if (ret != 0) + goto on_error; + + /* Scan to find existence of the device */ + ret = nand_scan(mtd, 1); + if (ret != 0) { + ret = -ENXIO; + goto on_error; + } + + return add_mtd_device(mtd); + +on_error: + free(host); + return ret; +} + +static struct driver_d s3c24x0_nand_driver = { + .name = "s3c24x0_nand", + .probe = s3c24x0_nand_probe, +}; + +#ifdef CONFIG_S3C24XX_NAND_BOOT + +static void __nand_boot_init wait_for_completion(unsigned long host) +{ + while (!(readw(host + NFSTAT) & NFSTAT_BUSY)) + ; +} + +static void __nand_boot_init nfc_addr(unsigned long host, uint32_t offs) +{ + send_addr(host, offs & 0xff); + send_addr(host, (offs >> 9) & 0xff); + send_addr(host, (offs >> 17) & 0xff); + send_addr(host, (offs >> 25) & 0xff); +} + +/** + * Load a sequential count of blocks from the NAND into memory + * @param[out] dest Pointer to target area (in SDRAM) + * @param[in] size Bytes to read from NAND device + * @param[in] page Start page to read from + * @param[in] pagesize Size of each page in the NAND + * + * This function must be located in the first 4kiB of the U-Boot-v2 image + * (guess why). When this routine is running the SDRAM is up and running + * and it runs from the correct address (physical=linked address). + * TODO Could we access the platform data from the boardfile? + * Due to it makes no sense this function does not return in case of failure. + */ +void __nand_boot_init s3c24x0_nand_load_image(void *dest, int size, int page, int pagesize) +{ + unsigned long host = S3C24X0_NAND_BASE; + int i; + + /* + * Reenable the NFC and use the default (but slow) access + * timing or the board specific setting if provided. + */ + enable_nand_controller(host, BOARD_DEFAULT_NAND_TIMING); + enable_cs(host); + + /* Reset the NAND device */ + send_cmd(host, NAND_CMD_RESET); + wait_for_completion(host); + disable_cs(host); + + do { + enable_cs(host); + send_cmd(host, NAND_CMD_READ0); + nfc_addr(host, page * pagesize); + wait_for_completion(host); + /* copy one page (do *not* use readsb() here!)*/ + for (i = 0; i < pagesize; i++) + writeb(readb(host + NFDATA), (unsigned long)(dest + i)); + disable_cs(host); + + page++; + dest += pagesize; + size -= pagesize; + } while (size >= 0); + + /* disable the controller again */ + disable_nand_controller(host); +} + +#ifdef CONFIG_NAND_S3C24XX_BOOT_DEBUG +#include <command.h> + +static int do_nand_boot_test(cmd_tbl_t *cmdtp, int argc, char *argv[]) +{ + void *dest; + int size, pagesize; + + if (argc < 3) { + u_boot_cmd_usage(cmdtp); + return 1; + } + + dest = (void *)strtoul_suffix(argv[1], NULL, 0); + size = strtoul_suffix(argv[2], NULL, 0); + pagesize = strtoul_suffix(argv[3], NULL, 0); + + s3c24x0_nand_load_image(dest, size, 0, pagesize); + + return 0; +} + +static const __maybe_unused char cmd_nand_boot_test_help[] = +"Usage: nand_boot_test <dest> <size> <pagesize>\n"; + +U_BOOT_CMD_START(nand_boot_test) + .maxargs = CONFIG_MAXARGS, + .cmd = do_nand_boot_test, + .usage = "load an image from NAND", + U_BOOT_CMD_HELP(cmd_nand_boot_test_help) +U_BOOT_CMD_END +#endif + +#endif /* CONFIG_S3C24XX_NAND_BOOT */ + +/* + * Main initialization routine + * @return 0 if successful; non-zero otherwise + */ +static int __init s3c24x0_nand_init(void) +{ + return register_driver(&s3c24x0_nand_driver); +} + +device_initcall(s3c24x0_nand_init); diff --git a/drivers/serial/Kconfig b/drivers/serial/Kconfig index f6905b2afd..6acce19630 100644 --- a/drivers/serial/Kconfig +++ b/drivers/serial/Kconfig @@ -42,4 +42,19 @@ config DRIVER_SERIAL_NS16550_OMAP_EXTENSIONS depends on ARCH_OMAP help Say Y here if you are using OMAP extensions to NS16550 + +config DRIVER_SERIAL_S3C24X0 + bool "Samsung S3C24X0 serial driver" + depends on ARCH_S3C24xx + default y + help + Say Y here if you want to use the CONS on a S3C24X0 CPU + +config DRIVER_SERIAL_S3C24X0_AUTOSYNC + bool "Enable auto flow" + depends on DRIVER_SERIAL_S3C24X0 + help + Say Y here if you want to use the auto flow feature of this + UART. RTS and CTS will be handled by the hardware when enabled. + endmenu diff --git a/drivers/serial/Makefile b/drivers/serial/Makefile index fc1e891739..3bc965c6e3 100644 --- a/drivers/serial/Makefile +++ b/drivers/serial/Makefile @@ -12,3 +12,4 @@ obj-$(CONFIG_DRIVER_SERIAL_LINUX_COMSOLE) += linux_console.o obj-$(CONFIG_DRIVER_SERIAL_MPC5XXX) += serial_mpc5xxx.o obj-$(CONFIG_DRIVER_SERIAL_BLACKFIN) += serial_blackfin.o obj-$(CONFIG_DRIVER_SERIAL_NS16550) += serial_ns16550.o +obj-$(CONFIG_DRIVER_SERIAL_S3C24X0) += serial_s3c24x0.o diff --git a/drivers/serial/serial_s3c24x0.c b/drivers/serial/serial_s3c24x0.c new file mode 100644 index 0000000000..43a4cd2d47 --- /dev/null +++ b/drivers/serial/serial_s3c24x0.c @@ -0,0 +1,165 @@ +/* + * (c) 2009 Juergen Beisert <j.beisert@saschahauer.de> + * + * Based on code from: + * (c) 2004 Sascha Hauer <sascha@saschahauer.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 <driver.h> +#include <init.h> +#include <malloc.h> +#include <asm/io.h> +#include <asm/arch/s3c24xx-generic.h> +#include <asm/arch/s3c24x0-iomap.h> + +/* Note: Offsets are for little endian access */ +#define ULCON 0x00 /* line control */ +#define UCON 0x04 /* UART control */ +#define UFCON 0x08 /* FIFO control */ +#define UMCON 0x0c /* modem control */ +#define UTRSTAT 0x10 /* Rx/Tx status */ +#define UERSTAT 0x14 /* error status */ +#define UFSTAT 0x18 /* FIFO status */ +#define UMSTAT 0x1c /* modem status */ +#define UTXH 0x20 /* transmitt */ +#define URXH 0x24 /* receive */ +#define UBRDIV 0x28 /* baudrate generator */ + +static int s3c24x0_serial_setbaudrate(struct console_device *cdev, int baudrate) +{ + struct device_d *dev = cdev->dev; + unsigned val; + + /* value is calculated so : PCLK / (16 * baudrate) -1 */ + val = s3c24xx_get_pclk() / (16 * baudrate) - 1; + writew(val, dev->map_base + UBRDIV); + + return 0; +} + +static int s3c24x0_serial_init_port(struct console_device *cdev) +{ + struct device_d *dev = cdev->dev; + + /* FIFO enable, Tx/Rx FIFO clear */ + writeb(0x07, dev->map_base + UFCON); + writeb(0x00, dev->map_base + UMCON); + + /* Normal,No parity,1 stop,8 bit */ + writeb(0x03, dev->map_base + ULCON); + /* + * tx=level,rx=edge,disable timeout int.,enable rx error int., + * normal,interrupt or polling + */ + writew(0x0245, dev->map_base + UCON); + +#ifdef CONFIG_DRIVER_SERIAL_S3C24X0_AUTOSYNC + writeb(0x01, dev->map_base + UMCON); /* RTS up */ +#endif + + return 0; +} + +static void s3c24x0_serial_putc(struct console_device *cdev, char c) +{ + struct device_d *dev = cdev->dev; + + /* Wait for Tx FIFO not full */ + while (!(readb(dev->map_base + UTRSTAT) & 0x2)) + ; + + writeb(c, dev->map_base + UTXH); +} + +static int s3c24x0_serial_tstc(struct console_device *cdev) +{ + struct device_d *dev = cdev->dev; + + /* If receive fifo is empty, return false */ + if (readb(dev->map_base + UTRSTAT) & 0x1) + return 1; + + return 0; +} + +static int s3c24x0_serial_getc(struct console_device *cdev) +{ + struct device_d *dev = cdev->dev; + + /* wait for a character */ + while (!(readb(dev->map_base + UTRSTAT) & 0x1)) + ; + + return readb(dev->map_base + URXH); +} + +static void s3c24x0_serial_flush(struct console_device *cdev) +{ + struct device_d *dev = cdev->dev; + + while (!readb(dev->map_base + UTRSTAT) & 0x4) + ; +} + +static int s3c24x0_serial_probe(struct device_d *dev) +{ + struct console_device *cdev; + + cdev = malloc(sizeof(struct console_device)); + + dev->type_data = cdev; + cdev->dev = dev; + cdev->f_caps = CONSOLE_STDIN | CONSOLE_STDOUT | CONSOLE_STDERR; + cdev->tstc = s3c24x0_serial_tstc; + cdev->putc = s3c24x0_serial_putc; + cdev->getc = s3c24x0_serial_getc; + cdev->flush = s3c24x0_serial_flush; + cdev->setbrg = s3c24x0_serial_setbaudrate; + + s3c24x0_serial_init_port(cdev); + + /* Enable UART */ + console_register(cdev); + + return 0; +} + +static void s3c24x0_serial_remove(struct device_d *dev) +{ + struct console_device *cdev = dev->type_data; + + s3c24x0_serial_flush(cdev); + free(cdev); + dev->type_data = NULL; +} + +static struct driver_d s3c24x0_serial_driver = { + .name = "s3c24x0_serial", + .probe = s3c24x0_serial_probe, + .remove = s3c24x0_serial_remove, + .type = DEVICE_TYPE_CONSOLE, +}; + +static int s3c24x0_serial_init(void) +{ + register_driver(&s3c24x0_serial_driver); + return 0; +} + +console_initcall(s3c24x0_serial_init); |