aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/omap_hsi/hsi_driver_if.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/omap_hsi/hsi_driver_if.c')
-rw-r--r--drivers/omap_hsi/hsi_driver_if.c149
1 files changed, 110 insertions, 39 deletions
diff --git a/drivers/omap_hsi/hsi_driver_if.c b/drivers/omap_hsi/hsi_driver_if.c
index 19012e5..79e05cc 100644
--- a/drivers/omap_hsi/hsi_driver_if.c
+++ b/drivers/omap_hsi/hsi_driver_if.c
@@ -50,7 +50,6 @@ int hsi_set_rx_divisor(struct hsi_port *sport, struct hsr_ctx *cfg)
} else if (cfg->divisor != HSI_HSR_DIVISOR_AUTO) {
/* Divisor set mode: use counters */
/* Leave auto mode: use new counters values */
- cfg->counters = 0xFFFFF;
sport->reg_counters = cfg->counters;
sport->counters_on = 1;
hsi_outl(cfg->counters, base,
@@ -260,6 +259,7 @@ int hsi_open(struct hsi_device *dev)
struct hsi_channel *ch;
struct hsi_port *port;
struct hsi_dev *hsi_ctrl;
+ int err;
if (!dev || !dev->ch) {
pr_err(LOG_NAME "Wrong HSI device %p\n", dev);
@@ -274,26 +274,69 @@ int hsi_open(struct hsi_device *dev)
"registered\n");
return -EINVAL;
}
- port = ch->hsi_port;
- hsi_ctrl = port->hsi_controller;
-
- spin_lock_bh(&hsi_ctrl->lock);
- hsi_clocks_enable_channel(dev->device.parent, ch->channel_number,
- __func__);
-
if (ch->flags & HSI_CH_OPEN) {
dev_err(dev->device.parent,
"Port %d Channel %d already OPENED\n",
dev->n_p, dev->n_ch);
- spin_unlock_bh(&hsi_ctrl->lock);
return -EBUSY;
}
+ port = ch->hsi_port;
+ hsi_ctrl = port->hsi_controller;
+ if (!hsi_ctrl) {
+ dev_err(dev->device.parent,
+ "%s: Port %d Channel %d has no hsi controller?\n",
+ __func__, dev->n_p, dev->n_ch);
+ return -EINVAL;
+ }
+
+ if (hsi_ctrl->clock_rate == 0) {
+ struct hsi_platform_data *pdata;
+
+ pdata = dev_get_platdata(hsi_ctrl->dev);
+ if (!pdata) {
+ dev_err(dev->device.parent,
+ "%s: Port %d Channel %d has no pdata\n",
+ __func__, dev->n_p, dev->n_ch);
+ return -EINVAL;
+ }
+ if (!pdata->device_scale) {
+ dev_err(dev->device.parent,
+ "%s: Undefined platform device_scale function\n",
+ __func__);
+ return -ENXIO;
+ }
+
+ /* Retry to set the HSI FCLK to default. */
+ err = pdata->device_scale(hsi_ctrl->dev, hsi_ctrl->dev,
+ pdata->default_hsi_fclk);
+ if (err) {
+ dev_err(dev->device.parent,
+ "%s: Error %d setting HSI FClk to %ld. "
+ "Will retry on next open\n",
+ __func__, err, pdata->default_hsi_fclk);
+ return err;
+ } else {
+ dev_info(dev->device.parent, "HSI clock is now %ld\n",
+ pdata->default_hsi_fclk);
+ hsi_ctrl->clock_rate = pdata->default_hsi_fclk;
+ }
+ }
+ spin_lock_bh(&hsi_ctrl->lock);
+ hsi_clocks_enable_channel(dev->device.parent, ch->channel_number,
+ __func__);
+
/* Restart with flags cleaned up */
ch->flags = HSI_CH_OPEN;
- hsi_driver_enable_interrupt(port, HSI_CAWAKEDETECTED | HSI_ERROROCCURED
- | HSI_BREAKDETECTED);
+ if (port->wake_rx_3_wires_mode)
+ hsi_driver_enable_interrupt(port, HSI_ERROROCCURED
+ | HSI_BREAKDETECTED);
+ else
+ hsi_driver_enable_interrupt(port, HSI_CAWAKEDETECTED
+ | HSI_ERROROCCURED
+ | HSI_BREAKDETECTED);
+
/* NOTE: error and break are port events and do not need to be
* enabled for HSI extended enable register */
@@ -312,7 +355,7 @@ EXPORT_SYMBOL(hsi_open);
* @addr - pointer to a 32-bit word data to be written.
* @size - number of 32-bit word to be written.
*
- * Return 0 on sucess, a negative value on failure.
+ * Return 0 on success, a negative value on failure.
* A success value only indicates that the request has been accepted.
* Transfer is only completed when the write_done callback is called.
*
@@ -342,6 +385,13 @@ int hsi_write(struct hsi_device *dev, u32 *addr, unsigned int size)
}
ch = dev->ch;
+ if (ch->write_data.addr != NULL) {
+ dev_err(dev->device.parent, "# Invalid request - Write "
+ "operation pending port %d channel %d\n",
+ ch->hsi_port->port_number,
+ ch->channel_number);
+ return -EINVAL;
+ }
spin_lock_bh(&ch->hsi_port->hsi_controller->lock);
if (pm_runtime_suspended(dev->device.parent) ||
@@ -353,18 +403,6 @@ int hsi_write(struct hsi_device *dev, u32 *addr, unsigned int size)
hsi_clocks_enable_channel(dev->device.parent,
ch->channel_number, __func__);
- if (ch->write_data.addr != NULL) {
- dev_err(dev->device.parent, "# Invalid request - Write "
- "operation pending port %d channel %d\n",
- ch->hsi_port->port_number,
- ch->channel_number);
-
- hsi_clocks_disable_channel(dev->device.parent,
- ch->channel_number, __func__);
- spin_unlock_bh(&ch->hsi_port->hsi_controller->lock);
- return -EINVAL;
- }
-
ch->write_data.addr = addr;
ch->write_data.size = size;
ch->write_data.lch = -1;
@@ -669,7 +707,7 @@ EXPORT_SYMBOL(hsi_unpoll);
* @command - HSI I/O control command
* @arg - parameter associated to the control command. NULL, if no parameter.
*
- * Return 0 on sucess, a negative value on failure.
+ * Return 0 on success, a negative value on failure.
*
*/
int hsi_ioctl(struct hsi_device *dev, unsigned int command, void *arg)
@@ -707,15 +745,14 @@ int hsi_ioctl(struct hsi_device *dev, unsigned int command, void *arg)
switch (command) {
case HSI_IOCTL_ACWAKE_UP:
+ /* Wake up request to Modem (typically OMAP initiated) */
+ /* Symetrical disable will be done in HSI_IOCTL_ACWAKE_DOWN */
if (ch->flags & HSI_CH_ACWAKE) {
dev_dbg(dev->device.parent, "Duplicate ACWAKE UP\n");
err = -EPERM;
goto out;
}
- /* Wake up request to Modem (typically OMAP initiated) */
- /* Symetrical disable will be done in HSI_IOCTL_ACWAKE_DOWN */
-
ch->flags |= HSI_CH_ACWAKE;
pport->acwake_status |= BIT(channel);
@@ -839,16 +876,45 @@ int hsi_ioctl(struct hsi_device *dev, unsigned int command, void *arg)
}
*(size_t *)arg = hsi_get_rx_fifo_occupancy(hsi_ctrl, fifo);
break;
- case HSI_IOCTL_SET_ACREADY_SAFEMODE:
- omap_writel(omap_readl(0x4A1000C8) | 0x7, 0x4A1000C8);
- break;
- case HSI_IOCTL_SET_ACREADY_NORMAL:
- omap_writel(omap_readl(0x4A1000C8) & 0xFFFFFFF9, 0x4A1000C8);
- case HSI_IOCTL_SET_3WIRE_MODE:
- omap_writel(0x30000, 0x4A058C08);
+ case HSI_IOCTL_SET_WAKE_RX_3WIRES_MODE:
+ dev_info(dev->device.parent,
+ "Entering RX wakeup in 3 wires mode (no CAWAKE)\n");
+ pport->wake_rx_3_wires_mode = 1;
+
+ /* HSI-C1BUG00085: ixxx: HSI wakeup issue in 3 wires mode
+ * HSI will NOT generate the Swakeup for 2nd frame if it entered
+ * IDLE after 1st received frame */
+ if (is_hsi_errata(hsi_ctrl, HSI_ERRATUM_ixxx_3WIRES_NO_SWAKEUP))
+ if (hsi_driver_device_is_hsi(to_platform_device
+ (hsi_ctrl->dev)))
+ hsi_set_pm_force_hsi_on(hsi_ctrl);
+
+ /* When WAKE is not available, ACREADY must be set to 1 at
+ * reset else remote will never have a chance to transmit. */
+ hsi_outl_or(HSI_SET_WAKE_3_WIRES | HSI_SET_WAKE_READY_LVL_1,
+ base, HSI_SYS_SET_WAKE_REG(port));
+ hsi_driver_disable_interrupt(pport, HSI_CAWAKEDETECTED);
break;
- case HSI_IOCTL_SET_4WIRE_MODE:
- omap_writel((omap_readl(0x4A058C08) & 0xFFFF), 0x4A058C08);
+ case HSI_IOCTL_SET_WAKE_RX_4WIRES_MODE:
+ dev_info(dev->device.parent,
+ "Entering RX wakeup in 4 wires mode\n");
+ pport->wake_rx_3_wires_mode = 0;
+
+ /* HSI-C1BUG00085: ixxx: HSI wakeup issue in 3 wires mode
+ * HSI will NOT generate the Swakeup for 2nd frame if it entered
+ * IDLE after 1st received frame */
+ if (is_hsi_errata(hsi_ctrl, HSI_ERRATUM_ixxx_3WIRES_NO_SWAKEUP))
+ if (hsi_driver_device_is_hsi(to_platform_device
+ (hsi_ctrl->dev)))
+ hsi_set_pm_default(hsi_ctrl);
+
+ /* Clean CA_WAKE status */
+ pport->cawake_status = -1;
+ hsi_outl(HSI_CAWAKEDETECTED, base,
+ HSI_SYS_MPU_STATUS_REG(port, pport->n_irq));
+ hsi_driver_enable_interrupt(pport, HSI_CAWAKEDETECTED);
+ hsi_outl_and(HSI_SET_WAKE_3_WIRES_MASK, base,
+ HSI_SYS_SET_WAKE_REG(port));
break;
default:
err = -ENOIOCTLCMD;
@@ -956,8 +1022,13 @@ void hsi_set_port_event_cb(struct hsi_device *dev,
spin_lock_bh(&hsi_ctrl->lock);
hsi_clocks_enable_channel(dev->device.parent, dev->ch->channel_number,
__func__);
- hsi_driver_enable_interrupt(port, HSI_CAWAKEDETECTED | HSI_ERROROCCURED
- | HSI_BREAKDETECTED);
+ if (port->wake_rx_3_wires_mode)
+ hsi_driver_enable_interrupt(port, HSI_ERROROCCURED
+ | HSI_BREAKDETECTED);
+ else
+ hsi_driver_enable_interrupt(port, HSI_CAWAKEDETECTED
+ | HSI_ERROROCCURED
+ | HSI_BREAKDETECTED);
hsi_clocks_disable_channel(dev->device.parent, dev->ch->channel_number,
__func__);
spin_unlock_bh(&hsi_ctrl->lock);