505 lines
14 KiB
C
Executable File
505 lines
14 KiB
C
Executable File
/*
|
|
* Novatek IVOT USB HOST EHCI Controller
|
|
*
|
|
* Copyright (C) 2020 NOVATEK MICROELECTRONICS CORP
|
|
*
|
|
* SPDX-License-Identifier: GPL-2.0+
|
|
*/
|
|
|
|
#include <common.h>
|
|
#include <dm.h>
|
|
#include <fdtdec.h>
|
|
#include <malloc.h>
|
|
#include <usb.h>
|
|
#include <asm/io.h>
|
|
#include "ehci.h"
|
|
#include <asm/arch/IOAddress.h>
|
|
#include <asm/arch/hardware.h>
|
|
#include <asm/gpio.h>
|
|
#include <asm/nvt-common/nvt_types.h>
|
|
#include <asm/nvt-common/nvt_common.h>
|
|
#include <linux/libfdt.h>
|
|
|
|
/* Declare global data pointer */
|
|
DECLARE_GLOBAL_DATA_PTR;
|
|
#define U2PHY_SETREG(ofs,value) writel((value), (volatile void __iomem *)(IOADDR_USBPHY_REG_BASE + ((ofs)<<2)))
|
|
#define U2PHY_GETREG(ofs) readl((volatile void __iomem *)(IOADDR_USBPHY_REG_BASE + ((ofs)<<2)))
|
|
#define DRV_VERSION "1.00.00"
|
|
|
|
static void nvtim_set_gpio_power(int index)
|
|
{
|
|
/* Enable it after dts parsing ready*/
|
|
ulong fdt_addr = nvt_readl((ulong)nvt_shminfo_boot_fdt_addr);
|
|
int nodeoffset;
|
|
u32 *cell = NULL;
|
|
char path[20] = {0};
|
|
int gpio_pin, gpio_value, err;
|
|
|
|
if (index) {
|
|
sprintf(path,"/usb20@2,%lx",(IOADDR_USB2_REG_BASE & 0xFFFFFFFF));
|
|
} else {
|
|
sprintf(path,"/usb20@2,%lx",(IOADDR_USB_REG_BASE & 0xFFFFFFFF));
|
|
}
|
|
|
|
nodeoffset = fdt_path_offset((const void*)fdt_addr, path);
|
|
|
|
cell = (u32*)fdt_getprop((const void*)fdt_addr, nodeoffset, "power_gpio", NULL);
|
|
|
|
if (cell > 0) {
|
|
gpio_pin = __be32_to_cpu(cell[0]);
|
|
gpio_value = __be32_to_cpu(cell[1]);
|
|
|
|
#if defined(CONFIG_TARGET_NA51090_A64)
|
|
if ((gpio_pin >= J_GPIO(0)) && (gpio_pin <= J_GPIO(4))) {
|
|
u32 pinmux_value;
|
|
unsigned long tmpval;
|
|
sprintf(path,"/top@%lx/misc",(IOADDR_TOP_REG_BASE & 0xFFFFFFFF));
|
|
nodeoffset = fdt_path_offset((const void*)fdt_addr, path);
|
|
cell = (u32*)fdt_getprop((const void*)fdt_addr, nodeoffset, "pinmux", NULL);
|
|
pinmux_value = __be32_to_cpu(cell[0]);
|
|
|
|
if ((pinmux_value & 0x1) == 0x0) {
|
|
tmpval = readl((volatile unsigned long *)(IOADDR_TOP_REG_BASE+0x04));
|
|
tmpval &= ~(0x1 << 16);
|
|
writel(tmpval, (volatile unsigned long *)(IOADDR_TOP_REG_BASE+0x04));
|
|
|
|
tmpval = readl((volatile unsigned long *)(IOADDR_TOP_REG_BASE+0xA4));
|
|
tmpval |= (0x1 << (gpio_pin & 0xF));
|
|
writel(tmpval, (volatile unsigned long *)(IOADDR_TOP_REG_BASE+0xA4));
|
|
}
|
|
}
|
|
#endif
|
|
err = gpio_request(gpio_pin, "vbus");
|
|
if (err) {
|
|
printf("#### failed to request vbus, %d\n", gpio_pin);
|
|
return;
|
|
}
|
|
|
|
gpio_direction_output(gpio_pin, gpio_value);
|
|
gpio_free(gpio_pin);
|
|
}
|
|
}
|
|
|
|
|
|
#ifdef CONFIG_DM_USB
|
|
struct uboot_ehci_nvt_priv_data{
|
|
struct ehci_ctrl ctrl; /* Needed by EHCI */
|
|
struct ehci_hccr *hccr;
|
|
struct ehci_hcor *hcor;
|
|
};
|
|
|
|
static int ehci_usb_ofdata_to_platdata(struct udevice *dev)
|
|
{
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
static int nvt_get_port_speed(struct ehci_ctrl *ctrl, uint32_t reg)
|
|
{
|
|
unsigned tmp = ehci_readl(((void *)ctrl->hcor) + 0x70);
|
|
|
|
switch ((tmp >> 22) & 3) {
|
|
case 0:
|
|
return PORTSC_PSPD_FS;
|
|
case 1:
|
|
return PORTSC_PSPD_LS;
|
|
case 2:
|
|
default:
|
|
return PORTSC_PSPD_HS;
|
|
}
|
|
}
|
|
|
|
const struct ehci_ops nvt_ehci_ops = {
|
|
.set_usb_mode = NULL,
|
|
.get_port_speed = nvt_get_port_speed,
|
|
.powerup_fixup = NULL,
|
|
.get_portsc_register = NULL,
|
|
};
|
|
|
|
#ifndef CONFIG_DM_USB
|
|
static void nvtim_init_usbhc(int index)
|
|
{
|
|
unsigned long usbbase;
|
|
unsigned long tmpval = 0;
|
|
|
|
usbbase = IOADDR_USB3PHY_REG_BASE;
|
|
tmpval = readl((volatile unsigned long *)(usbbase+0x680));
|
|
tmpval = 0x40;
|
|
writel(tmpval, (volatile unsigned long *)(usbbase+0x680));
|
|
|
|
if (index) {
|
|
usbbase = IOADDR_USB2_REG_BASE;
|
|
|
|
/* Enable Reset*/
|
|
tmpval = readl((volatile unsigned long *)(IOADDR_CG_REG_BASE+0x98));
|
|
tmpval &= ~(0x1<<16);
|
|
writel(tmpval, (volatile unsigned long *)(IOADDR_CG_REG_BASE+0x98));
|
|
|
|
udelay(10);
|
|
|
|
/* Disable Reset*/
|
|
tmpval = readl((volatile unsigned long *)(IOADDR_CG_REG_BASE+0x98));
|
|
tmpval |= 0x1<<16;
|
|
writel(tmpval, (volatile unsigned long *)(IOADDR_CG_REG_BASE+0x98));
|
|
|
|
/* Release sram shutdown*/
|
|
tmpval = readl((volatile unsigned long *)(IOADDR_TOP_REG_BASE+0x1000));
|
|
tmpval &= ~(0x1<<8);
|
|
writel(tmpval, (volatile unsigned long *)(IOADDR_TOP_REG_BASE+0x1000));
|
|
|
|
/* Enable clock*/
|
|
tmpval = readl((volatile unsigned long *)(IOADDR_CG_REG_BASE+0x74));
|
|
tmpval |= (0x1<<25);
|
|
writel(tmpval, (volatile unsigned long *)(IOADDR_CG_REG_BASE+0x74));
|
|
} else {
|
|
usbbase = IOADDR_USB_REG_BASE;
|
|
|
|
/* Enable Reset*/
|
|
tmpval = readl((volatile unsigned long *)(IOADDR_CG_REG_BASE+0x98));
|
|
tmpval &= ~(0x1<<15);
|
|
writel(tmpval, (volatile unsigned long *)(IOADDR_CG_REG_BASE+0x98));
|
|
|
|
udelay(10);
|
|
|
|
/* Disable Reset*/
|
|
tmpval = readl((volatile unsigned long *)(IOADDR_CG_REG_BASE+0x98));
|
|
tmpval |= 0x1<<15;
|
|
writel(tmpval, (volatile unsigned long *)(IOADDR_CG_REG_BASE+0x98));
|
|
|
|
/* Release sram shutdown*/
|
|
tmpval = readl((volatile unsigned long *)(IOADDR_TOP_REG_BASE+0x1000));
|
|
tmpval &= ~(0x1<<7);
|
|
writel(tmpval, (volatile unsigned long *)(IOADDR_TOP_REG_BASE+0x1000));
|
|
|
|
/* Enable clock*/
|
|
tmpval = readl((volatile unsigned long *)(IOADDR_CG_REG_BASE+0x74));
|
|
tmpval |= (0x1<<24);
|
|
writel(tmpval, (volatile unsigned long *)(IOADDR_CG_REG_BASE+0x74));
|
|
}
|
|
|
|
mdelay(10);
|
|
|
|
/* Set USB ID & VBUSI */
|
|
tmpval = readl((volatile unsigned long *)(usbbase+0x400));
|
|
tmpval |= 0x1 << 20;
|
|
writel(tmpval, (volatile unsigned long *)(usbbase+0x400));
|
|
|
|
/* Clear FORCE_FS[9] and handle HALF_SPEED[1] */
|
|
tmpval = readl((volatile unsigned long *)(usbbase+0x100));
|
|
tmpval &= ~(0x1<<9);
|
|
#ifdef CONFIG_NVT_FPGA_EMULATION
|
|
tmpval |= (0x1<<1);
|
|
#endif
|
|
writel(tmpval, (volatile unsigned long *)(usbbase+0x100));
|
|
|
|
/* Clear DEVPHY_SUSPEND[5] */
|
|
tmpval = readl((volatile unsigned long *)(usbbase+0x1C8));
|
|
tmpval &= ~(0x1<<5);
|
|
writel(tmpval, (volatile unsigned long *)(usbbase+0x1C8));
|
|
|
|
/* Clear HOSTPHY_SUSPEND[6] */
|
|
tmpval = readl((volatile unsigned long *)(usbbase+0x40));
|
|
tmpval &= ~(0x1<<6);
|
|
writel(tmpval, (volatile unsigned long *)(usbbase+0x40));
|
|
|
|
/* USB_ACCESS_SELECT[2] to 0 (DRAM only) */
|
|
tmpval = readl((volatile unsigned long *)(usbbase+0x1C4));
|
|
tmpval &= ~(0x1<<2);
|
|
writel(tmpval, (volatile unsigned long *)(usbbase+0x1C4));
|
|
|
|
/* Host EOF_BEHAVE[31] = 0 */
|
|
tmpval = readl((volatile unsigned long *)(usbbase+0x80));
|
|
tmpval &= ~(0x1<<31);
|
|
writel(tmpval, (volatile unsigned long *)(usbbase+0x80));
|
|
|
|
/* Clear EOF1=3[3:2] EOF2[5:4]=0 */
|
|
tmpval = readl((volatile unsigned long *)(usbbase+0x40));
|
|
tmpval &= ~(0x3<<4);
|
|
tmpval |= (0x3<<2);
|
|
tmpval &= ~(0x3F<<8);
|
|
tmpval |= (0x22<<8);
|
|
writel(tmpval, (volatile unsigned long *)(usbbase+0x40));
|
|
|
|
/* A_BUS_DROP[5] = 0 */
|
|
tmpval = readl((volatile unsigned long *)(usbbase+0x80));
|
|
tmpval &= ~(0x1<<5);
|
|
writel(tmpval, (volatile unsigned long *)(usbbase+0x80));
|
|
|
|
mdelay(2);
|
|
|
|
/* A_BUS_REQ[4] = 1 */
|
|
tmpval = readl((volatile unsigned long *)(usbbase+0x80));
|
|
tmpval |= (0x1<<4);
|
|
writel(tmpval, (volatile unsigned long *)(usbbase+0x80));
|
|
}
|
|
/*
|
|
* Create the appropriate control structures to manage
|
|
* a new EHCI host controller.
|
|
*/
|
|
int ehci_hcd_init(int index, enum usb_init_type init,
|
|
struct ehci_hccr **hccr, struct ehci_hcor **hcor)
|
|
{
|
|
nvtim_init_usbhc(index);
|
|
|
|
if (index)
|
|
*hccr = (struct ehci_hccr *)IOADDR_USB2_REG_BASE;
|
|
else
|
|
*hccr = (struct ehci_hccr *)IOADDR_USB_REG_BASE;
|
|
|
|
*hcor = (struct ehci_hcor *)((unsigned long)*hccr +
|
|
HC_LENGTH(ehci_readl(&(*hccr)->cr_capbase)));
|
|
|
|
nvtim_set_gpio_power(index);
|
|
|
|
ehci_set_controller_priv(index, NULL, &nvt_ehci_ops);
|
|
|
|
printf("nvtimusb 0x%lx %s\n", (unsigned long) *hccr, DRV_VERSION);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Destroy the appropriate control structures corresponding
|
|
* the the EHCI host controller.
|
|
*/
|
|
int ehci_hcd_stop(int index)
|
|
{
|
|
unsigned long usbbase;
|
|
unsigned long tmpval = 0;
|
|
|
|
if (index)
|
|
usbbase = IOADDR_USB2_REG_BASE;
|
|
else
|
|
usbbase = IOADDR_USB_REG_BASE;
|
|
|
|
/* Host Reset */
|
|
writel((readl((volatile unsigned long *)(usbbase+0x10)) | 0x2), (volatile unsigned long *)(usbbase+0x10));
|
|
|
|
/* A_BUS_REQ[4] = 0 */
|
|
tmpval = readl((volatile unsigned long *)(usbbase+0x80));
|
|
tmpval &= ~(0x1<<4);
|
|
writel(tmpval, (volatile unsigned long *)(usbbase+0x80));
|
|
|
|
/* A_BUS_DROP[5] = 1 */
|
|
tmpval = readl((volatile unsigned long *)(usbbase+0x80));
|
|
tmpval |= (0x1<<5);
|
|
writel(tmpval, (volatile unsigned long *)(usbbase+0x80));
|
|
|
|
|
|
/* Enable Reset*/
|
|
tmpval = readl((volatile unsigned long *)(IOADDR_CG_REG_BASE+0x84));
|
|
tmpval &= ~(0x1<<19);
|
|
writel(tmpval, (volatile unsigned long *)(IOADDR_CG_REG_BASE+0x84));
|
|
|
|
udelay(10);
|
|
|
|
/* Disable Reset*/
|
|
tmpval = readl((volatile unsigned long *)(IOADDR_CG_REG_BASE+0x84));
|
|
tmpval |= 0x1<<19;
|
|
writel(tmpval, (volatile unsigned long *)(IOADDR_CG_REG_BASE+0x84));
|
|
|
|
return 0;
|
|
}
|
|
|
|
#else
|
|
static void nvtim_init_usbhc_dm(int index, struct ehci_hccr **hccr, struct ehci_hcor **hcor)
|
|
{
|
|
unsigned long usbbase;
|
|
unsigned long tmpval = 0;
|
|
|
|
usbbase = IOADDR_USB3PHY_REG_BASE;
|
|
tmpval = readl((volatile unsigned long *)(usbbase+0x680));
|
|
tmpval = 0x40;
|
|
writel(tmpval, (volatile unsigned long *)(usbbase+0x680));
|
|
|
|
if (index) {
|
|
usbbase = IOADDR_USB2_REG_BASE;
|
|
|
|
/* Enable Reset*/
|
|
tmpval = readl((volatile unsigned long *)(IOADDR_CG_REG_BASE+0x98));
|
|
tmpval &= ~(0x1<<16);
|
|
writel(tmpval, (volatile unsigned long *)(IOADDR_CG_REG_BASE+0x98));
|
|
|
|
udelay(10);
|
|
|
|
/* Disable Reset*/
|
|
tmpval = readl((volatile unsigned long *)(IOADDR_CG_REG_BASE+0x98));
|
|
tmpval |= 0x1<<16;
|
|
writel(tmpval, (volatile unsigned long *)(IOADDR_CG_REG_BASE+0x98));
|
|
|
|
/* Release sram shutdown*/
|
|
tmpval = readl((volatile unsigned long *)(IOADDR_TOP_REG_BASE+0x1000));
|
|
tmpval &= ~(0x1<<8);
|
|
writel(tmpval, (volatile unsigned long *)(IOADDR_TOP_REG_BASE+0x1000));
|
|
|
|
/* Enable clock*/
|
|
tmpval = readl((volatile unsigned long *)(IOADDR_CG_REG_BASE+0x74));
|
|
tmpval |= (0x1<<25);
|
|
writel(tmpval, (volatile unsigned long *)(IOADDR_CG_REG_BASE+0x74));
|
|
} else {
|
|
usbbase = IOADDR_USB_REG_BASE;
|
|
|
|
/* Enable Reset*/
|
|
tmpval = readl((volatile unsigned long *)(IOADDR_CG_REG_BASE+0x98));
|
|
tmpval &= ~(0x1<<15);
|
|
writel(tmpval, (volatile unsigned long *)(IOADDR_CG_REG_BASE+0x98));
|
|
|
|
udelay(10);
|
|
|
|
/* Disable Reset*/
|
|
tmpval = readl((volatile unsigned long *)(IOADDR_CG_REG_BASE+0x98));
|
|
tmpval |= 0x1<<15;
|
|
writel(tmpval, (volatile unsigned long *)(IOADDR_CG_REG_BASE+0x98));
|
|
|
|
/* Release sram shutdown*/
|
|
tmpval = readl((volatile unsigned long *)(IOADDR_TOP_REG_BASE+0x1000));
|
|
tmpval &= ~(0x1<<7);
|
|
writel(tmpval, (volatile unsigned long *)(IOADDR_TOP_REG_BASE+0x1000));
|
|
|
|
/* Enable clock*/
|
|
tmpval = readl((volatile unsigned long *)(IOADDR_CG_REG_BASE+0x74));
|
|
tmpval |= (0x1<<24);
|
|
writel(tmpval, (volatile unsigned long *)(IOADDR_CG_REG_BASE+0x74));
|
|
}
|
|
|
|
mdelay(10);
|
|
|
|
/* Set USB ID & VBUSI */
|
|
tmpval = readl((volatile unsigned long *)(usbbase+0x400));
|
|
tmpval |= 0x1 << 20;
|
|
writel(tmpval, (volatile unsigned long *)(usbbase+0x400));
|
|
|
|
/* Clear FORCE_FS[9] and handle HALF_SPEED[1] */
|
|
tmpval = readl((volatile unsigned long *)(usbbase+0x100));
|
|
tmpval &= ~(0x1<<9);
|
|
#ifdef CONFIG_NVT_FPGA_EMULATION
|
|
tmpval |= (0x1<<1);
|
|
#endif
|
|
writel(tmpval, (volatile unsigned long *)(usbbase+0x100));
|
|
|
|
/* Clear DEVPHY_SUSPEND[5] */
|
|
tmpval = readl((volatile unsigned long *)(usbbase+0x1C8));
|
|
tmpval &= ~(0x1<<5);
|
|
writel(tmpval, (volatile unsigned long *)(usbbase+0x1C8));
|
|
|
|
/* Clear HOSTPHY_SUSPEND[6] */
|
|
tmpval = readl((volatile unsigned long *)(usbbase+0x40));
|
|
tmpval &= ~(0x1<<6);
|
|
writel(tmpval, (volatile unsigned long *)(usbbase+0x40));
|
|
|
|
/* USB_ACCESS_SELECT[2] to 0 (DRAM only) */
|
|
tmpval = readl((volatile unsigned long *)(usbbase+0x1C4));
|
|
tmpval &= ~(0x1<<2);
|
|
writel(tmpval, (volatile unsigned long *)(usbbase+0x1C4));
|
|
|
|
/* Host EOF_BEHAVE[31] = 0 */
|
|
tmpval = readl((volatile unsigned long *)(usbbase+0x80));
|
|
tmpval &= ~(0x1<<31);
|
|
writel(tmpval, (volatile unsigned long *)(usbbase+0x80));
|
|
|
|
/* Clear EOF1=3[3:2] EOF2[5:4]=0 */
|
|
tmpval = readl((volatile unsigned long *)(usbbase+0x40));
|
|
tmpval &= ~(0x3<<4);
|
|
tmpval |= (0x3<<2);
|
|
tmpval &= ~(0x3F<<8);
|
|
tmpval |= (0x22<<8);
|
|
writel(tmpval, (volatile unsigned long *)(usbbase+0x40));
|
|
|
|
/* A_BUS_DROP[5] = 0 */
|
|
tmpval = readl((volatile unsigned long *)(usbbase+0x80));
|
|
tmpval &= ~(0x1<<5);
|
|
writel(tmpval, (volatile unsigned long *)(usbbase+0x80));
|
|
|
|
mdelay(2);
|
|
|
|
/* A_BUS_REQ[4] = 1 */
|
|
tmpval = readl((volatile unsigned long *)(usbbase+0x80));
|
|
tmpval |= (0x1<<4);
|
|
writel(tmpval, (volatile unsigned long *)(usbbase+0x80));
|
|
|
|
*hccr = (struct ehci_hccr *)usbbase;
|
|
*hcor = (struct ehci_hcor *)((unsigned long)*hccr +
|
|
HC_LENGTH(ehci_readl(&(*hccr)->cr_capbase)));
|
|
}
|
|
|
|
static int ehci_usb_probe(struct udevice *dev)
|
|
{
|
|
struct uboot_ehci_nvt_priv_data *ctx = dev_get_priv(dev);
|
|
unsigned long base_addr = dev_read_addr(dev);
|
|
int index;
|
|
|
|
printf("0x%lx ver %s\n", base_addr, DRV_VERSION);
|
|
|
|
if (base_addr == IOADDR_USB_REG_BASE)
|
|
index = 0;
|
|
else
|
|
index = 1;
|
|
|
|
nvtim_init_usbhc_dm(index, &ctx->hccr , &ctx->hcor);
|
|
|
|
nvtim_set_gpio_power(index);
|
|
|
|
return ehci_register(dev, ctx->hccr, ctx->hcor, &nvt_ehci_ops, 0, USB_INIT_HOST);
|
|
}
|
|
|
|
static int ehci_usb_remove(struct udevice *dev)
|
|
{
|
|
int ret;
|
|
unsigned long usbbase;
|
|
unsigned long tmpval = 0;
|
|
unsigned long base_addr = dev_read_addr(dev);
|
|
|
|
usbbase = base_addr;
|
|
|
|
/* Host Reset */
|
|
writel((readl((volatile unsigned long *)(usbbase+0x10)) | 0x2), (volatile unsigned long *)(usbbase+0x10));
|
|
|
|
/* A_BUS_REQ[4] = 0 */
|
|
tmpval = readl((volatile unsigned long *)(usbbase+0x80));
|
|
tmpval &= ~(0x1<<4);
|
|
writel(tmpval, (volatile unsigned long *)(usbbase+0x80));
|
|
|
|
/* A_BUS_DROP[5] = 1 */
|
|
tmpval = readl((volatile unsigned long *)(usbbase+0x80));
|
|
tmpval |= (0x1<<5);
|
|
writel(tmpval, (volatile unsigned long *)(usbbase+0x80));
|
|
|
|
|
|
/* Enable Reset*/
|
|
tmpval = readl((volatile unsigned long *)(IOADDR_CG_REG_BASE+0x98));
|
|
tmpval &= ~(0x1<<15);
|
|
writel(tmpval, (volatile unsigned long *)(IOADDR_CG_REG_BASE+0x98));
|
|
|
|
udelay(10);
|
|
|
|
/* Disable Reset*/
|
|
tmpval = readl((volatile unsigned long *)(IOADDR_CG_REG_BASE+0x98));
|
|
tmpval |= 0x1<<15;
|
|
writel(tmpval, (volatile unsigned long *)(IOADDR_CG_REG_BASE+0x98));
|
|
|
|
|
|
ret = ehci_deregister(dev);
|
|
if (ret)
|
|
return ret;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static const struct udevice_id ehci_usb_ids[] = {
|
|
{ .compatible = "nvt,ehci-nvtivot" },
|
|
{ }
|
|
};
|
|
|
|
U_BOOT_DRIVER(usb_ehci) = {
|
|
.name = "ehci_nvt",
|
|
.id = UCLASS_USB,
|
|
.of_match = ehci_usb_ids,
|
|
.ofdata_to_platdata = ehci_usb_ofdata_to_platdata,
|
|
.probe = ehci_usb_probe,
|
|
.remove = ehci_usb_remove,
|
|
.ops = &ehci_usb_ops,
|
|
.priv_auto_alloc_size = sizeof(struct uboot_ehci_nvt_priv_data),
|
|
// .platdata_auto_alloc_size = sizeof(struct exynos_ehci_platdata),
|
|
.flags = DM_FLAG_ALLOC_PRIV_DMA,
|
|
};
|
|
#endif |