1/* linux/drivers/serial/s3c6400.c 2 * 3 * Driver for Samsung S3C6400 and S3C6410 SoC onboard UARTs. 4 * 5 * Copyright 2008 Openmoko, Inc. 6 * Copyright 2008 Simtec Electronics 7 * Ben Dooks <ben@simtec.co.uk> 8 * http://armlinux.simtec.co.uk/ 9 * 10 * This program is free software; you can redistribute it and/or modify 11 * it under the terms of the GNU General Public License version 2 as 12 * published by the Free Software Foundation. 13*/ 14 15#include <linux/module.h> 16#include <linux/ioport.h> 17#include <linux/io.h> 18#include <linux/platform_device.h> 19#include <linux/init.h> 20#include <linux/serial_core.h> 21#include <linux/serial.h> 22 23#include <asm/irq.h> 24#include <mach/hardware.h> 25 26#include <plat/regs-serial.h> 27 28#include "samsung.h" 29 30static int s3c6400_serial_setsource(struct uart_port *port, 31 struct s3c24xx_uart_clksrc *clk) 32{ 33 unsigned long ucon = rd_regl(port, S3C2410_UCON); 34 35 if (strcmp(clk->name, "uclk0") == 0) { 36 ucon &= ~S3C6400_UCON_CLKMASK; 37 ucon |= S3C6400_UCON_UCLK0; 38 } else if (strcmp(clk->name, "uclk1") == 0) 39 ucon |= S3C6400_UCON_UCLK1; 40 else if (strcmp(clk->name, "pclk") == 0) { 41 /* See notes about transitioning from UCLK to PCLK */ 42 ucon &= ~S3C6400_UCON_UCLK0; 43 } else { 44 printk(KERN_ERR "unknown clock source %s\n", clk->name); 45 return -EINVAL; 46 } 47 48 wr_regl(port, S3C2410_UCON, ucon); 49 return 0; 50} 51 52 53static int s3c6400_serial_getsource(struct uart_port *port, 54 struct s3c24xx_uart_clksrc *clk) 55{ 56 u32 ucon = rd_regl(port, S3C2410_UCON); 57 58 clk->divisor = 1; 59 60 switch (ucon & S3C6400_UCON_CLKMASK) { 61 case S3C6400_UCON_UCLK0: 62 clk->name = "uclk0"; 63 break; 64 65 case S3C6400_UCON_UCLK1: 66 clk->name = "uclk1"; 67 break; 68 69 case S3C6400_UCON_PCLK: 70 case S3C6400_UCON_PCLK2: 71 clk->name = "pclk"; 72 break; 73 } 74 75 return 0; 76} 77 78static int s3c6400_serial_resetport(struct uart_port *port, 79 struct s3c2410_uartcfg *cfg) 80{ 81 unsigned long ucon = rd_regl(port, S3C2410_UCON); 82 83 dbg("s3c6400_serial_resetport: port=%p (%08lx), cfg=%p\n", 84 port, port->mapbase, cfg); 85 86 /* ensure we don't change the clock settings... */ 87 88 ucon &= S3C6400_UCON_CLKMASK; 89 90 wr_regl(port, S3C2410_UCON, ucon | cfg->ucon); 91 wr_regl(port, S3C2410_ULCON, cfg->ulcon); 92 93 /* reset both fifos */ 94 95 wr_regl(port, S3C2410_UFCON, cfg->ufcon | S3C2410_UFCON_RESETBOTH); 96 wr_regl(port, S3C2410_UFCON, cfg->ufcon); 97 98 return 0; 99} 100 101static struct s3c24xx_uart_info s3c6400_uart_inf = { 102 .name = "Samsung S3C6400 UART", 103 .type = PORT_S3C6400, 104 .fifosize = 64, 105 .has_divslot = 1, 106 .rx_fifomask = S3C2440_UFSTAT_RXMASK, 107 .rx_fifoshift = S3C2440_UFSTAT_RXSHIFT, 108 .rx_fifofull = S3C2440_UFSTAT_RXFULL, 109 .tx_fifofull = S3C2440_UFSTAT_TXFULL, 110 .tx_fifomask = S3C2440_UFSTAT_TXMASK, 111 .tx_fifoshift = S3C2440_UFSTAT_TXSHIFT, 112 .get_clksrc = s3c6400_serial_getsource, 113 .set_clksrc = s3c6400_serial_setsource, 114 .reset_port = s3c6400_serial_resetport, 115}; 116 117/* device management */ 118 119static int s3c6400_serial_probe(struct platform_device *dev) 120{ 121 dbg("s3c6400_serial_probe: dev=%p\n", dev); 122 return s3c24xx_serial_probe(dev, &s3c6400_uart_inf); 123} 124 125static struct platform_driver s3c6400_serial_driver = { 126 .probe = s3c6400_serial_probe, 127 .remove = __devexit_p(s3c24xx_serial_remove), 128 .driver = { 129 .name = "s3c6400-uart", 130 .owner = THIS_MODULE, 131 }, 132}; 133 134s3c24xx_console_init(&s3c6400_serial_driver, &s3c6400_uart_inf); 135 136static int __init s3c6400_serial_init(void) 137{ 138 return s3c24xx_serial_init(&s3c6400_serial_driver, &s3c6400_uart_inf); 139} 140 141static void __exit s3c6400_serial_exit(void) 142{ 143 platform_driver_unregister(&s3c6400_serial_driver); 144} 145 146module_init(s3c6400_serial_init); 147module_exit(s3c6400_serial_exit); 148 149MODULE_DESCRIPTION("Samsung S3C6400,S3C6410 SoC Serial port driver"); 150MODULE_AUTHOR("Ben Dooks <ben@simtec.co.uk>"); 151MODULE_LICENSE("GPL v2"); 152MODULE_ALIAS("platform:s3c6400-uart"); 153