Skip to content

Commit

Permalink
dma: ace: Add ace gpdma driver
Browse files Browse the repository at this point in the history
Add ace gpdma driver.

Co-authored-by: Adrian Bonislawski <adrian.bonislawski@intel.com>
Signed-off-by: Adrian Bonislawski <adrian.bonislawski@intel.com>
Signed-off-by: Flavio Ceolin <flavio.ceolin@intel.com>
  • Loading branch information
2 people authored and carlescufi committed Jul 11, 2022
1 parent f18f94e commit d8c7aed
Show file tree
Hide file tree
Showing 3 changed files with 327 additions and 0 deletions.
1 change: 1 addition & 0 deletions drivers/dma/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,4 @@ zephyr_library_sources_ifdef(CONFIG_DMA_CAVS_HDA_HOST_IN dma_cavs_hda_host_in.c)
zephyr_library_sources_ifdef(CONFIG_DMA_CAVS_HDA_HOST_OUT dma_cavs_hda_host_out.c)
zephyr_library_sources_ifdef(CONFIG_DMA_CAVS_HDA_LINK_IN dma_cavs_hda_link_in.c)
zephyr_library_sources_ifdef(CONFIG_DMA_CAVS_HDA_LINK_OUT dma_cavs_hda_link_out.c)
zephyr_library_sources_ifdef(CONFIG_DMA_ACE_GPDMA dma_ace_gpdma.c dma_dw_common.c)
6 changes: 6 additions & 0 deletions drivers/dma/Kconfig.ace_gpdma
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,9 @@ config DMA_ACE_GPDMA
default $(dt_compat_enabled,$(DT_COMPAT_DMA_ACE_GPDMA))
help
Intel ACE DMA driver.

if DMA_ACE_GPDMA

source "drivers/dma/Kconfig.dw_common"

endif # DMA_ACE_GPDMA
320 changes: 320 additions & 0 deletions drivers/dma/dma_ace_gpdma.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,320 @@
/*
* Copyright (c) 2022 Intel Corporation.
*
* SPDX-License-Identifier: Apache-2.0
*/

#include "drivers/dma.h"
#define DT_DRV_COMPAT intel_ace_gpdma

/* TODO make device tree defined? */
#define GPDMA_CTL_OFFSET 0x0004
#define GPDMA_CTL_DGCD BIT(30)

/* TODO make device tree defined? */
#define GPDMA_CHLLPC_OFFSET(channel) (0x0010 + channel*0x10)
#define GPDMA_CHLLPC_EN BIT(7)
#define GPDMA_CHLLPC_DHRS(x) SET_BITS(6, 0, x)

/* TODO make device tree defined? */
#define GPDMA_CHLLPL(channel) (0x0018 + channel*0x10)
#define GPDMA_CHLLPU(channel) (0x001c + channel*0x10)

#define GPDMA_OSEL(x) SET_BITS(25, 24, x)
#define SHIM_CLKCTL_LPGPDMA_SPA BIT(0)
#define SHIM_CLKCTL_LPGPDMA_CPA BIT(8)

#include <dma/dma_dw_common.h>

#define LOG_LEVEL CONFIG_DMA_LOG_LEVEL
#include <logging/log.h>
LOG_MODULE_REGISTER(dma_ace_gpdma);


/* Device run time data */
struct ace_gpdma_data {
struct dw_dma_dev_data dw_data;
};

/* Device constant configuration parameters */
struct ace_gpdma_cfg {
struct dw_dma_dev_cfg dw_cfg;
uint32_t shim;
};

static void ace_gpdma_llp_config(const struct device *dev, uint32_t channel,
uint32_t addr)
{
const struct ace_gpdma_cfg *const dev_cfg = dev->config;

dw_write(dev_cfg->shim, GPDMA_CHLLPC_OFFSET(channel), GPDMA_CHLLPC_DHRS(addr));
}

static inline void ace_gpdma_llp_enable(const struct device *dev,
uint32_t channel)
{
const struct ace_gpdma_cfg *const dev_cfg = dev->config;
uint32_t val;

val = dw_read(dev_cfg->shim, GPDMA_CHLLPC_OFFSET(channel));
if (!(val & GPDMA_CHLLPC_EN)) {
dw_write(dev_cfg->shim, GPDMA_CHLLPC_OFFSET(channel), val | GPDMA_CHLLPC_EN);
}
}

