|
@@ -42,6 +42,7 @@
|
|
|
#include <linux/delay.h>
|
|
|
#include <linux/clk.h>
|
|
|
#include <linux/cpufreq.h>
|
|
|
+#include <linux/of.h>
|
|
|
|
|
|
#include <asm/irq.h>
|
|
|
|
|
@@ -49,6 +50,7 @@
|
|
|
#include <mach/map.h>
|
|
|
|
|
|
#include <plat/regs-serial.h>
|
|
|
+#include <plat/clock.h>
|
|
|
|
|
|
#include "samsung.h"
|
|
|
|
|
@@ -190,10 +192,13 @@ static inline struct s3c24xx_uart_info *s3c24xx_port_to_info(struct uart_port *p
|
|
|
|
|
|
static inline struct s3c2410_uartcfg *s3c24xx_port_to_cfg(struct uart_port *port)
|
|
|
{
|
|
|
+ struct s3c24xx_uart_port *ourport;
|
|
|
+
|
|
|
if (port->dev == NULL)
|
|
|
return NULL;
|
|
|
|
|
|
- return (struct s3c2410_uartcfg *)port->dev->platform_data;
|
|
|
+ ourport = container_of(port, struct s3c24xx_uart_port, port);
|
|
|
+ return ourport->cfg;
|
|
|
}
|
|
|
|
|
|
static int s3c24xx_serial_rx_fifocnt(struct s3c24xx_uart_port *ourport,
|
|
@@ -202,7 +207,7 @@ static int s3c24xx_serial_rx_fifocnt(struct s3c24xx_uart_port *ourport,
|
|
|
struct s3c24xx_uart_info *info = ourport->info;
|
|
|
|
|
|
if (ufstat & info->rx_fifofull)
|
|
|
- return info->fifosize;
|
|
|
+ return ourport->port.fifosize;
|
|
|
|
|
|
return (ufstat & info->rx_fifomask) >> info->rx_fifoshift;
|
|
|
}
|
|
@@ -555,154 +560,98 @@ static void s3c24xx_serial_pm(struct uart_port *port, unsigned int level,
|
|
|
*
|
|
|
*/
|
|
|
|
|
|
+#define MAX_CLK_NAME_LENGTH 15
|
|
|
|
|
|
-#define MAX_CLKS (8)
|
|
|
-
|
|
|
-static struct s3c24xx_uart_clksrc tmp_clksrc = {
|
|
|
- .name = "pclk",
|
|
|
- .min_baud = 0,
|
|
|
- .max_baud = 0,
|
|
|
- .divisor = 1,
|
|
|
-};
|
|
|
-
|
|
|
-static inline int
|
|
|
-s3c24xx_serial_getsource(struct uart_port *port, struct s3c24xx_uart_clksrc *c)
|
|
|
+static inline int s3c24xx_serial_getsource(struct uart_port *port)
|
|
|
{
|
|
|
struct s3c24xx_uart_info *info = s3c24xx_port_to_info(port);
|
|
|
+ unsigned int ucon;
|
|
|
|
|
|
- return (info->get_clksrc)(port, c);
|
|
|
-}
|
|
|
-
|
|
|
-static inline int
|
|
|
-s3c24xx_serial_setsource(struct uart_port *port, struct s3c24xx_uart_clksrc *c)
|
|
|
-{
|
|
|
- struct s3c24xx_uart_info *info = s3c24xx_port_to_info(port);
|
|
|
+ if (info->num_clks == 1)
|
|
|
+ return 0;
|
|
|
|
|
|
- return (info->set_clksrc)(port, c);
|
|
|
+ ucon = rd_regl(port, S3C2410_UCON);
|
|
|
+ ucon &= info->clksel_mask;
|
|
|
+ return ucon >> info->clksel_shift;
|
|
|
}
|
|
|
|
|
|
-struct baud_calc {
|
|
|
- struct s3c24xx_uart_clksrc *clksrc;
|
|
|
- unsigned int calc;
|
|
|
- unsigned int divslot;
|
|
|
- unsigned int quot;
|
|
|
- struct clk *src;
|
|
|
-};
|
|
|
-
|
|
|
-static int s3c24xx_serial_calcbaud(struct baud_calc *calc,
|
|
|
- struct uart_port *port,
|
|
|
- struct s3c24xx_uart_clksrc *clksrc,
|
|
|
- unsigned int baud)
|
|
|
+static void s3c24xx_serial_setsource(struct uart_port *port,
|
|
|
+ unsigned int clk_sel)
|
|
|
{
|
|
|
- struct s3c24xx_uart_port *ourport = to_ourport(port);
|
|
|
- unsigned long rate;
|
|
|
-
|
|
|
- calc->src = clk_get(port->dev, clksrc->name);
|
|
|
- if (calc->src == NULL || IS_ERR(calc->src))
|
|
|
- return 0;
|
|
|
-
|
|
|
- rate = clk_get_rate(calc->src);
|
|
|
- rate /= clksrc->divisor;
|
|
|
+ struct s3c24xx_uart_info *info = s3c24xx_port_to_info(port);
|
|
|
+ unsigned int ucon;
|
|
|
|
|
|
- calc->clksrc = clksrc;
|
|
|
+ if (info->num_clks == 1)
|
|
|
+ return;
|
|
|
|
|
|
- if (ourport->info->has_divslot) {
|
|
|
- unsigned long div = rate / baud;
|
|
|
-
|
|
|
- /* The UDIVSLOT register on the newer UARTs allows us to
|
|
|
- * get a divisor adjustment of 1/16th on the baud clock.
|
|
|
- *
|
|
|
- * We don't keep the UDIVSLOT value (the 16ths we calculated
|
|
|
- * by not multiplying the baud by 16) as it is easy enough
|
|
|
- * to recalculate.
|
|
|
- */
|
|
|
-
|
|
|
- calc->quot = div / 16;
|
|
|
- calc->calc = rate / div;
|
|
|
- } else {
|
|
|
- calc->quot = (rate + (8 * baud)) / (16 * baud);
|
|
|
- calc->calc = (rate / (calc->quot * 16));
|
|
|
- }
|
|
|
+ ucon = rd_regl(port, S3C2410_UCON);
|
|
|
+ if ((ucon & info->clksel_mask) >> info->clksel_shift == clk_sel)
|
|
|
+ return;
|
|
|
|
|
|
- calc->quot--;
|
|
|
- return 1;
|
|
|
+ ucon &= ~info->clksel_mask;
|
|
|
+ ucon |= clk_sel << info->clksel_shift;
|
|
|
+ wr_regl(port, S3C2410_UCON, ucon);
|
|
|
}
|
|
|
|
|
|
-static unsigned int s3c24xx_serial_getclk(struct uart_port *port,
|
|
|
- struct s3c24xx_uart_clksrc **clksrc,
|
|
|
- struct clk **clk,
|
|
|
- unsigned int baud)
|
|
|
+static unsigned int s3c24xx_serial_getclk(struct s3c24xx_uart_port *ourport,
|
|
|
+ unsigned int req_baud, struct clk **best_clk,
|
|
|
+ unsigned int *clk_num)
|
|
|
{
|
|
|
- struct s3c2410_uartcfg *cfg = s3c24xx_port_to_cfg(port);
|
|
|
- struct s3c24xx_uart_clksrc *clkp;
|
|
|
- struct baud_calc res[MAX_CLKS];
|
|
|
- struct baud_calc *resptr, *best, *sptr;
|
|
|
- int i;
|
|
|
-
|
|
|
- clkp = cfg->clocks;
|
|
|
- best = NULL;
|
|
|
-
|
|
|
- if (cfg->clocks_size < 2) {
|
|
|
- if (cfg->clocks_size == 0)
|
|
|
- clkp = &tmp_clksrc;
|
|
|
-
|
|
|
- /* check to see if we're sourcing fclk, and if so we're
|
|
|
- * going to have to update the clock source
|
|
|
- */
|
|
|
-
|
|
|
- if (strcmp(clkp->name, "fclk") == 0) {
|
|
|
- struct s3c24xx_uart_clksrc src;
|
|
|
-
|
|
|
- s3c24xx_serial_getsource(port, &src);
|
|
|
-
|
|
|
- /* check that the port already using fclk, and if
|
|
|
- * not, then re-select fclk
|
|
|
+ struct s3c24xx_uart_info *info = ourport->info;
|
|
|
+ struct clk *clk;
|
|
|
+ unsigned long rate;
|
|
|
+ unsigned int cnt, baud, quot, clk_sel, best_quot = 0;
|
|
|
+ char clkname[MAX_CLK_NAME_LENGTH];
|
|
|
+ int calc_deviation, deviation = (1 << 30) - 1;
|
|
|
+
|
|
|
+ *best_clk = NULL;
|
|
|
+ clk_sel = (ourport->cfg->clk_sel) ? ourport->cfg->clk_sel :
|
|
|
+ ourport->info->def_clk_sel;
|
|
|
+ for (cnt = 0; cnt < info->num_clks; cnt++) {
|
|
|
+ if (!(clk_sel & (1 << cnt)))
|
|
|
+ continue;
|
|
|
+
|
|
|
+ sprintf(clkname, "clk_uart_baud%d", cnt);
|
|
|
+ clk = clk_get(ourport->port.dev, clkname);
|
|
|
+ if (IS_ERR_OR_NULL(clk))
|
|
|
+ continue;
|
|
|
+
|
|
|
+ rate = clk_get_rate(clk);
|
|
|
+ if (!rate)
|
|
|
+ continue;
|
|
|
+
|
|
|
+ if (ourport->info->has_divslot) {
|
|
|
+ unsigned long div = rate / req_baud;
|
|
|
+
|
|
|
+ /* The UDIVSLOT register on the newer UARTs allows us to
|
|
|
+ * get a divisor adjustment of 1/16th on the baud clock.
|
|
|
+ *
|
|
|
+ * We don't keep the UDIVSLOT value (the 16ths we
|
|
|
+ * calculated by not multiplying the baud by 16) as it
|
|
|
+ * is easy enough to recalculate.
|
|
|
*/
|
|
|
|
|
|
- if (strcmp(src.name, clkp->name) == 0) {
|
|
|
- s3c24xx_serial_setsource(port, clkp);
|
|
|
- s3c24xx_serial_getsource(port, &src);
|
|
|
- }
|
|
|
-
|
|
|
- clkp->divisor = src.divisor;
|
|
|
- }
|
|
|
-
|
|
|
- s3c24xx_serial_calcbaud(res, port, clkp, baud);
|
|
|
- best = res;
|
|
|
- resptr = best + 1;
|
|
|
- } else {
|
|
|
- resptr = res;
|
|
|
-
|
|
|
- for (i = 0; i < cfg->clocks_size; i++, clkp++) {
|
|
|
- if (s3c24xx_serial_calcbaud(resptr, port, clkp, baud))
|
|
|
- resptr++;
|
|
|
+ quot = div / 16;
|
|
|
+ baud = rate / div;
|
|
|
+ } else {
|
|
|
+ quot = (rate + (8 * req_baud)) / (16 * req_baud);
|
|
|
+ baud = rate / (quot * 16);
|
|
|
}
|
|
|
- }
|
|
|
-
|
|
|
- /* ok, we now need to select the best clock we found */
|
|
|
-
|
|
|
- if (!best) {
|
|
|
- unsigned int deviation = (1<<30)|((1<<30)-1);
|
|
|
- int calc_deviation;
|
|
|
+ quot--;
|
|
|
|
|
|
- for (sptr = res; sptr < resptr; sptr++) {
|
|
|
- calc_deviation = baud - sptr->calc;
|
|
|
- if (calc_deviation < 0)
|
|
|
- calc_deviation = -calc_deviation;
|
|
|
+ calc_deviation = req_baud - baud;
|
|
|
+ if (calc_deviation < 0)
|
|
|
+ calc_deviation = -calc_deviation;
|
|
|
|
|
|
- if (calc_deviation < deviation) {
|
|
|
- best = sptr;
|
|
|
- deviation = calc_deviation;
|
|
|
- }
|
|
|
+ if (calc_deviation < deviation) {
|
|
|
+ *best_clk = clk;
|
|
|
+ best_quot = quot;
|
|
|
+ *clk_num = cnt;
|
|
|
+ deviation = calc_deviation;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- /* store results to pass back */
|
|
|
-
|
|
|
- *clksrc = best->clksrc;
|
|
|
- *clk = best->src;
|
|
|
-
|
|
|
- return best->quot;
|
|
|
+ return best_quot;
|
|
|
}
|
|
|
|
|
|
/* udivslot_table[]
|
|
@@ -735,10 +684,9 @@ static void s3c24xx_serial_set_termios(struct uart_port *port,
|
|
|
{
|
|
|
struct s3c2410_uartcfg *cfg = s3c24xx_port_to_cfg(port);
|
|
|
struct s3c24xx_uart_port *ourport = to_ourport(port);
|
|
|
- struct s3c24xx_uart_clksrc *clksrc = NULL;
|
|
|
struct clk *clk = NULL;
|
|
|
unsigned long flags;
|
|
|
- unsigned int baud, quot;
|
|
|
+ unsigned int baud, quot, clk_sel = 0;
|
|
|
unsigned int ulcon;
|
|
|
unsigned int umcon;
|
|
|
unsigned int udivslot = 0;
|
|
@@ -754,17 +702,16 @@ static void s3c24xx_serial_set_termios(struct uart_port *port,
|
|
|
*/
|
|
|
|
|
|
baud = uart_get_baud_rate(port, termios, old, 0, 115200*8);
|
|
|
-
|
|
|
+ quot = s3c24xx_serial_getclk(ourport, baud, &clk, &clk_sel);
|
|
|
if (baud == 38400 && (port->flags & UPF_SPD_MASK) == UPF_SPD_CUST)
|
|
|
quot = port->custom_divisor;
|
|
|
- else
|
|
|
- quot = s3c24xx_serial_getclk(port, &clksrc, &clk, baud);
|
|
|
+ if (!clk)
|
|
|
+ return;
|
|
|
|
|
|
/* check to see if we need to change clock source */
|
|
|
|
|
|
- if (ourport->clksrc != clksrc || ourport->baudclk != clk) {
|
|
|
- dbg("selecting clock %p\n", clk);
|
|
|
- s3c24xx_serial_setsource(port, clksrc);
|
|
|
+ if (ourport->baudclk != clk) {
|
|
|
+ s3c24xx_serial_setsource(port, clk_sel);
|
|
|
|
|
|
if (ourport->baudclk != NULL && !IS_ERR(ourport->baudclk)) {
|
|
|
clk_disable(ourport->baudclk);
|
|
@@ -773,7 +720,6 @@ static void s3c24xx_serial_set_termios(struct uart_port *port,
|
|
|
|
|
|
clk_enable(clk);
|
|
|
|
|
|
- ourport->clksrc = clksrc;
|
|
|
ourport->baudclk = clk;
|
|
|
ourport->baudclk_rate = clk ? clk_get_rate(clk) : 0;
|
|
|
}
|
|
@@ -1020,16 +966,29 @@ static struct s3c24xx_uart_port s3c24xx_serial_ports[CONFIG_SERIAL_SAMSUNG_UARTS
|
|
|
|
|
|
/* s3c24xx_serial_resetport
|
|
|
*
|
|
|
- * wrapper to call the specific reset for this port (reset the fifos
|
|
|
- * and the settings)
|
|
|
+ * reset the fifos and other the settings.
|
|
|
*/
|
|
|
|
|
|
-static inline int s3c24xx_serial_resetport(struct uart_port *port,
|
|
|
- struct s3c2410_uartcfg *cfg)
|
|
|
+static void s3c24xx_serial_resetport(struct uart_port *port,
|
|
|
+ struct s3c2410_uartcfg *cfg)
|
|
|
{
|
|
|
struct s3c24xx_uart_info *info = s3c24xx_port_to_info(port);
|
|
|
+ unsigned long ucon = rd_regl(port, S3C2410_UCON);
|
|
|
+ unsigned int ucon_mask;
|
|
|
+
|
|
|
+ ucon_mask = info->clksel_mask;
|
|
|
+ if (info->type == PORT_S3C2440)
|
|
|
+ ucon_mask |= S3C2440_UCON0_DIVMASK;
|
|
|
|
|
|
- return (info->reset_port)(port, cfg);
|
|
|
+ ucon &= ucon_mask;
|
|
|
+ wr_regl(port, S3C2410_UCON, ucon | cfg->ucon);
|
|
|
+
|
|
|
+ /* reset both fifos */
|
|
|
+ wr_regl(port, S3C2410_UFCON, cfg->ufcon | S3C2410_UFCON_RESETBOTH);
|
|
|
+ wr_regl(port, S3C2410_UFCON, cfg->ufcon);
|
|
|
+
|
|
|
+ /* some delay is required after fifo reset */
|
|
|
+ udelay(1);
|
|
|
}
|
|
|
|
|
|
|
|
@@ -1121,11 +1080,10 @@ static inline void s3c24xx_serial_cpufreq_deregister(struct s3c24xx_uart_port *p
|
|
|
*/
|
|
|
|
|
|
static int s3c24xx_serial_init_port(struct s3c24xx_uart_port *ourport,
|
|
|
- struct s3c24xx_uart_info *info,
|
|
|
struct platform_device *platdev)
|
|
|
{
|
|
|
struct uart_port *port = &ourport->port;
|
|
|
- struct s3c2410_uartcfg *cfg;
|
|
|
+ struct s3c2410_uartcfg *cfg = ourport->cfg;
|
|
|
struct resource *res;
|
|
|
int ret;
|
|
|
|
|
@@ -1134,30 +1092,16 @@ static int s3c24xx_serial_init_port(struct s3c24xx_uart_port *ourport,
|
|
|
if (platdev == NULL)
|
|
|
return -ENODEV;
|
|
|
|
|
|
- cfg = s3c24xx_dev_to_cfg(&platdev->dev);
|
|
|
-
|
|
|
if (port->mapbase != 0)
|
|
|
return 0;
|
|
|
|
|
|
- if (cfg->hwport > CONFIG_SERIAL_SAMSUNG_UARTS) {
|
|
|
- printk(KERN_ERR "%s: port %d bigger than %d\n", __func__,
|
|
|
- cfg->hwport, CONFIG_SERIAL_SAMSUNG_UARTS);
|
|
|
- return -ERANGE;
|
|
|
- }
|
|
|
-
|
|
|
/* setup info for port */
|
|
|
port->dev = &platdev->dev;
|
|
|
- ourport->info = info;
|
|
|
|
|
|
/* Startup sequence is different for s3c64xx and higher SoC's */
|
|
|
if (s3c24xx_serial_has_interrupt_mask(port))
|
|
|
s3c24xx_serial_ops.startup = s3c64xx_serial_startup;
|
|
|
|
|
|
- /* copy the info in from provided structure */
|
|
|
- ourport->port.fifosize = info->fifosize;
|
|
|
-
|
|
|
- dbg("s3c24xx_serial_init_port: %p (hw %d)...\n", port, cfg->hwport);
|
|
|
-
|
|
|
port->uartclk = 1;
|
|
|
|
|
|
if (cfg->uart_flags & UPF_CONS_FLOW) {
|
|
@@ -1215,43 +1159,74 @@ static ssize_t s3c24xx_serial_show_clksrc(struct device *dev,
|
|
|
struct uart_port *port = s3c24xx_dev_to_port(dev);
|
|
|
struct s3c24xx_uart_port *ourport = to_ourport(port);
|
|
|
|
|
|
- return snprintf(buf, PAGE_SIZE, "* %s\n", ourport->clksrc->name);
|
|
|
+ return snprintf(buf, PAGE_SIZE, "* %s\n", ourport->baudclk->name);
|
|
|
}
|
|
|
|
|
|
static DEVICE_ATTR(clock_source, S_IRUGO, s3c24xx_serial_show_clksrc, NULL);
|
|
|
|
|
|
+
|
|
|
/* Device driver serial port probe */
|
|
|
|
|
|
+static const struct of_device_id s3c24xx_uart_dt_match[];
|
|
|
static int probe_index;
|
|
|
|
|
|
-int s3c24xx_serial_probe(struct platform_device *dev,
|
|
|
- struct s3c24xx_uart_info *info)
|
|
|
+static inline struct s3c24xx_serial_drv_data *s3c24xx_get_driver_data(
|
|
|
+ struct platform_device *pdev)
|
|
|
+{
|
|
|
+#ifdef CONFIG_OF
|
|
|
+ if (pdev->dev.of_node) {
|
|
|
+ const struct of_device_id *match;
|
|
|
+ match = of_match_node(s3c24xx_uart_dt_match, pdev->dev.of_node);
|
|
|
+ return (struct s3c24xx_serial_drv_data *)match->data;
|
|
|
+ }
|
|
|
+#endif
|
|
|
+ return (struct s3c24xx_serial_drv_data *)
|
|
|
+ platform_get_device_id(pdev)->driver_data;
|
|
|
+}
|
|
|
+
|
|
|
+static int s3c24xx_serial_probe(struct platform_device *pdev)
|
|
|
{
|
|
|
struct s3c24xx_uart_port *ourport;
|
|
|
int ret;
|
|
|
|
|
|
- dbg("s3c24xx_serial_probe(%p, %p) %d\n", dev, info, probe_index);
|
|
|
+ dbg("s3c24xx_serial_probe(%p) %d\n", pdev, probe_index);
|
|
|
|
|
|
ourport = &s3c24xx_serial_ports[probe_index];
|
|
|
+
|
|
|
+ ourport->drv_data = s3c24xx_get_driver_data(pdev);
|
|
|
+ if (!ourport->drv_data) {
|
|
|
+ dev_err(&pdev->dev, "could not find driver data\n");
|
|
|
+ return -ENODEV;
|
|
|
+ }
|
|
|
+
|
|
|
+ ourport->info = ourport->drv_data->info;
|
|
|
+ ourport->cfg = (pdev->dev.platform_data) ?
|
|
|
+ (struct s3c2410_uartcfg *)pdev->dev.platform_data :
|
|
|
+ ourport->drv_data->def_cfg;
|
|
|
+
|
|
|
+ ourport->port.fifosize = (ourport->info->fifosize) ?
|
|
|
+ ourport->info->fifosize :
|
|
|
+ ourport->drv_data->fifosize[probe_index];
|
|
|
+
|
|
|
probe_index++;
|
|
|
|
|
|
dbg("%s: initialising port %p...\n", __func__, ourport);
|
|
|
|
|
|
- ret = s3c24xx_serial_init_port(ourport, info, dev);
|
|
|
+ ret = s3c24xx_serial_init_port(ourport, pdev);
|
|
|
if (ret < 0)
|
|
|
goto probe_err;
|
|
|
|
|
|
dbg("%s: adding port\n", __func__);
|
|
|
uart_add_one_port(&s3c24xx_uart_drv, &ourport->port);
|
|
|
- platform_set_drvdata(dev, &ourport->port);
|
|
|
+ platform_set_drvdata(pdev, &ourport->port);
|
|
|
|
|
|
- ret = device_create_file(&dev->dev, &dev_attr_clock_source);
|
|
|
+ ret = device_create_file(&pdev->dev, &dev_attr_clock_source);
|
|
|
if (ret < 0)
|
|
|
- printk(KERN_ERR "%s: failed to add clksrc attr.\n", __func__);
|
|
|
+ dev_err(&pdev->dev, "failed to add clock source attr.\n");
|
|
|
|
|
|
ret = s3c24xx_serial_cpufreq_register(ourport);
|
|
|
if (ret < 0)
|
|
|
- dev_err(&dev->dev, "failed to add cpufreq notifier\n");
|
|
|
+ dev_err(&pdev->dev, "failed to add cpufreq notifier\n");
|
|
|
|
|
|
return 0;
|
|
|
|
|
@@ -1259,9 +1234,7 @@ int s3c24xx_serial_probe(struct platform_device *dev,
|
|
|
return ret;
|
|
|
}
|
|
|
|
|
|
-EXPORT_SYMBOL_GPL(s3c24xx_serial_probe);
|
|
|
-
|
|
|
-int __devexit s3c24xx_serial_remove(struct platform_device *dev)
|
|
|
+static int __devexit s3c24xx_serial_remove(struct platform_device *dev)
|
|
|
{
|
|
|
struct uart_port *port = s3c24xx_dev_to_port(&dev->dev);
|
|
|
|
|
@@ -1274,8 +1247,6 @@ int __devexit s3c24xx_serial_remove(struct platform_device *dev)
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
-EXPORT_SYMBOL_GPL(s3c24xx_serial_remove);
|
|
|
-
|
|
|
/* UART power management code */
|
|
|
#ifdef CONFIG_PM_SLEEP
|
|
|
static int s3c24xx_serial_suspend(struct device *dev)
|
|
@@ -1315,41 +1286,6 @@ static const struct dev_pm_ops s3c24xx_serial_pm_ops = {
|
|
|
#define SERIAL_SAMSUNG_PM_OPS NULL
|
|
|
#endif /* CONFIG_PM_SLEEP */
|
|
|
|
|
|
-int s3c24xx_serial_init(struct platform_driver *drv,
|
|
|
- struct s3c24xx_uart_info *info)
|
|
|
-{
|
|
|
- dbg("s3c24xx_serial_init(%p,%p)\n", drv, info);
|
|
|
-
|
|
|
- drv->driver.pm = SERIAL_SAMSUNG_PM_OPS;
|
|
|
-
|
|
|
- return platform_driver_register(drv);
|
|
|
-}
|
|
|
-
|
|
|
-EXPORT_SYMBOL_GPL(s3c24xx_serial_init);
|
|
|
-
|
|
|
-/* module initialisation code */
|
|
|
-
|
|
|
-static int __init s3c24xx_serial_modinit(void)
|
|
|
-{
|
|
|
- int ret;
|
|
|
-
|
|
|
- ret = uart_register_driver(&s3c24xx_uart_drv);
|
|
|
- if (ret < 0) {
|
|
|
- printk(KERN_ERR "failed to register UART driver\n");
|
|
|
- return -1;
|
|
|
- }
|
|
|
-
|
|
|
- return 0;
|
|
|
-}
|
|
|
-
|
|
|
-static void __exit s3c24xx_serial_modexit(void)
|
|
|
-{
|
|
|
- uart_unregister_driver(&s3c24xx_uart_drv);
|
|
|
-}
|
|
|
-
|
|
|
-module_init(s3c24xx_serial_modinit);
|
|
|
-module_exit(s3c24xx_serial_modexit);
|
|
|
-
|
|
|
/* Console code */
|
|
|
|
|
|
#ifdef CONFIG_SERIAL_SAMSUNG_CONSOLE
|
|
@@ -1395,12 +1331,13 @@ static void __init
|
|
|
s3c24xx_serial_get_options(struct uart_port *port, int *baud,
|
|
|
int *parity, int *bits)
|
|
|
{
|
|
|
- struct s3c24xx_uart_clksrc clksrc;
|
|
|
struct clk *clk;
|
|
|
unsigned int ulcon;
|
|
|
unsigned int ucon;
|
|
|
unsigned int ubrdiv;
|
|
|
unsigned long rate;
|
|
|
+ unsigned int clk_sel;
|
|
|
+ char clk_name[MAX_CLK_NAME_LENGTH];
|
|
|
|
|
|
ulcon = rd_regl(port, S3C2410_ULCON);
|
|
|
ucon = rd_regl(port, S3C2410_UCON);
|
|
@@ -1445,44 +1382,21 @@ s3c24xx_serial_get_options(struct uart_port *port, int *baud,
|
|
|
|
|
|
/* now calculate the baud rate */
|
|
|
|
|
|
- s3c24xx_serial_getsource(port, &clksrc);
|
|
|
+ clk_sel = s3c24xx_serial_getsource(port);
|
|
|
+ sprintf(clk_name, "clk_uart_baud%d", clk_sel);
|
|
|
|
|
|
- clk = clk_get(port->dev, clksrc.name);
|
|
|
+ clk = clk_get(port->dev, clk_name);
|
|
|
if (!IS_ERR(clk) && clk != NULL)
|
|
|
- rate = clk_get_rate(clk) / clksrc.divisor;
|
|
|
+ rate = clk_get_rate(clk);
|
|
|
else
|
|
|
rate = 1;
|
|
|
|
|
|
-
|
|
|
*baud = rate / (16 * (ubrdiv + 1));
|
|
|
dbg("calculated baud %d\n", *baud);
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
-/* s3c24xx_serial_init_ports
|
|
|
- *
|
|
|
- * initialise the serial ports from the machine provided initialisation
|
|
|
- * data.
|
|
|
-*/
|
|
|
-
|
|
|
-static int s3c24xx_serial_init_ports(struct s3c24xx_uart_info **info)
|
|
|
-{
|
|
|
- struct s3c24xx_uart_port *ptr = s3c24xx_serial_ports;
|
|
|
- struct platform_device **platdev_ptr;
|
|
|
- int i;
|
|
|
-
|
|
|
- dbg("s3c24xx_serial_init_ports: initialising ports...\n");
|
|
|
-
|
|
|
- platdev_ptr = s3c24xx_uart_devs;
|
|
|
-
|
|
|
- for (i = 0; i < CONFIG_SERIAL_SAMSUNG_UARTS; i++, ptr++, platdev_ptr++) {
|
|
|
- s3c24xx_serial_init_port(ptr, info[i], *platdev_ptr);
|
|
|
- }
|
|
|
-
|
|
|
- return 0;
|
|
|
-}
|
|
|
-
|
|
|
static int __init
|
|
|
s3c24xx_serial_console_setup(struct console *co, char *options)
|
|
|
{
|
|
@@ -1526,11 +1440,6 @@ s3c24xx_serial_console_setup(struct console *co, char *options)
|
|
|
return uart_set_options(port, co, baud, parity, bits, flow);
|
|
|
}
|
|
|
|
|
|
-/* s3c24xx_serial_initconsole
|
|
|
- *
|
|
|
- * initialise the console from one of the uart drivers
|
|
|
-*/
|
|
|
-
|
|
|
static struct console s3c24xx_serial_console = {
|
|
|
.name = S3C24XX_SERIAL_NAME,
|
|
|
.device = uart_console_device,
|
|
@@ -1540,34 +1449,250 @@ static struct console s3c24xx_serial_console = {
|
|
|
.setup = s3c24xx_serial_console_setup,
|
|
|
.data = &s3c24xx_uart_drv,
|
|
|
};
|
|
|
+#endif /* CONFIG_SERIAL_SAMSUNG_CONSOLE */
|
|
|
|
|
|
-int s3c24xx_serial_initconsole(struct platform_driver *drv,
|
|
|
- struct s3c24xx_uart_info **info)
|
|
|
+#ifdef CONFIG_CPU_S3C2410
|
|
|
+static struct s3c24xx_serial_drv_data s3c2410_serial_drv_data = {
|
|
|
+ .info = &(struct s3c24xx_uart_info) {
|
|
|
+ .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,
|
|
|
+ .def_clk_sel = S3C2410_UCON_CLKSEL0,
|
|
|
+ .num_clks = 2,
|
|
|
+ .clksel_mask = S3C2410_UCON_CLKMASK,
|
|
|
+ .clksel_shift = S3C2410_UCON_CLKSHIFT,
|
|
|
+ },
|
|
|
+ .def_cfg = &(struct s3c2410_uartcfg) {
|
|
|
+ .ucon = S3C2410_UCON_DEFAULT,
|
|
|
+ .ufcon = S3C2410_UFCON_DEFAULT,
|
|
|
+ },
|
|
|
+};
|
|
|
+#define S3C2410_SERIAL_DRV_DATA ((kernel_ulong_t)&s3c2410_serial_drv_data)
|
|
|
+#else
|
|
|
+#define S3C2410_SERIAL_DRV_DATA (kernel_ulong_t)NULL
|
|
|
+#endif
|
|
|
|
|
|
-{
|
|
|
- struct platform_device *dev = s3c24xx_uart_devs[0];
|
|
|
+#ifdef CONFIG_CPU_S3C2412
|
|
|
+static struct s3c24xx_serial_drv_data s3c2412_serial_drv_data = {
|
|
|
+ .info = &(struct s3c24xx_uart_info) {
|
|
|
+ .name = "Samsung S3C2412 UART",
|
|
|
+ .type = PORT_S3C2412,
|
|
|
+ .fifosize = 64,
|
|
|
+ .has_divslot = 1,
|
|
|
+ .rx_fifomask = S3C2440_UFSTAT_RXMASK,
|
|
|
+ .rx_fifoshift = S3C2440_UFSTAT_RXSHIFT,
|
|
|
+ .rx_fifofull = S3C2440_UFSTAT_RXFULL,
|
|
|
+ .tx_fifofull = S3C2440_UFSTAT_TXFULL,
|
|
|
+ .tx_fifomask = S3C2440_UFSTAT_TXMASK,
|
|
|
+ .tx_fifoshift = S3C2440_UFSTAT_TXSHIFT,
|
|
|
+ .def_clk_sel = S3C2410_UCON_CLKSEL2,
|
|
|
+ .num_clks = 4,
|
|
|
+ .clksel_mask = S3C2412_UCON_CLKMASK,
|
|
|
+ .clksel_shift = S3C2412_UCON_CLKSHIFT,
|
|
|
+ },
|
|
|
+ .def_cfg = &(struct s3c2410_uartcfg) {
|
|
|
+ .ucon = S3C2410_UCON_DEFAULT,
|
|
|
+ .ufcon = S3C2410_UFCON_DEFAULT,
|
|
|
+ },
|
|
|
+};
|
|
|
+#define S3C2412_SERIAL_DRV_DATA ((kernel_ulong_t)&s3c2412_serial_drv_data)
|
|
|
+#else
|
|
|
+#define S3C2412_SERIAL_DRV_DATA (kernel_ulong_t)NULL
|
|
|
+#endif
|
|
|
|
|
|
- dbg("s3c24xx_serial_initconsole\n");
|
|
|
+#if defined(CONFIG_CPU_S3C2440) || defined(CONFIG_CPU_S3C2416) || \
|
|
|
+ defined(CONFIG_CPU_S3C2443)
|
|
|
+static struct s3c24xx_serial_drv_data s3c2440_serial_drv_data = {
|
|
|
+ .info = &(struct s3c24xx_uart_info) {
|
|
|
+ .name = "Samsung S3C2440 UART",
|
|
|
+ .type = PORT_S3C2440,
|
|
|
+ .fifosize = 64,
|
|
|
+ .has_divslot = 1,
|
|
|
+ .rx_fifomask = S3C2440_UFSTAT_RXMASK,
|
|
|
+ .rx_fifoshift = S3C2440_UFSTAT_RXSHIFT,
|
|
|
+ .rx_fifofull = S3C2440_UFSTAT_RXFULL,
|
|
|
+ .tx_fifofull = S3C2440_UFSTAT_TXFULL,
|
|
|
+ .tx_fifomask = S3C2440_UFSTAT_TXMASK,
|
|
|
+ .tx_fifoshift = S3C2440_UFSTAT_TXSHIFT,
|
|
|
+ .def_clk_sel = S3C2410_UCON_CLKSEL2,
|
|
|
+ .num_clks = 4,
|
|
|
+ .clksel_mask = S3C2412_UCON_CLKMASK,
|
|
|
+ .clksel_shift = S3C2412_UCON_CLKSHIFT,
|
|
|
+ },
|
|
|
+ .def_cfg = &(struct s3c2410_uartcfg) {
|
|
|
+ .ucon = S3C2410_UCON_DEFAULT,
|
|
|
+ .ufcon = S3C2410_UFCON_DEFAULT,
|
|
|
+ },
|
|
|
+};
|
|
|
+#define S3C2440_SERIAL_DRV_DATA ((kernel_ulong_t)&s3c2440_serial_drv_data)
|
|
|
+#else
|
|
|
+#define S3C2440_SERIAL_DRV_DATA (kernel_ulong_t)NULL
|
|
|
+#endif
|
|
|
|
|
|
- /* select driver based on the cpu */
|
|
|
+#if defined(CONFIG_CPU_S3C6400) || defined(CONFIG_CPU_S3C6410) || \
|
|
|
+ defined(CONFIG_CPU_S5P6440) || defined(CONFIG_CPU_S5P6450) || \
|
|
|
+ defined(CONFIG_CPU_S5PC100)
|
|
|
+static struct s3c24xx_serial_drv_data s3c6400_serial_drv_data = {
|
|
|
+ .info = &(struct s3c24xx_uart_info) {
|
|
|
+ .name = "Samsung S3C6400 UART",
|
|
|
+ .type = PORT_S3C6400,
|
|
|
+ .fifosize = 64,
|
|
|
+ .has_divslot = 1,
|
|
|
+ .rx_fifomask = S3C2440_UFSTAT_RXMASK,
|
|
|
+ .rx_fifoshift = S3C2440_UFSTAT_RXSHIFT,
|
|
|
+ .rx_fifofull = S3C2440_UFSTAT_RXFULL,
|
|
|
+ .tx_fifofull = S3C2440_UFSTAT_TXFULL,
|
|
|
+ .tx_fifomask = S3C2440_UFSTAT_TXMASK,
|
|
|
+ .tx_fifoshift = S3C2440_UFSTAT_TXSHIFT,
|
|
|
+ .def_clk_sel = S3C2410_UCON_CLKSEL2,
|
|
|
+ .num_clks = 4,
|
|
|
+ .clksel_mask = S3C6400_UCON_CLKMASK,
|
|
|
+ .clksel_shift = S3C6400_UCON_CLKSHIFT,
|
|
|
+ },
|
|
|
+ .def_cfg = &(struct s3c2410_uartcfg) {
|
|
|
+ .ucon = S3C2410_UCON_DEFAULT,
|
|
|
+ .ufcon = S3C2410_UFCON_DEFAULT,
|
|
|
+ },
|
|
|
+};
|
|
|
+#define S3C6400_SERIAL_DRV_DATA ((kernel_ulong_t)&s3c6400_serial_drv_data)
|
|
|
+#else
|
|
|
+#define S3C6400_SERIAL_DRV_DATA (kernel_ulong_t)NULL
|
|
|
+#endif
|
|
|
|
|
|
- if (dev == NULL) {
|
|
|
- printk(KERN_ERR "s3c24xx: no devices for console init\n");
|
|
|
- return 0;
|
|
|
- }
|
|
|
+#ifdef CONFIG_CPU_S5PV210
|
|
|
+static struct s3c24xx_serial_drv_data s5pv210_serial_drv_data = {
|
|
|
+ .info = &(struct s3c24xx_uart_info) {
|
|
|
+ .name = "Samsung S5PV210 UART",
|
|
|
+ .type = PORT_S3C6400,
|
|
|
+ .has_divslot = 1,
|
|
|
+ .rx_fifomask = S5PV210_UFSTAT_RXMASK,
|
|
|
+ .rx_fifoshift = S5PV210_UFSTAT_RXSHIFT,
|
|
|
+ .rx_fifofull = S5PV210_UFSTAT_RXFULL,
|
|
|
+ .tx_fifofull = S5PV210_UFSTAT_TXFULL,
|
|
|
+ .tx_fifomask = S5PV210_UFSTAT_TXMASK,
|
|
|
+ .tx_fifoshift = S5PV210_UFSTAT_TXSHIFT,
|
|
|
+ .def_clk_sel = S3C2410_UCON_CLKSEL0,
|
|
|
+ .num_clks = 2,
|
|
|
+ .clksel_mask = S5PV210_UCON_CLKMASK,
|
|
|
+ .clksel_shift = S5PV210_UCON_CLKSHIFT,
|
|
|
+ },
|
|
|
+ .def_cfg = &(struct s3c2410_uartcfg) {
|
|
|
+ .ucon = S5PV210_UCON_DEFAULT,
|
|
|
+ .ufcon = S5PV210_UFCON_DEFAULT,
|
|
|
+ },
|
|
|
+ .fifosize = { 256, 64, 16, 16 },
|
|
|
+};
|
|
|
+#define S5PV210_SERIAL_DRV_DATA ((kernel_ulong_t)&s5pv210_serial_drv_data)
|
|
|
+#else
|
|
|
+#define S5PV210_SERIAL_DRV_DATA (kernel_ulong_t)NULL
|
|
|
+#endif
|
|
|
|
|
|
- if (strcmp(dev->name, drv->driver.name) != 0)
|
|
|
- return 0;
|
|
|
+#ifdef CONFIG_CPU_EXYNOS4210
|
|
|
+static struct s3c24xx_serial_drv_data exynos4210_serial_drv_data = {
|
|
|
+ .info = &(struct s3c24xx_uart_info) {
|
|
|
+ .name = "Samsung Exynos4 UART",
|
|
|
+ .type = PORT_S3C6400,
|
|
|
+ .has_divslot = 1,
|
|
|
+ .rx_fifomask = S5PV210_UFSTAT_RXMASK,
|
|
|
+ .rx_fifoshift = S5PV210_UFSTAT_RXSHIFT,
|
|
|
+ .rx_fifofull = S5PV210_UFSTAT_RXFULL,
|
|
|
+ .tx_fifofull = S5PV210_UFSTAT_TXFULL,
|
|
|
+ .tx_fifomask = S5PV210_UFSTAT_TXMASK,
|
|
|
+ .tx_fifoshift = S5PV210_UFSTAT_TXSHIFT,
|
|
|
+ .def_clk_sel = S3C2410_UCON_CLKSEL0,
|
|
|
+ .num_clks = 1,
|
|
|
+ .clksel_mask = 0,
|
|
|
+ .clksel_shift = 0,
|
|
|
+ },
|
|
|
+ .def_cfg = &(struct s3c2410_uartcfg) {
|
|
|
+ .ucon = S5PV210_UCON_DEFAULT,
|
|
|
+ .ufcon = S5PV210_UFCON_DEFAULT,
|
|
|
+ .has_fracval = 1,
|
|
|
+ },
|
|
|
+ .fifosize = { 256, 64, 16, 16 },
|
|
|
+};
|
|
|
+#define EXYNOS4210_SERIAL_DRV_DATA ((kernel_ulong_t)&exynos4210_serial_drv_data)
|
|
|
+#else
|
|
|
+#define EXYNOS4210_SERIAL_DRV_DATA (kernel_ulong_t)NULL
|
|
|
+#endif
|
|
|
|
|
|
- s3c24xx_serial_console.data = &s3c24xx_uart_drv;
|
|
|
- s3c24xx_serial_init_ports(info);
|
|
|
+static struct platform_device_id s3c24xx_serial_driver_ids[] = {
|
|
|
+ {
|
|
|
+ .name = "s3c2410-uart",
|
|
|
+ .driver_data = S3C2410_SERIAL_DRV_DATA,
|
|
|
+ }, {
|
|
|
+ .name = "s3c2412-uart",
|
|
|
+ .driver_data = S3C2412_SERIAL_DRV_DATA,
|
|
|
+ }, {
|
|
|
+ .name = "s3c2440-uart",
|
|
|
+ .driver_data = S3C2440_SERIAL_DRV_DATA,
|
|
|
+ }, {
|
|
|
+ .name = "s3c6400-uart",
|
|
|
+ .driver_data = S3C6400_SERIAL_DRV_DATA,
|
|
|
+ }, {
|
|
|
+ .name = "s5pv210-uart",
|
|
|
+ .driver_data = S5PV210_SERIAL_DRV_DATA,
|
|
|
+ }, {
|
|
|
+ .name = "exynos4210-uart",
|
|
|
+ .driver_data = EXYNOS4210_SERIAL_DRV_DATA,
|
|
|
+ },
|
|
|
+ { },
|
|
|
+};
|
|
|
+MODULE_DEVICE_TABLE(platform, s3c24xx_serial_driver_ids);
|
|
|
|
|
|
- register_console(&s3c24xx_serial_console);
|
|
|
- return 0;
|
|
|
+#ifdef CONFIG_OF
|
|
|
+static const struct of_device_id s3c24xx_uart_dt_match[] = {
|
|
|
+ { .compatible = "samsung,exynos4210-uart",
|
|
|
+ .data = (void *)EXYNOS4210_SERIAL_DRV_DATA },
|
|
|
+ {},
|
|
|
+};
|
|
|
+MODULE_DEVICE_TABLE(of, s3c24xx_uart_dt_match);
|
|
|
+#else
|
|
|
+#define s3c24xx_uart_dt_match NULL
|
|
|
+#endif
|
|
|
+
|
|
|
+static struct platform_driver samsung_serial_driver = {
|
|
|
+ .probe = s3c24xx_serial_probe,
|
|
|
+ .remove = __devexit_p(s3c24xx_serial_remove),
|
|
|
+ .id_table = s3c24xx_serial_driver_ids,
|
|
|
+ .driver = {
|
|
|
+ .name = "samsung-uart",
|
|
|
+ .owner = THIS_MODULE,
|
|
|
+ .pm = SERIAL_SAMSUNG_PM_OPS,
|
|
|
+ .of_match_table = s3c24xx_uart_dt_match,
|
|
|
+ },
|
|
|
+};
|
|
|
+
|
|
|
+/* module initialisation code */
|
|
|
+
|
|
|
+static int __init s3c24xx_serial_modinit(void)
|
|
|
+{
|
|
|
+ int ret;
|
|
|
+
|
|
|
+ ret = uart_register_driver(&s3c24xx_uart_drv);
|
|
|
+ if (ret < 0) {
|
|
|
+ printk(KERN_ERR "failed to register UART driver\n");
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+
|
|
|
+ return platform_driver_register(&samsung_serial_driver);
|
|
|
}
|
|
|
|
|
|
-#endif /* CONFIG_SERIAL_SAMSUNG_CONSOLE */
|
|
|
+static void __exit s3c24xx_serial_modexit(void)
|
|
|
+{
|
|
|
+ uart_unregister_driver(&s3c24xx_uart_drv);
|
|
|
+}
|
|
|
+
|
|
|
+module_init(s3c24xx_serial_modinit);
|
|
|
+module_exit(s3c24xx_serial_modexit);
|
|
|
|
|
|
+MODULE_ALIAS("platform:samsung-uart");
|
|
|
MODULE_DESCRIPTION("Samsung SoC Serial port driver");
|
|
|
MODULE_AUTHOR("Ben Dooks <ben@simtec.co.uk>");
|
|
|
MODULE_LICENSE("GPL v2");
|