diff mbox

[v5,10/44] clk: davinci: New driver for davinci PSC clocks

Message ID 1515377863-20358-11-git-send-email-david@lechnology.com (mailing list archive)
State New, archived
Headers show

Commit Message

David Lechner Jan. 8, 2018, 2:17 a.m. UTC
This adds a new driver for mach-davinci PSC clocks. This is porting the
code from arch/arm/mach-davinci/psc.c to the common clock framework and
is converting it to use regmap to simplify the code. Additionally, it adds
device tree support for these clocks.

Note: although there are similar clocks for TI Keystone we are not able
to share the code for a few reasons. The keystone clocks are device tree
only and use legacy one-node-per-clock bindings. Also the keystone driver
makes the assumption that there is only one PSC per SoC and uses global
variables, but here we have two controllers per SoC.

Signed-off-by: David Lechner <david@lechnology.com>
---
 drivers/clk/davinci/Makefile |   2 +
 drivers/clk/davinci/psc.c    | 282 +++++++++++++++++++++++++++++++++++++++++++
 drivers/clk/davinci/psc.h    |  49 ++++++++
 3 files changed, 333 insertions(+)
 create mode 100644 drivers/clk/davinci/psc.c
 create mode 100644 drivers/clk/davinci/psc.h

Comments

Sekhar Nori Jan. 16, 2018, 11:03 a.m. UTC | #1
On Monday 08 January 2018 07:47 AM, David Lechner wrote:
> This adds a new driver for mach-davinci PSC clocks. This is porting the
> code from arch/arm/mach-davinci/psc.c to the common clock framework and
> is converting it to use regmap to simplify the code. Additionally, it adds
> device tree support for these clocks.
> 
> Note: although there are similar clocks for TI Keystone we are not able
> to share the code for a few reasons. The keystone clocks are device tree
> only and use legacy one-node-per-clock bindings. Also the keystone driver
> makes the assumption that there is only one PSC per SoC and uses global
> variables, but here we have two controllers per SoC.
> 
> Signed-off-by: David Lechner <david@lechnology.com>
> ---
>  drivers/clk/davinci/Makefile |   2 +
>  drivers/clk/davinci/psc.c    | 282 +++++++++++++++++++++++++++++++++++++++++++
>  drivers/clk/davinci/psc.h    |  49 ++++++++
>  3 files changed, 333 insertions(+)
>  create mode 100644 drivers/clk/davinci/psc.c
>  create mode 100644 drivers/clk/davinci/psc.h
> 
> diff --git a/drivers/clk/davinci/Makefile b/drivers/clk/davinci/Makefile
> index d471386..cd1bf2c 100644
> --- a/drivers/clk/davinci/Makefile
> +++ b/drivers/clk/davinci/Makefile
> @@ -8,4 +8,6 @@ obj-$(CONFIG_ARCH_DAVINCI_DM355)	+= pll-dm355.o
>  obj-$(CONFIG_ARCH_DAVINCI_DM365)	+= pll-dm365.o
>  obj-$(CONFIG_ARCH_DAVINCI_DM644x)	+= pll-dm644x.o
>  obj-$(CONFIG_ARCH_DAVINCI_DM646x)	+= pll-dm646x.o
> +
> +obj-y += psc.o
>  endif
> diff --git a/drivers/clk/davinci/psc.c b/drivers/clk/davinci/psc.c
> new file mode 100644
> index 0000000..a8b5f57
> --- /dev/null
> +++ b/drivers/clk/davinci/psc.c
> @@ -0,0 +1,282 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Clock driver for TI Davinci PSC controllers
> + *
> + * Copyright (C) 2017 David Lechner <david@lechnology.com>

2018

