Message ID | 1351181518-11882-3-git-send-email-m-karicheri2@ti.com (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
On Thu, Oct 25, 2012 at 6:11 PM, Murali Karicheri <m-karicheri2@ti.com> wrote: > This is the driver for the Power Sleep Controller (PSC) hardware > found on DM SoCs as well Keystone SoCs (c6x). This driver borrowed > code from arch/arm/mach-davinci/psc.c and implemented the driver > as per common clock provider API. The PSC module is responsible for > enabling/disabling the Power Domain and Clock domain for different IPs > present in the SoC. The driver is configured through the clock data > passed to the driver through struct clk_psc_data. > > Signed-off-by: Murali Karicheri <m-karicheri2@ti.com> Acked-by: Linus Walleij <linus.walleij@linaro.org> Here is some pedantic stuff if you're really bored: > diff --git a/drivers/clk/davinci/clk-psc.c b/drivers/clk/davinci/clk-psc.c (...) > + ptcmd = 1 << domain; ptcmd = BIT(domain); > + pdctl = readl(psc_base + PDCTL + 4 * domain); > + pdctl |= 0x100; pdctl |= BIT(8); /* and here a comment explaing what on earth that means */ > + } else { > + ptcmd = 1 << domain; ptcmd = BIT(domain); Yours, Linus Walleij
On 10/28/2012 03:24 PM, Linus Walleij wrote: > On Thu, Oct 25, 2012 at 6:11 PM, Murali Karicheri <m-karicheri2@ti.com> wrote: > >> This is the driver for the Power Sleep Controller (PSC) hardware >> found on DM SoCs as well Keystone SoCs (c6x). This driver borrowed >> code from arch/arm/mach-davinci/psc.c and implemented the driver >> as per common clock provider API. The PSC module is responsible for >> enabling/disabling the Power Domain and Clock domain for different IPs >> present in the SoC. The driver is configured through the clock data >> passed to the driver through struct clk_psc_data. >> >> Signed-off-by: Murali Karicheri <m-karicheri2@ti.com> > Acked-by: Linus Walleij <linus.walleij@linaro.org> > > Here is some pedantic stuff if you're really bored: > >> diff --git a/drivers/clk/davinci/clk-psc.c b/drivers/clk/davinci/clk-psc.c > (...) >> + ptcmd = 1 << domain; > ptcmd = BIT(domain); > >> + pdctl = readl(psc_base + PDCTL + 4 * domain); >> + pdctl |= 0x100; > pdctl |= BIT(8); /* and here a comment explaing what on earth that means */ > >> + } else { >> + ptcmd = 1 << domain; > ptcmd = BIT(domain); > > Yours, > Linus Walleij > > Linus, Thanks. I will fix the above and add your Acked-by in the next revision of the patch. Murali
On 10/25/2012 9:41 PM, Murali Karicheri wrote: > This is the driver for the Power Sleep Controller (PSC) hardware > found on DM SoCs as well Keystone SoCs (c6x). This driver borrowed > code from arch/arm/mach-davinci/psc.c and implemented the driver > as per common clock provider API. The PSC module is responsible for > enabling/disabling the Power Domain and Clock domain for different IPs > present in the SoC. The driver is configured through the clock data > passed to the driver through struct clk_psc_data. > > Signed-off-by: Murali Karicheri <m-karicheri2@ti.com> > --- > +/** > + * struct clk_psc - DaVinci PSC clock driver data > + * > + * @hw: clk_hw for the psc > + * @psc_data: Driver specific data > + */ > +struct clk_psc { > + struct clk_hw hw; > + struct clk_psc_data *psc_data; > + spinlock_t *lock; Unused member? I don't see this being used. Thanks, Sekhar
On 11/03/2012 08:07 AM, Sekhar Nori wrote: > On 10/25/2012 9:41 PM, Murali Karicheri wrote: >> This is the driver for the Power Sleep Controller (PSC) hardware >> found on DM SoCs as well Keystone SoCs (c6x). This driver borrowed >> code from arch/arm/mach-davinci/psc.c and implemented the driver >> as per common clock provider API. The PSC module is responsible for >> enabling/disabling the Power Domain and Clock domain for different IPs >> present in the SoC. The driver is configured through the clock data >> passed to the driver through struct clk_psc_data. >> >> Signed-off-by: Murali Karicheri <m-karicheri2@ti.com> >> --- >> +/** >> + * struct clk_psc - DaVinci PSC clock driver data >> + * >> + * @hw: clk_hw for the psc >> + * @psc_data: Driver specific data >> + */ >> +struct clk_psc { >> + struct clk_hw hw; >> + struct clk_psc_data *psc_data; >> + spinlock_t *lock; > Unused member? I don't see this being used. OK. Will remove. > > Thanks, > Sekhar >
Quoting Murali Karicheri (2012-11-05 07:10:52) > On 11/03/2012 08:07 AM, Sekhar Nori wrote: > > On 10/25/2012 9:41 PM, Murali Karicheri wrote: > >> This is the driver for the Power Sleep Controller (PSC) hardware > >> found on DM SoCs as well Keystone SoCs (c6x). This driver borrowed > >> code from arch/arm/mach-davinci/psc.c and implemented the driver > >> as per common clock provider API. The PSC module is responsible for > >> enabling/disabling the Power Domain and Clock domain for different IPs > >> present in the SoC. The driver is configured through the clock data > >> passed to the driver through struct clk_psc_data. > >> > >> Signed-off-by: Murali Karicheri <m-karicheri2@ti.com> > >> --- > >> +/** > >> + * struct clk_psc - DaVinci PSC clock driver data > >> + * > >> + * @hw: clk_hw for the psc > >> + * @psc_data: Driver specific data > >> + */ > >> +struct clk_psc { > >> + struct clk_hw hw; > >> + struct clk_psc_data *psc_data; > >> + spinlock_t *lock; > > Unused member? I don't see this being used. > > OK. Will remove. Those locks are only used for the case where a register might contain bits for several clocks. Thus RMW operations are protected. On OMAP this isn't necessary due to a very generous register layout (typically one 32-bit reg per module) controlling clocks. Seems tha tmaybe this is not needed for PSC module either? Regards, Mike > > > > Thanks, > > Sekhar > >
Hi Mike, On 11/10/2012 7:52 AM, Mike Turquette wrote: > Quoting Murali Karicheri (2012-11-05 07:10:52) >> On 11/03/2012 08:07 AM, Sekhar Nori wrote: >>> On 10/25/2012 9:41 PM, Murali Karicheri wrote: >>>> This is the driver for the Power Sleep Controller (PSC) hardware >>>> found on DM SoCs as well Keystone SoCs (c6x). This driver borrowed >>>> code from arch/arm/mach-davinci/psc.c and implemented the driver >>>> as per common clock provider API. The PSC module is responsible for >>>> enabling/disabling the Power Domain and Clock domain for different IPs >>>> present in the SoC. The driver is configured through the clock data >>>> passed to the driver through struct clk_psc_data. >>>> >>>> Signed-off-by: Murali Karicheri <m-karicheri2@ti.com> >>>> --- >>>> +/** >>>> + * struct clk_psc - DaVinci PSC clock driver data >>>> + * >>>> + * @hw: clk_hw for the psc >>>> + * @psc_data: Driver specific data >>>> + */ >>>> +struct clk_psc { >>>> + struct clk_hw hw; >>>> + struct clk_psc_data *psc_data; >>>> + spinlock_t *lock; >>> Unused member? I don't see this being used. >> >> OK. Will remove. > > Those locks are only used for the case where a register might contain > bits for several clocks. Thus RMW operations are protected. On OMAP > this isn't necessary due to a very generous register layout (typically > one 32-bit reg per module) controlling clocks. Seems tha tmaybe this is > not needed for PSC module either? Sorry about the late reply. The above is not totally true for PSC. There are some registers (like PTCMD) which are common for all clocks. There is an enable_lock used in drivers/clk/clk.c which serializes all enable/disable calls across the clock tree. Since that is done, further locking at clk-psc level is not really needed, no? Thanks, Sekhar
Quoting Sekhar Nori (2012-11-27 07:05:21) > Hi Mike, > > On 11/10/2012 7:52 AM, Mike Turquette wrote: > > Quoting Murali Karicheri (2012-11-05 07:10:52) > >> On 11/03/2012 08:07 AM, Sekhar Nori wrote: > >>> On 10/25/2012 9:41 PM, Murali Karicheri wrote: > >>>> This is the driver for the Power Sleep Controller (PSC) hardware > >>>> found on DM SoCs as well Keystone SoCs (c6x). This driver borrowed > >>>> code from arch/arm/mach-davinci/psc.c and implemented the driver > >>>> as per common clock provider API. The PSC module is responsible for > >>>> enabling/disabling the Power Domain and Clock domain for different IPs > >>>> present in the SoC. The driver is configured through the clock data > >>>> passed to the driver through struct clk_psc_data. > >>>> > >>>> Signed-off-by: Murali Karicheri <m-karicheri2@ti.com> > >>>> --- > >>>> +/** > >>>> + * struct clk_psc - DaVinci PSC clock driver data > >>>> + * > >>>> + * @hw: clk_hw for the psc > >>>> + * @psc_data: Driver specific data > >>>> + */ > >>>> +struct clk_psc { > >>>> + struct clk_hw hw; > >>>> + struct clk_psc_data *psc_data; > >>>> + spinlock_t *lock; > >>> Unused member? I don't see this being used. > >> > >> OK. Will remove. > > > > Those locks are only used for the case where a register might contain > > bits for several clocks. Thus RMW operations are protected. On OMAP > > this isn't necessary due to a very generous register layout (typically > > one 32-bit reg per module) controlling clocks. Seems tha tmaybe this is > > not needed for PSC module either? > > Sorry about the late reply. The above is not totally true for PSC. There > are some registers (like PTCMD) which are common for all clocks. > > There is an enable_lock used in drivers/clk/clk.c which serializes all > enable/disable calls across the clock tree. Since that is done, further > locking at clk-psc level is not really needed, no? > I haven't finished looking through the PSC design document yet, but my answer to your question is this: If a register is only used for clk_enable/disable calls (not touched by anything held under the prepare_lock mutex) and if that register isn't used anywhere else in the code (outside of the clk framework) then yes, the enable_lock spinlock is enough for you. Also have you looked into regmap? Since you are defining your own clock type that might be something nice for you. Regards, Mike > Thanks, > Sekhar
On 11/27/2012 12:29 PM, Mike Turquette wrote: > Quoting Sekhar Nori (2012-11-27 07:05:21) >> Hi Mike, >> >> On 11/10/2012 7:52 AM, Mike Turquette wrote: >>> Quoting Murali Karicheri (2012-11-05 07:10:52) >>>> On 11/03/2012 08:07 AM, Sekhar Nori wrote: >>>>> On 10/25/2012 9:41 PM, Murali Karicheri wrote: >>>>>> This is the driver for the Power Sleep Controller (PSC) hardware >>>>>> found on DM SoCs as well Keystone SoCs (c6x). This driver borrowed >>>>>> code from arch/arm/mach-davinci/psc.c and implemented the driver >>>>>> as per common clock provider API. The PSC module is responsible for >>>>>> enabling/disabling the Power Domain and Clock domain for different IPs >>>>>> present in the SoC. The driver is configured through the clock data >>>>>> passed to the driver through struct clk_psc_data. >>>>>> >>>>>> Signed-off-by: Murali Karicheri <m-karicheri2@ti.com> >>>>>> --- >>>>>> +/** >>>>>> + * struct clk_psc - DaVinci PSC clock driver data >>>>>> + * >>>>>> + * @hw: clk_hw for the psc >>>>>> + * @psc_data: Driver specific data >>>>>> + */ >>>>>> +struct clk_psc { >>>>>> + struct clk_hw hw; >>>>>> + struct clk_psc_data *psc_data; >>>>>> + spinlock_t *lock; >>>>> Unused member? I don't see this being used. >>>> OK. Will remove. >>> Those locks are only used for the case where a register might contain >>> bits for several clocks. Thus RMW operations are protected. On OMAP >>> this isn't necessary due to a very generous register layout (typically >>> one 32-bit reg per module) controlling clocks. Seems tha tmaybe this is >>> not needed for PSC module either? >> Sorry about the late reply. The above is not totally true for PSC. There >> are some registers (like PTCMD) which are common for all clocks. >> >> There is an enable_lock used in drivers/clk/clk.c which serializes all >> enable/disable calls across the clock tree. Since that is done, further >> locking at clk-psc level is not really needed, no? >> > I haven't finished looking through the PSC design document yet, but my > answer to your question is this: > > If a register is only used for clk_enable/disable calls (not touched by > anything held under the prepare_lock mutex) and if that register isn't > used anywhere else in the code (outside of the clk framework) then yes, > the enable_lock spinlock is enough for you. The psc clocks share registers such as PTCMD in addition to the clock specific register. So if there are multiple concurrent paths to clk_enable()/clk_disable() possible, then PTCMD write is not protected through the main enable()/disable() lock. Now I am not sure if there are multiple concurrent paths possible such as invoking the API in the context of a user process, kernel thread etc. If this is a possibility then IMO, a lock is needed. Murali > Also have you looked into regmap? Since you are defining your own clock > type that might be something nice for you. > > Regards, > Mike > >> Thanks, >> Sekhar >
On 11/27/2012 10:59 PM, Mike Turquette wrote: > Quoting Sekhar Nori (2012-11-27 07:05:21) >> Hi Mike, >> >> On 11/10/2012 7:52 AM, Mike Turquette wrote: >>> Quoting Murali Karicheri (2012-11-05 07:10:52) >>>> On 11/03/2012 08:07 AM, Sekhar Nori wrote: >>>>> On 10/25/2012 9:41 PM, Murali Karicheri wrote: >>>>>> This is the driver for the Power Sleep Controller (PSC) hardware >>>>>> found on DM SoCs as well Keystone SoCs (c6x). This driver borrowed >>>>>> code from arch/arm/mach-davinci/psc.c and implemented the driver >>>>>> as per common clock provider API. The PSC module is responsible for >>>>>> enabling/disabling the Power Domain and Clock domain for different IPs >>>>>> present in the SoC. The driver is configured through the clock data >>>>>> passed to the driver through struct clk_psc_data. >>>>>> >>>>>> Signed-off-by: Murali Karicheri <m-karicheri2@ti.com> >>>>>> --- >>>>>> +/** >>>>>> + * struct clk_psc - DaVinci PSC clock driver data >>>>>> + * >>>>>> + * @hw: clk_hw for the psc >>>>>> + * @psc_data: Driver specific data >>>>>> + */ >>>>>> +struct clk_psc { >>>>>> + struct clk_hw hw; >>>>>> + struct clk_psc_data *psc_data; >>>>>> + spinlock_t *lock; >>>>> Unused member? I don't see this being used. >>>> >>>> OK. Will remove. >>> >>> Those locks are only used for the case where a register might contain >>> bits for several clocks. Thus RMW operations are protected. On OMAP >>> this isn't necessary due to a very generous register layout (typically >>> one 32-bit reg per module) controlling clocks. Seems tha tmaybe this is >>> not needed for PSC module either? >> >> Sorry about the late reply. The above is not totally true for PSC. There >> are some registers (like PTCMD) which are common for all clocks. >> >> There is an enable_lock used in drivers/clk/clk.c which serializes all >> enable/disable calls across the clock tree. Since that is done, further >> locking at clk-psc level is not really needed, no? >> > > I haven't finished looking through the PSC design document yet, but my > answer to your question is this: > > If a register is only used for clk_enable/disable calls (not touched by This is right, all PSC registers are only touched in clk_enable/disable calls. clk-psc.c also populates .is_enabled, but that's only a regsiter read anyway. Plus looks like even that is always accessed under enable_lock. > anything held under the prepare_lock mutex) and if that register isn't The requirement that these registers should not be touched when held under prepare_lock mutex is not met because functions like clk_disabled_unused_subtree() which are called under prepare_lock mutex will end up calling clk_disable() anyway. Perhaps you meant: not touched by anything under 'prepare_lock' mutex while being outside of the 'enable_lock' spinlock? > used anywhere else in the code (outside of the clk framework) then yes, > the enable_lock spinlock is enough for you. No, they are not accessed out side of clk framework. At least currently. PSC can also be used to provide a "reset" to some IPs, but there is no "reset framework" which uses this feature. > Also have you looked into regmap? Since you are defining your own clock > type that might be something nice for you. No, haven't looked at regmap yet. Will look at that. Thanks, Sekhar
On 10/29/2012 12:54 AM, Linus Walleij wrote: > On Thu, Oct 25, 2012 at 6:11 PM, Murali Karicheri <m-karicheri2@ti.com> wrote: > >> This is the driver for the Power Sleep Controller (PSC) hardware >> found on DM SoCs as well Keystone SoCs (c6x). This driver borrowed >> code from arch/arm/mach-davinci/psc.c and implemented the driver >> as per common clock provider API. The PSC module is responsible for >> enabling/disabling the Power Domain and Clock domain for different IPs >> present in the SoC. The driver is configured through the clock data >> passed to the driver through struct clk_psc_data. >> >> Signed-off-by: Murali Karicheri <m-karicheri2@ti.com> > > Acked-by: Linus Walleij <linus.walleij@linaro.org> > > Here is some pedantic stuff if you're really bored: > >> diff --git a/drivers/clk/davinci/clk-psc.c b/drivers/clk/davinci/clk-psc.c > (...) >> + ptcmd = 1 << domain; > > ptcmd = BIT(domain); > >> + pdctl = readl(psc_base + PDCTL + 4 * domain); >> + pdctl |= 0x100; > > pdctl |= BIT(8); /* and here a comment explaing what on earth that means */ It turns out this piece of code is superfluous and the exact same thing is being done again just down below. Thanks, Sekhar
Mike, On 11/28/2012 6:52 PM, Sekhar Nori wrote: > On 11/27/2012 10:59 PM, Mike Turquette wrote: >> Also have you looked into regmap? Since you are defining your own clock >> type that might be something nice for you. > > No, haven't looked at regmap yet. Will look at that. I could get to this only now. regmap needs a valid struct device. We aren't modelling psc clocks as device so regmap cannot be used here. Thanks, Sekhar
Quoting Sekhar Nori (2013-03-22 04:20:28) > Mike, > > On 11/28/2012 6:52 PM, Sekhar Nori wrote: > > On 11/27/2012 10:59 PM, Mike Turquette wrote: > > >> Also have you looked into regmap? Since you are defining your own clock > >> type that might be something nice for you. > > > > No, haven't looked at regmap yet. Will look at that. > > I could get to this only now. regmap needs a valid struct device. We > aren't modelling psc clocks as device so regmap cannot be used here. > According to Murali's reply back in November it was still not known if the PTCMD register was going to be concurrently accessed by sources outside the clock framework. If this is so then neither the existing framework-level lock (e.g. enable_lock) nor the shared clock driver lock (e.g. spinlock in struct clk_psc) are adequate. You may need a device driver which represents the PSC and use accessor functions to write to those register which provide coordination. Regards, Mike > Thanks, > Sekhar
Hi Mike, On 3/23/2013 2:07 AM, Mike Turquette wrote: > Quoting Sekhar Nori (2013-03-22 04:20:28) >> Mike, >> >> On 11/28/2012 6:52 PM, Sekhar Nori wrote: >>> On 11/27/2012 10:59 PM, Mike Turquette wrote: >> >>>> Also have you looked into regmap? Since you are defining your own clock >>>> type that might be something nice for you. >>> >>> No, haven't looked at regmap yet. Will look at that. >> >> I could get to this only now. regmap needs a valid struct device. We >> aren't modelling psc clocks as device so regmap cannot be used here. >> > > According to Murali's reply back in November it was still not known if > the PTCMD register was going to be concurrently accessed by sources > outside the clock framework. If this is so then neither the existing I can confirm that PTCMD is a PSC specific register and it is not accessed outside of clock framework. > framework-level lock (e.g. enable_lock) nor the shared clock driver lock > (e.g. spinlock in struct clk_psc) are adequate. You may need a device > driver which represents the PSC and use accessor functions to write to > those register which provide coordination. Well the only reason to access *any* PSC register outside of clock framework would be to handle the corner case where DSP needs to be reset using its PSC. In this case, there needs to be an access to MDCTL register specific to that module (ie the DSP). Right now this functionality is implemented using the function davinci_psc_reset(). When moving to common clock framework, I think this can be moved to drivers/clk/davinci/clk-psc.c (it will be similar to the way tegra_periph_reset() is handled in drivers/clk/tegra/clk-periph-gate.c). And in this case I do agree we need a spinlock local to clk-psc.c (like the periph_ref_lock spinlock used in tegra implementation). That said, the current series only enables DM644x and does not really implement the reset stuff needed for DA850. So we can either add the spinlock now, or defer it to later when DA850 get supported (so there is a clear history trace of why it was added). Thanks, Sekhar
diff --git a/drivers/clk/davinci/clk-psc.c b/drivers/clk/davinci/clk-psc.c new file mode 100644 index 0000000..40d5f06 --- /dev/null +++ b/drivers/clk/davinci/clk-psc.c @@ -0,0 +1,207 @@ +/* + * PSC clk driver for DaVinci devices + * + * Copyright (C) 2006-2012 Texas Instruments. + * Copyright (C) 2008-2009 Deep Root Systems, LLC + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ +#include <linux/clk.h> +#include <linux/clk-provider.h> +#include <linux/delay.h> +#include <linux/err.h> +#include <linux/io.h> +#include <linux/slab.h> + +#include "clk-psc.h" + +/* PSC register offsets */ +#define EPCPR 0x070 +#define PTCMD 0x120 +#define PTSTAT 0x128 +#define PDSTAT 0x200 +#define PDCTL 0x300 +#define MDSTAT 0x800 +#define MDCTL 0xA00 + +/* PSC module states */ +#define PSC_STATE_SWRSTDISABLE 0 +#define PSC_STATE_SYNCRST 1 +#define PSC_STATE_DISABLE 2 +#define PSC_STATE_ENABLE 3 + +#define MDSTAT_STATE_MASK 0x3f +#define PDSTAT_STATE_MASK 0x1f +#define MDCTL_FORCE BIT(31) +#define PDCTL_NEXT BIT(0) +#define PDCTL_EPCGOOD BIT(8) + +/** + * struct clk_psc - DaVinci PSC clock driver data + * + * @hw: clk_hw for the psc + * @psc_data: Driver specific data + */ +struct clk_psc { + struct clk_hw hw; + struct clk_psc_data *psc_data; + spinlock_t *lock; +}; + +#define to_clk_psc(_hw) container_of(_hw, struct clk_psc, hw) + +/** + * clk_psc_config() - configure psc hardware + * + * @base: io mapped base address of the psc + * @domain: Power Domain id of the module + * @id: lpsc id + * @enable: 1 - enable psc, 0 - disable psc + * @flags: psc driver specific flags + */ +static void clk_psc_config(void __iomem *base, unsigned int domain, + unsigned int id, bool enable, u32 flags) +{ + u32 epcpr, ptcmd, ptstat, pdstat, pdctl, mdstat, mdctl; + u32 next_state = PSC_STATE_ENABLE; + void __iomem *psc_base = base; + + if (!enable) { + if (flags & CLK_PSC_SWRSTDISABLE) + next_state = PSC_STATE_SWRSTDISABLE; + else + next_state = PSC_STATE_DISABLE; + } + + mdctl = readl(psc_base + MDCTL + 4 * id); + mdctl &= ~MDSTAT_STATE_MASK; + mdctl |= next_state; + if (flags & CLK_PSC_FORCE) + mdctl |= MDCTL_FORCE; + writel(mdctl, psc_base + MDCTL + 4 * id); + + pdstat = readl(psc_base + PDSTAT + 4 * domain); + if ((pdstat & PDSTAT_STATE_MASK) == 0) { + pdctl = readl(psc_base + PDCTL + 4 * domain); + pdctl |= PDCTL_NEXT; + writel(pdctl, psc_base + PDCTL + 4 * domain); + + ptcmd = 1 << domain; + writel(ptcmd, psc_base + PTCMD); + + if (flags & CLK_PSC_HAS_EXT_POWER_CNTL) { + do { + epcpr = readl(psc_base + EPCPR); + } while ((((epcpr >> domain) & 1) == 0)); + } + + pdctl = readl(psc_base + PDCTL + 4 * domain); + pdctl |= 0x100; + writel(pdctl, psc_base + PDCTL + 4 * domain); + + pdctl = readl(psc_base + PDCTL + 4 * domain); + pdctl |= PDCTL_EPCGOOD; + writel(pdctl, psc_base + PDCTL + 4 * domain); + } else { + ptcmd = 1 << domain; + writel(ptcmd, psc_base + PTCMD); + } + + do { + ptstat = readl(psc_base + PTSTAT); + } while (!(((ptstat >> domain) & 1) == 0)); + + do { + mdstat = readl(psc_base + MDSTAT + 4 * id); + } while (!((mdstat & MDSTAT_STATE_MASK) == next_state)); +} + +/** + * clk_psc_is_enabled() - Is psc clock enabled + * + * @hw: clk hw for the psc + */ +static int clk_psc_is_enabled(struct clk_hw *hw) +{ + struct clk_psc *psc = to_clk_psc(hw); + struct clk_psc_data *psc_data = psc->psc_data; + u32 mdstat; + + mdstat = readl(psc_data->reg_base + MDSTAT + 4 * psc_data->lpsc_id); + + /* if clocked, state can be "Enable" or "SyncReset" */ + return (mdstat & BIT(12)) ? 1 : 0; +} + +/** + * clk_psc_enable() - Enable psc clock + * + * @hw: clk hw for the psc + */ +static int clk_psc_enable(struct clk_hw *hw) +{ + struct clk_psc *psc = to_clk_psc(hw); + struct clk_psc_data *psc_data = psc->psc_data; + + clk_psc_config(psc_data->reg_base, psc_data->domain_id, + psc_data->lpsc_id, 1, psc_data->psc_flags); + return 0; +} + +/** + * clk_psc_disable() - disable psc clock + * + * @hw: clk hw for the psc + */ +static void clk_psc_disable(struct clk_hw *hw) +{ + struct clk_psc *psc = to_clk_psc(hw); + struct clk_psc_data *psc_data = psc->psc_data; + + clk_psc_config(psc_data->reg_base, psc_data->domain_id, + psc_data->lpsc_id, 0, psc_data->psc_flags); +} + +static const struct clk_ops clk_psc_ops = { + .enable = clk_psc_enable, + .disable = clk_psc_disable, + .is_enabled = clk_psc_is_enabled, +}; + +/** + * clk_register_davinci_psc() - register function for DaVinci PSC clock + * + * @dev: clk device + * @name: name of the clock + * @parent_name: name of the parent clock + * @psc_data: ptr to psc clk data + */ +struct clk *clk_register_davinci_psc(struct device *dev, const char *name, + const char *parent_name, struct clk_psc_data *psc_data) +{ + struct clk_init_data init; + struct clk_psc *psc; + struct clk *clk; + + psc = kzalloc(sizeof(*psc), GFP_KERNEL); + if (!psc) + return ERR_PTR(-ENOMEM); + + init.name = name; + init.ops = &clk_psc_ops; + init.flags = psc_data->flags; + init.parent_names = (parent_name ? &parent_name : NULL); + init.num_parents = (parent_name ? 1 : 0); + + psc->psc_data = psc_data; + psc->hw.init = &init; + + clk = clk_register(NULL, &psc->hw); + if (IS_ERR(clk)) + kfree(psc); + + return clk; +} diff --git a/drivers/clk/davinci/clk-psc.h b/drivers/clk/davinci/clk-psc.h new file mode 100644 index 0000000..26327d6 --- /dev/null +++ b/drivers/clk/davinci/clk-psc.h @@ -0,0 +1,46 @@ +/* + * PSC clk driver for DaVinci devices + * + * Copyright (C) 2006-2012 Texas Instruments. + * Copyright (C) 2008-2009 Deep Root Systems, LLC + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ +#ifndef __CLK_DAVINCI_PSC_H +#define __CLK_DAVINCI_PSC_H + +/* PSC flags */ + +/* Disable state is SwRstDisable */ +#define CLK_PSC_SWRSTDISABLE BIT(0) +/* Force module state transtition */ +#define CLK_PSC_FORCE BIT(1) +/* PSC has external power control available (for DM6446 SoC) */ +#define CLK_PSC_HAS_EXT_POWER_CNTL BIT(2) + +/** + * struct clk_psc_data - configuration for DaVinci psc clk driver + * + * @reg_base: io mapped address of psc register base + * @flags: clk driver base flags + * @psc_flags: clk_psc driver flags + * @lpsc_id: local power sleep controller id + * @gpsc_id: global power sleep controller id + * @domain_id: Power domain id + */ +struct clk_psc_data { + void __iomem *reg_base; + u32 flags; + u32 psc_flags; + u8 lpsc_id; + u8 gpsc_id; + u8 domain_id; +}; + +struct clk *clk_register_davinci_psc(struct device *dev, + const char *name, const char *parent_name, + struct clk_psc_data *psc_data); +#endif /* __CLK_DAVINCI_PSC_H */
This is the driver for the Power Sleep Controller (PSC) hardware found on DM SoCs as well Keystone SoCs (c6x). This driver borrowed code from arch/arm/mach-davinci/psc.c and implemented the driver as per common clock provider API. The PSC module is responsible for enabling/disabling the Power Domain and Clock domain for different IPs present in the SoC. The driver is configured through the clock data passed to the driver through struct clk_psc_data. Signed-off-by: Murali Karicheri <m-karicheri2@ti.com> --- drivers/clk/davinci/clk-psc.c | 207 +++++++++++++++++++++++++++++++++++++++++ drivers/clk/davinci/clk-psc.h | 46 +++++++++ 2 files changed, 253 insertions(+) create mode 100644 drivers/clk/davinci/clk-psc.c create mode 100644 drivers/clk/davinci/clk-psc.h