173 lines
4.4 KiB
C
173 lines
4.4 KiB
C
/*
|
|
* Copyright (c) 2023 Intel Corporation.
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
#define DT_DRV_COMPAT intel_lpss
|
|
|
|
#include <errno.h>
|
|
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <zephyr/kernel.h>
|
|
#include <zephyr/device.h>
|
|
#include <zephyr/init.h>
|
|
#include <zephyr/drivers/dma.h>
|
|
#include <zephyr/drivers/dma/dma_intel_lpss.h>
|
|
#include "dma_dw_common.h"
|
|
#include <soc.h>
|
|
|
|
#include <zephyr/logging/log.h>
|
|
#include <zephyr/irq.h>
|
|
LOG_MODULE_REGISTER(dma_intel_lpss, CONFIG_DMA_LOG_LEVEL);
|
|
|
|
struct dma_intel_lpss_cfg {
|
|
struct dw_dma_dev_cfg dw_cfg;
|
|
};
|
|
|
|
int dma_intel_lpss_setup(const struct device *dev)
|
|
{
|
|
struct dma_intel_lpss_cfg *dev_cfg = (struct dma_intel_lpss_cfg *)dev->config;
|
|
|
|
if (dev_cfg->dw_cfg.base != 0) {
|
|
return dw_dma_setup(dev);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
void dma_intel_lpss_set_base(const struct device *dev, uintptr_t base)
|
|
{
|
|
struct dma_intel_lpss_cfg *dev_cfg = (struct dma_intel_lpss_cfg *)dev->config;
|
|
|
|
dev_cfg->dw_cfg.base = base;
|
|
}
|
|
|
|
#ifdef CONFIG_DMA_64BIT
|
|
int dma_intel_lpss_reload(const struct device *dev, uint32_t channel,
|
|
uint64_t src, uint64_t dst, size_t size)
|
|
#else
|
|
int dma_intel_lpss_reload(const struct device *dev, uint32_t channel,
|
|
uint32_t src, uint32_t dst, size_t size)
|
|
#endif
|
|
{
|
|
struct dw_dma_dev_data *const dev_data = dev->data;
|
|
struct dma_intel_lpss_cfg *lpss_dev_cfg = (struct dma_intel_lpss_cfg *)dev->config;
|
|
struct dw_dma_dev_cfg *const dev_cfg = &lpss_dev_cfg->dw_cfg;
|
|
struct dw_dma_chan_data *chan_data;
|
|
uint32_t ctrl_hi = 0;
|
|
|
|
if (channel >= DW_CHAN_COUNT) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
chan_data = &dev_data->chan[channel];
|
|
|
|
chan_data->lli_current->sar = src;
|
|
chan_data->lli_current->dar = dst;
|
|
chan_data->ptr_data.current_ptr = dst;
|
|
chan_data->ptr_data.buffer_bytes = size;
|
|
|
|
ctrl_hi = dw_read(dev_cfg->base, DW_CTRL_HIGH(channel));
|
|
ctrl_hi &= ~(DW_CTLH_DONE(1) | DW_CTLH_BLOCK_TS_MASK);
|
|
ctrl_hi |= size & DW_CTLH_BLOCK_TS_MASK;
|
|
|
|
chan_data->lli_current->ctrl_hi = ctrl_hi;
|
|
chan_data->ptr_data.start_ptr = DW_DMA_LLI_ADDRESS(chan_data->lli_current,
|
|
chan_data->direction);
|
|
chan_data->ptr_data.end_ptr = chan_data->ptr_data.start_ptr +
|
|
chan_data->ptr_data.buffer_bytes;
|
|
chan_data->ptr_data.hw_ptr = chan_data->ptr_data.start_ptr;
|
|
|
|
chan_data->state = DW_DMA_PREPARED;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int dma_intel_lpss_get_status(const struct device *dev, uint32_t channel,
|
|
struct dma_status *stat)
|
|
{
|
|
struct dma_intel_lpss_cfg *lpss_dev_cfg = (struct dma_intel_lpss_cfg *)dev->config;
|
|
struct dw_dma_dev_cfg *const dev_cfg = &lpss_dev_cfg->dw_cfg;
|
|
struct dw_dma_dev_data *const dev_data = dev->data;
|
|
struct dw_dma_chan_data *chan_data;
|
|
uint32_t ctrl_hi;
|
|
size_t current_length;
|
|
bool done;
|
|
|
|
if (channel >= DW_CHAN_COUNT) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
chan_data = &dev_data->chan[channel];
|
|
ctrl_hi = dw_read(dev_cfg->base, DW_CTRL_HIGH(channel));
|
|
current_length = ctrl_hi & DW_CTLH_BLOCK_TS_MASK;
|
|
done = ctrl_hi & DW_CTLH_DONE(1);
|
|
|
|
if (!(dw_read(dev_cfg->base, DW_DMA_CHAN_EN) & DW_CHAN(channel))) {
|
|
stat->busy = false;
|
|
stat->pending_length = chan_data->ptr_data.buffer_bytes;
|
|
return 0;
|
|
}
|
|
stat->busy = true;
|
|
|
|
if (done) {
|
|
stat->pending_length = 0;
|
|
} else if (current_length == chan_data->ptr_data.buffer_bytes) {
|
|
stat->pending_length = chan_data->ptr_data.buffer_bytes;
|
|
} else {
|
|
stat->pending_length =
|
|
chan_data->ptr_data.buffer_bytes - current_length;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
void dma_intel_lpss_isr(const struct device *dev)
|
|
{
|
|
dw_dma_isr(dev);
|
|
}
|
|
|
|
static const struct dma_driver_api dma_intel_lpss_driver_api = {
|
|
.config = dw_dma_config,
|
|
.start = dw_dma_start,
|
|
.reload = dma_intel_lpss_reload,
|
|
.get_status = dma_intel_lpss_get_status,
|
|
.stop = dw_dma_stop,
|
|
};
|
|
|
|
#define DMA_INTEL_LPSS_INIT(n) \
|
|
\
|
|
static struct dw_drv_plat_data dma_intel_lpss##n = { \
|
|
.chan[0] = { \
|
|
.class = 6, \
|
|
.weight = 0, \
|
|
}, \
|
|
.chan[1] = { \
|
|
.class = 6, \
|
|
.weight = 0, \
|
|
}, \
|
|
}; \
|
|
\
|
|
\
|
|
static struct dma_intel_lpss_cfg dma_intel_lpss##n##_config = { \
|
|
.dw_cfg = { \
|
|
.base = 0, \
|
|
}, \
|
|
}; \
|
|
\
|
|
static struct dw_dma_dev_data dma_intel_lpss##n##_data = { \
|
|
.channel_data = &dma_intel_lpss##n, \
|
|
}; \
|
|
\
|
|
DEVICE_DT_INST_DEFINE(n, \
|
|
NULL, \
|
|
NULL, \
|
|
&dma_intel_lpss##n##_data, \
|
|
&dma_intel_lpss##n##_config, PRE_KERNEL_1, \
|
|
CONFIG_DMA_INIT_PRIORITY, \
|
|
&dma_intel_lpss_driver_api); \
|
|
|
|
DT_INST_FOREACH_STATUS_OKAY(DMA_INTEL_LPSS_INIT)
|