Загрузка данных
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;
}