#include <common.h>
#include <div64.h>
#include <dm.h>
+#include <dm/device_compat.h>
#include <errno.h>
#include <log.h>
#include <serial.h>
#define BAUD_ALLOW_MAX(baud) ((baud) + (baud) * 3 / 100)
#define BAUD_ALLOW_MIX(baud) ((baud) - (baud) * 3 / 100)
+/* struct mtk_serial_priv - Structure holding all information used by the
+ * driver
+ * @regs: Register base of the serial port
+ * @clk: The baud clock device
+ * @fixed_clk_rate: Fallback fixed baud clock rate if baud clock
+ * device is not specified
+ * @force_highspeed: Force using high-speed mode
+ */
struct mtk_serial_priv {
struct mtk_serial_regs __iomem *regs;
- u32 clock;
+ struct clk clk;
+ u32 fixed_clk_rate;
bool force_highspeed;
};
-static void _mtk_serial_setbrg(struct mtk_serial_priv *priv, int baud)
+static void _mtk_serial_setbrg(struct mtk_serial_priv *priv, int baud,
+ uint clk_rate)
{
u32 quot, realbaud, samplecount = 1;
/* Special case for low baud clock */
- if (baud <= 115200 && priv->clock <= 12000000) {
+ if (baud <= 115200 && clk_rate == 12000000) {
writel(3, &priv->regs->highspeed);
- quot = DIV_ROUND_CLOSEST(priv->clock, 256 * baud);
+ quot = DIV_ROUND_CLOSEST(clk_rate, 256 * baud);
if (quot == 0)
quot = 1;
- samplecount = DIV_ROUND_CLOSEST(priv->clock, quot * baud);
+ samplecount = DIV_ROUND_CLOSEST(clk_rate, quot * baud);
- realbaud = priv->clock / samplecount / quot;
+ realbaud = clk_rate / samplecount / quot;
if (realbaud > BAUD_ALLOW_MAX(baud) ||
realbaud < BAUD_ALLOW_MIX(baud)) {
pr_info("baud %d can't be handled\n", baud);
if (baud <= 115200) {
writel(0, &priv->regs->highspeed);
- quot = DIV_ROUND_CLOSEST(priv->clock, 16 * baud);
+ quot = DIV_ROUND_CLOSEST(clk_rate, 16 * baud);
} else if (baud <= 576000) {
writel(2, &priv->regs->highspeed);
if ((baud == 500000) || (baud == 576000))
baud = 460800;
- quot = DIV_ROUND_UP(priv->clock, 4 * baud);
+ quot = DIV_ROUND_UP(clk_rate, 4 * baud);
} else {
use_hs3:
writel(3, &priv->regs->highspeed);
- quot = DIV_ROUND_UP(priv->clock, 256 * baud);
- samplecount = DIV_ROUND_CLOSEST(priv->clock, quot * baud);
+ quot = DIV_ROUND_UP(clk_rate, 256 * baud);
+ samplecount = DIV_ROUND_CLOSEST(clk_rate, quot * baud);
}
set_baud:
static int mtk_serial_setbrg(struct udevice *dev, int baudrate)
{
struct mtk_serial_priv *priv = dev_get_priv(dev);
+ u32 clk_rate;
+
+ clk_rate = clk_get_rate(&priv->clk);
+ if (IS_ERR_VALUE(clk_rate) || clk_rate == 0)
+ clk_rate = priv->fixed_clk_rate;
- _mtk_serial_setbrg(priv, baudrate);
+ _mtk_serial_setbrg(priv, baudrate, clk_rate);
return 0;
}
{
struct mtk_serial_priv *priv = dev_get_priv(dev);
fdt_addr_t addr;
- struct clk clk;
int err;
addr = dev_read_addr(dev);
priv->regs = map_physmem(addr, 0, MAP_NOCACHE);
- err = clk_get_by_index(dev, 0, &clk);
- if (!err) {
- err = clk_get_rate(&clk);
- if (!IS_ERR_VALUE(err))
- priv->clock = err;
- } else if (err != -ENOENT && err != -ENODEV && err != -ENOSYS) {
- debug("mtk_serial: failed to get clock\n");
- return err;
- }
-
- if (!priv->clock)
- priv->clock = dev_read_u32_default(dev, "clock-frequency", 0);
-
- if (!priv->clock) {
- debug("mtk_serial: clock not defined\n");
- return -EINVAL;
+ err = clk_get_by_index(dev, 0, &priv->clk);
+ if (err) {
+ err = dev_read_u32(dev, "clock-frequency", &priv->fixed_clk_rate);
+ if (err) {
+ dev_err(dev, "baud clock not defined\n");
+ return -EINVAL;
+ }
+ } else {
+ err = clk_get_rate(&priv->clk);
+ if (IS_ERR_VALUE(err)) {
+ dev_err(dev, "invalid baud clock\n");
+ return -EINVAL;
+ }
}
priv->force_highspeed = dev_read_bool(dev, "mediatek,force-highspeed");
#define DECLARE_HSUART_PRIV(port) \
static struct mtk_serial_priv mtk_hsuart##port = { \
.regs = (struct mtk_serial_regs *)CONFIG_SYS_NS16550_COM##port, \
- .clock = CONFIG_SYS_NS16550_CLK \
+ .fixed_clk_rate = CONFIG_SYS_NS16550_CLK \
};
#define DECLARE_HSUART_FUNCTIONS(port) \
writel(0, &mtk_hsuart##port.regs->ier); \
writel(UART_MCRVAL, &mtk_hsuart##port.regs->mcr); \
writel(UART_FCRVAL, &mtk_hsuart##port.regs->fcr); \
- _mtk_serial_setbrg(&mtk_hsuart##port, gd->baudrate); \
+ _mtk_serial_setbrg(&mtk_hsuart##port, gd->baudrate, \
+ mtk_hsuart##port.fixed_clk_rate); \
return 0 ; \
} \
static void mtk_serial##port##_setbrg(void) \
{ \
- _mtk_serial_setbrg(&mtk_hsuart##port, gd->baudrate); \
+ _mtk_serial_setbrg(&mtk_hsuart##port, gd->baudrate, \
+ mtk_hsuart##port.fixed_clk_rate); \
} \
static int mtk_serial##port##_getc(void) \
{ \
struct mtk_serial_priv priv;
priv.regs = (void *) CONFIG_VAL(DEBUG_UART_BASE);
- priv.clock = CONFIG_DEBUG_UART_CLOCK;
+ priv.fixed_clk_rate = CONFIG_DEBUG_UART_CLOCK;
writel(0, &priv.regs->ier);
writel(UART_MCRVAL, &priv.regs->mcr);
writel(UART_FCRVAL, &priv.regs->fcr);
- _mtk_serial_setbrg(&priv, CONFIG_BAUDRATE);
+ _mtk_serial_setbrg(&priv, CONFIG_BAUDRATE, priv.fixed_clk_rate);
}
static inline void _debug_uart_putc(int ch)