Message ID | 1363535031-21402-2-git-send-email-hechtb+renesas@gmail.com (mailing list archive) |
---|---|
State | Superseded |
Headers | show |
On Sun, Mar 17, 2013 at 04:43:46PM +0100, Bastian Hecht wrote: > We add the capabilty to probe SH CMT timer devices using Device Tree > setup. > > Signed-off-by: Bastian Hecht <hechtb+renesas@gmail.com> > --- > v3: same (only patch 0003 changed) > > drivers/clocksource/sh_cmt.c | 109 +++++++++++++++++++++++++++++++++++++----- This change appears to break booting of kzm9g-reference with MMC enabled. I assume because it somehow causes CMT to (once again) be initialised after SDHI and/or MMC. Tested using next with the defconfig + CONFIG_MACH_KZM9G_REFERENCE=y + initrd root fs. > 1 file changed, 96 insertions(+), 13 deletions(-) > > diff --git a/drivers/clocksource/sh_cmt.c b/drivers/clocksource/sh_cmt.c > index 08d0c41..79bc526 100644 > --- a/drivers/clocksource/sh_cmt.c > +++ b/drivers/clocksource/sh_cmt.c > @@ -34,10 +34,13 @@ > #include <linux/module.h> > #include <linux/pm_domain.h> > #include <linux/pm_runtime.h> > +#include <linux/of.h> > > struct sh_cmt_priv { > void __iomem *mapbase; > struct clk *clk; > + long channel_offset; > + int timer_bit; > unsigned long width; /* 16 or 32 bit version of hardware block */ > unsigned long overflow_bit; > unsigned long clear_bits; > @@ -109,9 +112,7 @@ static void sh_cmt_write32(void __iomem *base, unsigned long offs, > > static inline unsigned long sh_cmt_read_cmstr(struct sh_cmt_priv *p) > { > - struct sh_timer_config *cfg = p->pdev->dev.platform_data; > - > - return p->read_control(p->mapbase - cfg->channel_offset, 0); > + return p->read_control(p->mapbase - p->channel_offset, 0); > } > > static inline unsigned long sh_cmt_read_cmcsr(struct sh_cmt_priv *p) > @@ -127,9 +128,7 @@ static inline unsigned long sh_cmt_read_cmcnt(struct sh_cmt_priv *p) > static inline void sh_cmt_write_cmstr(struct sh_cmt_priv *p, > unsigned long value) > { > - struct sh_timer_config *cfg = p->pdev->dev.platform_data; > - > - p->write_control(p->mapbase - cfg->channel_offset, 0, value); > + p->write_control(p->mapbase - p->channel_offset, 0, value); > } > > static inline void sh_cmt_write_cmcsr(struct sh_cmt_priv *p, > @@ -176,7 +175,6 @@ static DEFINE_RAW_SPINLOCK(sh_cmt_lock); > > static void sh_cmt_start_stop_ch(struct sh_cmt_priv *p, int start) > { > - struct sh_timer_config *cfg = p->pdev->dev.platform_data; > unsigned long flags, value; > > /* start stop register shared by multiple timer channels */ > @@ -184,9 +182,9 @@ static void sh_cmt_start_stop_ch(struct sh_cmt_priv *p, int start) > value = sh_cmt_read_cmstr(p); > > if (start) > - value |= 1 << cfg->timer_bit; > + value |= 1 << p->timer_bit; > else > - value &= ~(1 << cfg->timer_bit); > + value &= ~(1 << p->timer_bit); > > sh_cmt_write_cmstr(p, value); > raw_spin_unlock_irqrestore(&sh_cmt_lock, flags); > @@ -673,9 +671,85 @@ static int sh_cmt_register(struct sh_cmt_priv *p, char *name, > return 0; > } > > -static int sh_cmt_setup(struct sh_cmt_priv *p, struct platform_device *pdev) > +static const struct of_device_id of_sh_cmt_match[] = { > + { .compatible = "renesas,cmt-timer" }, > + {}, > +}; > +MODULE_DEVICE_TABLE(of, of_sh_cmt_match); > + > +static const int sh_cmt_offset_multiplier[] = { 0x60, 0x10, 0x40, 0x40, 0x40 }; > +#define CMT_MAX_CHANNELS 6 > + > +static struct sh_timer_config *sh_cmt_parse_dt(struct device *dev) > +{ > + struct sh_timer_config *cfg; > + struct device_node *np = dev->of_node; > + u32 timer_id, channel_id, rating; > + > + if (!IS_ENABLED(CONFIG_OF) || !np) > + return NULL; > + > + cfg = devm_kzalloc(dev, sizeof(struct sh_timer_config), GFP_KERNEL); > + if (!cfg) { > + dev_err(dev, "failed to allocate DT config data\n"); > + return NULL; > + } > + > + if (of_property_read_u32(np, "renesas,device-id", &timer_id)) { > + dev_err(dev, "device id missing\n"); > + return NULL; > + } > + if (timer_id >= ARRAY_SIZE(sh_cmt_offset_multiplier)) { > + dev_err(dev, "invalid device id\n"); > + return NULL; > + } > + > + if (of_property_read_u32(np, "renesas,channel-id", &channel_id)) { > + dev_err(dev, "channel id missing\n"); > + return NULL; > + } > + if (channel_id >= CMT_MAX_CHANNELS) { > + dev_err(dev, "invalid channel id\n"); > + return NULL; > + } > + > + cfg->channel_offset = sh_cmt_offset_multiplier[timer_id] * > + (channel_id + 1); > + cfg->timer_bit = channel_id; > + > + /* > + * We convert the {source,event}-quality DT properties to linux specific > + * clock{source,event}_ratings. > + */ > + if (!of_property_read_u32(np, "renesas,source-quality", &rating)) { > + if (rating > 10) { > + dev_err(dev, "invalid source-quality\n"); > + return NULL; > + } > + if (rating) > + cfg->clocksource_rating = rating * 50 - 1; > + } > + > + if (!of_property_read_u32(np, "renesas,event-quality", &rating)) { > + if (rating > 10) { > + dev_err(dev, "invalid event-quality\n"); > + return NULL; > + } > + if (rating) > + cfg->clockevent_rating = rating * 50 - 1; > + } > + > + if (!cfg->clocksource_rating && !cfg->clockevent_rating) { > + dev_err(dev, "source- and event-quality 0, timer is unused\n"); > + return NULL; > + } > + > + return cfg; > +} > + > +static int sh_cmt_setup(struct sh_cmt_priv *p, struct platform_device *pdev, > + struct sh_timer_config *cfg) > { > - struct sh_timer_config *cfg = pdev->dev.platform_data; > struct resource *res; > int irq, ret; > ret = -ENXIO; > @@ -762,6 +836,9 @@ static int sh_cmt_setup(struct sh_cmt_priv *p, struct platform_device *pdev) > goto err2; > } > > + p->channel_offset = cfg->channel_offset; > + p->timer_bit = cfg->timer_bit; > + > platform_set_drvdata(pdev, p); > > return 0; > @@ -777,7 +854,7 @@ err0: > static int sh_cmt_probe(struct platform_device *pdev) > { > struct sh_cmt_priv *p = platform_get_drvdata(pdev); > - struct sh_timer_config *cfg = pdev->dev.platform_data; > + struct sh_timer_config *cfg; > int ret; > > if (!is_early_platform_device(pdev)) { > @@ -785,6 +862,11 @@ static int sh_cmt_probe(struct platform_device *pdev) > pm_runtime_enable(&pdev->dev); > } > > + if (pdev->dev.of_node) > + cfg = sh_cmt_parse_dt(&pdev->dev); > + else > + cfg = pdev->dev.platform_data; > + > if (p) { > dev_info(&pdev->dev, "kept as earlytimer\n"); > goto out; > @@ -796,7 +878,7 @@ static int sh_cmt_probe(struct platform_device *pdev) > return -ENOMEM; > } > > - ret = sh_cmt_setup(p, pdev); > + ret = sh_cmt_setup(p, pdev, cfg); > if (ret) { > kfree(p); > pm_runtime_idle(&pdev->dev); > @@ -824,6 +906,7 @@ static struct platform_driver sh_cmt_device_driver = { > .remove = sh_cmt_remove, > .driver = { > .name = "sh_cmt", > + .of_match_table = of_match_ptr(of_sh_cmt_match), > } > }; > > -- > 1.7.9.5 > > -- > To unsubscribe from this list: send the line "unsubscribe linux-sh" in > the body of a message to majordomo@vger.kernel.org > More majordomo info at http://vger.kernel.org/majordomo-info.html > -- To unsubscribe from this list: send the line "unsubscribe linux-sh" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
On ?, 3? 21, 2013 at 10:07:15?? +0900, Simon Horman wrote: > On Sun, Mar 17, 2013 at 04:43:46PM +0100, Bastian Hecht wrote: > > We add the capabilty to probe SH CMT timer devices using Device Tree > > setup. > > > > Signed-off-by: Bastian Hecht <hechtb+renesas@gmail.com> > > --- > > v3: same (only patch 0003 changed) > > > > drivers/clocksource/sh_cmt.c | 109 +++++++++++++++++++++++++++++++++++++----- > > > This change appears to break booting of kzm9g-reference > with MMC enabled. I assume because it somehow causes CMT to > (once again) be initialised after SDHI and/or MMC. > > Tested using next with the defconfig + CONFIG_MACH_KZM9G_REFERENCE=y + > initrd root fs. Ping. > > 1 file changed, 96 insertions(+), 13 deletions(-) > > > > diff --git a/drivers/clocksource/sh_cmt.c b/drivers/clocksource/sh_cmt.c > > index 08d0c41..79bc526 100644 > > --- a/drivers/clocksource/sh_cmt.c > > +++ b/drivers/clocksource/sh_cmt.c > > @@ -34,10 +34,13 @@ > > #include <linux/module.h> > > #include <linux/pm_domain.h> > > #include <linux/pm_runtime.h> > > +#include <linux/of.h> > > > > struct sh_cmt_priv { > > void __iomem *mapbase; > > struct clk *clk; > > + long channel_offset; > > + int timer_bit; > > unsigned long width; /* 16 or 32 bit version of hardware block */ > > unsigned long overflow_bit; > > unsigned long clear_bits; > > @@ -109,9 +112,7 @@ static void sh_cmt_write32(void __iomem *base, unsigned long offs, > > > > static inline unsigned long sh_cmt_read_cmstr(struct sh_cmt_priv *p) > > { > > - struct sh_timer_config *cfg = p->pdev->dev.platform_data; > > - > > - return p->read_control(p->mapbase - cfg->channel_offset, 0); > > + return p->read_control(p->mapbase - p->channel_offset, 0); > > } > > > > static inline unsigned long sh_cmt_read_cmcsr(struct sh_cmt_priv *p) > > @@ -127,9 +128,7 @@ static inline unsigned long sh_cmt_read_cmcnt(struct sh_cmt_priv *p) > > static inline void sh_cmt_write_cmstr(struct sh_cmt_priv *p, > > unsigned long value) > > { > > - struct sh_timer_config *cfg = p->pdev->dev.platform_data; > > - > > - p->write_control(p->mapbase - cfg->channel_offset, 0, value); > > + p->write_control(p->mapbase - p->channel_offset, 0, value); > > } > > > > static inline void sh_cmt_write_cmcsr(struct sh_cmt_priv *p, > > @@ -176,7 +175,6 @@ static DEFINE_RAW_SPINLOCK(sh_cmt_lock); > > > > static void sh_cmt_start_stop_ch(struct sh_cmt_priv *p, int start) > > { > > - struct sh_timer_config *cfg = p->pdev->dev.platform_data; > > unsigned long flags, value; > > > > /* start stop register shared by multiple timer channels */ > > @@ -184,9 +182,9 @@ static void sh_cmt_start_stop_ch(struct sh_cmt_priv *p, int start) > > value = sh_cmt_read_cmstr(p); > > > > if (start) > > - value |= 1 << cfg->timer_bit; > > + value |= 1 << p->timer_bit; > > else > > - value &= ~(1 << cfg->timer_bit); > > + value &= ~(1 << p->timer_bit); > > > > sh_cmt_write_cmstr(p, value); > > raw_spin_unlock_irqrestore(&sh_cmt_lock, flags); > > @@ -673,9 +671,85 @@ static int sh_cmt_register(struct sh_cmt_priv *p, char *name, > > return 0; > > } > > > > -static int sh_cmt_setup(struct sh_cmt_priv *p, struct platform_device *pdev) > > +static const struct of_device_id of_sh_cmt_match[] = { > > + { .compatible = "renesas,cmt-timer" }, > > + {}, > > +}; > > +MODULE_DEVICE_TABLE(of, of_sh_cmt_match); > > + > > +static const int sh_cmt_offset_multiplier[] = { 0x60, 0x10, 0x40, 0x40, 0x40 }; > > +#define CMT_MAX_CHANNELS 6 > > + > > +static struct sh_timer_config *sh_cmt_parse_dt(struct device *dev) > > +{ > > + struct sh_timer_config *cfg; > > + struct device_node *np = dev->of_node; > > + u32 timer_id, channel_id, rating; > > + > > + if (!IS_ENABLED(CONFIG_OF) || !np) > > + return NULL; > > + > > + cfg = devm_kzalloc(dev, sizeof(struct sh_timer_config), GFP_KERNEL); > > + if (!cfg) { > > + dev_err(dev, "failed to allocate DT config data\n"); > > + return NULL; > > + } > > + > > + if (of_property_read_u32(np, "renesas,device-id", &timer_id)) { > > + dev_err(dev, "device id missing\n"); > > + return NULL; > > + } > > + if (timer_id >= ARRAY_SIZE(sh_cmt_offset_multiplier)) { > > + dev_err(dev, "invalid device id\n"); > > + return NULL; > > + } > > + > > + if (of_property_read_u32(np, "renesas,channel-id", &channel_id)) { > > + dev_err(dev, "channel id missing\n"); > > + return NULL; > > + } > > + if (channel_id >= CMT_MAX_CHANNELS) { > > + dev_err(dev, "invalid channel id\n"); > > + return NULL; > > + } > > + > > + cfg->channel_offset = sh_cmt_offset_multiplier[timer_id] * > > + (channel_id + 1); > > + cfg->timer_bit = channel_id; > > + > > + /* > > + * We convert the {source,event}-quality DT properties to linux specific > > + * clock{source,event}_ratings. > > + */ > > + if (!of_property_read_u32(np, "renesas,source-quality", &rating)) { > > + if (rating > 10) { > > + dev_err(dev, "invalid source-quality\n"); > > + return NULL; > > + } > > + if (rating) > > + cfg->clocksource_rating = rating * 50 - 1; > > + } > > + > > + if (!of_property_read_u32(np, "renesas,event-quality", &rating)) { > > + if (rating > 10) { > > + dev_err(dev, "invalid event-quality\n"); > > + return NULL; > > + } > > + if (rating) > > + cfg->clockevent_rating = rating * 50 - 1; > > + } > > + > > + if (!cfg->clocksource_rating && !cfg->clockevent_rating) { > > + dev_err(dev, "source- and event-quality 0, timer is unused\n"); > > + return NULL; > > + } > > + > > + return cfg; > > +} > > + > > +static int sh_cmt_setup(struct sh_cmt_priv *p, struct platform_device *pdev, > > + struct sh_timer_config *cfg) > > { > > - struct sh_timer_config *cfg = pdev->dev.platform_data; > > struct resource *res; > > int irq, ret; > > ret = -ENXIO; > > @@ -762,6 +836,9 @@ static int sh_cmt_setup(struct sh_cmt_priv *p, struct platform_device *pdev) > > goto err2; > > } > > > > + p->channel_offset = cfg->channel_offset; > > + p->timer_bit = cfg->timer_bit; > > + > > platform_set_drvdata(pdev, p); > > > > return 0; > > @@ -777,7 +854,7 @@ err0: > > static int sh_cmt_probe(struct platform_device *pdev) > > { > > struct sh_cmt_priv *p = platform_get_drvdata(pdev); > > - struct sh_timer_config *cfg = pdev->dev.platform_data; > > + struct sh_timer_config *cfg; > > int ret; > > > > if (!is_early_platform_device(pdev)) { > > @@ -785,6 +862,11 @@ static int sh_cmt_probe(struct platform_device *pdev) > > pm_runtime_enable(&pdev->dev); > > } > > > > + if (pdev->dev.of_node) > > + cfg = sh_cmt_parse_dt(&pdev->dev); > > + else > > + cfg = pdev->dev.platform_data; > > + > > if (p) { > > dev_info(&pdev->dev, "kept as earlytimer\n"); > > goto out; > > @@ -796,7 +878,7 @@ static int sh_cmt_probe(struct platform_device *pdev) > > return -ENOMEM; > > } > > > > - ret = sh_cmt_setup(p, pdev); > > + ret = sh_cmt_setup(p, pdev, cfg); > > if (ret) { > > kfree(p); > > pm_runtime_idle(&pdev->dev); > > @@ -824,6 +906,7 @@ static struct platform_driver sh_cmt_device_driver = { > > .remove = sh_cmt_remove, > > .driver = { > > .name = "sh_cmt", > > + .of_match_table = of_match_ptr(of_sh_cmt_match), > > } > > }; > > > > -- > > 1.7.9.5 > > > > -- > > To unsubscribe from this list: send the line "unsubscribe linux-sh" in > > the body of a message to majordomo@vger.kernel.org > > More majordomo info at http://vger.kernel.org/majordomo-info.html > > > -- > To unsubscribe from this list: send the line "unsubscribe linux-sh" in > the body of a message to majordomo@vger.kernel.org > More majordomo info at http://vger.kernel.org/majordomo-info.html > -- To unsubscribe from this list: send the line "unsubscribe linux-sh" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
Hi Simon, 2013/4/9 Simon Horman <horms@verge.net.au>: > On ?, 3? 21, 2013 at 10:07:15?? +0900, Simon Horman wrote: >> On Sun, Mar 17, 2013 at 04:43:46PM +0100, Bastian Hecht wrote: >> > We add the capabilty to probe SH CMT timer devices using Device Tree >> > setup. >> > >> > Signed-off-by: Bastian Hecht <hechtb+renesas@gmail.com> >> > --- >> > v3: same (only patch 0003 changed) >> > >> > drivers/clocksource/sh_cmt.c | 109 +++++++++++++++++++++++++++++++++++++----- >> >> >> This change appears to break booting of kzm9g-reference >> with MMC enabled. I assume because it somehow causes CMT to >> (once again) be initialised after SDHI and/or MMC. >> >> Tested using next with the defconfig + CONFIG_MACH_KZM9G_REFERENCE=y + >> initrd root fs. > Thanks for finding this flaw. I've got it on my agenda. Bastian >> > 1 file changed, 96 insertions(+), 13 deletions(-) >> > >> > diff --git a/drivers/clocksource/sh_cmt.c b/drivers/clocksource/sh_cmt.c >> > index 08d0c41..79bc526 100644 >> > --- a/drivers/clocksource/sh_cmt.c >> > +++ b/drivers/clocksource/sh_cmt.c >> > @@ -34,10 +34,13 @@ >> > #include <linux/module.h> >> > #include <linux/pm_domain.h> >> > #include <linux/pm_runtime.h> >> > +#include <linux/of.h> >> > >> > struct sh_cmt_priv { >> > void __iomem *mapbase; >> > struct clk *clk; >> > + long channel_offset; >> > + int timer_bit; >> > unsigned long width; /* 16 or 32 bit version of hardware block */ >> > unsigned long overflow_bit; >> > unsigned long clear_bits; >> > @@ -109,9 +112,7 @@ static void sh_cmt_write32(void __iomem *base, unsigned long offs, >> > >> > static inline unsigned long sh_cmt_read_cmstr(struct sh_cmt_priv *p) >> > { >> > - struct sh_timer_config *cfg = p->pdev->dev.platform_data; >> > - >> > - return p->read_control(p->mapbase - cfg->channel_offset, 0); >> > + return p->read_control(p->mapbase - p->channel_offset, 0); >> > } >> > >> > static inline unsigned long sh_cmt_read_cmcsr(struct sh_cmt_priv *p) >> > @@ -127,9 +128,7 @@ static inline unsigned long sh_cmt_read_cmcnt(struct sh_cmt_priv *p) >> > static inline void sh_cmt_write_cmstr(struct sh_cmt_priv *p, >> > unsigned long value) >> > { >> > - struct sh_timer_config *cfg = p->pdev->dev.platform_data; >> > - >> > - p->write_control(p->mapbase - cfg->channel_offset, 0, value); >> > + p->write_control(p->mapbase - p->channel_offset, 0, value); >> > } >> > >> > static inline void sh_cmt_write_cmcsr(struct sh_cmt_priv *p, >> > @@ -176,7 +175,6 @@ static DEFINE_RAW_SPINLOCK(sh_cmt_lock); >> > >> > static void sh_cmt_start_stop_ch(struct sh_cmt_priv *p, int start) >> > { >> > - struct sh_timer_config *cfg = p->pdev->dev.platform_data; >> > unsigned long flags, value; >> > >> > /* start stop register shared by multiple timer channels */ >> > @@ -184,9 +182,9 @@ static void sh_cmt_start_stop_ch(struct sh_cmt_priv *p, int start) >> > value = sh_cmt_read_cmstr(p); >> > >> > if (start) >> > - value |= 1 << cfg->timer_bit; >> > + value |= 1 << p->timer_bit; >> > else >> > - value &= ~(1 << cfg->timer_bit); >> > + value &= ~(1 << p->timer_bit); >> > >> > sh_cmt_write_cmstr(p, value); >> > raw_spin_unlock_irqrestore(&sh_cmt_lock, flags); >> > @@ -673,9 +671,85 @@ static int sh_cmt_register(struct sh_cmt_priv *p, char *name, >> > return 0; >> > } >> > >> > -static int sh_cmt_setup(struct sh_cmt_priv *p, struct platform_device *pdev) >> > +static const struct of_device_id of_sh_cmt_match[] = { >> > + { .compatible = "renesas,cmt-timer" }, >> > + {}, >> > +}; >> > +MODULE_DEVICE_TABLE(of, of_sh_cmt_match); >> > + >> > +static const int sh_cmt_offset_multiplier[] = { 0x60, 0x10, 0x40, 0x40, 0x40 }; >> > +#define CMT_MAX_CHANNELS 6 >> > + >> > +static struct sh_timer_config *sh_cmt_parse_dt(struct device *dev) >> > +{ >> > + struct sh_timer_config *cfg; >> > + struct device_node *np = dev->of_node; >> > + u32 timer_id, channel_id, rating; >> > + >> > + if (!IS_ENABLED(CONFIG_OF) || !np) >> > + return NULL; >> > + >> > + cfg = devm_kzalloc(dev, sizeof(struct sh_timer_config), GFP_KERNEL); >> > + if (!cfg) { >> > + dev_err(dev, "failed to allocate DT config data\n"); >> > + return NULL; >> > + } >> > + >> > + if (of_property_read_u32(np, "renesas,device-id", &timer_id)) { >> > + dev_err(dev, "device id missing\n"); >> > + return NULL; >> > + } >> > + if (timer_id >= ARRAY_SIZE(sh_cmt_offset_multiplier)) { >> > + dev_err(dev, "invalid device id\n"); >> > + return NULL; >> > + } >> > + >> > + if (of_property_read_u32(np, "renesas,channel-id", &channel_id)) { >> > + dev_err(dev, "channel id missing\n"); >> > + return NULL; >> > + } >> > + if (channel_id >= CMT_MAX_CHANNELS) { >> > + dev_err(dev, "invalid channel id\n"); >> > + return NULL; >> > + } >> > + >> > + cfg->channel_offset = sh_cmt_offset_multiplier[timer_id] * >> > + (channel_id + 1); >> > + cfg->timer_bit = channel_id; >> > + >> > + /* >> > + * We convert the {source,event}-quality DT properties to linux specific >> > + * clock{source,event}_ratings. >> > + */ >> > + if (!of_property_read_u32(np, "renesas,source-quality", &rating)) { >> > + if (rating > 10) { >> > + dev_err(dev, "invalid source-quality\n"); >> > + return NULL; >> > + } >> > + if (rating) >> > + cfg->clocksource_rating = rating * 50 - 1; >> > + } >> > + >> > + if (!of_property_read_u32(np, "renesas,event-quality", &rating)) { >> > + if (rating > 10) { >> > + dev_err(dev, "invalid event-quality\n"); >> > + return NULL; >> > + } >> > + if (rating) >> > + cfg->clockevent_rating = rating * 50 - 1; >> > + } >> > + >> > + if (!cfg->clocksource_rating && !cfg->clockevent_rating) { >> > + dev_err(dev, "source- and event-quality 0, timer is unused\n"); >> > + return NULL; >> > + } >> > + >> > + return cfg; >> > +} >> > + >> > +static int sh_cmt_setup(struct sh_cmt_priv *p, struct platform_device *pdev, >> > + struct sh_timer_config *cfg) >> > { >> > - struct sh_timer_config *cfg = pdev->dev.platform_data; >> > struct resource *res; >> > int irq, ret; >> > ret = -ENXIO; >> > @@ -762,6 +836,9 @@ static int sh_cmt_setup(struct sh_cmt_priv *p, struct platform_device *pdev) >> > goto err2; >> > } >> > >> > + p->channel_offset = cfg->channel_offset; >> > + p->timer_bit = cfg->timer_bit; >> > + >> > platform_set_drvdata(pdev, p); >> > >> > return 0; >> > @@ -777,7 +854,7 @@ err0: >> > static int sh_cmt_probe(struct platform_device *pdev) >> > { >> > struct sh_cmt_priv *p = platform_get_drvdata(pdev); >> > - struct sh_timer_config *cfg = pdev->dev.platform_data; >> > + struct sh_timer_config *cfg; >> > int ret; >> > >> > if (!is_early_platform_device(pdev)) { >> > @@ -785,6 +862,11 @@ static int sh_cmt_probe(struct platform_device *pdev) >> > pm_runtime_enable(&pdev->dev); >> > } >> > >> > + if (pdev->dev.of_node) >> > + cfg = sh_cmt_parse_dt(&pdev->dev); >> > + else >> > + cfg = pdev->dev.platform_data; >> > + >> > if (p) { >> > dev_info(&pdev->dev, "kept as earlytimer\n"); >> > goto out; >> > @@ -796,7 +878,7 @@ static int sh_cmt_probe(struct platform_device *pdev) >> > return -ENOMEM; >> > } >> > >> > - ret = sh_cmt_setup(p, pdev); >> > + ret = sh_cmt_setup(p, pdev, cfg); >> > if (ret) { >> > kfree(p); >> > pm_runtime_idle(&pdev->dev); >> > @@ -824,6 +906,7 @@ static struct platform_driver sh_cmt_device_driver = { >> > .remove = sh_cmt_remove, >> > .driver = { >> > .name = "sh_cmt", >> > + .of_match_table = of_match_ptr(of_sh_cmt_match), >> > } >> > }; >> > >> > -- >> > 1.7.9.5 >> > >> > -- >> > To unsubscribe from this list: send the line "unsubscribe linux-sh" in >> > the body of a message to majordomo@vger.kernel.org >> > More majordomo info at http://vger.kernel.org/majordomo-info.html >> > >> -- >> To unsubscribe from this list: send the line "unsubscribe linux-sh" in >> the body of a message to majordomo@vger.kernel.org >> More majordomo info at http://vger.kernel.org/majordomo-info.html >> -- To unsubscribe from this list: send the line "unsubscribe linux-sh" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
Hi Simon, 2013/4/9 Bastian Hecht <hechtb@gmail.com>: > Hi Simon, > > 2013/4/9 Simon Horman <horms@verge.net.au>: >> On ?, 3? 21, 2013 at 10:07:15?? +0900, Simon Horman wrote: >>> On Sun, Mar 17, 2013 at 04:43:46PM +0100, Bastian Hecht wrote: >>> > We add the capabilty to probe SH CMT timer devices using Device Tree >>> > setup. >>> > >>> > Signed-off-by: Bastian Hecht <hechtb+renesas@gmail.com> >>> > --- >>> > v3: same (only patch 0003 changed) >>> > >>> > drivers/clocksource/sh_cmt.c | 109 +++++++++++++++++++++++++++++++++++++----- >>> >>> >>> This change appears to break booting of kzm9g-reference >>> with MMC enabled. I assume because it somehow causes CMT to >>> (once again) be initialised after SDHI and/or MMC. >>> >>> Tested using next with the defconfig + CONFIG_MACH_KZM9G_REFERENCE=y + >>> initrd root fs. >> > > Thanks for finding this flaw. I've got it on my agenda. > > Bastian > >>> > 1 file changed, 96 insertions(+), 13 deletions(-) >>> > >>> > diff --git a/drivers/clocksource/sh_cmt.c b/drivers/clocksource/sh_cmt.c >>> > index 08d0c41..79bc526 100644 >>> > --- a/drivers/clocksource/sh_cmt.c >>> > +++ b/drivers/clocksource/sh_cmt.c >>> > @@ -34,10 +34,13 @@ >>> > #include <linux/module.h> >>> > #include <linux/pm_domain.h> >>> > #include <linux/pm_runtime.h> >>> > +#include <linux/of.h> >>> > >>> > struct sh_cmt_priv { >>> > void __iomem *mapbase; >>> > struct clk *clk; >>> > + long channel_offset; >>> > + int timer_bit; >>> > unsigned long width; /* 16 or 32 bit version of hardware block */ >>> > unsigned long overflow_bit; >>> > unsigned long clear_bits; >>> > @@ -109,9 +112,7 @@ static void sh_cmt_write32(void __iomem *base, unsigned long offs, >>> > >>> > static inline unsigned long sh_cmt_read_cmstr(struct sh_cmt_priv *p) >>> > { >>> > - struct sh_timer_config *cfg = p->pdev->dev.platform_data; >>> > - >>> > - return p->read_control(p->mapbase - cfg->channel_offset, 0); >>> > + return p->read_control(p->mapbase - p->channel_offset, 0); >>> > } >>> > >>> > static inline unsigned long sh_cmt_read_cmcsr(struct sh_cmt_priv *p) >>> > @@ -127,9 +128,7 @@ static inline unsigned long sh_cmt_read_cmcnt(struct sh_cmt_priv *p) >>> > static inline void sh_cmt_write_cmstr(struct sh_cmt_priv *p, >>> > unsigned long value) >>> > { >>> > - struct sh_timer_config *cfg = p->pdev->dev.platform_data; >>> > - >>> > - p->write_control(p->mapbase - cfg->channel_offset, 0, value); >>> > + p->write_control(p->mapbase - p->channel_offset, 0, value); >>> > } >>> > >>> > static inline void sh_cmt_write_cmcsr(struct sh_cmt_priv *p, >>> > @@ -176,7 +175,6 @@ static DEFINE_RAW_SPINLOCK(sh_cmt_lock); >>> > >>> > static void sh_cmt_start_stop_ch(struct sh_cmt_priv *p, int start) >>> > { >>> > - struct sh_timer_config *cfg = p->pdev->dev.platform_data; >>> > unsigned long flags, value; >>> > >>> > /* start stop register shared by multiple timer channels */ >>> > @@ -184,9 +182,9 @@ static void sh_cmt_start_stop_ch(struct sh_cmt_priv *p, int start) >>> > value = sh_cmt_read_cmstr(p); >>> > >>> > if (start) >>> > - value |= 1 << cfg->timer_bit; >>> > + value |= 1 << p->timer_bit; >>> > else >>> > - value &= ~(1 << cfg->timer_bit); >>> > + value &= ~(1 << p->timer_bit); >>> > >>> > sh_cmt_write_cmstr(p, value); >>> > raw_spin_unlock_irqrestore(&sh_cmt_lock, flags); >>> > @@ -673,9 +671,85 @@ static int sh_cmt_register(struct sh_cmt_priv *p, char *name, >>> > return 0; >>> > } >>> > >>> > -static int sh_cmt_setup(struct sh_cmt_priv *p, struct platform_device *pdev) >>> > +static const struct of_device_id of_sh_cmt_match[] = { >>> > + { .compatible = "renesas,cmt-timer" }, >>> > + {}, >>> > +}; >>> > +MODULE_DEVICE_TABLE(of, of_sh_cmt_match); >>> > + >>> > +static const int sh_cmt_offset_multiplier[] = { 0x60, 0x10, 0x40, 0x40, 0x40 }; >>> > +#define CMT_MAX_CHANNELS 6 >>> > + >>> > +static struct sh_timer_config *sh_cmt_parse_dt(struct device *dev) >>> > +{ >>> > + struct sh_timer_config *cfg; >>> > + struct device_node *np = dev->of_node; >>> > + u32 timer_id, channel_id, rating; >>> > + >>> > + if (!IS_ENABLED(CONFIG_OF) || !np) >>> > + return NULL; >>> > + >>> > + cfg = devm_kzalloc(dev, sizeof(struct sh_timer_config), GFP_KERNEL); >>> > + if (!cfg) { >>> > + dev_err(dev, "failed to allocate DT config data\n"); >>> > + return NULL; >>> > + } >>> > + >>> > + if (of_property_read_u32(np, "renesas,device-id", &timer_id)) { >>> > + dev_err(dev, "device id missing\n"); >>> > + return NULL; >>> > + } >>> > + if (timer_id >= ARRAY_SIZE(sh_cmt_offset_multiplier)) { >>> > + dev_err(dev, "invalid device id\n"); >>> > + return NULL; >>> > + } >>> > + >>> > + if (of_property_read_u32(np, "renesas,channel-id", &channel_id)) { >>> > + dev_err(dev, "channel id missing\n"); >>> > + return NULL; >>> > + } >>> > + if (channel_id >= CMT_MAX_CHANNELS) { >>> > + dev_err(dev, "invalid channel id\n"); >>> > + return NULL; >>> > + } >>> > + >>> > + cfg->channel_offset = sh_cmt_offset_multiplier[timer_id] * >>> > + (channel_id + 1); >>> > + cfg->timer_bit = channel_id; >>> > + >>> > + /* >>> > + * We convert the {source,event}-quality DT properties to linux specific >>> > + * clock{source,event}_ratings. >>> > + */ >>> > + if (!of_property_read_u32(np, "renesas,source-quality", &rating)) { >>> > + if (rating > 10) { >>> > + dev_err(dev, "invalid source-quality\n"); >>> > + return NULL; >>> > + } >>> > + if (rating) >>> > + cfg->clocksource_rating = rating * 50 - 1; >>> > + } >>> > + >>> > + if (!of_property_read_u32(np, "renesas,event-quality", &rating)) { >>> > + if (rating > 10) { >>> > + dev_err(dev, "invalid event-quality\n"); >>> > + return NULL; >>> > + } >>> > + if (rating) >>> > + cfg->clockevent_rating = rating * 50 - 1; >>> > + } >>> > + >>> > + if (!cfg->clocksource_rating && !cfg->clockevent_rating) { >>> > + dev_err(dev, "source- and event-quality 0, timer is unused\n"); >>> > + return NULL; >>> > + } >>> > + >>> > + return cfg; >>> > +} >>> > + >>> > +static int sh_cmt_setup(struct sh_cmt_priv *p, struct platform_device *pdev, >>> > + struct sh_timer_config *cfg) >>> > { >>> > - struct sh_timer_config *cfg = pdev->dev.platform_data; >>> > struct resource *res; >>> > int irq, ret; >>> > ret = -ENXIO; >>> > @@ -762,6 +836,9 @@ static int sh_cmt_setup(struct sh_cmt_priv *p, struct platform_device *pdev) >>> > goto err2; >>> > } >>> > >>> > + p->channel_offset = cfg->channel_offset; >>> > + p->timer_bit = cfg->timer_bit; >>> > + This happens too late. After interrupt registration. I have to check the TMU patch as well and I'll send out a v4 later. Thanks again for detecting this flaw. Bastian >>> > platform_set_drvdata(pdev, p); >>> > >>> > return 0; >>> > @@ -777,7 +854,7 @@ err0: >>> > static int sh_cmt_probe(struct platform_device *pdev) >>> > { >>> > struct sh_cmt_priv *p = platform_get_drvdata(pdev); >>> > - struct sh_timer_config *cfg = pdev->dev.platform_data; >>> > + struct sh_timer_config *cfg; >>> > int ret; >>> > >>> > if (!is_early_platform_device(pdev)) { >>> > @@ -785,6 +862,11 @@ static int sh_cmt_probe(struct platform_device *pdev) >>> > pm_runtime_enable(&pdev->dev); >>> > } >>> > >>> > + if (pdev->dev.of_node) >>> > + cfg = sh_cmt_parse_dt(&pdev->dev); >>> > + else >>> > + cfg = pdev->dev.platform_data; >>> > + >>> > if (p) { >>> > dev_info(&pdev->dev, "kept as earlytimer\n"); >>> > goto out; >>> > @@ -796,7 +878,7 @@ static int sh_cmt_probe(struct platform_device *pdev) >>> > return -ENOMEM; >>> > } >>> > >>> > - ret = sh_cmt_setup(p, pdev); >>> > + ret = sh_cmt_setup(p, pdev, cfg); >>> > if (ret) { >>> > kfree(p); >>> > pm_runtime_idle(&pdev->dev); >>> > @@ -824,6 +906,7 @@ static struct platform_driver sh_cmt_device_driver = { >>> > .remove = sh_cmt_remove, >>> > .driver = { >>> > .name = "sh_cmt", >>> > + .of_match_table = of_match_ptr(of_sh_cmt_match), >>> > } >>> > }; >>> > >>> > -- >>> > 1.7.9.5 >>> > >>> > -- >>> > To unsubscribe from this list: send the line "unsubscribe linux-sh" in >>> > the body of a message to majordomo@vger.kernel.org >>> > More majordomo info at http://vger.kernel.org/majordomo-info.html >>> > >>> -- >>> To unsubscribe from this list: send the line "unsubscribe linux-sh" in >>> the body of a message to majordomo@vger.kernel.org >>> More majordomo info at http://vger.kernel.org/majordomo-info.html >>> -- To unsubscribe from this list: send the line "unsubscribe linux-sh" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
diff --git a/drivers/clocksource/sh_cmt.c b/drivers/clocksource/sh_cmt.c index 08d0c41..79bc526 100644 --- a/drivers/clocksource/sh_cmt.c +++ b/drivers/clocksource/sh_cmt.c @@ -34,10 +34,13 @@ #include <linux/module.h> #include <linux/pm_domain.h> #include <linux/pm_runtime.h> +#include <linux/of.h> struct sh_cmt_priv { void __iomem *mapbase; struct clk *clk; + long channel_offset; + int timer_bit; unsigned long width; /* 16 or 32 bit version of hardware block */ unsigned long overflow_bit; unsigned long clear_bits; @@ -109,9 +112,7 @@ static void sh_cmt_write32(void __iomem *base, unsigned long offs, static inline unsigned long sh_cmt_read_cmstr(struct sh_cmt_priv *p) { - struct sh_timer_config *cfg = p->pdev->dev.platform_data; - - return p->read_control(p->mapbase - cfg->channel_offset, 0); + return p->read_control(p->mapbase - p->channel_offset, 0); } static inline unsigned long sh_cmt_read_cmcsr(struct sh_cmt_priv *p) @@ -127,9 +128,7 @@ static inline unsigned long sh_cmt_read_cmcnt(struct sh_cmt_priv *p) static inline void sh_cmt_write_cmstr(struct sh_cmt_priv *p, unsigned long value) { - struct sh_timer_config *cfg = p->pdev->dev.platform_data; - - p->write_control(p->mapbase - cfg->channel_offset, 0, value); + p->write_control(p->mapbase - p->channel_offset, 0, value); } static inline void sh_cmt_write_cmcsr(struct sh_cmt_priv *p, @@ -176,7 +175,6 @@ static DEFINE_RAW_SPINLOCK(sh_cmt_lock); static void sh_cmt_start_stop_ch(struct sh_cmt_priv *p, int start) { - struct sh_timer_config *cfg = p->pdev->dev.platform_data; unsigned long flags, value; /* start stop register shared by multiple timer channels */ @@ -184,9 +182,9 @@ static void sh_cmt_start_stop_ch(struct sh_cmt_priv *p, int start) value = sh_cmt_read_cmstr(p); if (start) - value |= 1 << cfg->timer_bit; + value |= 1 << p->timer_bit; else - value &= ~(1 << cfg->timer_bit); + value &= ~(1 << p->timer_bit); sh_cmt_write_cmstr(p, value); raw_spin_unlock_irqrestore(&sh_cmt_lock, flags); @@ -673,9 +671,85 @@ static int sh_cmt_register(struct sh_cmt_priv *p, char *name, return 0; } -static int sh_cmt_setup(struct sh_cmt_priv *p, struct platform_device *pdev) +static const struct of_device_id of_sh_cmt_match[] = { + { .compatible = "renesas,cmt-timer" }, + {}, +}; +MODULE_DEVICE_TABLE(of, of_sh_cmt_match); + +static const int sh_cmt_offset_multiplier[] = { 0x60, 0x10, 0x40, 0x40, 0x40 }; +#define CMT_MAX_CHANNELS 6 + +static struct sh_timer_config *sh_cmt_parse_dt(struct device *dev) +{ + struct sh_timer_config *cfg; + struct device_node *np = dev->of_node; + u32 timer_id, channel_id, rating; + + if (!IS_ENABLED(CONFIG_OF) || !np) + return NULL; + + cfg = devm_kzalloc(dev, sizeof(struct sh_timer_config), GFP_KERNEL); + if (!cfg) { + dev_err(dev, "failed to allocate DT config data\n"); + return NULL; + } + + if (of_property_read_u32(np, "renesas,device-id", &timer_id)) { + dev_err(dev, "device id missing\n"); + return NULL; + } + if (timer_id >= ARRAY_SIZE(sh_cmt_offset_multiplier)) { + dev_err(dev, "invalid device id\n"); + return NULL; + } + + if (of_property_read_u32(np, "renesas,channel-id", &channel_id)) { + dev_err(dev, "channel id missing\n"); + return NULL; + } + if (channel_id >= CMT_MAX_CHANNELS) { + dev_err(dev, "invalid channel id\n"); + return NULL; + } + + cfg->channel_offset = sh_cmt_offset_multiplier[timer_id] * + (channel_id + 1); + cfg->timer_bit = channel_id; + + /* + * We convert the {source,event}-quality DT properties to linux specific + * clock{source,event}_ratings. + */ + if (!of_property_read_u32(np, "renesas,source-quality", &rating)) { + if (rating > 10) { + dev_err(dev, "invalid source-quality\n"); + return NULL; + } + if (rating) + cfg->clocksource_rating = rating * 50 - 1; + } + + if (!of_property_read_u32(np, "renesas,event-quality", &rating)) { + if (rating > 10) { + dev_err(dev, "invalid event-quality\n"); + return NULL; + } + if (rating) + cfg->clockevent_rating = rating * 50 - 1; + } + + if (!cfg->clocksource_rating && !cfg->clockevent_rating) { + dev_err(dev, "source- and event-quality 0, timer is unused\n"); + return NULL; + } + + return cfg; +} + +static int sh_cmt_setup(struct sh_cmt_priv *p, struct platform_device *pdev, + struct sh_timer_config *cfg) { - struct sh_timer_config *cfg = pdev->dev.platform_data; struct resource *res; int irq, ret; ret = -ENXIO; @@ -762,6 +836,9 @@ static int sh_cmt_setup(struct sh_cmt_priv *p, struct platform_device *pdev) goto err2; } + p->channel_offset = cfg->channel_offset; + p->timer_bit = cfg->timer_bit; + platform_set_drvdata(pdev, p); return 0; @@ -777,7 +854,7 @@ err0: static int sh_cmt_probe(struct platform_device *pdev) { struct sh_cmt_priv *p = platform_get_drvdata(pdev); - struct sh_timer_config *cfg = pdev->dev.platform_data; + struct sh_timer_config *cfg; int ret; if (!is_early_platform_device(pdev)) { @@ -785,6 +862,11 @@ static int sh_cmt_probe(struct platform_device *pdev) pm_runtime_enable(&pdev->dev); } + if (pdev->dev.of_node) + cfg = sh_cmt_parse_dt(&pdev->dev); + else + cfg = pdev->dev.platform_data; + if (p) { dev_info(&pdev->dev, "kept as earlytimer\n"); goto out; @@ -796,7 +878,7 @@ static int sh_cmt_probe(struct platform_device *pdev) return -ENOMEM; } - ret = sh_cmt_setup(p, pdev); + ret = sh_cmt_setup(p, pdev, cfg); if (ret) { kfree(p); pm_runtime_idle(&pdev->dev); @@ -824,6 +906,7 @@ static struct platform_driver sh_cmt_device_driver = { .remove = sh_cmt_remove, .driver = { .name = "sh_cmt", + .of_match_table = of_match_ptr(of_sh_cmt_match), } };
We add the capabilty to probe SH CMT timer devices using Device Tree setup. Signed-off-by: Bastian Hecht <hechtb+renesas@gmail.com> --- v3: same (only patch 0003 changed) drivers/clocksource/sh_cmt.c | 109 +++++++++++++++++++++++++++++++++++++----- 1 file changed, 96 insertions(+), 13 deletions(-)