> + *
> + * Based on: drivers/clk/keystone/gate.c
> + * Copyright (C) 2013 Texas Instruments.
> + *	Murali Karicheri <m-karicheri2@ti.com>
> + *	Santosh Shilimkar <santosh.shilimkar@ti.com>
> + *
> + * And: arch/arm/mach-davinci/psc.c
> + * Copyright (C) 2006 Texas Instruments.
> + */
> +
> +#include <linux/clk-provider.h>
> +#include <linux/clk/davinci.h>
> +#include <linux/clkdev.h>
> +#include <linux/err.h>
> +#include <linux/of_address.h>
> +#include <linux/of.h>
> +#include <linux/regmap.h>
> +#include <linux/slab.h>
> +#include <linux/types.h>
> +
> +#include "psc.h"
> +
> +/* PSC register offsets */
> +#define EPCPR			0x070
> +#define PTCMD			0x120
> +#define PTSTAT			0x128
> +#define PDSTAT(n)		(0x200 + 4 * (n))
> +#define PDCTL(n)		(0x300 + 4 * (n))
> +#define MDSTAT(n)		(0x800 + 4 * (n))
> +#define MDCTL(n)		(0xa00 + 4 * (n))
> +
> +/* PSC module states */
> +enum davinci_psc_state {
> +	PSC_STATE_SWRSTDISABLE	= 0,
> +	PSC_STATE_SYNCRST	= 1,
> +	PSC_STATE_DISABLE	= 2,
> +	PSC_STATE_ENABLE	= 3,
> +};
> +
> +#define MDSTAT_STATE_MASK	0x3f> +#define MDSTAT_MCKOUT		BIT(12)
> +#define PDSTAT_STATE_MASK	0x1f

GENMASK() for masks.

> +#define MDCTL_FORCE		BIT(31)
> +#define MDCTL_LRESET		BIT(8)
> +#define PDCTL_EPCGOOD		BIT(8)
> +#define PDCTL_NEXT		BIT(0)
> +
> +/**
> + * struct davinci_psc_clk - PSC clock structure
> + * @hw: clk_hw for the psc
> + * @regmap: PSC MMIO region
> + * @lpsc: Local PSC number (module id)
> + * @pd: Power domain
> + * @flags: LPSC_* quirk flags
> + */
> +struct davinci_psc_clk {
> +	struct clk_hw hw;
> +	struct regmap *regmap;
> +	u32 lpsc;
> +	u32 pd;
> +	u32 flags;
> +};
> +
> +#define to_davinci_psc_clk(_hw) container_of(_hw, struct davinci_psc_clk, hw)
> +
> +static void psc_config(struct davinci_psc_clk *psc,
> +		       enum davinci_psc_state next_state)
> +{
> +	u32 epcpr, pdstat, mdstat, mdctl, ptstat;
> +
> +	mdctl = next_state;
> +	if (psc->flags & LPSC_FORCE)
> +		mdctl |= MDCTL_FORCE;
> +	regmap_write_bits(psc->regmap, MDCTL(psc->lpsc), MDSTAT_STATE_MASK,
> +			  mdctl);

Wont this ignore the MDCTL_FORCE bit since MDSTAT_STATE_MASK does not
cover that?

> +
> +	regmap_read(psc->regmap, PDSTAT(psc->pd), &pdstat);
> +	if ((pdstat & PDSTAT_STATE_MASK) == 0) {
> +		regmap_write_bits(psc->regmap, PDSTAT(psc->pd),
> +				  PDSTAT_STATE_MASK, PDCTL_NEXT);

Shouldn't this be a write to PDCTL register?

> +
> +		regmap_write(psc->regmap, PTCMD, BIT(psc->pd));
> +
> +		regmap_read_poll_timeout(psc->regmap, EPCPR, epcpr,
> +					 epcpr & BIT(psc->pd), 0, 0);
> +
> +		regmap_write_bits(psc->regmap, PDCTL(psc->pd), PDCTL_EPCGOOD,
> +				  PDCTL_EPCGOOD);
> +	} else {
> +		regmap_write(psc->regmap, PTCMD, BIT(psc->pd));
> +	}
> +
> +	regmap_read_poll_timeout(psc->regmap, PTSTAT, ptstat,
> +				 !(ptstat & BIT(psc->pd)), 0, 0);
> +
> +	regmap_read_poll_timeout(psc->regmap, MDSTAT(psc->lpsc), mdstat,
> +				 (mdstat & MDSTAT_STATE_MASK) == next_state,
> +				 0, 0);
> +}
> +

