diff --git a/doc/device-tree-bindings/clock/clock-bindings.txt b/doc/device-tree-bindings/clock/clock-bindings.txt
new file mode 100644
index 0000000000..8a55fdcf96
--- /dev/null
+++ b/doc/device-tree-bindings/clock/clock-bindings.txt
@@ -0,0 +1,186 @@
+This binding is a work-in-progress, and are based on some experimental
+work by benh[1].
+
+Sources of clock signal can be represented by any node in the device
+tree.  Those nodes are designated as clock providers.  Clock consumer
+nodes use a phandle and clock specifier pair to connect clock provider
+outputs to clock inputs.  Similar to the gpio specifiers, a clock
+specifier is an array of zero, one or more cells identifying the clock
+output on a device.  The length of a clock specifier is defined by the
+value of a #clock-cells property in the clock provider node.
+
+[1] http://patchwork.ozlabs.org/patch/31551/
+
+==Clock providers==
+
+Required properties:
+#clock-cells:	   Number of cells in a clock specifier; Typically 0 for nodes
+		   with a single clock output and 1 for nodes with multiple
+		   clock outputs.
+
+Optional properties:
+clock-output-names: Recommended to be a list of strings of clock output signal
+		    names indexed by the first cell in the clock specifier.
+		    However, the meaning of clock-output-names is domain
+		    specific to the clock provider, and is only provided to
+		    encourage using the same meaning for the majority of clock
+		    providers.  This format may not work for clock providers
+		    using a complex clock specifier format.  In those cases it
+		    is recommended to omit this property and create a binding
+		    specific names property.
+
+		    Clock consumer nodes must never directly reference
+		    the provider's clock-output-names property.
+
+For example:
+
+    oscillator {
+        #clock-cells = <1>;
+        clock-output-names = "ckil", "ckih";
+    };
+
+- this node defines a device with two clock outputs, the first named
+  "ckil" and the second named "ckih".  Consumer nodes always reference
+  clocks by index. The names should reflect the clock output signal
+  names for the device.
+
+clock-indices:	   If the identifying number for the clocks in the node
+		   is not linear from zero, then this allows the mapping of
+		   identifiers into the clock-output-names array.
+
+For example, if we have two clocks <&oscillator 1> and <&oscillator 3>:
+
+	oscillator {
+		compatible = "myclocktype";
+		#clock-cells = <1>;
+		clock-indices = <1>, <3>;
+		clock-output-names = "clka", "clkb";
+	}
+
+	This ensures we do not have any empty strings in clock-output-names
+
+
+==Clock consumers==
+
+Required properties:
+clocks:		List of phandle and clock specifier pairs, one pair
+		for each clock input to the device.  Note: if the
+		clock provider specifies '0' for #clock-cells, then
+		only the phandle portion of the pair will appear.
+
+Optional properties:
+clock-names:	List of clock input name strings sorted in the same
+		order as the clocks property.  Consumers drivers
+		will use clock-names to match clock input names
+		with clocks specifiers.
+clock-ranges:	Empty property indicating that child nodes can inherit named
+		clocks from this node. Useful for bus nodes to provide a
+		clock to their children.
+
+For example:
+
+    device {
+        clocks = <&osc 1>, <&ref 0>;
+        clock-names = "baud", "register";
+    };
+
+
+This represents a device with two clock inputs, named "baud" and "register".
+The baud clock is connected to output 1 of the &osc device, and the register
+clock is connected to output 0 of the &ref.
+
+==Example==
+
+    /* external oscillator */
+    osc: oscillator {
+        compatible = "fixed-clock";
+        #clock-cells = <0>;
+        clock-frequency  = <32678>;
+        clock-output-names = "osc";
+    };
+
+    /* phase-locked-loop device, generates a higher frequency clock
+     * from the external oscillator reference */
+    pll: pll@4c000 {
+        compatible = "vendor,some-pll-interface"
+        #clock-cells = <1>;
+        clocks = <&osc 0>;
+        clock-names = "ref";
+        reg = <0x4c000 0x1000>;
+        clock-output-names = "pll", "pll-switched";
+    };
+
+    /* UART, using the low frequency oscillator for the baud clock,
+     * and the high frequency switched PLL output for register
+     * clocking */
+    uart@a000 {
+        compatible = "fsl,imx-uart";
+        reg = <0xa000 0x1000>;
+        interrupts = <33>;
+        clocks = <&osc 0>, <&pll 1>;
+        clock-names = "baud", "register";
+    };
+
+This DT fragment defines three devices: an external oscillator to provide a
+low-frequency reference clock, a PLL device to generate a higher frequency
+clock signal, and a UART.
+
+* The oscillator is fixed-frequency, and provides one clock output, named "osc".
+* The PLL is both a clock provider and a clock consumer. It uses the clock
+  signal generated by the external oscillator, and provides two output signals
+  ("pll" and "pll-switched").
+* The UART has its baud clock connected the external oscillator and its
+  register clock connected to the PLL clock (the "pll-switched" signal)
+
+==Assigned clock parents and rates==
+
+Some platforms may require initial configuration of default parent clocks
+and clock frequencies. Such a configuration can be specified in a device tree
+node through assigned-clocks, assigned-clock-parents and assigned-clock-rates
+properties. The assigned-clock-parents property should contain a list of parent
+clocks in the form of a phandle and clock specifier pair and the
+assigned-clock-rates property should contain a list of frequencies in Hz. Both
+these properties should correspond to the clocks listed in the assigned-clocks
+property.
+
+To skip setting parent or rate of a clock its corresponding entry should be
+set to 0, or can be omitted if it is not followed by any non-zero entry.
+
+    uart@a000 {
+        compatible = "fsl,imx-uart";
+        reg = <0xa000 0x1000>;
+        ...
+        clocks = <&osc 0>, <&pll 1>;
+        clock-names = "baud", "register";
+
+        assigned-clocks = <&clkcon 0>, <&pll 2>;
+        assigned-clock-parents = <&pll 2>;
+        assigned-clock-rates = <0>, <460800>;
+    };
+
+In this example the <&pll 2> clock is set as parent of clock <&clkcon 0> and
+the <&pll 2> clock is assigned a frequency value of 460800 Hz.
+
+Configuring a clock's parent and rate through the device node that consumes
+the clock can be done only for clocks that have a single user. Specifying
+conflicting parent or rate configuration in multiple consumer nodes for
+a shared clock is forbidden.
+
+Configuration of common clocks, which affect multiple consumer devices can
+be similarly specified in the clock provider node.
+
+==Protected clocks==
+
+Some platforms or firmwares may not fully expose all the clocks to the OS, such
+as in situations where those clks are used by drivers running in ARM secure
+execution levels. Such a configuration can be specified in device tree with the
+protected-clocks property in the form of a clock specifier list. This property should
+only be specified in the node that is providing the clocks being protected:
+
+   clock-controller@a000f000 {
+        compatible = "vendor,clk95;
+        reg = <0xa000f000 0x1000>
+        #clocks-cells = <1>;
+        ...
+        protected-clocks = <UART3_CLK>, <SPI5_CLK>;
+   };
diff --git a/doc/device-tree-bindings/clock/ti,mux.txt b/doc/device-tree-bindings/clock/ti,mux.txt
new file mode 100644
index 0000000000..4c279288cb
--- /dev/null
+++ b/doc/device-tree-bindings/clock/ti,mux.txt
@@ -0,0 +1,79 @@
+Binding for TI mux clock.
+
+Binding status: Unstable - ABI compatibility may be broken in the future
+
+This binding uses the common clock binding[1].  It assumes a
+register-mapped multiplexer with multiple input clock signals or
+parents, one of which can be selected as output.  This clock does not
+gate or adjust the parent rate via a divider or multiplier.
+
+By default the "clocks" property lists the parents in the same order
+as they are programmed into the register.  E.g:
+
+	clocks = <&foo_clock>, <&bar_clock>, <&baz_clock>;
+
+results in programming the register as follows:
+
+register value		selected parent clock
+0			foo_clock
+1			bar_clock
+2			baz_clock
+
+Some clock controller IPs do not allow a value of zero to be programmed
+into the register, instead indexing begins at 1.  The optional property
+"index-starts-at-one" modified the scheme as follows:
+
+register value		selected clock parent
+1			foo_clock
+2			bar_clock
+3			baz_clock
+
+The binding must provide the register to control the mux. Optionally
+the number of bits to shift the control field in the register can be
+supplied. If the shift value is missing it is the same as supplying
+a zero shift.
+
+[1] doc/device-tree-bindings/clock/clock-bindings.txt
+
+Required properties:
+- compatible : shall be "ti,mux-clock" or "ti,composite-mux-clock".
+- #clock-cells : from common clock binding; shall be set to 0.
+- clocks : link phandles of parent clocks
+- reg : register offset for register controlling adjustable mux
+
+Optional properties:
+- ti,bit-shift : number of bits to shift the bit-mask, defaults to
+  0 if not present
+- ti,index-starts-at-one : valid input select programming starts at 1, not
+  zero
+- ti,set-rate-parent : clk_set_rate is propagated to parent clock,
+  not supported by the composite-mux-clock subtype
+- ti,latch-bit : latch the mux value to HW, only needed if the register
+  access requires this. As an example, dra7x DPLL_GMAC H14 muxing
+  implements such behavior.
+
+Examples:
+
+sys_clkin_ck: sys_clkin_ck@4a306110 {
+	#clock-cells = <0>;
+	compatible = "ti,mux-clock";
+	clocks = <&virt_12000000_ck>, <&virt_13000000_ck>, <&virt_16800000_ck>, <&virt_19200000_ck>, <&virt_26000000_ck>, <&virt_27000000_ck>, <&virt_38400000_ck>;
+	reg = <0x0110>;
+	ti,index-starts-at-one;
+};
+
+abe_dpll_bypass_clk_mux_ck: abe_dpll_bypass_clk_mux_ck@4a306108 {
+	#clock-cells = <0>;
+	compatible = "ti,mux-clock";
+	clocks = <&sys_clkin_ck>, <&sys_32k_ck>;
+	ti,bit-shift = <24>;
+	reg = <0x0108>;
+};
+
+mcbsp5_mux_fck: mcbsp5_mux_fck {
+	#clock-cells = <0>;
+	compatible = "ti,composite-mux-clock";
+	clocks = <&core_96m_fck>, <&mcbsp_clks>;
+	ti,bit-shift = <4>;
+	reg = <0x02d8>;
+};
diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig
index 6003e140b5..f070d2637f 100644
--- a/drivers/clk/Kconfig
+++ b/drivers/clk/Kconfig
@@ -98,6 +98,12 @@ config CLK_STM32F
 	  This clock driver adds support for RCC clock management
 	  for STM32F4 and STM32F7 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
diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile
index cda4b4b605..c0dfe4b599 100644
--- a/drivers/clk/Makefile
+++ b/drivers/clk/Makefile
@@ -46,6 +46,7 @@ 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_MUX) += clk-ti-mux.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-ti-mux.c b/drivers/clk/clk-ti-mux.c
new file mode 100644
index 0000000000..7e39dd3477
--- /dev/null
+++ b/drivers/clk/clk-ti-mux.c
@@ -0,0 +1,275 @@
+// 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 <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,
+};
