Message ID | 20171128071908.12279-5-joel@jms.id.au |
---|---|
State | New |
Headers | show |
Series | clk: Add Aspeed clock driver | expand |
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; > +} -- Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, a Linux Foundation Collaborative Project
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; > > +}
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; > > > +}
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. -- Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, a Linux Foundation Collaborative Project
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.
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.
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. -- Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, a Linux Foundation Collaborative Project
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, ®); + + 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; };