Re: [PATCH 6/6] HSI: omap-ssi: add clk change support
On Sat 2016-04-30 04:09:13, Sebastian Reichel wrote: > This adds support for frequency changes of the SSI > functional clock, which may occur due to DVFS. > > Signed-off-By: Sebastian ReichelAcked-by: Pavel Machek -- (english) http://www.livejournal.com/~pavelmachek (cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html
Re: [PATCH 6/6] HSI: omap-ssi: add clk change support
On Sat 2016-04-30 04:09:13, Sebastian Reichel wrote: > This adds support for frequency changes of the SSI > functional clock, which may occur due to DVFS. > > Signed-off-By: Sebastian Reichel Acked-by: Pavel Machek -- (english) http://www.livejournal.com/~pavelmachek (cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html
[PATCH 6/6] HSI: omap-ssi: add clk change support
This adds support for frequency changes of the SSI functional clock, which may occur due to DVFS. Signed-off-By: Sebastian Reichel--- drivers/hsi/controllers/omap_ssi.h | 6 drivers/hsi/controllers/omap_ssi_core.c | 63 + drivers/hsi/controllers/omap_ssi_port.c | 20 +++ 3 files changed, 89 insertions(+) diff --git a/drivers/hsi/controllers/omap_ssi.h b/drivers/hsi/controllers/omap_ssi.h index e493321cb0c3..7b4dec2c69ff 100644 --- a/drivers/hsi/controllers/omap_ssi.h +++ b/drivers/hsi/controllers/omap_ssi.h @@ -134,6 +134,8 @@ struct gdd_trn { * @gdd_tasklet: bottom half for DMA transfers * @gdd_trn: Array of GDD transaction data for ongoing GDD transfers * @lock: lock to serialize access to GDD + * @fck_nb: DVFS notfifier block + * @fck_rate: clock rate * @loss_count: To follow if we need to restore context or not * @max_speed: Maximum TX speed (Kb/s) set by the clients. * @sysconfig: SSI controller saved context @@ -151,6 +153,7 @@ struct omap_ssi_controller { struct tasklet_struct gdd_tasklet; struct gdd_trn gdd_trn[SSI_MAX_GDD_LCH]; spinlock_t lock; + struct notifier_block fck_nb; unsigned long fck_rate; u32 loss_count; u32 max_speed; @@ -164,6 +167,9 @@ struct omap_ssi_controller { #endif }; +void omap_ssi_port_update_fclk(struct hsi_controller *ssi, + struct omap_ssi_port *omap_port); + extern struct platform_driver ssi_port_pdriver; #endif /* __LINUX_HSI_OMAP_SSI_H__ */ diff --git a/drivers/hsi/controllers/omap_ssi_core.c b/drivers/hsi/controllers/omap_ssi_core.c index 535c76038288..15b2a600d77b 100644 --- a/drivers/hsi/controllers/omap_ssi_core.c +++ b/drivers/hsi/controllers/omap_ssi_core.c @@ -290,6 +290,64 @@ static unsigned long ssi_get_clk_rate(struct hsi_controller *ssi) return rate; } +static int ssi_clk_event(struct notifier_block *nb, unsigned long event, + void *data) +{ + struct omap_ssi_controller *omap_ssi = container_of(nb, + struct omap_ssi_controller, fck_nb); + struct hsi_controller *ssi = to_hsi_controller(omap_ssi->dev); + struct clk_notifier_data *clk_data = data; + struct omap_ssi_port *omap_port; + int i; + + switch (event) { + case PRE_RATE_CHANGE: + dev_dbg(>device, "pre rate change\n"); + + for (i = 0; i < ssi->num_ports; i++) { + omap_port = omap_ssi->port[i]; + + if (!omap_port) + continue; + + /* Workaround for SWBREAK + CAwake down race in CMT */ + tasklet_disable(_port->wake_tasklet); + + /* stop all ssi communication */ + pinctrl_pm_select_idle_state(omap_port->pdev); + udelay(1); /* wait for racing frames */ + } + + break; + case ABORT_RATE_CHANGE: + dev_dbg(>device, "abort rate change\n"); + /* Fall through */ + case POST_RATE_CHANGE: + dev_dbg(>device, "post rate change (%lu -> %lu)\n", + clk_data->old_rate, clk_data->new_rate); + omap_ssi->fck_rate = DIV_ROUND_CLOSEST(clk_data->new_rate, 1000); /* KHz */ + + for (i = 0; i < ssi->num_ports; i++) { + omap_port = omap_ssi->port[i]; + + if (!omap_port) + continue; + + omap_ssi_port_update_fclk(ssi, omap_port); + + /* resume ssi communication */ + pinctrl_pm_select_default_state(omap_port->pdev); + tasklet_enable(_port->wake_tasklet); + } + + break; + default: + break; + } + + return NOTIFY_DONE; +} + static int ssi_get_iomem(struct platform_device *pd, const char *name, void __iomem **pbase, dma_addr_t *phy) { @@ -369,6 +427,10 @@ static int ssi_add_controller(struct hsi_controller *ssi, goto out_err; } + omap_ssi->fck_nb.notifier_call = ssi_clk_event; + omap_ssi->fck_nb.priority = INT_MAX; + clk_notifier_register(omap_ssi->fck, _ssi->fck_nb); + /* TODO: find register, which can be used to detect context loss */ omap_ssi->get_loss = NULL; @@ -432,6 +494,7 @@ static void ssi_remove_controller(struct hsi_controller *ssi) int id = ssi->id; tasklet_kill(_ssi->gdd_tasklet); hsi_unregister_controller(ssi); + clk_notifier_unregister(omap_ssi->fck, _ssi->fck_nb); ida_simple_remove(_omap_ssi_ida, id); } diff --git
[PATCH 6/6] HSI: omap-ssi: add clk change support
This adds support for frequency changes of the SSI functional clock, which may occur due to DVFS. Signed-off-By: Sebastian Reichel --- drivers/hsi/controllers/omap_ssi.h | 6 drivers/hsi/controllers/omap_ssi_core.c | 63 + drivers/hsi/controllers/omap_ssi_port.c | 20 +++ 3 files changed, 89 insertions(+) diff --git a/drivers/hsi/controllers/omap_ssi.h b/drivers/hsi/controllers/omap_ssi.h index e493321cb0c3..7b4dec2c69ff 100644 --- a/drivers/hsi/controllers/omap_ssi.h +++ b/drivers/hsi/controllers/omap_ssi.h @@ -134,6 +134,8 @@ struct gdd_trn { * @gdd_tasklet: bottom half for DMA transfers * @gdd_trn: Array of GDD transaction data for ongoing GDD transfers * @lock: lock to serialize access to GDD + * @fck_nb: DVFS notfifier block + * @fck_rate: clock rate * @loss_count: To follow if we need to restore context or not * @max_speed: Maximum TX speed (Kb/s) set by the clients. * @sysconfig: SSI controller saved context @@ -151,6 +153,7 @@ struct omap_ssi_controller { struct tasklet_struct gdd_tasklet; struct gdd_trn gdd_trn[SSI_MAX_GDD_LCH]; spinlock_t lock; + struct notifier_block fck_nb; unsigned long fck_rate; u32 loss_count; u32 max_speed; @@ -164,6 +167,9 @@ struct omap_ssi_controller { #endif }; +void omap_ssi_port_update_fclk(struct hsi_controller *ssi, + struct omap_ssi_port *omap_port); + extern struct platform_driver ssi_port_pdriver; #endif /* __LINUX_HSI_OMAP_SSI_H__ */ diff --git a/drivers/hsi/controllers/omap_ssi_core.c b/drivers/hsi/controllers/omap_ssi_core.c index 535c76038288..15b2a600d77b 100644 --- a/drivers/hsi/controllers/omap_ssi_core.c +++ b/drivers/hsi/controllers/omap_ssi_core.c @@ -290,6 +290,64 @@ static unsigned long ssi_get_clk_rate(struct hsi_controller *ssi) return rate; } +static int ssi_clk_event(struct notifier_block *nb, unsigned long event, + void *data) +{ + struct omap_ssi_controller *omap_ssi = container_of(nb, + struct omap_ssi_controller, fck_nb); + struct hsi_controller *ssi = to_hsi_controller(omap_ssi->dev); + struct clk_notifier_data *clk_data = data; + struct omap_ssi_port *omap_port; + int i; + + switch (event) { + case PRE_RATE_CHANGE: + dev_dbg(>device, "pre rate change\n"); + + for (i = 0; i < ssi->num_ports; i++) { + omap_port = omap_ssi->port[i]; + + if (!omap_port) + continue; + + /* Workaround for SWBREAK + CAwake down race in CMT */ + tasklet_disable(_port->wake_tasklet); + + /* stop all ssi communication */ + pinctrl_pm_select_idle_state(omap_port->pdev); + udelay(1); /* wait for racing frames */ + } + + break; + case ABORT_RATE_CHANGE: + dev_dbg(>device, "abort rate change\n"); + /* Fall through */ + case POST_RATE_CHANGE: + dev_dbg(>device, "post rate change (%lu -> %lu)\n", + clk_data->old_rate, clk_data->new_rate); + omap_ssi->fck_rate = DIV_ROUND_CLOSEST(clk_data->new_rate, 1000); /* KHz */ + + for (i = 0; i < ssi->num_ports; i++) { + omap_port = omap_ssi->port[i]; + + if (!omap_port) + continue; + + omap_ssi_port_update_fclk(ssi, omap_port); + + /* resume ssi communication */ + pinctrl_pm_select_default_state(omap_port->pdev); + tasklet_enable(_port->wake_tasklet); + } + + break; + default: + break; + } + + return NOTIFY_DONE; +} + static int ssi_get_iomem(struct platform_device *pd, const char *name, void __iomem **pbase, dma_addr_t *phy) { @@ -369,6 +427,10 @@ static int ssi_add_controller(struct hsi_controller *ssi, goto out_err; } + omap_ssi->fck_nb.notifier_call = ssi_clk_event; + omap_ssi->fck_nb.priority = INT_MAX; + clk_notifier_register(omap_ssi->fck, _ssi->fck_nb); + /* TODO: find register, which can be used to detect context loss */ omap_ssi->get_loss = NULL; @@ -432,6 +494,7 @@ static void ssi_remove_controller(struct hsi_controller *ssi) int id = ssi->id; tasklet_kill(_ssi->gdd_tasklet); hsi_unregister_controller(ssi); + clk_notifier_unregister(omap_ssi->fck, _ssi->fck_nb); ida_simple_remove(_omap_ssi_ida, id); } diff --git a/drivers/hsi/controllers/omap_ssi_port.c