Загрузка данных


static irqreturn_t lpi2c_imx_target_isr(struct lpi2c_imx_struct *lpi2c_imx,
					   u32 ssr, u32 sier_filter)
{
	u8 value;
	u32 sasr;

	if (sier_filter & SSR_BEF) { /* Arbitration lost */
		writel(0, lpi2c_imx->base + LPI2C_SIER);
		return IRQ_HANDLED;
	}

	/* address detected */
	if (sier_filter & SSR_AVF) {
		sasr = readl(lpi2c_imx->base + LPI2C_SASR);
		if (SASR_READ_REQ & sasr) {
			/*controller give a read request and send first value with start*/
			i2c_slave_event(lpi2c_imx->target, I2C_SLAVE_READ_REQUESTED, &value);
			writel(value, lpi2c_imx->base + LPI2C_STDR);
			goto ret;
		} else {
			/*controller request to write to us*/
			i2c_slave_event(lpi2c_imx->target, I2C_SLAVE_WRITE_REQUESTED, &value);
		}
	}

	if (sier_filter & SSR_SDF) {
		/* STOP */
		i2c_slave_event(lpi2c_imx->target, I2C_SLAVE_STOP, &value);
	}

	if (sier_filter & SSR_TDF) {
		/* controller wants to read from us */
		i2c_slave_event(lpi2c_imx->target, I2C_SLAVE_READ_PROCESSED, &value);
		writel(value, lpi2c_imx->base + LPI2C_STDR);
	}

	if (sier_filter & SSR_RDF) {
		/* controller wants to send data to us */
		value = readl(lpi2c_imx->base + LPI2C_SRDR);
		i2c_slave_event(lpi2c_imx->target, I2C_SLAVE_WRITE_RECEIVED, &value);
	}

ret:
	/* Clear SSR, too, because of old STOPs to other clients than us */
	writel(ssr & SSR_CLEAR_BITS, lpi2c_imx->base + LPI2C_SSR);
	return IRQ_HANDLED;
}

static irqreturn_t lpi2c_imx_master_isr(struct lpi2c_imx_struct *lpi2c_imx)
{
	unsigned int enabled;
	unsigned int temp;

	enabled = readl(lpi2c_imx->base + LPI2C_MIER);

	lpi2c_imx_intctrl(lpi2c_imx, 0);
	temp = readl(lpi2c_imx->base + LPI2C_MSR);
	temp &= enabled;

	if (temp & MSR_NDF)
		complete(&lpi2c_imx->complete);
	else if (temp & MSR_RDF)
		lpi2c_imx_read_rxfifo(lpi2c_imx);
	else if (temp & MSR_TDF)
		lpi2c_imx_write_txfifo(lpi2c_imx);

	return IRQ_HANDLED;
}

static irqreturn_t lpi2c_imx_isr(int irq, void *dev_id)
{
	struct lpi2c_imx_struct *lpi2c_imx = dev_id;
	u32 ssr, sier_filter;
	unsigned int scr;

	if (lpi2c_imx->target) {
		scr = readl(lpi2c_imx->base + LPI2C_SCR);
		ssr = readl(lpi2c_imx->base + LPI2C_SSR);
		sier_filter = ssr & readl(lpi2c_imx->base + LPI2C_SIER);
		if ((scr & SCR_SEN) && sier_filter)
			return lpi2c_imx_target_isr(lpi2c_imx, ssr, sier_filter);
		else
			return lpi2c_imx_master_isr(lpi2c_imx);
	} else {
		return lpi2c_imx_master_isr(lpi2c_imx);
	}
}

static void lpi2c_imx_target_init(struct lpi2c_imx_struct *lpi2c_imx)
{
	int temp;

	/* reset target module */
	writel(SCR_RST, lpi2c_imx->base + LPI2C_SCR);
	writel(0, lpi2c_imx->base + LPI2C_SCR);

	/* Set target addr */
	writel((lpi2c_imx->target->addr << 1), lpi2c_imx->base + LPI2C_SAMR);

	writel(SCFGR1_RXSTALL | SCFGR1_TXDSTALL, lpi2c_imx->base + LPI2C_SCFGR1);

	/*
	 * set SCFGR2: FILTSDA, FILTSCL and CLKHOLD
	 *
	 * FILTSCL/FILTSDA can eliminate signal skew. It should generally be
	 * set to the same value and should be set >= 50ns.
	 *
	 * CLKHOLD is only used when clock stretching is enabled, but it will
	 * extend the clock stretching to ensure there is an additional delay
	 * between the target driving SDA and the target releasing the SCL pin.
	 *
	 * CLKHOLD setting is crucial for lpi2c target. When master read data
	 * from target, if there is a delay caused by cpu idle, excessive load,
	 * or other delays between two bytes in one message transmission. so it
	 * will cause a short interval time between the driving SDA signal and
	 * releasing SCL signal. Lpi2c master will mistakenly think it is a stop
	 * signal resulting in an arbitration failure. This issue can be avoided
	 * by setting CLKHOLD.
	 *
	 * In order to ensure lpi2c function normally when the lpi2c speed is as
	 * low as 100kHz, CLKHOLD should be set 3 and it is also compatible with
	 * higher clock frequency like 400kHz and 1MHz.
	 */
	temp = SCFGR2_FILTSDA(2) | SCFGR2_FILTSCL(2) | SCFGR2_CLKHOLD(3);
	writel(temp, lpi2c_imx->base + LPI2C_SCFGR2);

	/*
	 * Enable module:
	 * SCR_FILTEN can enable digital filter and output delay counter for LPI2C
	 * target mode. So SCR_FILTEN need be asserted when enable SDA/SCL FILTER
	 * and CLKHOLD.
	 */
	writel(SCR_SEN | SCR_FILTEN, lpi2c_imx->base + LPI2C_SCR);

	/* Enable interrupt from i2c module */
	writel(SLAVE_INT_FLAG, lpi2c_imx->base + LPI2C_SIER);
}

