1 /*
2  * Copyright (c) 2023 Intel Corporation.
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #define DT_DRV_COMPAT intel_lpss
8 
9 #include <errno.h>
10 
11 #include <stdio.h>
12 #include <string.h>
13 #include <zephyr/kernel.h>
14 #include <zephyr/device.h>
15 #include <zephyr/init.h>
16 #include <zephyr/drivers/dma.h>
17 #include <zephyr/drivers/dma/dma_intel_lpss.h>
18 #include "dma_dw_common.h"
19 #include <soc.h>
20 
21 #include <zephyr/logging/log.h>
22 #include <zephyr/irq.h>
23 LOG_MODULE_REGISTER(dma_intel_lpss, CONFIG_DMA_LOG_LEVEL);
24 
25 struct dma_intel_lpss_cfg {
26 	struct dw_dma_dev_cfg dw_cfg;
27 };
28 
dma_intel_lpss_setup(const struct device * dev)29 int dma_intel_lpss_setup(const struct device *dev)
30 {
31 	struct dma_intel_lpss_cfg *dev_cfg = (struct dma_intel_lpss_cfg *)dev->config;
32 
33 	if (dev_cfg->dw_cfg.base != 0) {
34 		return dw_dma_setup(dev);
35 	}
36 
37 	return 0;
38 }
39 
dma_intel_lpss_set_base(const struct device * dev,uintptr_t base)40 void dma_intel_lpss_set_base(const struct device *dev, uintptr_t base)
41 {
42 	struct dma_intel_lpss_cfg *dev_cfg = (struct dma_intel_lpss_cfg *)dev->config;
43 
44 	dev_cfg->dw_cfg.base = base;
45 }
46 
47 #ifdef CONFIG_DMA_64BIT
dma_intel_lpss_reload(const struct device * dev,uint32_t channel,uint64_t src,uint64_t dst,size_t size)48 int dma_intel_lpss_reload(const struct device *dev, uint32_t channel,
49 			      uint64_t src, uint64_t dst, size_t size)
50 #else
51 int dma_intel_lpss_reload(const struct device *dev, uint32_t channel,
52 			      uint32_t src, uint32_t dst, size_t size)
53 #endif
54 {
55 	struct dw_dma_dev_data *const dev_data = dev->data;
56 	struct dma_intel_lpss_cfg *lpss_dev_cfg = (struct dma_intel_lpss_cfg *)dev->config;
57 	struct dw_dma_dev_cfg *const dev_cfg = &lpss_dev_cfg->dw_cfg;
58 	struct dw_dma_chan_data *chan_data;
59 	uint32_t ctrl_hi = 0;
60 
61 	if (channel >= DW_CHAN_COUNT) {
62 		return -EINVAL;
63 	}
64 
65 	chan_data = &dev_data->chan[channel];
66 
67 	chan_data->lli_current->sar = src;
68 	chan_data->lli_current->dar = dst;
69 	chan_data->ptr_data.current_ptr = dst;
70 	chan_data->ptr_data.buffer_bytes = size;
71 
72 	ctrl_hi = dw_read(dev_cfg->base, DW_CTRL_HIGH(channel));
73 	ctrl_hi &= ~(DW_CTLH_DONE(1) | DW_CTLH_BLOCK_TS_MASK);
74 	ctrl_hi |= size & DW_CTLH_BLOCK_TS_MASK;
75 
76 	chan_data->lli_current->ctrl_hi = ctrl_hi;
77 	chan_data->ptr_data.start_ptr = DW_DMA_LLI_ADDRESS(chan_data->lli_current,
78 							 chan_data->direction);
79 	chan_data->ptr_data.end_ptr = chan_data->ptr_data.start_ptr +
80 				    chan_data->ptr_data.buffer_bytes;
81 	chan_data->ptr_data.hw_ptr = chan_data->ptr_data.start_ptr;
82 
83 	chan_data->state = DW_DMA_PREPARED;
84 
85 	return 0;
86 }
87 
dma_intel_lpss_get_status(const struct device * dev,uint32_t channel,struct dma_status * stat)88 int dma_intel_lpss_get_status(const struct device *dev, uint32_t channel,
89 			      struct dma_status *stat)
90 {
91 	struct dma_intel_lpss_cfg *lpss_dev_cfg = (struct dma_intel_lpss_cfg *)dev->config;
92 	struct dw_dma_dev_cfg *const dev_cfg = &lpss_dev_cfg->dw_cfg;
93 	struct dw_dma_dev_data *const dev_data = dev->data;
94 	struct dw_dma_chan_data *chan_data;
95 	uint32_t ctrl_hi;
96 	size_t current_length;
97 	bool done;
98 
99 	if (channel >= DW_CHAN_COUNT) {
100 		return -EINVAL;
101 	}
102 
103 	chan_data = &dev_data->chan[channel];
104 	ctrl_hi = dw_read(dev_cfg->base, DW_CTRL_HIGH(channel));
105 	current_length = ctrl_hi & DW_CTLH_BLOCK_TS_MASK;
106 	done = ctrl_hi & DW_CTLH_DONE(1);
107 
108 	if (!(dw_read(dev_cfg->base, DW_DMA_CHAN_EN) & DW_CHAN(channel))) {
109 		stat->busy = false;
110 		stat->pending_length = chan_data->ptr_data.buffer_bytes;
111 		return 0;
112 	}
113 	stat->busy = true;
114 
115 	if (done) {
116 		stat->pending_length = 0;
117 	} else if (current_length == chan_data->ptr_data.buffer_bytes) {
118 		stat->pending_length = chan_data->ptr_data.buffer_bytes;
119 	} else {
120 		stat->pending_length =
121 			chan_data->ptr_data.buffer_bytes - current_length;
122 	}
123 
124 	return 0;
125 }
126 
dma_intel_lpss_isr(const struct device * dev)127 void dma_intel_lpss_isr(const struct device *dev)
128 {
129 	dw_dma_isr(dev);
130 }
131 
132 static const struct dma_driver_api dma_intel_lpss_driver_api = {
133 	.config = dw_dma_config,
134 	.start = dw_dma_start,
135 	.reload = dma_intel_lpss_reload,
136 	.get_status = dma_intel_lpss_get_status,
137 	.stop = dw_dma_stop,
138 };
139 
140 #define DMA_INTEL_LPSS_INIT(n)						\
141 									\
142 	static struct dw_drv_plat_data dma_intel_lpss##n = {		\
143 		.chan[0] = {						\
144 			.class  = 6,					\
145 			.weight = 0,					\
146 		},							\
147 		.chan[1] = {						\
148 			.class  = 6,					\
149 			.weight = 0,					\
150 		},							\
151 	};								\
152 									\
153 									\
154 	static struct dma_intel_lpss_cfg dma_intel_lpss##n##_config = {	\
155 		.dw_cfg = {						\
156 			.base = 0,					\
157 		},							\
158 	};								\
159 									\
160 	static struct dw_dma_dev_data dma_intel_lpss##n##_data = {	\
161 		.channel_data = &dma_intel_lpss##n,			\
162 	};								\
163 									\
164 	DEVICE_DT_INST_DEFINE(n,					\
165 			    NULL,					\
166 			    NULL,					\
167 			    &dma_intel_lpss##n##_data,			\
168 			    &dma_intel_lpss##n##_config, PRE_KERNEL_1,	\
169 			    CONFIG_DMA_INIT_PRIORITY,			\
170 			    &dma_intel_lpss_driver_api);		\
171 
172 DT_INST_FOREACH_STATUS_OKAY(DMA_INTEL_LPSS_INIT)
173