+ res = platform_get_resource(platdev, IORESOURCE_MEM, 0);
+ if (res == NULL) {
+ printk(KERN_ERR "failed to find memory resource for uart\n");
+ return -EINVAL;
+ }
+
+ dbg("resource %p (%lx..%lx)\n", res, res->start, res->end);
+
+ port->mapbase = res->start;
+ port->membase = (void __iomem *)(res->start - S3C2410_PA_UART);
+ port->membase += S3C2410_VA_UART;
+ port->irq = platform_get_irq(platdev, 0);
+
+ ourport->clk = clk_get(&platdev->dev, "uart");
+
+ if (ourport->clk != NULL && !IS_ERR(ourport->clk))
+ clk_use(ourport->clk);
+
+ dbg("port: map=%08x, mem=%08x, irq=%d, clock=%ld\n",
+ port->mapbase, port->membase, port->irq, port->uartclk);
+
+ /* reset the fifos (and setup the uart) */
+ s3c24xx_serial_resetport(port, cfg);
+ return 0;
+}
+
+/* Device driver serial port probe */
+
+static int probe_index = 0;
+
+int s3c24xx_serial_probe(struct device *_dev,
+ struct s3c24xx_uart_info *info)
+{
+ struct s3c24xx_uart_port *ourport;
+ struct platform_device *dev = to_platform_device(_dev);
+ int ret;
+
+ dbg("s3c24xx_serial_probe(%p, %p) %d\n", _dev, info, probe_index);
+
+ ourport = &s3c24xx_serial_ports[probe_index];
+ probe_index++;
+
+ dbg("%s: initialising port %p...\n", __FUNCTION__, ourport);
+
+ ret = s3c24xx_serial_init_port(ourport, info, dev);
+ if (ret < 0)
+ goto probe_err;
+
+ dbg("%s: adding port\n", __FUNCTION__);
+ uart_add_one_port(&s3c24xx_uart_drv, &ourport->port);
+ dev_set_drvdata(_dev, &ourport->port);
+
+ return 0;
+
+ probe_err:
+ return ret;
+}
+
+int s3c24xx_serial_remove(struct device *_dev)
+{
+ struct uart_port *port = s3c24xx_dev_to_port(_dev);
+
+ if (port)
+ uart_remove_one_port(&s3c24xx_uart_drv, port);
+
+ return 0;
+}
+
+/* UART power management code */
+
+#ifdef CONFIG_PM
+
+int s3c24xx_serial_suspend(struct device *dev, u32 state, u32 level)
+{
+ struct uart_port *port = s3c24xx_dev_to_port(dev);
+
+ if (port && level == SUSPEND_DISABLE)
+ uart_suspend_port(&s3c24xx_uart_drv, port);
+
+ return 0;
+}
+
+int s3c24xx_serial_resume(struct device *dev, u32 level)
+{
+ struct uart_port *port = s3c24xx_dev_to_port(dev);
+ struct s3c24xx_uart_port *ourport = to_ourport(port);
+
+ if (port && level == RESUME_ENABLE) {
+ clk_enable(ourport->clk);
+ s3c24xx_serial_resetport(port, s3c24xx_port_to_cfg(port));
+ clk_disable(ourport->clk);
+
+ uart_resume_port(&s3c24xx_uart_drv, port);
+ }
+
+ return 0;
+}
+
+#else
+#define s3c24xx_serial_suspend NULL
+#define s3c24xx_serial_resume NULL
+#endif
+
+int s3c24xx_serial_init(struct device_driver *drv,
+ struct s3c24xx_uart_info *info)
+{
+ dbg("s3c24xx_serial_init(%p,%p)\n", drv, info);
+ return driver_register(drv);
+}
+
+
+/* now comes the code to initialise either the s3c2410 or s3c2440 serial
+ * port information
+*/
+
+/* cpu specific variations on the serial port support */
+
+#ifdef CONFIG_CPU_S3C2410
+
+static int s3c2410_serial_setsource(struct uart_port *port,
+ struct s3c24xx_uart_clksrc *clk)
+{
+ unsigned long ucon = rd_regl(port, S3C2410_UCON);
+
+ if (strcmp(clk->name, "uclk") == 0)
+ ucon |= S3C2410_UCON_UCLK;
+ else
+ ucon &= ~S3C2410_UCON_UCLK;
+
+ wr_regl(port, S3C2410_UCON, ucon);
+ return 0;
+}
+
+static int s3c2410_serial_getsource(struct uart_port *port,
+ struct s3c24xx_uart_clksrc *clk)
+{
+ unsigned long ucon = rd_regl(port, S3C2410_UCON);
+
+ clk->divisor = 1;
+ clk->name = (ucon & S3C2410_UCON_UCLK) ? "uclk" : "pclk";
+
+ return 0;
+}
+
+static struct s3c24xx_uart_info s3c2410_uart_inf = {
+ .name = "Samsung S3C2410 UART",
+ .type = PORT_S3C2410,
+ .fifosize = 16,
+ .rx_fifomask = S3C2410_UFSTAT_RXMASK,
+ .rx_fifoshift = S3C2410_UFSTAT_RXSHIFT,
+ .rx_fifofull = S3C2410_UFSTAT_RXFULL,
+ .tx_fifofull = S3C2410_UFSTAT_TXFULL,
+ .tx_fifomask = S3C2410_UFSTAT_TXMASK,
+ .tx_fifoshift = S3C2410_UFSTAT_TXSHIFT,
+ .get_clksrc = s3c2410_serial_getsource,
+ .set_clksrc = s3c2410_serial_setsource,
+};
+
+/* device management */
+
+static int s3c2410_serial_probe(struct device *dev)
+{
+ return s3c24xx_serial_probe(dev, &s3c2410_uart_inf);
+}
+
+static struct device_driver s3c2410_serial_drv = {
+ .name = "s3c2410-uart",
+ .bus = &platform_bus_type,
+ .probe = s3c2410_serial_probe,
+ .remove = s3c24xx_serial_remove,
+ .suspend = s3c24xx_serial_suspend,
+ .resume = s3c24xx_serial_resume,
+};
+
+static inline int s3c2410_serial_init(void)
+{
+ return s3c24xx_serial_init(&s3c2410_serial_drv, &s3c2410_uart_inf);
+}
+
+static inline void s3c2410_serial_exit(void)
+{
+ driver_unregister(&s3c2410_serial_drv);
+}
+
+#define s3c2410_uart_inf_at &s3c2410_uart_inf
+#else
+
+static inline int s3c2410_serial_init(void)
+{
+ return 0;
+}
+
+static inline void s3c2410_serial_exit(void)
+{
+}
+
+#define s3c2410_uart_inf_at NULL
+
+#endif /* CONFIG_CPU_S3C2410 */
+
+#ifdef CONFIG_CPU_S3C2440
+
+static int s3c2440_serial_setsource(struct uart_port *port,
+ struct s3c24xx_uart_clksrc *clk)
+{
+ unsigned long ucon = rd_regl(port, S3C2410_UCON);
+
+ // todo - proper fclk<>nonfclk switch //
+
+ ucon &= ~S3C2440_UCON_CLKMASK;
+
+ if (strcmp(clk->name, "uclk") == 0)
+ ucon |= S3C2440_UCON_UCLK;
+ else if (strcmp(clk->name, "pclk") == 0)
+ ucon |= S3C2440_UCON_PCLK;
+ else if (strcmp(clk->name, "fclk") == 0)
+ ucon |= S3C2440_UCON_FCLK;
+ else {
+ printk(KERN_ERR "unknown clock source %s\n", clk->name);
+ return -EINVAL;
+ }
+
+ wr_regl(port, S3C2410_UCON, ucon);
+ return 0;
+}
+
+
+static int s3c2440_serial_getsource(struct uart_port *port,
+ struct s3c24xx_uart_clksrc *clk)
+{
+ unsigned long ucon = rd_regl(port, S3C2410_UCON);
+
+ switch (ucon & S3C2440_UCON_CLKMASK) {
+ case S3C2440_UCON_UCLK:
+ clk->divisor = 1;
+ clk->name = "uclk";
+ break;