diff options
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/clk/Makefile | 1 | ||||
-rw-r--r-- | drivers/clk/nuvoton/Makefile | 2 | ||||
-rw-r--r-- | drivers/clk/nuvoton/clk_npcm.c | 299 | ||||
-rw-r--r-- | drivers/clk/nuvoton/clk_npcm.h | 105 | ||||
-rw-r--r-- | drivers/clk/nuvoton/clk_npcm7xx.c | 95 | ||||
-rw-r--r-- | drivers/gpio/Kconfig | 7 | ||||
-rw-r--r-- | drivers/gpio/Makefile | 1 | ||||
-rw-r--r-- | drivers/gpio/npcm_gpio.c | 123 | ||||
-rw-r--r-- | drivers/pinctrl/mediatek/pinctrl-mtk-common.c | 221 | ||||
-rw-r--r-- | drivers/pinctrl/mediatek/pinctrl-mtk-common.h | 78 | ||||
-rw-r--r-- | drivers/serial/Kconfig | 9 | ||||
-rw-r--r-- | drivers/serial/Makefile | 1 | ||||
-rw-r--r-- | drivers/serial/serial_npcm.c | 157 | ||||
-rw-r--r-- | drivers/timer/Kconfig | 9 | ||||
-rw-r--r-- | drivers/timer/Makefile | 1 | ||||
-rw-r--r-- | drivers/timer/npcm-timer.c | 115 |
16 files changed, 1173 insertions, 51 deletions
diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile index bb4eee5d99b..f5b553172c2 100644 --- a/drivers/clk/Makefile +++ b/drivers/clk/Makefile @@ -20,6 +20,7 @@ obj-$(CONFIG_ARCH_ASPEED) += aspeed/ obj-$(CONFIG_ARCH_MEDIATEK) += mediatek/ obj-$(CONFIG_ARCH_MESON) += meson/ obj-$(CONFIG_ARCH_MTMIPS) += mtmips/ +obj-$(CONFIG_ARCH_NPCM) += nuvoton/ obj-$(CONFIG_ARCH_ROCKCHIP) += rockchip/ obj-$(CONFIG_ARCH_SOCFPGA) += altera/ obj-$(CONFIG_ARCH_SUNXI) += sunxi/ diff --git a/drivers/clk/nuvoton/Makefile b/drivers/clk/nuvoton/Makefile new file mode 100644 index 00000000000..c63d9c16f1a --- /dev/null +++ b/drivers/clk/nuvoton/Makefile @@ -0,0 +1,2 @@ +obj-$(CONFIG_ARCH_NPCM) += clk_npcm.o +obj-$(CONFIG_ARCH_NPCM7xx) += clk_npcm7xx.o diff --git a/drivers/clk/nuvoton/clk_npcm.c b/drivers/clk/nuvoton/clk_npcm.c new file mode 100644 index 00000000000..8d71f2a24b8 --- /dev/null +++ b/drivers/clk/nuvoton/clk_npcm.c @@ -0,0 +1,299 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2022 Nuvoton Technology Corp. + * + * Formula for calculating clock rate: + * Fout = ((Fin / PRE_DIV) / div) / POST_DIV + */ + +#include <div64.h> +#include <dm.h> +#include <asm/io.h> +#include <linux/bitfield.h> +#include <linux/log2.h> +#include "clk_npcm.h" + +static int clkid_to_clksel(struct npcm_clk_select *selector, int id) +{ + int i; + + for (i = 0; i < selector->num_parents; i++) { + if (selector->parents[i].id == id) + return selector->parents[i].clksel; + } + + return -EINVAL; +} + +static int clksel_to_clkid(struct npcm_clk_select *selector, int clksel) +{ + int i; + + for (i = 0; i < selector->num_parents; i++) { + if (selector->parents[i].clksel == clksel) + return selector->parents[i].id; + } + + return -EINVAL; +} + +static struct npcm_clk_pll *npcm_clk_pll_get(struct npcm_clk_data *clk_data, int id) +{ + struct npcm_clk_pll *pll = clk_data->clk_plls; + int i; + + for (i = 0; i < clk_data->num_plls; i++) { + if (pll->id == id) + return pll; + pll++; + } + + return NULL; +} + +static struct npcm_clk_select *npcm_clk_selector_get(struct npcm_clk_data *clk_data, + int id) +{ + struct npcm_clk_select *selector = clk_data->clk_selectors; + int i; + + for (i = 0; i < clk_data->num_selectors; i++) { + if (selector->id == id) + return selector; + selector++; + } + + return NULL; +} + +static struct npcm_clk_div *npcm_clk_divider_get(struct npcm_clk_data *clk_data, + int id) +{ + struct npcm_clk_div *divider = clk_data->clk_dividers; + int i; + + for (i = 0; i < clk_data->num_dividers; i++) { + if (divider->id == id) + return divider; + divider++; + } + + return NULL; +} + +static ulong npcm_clk_get_fin(struct clk *clk) +{ + struct npcm_clk_priv *priv = dev_get_priv(clk->dev); + struct npcm_clk_select *selector; + struct clk parent; + ulong parent_rate; + u32 val, clksel; + int ret; + + selector = npcm_clk_selector_get(priv->clk_data, clk->id); + if (!selector) + return 0; + + if (selector->flags & FIXED_PARENT) { + clksel = 0; + } else { + val = readl(priv->base + selector->reg); + clksel = (val & selector->mask) >> (ffs(selector->mask) - 1); + } + parent.id = clksel_to_clkid(selector, clksel); + + ret = clk_request(clk->dev, &parent); + if (ret) + return 0; + + parent_rate = clk_get_rate(&parent); + + debug("fin of clk%lu = %lu\n", clk->id, parent_rate); + return parent_rate; +} + +static u32 npcm_clk_get_div(struct clk *clk) +{ + struct npcm_clk_priv *priv = dev_get_priv(clk->dev); + struct npcm_clk_div *divider; + u32 val, div; + + divider = npcm_clk_divider_get(priv->clk_data, clk->id); + if (!divider) + return 0; + + val = readl(priv->base + divider->reg); + div = (val & divider->mask) >> (ffs(divider->mask) - 1); + if (divider->flags & DIV_TYPE1) + div = div + 1; + else + div = 1 << div; + + if (divider->flags & PRE_DIV2) + div = div << 1; + + return div; +} + +static u32 npcm_clk_set_div(struct clk *clk, u32 div) +{ + struct npcm_clk_priv *priv = dev_get_priv(clk->dev); + struct npcm_clk_div *divider; + u32 val, clkdiv; + + divider = npcm_clk_divider_get(priv->clk_data, clk->id); + if (!divider) + return -EINVAL; + + if (divider->flags & PRE_DIV2) + div = div >> 1; + + if (divider->flags & DIV_TYPE1) + clkdiv = div - 1; + else + clkdiv = ilog2(div); + + val = readl(priv->base + divider->reg); + val &= ~divider->mask; + val |= (clkdiv << (ffs(divider->mask) - 1)) & divider->mask; + writel(val, priv->base + divider->reg); + + return 0; +} + +static ulong npcm_clk_get_fout(struct clk *clk) +{ + ulong parent_rate; + u32 div; + + parent_rate = npcm_clk_get_fin(clk); + if (!parent_rate) + return -EINVAL; + + div = npcm_clk_get_div(clk); + if (!div) + return -EINVAL; + + debug("fout of clk%lu = (%lu / %u)\n", clk->id, parent_rate, div); + return (parent_rate / div); +} + +static ulong npcm_clk_get_pll_fout(struct clk *clk) +{ + struct npcm_clk_priv *priv = dev_get_priv(clk->dev); + struct npcm_clk_pll *pll; + struct clk parent; + ulong parent_rate; + ulong fbdv, indv, otdv1, otdv2; + u32 val; + u64 ret; + + pll = npcm_clk_pll_get(priv->clk_data, clk->id); + if (!pll) + return -ENODEV; + + parent.id = pll->parent_id; + ret = clk_request(clk->dev, &parent); + if (ret) + return ret; + + parent_rate = clk_get_rate(&parent); + + val = readl(priv->base + pll->reg); + indv = FIELD_GET(PLLCON_INDV, val); + fbdv = FIELD_GET(PLLCON_FBDV, val); + otdv1 = FIELD_GET(PLLCON_OTDV1, val); + otdv2 = FIELD_GET(PLLCON_OTDV2, val); + + ret = (u64)parent_rate * fbdv; + do_div(ret, indv * otdv1 * otdv2); + if (pll->flags & POST_DIV2) + do_div(ret, 2); + + debug("fout of pll(id %lu) = %llu\n", clk->id, ret); + return ret; +} + +static ulong npcm_clk_get_rate(struct clk *clk) +{ + struct npcm_clk_priv *priv = dev_get_priv(clk->dev); + struct npcm_clk_data *clk_data = priv->clk_data; + struct clk refclk; + int ret; + + debug("%s: id %lu\n", __func__, clk->id); + if (clk->id == clk_data->refclk_id) { + ret = clk_get_by_name(clk->dev, "refclk", &refclk); + if (!ret) + return clk_get_rate(&refclk); + else + return ret; + } + + if (clk->id >= clk_data->pll0_id && + clk->id < clk_data->pll0_id + clk_data->num_plls) + return npcm_clk_get_pll_fout(clk); + else + return npcm_clk_get_fout(clk); +} + +static ulong npcm_clk_set_rate(struct clk *clk, ulong rate) +{ + ulong parent_rate; + u32 div; + int ret; + + debug("%s: id %lu, rate %lu\n", __func__, clk->id, rate); + parent_rate = npcm_clk_get_fin(clk); + if (!parent_rate) + return -EINVAL; + + div = DIV_ROUND_UP(parent_rate, rate); + ret = npcm_clk_set_div(clk, div); + if (ret) + return ret; + + debug("%s: rate %lu, new rate (%lu / %u)\n", __func__, rate, parent_rate, div); + return (parent_rate / div); +} + +static int npcm_clk_set_parent(struct clk *clk, struct clk *parent) +{ + struct npcm_clk_priv *priv = dev_get_priv(clk->dev); + struct npcm_clk_select *selector; + int clksel; + u32 val; + + debug("%s: id %lu, parent %lu\n", __func__, clk->id, parent->id); + selector = npcm_clk_selector_get(priv->clk_data, clk->id); + if (!selector) + return -EINVAL; + + clksel = clkid_to_clksel(selector, parent->id); + if (clksel < 0) + return -EINVAL; + + val = readl(priv->base + selector->reg); + val &= ~selector->mask; + val |= clksel << (ffs(selector->mask) - 1); + writel(val, priv->base + selector->reg); + + return 0; +} + +static int npcm_clk_request(struct clk *clk) +{ + struct npcm_clk_priv *priv = dev_get_priv(clk->dev); + + if (clk->id >= priv->num_clks) + return -EINVAL; + + return 0; +} + +const struct clk_ops npcm_clk_ops = { + .get_rate = npcm_clk_get_rate, + .set_rate = npcm_clk_set_rate, + .set_parent = npcm_clk_set_parent, + .request = npcm_clk_request, +}; diff --git a/drivers/clk/nuvoton/clk_npcm.h b/drivers/clk/nuvoton/clk_npcm.h new file mode 100644 index 00000000000..06b60dc8b8d --- /dev/null +++ b/drivers/clk/nuvoton/clk_npcm.h @@ -0,0 +1,105 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ + +#ifndef _CLK_NPCM_H_ +#define _CLK_NPCM_H_ + +#include <clk-uclass.h> + +/* Register offsets */ +#define CLKSEL 0x04 /* clock source selection */ +#define CLKDIV1 0x08 /* clock divider 1 */ +#define CLKDIV2 0x2C /* clock divider 2 */ +#define CLKDIV3 0x58 /* clock divider 3 */ +#define PLLCON0 0x0C /* pll0 control */ +#define PLLCON1 0x10 /* pll1 control */ +#define PLLCON2 0x54 /* pll2 control */ + +/* CLKSEL bit filed */ +#define NPCM7XX_CPUCKSEL GENMASK(1, 0) +#define NPCM8XX_CPUCKSEL GENMASK(2, 0) +#define SDCKSEL GENMASK(7, 6) +#define UARTCKSEL GENMASK(9, 8) +#define TIMCKSEL GENMASK(15, 14) + +/* CLKDIV1 bit filed */ +#define SPI3CKDIV GENMASK(10, 6) +#define MMCCKDIV GENMASK(15, 11) +#define UARTDIV1 GENMASK(20, 16) +#define TIMCKDIV GENMASK(25, 21) +#define CLK4DIV GENMASK(27, 26) + +/* CLKDIV2 bit filed */ +#define APB5CKDIV GENMASK(23, 22) +#define APB2CKDIV GENMASK(27, 26) + +/* CLKDIV3 bit filed */ +#define SPIXCKDIV GENMASK(5, 1) +#define SPI0CKDIV GENMASK(10, 6) +#define UARTDIV2 GENMASK(15, 11) +#define SPI1CKDIV GENMASK(23, 16) + +/* PLLCON bit filed */ +#define PLLCON_INDV GENMASK(5, 0) +#define PLLCON_OTDV1 GENMASK(10, 8) +#define PLLCON_OTDV2 GENMASK(15, 13) +#define PLLCON_FBDV GENMASK(27, 16) + +/* Flags */ +#define DIV_TYPE1 BIT(0) /* div = clkdiv + 1 */ +#define DIV_TYPE2 BIT(1) /* div = 1 << clkdiv */ +#define PRE_DIV2 BIT(2) /* Pre divisor = 2 */ +#define POST_DIV2 BIT(3) /* Post divisor = 2 */ +#define FIXED_PARENT BIT(4) /* clock source is fixed */ + +/* Parameters of PLL configuration */ +struct npcm_clk_pll { + const int id; + const int parent_id; + u32 reg; + u32 flags; +}; + +/* Parent clock id to clksel mapping */ +struct parent_data { + int id; + int clksel; +}; + +/* Parameters of parent selection */ +struct npcm_clk_select { + const int id; + const struct parent_data *parents; + u32 reg; + u32 mask; + u8 num_parents; + u32 flags; +}; + +/* Parameters of clock divider */ +struct npcm_clk_div { + const int id; + u32 reg; + u32 mask; + u32 flags; +}; + +struct npcm_clk_data { + struct npcm_clk_pll *clk_plls; + int num_plls; + struct npcm_clk_select *clk_selectors; + int num_selectors; + struct npcm_clk_div *clk_dividers; + int num_dividers; + int refclk_id; + int pll0_id; +}; + +struct npcm_clk_priv { + void __iomem *base; + struct npcm_clk_data *clk_data; + int num_clks; +}; + +extern const struct clk_ops npcm_clk_ops; + +#endif diff --git a/drivers/clk/nuvoton/clk_npcm7xx.c b/drivers/clk/nuvoton/clk_npcm7xx.c new file mode 100644 index 00000000000..a12aaa2f4cd --- /dev/null +++ b/drivers/clk/nuvoton/clk_npcm7xx.c @@ -0,0 +1,95 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2021 Nuvoton Technology Corp. + */ + +#include <dm.h> +#include <dt-bindings/clock/nuvoton,npcm7xx-clock.h> +#include "clk_npcm.h" + +/* Parent clock map */ +static const struct parent_data pll_parents[] = { + {NPCM7XX_CLK_PLL0, 0}, + {NPCM7XX_CLK_PLL1, 1}, + {NPCM7XX_CLK_REFCLK, 2}, + {NPCM7XX_CLK_PLL2DIV2, 3} +}; + +static const struct parent_data cpuck_parents[] = { + {NPCM7XX_CLK_PLL0, 0}, + {NPCM7XX_CLK_PLL1, 1}, + {NPCM7XX_CLK_REFCLK, 2}, +}; + +static const struct parent_data apb_parent[] = {{NPCM7XX_CLK_AHB, 0}}; + +static struct npcm_clk_pll npcm7xx_clk_plls[] = { + {NPCM7XX_CLK_PLL0, NPCM7XX_CLK_REFCLK, PLLCON0, 0}, + {NPCM7XX_CLK_PLL1, NPCM7XX_CLK_REFCLK, PLLCON1, 0}, + {NPCM7XX_CLK_PLL2, NPCM7XX_CLK_REFCLK, PLLCON2, 0}, + {NPCM7XX_CLK_PLL2DIV2, NPCM7XX_CLK_REFCLK, PLLCON2, POST_DIV2} +}; + +static struct npcm_clk_select npcm7xx_clk_selectors[] = { + {NPCM7XX_CLK_AHB, cpuck_parents, CLKSEL, NPCM7XX_CPUCKSEL, 3, 0}, + {NPCM7XX_CLK_APB2, apb_parent, 0, 0, 1, FIXED_PARENT}, + {NPCM7XX_CLK_APB5, apb_parent, 0, 0, 1, FIXED_PARENT}, + {NPCM7XX_CLK_SPI0, apb_parent, 0, 0, 1, FIXED_PARENT}, + {NPCM7XX_CLK_SPI3, apb_parent, 0, 0, 1, FIXED_PARENT}, + {NPCM7XX_CLK_SPIX, apb_parent, 0, 0, 1, FIXED_PARENT}, + {NPCM7XX_CLK_UART, pll_parents, CLKSEL, UARTCKSEL, 4, 0}, + {NPCM7XX_CLK_TIMER, pll_parents, CLKSEL, TIMCKSEL, 4, 0}, + {NPCM7XX_CLK_SDHC, pll_parents, CLKSEL, SDCKSEL, 4, 0} +}; + +static struct npcm_clk_div npcm7xx_clk_dividers[] = { + {NPCM7XX_CLK_AHB, CLKDIV1, CLK4DIV, DIV_TYPE1 | PRE_DIV2}, + {NPCM7XX_CLK_APB2, CLKDIV2, APB2CKDIV, DIV_TYPE2}, + {NPCM7XX_CLK_APB5, CLKDIV2, APB5CKDIV, DIV_TYPE2}, + {NPCM7XX_CLK_SPI0, CLKDIV3, SPI0CKDIV, DIV_TYPE1}, + {NPCM7XX_CLK_SPI3, CLKDIV1, SPI3CKDIV, DIV_TYPE1}, + {NPCM7XX_CLK_SPIX, CLKDIV3, SPIXCKDIV, DIV_TYPE1}, + {NPCM7XX_CLK_UART, CLKDIV1, UARTDIV1, DIV_TYPE1}, + {NPCM7XX_CLK_TIMER, CLKDIV1, TIMCKDIV, DIV_TYPE2}, + {NPCM7XX_CLK_SDHC, CLKDIV1, MMCCKDIV, DIV_TYPE1} +}; + +static struct npcm_clk_data npcm7xx_clk_data = { + .clk_plls = npcm7xx_clk_plls, + .num_plls = ARRAY_SIZE(npcm7xx_clk_plls), + .clk_selectors = npcm7xx_clk_selectors, + .num_selectors = ARRAY_SIZE(npcm7xx_clk_selectors), + .clk_dividers = npcm7xx_clk_dividers, + .num_dividers = ARRAY_SIZE(npcm7xx_clk_dividers), + .refclk_id = NPCM7XX_CLK_REFCLK, + .pll0_id = NPCM7XX_CLK_PLL0, +}; + +static int npcm7xx_clk_probe(struct udevice *dev) +{ + struct npcm_clk_priv *priv = dev_get_priv(dev); + + priv->base = dev_read_addr_ptr(dev); + if (!priv->base) + return -EINVAL; + + priv->clk_data = &npcm7xx_clk_data; + priv->num_clks = NPCM7XX_NUM_CLOCKS; + + return 0; +} + +static const struct udevice_id npcm7xx_clk_ids[] = { + { .compatible = "nuvoton,npcm750-clk" }, + { } +}; + +U_BOOT_DRIVER(clk_npcm) = { + .name = "clk_npcm", + .id = UCLASS_CLK, + .of_match = npcm7xx_clk_ids, + .ops = &npcm_clk_ops, + .priv_auto = sizeof(struct npcm_clk_priv), + .probe = npcm7xx_clk_probe, + .flags = DM_FLAG_PRE_RELOC, +}; diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 886cdbcdbaa..149a62ffe61 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -279,6 +279,13 @@ config MXS_GPIO help Support GPIO controllers on i.MX23 and i.MX28 platforms +config NPCM_GPIO + bool "Nuvoton NPCM GPIO driver" + depends on DM_GPIO + help + Support GPIO controllers on Nuvovon NPCM SoCs. + NPCM7xx/NPCM8xx contain 8 GPIO banks, each bank contains 32 pins. + config OMAP_GPIO bool "TI OMAP GPIO driver" depends on ARCH_OMAP2PLUS diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index b39dde12abf..d7552762d06 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -28,6 +28,7 @@ obj-$(CONFIG_MARVELL_GPIO) += mvgpio.o obj-$(CONFIG_MCP230XX_GPIO) += mcp230xx_gpio.o obj-$(CONFIG_MXC_GPIO) += mxc_gpio.o obj-$(CONFIG_MXS_GPIO) += mxs_gpio.o +obj-$(CONFIG_NPCM_GPIO) += npcm_gpio.o obj-$(CONFIG_PCA953X) += pca953x.o obj-$(CONFIG_PCA9698) += pca9698.o obj-$(CONFIG_ROCKCHIP_GPIO) += rk_gpio.o diff --git a/drivers/gpio/npcm_gpio.c b/drivers/gpio/npcm_gpio.c new file mode 100644 index 00000000000..8afd57fa8e5 --- /dev/null +++ b/drivers/gpio/npcm_gpio.c @@ -0,0 +1,123 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2022 Nuvoton Technology Corp. + */ + +#include <common.h> +#include <dm.h> +#include <asm/gpio.h> +#include <linux/io.h> + +#define NPCM_GPIOS_PER_BANK 32 + +/* Register offsets */ +#define GPIO_DIN 0x4 /* RO - Data In */ +#define GPIO_DOUT 0xC /* RW - Data Out */ +#define GPIO_OE 0x10 /* RW - Output Enable */ +#define GPIO_IEM 0x58 /* RW - Input Enable Mask */ +#define GPIO_OES 0x70 /* WO - Output Enable Register Set */ +#define GPIO_OEC 0x74 /* WO - Output Enable Register Clear */ + +struct npcm_gpio_priv { + void __iomem *base; +}; + +static int npcm_gpio_direction_input(struct udevice *dev, unsigned int offset) +{ + struct npcm_gpio_priv *priv = dev_get_priv(dev); + + writel(BIT(offset), priv->base + GPIO_OEC); + setbits_le32(priv->base + GPIO_IEM, BIT(offset)); + + return 0; +} + +static int npcm_gpio_direction_output(struct udevice *dev, unsigned int offset, + int value) +{ + struct npcm_gpio_priv *priv = dev_get_priv(dev); + + clrbits_le32(priv->base + GPIO_IEM, BIT(offset)); + writel(BIT(offset), priv->base + GPIO_OES); + + if (value) + setbits_le32(priv->base + GPIO_DOUT, BIT(offset)); + else + clrbits_le32(priv->base + GPIO_DOUT, BIT(offset)); + + return 0; +} + +static int npcm_gpio_get_value(struct udevice *dev, unsigned int offset) +{ + struct npcm_gpio_priv *priv = dev_get_priv(dev); + + if (readl(priv->base + GPIO_IEM) & BIT(offset)) + return !!(readl(priv->base + GPIO_DIN) & BIT(offset)); + + if (readl(priv->base + GPIO_OE) & BIT(offset)) + return !!(readl(priv->base + GPIO_DOUT) & BIT(offset)); + + return -EINVAL; +} + +static int npcm_gpio_set_value(struct udevice *dev, unsigned int offset, + int value) +{ + struct npcm_gpio_priv *priv = dev_get_priv(dev); + + if (value) + setbits_le32(priv->base + GPIO_DOUT, BIT(offset)); + else + clrbits_le32(priv->base + GPIO_DOUT, BIT(offset)); + + return 0; +} + +static int npcm_gpio_get_function(struct udevice *dev, unsigned int offset) +{ + struct npcm_gpio_priv *priv = dev_get_priv(dev); + + if (readl(priv->base + GPIO_IEM) & BIT(offset)) + return GPIOF_INPUT; + + if (readl(priv->base + GPIO_OE) & BIT(offset)) + return GPIOF_OUTPUT; + + return GPIOF_FUNC; +} + +static const struct dm_gpio_ops npcm_gpio_ops = { + .direction_input = npcm_gpio_direction_input, + .direction_output = npcm_gpio_direction_output, + .get_value = npcm_gpio_get_value, + .set_value = npcm_gpio_set_value, + .get_function = npcm_gpio_get_function, +}; + +static int npcm_gpio_probe(struct udevice *dev) +{ + struct npcm_gpio_priv *priv = dev_get_priv(dev); + struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev); + + priv->base = dev_read_addr_ptr(dev); + uc_priv->gpio_count = NPCM_GPIOS_PER_BANK; + uc_priv->bank_name = dev->name; + + return 0; +} + +static const struct udevice_id npcm_gpio_match[] = { + { .compatible = "nuvoton,npcm845-gpio" }, + { .compatible = "nuvoton,npcm750-gpio" }, + { } +}; + +U_BOOT_DRIVER(npcm_gpio) = { + .name = "npcm_gpio", + .id = UCLASS_GPIO, + .of_match = npcm_gpio_match, + .probe = npcm_gpio_probe, + .priv_auto = sizeof(struct npcm_gpio_priv), + .ops = &npcm_gpio_ops, +}; diff --git a/drivers/pinctrl/mediatek/pinctrl-mtk-common.c b/drivers/pinctrl/mediatek/pinctrl-mtk-common.c index a9cedda164c..47e2d67426f 100644 --- a/drivers/pinctrl/mediatek/pinctrl-mtk-common.c +++ b/drivers/pinctrl/mediatek/pinctrl-mtk-common.c @@ -45,18 +45,18 @@ static const struct mtk_drive_desc mtk_drive[] = { static const char *mtk_pinctrl_dummy_name = "_dummy"; -static void mtk_w32(struct udevice *dev, u32 reg, u32 val) +static void mtk_w32(struct udevice *dev, u8 i, u32 reg, u32 val) { struct mtk_pinctrl_priv *priv = dev_get_priv(dev); - __raw_writel(val, priv->base + reg); + __raw_writel(val, priv->base[i] + reg); } -static u32 mtk_r32(struct udevice *dev, u32 reg) +static u32 mtk_r32(struct udevice *dev, u8 i, u32 reg) { struct mtk_pinctrl_priv *priv = dev_get_priv(dev); - return __raw_readl(priv->base + reg); + return __raw_readl(priv->base[i] + reg); } static inline int get_count_order(unsigned int count) @@ -71,20 +71,27 @@ static inline int get_count_order(unsigned int count) void mtk_rmw(struct udevice *dev, u32 reg, u32 mask, u32 set) { + return mtk_i_rmw(dev, 0, reg, mask, set); +} + +void mtk_i_rmw(struct udevice *dev, u8 i, u32 reg, u32 mask, u32 set) +{ u32 val; - val = mtk_r32(dev, reg); + val = mtk_r32(dev, i, reg); val &= ~mask; val |= set; - mtk_w32(dev, reg, val); + mtk_w32(dev, i, reg, val); } static int mtk_hw_pin_field_lookup(struct udevice *dev, int pin, const struct mtk_pin_reg_calc *rc, struct mtk_pin_field *pfd) { + struct mtk_pinctrl_priv *priv = dev_get_priv(dev); const struct mtk_pin_field_calc *c, *e; u32 bits; + u32 base_calc = priv->soc->base_calc; c = rc->range; e = c + rc->nranges; @@ -111,6 +118,11 @@ static int mtk_hw_pin_field_lookup(struct udevice *dev, int pin, pfd->bitpos = bits % c->sz_reg; pfd->mask = (1 << c->x_bits) - 1; + if (base_calc) + pfd->index = c->i_base; + else + pfd->index = 0; + /* pfd->next is used for indicating that bit wrapping-around happens * which requires the manipulation for bit 0 starting in the next * register to form the complete field read/write. @@ -150,10 +162,10 @@ static void mtk_hw_write_cross_field(struct udevice *dev, mtk_hw_bits_part(pf, &nbits_h, &nbits_l); - mtk_rmw(dev, pf->offset, pf->mask << pf->bitpos, + mtk_i_rmw(dev, pf->index, pf->offset, pf->mask << pf->bitpos, (value & pf->mask) << pf->bitpos); - mtk_rmw(dev, pf->offset + pf->next, BIT(nbits_h) - 1, + mtk_i_rmw(dev, pf->index, pf->offset + pf->next, BIT(nbits_h) - 1, (value & pf->mask) >> nbits_l); } @@ -164,8 +176,8 @@ static void mtk_hw_read_cross_field(struct udevice *dev, mtk_hw_bits_part(pf, &nbits_h, &nbits_l); - l = (mtk_r32(dev, pf->offset) >> pf->bitpos) & (BIT(nbits_l) - 1); - h = (mtk_r32(dev, pf->offset + pf->next)) & (BIT(nbits_h) - 1); + l = (mtk_r32(dev, pf->index, pf->offset) >> pf->bitpos) & (BIT(nbits_l) - 1); + h = (mtk_r32(dev, pf->index, pf->offset + pf->next)) & (BIT(nbits_h) - 1); *value = (h << nbits_l) | l; } @@ -181,7 +193,7 @@ static int mtk_hw_set_value(struct udevice *dev, int pin, int field, return err; if (!pf.next) - mtk_rmw(dev, pf.offset, pf.mask << pf.bitpos, + mtk_i_rmw(dev, pf.index, pf.offset, pf.mask << pf.bitpos, (value & pf.mask) << pf.bitpos); else mtk_hw_write_cross_field(dev, &pf, value); @@ -200,13 +212,32 @@ static int mtk_hw_get_value(struct udevice *dev, int pin, int field, return err; if (!pf.next) - *value = (mtk_r32(dev, pf.offset) >> pf.bitpos) & pf.mask; + *value = (mtk_r32(dev, pf.index, pf.offset) >> pf.bitpos) & pf.mask; else mtk_hw_read_cross_field(dev, &pf, value); return 0; } +#if CONFIG_IS_ENABLED(PINCONF) +static int mtk_get_pin_io_type(struct udevice *dev, int pin, + struct mtk_io_type_desc *io_type) +{ + struct mtk_pinctrl_priv *priv = dev_get_priv(dev); + u8 io_n = priv->soc->pins[pin].io_n; + + if (io_n >= priv->soc->ntype) + return -EINVAL; + + io_type->name = priv->soc->io_type[io_n].name; + io_type->bias_set = priv->soc->io_type[io_n].bias_set; + io_type->drive_set = priv->soc->io_type[io_n].drive_set; + io_type->input_enable = priv->soc->io_type[io_n].input_enable; + + return 0; +} +#endif + static int mtk_get_groups_count(struct udevice *dev) { struct mtk_pinctrl_priv *priv = dev_get_priv(dev); @@ -236,7 +267,6 @@ static int mtk_get_pin_muxing(struct udevice *dev, unsigned int selector, char *buf, int size) { int val, err; - err = mtk_hw_get_value(dev, selector, PINCTRL_PIN_REG_MODE, &val); if (err) return err; @@ -308,13 +338,31 @@ static const struct pinconf_param mtk_conf_params[] = { { "drive-strength", PIN_CONFIG_DRIVE_STRENGTH, 0 }, }; +int mtk_pinconf_bias_set_v0(struct udevice *dev, u32 pin, bool disable, + bool pullup, u32 val) +{ + return mtk_pinconf_bias_set_pu_pd(dev, pin, disable, pullup, val); +} -int mtk_pinconf_bias_set_v0(struct udevice *dev, u32 pin, u32 arg, u32 val) +int mtk_pinconf_bias_set_v1(struct udevice *dev, u32 pin, bool disable, + bool pullup, u32 val) { - int err, disable, pullup; + int err; - disable = (arg == PIN_CONFIG_BIAS_DISABLE); - pullup = (arg == PIN_CONFIG_BIAS_PULL_UP); + /* try pupd_r1_r0 if pullen_pullsel return error */ + err = mtk_pinconf_bias_set_pullen_pullsel(dev, pin, disable, pullup, + val); + if (err) + return mtk_pinconf_bias_set_pupd_r1_r0(dev, pin, disable, + pullup, val); + + return err; +} + +int mtk_pinconf_bias_set_pu_pd(struct udevice *dev, u32 pin, bool disable, + bool pullup, u32 val) +{ + int err; if (disable) { err = mtk_hw_set_value(dev, pin, PINCTRL_PIN_REG_PU, 0); @@ -323,7 +371,6 @@ int mtk_pinconf_bias_set_v0(struct udevice *dev, u32 pin, u32 arg, u32 val) err = mtk_hw_set_value(dev, pin, PINCTRL_PIN_REG_PD, 0); if (err) return err; - } else { err = mtk_hw_set_value(dev, pin, PINCTRL_PIN_REG_PU, pullup); if (err) @@ -336,14 +383,10 @@ int mtk_pinconf_bias_set_v0(struct udevice *dev, u32 pin, u32 arg, u32 val) return 0; } -int mtk_pinconf_bias_set_v1(struct udevice *dev, u32 pin, u32 arg, u32 val) +int mtk_pinconf_bias_set_pullen_pullsel(struct udevice *dev, u32 pin, + bool disable, bool pullup, u32 val) { - int err, disable, pullup, r0, r1; - - disable = (arg == PIN_CONFIG_BIAS_DISABLE); - pullup = (arg == PIN_CONFIG_BIAS_PULL_UP); - r0 = !!(val & 1); - r1 = !!(val & 2); + int err; if (disable) { err = mtk_hw_set_value(dev, pin, PINCTRL_PIN_REG_PULLEN, 0); @@ -359,16 +402,62 @@ int mtk_pinconf_bias_set_v1(struct udevice *dev, u32 pin, u32 arg, u32 val) return err; } - /* Also set PUPD/R0/R1 if the pin has them */ - err = mtk_hw_set_value(dev, pin, PINCTRL_PIN_REG_PUPD, !pullup); - if (err != -EINVAL) { - mtk_hw_set_value(dev, pin, PINCTRL_PIN_REG_R0, r0); - mtk_hw_set_value(dev, pin, PINCTRL_PIN_REG_R1, r1); + return 0; +} + +int mtk_pinconf_bias_set_pupd_r1_r0(struct udevice *dev, u32 pin, bool disable, + bool pullup, u32 val) +{ + int err, r0, r1; + + r0 = !!(val & 1); + r1 = !!(val & 2); + + if (disable) { + pullup = 0; + r0 = 0; + r1 = 0; } + /* MTK HW PUPD bit: 1 for pull-down, 0 for pull-up */ + err = mtk_hw_set_value(dev, pin, PINCTRL_PIN_REG_PUPD, !pullup); + if (err) + return err; + + /* Also set PUPD/R0/R1 if the pin has them */ + mtk_hw_set_value(dev, pin, PINCTRL_PIN_REG_R0, r0); + mtk_hw_set_value(dev, pin, PINCTRL_PIN_REG_R1, r1); + return 0; } +int mtk_pinconf_bias_set(struct udevice *dev, u32 pin, u32 arg, u32 val) +{ + int err; + struct mtk_pinctrl_priv *priv = dev_get_priv(dev); + struct mtk_io_type_desc io_type; + int rev = priv->soc->rev; + bool disable, pullup; + + disable = (arg == PIN_CONFIG_BIAS_DISABLE); + pullup = (arg == PIN_CONFIG_BIAS_PULL_UP); + + if (!mtk_get_pin_io_type(dev, pin, &io_type)) { + if (io_type.bias_set) + err = io_type.bias_set(dev, pin, disable, pullup, + val); + else + err = -EINVAL; + + } else if (rev == MTK_PINCTRL_V0) { + err = mtk_pinconf_bias_set_v0(dev, pin, disable, pullup, val); + } else { + err = mtk_pinconf_bias_set_v1(dev, pin, disable, pullup, val); + } + + return err; +} + int mtk_pinconf_input_enable_v1(struct udevice *dev, u32 pin, u32 arg) { int err; @@ -379,6 +468,23 @@ int mtk_pinconf_input_enable_v1(struct udevice *dev, u32 pin, u32 arg) err = mtk_hw_set_value(dev, pin, PINCTRL_PIN_REG_DIR, 0); if (err) return err; + + return 0; +} + +int mtk_pinconf_input_enable(struct udevice *dev, u32 pin, u32 arg) +{ + struct mtk_pinctrl_priv *priv = dev_get_priv(dev); + struct mtk_io_type_desc io_type; + + int rev = priv->soc->rev; + + if (!mtk_get_pin_io_type(dev, pin, &io_type)) + if (io_type.input_enable) + return io_type.input_enable(dev, pin, arg); + if (rev == MTK_PINCTRL_V1) + return mtk_pinconf_input_enable_v1(dev, pin, arg); + return 0; } @@ -410,7 +516,6 @@ int mtk_pinconf_drive_set_v0(struct udevice *dev, u32 pin, u32 arg) return 0; } - int mtk_pinconf_drive_set_v1(struct udevice *dev, u32 pin, u32 arg) { struct mtk_pinctrl_priv *priv = dev_get_priv(dev); @@ -429,21 +534,37 @@ int mtk_pinconf_drive_set_v1(struct udevice *dev, u32 pin, u32 arg) return 0; } +int mtk_pinconf_drive_set(struct udevice *dev, u32 pin, u32 arg) +{ + int err; + struct mtk_pinctrl_priv *priv = dev_get_priv(dev); + struct mtk_io_type_desc io_type; + int rev = priv->soc->rev; + + if (!mtk_get_pin_io_type(dev, pin, &io_type)) { + if (io_type.drive_set) + err = io_type.drive_set(dev, pin, arg); + else + err = -EINVAL; + } else if (rev == MTK_PINCTRL_V0) { + err = mtk_pinconf_drive_set_v0(dev, pin, arg); + } else { + err = mtk_pinconf_drive_set_v1(dev, pin, arg); + } + + return err; +} + static int mtk_pinconf_set(struct udevice *dev, unsigned int pin, unsigned int param, unsigned int arg) { int err = 0; - struct mtk_pinctrl_priv *priv = dev_get_priv(dev); - int rev = priv->soc->rev; switch (param) { case PIN_CONFIG_BIAS_DISABLE: case PIN_CONFIG_BIAS_PULL_UP: case PIN_CONFIG_BIAS_PULL_DOWN: - if (rev == MTK_PINCTRL_V0) - err = mtk_pinconf_bias_set_v0(dev, pin, param, arg); - else - err = mtk_pinconf_bias_set_v1(dev, pin, param, arg); + err = mtk_pinconf_bias_set(dev, pin, param, arg); if (err) goto err; break; @@ -456,8 +577,7 @@ static int mtk_pinconf_set(struct udevice *dev, unsigned int pin, goto err; break; case PIN_CONFIG_INPUT_ENABLE: - if (rev == MTK_PINCTRL_V1) - err = mtk_pinconf_input_enable_v1(dev, pin, param); + err = mtk_pinconf_input_enable(dev, pin, param); if (err) goto err; break; @@ -486,10 +606,7 @@ static int mtk_pinconf_set(struct udevice *dev, unsigned int pin, goto err; break; case PIN_CONFIG_DRIVE_STRENGTH: - if (rev == MTK_PINCTRL_V0) - err = mtk_pinconf_drive_set_v0(dev, pin, arg); - else - err = mtk_pinconf_drive_set_v1(dev, pin, arg); + err = mtk_pinconf_drive_set(dev, pin, arg); if (err) goto err; break; @@ -656,13 +773,23 @@ int mtk_pinctrl_common_probe(struct udevice *dev, { struct mtk_pinctrl_priv *priv = dev_get_priv(dev); int ret = 0; - - priv->base = dev_read_addr_ptr(dev); - if (!priv->base) - return -EINVAL; + u32 i = 0; + fdt_addr_t addr; + u32 base_calc = soc->base_calc; + u32 nbase_names = soc->nbase_names; priv->soc = soc; + if (!base_calc) + nbase_names = 1; + + for (i = 0; i < nbase_names; i++) { + addr = devfdt_get_addr_index(dev, i); + if (addr == FDT_ADDR_T_NONE) + return -EINVAL; + priv->base[i] = (void __iomem *)addr; + } + #if CONFIG_IS_ENABLED(DM_GPIO) || \ (defined(CONFIG_SPL_BUILD) && defined(CONFIG_SPL_GPIO)) ret = mtk_gpiochip_register(dev); diff --git a/drivers/pinctrl/mediatek/pinctrl-mtk-common.h b/drivers/pinctrl/mediatek/pinctrl-mtk-common.h index 5e51a9a90c1..0d9596fa72c 100644 --- a/drivers/pinctrl/mediatek/pinctrl-mtk-common.h +++ b/drivers/pinctrl/mediatek/pinctrl-mtk-common.h @@ -8,12 +8,19 @@ #define MTK_PINCTRL_V0 0x0 #define MTK_PINCTRL_V1 0x1 +#define BASE_CALC_NONE 0 +#define MAX_BASE_CALC 10 #define MTK_RANGE(_a) { .range = (_a), .nranges = ARRAY_SIZE(_a), } -#define MTK_PIN(_number, _name, _drv_n) { \ + +#define MTK_PIN(_number, _name, _drv_n) \ + MTK_TYPED_PIN(_number, _name, _drv_n, IO_TYPE_DEFAULT) + +#define MTK_TYPED_PIN(_number, _name, _drv_n, _io_n) { \ .number = _number, \ .name = _name, \ .drv_n = _drv_n, \ + .io_n = _io_n, \ } #define PINCTRL_PIN_GROUP(name, id) \ @@ -24,10 +31,11 @@ id##_funcs, \ } -#define PIN_FIELD_CALC(_s_pin, _e_pin, _s_addr, _x_addrs, _s_bit, \ - _x_bits, _sz_reg, _fixed) { \ +#define PIN_FIELD_BASE_CALC(_s_pin, _e_pin, _i_base, _s_addr, _x_addrs, \ + _s_bit, _x_bits, _sz_reg, _fixed) { \ .s_pin = _s_pin, \ .e_pin = _e_pin, \ + .i_base = _i_base, \ .s_addr = _s_addr, \ .x_addrs = _x_addrs, \ .s_bit = _s_bit, \ @@ -36,6 +44,11 @@ .fixed = _fixed, \ } +#define PIN_FIELD_CALC(_s_pin, _e_pin, _s_addr, _x_addrs, _s_bit, \ + _x_bits, _sz_reg, _fixed) \ + PIN_FIELD_BASE_CALC(_s_pin, _e_pin, BASE_CALC_NONE, _s_addr, \ + _x_addrs, _s_bit, _x_bits, _sz_reg, _fixed) + /* List these attributes which could be modified for the pin */ enum { PINCTRL_PIN_REG_MODE, @@ -67,9 +80,22 @@ enum { DRV_GRP4, }; +/* Group the pins by the io type */ +enum { + IO_TYPE_DEFAULT, + IO_TYPE_GRP0, + IO_TYPE_GRP1, + IO_TYPE_GRP2, + IO_TYPE_GRP3, + IO_TYPE_GRP4, + IO_TYPE_GRP5, + IO_TYPE_GRP6, +}; + /** * struct mtk_pin_field - the structure that holds the information of the field * used to describe the attribute for the pin + * @index: the index pointing to the entry in base address list * @offset: the register offset relative to the base address * @mask: the mask used to filter out the field from the register * @bitpos: the start bit relative to the register @@ -77,6 +103,7 @@ enum { next register */ struct mtk_pin_field { + u8 index; u32 offset; u32 mask; u8 bitpos; @@ -88,6 +115,7 @@ struct mtk_pin_field { * the guide used to look up the relevant field * @s_pin: the start pin within the range * @e_pin: the end pin within the range + * @i_base: the index pointing to the entry in base address list * @s_addr: the start address for the range * @x_addrs: the address distance between two consecutive registers * within the range @@ -101,6 +129,7 @@ struct mtk_pin_field { struct mtk_pin_field_calc { u16 s_pin; u16 e_pin; + u8 i_base; u32 s_addr; u8 x_addrs; u8 s_bit; @@ -127,11 +156,13 @@ struct mtk_pin_reg_calc { * @number: unique pin number from the global pin number space * @name: name for this pin * @drv_n: the index with the driving group + * @io_n: the index with the io type */ struct mtk_pin_desc { unsigned int number; const char *name; u8 drv_n; + u8 io_n; }; /** @@ -160,6 +191,21 @@ struct mtk_function_desc { int num_group_names; }; +/** + * struct mtk_io_type_desc - io class descriptor for specific pins + * @name: name of the io class + */ +struct mtk_io_type_desc { + const char *name; +#if CONFIG_IS_ENABLED(PINCONF) + /* Specific pinconfig operations */ + int (*bias_set)(struct udevice *dev, u32 pin, bool disable, + bool pullup, u32 val); + int (*drive_set)(struct udevice *dev, u32 pin, u32 arg); + int (*input_enable)(struct udevice *dev, u32 pin, u32 arg); +#endif +}; + /* struct mtk_pin_soc - the structure that holds SoC-specific data */ struct mtk_pinctrl_soc { const char *name; @@ -170,8 +216,13 @@ struct mtk_pinctrl_soc { int ngrps; const struct mtk_function_desc *funcs; int nfuncs; + const struct mtk_io_type_desc *io_type; + u8 ntype; int gpio_mode; + const char * const *base_names; + unsigned int nbase_names; int rev; + int base_calc; }; /** @@ -181,7 +232,7 @@ struct mtk_pinctrl_soc { * @soc: SoC specific data */ struct mtk_pinctrl_priv { - void __iomem *base; + void __iomem *base[MAX_BASE_CALC]; struct mtk_pinctrl_soc *soc; }; @@ -189,7 +240,26 @@ extern const struct pinctrl_ops mtk_pinctrl_ops; /* A common read-modify-write helper for MediaTek chips */ void mtk_rmw(struct udevice *dev, u32 reg, u32 mask, u32 set); +void mtk_i_rmw(struct udevice *dev, u8 i, u32 reg, u32 mask, u32 set); int mtk_pinctrl_common_probe(struct udevice *dev, struct mtk_pinctrl_soc *soc); +#if CONFIG_IS_ENABLED(PINCONF) + +int mtk_pinconf_bias_set_pu_pd(struct udevice *dev, u32 pin, bool disable, + bool pullup, u32 val); +int mtk_pinconf_bias_set_pullen_pullsel(struct udevice *dev, u32 pin, + bool disable, bool pullup, u32 val); +int mtk_pinconf_bias_set_pupd_r1_r0(struct udevice *dev, u32 pin, bool disable, + bool pullup, u32 val); +int mtk_pinconf_bias_set_v0(struct udevice *dev, u32 pin, bool disable, + bool pullup, u32 val); +int mtk_pinconf_bias_set_v1(struct udevice *dev, u32 pin, bool disable, + bool pullup, u32 val); +int mtk_pinconf_input_enable_v1(struct udevice *dev, u32 pin, u32 arg); +int mtk_pinconf_drive_set_v0(struct udevice *dev, u32 pin, u32 arg); +int mtk_pinconf_drive_set_v1(struct udevice *dev, u32 pin, u32 arg); + +#endif + #endif /* __PINCTRL_MEDIATEK_H__ */ diff --git a/drivers/serial/Kconfig b/drivers/serial/Kconfig index 5dcf5c019da..26fa498bbbb 100644 --- a/drivers/serial/Kconfig +++ b/drivers/serial/Kconfig @@ -1016,6 +1016,15 @@ config MPC8XX_CONS depends on MPC8xx default y +config NPCM_SERIAL + bool "Nuvoton NPCM UART driver" + depends on DM_SERIAL + help + Select this to enable UART support for Nuvoton BMCs + (NPCM7xx and NPCM8xx). + The driver enables the onboard serial port with 8-N-1 + configuration. + config XEN_SERIAL bool "XEN serial support" depends on XEN diff --git a/drivers/serial/Makefile b/drivers/serial/Makefile index d8e26d72ea2..51de06a78c5 100644 --- a/drivers/serial/Makefile +++ b/drivers/serial/Makefile @@ -66,6 +66,7 @@ obj-$(CONFIG_MSM_GENI_SERIAL) += serial_msm_geni.o obj-$(CONFIG_MVEBU_A3700_UART) += serial_mvebu_a3700.o obj-$(CONFIG_MPC8XX_CONS) += serial_mpc8xx.o obj-$(CONFIG_NULLDEV_SERIAL) += serial_nulldev.o +obj-$(CONFIG_NPCM_SERIAL) += serial_npcm.o obj-$(CONFIG_OCTEON_SERIAL_BOOTCMD) += serial_octeon_bootcmd.o obj-$(CONFIG_OCTEON_SERIAL_PCIE_CONSOLE) += serial_octeon_pcie_console.o obj-$(CONFIG_OWL_SERIAL) += serial_owl.o diff --git a/drivers/serial/serial_npcm.c b/drivers/serial/serial_npcm.c new file mode 100644 index 00000000000..76ac7cb80db --- /dev/null +++ b/drivers/serial/serial_npcm.c @@ -0,0 +1,157 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2021 Nuvoton Technology Corp. + */ + +#include <common.h> +#include <clk.h> +#include <dm.h> +#include <serial.h> + +struct npcm_uart { + union { + u32 rbr; /* Receive Buffer Register */ + u32 thr; /* Transmit Holding Register */ + u32 dll; /* Divisor Latch (Low Byte) Register */ + }; + union { + u32 ier; /* Interrupt Enable Register */ + u32 dlm; /* Divisor Latch (Low Byte) Register */ + }; + union { + u32 iir; /* Interrupt Identification Register */ + u32 fcr; /* FIFO Control Register */ + }; + u32 lcr; /* Line Control Register */ + u32 mcr; /* Modem Control Register */ + u32 lsr; /* Line Status Control Register */ + u32 msr; /* Modem Status Register */ + u32 tor; /* Timeout Register */ +}; + +#define LCR_WLS_8BITS 3 /* 8-bit word length select */ +#define FCR_TFR BIT(2) /* TxFIFO reset */ +#define FCR_RFR BIT(1) /* RxFIFO reset */ +#define FCR_FME BIT(0) /* FIFO mode enable */ +#define LSR_THRE BIT(5) /* Status of TxFIFO empty */ +#define LSR_RFDR BIT(0) /* Status of RxFIFO data ready */ +#define LCR_DLAB BIT(7) /* Divisor latch access bit */ + +struct npcm_serial_plat { + struct npcm_uart *reg; + u32 uart_clk; /* frequency of uart clock source */ +}; + +static int npcm_serial_pending(struct udevice *dev, bool input) +{ + struct npcm_serial_plat *plat = dev_get_plat(dev); + struct npcm_uart *uart = plat->reg; + + if (input) + return readb(&uart->lsr) & LSR_RFDR ? 1 : 0; + else + return readb(&uart->lsr) & LSR_THRE ? 0 : 1; +} + +static int npcm_serial_putc(struct udevice *dev, const char ch) +{ + struct npcm_serial_plat *plat = dev_get_plat(dev); + struct npcm_uart *uart = plat->reg; + + if (!(readb(&uart->lsr) & LSR_THRE)) + return -EAGAIN; + + writeb(ch, &uart->thr); + + return 0; +} + +static int npcm_serial_getc(struct udevice *dev) +{ + struct npcm_serial_plat *plat = dev_get_plat(dev); + struct npcm_uart *uart = plat->reg; + + if (!(readb(&uart->lsr) & LSR_RFDR)) + return -EAGAIN; + + return readb(&uart->rbr); +} + +static int npcm_serial_setbrg(struct udevice *dev, int baudrate) +{ + struct npcm_serial_plat *plat = dev_get_plat(dev); + struct npcm_uart *uart = plat->reg; + u16 divisor; + + /* BaudOut = UART Clock / (16 * [Divisor + 2]) */ + divisor = DIV_ROUND_CLOSEST(plat->uart_clk, 16 * baudrate + 2) - 2; + + setbits_8(&uart->lcr, LCR_DLAB); + writeb(divisor & 0xff, &uart->dll); + writeb(divisor >> 8, &uart->dlm); + clrbits_8(&uart->lcr, LCR_DLAB); + + return 0; +} + +static int npcm_serial_probe(struct udevice *dev) +{ + struct npcm_serial_plat *plat = dev_get_plat(dev); + struct npcm_uart *uart = plat->reg; + struct clk clk, parent; + u32 freq; + int ret; + + plat->reg = dev_read_addr_ptr(dev); + freq = dev_read_u32_default(dev, "clock-frequency", 0); + + ret = clk_get_by_index(dev, 0, &clk); + if (ret < 0) + return ret; + + ret = clk_get_by_index(dev, 1, &parent); + if (!ret) { + ret = clk_set_parent(&clk, &parent); + if (ret) + return ret; + } + + ret = clk_set_rate(&clk, freq); + if (ret < 0) + return ret; + plat->uart_clk = ret; + + /* Disable all interrupt */ + writeb(0, &uart->ier); + + /* Set 8 bit, 1 stop, no parity */ + writeb(LCR_WLS_8BITS, &uart->lcr); + + /* Reset RX/TX FIFO */ + writeb(FCR_FME | FCR_RFR | FCR_TFR, &uart->fcr); + + return 0; +} + +static const struct dm_serial_ops npcm_serial_ops = { + .getc = npcm_serial_getc, + .setbrg = npcm_serial_setbrg, + .putc = npcm_serial_putc, + .pending = npcm_serial_pending, +}; + +static const struct udevice_id npcm_serial_ids[] = { + { .compatible = "nuvoton,npcm750-uart" }, + { .compatible = "nuvoton,npcm845-uart" }, + { } +}; + +U_BOOT_DRIVER(serial_npcm) = { + .name = "serial_npcm", + .id = UCLASS_SERIAL, + .of_match = npcm_serial_ids, + .plat_auto = sizeof(struct npcm_serial_plat), + .probe = npcm_serial_probe, + .ops = &npcm_serial_ops, + .flags = DM_FLAG_PRE_RELOC, +}; diff --git a/drivers/timer/Kconfig b/drivers/timer/Kconfig index 2eda3bcca5b..7b8ab56ed32 100644 --- a/drivers/timer/Kconfig +++ b/drivers/timer/Kconfig @@ -172,6 +172,15 @@ config NOMADIK_MTU_TIMER The MTU provides 4 decrementing free-running timers. At the moment, only the first timer is used by the driver. +config NPCM_TIMER + bool "Nuvoton NPCM timer support" + depends on TIMER + help + Select this to enable a timer on Nuvoton NPCM SoCs. + NPCM timer module has 5 down-counting timers, only the first timer + is used to implement timer ops. No support for early timer and + boot timer. + config OMAP_TIMER bool "Omap timer support" depends on TIMER diff --git a/drivers/timer/Makefile b/drivers/timer/Makefile index 35cc490db1e..b2f002d5978 100644 --- a/drivers/timer/Makefile +++ b/drivers/timer/Makefile @@ -14,6 +14,7 @@ obj-$(CONFIG_CADENCE_TTC_TIMER) += cadence-ttc.o obj-$(CONFIG_DESIGNWARE_APB_TIMER) += dw-apb-timer.o obj-$(CONFIG_MPC83XX_TIMER) += mpc83xx_timer.o obj-$(CONFIG_NOMADIK_MTU_TIMER) += nomadik-mtu-timer.o +obj-$(CONFIG_NPCM_TIMER) += npcm-timer.o obj-$(CONFIG_OMAP_TIMER) += omap-timer.o obj-$(CONFIG_RENESAS_OSTM_TIMER) += ostm_timer.o obj-$(CONFIG_RISCV_TIMER) += riscv_timer.o diff --git a/drivers/timer/npcm-timer.c b/drivers/timer/npcm-timer.c new file mode 100644 index 00000000000..4562a6f2311 --- /dev/null +++ b/drivers/timer/npcm-timer.c @@ -0,0 +1,115 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2022 Nuvoton Technology Corp. + */ + +#include <common.h> +#include <clk.h> +#include <dm.h> +#include <timer.h> +#include <asm/io.h> + +#define NPCM_TIMER_CLOCK_RATE 1000000UL /* 1MHz timer */ +#define NPCM_TIMER_INPUT_RATE 25000000UL /* Rate of input clock */ +#define NPCM_TIMER_TDR_MASK GENMASK(23, 0) +#define NPCM_TIMER_MAX_VAL NPCM_TIMER_TDR_MASK /* max counter value */ + +/* Register offsets */ +#define TCR0 0x0 /* Timer Control and Status Register */ +#define TICR0 0x8 /* Timer Initial Count Register */ +#define TDR0 0x10 /* Timer Data Register */ + +/* TCR fields */ +#define TCR_MODE_PERIODIC BIT(27) +#define TCR_EN BIT(30) +#define TCR_PRESCALE (NPCM_TIMER_INPUT_RATE / NPCM_TIMER_CLOCK_RATE - 1) + +enum input_clock_type { + INPUT_CLOCK_FIXED, /* input clock rate is fixed */ + INPUT_CLOCK_NON_FIXED +}; + +/** + * struct npcm_timer_priv - private data for npcm timer driver + * npcm timer is a 24-bits down-counting timer. + * + * @last_count: last hw counter value + * @counter: the value to be returned for get_count ops + */ +struct npcm_timer_priv { + void __iomem *base; + u32 last_count; + u64 counter; +}; + +static u64 npcm_timer_get_count(struct udevice *dev) +{ + struct npcm_timer_priv *priv = dev_get_priv(dev); + u32 val; + + /* The timer is counting down */ + val = readl(priv->base + TDR0) & NPCM_TIMER_TDR_MASK; + if (val <= priv->last_count) + priv->counter += priv->last_count - val; + else + priv->counter += priv->last_count + (NPCM_TIMER_MAX_VAL + 1 - val); + priv->last_count = val; + + return priv->counter; +} + +static int npcm_timer_probe(struct udevice *dev) +{ + struct timer_dev_priv *uc_priv = dev_get_uclass_priv(dev); + struct npcm_timer_priv *priv = dev_get_priv(dev); + enum input_clock_type type = dev_get_driver_data(dev); + struct clk clk; + int ret; + + priv->base = dev_read_addr_ptr(dev); + if (!priv->base) + return -EINVAL; + uc_priv->clock_rate = NPCM_TIMER_CLOCK_RATE; + + if (type == INPUT_CLOCK_NON_FIXED) { + ret = clk_get_by_index(dev, 0, &clk); + if (ret < 0) + return ret; + + ret = clk_set_rate(&clk, NPCM_TIMER_INPUT_RATE); + if (ret < 0) + return ret; + } + + /* + * Configure timer and start + * periodic mode + * timer clock rate = input clock / prescale + */ + writel(0, priv->base + TCR0); + writel(NPCM_TIMER_MAX_VAL, priv->base + TICR0); + writel(TCR_EN | TCR_MODE_PERIODIC | TCR_PRESCALE, + priv->base + TCR0); + + return 0; +} + +static const struct timer_ops npcm_timer_ops = { + .get_count = npcm_timer_get_count, +}; + +static const struct udevice_id npcm_timer_ids[] = { + { .compatible = "nuvoton,npcm845-timer", .data = INPUT_CLOCK_FIXED}, + { .compatible = "nuvoton,npcm750-timer", .data = INPUT_CLOCK_NON_FIXED}, + {} +}; + +U_BOOT_DRIVER(npcm_timer) = { + .name = "npcm_timer", + .id = UCLASS_TIMER, + .of_match = npcm_timer_ids, + .priv_auto = sizeof(struct npcm_timer_priv), + .probe = npcm_timer_probe, + .ops = &npcm_timer_ops, + .flags = DM_FLAG_PRE_RELOC, +}; |