Beispiel #1
0
/**
* 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;

    /* HSI-C1BUG00088: i696 : HSI: Issue with SW reset
     * No recovery from SW reset under specific circumstances
     * If a SW RESET is done while some HSI errors are still not
     * acknowledged, the HSR FSM is stucked. */
    if (is_hsi_errata(hsi_ctrl, HSI_ERRATUM_i696_SW_RESET_FSM_STUCK)) {
        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 {
        status = hsi_inl(base, HSI_SYS_SYSSTATUS_REG);
        ind++;
    } while ((!(status & HSI_RESETDONE)) &&
             (ind < HSI_RESETDONE_MAX_RETRIES));

    if (ind >= HSI_RESETDONE_MAX_RETRIES) {
        dev_err(hsi_ctrl->dev, "HSI SW_RESET failed to complete within"
                " %d retries.\n", HSI_RESETDONE_MAX_RETRIES);
        return -EIO;
    } else if (ind > HSI_RESETDONE_NORMAL_RETRIES) {
        dev_warn(hsi_ctrl->dev, "HSI SW_RESET abnormally long:"
                 " %d retries to complete.\n", ind);
    }

    ind = 0;
    /* Reseting DMA Engine */
    hsi_outl_or(HSI_GDD_GRST_SWRESET, base, HSI_GDD_GRST_REG);
    do {
        status = hsi_inl(base, HSI_GDD_GRST_REG);
        ind++;
    } while ((status & HSI_GDD_GRST_SWRESET) &&
             (ind < HSI_RESETDONE_MAX_RETRIES));

    if (ind >= HSI_RESETDONE_MAX_RETRIES) {
        dev_err(hsi_ctrl->dev, "HSI DMA SW_RESET failed to complete"
                " within %d retries.\n", HSI_RESETDONE_MAX_RETRIES);
        return -EIO;
    }

    if (ind > HSI_RESETDONE_NORMAL_RETRIES) {
        dev_warn(hsi_ctrl->dev, "HSI DMA SW_RESET abnormally long:"
                 " %d retries to complete.\n", ind);
    }

    return 0;
}
Beispiel #2
0
/**
* 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 {
        status = hsi_inl(base, HSI_SYS_SYSSTATUS_REG);
        ind++;
    } while ((!(status & HSI_RESETDONE)) &&
             (ind < HSI_RESETDONE_MAX_RETRIES));

    if (ind >= HSI_RESETDONE_MAX_RETRIES) {
        dev_err(hsi_ctrl->dev, "HSI SW_RESET failed to complete within"
                " %d retries.\n", HSI_RESETDONE_MAX_RETRIES);
        return -EIO;
    } else if (ind > HSI_RESETDONE_NORMAL_RETRIES) {
        dev_warn(hsi_ctrl->dev, "HSI SW_RESET abnormally long:"
                 " %d retries to complete.\n", ind);
    }

    ind = 0;
    /* Reseting DMA Engine */
    hsi_outl_or(HSI_GDD_GRST_SWRESET, base, HSI_GDD_GRST_REG);
    do {
        status = hsi_inl(base, HSI_GDD_GRST_REG);
        ind++;
    } while ((status & HSI_GDD_GRST_SWRESET) &&
             (ind < HSI_RESETDONE_MAX_RETRIES));

    if (ind >= HSI_RESETDONE_MAX_RETRIES) {
        dev_err(hsi_ctrl->dev, "HSI DMA SW_RESET failed to complete"
                " within %d retries.\n", HSI_RESETDONE_MAX_RETRIES);
        return -EIO;
    }

    if (ind > HSI_RESETDONE_NORMAL_RETRIES) {
        dev_warn(hsi_ctrl->dev, "HSI DMA SW_RESET abnormally long:"
                 " %d retries to complete.\n", ind);
    }

    return 0;
}
/* Enables the CAWAKE, BREAK, or ERROR interrupt for the given port
 *
 * Since these 3 interrupts ENABLE and STATUS bits are duplicated in both
 * HSI_Pp_M_IRQr_xxx(channels [0..7]) and HSI_Pp_M_IRQrU_xxx(channels [8..15]),
 * for convenience we always enable the interrupts for channels [0..7].
 *
 * BREAK and ERROR interrupts in HSI_Pp_M_IRQrU_xxx are not used.
 * CAWAKE interrupt in HSI_Pp_M_IRQrU_xxx is used as a backup interrupt to be
 * sure we don't miss a CAWAKE interrupt while clearing the previous one.
 */
