Commit | Line | Data |
---|---|---|
bb8822cb PYM |
1 | /* |
2 | * i2c-stm32.c | |
3 | * | |
4 | * Copyright (C) M'boumba Cedric Madianga 2017 | |
5 | * Author: M'boumba Cedric Madianga <cedric.madianga@gmail.com> | |
6 | * | |
7 | * License terms: GNU General Public License (GPL), version 2 | |
8 | */ | |
9 | ||
10 | #include "i2c-stm32.h" | |
11 | ||
12 | /* Functions for DMA support */ | |
13 | struct stm32_i2c_dma *stm32_i2c_dma_request(struct device *dev, | |
14 | dma_addr_t phy_addr, | |
15 | u32 txdr_offset, | |
16 | u32 rxdr_offset) | |
17 | { | |
18 | struct stm32_i2c_dma *dma; | |
19 | struct dma_slave_config dma_sconfig; | |
20 | int ret; | |
21 | ||
22 | dma = devm_kzalloc(dev, sizeof(*dma), GFP_KERNEL); | |
23 | if (!dma) | |
24 | return NULL; | |
25 | ||
26 | /* Request and configure I2C TX dma channel */ | |
27 | dma->chan_tx = dma_request_slave_channel(dev, "tx"); | |
28 | if (!dma->chan_tx) { | |
29 | dev_dbg(dev, "can't request DMA tx channel\n"); | |
30 | ret = -EINVAL; | |
31 | goto fail_al; | |
32 | } | |
33 | ||
34 | memset(&dma_sconfig, 0, sizeof(dma_sconfig)); | |
35 | dma_sconfig.dst_addr = phy_addr + txdr_offset; | |
36 | dma_sconfig.dst_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE; | |
37 | dma_sconfig.dst_maxburst = 1; | |
38 | dma_sconfig.direction = DMA_MEM_TO_DEV; | |
39 | ret = dmaengine_slave_config(dma->chan_tx, &dma_sconfig); | |
40 | if (ret < 0) { | |
41 | dev_err(dev, "can't configure tx channel\n"); | |
42 | goto fail_tx; | |
43 | } | |
44 | ||
45 | /* Request and configure I2C RX dma channel */ | |
46 | dma->chan_rx = dma_request_slave_channel(dev, "rx"); | |
47 | if (!dma->chan_rx) { | |
48 | dev_err(dev, "can't request DMA rx channel\n"); | |
49 | ret = -EINVAL; | |
50 | goto fail_tx; | |
51 | } | |
52 | ||
53 | memset(&dma_sconfig, 0, sizeof(dma_sconfig)); | |
54 | dma_sconfig.src_addr = phy_addr + rxdr_offset; | |
55 | dma_sconfig.src_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE; | |
56 | dma_sconfig.src_maxburst = 1; | |
57 | dma_sconfig.direction = DMA_DEV_TO_MEM; | |
58 | ret = dmaengine_slave_config(dma->chan_rx, &dma_sconfig); | |
59 | if (ret < 0) { | |
60 | dev_err(dev, "can't configure rx channel\n"); | |
61 | goto fail_rx; | |
62 | } | |
63 | ||
64 | init_completion(&dma->dma_complete); | |
65 | ||
66 | dev_info(dev, "using %s (tx) and %s (rx) for DMA transfers\n", | |
67 | dma_chan_name(dma->chan_tx), dma_chan_name(dma->chan_rx)); | |
68 | ||
69 | return dma; | |
70 | ||
71 | fail_rx: | |
72 | dma_release_channel(dma->chan_rx); | |
73 | fail_tx: | |
74 | dma_release_channel(dma->chan_tx); | |
75 | fail_al: | |
76 | devm_kfree(dev, dma); | |
77 | dev_info(dev, "can't use DMA\n"); | |
78 | ||
79 | return NULL; | |
80 | } | |
81 | ||
82 | void stm32_i2c_dma_free(struct stm32_i2c_dma *dma) | |
83 | { | |
84 | dma->dma_buf = 0; | |
85 | dma->dma_len = 0; | |
86 | ||
87 | dma_release_channel(dma->chan_tx); | |
88 | dma->chan_tx = NULL; | |
89 | ||
90 | dma_release_channel(dma->chan_rx); | |
91 | dma->chan_rx = NULL; | |
92 | ||
93 | dma->chan_using = NULL; | |
94 | } | |
95 | ||
96 | int stm32_i2c_prep_dma_xfer(struct device *dev, struct stm32_i2c_dma *dma, | |
97 | bool rd_wr, u32 len, u8 *buf, | |
98 | dma_async_tx_callback callback, | |
99 | void *dma_async_param) | |
100 | { | |
101 | struct dma_async_tx_descriptor *txdesc; | |
102 | struct device *chan_dev; | |
103 | int ret; | |
104 | ||
105 | if (rd_wr) { | |
106 | dma->chan_using = dma->chan_rx; | |
107 | dma->dma_transfer_dir = DMA_DEV_TO_MEM; | |
108 | dma->dma_data_dir = DMA_FROM_DEVICE; | |
109 | } else { | |
110 | dma->chan_using = dma->chan_tx; | |
111 | dma->dma_transfer_dir = DMA_MEM_TO_DEV; | |
112 | dma->dma_data_dir = DMA_TO_DEVICE; | |
113 | } | |
114 | ||
115 | dma->dma_len = len; | |
116 | chan_dev = dma->chan_using->device->dev; | |
117 | ||
118 | dma->dma_buf = dma_map_single(chan_dev, buf, dma->dma_len, | |
119 | dma->dma_data_dir); | |
120 | if (dma_mapping_error(chan_dev, dma->dma_buf)) { | |
121 | dev_err(dev, "DMA mapping failed\n"); | |
122 | return -EINVAL; | |
123 | } | |
124 | ||
125 | txdesc = dmaengine_prep_slave_single(dma->chan_using, dma->dma_buf, | |
126 | dma->dma_len, | |
127 | dma->dma_transfer_dir, | |
128 | DMA_PREP_INTERRUPT); | |
129 | if (!txdesc) { | |
130 | dev_err(dev, "Not able to get desc for DMA xfer\n"); | |
131 | ret = -EINVAL; | |
132 | goto err; | |
133 | } | |
134 | ||
135 | reinit_completion(&dma->dma_complete); | |
136 | ||
137 | txdesc->callback = callback; | |
138 | txdesc->callback_param = dma_async_param; | |
139 | ret = dma_submit_error(dmaengine_submit(txdesc)); | |
140 | if (ret < 0) { | |
141 | dev_err(dev, "DMA submit failed\n"); | |
142 | goto err; | |
143 | } | |
144 | ||
145 | dma_async_issue_pending(dma->chan_using); | |
146 | ||
147 | return 0; | |
148 | ||
149 | err: | |
150 | dma_unmap_single(chan_dev, dma->dma_buf, dma->dma_len, | |
151 | dma->dma_data_dir); | |
152 | return ret; | |
153 | } |