static void do_hsi_cawake_tasklet(unsigned long hsi_p) { struct hsi_port *port = (struct hsi_port *)hsi_p; struct hsi_dev *hsi_ctrl = port->hsi_controller; spin_lock(&hsi_ctrl->lock); hsi_clocks_enable(hsi_ctrl->dev, __func__); port->in_cawake_tasklet = true; port->cawake_status = hsi_get_cawake(port); hsi_do_cawake_process(port); port->in_cawake_tasklet = false; hsi_clocks_disable(hsi_ctrl->dev, __func__); spin_unlock(&hsi_ctrl->lock); }
/* Check if a HSI port is busy : * - ACWAKE is high * - data transfer (Write) is ongoing for a given HSI channel * - CAWAKE is high * - CAWAKE is not used (receiver in 3-wires mode) * - Currently in HSI interrupt tasklet * - Currently in HSI CAWAKE tasklet (for SSI) */ bool hsi_is_hsi_port_busy(struct hsi_port *pport) { struct hsi_dev *hsi_ctrl = pport->hsi_controller; bool cur_cawake = hsi_get_cawake(pport); int ch; if (pport->in_int_tasklet) { dev_dbg(hsi_ctrl->dev, "Interrupt tasklet running\n"); return true; } if (pport->in_cawake_tasklet) { dev_dbg(hsi_ctrl->dev, "SSI Cawake tasklet running\n"); return true; } if (pport->wake_rx_3_wires_mode) { dev_dbg(hsi_ctrl->dev, "Receiver Port %d in 3 wires mode," "acwake_status %d\n", pport->port_number, pport->acwake_status); return true; } if (cur_cawake || pport->acwake_status) { dev_dbg(hsi_ctrl->dev, "Port %d: WAKE status: acwake_status %d," "cur_cawake %d", pport->port_number, pport->acwake_status, cur_cawake); return true; } for (ch = 0; ch < pport->max_ch; ch++) if (hsi_is_channel_busy(&pport->hsi_channel[ch])) { dev_dbg(hsi_ctrl->dev, "Port %d; channel %d " "busy\n", pport->port_number, ch); return true; } return false; }
/** * hsi_ioctl - HSI I/O control * @dev - hsi device channel reference to apply the I/O control * (or port associated to it) * @command - HSI I/O control command * @arg - parameter associated to the control command. NULL, if no parameter. * * Return 0 on success, a negative value on failure. * */ int hsi_ioctl(struct hsi_device *dev, unsigned int command, void *arg) { struct hsi_channel *ch; struct hsi_dev *hsi_ctrl; struct hsi_port *pport; void __iomem *base; unsigned int port, channel; u32 acwake; int err = 0; int fifo = 0; u8 ret; struct hsi_platform_data *pdata; if (unlikely((!dev) || (!dev->ch) || (!dev->ch->hsi_port) || (!dev->ch->hsi_port->hsi_controller)) || (!(dev->ch->flags & HSI_CH_OPEN))) { pr_err(LOG_NAME "HSI IOCTL Invalid parameter\n"); return -EINVAL; } ch = dev->ch; pport = ch->hsi_port; hsi_ctrl = ch->hsi_port->hsi_controller; port = ch->hsi_port->port_number; channel = ch->channel_number; base = hsi_ctrl->base; dev_dbg(hsi_ctrl->dev, "IOCTL: ch %d, command %d\n", channel, command); spin_lock_bh(&hsi_ctrl->lock); hsi_clocks_enable_channel(hsi_ctrl->dev, channel, __func__); 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(hsi_ctrl->dev, "Duplicate ACWAKE UP\n"); err = -EPERM; goto out; } ch->flags |= HSI_CH_ACWAKE; pport->acwake_status |= BIT(channel); /* We only claim once the wake line per channel */ acwake = hsi_inl(base, HSI_SYS_WAKE_REG(port)); if (!(acwake & HSI_WAKE(channel))) { hsi_outl(HSI_SET_WAKE(channel), base, HSI_SYS_SET_WAKE_REG(port)); } goto out; break; case HSI_IOCTL_ACWAKE_DOWN: /* Low power request initiation (OMAP initiated, typically */ /* following inactivity timeout) */ /* ACPU HSI block shall still be capable of receiving */ if (!(ch->flags & HSI_CH_ACWAKE)) { dev_dbg(hsi_ctrl->dev, "Duplicate ACWAKE DOWN\n"); err = -EPERM; goto out; } acwake = hsi_inl(base, HSI_SYS_WAKE_REG(port)); if (unlikely(pport->acwake_status != (acwake & HSI_WAKE_MASK))) { dev_warn(hsi_ctrl->dev, "ACWAKE shadow register mismatch" " acwake_status: 0x%x, HSI_SYS_WAKE_REG: 0x%x", pport->acwake_status, acwake); pport->acwake_status = acwake & HSI_WAKE_MASK; } /* SSI_TODO: add safety check for SSI also */ ch->flags &= ~HSI_CH_ACWAKE; pport->acwake_status &= ~BIT(channel); /* Release the wake line per channel */ if ((acwake & HSI_WAKE(channel))) { hsi_outl(HSI_CLEAR_WAKE(channel), base, HSI_SYS_CLEAR_WAKE_REG(port)); } goto out; break; case HSI_IOCTL_SEND_BREAK: hsi_outl(1, base, HSI_HST_BREAK_REG(port)); /*HSI_TODO : need to deactivate clock after BREAK frames sent*/ /*Use interrupt ? (if TX BREAK INT exists)*/ break; case HSI_IOCTL_GET_ACWAKE: if (!arg) { err = -EINVAL; goto out; } *(u32 *)arg = hsi_inl(base, HSI_SYS_WAKE_REG(port)); break; case HSI_IOCTL_FLUSH_RX: ret = hsi_hsr_fifo_flush_channel(hsi_ctrl, port, channel); if (arg) *(size_t *)arg = ret; /* Ack the RX Int */ hsi_outl_and(~HSI_HSR_DATAAVAILABLE(channel), base, HSI_SYS_MPU_STATUS_CH_REG(port, pport->n_irq, channel)); break; case HSI_IOCTL_FLUSH_TX: ret = hsi_hst_fifo_flush_channel(hsi_ctrl, port, channel); if (arg) *(size_t *)arg = ret; /* Ack the TX Int */ hsi_outl_and(~HSI_HST_DATAACCEPT(channel), base, HSI_SYS_MPU_STATUS_CH_REG(port, pport->n_irq, channel)); break; case HSI_IOCTL_GET_CAWAKE: if (!arg) { err = -EINVAL; goto out; } err = hsi_get_cawake(dev->ch->hsi_port); if (err < 0) { err = -ENODEV; goto out; } *(u32 *)arg = err; break; case HSI_IOCTL_SET_RX: if (!arg) { err = -EINVAL; goto out; } err = hsi_set_rx(dev->ch->hsi_port, (struct hsr_ctx *)arg); break; case HSI_IOCTL_GET_RX: if (!arg) { err = -EINVAL; goto out; } hsi_get_rx(dev->ch->hsi_port, (struct hsr_ctx *)arg); break; case HSI_IOCTL_SET_TX: if (!arg) { err = -EINVAL; goto out; } err = hsi_set_tx(dev->ch->hsi_port, (struct hst_ctx *)arg); break; case HSI_IOCTL_GET_TX: if (!arg) { err = -EINVAL; goto out; } hsi_get_tx(dev->ch->hsi_port, (struct hst_ctx *)arg); break; case HSI_IOCTL_SW_RESET: dev_info(hsi_ctrl->dev, "SW Reset\n"); err = hsi_softreset(hsi_ctrl); /* Reset HSI config to default */ hsi_softreset_driver(hsi_ctrl); break; case HSI_IOCTL_GET_FIFO_OCCUPANCY: if (!arg) { err = -EINVAL; goto out; } fifo = hsi_fifo_get_id(hsi_ctrl, channel, port); if (unlikely(fifo < 0)) { dev_err(hsi_ctrl->dev, "No valid FIFO id found for " "channel %d.\n", channel); err = -EFAULT; goto out; } *(size_t *)arg = hsi_get_rx_fifo_occupancy(hsi_ctrl, fifo); break; case HSI_IOCTL_SET_WAKE_RX_3WIRES_MODE: dev_info(hsi_ctrl->dev, "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_WAKE_RX_4WIRES_MODE: dev_info(hsi_ctrl->dev, "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); hsi_driver_enable_interrupt(pport, HSI_CAWAKEDETECTED); hsi_outl_and(HSI_SET_WAKE_3_WIRES_MASK, base, HSI_SYS_SET_WAKE_REG(port)); break; case HSI_IOCTL_SET_HI_SPEED: if (!arg) { err = -EINVAL; goto out; } hsi_ctrl->hsi_fclk_req = *(unsigned int *)arg ? HSI_FCLK_HI_SPEED : HSI_FCLK_LOW_SPEED; if (hsi_ctrl->hsi_fclk_req == hsi_ctrl->hsi_fclk_current) { dev_dbg(hsi_ctrl->dev, "HSI FClk already @%ldHz\n", hsi_ctrl->hsi_fclk_current); goto out; } if (hsi_is_controller_transfer_ongoing(hsi_ctrl)) { err = -EBUSY; goto out; } hsi_ctrl->clock_change_ongoing = true; spin_unlock_bh(&hsi_ctrl->lock); pdata = dev_get_platdata(hsi_ctrl->dev); /* Set the HSI FCLK to requested value. */ err = pdata->device_scale(hsi_ctrl->dev, hsi_ctrl->dev, hsi_ctrl->hsi_fclk_req); if (err < 0) { dev_err(hsi_ctrl->dev, "%s: Cannot set HSI FClk to" " %ldHz, err %d\n", __func__, hsi_ctrl->hsi_fclk_req, err); } else { dev_info(hsi_ctrl->dev, "HSI FClk changed from %ldHz to" " %ldHz\n", hsi_ctrl->hsi_fclk_current, hsi_ctrl->hsi_fclk_req); hsi_ctrl->hsi_fclk_current = hsi_ctrl->hsi_fclk_req; } spin_lock_bh(&hsi_ctrl->lock); hsi_ctrl->clock_change_ongoing = false; break; case HSI_IOCTL_GET_SPEED: if (!arg) { err = -EINVAL; goto out; } *(unsigned long *)arg = hsi_ctrl->hsi_fclk_current; break; default: err = -ENOIOCTLCMD; break; } out: /* All IOCTL end by disabling the clocks, except ACWAKE high. */ hsi_clocks_disable_channel(hsi_ctrl->dev, channel, __func__); spin_unlock_bh(&hsi_ctrl->lock); return err; }
/** * hsi_ioctl - HSI I/O control * @dev - hsi device channel reference to apply the I/O control * (or port associated to it) * @command - HSI I/O control command * @arg - parameter associated to the control command. NULL, if no parameter. * * Return 0 on success, a negative value on failure. * */ int hsi_ioctl(struct hsi_device *dev, unsigned int command, void *arg) { struct hsi_channel *ch; struct hsi_dev *hsi_ctrl; struct hsi_port *pport; void __iomem *base; unsigned int port, channel; u32 acwake; int err = 0; int fifo = 0; if (unlikely((!dev) || (!dev->ch) || (!dev->ch->hsi_port) || (!dev->ch->hsi_port->hsi_controller)) || (!(dev->ch->flags & HSI_CH_OPEN))) { pr_err(LOG_NAME "HSI IOCTL Invalid parameter\n"); return -EINVAL; } ch = dev->ch; pport = ch->hsi_port; hsi_ctrl = ch->hsi_port->hsi_controller; port = ch->hsi_port->port_number; channel = ch->channel_number; base = hsi_ctrl->base; dev_dbg(dev->device.parent, "IOCTL: ch %d, command %d\n", channel, command); spin_lock_bh(&hsi_ctrl->lock); hsi_clocks_enable_channel(dev->device.parent, channel, __func__); switch (command) { case HSI_IOCTL_ACWAKE_UP: 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); /* We only claim once the wake line per channel */ acwake = hsi_inl(base, HSI_SYS_WAKE_REG(port)); if (!(acwake & HSI_WAKE(channel))) { hsi_outl(HSI_SET_WAKE(channel), base, HSI_SYS_SET_WAKE_REG(port)); } goto out; break; case HSI_IOCTL_ACWAKE_DOWN: /* Low power request initiation (OMAP initiated, typically */ /* following inactivity timeout) */ /* ACPU HSI block shall still be capable of receiving */ if (!(ch->flags & HSI_CH_ACWAKE)) { dev_dbg(dev->device.parent, "Duplicate ACWAKE DOWN\n"); err = -EPERM; goto out; } acwake = hsi_inl(base, HSI_SYS_WAKE_REG(port)); if (unlikely(pport->acwake_status != (acwake & HSI_WAKE_MASK))) { dev_warn(dev->device.parent, "ACWAKE shadow register mismatch" " acwake_status: 0x%x, HSI_SYS_WAKE_REG: 0x%x", pport->acwake_status, acwake); pport->acwake_status = acwake & HSI_WAKE_MASK; } /* SSI_TODO: add safety check for SSI also */ ch->flags &= ~HSI_CH_ACWAKE; pport->acwake_status &= ~BIT(channel); /* Release the wake line per channel */ if ((acwake & HSI_WAKE(channel))) { hsi_outl(HSI_CLEAR_WAKE(channel), base, HSI_SYS_CLEAR_WAKE_REG(port)); } goto out; break; case HSI_IOCTL_SEND_BREAK: hsi_outl(1, base, HSI_HST_BREAK_REG(port)); /*HSI_TODO : need to deactivate clock after BREAK frames sent*/ /*Use interrupt ? (if TX BREAK INT exists)*/ break; case HSI_IOCTL_GET_ACWAKE: if (!arg) { err = -EINVAL; goto out; } *(u32 *)arg = hsi_inl(base, HSI_SYS_WAKE_REG(port)); break; case HSI_IOCTL_FLUSH_RX: hsi_outl(0, base, HSI_HSR_RXSTATE_REG(port)); break; case HSI_IOCTL_FLUSH_TX: hsi_outl(0, base, HSI_HST_TXSTATE_REG(port)); break; case HSI_IOCTL_GET_CAWAKE: if (!arg) { err = -EINVAL; goto out; } err = hsi_get_cawake(dev->ch->hsi_port); if (err < 0) { err = -ENODEV; goto out; } *(u32 *)arg = err; break; case HSI_IOCTL_SET_RX: if (!arg) { err = -EINVAL; goto out; } err = hsi_set_rx(dev->ch->hsi_port, (struct hsr_ctx *)arg); break; case HSI_IOCTL_GET_RX: if (!arg) { err = -EINVAL; goto out; } hsi_get_rx(dev->ch->hsi_port, (struct hsr_ctx *)arg); break; case HSI_IOCTL_SET_TX: if (!arg) { err = -EINVAL; goto out; } err = hsi_set_tx(dev->ch->hsi_port, (struct hst_ctx *)arg); break; case HSI_IOCTL_GET_TX: if (!arg) { err = -EINVAL; goto out; } hsi_get_tx(dev->ch->hsi_port, (struct hst_ctx *)arg); break; case HSI_IOCTL_SW_RESET: dev_info(dev->device.parent, "SW Reset\n"); err = hsi_softreset(hsi_ctrl); /* Reset HSI config to default */ hsi_softreset_driver(hsi_ctrl); break; case HSI_IOCTL_GET_FIFO_OCCUPANCY: if (!arg) { err = -EINVAL; goto out; } fifo = hsi_fifo_get_id(hsi_ctrl, channel, port); if (unlikely(fifo < 0)) { dev_err(hsi_ctrl->dev, "No valid FIFO id found for " "channel %d.\n", channel); err = -EFAULT; goto out; } *(size_t *)arg = hsi_get_rx_fifo_occupancy(hsi_ctrl, fifo); break; default: err = -ENOIOCTLCMD; break; } out: /* All IOCTL end by disabling the clocks, except ACWAKE high. */ hsi_clocks_disable_channel(dev->device.parent, channel, __func__); spin_unlock_bh(&hsi_ctrl->lock); return err; }
/** * hsi_do_cawake_process - CAWAKE line management * @pport - HSI port to process * * This function handles the CAWAKE L/H transitions and call the event callback * accordingly. * * Returns 0 if CAWAKE event process, -EAGAIN if CAWAKE event processing is * delayed due to a pending DMA interrupt. * If -EAGAIN is returned, pport->hsi_tasklet has to be re-scheduled once * DMA tasklet has be executed. This should be done automatically by driver. * */ int hsi_do_cawake_process(struct hsi_port *pport) { struct hsi_dev *hsi_ctrl = pport->hsi_controller; bool cawake_status = hsi_get_cawake(pport); if (pport->wake_rx_3_wires_mode) { dev_warn(hsi_ctrl->dev, "CAWAKE edge in RX 3 wires, exiting\n"); return 0; } /* Deal with init condition */ if (unlikely(pport->cawake_status < 0)) pport->cawake_status = !cawake_status; dev_dbg(hsi_ctrl->dev, "%s: Interrupts are not enabled but CAWAKE came." "hsi: port[%d] irq[%d] irq_en=0x%08x dma_irq_en=0x%08x\n", __func__, pport->port_number, pport->n_irq, hsi_inl(pport->hsi_controller->base, HSI_SYS_MPU_ENABLE_REG(pport->port_number, pport->n_irq)), hsi_inl(pport->hsi_controller->base, HSI_SYS_GDD_MPU_IRQ_ENABLE_REG)); /* Check CAWAKE line status */ if (cawake_status) { dev_dbg(hsi_ctrl->dev, "CAWAKE rising edge detected\n"); /* Check for possible mismatch (race condition) */ if (unlikely(pport->cawake_status)) { dev_warn(hsi_ctrl->dev, "Missed previous CAWAKE falling edge...\n"); spin_unlock(&hsi_ctrl->lock); hsi_port_event_handler(pport, HSI_EVENT_CAWAKE_DOWN, NULL); spin_lock(&hsi_ctrl->lock); /* In case another CAWAKE interrupt occured and caused * a race condition, clear CAWAKE backup interrupt to * avoid handling twice the race condition */ hsi_driver_ack_interrupt(pport, HSI_CAWAKEDETECTED, true); } pport->cawake_status = 1; spin_unlock(&hsi_ctrl->lock); hsi_port_event_handler(pport, HSI_EVENT_CAWAKE_UP, NULL); spin_lock(&hsi_ctrl->lock); /* * HSI - OMAP4430-2.2BUG00055: i702 * HSI: DSP Swakeup generated is the same than MPU Swakeup. * System cannot enter in off mode due to the DSP. */ if (is_hsi_errata(hsi_ctrl, HSI_ERRATUM_i702_PM_HSI_SWAKEUP)) omap_pm_clear_dsp_wake_up(); } else { dev_dbg(hsi_ctrl->dev, "CAWAKE falling edge detected\n"); /* Check for pending DMA interrupt */ if (hsi_is_dma_read_int_pending(hsi_ctrl)) { dev_dbg(hsi_ctrl->dev, "Pending DMA Read interrupt " "before CAWAKE->L, exiting " "Interrupt tasklet.\n"); return -EAGAIN; } if (unlikely(!pport->cawake_status)) { dev_warn(hsi_ctrl->dev, "Missed previous CAWAKE rising edge...\n"); spin_unlock(&hsi_ctrl->lock); hsi_port_event_handler(pport, HSI_EVENT_CAWAKE_UP, NULL); spin_lock(&hsi_ctrl->lock); /* In case another CAWAKE interrupt occured and caused * a race condition, clear CAWAKE backup interrupt to * avoid handling twice the race condition */ hsi_driver_ack_interrupt(pport, HSI_CAWAKEDETECTED, true); } pport->cawake_status = 0; spin_unlock(&hsi_ctrl->lock); hsi_port_event_handler(pport, HSI_EVENT_CAWAKE_DOWN, NULL); spin_lock(&hsi_ctrl->lock); } /* If another CAWAKE event occured while previous is still processed */ /* do not clear the status bit */ cawake_status = hsi_get_cawake(pport); if (cawake_status != pport->cawake_status) { dev_warn(hsi_ctrl->dev, "CAWAKE line changed to %d while CAWAKE" "event is still being processed\n", cawake_status); return -EAGAIN; } return 0; }
static int hsi_debug_port_show(struct seq_file *m, void *p) { struct hsi_port *hsi_port = m->private; struct hsi_dev *hsi_ctrl = hsi_port->hsi_controller; void __iomem *base = hsi_ctrl->base; unsigned int port = hsi_port->port_number; int ch, fifo; long buff_offset; struct platform_device *pdev = to_platform_device(hsi_ctrl->dev); hsi_clocks_enable(hsi_ctrl->dev, __func__); if (hsi_port->cawake_gpio >= 0) seq_printf(m, "CAWAKE\t\t: %d\n", hsi_get_cawake(hsi_port)); seq_printf(m, "WAKE\t\t: 0x%08x\n", hsi_inl(base, HSI_SYS_WAKE_REG(port))); seq_printf(m, "MPU_ENABLE_IRQ%d\t: 0x%08x\n", hsi_port->n_irq, hsi_inl(base, HSI_SYS_MPU_ENABLE_REG(port, hsi_port->n_irq))); seq_printf(m, "MPU_STATUS_IRQ%d\t: 0x%08x\n", hsi_port->n_irq, hsi_inl(base, HSI_SYS_MPU_STATUS_REG(port, hsi_port->n_irq))); if (hsi_driver_device_is_hsi(pdev)) { seq_printf(m, "MPU_U_ENABLE_IRQ%d\t: 0x%08x\n", hsi_port->n_irq, hsi_inl(base, HSI_SYS_MPU_U_ENABLE_REG(port, hsi_port->n_irq))); seq_printf(m, "MPU_U_STATUS_IRQ%d\t: 0x%08x\n", hsi_port->n_irq, hsi_inl(base, HSI_SYS_MPU_U_STATUS_REG(port, hsi_port->n_irq))); } /* HST */ seq_printf(m, "\nHST\n===\n"); seq_printf(m, "MODE\t\t: 0x%08x\n", hsi_inl(base, HSI_HST_MODE_REG(port))); seq_printf(m, "FRAMESIZE\t: 0x%08x\n", hsi_inl(base, HSI_HST_FRAMESIZE_REG(port))); seq_printf(m, "DIVISOR\t\t: 0x%08x\n", hsi_inl(base, HSI_HST_DIVISOR_REG(port))); seq_printf(m, "CHANNELS\t: 0x%08x\n", hsi_inl(base, HSI_HST_CHANNELS_REG(port))); seq_printf(m, "ARBMODE\t\t: 0x%08x\n", hsi_inl(base, HSI_HST_ARBMODE_REG(port))); seq_printf(m, "TXSTATE\t\t: 0x%08x\n", hsi_inl(base, HSI_HST_TXSTATE_REG(port))); if (hsi_driver_device_is_hsi(pdev)) { seq_printf(m, "BUFSTATE P1\t: 0x%08x\n", hsi_inl(base, HSI_HST_BUFSTATE_REG(1))); seq_printf(m, "BUFSTATE P2\t: 0x%08x\n", hsi_inl(base, HSI_HST_BUFSTATE_REG(2))); } else { seq_printf(m, "BUFSTATE\t: 0x%08x\n", hsi_inl(base, HSI_HST_BUFSTATE_REG(port))); } seq_printf(m, "BREAK\t\t: 0x%08x\n", hsi_inl(base, HSI_HST_BREAK_REG(port))); for (ch = 0; ch < 8; ch++) { buff_offset = hsi_hst_buffer_reg(hsi_ctrl, port, ch); if (buff_offset >= 0) seq_printf(m, "BUFFER_CH%d\t: 0x%08x\n", ch, hsi_inl(base, buff_offset)); } if (hsi_driver_device_is_hsi(pdev)) { for (fifo = 0; fifo < HSI_HST_FIFO_COUNT; fifo++) { seq_printf(m, "FIFO MAPPING%d\t: 0x%08x\n", fifo, hsi_inl(base, HSI_HST_MAPPING_FIFO_REG(fifo))); } } /* HSR */ seq_printf(m, "\nHSR\n===\n"); seq_printf(m, "MODE\t\t: 0x%08x\n", hsi_inl(base, HSI_HSR_MODE_REG(port))); seq_printf(m, "FRAMESIZE\t: 0x%08x\n", hsi_inl(base, HSI_HSR_FRAMESIZE_REG(port))); seq_printf(m, "CHANNELS\t: 0x%08x\n", hsi_inl(base, HSI_HSR_CHANNELS_REG(port))); seq_printf(m, "COUNTERS\t: 0x%08x\n", hsi_inl(base, HSI_HSR_COUNTERS_REG(port))); seq_printf(m, "RXSTATE\t\t: 0x%08x\n", hsi_inl(base, HSI_HSR_RXSTATE_REG(port))); if (hsi_driver_device_is_hsi(pdev)) { seq_printf(m, "BUFSTATE P1\t: 0x%08x\n", hsi_inl(base, HSI_HSR_BUFSTATE_REG(1))); seq_printf(m, "BUFSTATE P2\t: 0x%08x\n", hsi_inl(base, HSI_HSR_BUFSTATE_REG(2))); } else { seq_printf(m, "BUFSTATE\t: 0x%08x\n", hsi_inl(base, HSI_HSR_BUFSTATE_REG(port))); } seq_printf(m, "BREAK\t\t: 0x%08x\n", hsi_inl(base, HSI_HSR_BREAK_REG(port))); seq_printf(m, "ERROR\t\t: 0x%08x\n", hsi_inl(base, HSI_HSR_ERROR_REG(port))); seq_printf(m, "ERRORACK\t: 0x%08x\n", hsi_inl(base, HSI_HSR_ERRORACK_REG(port))); for (ch = 0; ch < 8; ch++) { buff_offset = hsi_hsr_buffer_reg(hsi_ctrl, port, ch); if (buff_offset >= 0) seq_printf(m, "BUFFER_CH%d\t: 0x%08x\n", ch, hsi_inl(base, buff_offset)); } if (hsi_driver_device_is_hsi(pdev)) { for (fifo = 0; fifo < HSI_HSR_FIFO_COUNT; fifo++) { seq_printf(m, "FIFO MAPPING%d\t: 0x%08x\n", fifo, hsi_inl(base, HSI_HSR_MAPPING_FIFO_REG(fifo))); } seq_printf(m, "DLL\t: 0x%08x\n", hsi_inl(base, HSI_HSR_DLL_REG)); seq_printf(m, "DIVISOR\t: 0x%08x\n", hsi_inl(base, HSI_HSR_DIVISOR_REG(port))); } hsi_clocks_disable(hsi_ctrl->dev, __func__); return 0; }
/** * hsi_do_cawake_process - CAWAKE line management * @pport - HSI port to process * * This function handles the CAWAKE L/H transitions and call the event callback * accordingly. * * Returns 0 if CAWAKE event process, -EAGAIN if CAWAKE event processing is * delayed due to a pending DMA interrupt. * If -EAGAIN is returned, pport->hsi_tasklet has to be re-scheduled once * DMA tasklet has be executed. This should be done automatically by driver. * */ int hsi_do_cawake_process(struct hsi_port *pport) { struct hsi_dev *hsi_ctrl = pport->hsi_controller; bool cawake_status = hsi_get_cawake(pport); if (pport->wake_rx_3_wires_mode) { dev_warn(hsi_ctrl->dev, "CAWAKE edge in RX 3 wires, exiting\n"); return 0; } /* Deal with init condition */ if (unlikely(pport->cawake_status < 0)) pport->cawake_status = !cawake_status; /* Check CAWAKE line status */ if (cawake_status) { dev_dbg(hsi_ctrl->dev, "CAWAKE rising edge detected\n"); /* Check for possible mismatch (race condition) */ if (unlikely(pport->cawake_status)) { dev_warn(hsi_ctrl->dev, "Missed previous CAWAKE falling edge...\n"); spin_unlock(&hsi_ctrl->lock); hsi_port_event_handler(pport, HSI_EVENT_CAWAKE_DOWN, NULL); spin_lock(&hsi_ctrl->lock); /* In case another CAWAKE interrupt occured and caused * a race condition, clear CAWAKE backup interrupt to * avoid handling twice the race condition */ hsi_driver_ack_interrupt(pport, HSI_CAWAKEDETECTED, true); } pport->cawake_status = 1; /* Allow data reception */ hsi_hsr_resume(hsi_ctrl); spin_unlock(&hsi_ctrl->lock); hsi_port_event_handler(pport, HSI_EVENT_CAWAKE_UP, NULL); spin_lock(&hsi_ctrl->lock); /* * HSI - OMAP4430-2.2BUG00055: i702 * HSI: DSP Swakeup generated is the same than MPU Swakeup. * System cannot enter in off mode due to the DSP. */ if (is_hsi_errata(hsi_ctrl, HSI_ERRATUM_i702_PM_HSI_SWAKEUP)) omap_pm_clear_dsp_wake_up(); } else { dev_dbg(hsi_ctrl->dev, "CAWAKE falling edge detected\n"); /* Check for pending DMA interrupt */ if (hsi_is_dma_read_int_pending(hsi_ctrl)) { dev_dbg(hsi_ctrl->dev, "Pending DMA Read interrupt " "before CAWAKE->L, exiting " "Interrupt tasklet.\n"); return -EAGAIN; } if (unlikely(!pport->cawake_status)) { dev_warn(hsi_ctrl->dev, "Missed previous CAWAKE rising edge...\n"); spin_unlock(&hsi_ctrl->lock); hsi_port_event_handler(pport, HSI_EVENT_CAWAKE_UP, NULL); spin_lock(&hsi_ctrl->lock); /* In case another CAWAKE interrupt occured and caused * a race condition, clear CAWAKE backup interrupt to * avoid handling twice the race condition */ hsi_driver_ack_interrupt(pport, HSI_CAWAKEDETECTED, true); } pport->cawake_status = 0; /* Forbid data reception */ hsi_hsr_suspend(hsi_ctrl); spin_unlock(&hsi_ctrl->lock); hsi_port_event_handler(pport, HSI_EVENT_CAWAKE_DOWN, NULL); spin_lock(&hsi_ctrl->lock); } return 0; }