162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * i2c-stm32.c
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (C) M'boumba Cedric Madianga 2017
662306a36Sopenharmony_ci * Author: M'boumba Cedric Madianga <cedric.madianga@gmail.com>
762306a36Sopenharmony_ci */
862306a36Sopenharmony_ci
962306a36Sopenharmony_ci#include "i2c-stm32.h"
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_ci/* Functions for DMA support */
1262306a36Sopenharmony_cistruct stm32_i2c_dma *stm32_i2c_dma_request(struct device *dev,
1362306a36Sopenharmony_ci					    dma_addr_t phy_addr,
1462306a36Sopenharmony_ci					    u32 txdr_offset,
1562306a36Sopenharmony_ci					    u32 rxdr_offset)
1662306a36Sopenharmony_ci{
1762306a36Sopenharmony_ci	struct stm32_i2c_dma *dma;
1862306a36Sopenharmony_ci	struct dma_slave_config dma_sconfig;
1962306a36Sopenharmony_ci	int ret;
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_ci	dma = devm_kzalloc(dev, sizeof(*dma), GFP_KERNEL);
2262306a36Sopenharmony_ci	if (!dma)
2362306a36Sopenharmony_ci		return ERR_PTR(-ENOMEM);
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_ci	/* Request and configure I2C TX dma channel */
2662306a36Sopenharmony_ci	dma->chan_tx = dma_request_chan(dev, "tx");
2762306a36Sopenharmony_ci	if (IS_ERR(dma->chan_tx)) {
2862306a36Sopenharmony_ci		ret = PTR_ERR(dma->chan_tx);
2962306a36Sopenharmony_ci		if (ret != -ENODEV)
3062306a36Sopenharmony_ci			ret = dev_err_probe(dev, ret,
3162306a36Sopenharmony_ci					    "can't request DMA tx channel\n");
3262306a36Sopenharmony_ci		goto fail_al;
3362306a36Sopenharmony_ci	}
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_ci	memset(&dma_sconfig, 0, sizeof(dma_sconfig));
3662306a36Sopenharmony_ci	dma_sconfig.dst_addr = phy_addr + txdr_offset;
3762306a36Sopenharmony_ci	dma_sconfig.dst_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE;
3862306a36Sopenharmony_ci	dma_sconfig.dst_maxburst = 1;
3962306a36Sopenharmony_ci	dma_sconfig.direction = DMA_MEM_TO_DEV;
4062306a36Sopenharmony_ci	ret = dmaengine_slave_config(dma->chan_tx, &dma_sconfig);
4162306a36Sopenharmony_ci	if (ret < 0) {
4262306a36Sopenharmony_ci		dev_err(dev, "can't configure tx channel\n");
4362306a36Sopenharmony_ci		goto fail_tx;
4462306a36Sopenharmony_ci	}
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_ci	/* Request and configure I2C RX dma channel */
4762306a36Sopenharmony_ci	dma->chan_rx = dma_request_chan(dev, "rx");
4862306a36Sopenharmony_ci	if (IS_ERR(dma->chan_rx)) {
4962306a36Sopenharmony_ci		ret = PTR_ERR(dma->chan_rx);
5062306a36Sopenharmony_ci		if (ret != -ENODEV)
5162306a36Sopenharmony_ci			ret = dev_err_probe(dev, ret,
5262306a36Sopenharmony_ci					    "can't request DMA rx channel\n");
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_ci		goto fail_tx;
5562306a36Sopenharmony_ci	}
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_ci	memset(&dma_sconfig, 0, sizeof(dma_sconfig));
5862306a36Sopenharmony_ci	dma_sconfig.src_addr = phy_addr + rxdr_offset;
5962306a36Sopenharmony_ci	dma_sconfig.src_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE;
6062306a36Sopenharmony_ci	dma_sconfig.src_maxburst = 1;
6162306a36Sopenharmony_ci	dma_sconfig.direction = DMA_DEV_TO_MEM;
6262306a36Sopenharmony_ci	ret = dmaengine_slave_config(dma->chan_rx, &dma_sconfig);
6362306a36Sopenharmony_ci	if (ret < 0) {
6462306a36Sopenharmony_ci		dev_err(dev, "can't configure rx channel\n");
6562306a36Sopenharmony_ci		goto fail_rx;
6662306a36Sopenharmony_ci	}
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_ci	init_completion(&dma->dma_complete);
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_ci	dev_info(dev, "using %s (tx) and %s (rx) for DMA transfers\n",
7162306a36Sopenharmony_ci		 dma_chan_name(dma->chan_tx), dma_chan_name(dma->chan_rx));
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_ci	return dma;
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_cifail_rx:
7662306a36Sopenharmony_ci	dma_release_channel(dma->chan_rx);
7762306a36Sopenharmony_cifail_tx:
7862306a36Sopenharmony_ci	dma_release_channel(dma->chan_tx);
7962306a36Sopenharmony_cifail_al:
8062306a36Sopenharmony_ci	devm_kfree(dev, dma);
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_ci	return ERR_PTR(ret);
8362306a36Sopenharmony_ci}
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_civoid stm32_i2c_dma_free(struct stm32_i2c_dma *dma)
8662306a36Sopenharmony_ci{
8762306a36Sopenharmony_ci	dma->dma_buf = 0;
8862306a36Sopenharmony_ci	dma->dma_len = 0;
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_ci	dma_release_channel(dma->chan_tx);
9162306a36Sopenharmony_ci	dma->chan_tx = NULL;
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_ci	dma_release_channel(dma->chan_rx);
9462306a36Sopenharmony_ci	dma->chan_rx = NULL;
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_ci	dma->chan_using = NULL;
9762306a36Sopenharmony_ci}
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_ciint stm32_i2c_prep_dma_xfer(struct device *dev, struct stm32_i2c_dma *dma,
10062306a36Sopenharmony_ci			    bool rd_wr, u32 len, u8 *buf,
10162306a36Sopenharmony_ci			    dma_async_tx_callback callback,
10262306a36Sopenharmony_ci			    void *dma_async_param)
10362306a36Sopenharmony_ci{
10462306a36Sopenharmony_ci	struct dma_async_tx_descriptor *txdesc;
10562306a36Sopenharmony_ci	struct device *chan_dev;
10662306a36Sopenharmony_ci	int ret;
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_ci	if (rd_wr) {
10962306a36Sopenharmony_ci		dma->chan_using = dma->chan_rx;
11062306a36Sopenharmony_ci		dma->dma_transfer_dir = DMA_DEV_TO_MEM;
11162306a36Sopenharmony_ci		dma->dma_data_dir = DMA_FROM_DEVICE;
11262306a36Sopenharmony_ci	} else {
11362306a36Sopenharmony_ci		dma->chan_using = dma->chan_tx;
11462306a36Sopenharmony_ci		dma->dma_transfer_dir = DMA_MEM_TO_DEV;
11562306a36Sopenharmony_ci		dma->dma_data_dir = DMA_TO_DEVICE;
11662306a36Sopenharmony_ci	}
11762306a36Sopenharmony_ci
11862306a36Sopenharmony_ci	dma->dma_len = len;
11962306a36Sopenharmony_ci	chan_dev = dma->chan_using->device->dev;
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_ci	dma->dma_buf = dma_map_single(chan_dev, buf, dma->dma_len,
12262306a36Sopenharmony_ci				      dma->dma_data_dir);
12362306a36Sopenharmony_ci	if (dma_mapping_error(chan_dev, dma->dma_buf)) {
12462306a36Sopenharmony_ci		dev_err(dev, "DMA mapping failed\n");
12562306a36Sopenharmony_ci		return -EINVAL;
12662306a36Sopenharmony_ci	}
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_ci	txdesc = dmaengine_prep_slave_single(dma->chan_using, dma->dma_buf,
12962306a36Sopenharmony_ci					     dma->dma_len,
13062306a36Sopenharmony_ci					     dma->dma_transfer_dir,
13162306a36Sopenharmony_ci					     DMA_PREP_INTERRUPT);
13262306a36Sopenharmony_ci	if (!txdesc) {
13362306a36Sopenharmony_ci		dev_err(dev, "Not able to get desc for DMA xfer\n");
13462306a36Sopenharmony_ci		ret = -EINVAL;
13562306a36Sopenharmony_ci		goto err;
13662306a36Sopenharmony_ci	}
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_ci	reinit_completion(&dma->dma_complete);
13962306a36Sopenharmony_ci
14062306a36Sopenharmony_ci	txdesc->callback = callback;
14162306a36Sopenharmony_ci	txdesc->callback_param = dma_async_param;
14262306a36Sopenharmony_ci	ret = dma_submit_error(dmaengine_submit(txdesc));
14362306a36Sopenharmony_ci	if (ret < 0) {
14462306a36Sopenharmony_ci		dev_err(dev, "DMA submit failed\n");
14562306a36Sopenharmony_ci		goto err;
14662306a36Sopenharmony_ci	}
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_ci	dma_async_issue_pending(dma->chan_using);
14962306a36Sopenharmony_ci
15062306a36Sopenharmony_ci	return 0;
15162306a36Sopenharmony_ci
15262306a36Sopenharmony_cierr:
15362306a36Sopenharmony_ci	dma_unmap_single(chan_dev, dma->dma_buf, dma->dma_len,
15462306a36Sopenharmony_ci			 dma->dma_data_dir);
15562306a36Sopenharmony_ci	return ret;
15662306a36Sopenharmony_ci}
157