int hsi_driver_enable_interrupt(struct hsi_port *pport, u32 flag)
{
	hsi_outl_or(flag, pport->hsi_controller->base,
		    HSI_SYS_MPU_ENABLE_REG(pport->port_number, pport->n_irq));

	return 0;
}
Beispiel #4
0
int hsi_softreset(struct hsi_dev *hsi_ctrl)
{
	unsigned int ind = 0;
	void __iomem *base = hsi_ctrl->base;
	u32 status;

	/* Reseting HSI Block */
	hsi_outl_or(HSI_SOFTRESET, base, HSI_SYS_SYSCONFIG_REG);
	do {
		status = hsi_inl(base, HSI_SYS_SYSSTATUS_REG);
		ind++;
	} while ((!(status & HSI_RESETDONE)) &&
		   (ind < HSI_RESETDONE_MAX_RETRIES));

	if (ind >= HSI_RESETDONE_MAX_RETRIES) {
		dev_err(hsi_ctrl->dev, "HSI SW_RESET failed to complete within"
			" %d retries.\n", HSI_RESETDONE_MAX_RETRIES);
		return -EIO;
	} else if (ind > HSI_RESETDONE_NORMAL_RETRIES) {
		dev_warn(hsi_ctrl->dev, "HSI SW_RESET abnormally long:"
			" %d retries to complete.\n", ind);
	}

	ind = 0;
	/* Reseting DMA Engine */
	hsi_outl_or(HSI_GDD_GRST_SWRESET, base, HSI_GDD_GRST_REG);
	do {
		status = hsi_inl(base, HSI_GDD_GRST_REG);
		ind++;
	} while ((status & HSI_GDD_GRST_SWRESET) &&
		 (ind < HSI_RESETDONE_MAX_RETRIES));

	if (ind >= HSI_RESETDONE_MAX_RETRIES) {
		dev_err(hsi_ctrl->dev, "HSI DMA SW_RESET failed to complete"
			" within %d retries.\n", HSI_RESETDONE_MAX_RETRIES);
		return -EIO;
	}

	if (ind > HSI_RESETDONE_NORMAL_RETRIES) {
		dev_warn(hsi_ctrl->dev, "HSI DMA SW_RESET abnormally long:"
			" %d retries to complete.\n", ind);
	}

	return 0;
}
/* Enables the Data Available Interrupt of HSR for the given channel */
int hsi_driver_enable_read_interrupt(struct hsi_channel *ch, u32 * data)
{
	struct hsi_port *p = ch->hsi_port;
	unsigned int port = p->port_number;
	unsigned int channel = ch->channel_number;

	hsi_outl_or(HSI_HSR_DATAAVAILABLE(channel), p->hsi_controller->base,
		    HSI_SYS_MPU_ENABLE_CH_REG(port, p->n_irq, channel));

	return 0;
}
Beispiel #6
0
static int __init hsi_softreset(struct hsi_dev *hsi_ctrl)
{
	int ind = 0;
	void __iomem *base = hsi_ctrl->base;
	u32 status;

	hsi_outl_or(HSI_SOFTRESET, base, HSI_SYS_SYSCONFIG_REG);

	do {
		set_current_state(TASK_UNINTERRUPTIBLE);
		schedule_timeout(msecs_to_jiffies(HSI_RESETDONE_TIMEOUT));
		status = hsi_inl(base, HSI_SYS_SYSSTATUS_REG);
		ind++;
	} while ((!(status & HSI_RESETDONE)) && (ind < HSI_RESETDONE_RETRIES));

	if (ind >= HSI_RESETDONE_RETRIES)
		return -EIO;

	/* Reseting GDD */
	hsi_outl_or(HSI_SWRESET, base, HSI_GDD_GRST_REG);

	return 0;
}
Beispiel #7
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;
}
Beispiel #8
0
/**
 * hsi_driver_read_dma - Program GDD [DMA] to write data to memory from
 * the hsi channel buffer.
 * @hsi_channel - pointer to the hsi_channel to read data from.
 * @data - 32-bit word pointer where to store the incoming data.
 * @size - Number of 32bit words to be transfered to the buffer.
 *
 * hsi_controller lock must be held before calling this function.
 *
 * Return 0 on success and < 0 on error.
 */