static inline void ace_gpdma_llp_disable(const struct device *dev,
uint32_t channel)
{
const struct ace_gpdma_cfg *const dev_cfg = dev->config;
uint32_t val;

val = dw_read(dev_cfg->shim, GPDMA_CHLLPC_OFFSET(channel));
dw_write(dev_cfg->shim, GPDMA_CHLLPC_OFFSET(channel), val | GPDMA_CHLLPC_EN);
}

static inline void ace_gpdma_llp_read(const struct device *dev,
uint32_t channel,
uint32_t *llp_l,
uint32_t *llp_u)
{
const struct ace_gpdma_cfg *const dev_cfg = dev->config;

*llp_l = dw_read(dev_cfg->shim, GPDMA_CHLLPL(channel));
*llp_u = dw_read(dev_cfg->shim, GPDMA_CHLLPU(channel));
}


static int ace_gpdma_config(const struct device *dev, uint32_t channel,
struct dma_config *cfg)
{
int res = dw_dma_config(dev, channel, cfg);

if (res != 0) {
return res;
}

struct dma_block_config *block_cfg = cfg->head_block;

/* Assume all scatter/gathers are for the same device? */
switch (cfg->channel_direction) {
case MEMORY_TO_PERIPHERAL:
LOG_DBG("%s: dma %s configuring llp for destination %x",
__func__, dev->name, block_cfg->dest_address);
ace_gpdma_llp_config(dev, channel, block_cfg->dest_address);
break;
case PERIPHERAL_TO_MEMORY:
LOG_DBG("%s: dma %s configuring llp for source %x",
__func__, dev->name, block_cfg->source_address);
ace_gpdma_llp_config(dev, channel, block_cfg->source_address);
break;
default:
break;
}

return res;
}

static int ace_gpdma_start(const struct device *dev, uint32_t channel)
{
int ret;

ace_gpdma_llp_enable(dev, channel);
ret = dw_dma_start(dev, channel);
if (ret != 0) {
ace_gpdma_llp_disable(dev, channel);
}
return ret;
}

static int ace_gpdma_stop(const struct device *dev, uint32_t channel)
{
int ret;

ret = dw_dma_stop(dev, channel);
if (ret == 0) {
ace_gpdma_llp_disable(dev, channel);
}
return ret;
}

int ace_gpdma_copy(const struct device *dev, uint32_t channel,
uint32_t src, uint32_t dst, size_t size)
{
struct dw_dma_dev_data *const dev_data = dev->data;
struct dw_dma_chan_data *chan_data;

if (channel >= DW_MAX_CHAN) {
return -EINVAL;
}

chan_data = &dev_data->chan[channel];

/* default action is to clear the DONE bit for all LLI making
* sure the cache is coherent between DSP and DMAC.
*/
for (int i = 0; i < chan_data->lli_count; i++) {
chan_data->lli[i].ctrl_hi &= ~DW_CTLH_DONE(1);
}

chan_data->ptr_data.current_ptr += size;
if (chan_data->ptr_data.current_ptr >= chan_data->ptr_data.end_ptr) {
chan_data->ptr_data.current_ptr = chan_data->ptr_data.start_ptr +
(chan_data->ptr_data.current_ptr - chan_data->ptr_data.end_ptr);
}

return 0;
}

/* Disables automatic clock gating (force disable clock gate) */
static void ace_gpdma_clock_enable(const struct device *dev)
{
const struct ace_gpdma_cfg *const dev_cfg = dev->config;
uint32_t reg = dev_cfg->shim + GPDMA_CTL_OFFSET;
uint32_t val = sys_read32(reg) | GPDMA_CTL_DGCD;

sys_write32(val, reg);
}

static void ace_gpdma_select_owner(const struct device *dev)
{
const struct ace_gpdma_cfg *const dev_cfg = dev->config;
uint32_t reg = dev_cfg->shim + GPDMA_CTL_OFFSET;
uint32_t val = sys_read32(reg) | GPDMA_OSEL(0x3);

sys_write32(val, reg);
}

