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