[...]

> +
> +/**
> + * davinci_psc_clk_register - register psc clock
> + * @dev: device that is registering this clock

No dev parameter below.

> + * @name: name of this clock
> + * @parent_name: name of clock's parent
> + * @regmap: PSC MMIO region
> + * @lpsc: local PSC number
> + * @pd: power domain
> + * @flags: LPSC_* flags
> + */
> +static struct clk *davinci_psc_clk_register(const char *name,
> +					    const char *parent_name,
> +					    struct regmap *regmap,
> +					    u32 lpsc, u32 pd, u32 flags)
> +{
> +	struct clk_init_data init;
> +	struct davinci_psc_clk *psc;
> +	struct clk *clk;
> +
> +	psc = kzalloc(sizeof(*psc), GFP_KERNEL);
> +	if (!psc)
> +		return ERR_PTR(-ENOMEM);
> +
> +	init.name = name;
> +	init.ops = &davinci_psc_clk_ops;
> +	init.parent_names = (parent_name ? &parent_name : NULL);
> +	init.num_parents = (parent_name ? 1 : 0);
> +	init.flags = CLK_SET_RATE_PARENT;

Is this needed since PSC does not cause any rate change?

> +
> +	if (flags & LPSC_ALWAYS_ENABLED)
> +		init.flags |= CLK_IS_CRITICAL;
> +
> +	psc->regmap = regmap;
> +	psc->hw.init = &init;
> +	psc->lpsc = lpsc;
> +	psc->pd = pd;
> +	psc->flags = flags;
> +
> +	clk = clk_register(NULL, &psc->hw);
> +	if (IS_ERR(clk))
> +		kfree(psc);
> +
> +	return clk;
> +}
> +
> +/*
> + * FIXME: This needs to be converted to a reset controller. But, the reset
> + * framework is currently device tree only.

Yeah, I see that __reset_control_get() fails with -EINVAL if there is no
of_node.

> + */
> +
> +static int davinci_psc_clk_reset(struct davinci_psc_clk *psc, bool reset)
> +{
> +	u32 mdctl;
> +
> +	if (IS_ERR_OR_NULL(psc))
> +		return -EINVAL;
> +
> +	mdctl = reset ? 0 : MDCTL_LRESET;
> +	regmap_write_bits(psc->regmap, MDCTL(psc->lpsc), MDCTL_LRESET, mdctl);
> +
> +	return 0;
> +}
> +
> +int davinci_clk_reset_assert(struct clk *clk)
> +{
> +	struct davinci_psc_clk *psc = to_davinci_psc_clk(__clk_get_hw(clk));
> +
> +	return davinci_psc_clk_reset(psc, true);
> +}
> +EXPORT_SYMBOL(davinci_clk_reset_assert);
> +
> +int davinci_clk_reset_deassert(struct clk *clk)
> +{
> +	struct davinci_psc_clk *psc = to_davinci_psc_clk(__clk_get_hw(clk));
> +
> +	return davinci_psc_clk_reset(psc, false);
> +}
> +EXPORT_SYMBOL(davinci_clk_reset_deassert);
> +

[...]

