summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
Diffstat (limited to 'drivers')
-rw-r--r--drivers/clk/Makefile1
-rw-r--r--drivers/clk/nuvoton/Makefile2
-rw-r--r--drivers/clk/nuvoton/clk_npcm.c299
-rw-r--r--drivers/clk/nuvoton/clk_npcm.h105
-rw-r--r--drivers/clk/nuvoton/clk_npcm7xx.c95
-rw-r--r--drivers/gpio/Kconfig7
-rw-r--r--drivers/gpio/Makefile1
-rw-r--r--drivers/gpio/npcm_gpio.c123
-rw-r--r--drivers/pinctrl/mediatek/pinctrl-mtk-common.c221
-rw-r--r--drivers/pinctrl/mediatek/pinctrl-mtk-common.h78
-rw-r--r--drivers/serial/Kconfig9
-rw-r--r--drivers/serial/Makefile1
-rw-r--r--drivers/serial/serial_npcm.c157
-rw-r--r--drivers/timer/Kconfig9
-rw-r--r--drivers/timer/Makefile1
-rw-r--r--drivers/timer/npcm-timer.c115
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,
+};