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