static int ace_gpdma_enable(const struct device *dev)
{
const struct ace_gpdma_cfg *const dev_cfg = dev->config;
uint32_t reg = dev_cfg->shim + GPDMA_CTL_OFFSET;

sys_write32(SHIM_CLKCTL_LPGPDMA_SPA, reg);

if (!WAIT_FOR((sys_read32(reg) & SHIM_CLKCTL_LPGPDMA_CPA), 10000, k_busy_wait(1))) {
return -1;
}

return 0;
}

int ace_gpdma_init(const struct device *dev)
{
const struct ace_gpdma_cfg *const dev_cfg = dev->config;
int ret;

/* Power up */
ret = ace_gpdma_enable(dev);
if (ret != 0) {
LOG_ERR("%s: dma %s failed to initialize", __func__,
dev->name);
goto out;
}

/* Disable dynamic clock gating appropriately before initializing */
ace_gpdma_clock_enable(dev);

/* DW DMA Owner Select to DSP */
ace_gpdma_select_owner(dev);

/* Disable all channels and Channel interrupts */
ret = dw_dma_setup(dev);
if (ret != 0) {
LOG_ERR("%s: dma %s failed to initialize", __func__,
dev->name);
goto out;
}

/* Configure interrupts */
dev_cfg->dw_cfg.irq_config();

LOG_INF("%s: dma %s initialized", __func__,
dev->name);

out:
return 0;
}

static const struct dma_driver_api ace_gpdma_driver_api = {
.config = ace_gpdma_config,
.reload = ace_gpdma_copy,
.start = ace_gpdma_start,
.stop = ace_gpdma_stop,
.suspend = dw_dma_suspend,
.resume = dw_dma_resume,
.get_status = dw_dma_get_status,
};

#define ACE_GPDMA_CHAN_ARB_DATA(inst) \
static struct dw_drv_plat_data dmac##inst = { \
.chan[0] = { \
.class = 6, \
.weight = 0, \
}, \
.chan[1] = { \
.class = 6, \
.weight = 0, \
}, \
.chan[2] = { \
.class = 6, \
.weight = 0, \
}, \
.chan[3] = { \
.class = 6, \
.weight = 0, \
}, \
.chan[4] = { \
.class = 6, \
.weight = 0, \
}, \
.chan[5] = { \
.class = 6, \
.weight = 0, \
}, \
.chan[6] = { \
.class = 6, \
.weight = 0, \
}, \
.chan[7] = { \
.class = 6, \
.weight = 0, \
}, \
}

#define ACE_GPDMA_INIT(inst) \
ACE_GPDMA_CHAN_ARB_DATA(inst); \
static void ace_gpdma##inst##_irq_config(void); \
\
static const struct ace_gpdma_cfg ace_gpdma##inst##_config = { \
.dw_cfg = { \
.base = DT_INST_REG_ADDR(inst), \
.irq_config = ace_gpdma##inst##_irq_config, \
}, \
.shim = DT_INST_PROP_BY_IDX(inst, shim, 0), \
}; \
\
static struct ace_gpdma_data ace_gpdma##inst##_data = { \
.dw_data = { \
.channel_data = &dmac##inst, \
}, \
}; \
\
\
DEVICE_DT_INST_DEFINE(inst, \
&ace_gpdma_init, \
NULL, \
&ace_gpdma##inst##_data, \
&ace_gpdma##inst##_config, POST_KERNEL, \
CONFIG_DMA_INIT_PRIORITY, \
&ace_gpdma_driver_api); \
\
static void ace_gpdma##inst##_irq_config(void) \
{ \
IRQ_CONNECT(DT_INST_IRQN(inst), \
DT_INST_IRQ(inst, priority), dw_dma_isr, \
DEVICE_DT_INST_GET(inst), \
DT_INST_IRQ(inst, sense)); \
irq_enable(DT_INST_IRQN(inst)); \
}

DT_INST_FOREACH_STATUS_OKAY(ACE_GPDMA_INIT)

0 comments on commit d8c7aed

Please sign in to comment.