/** * hsi_read_cancel - Cancel pending read request. * @dev - hsi device channel where to cancel the pending read. * * read_done() callback will not be called after success of this function. * * Return: -ENXIO : No DMA channel found for specified HSI channel * -ECANCELED : read cancel success, data not available at expected * address. * 0 : transfer is already over, data already available at expected * address. * * Note: whatever returned value, read callback will not be called after cancel. */ int hsi_read_cancel(struct hsi_device *dev) { struct hsi_dev *hsi_ctrl; int err; if (unlikely(!dev || !dev->ch)) { pr_err(LOG_NAME "Wrong HSI device %p\n", dev); return -ENODEV; } hsi_ctrl = dev->ch->hsi_port->hsi_controller; dev_dbg(hsi_ctrl->dev, "%s ch %d\n", __func__, dev->n_ch); if (unlikely(!(dev->ch->flags & HSI_CH_OPEN))) { dev_err(hsi_ctrl->dev, "HSI device NOT open\n"); return -ENODEV; } spin_lock_bh(&hsi_ctrl->lock); hsi_clocks_enable_channel(hsi_ctrl->dev, dev->ch->channel_number, __func__); err = __hsi_read_cancel(dev->ch); hsi_clocks_disable_channel(hsi_ctrl->dev, dev->ch->channel_number, __func__); spin_unlock_bh(&hsi_ctrl->lock); return err; }
/** * hsi_unpoll - HSI poll feature, disables data interrupt on frame reception * @dev - hsi device channel reference to apply the I/O control * (or port associated to it) * * Return 0 on success, a negative value on failure. * */ int hsi_unpoll(struct hsi_device *dev) { struct hsi_channel *ch; struct hsi_dev *hsi_ctrl; if (unlikely(!dev || !dev->ch)) return -EINVAL; dev_dbg(dev->device.parent, "%s ch %d\n", __func__, dev->n_ch); if (unlikely(!(dev->ch->flags & HSI_CH_OPEN))) { dev_err(dev->device.parent, "HSI device NOT open\n"); return -EINVAL; } ch = dev->ch; hsi_ctrl = ch->hsi_port->hsi_controller; spin_lock_bh(&hsi_ctrl->lock); hsi_clocks_enable_channel(hsi_ctrl->dev, dev->ch->channel_number, __func__); ch->flags &= ~HSI_CH_RX_POLL; hsi_driver_disable_read_interrupt(ch); hsi_clocks_disable_channel(hsi_ctrl->dev, dev->ch->channel_number, __func__); spin_unlock_bh(&hsi_ctrl->lock); return 0; }
/** * hsi_set_port_event_cb - register port_event callback. * @dev - reference to hsi device channel where the callback is associated to. * @port_event_cb - callback to signal events from the channel port. */ void hsi_set_port_event_cb(struct hsi_device *dev, void (*port_event_cb) (struct hsi_device *dev, unsigned int event, void *arg)) { struct hsi_port *port = dev->ch->hsi_port; struct hsi_dev *hsi_ctrl = port->hsi_controller; dev_dbg(hsi_ctrl->dev, "%s ch %d\n", __func__, dev->n_ch); write_lock_bh(&dev->ch->rw_lock); dev->ch->port_event = port_event_cb; write_unlock_bh(&dev->ch->rw_lock); /* Since we now have a callback registered for events, we can now */ /* enable the CAWAKE, ERROR and BREAK interrupts */ spin_lock_bh(&hsi_ctrl->lock); hsi_clocks_enable_channel(hsi_ctrl->dev, dev->ch->channel_number, __func__); hsi_driver_enable_interrupt(port, HSI_CAWAKEDETECTED | HSI_ERROROCCURED | HSI_BREAKDETECTED); hsi_clocks_disable_channel(hsi_ctrl->dev, dev->ch->channel_number, __func__); spin_unlock_bh(&hsi_ctrl->lock); }
/** * hsi_close - close given hsi device channel * @dev - reference to hsi device channel. */ void hsi_close(struct hsi_device *dev) { struct hsi_dev *hsi_ctrl; if (!dev || !dev->ch) { pr_err(LOG_NAME "Trying to close wrong HSI device %p\n", dev); return; } dev_dbg(dev->device.parent, "%s ch %d\n", __func__, dev->n_ch); hsi_ctrl = dev->ch->hsi_port->hsi_controller; spin_lock_bh(&hsi_ctrl->lock); hsi_clocks_enable_channel(hsi_ctrl->dev, dev->ch->channel_number, __func__); if (dev->ch->flags & HSI_CH_OPEN) { dev->ch->flags &= ~HSI_CH_OPEN; __hsi_write_cancel(dev->ch); __hsi_read_cancel(dev->ch); } hsi_clocks_disable_channel(hsi_ctrl->dev, dev->ch->channel_number, __func__); spin_unlock_bh(&hsi_ctrl->lock); }
/** * hsi_open - open a hsi device channel. * @dev - Reference to the hsi device channel to be openned. * * Returns 0 on success, -EINVAL on bad parameters, -EBUSY if is already opened. */ 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); return -EINVAL; } dev_dbg(dev->device.parent, "%s ch %d\n", __func__, dev->n_ch); ch = dev->ch; if (!ch->read_done || !ch->write_done) { dev_err(dev->device.parent, "Trying to open with no (read/write) callbacks " "registered\n"); return -EINVAL; } if (ch->flags & HSI_CH_OPEN) { dev_err(dev->device.parent, "Port %d Channel %d already open\n", dev->n_p, dev->n_ch); return -EBUSY; } port = ch->hsi_port; hsi_ctrl = port->hsi_controller; spin_lock_bh(&hsi_ctrl->lock); err = hsi_clocks_enable_channel(hsi_ctrl->dev, ch->channel_number, __func__); if (err < 0) { spin_unlock_bh(&hsi_ctrl->lock); return err; } /* Restart with flags cleaned up */ ch->flags = HSI_CH_OPEN; 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 */ hsi_clocks_disable_channel(hsi_ctrl->dev, ch->channel_number, __func__); spin_unlock_bh(&hsi_ctrl->lock); return 0; }
/** * 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_read - read data from the hsi device channel * @dev - hsi device channel reference to read data from. * @addr - pointer to a 32-bit word data to store the data. * @size - number of 32-bit word to be stored. * * Return 0 on sucess, a negative value on failure. * A success value only indicates that the request has been accepted. * Data is only available in the buffer when the read_done callback is called. * */ int hsi_read(struct hsi_device *dev, u32 *addr, unsigned int size) { struct hsi_channel *ch; struct hsi_dev *hsi_ctrl; int err; if (unlikely(!dev)) { pr_err(LOG_NAME "Null dev pointer in hsi_read\n"); return -EINVAL; } if (unlikely(!dev->ch || !addr || (size <= 0))) { dev_err(dev->device.parent, "Wrong parameters " "hsi_device %p data %p count %d", dev, addr, size); return -EINVAL; } if (unlikely(!(dev->ch->flags & HSI_CH_OPEN))) { dev_err(dev->device.parent, "HSI device NOT open\n"); return -EINVAL; } ch = dev->ch; hsi_ctrl = ch->hsi_port->hsi_controller; spin_lock_bh(&hsi_ctrl->lock); if (hsi_ctrl->clock_change_ongoing) { dev_warn(hsi_ctrl->dev, "HSI Fclock change ongoing, retry.\n"); spin_unlock_bh(&hsi_ctrl->lock); return -EAGAIN; } if (pm_runtime_suspended(hsi_ctrl->dev) || !hsi_ctrl->clock_enabled) dev_dbg(hsi_ctrl->dev, "hsi_read with HSI clocks OFF, clock_enabled = %d\n", hsi_ctrl->clock_enabled); hsi_clocks_enable_channel(dev->device.parent, ch->channel_number, __func__); if (ch->read_data.addr != NULL) { dev_err(hsi_ctrl->dev, "# Invalid request - Read " "operation pending port %d channel %d\n", ch->hsi_port->port_number, ch->channel_number); err = -EINVAL; goto done; } ch->read_data.addr = addr; ch->read_data.size = size; ch->read_data.lch = -1; if (size == 1) err = hsi_driver_enable_read_interrupt(ch, addr); else err = hsi_driver_read_dma(ch, addr, size); if (unlikely(err < 0)) { ch->read_data.addr = NULL; ch->read_data.size = 0; dev_err(hsi_ctrl->dev, "Failed to program read\n"); } done: hsi_clocks_disable_channel(hsi_ctrl->dev, ch->channel_number, __func__); spin_unlock_bh(&hsi_ctrl->lock); return err; }
/** * hsi_write - write data into the hsi device channel * @dev - reference to the hsi device channel to write into. * @addr - pointer to a 32-bit word data to be written. * @size - number of 32-bit word to be written. * * 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. * */ int hsi_write(struct hsi_device *dev, u32 *addr, unsigned int size) { struct hsi_channel *ch; struct hsi_dev *hsi_ctrl; int err; if (unlikely(!dev)) { pr_err(LOG_NAME "Null dev pointer in hsi_write\n"); return -EINVAL; } if (unlikely(!dev->ch || !addr || (size <= 0))) { dev_err(dev->device.parent, "Wrong parameters hsi_device %p data %p count %d", dev, addr, size); return -EINVAL; } ch = dev->ch; hsi_ctrl = ch->hsi_port->hsi_controller; dev_dbg(hsi_ctrl->dev, "%s ch %d, @%x, size %d u32\n", __func__, dev->n_ch, (u32) addr, size); if (unlikely(!(dev->ch->flags & HSI_CH_OPEN))) { dev_err(hsi_ctrl->dev, "HSI device NOT open\n"); return -EINVAL; } if (ch->write_data.addr != NULL) { dev_err(hsi_ctrl->dev, "# Invalid request - Write " "operation pending port %d channel %d\n", ch->hsi_port->port_number, ch->channel_number); return -EINVAL; } spin_lock_bh(&hsi_ctrl->lock); if (hsi_ctrl->clock_change_ongoing) { dev_warn(hsi_ctrl->dev, "HSI Fclock change ongoing, retry.\n"); spin_unlock_bh(&hsi_ctrl->lock); return -EAGAIN; } if (pm_runtime_suspended(hsi_ctrl->dev) || !hsi_ctrl->clock_enabled) dev_dbg(hsi_ctrl->dev, "hsi_write with HSI clocks OFF, clock_enabled = %d\n", hsi_ctrl->clock_enabled); hsi_clocks_enable_channel(dev->device.parent, ch->channel_number, __func__); ch->write_data.addr = addr; ch->write_data.size = size; ch->write_data.lch = -1; if (size == 1) err = hsi_driver_enable_write_interrupt(ch, addr); else err = hsi_driver_write_dma(ch, addr, size); if (unlikely(err < 0)) { ch->write_data.addr = NULL; ch->write_data.size = 0; dev_err(hsi_ctrl->dev, "Failed to program write\n"); } spin_unlock_bh(&hsi_ctrl->lock); /* Leave clocks enabled until transfer is complete (write callback */ /* is called */ return err; }
/** * hsi_open - open a hsi device channel. * @dev - Reference to the hsi device channel to be openned. * * Returns 0 on success, -EINVAL on bad parameters, -EBUSY if is already opened. */ 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); return -EINVAL; } dev_dbg(dev->device.parent, "%s ch %d\n", __func__, dev->n_ch); ch = dev->ch; if (!ch->read_done || !ch->write_done) { dev_err(dev->device.parent, "Trying to open with no (read/write) callbacks " "registered\n"); return -EINVAL; } if (ch->flags & HSI_CH_OPEN) { dev_err(dev->device.parent, "Port %d Channel %d already open\n", dev->n_p, dev->n_ch); 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->hsi_fclk_current == 0) { struct hsi_platform_data *pdata; pdata = dev_get_platdata(hsi_ctrl->dev); /* 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->hsi_fclk_current = 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); /* NOTE: error and break are port events and do not need to be * enabled for HSI extended enable register */ hsi_clocks_disable_channel(hsi_ctrl->dev, ch->channel_number, __func__); spin_unlock_bh(&hsi_ctrl->lock); return 0; }
/** * 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; }