diff mbox

[v6,4/5] clk: aspeed: Register gated clocks

Message ID 20171128071908.12279-5-joel@jms.id.au (mailing list archive)
State New, archived
Headers show

Commit Message

Joel Stanley Nov. 28, 2017, 7:19 a.m. UTC
The majority of the clocks in the system are gates paired with a reset
controller that holds the IP in reset.

This borrows from clk_hw_register_gate, but registers two 'gates', one
to control the clock enable register and the other to control the reset
IP. This allows us to enforce the ordering:

 1. Place IP in reset
 2. Enable clock
 3. Delay
 4. Release reset

There are some gates that do not have an associated reset; these are
handled by using -1 as the index for the reset.

Reviewed-by: Andrew Jeffery <andrew@aj.id.au>
Signed-off-by: Joel Stanley <joel@jms.id.au>
---
v5:
  - Add Andrew's Reviewed-by
v4:
 - Drop useless 'disable clock' comment
 - Drop CLK_IS_BASIC flag
 - Fix 'there are a number of clocks...' comment
 - Pass device to clk registration functions
 - Check for errors when registering clk_hws
v3:
 - Remove gates offset as gates are now at the start of the list
---
 drivers/clk/clk-aspeed.c | 131 +++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 131 insertions(+)

Comments

Stephen Boyd Dec. 21, 2017, 11:39 p.m. UTC | #1
On 11/28, Joel Stanley wrote:
> diff --git a/drivers/clk/clk-aspeed.c b/drivers/clk/clk-aspeed.c
> index 839243691b26..b5dc3e298693 100644
> --- a/drivers/clk/clk-aspeed.c
> +++ b/drivers/clk/clk-aspeed.c
> @@ -202,6 +202,107 @@ static const struct aspeed_clk_soc_data ast2400_data = {
>  	.calc_pll = aspeed_ast2400_calc_pll,
>  };
>  
> +static int aspeed_clk_enable(struct clk_hw *hw)
> +{
> +	struct aspeed_clk_gate *gate = to_aspeed_clk_gate(hw);
> +	unsigned long flags;
> +	u32 clk = BIT(gate->clock_idx);
> +	u32 rst = BIT(gate->reset_idx);
> +
> +	spin_lock_irqsave(gate->lock, flags);
> +
> +	if (gate->reset_idx >= 0) {
> +		/* Put IP in reset */
> +		regmap_update_bits(gate->map, ASPEED_RESET_CTRL, rst, rst);
> +
> +		/* Delay 100us */
> +		udelay(100);
> +	}
> +
> +	/* Enable clock */
> +	regmap_update_bits(gate->map, ASPEED_CLK_STOP_CTRL, clk, 0);
> +
> +	if (gate->reset_idx >= 0) {
> +		/* Delay 10ms */
> +		/* TODO: can we sleep here? */
> +		msleep(10);

No you can't sleep here. It needs to delay because this is inside
spinlock_irqsave.

> +
> +		/* Take IP out of reset */
> +		regmap_update_bits(gate->map, ASPEED_RESET_CTRL, rst, 0);
> +	}
> +
> +	spin_unlock_irqrestore(gate->lock, flags);
> +
> +	return 0;
> +}
Benjamin Herrenschmidt Dec. 22, 2017, 2:36 a.m. UTC | #2
On Thu, 2017-12-21 at 15:39 -0800, Stephen Boyd wrote:
> On 11/28, Joel Stanley wrote:
> > diff --git a/drivers/clk/clk-aspeed.c b/drivers/clk/clk-aspeed.c
> > index 839243691b26..b5dc3e298693 100644
> > --- a/drivers/clk/clk-aspeed.c
> > +++ b/drivers/clk/clk-aspeed.c
> > @@ -202,6 +202,107 @@ static const struct aspeed_clk_soc_data ast2400_data = {
> >        .calc_pll = aspeed_ast2400_calc_pll,
> >   };
> >   
> > +static int aspeed_clk_enable(struct clk_hw *hw)
> > +{
> > +     struct aspeed_clk_gate *gate = to_aspeed_clk_gate(hw);
> > +     unsigned long flags;
> > +     u32 clk = BIT(gate->clock_idx);
> > +     u32 rst = BIT(gate->reset_idx);
> > +
> > +     spin_lock_irqsave(gate->lock, flags);
> > +
> > +     if (gate->reset_idx >= 0) {
> > +             /* Put IP in reset */
> > +             regmap_update_bits(gate->map, ASPEED_RESET_CTRL, rst, rst);
> > +
> > +             /* Delay 100us */
> > +             udelay(100);
> > +     }
> > +
> > +     /* Enable clock */
> > +     regmap_update_bits(gate->map, ASPEED_CLK_STOP_CTRL, clk, 0);
> > +
> > +     if (gate->reset_idx >= 0) {
> > +             /* Delay 10ms */
> > +             /* TODO: can we sleep here? */
> > +             msleep(10);
> 
> No you can't sleep here. It needs to delay because this is inside
> spinlock_irqsave.

Additionally you really don't want to delay for 10ms with interrupts
off :-(

Sadly, it looks like the clk framework already calls you with spinlock
irqsafe, which is a rather major suckage.

Stephen, why is that so ? That pretty much makes it impossible to
do sleeping things, which prevents things like i2c based clock
controllers etc...

I think the clk framework needs to be overhauled to use sleeping
mutexes instead. Doing clock enable/disable at interrupt time is
a bad idea anyway.


In the meantime, Joel, you have little choice but do an mdelay
though that really sucks.

> > +             /* Take IP out of reset */
> > +             regmap_update_bits(gate->map, ASPEED_RESET_CTRL, rst, 0);
> > +     }
> > +
> > +     spin_unlock_irqrestore(gate->lock, flags);
> > +
> > +     return 0;
> > +}
Benjamin Herrenschmidt Dec. 22, 2017, 2:43 a.m. UTC | #3
On Fri, 2017-12-22 at 13:36 +1100, Benjamin Herrenschmidt wrote:
> 
> > No you can't sleep here. It needs to delay because this is inside
> > spinlock_irqsave.
> 
> Additionally you really don't want to delay for 10ms with interrupts
> off :-(
> 
> Sadly, it looks like the clk framework already calls you with spinlock
> irqsafe, which is a rather major suckage.
> 
> Stephen, why is that so ? That pretty much makes it impossible to
> do sleeping things, which prevents things like i2c based clock
> controllers etc...

I noticed we do have a few i2c based clock drivers... how are they ever
supposed to work ? i2c bus controllers are allowed to sleep and the i2c
core takes mutexes...

> I think the clk framework needs to be overhauled to use sleeping
> mutexes instead. Doing clock enable/disable at interrupt time is
> a bad idea anyway.
> 
> 
> In the meantime, Joel, you have little choice but do an mdelay
> though that really sucks.
> 
> > > +             /* Take IP out of reset */
> > > +             regmap_update_bits(gate->map, ASPEED_RESET_CTRL, rst, 0);
> > > +     }
> > > +
> > > +     spin_unlock_irqrestore(gate->lock, flags);
> > > +
> > > +     return 0;
> > > +}
Stephen Boyd Dec. 27, 2017, 1:32 a.m. UTC | #4
On 12/22, Benjamin Herrenschmidt wrote:
> On Fri, 2017-12-22 at 13:36 +1100, Benjamin Herrenschmidt wrote:
> > 
> > > No you can't sleep here. It needs to delay because this is inside
> > > spinlock_irqsave.
> > 
> > Additionally you really don't want to delay for 10ms with interrupts
> > off :-(
> > 
> > Sadly, it looks like the clk framework already calls you with spinlock
> > irqsafe, which is a rather major suckage.
> > 
> > Stephen, why is that so ? That pretty much makes it impossible to
> > do sleeping things, which prevents things like i2c based clock
> > controllers etc...
> 
> I noticed we do have a few i2c based clock drivers... how are they ever
> supposed to work ? i2c bus controllers are allowed to sleep and the i2c
> core takes mutexes...

We have clk_prepare()/clk_unprepare() for sleeping suckage. You
can use that, and i2c based clk drivers do that today.
Benjamin Herrenschmidt Dec. 29, 2017, 10:03 p.m. UTC | #5
On Tue, 2017-12-26 at 17:32 -0800, Stephen Boyd wrote:
> > I noticed we do have a few i2c based clock drivers... how are they ever
> > supposed to work ? i2c bus controllers are allowed to sleep and the i2c
> > core takes mutexes...
> 
> We have clk_prepare()/clk_unprepare() for sleeping suckage. You
> can use that, and i2c based clk drivers do that today.

"suckage" ? Hehe ... the suckage should rather be stuff that cannot
sleep. Arbitrary latencies and jitter caused by too much code wanting
to be "atomic" when unnecessary are a bad thing.

In the case of clocks like the aspeed where we have to wait for a
rather long stabilization delay, way too long to legitimately do a non-
sleepable delay with a lock held, do we need to do everything in
prepare() then ?

Ben.
Benjamin Herrenschmidt Jan. 2, 2018, 5:46 a.m. UTC | #6
On Sat, 2017-12-30 at 09:03 +1100, Benjamin Herrenschmidt wrote:
> On Tue, 2017-12-26 at 17:32 -0800, Stephen Boyd wrote:
> > > I noticed we do have a few i2c based clock drivers... how are they ever
> > > supposed to work ? i2c bus controllers are allowed to sleep and the i2c
> > > core takes mutexes...
> > 
> > We have clk_prepare()/clk_unprepare() for sleeping suckage. You
> > can use that, and i2c based clk drivers do that today.
> 
> "suckage" ? Hehe ... the suckage should rather be stuff that cannot
> sleep. Arbitrary latencies and jitter caused by too much code wanting
> to be "atomic" when unnecessary are a bad thing.
> 
> In the case of clocks like the aspeed where we have to wait for a
> rather long stabilization delay, way too long to legitimately do a non-
> sleepable delay with a lock held, do we need to do everything in
> prepare() then ?

BTW. Pls don't hold Joel's patches for this. Without that clk framework
a lot of the aspeed stuff already upstream doesn't actually work
without additional out-of-tree hacks or uboot black magic.

We can sort out the sleeping issues (and possibly move to using prepare
for the clocks that have that delay requirement) via subsequent
improvements.

Cheers,
Ben.
Stephen Boyd Jan. 2, 2018, 6:16 p.m. UTC | #7
On 12/30, Benjamin Herrenschmidt wrote:
> On Tue, 2017-12-26 at 17:32 -0800, Stephen Boyd wrote:
> > > I noticed we do have a few i2c based clock drivers... how are they ever
> > > supposed to work ? i2c bus controllers are allowed to sleep and the i2c
> > > core takes mutexes...
> > 
> > We have clk_prepare()/clk_unprepare() for sleeping suckage. You
> > can use that, and i2c based clk drivers do that today.
> 
> "suckage" ? Hehe ... the suckage should rather be stuff that cannot
> sleep. Arbitrary latencies and jitter caused by too much code wanting
> to be "atomic" when unnecessary are a bad thing.

Heh. Of course.

> 
> In the case of clocks like the aspeed where we have to wait for a
> rather long stabilization delay, way too long to legitimately do a non-
> sleepable delay with a lock held, do we need to do everything in
> prepare() then ?
> 

Yes. If we have to wait a long time in the enable path it makes
sense to move it to the prepare path instead, if possible. That
way we avoid holding a spinlock for a long time.
diff mbox

Patch

diff --git a/drivers/clk/clk-aspeed.c b/drivers/clk/clk-aspeed.c
index 839243691b26..b5dc3e298693 100644
--- a/drivers/clk/clk-aspeed.c
+++ b/drivers/clk/clk-aspeed.c
@@ -202,6 +202,107 @@  static const struct aspeed_clk_soc_data ast2400_data = {
 	.calc_pll = aspeed_ast2400_calc_pll,
 };
 
+static int aspeed_clk_enable(struct clk_hw *hw)
+{
+	struct aspeed_clk_gate *gate = to_aspeed_clk_gate(hw);
+	unsigned long flags;
+	u32 clk = BIT(gate->clock_idx);
+	u32 rst = BIT(gate->reset_idx);
+
+	spin_lock_irqsave(gate->lock, flags);
+
+	if (gate->reset_idx >= 0) {
+		/* Put IP in reset */
+		regmap_update_bits(gate->map, ASPEED_RESET_CTRL, rst, rst);
+
+		/* Delay 100us */
+		udelay(100);
+	}
+
+	/* Enable clock */
+	regmap_update_bits(gate->map, ASPEED_CLK_STOP_CTRL, clk, 0);
+
+	if (gate->reset_idx >= 0) {
+		/* Delay 10ms */
+		/* TODO: can we sleep here? */
+		msleep(10);
+
+		/* Take IP out of reset */
+		regmap_update_bits(gate->map, ASPEED_RESET_CTRL, rst, 0);
+	}
+
+	spin_unlock_irqrestore(gate->lock, flags);
+
+	return 0;
+}
+
+static void aspeed_clk_disable(struct clk_hw *hw)
+{
+	struct aspeed_clk_gate *gate = to_aspeed_clk_gate(hw);
+	unsigned long flags;
+	u32 clk = BIT(gate->clock_idx);
+
+	spin_lock_irqsave(gate->lock, flags);
+
+	regmap_update_bits(gate->map, ASPEED_CLK_STOP_CTRL, clk, clk);
+
+	spin_unlock_irqrestore(gate->lock, flags);
+}
+
+static int aspeed_clk_is_enabled(struct clk_hw *hw)
+{
+	struct aspeed_clk_gate *gate = to_aspeed_clk_gate(hw);
+	u32 clk = BIT(gate->clock_idx);
+	u32 reg;
+
+	regmap_read(gate->map, ASPEED_CLK_STOP_CTRL, &reg);
+
+	return (reg & clk) ? 0 : 1;
+}
+
+static const struct clk_ops aspeed_clk_gate_ops = {
+	.enable = aspeed_clk_enable,
+	.disable = aspeed_clk_disable,
+	.is_enabled = aspeed_clk_is_enabled,
+};
+
+static struct clk_hw *aspeed_clk_hw_register_gate(struct device *dev,
+		const char *name, const char *parent_name, unsigned long flags,
+		struct regmap *map, u8 clock_idx, u8 reset_idx,
+		u8 clk_gate_flags, spinlock_t *lock)
+{
+	struct aspeed_clk_gate *gate;
+	struct clk_init_data init;
+	struct clk_hw *hw;
+	int ret;
+
+	gate = kzalloc(sizeof(*gate), GFP_KERNEL);
+	if (!gate)
+		return ERR_PTR(-ENOMEM);
+
+	init.name = name;
+	init.ops = &aspeed_clk_gate_ops;
+	init.flags = flags;
+	init.parent_names = parent_name ? &parent_name : NULL;
+	init.num_parents = parent_name ? 1 : 0;
+
+	gate->map = map;
+	gate->clock_idx = clock_idx;
+	gate->reset_idx = reset_idx;
+	gate->flags = clk_gate_flags;
+	gate->lock = lock;
+	gate->hw.init = &init;
+
+	hw = &gate->hw;
+	ret = clk_hw_register(dev, hw);
+	if (ret) {
+		kfree(gate);
+		hw = ERR_PTR(ret);
+	}
+
+	return hw;
+}
+
 static int aspeed_clk_probe(struct platform_device *pdev)
 {
 	const struct aspeed_clk_soc_data *soc_data;
@@ -209,6 +310,7 @@  static int aspeed_clk_probe(struct platform_device *pdev)
 	struct regmap *map;
 	struct clk_hw *hw;
 	u32 val, rate;
+	int i;
 
 	map = syscon_node_to_regmap(dev->of_node);
 	if (IS_ERR(map)) {
@@ -281,6 +383,35 @@  static int aspeed_clk_probe(struct platform_device *pdev)
 		return PTR_ERR(hw);
 	aspeed_clk_data->hws[ASPEED_CLK_BCLK] = hw;
 
+	/*
+	 * TODO: There are a number of clocks that not included in this driver
+	 * as more information is required:
+	 *   D2-PLL
+	 *   D-PLL
+	 *   YCLK
+	 *   RGMII
+	 *   RMII
+	 *   UART[1..5] clock source mux
+	 *   Video Engine (ECLK) mux and clock divider
+	 */
+
+	for (i = 0; i < ARRAY_SIZE(aspeed_gates); i++) {
+		const struct aspeed_gate_data *gd = &aspeed_gates[i];
+
+		hw = aspeed_clk_hw_register_gate(dev,
+				gd->name,
+				gd->parent_name,
+				gd->flags,
+				map,
+				gd->clock_idx,
+				gd->reset_idx,
+				CLK_GATE_SET_TO_DISABLE,
+				&aspeed_clk_lock);
+		if (IS_ERR(hw))
+			return PTR_ERR(hw);
+		aspeed_clk_data->hws[i] = hw;
+	}
+
 	return 0;
 };