summaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorTom Rini <trini@konsulko.com>2021-01-12 09:32:48 -0500
committerTom Rini <trini@konsulko.com>2021-01-12 09:32:48 -0500
commitee6726be4f0dccb612f0193c62ca149164c8a5af (patch)
treec58716b51bec487da0c5ac8929bc072549c90b07 /drivers
parent996f217ea368ecaef84863bb29699c0e185b9be7 (diff)
parentea3f5348063ebe4f41be7d1ba3ef0afe56a04a40 (diff)
downloadu-boot-ee6726be4f0dccb612f0193c62ca149164c8a5af.tar.gz
u-boot-ee6726be4f0dccb612f0193c62ca149164c8a5af.tar.xz
u-boot-ee6726be4f0dccb612f0193c62ca149164c8a5af.zip
Merge tag 'ti-v2021.04-rc1' of https://gitlab.denx.de/u-boot/custodians/u-boot-ti
- DM support for OMAP PWM backlight - USB host mode support for AM654 - Minor SPI fixes - Add support k2g ice board with 1GHz silicon - Fix GTC programming for K3 devices
Diffstat (limited to 'drivers')
-rw-r--r--drivers/bus/Kconfig13
-rw-r--r--drivers/bus/Makefile2
-rw-r--r--drivers/bus/ti-pwmss.c21
-rw-r--r--drivers/bus/ti-sysc.c166
-rw-r--r--drivers/clk/Kconfig9
-rw-r--r--drivers/clk/Makefile2
-rw-r--r--drivers/clk/clk-divider.c24
-rw-r--r--drivers/clk/clk-uclass.c15
-rw-r--r--drivers/clk/clk_sandbox.c17
-rw-r--r--drivers/clk/clk_sandbox_test.c10
-rw-r--r--drivers/clk/ti/Kconfig43
-rw-r--r--drivers/clk/ti/Makefile13
-rw-r--r--drivers/clk/ti/clk-am3-dpll-x2.c79
-rw-r--r--drivers/clk/ti/clk-am3-dpll.c268
-rw-r--r--drivers/clk/ti/clk-ctrl.c154
-rw-r--r--drivers/clk/ti/clk-divider.c381
-rw-r--r--drivers/clk/ti/clk-gate.c93
-rw-r--r--drivers/clk/ti/clk-mux.c253
-rw-r--r--drivers/clk/ti/clk-sci.c (renamed from drivers/clk/clk-ti-sci.c)0
-rw-r--r--drivers/clk/ti/clk.c34
-rw-r--r--drivers/clk/ti/clk.h13
-rw-r--r--drivers/clk/ti/omap4-cm.c22
-rw-r--r--drivers/core/Kconfig12
-rw-r--r--drivers/core/fdtaddr.c2
-rw-r--r--drivers/core/of_addr.c13
-rw-r--r--drivers/core/ofnode.c7
-rw-r--r--drivers/core/read.c6
-rw-r--r--drivers/core/root.c3
-rw-r--r--drivers/gpio/tca642x.c49
-rw-r--r--drivers/pwm/Kconfig7
-rw-r--r--drivers/pwm/Makefile1
-rw-r--r--drivers/pwm/pwm-ti-ehrpwm.c468
-rw-r--r--drivers/remoteproc/ti_k3_arm64_rproc.c15
-rw-r--r--drivers/spi/omap3_spi.c17
-rw-r--r--drivers/spi/ti_qspi.c4
-rw-r--r--drivers/timer/omap-timer.c6
-rw-r--r--drivers/video/Kconfig5
-rw-r--r--drivers/video/Makefile2
-rw-r--r--drivers/video/ti/Kconfig8
-rw-r--r--drivers/video/ti/Makefile10
-rw-r--r--drivers/video/ti/am335x-fb.c (renamed from drivers/video/am335x-fb.c)341
-rw-r--r--drivers/video/ti/am335x-fb.h (renamed from drivers/video/am335x-fb.h)4
-rw-r--r--drivers/video/ti/tilcdc-panel.c172
-rw-r--r--drivers/video/ti/tilcdc-panel.h14
-rw-r--r--drivers/video/ti/tilcdc.c425
-rw-r--r--drivers/video/ti/tilcdc.h38
46 files changed, 2874 insertions, 387 deletions
diff --git a/drivers/bus/Kconfig b/drivers/bus/Kconfig
index 07a33c6287..d742ed333b 100644
--- a/drivers/bus/Kconfig
+++ b/drivers/bus/Kconfig
@@ -5,6 +5,19 @@
menu "Bus devices"
+config TI_PWMSS
+ bool
+ default y if ARCH_OMAP2PLUS && PWM_TI_EHRPWM
+ help
+ PWM Subsystem driver support for AM33xx SOC.
+
+config TI_SYSC
+ bool "TI sysc interconnect target module driver"
+ depends on ARCH_OMAP2PLUS
+ help
+ Generic driver for Texas Instruments interconnect target module
+ found on many TI SoCs.
+
config UNIPHIER_SYSTEM_BUS
bool "UniPhier System Bus driver"
depends on ARCH_UNIPHIER
diff --git a/drivers/bus/Makefile b/drivers/bus/Makefile
index 0b97fc1f8b..a2e71c7b3b 100644
--- a/drivers/bus/Makefile
+++ b/drivers/bus/Makefile
@@ -3,4 +3,6 @@
# Makefile for the bus drivers.
#
+obj-$(CONFIG_TI_PWMSS) += ti-pwmss.o
+obj-$(CONFIG_TI_SYSC) += ti-sysc.o
obj-$(CONFIG_UNIPHIER_SYSTEM_BUS) += uniphier-system-bus.o
diff --git a/drivers/bus/ti-pwmss.c b/drivers/bus/ti-pwmss.c
new file mode 100644
index 0000000000..265b4cf83b
--- /dev/null
+++ b/drivers/bus/ti-pwmss.c
@@ -0,0 +1,21 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Pulse-Width Modulation Subsystem (pwmss)
+ *
+ * Copyright (C) 2020 Dario Binacchi <dariobin@libero.it>
+ */
+
+#include <common.h>
+#include <dm.h>
+
+static const struct udevice_id ti_pwmss_ids[] = {
+ {.compatible = "ti,am33xx-pwmss"},
+ {}
+};
+
+U_BOOT_DRIVER(ti_pwmss) = {
+ .name = "ti_pwmss",
+ .id = UCLASS_SIMPLE_BUS,
+ .of_match = ti_pwmss_ids,
+ .bind = dm_scan_fdt_dev,
+};
diff --git a/drivers/bus/ti-sysc.c b/drivers/bus/ti-sysc.c
new file mode 100644
index 0000000000..4e3d610300
--- /dev/null
+++ b/drivers/bus/ti-sysc.c
@@ -0,0 +1,166 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Texas Instruments sysc interconnect target driver
+ *
+ * Copyright (C) 2020 Dario Binacchi <dariobin@libero.it>
+ */
+
+#include <common.h>
+#include <clk.h>
+#include <dm.h>
+#include <dm/device_compat.h>
+
+enum ti_sysc_clocks {
+ TI_SYSC_FCK,
+ TI_SYSC_ICK,
+ TI_SYSC_MAX_CLOCKS,
+};
+
+static const char *const clock_names[] = {"fck", "ick"};
+
+struct ti_sysc_priv {
+ int clocks_count;
+ struct clk clocks[TI_SYSC_MAX_CLOCKS];
+};
+
+static const struct udevice_id ti_sysc_ids[] = {
+ {.compatible = "ti,sysc-omap2"},
+ {.compatible = "ti,sysc-omap4"},
+ {.compatible = "ti,sysc-omap4-simple"},
+ {.compatible = "ti,sysc-omap3430-sr"},
+ {.compatible = "ti,sysc-omap3630-sr"},
+ {.compatible = "ti,sysc-omap4-sr"},
+ {.compatible = "ti,sysc-omap3-sham"},
+ {.compatible = "ti,sysc-omap-aes"},
+ {.compatible = "ti,sysc-mcasp"},
+ {.compatible = "ti,sysc-usb-host-fs"},
+ {}
+};
+
+static int ti_sysc_get_one_clock(struct udevice *dev, enum ti_sysc_clocks index)
+{
+ struct ti_sysc_priv *priv = dev_get_priv(dev);
+ const char *name;
+ int err;
+
+ switch (index) {
+ case TI_SYSC_FCK:
+ break;
+ case TI_SYSC_ICK:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ name = clock_names[index];
+
+ err = clk_get_by_name(dev, name, &priv->clocks[index]);
+ if (err) {
+ if (err == -ENODATA)
+ return 0;
+
+ dev_err(dev, "failed to get %s clock\n", name);
+ return err;
+ }
+
+ return 0;
+}
+
+static int ti_sysc_put_clocks(struct udevice *dev)
+{
+ struct ti_sysc_priv *priv = dev_get_priv(dev);
+ int err;
+
+ err = clk_release_all(priv->clocks, priv->clocks_count);
+ if (err)
+ dev_err(dev, "failed to release all clocks\n");
+
+ return err;
+}
+
+static int ti_sysc_get_clocks(struct udevice *dev)
+{
+ struct ti_sysc_priv *priv = dev_get_priv(dev);
+ int i, err;
+
+ for (i = 0; i < TI_SYSC_MAX_CLOCKS; i++) {
+ err = ti_sysc_get_one_clock(dev, i);
+ if (!err)
+ priv->clocks_count++;
+ else if (err != -ENOENT)
+ return err;
+ }
+
+ return 0;
+}
+
+static int ti_sysc_child_post_remove(struct udevice *dev)
+{
+ struct ti_sysc_priv *priv = dev_get_priv(dev->parent);
+ int i, err;
+
+ for (i = 0; i < priv->clocks_count; i++) {
+ err = clk_disable(&priv->clocks[i]);
+ if (err) {
+ dev_err(dev->parent, "failed to disable %s clock\n",
+ clock_names[i]);
+ return err;
+ }
+ }
+
+ return 0;
+}
+
+static int ti_sysc_child_pre_probe(struct udevice *dev)
+{
+ struct ti_sysc_priv *priv = dev_get_priv(dev->parent);
+ int i, err;
+
+ for (i = 0; i < priv->clocks_count; i++) {
+ err = clk_enable(&priv->clocks[i]);
+ if (err) {
+ dev_err(dev->parent, "failed to enable %s clock\n",
+ clock_names[i]);
+ return err;
+ }
+ }
+
+ return 0;
+}
+
+static int ti_sysc_remove(struct udevice *dev)
+{
+ return ti_sysc_put_clocks(dev);
+}
+
+static int ti_sysc_probe(struct udevice *dev)
+{
+ int err;
+
+ err = ti_sysc_get_clocks(dev);
+ if (err)
+ goto clocks_err;
+
+ return 0;
+
+clocks_err:
+ ti_sysc_put_clocks(dev);
+ return err;
+}
+
+UCLASS_DRIVER(ti_sysc) = {
+ .id = UCLASS_SIMPLE_BUS,
+ .name = "ti_sysc",
+ .post_bind = dm_scan_fdt_dev
+};
+
+U_BOOT_DRIVER(ti_sysc) = {
+ .name = "ti_sysc",
+ .id = UCLASS_SIMPLE_BUS,
+ .of_match = ti_sysc_ids,
+ .probe = ti_sysc_probe,
+ .remove = ti_sysc_remove,
+ .child_pre_probe = ti_sysc_child_pre_probe,
+ .child_post_remove = ti_sysc_child_post_remove,
+ .priv_auto = sizeof(struct ti_sysc_priv)
+};
diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig
index 4dfbad7986..db06f276ec 100644
--- a/drivers/clk/Kconfig
+++ b/drivers/clk/Kconfig
@@ -98,14 +98,6 @@ config CLK_STM32F
This clock driver adds support for RCC clock management
for STM32F4 and STM32F7 SoCs.
-config CLK_TI_SCI
- bool "TI System Control Interface (TI SCI) clock driver"
- depends on CLK && TI_SCI_PROTOCOL && OF_CONTROL
- help
- This enables the clock driver support over TI System Control Interface
- available on some new TI's SoCs. If you wish to use clock resources
- managed by the TI System Controller, say Y here. Otherwise, say N.
-
config CLK_HSDK
bool "Enable cgu clock driver for HSDK boards"
depends on CLK && TARGET_HSDK
@@ -179,6 +171,7 @@ source "drivers/clk/renesas/Kconfig"
source "drivers/clk/sunxi/Kconfig"
source "drivers/clk/sifive/Kconfig"
source "drivers/clk/tegra/Kconfig"
+source "drivers/clk/ti/Kconfig"
source "drivers/clk/uniphier/Kconfig"
config ICS8N3QV01
diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile
index d1e295ac7c..f8383e523d 100644
--- a/drivers/clk/Makefile
+++ b/drivers/clk/Makefile
@@ -14,6 +14,7 @@ obj-$(CONFIG_$(SPL_TPL_)CLK_COMPOSITE_CCF) += clk-composite.o
obj-y += analogbits/
obj-y += imx/
obj-y += tegra/
+obj-y += ti/
obj-$(CONFIG_ARCH_ASPEED) += aspeed/
obj-$(CONFIG_ARCH_MEDIATEK) += mediatek/
obj-$(CONFIG_ARCH_MTMIPS) += mtmips/
@@ -47,6 +48,5 @@ obj-$(CONFIG_SANDBOX) += clk_sandbox.o
obj-$(CONFIG_SANDBOX) += clk_sandbox_test.o
obj-$(CONFIG_SANDBOX_CLK_CCF) += clk_sandbox_ccf.o
obj-$(CONFIG_STM32H7) += clk_stm32h7.o
-obj-$(CONFIG_CLK_TI_SCI) += clk-ti-sci.o
obj-$(CONFIG_CLK_VERSAL) += clk_versal.o
obj-$(CONFIG_CLK_CDCE9XX) += clk-cdce9xx.o
diff --git a/drivers/clk/clk-divider.c b/drivers/clk/clk-divider.c
index 8f59d7fb72..9df50a5e72 100644
--- a/drivers/clk/clk-divider.c
+++ b/drivers/clk/clk-divider.c
@@ -28,8 +28,8 @@
#define UBOOT_DM_CLK_CCF_DIVIDER "ccf_clk_divider"
-static unsigned int _get_table_div(const struct clk_div_table *table,
- unsigned int val)
+unsigned int clk_divider_get_table_div(const struct clk_div_table *table,
+ unsigned int val)
{
const struct clk_div_table *clkt;
@@ -49,7 +49,7 @@ static unsigned int _get_div(const struct clk_div_table *table,
if (flags & CLK_DIVIDER_MAX_AT_ZERO)
return val ? val : clk_div_mask(width) + 1;
if (table)
- return _get_table_div(table, val);
+ return clk_divider_get_table_div(table, val);
return val + 1;
}
@@ -89,8 +89,8 @@ static ulong clk_divider_recalc_rate(struct clk *clk)
divider->flags, divider->width);
}
-static bool _is_valid_table_div(const struct clk_div_table *table,
- unsigned int div)
+bool clk_divider_is_valid_table_div(const struct clk_div_table *table,
+ unsigned int div)
{
const struct clk_div_table *clkt;
@@ -100,18 +100,18 @@ static bool _is_valid_table_div(const struct clk_div_table *table,
return false;
}
-static bool _is_valid_div(const struct clk_div_table *table, unsigned int div,
- unsigned long flags)
+bool clk_divider_is_valid_div(const struct clk_div_table *table,
+ unsigned int div, unsigned long flags)
{
if (flags & CLK_DIVIDER_POWER_OF_TWO)
return is_power_of_2(div);
if (table)
- return _is_valid_table_div(table, div);
+ return clk_divider_is_valid_table_div(table, div);
return true;
}
-static unsigned int _get_table_val(const struct clk_div_table *table,
- unsigned int div)
+unsigned int clk_divider_get_table_val(const struct clk_div_table *table,
+ unsigned int div)
{
const struct clk_div_table *clkt;
@@ -131,7 +131,7 @@ static unsigned int _get_val(const struct clk_div_table *table,
if (flags & CLK_DIVIDER_MAX_AT_ZERO)
return (div == clk_div_mask(width) + 1) ? 0 : div;
if (table)
- return _get_table_val(table, div);
+ return clk_divider_get_table_val(table, div);
return div - 1;
}
int divider_get_val(unsigned long rate, unsigned long parent_rate,
@@ -142,7 +142,7 @@ int divider_get_val(unsigned long rate, unsigned long parent_rate,
div = DIV_ROUND_UP_ULL((u64)parent_rate, rate);
- if (!_is_valid_div(table, div, flags))
+ if (!clk_divider_is_valid_div(table, div, flags))
return -EINVAL;
value = _get_val(table, div, flags, width);
diff --git a/drivers/clk/clk-uclass.c b/drivers/clk/clk-uclass.c
index 5cfd00ce77..b75056718b 100644
--- a/drivers/clk/clk-uclass.c
+++ b/drivers/clk/clk-uclass.c
@@ -523,6 +523,21 @@ long long clk_get_parent_rate(struct clk *clk)
return pclk->rate;
}
+ulong clk_round_rate(struct clk *clk, ulong rate)
+{
+ const struct clk_ops *ops;
+
+ debug("%s(clk=%p, rate=%lu)\n", __func__, clk, rate);
+ if (!clk_valid(clk))
+ return 0;
+
+ ops = clk_dev_ops(clk->dev);
+ if (!ops->round_rate)
+ return -ENOSYS;
+
+ return ops->round_rate(clk, rate);
+}
+
ulong clk_set_rate(struct clk *clk, ulong rate)
{
const struct clk_ops *ops;
diff --git a/drivers/clk/clk_sandbox.c b/drivers/clk/clk_sandbox.c
index 2c6c0e239f..b28b67b448 100644
--- a/drivers/clk/clk_sandbox.c
+++ b/drivers/clk/clk_sandbox.c
@@ -30,6 +30,22 @@ static ulong sandbox_clk_get_rate(struct clk *clk)
return priv->rate[clk->id];
}
+static ulong sandbox_clk_round_rate(struct clk *clk, ulong rate)
+{
+ struct sandbox_clk_priv *priv = dev_get_priv(clk->dev);
+
+ if (!priv->probed)
+ return -ENODEV;
+
+ if (clk->id >= SANDBOX_CLK_ID_COUNT)
+ return -EINVAL;
+
+ if (!rate)
+ return -EINVAL;
+
+ return rate;
+}
+
static ulong sandbox_clk_set_rate(struct clk *clk, ulong rate)
{
struct sandbox_clk_priv *priv = dev_get_priv(clk->dev);
@@ -103,6 +119,7 @@ static int sandbox_clk_free(struct clk *clk)
}
static struct clk_ops sandbox_clk_ops = {
+ .round_rate = sandbox_clk_round_rate,
.get_rate = sandbox_clk_get_rate,
.set_rate = sandbox_clk_set_rate,
.enable = sandbox_clk_enable,
diff --git a/drivers/clk/clk_sandbox_test.c b/drivers/clk/clk_sandbox_test.c
index e9eb738684..c4e4481508 100644
--- a/drivers/clk/clk_sandbox_test.c
+++ b/drivers/clk/clk_sandbox_test.c
@@ -86,6 +86,16 @@ ulong sandbox_clk_test_get_rate(struct udevice *dev, int id)
return clk_get_rate(sbct->clkps[id]);
}
+ulong sandbox_clk_test_round_rate(struct udevice *dev, int id, ulong rate)
+{
+ struct sandbox_clk_test *sbct = dev_get_priv(dev);
+
+ if (id < 0 || id >= SANDBOX_CLK_TEST_ID_COUNT)
+ return -EINVAL;
+
+ return clk_round_rate(sbct->clkps[id], rate);
+}
+
ulong sandbox_clk_test_set_rate(struct udevice *dev, int id, ulong rate)
{
struct sandbox_clk_test *sbct = dev_get_priv(dev);
diff --git a/drivers/clk/ti/Kconfig b/drivers/clk/ti/Kconfig
new file mode 100644
index 0000000000..2dc86d44a9
--- /dev/null
+++ b/drivers/clk/ti/Kconfig
@@ -0,0 +1,43 @@
+# SPDX-License-Identifier: GPL-2.0+
+#
+# Copyright (C) 2020 Dario Binacchi <dariobin@libero.it>
+#
+
+config CLK_TI_AM3_DPLL
+ bool "TI AM33XX Digital Phase-Locked Loop (DPLL) clock drivers"
+ depends on CLK && OF_CONTROL
+ help
+ This enables the DPLL clock drivers support on AM33XX SoCs. The DPLL
+ provides all interface clocks and functional clocks to the processor.
+
+config CLK_TI_CTRL
+ bool "TI OMAP4 clock controller"
+ depends on CLK && OF_CONTROL
+ help
+ This enables the clock controller driver support on TI's SoCs.
+
+config CLK_TI_DIVIDER
+ bool "TI divider clock driver"
+ depends on CLK && OF_CONTROL && CLK_CCF
+ help
+ This enables the divider clock driver support on TI's SoCs.
+
+config CLK_TI_GATE
+ bool "TI gate clock driver"
+ depends on CLK && OF_CONTROL
+ help
+ This enables the gate clock driver support on TI's SoCs.
+
+config CLK_TI_MUX
+ bool "TI mux clock driver"
+ depends on CLK && OF_CONTROL && CLK_CCF
+ help
+ This enables the mux clock driver support on TI's SoCs.
+
+config CLK_TI_SCI
+ bool "TI System Control Interface (TI SCI) clock driver"
+ depends on CLK && TI_SCI_PROTOCOL && OF_CONTROL
+ help
+ This enables the clock driver support over TI System Control Interface
+ available on some new TI's SoCs. If you wish to use clock resources
+ managed by the TI System Controller, say Y here. Otherwise, say N.
diff --git a/drivers/clk/ti/Makefile b/drivers/clk/ti/Makefile
new file mode 100644
index 0000000000..9f56b47736
--- /dev/null
+++ b/drivers/clk/ti/Makefile
@@ -0,0 +1,13 @@
+# SPDX-License-Identifier: GPL-2.0+
+#
+# Copyright (C) 2020 Dario Binacchi <dariobin@libero.it>
+#
+
+obj-$(CONFIG_ARCH_OMAP2PLUS) += clk.o omap4-cm.o
+
+obj-$(CONFIG_CLK_TI_AM3_DPLL) += clk-am3-dpll.o clk-am3-dpll-x2.o
+obj-$(CONFIG_CLK_TI_CTRL) += clk-ctrl.o
+obj-$(CONFIG_CLK_TI_DIVIDER) += clk-divider.o
+obj-$(CONFIG_CLK_TI_GATE) += clk-gate.o
+obj-$(CONFIG_CLK_TI_MUX) += clk-mux.o
+obj-$(CONFIG_CLK_TI_SCI) += clk-sci.o
diff --git a/drivers/clk/ti/clk-am3-dpll-x2.c b/drivers/clk/ti/clk-am3-dpll-x2.c
new file mode 100644
index 0000000000..3cf279d6a3
--- /dev/null
+++ b/drivers/clk/ti/clk-am3-dpll-x2.c
@@ -0,0 +1,79 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * TI DPLL x2 clock support
+ *
+ * Copyright (C) 2020 Dario Binacchi <dariobin@libero.it>
+ *
+ * Loosely based on Linux kernel drivers/clk/ti/dpll.c
+ */
+
+#include <common.h>
+#include <clk-uclass.h>
+#include <dm.h>
+#include <dm/device_compat.h>
+#include <linux/clk-provider.h>
+
+struct clk_ti_am3_dpll_x2_priv {
+ struct clk parent;
+};
+
+static ulong clk_ti_am3_dpll_x2_get_rate(struct clk *clk)
+{
+ struct clk_ti_am3_dpll_x2_priv *priv = dev_get_priv(clk->dev);
+ unsigned long rate;
+
+ rate = clk_get_rate(&priv->parent);
+ if (IS_ERR_VALUE(rate))
+ return rate;
+
+ rate *= 2;
+ dev_dbg(clk->dev, "rate=%ld\n", rate);
+ return rate;
+}
+
+const struct clk_ops clk_ti_am3_dpll_x2_ops = {
+ .get_rate = clk_ti_am3_dpll_x2_get_rate,
+};
+
+static int clk_ti_am3_dpll_x2_remove(struct udevice *dev)
+{
+ struct clk_ti_am3_dpll_x2_priv *priv = dev_get_priv(dev);
+ int err;
+
+ err = clk_release_all(&priv->parent, 1);
+ if (err) {
+ dev_err(dev, "failed to release parent clock\n");
+ return err;
+ }
+
+ return 0;
+}
+
+static int clk_ti_am3_dpll_x2_probe(struct udevice *dev)
+{
+ struct clk_ti_am3_dpll_x2_priv *priv = dev_get_priv(dev);
+ int err;
+
+ err = clk_get_by_index(dev, 0, &priv->parent);
+ if (err) {
+ dev_err(dev, "%s: failed to get parent clock\n", __func__);
+ return err;
+ }
+
+ return 0;
+}
+
+static const struct udevice_id clk_ti_am3_dpll_x2_of_match[] = {
+ {.compatible = "ti,am3-dpll-x2-clock"},
+ {}
+};
+
+U_BOOT_DRIVER(clk_ti_am3_dpll_x2) = {
+ .name = "ti_am3_dpll_x2_clock",
+ .id = UCLASS_CLK,
+ .of_match = clk_ti_am3_dpll_x2_of_match,
+ .probe = clk_ti_am3_dpll_x2_probe,
+ .remove = clk_ti_am3_dpll_x2_remove,
+ .priv_auto = sizeof(struct clk_ti_am3_dpll_x2_priv),
+ .ops = &clk_ti_am3_dpll_x2_ops,
+};
diff --git a/drivers/clk/ti/clk-am3-dpll.c b/drivers/clk/ti/clk-am3-dpll.c
new file mode 100644
index 0000000000..7916a24538
--- /dev/null
+++ b/drivers/clk/ti/clk-am3-dpll.c
@@ -0,0 +1,268 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * TI DPLL clock support
+ *
+ * Copyright (C) 2020 Dario Binacchi <dariobin@libero.it>
+ *
+ * Loosely based on Linux kernel drivers/clk/ti/dpll.c
+ */
+
+#include <common.h>
+#include <clk.h>
+#include <clk-uclass.h>
+#include <div64.h>
+#include <dm.h>
+#include <dm/device_compat.h>
+#include <hang.h>
+#include <asm/arch/clock.h>
+#include <asm/arch/sys_proto.h>
+#include <asm/io.h>
+
+struct clk_ti_am3_dpll_drv_data {
+ ulong max_rate;
+};
+
+struct clk_ti_am3_dpll_priv {
+ fdt_addr_t clkmode_reg;
+ fdt_addr_t idlest_reg;
+ fdt_addr_t clksel_reg;
+ struct clk clk_bypass;
+ struct clk clk_ref;
+ u16 last_rounded_mult;
+ u8 last_rounded_div;
+ ulong max_rate;
+};
+
+static ulong clk_ti_am3_dpll_round_rate(struct clk *clk, ulong rate)
+{
+ struct clk_ti_am3_dpll_priv *priv = dev_get_priv(clk->dev);
+ ulong ret, ref_rate, r;
+ int m, d, err_min, err;
+ int mult = INT_MAX, div = INT_MAX;
+
+ if (priv->max_rate && rate > priv->max_rate) {
+ dev_warn(clk->dev, "%ld is to high a rate, lowered to %ld\n",
+ rate, priv->max_rate);
+ rate = priv->max_rate;
+ }
+
+ ret = -EFAULT;
+ err = rate;
+ err_min = rate;
+ ref_rate = clk_get_rate(&priv->clk_ref);
+ for (d = 1; err_min && d <= 128; d++) {
+ for (m = 2; m <= 2047; m++) {
+ r = (ref_rate * m) / d;
+ err = abs(r - rate);
+ if (err < err_min) {
+ err_min = err;
+ ret = r;
+ mult = m;
+ div = d;
+
+ if (err == 0)
+ break;
+ } else if (r > rate) {
+ break;
+ }
+ }
+ }
+
+ priv->last_rounded_mult = mult;
+ priv->last_rounded_div = div;
+ dev_dbg(clk->dev, "rate=%ld, best_rate=%ld, mult=%d, div=%d\n", rate,
+ ret, mult, div);
+ return ret;
+}
+
+static ulong clk_ti_am3_dpll_set_rate(struct clk *clk, ulong rate)
+{
+ struct clk_ti_am3_dpll_priv *priv = dev_get_priv(clk->dev);
+ u32 v;
+ ulong round_rate;
+
+ round_rate = clk_ti_am3_dpll_round_rate(clk, rate);
+ if (IS_ERR_VALUE(round_rate))
+ return round_rate;
+
+ v = readl(priv->clksel_reg);
+
+ /* enter bypass mode */
+ clrsetbits_le32(priv->clkmode_reg, CM_CLKMODE_DPLL_DPLL_EN_MASK,
+ DPLL_EN_MN_BYPASS << CM_CLKMODE_DPLL_EN_SHIFT);
+
+ /* wait for bypass mode */
+ if (!wait_on_value(ST_DPLL_CLK_MASK, 0,
+ (void *)priv->idlest_reg, LDELAY))
+ dev_err(clk->dev, "failed bypassing dpll\n");
+
+ /* set M & N */
+ v &= ~CM_CLKSEL_DPLL_M_MASK;
+ v |= (priv->last_rounded_mult << CM_CLKSEL_DPLL_M_SHIFT) &
+ CM_CLKSEL_DPLL_M_MASK;
+
+ v &= ~CM_CLKSEL_DPLL_N_MASK;
+ v |= ((priv->last_rounded_div - 1) << CM_CLKSEL_DPLL_N_SHIFT) &
+ CM_CLKSEL_DPLL_N_MASK;
+
+ writel(v, priv->clksel_reg);
+
+ /* lock dpll */
+ clrsetbits_le32(priv->clkmode_reg, CM_CLKMODE_DPLL_DPLL_EN_MASK,
+ DPLL_EN_LOCK << CM_CLKMODE_DPLL_EN_SHIFT);
+
+ /* wait till the dpll locks */
+ if (!wait_on_value(ST_DPLL_CLK_MASK, ST_DPLL_CLK_MASK,
+ (void *)priv->idlest_reg, LDELAY)) {
+ dev_err(clk->dev, "failed locking dpll\n");
+ hang();
+ }
+
+ return round_rate;
+}
+
+static ulong clk_ti_am3_dpll_get_rate(struct clk *clk)
+{
+ struct clk_ti_am3_dpll_priv *priv = dev_get_priv(clk->dev);
+ u64 rate;
+ u32 m, n, v;
+
+ /* Return bypass rate if DPLL is bypassed */
+ v = readl(priv->clkmode_reg);
+ v &= CM_CLKMODE_DPLL_EN_MASK;
+ v >>= CM_CLKMODE_DPLL_EN_SHIFT;
+
+ switch (v) {
+ case DPLL_EN_MN_BYPASS:
+ case DPLL_EN_LOW_POWER_BYPASS:
+ case DPLL_EN_FAST_RELOCK_BYPASS:
+ rate = clk_get_rate(&priv->clk_bypass);
+ dev_dbg(clk->dev, "rate=%lld\n", rate);
+ return rate;
+ }
+
+ v = readl(priv->clksel_reg);
+ m = v & CM_CLKSEL_DPLL_M_MASK;
+ m >>= CM_CLKSEL_DPLL_M_SHIFT;
+ n = v & CM_CLKSEL_DPLL_N_MASK;
+ n >>= CM_CLKSEL_DPLL_N_SHIFT;
+
+ rate = clk_get_rate(&priv->clk_ref) * m;
+ do_div(rate, n + 1);
+ dev_dbg(clk->dev, "rate=%lld\n", rate);
+ return rate;
+}
+
+const struct clk_ops clk_ti_am3_dpll_ops = {
+ .round_rate = clk_ti_am3_dpll_round_rate,
+ .get_rate = clk_ti_am3_dpll_get_rate,
+ .set_rate = clk_ti_am3_dpll_set_rate,
+};
+
+static int clk_ti_am3_dpll_remove(struct udevice *dev)
+{
+ struct clk_ti_am3_dpll_priv *priv = dev_get_priv(dev);
+ int err;
+
+ err = clk_release_all(&priv->clk_bypass, 1);
+ if (err) {
+ dev_err(dev, "failed to release bypass clock\n");
+ return err;
+ }
+
+ err = clk_release_all(&priv->clk_ref, 1);
+ if (err) {
+ dev_err(dev, "failed to release reference clock\n");
+ return err;
+ }
+
+ return 0;
+}
+
+static int clk_ti_am3_dpll_probe(struct udevice *dev)
+{
+ struct clk_ti_am3_dpll_priv *priv = dev_get_priv(dev);
+ int err;
+
+ err = clk_get_by_index(dev, 0, &priv->clk_ref);
+ if (err) {
+ dev_err(dev, "failed to get reference clock\n");
+ return err;
+ }
+
+ err = clk_get_by_index(dev, 1, &priv->clk_bypass);
+ if (err) {
+ dev_err(dev, "failed to get bypass clock\n");
+ return err;
+ }
+
+ return 0;
+}
+
+static int clk_ti_am3_dpll_of_to_plat(struct udevice *dev)
+{
+ struct clk_ti_am3_dpll_priv *priv = dev_get_priv(dev);
+ struct clk_ti_am3_dpll_drv_data *data =
+ (struct clk_ti_am3_dpll_drv_data *)dev_get_driver_data(dev);
+
+ priv->max_rate = data->max_rate;
+
+ priv->clkmode_reg = dev_read_addr_index(dev, 0);
+ if (priv->clkmode_reg == FDT_ADDR_T_NONE) {
+ dev_err(dev, "failed to get clkmode register\n");
+ return -EINVAL;
+ }
+
+ dev_dbg(dev, "clkmode_reg=0x%08lx\n", priv->clkmode_reg);
+
+ priv->idlest_reg = dev_read_addr_index(dev, 1);
+ if (priv->idlest_reg == FDT_ADDR_T_NONE) {
+ dev_err(dev, "failed to get idlest register\n");
+ return -EINVAL;
+ }
+
+ dev_dbg(dev, "idlest_reg=0x%08lx\n", priv->idlest_reg);
+
+ priv->clksel_reg = dev_read_addr_index(dev, 2);
+ if (priv->clksel_reg == FDT_ADDR_T_NONE) {
+ dev_err(dev, "failed to get clksel register\n");
+ return -EINVAL;
+ }
+
+ dev_dbg(dev, "clksel_reg=0x%08lx\n", priv->clksel_reg);
+
+ return 0;
+}
+
+static const struct clk_ti_am3_dpll_drv_data dpll_no_gate_data = {
+ .max_rate = 1000000000
+};
+
+static const struct clk_ti_am3_dpll_drv_data dpll_no_gate_j_type_data = {
+ .max_rate = 2000000000
+};
+
+static const struct clk_ti_am3_dpll_drv_data dpll_core_data = {
+ .max_rate = 1000000000
+};
+
+static const struct udevice_id clk_ti_am3_dpll_of_match[] = {
+ {.compatible = "ti,am3-dpll-core-clock",
+ .data = (ulong)&dpll_core_data},
+ {.compatible = "ti,am3-dpll-no-gate-clock",
+ .data = (ulong)&dpll_no_gate_data},
+ {.compatible = "ti,am3-dpll-no-gate-j-type-clock",
+ .data = (ulong)&dpll_no_gate_j_type_data},
+ {}
+};
+
+U_BOOT_DRIVER(clk_ti_am3_dpll) = {
+ .name = "ti_am3_dpll_clock",
+ .id = UCLASS_CLK,
+ .of_match = clk_ti_am3_dpll_of_match,
+ .ofdata_to_platdata = clk_ti_am3_dpll_of_to_plat,
+ .probe = clk_ti_am3_dpll_probe,
+ .remove = clk_ti_am3_dpll_remove,
+ .priv_auto = sizeof(struct clk_ti_am3_dpll_priv),
+ .ops = &clk_ti_am3_dpll_ops,
+};
diff --git a/drivers/clk/ti/clk-ctrl.c b/drivers/clk/ti/clk-ctrl.c
new file mode 100644
index 0000000000..940e8d6caf
--- /dev/null
+++ b/drivers/clk/ti/clk-ctrl.c
@@ -0,0 +1,154 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * OMAP clock controller support
+ *
+ * Copyright (C) 2020 Dario Binacchi <dariobin@libero.it>
+ */
+
+#include <common.h>
+#include <dm.h>
+#include <dm/device_compat.h>
+#include <clk-uclass.h>
+#include <asm/arch-am33xx/clock.h>
+
+struct clk_ti_ctrl_offs {
+ fdt_addr_t start;
+ fdt_size_t end;
+};
+
+struct clk_ti_ctrl_priv {
+ int offs_num;
+ struct clk_ti_ctrl_offs *offs;
+};
+
+static int clk_ti_ctrl_check_offs(struct clk *clk, fdt_addr_t offs)
+{
+ struct clk_ti_ctrl_priv *priv = dev_get_priv(clk->dev);
+ int i;
+
+ for (i = 0; i < priv->offs_num; i++) {
+ if (offs >= priv->offs[i].start && offs <= priv->offs[i].end)
+ return 0;
+ }
+
+ return -EFAULT;
+}
+
+static int clk_ti_ctrl_disable(struct clk *clk)
+{
+ struct clk_ti_ctrl_priv *priv = dev_get_priv(clk->dev);
+ u32 *clk_modules[2] = { };
+ fdt_addr_t offs;
+ int err;
+
+ offs = priv->offs[0].start + clk->id;
+ err = clk_ti_ctrl_check_offs(clk, offs);
+ if (err) {
+ dev_err(clk->dev, "invalid offset: 0x%lx\n", offs);
+ return err;
+ }
+
+ clk_modules[0] = (u32 *)(offs);
+ dev_dbg(clk->dev, "module address=%p\n", clk_modules[0]);
+ do_disable_clocks(NULL, clk_modules, 1);
+ return 0;
+}
+
+static int clk_ti_ctrl_enable(struct clk *clk)
+{
+ struct clk_ti_ctrl_priv *priv = dev_get_priv(clk->dev);
+ u32 *clk_modules[2] = { };
+ fdt_addr_t offs;
+ int err;
+
+ offs = priv->offs[0].start + clk->id;
+ err = clk_ti_ctrl_check_offs(clk, offs);
+ if (err) {
+ dev_err(clk->dev, "invalid offset: 0x%lx\n", offs);
+ return err;
+ }
+
+ clk_modules[0] = (u32 *)(offs);
+ dev_dbg(clk->dev, "module address=%p\n", clk_modules[0]);
+ do_enable_clocks(NULL, clk_modules, 1);
+ return 0;
+}
+
+static ulong clk_ti_ctrl_get_rate(struct clk *clk)
+{
+ return 0;
+}
+
+static int clk_ti_ctrl_of_xlate(struct clk *clk,
+ struct ofnode_phandle_args *args)
+{
+ if (args->args_count != 2) {
+ dev_err(clk->dev, "invaild args_count: %d\n", args->args_count);
+ return -EINVAL;
+ }
+
+ if (args->args_count)
+ clk->id = args->args[0];
+ else
+ clk->id = 0;
+
+ dev_dbg(clk->dev, "name=%s, id=%ld\n", clk->dev->name, clk->id);
+ return 0;
+}
+
+static int clk_ti_ctrl_of_to_plat(struct udevice *dev)
+{
+ struct clk_ti_ctrl_priv *priv = dev_get_priv(dev);
+ fdt_size_t fdt_size;
+ int i, size;
+
+ size = dev_read_size(dev, "reg");
+ if (size < 0) {
+ dev_err(dev, "failed to get 'reg' size\n");
+ return size;
+ }
+
+ priv->offs_num = size / 2 / sizeof(u32);
+ dev_dbg(dev, "size=%d, regs_num=%d\n", size, priv->offs_num);
+
+ priv->offs = kmalloc_array(priv->offs_num, sizeof(*priv->offs),
+ GFP_KERNEL);
+ if (!priv->offs)
+ return -ENOMEM;
+
+ for (i = 0; i < priv->offs_num; i++) {
+ priv->offs[i].start =
+ dev_read_addr_size_index(dev, i, &fdt_size);
+ if (priv->offs[i].start == FDT_ADDR_T_NONE) {
+ dev_err(dev, "failed to get offset %d\n", i);
+ return -EINVAL;
+ }
+
+ priv->offs[i].end = priv->offs[i].start + fdt_size;
+ dev_dbg(dev, "start=0x%08lx, end=0x%08lx\n",
+ priv->offs[i].start, priv->offs[i].end);
+ }
+
+ return 0;
+}
+
+static struct clk_ops clk_ti_ctrl_ops = {
+ .of_xlate = clk_ti_ctrl_of_xlate,
+ .enable = clk_ti_ctrl_enable,
+ .disable = clk_ti_ctrl_disable,
+ .get_rate = clk_ti_ctrl_get_rate,
+};
+
+static const struct udevice_id clk_ti_ctrl_ids[] = {
+ {.compatible = "ti,clkctrl"},
+ {},
+};
+
+U_BOOT_DRIVER(clk_ti_ctrl) = {
+ .name = "ti_ctrl_clk",
+ .id = UCLASS_CLK,
+ .of_match = clk_ti_ctrl_ids,
+ .ofdata_to_platdata = clk_ti_ctrl_of_to_plat,
+ .ops = &clk_ti_ctrl_ops,
+ .priv_auto = sizeof(struct clk_ti_ctrl_priv),
+};
diff --git a/drivers/clk/ti/clk-divider.c b/drivers/clk/ti/clk-divider.c
new file mode 100644
index 0000000000..a862637785
--- /dev/null
+++ b/drivers/clk/ti/clk-divider.c
@@ -0,0 +1,381 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * TI divider clock support
+ *
+ * Copyright (C) 2020 Dario Binacchi <dariobin@libero.it>
+ *
+ * Loosely based on Linux kernel drivers/clk/ti/divider.c
+ */
+
+#include <common.h>
+#include <clk.h>
+#include <clk-uclass.h>
+#include <div64.h>
+#include <dm.h>
+#include <dm/device_compat.h>
+#include <asm/io.h>
+#include <linux/clk-provider.h>
+#include <linux/kernel.h>
+#include <linux/log2.h>
+#include "clk.h"
+
+/*
+ * The reverse of DIV_ROUND_UP: The maximum number which
+ * divided by m is r
+ */
+#define MULT_ROUND_UP(r, m) ((r) * (m) + (m) - 1)
+
+struct clk_ti_divider_priv {
+ struct clk parent;
+ fdt_addr_t reg;
+ const struct clk_div_table *table;
+ u8 shift;
+ u8 flags;
+ u8 div_flags;
+ s8 latch;
+ u16 min;
+ u16 max;
+ u16 mask;
+};
+
+static unsigned int _get_div(const struct clk_div_table *table, ulong flags,
+ unsigned int val)
+{
+ if (flags & CLK_DIVIDER_ONE_BASED)
+ return val;
+
+ if (flags & CLK_DIVIDER_POWER_OF_TWO)
+ return 1 << val;
+
+ if (table)
+ return clk_divider_get_table_div(table, val);
+
+ return val + 1;
+}
+
+static unsigned int _get_val(const struct clk_div_table *table, ulong flags,
+ unsigned int div)
+{
+ if (flags & CLK_DIVIDER_ONE_BASED)
+ return div;
+
+ if (flags & CLK_DIVIDER_POWER_OF_TWO)
+ return __ffs(div);
+
+ if (table)
+ return clk_divider_get_table_val(table, div);
+
+ return div - 1;
+}
+
+static int _div_round_up(const struct clk_div_table *table, ulong parent_rate,
+ ulong rate)
+{
+ const struct clk_div_table *clkt;
+ int up = INT_MAX;
+ int div = DIV_ROUND_UP_ULL((u64)parent_rate, rate);
+
+ for (clkt = table; clkt->div; clkt++) {
+ if (clkt->div == div)
+ return clkt->div;
+ else if (clkt->div < div)
+ continue;
+
+ if ((clkt->div - div) < (up - div))
+ up = clkt->div;
+ }
+
+ return up;
+}
+
+static int _div_round(const struct clk_div_table *table, ulong parent_rate,
+ ulong rate)
+{
+ if (table)
+ return _div_round_up(table, parent_rate, rate);
+
+ return DIV_ROUND_UP(parent_rate, rate);
+}
+
+static int clk_ti_divider_best_div(struct clk *clk, ulong rate,
+ ulong *best_parent_rate)
+{
+ struct clk_ti_divider_priv *priv = dev_get_priv(clk->dev);
+ ulong parent_rate, parent_round_rate, max_div;
+ ulong best_rate, r;
+ int i, best_div = 0;
+
+ parent_rate = clk_get_rate(&priv->parent);
+ if (IS_ERR_VALUE(parent_rate))
+ return parent_rate;
+
+ if (!rate)
+ rate = 1;
+
+ if (!(clk->flags & CLK_SET_RATE_PARENT)) {
+ best_div = _div_round(priv->table, parent_rate, rate);
+ if (best_div == 0)
+ best_div = 1;
+
+ if (best_div > priv->max)
+ best_div = priv->max;
+
+ *best_parent_rate = parent_rate;
+ return best_div;
+ }
+
+ max_div = min(ULONG_MAX / rate, (ulong)priv->max);
+ for (best_rate = 0, i = 1; i <= max_div; i++) {
+ if (!clk_divider_is_valid_div(priv->table, priv->div_flags, i))
+ continue;
+
+ /*
+ * It's the most ideal case if the requested rate can be
+ * divided from parent clock without needing to change
+ * parent rate, so return the divider immediately.
+ */
+ if ((rate * i) == parent_rate) {
+ *best_parent_rate = parent_rate;
+ dev_dbg(clk->dev, "rate=%ld, best_rate=%ld, div=%d\n",
+ rate, rate, i);
+ return i;
+ }
+
+ parent_round_rate = clk_round_rate(&priv->parent,
+ MULT_ROUND_UP(rate, i));
+ if (IS_ERR_VALUE(parent_round_rate))
+ continue;
+
+ r = DIV_ROUND_UP(parent_round_rate, i);
+ if (r <= rate && r > best_rate) {
+ best_div = i;
+ best_rate = r;
+ *best_parent_rate = parent_round_rate;
+ if (best_rate == rate)
+ break;
+ }
+ }
+
+ if (best_div == 0) {
+ best_div = priv->max;
+ parent_round_rate = clk_round_rate(&priv->parent, 1);
+ if (IS_ERR_VALUE(parent_round_rate))
+ return parent_round_rate;
+ }
+
+ dev_dbg(clk->dev, "rate=%ld, best_rate=%ld, div=%d\n", rate, best_rate,
+ best_div);
+
+ return best_div;
+}
+
+static ulong clk_ti_divider_round_rate(struct clk *clk, ulong rate)
+{
+ ulong parent_rate;
+ int div;
+
+ div = clk_ti_divider_best_div(clk, rate, &parent_rate);
+ if (div < 0)
+ return div;
+
+ return DIV_ROUND_UP(parent_rate, div);
+}
+
+static ulong clk_ti_divider_set_rate(struct clk *clk, ulong rate)
+{
+ struct clk_ti_divider_priv *priv = dev_get_priv(clk->dev);
+ ulong parent_rate;
+ int div;
+ u32 val, v;
+
+ div = clk_ti_divider_best_div(clk, rate, &parent_rate);
+ if (div < 0)
+ return div;
+
+ if (clk->flags & CLK_SET_RATE_PARENT) {
+ parent_rate = clk_set_rate(&priv->parent, parent_rate);
+ if (IS_ERR_VALUE(parent_rate))
+ return parent_rate;
+ }
+
+ val = _get_val(priv->table, priv->div_flags, div);
+
+ v = readl(priv->reg);
+ v &= ~(priv->mask << priv->shift);
+ v |= val << priv->shift;
+ writel(v, priv->reg);
+ clk_ti_latch(priv->reg, priv->latch);
+
+ return clk_get_rate(clk);
+}
+
+static ulong clk_ti_divider_get_rate(struct clk *clk)
+{
+ struct clk_ti_divider_priv *priv = dev_get_priv(clk->dev);
+ ulong rate, parent_rate;
+ unsigned int div;
+ u32 v;
+
+ parent_rate = clk_get_rate(&priv->parent);
+ if (IS_ERR_VALUE(parent_rate))
+ return parent_rate;
+
+ v = readl(priv->reg) >> priv->shift;
+ v &= priv->mask;
+
+ div = _get_div(priv->table, priv->div_flags, v);
+ if (!div) {
+ if (!(priv->div_flags & CLK_DIVIDER_ALLOW_ZERO))
+ dev_warn(clk->dev,
+ "zero divisor and CLK_DIVIDER_ALLOW_ZERO not set\n");
+ return parent_rate;
+ }
+
+ rate = DIV_ROUND_UP(parent_rate, div);
+ dev_dbg(clk->dev, "rate=%ld\n", rate);
+ return rate;
+}
+
+static int clk_ti_divider_request(struct clk *clk)
+{
+ struct clk_ti_divider_priv *priv = dev_get_priv(clk->dev);
+
+ clk->flags = priv->flags;
+ return 0;
+}
+
+const struct clk_ops clk_ti_divider_ops = {
+ .request = clk_ti_divider_request,
+ .round_rate = clk_ti_divider_round_rate,
+ .get_rate = clk_ti_divider_get_rate,
+ .set_rate = clk_ti_divider_set_rate
+};
+
+static int clk_ti_divider_remove(struct udevice *dev)
+{
+ struct clk_ti_divider_priv *priv = dev_get_priv(dev);
+ int err;
+
+ err = clk_release_all(&priv->parent, 1);
+ if (err) {
+ dev_err(dev, "failed to release parent clock\n");
+ return err;
+ }
+
+ return 0;
+}
+
+static int clk_ti_divider_probe(struct udevice *dev)
+{
+ struct clk_ti_divider_priv *priv = dev_get_priv(dev);
+ int err;
+
+ err = clk_get_by_index(dev, 0, &priv->parent);
+ if (err) {
+ dev_err(dev, "failed to get parent clock\n");
+ return err;
+ }
+
+ return 0;
+}
+
+static int clk_ti_divider_of_to_plat(struct udevice *dev)
+{
+ struct clk_ti_divider_priv *priv = dev_get_priv(dev);
+ struct clk_div_table *table = NULL;
+ u32 val, valid_div;
+ u32 min_div = 0;
+ u32 max_val, max_div = 0;
+ u16 mask;
+ int i, div_num;
+
+ priv->reg = dev_read_addr(dev);
+ dev_dbg(dev, "reg=0x%08lx\n", priv->reg);
+ priv->shift = dev_read_u32_default(dev, "ti,bit-shift", 0);
+ priv->latch = dev_read_s32_default(dev, "ti,latch-bit", -EINVAL);
+ if (dev_read_bool(dev, "ti,index-starts-at-one"))
+ priv->div_flags |= CLK_DIVIDER_ONE_BASED;
+
+ if (dev_read_bool(dev, "ti,index-power-of-two"))
+ priv->div_flags |= CLK_DIVIDER_POWER_OF_TWO;
+
+ if (dev_read_bool(dev, "ti,set-rate-parent"))
+ priv->flags |= CLK_SET_RATE_PARENT;
+
+ if (dev_read_prop(dev, "ti,dividers", &div_num)) {
+ div_num /= sizeof(u32);
+
+ /* Determine required size for divider table */
+ for (i = 0, valid_div = 0; i < div_num; i++) {
+ dev_read_u32_index(dev, "ti,dividers", i, &val);
+ if (val)
+ valid_div++;
+ }
+
+ if (!valid_div) {
+ dev_err(dev, "no valid dividers\n");
+ return -EINVAL;
+ }
+
+ table = calloc(valid_div + 1, sizeof(*table));
+ if (!table)
+ return -ENOMEM;
+
+ for (i = 0, valid_div = 0; i < div_num; i++) {
+ dev_read_u32_index(dev, "ti,dividers", i, &val);
+ if (!val)
+ continue;
+
+ table[valid_div].div = val;
+ table[valid_div].val = i;
+ valid_div++;
+ if (val > max_div)
+ max_div = val;
+
+ if (!min_div || val < min_div)
+ min_div = val;
+ }
+
+ max_val = max_div;
+ } else {
+ /* Divider table not provided, determine min/max divs */
+ min_div = dev_read_u32_default(dev, "ti,min-div", 1);
+ if (dev_read_u32(dev, "ti,max-div", &max_div)) {
+ dev_err(dev, "missing 'max-div' property\n");
+ return -EFAULT;
+ }
+
+ max_val = max_div;
+ if (!(priv->div_flags & CLK_DIVIDER_ONE_BASED) &&
+ !(priv->div_flags & CLK_DIVIDER_POWER_OF_TWO))
+ max_val--;
+ }
+
+ priv->table = table;
+ priv->min = min_div;
+ priv->max = max_div;
+
+ if (priv->div_flags & CLK_DIVIDER_POWER_OF_TWO)
+ mask = fls(max_val) - 1;
+ else
+ mask = max_val;
+
+ priv->mask = (1 << fls(mask)) - 1;
+ return 0;
+}
+
+static const struct udevice_id clk_ti_divider_of_match[] = {
+ {.compatible = "ti,divider-clock"},
+ {}
+};
+
+U_BOOT_DRIVER(clk_ti_divider) = {
+ .name = "ti_divider_clock",
+ .id = UCLASS_CLK,
+ .of_match = clk_ti_divider_of_match,
+ .ofdata_to_platdata = clk_ti_divider_of_to_plat,
+ .probe = clk_ti_divider_probe,
+ .remove = clk_ti_divider_remove,
+ .priv_auto = sizeof(struct clk_ti_divider_priv),
+ .ops = &clk_ti_divider_ops,
+};
diff --git a/drivers/clk/ti/clk-gate.c b/drivers/clk/ti/clk-gate.c
new file mode 100644
index 0000000000..236eaed6df
--- /dev/null
+++ b/drivers/clk/ti/clk-gate.c
@@ -0,0 +1,93 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * TI gate clock support
+ *
+ * Copyright (C) 2020 Dario Binacchi <dariobin@libero.it>
+ *
+ * Loosely based on Linux kernel drivers/clk/ti/gate.c
+ */
+
+#include <common.h>
+#include <dm.h>
+#include <dm/device_compat.h>
+#include <clk-uclass.h>
+#include <asm/io.h>
+#include <linux/clk-provider.h>
+
+struct clk_ti_gate_priv {
+ fdt_addr_t reg;
+ u8 enable_bit;
+ u32 flags;
+ bool invert_enable;
+};
+
+static int clk_ti_gate_disable(struct clk *clk)
+{
+ struct clk_ti_gate_priv *priv = dev_get_priv(clk->dev);
+ u32 v;
+
+ v = readl(priv->reg);
+ if (priv->invert_enable)
+ v |= (1 << priv->enable_bit);
+ else
+ v &= ~(1 << priv->enable_bit);
+
+ writel(v, priv->reg);
+ /* No OCP barrier needed here since it is a disable operation */
+ return 0;
+}
+
+static int clk_ti_gate_enable(struct clk *clk)
+{
+ struct clk_ti_gate_priv *priv = dev_get_priv(clk->dev);
+ u32 v;
+
+ v = readl(priv->reg);
+ if (priv->invert_enable)
+ v &= ~(1 << priv->enable_bit);
+ else
+ v |= (1 << priv->enable_bit);
+
+ writel(v, priv->reg);
+ /* OCP barrier */
+ v = readl(priv->reg);
+ return 0;
+}
+
+static int clk_ti_gate_of_to_plat(struct udevice *dev)
+{
+ struct clk_ti_gate_priv *priv = dev_get_priv(dev);
+
+ priv->reg = dev_read_addr(dev);
+ if (priv->reg == FDT_ADDR_T_NONE) {
+ dev_err(dev, "failed to get control register\n");
+ return -EINVAL;
+ }
+
+ dev_dbg(dev, "reg=0x%08lx\n", priv->reg);
+ priv->enable_bit = dev_read_u32_default(dev, "ti,bit-shift", 0);
+ if (dev_read_bool(dev, "ti,set-rate-parent"))
+ priv->flags |= CLK_SET_RATE_PARENT;
+
+ priv->invert_enable = dev_read_bool(dev, "ti,set-bit-to-disable");
+ return 0;
+}
+
+static struct clk_ops clk_ti_gate_ops = {
+ .enable = clk_ti_gate_enable,
+ .disable = clk_ti_gate_disable,
+};
+
+static const struct udevice_id clk_ti_gate_of_match[] = {
+ { .compatible = "ti,gate-clock" },
+ { },
+};
+
+U_BOOT_DRIVER(clk_ti_gate) = {
+ .name = "ti_gate_clock",
+ .id = UCLASS_CLK,
+ .of_match = clk_ti_gate_of_match,
+ .ofdata_to_platdata = clk_ti_gate_of_to_plat,
+ .priv_auto = sizeof(struct clk_ti_gate_priv),
+ .ops = &clk_ti_gate_ops,
+};
diff --git a/drivers/clk/ti/clk-mux.c b/drivers/clk/ti/clk-mux.c
new file mode 100644
index 0000000000..419502c389
--- /dev/null
+++ b/drivers/clk/ti/clk-mux.c
@@ -0,0 +1,253 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * TI multiplexer clock support
+ *
+ * Copyright (C) 2020 Dario Binacchi <dariobin@libero.it>
+ *
+ * Based on Linux kernel drivers/clk/ti/mux.c
+ */
+
+#include <common.h>
+#include <dm.h>
+#include <dm/device_compat.h>
+#include <clk-uclass.h>
+#include <asm/io.h>
+#include <linux/clk-provider.h>
+#include "clk.h"
+
+struct clk_ti_mux_priv {
+ struct clk_bulk parents;
+ fdt_addr_t reg;
+ u32 flags;
+ u32 mux_flags;
+ u32 mask;
+ u32 shift;
+ s32 latch;
+};
+
+static struct clk *clk_ti_mux_get_parent_by_index(struct clk_bulk *parents,
+ int index)
+{
+ if (index < 0 || !parents)
+ return ERR_PTR(-EINVAL);
+
+ if (index >= parents->count)
+ return ERR_PTR(-ENODEV);
+
+ return &parents->clks[index];
+}
+
+static int clk_ti_mux_get_parent_index(struct clk_bulk *parents,
+ struct clk *parent)
+{
+ int i;
+
+ if (!parents || !parent)
+ return -EINVAL;
+
+ for (i = 0; i < parents->count; i++) {
+ if (parents->clks[i].dev == parent->dev)
+ return i;
+ }
+
+ return -ENODEV;
+}
+
+static int clk_ti_mux_get_index(struct clk *clk)
+{
+ struct clk_ti_mux_priv *priv = dev_get_priv(clk->dev);
+ u32 val;
+
+ val = readl(priv->reg);
+ val >>= priv->shift;
+ val &= priv->mask;
+
+ if (val && (priv->flags & CLK_MUX_INDEX_BIT))
+ val = ffs(val) - 1;
+
+ if (val && (priv->flags & CLK_MUX_INDEX_ONE))
+ val--;
+
+ if (val >= priv->parents.count)
+ return -EINVAL;
+
+ return val;
+}
+
+static int clk_ti_mux_set_parent(struct clk *clk, struct clk *parent)
+{
+ struct clk_ti_mux_priv *priv = dev_get_priv(clk->dev);
+ int index;
+ u32 val;
+
+ index = clk_ti_mux_get_parent_index(&priv->parents, parent);
+ if (index < 0) {
+ dev_err(clk->dev, "failed to get parent clock\n");
+ return index;
+ }
+
+ index = clk_mux_index_to_val(NULL, priv->flags, index);
+
+ if (priv->flags & CLK_MUX_HIWORD_MASK) {
+ val = priv->mask << (priv->shift + 16);
+ } else {
+ val = readl(priv->reg);
+ val &= ~(priv->mask << priv->shift);
+ }
+
+ val |= index << priv->shift;
+ writel(val, priv->reg);
+ clk_ti_latch(priv->reg, priv->latch);
+ return 0;
+}
+
+static ulong clk_ti_mux_set_rate(struct clk *clk, ulong rate)
+{
+ struct clk_ti_mux_priv *priv = dev_get_priv(clk->dev);
+ struct clk *parent;
+ int index;
+
+ if ((clk->flags & CLK_SET_RATE_PARENT) == 0)
+ return -ENOSYS;
+
+ index = clk_ti_mux_get_index(clk);
+ parent = clk_ti_mux_get_parent_by_index(&priv->parents, index);
+ if (IS_ERR(parent))
+ return PTR_ERR(parent);
+
+ rate = clk_set_rate(parent, rate);
+ dev_dbg(clk->dev, "rate=%ld\n", rate);
+ return rate;
+}
+
+static ulong clk_ti_mux_get_rate(struct clk *clk)
+{
+ struct clk_ti_mux_priv *priv = dev_get_priv(clk->dev);
+ int index;
+ struct clk *parent;
+ ulong rate;
+
+ index = clk_ti_mux_get_index(clk);
+ parent = clk_ti_mux_get_parent_by_index(&priv->parents, index);
+ if (IS_ERR(parent))
+ return PTR_ERR(parent);
+
+ rate = clk_get_rate(parent);
+ dev_dbg(clk->dev, "rate=%ld\n", rate);
+ return rate;
+}
+
+static ulong clk_ti_mux_round_rate(struct clk *clk, ulong rate)
+{
+ struct clk_ti_mux_priv *priv = dev_get_priv(clk->dev);
+ struct clk *parent;
+ int index;
+
+ if ((clk->flags & CLK_SET_RATE_PARENT) == 0)
+ return -ENOSYS;
+
+ index = clk_ti_mux_get_index(clk);
+ parent = clk_ti_mux_get_parent_by_index(&priv->parents, index);
+ if (IS_ERR(parent))
+ return PTR_ERR(parent);
+
+ rate = clk_round_rate(parent, rate);
+ dev_dbg(clk->dev, "rate=%ld\n", rate);
+ return rate;
+}
+
+static int clk_ti_mux_request(struct clk *clk)
+{
+ struct clk_ti_mux_priv *priv = dev_get_priv(clk->dev);
+ struct clk *parent;
+ int index;
+
+ clk->flags = priv->flags;
+
+ index = clk_ti_mux_get_index(clk);
+ parent = clk_ti_mux_get_parent_by_index(&priv->parents, index);
+ if (IS_ERR(parent))
+ return PTR_ERR(parent);
+
+ return clk_ti_mux_set_parent(clk, parent);
+}
+
+static struct clk_ops clk_ti_mux_ops = {
+ .request = clk_ti_mux_request,
+ .round_rate = clk_ti_mux_round_rate,
+ .get_rate = clk_ti_mux_get_rate,
+ .set_rate = clk_ti_mux_set_rate,
+ .set_parent = clk_ti_mux_set_parent,
+};
+
+static int clk_ti_mux_remove(struct udevice *dev)
+{
+ struct clk_ti_mux_priv *priv = dev_get_priv(dev);
+ int err;
+
+ err = clk_release_all(priv->parents.clks, priv->parents.count);
+ if (err)
+ dev_dbg(dev, "could not release all parents' clocks\n");
+
+ return err;
+}
+
+static int clk_ti_mux_probe(struct udevice *dev)
+{
+ struct clk_ti_mux_priv *priv = dev_get_priv(dev);
+ int err;
+
+ err = clk_get_bulk(dev, &priv->parents);
+ if (err || priv->parents.count < 2) {
+ dev_err(dev, "mux-clock must have parents\n");
+ return err ? err : -EFAULT;
+ }
+
+ /* Generate bit-mask based on parents info */
+ priv->mask = priv->parents.count;
+ if (!(priv->mux_flags & CLK_MUX_INDEX_ONE))
+ priv->mask--;
+
+ priv->mask = (1 << fls(priv->mask)) - 1;
+ return 0;
+}
+
+static int clk_ti_mux_of_to_plat(struct udevice *dev)
+{
+ struct clk_ti_mux_priv *priv = dev_get_priv(dev);
+
+ priv->reg = dev_read_addr(dev);
+ if (priv->reg == FDT_ADDR_T_NONE) {
+ dev_err(dev, "failed to get register\n");
+ return -EINVAL;
+ }
+
+ dev_dbg(dev, "reg=0x%08lx\n", priv->reg);
+ priv->shift = dev_read_u32_default(dev, "ti,bit-shift", 0);
+ priv->latch = dev_read_s32_default(dev, "ti,latch-bit", -EINVAL);
+
+ priv->flags = CLK_SET_RATE_NO_REPARENT;
+ if (dev_read_bool(dev, "ti,set-rate-parent"))
+ priv->flags |= CLK_SET_RATE_PARENT;
+
+ if (dev_read_bool(dev, "ti,index-starts-at-one"))
+ priv->mux_flags |= CLK_MUX_INDEX_ONE;
+
+ return 0;
+}
+
+static const struct udevice_id clk_ti_mux_of_match[] = {
+ {.compatible = "ti,mux-clock"},
+ {},
+};
+
+U_BOOT_DRIVER(clk_ti_mux) = {
+ .name = "ti_mux_clock",
+ .id = UCLASS_CLK,
+ .of_match = clk_ti_mux_of_match,
+ .ofdata_to_platdata = clk_ti_mux_of_to_plat,
+ .probe = clk_ti_mux_probe,
+ .remove = clk_ti_mux_remove,
+ .priv_auto = sizeof(struct clk_ti_mux_priv),
+ .ops = &clk_ti_mux_ops,
+};
diff --git a/drivers/clk/clk-ti-sci.c b/drivers/clk/ti/clk-sci.c
index 6f0fdaa111..6f0fdaa111 100644
--- a/drivers/clk/clk-ti-sci.c
+++ b/drivers/clk/ti/clk-sci.c
diff --git a/drivers/clk/ti/clk.c b/drivers/clk/ti/clk.c
new file mode 100644
index 0000000000..e44b90ad6a
--- /dev/null
+++ b/drivers/clk/ti/clk.c
@@ -0,0 +1,34 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * TI clock utilities
+ *
+ * Copyright (C) 2020 Dario Binacchi <dariobin@libero.it>
+ */
+
+#include <common.h>
+#include <asm/io.h>
+#include "clk.h"
+
+static void clk_ti_rmw(u32 val, u32 mask, fdt_addr_t reg)
+{
+ u32 v;
+
+ v = readl(reg);
+ v &= ~mask;
+ v |= val;
+ writel(v, reg);
+}
+
+void clk_ti_latch(fdt_addr_t reg, s8 shift)
+{
+ u32 latch;
+
+ if (shift < 0)
+ return;
+
+ latch = 1 << shift;
+
+ clk_ti_rmw(latch, latch, reg);
+ clk_ti_rmw(0, latch, reg);
+ readl(reg); /* OCP barrier */
+}
diff --git a/drivers/clk/ti/clk.h b/drivers/clk/ti/clk.h
new file mode 100644
index 0000000000..601c3823f7
--- /dev/null
+++ b/drivers/clk/ti/clk.h
@@ -0,0 +1,13 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * TI clock utilities header
+ *
+ * Copyright (C) 2020 Dario Binacchi <dariobin@libero.it>
+ */
+
+#ifndef _CLK_TI_H
+#define _CLK_TI_H
+
+void clk_ti_latch(fdt_addr_t reg, s8 shift);
+
+#endif /* #ifndef _CLK_TI_H */
diff --git a/drivers/clk/ti/omap4-cm.c b/drivers/clk/ti/omap4-cm.c
new file mode 100644
index 0000000000..3cdc9b2888
--- /dev/null
+++ b/drivers/clk/ti/omap4-cm.c
@@ -0,0 +1,22 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * OMAP4 clock manager (cm)
+ *
+ * Copyright (C) 2020 Dario Binacchi <dariobin@libero.it>
+ */
+
+#include <common.h>
+#include <dm.h>
+#include <dm/lists.h>
+
+static const struct udevice_id ti_omap4_cm_ids[] = {
+ {.compatible = "ti,omap4-cm"},
+ {}
+};
+
+U_BOOT_DRIVER(ti_omap4_cm) = {
+ .name = "ti_omap4_cm",
+ .id = UCLASS_SIMPLE_BUS,
+ .of_match = ti_omap4_cm_ids,
+ .bind = dm_scan_fdt_dev,
+};
diff --git a/drivers/core/Kconfig b/drivers/core/Kconfig
index 65a503e76d..dbfe51c6e8 100644
--- a/drivers/core/Kconfig
+++ b/drivers/core/Kconfig
@@ -247,6 +247,18 @@ config OF_TRANSLATE
used for the address translation. This function is faster and
smaller in size than fdt_translate_address().
+config OF_TRANSLATE_ZERO_SIZE_CELLS
+ bool "Enable translation for zero size cells"
+ depends on OF_TRANSLATE
+ default n
+ help
+ The routine used to translate an FDT address into a physical CPU
+ address was developed by IBM. It considers that crossing any level
+ with #size-cells = <0> makes translation impossible, even if it is
+ not the way it was specified.
+ Enabling this option makes translation possible even in the case
+ of crossing levels with #size-cells = <0>.
+
config SPL_OF_TRANSLATE
bool "Translate addresses using fdt_translate_address in SPL"
depends on SPL_DM && SPL_OF_CONTROL
diff --git a/drivers/core/fdtaddr.c b/drivers/core/fdtaddr.c
index 8b48aa5bc5..ed55f69de1 100644
--- a/drivers/core/fdtaddr.c
+++ b/drivers/core/fdtaddr.c
@@ -49,7 +49,7 @@ fdt_addr_t devfdt_get_addr_index(const struct udevice *dev, int index)
reg += index * (na + ns);
- if (ns) {
+ if (ns || gd_size_cells_0()) {
/*
* Use the full-fledged translate function for complex
* bus setups.
diff --git a/drivers/core/of_addr.c b/drivers/core/of_addr.c
index ca34d84922..bbe80136ba 100644
--- a/drivers/core/of_addr.c
+++ b/drivers/core/of_addr.c
@@ -18,7 +18,8 @@
/* Max address size we deal with */
#define OF_MAX_ADDR_CELLS 4
#define OF_CHECK_ADDR_COUNT(na) ((na) > 0 && (na) <= OF_MAX_ADDR_CELLS)
-#define OF_CHECK_COUNTS(na, ns) (OF_CHECK_ADDR_COUNT(na) && (ns) > 0)
+#define OF_CHECK_COUNTS(na, ns) (OF_CHECK_ADDR_COUNT(na) && \
+ ((ns) > 0 || gd_size_cells_0()))
static struct of_bus *of_match_bus(struct device_node *np);
@@ -162,11 +163,6 @@ const __be32 *of_get_address(const struct device_node *dev, int index,
}
EXPORT_SYMBOL(of_get_address);
-static int of_empty_ranges_quirk(const struct device_node *np)
-{
- return false;
-}
-
static int of_translate_one(const struct device_node *parent,
struct of_bus *bus, struct of_bus *pbus,
__be32 *addr, int na, int ns, int pna,
@@ -193,11 +189,8 @@ static int of_translate_one(const struct device_node *parent,
* As far as we know, this damage only exists on Apple machines, so
* This code is only enabled on powerpc. --gcl
*/
+
ranges = of_get_property(parent, rprop, &rlen);
- if (ranges == NULL && !of_empty_ranges_quirk(parent)) {
- debug("no ranges; cannot translate\n");
- return 1;
- }
if (ranges == NULL || rlen == 0) {
offset = of_read_number(addr, na);
memset(addr, 0, pna * 4);
diff --git a/drivers/core/ofnode.c b/drivers/core/ofnode.c
index 2a6e43ddc6..7a5f4c0a73 100644
--- a/drivers/core/ofnode.c
+++ b/drivers/core/ofnode.c
@@ -316,7 +316,8 @@ fdt_addr_t ofnode_get_addr_size_index(ofnode node, int index, fdt_size_t *size)
ns = of_n_size_cells(ofnode_to_np(node));
- if (IS_ENABLED(CONFIG_OF_TRANSLATE) && ns > 0) {
+ if (IS_ENABLED(CONFIG_OF_TRANSLATE) &&
+ (ns > 0 || gd_size_cells_0())) {
return of_translate_address(ofnode_to_np(node), prop_val);
} else {
na = of_n_addr_cells(ofnode_to_np(node));
@@ -690,8 +691,10 @@ fdt_addr_t ofnode_get_addr_size(ofnode node, const char *property,
ns = of_n_size_cells(np);
*sizep = of_read_number(prop + na, ns);
- if (CONFIG_IS_ENABLED(OF_TRANSLATE) && ns > 0)
+ if (CONFIG_IS_ENABLED(OF_TRANSLATE) &&
+ (ns > 0 || gd_size_cells_0())) {
return of_translate_address(np, prop);
+ }
else
return of_read_number(prop, na);
} else {
diff --git a/drivers/core/read.c b/drivers/core/read.c
index fc74d64814..4d9b5dd038 100644
--- a/drivers/core/read.c
+++ b/drivers/core/read.c
@@ -379,3 +379,9 @@ int dev_read_pci_bus_range(const struct udevice *dev,
return 0;
}
+
+int dev_decode_display_timing(const struct udevice *dev, int index,
+ struct display_timing *config)
+{
+ return ofnode_decode_display_timing(dev_ofnode(dev), index, config);
+}
diff --git a/drivers/core/root.c b/drivers/core/root.c
index 78de7cdf87..2bfa75b472 100644
--- a/drivers/core/root.c
+++ b/drivers/core/root.c
@@ -132,6 +132,9 @@ int dm_init(bool of_live)
{
int ret;
+ if (IS_ENABLED(CONFIG_OF_TRANSLATE_ZERO_SIZE_CELLS))
+ gd->dm_flags |= GD_DM_FLG_SIZE_CELLS_0;
+
if (gd->dm_root) {
dm_warn("Virtual root driver already exists!\n");
return -EINVAL;
diff --git a/drivers/gpio/tca642x.c b/drivers/gpio/tca642x.c
index 463cfe879a..7007c7a002 100644
--- a/drivers/gpio/tca642x.c
+++ b/drivers/gpio/tca642x.c
@@ -213,6 +213,24 @@ static int tca642x_info(uchar chip)
return 0;
}
+static int tca642x_get_bank(int pin)
+{
+ int gpio_bank;
+
+ if (pin <= 7) {
+ gpio_bank = 0;
+ } else if ((pin >= 10) && (pin <= 17)) {
+ gpio_bank = 1;
+ } else if ((pin >= 20) && (pin <= 27)) {
+ gpio_bank = 2;
+ } else {
+ printf("Requested pin is not available\n");
+ gpio_bank = -1;
+ }
+
+ return gpio_bank;
+}
+
static struct cmd_tbl cmd_tca642x[] = {
U_BOOT_CMD_MKENT(device, 3, 0, (void *)TCA642X_CMD_DEVICE, "", ""),
U_BOOT_CMD_MKENT(output, 4, 0, (void *)TCA642X_CMD_OUTPUT, "", ""),
@@ -226,7 +244,7 @@ static int do_tca642x(struct cmd_tbl *cmdtp, int flag, int argc,
{
static uchar chip = CONFIG_SYS_I2C_TCA642X_ADDR;
int ret = CMD_RET_USAGE, val;
- uint8_t gpio_bank = 0;
+ int gpio_bank = 0;
uint8_t bank_shift;
ulong ul_arg2 = 0;
ulong ul_arg3 = 0;
@@ -247,20 +265,8 @@ static int do_tca642x(struct cmd_tbl *cmdtp, int flag, int argc,
ul_arg2 = simple_strtoul(argv[2], NULL, 10);
/* arg3 used as pin or invert value */
- if (argc > 3) {
+ if (argc > 3)
ul_arg3 = simple_strtoul(argv[3], NULL, 10) & 0x1;
- if (ul_arg2 <= 7) {
- gpio_bank = 0;
- } else if ((ul_arg2 >= 10) && (ul_arg2 <= 17)) {
- gpio_bank = 1;
- } else if ((ul_arg2 >= 20) && (ul_arg2 <= 27)) {
- gpio_bank = 2;
- } else {
- printf("Requested pin is not available\n");
- ret = CMD_RET_FAILURE;
- goto error;
- }
- }
switch ((int)c->cmd) {
case TCA642X_CMD_INFO:
@@ -277,6 +283,11 @@ static int do_tca642x(struct cmd_tbl *cmdtp, int flag, int argc,
break;
case TCA642X_CMD_INPUT:
+ gpio_bank = tca642x_get_bank(ul_arg2);
+ if (gpio_bank < 0) {
+ ret = CMD_RET_FAILURE;
+ goto error;
+ }
bank_shift = ul_arg2 - (gpio_bank * 10);
ret = tca642x_set_dir(chip, gpio_bank, (1 << bank_shift),
TCA642X_DIR_IN << bank_shift);
@@ -291,6 +302,11 @@ static int do_tca642x(struct cmd_tbl *cmdtp, int flag, int argc,
break;
case TCA642X_CMD_OUTPUT:
+ gpio_bank = tca642x_get_bank(ul_arg2);
+ if (gpio_bank < 0) {
+ ret = CMD_RET_FAILURE;
+ goto error;
+ }
bank_shift = ul_arg2 - (gpio_bank * 10);
ret = tca642x_set_dir(chip, gpio_bank, (1 << bank_shift),
(TCA642X_DIR_OUT << bank_shift));
@@ -303,6 +319,11 @@ static int do_tca642x(struct cmd_tbl *cmdtp, int flag, int argc,
break;
case TCA642X_CMD_INVERT:
+ gpio_bank = tca642x_get_bank(ul_arg2);
+ if (gpio_bank < 0) {
+ ret = CMD_RET_FAILURE;
+ goto error;
+ }
bank_shift = ul_arg2 - (gpio_bank * 10);
ret = tca642x_set_pol(chip, gpio_bank, (1 << bank_shift),
(ul_arg3 << bank_shift));
diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig
index b3bd5c6bb7..ccf81abbe9 100644
--- a/drivers/pwm/Kconfig
+++ b/drivers/pwm/Kconfig
@@ -75,3 +75,10 @@ config PWM_SUNXI
help
This PWM is found on H3, A64 and other Allwinner SoCs. It supports a
programmable period and duty cycle. A 16-bit counter is used.
+
+config PWM_TI_EHRPWM
+ bool "Enable support for EHRPWM PWM"
+ depends on DM_PWM && ARCH_OMAP2PLUS
+ default y
+ help
+ PWM driver support for the EHRPWM controller found on TI SOCs.
diff --git a/drivers/pwm/Makefile b/drivers/pwm/Makefile
index f21ae7d76e..0b9d2698a3 100644
--- a/drivers/pwm/Makefile
+++ b/drivers/pwm/Makefile
@@ -19,3 +19,4 @@ obj-$(CONFIG_PWM_SANDBOX) += sandbox_pwm.o
obj-$(CONFIG_PWM_SIFIVE) += pwm-sifive.o
obj-$(CONFIG_PWM_TEGRA) += tegra_pwm.o
obj-$(CONFIG_PWM_SUNXI) += sunxi_pwm.o
+obj-$(CONFIG_PWM_TI_EHRPWM) += pwm-ti-ehrpwm.o
diff --git a/drivers/pwm/pwm-ti-ehrpwm.c b/drivers/pwm/pwm-ti-ehrpwm.c
new file mode 100644
index 0000000000..ac3d731d22
--- /dev/null
+++ b/drivers/pwm/pwm-ti-ehrpwm.c
@@ -0,0 +1,468 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * EHRPWM PWM driver
+ *
+ * Copyright (C) 2020 Dario Binacchi <dariobin@libero.it>
+ *
+ * Based on Linux kernel drivers/pwm/pwm-tiehrpwm.c
+ */
+
+#include <common.h>
+#include <clk.h>
+#include <div64.h>
+#include <dm.h>
+#include <dm/device_compat.h>
+#include <pwm.h>
+#include <asm/io.h>
+
+#define NSEC_PER_SEC 1000000000L
+
+/* Time base module registers */
+#define TI_EHRPWM_TBCTL 0x00
+#define TI_EHRPWM_TBPRD 0x0A
+
+#define TI_EHRPWM_TBCTL_PRDLD_MASK BIT(3)
+#define TI_EHRPWM_TBCTL_PRDLD_SHDW 0
+#define TI_EHRPWM_TBCTL_PRDLD_IMDT BIT(3)
+#define TI_EHRPWM_TBCTL_CLKDIV_MASK GENMASK(12, 7)
+#define TI_EHRPWM_TBCTL_CTRMODE_MASK GENMASK(1, 0)
+#define TI_EHRPWM_TBCTL_CTRMODE_UP 0
+#define TI_EHRPWM_TBCTL_CTRMODE_DOWN BIT(0)
+#define TI_EHRPWM_TBCTL_CTRMODE_UPDOWN BIT(1)
+#define TI_EHRPWM_TBCTL_CTRMODE_FREEZE GENMASK(1, 0)
+
+#define TI_EHRPWM_TBCTL_HSPCLKDIV_SHIFT 7
+#define TI_EHRPWM_TBCTL_CLKDIV_SHIFT 10
+
+#define TI_EHRPWM_CLKDIV_MAX 7
+#define TI_EHRPWM_HSPCLKDIV_MAX 7
+#define TI_EHRPWM_PERIOD_MAX 0xFFFF
+
+/* Counter compare module registers */
+#define TI_EHRPWM_CMPA 0x12
+#define TI_EHRPWM_CMPB 0x14
+
+/* Action qualifier module registers */
+#define TI_EHRPWM_AQCTLA 0x16
+#define TI_EHRPWM_AQCTLB 0x18
+#define TI_EHRPWM_AQSFRC 0x1A
+#define TI_EHRPWM_AQCSFRC 0x1C
+
+#define TI_EHRPWM_AQCTL_CBU_MASK GENMASK(9, 8)
+#define TI_EHRPWM_AQCTL_CBU_FRCLOW BIT(8)
+#define TI_EHRPWM_AQCTL_CBU_FRCHIGH BIT(9)
+#define TI_EHRPWM_AQCTL_CBU_FRCTOGGLE GENMASK(9, 8)
+#define TI_EHRPWM_AQCTL_CAU_MASK GENMASK(5, 4)
+#define TI_EHRPWM_AQCTL_CAU_FRCLOW BIT(4)
+#define TI_EHRPWM_AQCTL_CAU_FRCHIGH BIT(5)
+#define TI_EHRPWM_AQCTL_CAU_FRCTOGGLE GENMASK(5, 4)
+#define TI_EHRPWM_AQCTL_PRD_MASK GENMASK(3, 2)
+#define TI_EHRPWM_AQCTL_PRD_FRCLOW BIT(2)
+#define TI_EHRPWM_AQCTL_PRD_FRCHIGH BIT(3)
+#define TI_EHRPWM_AQCTL_PRD_FRCTOGGLE GENMASK(3, 2)
+#define TI_EHRPWM_AQCTL_ZRO_MASK GENMASK(1, 0)
+#define TI_EHRPWM_AQCTL_ZRO_FRCLOW BIT(0)
+#define TI_EHRPWM_AQCTL_ZRO_FRCHIGH BIT(1)
+#define TI_EHRPWM_AQCTL_ZRO_FRCTOGGLE GENMASK(1, 0)
+
+#define TI_EHRPWM_AQCTL_CHANA_POLNORMAL (TI_EHRPWM_AQCTL_CAU_FRCLOW | \
+ TI_EHRPWM_AQCTL_PRD_FRCHIGH | \
+ TI_EHRPWM_AQCTL_ZRO_FRCHIGH)
+#define TI_EHRPWM_AQCTL_CHANA_POLINVERSED (TI_EHRPWM_AQCTL_CAU_FRCHIGH | \
+ TI_EHRPWM_AQCTL_PRD_FRCLOW | \
+ TI_EHRPWM_AQCTL_ZRO_FRCLOW)
+#define TI_EHRPWM_AQCTL_CHANB_POLNORMAL (TI_EHRPWM_AQCTL_CBU_FRCLOW | \
+ TI_EHRPWM_AQCTL_PRD_FRCHIGH | \
+ TI_EHRPWM_AQCTL_ZRO_FRCHIGH)
+#define TI_EHRPWM_AQCTL_CHANB_POLINVERSED (TI_EHRPWM_AQCTL_CBU_FRCHIGH | \
+ TI_EHRPWM_AQCTL_PRD_FRCLOW | \
+ TI_EHRPWM_AQCTL_ZRO_FRCLOW)
+
+#define TI_EHRPWM_AQSFRC_RLDCSF_MASK GENMASK(7, 6)
+#define TI_EHRPWM_AQSFRC_RLDCSF_ZRO 0
+#define TI_EHRPWM_AQSFRC_RLDCSF_PRD BIT(6)
+#define TI_EHRPWM_AQSFRC_RLDCSF_ZROPRD BIT(7)
+#define TI_EHRPWM_AQSFRC_RLDCSF_IMDT GENMASK(7, 6)
+
+#define TI_EHRPWM_AQCSFRC_CSFB_MASK GENMASK(3, 2)
+#define TI_EHRPWM_AQCSFRC_CSFB_FRCDIS 0
+#define TI_EHRPWM_AQCSFRC_CSFB_FRCLOW BIT(2)
+#define TI_EHRPWM_AQCSFRC_CSFB_FRCHIGH BIT(3)
+#define TI_EHRPWM_AQCSFRC_CSFB_DISSWFRC GENMASK(3, 2)
+#define TI_EHRPWM_AQCSFRC_CSFA_MASK GENMASK(1, 0)
+#define TI_EHRPWM_AQCSFRC_CSFA_FRCDIS 0
+#define TI_EHRPWM_AQCSFRC_CSFA_FRCLOW BIT(0)
+#define TI_EHRPWM_AQCSFRC_CSFA_FRCHIGH BIT(1)
+#define TI_EHRPWM_AQCSFRC_CSFA_DISSWFRC GENMASK(1, 0)
+
+#define TI_EHRPWM_NUM_CHANNELS 2
+
+struct ti_ehrpwm_priv {
+ fdt_addr_t regs;
+ u32 clk_rate;
+ struct clk tbclk;
+ unsigned long period_cycles[TI_EHRPWM_NUM_CHANNELS];
+ bool polarity_reversed[TI_EHRPWM_NUM_CHANNELS];
+};
+
+static void ti_ehrpwm_modify(u16 val, u16 mask, fdt_addr_t reg)
+{
+ unsigned short v;
+
+ v = readw(reg);
+ v &= ~mask;
+ v |= val & mask;
+ writew(v, reg);
+}
+
+static int ti_ehrpwm_set_invert(struct udevice *dev, uint channel,
+ bool polarity)
+{
+ struct ti_ehrpwm_priv *priv = dev_get_priv(dev);
+
+ if (channel >= TI_EHRPWM_NUM_CHANNELS)
+ return -ENOSPC;
+
+ /* Configuration of polarity in hardware delayed, do at enable */
+ priv->polarity_reversed[channel] = polarity;
+ return 0;
+}
+
+/**
+ * set_prescale_div - Set up the prescaler divider function
+ * @rqst_prescaler: prescaler value min
+ * @prescale_div: prescaler value set
+ * @tb_clk_div: Time Base Control prescaler bits
+ */
+static int set_prescale_div(unsigned long rqst_prescaler, u16 *prescale_div,
+ u16 *tb_clk_div)
+{
+ unsigned int clkdiv, hspclkdiv;
+
+ for (clkdiv = 0; clkdiv <= TI_EHRPWM_CLKDIV_MAX; clkdiv++) {
+ for (hspclkdiv = 0; hspclkdiv <= TI_EHRPWM_HSPCLKDIV_MAX;
+ hspclkdiv++) {
+ /*
+ * calculations for prescaler value :
+ * prescale_div = HSPCLKDIVIDER * CLKDIVIDER.
+ * HSPCLKDIVIDER = 2 ** hspclkdiv
+ * CLKDIVIDER = (1), if clkdiv == 0 *OR*
+ * (2 * clkdiv), if clkdiv != 0
+ *
+ * Configure prescale_div value such that period
+ * register value is less than 65535.
+ */
+
+ *prescale_div = (1 << clkdiv) *
+ (hspclkdiv ? (hspclkdiv * 2) : 1);
+ if (*prescale_div > rqst_prescaler) {
+ *tb_clk_div =
+ (clkdiv << TI_EHRPWM_TBCTL_CLKDIV_SHIFT) |
+ (hspclkdiv <<
+ TI_EHRPWM_TBCTL_HSPCLKDIV_SHIFT);
+ return 0;
+ }
+ }
+ }
+
+ return 1;
+}
+
+static void ti_ehrpwm_configure_polarity(struct udevice *dev, uint channel)
+{
+ struct ti_ehrpwm_priv *priv = dev_get_priv(dev);
+ u16 aqctl_val, aqctl_mask;
+ unsigned int aqctl_reg;
+
+ /*
+ * Configure PWM output to HIGH/LOW level on counter
+ * reaches compare register value and LOW/HIGH level
+ * on counter value reaches period register value and
+ * zero value on counter
+ */
+ if (channel == 1) {
+ aqctl_reg = TI_EHRPWM_AQCTLB;
+ aqctl_mask = TI_EHRPWM_AQCTL_CBU_MASK;
+
+ if (priv->polarity_reversed[channel])
+ aqctl_val = TI_EHRPWM_AQCTL_CHANB_POLINVERSED;
+ else
+ aqctl_val = TI_EHRPWM_AQCTL_CHANB_POLNORMAL;
+ } else {
+ aqctl_reg = TI_EHRPWM_AQCTLA;
+ aqctl_mask = TI_EHRPWM_AQCTL_CAU_MASK;
+
+ if (priv->polarity_reversed[channel])
+ aqctl_val = TI_EHRPWM_AQCTL_CHANA_POLINVERSED;
+ else
+ aqctl_val = TI_EHRPWM_AQCTL_CHANA_POLNORMAL;
+ }
+
+ aqctl_mask |= TI_EHRPWM_AQCTL_PRD_MASK | TI_EHRPWM_AQCTL_ZRO_MASK;
+ ti_ehrpwm_modify(aqctl_val, aqctl_mask, priv->regs + aqctl_reg);
+}
+
+/*
+ * period_ns = 10^9 * (ps_divval * period_cycles) / PWM_CLK_RATE
+ * duty_ns = 10^9 * (ps_divval * duty_cycles) / PWM_CLK_RATE
+ */
+static int ti_ehrpwm_set_config(struct udevice *dev, uint channel,
+ uint period_ns, uint duty_ns)
+{
+ struct ti_ehrpwm_priv *priv = dev_get_priv(dev);
+ u32 period_cycles, duty_cycles;
+ u16 ps_divval, tb_divval;
+ unsigned int i, cmp_reg;
+ unsigned long long c;
+
+ if (channel >= TI_EHRPWM_NUM_CHANNELS)
+ return -ENOSPC;
+
+ if (period_ns > NSEC_PER_SEC)
+ return -ERANGE;
+
+ c = priv->clk_rate;
+ c = c * period_ns;
+ do_div(c, NSEC_PER_SEC);
+ period_cycles = (unsigned long)c;
+
+ if (period_cycles < 1) {
+ period_cycles = 1;
+ duty_cycles = 1;
+ } else {
+ c = priv->clk_rate;
+ c = c * duty_ns;
+ do_div(c, NSEC_PER_SEC);
+ duty_cycles = (unsigned long)c;
+ }
+
+ dev_dbg(dev, "channel=%d, period_ns=%d, duty_ns=%d\n",
+ channel, period_ns, duty_ns);
+
+ /*
+ * Period values should be same for multiple PWM channels as IP uses
+ * same period register for multiple channels.
+ */
+ for (i = 0; i < TI_EHRPWM_NUM_CHANNELS; i++) {
+ if (priv->period_cycles[i] &&
+ priv->period_cycles[i] != period_cycles) {
+ /*
+ * Allow channel to reconfigure period if no other
+ * channels being configured.
+ */
+ if (i == channel)
+ continue;
+
+ dev_err(dev, "period value conflicts with channel %u\n",
+ i);
+ return -EINVAL;
+ }
+ }
+
+ priv->period_cycles[channel] = period_cycles;
+
+ /* Configure clock prescaler to support Low frequency PWM wave */
+ if (set_prescale_div(period_cycles / TI_EHRPWM_PERIOD_MAX, &ps_divval,
+ &tb_divval)) {
+ dev_err(dev, "unsupported values\n");
+ return -EINVAL;
+ }
+
+ /* Update clock prescaler values */
+ ti_ehrpwm_modify(tb_divval, TI_EHRPWM_TBCTL_CLKDIV_MASK,
+ priv->regs + TI_EHRPWM_TBCTL);
+
+ /* Update period & duty cycle with presacler division */
+ period_cycles = period_cycles / ps_divval;
+ duty_cycles = duty_cycles / ps_divval;
+
+ /* Configure shadow loading on Period register */
+ ti_ehrpwm_modify(TI_EHRPWM_TBCTL_PRDLD_SHDW, TI_EHRPWM_TBCTL_PRDLD_MASK,
+ priv->regs + TI_EHRPWM_TBCTL);
+
+ writew(period_cycles, priv->regs + TI_EHRPWM_TBPRD);
+
+ /* Configure ehrpwm counter for up-count mode */
+ ti_ehrpwm_modify(TI_EHRPWM_TBCTL_CTRMODE_UP,
+ TI_EHRPWM_TBCTL_CTRMODE_MASK,
+ priv->regs + TI_EHRPWM_TBCTL);
+
+ if (channel == 1)
+ /* Channel 1 configured with compare B register */
+ cmp_reg = TI_EHRPWM_CMPB;
+ else
+ /* Channel 0 configured with compare A register */
+ cmp_reg = TI_EHRPWM_CMPA;
+
+ writew(duty_cycles, priv->regs + cmp_reg);
+ return 0;
+}
+
+static int ti_ehrpwm_disable(struct udevice *dev, uint channel)
+{
+ struct ti_ehrpwm_priv *priv = dev_get_priv(dev);
+ u16 aqcsfrc_val, aqcsfrc_mask;
+ int err;
+
+ if (channel >= TI_EHRPWM_NUM_CHANNELS)
+ return -ENOSPC;
+
+ /* Action Qualifier puts PWM output low forcefully */
+ if (channel) {
+ aqcsfrc_val = TI_EHRPWM_AQCSFRC_CSFB_FRCLOW;
+ aqcsfrc_mask = TI_EHRPWM_AQCSFRC_CSFB_MASK;
+ } else {
+ aqcsfrc_val = TI_EHRPWM_AQCSFRC_CSFA_FRCLOW;
+ aqcsfrc_mask = TI_EHRPWM_AQCSFRC_CSFA_MASK;
+ }
+
+ /* Update shadow register first before modifying active register */
+ ti_ehrpwm_modify(TI_EHRPWM_AQSFRC_RLDCSF_ZRO,
+ TI_EHRPWM_AQSFRC_RLDCSF_MASK,
+ priv->regs + TI_EHRPWM_AQSFRC);
+
+ ti_ehrpwm_modify(aqcsfrc_val, aqcsfrc_mask,
+ priv->regs + TI_EHRPWM_AQCSFRC);
+
+ /*
+ * Changes to immediate action on Action Qualifier. This puts
+ * Action Qualifier control on PWM output from next TBCLK
+ */
+ ti_ehrpwm_modify(TI_EHRPWM_AQSFRC_RLDCSF_IMDT,
+ TI_EHRPWM_AQSFRC_RLDCSF_MASK,
+ priv->regs + TI_EHRPWM_AQSFRC);
+
+ ti_ehrpwm_modify(aqcsfrc_val, aqcsfrc_mask,
+ priv->regs + TI_EHRPWM_AQCSFRC);
+
+ /* Disabling TBCLK on PWM disable */
+ err = clk_disable(&priv->tbclk);
+ if (err) {
+ dev_err(dev, "failed to disable tbclk\n");
+ return err;
+ }
+
+ return 0;
+}
+
+static int ti_ehrpwm_enable(struct udevice *dev, uint channel)
+{
+ struct ti_ehrpwm_priv *priv = dev_get_priv(dev);
+ u16 aqcsfrc_val, aqcsfrc_mask;
+ int err;
+
+ if (channel >= TI_EHRPWM_NUM_CHANNELS)
+ return -ENOSPC;
+
+ /* Disabling Action Qualifier on PWM output */
+ if (channel) {
+ aqcsfrc_val = TI_EHRPWM_AQCSFRC_CSFB_FRCDIS;
+ aqcsfrc_mask = TI_EHRPWM_AQCSFRC_CSFB_MASK;
+ } else {
+ aqcsfrc_val = TI_EHRPWM_AQCSFRC_CSFA_FRCDIS;
+ aqcsfrc_mask = TI_EHRPWM_AQCSFRC_CSFA_MASK;
+ }
+
+ /* Changes to shadow mode */
+ ti_ehrpwm_modify(TI_EHRPWM_AQSFRC_RLDCSF_ZRO,
+ TI_EHRPWM_AQSFRC_RLDCSF_MASK,
+ priv->regs + TI_EHRPWM_AQSFRC);
+
+ ti_ehrpwm_modify(aqcsfrc_val, aqcsfrc_mask,
+ priv->regs + TI_EHRPWM_AQCSFRC);
+
+ /* Channels polarity can be configured from action qualifier module */
+ ti_ehrpwm_configure_polarity(dev, channel);
+
+ err = clk_enable(&priv->tbclk);
+ if (err) {
+ dev_err(dev, "failed to enable tbclk\n");
+ return err;
+ }
+
+ return 0;
+}
+
+static int ti_ehrpwm_set_enable(struct udevice *dev, uint channel, bool enable)
+{
+ if (enable)
+ return ti_ehrpwm_enable(dev, channel);
+
+ return ti_ehrpwm_disable(dev, channel);
+}
+
+static int ti_ehrpwm_of_to_plat(struct udevice *dev)
+{
+ struct ti_ehrpwm_priv *priv = dev_get_priv(dev);
+
+ priv->regs = dev_read_addr(dev);
+ if (priv->regs == FDT_ADDR_T_NONE) {
+ dev_err(dev, "invalid address\n");
+ return -EINVAL;
+ }
+
+ dev_dbg(dev, "regs=0x%08lx\n", priv->regs);
+ return 0;
+}
+
+static int ti_ehrpwm_remove(struct udevice *dev)
+{
+ struct ti_ehrpwm_priv *priv = dev_get_priv(dev);
+
+ clk_release_all(&priv->tbclk, 1);
+ return 0;
+}
+
+static int ti_ehrpwm_probe(struct udevice *dev)
+{
+ struct ti_ehrpwm_priv *priv = dev_get_priv(dev);
+ struct clk clk;
+ int err;
+
+ err = clk_get_by_name(dev, "fck", &clk);
+ if (err) {
+ dev_err(dev, "failed to get clock\n");
+ return err;
+ }
+
+ priv->clk_rate = clk_get_rate(&clk);
+ if (IS_ERR_VALUE(priv->clk_rate) || !priv->clk_rate) {
+ dev_err(dev, "failed to get clock rate\n");
+ if (IS_ERR_VALUE(priv->clk_rate))
+ return priv->clk_rate;
+
+ return -EINVAL;
+ }
+
+ /* Acquire tbclk for Time Base EHRPWM submodule */
+ err = clk_get_by_name(dev, "tbclk", &priv->tbclk);
+ if (err) {
+ dev_err(dev, "failed to get tbclk clock\n");
+ return err;
+ }
+
+ return 0;
+}
+
+static const struct pwm_ops ti_ehrpwm_ops = {
+ .set_config = ti_ehrpwm_set_config,
+ .set_enable = ti_ehrpwm_set_enable,
+ .set_invert = ti_ehrpwm_set_invert,
+};
+
+static const struct udevice_id ti_ehrpwm_ids[] = {
+ {.compatible = "ti,am3352-ehrpwm"},
+ {.compatible = "ti,am33xx-ehrpwm"},
+ {}
+};
+
+U_BOOT_DRIVER(ti_ehrpwm) = {
+ .name = "ti_ehrpwm",
+ .id = UCLASS_PWM,
+ .of_match = ti_ehrpwm_ids,
+ .ops = &ti_ehrpwm_ops,
+ .ofdata_to_platdata = ti_ehrpwm_of_to_plat,
+ .probe = ti_ehrpwm_probe,
+ .remove = ti_ehrpwm_remove,
+ .priv_auto = sizeof(struct ti_ehrpwm_priv),
+};
diff --git a/drivers/remoteproc/ti_k3_arm64_rproc.c b/drivers/remoteproc/ti_k3_arm64_rproc.c
index 1041f3805f..1f2415dc1a 100644
--- a/drivers/remoteproc/ti_k3_arm64_rproc.c
+++ b/drivers/remoteproc/ti_k3_arm64_rproc.c
@@ -23,6 +23,7 @@
#define INVALID_ID 0xffff
#define GTC_CNTCR_REG 0x0
+#define GTC_CNTFID0_REG 0x20
#define GTC_CNTR_EN 0x3
/**
@@ -31,6 +32,7 @@
* @rproc_rst: rproc reset control data
* @sci: Pointer to TISCI handle
* @tsp: TISCI processor control helper structure
+ * @gtc_clk: GTC clock description
* @gtc_base: Timer base address.
*/
struct k3_arm64_privdata {
@@ -38,6 +40,7 @@ struct k3_arm64_privdata {
struct power_domain gtc_pwrdmn;
struct reset_ctl rproc_rst;
struct ti_sci_proc tsp;
+ struct clk gtc_clk;
void *gtc_base;
};
@@ -73,6 +76,7 @@ static int k3_arm64_load(struct udevice *dev, ulong addr, ulong size)
static int k3_arm64_start(struct udevice *dev)
{
struct k3_arm64_privdata *rproc = dev_get_priv(dev);
+ ulong gtc_rate;
int ret;
dev_dbg(dev, "%s\n", __func__);
@@ -83,6 +87,11 @@ static int k3_arm64_start(struct udevice *dev)
return ret;
}
+ gtc_rate = clk_get_rate(&rproc->gtc_clk);
+ dev_dbg(dev, "GTC RATE= %d\n", (u32) gtc_rate);
+ /* Store the clock frequency down for GTC users to pick up */
+ writel((u32)gtc_rate, rproc->gtc_base + GTC_CNTFID0_REG);
+
/* Enable the timer before starting remote core */
writel(GTC_CNTR_EN, rproc->gtc_base + GTC_CNTCR_REG);
@@ -169,6 +178,12 @@ static int k3_arm64_of_to_priv(struct udevice *dev,
return ret;
}
+ ret = clk_get_by_index(dev, 0, &rproc->gtc_clk);
+ if (ret) {
+ dev_err(dev, "clk_get failed: %d\n", ret);
+ return ret;
+ }
+
ret = reset_get_by_index(dev, 0, &rproc->rproc_rst);
if (ret) {
dev_err(dev, "reset_get() failed: %d\n", ret);
diff --git a/drivers/spi/omap3_spi.c b/drivers/spi/omap3_spi.c
index 78e2a25cdb..74931768c0 100644
--- a/drivers/spi/omap3_spi.c
+++ b/drivers/spi/omap3_spi.c
@@ -37,6 +37,8 @@ struct omap3_spi_priv {
unsigned int mode;
unsigned int wordlen;
unsigned int pin_dir:1;
+
+ bool bus_claimed;
};
static void omap3_spi_write_chconf(struct omap3_spi_priv *priv, int val)
@@ -372,6 +374,8 @@ static void _omap3_spi_claim_bus(struct omap3_spi_priv *priv)
conf |= OMAP3_MCSPI_MODULCTRL_SINGLE;
writel(conf, &priv->regs->modulctrl);
+
+ priv->bus_claimed = true;
}
static int omap3_spi_claim_bus(struct udevice *dev)
@@ -381,9 +385,12 @@ static int omap3_spi_claim_bus(struct udevice *dev)
struct dm_spi_slave_plat *slave_plat = dev_get_parent_plat(dev);
priv->cs = slave_plat->cs;
- priv->freq = slave_plat->max_hz;
+ if (!priv->freq)
+ priv->freq = slave_plat->max_hz;
_omap3_spi_claim_bus(priv);
+ _omap3_spi_set_speed(priv);
+ _omap3_spi_set_mode(priv);
return 0;
}
@@ -395,6 +402,8 @@ static int omap3_spi_release_bus(struct udevice *dev)
writel(OMAP3_MCSPI_MODULCTRL_MS, &priv->regs->modulctrl);
+ priv->bus_claimed = false;
+
return 0;
}
@@ -440,7 +449,8 @@ static int omap3_spi_set_speed(struct udevice *dev, unsigned int speed)
struct omap3_spi_priv *priv = dev_get_priv(dev);
priv->freq = speed;
- _omap3_spi_set_speed(priv);
+ if (priv->bus_claimed)
+ _omap3_spi_set_speed(priv);
return 0;
}
@@ -451,7 +461,8 @@ static int omap3_spi_set_mode(struct udevice *dev, uint mode)
priv->mode = mode;
- _omap3_spi_set_mode(priv);
+ if (priv->bus_claimed)
+ _omap3_spi_set_mode(priv);
return 0;
}
diff --git a/drivers/spi/ti_qspi.c b/drivers/spi/ti_qspi.c
index 7c3b1f7b88..76bc480f43 100644
--- a/drivers/spi/ti_qspi.c
+++ b/drivers/spi/ti_qspi.c
@@ -467,8 +467,8 @@ static int ti_qspi_of_to_plat(struct udevice *bus)
priv->memory_map = map_physmem(mmap_addr, mmap_size, MAP_NOCACHE);
priv->mmap_size = mmap_size;
- priv->max_hz = fdtdec_get_int(blob, node, "spi-max-frequency", -1);
- if (priv->max_hz < 0) {
+ priv->max_hz = dev_read_u32_default(bus, "spi-max-frequency", 0);
+ if (!priv->max_hz) {
debug("Error: Max frequency missing\n");
return -ENODEV;
}
diff --git a/drivers/timer/omap-timer.c b/drivers/timer/omap-timer.c
index 7ac20d78dd..721e385fd1 100644
--- a/drivers/timer/omap-timer.c
+++ b/drivers/timer/omap-timer.c
@@ -19,8 +19,6 @@
#define TCLR_PRE_EN BIT(5) /* Pre-scaler enable */
#define TCLR_PTV_SHIFT (2) /* Pre-scaler shift value */
-#define TIMER_CLOCK (V_SCLK / (2 << CONFIG_SYS_PTV))
-
struct omap_gptimer_regs {
unsigned int tidr; /* offset 0x00 */
unsigned char res1[12];
@@ -61,7 +59,9 @@ static int omap_timer_probe(struct udevice *dev)
struct omap_timer_priv *priv = dev_get_priv(dev);
if (!uc_priv->clock_rate)
- uc_priv->clock_rate = TIMER_CLOCK;
+ uc_priv->clock_rate = V_SCLK;
+
+ uc_priv->clock_rate /= (2 << CONFIG_SYS_PTV);
/* start the counter ticking up, reload value on overflow */
writel(0, &priv->regs->tldr);
diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig
index 71363409f0..a3f8eeba5d 100644
--- a/drivers/video/Kconfig
+++ b/drivers/video/Kconfig
@@ -546,10 +546,7 @@ config ATMEL_HLCD
help
HLCDC supports video output to an attached LCD panel.
-config AM335X_LCD
- bool "Enable AM335x video support"
- help
- Supports video output to an attached LCD panel.
+source "drivers/video/ti/Kconfig"
config LOGICORE_DP_TX
bool "Enable Logicore DP TX driver"
diff --git a/drivers/video/Makefile b/drivers/video/Makefile
index 9db96aa891..76e3914678 100644
--- a/drivers/video/Makefile
+++ b/drivers/video/Makefile
@@ -22,8 +22,8 @@ obj-${CONFIG_EXYNOS_FB} += exynos/
obj-${CONFIG_VIDEO_ROCKCHIP} += rockchip/
obj-${CONFIG_VIDEO_STM32} += stm32/
obj-${CONFIG_VIDEO_TEGRA124} += tegra124/
+obj-y += ti/
-obj-$(CONFIG_AM335X_LCD) += am335x-fb.o
obj-$(CONFIG_ATI_RADEON_FB) += ati_radeon_fb.o videomodes.o
obj-$(CONFIG_ATMEL_HLCD) += atmel_hlcdfb.o
obj-$(CONFIG_ATMEL_LCD) += atmel_lcdfb.o
diff --git a/drivers/video/ti/Kconfig b/drivers/video/ti/Kconfig
new file mode 100644
index 0000000000..3081e9e8c0
--- /dev/null
+++ b/drivers/video/ti/Kconfig
@@ -0,0 +1,8 @@
+# SPDX-License-Identifier: GPL-2.0+
+#
+# Copyright (C) 2020 Dario Binacchi <dariobin@libero.it>
+#
+config AM335X_LCD
+ bool "Enable AM335x video support"
+ help
+ Supports video output to an attached LCD panel.
diff --git a/drivers/video/ti/Makefile b/drivers/video/ti/Makefile
new file mode 100644
index 0000000000..ddddd59216
--- /dev/null
+++ b/drivers/video/ti/Makefile
@@ -0,0 +1,10 @@
+# SPDX-License-Identifier: GPL-2.0+
+#
+# Copyright (C) 2020 Dario Binacchi <dariobin@libero.it>
+#
+
+ifdef CONFIG_DM_VIDEO
+obj-$(CONFIG_AM335X_LCD) += tilcdc.o tilcdc-panel.o
+else
+obj-$(CONFIG_AM335X_LCD) += am335x-fb.o
+endif
diff --git a/drivers/video/am335x-fb.c b/drivers/video/ti/am335x-fb.c
index e99a9185a2..5fa6f794ec 100644
--- a/drivers/video/am335x-fb.c
+++ b/drivers/video/ti/am335x-fb.c
@@ -12,16 +12,13 @@
* - starts output DMA from gd->fb_base buffer
*/
#include <common.h>
-#include <dm.h>
#include <lcd.h>
#include <log.h>
-#include <video.h>
#include <asm/arch/clock.h>
#include <asm/arch/hardware.h>
#include <asm/arch/omap.h>
#include <asm/arch/sys_proto.h>
#include <asm/io.h>
-#include <asm/utils.h>
#include <linux/delay.h>
#include <linux/err.h>
#include "am335x-fb.h"
@@ -110,6 +107,25 @@ struct am335x_lcdhw {
unsigned int clkc_reset; /* 0x70 */
};
+DECLARE_GLOBAL_DATA_PTR;
+
+#if !defined(LCD_CNTL_BASE)
+#error "hw-base address of LCD-Controller (LCD_CNTL_BASE) not defined!"
+#endif
+
+/* Macro definitions */
+#define FBSIZE(x) (((x)->hactive * (x)->vactive * (x)->bpp) >> 3)
+
+#define LCDC_RASTER_TIMING_2_INVMASK(x) ((x) & GENMASK(25, 20))
+
+static struct am335x_lcdhw *lcdhw = (void *)LCD_CNTL_BASE;
+
+int lcd_get_size(int *line_length)
+{
+ *line_length = (panel_info.vl_col * NBITS(panel_info.vl_bpix)) / 8;
+ return *line_length * panel_info.vl_row + 0x20;
+}
+
struct dpll_data {
unsigned long rounded_rate;
u16 rounded_m;
@@ -117,8 +133,6 @@ struct dpll_data {
u8 rounded_div;
};
-DECLARE_GLOBAL_DATA_PTR;
-
/**
* am335x_dpll_round_rate() - Round a target rate for an OMAP DPLL
*
@@ -197,25 +211,6 @@ static ulong am335x_fb_set_pixel_clk_rate(struct am335x_lcdhw *regs, ulong rate)
return round_rate;
}
-#if !CONFIG_IS_ENABLED(DM_VIDEO)
-
-#if !defined(LCD_CNTL_BASE)
-#error "hw-base address of LCD-Controller (LCD_CNTL_BASE) not defined!"
-#endif
-
-/* Macro definitions */
-#define FBSIZE(x) (((x)->hactive * (x)->vactive * (x)->bpp) >> 3)
-
-#define LCDC_RASTER_TIMING_2_INVMASK(x) ((x) & GENMASK(25, 20))
-
-static struct am335x_lcdhw *lcdhw = (void *)LCD_CNTL_BASE;
-
-int lcd_get_size(int *line_length)
-{
- *line_length = (panel_info.vl_col * NBITS(panel_info.vl_bpix)) / 8;
- return *line_length * panel_info.vl_row + 0x20;
-}
-
int am335xfb_init(struct am335x_lcdpanel *panel)
{
u32 raster_ctrl = 0;
@@ -320,301 +315,3 @@ int am335xfb_init(struct am335x_lcdpanel *panel)
return 0;
}
-
-#else /* CONFIG_DM_VIDEO */
-
-#define FBSIZE(t, p) (((t)->hactive.typ * (t)->vactive.typ * (p)->bpp) >> 3)
-
-enum {
- LCD_MAX_WIDTH = 2048,
- LCD_MAX_HEIGHT = 2048,
- LCD_MAX_LOG2_BPP = VIDEO_BPP32,
-};
-
-/**
- * tilcdc_panel_info: Panel parameters
- *
- * @ac_bias: AC Bias Pin Frequency
- * @ac_bias_intrpt: AC Bias Pin Transitions per Interrupt
- * @dma_burst_sz: DMA burst size
- * @bpp: Bits per pixel
- * @fdd: FIFO DMA Request Delay
- * @tft_alt_mode: TFT Alternative Signal Mapping (Only for active)
- * @invert_pxl_clk: Invert pixel clock
- * @sync_edge: Horizontal and Vertical Sync Edge: 0=rising 1=falling
- * @sync_ctrl: Horizontal and Vertical Sync: Control: 0=ignore
- * @raster_order: Raster Data Order Select: 1=Most-to-least 0=Least-to-most
- * @fifo_th: DMA FIFO threshold
- */
-struct tilcdc_panel_info {
- u32 ac_bias;
- u32 ac_bias_intrpt;
- u32 dma_burst_sz;
- u32 bpp;
- u32 fdd;
- bool tft_alt_mode;
- bool invert_pxl_clk;
- u32 sync_edge;
- u32 sync_ctrl;
- u32 raster_order;
- u32 fifo_th;
-};
-
-struct am335x_fb_priv {
- struct am335x_lcdhw *regs;
- struct tilcdc_panel_info panel;
- struct display_timing timing;
-};
-
-static int am335x_fb_remove(struct udevice *dev)
-{
- struct video_uc_plat *uc_plat = dev_get_uclass_plat(dev);
-
- uc_plat->base -= 0x20;
- uc_plat->size += 0x20;
- return 0;
-}
-
-static int am335x_fb_probe(struct udevice *dev)
-{
- struct video_uc_plat *uc_plat = dev_get_uclass_plat(dev);
- struct video_priv *uc_priv = dev_get_uclass_priv(dev);
- struct am335x_fb_priv *priv = dev_get_priv(dev);
- struct am335x_lcdhw *regs = priv->regs;
- struct tilcdc_panel_info *panel = &priv->panel;
- struct display_timing *timing = &priv->timing;
- struct cm_dpll *const cmdpll = (struct cm_dpll *)CM_DPLL;
- u32 reg;
-
- /* Before relocation we don't need to do anything */
- if (!(gd->flags & GD_FLG_RELOC))
- return 0;
-
- am335x_fb_set_pixel_clk_rate(regs, timing->pixelclock.typ);
-
- /* clock source for LCDC from dispPLL M2 */
- writel(0, &cmdpll->clklcdcpixelclk);
-
- /* palette default entry */
- memset((void *)uc_plat->base, 0, 0x20);
- *(unsigned int *)uc_plat->base = 0x4000;
- /* point fb behind palette */
- uc_plat->base += 0x20;
- uc_plat->size -= 0x20;
-
- writel(LCDC_CLKC_ENABLE_CORECLKEN | LCDC_CLKC_ENABLE_LIDDCLKEN |
- LCDC_CLKC_ENABLE_DMACLKEN, &regs->clkc_enable);
- writel(0, &regs->raster_ctrl);
-
- reg = readl(&regs->ctrl) & LCDC_CTRL_CLK_DIVISOR_MASK;
- reg |= LCDC_CTRL_RASTER_MODE;
- writel(reg, &regs->ctrl);
-
- writel(uc_plat->base, &regs->lcddma_fb0_base);
- writel(uc_plat->base + FBSIZE(timing, panel),
- &regs->lcddma_fb0_ceiling);
- writel(uc_plat->base, &regs->lcddma_fb1_base);
- writel(uc_plat->base + FBSIZE(timing, panel),
- &regs->lcddma_fb1_ceiling);
-
- reg = LCDC_DMA_CTRL_FIFO_TH(panel->fifo_th);
- switch (panel->dma_burst_sz) {
- case 1:
- reg |= LCDC_DMA_CTRL_BURST_SIZE(LCDC_DMA_CTRL_BURST_1);
- break;
- case 2:
- reg |= LCDC_DMA_CTRL_BURST_SIZE(LCDC_DMA_CTRL_BURST_2);
- break;
- case 4:
- reg |= LCDC_DMA_CTRL_BURST_SIZE(LCDC_DMA_CTRL_BURST_4);
- break;
- case 8:
- reg |= LCDC_DMA_CTRL_BURST_SIZE(LCDC_DMA_CTRL_BURST_8);
- break;
- case 16:
- reg |= LCDC_DMA_CTRL_BURST_SIZE(LCDC_DMA_CTRL_BURST_16);
- break;
- }
-
- writel(reg, &regs->lcddma_ctrl);
-
- writel(LCDC_RASTER_TIMING_0_HORLSB(timing->hactive.typ) |
- LCDC_RASTER_TIMING_0_HORMSB(timing->hactive.typ) |
- LCDC_RASTER_TIMING_0_HFPLSB(timing->hfront_porch.typ) |
- LCDC_RASTER_TIMING_0_HBPLSB(timing->hback_porch.typ) |
- LCDC_RASTER_TIMING_0_HSWLSB(timing->hsync_len.typ),
- &regs->raster_timing0);
-
- writel(LCDC_RASTER_TIMING_1_VBP(timing->vback_porch.typ) |
- LCDC_RASTER_TIMING_1_VFP(timing->vfront_porch.typ) |
- LCDC_RASTER_TIMING_1_VSW(timing->vsync_len.typ) |
- LCDC_RASTER_TIMING_1_VERLSB(timing->vactive.typ),
- &regs->raster_timing1);
-
- reg = LCDC_RASTER_TIMING_2_ACB(panel->ac_bias) |
- LCDC_RASTER_TIMING_2_ACBI(panel->ac_bias_intrpt) |
- LCDC_RASTER_TIMING_2_HSWMSB(timing->hsync_len.typ) |
- LCDC_RASTER_TIMING_2_VERMSB(timing->vactive.typ) |
- LCDC_RASTER_TIMING_2_HBPMSB(timing->hback_porch.typ) |
- LCDC_RASTER_TIMING_2_HFPMSB(timing->hfront_porch.typ);
-
- if (timing->flags & DISPLAY_FLAGS_VSYNC_LOW)
- reg |= LCDC_RASTER_TIMING_2_VSYNC_INVERT;
-
- if (timing->flags & DISPLAY_FLAGS_HSYNC_LOW)
- reg |= LCDC_RASTER_TIMING_2_HSYNC_INVERT;
-
- if (panel->invert_pxl_clk)
- reg |= LCDC_RASTER_TIMING_2_PXCLK_INVERT;
-
- if (panel->sync_edge)
- reg |= LCDC_RASTER_TIMING_2_HSVS_RISEFALL;
-
- if (panel->sync_ctrl)
- reg |= LCDC_RASTER_TIMING_2_HSVS_CONTROL;
-
- writel(reg, &regs->raster_timing2);
-
- reg = LCDC_RASTER_CTRL_PALMODE_RAWDATA | LCDC_RASTER_CTRL_TFT_MODE |
- LCDC_RASTER_CTRL_ENABLE | LCDC_RASTER_CTRL_REQDLY(panel->fdd);
-
- if (panel->tft_alt_mode)
- reg |= LCDC_RASTER_CTRL_TFT_ALT_ENABLE;
-
- if (panel->bpp == 24)
- reg |= LCDC_RASTER_CTRL_TFT_24BPP_MODE;
- else if (panel->bpp == 32)
- reg |= LCDC_RASTER_CTRL_TFT_24BPP_MODE |
- LCDC_RASTER_CTRL_TFT_24BPP_UNPACK;
-
- if (panel->raster_order)
- reg |= LCDC_RASTER_CTRL_DATA_ORDER;
-
- writel(reg, &regs->raster_ctrl);
-
- uc_priv->xsize = timing->hactive.typ;
- uc_priv->ysize = timing->vactive.typ;
- uc_priv->bpix = log_2_n_round_up(panel->bpp);
- return 0;
-}
-
-static int am335x_fb_of_to_plat(struct udevice *dev)
-{
- struct am335x_fb_priv *priv = dev_get_priv(dev);
- struct tilcdc_panel_info *panel = &priv->panel;
- struct display_timing *timing = &priv->timing;
- ofnode node;
- int err;
-
- node = ofnode_by_compatible(ofnode_null(), "ti,am33xx-tilcdc");
- if (!ofnode_valid(node)) {
- dev_err(dev, "missing 'ti,am33xx-tilcdc' node\n");
- return -ENXIO;
- }
-
- priv->regs = (struct am335x_lcdhw *)ofnode_get_addr(node);
- dev_dbg(dev, "LCD: base address=0x%x\n", (unsigned int)priv->regs);
-
- err = ofnode_decode_display_timing(dev_ofnode(dev), 0, timing);
- if (err) {
- dev_err(dev, "failed to get display timing\n");
- return err;
- }
-
- if (timing->pixelclock.typ > (LCDC_FMAX / 2)) {
- dev_err(dev, "invalid display clock-frequency: %d Hz\n",
- timing->pixelclock.typ);
- return -EINVAL;
- }
-
- if (timing->hactive.typ > LCD_MAX_WIDTH)
- timing->hactive.typ = LCD_MAX_WIDTH;
-
- if (timing->vactive.typ > LCD_MAX_HEIGHT)
- timing->vactive.typ = LCD_MAX_HEIGHT;
-
- node = ofnode_find_subnode(dev_ofnode(dev), "panel-info");
- if (!ofnode_valid(node)) {
- dev_err(dev, "missing 'panel-info' node\n");
- return -ENXIO;
- }
-
- err |= ofnode_read_u32(node, "ac-bias", &panel->ac_bias);
- err |= ofnode_read_u32(node, "ac-bias-intrpt", &panel->ac_bias_intrpt);
- err |= ofnode_read_u32(node, "dma-burst-sz", &panel->dma_burst_sz);
- err |= ofnode_read_u32(node, "bpp", &panel->bpp);
- err |= ofnode_read_u32(node, "fdd", &panel->fdd);
- err |= ofnode_read_u32(node, "sync-edge", &panel->sync_edge);
- err |= ofnode_read_u32(node, "sync-ctrl", &panel->sync_ctrl);
- err |= ofnode_read_u32(node, "raster-order", &panel->raster_order);
- err |= ofnode_read_u32(node, "fifo-th", &panel->fifo_th);
- if (err) {
- dev_err(dev, "failed to get panel info\n");
- return err;
- }
-
- switch (panel->bpp) {
- case 16:
- case 24:
- case 32:
- break;
- default:
- dev_err(dev, "invalid seting, bpp: %d\n", panel->bpp);
- return -EINVAL;
- }
-
- switch (panel->dma_burst_sz) {
- case 1:
- case 2:
- case 4:
- case 8:
- case 16:
- break;
- default:
- dev_err(dev, "invalid setting, dma-burst-sz: %d\n",
- panel->dma_burst_sz);
- return -EINVAL;
- }
-
- /* optional */
- panel->tft_alt_mode = ofnode_read_bool(node, "tft-alt-mode");
- panel->invert_pxl_clk = ofnode_read_bool(node, "invert-pxl-clk");
-
- dev_dbg(dev, "LCD: %dx%d, bpp=%d, clk=%d Hz\n", timing->hactive.typ,
- timing->vactive.typ, panel->bpp, timing->pixelclock.typ);
- dev_dbg(dev, " hbp=%d, hfp=%d, hsw=%d\n", timing->hback_porch.typ,
- timing->hfront_porch.typ, timing->hsync_len.typ);
- dev_dbg(dev, " vbp=%d, vfp=%d, vsw=%d\n", timing->vback_porch.typ,
- timing->vfront_porch.typ, timing->vsync_len.typ);
-
- return 0;
-}
-
-static int am335x_fb_bind(struct udevice *dev)
-{
- struct video_uc_plat *uc_plat = dev_get_uclass_plat(dev);
-
- uc_plat->size = ((LCD_MAX_WIDTH * LCD_MAX_HEIGHT *
- (1 << LCD_MAX_LOG2_BPP)) >> 3) + 0x20;
-
- dev_dbg(dev, "frame buffer size 0x%x\n", uc_plat->size);
- return 0;
-}
-
-static const struct udevice_id am335x_fb_ids[] = {
- { .compatible = "ti,tilcdc,panel" },
- { }
-};
-
-U_BOOT_DRIVER(am335x_fb) = {
- .name = "am335x_fb",
- .id = UCLASS_VIDEO,
- .of_match = am335x_fb_ids,
- .bind = am335x_fb_bind,
- .of_to_plat = am335x_fb_of_to_plat,
- .probe = am335x_fb_probe,
- .remove = am335x_fb_remove,
- .priv_auto = sizeof(struct am335x_fb_priv),
-};
-
-#endif /* CONFIG_DM_VIDEO */
diff --git a/drivers/video/am335x-fb.h b/drivers/video/ti/am335x-fb.h
index c9f92bc389..ad9b015e09 100644
--- a/drivers/video/am335x-fb.h
+++ b/drivers/video/ti/am335x-fb.h
@@ -7,8 +7,6 @@
#ifndef AM335X_FB_H
#define AM335X_FB_H
-#if !CONFIG_IS_ENABLED(DM_VIDEO)
-
#define HSVS_CONTROL BIT(25) /*
* 0 = lcd_lp and lcd_fp are driven on
* opposite edges of pixel clock than
@@ -70,6 +68,4 @@ struct am335x_lcdpanel {
int am335xfb_init(struct am335x_lcdpanel *panel);
-#endif /* CONFIG_DM_VIDEO */
-
#endif /* AM335X_FB_H */
diff --git a/drivers/video/ti/tilcdc-panel.c b/drivers/video/ti/tilcdc-panel.c
new file mode 100644
index 0000000000..b90dfae4ae
--- /dev/null
+++ b/drivers/video/ti/tilcdc-panel.c
@@ -0,0 +1,172 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * OMAP panel support
+ *
+ * Copyright (C) 2020 Dario Binacchi <dariobin@libero.it>
+ */
+
+#include <common.h>
+#include <backlight.h>
+#include <clk.h>
+#include <display.h>
+#include <dm.h>
+#include <dm/device_compat.h>
+#include <log.h>
+#include <panel.h>
+#include <asm/gpio.h>
+#include <linux/err.h>
+#include "tilcdc.h"
+
+struct tilcdc_panel_priv {
+ struct tilcdc_panel_info info;
+ struct display_timing timing;
+ struct udevice *backlight;
+ struct gpio_desc enable;
+};
+
+static int tilcdc_panel_enable_backlight(struct udevice *dev)
+{
+ struct tilcdc_panel_priv *priv = dev_get_priv(dev);
+
+ if (dm_gpio_is_valid(&priv->enable))
+ dm_gpio_set_value(&priv->enable, 1);
+
+ if (priv->backlight)
+ return backlight_enable(priv->backlight);
+
+ return 0;
+}
+
+static int tilcdc_panel_set_backlight(struct udevice *dev, int percent)
+{
+ struct tilcdc_panel_priv *priv = dev_get_priv(dev);
+
+ if (dm_gpio_is_valid(&priv->enable))
+ dm_gpio_set_value(&priv->enable, 1);
+
+ if (priv->backlight)
+ return backlight_set_brightness(priv->backlight, percent);
+
+ return 0;
+}
+
+int tilcdc_panel_get_display_info(struct udevice *dev,
+ struct tilcdc_panel_info *info)
+{
+ struct tilcdc_panel_priv *priv = dev_get_priv(dev);
+
+ memcpy(info, &priv->info, sizeof(*info));
+ return 0;
+}
+
+static int tilcdc_panel_get_display_timing(struct udevice *dev,
+ struct display_timing *timing)
+{
+ struct tilcdc_panel_priv *priv = dev_get_priv(dev);
+
+ memcpy(timing, &priv->timing, sizeof(*timing));
+ return 0;
+}
+
+static int tilcdc_panel_remove(struct udevice *dev)
+{
+ struct tilcdc_panel_priv *priv = dev_get_priv(dev);
+
+ if (dm_gpio_is_valid(&priv->enable))
+ dm_gpio_free(dev, &priv->enable);
+
+ return 0;
+}
+
+static int tilcdc_panel_probe(struct udevice *dev)
+{
+ struct tilcdc_panel_priv *priv = dev_get_priv(dev);
+ int err;
+
+ err = uclass_get_device_by_phandle(UCLASS_PANEL_BACKLIGHT, dev,
+ "backlight", &priv->backlight);
+ if (err)
+ dev_warn(dev, "failed to get backlight\n");
+
+ err = gpio_request_by_name(dev, "enable-gpios", 0, &priv->enable,
+ GPIOD_IS_OUT);
+ if (err) {
+ dev_warn(dev, "failed to get enable GPIO\n");
+ if (err != -ENOENT)
+ return err;
+ }
+
+ return 0;
+}
+
+static int tilcdc_panel_of_to_plat(struct udevice *dev)
+{
+ struct tilcdc_panel_priv *priv = dev_get_priv(dev);
+ ofnode node;
+ int err;
+
+ err = ofnode_decode_display_timing(dev_ofnode(dev), 0, &priv->timing);
+ if (err) {
+ dev_err(dev, "failed to get display timing\n");
+ return err;
+ }
+
+ node = dev_read_subnode(dev, "panel-info");
+ if (!ofnode_valid(node)) {
+ dev_err(dev, "missing 'panel-info' node\n");
+ return -ENXIO;
+ }
+
+ err |= ofnode_read_u32(node, "ac-bias", &priv->info.ac_bias);
+ err |= ofnode_read_u32(node, "ac-bias-intrpt",
+ &priv->info.ac_bias_intrpt);
+ err |= ofnode_read_u32(node, "dma-burst-sz", &priv->info.dma_burst_sz);
+ err |= ofnode_read_u32(node, "bpp", &priv->info.bpp);
+ err |= ofnode_read_u32(node, "fdd", &priv->info.fdd);
+ err |= ofnode_read_u32(node, "sync-edge", &priv->info.sync_edge);
+ err |= ofnode_read_u32(node, "sync-ctrl", &priv->info.sync_ctrl);
+ err |= ofnode_read_u32(node, "raster-order", &priv->info.raster_order);
+ err |= ofnode_read_u32(node, "fifo-th", &priv->info.fifo_th);
+ if (err) {
+ dev_err(dev, "failed to get panel info\n");
+ return err;
+ }
+
+ /* optional */
+ priv->info.tft_alt_mode = ofnode_read_bool(node, "tft-alt-mode");
+ priv->info.invert_pxl_clk = ofnode_read_bool(node, "invert-pxl-clk");
+
+ dev_dbg(dev, "LCD: %dx%d, bpp=%d, clk=%d Hz\n",
+ priv->timing.hactive.typ, priv->timing.vactive.typ,
+ priv->info.bpp, priv->timing.pixelclock.typ);
+ dev_dbg(dev, " hbp=%d, hfp=%d, hsw=%d\n",
+ priv->timing.hback_porch.typ, priv->timing.hfront_porch.typ,
+ priv->timing.hsync_len.typ);
+ dev_dbg(dev, " vbp=%d, vfp=%d, vsw=%d\n",
+ priv->timing.vback_porch.typ, priv->timing.vfront_porch.typ,
+ priv->timing.vsync_len.typ);
+
+ return 0;
+}
+
+static const struct panel_ops tilcdc_panel_ops = {
+ .enable_backlight = tilcdc_panel_enable_backlight,
+ .set_backlight = tilcdc_panel_set_backlight,
+ .get_display_timing = tilcdc_panel_get_display_timing,
+};
+
+static const struct udevice_id tilcdc_panel_ids[] = {
+ {.compatible = "ti,tilcdc,panel"},
+ {}
+};
+
+U_BOOT_DRIVER(tilcdc_panel) = {
+ .name = "tilcdc_panel",
+ .id = UCLASS_PANEL,
+ .of_match = tilcdc_panel_ids,
+ .ops = &tilcdc_panel_ops,
+ .ofdata_to_platdata = tilcdc_panel_of_to_plat,
+ .probe = tilcdc_panel_probe,
+ .remove = tilcdc_panel_remove,
+ .priv_auto = sizeof(struct tilcdc_panel_priv),
+};
diff --git a/drivers/video/ti/tilcdc-panel.h b/drivers/video/ti/tilcdc-panel.h
new file mode 100644
index 0000000000..6bcfbf8a8b
--- /dev/null
+++ b/drivers/video/ti/tilcdc-panel.h
@@ -0,0 +1,14 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Copyright (C) 2020 Dario Binacchi <dariobin@libero.it>
+ */
+
+#ifndef _TILCDC_PANEL_H
+#define _TILCDC_PANEL_H
+
+#include "tilcdc.h"
+
+int tilcdc_panel_get_display_info(struct udevice *dev,
+ struct tilcdc_panel_info *info);
+
+#endif /* _TILCDC_PANEL_H */
diff --git a/drivers/video/ti/tilcdc.c b/drivers/video/ti/tilcdc.c
new file mode 100644
index 0000000000..d13cc11801
--- /dev/null
+++ b/drivers/video/ti/tilcdc.c
@@ -0,0 +1,425 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2020 Dario Binacchi <dariobin@libero.it>
+ */
+
+#include <common.h>
+#include <clk.h>
+#include <dm.h>
+#include <dm/device_compat.h>
+#include <lcd.h>
+#include <log.h>
+#include <panel.h>
+#include <video.h>
+#include <asm/io.h>
+#include <asm/utils.h>
+#include "tilcdc.h"
+#include "tilcdc-panel.h"
+
+#define LCDC_FMAX 200000000
+
+/* LCD Control Register */
+#define LCDC_CTRL_CLK_DIVISOR_MASK GENMASK(15, 8)
+#define LCDC_CTRL_RASTER_MODE BIT(0)
+#define LCDC_CTRL_CLK_DIVISOR(x) (((x) & GENMASK(7, 0)) << 8)
+/* LCD Clock Enable Register */
+#define LCDC_CLKC_ENABLE_CORECLKEN BIT(0)
+#define LCDC_CLKC_ENABLE_LIDDCLKEN BIT(1)
+#define LCDC_CLKC_ENABLE_DMACLKEN BIT(2)
+/* LCD DMA Control Register */
+#define LCDC_DMA_CTRL_BURST_SIZE(x) (((x) & GENMASK(2, 0)) << 4)
+#define LCDC_DMA_CTRL_BURST_1 0x0
+#define LCDC_DMA_CTRL_BURST_2 0x1
+#define LCDC_DMA_CTRL_BURST_4 0x2
+#define LCDC_DMA_CTRL_BURST_8 0x3
+#define LCDC_DMA_CTRL_BURST_16 0x4
+#define LCDC_DMA_CTRL_FIFO_TH(x) (((x) & GENMASK(2, 0)) << 8)
+/* LCD Timing_0 Register */
+#define LCDC_RASTER_TIMING_0_HORMSB(x) ((((x) - 1) & BIT(10)) >> 7)
+#define LCDC_RASTER_TIMING_0_HORLSB(x) (((((x) >> 4) - 1) & GENMASK(5, 0)) << 4)
+#define LCDC_RASTER_TIMING_0_HSWLSB(x) ((((x) - 1) & GENMASK(5, 0)) << 10)
+#define LCDC_RASTER_TIMING_0_HFPLSB(x) ((((x) - 1) & GENMASK(7, 0)) << 16)
+#define LCDC_RASTER_TIMING_0_HBPLSB(x) ((((x) - 1) & GENMASK(7, 0)) << 24)
+/* LCD Timing_1 Register */
+#define LCDC_RASTER_TIMING_1_VERLSB(x) (((x) - 1) & GENMASK(9, 0))
+#define LCDC_RASTER_TIMING_1_VSW(x) ((((x) - 1) & GENMASK(5, 0)) << 10)
+#define LCDC_RASTER_TIMING_1_VFP(x) (((x) & GENMASK(7, 0)) << 16)
+#define LCDC_RASTER_TIMING_1_VBP(x) (((x) & GENMASK(7, 0)) << 24)
+/* LCD Timing_2 Register */
+#define LCDC_RASTER_TIMING_2_HFPMSB(x) ((((x) - 1) & GENMASK(9, 8)) >> 8)
+#define LCDC_RASTER_TIMING_2_HBPMSB(x) ((((x) - 1) & GENMASK(9, 8)) >> 4)
+#define LCDC_RASTER_TIMING_2_ACB(x) (((x) & GENMASK(7, 0)) << 8)
+#define LCDC_RASTER_TIMING_2_ACBI(x) (((x) & GENMASK(3, 0)) << 16)
+#define LCDC_RASTER_TIMING_2_VSYNC_INVERT BIT(20)
+#define LCDC_RASTER_TIMING_2_HSYNC_INVERT BIT(21)
+#define LCDC_RASTER_TIMING_2_PXCLK_INVERT BIT(22)
+#define LCDC_RASTER_TIMING_2_DE_INVERT BIT(23)
+#define LCDC_RASTER_TIMING_2_HSVS_RISEFALL BIT(24)
+#define LCDC_RASTER_TIMING_2_HSVS_CONTROL BIT(25)
+#define LCDC_RASTER_TIMING_2_VERMSB(x) ((((x) - 1) & BIT(10)) << 16)
+#define LCDC_RASTER_TIMING_2_HSWMSB(x) ((((x) - 1) & GENMASK(9, 6)) << 21)
+/* LCD Raster Ctrl Register */
+#define LCDC_RASTER_CTRL_ENABLE BIT(0)
+#define LCDC_RASTER_CTRL_TFT_MODE BIT(7)
+#define LCDC_RASTER_CTRL_DATA_ORDER BIT(8)
+#define LCDC_RASTER_CTRL_REQDLY(x) (((x) & GENMASK(7, 0)) << 12)
+#define LCDC_RASTER_CTRL_PALMODE_RAWDATA (0x02 << 20)
+#define LCDC_RASTER_CTRL_TFT_ALT_ENABLE BIT(23)
+#define LCDC_RASTER_CTRL_TFT_24BPP_MODE BIT(25)
+#define LCDC_RASTER_CTRL_TFT_24BPP_UNPACK BIT(26)
+
+enum {
+ LCDC_MAX_WIDTH = 2048,
+ LCDC_MAX_HEIGHT = 2048,
+ LCDC_MAX_LOG2_BPP = VIDEO_BPP32,
+};
+
+struct tilcdc_regs {
+ u32 pid;
+ u32 ctrl;
+ u32 gap0;
+ u32 lidd_ctrl;
+ u32 lidd_cs0_conf;
+ u32 lidd_cs0_addr;
+ u32 lidd_cs0_data;
+ u32 lidd_cs1_conf;
+ u32 lidd_cs1_addr;
+ u32 lidd_cs1_data;
+ u32 raster_ctrl;
+ u32 raster_timing0;
+ u32 raster_timing1;
+ u32 raster_timing2;
+ u32 raster_subpanel;
+ u32 raster_subpanel2;
+ u32 lcddma_ctrl;
+ u32 lcddma_fb0_base;
+ u32 lcddma_fb0_ceiling;
+ u32 lcddma_fb1_base;
+ u32 lcddma_fb1_ceiling;
+ u32 sysconfig;
+ u32 irqstatus_raw;
+ u32 irqstatus;
+ u32 irqenable_set;
+ u32 irqenable_clear;
+ u32 gap1;
+ u32 clkc_enable;
+ u32 clkc_reset;
+};
+
+struct tilcdc_priv {
+ struct tilcdc_regs *regs;
+ struct clk gclk;
+ struct clk dpll_m2_clk;
+};
+
+DECLARE_GLOBAL_DATA_PTR;
+
+static ulong tilcdc_set_pixel_clk_rate(struct udevice *dev, ulong rate)
+{
+ struct tilcdc_priv *priv = dev_get_priv(dev);
+ struct tilcdc_regs *regs = priv->regs;
+ ulong mult_rate, mult_round_rate, best_err, err;
+ u32 v;
+ int div, i;
+
+ best_err = rate;
+ div = 0;
+ for (i = 2; i <= 255; i++) {
+ mult_rate = rate * i;
+ mult_round_rate = clk_round_rate(&priv->gclk, mult_rate);
+ if (IS_ERR_VALUE(mult_round_rate))
+ return mult_round_rate;
+
+ err = mult_rate - mult_round_rate;
+ if (err < best_err) {
+ best_err = err;
+ div = i;
+ if (err == 0)
+ break;
+ }
+ }
+
+ if (div == 0) {
+ dev_err(dev, "failed to find a divisor\n");
+ return -EFAULT;
+ }
+
+ mult_rate = clk_set_rate(&priv->gclk, rate * div);
+ v = readl(&regs->ctrl) & ~LCDC_CTRL_CLK_DIVISOR_MASK;
+ v |= LCDC_CTRL_CLK_DIVISOR(div);
+ writel(v, &regs->ctrl);
+ rate = mult_rate / div;
+ dev_dbg(dev, "rate=%ld, div=%d, err=%ld\n", rate, div, err);
+ return rate;
+}
+
+static int tilcdc_remove(struct udevice *dev)
+{
+ struct video_uc_platdata *uc_plat = dev_get_uclass_platdata(dev);
+ struct tilcdc_priv *priv = dev_get_priv(dev);
+
+ uc_plat->base -= 0x20;
+ uc_plat->size += 0x20;
+ clk_release_all(&priv->gclk, 1);
+ clk_release_all(&priv->dpll_m2_clk, 1);
+ return 0;
+}
+
+static int tilcdc_probe(struct udevice *dev)
+{
+ struct video_uc_platdata *uc_plat = dev_get_uclass_platdata(dev);
+ struct video_priv *uc_priv = dev_get_uclass_priv(dev);
+ struct tilcdc_priv *priv = dev_get_priv(dev);
+ struct tilcdc_regs *regs = priv->regs;
+ struct udevice *panel, *clk_dev;
+ struct tilcdc_panel_info info;
+ struct display_timing timing;
+ ulong rate;
+ u32 reg;
+ int err;
+
+ /* Before relocation we don't need to do anything */
+ if (!(gd->flags & GD_FLG_RELOC))
+ return 0;
+
+ err = uclass_get_device(UCLASS_PANEL, 0, &panel);
+ if (err) {
+ dev_err(dev, "failed to get panel\n");
+ return err;
+ }
+
+ err = panel_get_display_timing(panel, &timing);
+ if (err) {
+ dev_err(dev, "failed to get display timing\n");
+ return err;
+ }
+
+ if (timing.pixelclock.typ > (LCDC_FMAX / 2)) {
+ dev_err(dev, "invalid display clock-frequency: %d Hz\n",
+ timing.pixelclock.typ);
+ return -EINVAL;
+ }
+
+ if (timing.hactive.typ > LCDC_MAX_WIDTH)
+ timing.hactive.typ = LCDC_MAX_WIDTH;
+
+ if (timing.vactive.typ > LCDC_MAX_HEIGHT)
+ timing.vactive.typ = LCDC_MAX_HEIGHT;
+
+ err = tilcdc_panel_get_display_info(panel, &info);
+ if (err) {
+ dev_err(dev, "failed to get panel info\n");
+ return err;
+ }
+
+ switch (info.bpp) {
+ case 16:
+ case 24:
+ case 32:
+ break;
+ default:
+ dev_err(dev, "invalid seting, bpp: %d\n", info.bpp);
+ return -EINVAL;
+ }
+
+ switch (info.dma_burst_sz) {
+ case 1:
+ case 2:
+ case 4:
+ case 8:
+ case 16:
+ break;
+ default:
+ dev_err(dev, "invalid setting, dma-burst-sz: %d\n",
+ info.dma_burst_sz);
+ return -EINVAL;
+ }
+
+ err = uclass_get_device_by_name(UCLASS_CLK, "lcd_gclk@534", &clk_dev);
+ if (err) {
+ dev_err(dev, "failed to get lcd_gclk device\n");
+ return err;
+ }
+
+ err = clk_request(clk_dev, &priv->gclk);
+ if (err) {
+ dev_err(dev, "failed to get %s clock\n", clk_dev->name);
+ return err;
+ }
+
+ rate = tilcdc_set_pixel_clk_rate(dev, timing.pixelclock.typ);
+ if (IS_ERR_VALUE(rate)) {
+ dev_err(dev, "failed to set pixel clock rate\n");
+ return rate;
+ }
+
+ err = uclass_get_device_by_name(UCLASS_CLK, "dpll_disp_m2_ck@4a4",
+ &clk_dev);
+ if (err) {
+ dev_err(dev, "failed to get dpll_disp_m2 clock device\n");
+ return err;
+ }
+
+ err = clk_request(clk_dev, &priv->dpll_m2_clk);
+ if (err) {
+ dev_err(dev, "failed to get %s clock\n", clk_dev->name);
+ return err;
+ }
+
+ err = clk_set_parent(&priv->gclk, &priv->dpll_m2_clk);
+ if (err) {
+ dev_err(dev, "failed to set %s clock as %s's parent\n",
+ priv->dpll_m2_clk.dev->name, priv->gclk.dev->name);
+ return err;
+ }
+
+ /* palette default entry */
+ memset((void *)uc_plat->base, 0, 0x20);
+ *(unsigned int *)uc_plat->base = 0x4000;
+ /* point fb behind palette */
+ uc_plat->base += 0x20;
+ uc_plat->size -= 0x20;
+
+ writel(LCDC_CLKC_ENABLE_CORECLKEN | LCDC_CLKC_ENABLE_LIDDCLKEN |
+ LCDC_CLKC_ENABLE_DMACLKEN, &regs->clkc_enable);
+ writel(0, &regs->raster_ctrl);
+
+ reg = readl(&regs->ctrl) & LCDC_CTRL_CLK_DIVISOR_MASK;
+ reg |= LCDC_CTRL_RASTER_MODE;
+ writel(reg, &regs->ctrl);
+
+ reg = (timing.hactive.typ * timing.vactive.typ * info.bpp) >> 3;
+ reg += uc_plat->base;
+ writel(uc_plat->base, &regs->lcddma_fb0_base);
+ writel(reg, &regs->lcddma_fb0_ceiling);
+ writel(uc_plat->base, &regs->lcddma_fb1_base);
+ writel(reg, &regs->lcddma_fb1_ceiling);
+
+ reg = LCDC_DMA_CTRL_FIFO_TH(info.fifo_th);
+ switch (info.dma_burst_sz) {
+ case 1:
+ reg |= LCDC_DMA_CTRL_BURST_SIZE(LCDC_DMA_CTRL_BURST_1);
+ break;
+ case 2:
+ reg |= LCDC_DMA_CTRL_BURST_SIZE(LCDC_DMA_CTRL_BURST_2);
+ break;
+ case 4:
+ reg |= LCDC_DMA_CTRL_BURST_SIZE(LCDC_DMA_CTRL_BURST_4);
+ break;
+ case 8:
+ reg |= LCDC_DMA_CTRL_BURST_SIZE(LCDC_DMA_CTRL_BURST_8);
+ break;
+ case 16:
+ reg |= LCDC_DMA_CTRL_BURST_SIZE(LCDC_DMA_CTRL_BURST_16);
+ break;
+ }
+
+ writel(reg, &regs->lcddma_ctrl);
+
+ writel(LCDC_RASTER_TIMING_0_HORLSB(timing.hactive.typ) |
+ LCDC_RASTER_TIMING_0_HORMSB(timing.hactive.typ) |
+ LCDC_RASTER_TIMING_0_HFPLSB(timing.hfront_porch.typ) |
+ LCDC_RASTER_TIMING_0_HBPLSB(timing.hback_porch.typ) |
+ LCDC_RASTER_TIMING_0_HSWLSB(timing.hsync_len.typ),
+ &regs->raster_timing0);
+
+ writel(LCDC_RASTER_TIMING_1_VBP(timing.vback_porch.typ) |
+ LCDC_RASTER_TIMING_1_VFP(timing.vfront_porch.typ) |
+ LCDC_RASTER_TIMING_1_VSW(timing.vsync_len.typ) |
+ LCDC_RASTER_TIMING_1_VERLSB(timing.vactive.typ),
+ &regs->raster_timing1);
+
+ reg = LCDC_RASTER_TIMING_2_ACB(info.ac_bias) |
+ LCDC_RASTER_TIMING_2_ACBI(info.ac_bias_intrpt) |
+ LCDC_RASTER_TIMING_2_HSWMSB(timing.hsync_len.typ) |
+ LCDC_RASTER_TIMING_2_VERMSB(timing.vactive.typ) |
+ LCDC_RASTER_TIMING_2_HBPMSB(timing.hback_porch.typ) |
+ LCDC_RASTER_TIMING_2_HFPMSB(timing.hfront_porch.typ);
+
+ if (timing.flags & DISPLAY_FLAGS_VSYNC_LOW)
+ reg |= LCDC_RASTER_TIMING_2_VSYNC_INVERT;
+
+ if (timing.flags & DISPLAY_FLAGS_HSYNC_LOW)
+ reg |= LCDC_RASTER_TIMING_2_HSYNC_INVERT;
+
+ if (info.invert_pxl_clk)
+ reg |= LCDC_RASTER_TIMING_2_PXCLK_INVERT;
+
+ if (info.sync_edge)
+ reg |= LCDC_RASTER_TIMING_2_HSVS_RISEFALL;
+
+ if (info.sync_ctrl)
+ reg |= LCDC_RASTER_TIMING_2_HSVS_CONTROL;
+
+ writel(reg, &regs->raster_timing2);
+
+ reg = LCDC_RASTER_CTRL_PALMODE_RAWDATA | LCDC_RASTER_CTRL_TFT_MODE |
+ LCDC_RASTER_CTRL_ENABLE | LCDC_RASTER_CTRL_REQDLY(info.fdd);
+
+ if (info.tft_alt_mode)
+ reg |= LCDC_RASTER_CTRL_TFT_ALT_ENABLE;
+
+ if (info.bpp == 24)
+ reg |= LCDC_RASTER_CTRL_TFT_24BPP_MODE;
+ else if (info.bpp == 32)
+ reg |= LCDC_RASTER_CTRL_TFT_24BPP_MODE |
+ LCDC_RASTER_CTRL_TFT_24BPP_UNPACK;
+
+ if (info.raster_order)
+ reg |= LCDC_RASTER_CTRL_DATA_ORDER;
+
+ writel(reg, &regs->raster_ctrl);
+
+ uc_priv->xsize = timing.hactive.typ;
+ uc_priv->ysize = timing.vactive.typ;
+ uc_priv->bpix = log_2_n_round_up(info.bpp);
+
+ err = panel_enable_backlight(panel);
+ if (err) {
+ dev_err(dev, "failed to enable panel backlight\n");
+ return err;
+ }
+
+ return 0;
+}
+
+static int tilcdc_of_to_plat(struct udevice *dev)
+{
+ struct tilcdc_priv *priv = dev_get_priv(dev);
+
+ priv->regs = (struct tilcdc_regs *)dev_read_addr(dev);
+ if ((fdt_addr_t)priv->regs == FDT_ADDR_T_NONE) {
+ dev_err(dev, "failed to get base address\n");
+ return -EINVAL;
+ }
+
+ dev_dbg(dev, "LCD: base address=0x%x\n", (unsigned int)priv->regs);
+ return 0;
+}
+
+static int tilcdc_bind(struct udevice *dev)
+{
+ struct video_uc_platdata *uc_plat = dev_get_uclass_platdata(dev);
+
+ uc_plat->size = ((LCDC_MAX_WIDTH * LCDC_MAX_HEIGHT *
+ (1 << LCDC_MAX_LOG2_BPP)) >> 3) + 0x20;
+
+ dev_dbg(dev, "frame buffer size 0x%x\n", uc_plat->size);
+ return 0;
+}
+
+static const struct udevice_id tilcdc_ids[] = {
+ {.compatible = "ti,am33xx-tilcdc"},
+ {}
+};
+
+U_BOOT_DRIVER(tilcdc) = {
+ .name = "tilcdc",
+ .id = UCLASS_VIDEO,
+ .of_match = tilcdc_ids,
+ .bind = tilcdc_bind,
+ .ofdata_to_platdata = tilcdc_of_to_plat,
+ .probe = tilcdc_probe,
+ .remove = tilcdc_remove,
+ .priv_auto = sizeof(struct tilcdc_priv)
+};
diff --git a/drivers/video/ti/tilcdc.h b/drivers/video/ti/tilcdc.h
new file mode 100644
index 0000000000..2645921df6
--- /dev/null
+++ b/drivers/video/ti/tilcdc.h
@@ -0,0 +1,38 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Copyright (C) 2020 Dario Binacchi <dariobin@libero.it>
+ */
+
+#ifndef _TILCDC_H
+#define _TILCDC_H
+
+/**
+ * tilcdc_panel_info: Panel parameters
+ *
+ * @ac_bias: AC Bias Pin Frequency
+ * @ac_bias_intrpt: AC Bias Pin Transitions per Interrupt
+ * @dma_burst_sz: DMA burst size
+ * @bpp: Bits per pixel
+ * @fdd: FIFO DMA Request Delay
+ * @tft_alt_mode: TFT Alternative Signal Mapping (Only for active)
+ * @invert_pxl_clk: Invert pixel clock
+ * @sync_edge: Horizontal and Vertical Sync Edge: 0=rising 1=falling
+ * @sync_ctrl: Horizontal and Vertical Sync: Control: 0=ignore
+ * @raster_order: Raster Data Order Select: 1=Most-to-least 0=Least-to-most
+ * @fifo_th: DMA FIFO threshold
+ */
+struct tilcdc_panel_info {
+ u32 ac_bias;
+ u32 ac_bias_intrpt;
+ u32 dma_burst_sz;
+ u32 bpp;
+ u32 fdd;
+ bool tft_alt_mode;
+ bool invert_pxl_clk;
+ u32 sync_edge;
+ u32 sync_ctrl;
+ u32 raster_order;
+ u32 fifo_th;
+};
+
+#endif /* _TILCDC_H */