int hsi_driver_read_dma(struct hsi_channel *hsi_channel, u32 * data,
			unsigned int count)
{
	struct hsi_dev *hsi_ctrl = hsi_channel->hsi_port->hsi_controller;
	void __iomem *base = hsi_ctrl->base;
	unsigned int port = hsi_channel->hsi_port->port_number;
	unsigned int channel = hsi_channel->channel_number;
	unsigned int sync;
	int lch;
	dma_addr_t src_addr;
	dma_addr_t dest_addr;
	u16 tmp;
	int fifo;

	lch = hsi_get_free_lch(hsi_ctrl);
	if (lch < 0) {
		dev_err(hsi_ctrl->dev, "No free DMA channels.\n");
		return -EBUSY;	/* No free GDD logical channels. */
	} else {
		dev_dbg(hsi_ctrl->dev, "Allocated DMA channel %d for read on"
					" HSI channel %d.\n", lch,
					hsi_channel->channel_number);
	}

	/* When DMA is used for Rx, disable the Rx Interrupt.
	 * (else DATAAVAILLABLE event would get triggered on first
	 * received data word)
	 * (Rx interrupt might be active for polling feature)
	 */
	hsi_driver_disable_read_interrupt(hsi_channel);

	/*
	 * NOTE: Gettting a free gdd logical channel and
	 * reserve it must be done atomicaly.
	 */
	hsi_channel->read_data.lch = lch;

	/* Sync is required for SSI but not for HSI */
	sync = hsi_sync_table[HSI_SYNC_READ][port - 1][channel];

	dest_addr = dma_map_single(hsi_ctrl->dev, data, count * 4,
				  DMA_FROM_DEVICE);
	if (unlikely(dma_mapping_error(hsi_ctrl->dev, dest_addr))) {
		dev_err(hsi_ctrl->dev, "Failed to create DMA read mapping.\n");
		return -ENOMEM;
	}

	tmp = HSI_DST_BURST_4x32_BIT |
	    HSI_DST_MEMORY_PORT |
	    HSI_SRC_BURST_4x32_BIT |
	    HSI_SRC_PERIPHERAL_PORT | HSI_DATA_TYPE_S32;
	hsi_outw(tmp, base, HSI_GDD_CSDP_REG(lch));

	tmp = HSI_DST_AMODE_POSTINC | HSI_SRC_AMODE_CONST | sync;
	hsi_outw(tmp, base, HSI_GDD_CCR_REG(lch));

	hsi_outw((HSI_BLOCK_IE | HSI_TOUT_IE), base, HSI_GDD_CCIR_REG(lch));

	if (hsi_driver_device_is_hsi(to_platform_device(hsi_ctrl->dev))) {
		fifo = hsi_fifo_get_id(hsi_ctrl, channel, port);
		if (unlikely(fifo < 0)) {
			dev_err(hsi_ctrl->dev, "No valid FIFO id for DMA "
				"transfer from FIFO.\n");
			return -EFAULT;
		}
		/* HSI CSSA register takes a FIFO ID when copying from FIFO */
		hsi_outl(fifo, base, HSI_GDD_CSSA_REG(lch));
	} else{
		src_addr = hsi_ctrl->phy_base + HSI_HSR_BUFFER_CH_REG(port,
								channel);
		/* SSI CSSA register always takes a 32-bit address */
		hsi_outl(src_addr, base, HSI_GDD_CSSA_REG(lch));
	}

	/* HSI CDSA register takes a 32-bit address when copying to memory */
	/* SSI CDSA register always takes a 32-bit address */
	hsi_outl(dest_addr, base, HSI_GDD_CDSA_REG(lch));
	hsi_outw(count, base, HSI_GDD_CEN_REG(lch));

	/* TODO : Need to clean interrupt status here to avoid spurious int */

	hsi_outl_or(HSI_GDD_LCH(lch), base, HSI_SYS_GDD_MPU_IRQ_ENABLE_REG);
	hsi_outw_or(HSI_CCR_ENABLE, base, HSI_GDD_CCR_REG(lch));

	return 0;
}
Beispiel #9
0
/**
 * hsi_driver_write_dma - Program GDD [DMA] to write data from memory to
 * the hsi channel buffer.
 * @hsi_channel - pointer to the hsi_channel to write data to.
 * @data - 32-bit word pointer to the data.
 * @size - Number of 32bit words to be transfered.
 *
 * hsi_controller lock must be held before calling this function.
 *
 * Return 0 on success and < 0 on error.
 */