> diff --git a/drivers/clk/davinci/psc.h b/drivers/clk/davinci/psc.h
> new file mode 100644
> index 0000000..6022f6e
> --- /dev/null
> +++ b/drivers/clk/davinci/psc.h
> @@ -0,0 +1,49 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Clock driver for TI Davinci PSC controllers
> + *
> + * Copyright (C) 2017 David Lechner <david@lechnology.com>
> + */
> +
> +#ifndef __CLK_DAVINCI_PSC_H__
> +#define __CLK_DAVINCI_PSC_H__
> +
> +#include <linux/types.h>
> +
> +/* PSC quirk flags */
> +#define LPSC_ALWAYS_ENABLED	BIT(1) /* never disable this clock */
> +#define LPSC_FORCE		BIT(2) /* requires MDCTL FORCE bit */
> +#define LPSC_LOCAL_RESET	BIT(3) /* acts as reset provider */
> +
> +struct clk_onecell_data;

Rather clk-provider.h should be included in this file?

Thanks,
Sekhar
David Lechner Jan. 16, 2018, 4:51 p.m. UTC | #2
On 01/16/2018 05:03 AM, Sekhar Nori wrote:
> On Monday 08 January 2018 07:47 AM, David Lechner wrote:
>> This adds a new driver for mach-davinci PSC clocks. This is porting the
>> code from arch/arm/mach-davinci/psc.c to the common clock framework and
>> is converting it to use regmap to simplify the code. Additionally, it adds
>> device tree support for these clocks.
>>
>> Note: although there are similar clocks for TI Keystone we are not able
>> to share the code for a few reasons. The keystone clocks are device tree
>> only and use legacy one-node-per-clock bindings. Also the keystone driver
>> makes the assumption that there is only one PSC per SoC and uses global
>> variables, but here we have two controllers per SoC.
>>
>> Signed-off-by: David Lechner <david@lechnology.com>
>> ---

