|  | /* | 
|  | * 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 DEVICE_API(dma, 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) |