int hsi_driver_write_dma(struct hsi_channel *hsi_channel, u32 * data,
			 unsigned int size)
{
	struct hsi_dev *hsi_ctrl = hsi_channel->hsi_port->hsi_controller;
	void __iomem *base = hsi_ctrl->base;
	unsigned int port = hsi_channel->hsi_port->port_number;
	unsigned int channel = hsi_channel->channel_number;
	unsigned int sync;
	int lch;
	dma_addr_t src_addr;
	dma_addr_t dest_addr;
	u16 tmp;
	int fifo;

	if ((size < 1) || (data == NULL))
		return -EINVAL;

	lch = hsi_get_free_lch(hsi_ctrl);
	if (lch < 0) {
		dev_err(hsi_ctrl->dev, "No free DMA channels.\n");
		return -EBUSY;	/* No free GDD logical channels. */
	} else {
		dev_dbg(hsi_ctrl->dev, "Allocated DMA channel %d for write on"
					" HSI channel %d.\n", lch,
					hsi_channel->channel_number);
	}

	/* NOTE: Getting a free gdd logical channel and
	 * reserve it must be done atomicaly. */
	hsi_channel->write_data.lch = lch;

	/* Sync is required for SSI but not for HSI */
	sync = hsi_sync_table[HSI_SYNC_WRITE][port - 1][channel];

	src_addr = dma_map_single(hsi_ctrl->dev, data, size * 4, DMA_TO_DEVICE);
	if (unlikely(dma_mapping_error(hsi_ctrl->dev, src_addr))) {
		dev_err(hsi_ctrl->dev, "Failed to create DMA write mapping.\n");
		return -ENOMEM;
	}

	tmp = HSI_SRC_BURST_4x32_BIT|
	    HSI_SRC_MEMORY_PORT |
	    HSI_DST_BURST_4x32_BIT |
	    HSI_DST_PERIPHERAL_PORT | HSI_DATA_TYPE_S32;
	hsi_outw(tmp, base, HSI_GDD_CSDP_REG(lch));

	tmp = HSI_SRC_AMODE_POSTINC | HSI_DST_AMODE_CONST | sync;
	hsi_outw(tmp, base, HSI_GDD_CCR_REG(lch));

	hsi_outw((HSI_BLOCK_IE | HSI_TOUT_IE), base, HSI_GDD_CCIR_REG(lch));

	if (hsi_driver_device_is_hsi(to_platform_device(hsi_ctrl->dev))) {
		fifo = hsi_fifo_get_id(hsi_ctrl, channel, port);
		if (unlikely(fifo < 0)) {
			dev_err(hsi_ctrl->dev, "No valid FIFO id for DMA "
				"transfer to FIFO.\n");
			return -EFAULT;
		}
		/* HSI CDSA register takes a FIFO ID when copying to FIFO */
		hsi_outl(fifo, base, HSI_GDD_CDSA_REG(lch));
	} else {
		dest_addr = hsi_ctrl->phy_base + HSI_HST_BUFFER_CH_REG(port,
								channel);
		/* SSI CDSA register always takes a 32-bit address */
		hsi_outl(dest_addr, base, HSI_GDD_CDSA_REG(lch));
	}

	/* HSI CSSA register takes a 32-bit address when copying from memory */
	/* SSI CSSA register always takes a 32-bit address */
	hsi_outl(src_addr, base, HSI_GDD_CSSA_REG(lch));
	hsi_outw(size, base, HSI_GDD_CEN_REG(lch));

	/* TODO : Need to clean interrupt status here to avoid spurious int */

	hsi_outl_or(HSI_GDD_LCH(lch), base, HSI_SYS_GDD_MPU_IRQ_ENABLE_REG);
	hsi_outw_or(HSI_CCR_ENABLE, base, HSI_GDD_CCR_REG(lch));

	return 0;
}