diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index 2871c66..e4db8da 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -34,6 +34,7 @@ select CLKDEV_LOOKUP select HAS_DEBUG_LL select HAVE_MACH_ARM_HEAD + select HAVE_CLK config ARCH_BCM2835 bool "Broadcom BCM2835 boards" @@ -101,6 +102,7 @@ bool "STMicroelectronics Nomadik" select CPU_ARM926T select CLOCKSOURCE_NOMADIK + select HAVE_CLK help Support for the Nomadik platform by ST-Ericsson @@ -135,6 +137,7 @@ bool "ARM Versatile boards (ARM926EJ-S)" select CPU_ARM926T select GPIOLIB + select HAVE_CLK config ARCH_VEXPRESS bool "ARM Vexpres boards" diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig index a00e539..daf778a 100644 --- a/drivers/clk/Kconfig +++ b/drivers/clk/Kconfig @@ -1,8 +1,11 @@ +config HAVE_CLK + bool config CLKDEV_LOOKUP bool config COMMON_CLK + select HAVE_CLK bool config COMMON_CLK_OF_PROVIDER diff --git a/drivers/serial/serial_ns16550.c b/drivers/serial/serial_ns16550.c index b7913aa..5899d57 100644 --- a/drivers/serial/serial_ns16550.c +++ b/drivers/serial/serial_ns16550.c @@ -38,12 +38,22 @@ #include #include #include +#include #include "serial_ns16550.h" #include -/*********** Private Functions **********************************/ -static int ns16550_setbaudrate(struct console_device *cdev, int baud_rate); +struct ns16550_priv { + struct console_device cdev; + struct NS16550_plat plat; + int access_width; + struct clk *clk; +}; + +static inline struct ns16550_priv *to_ns16550_priv(struct console_device *cdev) +{ + return container_of(cdev, struct ns16550_priv, cdev); +} /** * @brief read register @@ -55,9 +65,10 @@ */ static uint32_t ns16550_read(struct console_device *cdev, uint32_t off) { + struct ns16550_priv *priv = to_ns16550_priv(cdev); struct device_d *dev = cdev->dev; - struct NS16550_plat *plat = (struct NS16550_plat *)dev->platform_data; - int width = dev->resource[0].flags & IORESOURCE_MEM_TYPE_MASK; + struct NS16550_plat *plat = &priv->plat; + int width = priv->access_width; off <<= plat->shift; @@ -85,9 +96,10 @@ static void ns16550_write(struct console_device *cdev, uint32_t val, uint32_t off) { + struct ns16550_priv *priv = to_ns16550_priv(cdev); struct device_d *dev = cdev->dev; - struct NS16550_plat *plat = (struct NS16550_plat *)dev->platform_data; - int width = dev->resource[0].flags & IORESOURCE_MEM_TYPE_MASK; + struct NS16550_plat *plat = &priv->plat; + int width = priv->access_width; off <<= plat->shift; @@ -120,8 +132,8 @@ static inline unsigned int ns16550_calc_divisor(struct console_device *cdev, unsigned int baudrate) { - struct NS16550_plat *plat = (struct NS16550_plat *) - cdev->dev->platform_data; + struct ns16550_priv *priv = to_ns16550_priv(cdev); + struct NS16550_plat *plat = &priv->plat; unsigned int clk = plat->clock; return (clk / MODE_X_DIV / baudrate); @@ -129,6 +141,34 @@ } /** + * @brief Set the baudrate for the uart port + * + * @param[in] cdev console device + * @param[in] baud_rate baud rate to set + * + * @return 0-implied to support the baudrate + */ +static int ns16550_setbaudrate(struct console_device *cdev, int baud_rate) +{ + unsigned int baud_divisor = ns16550_calc_divisor(cdev, baud_rate); + struct ns16550_priv *priv = to_ns16550_priv(cdev); + struct NS16550_plat *plat = &priv->plat; + + ns16550_write(cdev, LCR_BKSE, lcr); + ns16550_write(cdev, baud_divisor & 0xff, dll); + ns16550_write(cdev, (baud_divisor >> 8) & 0xff, dlm); + ns16550_write(cdev, LCRVAL, lcr); + ns16550_write(cdev, MCRVAL, mcr); + + if (plat->flags & NS16650_FLAG_DISABLE_FIFO) + ns16550_write(cdev, FCRVAL & ~FCR_FIFO_EN, fcr); + else + ns16550_write(cdev, FCRVAL, fcr); + + return 0; +} + +/** * @brief Initialize the device * * @param[in] cdev pointer to console device @@ -191,32 +231,14 @@ return ((ns16550_read(cdev, lsr) & LSR_DR) != 0); } -/** - * @brief Set the baudrate for the uart port - * - * @param[in] cdev console device - * @param[in] baud_rate baud rate to set - * - * @return 0-implied to support the baudrate - */ -static int ns16550_setbaudrate(struct console_device *cdev, int baud_rate) +static void ns16550_probe_dt(struct device_d *dev, struct ns16550_priv *priv) { - unsigned int baud_divisor = ns16550_calc_divisor(cdev, baud_rate); - struct NS16550_plat *plat = (struct NS16550_plat *) - cdev->dev->platform_data; + struct device_node *np = dev->device_node; - ns16550_write(cdev, LCR_BKSE, lcr); - ns16550_write(cdev, baud_divisor & 0xff, dll); - ns16550_write(cdev, (baud_divisor >> 8) & 0xff, dlm); - ns16550_write(cdev, LCRVAL, lcr); - ns16550_write(cdev, MCRVAL, mcr); + if (!IS_ENABLED(CONFIG_OFDEVICE)) + return; - if (plat->flags & NS16650_FLAG_DISABLE_FIFO) - ns16550_write(cdev, FCRVAL & ~FCR_FIFO_EN, fcr); - else - ns16550_write(cdev, FCRVAL, fcr); - - return 0; + of_property_read_u32(np, "reg-shift", &priv->plat.shift); } /** @@ -230,21 +252,46 @@ */ static int ns16550_probe(struct device_d *dev) { + struct ns16550_priv *priv; struct console_device *cdev; struct NS16550_plat *plat = (struct NS16550_plat *)dev->platform_data; + int ret; - /* we do expect platform specific data */ - if (plat == NULL) - return -EINVAL; dev->priv = dev_request_mem_region(dev, 0); - cdev = xzalloc(sizeof(*cdev)); + priv = xzalloc(sizeof(*priv)); - cdev->dev = dev; - if (plat->f_caps) - cdev->f_caps = plat->f_caps; + if (plat) + priv->plat = *plat; else - cdev->f_caps = CONSOLE_STDIN | CONSOLE_STDOUT | CONSOLE_STDERR; + ns16550_probe_dt(dev, priv); + + if (!plat || !plat->clock) { + priv->clk = clk_get(dev, NULL); + if (IS_ERR(priv->clk)) { + ret = PTR_ERR(priv->clk); + goto err; + } + priv->plat.clock = clk_get_rate(priv->clk); + } + + if (priv->plat.clock == 0 && IS_ENABLED(CONFIG_OFDEVICE)) { + struct device_node *np = dev->device_node; + + of_property_read_u32(np, "clock-frequency", &priv->plat.clock); + } + + if (priv->plat.clock == 0) { + dev_err(dev, "no valid clockrate\n"); + ret = -EINVAL; + goto err; + } + + priv->access_width = dev->resource[0].flags & IORESOURCE_MEM_TYPE_MASK; + + cdev = &priv->cdev; + cdev->dev = dev; + cdev->f_caps = CONSOLE_STDIN | CONSOLE_STDOUT | CONSOLE_STDERR; cdev->tstc = ns16550_tstc; cdev->putc = ns16550_putc; cdev->getc = ns16550_getc; @@ -253,13 +300,27 @@ ns16550_serial_init_port(cdev); return console_register(cdev); + +err: + free(priv); + + return ret; } +static struct of_device_id ns16550_serial_dt_ids[] = { + { + .compatible = "ns16550a", + }, { + /* sentinel */ + }, +}; + /** * @brief Driver registration structure */ static struct driver_d ns16550_serial_driver = { .name = "ns16550_serial", .probe = ns16550_probe, + .of_compatible = DRV_OF_COMPAT(ns16550_serial_dt_ids), }; console_platform_driver(ns16550_serial_driver); diff --git a/include/linux/clk.h b/include/linux/clk.h index 0a565ef..6aed1de 100644 --- a/include/linux/clk.h +++ b/include/linux/clk.h @@ -19,12 +19,13 @@ * The base API. */ - /* * struct clk - an machine class defined object / cookie. */ struct clk; +#ifdef CONFIG_HAVE_CLK + /** * clk_get - lookup and obtain a reference to a clock producer. * @dev: device for clock "consumer" @@ -157,6 +158,42 @@ int clk_add_alias(const char *alias, const char *alias_dev_name, char *id, struct device_d *dev); +#else + +static inline struct clk *clk_get(struct device_d *dev, const char *id) +{ + return NULL; +} + +static inline int clk_enable(struct clk *clk) +{ + return 0; +} + +static inline void clk_disable(struct clk *clk) +{ +} + +static inline unsigned long clk_get_rate(struct clk *clk) +{ + return 0; +} + +static inline void clk_put(struct clk *clk) +{ +} + +static inline long clk_round_rate(struct clk *clk, unsigned long rate) +{ + return 0; +} + +static inline int clk_set_rate(struct clk *clk, unsigned long rate) +{ + return 0; +} +#endif + #ifdef CONFIG_COMMON_CLK struct clk_ops { int (*enable)(struct clk *clk); diff --git a/include/ns16550.h b/include/ns16550.h index 27cb4cf..36aa5ff 100644 --- a/include/ns16550.h +++ b/include/ns16550.h @@ -33,10 +33,6 @@ struct NS16550_plat { /** Clock speed */ unsigned int clock; - /** Console capabilities: - * CONSOLE_STDIN | CONSOLE_STDOUT | CONSOLE_STDERR @see console.h - */ - unsigned char f_caps; /** * register read access capability */