diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig
index 4dfbad7986..9e54929039 100644
--- a/drivers/clk/Kconfig
+++ b/drivers/clk/Kconfig
@@ -179,6 +179,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..2581fe0a19 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/
diff --git a/drivers/clk/ti/Kconfig b/drivers/clk/ti/Kconfig
new file mode 100644
index 0000000000..be4f26817f
--- /dev/null
+++ b/drivers/clk/ti/Kconfig
@@ -0,0 +1,10 @@
+# SPDX-License-Identifier: GPL-2.0+
+#
+# Copyright (C) 2020 Dario Binacchi <dariobin@libero.it>
+#
+
+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.
diff --git a/drivers/clk/ti/Makefile b/drivers/clk/ti/Makefile
new file mode 100644
index 0000000000..5faf68d30e
--- /dev/null
+++ b/drivers/clk/ti/Makefile
@@ -0,0 +1,6 @@
+# SPDX-License-Identifier: GPL-2.0+
+#
+# Copyright (C) 2020 Dario Binacchi <dariobin@libero.it>
+#
+
+obj-$(CONFIG_CLK_TI_MUX) += clk-mux.o
diff --git a/drivers/clk/ti/clk-mux.c b/drivers/clk/ti/clk-mux.c
new file mode 100644
index 0000000000..9720c84513
--- /dev/null
+++ b/drivers/clk/ti/clk-mux.c
@@ -0,0 +1,276 @@
+// 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>
+
+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 void clk_ti_mux_rmw(u32 val, u32 mask, fdt_addr_t reg)
+{
+	u32 v;
+
+	v = readl(reg);
+	v &= ~mask;
+	v |= val;
+	writel(v, reg);
+}
+
+static void clk_ti_mux_latch(fdt_addr_t reg, s8 shift)
+{
+	u32 latch;
+
+	if (shift < 0)
+		return;
+
+	latch = 1 << shift;
+
+	clk_ti_mux_rmw(latch, latch, reg);
+	clk_ti_mux_rmw(0, latch, reg);
+	readl(reg);		/* OCP barrier */
+}
+
+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_mux_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_ofdata_to_platdata(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_ofdata_to_platdata,
+	.probe = clk_ti_mux_probe,
+	.remove = clk_ti_mux_remove,
+	.priv_auto_alloc_size = sizeof(struct clk_ti_mux_priv),
+	.ops = &clk_ti_mux_ops,
+};
