1d7bff893SBaolin Wang // SPDX-License-Identifier: GPL-2.0
2d7bff893SBaolin Wang // Copyright (C) 2019 Spreadtrum Communications Inc.
3d7bff893SBaolin Wang
4d7bff893SBaolin Wang #include <linux/errno.h>
5d7bff893SBaolin Wang #include <linux/interrupt.h>
6d7bff893SBaolin Wang #include <linux/io.h>
7d7bff893SBaolin Wang #include <linux/kernel.h>
8d7bff893SBaolin Wang #include <linux/module.h>
9d7bff893SBaolin Wang #include <linux/mutex.h>
10d7bff893SBaolin Wang #include <linux/of.h>
11d7bff893SBaolin Wang #include <linux/platform_device.h>
12d7bff893SBaolin Wang #include <linux/spinlock.h>
13d7bff893SBaolin Wang
14d7bff893SBaolin Wang #include "sprd-mcdt.h"
15d7bff893SBaolin Wang
16d7bff893SBaolin Wang /* MCDT registers definition */
17d7bff893SBaolin Wang #define MCDT_CH0_TXD 0x0
18d7bff893SBaolin Wang #define MCDT_CH0_RXD 0x28
19d7bff893SBaolin Wang #define MCDT_DAC0_WTMK 0x60
20d7bff893SBaolin Wang #define MCDT_ADC0_WTMK 0x88
21d7bff893SBaolin Wang #define MCDT_DMA_EN 0xb0
22d7bff893SBaolin Wang
23d7bff893SBaolin Wang #define MCDT_INT_EN0 0xb4
24d7bff893SBaolin Wang #define MCDT_INT_EN1 0xb8
25d7bff893SBaolin Wang #define MCDT_INT_EN2 0xbc
26d7bff893SBaolin Wang
27d7bff893SBaolin Wang #define MCDT_INT_CLR0 0xc0
28d7bff893SBaolin Wang #define MCDT_INT_CLR1 0xc4
29d7bff893SBaolin Wang #define MCDT_INT_CLR2 0xc8
30d7bff893SBaolin Wang
31d7bff893SBaolin Wang #define MCDT_INT_RAW1 0xcc
32d7bff893SBaolin Wang #define MCDT_INT_RAW2 0xd0
33d7bff893SBaolin Wang #define MCDT_INT_RAW3 0xd4
34d7bff893SBaolin Wang
35d7bff893SBaolin Wang #define MCDT_INT_MSK1 0xd8
36d7bff893SBaolin Wang #define MCDT_INT_MSK2 0xdc
37d7bff893SBaolin Wang #define MCDT_INT_MSK3 0xe0
38d7bff893SBaolin Wang
39d7bff893SBaolin Wang #define MCDT_DAC0_FIFO_ADDR_ST 0xe4
40d7bff893SBaolin Wang #define MCDT_ADC0_FIFO_ADDR_ST 0xe8
41d7bff893SBaolin Wang
42d7bff893SBaolin Wang #define MCDT_CH_FIFO_ST0 0x134
43d7bff893SBaolin Wang #define MCDT_CH_FIFO_ST1 0x138
44d7bff893SBaolin Wang #define MCDT_CH_FIFO_ST2 0x13c
45d7bff893SBaolin Wang
46d7bff893SBaolin Wang #define MCDT_INT_MSK_CFG0 0x140
47d7bff893SBaolin Wang #define MCDT_INT_MSK_CFG1 0x144
48d7bff893SBaolin Wang
49d7bff893SBaolin Wang #define MCDT_DMA_CFG0 0x148
50d7bff893SBaolin Wang #define MCDT_FIFO_CLR 0x14c
51d7bff893SBaolin Wang #define MCDT_DMA_CFG1 0x150
52d7bff893SBaolin Wang #define MCDT_DMA_CFG2 0x154
53d7bff893SBaolin Wang #define MCDT_DMA_CFG3 0x158
54d7bff893SBaolin Wang #define MCDT_DMA_CFG4 0x15c
55d7bff893SBaolin Wang #define MCDT_DMA_CFG5 0x160
56d7bff893SBaolin Wang
57d7bff893SBaolin Wang /* Channel water mark definition */
58d7bff893SBaolin Wang #define MCDT_CH_FIFO_AE_SHIFT 16
59d7bff893SBaolin Wang #define MCDT_CH_FIFO_AE_MASK GENMASK(24, 16)
60d7bff893SBaolin Wang #define MCDT_CH_FIFO_AF_MASK GENMASK(8, 0)
61d7bff893SBaolin Wang
62d7bff893SBaolin Wang /* DMA channel select definition */
63d7bff893SBaolin Wang #define MCDT_DMA_CH0_SEL_MASK GENMASK(3, 0)
64d7bff893SBaolin Wang #define MCDT_DMA_CH0_SEL_SHIFT 0
65d7bff893SBaolin Wang #define MCDT_DMA_CH1_SEL_MASK GENMASK(7, 4)
66d7bff893SBaolin Wang #define MCDT_DMA_CH1_SEL_SHIFT 4
67d7bff893SBaolin Wang #define MCDT_DMA_CH2_SEL_MASK GENMASK(11, 8)
68d7bff893SBaolin Wang #define MCDT_DMA_CH2_SEL_SHIFT 8
69d7bff893SBaolin Wang #define MCDT_DMA_CH3_SEL_MASK GENMASK(15, 12)
70d7bff893SBaolin Wang #define MCDT_DMA_CH3_SEL_SHIFT 12
71d7bff893SBaolin Wang #define MCDT_DMA_CH4_SEL_MASK GENMASK(19, 16)
72d7bff893SBaolin Wang #define MCDT_DMA_CH4_SEL_SHIFT 16
73d7bff893SBaolin Wang #define MCDT_DAC_DMA_SHIFT 16
74d7bff893SBaolin Wang
75d7bff893SBaolin Wang /* DMA channel ACK select definition */
76d7bff893SBaolin Wang #define MCDT_DMA_ACK_SEL_MASK GENMASK(3, 0)
77d7bff893SBaolin Wang
78d7bff893SBaolin Wang /* Channel FIFO definition */
79d7bff893SBaolin Wang #define MCDT_CH_FIFO_ADDR_SHIFT 16
80d7bff893SBaolin Wang #define MCDT_CH_FIFO_ADDR_MASK GENMASK(9, 0)
81d7bff893SBaolin Wang #define MCDT_ADC_FIFO_SHIFT 16
82d7bff893SBaolin Wang #define MCDT_FIFO_LENGTH 512
83d7bff893SBaolin Wang
84d7bff893SBaolin Wang #define MCDT_ADC_CHANNEL_NUM 10
85d7bff893SBaolin Wang #define MCDT_DAC_CHANNEL_NUM 10
86d7bff893SBaolin Wang #define MCDT_CHANNEL_NUM (MCDT_ADC_CHANNEL_NUM + MCDT_DAC_CHANNEL_NUM)
87d7bff893SBaolin Wang
88d7bff893SBaolin Wang enum sprd_mcdt_fifo_int {
89d7bff893SBaolin Wang MCDT_ADC_FIFO_AE_INT,
90d7bff893SBaolin Wang MCDT_ADC_FIFO_AF_INT,
91d7bff893SBaolin Wang MCDT_DAC_FIFO_AE_INT,
92d7bff893SBaolin Wang MCDT_DAC_FIFO_AF_INT,
93d7bff893SBaolin Wang MCDT_ADC_FIFO_OV_INT,
94d7bff893SBaolin Wang MCDT_DAC_FIFO_OV_INT
95d7bff893SBaolin Wang };
96d7bff893SBaolin Wang
97d7bff893SBaolin Wang enum sprd_mcdt_fifo_sts {
98d7bff893SBaolin Wang MCDT_ADC_FIFO_REAL_FULL,
99d7bff893SBaolin Wang MCDT_ADC_FIFO_REAL_EMPTY,
100d7bff893SBaolin Wang MCDT_ADC_FIFO_AF,
101d7bff893SBaolin Wang MCDT_ADC_FIFO_AE,
102d7bff893SBaolin Wang MCDT_DAC_FIFO_REAL_FULL,
103d7bff893SBaolin Wang MCDT_DAC_FIFO_REAL_EMPTY,
104d7bff893SBaolin Wang MCDT_DAC_FIFO_AF,
105d7bff893SBaolin Wang MCDT_DAC_FIFO_AE
106d7bff893SBaolin Wang };
107d7bff893SBaolin Wang
108d7bff893SBaolin Wang struct sprd_mcdt_dev {
109d7bff893SBaolin Wang struct device *dev;
110d7bff893SBaolin Wang void __iomem *base;
111d7bff893SBaolin Wang spinlock_t lock;
112d7bff893SBaolin Wang struct sprd_mcdt_chan chan[MCDT_CHANNEL_NUM];
113d7bff893SBaolin Wang };
114d7bff893SBaolin Wang
115d7bff893SBaolin Wang static LIST_HEAD(sprd_mcdt_chan_list);
116d7bff893SBaolin Wang static DEFINE_MUTEX(sprd_mcdt_list_mutex);
117d7bff893SBaolin Wang
sprd_mcdt_update(struct sprd_mcdt_dev * mcdt,u32 reg,u32 val,u32 mask)118d7bff893SBaolin Wang static void sprd_mcdt_update(struct sprd_mcdt_dev *mcdt, u32 reg, u32 val,
119d7bff893SBaolin Wang u32 mask)
120d7bff893SBaolin Wang {
121d7bff893SBaolin Wang u32 orig = readl_relaxed(mcdt->base + reg);
122d7bff893SBaolin Wang u32 tmp;
123d7bff893SBaolin Wang
124d7bff893SBaolin Wang tmp = (orig & ~mask) | val;
125d7bff893SBaolin Wang writel_relaxed(tmp, mcdt->base + reg);
126d7bff893SBaolin Wang }
127d7bff893SBaolin Wang
sprd_mcdt_dac_set_watermark(struct sprd_mcdt_dev * mcdt,u8 channel,u32 full,u32 empty)128d7bff893SBaolin Wang static void sprd_mcdt_dac_set_watermark(struct sprd_mcdt_dev *mcdt, u8 channel,
129d7bff893SBaolin Wang u32 full, u32 empty)
130d7bff893SBaolin Wang {
131d7bff893SBaolin Wang u32 reg = MCDT_DAC0_WTMK + channel * 4;
132d7bff893SBaolin Wang u32 water_mark =
133d7bff893SBaolin Wang (empty << MCDT_CH_FIFO_AE_SHIFT) & MCDT_CH_FIFO_AE_MASK;
134d7bff893SBaolin Wang
135d7bff893SBaolin Wang water_mark |= full & MCDT_CH_FIFO_AF_MASK;
136d7bff893SBaolin Wang sprd_mcdt_update(mcdt, reg, water_mark,
137d7bff893SBaolin Wang MCDT_CH_FIFO_AE_MASK | MCDT_CH_FIFO_AF_MASK);
138d7bff893SBaolin Wang }
139d7bff893SBaolin Wang
sprd_mcdt_adc_set_watermark(struct sprd_mcdt_dev * mcdt,u8 channel,u32 full,u32 empty)140d7bff893SBaolin Wang static void sprd_mcdt_adc_set_watermark(struct sprd_mcdt_dev *mcdt, u8 channel,
141d7bff893SBaolin Wang u32 full, u32 empty)
142d7bff893SBaolin Wang {
143d7bff893SBaolin Wang u32 reg = MCDT_ADC0_WTMK + channel * 4;
144d7bff893SBaolin Wang u32 water_mark =
145d7bff893SBaolin Wang (empty << MCDT_CH_FIFO_AE_SHIFT) & MCDT_CH_FIFO_AE_MASK;
146d7bff893SBaolin Wang
147d7bff893SBaolin Wang water_mark |= full & MCDT_CH_FIFO_AF_MASK;
148d7bff893SBaolin Wang sprd_mcdt_update(mcdt, reg, water_mark,
149d7bff893SBaolin Wang MCDT_CH_FIFO_AE_MASK | MCDT_CH_FIFO_AF_MASK);
150d7bff893SBaolin Wang }
151d7bff893SBaolin Wang
sprd_mcdt_dac_dma_enable(struct sprd_mcdt_dev * mcdt,u8 channel,bool enable)152d7bff893SBaolin Wang static void sprd_mcdt_dac_dma_enable(struct sprd_mcdt_dev *mcdt, u8 channel,
153d7bff893SBaolin Wang bool enable)
154d7bff893SBaolin Wang {
155d7bff893SBaolin Wang u32 shift = MCDT_DAC_DMA_SHIFT + channel;
156d7bff893SBaolin Wang
157d7bff893SBaolin Wang if (enable)
158d7bff893SBaolin Wang sprd_mcdt_update(mcdt, MCDT_DMA_EN, BIT(shift), BIT(shift));
159d7bff893SBaolin Wang else
160d7bff893SBaolin Wang sprd_mcdt_update(mcdt, MCDT_DMA_EN, 0, BIT(shift));
161d7bff893SBaolin Wang }
162d7bff893SBaolin Wang
sprd_mcdt_adc_dma_enable(struct sprd_mcdt_dev * mcdt,u8 channel,bool enable)163d7bff893SBaolin Wang static void sprd_mcdt_adc_dma_enable(struct sprd_mcdt_dev *mcdt, u8 channel,
164d7bff893SBaolin Wang bool enable)
165d7bff893SBaolin Wang {
166d7bff893SBaolin Wang if (enable)
167d7bff893SBaolin Wang sprd_mcdt_update(mcdt, MCDT_DMA_EN, BIT(channel), BIT(channel));
168d7bff893SBaolin Wang else
169d7bff893SBaolin Wang sprd_mcdt_update(mcdt, MCDT_DMA_EN, 0, BIT(channel));
170d7bff893SBaolin Wang }
171d7bff893SBaolin Wang
sprd_mcdt_ap_int_enable(struct sprd_mcdt_dev * mcdt,u8 channel,bool enable)172d7bff893SBaolin Wang static void sprd_mcdt_ap_int_enable(struct sprd_mcdt_dev *mcdt, u8 channel,
173d7bff893SBaolin Wang bool enable)
174d7bff893SBaolin Wang {
175d7bff893SBaolin Wang if (enable)
176d7bff893SBaolin Wang sprd_mcdt_update(mcdt, MCDT_INT_MSK_CFG0, BIT(channel),
177d7bff893SBaolin Wang BIT(channel));
178d7bff893SBaolin Wang else
179d7bff893SBaolin Wang sprd_mcdt_update(mcdt, MCDT_INT_MSK_CFG0, 0, BIT(channel));
180d7bff893SBaolin Wang }
181d7bff893SBaolin Wang
sprd_mcdt_dac_write_fifo(struct sprd_mcdt_dev * mcdt,u8 channel,u32 val)182d7bff893SBaolin Wang static void sprd_mcdt_dac_write_fifo(struct sprd_mcdt_dev *mcdt, u8 channel,
183d7bff893SBaolin Wang u32 val)
184d7bff893SBaolin Wang {
185d7bff893SBaolin Wang u32 reg = MCDT_CH0_TXD + channel * 4;
186d7bff893SBaolin Wang
187d7bff893SBaolin Wang writel_relaxed(val, mcdt->base + reg);
188d7bff893SBaolin Wang }
189d7bff893SBaolin Wang
sprd_mcdt_adc_read_fifo(struct sprd_mcdt_dev * mcdt,u8 channel,u32 * val)190d7bff893SBaolin Wang static void sprd_mcdt_adc_read_fifo(struct sprd_mcdt_dev *mcdt, u8 channel,
191d7bff893SBaolin Wang u32 *val)
192d7bff893SBaolin Wang {
193d7bff893SBaolin Wang u32 reg = MCDT_CH0_RXD + channel * 4;
194d7bff893SBaolin Wang
195d7bff893SBaolin Wang *val = readl_relaxed(mcdt->base + reg);
196d7bff893SBaolin Wang }
197d7bff893SBaolin Wang
sprd_mcdt_dac_dma_chn_select(struct sprd_mcdt_dev * mcdt,u8 channel,enum sprd_mcdt_dma_chan dma_chan)198d7bff893SBaolin Wang static void sprd_mcdt_dac_dma_chn_select(struct sprd_mcdt_dev *mcdt, u8 channel,
199d7bff893SBaolin Wang enum sprd_mcdt_dma_chan dma_chan)
200d7bff893SBaolin Wang {
201d7bff893SBaolin Wang switch (dma_chan) {
202d7bff893SBaolin Wang case SPRD_MCDT_DMA_CH0:
203d7bff893SBaolin Wang sprd_mcdt_update(mcdt, MCDT_DMA_CFG0,
204d7bff893SBaolin Wang channel << MCDT_DMA_CH0_SEL_SHIFT,
205d7bff893SBaolin Wang MCDT_DMA_CH0_SEL_MASK);
206d7bff893SBaolin Wang break;
207d7bff893SBaolin Wang
208d7bff893SBaolin Wang case SPRD_MCDT_DMA_CH1:
209d7bff893SBaolin Wang sprd_mcdt_update(mcdt, MCDT_DMA_CFG0,
210d7bff893SBaolin Wang channel << MCDT_DMA_CH1_SEL_SHIFT,
211d7bff893SBaolin Wang MCDT_DMA_CH1_SEL_MASK);
212d7bff893SBaolin Wang break;
213d7bff893SBaolin Wang
214d7bff893SBaolin Wang case SPRD_MCDT_DMA_CH2:
215d7bff893SBaolin Wang sprd_mcdt_update(mcdt, MCDT_DMA_CFG0,
216d7bff893SBaolin Wang channel << MCDT_DMA_CH2_SEL_SHIFT,
217d7bff893SBaolin Wang MCDT_DMA_CH2_SEL_MASK);
218d7bff893SBaolin Wang break;
219d7bff893SBaolin Wang
220d7bff893SBaolin Wang case SPRD_MCDT_DMA_CH3:
221d7bff893SBaolin Wang sprd_mcdt_update(mcdt, MCDT_DMA_CFG0,
222d7bff893SBaolin Wang channel << MCDT_DMA_CH3_SEL_SHIFT,
223d7bff893SBaolin Wang MCDT_DMA_CH3_SEL_MASK);
224d7bff893SBaolin Wang break;
225d7bff893SBaolin Wang
226d7bff893SBaolin Wang case SPRD_MCDT_DMA_CH4:
227d7bff893SBaolin Wang sprd_mcdt_update(mcdt, MCDT_DMA_CFG0,
228d7bff893SBaolin Wang channel << MCDT_DMA_CH4_SEL_SHIFT,
229d7bff893SBaolin Wang MCDT_DMA_CH4_SEL_MASK);
230d7bff893SBaolin Wang break;
231d7bff893SBaolin Wang }
232d7bff893SBaolin Wang }
233d7bff893SBaolin Wang
sprd_mcdt_adc_dma_chn_select(struct sprd_mcdt_dev * mcdt,u8 channel,enum sprd_mcdt_dma_chan dma_chan)234d7bff893SBaolin Wang static void sprd_mcdt_adc_dma_chn_select(struct sprd_mcdt_dev *mcdt, u8 channel,
235d7bff893SBaolin Wang enum sprd_mcdt_dma_chan dma_chan)
236d7bff893SBaolin Wang {
237d7bff893SBaolin Wang switch (dma_chan) {
238d7bff893SBaolin Wang case SPRD_MCDT_DMA_CH0:
239d7bff893SBaolin Wang sprd_mcdt_update(mcdt, MCDT_DMA_CFG1,
240d7bff893SBaolin Wang channel << MCDT_DMA_CH0_SEL_SHIFT,
241d7bff893SBaolin Wang MCDT_DMA_CH0_SEL_MASK);
242d7bff893SBaolin Wang break;
243d7bff893SBaolin Wang
244d7bff893SBaolin Wang case SPRD_MCDT_DMA_CH1:
245d7bff893SBaolin Wang sprd_mcdt_update(mcdt, MCDT_DMA_CFG1,
246d7bff893SBaolin Wang channel << MCDT_DMA_CH1_SEL_SHIFT,
247d7bff893SBaolin Wang MCDT_DMA_CH1_SEL_MASK);
248d7bff893SBaolin Wang break;
249d7bff893SBaolin Wang
250d7bff893SBaolin Wang case SPRD_MCDT_DMA_CH2:
251d7bff893SBaolin Wang sprd_mcdt_update(mcdt, MCDT_DMA_CFG1,
252d7bff893SBaolin Wang channel << MCDT_DMA_CH2_SEL_SHIFT,
253d7bff893SBaolin Wang MCDT_DMA_CH2_SEL_MASK);
254d7bff893SBaolin Wang break;
255d7bff893SBaolin Wang
256d7bff893SBaolin Wang case SPRD_MCDT_DMA_CH3:
257d7bff893SBaolin Wang sprd_mcdt_update(mcdt, MCDT_DMA_CFG1,
258d7bff893SBaolin Wang channel << MCDT_DMA_CH3_SEL_SHIFT,
259d7bff893SBaolin Wang MCDT_DMA_CH3_SEL_MASK);
260d7bff893SBaolin Wang break;
261d7bff893SBaolin Wang
262d7bff893SBaolin Wang case SPRD_MCDT_DMA_CH4:
263d7bff893SBaolin Wang sprd_mcdt_update(mcdt, MCDT_DMA_CFG1,
264d7bff893SBaolin Wang channel << MCDT_DMA_CH4_SEL_SHIFT,
265d7bff893SBaolin Wang MCDT_DMA_CH4_SEL_MASK);
266d7bff893SBaolin Wang break;
267d7bff893SBaolin Wang }
268d7bff893SBaolin Wang }
269d7bff893SBaolin Wang
sprd_mcdt_dma_ack_shift(u8 channel)270d7bff893SBaolin Wang static u32 sprd_mcdt_dma_ack_shift(u8 channel)
271d7bff893SBaolin Wang {
272d7bff893SBaolin Wang switch (channel) {
273d7bff893SBaolin Wang default:
274d7bff893SBaolin Wang case 0:
275d7bff893SBaolin Wang case 8:
276d7bff893SBaolin Wang return 0;
277d7bff893SBaolin Wang case 1:
278d7bff893SBaolin Wang case 9:
279d7bff893SBaolin Wang return 4;
280d7bff893SBaolin Wang case 2:
281d7bff893SBaolin Wang return 8;
282d7bff893SBaolin Wang case 3:
283d7bff893SBaolin Wang return 12;
284d7bff893SBaolin Wang case 4:
285d7bff893SBaolin Wang return 16;
286d7bff893SBaolin Wang case 5:
287d7bff893SBaolin Wang return 20;
288d7bff893SBaolin Wang case 6:
289d7bff893SBaolin Wang return 24;
290d7bff893SBaolin Wang case 7:
291d7bff893SBaolin Wang return 28;
292d7bff893SBaolin Wang }
293d7bff893SBaolin Wang }
294d7bff893SBaolin Wang
sprd_mcdt_dac_dma_ack_select(struct sprd_mcdt_dev * mcdt,u8 channel,enum sprd_mcdt_dma_chan dma_chan)295d7bff893SBaolin Wang static void sprd_mcdt_dac_dma_ack_select(struct sprd_mcdt_dev *mcdt, u8 channel,
296d7bff893SBaolin Wang enum sprd_mcdt_dma_chan dma_chan)
297d7bff893SBaolin Wang {
298d7bff893SBaolin Wang u32 reg, shift = sprd_mcdt_dma_ack_shift(channel), ack = dma_chan;
299d7bff893SBaolin Wang
300d7bff893SBaolin Wang switch (channel) {
301d7bff893SBaolin Wang case 0 ... 7:
302d7bff893SBaolin Wang reg = MCDT_DMA_CFG2;
303d7bff893SBaolin Wang break;
304d7bff893SBaolin Wang
305d7bff893SBaolin Wang case 8 ... 9:
306d7bff893SBaolin Wang reg = MCDT_DMA_CFG3;
307d7bff893SBaolin Wang break;
308d7bff893SBaolin Wang
309d7bff893SBaolin Wang default:
310d7bff893SBaolin Wang return;
311d7bff893SBaolin Wang }
312d7bff893SBaolin Wang
313d7bff893SBaolin Wang sprd_mcdt_update(mcdt, reg, ack << shift,
314d7bff893SBaolin Wang MCDT_DMA_ACK_SEL_MASK << shift);
315d7bff893SBaolin Wang }
316d7bff893SBaolin Wang
sprd_mcdt_adc_dma_ack_select(struct sprd_mcdt_dev * mcdt,u8 channel,enum sprd_mcdt_dma_chan dma_chan)317d7bff893SBaolin Wang static void sprd_mcdt_adc_dma_ack_select(struct sprd_mcdt_dev *mcdt, u8 channel,
318d7bff893SBaolin Wang enum sprd_mcdt_dma_chan dma_chan)
319d7bff893SBaolin Wang {
320d7bff893SBaolin Wang u32 reg, shift = sprd_mcdt_dma_ack_shift(channel), ack = dma_chan;
321d7bff893SBaolin Wang
322d7bff893SBaolin Wang switch (channel) {
323d7bff893SBaolin Wang case 0 ... 7:
324d7bff893SBaolin Wang reg = MCDT_DMA_CFG4;
325d7bff893SBaolin Wang break;
326d7bff893SBaolin Wang
327d7bff893SBaolin Wang case 8 ... 9:
328d7bff893SBaolin Wang reg = MCDT_DMA_CFG5;
329d7bff893SBaolin Wang break;
330d7bff893SBaolin Wang
331d7bff893SBaolin Wang default:
332d7bff893SBaolin Wang return;
333d7bff893SBaolin Wang }
334d7bff893SBaolin Wang
335d7bff893SBaolin Wang sprd_mcdt_update(mcdt, reg, ack << shift,
336d7bff893SBaolin Wang MCDT_DMA_ACK_SEL_MASK << shift);
337d7bff893SBaolin Wang }
338d7bff893SBaolin Wang
sprd_mcdt_chan_fifo_sts(struct sprd_mcdt_dev * mcdt,u8 channel,enum sprd_mcdt_fifo_sts fifo_sts)339d7bff893SBaolin Wang static bool sprd_mcdt_chan_fifo_sts(struct sprd_mcdt_dev *mcdt, u8 channel,
340d7bff893SBaolin Wang enum sprd_mcdt_fifo_sts fifo_sts)
341d7bff893SBaolin Wang {
342d7bff893SBaolin Wang u32 reg, shift;
343d7bff893SBaolin Wang
344d7bff893SBaolin Wang switch (channel) {
345d7bff893SBaolin Wang case 0 ... 3:
346d7bff893SBaolin Wang reg = MCDT_CH_FIFO_ST0;
347d7bff893SBaolin Wang break;
348d7bff893SBaolin Wang case 4 ... 7:
349d7bff893SBaolin Wang reg = MCDT_CH_FIFO_ST1;
350d7bff893SBaolin Wang break;
351d7bff893SBaolin Wang case 8 ... 9:
352d7bff893SBaolin Wang reg = MCDT_CH_FIFO_ST2;
353d7bff893SBaolin Wang break;
354d7bff893SBaolin Wang default:
355d7bff893SBaolin Wang return false;
356d7bff893SBaolin Wang }
357d7bff893SBaolin Wang
358d7bff893SBaolin Wang switch (channel) {
359d7bff893SBaolin Wang case 0:
360d7bff893SBaolin Wang case 4:
361d7bff893SBaolin Wang case 8:
362d7bff893SBaolin Wang shift = fifo_sts;
363d7bff893SBaolin Wang break;
364d7bff893SBaolin Wang
365d7bff893SBaolin Wang case 1:
366d7bff893SBaolin Wang case 5:
367d7bff893SBaolin Wang case 9:
368d7bff893SBaolin Wang shift = 8 + fifo_sts;
369d7bff893SBaolin Wang break;
370d7bff893SBaolin Wang
371d7bff893SBaolin Wang case 2:
372d7bff893SBaolin Wang case 6:
373d7bff893SBaolin Wang shift = 16 + fifo_sts;
374d7bff893SBaolin Wang break;
375d7bff893SBaolin Wang
376d7bff893SBaolin Wang case 3:
377d7bff893SBaolin Wang case 7:
378d7bff893SBaolin Wang shift = 24 + fifo_sts;
379d7bff893SBaolin Wang break;
380d7bff893SBaolin Wang
381d7bff893SBaolin Wang default:
382d7bff893SBaolin Wang return false;
383d7bff893SBaolin Wang }
384d7bff893SBaolin Wang
385d7bff893SBaolin Wang return !!(readl_relaxed(mcdt->base + reg) & BIT(shift));
386d7bff893SBaolin Wang }
387d7bff893SBaolin Wang
sprd_mcdt_dac_fifo_clear(struct sprd_mcdt_dev * mcdt,u8 channel)388d7bff893SBaolin Wang static void sprd_mcdt_dac_fifo_clear(struct sprd_mcdt_dev *mcdt, u8 channel)
389d7bff893SBaolin Wang {
390d7bff893SBaolin Wang sprd_mcdt_update(mcdt, MCDT_FIFO_CLR, BIT(channel), BIT(channel));
391d7bff893SBaolin Wang }
392d7bff893SBaolin Wang
sprd_mcdt_adc_fifo_clear(struct sprd_mcdt_dev * mcdt,u8 channel)393d7bff893SBaolin Wang static void sprd_mcdt_adc_fifo_clear(struct sprd_mcdt_dev *mcdt, u8 channel)
394d7bff893SBaolin Wang {
395d7bff893SBaolin Wang u32 shift = MCDT_ADC_FIFO_SHIFT + channel;
396d7bff893SBaolin Wang
397d7bff893SBaolin Wang sprd_mcdt_update(mcdt, MCDT_FIFO_CLR, BIT(shift), BIT(shift));
398d7bff893SBaolin Wang }
399d7bff893SBaolin Wang
sprd_mcdt_dac_fifo_avail(struct sprd_mcdt_dev * mcdt,u8 channel)400d7bff893SBaolin Wang static u32 sprd_mcdt_dac_fifo_avail(struct sprd_mcdt_dev *mcdt, u8 channel)
401d7bff893SBaolin Wang {
402d7bff893SBaolin Wang u32 reg = MCDT_DAC0_FIFO_ADDR_ST + channel * 8;
403d7bff893SBaolin Wang u32 r_addr = (readl_relaxed(mcdt->base + reg) >>
404d7bff893SBaolin Wang MCDT_CH_FIFO_ADDR_SHIFT) & MCDT_CH_FIFO_ADDR_MASK;
405d7bff893SBaolin Wang u32 w_addr = readl_relaxed(mcdt->base + reg) & MCDT_CH_FIFO_ADDR_MASK;
406d7bff893SBaolin Wang
407d7bff893SBaolin Wang if (w_addr >= r_addr)
408d7bff893SBaolin Wang return 4 * (MCDT_FIFO_LENGTH - w_addr + r_addr);
409d7bff893SBaolin Wang else
410d7bff893SBaolin Wang return 4 * (r_addr - w_addr);
411d7bff893SBaolin Wang }
412d7bff893SBaolin Wang
sprd_mcdt_adc_fifo_avail(struct sprd_mcdt_dev * mcdt,u8 channel)413d7bff893SBaolin Wang static u32 sprd_mcdt_adc_fifo_avail(struct sprd_mcdt_dev *mcdt, u8 channel)
414d7bff893SBaolin Wang {
415d7bff893SBaolin Wang u32 reg = MCDT_ADC0_FIFO_ADDR_ST + channel * 8;
416d7bff893SBaolin Wang u32 r_addr = (readl_relaxed(mcdt->base + reg) >>
417d7bff893SBaolin Wang MCDT_CH_FIFO_ADDR_SHIFT) & MCDT_CH_FIFO_ADDR_MASK;
418d7bff893SBaolin Wang u32 w_addr = readl_relaxed(mcdt->base + reg) & MCDT_CH_FIFO_ADDR_MASK;
419d7bff893SBaolin Wang
420d7bff893SBaolin Wang if (w_addr >= r_addr)
421d7bff893SBaolin Wang return 4 * (w_addr - r_addr);
422d7bff893SBaolin Wang else
423d7bff893SBaolin Wang return 4 * (MCDT_FIFO_LENGTH - r_addr + w_addr);
424d7bff893SBaolin Wang }
425d7bff893SBaolin Wang
sprd_mcdt_int_type_shift(u8 channel,enum sprd_mcdt_fifo_int int_type)426d7bff893SBaolin Wang static u32 sprd_mcdt_int_type_shift(u8 channel,
427d7bff893SBaolin Wang enum sprd_mcdt_fifo_int int_type)
428d7bff893SBaolin Wang {
429d7bff893SBaolin Wang switch (channel) {
430d7bff893SBaolin Wang case 0:
431d7bff893SBaolin Wang case 4:
432d7bff893SBaolin Wang case 8:
433d7bff893SBaolin Wang return int_type;
434d7bff893SBaolin Wang
435d7bff893SBaolin Wang case 1:
436d7bff893SBaolin Wang case 5:
437d7bff893SBaolin Wang case 9:
438d7bff893SBaolin Wang return 8 + int_type;
439d7bff893SBaolin Wang
440d7bff893SBaolin Wang case 2:
441d7bff893SBaolin Wang case 6:
442d7bff893SBaolin Wang return 16 + int_type;
443d7bff893SBaolin Wang
444d7bff893SBaolin Wang case 3:
445d7bff893SBaolin Wang case 7:
446d7bff893SBaolin Wang return 24 + int_type;
447d7bff893SBaolin Wang
448d7bff893SBaolin Wang default:
449d7bff893SBaolin Wang return 0;
450d7bff893SBaolin Wang }
451d7bff893SBaolin Wang }
452d7bff893SBaolin Wang
sprd_mcdt_chan_int_en(struct sprd_mcdt_dev * mcdt,u8 channel,enum sprd_mcdt_fifo_int int_type,bool enable)453d7bff893SBaolin Wang static void sprd_mcdt_chan_int_en(struct sprd_mcdt_dev *mcdt, u8 channel,
454d7bff893SBaolin Wang enum sprd_mcdt_fifo_int int_type, bool enable)
455d7bff893SBaolin Wang {
456d7bff893SBaolin Wang u32 reg, shift = sprd_mcdt_int_type_shift(channel, int_type);
457d7bff893SBaolin Wang
458d7bff893SBaolin Wang switch (channel) {
459d7bff893SBaolin Wang case 0 ... 3:
460d7bff893SBaolin Wang reg = MCDT_INT_EN0;
461d7bff893SBaolin Wang break;
462d7bff893SBaolin Wang case 4 ... 7:
463d7bff893SBaolin Wang reg = MCDT_INT_EN1;
464d7bff893SBaolin Wang break;
465d7bff893SBaolin Wang case 8 ... 9:
466d7bff893SBaolin Wang reg = MCDT_INT_EN2;
467d7bff893SBaolin Wang break;
468d7bff893SBaolin Wang default:
469d7bff893SBaolin Wang return;
470d7bff893SBaolin Wang }
471d7bff893SBaolin Wang
472d7bff893SBaolin Wang if (enable)
473d7bff893SBaolin Wang sprd_mcdt_update(mcdt, reg, BIT(shift), BIT(shift));
474d7bff893SBaolin Wang else
475d7bff893SBaolin Wang sprd_mcdt_update(mcdt, reg, 0, BIT(shift));
476d7bff893SBaolin Wang }
477d7bff893SBaolin Wang
sprd_mcdt_chan_int_clear(struct sprd_mcdt_dev * mcdt,u8 channel,enum sprd_mcdt_fifo_int int_type)478d7bff893SBaolin Wang static void sprd_mcdt_chan_int_clear(struct sprd_mcdt_dev *mcdt, u8 channel,
479d7bff893SBaolin Wang enum sprd_mcdt_fifo_int int_type)
480d7bff893SBaolin Wang {
481d7bff893SBaolin Wang u32 reg, shift = sprd_mcdt_int_type_shift(channel, int_type);
482d7bff893SBaolin Wang
483d7bff893SBaolin Wang switch (channel) {
484d7bff893SBaolin Wang case 0 ... 3:
485d7bff893SBaolin Wang reg = MCDT_INT_CLR0;
486d7bff893SBaolin Wang break;
487d7bff893SBaolin Wang case 4 ... 7:
488d7bff893SBaolin Wang reg = MCDT_INT_CLR1;
489d7bff893SBaolin Wang break;
490d7bff893SBaolin Wang case 8 ... 9:
491d7bff893SBaolin Wang reg = MCDT_INT_CLR2;
492d7bff893SBaolin Wang break;
493d7bff893SBaolin Wang default:
494d7bff893SBaolin Wang return;
495d7bff893SBaolin Wang }
496d7bff893SBaolin Wang
497d7bff893SBaolin Wang sprd_mcdt_update(mcdt, reg, BIT(shift), BIT(shift));
498d7bff893SBaolin Wang }
499d7bff893SBaolin Wang
sprd_mcdt_chan_int_sts(struct sprd_mcdt_dev * mcdt,u8 channel,enum sprd_mcdt_fifo_int int_type)500d7bff893SBaolin Wang static bool sprd_mcdt_chan_int_sts(struct sprd_mcdt_dev *mcdt, u8 channel,
501d7bff893SBaolin Wang enum sprd_mcdt_fifo_int int_type)
502d7bff893SBaolin Wang {
503d7bff893SBaolin Wang u32 reg, shift = sprd_mcdt_int_type_shift(channel, int_type);
504d7bff893SBaolin Wang
505d7bff893SBaolin Wang switch (channel) {
506d7bff893SBaolin Wang case 0 ... 3:
507d7bff893SBaolin Wang reg = MCDT_INT_MSK1;
508d7bff893SBaolin Wang break;
509d7bff893SBaolin Wang case 4 ... 7:
510d7bff893SBaolin Wang reg = MCDT_INT_MSK2;
511d7bff893SBaolin Wang break;
512d7bff893SBaolin Wang case 8 ... 9:
513d7bff893SBaolin Wang reg = MCDT_INT_MSK3;
514d7bff893SBaolin Wang break;
515d7bff893SBaolin Wang default:
516d7bff893SBaolin Wang return false;
517d7bff893SBaolin Wang }
518d7bff893SBaolin Wang
519d7bff893SBaolin Wang return !!(readl_relaxed(mcdt->base + reg) & BIT(shift));
520d7bff893SBaolin Wang }
521d7bff893SBaolin Wang
sprd_mcdt_irq_handler(int irq,void * dev_id)522d7bff893SBaolin Wang static irqreturn_t sprd_mcdt_irq_handler(int irq, void *dev_id)
523d7bff893SBaolin Wang {
524d7bff893SBaolin Wang struct sprd_mcdt_dev *mcdt = (struct sprd_mcdt_dev *)dev_id;
525d7bff893SBaolin Wang int i;
526d7bff893SBaolin Wang
527d7bff893SBaolin Wang spin_lock(&mcdt->lock);
528d7bff893SBaolin Wang
529d7bff893SBaolin Wang for (i = 0; i < MCDT_ADC_CHANNEL_NUM; i++) {
530d7bff893SBaolin Wang if (sprd_mcdt_chan_int_sts(mcdt, i, MCDT_ADC_FIFO_AF_INT)) {
531d7bff893SBaolin Wang struct sprd_mcdt_chan *chan = &mcdt->chan[i];
532d7bff893SBaolin Wang
533d7bff893SBaolin Wang sprd_mcdt_chan_int_clear(mcdt, i, MCDT_ADC_FIFO_AF_INT);
534d7bff893SBaolin Wang if (chan->cb)
535d7bff893SBaolin Wang chan->cb->notify(chan->cb->data);
536d7bff893SBaolin Wang }
537d7bff893SBaolin Wang }
538d7bff893SBaolin Wang
539d7bff893SBaolin Wang for (i = 0; i < MCDT_DAC_CHANNEL_NUM; i++) {
540d7bff893SBaolin Wang if (sprd_mcdt_chan_int_sts(mcdt, i, MCDT_DAC_FIFO_AE_INT)) {
541d7bff893SBaolin Wang struct sprd_mcdt_chan *chan =
542d7bff893SBaolin Wang &mcdt->chan[i + MCDT_ADC_CHANNEL_NUM];
543d7bff893SBaolin Wang
544d7bff893SBaolin Wang sprd_mcdt_chan_int_clear(mcdt, i, MCDT_DAC_FIFO_AE_INT);
545d7bff893SBaolin Wang if (chan->cb)
546d7bff893SBaolin Wang chan->cb->notify(chan->cb->data);
547d7bff893SBaolin Wang }
548d7bff893SBaolin Wang }
549d7bff893SBaolin Wang
550d7bff893SBaolin Wang spin_unlock(&mcdt->lock);
551d7bff893SBaolin Wang
552d7bff893SBaolin Wang return IRQ_HANDLED;
553d7bff893SBaolin Wang }
554d7bff893SBaolin Wang
555d7bff893SBaolin Wang /**
556d7bff893SBaolin Wang * sprd_mcdt_chan_write - write data to the MCDT channel's fifo
557d7bff893SBaolin Wang * @chan: the MCDT channel
558d7bff893SBaolin Wang * @tx_buf: send buffer
559d7bff893SBaolin Wang * @size: data size
560d7bff893SBaolin Wang *
561d7bff893SBaolin Wang * Note: We can not write data to the channel fifo when enabling the DMA mode,
562d7bff893SBaolin Wang * otherwise the channel fifo data will be invalid.
563d7bff893SBaolin Wang *
564d7bff893SBaolin Wang * If there are not enough space of the channel fifo, it will return errors
565d7bff893SBaolin Wang * to users.
566d7bff893SBaolin Wang *
567d7bff893SBaolin Wang * Returns 0 on success, or an appropriate error code on failure.
568d7bff893SBaolin Wang */
sprd_mcdt_chan_write(struct sprd_mcdt_chan * chan,char * tx_buf,u32 size)569d7bff893SBaolin Wang int sprd_mcdt_chan_write(struct sprd_mcdt_chan *chan, char *tx_buf, u32 size)
570d7bff893SBaolin Wang {
571d7bff893SBaolin Wang struct sprd_mcdt_dev *mcdt = chan->mcdt;
572d7bff893SBaolin Wang unsigned long flags;
573d7bff893SBaolin Wang int avail, i = 0, words = size / 4;
574d7bff893SBaolin Wang u32 *buf = (u32 *)tx_buf;
575d7bff893SBaolin Wang
576d7bff893SBaolin Wang spin_lock_irqsave(&mcdt->lock, flags);
577d7bff893SBaolin Wang
578d7bff893SBaolin Wang if (chan->dma_enable) {
579d7bff893SBaolin Wang dev_err(mcdt->dev,
580d7bff893SBaolin Wang "Can not write data when DMA mode enabled\n");
581d7bff893SBaolin Wang spin_unlock_irqrestore(&mcdt->lock, flags);
582d7bff893SBaolin Wang return -EINVAL;
583d7bff893SBaolin Wang }
584d7bff893SBaolin Wang
585d7bff893SBaolin Wang if (sprd_mcdt_chan_fifo_sts(mcdt, chan->id, MCDT_DAC_FIFO_REAL_FULL)) {
586d7bff893SBaolin Wang dev_err(mcdt->dev, "Channel fifo is full now\n");
587d7bff893SBaolin Wang spin_unlock_irqrestore(&mcdt->lock, flags);
588d7bff893SBaolin Wang return -EBUSY;
589d7bff893SBaolin Wang }
590d7bff893SBaolin Wang
591d7bff893SBaolin Wang avail = sprd_mcdt_dac_fifo_avail(mcdt, chan->id);
592d7bff893SBaolin Wang if (size > avail) {
593d7bff893SBaolin Wang dev_err(mcdt->dev,
594d7bff893SBaolin Wang "Data size is larger than the available fifo size\n");
595d7bff893SBaolin Wang spin_unlock_irqrestore(&mcdt->lock, flags);
596d7bff893SBaolin Wang return -EBUSY;
597d7bff893SBaolin Wang }
598d7bff893SBaolin Wang
599d7bff893SBaolin Wang while (i++ < words)
600d7bff893SBaolin Wang sprd_mcdt_dac_write_fifo(mcdt, chan->id, *buf++);
601d7bff893SBaolin Wang
602d7bff893SBaolin Wang spin_unlock_irqrestore(&mcdt->lock, flags);
603d7bff893SBaolin Wang return 0;
604d7bff893SBaolin Wang }
605d7bff893SBaolin Wang EXPORT_SYMBOL_GPL(sprd_mcdt_chan_write);
606d7bff893SBaolin Wang
607d7bff893SBaolin Wang /**
608d7bff893SBaolin Wang * sprd_mcdt_chan_read - read data from the MCDT channel's fifo
609d7bff893SBaolin Wang * @chan: the MCDT channel
610d7bff893SBaolin Wang * @rx_buf: receive buffer
611d7bff893SBaolin Wang * @size: data size
612d7bff893SBaolin Wang *
613d7bff893SBaolin Wang * Note: We can not read data from the channel fifo when enabling the DMA mode,
614d7bff893SBaolin Wang * otherwise the reading data will be invalid.
615d7bff893SBaolin Wang *
616d7bff893SBaolin Wang * Usually user need start to read data once receiving the fifo full interrupt.
617d7bff893SBaolin Wang *
618d7bff893SBaolin Wang * Returns data size of reading successfully, or an error code on failure.
619d7bff893SBaolin Wang */
sprd_mcdt_chan_read(struct sprd_mcdt_chan * chan,char * rx_buf,u32 size)620d7bff893SBaolin Wang int sprd_mcdt_chan_read(struct sprd_mcdt_chan *chan, char *rx_buf, u32 size)
621d7bff893SBaolin Wang {
622d7bff893SBaolin Wang struct sprd_mcdt_dev *mcdt = chan->mcdt;
623d7bff893SBaolin Wang unsigned long flags;
624d7bff893SBaolin Wang int i = 0, avail, words = size / 4;
625d7bff893SBaolin Wang u32 *buf = (u32 *)rx_buf;
626d7bff893SBaolin Wang
627d7bff893SBaolin Wang spin_lock_irqsave(&mcdt->lock, flags);
628d7bff893SBaolin Wang
629d7bff893SBaolin Wang if (chan->dma_enable) {
630d7bff893SBaolin Wang dev_err(mcdt->dev, "Can not read data when DMA mode enabled\n");
631d7bff893SBaolin Wang spin_unlock_irqrestore(&mcdt->lock, flags);
632d7bff893SBaolin Wang return -EINVAL;
633d7bff893SBaolin Wang }
634d7bff893SBaolin Wang
635d7bff893SBaolin Wang if (sprd_mcdt_chan_fifo_sts(mcdt, chan->id, MCDT_ADC_FIFO_REAL_EMPTY)) {
636d7bff893SBaolin Wang dev_err(mcdt->dev, "Channel fifo is empty\n");
637d7bff893SBaolin Wang spin_unlock_irqrestore(&mcdt->lock, flags);
638d7bff893SBaolin Wang return -EBUSY;
639d7bff893SBaolin Wang }
640d7bff893SBaolin Wang
641d7bff893SBaolin Wang avail = sprd_mcdt_adc_fifo_avail(mcdt, chan->id);
642d7bff893SBaolin Wang if (size > avail)
643d7bff893SBaolin Wang words = avail / 4;
644d7bff893SBaolin Wang
645d7bff893SBaolin Wang while (i++ < words)
646d7bff893SBaolin Wang sprd_mcdt_adc_read_fifo(mcdt, chan->id, buf++);
647d7bff893SBaolin Wang
648d7bff893SBaolin Wang spin_unlock_irqrestore(&mcdt->lock, flags);
649d7bff893SBaolin Wang return words * 4;
650d7bff893SBaolin Wang }
651d7bff893SBaolin Wang EXPORT_SYMBOL_GPL(sprd_mcdt_chan_read);
652d7bff893SBaolin Wang
653d7bff893SBaolin Wang /**
654d7bff893SBaolin Wang * sprd_mcdt_chan_int_enable - enable the interrupt mode for the MCDT channel
655d7bff893SBaolin Wang * @chan: the MCDT channel
656d7bff893SBaolin Wang * @water_mark: water mark to trigger a interrupt
657d7bff893SBaolin Wang * @cb: callback when a interrupt happened
658d7bff893SBaolin Wang *
659d7bff893SBaolin Wang * Now it only can enable fifo almost full interrupt for ADC channel and fifo
660d7bff893SBaolin Wang * almost empty interrupt for DAC channel. Morevoer for interrupt mode, user
661d7bff893SBaolin Wang * should use sprd_mcdt_chan_read() or sprd_mcdt_chan_write() to read or write
662d7bff893SBaolin Wang * data manually.
663d7bff893SBaolin Wang *
664d7bff893SBaolin Wang * For ADC channel, user can start to read data once receiving one fifo full
665d7bff893SBaolin Wang * interrupt. For DAC channel, user can start to write data once receiving one
666d7bff893SBaolin Wang * fifo empty interrupt or just call sprd_mcdt_chan_write() to write data
667d7bff893SBaolin Wang * directly.
668d7bff893SBaolin Wang *
669d7bff893SBaolin Wang * Returns 0 on success, or an error code on failure.
670d7bff893SBaolin Wang */
sprd_mcdt_chan_int_enable(struct sprd_mcdt_chan * chan,u32 water_mark,struct sprd_mcdt_chan_callback * cb)671d7bff893SBaolin Wang int sprd_mcdt_chan_int_enable(struct sprd_mcdt_chan *chan, u32 water_mark,
672d7bff893SBaolin Wang struct sprd_mcdt_chan_callback *cb)
673d7bff893SBaolin Wang {
674d7bff893SBaolin Wang struct sprd_mcdt_dev *mcdt = chan->mcdt;
675d7bff893SBaolin Wang unsigned long flags;
676d7bff893SBaolin Wang int ret = 0;
677d7bff893SBaolin Wang
678d7bff893SBaolin Wang spin_lock_irqsave(&mcdt->lock, flags);
679d7bff893SBaolin Wang
680d7bff893SBaolin Wang if (chan->dma_enable || chan->int_enable) {
681d7bff893SBaolin Wang dev_err(mcdt->dev, "Failed to set interrupt mode.\n");
682d7bff893SBaolin Wang spin_unlock_irqrestore(&mcdt->lock, flags);
683d7bff893SBaolin Wang return -EINVAL;
684d7bff893SBaolin Wang }
685d7bff893SBaolin Wang
686d7bff893SBaolin Wang switch (chan->type) {
687d7bff893SBaolin Wang case SPRD_MCDT_ADC_CHAN:
688d7bff893SBaolin Wang sprd_mcdt_adc_fifo_clear(mcdt, chan->id);
689d7bff893SBaolin Wang sprd_mcdt_adc_set_watermark(mcdt, chan->id, water_mark,
690d7bff893SBaolin Wang MCDT_FIFO_LENGTH - 1);
691d7bff893SBaolin Wang sprd_mcdt_chan_int_en(mcdt, chan->id,
692d7bff893SBaolin Wang MCDT_ADC_FIFO_AF_INT, true);
693d7bff893SBaolin Wang sprd_mcdt_ap_int_enable(mcdt, chan->id, true);
694d7bff893SBaolin Wang break;
695d7bff893SBaolin Wang
696d7bff893SBaolin Wang case SPRD_MCDT_DAC_CHAN:
697d7bff893SBaolin Wang sprd_mcdt_dac_fifo_clear(mcdt, chan->id);
698d7bff893SBaolin Wang sprd_mcdt_dac_set_watermark(mcdt, chan->id,
699d7bff893SBaolin Wang MCDT_FIFO_LENGTH - 1, water_mark);
700d7bff893SBaolin Wang sprd_mcdt_chan_int_en(mcdt, chan->id,
701d7bff893SBaolin Wang MCDT_DAC_FIFO_AE_INT, true);
702d7bff893SBaolin Wang sprd_mcdt_ap_int_enable(mcdt, chan->id, true);
703d7bff893SBaolin Wang break;
704d7bff893SBaolin Wang
705d7bff893SBaolin Wang default:
706d7bff893SBaolin Wang dev_err(mcdt->dev, "Unsupported channel type\n");
707d7bff893SBaolin Wang ret = -EINVAL;
708d7bff893SBaolin Wang }
709d7bff893SBaolin Wang
710d7bff893SBaolin Wang if (!ret) {
711d7bff893SBaolin Wang chan->cb = cb;
712d7bff893SBaolin Wang chan->int_enable = true;
713d7bff893SBaolin Wang }
714d7bff893SBaolin Wang
715d7bff893SBaolin Wang spin_unlock_irqrestore(&mcdt->lock, flags);
716d7bff893SBaolin Wang
717d7bff893SBaolin Wang return ret;
718d7bff893SBaolin Wang }
719d7bff893SBaolin Wang EXPORT_SYMBOL_GPL(sprd_mcdt_chan_int_enable);
720d7bff893SBaolin Wang
721d7bff893SBaolin Wang /**
722d7bff893SBaolin Wang * sprd_mcdt_chan_int_disable - disable the interrupt mode for the MCDT channel
723d7bff893SBaolin Wang * @chan: the MCDT channel
724d7bff893SBaolin Wang */
sprd_mcdt_chan_int_disable(struct sprd_mcdt_chan * chan)725d7bff893SBaolin Wang void sprd_mcdt_chan_int_disable(struct sprd_mcdt_chan *chan)
726d7bff893SBaolin Wang {
727d7bff893SBaolin Wang struct sprd_mcdt_dev *mcdt = chan->mcdt;
728d7bff893SBaolin Wang unsigned long flags;
729d7bff893SBaolin Wang
730d7bff893SBaolin Wang spin_lock_irqsave(&mcdt->lock, flags);
731d7bff893SBaolin Wang
732d7bff893SBaolin Wang if (!chan->int_enable) {
733d7bff893SBaolin Wang spin_unlock_irqrestore(&mcdt->lock, flags);
734d7bff893SBaolin Wang return;
735d7bff893SBaolin Wang }
736d7bff893SBaolin Wang
737d7bff893SBaolin Wang switch (chan->type) {
738d7bff893SBaolin Wang case SPRD_MCDT_ADC_CHAN:
739d7bff893SBaolin Wang sprd_mcdt_chan_int_en(mcdt, chan->id,
740d7bff893SBaolin Wang MCDT_ADC_FIFO_AF_INT, false);
741d7bff893SBaolin Wang sprd_mcdt_chan_int_clear(mcdt, chan->id, MCDT_ADC_FIFO_AF_INT);
742d7bff893SBaolin Wang sprd_mcdt_ap_int_enable(mcdt, chan->id, false);
743d7bff893SBaolin Wang break;
744d7bff893SBaolin Wang
745d7bff893SBaolin Wang case SPRD_MCDT_DAC_CHAN:
746d7bff893SBaolin Wang sprd_mcdt_chan_int_en(mcdt, chan->id,
747d7bff893SBaolin Wang MCDT_DAC_FIFO_AE_INT, false);
748d7bff893SBaolin Wang sprd_mcdt_chan_int_clear(mcdt, chan->id, MCDT_DAC_FIFO_AE_INT);
749d7bff893SBaolin Wang sprd_mcdt_ap_int_enable(mcdt, chan->id, false);
750d7bff893SBaolin Wang break;
751d7bff893SBaolin Wang
752d7bff893SBaolin Wang default:
753d7bff893SBaolin Wang break;
754d7bff893SBaolin Wang }
755d7bff893SBaolin Wang
756d7bff893SBaolin Wang chan->int_enable = false;
757d7bff893SBaolin Wang spin_unlock_irqrestore(&mcdt->lock, flags);
758d7bff893SBaolin Wang }
759d7bff893SBaolin Wang EXPORT_SYMBOL_GPL(sprd_mcdt_chan_int_disable);
760d7bff893SBaolin Wang
761d7bff893SBaolin Wang /**
762d7bff893SBaolin Wang * sprd_mcdt_chan_dma_enable - enable the DMA mode for the MCDT channel
763d7bff893SBaolin Wang * @chan: the MCDT channel
764d7bff893SBaolin Wang * @dma_chan: specify which DMA channel will be used for this MCDT channel
765d7bff893SBaolin Wang * @water_mark: water mark to trigger a DMA request
766d7bff893SBaolin Wang *
767d7bff893SBaolin Wang * Enable the DMA mode for the MCDT channel, that means we can use DMA to
768d7bff893SBaolin Wang * transfer data to the channel fifo and do not need reading/writing data
769d7bff893SBaolin Wang * manually.
770d7bff893SBaolin Wang *
771d7bff893SBaolin Wang * Returns 0 on success, or an error code on failure.
772d7bff893SBaolin Wang */
sprd_mcdt_chan_dma_enable(struct sprd_mcdt_chan * chan,enum sprd_mcdt_dma_chan dma_chan,u32 water_mark)773d7bff893SBaolin Wang int sprd_mcdt_chan_dma_enable(struct sprd_mcdt_chan *chan,
774d7bff893SBaolin Wang enum sprd_mcdt_dma_chan dma_chan,
775d7bff893SBaolin Wang u32 water_mark)
776d7bff893SBaolin Wang {
777d7bff893SBaolin Wang struct sprd_mcdt_dev *mcdt = chan->mcdt;
778d7bff893SBaolin Wang unsigned long flags;
779d7bff893SBaolin Wang int ret = 0;
780d7bff893SBaolin Wang
781d7bff893SBaolin Wang spin_lock_irqsave(&mcdt->lock, flags);
782d7bff893SBaolin Wang
783d7bff893SBaolin Wang if (chan->dma_enable || chan->int_enable ||
784d7bff893SBaolin Wang dma_chan > SPRD_MCDT_DMA_CH4) {
785d7bff893SBaolin Wang dev_err(mcdt->dev, "Failed to set DMA mode\n");
786d7bff893SBaolin Wang spin_unlock_irqrestore(&mcdt->lock, flags);
787d7bff893SBaolin Wang return -EINVAL;
788d7bff893SBaolin Wang }
789d7bff893SBaolin Wang
790d7bff893SBaolin Wang switch (chan->type) {
791d7bff893SBaolin Wang case SPRD_MCDT_ADC_CHAN:
792d7bff893SBaolin Wang sprd_mcdt_adc_fifo_clear(mcdt, chan->id);
793d7bff893SBaolin Wang sprd_mcdt_adc_set_watermark(mcdt, chan->id,
794d7bff893SBaolin Wang water_mark, MCDT_FIFO_LENGTH - 1);
795d7bff893SBaolin Wang sprd_mcdt_adc_dma_enable(mcdt, chan->id, true);
796d7bff893SBaolin Wang sprd_mcdt_adc_dma_chn_select(mcdt, chan->id, dma_chan);
797d7bff893SBaolin Wang sprd_mcdt_adc_dma_ack_select(mcdt, chan->id, dma_chan);
798d7bff893SBaolin Wang break;
799d7bff893SBaolin Wang
800d7bff893SBaolin Wang case SPRD_MCDT_DAC_CHAN:
801d7bff893SBaolin Wang sprd_mcdt_dac_fifo_clear(mcdt, chan->id);
802d7bff893SBaolin Wang sprd_mcdt_dac_set_watermark(mcdt, chan->id,
803d7bff893SBaolin Wang MCDT_FIFO_LENGTH - 1, water_mark);
804d7bff893SBaolin Wang sprd_mcdt_dac_dma_enable(mcdt, chan->id, true);
805d7bff893SBaolin Wang sprd_mcdt_dac_dma_chn_select(mcdt, chan->id, dma_chan);
806d7bff893SBaolin Wang sprd_mcdt_dac_dma_ack_select(mcdt, chan->id, dma_chan);
807d7bff893SBaolin Wang break;
808d7bff893SBaolin Wang
809d7bff893SBaolin Wang default:
810d7bff893SBaolin Wang dev_err(mcdt->dev, "Unsupported channel type\n");
811d7bff893SBaolin Wang ret = -EINVAL;
812d7bff893SBaolin Wang }
813d7bff893SBaolin Wang
814d7bff893SBaolin Wang if (!ret)
815d7bff893SBaolin Wang chan->dma_enable = true;
816d7bff893SBaolin Wang
817d7bff893SBaolin Wang spin_unlock_irqrestore(&mcdt->lock, flags);
818d7bff893SBaolin Wang
819d7bff893SBaolin Wang return ret;
820d7bff893SBaolin Wang }
821d7bff893SBaolin Wang EXPORT_SYMBOL_GPL(sprd_mcdt_chan_dma_enable);
822d7bff893SBaolin Wang
823d7bff893SBaolin Wang /**
824d7bff893SBaolin Wang * sprd_mcdt_chan_dma_disable - disable the DMA mode for the MCDT channel
825d7bff893SBaolin Wang * @chan: the MCDT channel
826d7bff893SBaolin Wang */
sprd_mcdt_chan_dma_disable(struct sprd_mcdt_chan * chan)827d7bff893SBaolin Wang void sprd_mcdt_chan_dma_disable(struct sprd_mcdt_chan *chan)
828d7bff893SBaolin Wang {
829d7bff893SBaolin Wang struct sprd_mcdt_dev *mcdt = chan->mcdt;
830d7bff893SBaolin Wang unsigned long flags;
831d7bff893SBaolin Wang
832d7bff893SBaolin Wang spin_lock_irqsave(&mcdt->lock, flags);
833d7bff893SBaolin Wang
834d7bff893SBaolin Wang if (!chan->dma_enable) {
835d7bff893SBaolin Wang spin_unlock_irqrestore(&mcdt->lock, flags);
836d7bff893SBaolin Wang return;
837d7bff893SBaolin Wang }
838d7bff893SBaolin Wang
839d7bff893SBaolin Wang switch (chan->type) {
840d7bff893SBaolin Wang case SPRD_MCDT_ADC_CHAN:
841d7bff893SBaolin Wang sprd_mcdt_adc_dma_enable(mcdt, chan->id, false);
842d7bff893SBaolin Wang sprd_mcdt_adc_fifo_clear(mcdt, chan->id);
843d7bff893SBaolin Wang break;
844d7bff893SBaolin Wang
845d7bff893SBaolin Wang case SPRD_MCDT_DAC_CHAN:
846d7bff893SBaolin Wang sprd_mcdt_dac_dma_enable(mcdt, chan->id, false);
847d7bff893SBaolin Wang sprd_mcdt_dac_fifo_clear(mcdt, chan->id);
848d7bff893SBaolin Wang break;
849d7bff893SBaolin Wang
850d7bff893SBaolin Wang default:
851d7bff893SBaolin Wang break;
852d7bff893SBaolin Wang }
853d7bff893SBaolin Wang
854d7bff893SBaolin Wang chan->dma_enable = false;
855d7bff893SBaolin Wang spin_unlock_irqrestore(&mcdt->lock, flags);
856d7bff893SBaolin Wang }
857d7bff893SBaolin Wang EXPORT_SYMBOL_GPL(sprd_mcdt_chan_dma_disable);
858d7bff893SBaolin Wang
859d7bff893SBaolin Wang /**
860d7bff893SBaolin Wang * sprd_mcdt_request_chan - request one MCDT channel
861d7bff893SBaolin Wang * @channel: channel id
862d7bff893SBaolin Wang * @type: channel type, it can be one ADC channel or DAC channel
863d7bff893SBaolin Wang *
864d7bff893SBaolin Wang * Rreturn NULL if no available channel.
865d7bff893SBaolin Wang */
sprd_mcdt_request_chan(u8 channel,enum sprd_mcdt_channel_type type)866d7bff893SBaolin Wang struct sprd_mcdt_chan *sprd_mcdt_request_chan(u8 channel,
867d7bff893SBaolin Wang enum sprd_mcdt_channel_type type)
868d7bff893SBaolin Wang {
86957fc2bbcSAndy Shevchenko struct sprd_mcdt_chan *temp;
870d7bff893SBaolin Wang
871d7bff893SBaolin Wang mutex_lock(&sprd_mcdt_list_mutex);
872d7bff893SBaolin Wang
873d7bff893SBaolin Wang list_for_each_entry(temp, &sprd_mcdt_chan_list, list) {
874d7bff893SBaolin Wang if (temp->type == type && temp->id == channel) {
87557fc2bbcSAndy Shevchenko list_del_init(&temp->list);
876d7bff893SBaolin Wang break;
877d7bff893SBaolin Wang }
878d7bff893SBaolin Wang }
879d7bff893SBaolin Wang
88057fc2bbcSAndy Shevchenko if (list_entry_is_head(temp, &sprd_mcdt_chan_list, list))
88157fc2bbcSAndy Shevchenko temp = NULL;
882d7bff893SBaolin Wang
883d7bff893SBaolin Wang mutex_unlock(&sprd_mcdt_list_mutex);
884d7bff893SBaolin Wang
88557fc2bbcSAndy Shevchenko return temp;
886d7bff893SBaolin Wang }
887d7bff893SBaolin Wang EXPORT_SYMBOL_GPL(sprd_mcdt_request_chan);
888d7bff893SBaolin Wang
889d7bff893SBaolin Wang /**
890d7bff893SBaolin Wang * sprd_mcdt_free_chan - free one MCDT channel
891d7bff893SBaolin Wang * @chan: the channel to be freed
892d7bff893SBaolin Wang */
sprd_mcdt_free_chan(struct sprd_mcdt_chan * chan)893d7bff893SBaolin Wang void sprd_mcdt_free_chan(struct sprd_mcdt_chan *chan)
894d7bff893SBaolin Wang {
895d7bff893SBaolin Wang struct sprd_mcdt_chan *temp;
896d7bff893SBaolin Wang
897d7bff893SBaolin Wang sprd_mcdt_chan_dma_disable(chan);
898d7bff893SBaolin Wang sprd_mcdt_chan_int_disable(chan);
899d7bff893SBaolin Wang
900d7bff893SBaolin Wang mutex_lock(&sprd_mcdt_list_mutex);
901d7bff893SBaolin Wang
902d7bff893SBaolin Wang list_for_each_entry(temp, &sprd_mcdt_chan_list, list) {
903d7bff893SBaolin Wang if (temp == chan) {
904d7bff893SBaolin Wang mutex_unlock(&sprd_mcdt_list_mutex);
905d7bff893SBaolin Wang return;
906d7bff893SBaolin Wang }
907d7bff893SBaolin Wang }
908d7bff893SBaolin Wang
909d7bff893SBaolin Wang list_add_tail(&chan->list, &sprd_mcdt_chan_list);
910d7bff893SBaolin Wang mutex_unlock(&sprd_mcdt_list_mutex);
911d7bff893SBaolin Wang }
912d7bff893SBaolin Wang EXPORT_SYMBOL_GPL(sprd_mcdt_free_chan);
913d7bff893SBaolin Wang
sprd_mcdt_init_chans(struct sprd_mcdt_dev * mcdt,struct resource * res)914d7bff893SBaolin Wang static void sprd_mcdt_init_chans(struct sprd_mcdt_dev *mcdt,
915d7bff893SBaolin Wang struct resource *res)
916d7bff893SBaolin Wang {
917d7bff893SBaolin Wang int i;
918d7bff893SBaolin Wang
919d7bff893SBaolin Wang for (i = 0; i < MCDT_CHANNEL_NUM; i++) {
920d7bff893SBaolin Wang struct sprd_mcdt_chan *chan = &mcdt->chan[i];
921d7bff893SBaolin Wang
922d7bff893SBaolin Wang if (i < MCDT_ADC_CHANNEL_NUM) {
923d7bff893SBaolin Wang chan->id = i;
924d7bff893SBaolin Wang chan->type = SPRD_MCDT_ADC_CHAN;
925d7bff893SBaolin Wang chan->fifo_phys = res->start + MCDT_CH0_RXD + i * 4;
926d7bff893SBaolin Wang } else {
927d7bff893SBaolin Wang chan->id = i - MCDT_ADC_CHANNEL_NUM;
928d7bff893SBaolin Wang chan->type = SPRD_MCDT_DAC_CHAN;
929d7bff893SBaolin Wang chan->fifo_phys = res->start + MCDT_CH0_TXD +
930d7bff893SBaolin Wang (i - MCDT_ADC_CHANNEL_NUM) * 4;
931d7bff893SBaolin Wang }
932d7bff893SBaolin Wang
933d7bff893SBaolin Wang chan->mcdt = mcdt;
934d7bff893SBaolin Wang INIT_LIST_HEAD(&chan->list);
935d7bff893SBaolin Wang
936d7bff893SBaolin Wang mutex_lock(&sprd_mcdt_list_mutex);
937d7bff893SBaolin Wang list_add_tail(&chan->list, &sprd_mcdt_chan_list);
938d7bff893SBaolin Wang mutex_unlock(&sprd_mcdt_list_mutex);
939d7bff893SBaolin Wang }
940d7bff893SBaolin Wang }
941d7bff893SBaolin Wang
sprd_mcdt_probe(struct platform_device * pdev)942d7bff893SBaolin Wang static int sprd_mcdt_probe(struct platform_device *pdev)
943d7bff893SBaolin Wang {
944d7bff893SBaolin Wang struct sprd_mcdt_dev *mcdt;
945d7bff893SBaolin Wang struct resource *res;
946d7bff893SBaolin Wang int ret, irq;
947d7bff893SBaolin Wang
948d7bff893SBaolin Wang mcdt = devm_kzalloc(&pdev->dev, sizeof(*mcdt), GFP_KERNEL);
949d7bff893SBaolin Wang if (!mcdt)
950d7bff893SBaolin Wang return -ENOMEM;
951d7bff893SBaolin Wang
95219b71456SYang Yingliang mcdt->base = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
9537c88b928SWei Yongjun if (IS_ERR(mcdt->base))
9547c88b928SWei Yongjun return PTR_ERR(mcdt->base);
955d7bff893SBaolin Wang
956d7bff893SBaolin Wang mcdt->dev = &pdev->dev;
957d7bff893SBaolin Wang spin_lock_init(&mcdt->lock);
958d7bff893SBaolin Wang platform_set_drvdata(pdev, mcdt);
959d7bff893SBaolin Wang
960d7bff893SBaolin Wang irq = platform_get_irq(pdev, 0);
961cf9441adSStephen Boyd if (irq < 0)
962d7bff893SBaolin Wang return irq;
963d7bff893SBaolin Wang
964d7bff893SBaolin Wang ret = devm_request_irq(&pdev->dev, irq, sprd_mcdt_irq_handler,
965d7bff893SBaolin Wang 0, "sprd-mcdt", mcdt);
966d7bff893SBaolin Wang if (ret) {
967d7bff893SBaolin Wang dev_err(&pdev->dev, "Failed to request MCDT IRQ\n");
968d7bff893SBaolin Wang return ret;
969d7bff893SBaolin Wang }
970d7bff893SBaolin Wang
971d7bff893SBaolin Wang sprd_mcdt_init_chans(mcdt, res);
972d7bff893SBaolin Wang
973d7bff893SBaolin Wang return 0;
974d7bff893SBaolin Wang }
975d7bff893SBaolin Wang
sprd_mcdt_remove(struct platform_device * pdev)976*ed771e2bSUwe Kleine-König static void sprd_mcdt_remove(struct platform_device *pdev)
977d7bff893SBaolin Wang {
97881a812c9SWei Yongjun struct sprd_mcdt_chan *chan, *temp;
979d7bff893SBaolin Wang
980d7bff893SBaolin Wang mutex_lock(&sprd_mcdt_list_mutex);
981d7bff893SBaolin Wang
98281a812c9SWei Yongjun list_for_each_entry_safe(chan, temp, &sprd_mcdt_chan_list, list)
98381a812c9SWei Yongjun list_del(&chan->list);
984d7bff893SBaolin Wang
985d7bff893SBaolin Wang mutex_unlock(&sprd_mcdt_list_mutex);
986d7bff893SBaolin Wang }
987d7bff893SBaolin Wang
988d7bff893SBaolin Wang static const struct of_device_id sprd_mcdt_of_match[] = {
989d7bff893SBaolin Wang { .compatible = "sprd,sc9860-mcdt", },
990d7bff893SBaolin Wang { }
991d7bff893SBaolin Wang };
992d7bff893SBaolin Wang MODULE_DEVICE_TABLE(of, sprd_mcdt_of_match);
993d7bff893SBaolin Wang
994d7bff893SBaolin Wang static struct platform_driver sprd_mcdt_driver = {
995d7bff893SBaolin Wang .probe = sprd_mcdt_probe,
996*ed771e2bSUwe Kleine-König .remove_new = sprd_mcdt_remove,
997d7bff893SBaolin Wang .driver = {
998d7bff893SBaolin Wang .name = "sprd-mcdt",
999d7bff893SBaolin Wang .of_match_table = sprd_mcdt_of_match,
1000d7bff893SBaolin Wang },
1001d7bff893SBaolin Wang };
1002d7bff893SBaolin Wang
1003d7bff893SBaolin Wang module_platform_driver(sprd_mcdt_driver);
1004d7bff893SBaolin Wang
1005d7bff893SBaolin Wang MODULE_DESCRIPTION("Spreadtrum Multi-Channel Data Transfer Driver");
1006d7bff893SBaolin Wang MODULE_LICENSE("GPL v2");
1007