diff options
Diffstat (limited to 'drivers/i2c/busses/i2c-stm32.c')
-rw-r--r-- | drivers/i2c/busses/i2c-stm32.c | 153 |
1 files changed, 153 insertions, 0 deletions
diff --git a/drivers/i2c/busses/i2c-stm32.c b/drivers/i2c/busses/i2c-stm32.c new file mode 100644 index 000000000000..d75fbcbf02ef --- /dev/null +++ b/drivers/i2c/busses/i2c-stm32.c | |||
@@ -0,0 +1,153 @@ | |||
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 | } | ||