aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/omap_hsi/hsi_driver.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/omap_hsi/hsi_driver.c')
-rw-r--r--drivers/omap_hsi/hsi_driver.c123
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();