static int lpi2c_imx_reg_target(struct i2c_client *client)
{
	struct lpi2c_imx_struct *lpi2c_imx = i2c_get_adapdata(client->adapter);
	int ret;

	if (lpi2c_imx->target)
		return -EBUSY;

	lpi2c_imx->target = client;

	ret = pm_runtime_resume_and_get(lpi2c_imx->adapter.dev.parent);
	if (ret < 0) {
		dev_err(&lpi2c_imx->adapter.dev, "failed to resume i2c controller");
		return ret;
	}

	lpi2c_imx_target_init(lpi2c_imx);

	return 0;
}

static int lpi2c_imx_unreg_target(struct i2c_client *client)
{
	struct lpi2c_imx_struct *lpi2c_imx = i2c_get_adapdata(client->adapter);
	int ret;

	if (!lpi2c_imx->target)
		return -EINVAL;

	/* Reset target address. */
	writel(0, lpi2c_imx->base + LPI2C_SAMR);

	writel(SCR_RST, lpi2c_imx->base + LPI2C_SCR);
	writel(0, lpi2c_imx->base + LPI2C_SCR);

	lpi2c_imx->target = NULL;

	ret = pm_runtime_put_sync(lpi2c_imx->adapter.dev.parent);
	if (ret < 0)
		dev_err(&lpi2c_imx->adapter.dev, "failed to suspend i2c controller");

	return ret;
}

static int lpi2c_imx_init_recovery_info(struct lpi2c_imx_struct *lpi2c_imx,
				  struct platform_device *pdev)
{
	struct i2c_bus_recovery_info *bri = &lpi2c_imx->rinfo;

	bri->pinctrl = devm_pinctrl_get(&pdev->dev);
	if (IS_ERR(bri->pinctrl))
		return PTR_ERR(bri->pinctrl);

	lpi2c_imx->adapter.bus_recovery_info = bri;

	return 0;
}

static void dma_exit(struct device *dev, struct lpi2c_imx_dma *dma)
{
	if (dma->chan_rx)
		dma_release_channel(dma->chan_rx);

	if (dma->chan_tx)
		dma_release_channel(dma->chan_tx);

	devm_kfree(dev, dma);
}

static int lpi2c_dma_init(struct device *dev, dma_addr_t phy_addr)
{
	struct lpi2c_imx_struct *lpi2c_imx = dev_get_drvdata(dev);
	struct lpi2c_imx_dma *dma;
	int ret;

	dma = devm_kzalloc(dev, sizeof(*dma), GFP_KERNEL);
	if (!dma)
		return -ENOMEM;

	dma->phy_addr = phy_addr;

	/* Prepare for TX DMA: */
	dma->chan_tx = dma_request_chan(dev, "tx");
	if (IS_ERR(dma->chan_tx)) {
		ret = PTR_ERR(dma->chan_tx);
		if (ret != -ENODEV && ret != -EPROBE_DEFER)
			dev_err(dev, "can't request DMA tx channel (%d)\n", ret);
		dma->chan_tx = NULL;
		goto dma_exit;
	}

	/* Prepare for RX DMA: */
	dma->chan_rx = dma_request_chan(dev, "rx");
	if (IS_ERR(dma->chan_rx)) {
		ret = PTR_ERR(dma->chan_rx);
		if (ret != -ENODEV && ret != -EPROBE_DEFER)
			dev_err(dev, "can't request DMA rx channel (%d)\n", ret);
		dma->chan_rx = NULL;
		goto dma_exit;
	}

	lpi2c_imx->can_use_dma = true;
	lpi2c_imx->dma = dma;
	return 0;

dma_exit:
	dma_exit(dev, dma);
	return ret;
}