>> +static void psc_config(struct davinci_psc_clk *psc,
>> +		       enum davinci_psc_state next_state)
>> +{
>> +	u32 epcpr, pdstat, mdstat, mdctl, ptstat;
>> +
>> +	mdctl = next_state;
>> +	if (psc->flags & LPSC_FORCE)
>> +		mdctl |= MDCTL_FORCE;
>> +	regmap_write_bits(psc->regmap, MDCTL(psc->lpsc), MDSTAT_STATE_MASK,
>> +			  mdctl);
> 
> Wont this ignore the MDCTL_FORCE bit since MDSTAT_STATE_MASK does not
> cover that?
> 
>> +
>> +	regmap_read(psc->regmap, PDSTAT(psc->pd), &pdstat);
>> +	if ((pdstat & PDSTAT_STATE_MASK) == 0) {
>> +		regmap_write_bits(psc->regmap, PDSTAT(psc->pd),
>> +				  PDSTAT_STATE_MASK, PDCTL_NEXT);
> 
> Shouldn't this be a write to PDCTL register?
> 

Looks like I have some mistakes here. Thank you.

...

>> +static struct clk *davinci_psc_clk_register(const char *name,
>> +					    const char *parent_name,
>> +					    struct regmap *regmap,
>> +					    u32 lpsc, u32 pd, u32 flags)
>> +{
>> +	struct clk_init_data init;
>> +	struct davinci_psc_clk *psc;
>> +	struct clk *clk;
>> +
>> +	psc = kzalloc(sizeof(*psc), GFP_KERNEL);
>> +	if (!psc)
>> +		return ERR_PTR(-ENOMEM);
>> +
>> +	init.name = name;
>> +	init.ops = &davinci_psc_clk_ops;
>> +	init.parent_names = (parent_name ? &parent_name : NULL);
>> +	init.num_parents = (parent_name ? 1 : 0);
>> +	init.flags = CLK_SET_RATE_PARENT;
> 
> Is this needed since PSC does not cause any rate change?

Yes, because one of the PSCs is the ARM clock and for cpufreq, we
need to propagate the rate change up the chain to SYSCLK6.
Sekhar Nori Jan. 17, 2018, 12:25 p.m. UTC | #3
On Tuesday 16 January 2018 10:21 PM, David Lechner wrote:

>>> +static struct clk *davinci_psc_clk_register(const char *name,
>>> +                        const char *parent_name,
>>> +                        struct regmap *regmap,
>>> +                        u32 lpsc, u32 pd, u32 flags)
>>> +{
>>> +    struct clk_init_data init;
>>> +    struct davinci_psc_clk *psc;
>>> +    struct clk *clk;
>>> +
>>> +    psc = kzalloc(sizeof(*psc), GFP_KERNEL);
>>> +    if (!psc)
>>> +        return ERR_PTR(-ENOMEM);
>>> +
>>> +    init.name = name;
>>> +    init.ops = &davinci_psc_clk_ops;
>>> +    init.parent_names = (parent_name ? &parent_name : NULL);
>>> +    init.num_parents = (parent_name ? 1 : 0);
>>> +    init.flags = CLK_SET_RATE_PARENT;
>>
>> Is this needed since PSC does not cause any rate change?
> 
> Yes, because one of the PSCs is the ARM clock and for cpufreq, we
> need to propagate the rate change up the chain to SYSCLK6.

Good point. But how about treating that as an exception with a new LPSC_
quirk flag?

Thanks,
Sekhar
David Lechner Jan. 17, 2018, 5:28 p.m. UTC | #4
On 01/17/2018 06:25 AM, Sekhar Nori wrote:
> On Tuesday 16 January 2018 10:21 PM, David Lechner wrote:
> 
>>>> +static struct clk *davinci_psc_clk_register(const char *name,
>>>> +                        const char *parent_name,
>>>> +                        struct regmap *regmap,
>>>> +                        u32 lpsc, u32 pd, u32 flags)
>>>> +{
>>>> +    struct clk_init_data init;
>>>> +    struct davinci_psc_clk *psc;
>>>> +    struct clk *clk;
>>>> +
>>>> +    psc = kzalloc(sizeof(*psc), GFP_KERNEL);
>>>> +    if (!psc)
>>>> +        return ERR_PTR(-ENOMEM);
>>>> +
>>>> +    init.name = name;
>>>> +    init.ops = &davinci_psc_clk_ops;
>>>> +    init.parent_names = (parent_name ? &parent_name : NULL);
>>>> +    init.num_parents = (parent_name ? 1 : 0);
>>>> +    init.flags = CLK_SET_RATE_PARENT;
>>>
>>> Is this needed since PSC does not cause any rate change?
>>
>> Yes, because one of the PSCs is the ARM clock and for cpufreq, we
>> need to propagate the rate change up the chain to SYSCLK6.
> 
> Good point. But how about treating that as an exception with a new LPSC_
> quirk flag?

Sure.
diff mbox

Patch

diff --git a/drivers/clk/davinci/Makefile b/drivers/clk/davinci/Makefile
index d471386..cd1bf2c 100644
--- a/drivers/clk/davinci/Makefile
+++ b/drivers/clk/davinci/Makefile
@@ -8,4 +8,6 @@  obj-$(CONFIG_ARCH_DAVINCI_DM355)	+= pll-dm355.o
 obj-$(CONFIG_ARCH_DAVINCI_DM365)	+= pll-dm365.o
 obj-$(CONFIG_ARCH_DAVINCI_DM644x)	+= pll-dm644x.o
 obj-$(CONFIG_ARCH_DAVINCI_DM646x)	+= pll-dm646x.o
+
+obj-y += psc.o
 endif
diff --git a/drivers/clk/davinci/psc.c b/drivers/clk/davinci/psc.c
new file mode 100644
index 0000000..a8b5f57
--- /dev/null
+++ b/drivers/clk/davinci/psc.c
@@ -0,0 +1,282 @@ 
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Clock driver for TI Davinci PSC controllers
+ *
+ * Copyright (C) 2017 David Lechner <david@lechnology.com>
+ *
+ * Based on: drivers/clk/keystone/gate.c
+ * Copyright (C) 2013 Texas Instruments.
+ *	Murali Karicheri <m-karicheri2@ti.com>
+ *	Santosh Shilimkar <santosh.shilimkar@ti.com>
+ *
+ * And: arch/arm/mach-davinci/psc.c
+ * Copyright (C) 2006 Texas Instruments.
+ */
+
+#include <linux/clk-provider.h>
+#include <linux/clk/davinci.h>
+#include <linux/clkdev.h>
+#include <linux/err.h>
+#include <linux/of_address.h>
+#include <linux/of.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+
+#include "psc.h"
+
+/* PSC register offsets */
+#define EPCPR			0x070
+#define PTCMD			0x120
+#define PTSTAT			0x128
+#define PDSTAT(n)		(0x200 + 4 * (n))
+#define PDCTL(n)		(0x300 + 4 * (n))
+#define MDSTAT(n)		(0x800 + 4 * (n))
+#define MDCTL(n)		(0xa00 + 4 * (n))
+
+/* PSC module states */
+enum davinci_psc_state {
+	PSC_STATE_SWRSTDISABLE	= 0,
+	PSC_STATE_SYNCRST	= 1,
+	PSC_STATE_DISABLE	= 2,
+	PSC_STATE_ENABLE	= 3,
+};
+
+#define MDSTAT_STATE_MASK	0x3f
+#define MDSTAT_MCKOUT		BIT(12)
+#define PDSTAT_STATE_MASK	0x1f
+#define MDCTL_FORCE		BIT(31)
+#define MDCTL_LRESET		BIT(8)
+#define PDCTL_EPCGOOD		BIT(8)
+#define PDCTL_NEXT		BIT(0)
+
+/**
+ * struct davinci_psc_clk - PSC clock structure
+ * @hw: clk_hw for the psc
+ * @regmap: PSC MMIO region
+ * @lpsc: Local PSC number (module id)
+ * @pd: Power domain
+ * @flags: LPSC_* quirk flags
+ */
+struct davinci_psc_clk {
+	struct clk_hw hw;
+	struct regmap *regmap;
+	u32 lpsc;
+	u32 pd;
+	u32 flags;
+};
+
+#define to_davinci_psc_clk(_hw) container_of(_hw, struct davinci_psc_clk, hw)
+
+static void psc_config(struct davinci_psc_clk *psc,
+		       enum davinci_psc_state next_state)
+{
+	u32 epcpr, pdstat, mdstat, mdctl, ptstat;
+
+	mdctl = next_state;
+	if (psc->flags & LPSC_FORCE)
+		mdctl |= MDCTL_FORCE;
+	regmap_write_bits(psc->regmap, MDCTL(psc->lpsc), MDSTAT_STATE_MASK,
+			  mdctl);
+
+	regmap_read(psc->regmap, PDSTAT(psc->pd), &pdstat);
+	if ((pdstat & PDSTAT_STATE_MASK) == 0) {
+		regmap_write_bits(psc->regmap, PDSTAT(psc->pd),
+				  PDSTAT_STATE_MASK, PDCTL_NEXT);
+
+		regmap_write(psc->regmap, PTCMD, BIT(psc->pd));
+
+		regmap_read_poll_timeout(psc->regmap, EPCPR, epcpr,
+					 epcpr & BIT(psc->pd), 0, 0);
+
+		regmap_write_bits(psc->regmap, PDCTL(psc->pd), PDCTL_EPCGOOD,
+				  PDCTL_EPCGOOD);
+	} else {
+		regmap_write(psc->regmap, PTCMD, BIT(psc->pd));
+	}
+
+	regmap_read_poll_timeout(psc->regmap, PTSTAT, ptstat,
+				 !(ptstat & BIT(psc->pd)), 0, 0);
+
+	regmap_read_poll_timeout(psc->regmap, MDSTAT(psc->lpsc), mdstat,
+				 (mdstat & MDSTAT_STATE_MASK) == next_state,
+				 0, 0);
+}
+
+static int davinci_psc_clk_enable(struct clk_hw *hw)
+{
+	struct davinci_psc_clk *psc = to_davinci_psc_clk(hw);
+
+	psc_config(psc, PSC_STATE_ENABLE);
+
+	return 0;
+}
+
+static void davinci_psc_clk_disable(struct clk_hw *hw)
+{
+	struct davinci_psc_clk *psc = to_davinci_psc_clk(hw);
+
+	psc_config(psc, PSC_STATE_DISABLE);
+}
+
+static int davinci_psc_clk_is_enabled(struct clk_hw *hw)
+{
+	struct davinci_psc_clk *psc = to_davinci_psc_clk(hw);
+	u32 mdstat;
+
+	regmap_read(psc->regmap, MDSTAT(psc->lpsc), &mdstat);
+
+	return (mdstat & MDSTAT_MCKOUT) ? 1 : 0;
+}
+
+static const struct clk_ops davinci_psc_clk_ops = {
+	.enable		= davinci_psc_clk_enable,
+	.disable	= davinci_psc_clk_disable,
+	.is_enabled	= davinci_psc_clk_is_enabled,
+};
+
+/**
+ * davinci_psc_clk_register - register psc clock
+ * @dev: device that is registering this clock
+ * @name: name of this clock
+ * @parent_name: name of clock's parent
+ * @regmap: PSC MMIO region
+ * @lpsc: local PSC number
+ * @pd: power domain
+ * @flags: LPSC_* flags
+ */
+static struct clk *davinci_psc_clk_register(const char *name,
+					    const char *parent_name,
+					    struct regmap *regmap,
+					    u32 lpsc, u32 pd, u32 flags)
+{
+	struct clk_init_data init;
+	struct davinci_psc_clk *psc;
+	struct clk *clk;
+
+	psc = kzalloc(sizeof(*psc), GFP_KERNEL);
+	if (!psc)
+		return ERR_PTR(-ENOMEM);
+
+	init.name = name;
+	init.ops = &davinci_psc_clk_ops;
+	init.parent_names = (parent_name ? &parent_name : NULL);
+	init.num_parents = (parent_name ? 1 : 0);
+	init.flags = CLK_SET_RATE_PARENT;
+
+	if (flags & LPSC_ALWAYS_ENABLED)
+		init.flags |= CLK_IS_CRITICAL;
+
+	psc->regmap = regmap;
+	psc->hw.init = &init;
+	psc->lpsc = lpsc;
+	psc->pd = pd;
+	psc->flags = flags;
+
+	clk = clk_register(NULL, &psc->hw);
+	if (IS_ERR(clk))
+		kfree(psc);
+
+	return clk;
+}
+
+/*
+ * FIXME: This needs to be converted to a reset controller. But, the reset
+ * framework is currently device tree only.
+ */
+
+static int davinci_psc_clk_reset(struct davinci_psc_clk *psc, bool reset)
+{
+	u32 mdctl;
+
+	if (IS_ERR_OR_NULL(psc))
+		return -EINVAL;
+
+	mdctl = reset ? 0 : MDCTL_LRESET;
+	regmap_write_bits(psc->regmap, MDCTL(psc->lpsc), MDCTL_LRESET, mdctl);
+
+	return 0;
+}
+
+int davinci_clk_reset_assert(struct clk *clk)
+{
+	struct davinci_psc_clk *psc = to_davinci_psc_clk(__clk_get_hw(clk));
+
+	return davinci_psc_clk_reset(psc, true);
+}
+EXPORT_SYMBOL(davinci_clk_reset_assert);
+
+int davinci_clk_reset_deassert(struct clk *clk)
+{
+	struct davinci_psc_clk *psc = to_davinci_psc_clk(__clk_get_hw(clk));
+
+	return davinci_psc_clk_reset(psc, false);
+}
+EXPORT_SYMBOL(davinci_clk_reset_deassert);
+
+static const struct regmap_config davinci_psc_regmap_config = {
+	.reg_bits = 32,
+	.reg_stride = 4,
+	.val_bits = 32,
+};
+
+struct clk_onecell_data *
+davinci_psc_register_clocks(void __iomem *base,
+			    const struct davinci_psc_clk_info *info,
+			    u8 num_clks)
+{
+	struct clk_onecell_data *clk_data;
+	struct regmap *regmap;
+
+	clk_data = clk_alloc_onecell_data(num_clks);
+	if (!clk_data) {
+		pr_err("%s: Out of memory\n", __func__);
+		return NULL;
+	}
+
+	regmap = regmap_init_mmio(NULL, base, &davinci_psc_regmap_config);
+	if (IS_ERR(regmap)) {
+		pr_err("%s: regmap_init_mmio failed (%ld)\n", __func__,
+		       PTR_ERR(regmap));
+		clk_free_onecell_data(clk_data);
+		return NULL;
+	}
+
+	for (; info->name; info++) {
+		struct clk *clk;
+
+		clk = davinci_psc_clk_register(info->name, info->parent, regmap,
+					       info->lpsc, info->pd, info->flags);
+		if (IS_ERR(clk)) {
+			pr_warn("%s: Failed to register %s (%ld)\n", __func__,
+				info->name, PTR_ERR(clk));
+			continue;
+		}
+
+		clk_data->clks[info->lpsc] = clk;
+	}
+
+	return clk_data;
+}
+
+#ifdef CONFIG_OF
+void of_davinci_psc_clk_init(struct device_node *node,
+			     const struct davinci_psc_clk_info *info,
+			     u8 num_clks)
+{
+	struct clk_onecell_data *clk_data;
+	void __iomem *base;
+
+	base = of_iomap(node, 0);
+	if (!base) {
+		pr_err("%s: ioremap failed\n", __func__);
+		return;
+	}
+
+	clk_data = davinci_psc_register_clocks(base, info, num_clks);
+	if (!clk_data)
+		return;
+
+	of_clk_add_provider(node, of_clk_src_onecell_get, clk_data);
+}
+#endif
diff --git a/drivers/clk/davinci/psc.h b/drivers/clk/davinci/psc.h
new file mode 100644
index 0000000..6022f6e
--- /dev/null
+++ b/drivers/clk/davinci/psc.h
@@ -0,0 +1,49 @@ 
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Clock driver for TI Davinci PSC controllers
+ *
+ * Copyright (C) 2017 David Lechner <david@lechnology.com>
+ */
+
+#ifndef __CLK_DAVINCI_PSC_H__
+#define __CLK_DAVINCI_PSC_H__
+
+#include <linux/types.h>
+
+/* PSC quirk flags */
+#define LPSC_ALWAYS_ENABLED	BIT(1) /* never disable this clock */
+#define LPSC_FORCE		BIT(2) /* requires MDCTL FORCE bit */
+#define LPSC_LOCAL_RESET	BIT(3) /* acts as reset provider */
+
+struct clk_onecell_data;
+
+struct davinci_psc_clk_info {
+	const char *name;
+	const char *parent;
+	u32 lpsc;
+	u32 pd;
+	unsigned long flags;
+	bool has_reset;
+};
+
+#define LPSC(l, d, n, p, f)	\
+{				\
+	.name	= #n,		\
+	.parent	= #p,		\
+	.lpsc	= (l),		\
+	.pd	= (d),		\
+	.flags	= (f),		\
+}
+
+struct clk_onecell_data *
+davinci_psc_register_clocks(void __iomem *base,
+			    const struct davinci_psc_clk_info *info,
+			    u8 num_clks);
+
+#ifdef CONFIG_OF
+void of_davinci_psc_clk_init(struct device_node *node,
+			     const struct davinci_psc_clk_info *info,
+			     u8 num_clks);
+#endif
+
+#endif /* __CLK_DAVINCI_PSC_H__ */