Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

HSI: omap-ssi: add clk change support

This adds support for frequency changes of the SSI
functional clock, which may occur due to DVFS.

Acked-by: Pavel Machek <pavel@ucw.cz>
Signed-off-By: Sebastian Reichel <sre@kernel.org>

+89
+6
drivers/hsi/controllers/omap_ssi.h
··· 134 134 * @gdd_tasklet: bottom half for DMA transfers 135 135 * @gdd_trn: Array of GDD transaction data for ongoing GDD transfers 136 136 * @lock: lock to serialize access to GDD 137 + * @fck_nb: DVFS notfifier block 138 + * @fck_rate: clock rate 137 139 * @loss_count: To follow if we need to restore context or not 138 140 * @max_speed: Maximum TX speed (Kb/s) set by the clients. 139 141 * @sysconfig: SSI controller saved context ··· 153 151 struct tasklet_struct gdd_tasklet; 154 152 struct gdd_trn gdd_trn[SSI_MAX_GDD_LCH]; 155 153 spinlock_t lock; 154 + struct notifier_block fck_nb; 156 155 unsigned long fck_rate; 157 156 u32 loss_count; 158 157 u32 max_speed; ··· 166 163 struct dentry *dir; 167 164 #endif 168 165 }; 166 + 167 + void omap_ssi_port_update_fclk(struct hsi_controller *ssi, 168 + struct omap_ssi_port *omap_port); 169 169 170 170 extern struct platform_driver ssi_port_pdriver; 171 171
+63
drivers/hsi/controllers/omap_ssi_core.c
··· 290 290 return rate; 291 291 } 292 292 293 + static int ssi_clk_event(struct notifier_block *nb, unsigned long event, 294 + void *data) 295 + { 296 + struct omap_ssi_controller *omap_ssi = container_of(nb, 297 + struct omap_ssi_controller, fck_nb); 298 + struct hsi_controller *ssi = to_hsi_controller(omap_ssi->dev); 299 + struct clk_notifier_data *clk_data = data; 300 + struct omap_ssi_port *omap_port; 301 + int i; 302 + 303 + switch (event) { 304 + case PRE_RATE_CHANGE: 305 + dev_dbg(&ssi->device, "pre rate change\n"); 306 + 307 + for (i = 0; i < ssi->num_ports; i++) { 308 + omap_port = omap_ssi->port[i]; 309 + 310 + if (!omap_port) 311 + continue; 312 + 313 + /* Workaround for SWBREAK + CAwake down race in CMT */ 314 + tasklet_disable(&omap_port->wake_tasklet); 315 + 316 + /* stop all ssi communication */ 317 + pinctrl_pm_select_idle_state(omap_port->pdev); 318 + udelay(1); /* wait for racing frames */ 319 + } 320 + 321 + break; 322 + case ABORT_RATE_CHANGE: 323 + dev_dbg(&ssi->device, "abort rate change\n"); 324 + /* Fall through */ 325 + case POST_RATE_CHANGE: 326 + dev_dbg(&ssi->device, "post rate change (%lu -> %lu)\n", 327 + clk_data->old_rate, clk_data->new_rate); 328 + omap_ssi->fck_rate = DIV_ROUND_CLOSEST(clk_data->new_rate, 1000); /* KHz */ 329 + 330 + for (i = 0; i < ssi->num_ports; i++) { 331 + omap_port = omap_ssi->port[i]; 332 + 333 + if (!omap_port) 334 + continue; 335 + 336 + omap_ssi_port_update_fclk(ssi, omap_port); 337 + 338 + /* resume ssi communication */ 339 + pinctrl_pm_select_default_state(omap_port->pdev); 340 + tasklet_enable(&omap_port->wake_tasklet); 341 + } 342 + 343 + break; 344 + default: 345 + break; 346 + } 347 + 348 + return NOTIFY_DONE; 349 + } 350 + 293 351 static int ssi_get_iomem(struct platform_device *pd, 294 352 const char *name, void __iomem **pbase, dma_addr_t *phy) 295 353 { ··· 427 369 goto out_err; 428 370 } 429 371 372 + omap_ssi->fck_nb.notifier_call = ssi_clk_event; 373 + omap_ssi->fck_nb.priority = INT_MAX; 374 + clk_notifier_register(omap_ssi->fck, &omap_ssi->fck_nb); 375 + 430 376 /* TODO: find register, which can be used to detect context loss */ 431 377 omap_ssi->get_loss = NULL; 432 378 ··· 494 432 int id = ssi->id; 495 433 tasklet_kill(&omap_ssi->gdd_tasklet); 496 434 hsi_unregister_controller(ssi); 435 + clk_notifier_unregister(omap_ssi->fck, &omap_ssi->fck_nb); 497 436 ida_simple_remove(&platform_omap_ssi_ida, id); 498 437 } 499 438
+20
drivers/hsi/controllers/omap_ssi_port.c
··· 23 23 #include <linux/platform_device.h> 24 24 #include <linux/dma-mapping.h> 25 25 #include <linux/pm_runtime.h> 26 + #include <linux/delay.h> 26 27 27 28 #include <linux/gpio/consumer.h> 28 29 #include <linux/debugfs.h> ··· 515 514 516 515 pm_runtime_get_sync(omap_port->pdev); 517 516 spin_lock_bh(&omap_port->lock); 517 + 518 + /* stop all ssi communication */ 519 + pinctrl_pm_select_idle_state(omap_port->pdev); 520 + udelay(1); /* wait for racing frames */ 521 + 518 522 /* Stop all DMA transfers */ 519 523 for (i = 0; i < SSI_MAX_GDD_LCH; i++) { 520 524 msg = omap_ssi->gdd_trn[i].msg; ··· 556 550 ssi_flush_queue(&omap_port->rxqueue[i], NULL); 557 551 } 558 552 ssi_flush_queue(&omap_port->brkqueue, NULL); 553 + 554 + /* Resume SSI communication */ 555 + pinctrl_pm_select_default_state(omap_port->pdev); 556 + 559 557 spin_unlock_bh(&omap_port->lock); 560 558 pm_runtime_put_sync(omap_port->pdev); 561 559 ··· 1311 1301 1312 1302 return 0; 1313 1303 } 1304 + 1305 + void omap_ssi_port_update_fclk(struct hsi_controller *ssi, 1306 + struct omap_ssi_port *omap_port) 1307 + { 1308 + /* update divisor */ 1309 + u32 div = ssi_calculate_div(ssi); 1310 + omap_port->sst.divisor = div; 1311 + ssi_restore_divisor(omap_port); 1312 + } 1313 + EXPORT_SYMBOL_GPL(omap_ssi_port_update_fclk); 1314 1314 1315 1315 static int omap_ssi_port_runtime_suspend(struct device *dev) 1316 1316 {