diff options
Diffstat (limited to 'drivers/omap_hsi/hsi_driver.c')
-rw-r--r-- | drivers/omap_hsi/hsi_driver.c | 123 |
1 files changed, 88 insertions, 35 deletions
diff --git a/drivers/omap_hsi/hsi_driver.c b/drivers/omap_hsi/hsi_driver.c index bb07c10..73353ea 100644 --- a/drivers/omap_hsi/hsi_driver.c +++ b/drivers/omap_hsi/hsi_driver.c @@ -41,7 +41,7 @@ static struct pm_qos_request_list *pm_qos_handle; #endif #define HSI_MODULENAME "omap_hsi" -#define HSI_DRIVER_VERSION "0.4.1" +#define HSI_DRIVER_VERSION "0.4.2" #define HSI_RESETDONE_MAX_RETRIES 5 /* Max 5*L4 Read cycles waiting for */ /* reset to complete */ #define HSI_RESETDONE_NORMAL_RETRIES 1 /* Reset should complete in 1 R/W */ @@ -51,7 +51,7 @@ void hsi_save_ctx(struct hsi_dev *hsi_ctrl) struct hsi_platform_data *pdata = hsi_ctrl->dev->platform_data; struct platform_device *pdev = to_platform_device(hsi_ctrl->dev); void __iomem *base = hsi_ctrl->base; - struct port_ctx *p; + struct hsi_port_ctx *p; int port; pdata->ctx->sysconfig = hsi_inl(base, HSI_SYS_SYSCONFIG_REG); @@ -92,7 +92,7 @@ void hsi_restore_ctx(struct hsi_dev *hsi_ctrl) struct hsi_platform_data *pdata = hsi_ctrl->dev->platform_data; struct platform_device *pdev = to_platform_device(hsi_ctrl->dev); void __iomem *base = hsi_ctrl->base; - struct port_ctx *p; + struct hsi_port_ctx *p; int port; hsi_outl(pdata->ctx->sysconfig, base, HSI_SYS_SYSCONFIG_REG); @@ -128,7 +128,7 @@ void hsi_restore_ctx(struct hsi_dev *hsi_ctrl) if (hsi_driver_device_is_hsi(pdev)) { /* SW strategy for HSI fifo management can be changed here */ - hsi_fifo_mapping(hsi_ctrl, HSI_FIFO_MAPPING_DEFAULT); + hsi_fifo_mapping(hsi_ctrl, hsi_ctrl->fifo_mapping_strategy); } /* As a last step move HSR from MODE_VAL.SLEEP to the relevant mode. */ @@ -275,12 +275,27 @@ void hsi_set_pm_force_hsi_on(struct hsi_dev *hsi_ctrl) /* HSI_TODO : use the HWMOD API : omap_hwmod_set_slave_idlemode() */ } +/** +* hsi_softreset - Force a SW RESET of HSI (core + DMA) +* +* @hsi_ctrl - reference to the hsi controller to be reset. +* +*/ int hsi_softreset(struct hsi_dev *hsi_ctrl) { unsigned int ind = 0; + unsigned int port; void __iomem *base = hsi_ctrl->base; u32 status; + /* SW WA for HSI-C1BUG00088 OMAP4430 HSI : No recovery from SW reset */ + /* under specific circumstances */ + for (port = 1; port <= hsi_ctrl->max_p; port++) { + hsi_outl_and(HSI_HSR_MODE_MODE_VAL_SLEEP, base, + HSI_HSR_MODE_REG(port)); + hsi_outl(HSI_HSR_ERROR_ALL, base, HSI_HSR_ERRORACK_REG(port)); + } + /* Reseting HSI Block */ hsi_outl_or(HSI_SOFTRESET, base, HSI_SYS_SYSCONFIG_REG); do { @@ -324,7 +339,7 @@ int hsi_softreset(struct hsi_dev *hsi_ctrl) static void hsi_set_ports_default(struct hsi_dev *hsi_ctrl, struct platform_device *pd) { - struct port_ctx *cfg; + struct hsi_port_ctx *cfg; struct hsi_platform_data *pdata = pd->dev.platform_data; unsigned int port = 0; void __iomem *base = hsi_ctrl->base; @@ -358,7 +373,7 @@ static void hsi_set_ports_default(struct hsi_dev *hsi_ctrl, if (hsi_driver_device_is_hsi(pdev)) { /* SW strategy for HSI fifo management can be changed here */ - hsi_fifo_mapping(hsi_ctrl, HSI_FIFO_MAPPING_DEFAULT); + hsi_fifo_mapping(hsi_ctrl, hsi_ctrl->fifo_mapping_strategy); hsi_outl(pdata->ctx->dll, base, HSI_HSR_DLL_REG); } } @@ -408,6 +423,13 @@ static int hsi_port_channels_reset(struct hsi_port *port) return 0; } +/** +* hsi_softreset_driver - Must be called following HSI SW RESET, to re-align +* variable states with new HW state. +* +* @hsi_ctrl - reference to the hsi controller to be re-aligned. +* +*/ void hsi_softreset_driver(struct hsi_dev *hsi_ctrl) { struct platform_device *pd = to_platform_device(hsi_ctrl->dev); @@ -424,7 +446,7 @@ void hsi_softreset_driver(struct hsi_dev *hsi_ctrl) hsi_port_channels_reset(&hsi_ctrl->hsi_port[port]); } - hsi_set_pm_default(hsi_ctrl); + hsi_set_pm_force_hsi_on(hsi_ctrl); /* Re-Configure HSI ports */ hsi_set_ports_default(hsi_ctrl, pd); @@ -516,7 +538,8 @@ static int __init hsi_ports_init(struct hsi_dev *hsi_ctrl) for (port = 0; port < hsi_ctrl->max_p; port++) { hsi_p = &hsi_ctrl->hsi_port[port]; - hsi_p->port_number = port + 1; + hsi_p->flags = 0; + hsi_p->port_number = pdata->ctx->pctx[port].port_number; hsi_p->hsi_controller = hsi_ctrl; hsi_p->max_ch = hsi_driver_device_is_hsi(pd) ? HSI_CHANNELS_MAX : HSI_SSI_CHANNELS_MAX; @@ -529,22 +552,21 @@ static int __init hsi_ports_init(struct hsi_dev *hsi_ctrl) hsi_p->counters_on = 1; hsi_p->reg_counters = pdata->ctx->pctx[port].hsr.counters; spin_lock_init(&hsi_p->lock); - err = hsi_port_channels_init(&hsi_ctrl->hsi_port[port]); + err = hsi_port_channels_init(hsi_p); if (err < 0) - goto rback1; + goto rback; err = hsi_request_mpu_irq(hsi_p); if (err < 0) - goto rback2; + goto rback; err = hsi_request_cawake_irq(hsi_p); if (err < 0) - goto rback3; + goto rback; + dev_info(hsi_ctrl->dev, "HSI port %d initialized\n", + hsi_p->port_number); } return 0; -rback3: - hsi_mpu_exit(hsi_p); -rback2: +rback: hsi_ports_exit(hsi_ctrl, port + 1); -rback1: return err; } @@ -636,7 +658,7 @@ void hsi_clocks_disable_channel(struct device *dev, u8 channel_number, } if (hsi_is_hst_controller_busy(hsi_ctrl)) - dev_dbg(dev, "Disabling clocks with HST FSM not IDLE !\n"); + dev_warn(dev, "Disabling clocks with HST FSM not IDLE !\n"); #ifdef K3_0_PORTING_HSI_MISSING_FEATURE /* Allow Fclk to change */ @@ -654,6 +676,9 @@ void hsi_clocks_disable_channel(struct device *dev, u8 channel_number, * @channel_number - channel number which requests clock to be enabled * 0xFF means no particular channel * +* Returns: -EEXIST if clocks were already active +* 0 if clocks were previously inactive +* * Note : there is no real HW clock management per HSI channel, this is only * virtual to keep track of active channels and ease debug * @@ -728,8 +753,10 @@ static int __init hsi_controller_init(struct hsi_dev *hsi_ctrl, return -ENXIO; } hsi_ctrl->max_p = pdata->num_ports; + hsi_ctrl->clock_enabled = false; + hsi_ctrl->clock_rate = 0; hsi_ctrl->in_dma_tasklet = false; - hsi_ctrl->fifo_mapping_strategy = HSI_FIFO_MAPPING_UNDEF; + hsi_ctrl->fifo_mapping_strategy = pdata->fifo_mapping_strategy; hsi_ctrl->dev = &pd->dev; spin_lock_init(&hsi_ctrl->lock); err = hsi_init_gdd_chan_count(hsi_ctrl); @@ -804,7 +831,7 @@ static int __init hsi_platform_device_probe(struct platform_device *pd) if (err < 0) goto rollback2; - hsi_set_pm_default(hsi_ctrl); + hsi_set_pm_force_hsi_on(hsi_ctrl); /* Configure HSI ports */ hsi_set_ports_default(hsi_ctrl, pd); @@ -835,19 +862,31 @@ static int __init hsi_platform_device_probe(struct platform_device *pd) /* Allow HSI to wake up the platform */ device_init_wakeup(hsi_ctrl->dev, true); -#ifdef K3_0_PORTING_HSI_MISSING_FEATURE /* Set the HSI FCLK to default. */ - err = omap_device_set_rate(hsi_ctrl->dev, hsi_ctrl->dev, - pdata->default_hsi_fclk); - if (err) - dev_err(&pd->dev, "Cannot set HSI FClk to default value: %ld\n", + if (!pdata->device_scale) { + dev_err(&pd->dev, "%s: No platform device_scale function\n", + __func__); + err = -ENXIO; + goto rollback3; + } + err = pdata->device_scale(hsi_ctrl->dev, hsi_ctrl->dev, + pdata->default_hsi_fclk); + if (err == -EBUSY) { + dev_warn(&pd->dev, "Cannot set HSI FClk to default value: %ld. " + "Will retry on next open\n", pdata->default_hsi_fclk); -#endif + } else if (err) { + dev_err(&pd->dev, "%s: Error %d setting HSI FClk to %ld.\n", + __func__, err, pdata->default_hsi_fclk); + goto rollback3; + } else { + hsi_ctrl->clock_rate = pdata->default_hsi_fclk; + } /* From here no need for HSI HW access */ hsi_clocks_disable(hsi_ctrl->dev, __func__); - return err; + return 0; rollback3: hsi_debug_remove_ctrl(hsi_ctrl); @@ -890,6 +929,7 @@ static int hsi_suspend_noirq(struct device *dev) struct hsi_platform_data *pdata = dev->platform_data; struct platform_device *pd = to_platform_device(dev); struct hsi_dev *hsi_ctrl = platform_get_drvdata(pd); + unsigned int i; dev_dbg(dev, "%s\n", __func__); @@ -903,7 +943,9 @@ static int hsi_suspend_noirq(struct device *dev) /* Perform HSI board specific action before platform suspend */ if (pdata->board_suspend) - pdata->board_suspend(0, device_may_wakeup(dev)); + for (i = 0; i < hsi_ctrl->max_p; i++) + pdata->board_suspend(hsi_ctrl->hsi_port[i].port_number, + device_may_wakeup(dev)); return 0; } @@ -911,6 +953,9 @@ static int hsi_suspend_noirq(struct device *dev) static int hsi_resume_noirq(struct device *dev) { struct hsi_platform_data *pdata = dev->platform_data; + struct platform_device *pd = to_platform_device(dev); + struct hsi_dev *hsi_ctrl = platform_get_drvdata(pd); + unsigned int i; dev_dbg(dev, "%s\n", __func__); @@ -927,7 +972,8 @@ static int hsi_resume_noirq(struct device *dev) /* Perform (optional) HSI board specific action after platform wakeup */ if (pdata->board_resume) - pdata->board_resume(0); + for (i = 0; i < hsi_ctrl->max_p; i++) + pdata->board_resume(hsi_ctrl->hsi_port[i].port_number); return 0; } @@ -945,6 +991,8 @@ int hsi_runtime_resume(struct device *dev) struct platform_device *pd = to_platform_device(dev); struct hsi_dev *hsi_ctrl = platform_get_drvdata(pd); struct hsi_platform_data *pdata = hsi_ctrl->dev->platform_data; + unsigned int i; + dev_dbg(dev, "%s\n", __func__); if (hsi_ctrl->clock_enabled) @@ -955,8 +1003,9 @@ int hsi_runtime_resume(struct device *dev) /* Restore context */ hsi_restore_ctx(hsi_ctrl); - /* When HSI is ON, no need for IO wakeup mechanism */ - pdata->wakeup_disable(0); + /* When HSI is ON, no need for IO wakeup mechanism on any HSI port */ + for (i = 0; i < hsi_ctrl->max_p; i++) + pdata->wakeup_disable(hsi_ctrl->hsi_port[i].port_number); /* HSI device is now fully operational and _must_ be able to */ /* complete I/O operations */ @@ -977,7 +1026,8 @@ int hsi_runtime_suspend(struct device *dev) struct platform_device *pd = to_platform_device(dev); struct hsi_dev *hsi_ctrl = platform_get_drvdata(pd); struct hsi_platform_data *pdata = hsi_ctrl->dev->platform_data; - int port; + int port, i; + dev_dbg(dev, "%s\n", __func__); if (!hsi_ctrl->clock_enabled) @@ -994,11 +1044,14 @@ int hsi_runtime_suspend(struct device *dev) HSI_HSR_MODE_REG(port)); } - /* HSI is going to INA/RET/OFF, it needs IO wakeup mechanism enabled */ + /* HSI is going to IDLE, it needs IO wakeup mechanism enabled */ if (device_may_wakeup(dev)) - pdata->wakeup_enable(0); + for (i = 0; i < hsi_ctrl->max_p; i++) + pdata->wakeup_enable(hsi_ctrl->hsi_port[i].port_number); else - pdata->wakeup_disable(0); + for (i = 0; i < hsi_ctrl->max_p; i++) + pdata->wakeup_disable( + hsi_ctrl->hsi_port[i].port_number); /* HSI is now ready to be put in low power state */ @@ -1086,7 +1139,7 @@ static int __init hsi_driver_init(void) { int err = 0; - pr_info(LOG_NAME "HSI DRIVER Version " HSI_DRIVER_VERSION "\n"); + pr_info(LOG_NAME "HSI driver version " HSI_DRIVER_VERSION "\n"); /* Register the (virtual) HSI bus */ err = hsi_bus_init(); |