GK SDK 源码库: XMIPCLinuxV100R005C00SPC030 (kernel/tools/open_source excluded)
This commit is contained in:
+163
@@ -0,0 +1,163 @@
|
||||
|
||||
menu "Lotus SoCs Configuration"
|
||||
|
||||
if ARCH_LOTUS
|
||||
|
||||
source "lotus/machine/Kconfig"
|
||||
|
||||
config TINY_BOOT
|
||||
bool "Enable Tiny boot support"
|
||||
default n
|
||||
help
|
||||
Support for lotus tiny boot solution.
|
||||
|
||||
config QS_MCU
|
||||
bool "Support AE MCU firmware"
|
||||
default n
|
||||
help
|
||||
Support filling AE MCU firmware to boot and loading it
|
||||
|
||||
config LOTUS_EDA
|
||||
bool "Enable EDA support"
|
||||
default n
|
||||
help
|
||||
Support for EDA simulation.
|
||||
|
||||
config LOTUS_FPGA
|
||||
bool "Enable FPGA support"
|
||||
default n
|
||||
help
|
||||
Support for FPGA debug.
|
||||
|
||||
config LOTUS_GZIP_BOOT
|
||||
bool "Use GZIP compressed boot"
|
||||
default y
|
||||
help
|
||||
Use GZIP compressed boot.
|
||||
|
||||
config LOTUS_DISABLE_CONSOLE
|
||||
bool "Disable console"
|
||||
default n
|
||||
help
|
||||
This will disable the console, it may be useful in the final product
|
||||
for security or quick starting resons.
|
||||
|
||||
config AUXCODE_LOG_CTRL
|
||||
int "Auxcode log control"
|
||||
default 1
|
||||
help
|
||||
0 - no log
|
||||
1 - use bootrom info level otp
|
||||
2 - show all log
|
||||
|
||||
config LOTUS_DISABLE_DOWNLOAD
|
||||
bool "Disable downloading from PC tools"
|
||||
default n
|
||||
help
|
||||
This will disable downloading function from the PC side, it may be useful
|
||||
in the final product for security or low-cost flash resons.
|
||||
|
||||
config AUXCODE_SVB_CTRL
|
||||
hex "Auxcode SVB control"
|
||||
default 0
|
||||
help
|
||||
0 - Enable SVB
|
||||
0xC35A3CA5 - Disable SVB
|
||||
|
||||
config HWDEC
|
||||
bool "Enable Hardward Decompressing Support"
|
||||
default y if TARGET_XMFALCON
|
||||
default y if TARGET_XMORCA
|
||||
depends on TARGET_XMFALCON || TARGET_XMORCA
|
||||
help
|
||||
Support hardware decompressing.
|
||||
|
||||
config GCAI_BAD_DDR
|
||||
bool "Enable GCAI Bad DDR Support"
|
||||
default n
|
||||
depends on TARGET_XMFALCON
|
||||
help
|
||||
Support GCAI Bad DDR
|
||||
|
||||
config MMC_8BIT
|
||||
bool "Enable MMC 8 Bit Mode"
|
||||
default n
|
||||
depends on TARGET_XMFALCON || TARGET_XMORCA
|
||||
help
|
||||
Support MMC 8 Bit
|
||||
|
||||
config DDR_PARAM_OFFSET
|
||||
hex "DDR Param Partition Offset"
|
||||
default 0xC0000
|
||||
depends on TARGET_XMORCA
|
||||
help
|
||||
DDR param partition offset
|
||||
|
||||
config DDR_PARAM_SIZE
|
||||
hex "DDR Param Partition Size"
|
||||
default 0x80000
|
||||
depends on TARGET_XMORCA
|
||||
help
|
||||
DDR param partition size
|
||||
|
||||
config AUXCODE_WATCHDOG
|
||||
bool "Auxcode Watchdog Enable"
|
||||
default y
|
||||
depends on WATCHDOG
|
||||
help
|
||||
Auxcode watchdog enable
|
||||
|
||||
config AUXCODE_WDT_TIMEOUT_MSECS
|
||||
int "Auxcode Watchdog Timeout Msecs"
|
||||
default 60000
|
||||
depends on AUXCODE_WATCHDOG
|
||||
help
|
||||
Auxcode watchdog timeout msecs
|
||||
|
||||
config SBL_SUPPORT
|
||||
bool "Enable Second Boot Loader(SBL) Support"
|
||||
default n
|
||||
help
|
||||
Support Second Boot Loader(SBL)
|
||||
|
||||
config SBL_LOAD_ADDR
|
||||
hex "SBL Load Address"
|
||||
default 0x40700000
|
||||
depends on SBL_SUPPORT
|
||||
help
|
||||
Second Boot Loader(SBL) Load Address in DDR
|
||||
|
||||
config SBL_SIZE
|
||||
hex "SBL Size"
|
||||
default 0x40000
|
||||
depends on SBL_SUPPORT
|
||||
help
|
||||
Second Boot Loader(SBL) Image Size
|
||||
|
||||
config SBL_OFFSET
|
||||
hex "SBL Offset"
|
||||
default 0x40000
|
||||
depends on SBL_SUPPORT
|
||||
help
|
||||
Second Boot Loader(SBL) Offset in Flash
|
||||
|
||||
config BOOT_CONSOLE_OPTIONAL
|
||||
bool "Select console support in quick-start mode"
|
||||
help
|
||||
Enable console support during quick-start boot.
|
||||
When enabled, boot mode can be selected via bootenv
|
||||
variables. If disabled, quick-start will run silently
|
||||
without console interaction.
|
||||
|
||||
config BOOTSTRAP_IMAGE
|
||||
string "Bootstrap build directory path"
|
||||
default "../../../../../bootstrap_builddir/u-boot-xmorca.bin"
|
||||
depends on BOOT_CONSOLE_OPTIONAL
|
||||
help
|
||||
Path to the U-Boot build directory.
|
||||
|
||||
source "lotus/drivers/Kconfig"
|
||||
|
||||
endif
|
||||
|
||||
endmenu
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
|
||||
ccflags-y += -Wall -Werror
|
||||
subdir-ccflags-y += -Wall -Werror
|
||||
|
||||
obj-y += machine/
|
||||
obj-y += atags/
|
||||
obj-y += ddr/
|
||||
obj-y += timestamp/
|
||||
obj-y += flash_api/
|
||||
obj-$(CONFIG_HWDEC) += hw_dec/
|
||||
obj-y += securec/
|
||||
obj-y += misc/
|
||||
ifneq ($(CONFIG_TARGET_XMORCA),y)
|
||||
obj-y += atf/
|
||||
endif
|
||||
|
||||
obj-y += drivers/
|
||||
@@ -0,0 +1,3 @@
|
||||
|
||||
obj-y += param_tags.o
|
||||
obj-y += atags.o
|
||||
+144
@@ -0,0 +1,144 @@
|
||||
/******************************************************************************
|
||||
* File: atags.c
|
||||
*
|
||||
* Author: Lynn
|
||||
* Created: 12/15/22
|
||||
* Description: Lotus atags for lotus private kernel booting and boot params
|
||||
*****************************************************************************/
|
||||
|
||||
#include <common.h>
|
||||
#include <command.h>
|
||||
#include <asm/global_data.h>
|
||||
#include <asm/io.h>
|
||||
#include <linux/libfdt.h>
|
||||
#include <image.h>
|
||||
#include <asm/cache.h>
|
||||
#include <bootm.h>
|
||||
#include <usb.h>
|
||||
#include <linux/lotus/hw_decompress.h>
|
||||
#include <linux/lotus/timestamp.h>
|
||||
#include <irq_func.h>
|
||||
#include <dm/device.h>
|
||||
#include <dm/root.h>
|
||||
#include <asm/setup.h>
|
||||
#include <linux/sizes.h>
|
||||
|
||||
#include <linux/lotus/tags.h>
|
||||
|
||||
/* Support ATAGs */
|
||||
static struct tag *g_params;
|
||||
/************************************************************************************************/
|
||||
static void setup_start_tag(bd_t *bd)
|
||||
{
|
||||
g_params = (struct tag *)bd->bi_boot_params;
|
||||
|
||||
g_params->hdr.tag = ATAG_CORE;
|
||||
g_params->hdr.size = tag_size(tag_core);
|
||||
|
||||
g_params->u.core.flags = 0;
|
||||
g_params->u.core.pagesize = 0;
|
||||
g_params->u.core.rootdev = 0;
|
||||
|
||||
g_params = tag_next(g_params);
|
||||
}
|
||||
|
||||
static void setup_commandline_tag(bd_t *bd, char *commandline)
|
||||
{
|
||||
char *p;
|
||||
|
||||
if (!commandline)
|
||||
return;
|
||||
|
||||
/* eat leading white space */
|
||||
for (p = commandline; *p == ' '; p++);
|
||||
|
||||
/* skip non-existent command lines so the kernel will still
|
||||
* use its default command line.
|
||||
*/
|
||||
if (*p == '\0')
|
||||
return;
|
||||
|
||||
g_params->hdr.tag = ATAG_CMDLINE;
|
||||
g_params->hdr.size =
|
||||
(sizeof(struct tag_header) + strlen(p) + 1 + 4) >> 2;
|
||||
|
||||
strcpy(g_params->u.cmdline.cmdline, p);
|
||||
|
||||
g_params = tag_next(g_params);
|
||||
}
|
||||
|
||||
static void setup_fdt_tag(bd_t *bd, char *fdt, int fdt_size)
|
||||
{
|
||||
g_params->hdr.tag = ATAG_FDT;
|
||||
g_params->hdr.size = (sizeof(struct tag_header) + fdt_size) >> 2;
|
||||
memcpy((char *)&g_params->u, fdt, fdt_size);
|
||||
g_params = tag_next (g_params);
|
||||
}
|
||||
|
||||
static void setup_end_tag(bd_t *bd)
|
||||
{
|
||||
g_params->hdr.tag = ATAG_NONE;
|
||||
g_params->hdr.size = 0;
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
* Function: static void setup_param_tag
|
||||
* Description: To setup the PDM params ATAG in the global ATAG area
|
||||
* In:
|
||||
* bd_t *bd - global board info, may not be used
|
||||
* Return: bd_t *bd - global board info, may not be used
|
||||
*****************************************************************************/
|
||||
static void setup_param_tag(bd_t *bd)
|
||||
{
|
||||
int length;
|
||||
|
||||
/* Setup end item in param tag */
|
||||
#define END_OF_PARAM_STR "End of param"
|
||||
set_param_data("end_tag", END_OF_PARAM_STR, sizeof(END_OF_PARAM_STR));
|
||||
|
||||
/* Get all PDM params data from PDM atag */
|
||||
length = get_param_tag_data((char *)&g_params->u);
|
||||
if (length == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
g_params->hdr.tag = ATAG_PARAM;
|
||||
g_params->hdr.size = ((sizeof(struct tag_header) + length) >> 2); /* 2: size is num of words */
|
||||
|
||||
g_params = tag_next(g_params);
|
||||
}
|
||||
|
||||
/* Replace __weak void setup_board_tags(struct tag **in_params) {} */
|
||||
void setup_board_tags(struct tag **in_params)
|
||||
{
|
||||
debug("Setup board tags\n");
|
||||
g_params = *in_params;
|
||||
setup_param_tag(gd->bd);
|
||||
*in_params = g_params;
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
* Function: void setup_atags
|
||||
* Description: Setup all ATAGs to linux
|
||||
* In:
|
||||
* char *fdt - FDT(Device Tree Blob) to be packeted to ATAGs list
|
||||
* int fdt_size - FDT size
|
||||
* Return: NA
|
||||
*****************************************************************************/
|
||||
void setup_atags(char *fdt, int fdt_size)
|
||||
{
|
||||
debug("Setup LOTUS ATAGs\n");
|
||||
|
||||
setup_start_tag(gd->bd);
|
||||
|
||||
#ifdef CONFIG_CMDLINE_TAG
|
||||
setup_commandline_tag(gd->bd, env_get("bootargs"));
|
||||
#endif
|
||||
|
||||
if (fdt)
|
||||
setup_fdt_tag(gd->bd, fdt, fdt_size);
|
||||
|
||||
setup_board_tags(&g_params);
|
||||
|
||||
setup_end_tag(gd->bd);
|
||||
}
|
||||
+110
@@ -0,0 +1,110 @@
|
||||
/******************************************************************************
|
||||
* File: param_tags.c
|
||||
*
|
||||
* Author: Lynn
|
||||
* Created: 12/08/22
|
||||
* Description: PDM Tag data lib, this depends on the ATAGs function of u-boot
|
||||
*****************************************************************************/
|
||||
|
||||
#include <common.h>
|
||||
#include <linux/string.h>
|
||||
|
||||
#define CONFIG_BOOT_PARAMS_SIZE 1024
|
||||
#define CONFIG_BOOT_PARAMS_ITEM_NUM 20
|
||||
|
||||
#undef pr_err
|
||||
#define pr_err(_V) printf _V
|
||||
|
||||
struct tags_item_t {
|
||||
char name[8];
|
||||
unsigned short offset;
|
||||
unsigned short length;
|
||||
};
|
||||
|
||||
struct tags_data_t {
|
||||
int version;
|
||||
unsigned int item_num;
|
||||
unsigned int buflen;
|
||||
char buf[4];
|
||||
};
|
||||
|
||||
struct tags_data_ctrl_t {
|
||||
char buf[CONFIG_BOOT_PARAMS_SIZE];
|
||||
struct tags_item_t item[CONFIG_BOOT_PARAMS_ITEM_NUM];
|
||||
unsigned int item_num;
|
||||
unsigned int buflen;
|
||||
};
|
||||
|
||||
/*****************************************************************************/
|
||||
static struct tags_data_ctrl_t td_ctrl = {
|
||||
.item_num = 0,
|
||||
.buflen = 0,
|
||||
};
|
||||
|
||||
void set_param_data(const char *name, const char *buf, int buflen)
|
||||
{
|
||||
struct tags_item_t *item = NULL;
|
||||
|
||||
/* parameter vaild check */
|
||||
if (!name || !*name || !buf) {
|
||||
pr_err(("%s: bad parameter.\n", __FILE__));
|
||||
BUG();
|
||||
}
|
||||
|
||||
if (td_ctrl.item_num >= CONFIG_BOOT_PARAMS_ITEM_NUM) {
|
||||
pr_err(("%s: boot has not enough parameter item room.\n",
|
||||
__FILE__));
|
||||
BUG();
|
||||
}
|
||||
|
||||
if ((buflen + td_ctrl.buflen) > CONFIG_BOOT_PARAMS_SIZE) {
|
||||
pr_err(("%s: boot has not enough parameter buffer.\n",
|
||||
__FILE__));
|
||||
BUG();
|
||||
}
|
||||
|
||||
for (item = td_ctrl.item;
|
||||
item < &td_ctrl.item[td_ctrl.item_num]; item++) {
|
||||
|
||||
if (!strncmp(item->name, name, 8)) {
|
||||
pr_err(("%s: parameter \"%s\" has exist.\n",
|
||||
__FILE__, name));
|
||||
BUG();
|
||||
}
|
||||
}
|
||||
|
||||
item = &td_ctrl.item[td_ctrl.item_num++];
|
||||
|
||||
strncpy(item->name, name, sizeof(item->name));
|
||||
if (item->name[sizeof(item->name)-1]) {
|
||||
pr_err(("%s: parameter name \"%s\" too longer.\n",
|
||||
__FILE__, name));
|
||||
BUG();
|
||||
}
|
||||
item->name[sizeof(item->name)-1] = '\0';
|
||||
|
||||
item->offset = td_ctrl.buflen;
|
||||
item->length = buflen;
|
||||
td_ctrl.buflen += item->length;
|
||||
|
||||
memcpy(td_ctrl.buf + item->offset, buf, item->length);
|
||||
}
|
||||
|
||||
int get_param_tag_data(char *tagbuf)
|
||||
{
|
||||
struct tags_data_t *data = (struct tags_data_t *)tagbuf;
|
||||
|
||||
if (!td_ctrl.item_num)
|
||||
return 0;
|
||||
|
||||
data->version = 0x00100000;
|
||||
data->item_num = td_ctrl.item_num;
|
||||
data->buflen = (sizeof(struct tags_item_t) * data->item_num);
|
||||
memcpy(data->buf, td_ctrl.item, data->buflen);
|
||||
memcpy(data->buf + data->buflen, td_ctrl.buf, td_ctrl.buflen);
|
||||
|
||||
data->buflen += td_ctrl.buflen;
|
||||
|
||||
/* more than 4 bytes */
|
||||
return ((sizeof(struct tags_data_t) + data->buflen + 0x03) & ~0x03);
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
|
||||
obj-y += bl31.o
|
||||
@@ -0,0 +1,189 @@
|
||||
/*
|
||||
* Copyright (c) LOTUS. All rights reserved.
|
||||
*/
|
||||
|
||||
#define pr_fmt(fmt) "%s: " fmt, "BOOT BL31"
|
||||
|
||||
#include <common.h>
|
||||
#include <image.h>
|
||||
#include <linux/compiler.h>
|
||||
#include <bootm.h>
|
||||
#include <asm/io.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <asm/mach-types.h>
|
||||
#include <linux/libfdt.h>
|
||||
#include <mapmem.h>
|
||||
#include <serial.h>
|
||||
#include <linux/lotus/flash_read.h>
|
||||
#include <linux/lotus/timestamp.h>
|
||||
#include <linux/lotus/atf.h>
|
||||
|
||||
#define OPTEE_MAGIC 0x4554504f
|
||||
|
||||
extern int cleanup_before_linux(void);
|
||||
|
||||
struct optee_header {
|
||||
uint32_t magic;
|
||||
uint8_t version;
|
||||
uint8_t arch;
|
||||
uint16_t flags;
|
||||
uint32_t init_size;
|
||||
uint32_t init_load_addr_hi;
|
||||
uint32_t init_load_addr_lo;
|
||||
uint32_t init_mem_usage;
|
||||
uint32_t paged_size;
|
||||
};
|
||||
|
||||
int do_load_optee_os(ulong addr, uint32_t *rtos_load_addr, uint32_t *aarch_mode)
|
||||
{
|
||||
struct optee_header *imgheader = (struct optee_header *)addr;
|
||||
char *src = (char *)(addr + sizeof(struct optee_header)), *dst = (char *)imgheader->init_load_addr_lo;
|
||||
uint32_t loadaddr, img_size;
|
||||
|
||||
printf("## OpenTEE OS Image:\n");
|
||||
printf(" version: 0x%X\n", imgheader->version);
|
||||
printf(" arch: %s\n", ((imgheader->version) ? "arm32" : "arm64"));
|
||||
printf(" init_size: 0x%X\n", imgheader->init_size);
|
||||
printf(" load_addr_hi: 0x%x (%u KB)\n", imgheader->init_load_addr_hi, imgheader->init_load_addr_hi >> 10);
|
||||
printf(" load_addr_lo: 0x%x (%u KB)\n", imgheader->init_load_addr_lo, imgheader->init_load_addr_lo >> 10);
|
||||
printf(" init_mem_usage: %d\n", imgheader->init_mem_usage);
|
||||
printf(" Tpaged_size: 0x%X (%u KB)\n", imgheader->paged_size, imgheader->paged_size >> 10);
|
||||
|
||||
if (aarch_mode)
|
||||
*aarch_mode = (imgheader->version == 1 ? MODE_RW_32 : MODE_RW_64);
|
||||
|
||||
loadaddr = imgheader->init_load_addr_lo;
|
||||
img_size = imgheader->init_size;
|
||||
|
||||
memmove(dst, src, img_size);
|
||||
|
||||
if (rtos_load_addr)
|
||||
*rtos_load_addr = (uint32_t)imgheader->init_load_addr_lo;
|
||||
|
||||
printf(" succeed to load Optee-OS to :0x%x\n", loadaddr);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int is_optee_img(char *buf)
|
||||
{
|
||||
struct optee_header *hdr = (struct optee_header *)buf;
|
||||
|
||||
if (hdr->magic != OPTEE_MAGIC)
|
||||
return 0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
void run_bl31(bootm_headers_t *bootm_hdr, uint32_t machid)
|
||||
{
|
||||
uint32_t arch;
|
||||
image_header_t *hdr = NULL;
|
||||
image_info_t os;
|
||||
entry_point_info_t bl32_ep;
|
||||
entry_point_info_t bl33_ep;
|
||||
bl31_params_t bl31_p;
|
||||
ulong bl31_pc = 0;
|
||||
u32 val = 0;
|
||||
void *image_buf;
|
||||
ulong image_size;
|
||||
char *show_timestamp;
|
||||
|
||||
if (!bootm_hdr) {
|
||||
pr_err("Invalid params for %s\n", __func__);
|
||||
return;
|
||||
}
|
||||
|
||||
hdr = &(bootm_hdr->legacy_hdr_os_copy);
|
||||
os = bootm_hdr->os;
|
||||
image_size = os.image_len;
|
||||
image_buf = map_sysmem(os.image_start, image_size);
|
||||
|
||||
pr_info("Image addr 0x%lX, len 0x%lX\n", (ulong)image_buf, image_size);
|
||||
|
||||
memset(&bl31_p, 0, sizeof(bl31_params_t));
|
||||
memset(&bl32_ep, 0, sizeof(entry_point_info_t));
|
||||
memset(&bl33_ep, 0, sizeof(entry_point_info_t));
|
||||
bl31_p.bl32_ep_info = (uint64_t)((uint32_t)&bl32_ep);
|
||||
bl31_p.bl33_ep_info = (uint64_t)((uint32_t)&bl33_ep);
|
||||
|
||||
if (image_get_arch(hdr) == IH_ARCH_ARM) {
|
||||
arch = BL33_IMG_ARM32;
|
||||
pr_info("Start bl31 with aarch32 bl33...\n");
|
||||
} else if (image_check_arch(hdr, IH_ARCH_ARM64)) {
|
||||
arch = BL33_IMG_ARM64;
|
||||
pr_info("Start bl31 with aarch64 bl33...\n");
|
||||
} else {
|
||||
pr_err("Invalid ARCH Type!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if(arch == BL33_IMG_ARM64) {
|
||||
bl33_ep.pc = (uint64_t)(image_get_ep(hdr));
|
||||
memmove((char *)CONFIG_DTB_BASE, (char *)(image_buf + image_size), CONFIG_DTB_MAX_SIZE);
|
||||
bl33_ep.args.arg0 = (uint64_t)(CONFIG_DTB_BASE);
|
||||
bl33_ep.args.arg1 = 0;
|
||||
bl33_ep.args.arg2 = 0;
|
||||
bl33_ep.args.arg3 = 0;
|
||||
bl33_ep.spsr = spsr_64(MODE_EL1,MODE_SP_ELX, DISABLE_ALL_EXCEPTIONS);
|
||||
} else if (arch == BL33_IMG_ARM32) {
|
||||
bl33_ep.pc = (uint64_t)(image_get_ep(hdr));
|
||||
memmove((char *)CONFIG_DTB_BASE, (char *)(image_buf + image_size), CONFIG_DTB_MAX_SIZE);
|
||||
bl33_ep.args.arg0 = 0;
|
||||
bl33_ep.args.arg1 = (uint64_t)(machid);
|
||||
bl33_ep.args.arg2 = (uint64_t)(CONFIG_DTB_BASE);
|
||||
bl33_ep.spsr = spsr_mode32(MODE32_SVC, 0x0 , EP_EE_LITTLE, DISABLE_ALL_EXCEPTIONS);
|
||||
}
|
||||
|
||||
if (is_optee_img((char *)CONFIG_BL32_BASE)) {
|
||||
uint32_t rtos_load_addr;
|
||||
uint32_t aarch_mode;
|
||||
|
||||
do_load_optee_os(CONFIG_BL32_BASE, &rtos_load_addr, &aarch_mode);
|
||||
|
||||
bl32_ep.pc = rtos_load_addr;
|
||||
bl32_ep.args.arg0 = aarch_mode;
|
||||
bl32_ep.args.arg1 = 0;
|
||||
bl32_ep.spsr = 0;
|
||||
}
|
||||
|
||||
pr_info("BL33 SPSR: 0x%X\n", bl33_ep.spsr);
|
||||
pr_info("DTB: 0x%llX\n", bl33_ep.args.arg0);
|
||||
|
||||
TIME_STAMP(0);
|
||||
stopwatch_trigger();
|
||||
show_timestamp = env_get("timestamp");
|
||||
if (*show_timestamp == 'y') {
|
||||
serial_enable_output(true);
|
||||
timestamp_print(0);
|
||||
stopwatch_print();
|
||||
}
|
||||
|
||||
bl31_pc = CONFIG_BL31_BASE;
|
||||
|
||||
writel((u32)(bl31_pc >> 2 & 0xFFFFFFFF), REG_SYS_RVBAR_ADDR0);
|
||||
writel(0, REG_SYS_RVBAR_ADDR1);
|
||||
writel(readl(REG_SYS_CPU_AARCH_MODE) | (0x3 << 14), REG_SYS_CPU_AARCH_MODE);
|
||||
|
||||
(*(volatile uint64_t *)0) = (uint64_t)((uintptr_t)&bl31_p);
|
||||
(*(volatile uint64_t *)8) = (uint64_t)((uint32_t)NULL);
|
||||
|
||||
/* __asm__ __volatile__("b ."); */
|
||||
|
||||
pr_info("Start Warm Reseting\n");
|
||||
|
||||
cleanup_before_linux();
|
||||
|
||||
/* warm reseting */
|
||||
__asm__ __volatile__("isb\n\r"
|
||||
"dsb\n\r"
|
||||
"mrc p15, 0, %0, c12, c0, 2\n\r"
|
||||
"orr %0, %0, #0x3\n\r"
|
||||
"mcr p15, 0, %0, c12, c0, 2\n\r"
|
||||
"isb\n\r"
|
||||
"wfi":"=r"(val)::"cc");
|
||||
pr_err("Fail to warm resetting...\n");
|
||||
hang();
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
obj-y += ddr_cmd_ctl.o
|
||||
obj-y += cmd_ddr_training.o
|
||||
obj-$(CONFIG_TARGET_XMFALCON) += ddr_training_xmfalcon.o
|
||||
obj-$(CONFIG_TARGET_XMORCA) += ddr_training_xmorca.o
|
||||
@@ -0,0 +1,311 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
|
||||
#include <common.h>
|
||||
#include <command.h>
|
||||
#include <malloc.h>
|
||||
#include <linux/io.h>
|
||||
#include "ddr_if.h"
|
||||
#include "linux/lotus/i2c.h"
|
||||
#include "linux/lotus/securec.h"
|
||||
|
||||
#define DDR_CMD_DATAEYE_STR "dataeye"
|
||||
#define DDR_CMD_GCAI_INFO "gcai-info"
|
||||
static struct ddr_training_result *g_ddrtr_result;
|
||||
|
||||
static int ddr_map_show(void);
|
||||
|
||||
static int cmd_ddr_match(const char *str, int *cmd)
|
||||
{
|
||||
if (!strncmp(str, DDR_CMD_DATAEYE_STR, sizeof(DDR_CMD_DATAEYE_STR))) {
|
||||
*cmd = DDR_TRAINING_CMD_DATAEYE;
|
||||
} else if (!strncmp(str, DDR_CMD_GCAI_INFO,
|
||||
sizeof(DDR_CMD_GCAI_INFO))) {
|
||||
*cmd = DDR_CMD_SHOW_GCAI_INFO;
|
||||
} else {
|
||||
printf("Command [ddr %s] is not supported\n", str);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cmd_ddr_handle(int cmd)
|
||||
{
|
||||
if (cmd == DDR_TRAINING_CMD_DATAEYE)
|
||||
return get_result(g_ddrtr_result);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cmd_ddr_dispatch(int cmd)
|
||||
{
|
||||
int result;
|
||||
|
||||
result = cmd_ddr_handle(cmd);
|
||||
if (result < 0)
|
||||
return -1;
|
||||
|
||||
switch (cmd) {
|
||||
case DDR_TRAINING_CMD_DATAEYE:
|
||||
ddr_cmd_result_display(g_ddrtr_result, DDR_TRAINING_CMD_DATAEYE);
|
||||
break;
|
||||
case DDR_CMD_SHOW_GCAI_INFO:
|
||||
ddr_map_show();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static int do_ddr_training(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
|
||||
{
|
||||
const char *str = NULL;
|
||||
int cmd;
|
||||
int ret = 0;
|
||||
|
||||
if (argc != 2)
|
||||
return -1;
|
||||
|
||||
str = argv[1];
|
||||
|
||||
if (cmd_ddr_match(str, &cmd)) {
|
||||
ret = -1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
g_ddrtr_result = malloc(sizeof(struct ddr_training_result));
|
||||
if (g_ddrtr_result == NULL) {
|
||||
printf("Fail to alloc DDR Training result memory!\n");
|
||||
ret = -1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (cmd_ddr_dispatch(cmd)) {
|
||||
ret = -1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
out:
|
||||
if (g_ddrtr_result)
|
||||
free(g_ddrtr_result);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int get_ddr4_bank_group_cnt(void)
|
||||
{
|
||||
u32 reg;
|
||||
|
||||
reg = readl(DDRC_AXI_BASE + DDRC_DDR_INFO_REG_OFFSET);
|
||||
reg &= DDR_BG_NUM_MASK;
|
||||
|
||||
return reg ? 4 : 2; /* 1:4-bg, 0: 2-bg */
|
||||
}
|
||||
|
||||
static int get_ddr_type(void)
|
||||
{
|
||||
u32 type = DDR3_TYPE;
|
||||
u32 v = readl(DDRC_TOP_FEATURE) & 0x7;
|
||||
|
||||
switch (v) {
|
||||
case 1:
|
||||
type = DDR3_TYPE;
|
||||
break;
|
||||
case 2:
|
||||
type = DDR4_TYPE;
|
||||
break;
|
||||
case 3:
|
||||
type = LPDDR4_TYPE;
|
||||
break;
|
||||
default:
|
||||
type = v;
|
||||
break;
|
||||
}
|
||||
|
||||
return type;
|
||||
}
|
||||
|
||||
static int get_ddr_bank_cnt(int rank_cnt)
|
||||
{
|
||||
int type, bank_cnt;
|
||||
|
||||
type = get_ddr_type();
|
||||
|
||||
switch (type) {
|
||||
case LPDDR4_TYPE:
|
||||
/* GCAI DDR support 2-chans */
|
||||
bank_cnt = LPDDR4_CHANNEL_NUM * rank_cnt *
|
||||
LPDDR4_BANK_NUM_OF_RANK;
|
||||
break;
|
||||
case DDR4_TYPE:
|
||||
/* Get DDR4 bank group cnt */
|
||||
int bg_cnt;
|
||||
|
||||
bg_cnt = get_ddr4_bank_group_cnt();
|
||||
bank_cnt = DDR4_BANK_NUM_OF_BG * bg_cnt;
|
||||
break;
|
||||
case DDR3_TYPE:
|
||||
/* DDR3 support single channel multi ranks */
|
||||
bank_cnt = rank_cnt * LPDDR4_BANK_NUM_OF_RANK;
|
||||
default:
|
||||
bank_cnt = -1;
|
||||
break;
|
||||
}
|
||||
|
||||
return bank_cnt;
|
||||
}
|
||||
|
||||
static int show_ddr_info(char *buf, unsigned int *rank_cnt,
|
||||
unsigned int *bank_cnt, unsigned int *block_cnt)
|
||||
{
|
||||
struct gcai_base_info *gcaiinfo;
|
||||
int ret;
|
||||
|
||||
gcaiinfo = (struct gcai_base_info *)buf;
|
||||
*rank_cnt = (unsigned int)gcaiinfo->cs_num;
|
||||
|
||||
*bank_cnt = get_ddr_bank_cnt(*rank_cnt);
|
||||
if (*bank_cnt <= 0) {
|
||||
printf("%s-%d: get_ddr_bank_cnt err\n", __func__, __LINE__);
|
||||
return -1;
|
||||
}
|
||||
|
||||
*block_cnt = (*bank_cnt) * 8;
|
||||
|
||||
ret = get_ddr_type();
|
||||
if (ret == LPDDR4_TYPE)
|
||||
printf("GCAI LPDDR4: channel=2, ");
|
||||
else if (ret == DDR4_TYPE)
|
||||
printf("GCAI DDR4: channel=1, ");
|
||||
else {
|
||||
printf("not support GCAI DDR type!\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
printf("rank=%u, bank=%u, block=%u\n", *rank_cnt, *bank_cnt,
|
||||
*block_cnt);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int show_gcai_bad_blk_info(char *bad_blk_info, int expect_len)
|
||||
{
|
||||
#ifdef CONFIG_LOTUS_I2C
|
||||
int ret = 0, i = 0, actual_len = 64;
|
||||
struct i2c_client client;
|
||||
char *read_ptr = bad_blk_info;
|
||||
|
||||
hal_i2c_init(0); /* i2c0 init */
|
||||
client.i2c_num = 0; /* read by i2c0 */
|
||||
client.dev_addr = 0x50; /* dev addr without RD flag */
|
||||
client.reg_width = 1; /* per reg addr width is 1-Byte */
|
||||
|
||||
printf("=======begin to show GCAI DDR info=======\n");
|
||||
for (i = 0; i < expect_len; i++) {
|
||||
/* read reg addr start from [0x0, 0x63] */
|
||||
client.reg_addr = i;
|
||||
/* per reg read return 1-byte data */
|
||||
ret = hal_i2c_recv(&client, (void *)read_ptr, 1);
|
||||
if (ret) {
|
||||
printf("%s: i2c recv err, ret=%d\n", __func__, ret);
|
||||
break;
|
||||
}
|
||||
read_ptr++;
|
||||
}
|
||||
|
||||
for (i = 0; i < actual_len; i++)
|
||||
printf("bad_blk_info[%d]: 0x%x\n", i, bad_blk_info[i]);
|
||||
|
||||
return ret;
|
||||
#else
|
||||
printf("%s: i2c is not enabled.\n", __func__);
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
static void show_bad_blk_config_reg(void)
|
||||
{
|
||||
int i;
|
||||
u32 reg, offset;
|
||||
|
||||
for (i = 0; i < BAD_BLK_MAP_REG_NUM; i++) {
|
||||
offset = i * 4;
|
||||
reg = readl(CHAN0_BAD_BLK_MAP_REG0 + offset);
|
||||
printf("ch0: reg%d(0x%x): 0x%x\n", i, CHAN0_BAD_BLK_MAP_REG0 + offset,
|
||||
reg);
|
||||
}
|
||||
|
||||
for (i = 0; i < BAD_BLK_MAP_REG_NUM; i++) {
|
||||
offset = i * 4;
|
||||
reg = readl(CHAN1_BAD_BLK_MAP_REG0 + offset);
|
||||
printf("ch1: reg%d(0x%x): 0x%x\n", i, CHAN1_BAD_BLK_MAP_REG0 + offset,
|
||||
reg);
|
||||
}
|
||||
printf("=======end of show GCAI DDR info=======\n");
|
||||
}
|
||||
|
||||
/*
|
||||
* is_gcai_ddr - Identify whether use GCAI DDR.
|
||||
* 1:GCAI DDR, 0:non-GCAI DDR.
|
||||
*/
|
||||
static int is_gcai_ddr(void)
|
||||
{
|
||||
u32 reg;
|
||||
|
||||
reg = readl(REG_BASE_SCTL + GCAI_FLAG_REG_OFFSET);
|
||||
|
||||
return (reg & GCAI_FLAG_MASK) ? 1 : 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* ddr_map_show - GCAI DDR bad blk remap info.
|
||||
*/
|
||||
static int ddr_map_show(void)
|
||||
{
|
||||
char *bad_blk_info = NULL;
|
||||
int ret, read_len = 256;
|
||||
unsigned int rank_cnt, bank_cnt, blk_cnt;
|
||||
|
||||
if (!is_gcai_ddr()) {
|
||||
printf("%s: not GCAI DDR, show nothing.\n", __func__);
|
||||
ret = 0;
|
||||
goto out;
|
||||
}
|
||||
|
||||
bad_blk_info = malloc(read_len * sizeof(char));
|
||||
if (bad_blk_info == NULL) {
|
||||
printf("%s: out of memory!\n", __func__);
|
||||
ret = -1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
memset_s(bad_blk_info, read_len, 0xEE, read_len);
|
||||
|
||||
/* show bad blk info that read from i2c */
|
||||
ret = show_gcai_bad_blk_info(bad_blk_info, read_len);
|
||||
if (ret)
|
||||
goto free_mem;
|
||||
|
||||
/* show ddr detail info */
|
||||
ret = show_ddr_info(bad_blk_info, &rank_cnt, &bank_cnt, &blk_cnt);
|
||||
if (ret)
|
||||
goto free_mem;
|
||||
|
||||
/* show all bad blk config reg */
|
||||
show_bad_blk_config_reg();
|
||||
|
||||
free_mem:
|
||||
free(bad_blk_info);
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
U_BOOT_CMD(
|
||||
ddr, CONFIG_SYS_MAXARGS, 0, do_ddr_training,
|
||||
"ddr training cmd",
|
||||
"dataeye - DDR dataeye training.\n"
|
||||
"gcai-info - show GCAI DDR bad block remap info.\n"
|
||||
);
|
||||
|
||||
@@ -0,0 +1,191 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
|
||||
#include <common.h>
|
||||
#include "ddr_if.h"
|
||||
|
||||
#ifndef PHY_DQ_BDL_LEVEL
|
||||
#define PHY_DQ_BDL_LEVEL 256
|
||||
#endif
|
||||
|
||||
static void print_dataeye_win(unsigned int dq_num, unsigned int range,
|
||||
unsigned int dq, unsigned int win)
|
||||
{
|
||||
unsigned int k;
|
||||
|
||||
printf("%-4u", dq_num);
|
||||
for (k = 0; k < PHY_DQ_BDL_LEVEL; k += 2) {
|
||||
if (k >= (range >> DDR_DATAEYE_RESULT_BIT) &&
|
||||
k <= (range & DDR_DATAEYE_RESULT_MASK))
|
||||
printf("%-1s", "-");
|
||||
else
|
||||
printf("%-1s", "X");
|
||||
}
|
||||
printf(" %-4u %-4u %-4u %-4u\n", range >> 16, range & 0xffff, dq, win);
|
||||
}
|
||||
|
||||
static void print_dataeye_title(void)
|
||||
{
|
||||
unsigned int k;
|
||||
|
||||
printf("%-4s", "DQ");
|
||||
for (k = 0; k < PHY_DQ_BDL_LEVEL; k++) {
|
||||
if (k % 8 == 0) /* Print out the CA number which is a multiple of 4 */
|
||||
printf("%-4u", k);
|
||||
}
|
||||
printf(" %-4s %-4s %-4s %-4s\n", "MIN", "MAX", "AVG", "WIN");
|
||||
}
|
||||
|
||||
static void ddr_cmd_result_print_write_dataeye(const struct ddr_training_data *ddrtr_data,
|
||||
unsigned int win_min, unsigned int win_max, unsigned int win_sum)
|
||||
{
|
||||
unsigned int i, j;
|
||||
unsigned int dq_num, dq, win;
|
||||
|
||||
printf("Write window:\n");
|
||||
printf("--------------------------------------------------------\n");
|
||||
print_dataeye_title();
|
||||
if (ddrtr_data->byte_num > DDR_PHY_BYTE_MAX) {
|
||||
printf("byte num error, byte_num = %x", ddrtr_data->byte_num);
|
||||
return;
|
||||
}
|
||||
for (j = 0; j < ddrtr_data->byte_num; j++) {
|
||||
for (i = 0; i < DDR_PHY_BIT_NUM; i++) {
|
||||
dq_num = (j << 3) + i; /* shift left 3: 8 bit */
|
||||
if (dq_num >= DDR_PHY_BIT_MAX)
|
||||
return;
|
||||
|
||||
win = ddrtr_data->write.ddr_bit_best[dq_num] >> DDR_DATAEYE_RESULT_BIT;
|
||||
if (win < win_min)
|
||||
win_min = win;
|
||||
if (win > win_max)
|
||||
win_max = win;
|
||||
win_sum += win;
|
||||
dq = ddrtr_data->write.ddr_bit_best[dq_num] & DDR_DATAEYE_RESULT_MASK;
|
||||
print_dataeye_win(dq_num,
|
||||
ddrtr_data->write.ddr_bit_result[dq_num], dq, win);
|
||||
}
|
||||
}
|
||||
printf("--------------------------------------------------------\n");
|
||||
printf("Sum WIN: %u. Avg WIN: %u\n", win_sum,
|
||||
win_sum / (ddrtr_data->byte_num * DDR_PHY_BIT_NUM));
|
||||
printf("Min WIN: %u. DQ Index: ", win_min);
|
||||
for (i = 0; i < DDR_PHY_BIT_MAX; i++) {
|
||||
win = ddrtr_data->write.ddr_bit_best[i] >> DDR_DATAEYE_RESULT_BIT;
|
||||
if (win == win_min)
|
||||
printf("%u ", i);
|
||||
}
|
||||
printf("\nMax WIN: %u. DQ Index: ", win_max);
|
||||
for (i = 0; i < DDR_PHY_BIT_MAX; i++) {
|
||||
win = ddrtr_data->write.ddr_bit_best[i] >> DDR_DATAEYE_RESULT_BIT;
|
||||
if (win == win_max)
|
||||
printf("%u ", i);
|
||||
}
|
||||
printf("\n\n");
|
||||
}
|
||||
|
||||
static void ddr_cmd_result_print_read_dataeye(const struct ddr_training_data *ddrtr_data,
|
||||
unsigned int win_min, unsigned int win_max, unsigned int win_sum)
|
||||
{
|
||||
unsigned int i, j;
|
||||
unsigned int dq_num, dq, win;
|
||||
|
||||
if (ddrtr_data->byte_num > DDR_PHY_BYTE_MAX) {
|
||||
printf("Invalid byte_num = %d", ddrtr_data->byte_num);
|
||||
return;
|
||||
}
|
||||
printf("Read window:\n");
|
||||
printf("--------------------------------------------------------\n");
|
||||
print_dataeye_title();
|
||||
for (j = 0; j < ddrtr_data->byte_num; j++) {
|
||||
for (i = 0; i < DDR_PHY_BIT_NUM; i++) {
|
||||
dq_num = (j << 3) + i; /* shift left 3: 8 bit */
|
||||
if (dq_num >= DDR_PHY_BIT_MAX)
|
||||
return;
|
||||
|
||||
win = ddrtr_data->read.ddr_bit_best[dq_num] >> DDR_DATAEYE_RESULT_BIT;
|
||||
if (win < win_min)
|
||||
win_min = win;
|
||||
if (win > win_max)
|
||||
win_max = win;
|
||||
win_sum += win;
|
||||
dq = ddrtr_data->read.ddr_bit_best[dq_num] & DDR_DATAEYE_RESULT_MASK;
|
||||
print_dataeye_win(dq_num,
|
||||
ddrtr_data->read.ddr_bit_result[dq_num], dq, win);
|
||||
}
|
||||
}
|
||||
printf("--------------------------------------------------------\n");
|
||||
printf("Sum WIN: %u. Avg WIN: %u\n", win_sum,
|
||||
win_sum / (ddrtr_data->byte_num * DDR_PHY_BIT_NUM));
|
||||
printf("Min WIN: %u. DQ Index: ", win_min);
|
||||
for (i = 0; i < DDR_PHY_BIT_MAX; i++) {
|
||||
win = ddrtr_data->read.ddr_bit_best[i] >> DDR_DATAEYE_RESULT_BIT;
|
||||
if (win == win_min)
|
||||
printf("%u ", i);
|
||||
}
|
||||
printf("\nMax WIN: %u. DQ Index: ", win_max);
|
||||
for (i = 0; i < DDR_PHY_BIT_MAX; i++) {
|
||||
win = ddrtr_data->read.ddr_bit_best[i] >> DDR_DATAEYE_RESULT_BIT;
|
||||
if (win == win_max)
|
||||
printf("%u ", i);
|
||||
}
|
||||
printf("\n\n");
|
||||
}
|
||||
|
||||
static void ddr_cmd_result_print_dataeye(const struct ddr_training_data *ddrtr_data)
|
||||
{
|
||||
/* Write window */
|
||||
ddr_cmd_result_print_write_dataeye(ddrtr_data, PHY_DQ_BDL_LEVEL, 0, 0);
|
||||
|
||||
/* Read window */
|
||||
ddr_cmd_result_print_read_dataeye(ddrtr_data, PHY_DQ_BDL_LEVEL, 0, 0);
|
||||
}
|
||||
|
||||
|
||||
static void ddr_cmd_result_print_by_rank(const struct ddr_training_result *ddrtr_result,
|
||||
unsigned int cmd, unsigned int phy_index, unsigned int rank_index)
|
||||
{
|
||||
const struct rank_data *rank = &ddrtr_result->phy[phy_index].rank[rank_index];
|
||||
|
||||
printf("\r\n[PHY%u][RANK%u]:\r\n", phy_index, rank_index);
|
||||
if (DDR_TRAINING_CMD_DATAEYE & cmd)
|
||||
ddr_cmd_result_print_dataeye(&rank->ddrtr_data);
|
||||
|
||||
}
|
||||
|
||||
static void ddr_cmd_result_print_by_phy(const struct ddr_training_result *ddrtr_result,
|
||||
unsigned int cmd, unsigned int phy_index)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (phy_index >= DDR_PHY_NUM) {
|
||||
printf("Array index phy_idx out of range");
|
||||
return;
|
||||
}
|
||||
if (ddrtr_result->phy[phy_index].rank_num > DDR_SUPPORT_RANK_MAX) {
|
||||
printf("loop upper limit rank number out of range, rank_num = %x",
|
||||
ddrtr_result->phy[phy_index].rank_num);
|
||||
return;
|
||||
}
|
||||
|
||||
for (i = 0; i < ddrtr_result->phy[phy_index].rank_num; i++)
|
||||
ddr_cmd_result_print_by_rank(ddrtr_result, cmd, phy_index, i);
|
||||
}
|
||||
|
||||
void ddr_cmd_result_display(const struct ddr_training_result *ddrtr_result, unsigned int cmd)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (ddrtr_result == NULL) {
|
||||
printf("Pointer parameter ddrtr_result is NULL!");
|
||||
return;
|
||||
}
|
||||
if (ddrtr_result->phy_num > DDR_PHY_NUM)
|
||||
return;
|
||||
|
||||
for (i = 0; i < ddrtr_result->phy_num; i++)
|
||||
ddr_cmd_result_print_by_phy(ddrtr_result, cmd, i);
|
||||
|
||||
show_win_reg();
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,92 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0+ */
|
||||
|
||||
#ifndef _DDR_IF_H
|
||||
#define _DDR_IF_H
|
||||
|
||||
#define DDR_PHY_BYTE_MAX 4
|
||||
#define DDR_PHY_BIT_NUM 8
|
||||
|
||||
/* Max bit 32 */
|
||||
#define DDR_PHY_BIT_MAX (DDR_PHY_BYTE_MAX * DDR_PHY_BIT_NUM)
|
||||
/* Max phy number */
|
||||
#define DDR_SUPPORT_PHY_MAX 2
|
||||
/* Max rank number */
|
||||
#define DDR_SUPPORT_RANK_MAX 2
|
||||
|
||||
#define DDR_DATAEYE_RESULT_MASK 0xffff
|
||||
#define DDR_DATAEYE_RESULT_BIT 16
|
||||
|
||||
#define DDR_TRAINING_CMD_DATAEYE (1 << 5)
|
||||
#define DDR_CMD_SHOW_GCAI_INFO (1 << 0)
|
||||
|
||||
/* phy number */
|
||||
#define DDR_PHY_NUM 1
|
||||
|
||||
#define DDRC_BASE 0x120D0000
|
||||
#define DDRC_AXI_BASE (DDRC_BASE + 0x1000)
|
||||
|
||||
#define DDRC_TOP_FEATURE (DDRC_BASE + 0)
|
||||
#define DDR3_TYPE 1
|
||||
#define DDR4_TYPE 2
|
||||
#define LPDDR4_TYPE 3
|
||||
|
||||
#define DDRC_DDR_INFO_REG_OFFSET 0x98
|
||||
#define DDR_BG_NUM_MASK (0x1 << 28)
|
||||
|
||||
#define LPDDR4_CHANNEL_NUM 0x2
|
||||
#define LPDDR4_BANK_NUM_OF_RANK 0x8
|
||||
#define DDR4_BANK_NUM_OF_BG 0x4
|
||||
|
||||
#define GCAI_FLAG_REG_OFFSET 0x134
|
||||
#define GCAI_FLAG_MASK (0x1 << 4)
|
||||
|
||||
#define BAD_BLK_MAP_REG_NUM 0x40
|
||||
#define CHAN0_BAD_BLK_MAP_REG_OFFSET 0x578
|
||||
#define CHAN1_BAD_BLK_MAP_REG_OFFSET 0x678
|
||||
#define CHAN0_BAD_BLK_MAP_REG0 (DDRC_AXI_BASE + CHAN0_BAD_BLK_MAP_REG_OFFSET)
|
||||
#define CHAN1_BAD_BLK_MAP_REG0 (DDRC_AXI_BASE + CHAN1_BAD_BLK_MAP_REG_OFFSET)
|
||||
|
||||
struct training_data {
|
||||
unsigned int ddr_bit_result[DDR_PHY_BIT_MAX];
|
||||
unsigned int ddr_bit_best[DDR_PHY_BIT_MAX];
|
||||
unsigned int ddr_win_sum;
|
||||
};
|
||||
|
||||
struct ddr_training_data {
|
||||
unsigned int byte_num;
|
||||
struct training_data read;
|
||||
struct training_data write;
|
||||
};
|
||||
|
||||
struct rank_data {
|
||||
unsigned int rank_idx;
|
||||
struct ddr_training_data ddrtr_data;
|
||||
};
|
||||
|
||||
struct phy_data {
|
||||
unsigned int rank_num;
|
||||
struct rank_data rank[DDR_SUPPORT_RANK_MAX];
|
||||
};
|
||||
|
||||
struct ddr_training_result {
|
||||
unsigned int phy_num;
|
||||
struct phy_data phy[DDR_SUPPORT_PHY_MAX];
|
||||
};
|
||||
|
||||
struct gcai_base_info {
|
||||
u8 unused0;
|
||||
u8 type;
|
||||
u8 cs_num;
|
||||
u8 cs0_cap;
|
||||
u8 cs1_cap;
|
||||
u8 unused1;
|
||||
u8 unused2;
|
||||
u8 unused3;
|
||||
u8 unused4;
|
||||
};
|
||||
|
||||
void ddr_cmd_result_display(const struct ddr_training_result *ddrtr_result, unsigned int cmd);
|
||||
int get_result(struct ddr_training_result *result);
|
||||
void show_win_reg(void);
|
||||
#endif /* _DDR_IF_H */
|
||||
|
||||
@@ -0,0 +1,137 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
|
||||
#include <common.h>
|
||||
#include <command.h>
|
||||
#include <linux/io.h>
|
||||
#include "ddr_if.h"
|
||||
|
||||
#define REG_DQ_W_BDL_BASE 0x12104110
|
||||
#define REG_DQ_R_BDL_BASE 0x12104130
|
||||
#define REG_DQ_RES_SW 0x12101020
|
||||
|
||||
int get_result(struct ddr_training_result *result)
|
||||
{
|
||||
int i, j;
|
||||
unsigned int *result_w, *result_r, *best_w, *best_r;
|
||||
|
||||
memset(result, 0, sizeof(struct ddr_training_result));
|
||||
|
||||
result->phy_num = DDR_PHY_NUM;
|
||||
|
||||
result->phy[0].rank_num = 1;
|
||||
|
||||
result->phy[0].rank[0].rank_idx = 0;
|
||||
|
||||
result->phy[0].rank[0].ddrtr_data.byte_num = 4;
|
||||
|
||||
result_w = result->phy[0].rank[0].ddrtr_data.write.ddr_bit_result;
|
||||
result_r = result->phy[0].rank[0].ddrtr_data.read.ddr_bit_result;
|
||||
|
||||
for (i = 0; i < 4; i++) {
|
||||
for (j = 0; j < 4; j++) {
|
||||
unsigned int v = readl(REG_DQ_W_BDL_BASE + 0x1000 * i + j * 4);
|
||||
|
||||
result_w[i * 8 + j * 2] |= v & 0xff;
|
||||
result_w[i * 8 + j * 2 + 1] |= (v >> 16) & 0xff;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < 4; i++) {
|
||||
for (j = 0; j < 4; j++) {
|
||||
unsigned int v = readl(REG_DQ_R_BDL_BASE + 0x1000 * i + j * 4);
|
||||
|
||||
result_r[i * 8 + j * 2] |= v & 0xff;
|
||||
result_r[i * 8 + j * 2 + 1] |= (v >> 16) & 0xff;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/* Switch to getting min bdl */
|
||||
writel(1, REG_DQ_RES_SW);
|
||||
|
||||
for (i = 0; i < 4; i++) {
|
||||
for (j = 0; j < 4; j++) {
|
||||
unsigned int v = readl(REG_DQ_W_BDL_BASE + 0x1000 * i + j * 4);
|
||||
|
||||
result_w[i * 8 + j * 2] |= (v & 0xff) << DDR_DATAEYE_RESULT_BIT;
|
||||
result_w[i * 8 + j * 2 + 1] |= (v >> 16 & 0xff) << DDR_DATAEYE_RESULT_BIT;
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < 4; i++) {
|
||||
for (j = 0; j < 4; j++) {
|
||||
unsigned int v = readl(REG_DQ_R_BDL_BASE + 0x1000 * i + j * 4);
|
||||
|
||||
result_r[i * 8 + j * 2] |= (v & 0xff) << DDR_DATAEYE_RESULT_BIT;
|
||||
result_r[i * 8 + j * 2 + 1] |= (v >> 16 & 0xff) << DDR_DATAEYE_RESULT_BIT;
|
||||
}
|
||||
}
|
||||
|
||||
/* Restore function mode */
|
||||
writel(0, REG_DQ_RES_SW);
|
||||
|
||||
best_w = result->phy[0].rank[0].ddrtr_data.write.ddr_bit_best;
|
||||
best_r = result->phy[0].rank[0].ddrtr_data.read.ddr_bit_best;
|
||||
for (i = 0; i < DDR_PHY_BIT_MAX; ++i) {
|
||||
unsigned int res;
|
||||
unsigned short max, min;
|
||||
|
||||
res = result_w[i];
|
||||
min = res >> DDR_DATAEYE_RESULT_BIT;
|
||||
max = (res & DDR_DATAEYE_RESULT_MASK) * 2 - min;
|
||||
result_w[i] = (min << DDR_DATAEYE_RESULT_BIT) | max;
|
||||
best_w[i] = (res & DDR_DATAEYE_RESULT_MASK) | ((max - min) << DDR_DATAEYE_RESULT_BIT);
|
||||
|
||||
res = result_r[i];
|
||||
min = res >> DDR_DATAEYE_RESULT_BIT;
|
||||
max = (res & DDR_DATAEYE_RESULT_MASK) * 2 - min;
|
||||
result_r[i] = (min << DDR_DATAEYE_RESULT_BIT) | max;
|
||||
best_r[i] = (res & DDR_DATAEYE_RESULT_MASK) | ((max - min) << DDR_DATAEYE_RESULT_BIT);
|
||||
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void show_win_reg(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
printf("DDR DQ Win REGs\n");
|
||||
printf("------------------------------------------------------\n");
|
||||
printf("Avg BDL of Writing:\n");
|
||||
writel(0x0, REG_DQ_RES_SW);
|
||||
for (i = 0; i < 4; ++i) {
|
||||
unsigned long addr = REG_DQ_W_BDL_BASE + 0x1000 * i;
|
||||
|
||||
print_buffer(addr, (void *)addr, 4, 4, 4);
|
||||
}
|
||||
|
||||
printf("Min BDL of Writing:\n");
|
||||
writel(0x1, REG_DQ_RES_SW);
|
||||
for (i = 0; i < 4; ++i) {
|
||||
unsigned long addr = REG_DQ_W_BDL_BASE + 0x1000 * i;
|
||||
|
||||
print_buffer(addr, (void *)addr, 4, 4, 4);
|
||||
}
|
||||
|
||||
printf("Avg BDL of Reading:\n");
|
||||
writel(0x0, REG_DQ_RES_SW);
|
||||
for (i = 0; i < 4; ++i) {
|
||||
unsigned long addr = REG_DQ_R_BDL_BASE + 0x1000 * i;
|
||||
|
||||
print_buffer(addr, (void *)addr, 4, 4, 4);
|
||||
}
|
||||
|
||||
printf("Min BDL of Reading:\n");
|
||||
writel(0x1, REG_DQ_RES_SW);
|
||||
for (i = 0; i < 4; ++i) {
|
||||
unsigned long addr = REG_DQ_R_BDL_BASE + 0x1000 * i;
|
||||
|
||||
print_buffer(addr, (void *)addr, 4, 4, 4);
|
||||
}
|
||||
|
||||
writel(0x0, REG_DQ_RES_SW);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
|
||||
#include <common.h>
|
||||
#include <command.h>
|
||||
#include <linux/io.h>
|
||||
#include "ddr_if.h"
|
||||
|
||||
int get_result(struct ddr_training_result *result)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
void show_win_reg(void)
|
||||
{
|
||||
}
|
||||
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
|
||||
menu "Device drivers"
|
||||
|
||||
source "lotus/drivers/i2c/Kconfig"
|
||||
|
||||
source "lotus/drivers/usb/Kconfig"
|
||||
|
||||
source "lotus/drivers/net/Kconfig"
|
||||
|
||||
source "lotus/drivers/mtd/Kconfig"
|
||||
|
||||
endmenu
|
||||
@@ -0,0 +1,7 @@
|
||||
|
||||
obj-$(CONFIG_LOTUS_USB) += usb/
|
||||
obj-$(CONFIG_NET) += net/
|
||||
obj-y += mtd/
|
||||
obj-$(CONFIG_MMC) += mmc/
|
||||
obj-$(CONFIG_LOTUS_I2C) += i2c/
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
#
|
||||
# I2C subsystem configuration
|
||||
#
|
||||
|
||||
menu "LOTUS I2C support"
|
||||
|
||||
config LOTUS_I2C
|
||||
bool "lotus i2c"
|
||||
help
|
||||
LOTUS I2C support.
|
||||
|
||||
config CMD_LOTUS_I2C
|
||||
bool "cmd lotus i2c"
|
||||
help
|
||||
CMD LOTUS I2C support.
|
||||
|
||||
endmenu
|
||||
@@ -0,0 +1,9 @@
|
||||
sinclude $(TOPDIR)/config.mk
|
||||
|
||||
ifdef CONFIG_LOTUS_I2C
|
||||
obj-y += i2c_drv.o
|
||||
endif
|
||||
|
||||
ifdef CONFIG_CMD_LOTUS_I2C
|
||||
obj-y += i2c_cmd.o
|
||||
endif
|
||||
@@ -0,0 +1,157 @@
|
||||
#include <command.h>
|
||||
#include <linux/lotus/i2c.h>
|
||||
#include <common.h>
|
||||
#include <asm/io.h>
|
||||
|
||||
|
||||
#define BUF_LEN 8
|
||||
|
||||
|
||||
extern int hal_i2c_init(unsigned char i2c_num);
|
||||
extern int hal_i2c_recv(const struct i2c_client *client, unsigned int *buf, unsigned int count);
|
||||
extern int hal_i2c_send(unsigned char i2c_num, unsigned short dev_addr, const char *buf,unsigned int count);
|
||||
|
||||
|
||||
static int do_i2c_recv(cmd_tbl_t *cmdtp, int flag, int argc, char *const argv[])
|
||||
{
|
||||
unsigned int va_byte = 0;
|
||||
int ret;
|
||||
int i2c_num;
|
||||
int reg_byte;
|
||||
int data_byte;
|
||||
unsigned short device_addr;
|
||||
unsigned int reg;
|
||||
struct i2c_client client = {0};
|
||||
|
||||
/* parse cmd args */
|
||||
if(argc != 6) {
|
||||
printf("invalid params\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
i2c_num = (unsigned int)simple_strtoul(argv[1], NULL, 10);
|
||||
device_addr = (unsigned short)simple_strtoul(argv[2], NULL, 16);
|
||||
reg = (unsigned int)simple_strtoul(argv[3], NULL, 16);
|
||||
reg_byte = (unsigned int )simple_strtoul(argv[4], NULL, 10);
|
||||
data_byte = (unsigned int )simple_strtoul(argv[5], NULL, 10);
|
||||
|
||||
printf("i2c paramaters:i2c_num[%d] device_addr[0x%x] reg[0x%x] reg_byte[%d] data_byte[%d]\n",i2c_num, device_addr, reg, reg_byte, data_byte);
|
||||
|
||||
client.i2c_num = i2c_num;
|
||||
client.dev_addr = device_addr;
|
||||
client.dev_addr = (client.dev_addr >> 1) & 0xff;
|
||||
client.reg_addr = reg;
|
||||
client.reg_width = reg_byte;
|
||||
|
||||
ret = hal_i2c_recv(&client, &va_byte, data_byte);
|
||||
if (ret != 0) {
|
||||
printf("[error] hal_i2c_recv err ret: %d\n", ret);
|
||||
return 0;
|
||||
}
|
||||
printf("recv data va_byte[0x%x]\n",va_byte);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int do_i2c_send(cmd_tbl_t *cmdtp, int flag, int argc, char *const argv[])
|
||||
{
|
||||
int i2c_num;
|
||||
int reg_byte;
|
||||
int data_byte;
|
||||
unsigned int idx = 0;
|
||||
int ret;
|
||||
char buf[BUF_LEN] = {0};
|
||||
unsigned short device_addr;
|
||||
unsigned int reg;
|
||||
unsigned int data;
|
||||
|
||||
/* parse cmd args */
|
||||
if(argc != 7) {
|
||||
printf("invalid params\n");
|
||||
return -1;
|
||||
}
|
||||
i2c_num = (unsigned int)simple_strtoul(argv[1], NULL, 10);
|
||||
device_addr = (unsigned short)simple_strtoul(argv[2], NULL, 16);
|
||||
reg = (unsigned int)simple_strtoul(argv[3], NULL, 16);
|
||||
reg_byte = (unsigned int )simple_strtoul(argv[4], NULL, 10);
|
||||
data = (unsigned int)simple_strtoul(argv[5], NULL, 16);
|
||||
data_byte = (unsigned int )simple_strtoul(argv[6], NULL, 10);
|
||||
|
||||
printf("i2c paramaters:i2c_num[%d] device_addr[0x%x] reg[0x%x] reg_byte[%d] data[0x%x] data_byte[%d]\n",i2c_num, device_addr, reg, reg_byte, data, data_byte);
|
||||
|
||||
writel(0x1d01, 0x112C0030);
|
||||
writel(0x1d01, 0x112C0034); /* i2c0 */
|
||||
|
||||
writel(0x1d01, 0x112C0038);
|
||||
writel(0x1d01, 0x112C003C); /* i2c2 */
|
||||
|
||||
hal_i2c_init(i2c_num);
|
||||
|
||||
if (reg_byte == 1) { /* 1 byte */
|
||||
buf[idx] = reg & 0xff;
|
||||
idx++;
|
||||
}else if(reg_byte == 2) { /* 2 byte */
|
||||
buf[idx] = (reg >> 8) & 0xff; /* shift 8 */
|
||||
idx++;
|
||||
buf[idx] = reg & 0xff;
|
||||
idx++;
|
||||
}
|
||||
|
||||
if(data_byte == 1) { /* 1 byte */
|
||||
buf[idx] = data & 0xff;
|
||||
idx++;
|
||||
}
|
||||
else if (data_byte == 2) { /* 2 byte */
|
||||
buf[idx] = (data >> 8) & 0xff; /* shift 8 */
|
||||
idx++;
|
||||
buf[idx] = data & 0xff;
|
||||
idx++;
|
||||
}
|
||||
|
||||
unsigned short i2c_addr = (device_addr >> 1);
|
||||
const unsigned int buf_len = reg_byte + data_byte;
|
||||
ret = hal_i2c_send(i2c_num, i2c_addr, buf, buf_len);
|
||||
if (ret <= 0) {
|
||||
printf("[error] hal_i2c_send err ret: %d\n", ret);
|
||||
return -1;
|
||||
}
|
||||
if (ret != (reg_byte + data_byte)) {
|
||||
printf("[error] hal_i2c_send error\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* i2c recv test:for example 7205v200 volt sendor
|
||||
* 1.i2c_recv 2 0x94 0x02 1 2
|
||||
*/
|
||||
U_BOOT_CMD(i2c_recv, 6, 1, do_i2c_recv,
|
||||
"i2c_recv - i2c recv from device.\n"
|
||||
"\t- i2c_recv [args i2c_num, device_addr,reg,reg_length, data_length]",
|
||||
"\nargs: [i2c_num, device_addr,reg,reg_length, data_length]\n"
|
||||
"\t-<i2c_num> : 0/1/2\n"
|
||||
"\t-<device_addr> : 0x94\n"
|
||||
"\t-<reg_addr> : 0xaa\n"
|
||||
"\t-<reg_length>: 1\n"
|
||||
"\t-<data_length>: 1\n");
|
||||
|
||||
|
||||
/*
|
||||
* i2c send test:for example 7205v200 volt sendor
|
||||
* 1.i2c_send 2 0x94 0x00 1 0x3fff 2
|
||||
* 2.i2c_send 2 0x94 0x05 1 0xa000 2
|
||||
*/
|
||||
U_BOOT_CMD(i2c_send, 7, 1, do_i2c_send,
|
||||
"i2c_send - i2c send to device.\n"
|
||||
"\t- i2c_send [args: i2c_num, device_addr,reg,reg_length,data, data_length]",
|
||||
"\nargs: [i2c_num, device_addr,reg,reg_length,data, data_length]\n"
|
||||
"\t-<i2c_num> : 0/1/2\n"
|
||||
"\t-<device_addr> : 0x94\n"
|
||||
"\t-<reg_addr> : 0xaa\n"
|
||||
"\t-<reg_length>: 1\n"
|
||||
"\t-<data> : 0x11\n"
|
||||
"\t-<data_length>: 1\n");
|
||||
@@ -0,0 +1,720 @@
|
||||
#include <linux/lotus/i2c.h>
|
||||
#include <common.h>
|
||||
#include <asm/io.h>
|
||||
|
||||
|
||||
/*
|
||||
* I2C Registers offsets
|
||||
*/
|
||||
#define I2C_GLB 0x0
|
||||
#define I2C_SCL_H 0x20
|
||||
#define I2C_SCL_L 0x24
|
||||
#define I2C_DATA1 0x28
|
||||
#define I2C_TXF 0x10
|
||||
#define I2C_RXF 0x14
|
||||
#define I2C_CMD_BASE 0x40
|
||||
#define I2C_LOOP1 0xc0
|
||||
#define I2C_DST1 0xc4
|
||||
#define I2C_TX_WATER 0x18
|
||||
#define I2C_RX_WATER 0x1c
|
||||
#define I2C_CTRL1 0x04
|
||||
#define I2C_CTRL2 0x08
|
||||
#define I2C_STAT 0x0c
|
||||
#define I2C_INTR_RAW 0x30
|
||||
#define I2C_INTR_EN 0x34
|
||||
#define I2C_INTR_STAT 0x38
|
||||
|
||||
/*
|
||||
* I2C Global Config Register -- I2C_GLB
|
||||
* */
|
||||
#define GLB_EN_MASK BIT(0)
|
||||
#define GLB_SDA_HOLD_MASK 0xffff0
|
||||
#define GLB_SDA_HOLD_SHIFT (4)
|
||||
|
||||
/*
|
||||
* I2C Timing CMD Register -- I2C_CMD_BASE + n * 4 (n = 0, 1, 2, ... 31)
|
||||
* */
|
||||
#define CMD_EXIT 0x0
|
||||
#define CMD_TX_S 0x1
|
||||
#define CMD_TX_D1_2 0x4
|
||||
#define CMD_TX_D1_1 0x5
|
||||
#define CMD_TX_FIFO 0x9
|
||||
#define CMD_RX_FIFO 0x12
|
||||
#define CMD_RX_ACK 0x13
|
||||
#define CMD_IGN_ACK 0x15
|
||||
#define CMD_TX_ACK 0x16
|
||||
#define CMD_TX_NACK 0x17
|
||||
#define CMD_JMP1 0x18
|
||||
#define CMD_UP_TXF 0x1d
|
||||
#define CMD_TX_RS 0x1e
|
||||
#define CMD_TX_P 0x1f
|
||||
|
||||
/*
|
||||
* I2C Control Register 1 -- I2C_CTRL1
|
||||
*/
|
||||
#define CTRL1_CMD_START_MASK BIT(0)
|
||||
|
||||
/*
|
||||
* I2C Status Register -- I2C_STAT
|
||||
*/
|
||||
#define STAT_RXF_NOE_MASK BIT(0) /* RX FIFO not empty flag */
|
||||
#define STAT_TXF_NOF_MASK BIT(3) /* TX FIFO not full flag */
|
||||
|
||||
/*
|
||||
* I2C Interrupt status and mask Register --
|
||||
* I2C_INTR_RAW, I2C_STAT, I2C_INTR_STAT
|
||||
*/
|
||||
#define INTR_ABORT_MASK (BIT(0) | BIT(3))
|
||||
#define INTR_RX_MASK BIT(5)
|
||||
#define INTR_TX_MASK BIT(6)
|
||||
#define INTR_CMD_DONE_MASK BIT(4)
|
||||
#define INTR_USE_MASK (INTR_ABORT_MASK | INTR_RX_MASK | INTR_TX_MASK | INTR_CMD_DONE_MASK)
|
||||
#define INTR_ALL_MASK 0xffffffff
|
||||
#define I2C_TXF_DEPTH 64
|
||||
#define I2C_RXF_DEPTH 64
|
||||
#define I2C_TXF_WATER 32
|
||||
#define I2C_RXF_WATER 32
|
||||
/* for i2c rescue */
|
||||
#define CHECK_SDA_IN_SHIFT (5)
|
||||
#define GPIO_MODE_SHIFT (0)
|
||||
#define FORCE_SCL_OEN_SHIFT (1)
|
||||
#define FORCE_SDA_OEN_SHIFT (2)
|
||||
|
||||
|
||||
struct platform_i2c {
|
||||
unsigned int msg_buf_ptr;
|
||||
int status;
|
||||
#define I2C_WAIT_RESPOND (1 << 0)
|
||||
};
|
||||
|
||||
struct i2c_platform_data {
|
||||
unsigned int freq;
|
||||
unsigned int clk;
|
||||
};
|
||||
|
||||
struct i2c_msg {
|
||||
unsigned short addr; /* slave address */
|
||||
unsigned short flags;
|
||||
#define I2C_M_TEN 0x0010
|
||||
#define I2C_M_RD 0x0001
|
||||
#define I2C_M_STOP 0x8000
|
||||
#define I2C_M_NOSTART 0x4000
|
||||
#define I2C_M_REV_DIR_ADDR 0x2000
|
||||
#define I2C_M_IGNORE_NAK 0x1000
|
||||
#define I2C_M_NO_RD_ACK 0x0800
|
||||
#define I2C_M_RECV_LEN 0x0400
|
||||
#define I2C_M_16BIT_DATA 0x0008
|
||||
#define I2C_M_16BIT_REG 0x0002
|
||||
unsigned short len; /* msg length */
|
||||
unsigned char *buf; /* pointer to msg data */
|
||||
};
|
||||
|
||||
struct i2c_driver_data {
|
||||
unsigned int reg_base;
|
||||
unsigned int freq;
|
||||
unsigned int irq;
|
||||
unsigned int clk;
|
||||
struct i2c_msg *msgs;
|
||||
unsigned int msg_num;
|
||||
unsigned int msg_idx;
|
||||
unsigned int lock;
|
||||
void *private;
|
||||
};
|
||||
|
||||
#ifdef CONFIG_LOTUS_FPGA
|
||||
#define CLK_LIMIT_DEFAULT 40000
|
||||
#else
|
||||
#define CLK_LIMIT_DEFAULT 400000
|
||||
#endif
|
||||
#define write_reg_bit(value, offset, addr) ({ \
|
||||
unsigned long t, mask; \
|
||||
mask = 1 << (offset); \
|
||||
t = readl(addr); \
|
||||
t &= ~mask; \
|
||||
t |= (value << (offset)) & mask; \
|
||||
writel(t, addr); \
|
||||
})
|
||||
|
||||
#define I2C_WAIT_TIMEOUT (100 * 3)
|
||||
#define I2C_TIMEOUT_COUNT 0x10000
|
||||
#define I2C_BUF_SIZE 8
|
||||
#define I2C_INTERRUPT_NUM 0
|
||||
|
||||
#if defined(CONFIG_TARGET_XMFALCON)
|
||||
|
||||
#define PERI_CRG110 0x01b8
|
||||
#define I2C_CRG_REG_BASE (CRG_REG_BASE + PERI_CRG110)
|
||||
|
||||
#define I2C0_REG_BASE 0x12060000
|
||||
#define I2C1_REG_BASE 0x12061000
|
||||
#define I2C2_REG_BASE 0x12062000
|
||||
#define I2C3_REG_BASE 0x12063000
|
||||
#define I2C4_REG_BASE 0x12064000
|
||||
#define I2C5_REG_BASE 0x12065000
|
||||
#define I2C6_REG_BASE 0x12066000
|
||||
#define I2C7_REG_BASE 0x12067000
|
||||
#define I2C_NUM 8
|
||||
|
||||
#ifdef CONFIG_LOTUS_FPGA
|
||||
#define get_bus_clk() 25000000
|
||||
#else
|
||||
#define get_bus_clk() 50000000
|
||||
#endif
|
||||
#define get_host_clock(i2c_num) ({ get_bus_clk(); })
|
||||
|
||||
static struct platform_i2c g_i2c_platform_data[I2C_NUM] = {0};
|
||||
static int g_i2c_host_cfg[I2C_NUM] = {0, 1, 2, 3, 4, 5, 6, 7}; /* i2c index */
|
||||
static struct i2c_driver_data g_i2c_data[I2C_NUM] = {
|
||||
{I2C0_REG_BASE, CLK_LIMIT_DEFAULT, I2C_INTERRUPT_NUM},
|
||||
{I2C1_REG_BASE, CLK_LIMIT_DEFAULT, I2C_INTERRUPT_NUM},
|
||||
{I2C2_REG_BASE, CLK_LIMIT_DEFAULT, I2C_INTERRUPT_NUM},
|
||||
{I2C3_REG_BASE, CLK_LIMIT_DEFAULT, I2C_INTERRUPT_NUM},
|
||||
{I2C4_REG_BASE, CLK_LIMIT_DEFAULT, I2C_INTERRUPT_NUM},
|
||||
{I2C5_REG_BASE, CLK_LIMIT_DEFAULT, I2C_INTERRUPT_NUM},
|
||||
{I2C6_REG_BASE, CLK_LIMIT_DEFAULT, I2C_INTERRUPT_NUM},
|
||||
{I2C7_REG_BASE, CLK_LIMIT_DEFAULT, I2C_INTERRUPT_NUM},
|
||||
};
|
||||
|
||||
#elif defined(CONFIG_TARGET_XMORCA)
|
||||
|
||||
#define PERI_CRG110 0x01b8
|
||||
#define I2C_CRG_REG_BASE (CRG_REG_BASE + PERI_CRG110)
|
||||
|
||||
#define I2C0_REG_BASE 0x12060000
|
||||
#define I2C1_REG_BASE 0x12061000
|
||||
#define I2C2_REG_BASE 0x12062000
|
||||
#define I2C3_REG_BASE 0x12063000
|
||||
#define I2C_NUM 4
|
||||
|
||||
#define get_bus_clk() 50000000
|
||||
#define get_host_clock(i2c_num) ({ get_bus_clk(); })
|
||||
|
||||
static struct platform_i2c g_i2c_platform_data[I2C_NUM] = {0};
|
||||
static int g_i2c_host_cfg[I2C_NUM] = {0, 1, 2, 3}; /* 0,1,2,3: i2c index */
|
||||
static struct i2c_driver_data g_i2c_data[I2C_NUM] = {
|
||||
{I2C0_REG_BASE, CLK_LIMIT_DEFAULT, I2C_INTERRUPT_NUM},
|
||||
{I2C1_REG_BASE, CLK_LIMIT_DEFAULT, I2C_INTERRUPT_NUM},
|
||||
{I2C2_REG_BASE, CLK_LIMIT_DEFAULT, I2C_INTERRUPT_NUM},
|
||||
{I2C3_REG_BASE, CLK_LIMIT_DEFAULT, I2C_INTERRUPT_NUM},
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
static void i2c_disable(const struct i2c_driver_data *i2c);
|
||||
static void i2c_cfg_irq(const struct i2c_driver_data *i2c, unsigned int flag);
|
||||
static unsigned int i2c_clr_irq(const struct i2c_driver_data *i2c);
|
||||
|
||||
static void i2c_rescue(const struct i2c_driver_data *i2c)
|
||||
{
|
||||
i2c_disable(i2c);
|
||||
i2c_cfg_irq(i2c, 0);
|
||||
i2c_clr_irq(i2c);
|
||||
|
||||
unsigned int val = (0x1 << GPIO_MODE_SHIFT) | (0x1 << FORCE_SCL_OEN_SHIFT) |
|
||||
(0x1 << FORCE_SDA_OEN_SHIFT);
|
||||
writel(val, i2c->reg_base + I2C_CTRL2);
|
||||
|
||||
unsigned int time_cnt = 0;
|
||||
do {
|
||||
for (int index = 0; index < 9; index++) { /* shift:9 */
|
||||
val = (0x1 << GPIO_MODE_SHIFT) | 0x1;
|
||||
writel(val, i2c->reg_base + I2C_CTRL2);
|
||||
udelay(5); /* delay: 5 us */
|
||||
val = (0x1 << GPIO_MODE_SHIFT) |
|
||||
(0x1 << FORCE_SCL_OEN_SHIFT) |
|
||||
(0x1 << FORCE_SDA_OEN_SHIFT);
|
||||
writel(val, i2c->reg_base + I2C_CTRL2);
|
||||
udelay(5); /* delay: 5 us */
|
||||
}
|
||||
|
||||
time_cnt++;
|
||||
if (time_cnt > I2C_WAIT_TIMEOUT) {
|
||||
goto disable_rescue;
|
||||
}
|
||||
|
||||
val = readl(i2c->reg_base + I2C_CTRL2);
|
||||
} while (!(val & (0x1 << CHECK_SDA_IN_SHIFT)));
|
||||
|
||||
val = (0x1 << GPIO_MODE_SHIFT) | (0x1 << FORCE_SCL_OEN_SHIFT) |
|
||||
(0x1 << FORCE_SDA_OEN_SHIFT);
|
||||
writel(val, i2c->reg_base + I2C_CTRL2);
|
||||
val = (0x1 << GPIO_MODE_SHIFT) | (0x1 << FORCE_SCL_OEN_SHIFT);
|
||||
writel(val, i2c->reg_base + I2C_CTRL2);
|
||||
udelay(10); /* delay: 10 us */
|
||||
val = (0x1 << GPIO_MODE_SHIFT) | (0x1 << FORCE_SCL_OEN_SHIFT) |
|
||||
(0x1 << FORCE_SDA_OEN_SHIFT);
|
||||
writel(val, i2c->reg_base + I2C_CTRL2);
|
||||
|
||||
disable_rescue:
|
||||
val = (0x1 << FORCE_SCL_OEN_SHIFT) | 0x1;
|
||||
writel(val, i2c->reg_base + I2C_CTRL2);
|
||||
}
|
||||
|
||||
static void i2c_disable(const struct i2c_driver_data *i2c)
|
||||
{
|
||||
unsigned int val = readl(i2c->reg_base + I2C_GLB);
|
||||
val &= ~GLB_EN_MASK;
|
||||
writel(val, i2c->reg_base + I2C_GLB);
|
||||
}
|
||||
|
||||
static void i2c_disable_irq(const struct i2c_driver_data *i2c, unsigned int flag)
|
||||
{
|
||||
unsigned int val = readl(i2c->reg_base + I2C_INTR_EN);
|
||||
val &= ~flag;
|
||||
writel(val, i2c->reg_base + I2C_INTR_EN);
|
||||
}
|
||||
|
||||
static unsigned int i2c_clr_irq(const struct i2c_driver_data *i2c)
|
||||
{
|
||||
unsigned int val = readl(i2c->reg_base + I2C_INTR_STAT);
|
||||
writel(INTR_ALL_MASK, i2c->reg_base + I2C_INTR_RAW);
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
static void i2c_set_freq(struct i2c_driver_data *i2c)
|
||||
{
|
||||
unsigned int val;
|
||||
unsigned int freq = i2c->freq;
|
||||
unsigned int clk_rate = i2c->clk;
|
||||
unsigned int max_freq = clk_rate >> 1;
|
||||
|
||||
if (freq > max_freq) {
|
||||
i2c->freq = max_freq;
|
||||
freq = i2c->freq;
|
||||
}
|
||||
|
||||
if (freq <= 100000) { /* 100000:100KHz */
|
||||
val = clk_rate / (freq * 2); /* 1/2:0.5 */
|
||||
writel(val, i2c->reg_base + I2C_SCL_H);
|
||||
writel(val, i2c->reg_base + I2C_SCL_L);
|
||||
} else {
|
||||
val = (clk_rate * 36) / (freq * 100); /* 36/100:0.36 */
|
||||
writel(val, i2c->reg_base + I2C_SCL_H);
|
||||
val = (clk_rate * 64) / (freq * 100); /* 64/100:0.64 */
|
||||
writel(val, i2c->reg_base + I2C_SCL_L);
|
||||
}
|
||||
val = readl(i2c->reg_base + I2C_GLB);
|
||||
val &= ~GLB_SDA_HOLD_MASK;
|
||||
val |= ((0xa << GLB_SDA_HOLD_SHIFT) & GLB_SDA_HOLD_MASK);
|
||||
writel(val, i2c->reg_base + I2C_GLB);
|
||||
}
|
||||
|
||||
/*
|
||||
* set i2c controller TX and RX FIFO water
|
||||
*/
|
||||
static void i2c_set_water(const struct i2c_driver_data *i2c)
|
||||
{
|
||||
writel(I2C_TXF_WATER, i2c->reg_base + I2C_TX_WATER);
|
||||
writel(I2C_RXF_WATER, i2c->reg_base + I2C_RX_WATER);
|
||||
}
|
||||
|
||||
static void i2c_enable_clk(unsigned char i2c_num)
|
||||
{
|
||||
const unsigned int clk_start_bit = 16 + i2c_num; /* 16: i2c clk start bit */
|
||||
const unsigned int rst_start_bit = 24 + i2c_num; /* 24: i2c rst start bit */
|
||||
const unsigned int enable_clck = 1;
|
||||
const unsigned int enable_rst = 0;
|
||||
write_reg_bit(enable_clck, clk_start_bit, (uintptr_t)I2C_CRG_REG_BASE);
|
||||
write_reg_bit(enable_rst, rst_start_bit, (uintptr_t)I2C_CRG_REG_BASE);
|
||||
}
|
||||
|
||||
/*
|
||||
* initialise the controller, set i2c bus interface freq
|
||||
*/
|
||||
static void i2c_init_cfg(const struct i2c_driver_data *i2c, unsigned char i2c_num)
|
||||
{
|
||||
i2c_enable_clk(i2c_num);
|
||||
i2c_disable(i2c);
|
||||
i2c_disable_irq(i2c, INTR_ALL_MASK);
|
||||
i2c_set_freq((struct i2c_driver_data *)i2c);
|
||||
i2c_set_water(i2c);
|
||||
}
|
||||
|
||||
static void i2c_cmdreg_set(const struct i2c_driver_data *i2c, unsigned int cmd,
|
||||
unsigned int *offset)
|
||||
{
|
||||
writel(cmd, i2c->reg_base + I2C_CMD_BASE + (*offset) * 4); /* 4: bytes */
|
||||
(*offset)++;
|
||||
}
|
||||
|
||||
static void i2c_cfg_cmd(const struct i2c_driver_data *i2c)
|
||||
{
|
||||
struct i2c_msg *msg = i2c->msgs;
|
||||
unsigned int offset = 0;
|
||||
|
||||
if (i2c->msg_idx == 0)
|
||||
i2c_cmdreg_set(i2c, CMD_TX_S, &offset);
|
||||
else
|
||||
i2c_cmdreg_set(i2c, CMD_TX_RS, &offset);
|
||||
|
||||
if (msg->flags & I2C_M_TEN) {
|
||||
if (i2c->msg_idx == 0) {
|
||||
i2c_cmdreg_set(i2c, CMD_TX_D1_2, &offset);
|
||||
i2c_cmdreg_set(i2c, CMD_RX_ACK, &offset);
|
||||
i2c_cmdreg_set(i2c, CMD_TX_D1_1, &offset);
|
||||
} else {
|
||||
i2c_cmdreg_set(i2c, CMD_TX_D1_2, &offset);
|
||||
}
|
||||
} else {
|
||||
i2c_cmdreg_set(i2c, CMD_TX_D1_1, &offset);
|
||||
}
|
||||
|
||||
if (msg->flags & I2C_M_IGNORE_NAK)
|
||||
i2c_cmdreg_set(i2c, CMD_IGN_ACK, &offset);
|
||||
else
|
||||
i2c_cmdreg_set(i2c, CMD_RX_ACK, &offset);
|
||||
|
||||
if (msg->flags & I2C_M_RD) {
|
||||
if (msg->len >= 2) { /* msg len:2 */
|
||||
writel(offset, i2c->reg_base + I2C_DST1);
|
||||
writel(msg->len - 2, i2c->reg_base + I2C_LOOP1); /* 2: max len */
|
||||
i2c_cmdreg_set(i2c, CMD_RX_FIFO, &offset);
|
||||
i2c_cmdreg_set(i2c, CMD_TX_ACK, &offset);
|
||||
i2c_cmdreg_set(i2c, CMD_JMP1, &offset);
|
||||
}
|
||||
i2c_cmdreg_set(i2c, CMD_RX_FIFO, &offset);
|
||||
i2c_cmdreg_set(i2c, CMD_TX_NACK, &offset);
|
||||
} else {
|
||||
writel(offset, i2c->reg_base + I2C_DST1);
|
||||
writel(msg->len - 1, i2c->reg_base + I2C_LOOP1);
|
||||
i2c_cmdreg_set(i2c, CMD_UP_TXF, &offset);
|
||||
i2c_cmdreg_set(i2c, CMD_TX_FIFO, &offset);
|
||||
|
||||
if (msg->flags & I2C_M_IGNORE_NAK) {
|
||||
i2c_cmdreg_set(i2c, CMD_IGN_ACK, &offset);
|
||||
}
|
||||
else {
|
||||
i2c_cmdreg_set(i2c, CMD_RX_ACK, &offset);
|
||||
}
|
||||
i2c_cmdreg_set(i2c, CMD_JMP1, &offset);
|
||||
}
|
||||
|
||||
if ((i2c->msg_idx == (i2c->msg_num - 1)) || (msg->flags & I2C_M_STOP)) {
|
||||
i2c_cmdreg_set(i2c, CMD_TX_P, &offset);
|
||||
}
|
||||
|
||||
i2c_cmdreg_set(i2c, CMD_EXIT, &offset);
|
||||
}
|
||||
|
||||
static void i2c_enable(const struct i2c_driver_data *i2c)
|
||||
{
|
||||
unsigned int val = readl(i2c->reg_base + I2C_GLB);
|
||||
val |= GLB_EN_MASK;
|
||||
writel(val, i2c->reg_base + I2C_GLB);
|
||||
}
|
||||
|
||||
/*
|
||||
* config i2c slave addr
|
||||
*/
|
||||
static void i2c_set_addr(const struct i2c_driver_data *i2c)
|
||||
{
|
||||
struct i2c_msg *msg = i2c->msgs;
|
||||
unsigned int addr;
|
||||
|
||||
if (msg->flags & I2C_M_TEN) {
|
||||
/* first byte is 11110XX0 where XX is upper 2 bits */
|
||||
addr = ((msg->addr & 0x300) << 1) | 0xf000;
|
||||
if (msg->flags & I2C_M_RD) {
|
||||
addr |= 1 << 8; /* shift:8 */
|
||||
}
|
||||
|
||||
/* second byte is the remaining 8 bits */
|
||||
addr |= msg->addr & 0xff;
|
||||
} else {
|
||||
addr = (msg->addr & 0x7f) << 1;
|
||||
if (msg->flags & I2C_M_RD) {
|
||||
addr |= 1;
|
||||
}
|
||||
}
|
||||
|
||||
writel(addr, i2c->reg_base + I2C_DATA1);
|
||||
}
|
||||
|
||||
/*
|
||||
* start command sequence
|
||||
*/
|
||||
static void i2c_start_cmd(const struct i2c_driver_data *i2c)
|
||||
{
|
||||
unsigned int val = readl(i2c->reg_base + I2C_CTRL1);
|
||||
val |= CTRL1_CMD_START_MASK;
|
||||
writel(val, i2c->reg_base + I2C_CTRL1);
|
||||
}
|
||||
|
||||
static int i2c_wait_rx_noempty(const struct i2c_driver_data *i2c)
|
||||
{
|
||||
unsigned int time_cnt = 0;
|
||||
unsigned int val;
|
||||
|
||||
do {
|
||||
val = readl(i2c->reg_base + I2C_STAT);
|
||||
if (val & STAT_RXF_NOE_MASK) {
|
||||
return 0;
|
||||
}
|
||||
udelay(50); /* delay:50 us */
|
||||
time_cnt++;
|
||||
} while (time_cnt < I2C_TIMEOUT_COUNT);
|
||||
|
||||
i2c_rescue(i2c);
|
||||
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
static int i2c_wait_tx_nofull(const struct i2c_driver_data *i2c)
|
||||
{
|
||||
unsigned int time_cnt = 0;
|
||||
unsigned int val;
|
||||
|
||||
do {
|
||||
val = readl(i2c->reg_base + I2C_STAT);
|
||||
if (val & STAT_TXF_NOF_MASK) {
|
||||
return 0;
|
||||
}
|
||||
udelay(50); /* delay:50 us */
|
||||
time_cnt++;
|
||||
} while (time_cnt < I2C_TIMEOUT_COUNT);
|
||||
|
||||
i2c_rescue(i2c);
|
||||
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
|
||||
static int i2c_wait_idle(const struct i2c_driver_data *i2c)
|
||||
{
|
||||
unsigned int time_cnt = 0;
|
||||
unsigned int val;
|
||||
|
||||
do {
|
||||
val = readl(i2c->reg_base + I2C_INTR_RAW);
|
||||
if (val & (INTR_ABORT_MASK)) {
|
||||
printf("i2c wait idle EIO,val==0x%x\n",val);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
if (val & INTR_CMD_DONE_MASK) {
|
||||
return 0;
|
||||
}
|
||||
udelay(50); /* delay:50 us */
|
||||
time_cnt++;
|
||||
} while (time_cnt < I2C_WAIT_TIMEOUT);
|
||||
|
||||
i2c_rescue(i2c);
|
||||
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
static int i2c_polling_xfer_one_msg(const struct i2c_driver_data *i2c)
|
||||
{
|
||||
int status;
|
||||
unsigned int val;
|
||||
struct i2c_msg *msg = i2c->msgs;
|
||||
unsigned int msg_buf_ptr = 0;
|
||||
|
||||
i2c_enable(i2c);
|
||||
i2c_clr_irq(i2c);
|
||||
i2c_set_addr(i2c);
|
||||
i2c_cfg_cmd(i2c);
|
||||
i2c_start_cmd(i2c);
|
||||
|
||||
if (msg->flags & I2C_M_RD) {
|
||||
while (msg_buf_ptr < msg->len) {
|
||||
status = i2c_wait_rx_noempty(i2c);
|
||||
if (status) {
|
||||
goto end;
|
||||
}
|
||||
val = readl(i2c->reg_base + I2C_RXF);
|
||||
msg->buf[msg_buf_ptr] = val;
|
||||
msg_buf_ptr++;
|
||||
}
|
||||
} else {
|
||||
while (msg_buf_ptr < msg->len) {
|
||||
status = i2c_wait_tx_nofull(i2c);
|
||||
if (status) {
|
||||
goto end;
|
||||
}
|
||||
val = msg->buf[msg_buf_ptr];
|
||||
writel(val, i2c->reg_base + I2C_TXF);
|
||||
msg_buf_ptr++;
|
||||
}
|
||||
}
|
||||
|
||||
status = i2c_wait_idle(i2c);
|
||||
end:
|
||||
i2c_disable(i2c);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
static void i2c_cfg_irq(const struct i2c_driver_data *i2c, unsigned int flag)
|
||||
{
|
||||
writel(flag, i2c->reg_base + I2C_INTR_EN);
|
||||
}
|
||||
|
||||
static int i2c_xfer(unsigned char i2c_num, const struct i2c_msg *msgs, int num)
|
||||
{
|
||||
int status = 0;
|
||||
|
||||
if (msgs == NULL) {
|
||||
printf("[error] msg pointer is null.\n");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
struct i2c_driver_data *i2c = &g_i2c_data[i2c_num];
|
||||
struct platform_i2c *hpi = &g_i2c_platform_data[i2c_num];
|
||||
|
||||
i2c->clk = get_host_clock(0);
|
||||
i2c->private = (void *)(hpi);
|
||||
|
||||
i2c->msgs = (struct i2c_msg *)msgs;
|
||||
i2c->msg_num = (unsigned int)num;
|
||||
i2c->msg_idx = 0;
|
||||
|
||||
while (i2c->msg_idx < i2c->msg_num) {
|
||||
status = i2c_polling_xfer_one_msg(i2c);
|
||||
if (status) {
|
||||
break;
|
||||
}
|
||||
|
||||
i2c->msgs++;
|
||||
i2c->msg_idx++;
|
||||
}
|
||||
|
||||
if (!status || i2c->msg_idx > 0) {
|
||||
status = i2c->msg_idx;
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
static int i2c_check_enable(unsigned char i2c_num)
|
||||
{
|
||||
if (i2c_num >= I2C_NUM) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return g_i2c_host_cfg[i2c_num];
|
||||
}
|
||||
|
||||
static int hal_i2c_recv_inner(const struct i2c_client *client, unsigned int *buf,
|
||||
unsigned int count)
|
||||
{
|
||||
struct i2c_msg msg[I2C_BUF_SIZE] = {0};
|
||||
unsigned char recv_buf[I2C_BUF_SIZE] = {0};
|
||||
|
||||
if (client->reg_width == 2) { /* reg_width:2 */
|
||||
recv_buf[0] = (client->reg_addr >> 8) & 0xff; /* shift:8 */
|
||||
recv_buf[1] = client->reg_addr & 0xff;
|
||||
} else {
|
||||
recv_buf[0] = client->reg_addr & 0xff;
|
||||
}
|
||||
|
||||
msg[0].addr = client->dev_addr;
|
||||
msg[0].flags = 0;
|
||||
msg[0].len = client->reg_width;
|
||||
msg[0].buf = recv_buf;
|
||||
|
||||
msg[1].addr = client->dev_addr;
|
||||
msg[1].flags = 0;
|
||||
msg[1].flags |= I2C_M_RD;
|
||||
msg[1].len = count;
|
||||
msg[1].buf = recv_buf;
|
||||
|
||||
int ret = i2c_xfer(client->i2c_num, msg, 2); /* msg num:2 */
|
||||
if (ret < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (count == 2) { /* data_width:2 */
|
||||
*buf = recv_buf[0] | (recv_buf[1] << 8); /* shift:8 */
|
||||
} else {
|
||||
*buf = recv_buf[0];
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int hal_i2c_send_inner(unsigned char i2c_num, unsigned short dev_addr, const char *buf,
|
||||
unsigned int count)
|
||||
{
|
||||
struct i2c_msg msg = {0};
|
||||
msg.addr = dev_addr;
|
||||
msg.flags = 0;
|
||||
msg.len = count;
|
||||
msg.buf = (unsigned char *)buf;
|
||||
int ret = i2c_xfer(i2c_num, &msg, 1);
|
||||
return (ret == 1) ? count : ret;
|
||||
}
|
||||
|
||||
static int hal_i2c_init_inner(unsigned char i2c_num)
|
||||
{
|
||||
struct i2c_driver_data *i2c = NULL;
|
||||
struct platform_i2c *hpi = NULL;
|
||||
|
||||
int ret = i2c_check_enable(i2c_num);
|
||||
if (ret < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
i2c = &g_i2c_data[i2c_num];
|
||||
hpi = &g_i2c_platform_data[i2c_num];
|
||||
i2c->clk = get_host_clock(i2c_num);
|
||||
i2c->private = (void *)(hpi);
|
||||
i2c->irq = 0;
|
||||
i2c_init_cfg(i2c, i2c_num);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int hal_i2c_recv(const struct i2c_client *client, unsigned int *buf,
|
||||
unsigned int count)
|
||||
{
|
||||
if ((client == NULL) || (buf == NULL)) {
|
||||
printf("[error] i2c recv param is null.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (client->i2c_num >= I2C_NUM) {
|
||||
printf("[error] i2c num(%u) is invalid.\n", client->i2c_num);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (count > I2C_BUF_SIZE) {
|
||||
printf("[error] buf count(%u) should lees than(%d).\n", count, I2C_BUF_SIZE);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return hal_i2c_recv_inner(client, buf, count);
|
||||
}
|
||||
|
||||
int hal_i2c_send(unsigned char i2c_num, unsigned short dev_addr, const char *buf,
|
||||
unsigned int count)
|
||||
{
|
||||
if (i2c_num >= I2C_NUM) {
|
||||
printf("[error] i2c num(%u) is invalid.\n", i2c_num);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (buf == NULL) {
|
||||
printf("[error] i2c send param is null.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (count > I2C_BUF_SIZE) {
|
||||
printf("[error] buf count(%u) should lees than(%d).\n", count, I2C_BUF_SIZE);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return hal_i2c_send_inner(i2c_num, dev_addr, buf, count);
|
||||
}
|
||||
|
||||
int hal_i2c_init(unsigned char i2c_num)
|
||||
{
|
||||
if (i2c_num >= I2C_NUM) {
|
||||
printf("[error] i2c num(%u) is invalid.\n", i2c_num);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return hal_i2c_init_inner(i2c_num);
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
|
||||
ccflags-y += -I$(srctree)/drivers/mmc
|
||||
|
||||
obj-$(CONFIG_TARGET_XMFALCON) += xmfalcon.o
|
||||
obj-$(CONFIG_TARGET_XMORCA) += xmorca.o
|
||||
@@ -0,0 +1,462 @@
|
||||
/*
|
||||
* Copyright (c) LOTUS. All rights reserved.
|
||||
*/
|
||||
|
||||
static void enable_card_clk(struct sdhci_host *host)
|
||||
{
|
||||
u16 clk;
|
||||
|
||||
clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL);
|
||||
clk |= SDHCI_CLOCK_CARD_EN;
|
||||
sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL);
|
||||
}
|
||||
|
||||
#ifdef MMC_SUPPORTS_TUNING
|
||||
static void disable_card_clk(struct sdhci_host *host)
|
||||
{
|
||||
u16 clk;
|
||||
|
||||
clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL);
|
||||
clk &= ~SDHCI_CLOCK_CARD_EN;
|
||||
sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL);
|
||||
}
|
||||
#endif
|
||||
|
||||
static void enable_internal_clk(struct sdhci_host *host)
|
||||
{
|
||||
u16 clk;
|
||||
u16 timeout = 20;
|
||||
|
||||
clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL);
|
||||
clk |= SDHCI_CLOCK_INT_EN | SDHCI_CLOCK_PLL_EN;
|
||||
sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL);
|
||||
|
||||
/* Wait max 20 ms */
|
||||
clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL);
|
||||
while (!(clk & SDHCI_CLOCK_INT_STABLE)) {
|
||||
if (timeout == 0) {
|
||||
printf("%s: Internal clock never stabilised.\n",
|
||||
__func__);
|
||||
return;
|
||||
}
|
||||
timeout--;
|
||||
udelay(1000); /* delay 1000us */
|
||||
clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL);
|
||||
}
|
||||
}
|
||||
|
||||
static void __maybe_unused disable_internal_clk(struct sdhci_host *host)
|
||||
{
|
||||
u16 clk;
|
||||
|
||||
clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL);
|
||||
clk &= ~SDHCI_CLOCK_INT_EN;
|
||||
sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL);
|
||||
}
|
||||
|
||||
static void set_drv_phase(struct sdhci_host *host, unsigned int phase)
|
||||
{
|
||||
uintptr_t crg_addr;
|
||||
unsigned int reg;
|
||||
|
||||
crg_addr = (host->type == MMC_TYPE_MMC) ?
|
||||
REG_EMMC_DRV_DLL_CTRL : REG_SDIO0_DRV_DLL_CTRL;
|
||||
reg = readl(crg_addr);
|
||||
reg &= ~SDIO_DRV_PHASE_SEL_MASK;
|
||||
reg |= sdio_drv_sel(phase);
|
||||
writel(reg, crg_addr);
|
||||
}
|
||||
|
||||
static void enable_sample(struct sdhci_host *host)
|
||||
{
|
||||
unsigned int reg;
|
||||
|
||||
reg = sdhci_readl(host, SDHCI_AT_CTRL);
|
||||
reg |= SDHCI_SAMPLE_EN;
|
||||
sdhci_writel(host, reg, SDHCI_AT_CTRL);
|
||||
}
|
||||
|
||||
static void set_sampl_phase(struct sdhci_host *host, unsigned int phase)
|
||||
{
|
||||
unsigned int reg;
|
||||
|
||||
reg = sdhci_readl(host, SDHCI_AT_STAT);
|
||||
reg &= ~SDHCI_PHASE_SEL_MASK;
|
||||
reg |= phase;
|
||||
sdhci_writel(host, reg, SDHCI_AT_STAT);
|
||||
}
|
||||
|
||||
static void wait_sampl_dll_slave_ready(struct sdhci_host *host)
|
||||
{
|
||||
unsigned int reg;
|
||||
unsigned int timeout = 20;
|
||||
uintptr_t reg_addr;
|
||||
|
||||
reg_addr = (host->type == MMC_TYPE_MMC) ?
|
||||
REG_EMMC_SAMPL_DLL_STATUS : REG_SDIO0_SAMPL_DLL_STATUS;
|
||||
do {
|
||||
reg = readl(reg_addr);
|
||||
if (reg & SDIO_SAMPL_DLL_SLAVE_READY)
|
||||
return;
|
||||
|
||||
udelay(1000); /* delay 1000us */
|
||||
timeout--;
|
||||
} while (timeout > 0);
|
||||
|
||||
printf("sdhci: SAMPL DLL slave not ready.\n");
|
||||
}
|
||||
|
||||
#ifdef MMC_SUPPORTS_TUNING
|
||||
static void enable_edge_tuning(struct sdhci_host *host)
|
||||
{
|
||||
uintptr_t crg_addr;
|
||||
unsigned int reg;
|
||||
|
||||
crg_addr = (host->type == MMC_TYPE_MMC) ?
|
||||
REG_EMMC_SAMPLB_DLL_CTRL : REG_SDIO0_SAMPLB_DLL_CTRL;
|
||||
|
||||
reg = readl(crg_addr);
|
||||
reg &= ~((host->type == MMC_TYPE_MMC) ?
|
||||
EMMC_SAMPLB_DLL_CLK_MASK : SDIO_SAMPLB_DLL_CLK_MASK);
|
||||
|
||||
#ifdef CONFIG_LOTUS_FPGA
|
||||
/* fpga 1 -> 45 degree */
|
||||
reg |= ((host->type == MMC_TYPE_MMC) ?
|
||||
emmc_samplb_sel(2) : sdio_samplb_sel(2));
|
||||
#else
|
||||
/* soc 1-> 11.25 degree */
|
||||
reg |= ((host->type == MMC_TYPE_MMC) ?
|
||||
emmc_samplb_sel(8) : sdio_samplb_sel(8)); /* sel 8 */
|
||||
#endif
|
||||
|
||||
writel(reg, crg_addr);
|
||||
|
||||
reg = sdhci_readl(host, SDHCI_MULTI_CYCLE);
|
||||
reg |= SDHCI_EDGE_DETECT_EN;
|
||||
sdhci_writel(host, reg, SDHCI_MULTI_CYCLE);
|
||||
}
|
||||
|
||||
static void disable_edge_tuning(struct sdhci_host *host)
|
||||
{
|
||||
unsigned int reg;
|
||||
|
||||
reg = sdhci_readl(host, SDHCI_MULTI_CYCLE);
|
||||
reg &= ~SDHCI_EDGE_DETECT_EN;
|
||||
sdhci_writel(host, reg, SDHCI_MULTI_CYCLE);
|
||||
}
|
||||
|
||||
static void select_sampl_phase(struct sdhci_host *host, unsigned int phase)
|
||||
{
|
||||
disable_card_clk(host);
|
||||
set_sampl_phase(host, phase);
|
||||
wait_sampl_dll_slave_ready(host);
|
||||
enable_card_clk(host);
|
||||
udelay(100); /* delay 100us */
|
||||
}
|
||||
|
||||
static int send_tuning(struct sdhci_host *host, u32 opcode, int* cmd_error)
|
||||
{
|
||||
int count, err;
|
||||
const int tuning_num = 1;
|
||||
|
||||
count = 0;
|
||||
do {
|
||||
err = mmc_send_tuning(host->mmc, opcode, NULL);
|
||||
if (err)
|
||||
break;
|
||||
|
||||
count++;
|
||||
} while (count < tuning_num);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int lotus_mmc_exec_tuning(struct sdhci_host *host, unsigned int opcode)
|
||||
{
|
||||
unsigned int index, val;
|
||||
unsigned int edge_p2f, edge_f2p, start, end, phase;
|
||||
unsigned int fall, rise, fall_updat_flag;
|
||||
unsigned int found;
|
||||
unsigned int prev_found = 0;
|
||||
int err;
|
||||
int prev_err = 0;
|
||||
unsigned short ctrl;
|
||||
|
||||
wait_drv_dll_lock(host);
|
||||
enable_sampl_dll_slave(host);
|
||||
enable_sample(host);
|
||||
enable_edge_tuning(host);
|
||||
host->is_tuning = 1;
|
||||
|
||||
start = 0;
|
||||
end = PHASE_SCALE / EDGE_TUNING_PHASE_STEP;
|
||||
|
||||
edge_p2f = start;
|
||||
edge_f2p = end;
|
||||
for (index = 0; index <= end; index++) {
|
||||
select_sampl_phase(host, index * EDGE_TUNING_PHASE_STEP);
|
||||
|
||||
err = mmc_send_tuning(host->mmc, opcode, NULL);
|
||||
if (!err) {
|
||||
val = sdhci_readl(host, SDHCI_MULTI_CYCLE);
|
||||
found = val & SDHCI_FOUND_EDGE;
|
||||
} else {
|
||||
found = 1;
|
||||
}
|
||||
|
||||
if (prev_found && !found)
|
||||
edge_f2p = index;
|
||||
else if (!prev_found && found)
|
||||
edge_p2f = index;
|
||||
|
||||
if ((edge_p2f != start) && (edge_f2p != end))
|
||||
break;
|
||||
|
||||
prev_found = found;
|
||||
}
|
||||
|
||||
if ((edge_p2f == start) && (edge_f2p == end)) {
|
||||
printf("sdhci: tuning failed! can not found edge!\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
disable_edge_tuning(host);
|
||||
|
||||
start = edge_p2f * EDGE_TUNING_PHASE_STEP;
|
||||
end = edge_f2p * EDGE_TUNING_PHASE_STEP;
|
||||
if (end <= start)
|
||||
end += PHASE_SCALE;
|
||||
|
||||
fall = start;
|
||||
rise = end;
|
||||
fall_updat_flag = 0;
|
||||
for (index = start; index <= end; index++) {
|
||||
select_sampl_phase(host, index % PHASE_SCALE);
|
||||
|
||||
err = send_tuning(host, opcode, NULL);
|
||||
if (err)
|
||||
debug("sdhci: send tuning CMD%u fail! phase:%u err:%d\n",
|
||||
opcode, index, err);
|
||||
|
||||
if (err && index == start) {
|
||||
if (!fall_updat_flag) {
|
||||
fall_updat_flag = 1;
|
||||
fall = start;
|
||||
}
|
||||
} else {
|
||||
if (!prev_err && err) {
|
||||
if (!fall_updat_flag) {
|
||||
fall_updat_flag = 1;
|
||||
fall = index;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (prev_err && !err)
|
||||
rise = index;
|
||||
|
||||
if (err && index == end)
|
||||
rise = end;
|
||||
|
||||
prev_err = err;
|
||||
}
|
||||
|
||||
phase = ((fall + rise) / 2 + PHASE_SCALE / 2) % PHASE_SCALE; /* 2 for cal average */
|
||||
|
||||
printf("sdhci: tuning done! valid phase shift [%u, %u] Final Phase:%u\n",
|
||||
rise % PHASE_SCALE, fall % PHASE_SCALE, phase);
|
||||
|
||||
host->tuning_phase = phase;
|
||||
select_sampl_phase(host, phase);
|
||||
|
||||
ctrl = sdhci_readw(host, SDHCI_HOST_CONTROL2);
|
||||
ctrl |= SDHCI_CTRL_TUNED_CLK;
|
||||
sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2);
|
||||
host->is_tuning = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
void sdhci_set_host_caps(struct sdhci_host *host)
|
||||
{
|
||||
host->host_caps = MMC_MODE_HS | MMC_MODE_HS_52MHz |
|
||||
MMC_MODE_DDR_52MHz | MMC_MODE_HS200 | MMC_MODE_4BIT;
|
||||
#ifdef CONFIG_MMC_8BIT
|
||||
host->host_caps |= MMC_MODE_8BIT;
|
||||
#endif
|
||||
#if !defined(CONFIG_AUTO_SD_UPDATE) && defined(CONFIG_TARGET_XMFALCON) && defined(CONFIG_MMC_HS400_SUPPORT)
|
||||
host->host_caps |= MMC_MODE_HS400 |
|
||||
MMC_MODE_8BIT;
|
||||
#if defined(CONFIG_MMC_HS400_ES_SUPPORT)
|
||||
host->host_caps |= MMC_MODE_HS400_ES;
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
int sdhci_add_port(int index, uintptr_t regbase, u32 type)
|
||||
{
|
||||
struct sdhci_host *host = NULL;
|
||||
|
||||
if (type == MMC_TYPE_MMC)
|
||||
emmc_hardware_init();
|
||||
else
|
||||
sd_hardware_init();
|
||||
|
||||
host = calloc(1, sizeof(struct sdhci_host));
|
||||
if (host == NULL) {
|
||||
puts("sdhci_host malloc fail!\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
host->name = "sdhci";
|
||||
host->index = index;
|
||||
host->type = type;
|
||||
host->ioaddr = (void *)regbase;
|
||||
host->quirks = 0;
|
||||
host->set_clock = lotus_mmc_set_clk;
|
||||
host->priv_init = lotus_mmc_priv_init;
|
||||
host->set_control_reg = lotus_mmc_set_ioconfig;
|
||||
#ifdef MMC_SUPPORTS_TUNING
|
||||
host->execute_tuning = lotus_mmc_exec_tuning;
|
||||
#endif
|
||||
#ifdef CONFIG_TARGET_XMORCA
|
||||
host->set_io_cfg = lotus_set_io_cfg;
|
||||
#endif
|
||||
sdhci_set_host_caps(host);
|
||||
add_sdhci(host, CONFIG_LOTUS_SDHCI_MAX_FREQ, MIN_FREQ);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void sdhci_hs400_enhanced_stobe(struct mmc *mmc, bool enable)
|
||||
{
|
||||
struct sdhci_host *host = mmc->priv;
|
||||
u32 ctrl;
|
||||
|
||||
ctrl = sdhci_readl(host, SDHCI_EMMC_CTRL);
|
||||
if (enable)
|
||||
ctrl |= SDHCI_ENH_STROBE_EN;
|
||||
else
|
||||
ctrl &= ~SDHCI_ENH_STROBE_EN;
|
||||
|
||||
sdhci_writel(host, ctrl, SDHCI_EMMC_CTRL);
|
||||
|
||||
#if 0 //FIXME: XXX
|
||||
ctrl = sdhci_readl(host, SDHCI_MULTI_CYCLE);
|
||||
if (enable)
|
||||
ctrl |= SDHCI_CMD_DLY_EN;
|
||||
else
|
||||
ctrl &= ~SDHCI_CMD_DLY_EN;
|
||||
|
||||
sdhci_writel(host, ctrl, SDHCI_MULTI_CYCLE);
|
||||
#endif
|
||||
}
|
||||
|
||||
static void print_mmcinfo(struct mmc *mmc)
|
||||
{
|
||||
int i;
|
||||
|
||||
printf("Device: %s\n", mmc->cfg->name);
|
||||
printf("Manufacturer ID: %x\n", mmc->cid[0] >> 24);
|
||||
printf("OEM: %x\n", (mmc->cid[0] >> 8) & 0xffff);
|
||||
printf("Name: %c%c%c%c%c\n", mmc->cid[0] & 0xff,
|
||||
(mmc->cid[1] >> 24), (mmc->cid[1] >> 16) & 0xff,
|
||||
(mmc->cid[1] >> 8) & 0xff, mmc->cid[1] & 0xff);
|
||||
|
||||
printf("Bus Speed: %d\n", mmc->clock);
|
||||
#if CONFIG_IS_ENABLED(MMC_VERBOSE)
|
||||
printf("Mode: %s\n", mmc_mode_name(mmc->selected_mode));
|
||||
mmc_dump_capabilities("card capabilities", mmc->card_caps);
|
||||
mmc_dump_capabilities("host capabilities", mmc->host_caps);
|
||||
#endif
|
||||
printf("Rd Block Len: %d\n", mmc->read_bl_len);
|
||||
|
||||
printf("%s version %d.%d", IS_SD(mmc) ? "SD" : "MMC",
|
||||
EXTRACT_SDMMC_MAJOR_VERSION(mmc->version),
|
||||
EXTRACT_SDMMC_MINOR_VERSION(mmc->version));
|
||||
if (EXTRACT_SDMMC_CHANGE_VERSION(mmc->version) != 0)
|
||||
printf(".%d", EXTRACT_SDMMC_CHANGE_VERSION(mmc->version));
|
||||
printf("\n");
|
||||
|
||||
printf("High Capacity: %s\n", mmc->high_capacity ? "Yes" : "No");
|
||||
print_to_tool("Capacity: %lld\r\n", mmc->capacity);
|
||||
|
||||
printf("Bus Width: %d-bit%s\n", mmc->bus_width,
|
||||
mmc->ddr_mode ? " DDR" : "");
|
||||
|
||||
#if CONFIG_IS_ENABLED(MMC_WRITE)
|
||||
puts("Erase Group Size: ");
|
||||
print_size(((u64)mmc->erase_grp_size) << 9, "\n");
|
||||
#endif
|
||||
|
||||
if (!IS_SD(mmc) && mmc->version >= MMC_VERSION_4_41) {
|
||||
bool has_enh = (mmc->part_support & ENHNCD_SUPPORT) != 0;
|
||||
bool usr_enh = has_enh && (mmc->part_attr & EXT_CSD_ENH_USR);
|
||||
|
||||
#if CONFIG_IS_ENABLED(MMC_HW_PARTITIONING)
|
||||
puts("HC WP Group Size: ");
|
||||
print_size(((u64)mmc->hc_wp_grp_size) << 9, "\n");
|
||||
#endif
|
||||
|
||||
puts("User Capacity: ");
|
||||
print_size(mmc->capacity_user, usr_enh ? " ENH" : "");
|
||||
if (mmc->wr_rel_set & EXT_CSD_WR_DATA_REL_USR)
|
||||
puts(" WRREL\n");
|
||||
else
|
||||
putc('\n');
|
||||
if (usr_enh) {
|
||||
puts("User Enhanced Start: ");
|
||||
print_size(mmc->enh_user_start, "\n");
|
||||
puts("User Enhanced Size: ");
|
||||
print_size(mmc->enh_user_size, "\n");
|
||||
}
|
||||
puts("Boot Capacity: ");
|
||||
print_size(mmc->capacity_boot, has_enh ? " ENH\n" : "\n");
|
||||
puts("RPMB Capacity: ");
|
||||
print_size(mmc->capacity_rpmb, has_enh ? " ENH\n" : "\n");
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(mmc->capacity_gp); i++) {
|
||||
bool is_enh = has_enh &&
|
||||
(mmc->part_attr & EXT_CSD_ENH_GP(i));
|
||||
if (mmc->capacity_gp[i]) {
|
||||
printf("GP%i Capacity: ", i+1);
|
||||
print_size(mmc->capacity_gp[i],
|
||||
is_enh ? " ENH" : "");
|
||||
if (mmc->wr_rel_set & EXT_CSD_WR_DATA_REL_GP(i))
|
||||
puts(" WRREL\n");
|
||||
else
|
||||
putc('\n');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int lotus_mmc_init(int dev_num)
|
||||
{
|
||||
struct mmc *mmc = find_mmc_device(dev_num);
|
||||
int ret;
|
||||
|
||||
if (mmc == NULL) {
|
||||
printf("mmc device not found!!\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ret = mmc_init(mmc);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
print_mmcinfo(mmc);
|
||||
|
||||
if (!IS_SD(mmc))
|
||||
return mmc_set_boot_config(mmc);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void printf_mmc(int dev_num)
|
||||
{
|
||||
struct mmc *mmc = find_mmc_device(dev_num);
|
||||
|
||||
if (mmc != NULL)
|
||||
print_mmcinfo(mmc);
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
|
||||
#ifndef _DRIVERS_LOTUS_SDHCI_H
|
||||
#define _DRIVERS_LOTUS_SDHCI_H
|
||||
|
||||
#include <asm/arch/platform.h>
|
||||
|
||||
#define EMMC_LAYER_MAX 2
|
||||
#define EMMC_SPEED_MAX 5
|
||||
|
||||
#define SDIO_LAYER_MAX 2
|
||||
#define SDIO_SPEED_MAX 5
|
||||
|
||||
#define BSP_CFG_CRG_REG (SYS_CTRL_REG_BASE + 0x134)
|
||||
|
||||
struct sdhci_pad_cell {
|
||||
unsigned char clk_pad;
|
||||
unsigned char dq_cmd_pad;
|
||||
};
|
||||
|
||||
#endif /* _DRIVERS_LOTUS_SDHCI_H */
|
||||
@@ -0,0 +1,615 @@
|
||||
/*
|
||||
* Copyright (c) LOTUS. All rights reserved.
|
||||
*/
|
||||
|
||||
#include <asm/arch/platform.h>
|
||||
#include <linux/lotus/chip.h>
|
||||
#include <common.h>
|
||||
#include <malloc.h>
|
||||
#include <sdhci.h>
|
||||
#include "mmc_private.h"
|
||||
#include "xmfalcon_drive_cap.c"
|
||||
|
||||
#define SDHCI_LOTUS_IO_PD_EN 0 /* enable IO up and down cfg */
|
||||
|
||||
#define MIN_FREQ 400000
|
||||
#define NOT_FOUND (-1)
|
||||
|
||||
#ifdef CONFIG_LOTUS_FPGA
|
||||
#define PHASE_SCALE 8
|
||||
#define EDGE_TUNING_PHASE_STEP 1
|
||||
#else
|
||||
#define PHASE_SCALE 32
|
||||
#define EDGE_TUNING_PHASE_STEP 4
|
||||
#endif
|
||||
|
||||
#define SDIO_DRV_DLL_SRST_REQ (0x1 << 29)
|
||||
#define SDIO_CLK_EN (0x1 << 28)
|
||||
#define SDIO_SRST_REQ (0x1 << 27)
|
||||
#define SDIO_CLK_SEL (0x7 << 24)
|
||||
#define SDIO_CLK_SEL_400K (0x1 << 24)
|
||||
|
||||
#define REG_EMMC_DRV_DLL_STATUS (CRG_REG_BASE + 0x210)
|
||||
#define REG_SDIO0_DRV_DLL_STATUS (CRG_REG_BASE + 0x228)
|
||||
#define REG_SDIO1_DRV_DLL_STATUS (CRG_REG_BASE + 0x23c)
|
||||
#define SDIO_DRV_DLL_LOCK BIT(15)
|
||||
#define SDIO_DRV_DLL_READY BIT(14)
|
||||
|
||||
#define REG_EMMC_SAMPL_DLL_STATUS (CRG_REG_BASE + 0x208)
|
||||
#define REG_SDIO0_SAMPL_DLL_STATUS (CRG_REG_BASE + 0x224)
|
||||
#define REG_SDIO1_SAMPL_DLL_STATUS (CRG_REG_BASE + 0x238)
|
||||
#define SDIO_SAMPL_DLL_SLAVE_READY BIT(0)
|
||||
|
||||
#define REG_EMMC_SAMPL_DLL_CTRL (CRG_REG_BASE + 0x1f4)
|
||||
#define REG_SDIO0_SAMPL_DLL_CTRL (CRG_REG_BASE + 0x22c)
|
||||
#define REG_SDIO1_SAMPL_DLL_CTRL (CRG_REG_BASE + 0x240)
|
||||
#define SDIO_SAMPL_DLL_SLAVE_EN BIT(16)
|
||||
|
||||
#define REG_EMMC_SAMPLB_DLL_CTRL (CRG_REG_BASE + 0x1f8)
|
||||
#define EMMC_SAMPLB_DLL_CLK_MASK (0x1f << 0)
|
||||
#define emmc_samplb_sel(phase) ((phase) << 0)
|
||||
#define REG_SDIO0_SAMPLB_DLL_CTRL (CRG_REG_BASE + 0x21c)
|
||||
#define REG_SDIO1_SAMPLB_DLL_CTRL (CRG_REG_BASE + 0x230)
|
||||
#define SDIO_SAMPLB_DLL_CLK_MASK (0x1f << 24)
|
||||
#define sdio_samplb_sel(phase) ((phase) << 24)
|
||||
|
||||
#define REG_EMMC_DRV_DLL_CTRL (CRG_REG_BASE + 0x1fc)
|
||||
#define REG_SDIO0_DRV_DLL_CTRL (CRG_REG_BASE + 0x220)
|
||||
#define REG_SDIO1_DRV_DLL_CTRL (CRG_REG_BASE + 0x234)
|
||||
#define SDIO_DRV_PHASE_SEL_MASK (0x1f << 24)
|
||||
#define sdio_drv_sel(phase) ((phase) << 24)
|
||||
|
||||
#define REG_EMMC_DS_DLL_CTRL (CRG_REG_BASE + 0x200)
|
||||
#define EMMC_DS_DLL_MODE_SSEL BIT(13)
|
||||
#define EMMC_DS_DLL_SSEL_MASK 0x7f
|
||||
|
||||
#define REG_EMMC_DS180_DLL_CTRL (CRG_REG_BASE + 0x204)
|
||||
#define EMMC_DS180_DLL_BYPASS BIT(15)
|
||||
#define REG_EMMC_DS180_DLL_STATUS (CRG_REG_BASE + 0x218)
|
||||
#define EMMC_DS180_DLL_READY BIT(0)
|
||||
|
||||
#define IO_CFG_SR BIT(10)
|
||||
#define IO_CFG_PULL_DOWN BIT(9)
|
||||
#define IO_CFG_PULL_UP BIT(8)
|
||||
#define IO_CFG_DRV_STR_MASK (0xf << 4)
|
||||
#define io_cfg_drv_str_sel(str) ((str) << 4)
|
||||
|
||||
#define REG_IO_CFG_BASE 0x100C0000
|
||||
/* EMMC_IOCFG */
|
||||
#define IO_CFG_EMMC_DATA_LINE_COUNT 8
|
||||
#define REG_CTRL_EMMC_CLK 0x0000
|
||||
#define REG_CTRL_EMMC_CMD 0x0004
|
||||
#define REG_CTRL_EMMC_DATA0 0x0008
|
||||
#define REG_CTRL_EMMC_DATA1 0x000c
|
||||
#define REG_CTRL_EMMC_DATA2 0x0010
|
||||
#define REG_CTRL_EMMC_DATA3 0x0014
|
||||
#define REG_CTRL_EMMC_DATA4 0x002c
|
||||
#define REG_CTRL_EMMC_DATA5 0x0030
|
||||
#define REG_CTRL_EMMC_DATA6 0x0024
|
||||
#define REG_CTRL_EMMC_DATA7 0x0028
|
||||
#define REG_CTRL_EMMC_DS 0x001c
|
||||
#define REG_CTRL_EMMC_RST 0x0018
|
||||
static unsigned int io_emmc_data_reg[IO_CFG_EMMC_DATA_LINE_COUNT] = {
|
||||
REG_CTRL_EMMC_DATA0, REG_CTRL_EMMC_DATA1,
|
||||
REG_CTRL_EMMC_DATA2, REG_CTRL_EMMC_DATA3,
|
||||
REG_CTRL_EMMC_DATA4, REG_CTRL_EMMC_DATA5,
|
||||
REG_CTRL_EMMC_DATA6, REG_CTRL_EMMC_DATA7
|
||||
};
|
||||
|
||||
#define IO_CFG_SDIO0_DATA_LINE_COUNT 4
|
||||
#define REG_CTRL_SDIO0_CLK 0x001c
|
||||
#define REG_CTRL_SDIO0_CMD 0x0020
|
||||
#define REG_CTRL_SDIO0_DATA0 0x0024
|
||||
#define REG_CTRL_SDIO0_DATA1 0x0028
|
||||
#define REG_CTRL_SDIO0_DATA2 0x002c
|
||||
#define REG_CTRL_SDIO0_DATA3 0x0030
|
||||
static unsigned int io_sdio0_data_reg[IO_CFG_SDIO0_DATA_LINE_COUNT] = {
|
||||
REG_CTRL_SDIO0_DATA0, REG_CTRL_SDIO0_DATA1,
|
||||
REG_CTRL_SDIO0_DATA2, REG_CTRL_SDIO0_DATA3
|
||||
};
|
||||
|
||||
#define CLK_100K 100000
|
||||
#define CLK_400K 400000
|
||||
#define CLK_25M 25000000
|
||||
#define CLK_50M 50000000
|
||||
#define CLK_90M 90000000
|
||||
#define CLK_112M 112000000
|
||||
#define CLK_150M 150000000
|
||||
#define CLK_198M 198000000
|
||||
|
||||
static void enable_sample(struct sdhci_host *host);
|
||||
static void set_drv_phase(struct sdhci_host *host, unsigned int phase);
|
||||
static void set_sampl_phase(struct sdhci_host *host, unsigned int phase);
|
||||
static void wait_sampl_dll_slave_ready(struct sdhci_host *host);
|
||||
static void enable_card_clk(struct sdhci_host *host);
|
||||
static void enable_internal_clk(struct sdhci_host *host);
|
||||
static void disable_internal_clk(struct sdhci_host *host);
|
||||
|
||||
static void dll_reset_assert(struct sdhci_host *host)
|
||||
{
|
||||
uintptr_t crg_addr;
|
||||
unsigned int reg;
|
||||
|
||||
crg_addr = (host->type == MMC_TYPE_MMC) ?
|
||||
REG_EMMC_CRG : REG_SDIO0_CRG;
|
||||
reg = readl(crg_addr);
|
||||
reg |= SDIO_DRV_DLL_SRST_REQ;
|
||||
writel(reg, crg_addr);
|
||||
}
|
||||
|
||||
static void dll_reset_deassert(struct sdhci_host *host)
|
||||
{
|
||||
uintptr_t crg_addr;
|
||||
unsigned int reg;
|
||||
|
||||
crg_addr = (host->type == MMC_TYPE_MMC) ?
|
||||
REG_EMMC_CRG : REG_SDIO0_CRG;
|
||||
reg = readl(crg_addr);
|
||||
reg &= ~SDIO_DRV_DLL_SRST_REQ;
|
||||
writel(reg, crg_addr);
|
||||
}
|
||||
|
||||
static void set_crg(struct sdhci_host *host, unsigned int clk)
|
||||
{
|
||||
uintptr_t crg_addr;
|
||||
unsigned int sel, reg;
|
||||
unsigned int clk_mux[] = {
|
||||
CLK_100K, CLK_400K, CLK_25M, CLK_50M,
|
||||
CLK_90M, CLK_112M, CLK_150M, CLK_198M
|
||||
};
|
||||
|
||||
crg_addr = (host->type == MMC_TYPE_MMC) ?
|
||||
REG_EMMC_CRG : REG_SDIO0_CRG;
|
||||
reg = readl(crg_addr);
|
||||
reg &= ~MMC_CLK_SEL_MASK;
|
||||
|
||||
if (clk <= MIN_FREQ) {
|
||||
sel = 1;
|
||||
} else {
|
||||
for (sel = 0x7; sel > 0; sel--) {
|
||||
if (clk >= clk_mux[sel])
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
reg |= mmc_clk_sel(sel);
|
||||
writel(reg, crg_addr);
|
||||
}
|
||||
|
||||
static void wait_ds180_dll_ready(void)
|
||||
{
|
||||
unsigned int reg;
|
||||
unsigned int timeout = 20;
|
||||
|
||||
do {
|
||||
reg = readl(REG_EMMC_DS180_DLL_STATUS);
|
||||
if (reg & EMMC_DS180_DLL_READY)
|
||||
return;
|
||||
|
||||
udelay(1000); /* delay 1000us */
|
||||
timeout--;
|
||||
} while (timeout > 0);
|
||||
|
||||
printf("DS180 DLL master not ready.\n");
|
||||
}
|
||||
|
||||
static void lotus_mmc_priv_init(struct sdhci_host *host)
|
||||
{
|
||||
unsigned short ctrl;
|
||||
unsigned int reg;
|
||||
|
||||
ctrl = sdhci_readw(host, SDHCI_MSHC_CTRL);
|
||||
ctrl &= ~SDHCI_CMD_CONFLIT_CHECK;
|
||||
sdhci_writew(host, ctrl, SDHCI_MSHC_CTRL);
|
||||
|
||||
reg = sdhci_readl(host, SDHCI_AXI_MBIIU_CTRL);
|
||||
reg |= SDHCI_GM_WR_OSRC_LMT | SDHCI_GM_RD_OSRC_LMT;
|
||||
reg &= ~SDHCI_UNDEFL_INCR_EN;
|
||||
sdhci_writel(host, reg, SDHCI_AXI_MBIIU_CTRL);
|
||||
|
||||
reg = sdhci_readl(host, SDHCI_MULTI_CYCLE);
|
||||
reg &= ~SDHCI_CMD_DLY_EN;
|
||||
reg |= SDHCI_EDGE_DETECT_EN | SDHCI_DATA_DLY_EN;
|
||||
sdhci_writel(host, reg, SDHCI_MULTI_CYCLE);
|
||||
|
||||
if (host->type == MMC_TYPE_MMC) {
|
||||
/*add opt emmc reset gpio*/
|
||||
reg = sdhci_readl(host, SDHCI_GP_OUT_R);
|
||||
reg &= ~SDHCI_MMC_RESET_N;
|
||||
sdhci_writel(host, reg, SDHCI_GP_OUT_R);
|
||||
mdelay(1);
|
||||
|
||||
reg |= SDHCI_MMC_RESET_N;
|
||||
sdhci_writel(host, reg, SDHCI_GP_OUT_R);
|
||||
mdelay(1);
|
||||
}
|
||||
}
|
||||
|
||||
static void set_drv_str(unsigned int offset, unsigned int pull_up,
|
||||
unsigned int pull_down, unsigned int sr,
|
||||
unsigned int drv_str)
|
||||
{
|
||||
unsigned int reg;
|
||||
const uintptr_t crg_addr = REG_IO_CFG_BASE + offset;
|
||||
|
||||
reg = readl(crg_addr);
|
||||
|
||||
#if SDHCI_LOTUS_IO_PD_EN
|
||||
reg &= ~(IO_CFG_PULL_UP | IO_CFG_PULL_DOWN);
|
||||
reg |= (pull_up ? IO_CFG_PULL_UP : 0);
|
||||
reg |= (pull_down ? IO_CFG_PULL_DOWN : 0);
|
||||
#endif
|
||||
|
||||
reg &= ~(IO_CFG_DRV_STR_MASK | IO_CFG_SR);
|
||||
reg |= (sr ? IO_CFG_SR : 0);
|
||||
reg |= io_cfg_drv_str_sel(drv_str);
|
||||
writel(reg, crg_addr);
|
||||
}
|
||||
|
||||
static int sd_hardware_init(void)
|
||||
{
|
||||
unsigned int reg;
|
||||
|
||||
/* clk enable */
|
||||
reg = readl(REG_SDIO0_CRG);
|
||||
reg |= SDIO_CLK_EN;
|
||||
writel(reg, REG_SDIO0_CRG);
|
||||
|
||||
/* reset assert */
|
||||
reg = readl(REG_SDIO0_CRG);
|
||||
reg |= SDIO_SRST_REQ | SDIO_DRV_DLL_SRST_REQ;
|
||||
writel(reg, REG_SDIO0_CRG);
|
||||
udelay(25); /* delay 25us */
|
||||
|
||||
/* reset deassert */
|
||||
reg &= ~SDIO_SRST_REQ;
|
||||
writel(reg, REG_SDIO0_CRG);
|
||||
udelay(1); /* delay 1us */
|
||||
|
||||
udelay(5000); /* delay 5000us */
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int emmc_hardware_init(void)
|
||||
{
|
||||
unsigned int reg;
|
||||
|
||||
/* eMMC clk enable */
|
||||
reg = readl(REG_EMMC_CRG);
|
||||
reg |= SDIO_CLK_EN;
|
||||
writel(reg, REG_EMMC_CRG);
|
||||
|
||||
/* eMMC reset assert */
|
||||
reg = readl(REG_EMMC_CRG);
|
||||
reg |= SDIO_SRST_REQ | SDIO_DRV_DLL_SRST_REQ;
|
||||
writel(reg, REG_EMMC_CRG);
|
||||
udelay(25); /* delay 25us */
|
||||
|
||||
/* select 400K clk */
|
||||
reg = readl(REG_EMMC_CRG);
|
||||
reg &= ~SDIO_CLK_SEL;
|
||||
reg |= SDIO_CLK_SEL_400K;
|
||||
writel(reg, REG_EMMC_CRG);
|
||||
udelay(25); /* delay 25us */
|
||||
|
||||
/* eMMC reset deassert */
|
||||
reg = readl(REG_EMMC_CRG);
|
||||
reg &= ~SDIO_SRST_REQ;
|
||||
writel(reg, REG_EMMC_CRG);
|
||||
udelay(1); /* delay 1us */
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void lotus_emmc_set_ioconfig(struct sdhci_host *host)
|
||||
{
|
||||
int i;
|
||||
int bus_width = host->mmc->bus_width;
|
||||
unsigned int reg, _reg;
|
||||
unsigned int emmc_intf, emmc_drive_offset;
|
||||
unsigned int inch_index, layer_index, speed_index;
|
||||
struct sdhci_pad_cell *emmc_drive = emmc_drive_cap;
|
||||
|
||||
const int emmc_speed[] = {
|
||||
MMC_HS_400_ES, 4,
|
||||
MMC_HS_400, 4,
|
||||
MMC_HS_200, 3,
|
||||
MMC_DDR_52, 2,
|
||||
MMC_HS_52, 1,
|
||||
MMC_HS, 1,
|
||||
MMC_LEGACY, 0,
|
||||
};
|
||||
|
||||
speed_index = 0;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(emmc_speed); i += 2) {
|
||||
if (host->mmc->selected_mode == emmc_speed[i]) {
|
||||
speed_index = emmc_speed[i + 1];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* bit[5-6]: layer bit[7-8]: inch*/
|
||||
emmc_intf = readl(BSP_CFG_CRG_REG);
|
||||
layer_index = (emmc_intf >> 5) & 0x03;
|
||||
inch_index = (emmc_intf >> 7) & 0x03;
|
||||
|
||||
emmc_drive_offset = inch_index*(EMMC_LAYER_MAX * EMMC_SPEED_MAX) +
|
||||
layer_index*(EMMC_SPEED_MAX) + speed_index;
|
||||
|
||||
emmc_drive += emmc_drive_offset;
|
||||
|
||||
if (bus_width == 8) {
|
||||
reg = readl(SYS_CTRL_REG_BASE + REG_MISC_CTRL80);
|
||||
_reg = readl(SYS_CTRL_REG_BASE + REG_MISC_CTRL78);
|
||||
if ((_reg & IO_CTRL_MS) == LSADC_DETECT) {
|
||||
if (reg & IO_FLASH_MS) {
|
||||
/* flash电源域供电1.8V, 需切换SDIO0电源域供电电压为1.8V */
|
||||
/* step1: 配置POWER SWITCH输出电压为1.8V */
|
||||
reg = readl(SYS_CTRL_REG_BASE + REG_MISC_CTRL71);
|
||||
reg |= PS_POWER_SEL;
|
||||
writel(reg, SYS_CTRL_REG_BASE + REG_MISC_CTRL71);
|
||||
|
||||
/* step2: 延迟2ms,要求大于1.2ms */
|
||||
mdelay(2);
|
||||
}
|
||||
} else {
|
||||
if (reg & POC_FLASH_MS) {
|
||||
/* flash电源域供电1.8V, 需切换SDIO0电源域供电电压为1.8V */
|
||||
/* step1: 配置POWER SWITCH输出电压为1.8V */
|
||||
reg = readl(SYS_CTRL_REG_BASE + REG_MISC_CTRL71);
|
||||
reg |= PS_POWER_SEL;
|
||||
writel(reg, SYS_CTRL_REG_BASE + REG_MISC_CTRL71);
|
||||
|
||||
/* step2: 延迟2ms,要求大于1.2ms */
|
||||
mdelay(2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
set_drv_str(REG_CTRL_EMMC_CLK, 0, 1,
|
||||
emmc_drive->clk_pad & 0x01,
|
||||
emmc_drive->clk_pad >> 1); /* set drv level */
|
||||
set_drv_str(REG_CTRL_EMMC_CMD, 1, 0,
|
||||
emmc_drive->dq_cmd_pad & 0x01,
|
||||
emmc_drive->dq_cmd_pad >> 1); /* set drv level */
|
||||
for (i = 0; i < bus_width; i++)
|
||||
set_drv_str(io_emmc_data_reg[i], 1, 0,
|
||||
emmc_drive->dq_cmd_pad & 0x01,
|
||||
emmc_drive->dq_cmd_pad >> 1); /* set drv level */
|
||||
|
||||
set_drv_str(REG_CTRL_EMMC_RST, 1, 0, 1, 0x0); /* set drv level */
|
||||
|
||||
if (host->mmc->selected_mode == MMC_HS_400 ||
|
||||
host->mmc->selected_mode == MMC_HS_400_ES) {
|
||||
set_drv_str(REG_CTRL_EMMC_DS, 1, 0,
|
||||
emmc_drive->dq_cmd_pad & 0x01,
|
||||
emmc_drive->dq_cmd_pad >> 1); /* set drv level */
|
||||
}
|
||||
|
||||
if (host->mmc->selected_mode == MMC_DDR_52) {
|
||||
reg = sdhci_readw(host, SDHCI_MULTI_CYCLE);
|
||||
reg &= ~SDHCI_DATA_DLY_EN;
|
||||
sdhci_writew(host, reg, SDHCI_MULTI_CYCLE);
|
||||
}
|
||||
}
|
||||
|
||||
static void sd_set_ioconfig(struct sdhci_host *host)
|
||||
{
|
||||
int i;
|
||||
unsigned int reg;
|
||||
unsigned int sdio_intf, sdio_drive_offset;
|
||||
unsigned int inch_index, layer_index, speed_index;
|
||||
struct sdhci_pad_cell *sdio_drive = sdio0_drive_cap;
|
||||
|
||||
const int sdio_speed[] = {
|
||||
UHS_SDR104, 4,
|
||||
UHS_DDR50, 3,
|
||||
UHS_SDR50, 2,
|
||||
UHS_SDR25, 1,
|
||||
UHS_SDR12, 1,
|
||||
SD_HS, 0,
|
||||
SD_LEGACY, 0,
|
||||
};
|
||||
|
||||
speed_index = 0;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(sdio_speed); i += 2) {
|
||||
if (host->mmc->selected_mode == sdio_speed[i]) {
|
||||
speed_index = sdio_speed[i + 1];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* bit[5-6]: layer bit[9-10]: inch*/
|
||||
sdio_intf = readl(BSP_CFG_CRG_REG);
|
||||
layer_index = (sdio_intf >> 5) & 0x03;
|
||||
inch_index = (sdio_intf >> 9) & 0x03;
|
||||
|
||||
sdio_drive_offset = inch_index*(SDIO_LAYER_MAX * SDIO_SPEED_MAX) +
|
||||
layer_index*(SDIO_SPEED_MAX) + speed_index;
|
||||
|
||||
sdio_drive += sdio_drive_offset;
|
||||
|
||||
if (host->mmc->signal_voltage == MMC_SIGNAL_VOLTAGE_180) {
|
||||
/* flash电源域供电1.8V, 需切换SDIO0电源域供电电压为1.8V */
|
||||
/* step1: 配置POWER SWITCH输出电压为1.8V */
|
||||
reg = readl(SYS_CTRL_REG_BASE + REG_MISC_CTRL71);
|
||||
reg |= PS_POWER_SEL;
|
||||
writel(reg, SYS_CTRL_REG_BASE + REG_MISC_CTRL71);
|
||||
|
||||
/* step2: 延迟2ms,要求大于1ms */
|
||||
mdelay(2);
|
||||
|
||||
/* step3: 配置SDIO0_VOUT电压为1.8V */
|
||||
reg = readl(SYS_CTRL_REG_BASE + REG_MISC_CTRL76);
|
||||
reg |= IO_SDIO0_MS;
|
||||
writel(reg, SYS_CTRL_REG_BASE + REG_MISC_CTRL76);
|
||||
|
||||
reg = sdhci_readw(host, SDHCI_HOST_CONTROL2);
|
||||
reg |= SDHCI_CTRL_VDD_180;
|
||||
sdhci_writew(host, reg, SDHCI_HOST_CONTROL2);
|
||||
} else {
|
||||
/* flash电源域供电3.3V, 需切换SDIO0电源域供电电压为3.3V */
|
||||
/* step1: 配置POWER SWITCH输出电压为3.3V */
|
||||
reg = readl(SYS_CTRL_REG_BASE + REG_MISC_CTRL71);
|
||||
reg &= ~PS_POWER_SEL;
|
||||
writel(reg, SYS_CTRL_REG_BASE + REG_MISC_CTRL71);
|
||||
|
||||
/* step2: 延迟2ms,要求大于1ms */
|
||||
mdelay(2);
|
||||
|
||||
/* step3: 配置SDIO0_VOUT电压为3.3V */
|
||||
reg = readl(SYS_CTRL_REG_BASE + REG_MISC_CTRL76);
|
||||
reg &= ~IO_SDIO0_MS;
|
||||
writel(reg, SYS_CTRL_REG_BASE + REG_MISC_CTRL76);
|
||||
|
||||
reg = sdhci_readw(host, SDHCI_HOST_CONTROL2);
|
||||
reg &= (~SDHCI_CTRL_VDD_180);
|
||||
sdhci_writew(host, reg, SDHCI_HOST_CONTROL2);
|
||||
}
|
||||
|
||||
set_drv_str(REG_CTRL_SDIO0_CLK, 0, 1,
|
||||
sdio_drive->clk_pad & 0x01,
|
||||
sdio_drive->clk_pad >> 1); /* set drv level */
|
||||
set_drv_str(REG_CTRL_SDIO0_CMD, 1, 0,
|
||||
sdio_drive->dq_cmd_pad & 0x01,
|
||||
sdio_drive->dq_cmd_pad >> 1); /* set drv level */
|
||||
for (i = 0; i < IO_CFG_SDIO0_DATA_LINE_COUNT; i++)
|
||||
set_drv_str(io_sdio0_data_reg[i], 1, 0,
|
||||
sdio_drive->dq_cmd_pad & 0x01,
|
||||
sdio_drive->dq_cmd_pad >> 1); /* set drv level */
|
||||
|
||||
if (host->mmc->selected_mode == UHS_DDR50) {
|
||||
reg = sdhci_readw(host, SDHCI_MULTI_CYCLE);
|
||||
reg &= ~SDHCI_DATA_DLY_EN;
|
||||
sdhci_writew(host, reg, SDHCI_MULTI_CYCLE);
|
||||
}
|
||||
}
|
||||
|
||||
static void lotus_mmc_set_ioconfig(struct sdhci_host *host)
|
||||
{
|
||||
unsigned int reg;
|
||||
|
||||
if (host->type == MMC_TYPE_MMC) {
|
||||
reg = sdhci_readw(host, SDHCI_EMMC_CTRL);
|
||||
reg |= SDHCI_CARD_IS_EMMC;
|
||||
sdhci_writew(host, reg, SDHCI_EMMC_CTRL);
|
||||
|
||||
lotus_emmc_set_ioconfig(host);
|
||||
} else {
|
||||
sd_set_ioconfig(host);
|
||||
}
|
||||
sdhci_set_uhs_timing(host);
|
||||
}
|
||||
|
||||
static void set_phase(struct sdhci_host *host)
|
||||
{
|
||||
unsigned int drv_phase, sample_phase;
|
||||
#ifdef CONFIG_LOTUS_FPGA
|
||||
unsigned int fix_num = 4;
|
||||
#else
|
||||
unsigned int fix_num = 1;
|
||||
#endif
|
||||
|
||||
if (host->mmc->selected_mode == MMC_HS_400_ES ||
|
||||
host->mmc->selected_mode == MMC_HS_400) {
|
||||
drv_phase = 8/fix_num; /* 8 for 90 degree */
|
||||
sample_phase = host->tuning_phase/fix_num;
|
||||
} else if (host->mmc->selected_mode == MMC_HS_200 ||
|
||||
host->mmc->selected_mode == UHS_SDR104) {
|
||||
drv_phase = 20/fix_num; /* 20 for 225 degree */
|
||||
sample_phase = host->tuning_phase/fix_num;
|
||||
} else if (host->mmc->selected_mode == UHS_SDR50) {
|
||||
drv_phase = 16/fix_num; /* 16 for 180 degree */
|
||||
sample_phase = host->tuning_phase/fix_num;
|
||||
} else if (host->mmc->selected_mode == MMC_DDR_52 ||
|
||||
host->mmc->selected_mode == UHS_DDR50) {
|
||||
drv_phase = 8/fix_num; /* 8 for 90 degree */
|
||||
sample_phase = 4/fix_num; /* 4 for 45 degree */
|
||||
} else if (host->mmc->selected_mode == MMC_HS ||
|
||||
host->mmc->selected_mode == MMC_HS_52) {
|
||||
drv_phase = 16/fix_num; /* 16 for 180 degree */
|
||||
sample_phase = 4/fix_num; /* 4 for 45 degree */
|
||||
} else if (host->mmc->selected_mode == UHS_SDR25 ||
|
||||
host->mmc->selected_mode == SD_HS) {
|
||||
drv_phase = 16/fix_num; /* 16 for 180 degree */
|
||||
sample_phase = 4/fix_num; /* 4 for 45 degree */
|
||||
} else {
|
||||
drv_phase = 16/fix_num; /* 16 for 180 degree */
|
||||
sample_phase = 0; /* 0 for 0 degree */
|
||||
}
|
||||
|
||||
set_drv_phase(host, drv_phase);
|
||||
enable_sample(host);
|
||||
set_sampl_phase(host, sample_phase);
|
||||
|
||||
udelay(25); /* delay 25us */
|
||||
}
|
||||
|
||||
static void wait_drv_dll_lock(const struct sdhci_host *host)
|
||||
{
|
||||
unsigned int timeout = 20;
|
||||
unsigned int reg;
|
||||
uintptr_t reg_addr;
|
||||
|
||||
reg_addr = (host->type == MMC_TYPE_MMC) ?
|
||||
REG_EMMC_DRV_DLL_STATUS : REG_SDIO0_DRV_DLL_STATUS;
|
||||
do {
|
||||
reg = readl(reg_addr);
|
||||
if (reg & SDIO_DRV_DLL_LOCK)
|
||||
return;
|
||||
|
||||
udelay(1000); /* delay 1000us */
|
||||
timeout--;
|
||||
} while (timeout > 0);
|
||||
|
||||
printf("sdhci: DRV DLL master not locked.\n");
|
||||
}
|
||||
|
||||
static void enable_sampl_dll_slave(struct sdhci_host *host)
|
||||
{
|
||||
unsigned int reg;
|
||||
uintptr_t reg_addr;
|
||||
|
||||
reg_addr = (host->type == MMC_TYPE_MMC) ?
|
||||
REG_EMMC_SAMPL_DLL_CTRL : REG_SDIO0_SAMPL_DLL_CTRL;
|
||||
reg = readl(reg_addr);
|
||||
reg |= SDIO_SAMPL_DLL_SLAVE_EN;
|
||||
writel(reg, reg_addr);
|
||||
}
|
||||
|
||||
static int lotus_mmc_set_clk(struct sdhci_host *host, unsigned int clk)
|
||||
{
|
||||
//FIX HS400 and HS400ES to 150M clk
|
||||
if ((host->mmc->selected_mode == MMC_HS_400 ||
|
||||
host->mmc->selected_mode == MMC_HS_400_ES) && clk > 150000000)
|
||||
clk = 150000000;
|
||||
|
||||
dll_reset_assert(host);
|
||||
udelay(25); /* delay 25us */
|
||||
set_crg(host, clk);
|
||||
set_phase(host);
|
||||
udelay(25); /* delay 25us */
|
||||
|
||||
if (clk > MMC_HIGH_52_MAX_DTR) {
|
||||
enable_sampl_dll_slave(host);
|
||||
dll_reset_deassert(host);
|
||||
}
|
||||
|
||||
enable_internal_clk(host);
|
||||
|
||||
if (clk > MMC_HIGH_52_MAX_DTR) {
|
||||
wait_drv_dll_lock(host);
|
||||
wait_sampl_dll_slave_ready(host);
|
||||
wait_ds180_dll_ready();
|
||||
}
|
||||
|
||||
enable_card_clk(host);
|
||||
udelay(100); /* delay 100us */
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#include "lotus_sdhci.c"
|
||||
@@ -0,0 +1,99 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
#include <lotus_sdhci.h>
|
||||
|
||||
static struct sdhci_pad_cell emmc_drive_cap[] = {
|
||||
// inch:2 layer:2 speed:5
|
||||
//inch_index*(layer * speed) + layer_index*(speed) + speed_index
|
||||
//1inch_4L_ls-sdr
|
||||
{0b0111, 0b0011},
|
||||
//1inch_4L_hs-sdr
|
||||
{0b0111, 0b0011},
|
||||
//1inch_4L_hs-ddr
|
||||
{0b0111, 0b0011},
|
||||
//1inch_4L_hs200
|
||||
{0b1000, 0b0100},
|
||||
//1inch_4L_hs400
|
||||
{0b0100, 0b0100},
|
||||
|
||||
//1inch_2L_ls-sdr
|
||||
{0b0111, 0b0011},
|
||||
//1inch_2L_hs-sdr
|
||||
{0b0111, 0b0011},
|
||||
//1inch_2L_hs-ddr
|
||||
{0b0111, 0b0011},
|
||||
//1inch_2L_hs200
|
||||
{0b0110, 0b0010},
|
||||
//1inch_2L_hs400
|
||||
{0b0010, 0b0010},
|
||||
/*****************2inch*************/
|
||||
//2inch_4L_ls-sdr
|
||||
{0b0111, 0b0011},
|
||||
//2inch_4L_hs-sdr
|
||||
{0b0111, 0b0011},
|
||||
//2inch_4L_hs-ddr
|
||||
{0b0111, 0b0011},
|
||||
//2inch_4L_hs200
|
||||
{0b1100, 0b1010},
|
||||
//2inch_4L_hs400
|
||||
{0b1100, 0b1100},
|
||||
|
||||
//2inch_2L_ls-sdr
|
||||
{0b0111, 0b0011},
|
||||
//2inch_2L_hs-sdr
|
||||
{0b0111, 0b0011},
|
||||
//2inch_2L_hs-ddr
|
||||
{0b0111, 0b0011},
|
||||
//2inch_2L_hs200
|
||||
{0b1010, 0b0110},
|
||||
//2inch_2L_hs400
|
||||
{0b1000, 0b1000},
|
||||
};
|
||||
static struct sdhci_pad_cell sdio0_drive_cap[] = {
|
||||
// inch:2 layer:2 speed:5
|
||||
//inch_index*(layer * speed) + layer_index*(speed) + speed_index
|
||||
//2inch_4L_hs-sdr
|
||||
{0b0101, 0b0011},
|
||||
//2inch_4L_sdr12-sdr25
|
||||
{0b0101, 0b0011},
|
||||
//2inch_4L_sdr50
|
||||
{0b0101, 0b0011},
|
||||
//2inch_4L_ddr50
|
||||
{0b0101, 0b0100},
|
||||
//2inch_4L_sdr104
|
||||
{0b1100, 0b0100},
|
||||
|
||||
//2inch_2L_hs-sdr
|
||||
{0b0101, 0b0011},
|
||||
//2inch_2L_sdr12-sdr25
|
||||
{0b0101, 0b0011},
|
||||
//2inch_2L_sdr50
|
||||
{0b0101, 0b0011},
|
||||
//2inch_2L_ddr50
|
||||
{0b0101, 0b0010},
|
||||
//2inch_2L_sdr104
|
||||
{0b1100, 0b0100},
|
||||
/*****************4inch*************/
|
||||
//4inch_4L_hs-sdr
|
||||
{0b0111, 0b0001},
|
||||
//4inch_4L_sdr12-sdr25
|
||||
{0b0111, 0b0001},
|
||||
//4inch_4L_sdr50
|
||||
{0b0110, 0b0011},
|
||||
//4inch_4L_ddr50
|
||||
{0b0110, 0b0011},
|
||||
//4inch_4L_sdr104
|
||||
{0b1110, 0b0110},
|
||||
|
||||
//4inch_2L_hs-sdr
|
||||
{0b0111, 0b0001},
|
||||
//4inch_2L_sdr12-sdr25
|
||||
{0b0111, 0b0001},
|
||||
//4inch_2L_sdr50
|
||||
{0b0110, 0b0011},
|
||||
//4inch_2L_ddr50
|
||||
{0b0110, 0b0011},
|
||||
//4inch_2L_sdr104
|
||||
{0b1110, 0b0110},
|
||||
};
|
||||
|
||||
@@ -0,0 +1,384 @@
|
||||
/*
|
||||
* Copyright (c) LOTUS. All rights reserved.
|
||||
*/
|
||||
|
||||
#include <asm/arch/platform.h>
|
||||
#include <linux/lotus/chip.h>
|
||||
#include <common.h>
|
||||
#include <malloc.h>
|
||||
#include <sdhci.h>
|
||||
#include "mmc_private.h"
|
||||
|
||||
#define MIN_FREQ 400000
|
||||
#define NOT_FOUND (-1)
|
||||
|
||||
#ifdef CONFIG_LOTUS_FPGA
|
||||
#define PHASE_SCALE 8
|
||||
#define EDGE_TUNING_PHASE_STEP 1
|
||||
#else
|
||||
#define PHASE_SCALE 32
|
||||
#define EDGE_TUNING_PHASE_STEP 4
|
||||
#endif
|
||||
|
||||
#define SDIO_DRV_DLL_SRST_REQ (0x1 << 29)
|
||||
#define SDIO_CLK_EN (0x1 << 28)
|
||||
#define SDIO_SRST_REQ (0x1 << 27)
|
||||
#define SDIO_CLK_SEL (0x7 << 24)
|
||||
#define SDIO_CLK_SEL_400K (0x1 << 24)
|
||||
|
||||
#define REG_EMMC_DRV_DLL_STATUS (CRG_REG_BASE + 0x210)
|
||||
#define REG_SDIO0_DRV_DLL_STATUS (CRG_REG_BASE + 0x228)
|
||||
#define REG_SDIO1_DRV_DLL_STATUS (CRG_REG_BASE + 0x23c)
|
||||
#define SDIO_DRV_DLL_LOCK BIT(15)
|
||||
#define SDIO_DRV_DLL_READY BIT(14)
|
||||
|
||||
#define REG_EMMC_SAMPL_DLL_STATUS (CRG_REG_BASE + 0x208)
|
||||
#define REG_SDIO0_SAMPL_DLL_STATUS (CRG_REG_BASE + 0x224)
|
||||
#define REG_SDIO1_SAMPL_DLL_STATUS (CRG_REG_BASE + 0x238)
|
||||
#define SDIO_SAMPL_DLL_SLAVE_READY BIT(0)
|
||||
|
||||
#define REG_EMMC_SAMPL_DLL_CTRL (CRG_REG_BASE + 0x1f4)
|
||||
#define REG_SDIO0_SAMPL_DLL_CTRL (CRG_REG_BASE + 0x22c)
|
||||
#define REG_SDIO1_SAMPL_DLL_CTRL (CRG_REG_BASE + 0x240)
|
||||
#define SDIO_SAMPL_DLL_SLAVE_EN BIT(16)
|
||||
|
||||
#define REG_EMMC_SAMPLB_DLL_CTRL (CRG_REG_BASE + 0x1f8)
|
||||
#define EMMC_SAMPLB_DLL_CLK_MASK (0x1f << 0)
|
||||
#define emmc_samplb_sel(phase) ((phase) << 0)
|
||||
#define REG_SDIO0_SAMPLB_DLL_CTRL (CRG_REG_BASE + 0x21c)
|
||||
#define REG_SDIO1_SAMPLB_DLL_CTRL (CRG_REG_BASE + 0x230)
|
||||
#define SDIO_SAMPLB_DLL_CLK_MASK (0x1f << 24)
|
||||
#define sdio_samplb_sel(phase) ((phase) << 24)
|
||||
|
||||
#define REG_EMMC_DRV_DLL_CTRL (CRG_REG_BASE + 0x1fc)
|
||||
#define REG_SDIO0_DRV_DLL_CTRL (CRG_REG_BASE + 0x220)
|
||||
#define REG_SDIO1_DRV_DLL_CTRL (CRG_REG_BASE + 0x234)
|
||||
#define SDIO_DRV_PHASE_SEL_MASK (0x1f << 24)
|
||||
#define sdio_drv_sel(phase) ((phase) << 24)
|
||||
|
||||
#define REG_EMMC_DS_DLL_CTRL (CRG_REG_BASE + 0x200)
|
||||
#define EMMC_DS_DLL_MODE_SSEL BIT(13)
|
||||
#define EMMC_DS_DLL_SSEL_MASK 0x7f
|
||||
|
||||
#define REG_EMMC_DS180_DLL_CTRL (CRG_REG_BASE + 0x204)
|
||||
#define EMMC_DS180_DLL_BYPASS BIT(15)
|
||||
#define REG_EMMC_DS180_DLL_STATUS (CRG_REG_BASE + 0x218)
|
||||
#define EMMC_DS180_DLL_READY BIT(0)
|
||||
|
||||
#define CLK_100K 100000
|
||||
#define CLK_400K 400000
|
||||
#define CLK_25M 25000000
|
||||
#define CLK_50M 50000000
|
||||
#define CLK_90M 90000000
|
||||
|
||||
static void enable_sample(struct sdhci_host *host);
|
||||
static void set_drv_phase(struct sdhci_host *host, unsigned int phase);
|
||||
static void set_sampl_phase(struct sdhci_host *host, unsigned int phase);
|
||||
static void wait_sampl_dll_slave_ready(struct sdhci_host *host);
|
||||
static void enable_card_clk(struct sdhci_host *host);
|
||||
static void enable_internal_clk(struct sdhci_host *host);
|
||||
static void disable_internal_clk(struct sdhci_host *host);
|
||||
|
||||
static void dll_reset_assert(struct sdhci_host *host)
|
||||
{
|
||||
uintptr_t crg_addr;
|
||||
unsigned int reg;
|
||||
|
||||
crg_addr = (host->type == MMC_TYPE_MMC) ?
|
||||
REG_EMMC_CRG : REG_SDIO0_CRG;
|
||||
reg = readl(crg_addr);
|
||||
reg |= SDIO_DRV_DLL_SRST_REQ;
|
||||
writel(reg, crg_addr);
|
||||
}
|
||||
|
||||
static void dll_reset_deassert(struct sdhci_host *host)
|
||||
{
|
||||
uintptr_t crg_addr;
|
||||
unsigned int reg;
|
||||
|
||||
crg_addr = (host->type == MMC_TYPE_MMC) ?
|
||||
REG_EMMC_CRG : REG_SDIO0_CRG;
|
||||
reg = readl(crg_addr);
|
||||
reg &= ~SDIO_DRV_DLL_SRST_REQ;
|
||||
writel(reg, crg_addr);
|
||||
}
|
||||
|
||||
static void set_crg(struct sdhci_host *host, unsigned int clk)
|
||||
{
|
||||
uintptr_t crg_addr;
|
||||
unsigned int sel, reg;
|
||||
unsigned int clk_mux[] = {
|
||||
CLK_100K, CLK_400K, CLK_25M, CLK_50M, CLK_90M,
|
||||
};
|
||||
|
||||
crg_addr = (host->type == MMC_TYPE_MMC) ?
|
||||
REG_EMMC_CRG : REG_SDIO0_CRG;
|
||||
reg = readl(crg_addr);
|
||||
reg &= ~MMC_CLK_SEL_MASK;
|
||||
|
||||
if (clk <= MIN_FREQ) {
|
||||
sel = 1;
|
||||
} else {
|
||||
for (sel = 0x4; sel > 0; sel--) {
|
||||
if (clk >= clk_mux[sel])
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
reg |= mmc_clk_sel(sel);
|
||||
writel(reg, crg_addr);
|
||||
}
|
||||
|
||||
static void wait_ds180_dll_ready(void)
|
||||
{
|
||||
unsigned int reg;
|
||||
unsigned int timeout = 20;
|
||||
|
||||
do {
|
||||
reg = readl(REG_EMMC_DS180_DLL_STATUS);
|
||||
if (reg & EMMC_DS180_DLL_READY)
|
||||
return;
|
||||
|
||||
udelay(1000); /* delay 1000us */
|
||||
timeout--;
|
||||
} while (timeout > 0);
|
||||
|
||||
printf("DS180 DLL master not ready.\n");
|
||||
}
|
||||
|
||||
static void lotus_mmc_priv_init(struct sdhci_host *host)
|
||||
{
|
||||
unsigned short ctrl;
|
||||
unsigned int reg;
|
||||
|
||||
ctrl = sdhci_readw(host, SDHCI_MSHC_CTRL);
|
||||
ctrl &= ~SDHCI_CMD_CONFLIT_CHECK;
|
||||
sdhci_writew(host, ctrl, SDHCI_MSHC_CTRL);
|
||||
|
||||
reg = sdhci_readl(host, SDHCI_AXI_MBIIU_CTRL);
|
||||
reg |= SDHCI_GM_WR_OSRC_LMT | SDHCI_GM_RD_OSRC_LMT;
|
||||
reg &= ~SDHCI_UNDEFL_INCR_EN;
|
||||
sdhci_writel(host, reg, SDHCI_AXI_MBIIU_CTRL);
|
||||
|
||||
reg = sdhci_readl(host, SDHCI_MULTI_CYCLE);
|
||||
reg &= ~SDHCI_CMD_DLY_EN;
|
||||
reg |= SDHCI_EDGE_DETECT_EN | SDHCI_DATA_DLY_EN;
|
||||
sdhci_writel(host, reg, SDHCI_MULTI_CYCLE);
|
||||
|
||||
if (host->type == MMC_TYPE_MMC) {
|
||||
/*add opt emmc reset gpio*/
|
||||
reg = sdhci_readl(host, SDHCI_GP_OUT_R);
|
||||
reg &= ~SDHCI_MMC_RESET_N;
|
||||
sdhci_writel(host, reg, SDHCI_GP_OUT_R);
|
||||
mdelay(1);
|
||||
|
||||
reg |= SDHCI_MMC_RESET_N;
|
||||
sdhci_writel(host, reg, SDHCI_GP_OUT_R);
|
||||
mdelay(1);
|
||||
}
|
||||
}
|
||||
|
||||
static int sd_hardware_init(void)
|
||||
{
|
||||
unsigned int reg;
|
||||
|
||||
/* clk enable */
|
||||
reg = readl(REG_SDIO0_CRG);
|
||||
reg |= SDIO_CLK_EN;
|
||||
writel(reg, REG_SDIO0_CRG);
|
||||
|
||||
/* reset assert */
|
||||
reg = readl(REG_SDIO0_CRG);
|
||||
reg |= SDIO_SRST_REQ | SDIO_DRV_DLL_SRST_REQ;
|
||||
writel(reg, REG_SDIO0_CRG);
|
||||
udelay(25); /* delay 25us */
|
||||
|
||||
/* reset deassert */
|
||||
reg &= ~SDIO_SRST_REQ;
|
||||
writel(reg, REG_SDIO0_CRG);
|
||||
udelay(1); /* delay 1us */
|
||||
|
||||
udelay(5000); /* delay 5000us */
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int emmc_hardware_init(void)
|
||||
{
|
||||
unsigned int reg;
|
||||
|
||||
/* eMMC clk enable */
|
||||
reg = readl(REG_EMMC_CRG);
|
||||
reg |= SDIO_CLK_EN;
|
||||
writel(reg, REG_EMMC_CRG);
|
||||
|
||||
/* eMMC reset assert */
|
||||
reg = readl(REG_EMMC_CRG);
|
||||
reg |= SDIO_SRST_REQ | SDIO_DRV_DLL_SRST_REQ;
|
||||
writel(reg, REG_EMMC_CRG);
|
||||
udelay(25); /* delay 25us */
|
||||
|
||||
/* select 400K clk */
|
||||
reg = readl(REG_EMMC_CRG);
|
||||
reg &= ~SDIO_CLK_SEL;
|
||||
reg |= SDIO_CLK_SEL_400K;
|
||||
writel(reg, REG_EMMC_CRG);
|
||||
udelay(25); /* delay 25us */
|
||||
|
||||
/* eMMC reset deassert */
|
||||
reg = readl(REG_EMMC_CRG);
|
||||
reg &= ~SDIO_SRST_REQ;
|
||||
writel(reg, REG_EMMC_CRG);
|
||||
udelay(1); /* delay 1us */
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void lotus_mmc_set_ioconfig(struct sdhci_host *host)
|
||||
{
|
||||
unsigned int reg;
|
||||
|
||||
if (host->type == MMC_TYPE_MMC) {
|
||||
reg = sdhci_readw(host, SDHCI_EMMC_CTRL);
|
||||
reg |= SDHCI_CARD_IS_EMMC;
|
||||
sdhci_writew(host, reg, SDHCI_EMMC_CTRL);
|
||||
|
||||
if (host->mmc->selected_mode == MMC_DDR_52) {
|
||||
reg = sdhci_readw(host, SDHCI_MULTI_CYCLE);
|
||||
reg &= ~SDHCI_DATA_DLY_EN;
|
||||
sdhci_writew(host, reg, SDHCI_MULTI_CYCLE);
|
||||
}
|
||||
}
|
||||
|
||||
sdhci_set_uhs_timing(host);
|
||||
}
|
||||
|
||||
static void set_phase(struct sdhci_host *host)
|
||||
{
|
||||
unsigned int drv_phase, sample_phase;
|
||||
#ifdef CONFIG_LOTUS_FPGA
|
||||
unsigned int fix_num = 4;
|
||||
#else
|
||||
unsigned int fix_num = 1;
|
||||
#endif
|
||||
|
||||
if (host->mmc->selected_mode == MMC_HS_400_ES ||
|
||||
host->mmc->selected_mode == MMC_HS_400) {
|
||||
drv_phase = 8/fix_num; /* 8 for 90 degree */
|
||||
sample_phase = host->tuning_phase/fix_num;
|
||||
} else if (host->mmc->selected_mode == MMC_HS_200 ||
|
||||
host->mmc->selected_mode == UHS_SDR104) {
|
||||
drv_phase = 20/fix_num; /* 20 for 225 degree */
|
||||
sample_phase = host->tuning_phase/fix_num;
|
||||
} else if (host->mmc->selected_mode == UHS_SDR50) {
|
||||
drv_phase = 16/fix_num; /* 16 for 180 degree */
|
||||
sample_phase = host->tuning_phase/fix_num;
|
||||
} else if (host->mmc->selected_mode == MMC_DDR_52 ||
|
||||
host->mmc->selected_mode == UHS_DDR50) {
|
||||
drv_phase = 8/fix_num; /* 8 for 90 degree */
|
||||
sample_phase = 4/fix_num; /* 4 for 45 degree */
|
||||
} else if (host->mmc->selected_mode == MMC_HS ||
|
||||
host->mmc->selected_mode == MMC_HS_52) {
|
||||
drv_phase = 16/fix_num; /* 16 for 180 degree */
|
||||
sample_phase = 4/fix_num; /* 4 for 45 degree */
|
||||
} else if (host->mmc->selected_mode == UHS_SDR25 ||
|
||||
host->mmc->selected_mode == SD_HS) {
|
||||
drv_phase = 16/fix_num; /* 16 for 180 degree */
|
||||
sample_phase = 4/fix_num; /* 4 for 45 degree */
|
||||
} else {
|
||||
drv_phase = 16/fix_num; /* 16 for 180 degree */
|
||||
sample_phase = 0; /* 0 for 0 degree */
|
||||
}
|
||||
|
||||
set_drv_phase(host, drv_phase);
|
||||
enable_sample(host);
|
||||
set_sampl_phase(host, sample_phase);
|
||||
|
||||
udelay(25); /* delay 25us */
|
||||
}
|
||||
|
||||
static void wait_drv_dll_lock(const struct sdhci_host *host)
|
||||
{
|
||||
unsigned int timeout = 20;
|
||||
unsigned int reg;
|
||||
uintptr_t reg_addr;
|
||||
|
||||
reg_addr = (host->type == MMC_TYPE_MMC) ?
|
||||
REG_EMMC_DRV_DLL_STATUS : REG_SDIO0_DRV_DLL_STATUS;
|
||||
do {
|
||||
reg = readl(reg_addr);
|
||||
if (reg & SDIO_DRV_DLL_LOCK)
|
||||
return;
|
||||
|
||||
udelay(1000); /* delay 1000us */
|
||||
timeout--;
|
||||
} while (timeout > 0);
|
||||
|
||||
printf("sdhci: DRV DLL master not locked.\n");
|
||||
}
|
||||
|
||||
static void enable_sampl_dll_slave(struct sdhci_host *host)
|
||||
{
|
||||
unsigned int reg;
|
||||
uintptr_t reg_addr;
|
||||
|
||||
reg_addr = (host->type == MMC_TYPE_MMC) ?
|
||||
REG_EMMC_SAMPL_DLL_CTRL : REG_SDIO0_SAMPL_DLL_CTRL;
|
||||
reg = readl(reg_addr);
|
||||
reg |= SDIO_SAMPL_DLL_SLAVE_EN;
|
||||
writel(reg, reg_addr);
|
||||
}
|
||||
|
||||
static int lotus_mmc_set_clk(struct sdhci_host *host, unsigned int clk)
|
||||
{
|
||||
dll_reset_assert(host);
|
||||
udelay(25); /* delay 25us */
|
||||
set_crg(host, clk);
|
||||
set_phase(host);
|
||||
udelay(25); /* delay 25us */
|
||||
|
||||
if (clk > MMC_HIGH_52_MAX_DTR) {
|
||||
enable_sampl_dll_slave(host);
|
||||
dll_reset_deassert(host);
|
||||
}
|
||||
|
||||
enable_internal_clk(host);
|
||||
|
||||
if (clk > MMC_HIGH_52_MAX_DTR) {
|
||||
wait_drv_dll_lock(host);
|
||||
wait_sampl_dll_slave_ready(host);
|
||||
wait_ds180_dll_ready();
|
||||
}
|
||||
|
||||
enable_card_clk(host);
|
||||
udelay(100); /* delay 100us */
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int lotus_set_io_cfg(struct sdhci_host *host, unsigned int cfg)
|
||||
{
|
||||
unsigned int reg;
|
||||
|
||||
if (host->type != MMC_TYPE_MMC) {//only sd card
|
||||
if (cfg) {
|
||||
reg = readl(0x11980040);
|
||||
reg &= ~(0xf);
|
||||
reg |= (0x01);
|
||||
writel(reg, 0x11980040);
|
||||
} else {
|
||||
reg = readl(0x11980040);
|
||||
reg &= ~(0xf);
|
||||
reg |= (0x00);
|
||||
writel(reg, 0x11980040);
|
||||
//gpio3_0
|
||||
writel(0x01, 0x120b3400);
|
||||
writel(0x00, 0x120b3004);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
#include "lotus_sdhci.c"
|
||||
@@ -0,0 +1,21 @@
|
||||
menu "Flash Controller"
|
||||
|
||||
config FMC
|
||||
bool "Enable FMC - Flash Memory Controller"
|
||||
depends on TARGET_XMFALCON || TARGET_XMORCA
|
||||
help
|
||||
lotus Flash Memory Controller support SPI Nor SPI Nand often used on
|
||||
embedded chip. This option will provide the generic support for FMC drivers to register.
|
||||
|
||||
source "lotus/drivers/mtd/nand/Kconfig"
|
||||
|
||||
source "lotus/drivers/mtd/spi_nor/Kconfig"
|
||||
|
||||
config SHOW_FLASH_SPEED
|
||||
bool "Show Flash R/W Speed in flash commands"
|
||||
default n
|
||||
depends on FMC_SPI_NAND || FMC_SPI_NOR || FMC_NAND
|
||||
help
|
||||
Support for testing flash speed.
|
||||
|
||||
endmenu
|
||||
@@ -0,0 +1,6 @@
|
||||
|
||||
obj-$(CONFIG_FMC) += fmc_common.o
|
||||
obj-$(CONFIG_TARGET_XMFALCON) += fmc_xmfalcon.o
|
||||
obj-$(CONFIG_TARGET_XMORCA) += fmc_xmorca.o
|
||||
obj-y += nand/
|
||||
obj-y += spi_nor/
|
||||
@@ -0,0 +1,131 @@
|
||||
/*
|
||||
* Copyright (c) LOTUS. All rights reserved.
|
||||
*/
|
||||
|
||||
/*****************************************************************************/
|
||||
#include <linux/lotus/fmc_common.h>
|
||||
#include <common.h>
|
||||
#include <asm/io.h>
|
||||
#include <errno.h>
|
||||
|
||||
#define BUFF_LEN 20
|
||||
|
||||
/*****************************************************************************/
|
||||
static unsigned char fmc_current_dev_type = FLASH_TYPE_DEFAULT;
|
||||
static unsigned char fmc_boot_dev_type = FLASH_TYPE_DEFAULT;
|
||||
|
||||
/*****************************************************************************/
|
||||
unsigned char fmc_ip_user;
|
||||
unsigned char fmc_cs_user[CONFIG_FMC_MAX_CS_NUM];
|
||||
|
||||
/*****************************************************************************/
|
||||
void *get_fmc_ip(void)
|
||||
{
|
||||
return &fmc_ip_user;
|
||||
}
|
||||
/*****************************************************************************/
|
||||
unsigned char *get_cs_number(unsigned char cs)
|
||||
{
|
||||
return fmc_cs_user + cs;
|
||||
}
|
||||
/*****************************************************************************/
|
||||
int fmc_ip_ver_check(void)
|
||||
{
|
||||
unsigned int fmc_ip_ver;
|
||||
|
||||
printf("Check Flash Memory Controller v200 ...");
|
||||
fmc_ip_ver = readl(CONFIG_FMC_REG_BASE + FMC_VERSION);
|
||||
if (fmc_ip_ver != FMC_VER_100) {
|
||||
printf("\n");
|
||||
return -EFAULT;
|
||||
}
|
||||
printf(" Found\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
void fmc_dev_type_switch(unsigned char type)
|
||||
{
|
||||
unsigned int reg, spi_device_type, flash_type;
|
||||
const char *str[] = {"SPI nor", "SPI nand", "Nand", "Boot"};
|
||||
|
||||
if (fmc_current_dev_type == type)
|
||||
return;
|
||||
|
||||
fmc_pr(BT_DBG, "\t|*-Start switch current device type\n");
|
||||
|
||||
if (type > FLASH_TYPE_DEFAULT) {
|
||||
fmc_pr(BT_DBG, "\t||-Switch unknown device type %d\n", type);
|
||||
return;
|
||||
}
|
||||
|
||||
if (fmc_boot_dev_type == FLASH_TYPE_DEFAULT) {
|
||||
reg = readl((void *)(SYS_CTRL_REG_BASE + REG_SYSSTAT));
|
||||
fmc_pr(BT_DBG, "\t||-Get system STATUS[%#x]%#x\n",
|
||||
SYS_CTRL_REG_BASE + REG_SYSSTAT, reg);
|
||||
fmc_boot_dev_type = get_spi_device_type(reg);
|
||||
fmc_pr(BT_DBG, "\t||-Init boot device type to %s flash\n",
|
||||
str[fmc_boot_dev_type]);
|
||||
}
|
||||
|
||||
if (type == FLASH_TYPE_DEFAULT)
|
||||
spi_device_type = fmc_boot_dev_type;
|
||||
else
|
||||
spi_device_type = type;
|
||||
|
||||
fmc_pr(BT_DBG, "\t||-Switch type to %s flash\n", str[type]);
|
||||
|
||||
reg = readl((void *)(CONFIG_FMC_REG_BASE + FMC_CFG));
|
||||
fmc_pr(BT_DBG, "\t||-Get FMC CFG[%#x]%#x\n", FMC_CFG, reg);
|
||||
flash_type = (reg & FLASH_SEL_MASK) >> FLASH_SEL_SHIFT;
|
||||
if (spi_device_type != flash_type) {
|
||||
reg &= ~FLASH_SEL_MASK;
|
||||
reg |= fmc_cfg_flash_sel(spi_device_type);
|
||||
writel(reg, (void *)(CONFIG_FMC_REG_BASE + FMC_CFG));
|
||||
fmc_pr(BT_DBG, "\t||-Set FMC CFG[%#x]%#x\n", FMC_CFG, reg);
|
||||
}
|
||||
fmc_current_dev_type = spi_device_type;
|
||||
|
||||
fmc_pr(BT_DBG, "\t|*-End switch current device type\n");
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
char *ulltostr(unsigned long long size)
|
||||
{
|
||||
int ix;
|
||||
static char buffer[BUFF_LEN];
|
||||
char *fmt[] = {"%u", "%uK", "%uM", "%uG", "%uT"};
|
||||
/* 4 size type ,0x3ff Obtains the lower six bits*/
|
||||
for (ix = 0; (ix < 4) && !(size & 0x3FF) && size; ix++)
|
||||
size = (size >> 10); /* byte to kb, right left 10 */
|
||||
|
||||
sprintf(buffer, fmt[ix], size);
|
||||
return buffer;
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
void debug_register_dump(void)
|
||||
{
|
||||
unsigned int ix;
|
||||
unsigned long base = CONFIG_FMC_REG_BASE;
|
||||
|
||||
printf("Register dump:");
|
||||
/* 0x98 Register length , 4 is Byte */
|
||||
for (ix = 0; ix <= 0x98; ix += 0x04) {
|
||||
if (!(ix & 0x0F))
|
||||
printf("\n0x%08lX: ", base + ix);
|
||||
printf("%08X ", readl((void *)(uintptr_t)(base + ix)));
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
/*****************************************************************************/
|
||||
/* REG_SYSSTAT 0: 3 Bytes address boot mode; 1: 4Bytes address boot mode */
|
||||
unsigned int get_fmc_boot_mode(void)
|
||||
{
|
||||
unsigned int regval;
|
||||
unsigned int boot_mode;
|
||||
regval = readl(SYS_CTRL_REG_BASE + REG_SYSSTAT);
|
||||
boot_mode = get_spi_nor_addr_mode(regval);
|
||||
return boot_mode;
|
||||
}
|
||||
@@ -0,0 +1,447 @@
|
||||
/*
|
||||
* Copyright (c) LOTUS. All rights reserved.
|
||||
*/
|
||||
|
||||
#ifndef __FMC_SPI_IDS_H__
|
||||
#define __FMC_SPI_IDS_H__
|
||||
|
||||
#include <linux/lotus/fmc_common.h>
|
||||
/*****************************************************************************/
|
||||
#define INFINITE 0xFFFFFFFF
|
||||
|
||||
/*****************************************************************************/
|
||||
#define SPI_IF_READ_STD 0x01
|
||||
#define SPI_IF_READ_FAST 0x02
|
||||
#define SPI_IF_READ_DUAL 0x04
|
||||
#define SPI_IF_READ_DUAL_ADDR 0x08
|
||||
#define SPI_IF_READ_QUAD 0x10
|
||||
#define SPI_IF_READ_QUAD_ADDR 0x20
|
||||
#define SPI_IF_READ_QUAD_DTR 0x40
|
||||
|
||||
#define SPI_IF_WRITE_STD 0x01
|
||||
#define SPI_IF_WRITE_DUAL 0x02
|
||||
#define SPI_IF_WRITE_DUAL_ADDR 0x04
|
||||
#define SPI_IF_WRITE_QUAD 0x08
|
||||
#define SPI_IF_WRITE_QUAD_ADDR 0x10
|
||||
|
||||
#define SPI_IF_ERASE_SECTOR_4K 0x01
|
||||
#define SPI_IF_ERASE_SECTOR_32K 0x02
|
||||
#define SPI_IF_ERASE_SECTOR_64K 0x04
|
||||
#define SPI_IF_ERASE_SECTOR_128K 0x08
|
||||
#define SPI_IF_ERASE_SECTOR_256K 0x10
|
||||
|
||||
/*****************************************************************************/
|
||||
#define FMC_SPI_NOR_SUPPORT_READ (SPI_IF_READ_STD | \
|
||||
SPI_IF_READ_FAST | \
|
||||
SPI_IF_READ_DUAL | \
|
||||
SPI_IF_READ_DUAL_ADDR | \
|
||||
SPI_IF_READ_QUAD | \
|
||||
SPI_IF_READ_QUAD_ADDR | \
|
||||
SPI_IF_READ_QUAD_DTR)
|
||||
|
||||
#define FMC_SPI_NOR_SUPPORT_WRITE (SPI_IF_WRITE_STD | \
|
||||
SPI_IF_WRITE_DUAL | \
|
||||
SPI_IF_WRITE_DUAL_ADDR | \
|
||||
SPI_IF_WRITE_QUAD | \
|
||||
SPI_IF_WRITE_QUAD_ADDR)
|
||||
|
||||
#define FMC_SPI_NOR_STR_MAX_DUMMY 7
|
||||
#define FMC_SPI_NOR_DTR_MAX_DUMMY 12
|
||||
|
||||
/******************************************************************************/
|
||||
#define FMC_SPI_NAND_SUPPORT_READ (SPI_IF_READ_STD | \
|
||||
SPI_IF_READ_FAST | \
|
||||
SPI_IF_READ_DUAL | \
|
||||
SPI_IF_READ_DUAL_ADDR | \
|
||||
SPI_IF_READ_QUAD | \
|
||||
SPI_IF_READ_QUAD_ADDR)
|
||||
|
||||
#define FMC_SPI_NAND_SUPPORT_WRITE (SPI_IF_WRITE_STD | SPI_IF_WRITE_QUAD)
|
||||
|
||||
#define FMC_SPI_NAND_SUPPORT_MAX_DUMMY 8
|
||||
|
||||
/*****************************************************************************/
|
||||
#define SPI_CMD_READ_STD 0x03 /* Standard read cache */
|
||||
#define SPI_CMD_READ_STD4B 0x13 /* Standard read cache 4byte mode */
|
||||
#define SPI_CMD_READ_FAST 0x0B /* Higher speed read cache */
|
||||
#define SPI_CMD_READ_FAST4B 0x0C /* Higher speed read cache 4byte mode */
|
||||
#define SPI_CMD_READ_DUAL 0x3B /* 2 IO read cache only date */
|
||||
#define SPI_CMD_READ_DUAL4B 0x3C /* 2 IO read cache only date 4byte mode */
|
||||
#define SPI_CMD_READ_DUAL_ADDR 0xBB /* 2 IO read cache date&addr */
|
||||
#define SPI_CMD_READ_DUAL_ADDR4B 0xBC /* 2 IO read cache date&addr 4byte mode */
|
||||
#define SPI_CMD_READ_QUAD 0x6B /* 4 IO read cache only date */
|
||||
#define SPI_CMD_READ_QUAD4B 0x6C /* 4 IO read cache only date 4byte mode */
|
||||
#define SPI_CMD_READ_QUAD_ADDR 0xEB /* 4 IO read cache date&addr */
|
||||
#define SPI_CMD_READ_QUAD_ADDR4B 0xEC /* 4 IO read cache date&addr 4byte mode */
|
||||
#define SPI_CMD_READ_QUAD_DTR 0xED /* 4DTR MODE */
|
||||
#define SPI_CMD_READ_QUAD_DTR4B 0xEE /* 4DTR MODE 4byte mode */
|
||||
#define SPI_CMD_READ_QUAD_DTR4B_WINBOND 0xEC /* 4DTR MODE */
|
||||
|
||||
#define SPI_CMD_WRITE_STD 0x02 /* Standard page program */
|
||||
#define SPI_CMD_WRITE_STD4B 0x12 /* Standard page program 4byte mode */
|
||||
#define SPI_CMD_WRITE_DUAL 0xA2 /* 2 IO program only date */
|
||||
#define SPI_CMD_WRITE_DUAL4B 0xA2 /* 2 IO program only date 4byte mode */
|
||||
#define SPI_CMD_WRITE_DUAL_ADDR 0xD2 /* 2 IO program date&addr */
|
||||
#define SPI_CMD_WRITE_DUAL_ADDR4B 0xD2 /* 2 IO program date&addr 4byte mode */
|
||||
#define SPI_CMD_WRITE_QUAD 0x32 /* 4 IO program only date */
|
||||
#define SPI_CMD_WRITE_QUAD4B 0x34 /* 4 IO program only date 4byte mode */
|
||||
#define SPI_CMD_WRITE_QUAD_ADDR 0x38 /* 4 IO program date&addr */
|
||||
#define SPI_CMD_WRITE_QUAD_ADDR4B 0x3E /* 4 IO program date&addr 4byte mode */
|
||||
|
||||
#define SPI_CMD_SE_4K 0x20 /* 4KB sector Erase */
|
||||
#define SPI_CMD_SE_4K4B 0x21 /* 4KB sector Erase 4byte mode */
|
||||
#define SPI_CMD_SE_32K 0x52 /* 32KB sector Erase */
|
||||
#define SPI_CMD_SE_32K4B 0x5C /* 32KB sector Erase 4byte mode */
|
||||
#define SPI_CMD_SE_64K 0xD8 /* 64KB sector Erase */
|
||||
#define SPI_CMD_SE_64K4B 0xDC /* 64KB sector Erase 4byte mode */
|
||||
#define SPI_CMD_SE_128K 0xD8 /* 128KB sector Erase */
|
||||
#define SPI_CMD_SE_128K4B 0xD8 /* 128KB sector Erase 4byte mode */
|
||||
#define SPI_CMD_SE_256K 0xD8 /* 256KB sector Erase */
|
||||
#define SPI_CMD_SE_256K4B 0xD8 /* 256KB sector Erase 4byte mode */
|
||||
|
||||
/*****************************************************************************/
|
||||
#define set_read_std(_dummy_, _size_, _clk_) \
|
||||
static struct spi_op read_std_##_dummy_##_size_##_clk_ = { \
|
||||
SPI_IF_READ_STD, SPI_CMD_READ_STD, _dummy_, _size_, _clk_ }
|
||||
|
||||
#define set_read_std4b(_dummy_, _size_, _clk_) \
|
||||
static struct spi_op read_std4b_##_dummy_##_size_##_clk_ = { \
|
||||
SPI_IF_READ_STD, SPI_CMD_READ_STD4B, _dummy_, _size_, _clk_ }
|
||||
|
||||
#define set_read_fast(_dummy_, _size_, _clk_) \
|
||||
static struct spi_op read_fast_##_dummy_##_size_##_clk_ = { \
|
||||
SPI_IF_READ_FAST, SPI_CMD_READ_FAST, _dummy_, _size_, _clk_ }
|
||||
|
||||
#define set_read_fast4b(_dummy_, _size_, _clk_) \
|
||||
static struct spi_op read_fast4b_##_dummy_##_size_##_clk_ = { \
|
||||
SPI_IF_READ_FAST, SPI_CMD_READ_FAST4B, _dummy_, _size_, _clk_ }
|
||||
|
||||
#define set_read_dual(_dummy_, _size_, _clk_) \
|
||||
static struct spi_op read_dual_##_dummy_##_size_##_clk_ = { \
|
||||
SPI_IF_READ_DUAL, SPI_CMD_READ_DUAL, _dummy_, _size_, _clk_ }
|
||||
|
||||
#define set_read_dual4b(_dummy_, _size_, _clk_) \
|
||||
static struct spi_op read_dual4b_##_dummy_##_size_##_clk_ = { \
|
||||
SPI_IF_READ_DUAL, SPI_CMD_READ_DUAL4B, _dummy_, _size_, _clk_ }
|
||||
|
||||
#define set_read_dual_addr(_dummy_, _size_, _clk_) \
|
||||
static struct spi_op read_dual_addr_##_dummy_##_size_##_clk_ = { \
|
||||
SPI_IF_READ_DUAL_ADDR, SPI_CMD_READ_DUAL_ADDR, _dummy_, _size_, _clk_ }
|
||||
|
||||
#define set_read_dual_addr4b(_dummy_, _size_, _clk_) \
|
||||
static struct spi_op read_dual_addr4b_##_dummy_##_size_##_clk_ = { \
|
||||
SPI_IF_READ_DUAL_ADDR, SPI_CMD_READ_DUAL_ADDR4B, _dummy_, _size_, _clk_ }
|
||||
|
||||
#define set_read_quad(_dummy_, _size_, _clk_) \
|
||||
static struct spi_op read_quad_##_dummy_##_size_##_clk_ = { \
|
||||
SPI_IF_READ_QUAD, SPI_CMD_READ_QUAD, _dummy_, _size_, _clk_ }
|
||||
|
||||
#define set_read_quad4b(_dummy_, _size_, _clk_) \
|
||||
static struct spi_op read_quad4b_##_dummy_##_size_##_clk_ = { \
|
||||
SPI_IF_READ_QUAD, SPI_CMD_READ_QUAD4B, _dummy_, _size_, _clk_ }
|
||||
|
||||
#define set_read_quad_addr(_dummy_, _size_, _clk_) \
|
||||
static struct spi_op read_quad_addr_##_dummy_##_size_##_clk_ = { \
|
||||
SPI_IF_READ_QUAD_ADDR, SPI_CMD_READ_QUAD_ADDR, _dummy_, _size_, _clk_ }
|
||||
|
||||
#define set_read_quad_addr4b(_dummy_, _size_, _clk_) \
|
||||
static struct spi_op read_quad_addr4b_##_dummy_##_size_##_clk_ = { \
|
||||
SPI_IF_READ_QUAD_ADDR, SPI_CMD_READ_QUAD_ADDR4B, _dummy_, _size_, _clk_ }
|
||||
|
||||
#ifdef CONFIG_DTR_MODE_SUPPORT
|
||||
#define set_read_quad_dtr(_dummy_, _size_, _clk_) \
|
||||
static struct spi_op read_quad_dtr_##_dummy_##_size_##_clk_ = { \
|
||||
SPI_IF_READ_QUAD_DTR, SPI_CMD_READ_QUAD_DTR, _dummy_, _size_, _clk_ }
|
||||
|
||||
#define set_read_quad_dtr4b(_dummy_, _size_, _clk_) \
|
||||
static struct spi_op read_quad_dtr4b_##_dummy_##_size_##_clk_ = { \
|
||||
SPI_IF_READ_QUAD_DTR, SPI_CMD_READ_QUAD_DTR4B, _dummy_, _size_, _clk_ }
|
||||
|
||||
#define set_read_quad_dtr4b_winbond(_dummy_, _size_, _clk_) \
|
||||
static struct spi_op read_quad_dtr_winbond_##_dummy_##_size_##_clk_ = \
|
||||
{SPI_IF_READ_QUAD_DTR, SPI_CMD_READ_QUAD_DTR4B_WINBOND, \
|
||||
_dummy_, _size_, _clk_ }
|
||||
#endif
|
||||
|
||||
/*****************************************************************************/
|
||||
#define set_write_std(_dummy_, _size_, _clk_) \
|
||||
static struct spi_op write_std_##_dummy_##_size_##_clk_ = { \
|
||||
SPI_IF_WRITE_STD, SPI_CMD_WRITE_STD, _dummy_, _size_, _clk_ }
|
||||
|
||||
#define set_write_std4b(_dummy_, _size_, _clk_) \
|
||||
static struct spi_op write_std4b_##_dummy_##_size_##_clk_ = { \
|
||||
SPI_IF_WRITE_STD, SPI_CMD_WRITE_STD4B, _dummy_, _size_, _clk_ }
|
||||
|
||||
#define set_write_dual(_dummy_, _size_, _clk_) \
|
||||
static struct spi_op write_dual_##_dummy_##_size_##_clk_ = { \
|
||||
SPI_IF_WRITE_DUAL, SPI_CMD_WRITE_DUAL, _dummy_, _size_, _clk_ }
|
||||
|
||||
#define set_write_dual4b(_dummy_, _size_, _clk_) \
|
||||
static struct spi_op write_dual4b_##_dummy_##_size_##_clk_ = { \
|
||||
SPI_IF_WRITE_DUAL, SPI_CMD_WRITE_DUAL4B, _dummy_, _size_, _clk_ }
|
||||
|
||||
#define set_write_dual_addr(_dummy_, _size_, _clk_) \
|
||||
static struct spi_op write_dual_addr_##_dummy_##_size_##_clk_ = { \
|
||||
SPI_IF_WRITE_DUAL_ADDR, SPI_CMD_WRITE_DUAL_ADDR, _dummy_, _size_, _clk_ }
|
||||
|
||||
#define set_write_dual_addr4b(_dummy_, _size_, _clk_) \
|
||||
static struct spi_op write_dual_addr4b_##_dummy_##_size_##_clk_ = { \
|
||||
SPI_IF_WRITE_DUAL_ADDR, SPI_CMD_WRITE_DUAL_ADDR4B, _dummy_, _size_, _clk_ }
|
||||
|
||||
#define set_write_quad(_dummy_, _size_, _clk_) \
|
||||
static struct spi_op write_quad_##_dummy_##_size_##_clk_ = { \
|
||||
SPI_IF_WRITE_QUAD, SPI_CMD_WRITE_QUAD, _dummy_, _size_, _clk_ }
|
||||
|
||||
#define set_write_quad4b(_dummy_, _size_, _clk_) \
|
||||
static struct spi_op write_quad4b_##_dummy_##_size_##_clk_ = { \
|
||||
SPI_IF_WRITE_QUAD, SPI_CMD_WRITE_QUAD4B, _dummy_, _size_, _clk_ }
|
||||
|
||||
#define set_write_quad_addr(_dummy_, _size_, _clk_) \
|
||||
static struct spi_op write_quad_addr_##_dummy_##_size_##_clk_ = { \
|
||||
SPI_IF_WRITE_QUAD_ADDR, SPI_CMD_WRITE_QUAD_ADDR, _dummy_, _size_, _clk_ }
|
||||
|
||||
#define set_write_quad_addr4b(_dummy_, _size_, _clk_) \
|
||||
static struct spi_op write_quad_addr4b_##_dummy_##_size_##_clk_ = { \
|
||||
SPI_IF_WRITE_QUAD_ADDR, SPI_CMD_WRITE_QUAD_ADDR4B, _dummy_, _size_, _clk_ }
|
||||
|
||||
/*****************************************************************************/
|
||||
#define set_erase_sector_4k(_dummy_, _size_, _clk_) \
|
||||
static struct spi_op erase_sector_4k_##_dummy_##_size_##_clk_ = { \
|
||||
SPI_IF_ERASE_SECTOR_4K, SPI_CMD_SE_4K, _dummy_, _size_, _clk_ }
|
||||
|
||||
#define set_erase_sector_4k4b(_dummy_, _size_, _clk_) \
|
||||
static struct spi_op erase_sector_4k4b_##_dummy_##_size_##_clk_ = { \
|
||||
SPI_IF_ERASE_SECTOR_4K, SPI_CMD_SE_4K4B, _dummy_, _size_, _clk_ }
|
||||
|
||||
#define set_erase_sector_32k(_dummy_, _size_, _clk_) \
|
||||
static struct spi_op erase_sector_32k_##_dummy_##_size_##_clk_ = { \
|
||||
SPI_IF_ERASE_SECTOR_32K, SPI_CMD_SE_32K, _dummy_, _size_, _clk_ }
|
||||
|
||||
#define set_erase_sector_32k4b(_dummy_, _size_, _clk_) \
|
||||
static struct spi_op erase_sector_32k4b_##_dummy_##_size_##_clk_ = { \
|
||||
SPI_IF_ERASE_SECTOR_32K, SPI_CMD_SE_32K4B, _dummy_, _size_, _clk_ }
|
||||
|
||||
#define set_erase_sector_64k(_dummy_, _size_, _clk_) \
|
||||
static struct spi_op erase_sector_64k_##_dummy_##_size_##_clk_ = { \
|
||||
SPI_IF_ERASE_SECTOR_64K, SPI_CMD_SE_64K, _dummy_, _size_, _clk_ }
|
||||
|
||||
#define set_erase_sector_64k4b(_dummy_, _size_, _clk_) \
|
||||
static struct spi_op erase_sector_64k4b_##_dummy_##_size_##_clk_ = { \
|
||||
SPI_IF_ERASE_SECTOR_64K, SPI_CMD_SE_64K4B, _dummy_, _size_, _clk_ }
|
||||
|
||||
#define set_erase_sector_128k(_dummy_, _size_, _clk_) \
|
||||
static struct spi_op erase_sector_128k_##_dummy_##_size_##_clk_ = { \
|
||||
SPI_IF_ERASE_SECTOR_128K, SPI_CMD_SE_128K, _dummy_, _size_, _clk_ }
|
||||
|
||||
#define set_erase_sector_128k4b(_dummy_, _size_, _clk_) \
|
||||
static struct spi_op erase_sector_128k4b_##_dummy_##_size_##_clk_ = { \
|
||||
SPI_IF_ERASE_SECTOR_128K, SPI_CMD_SE_128K4B, _dummy_, _size_, _clk_ }
|
||||
|
||||
#define set_erase_sector_256k(_dummy_, _size_, _clk_) \
|
||||
static struct spi_op erase_sector_256k_##_dummy_##_size_##_clk_ = { \
|
||||
SPI_IF_ERASE_SECTOR_256K, SPI_CMD_SE_256K, _dummy_, _size_, _clk_ }
|
||||
|
||||
#define set_erase_sector_256k4b(_dummy_, _size_, _clk_) \
|
||||
static struct spi_op erase_sector_256k4b_##_dummy_##_size_##_clk_ = { \
|
||||
SPI_IF_ERASE_SECTOR_256K, SPI_CMD_SE_256K4B, _dummy_, _size_, _clk_ }
|
||||
|
||||
/*****************************************************************************/
|
||||
#define read_std(_dummy_, _size_, _clk_) read_std_##_dummy_##_size_##_clk_
|
||||
#define read_std4b(_dummy_, _size_, _clk_) read_std4b_##_dummy_##_size_##_clk_
|
||||
#define read_fast(_dummy_, _size_, _clk_) read_fast_##_dummy_##_size_##_clk_
|
||||
#define read_fast4b(_dummy_, _size_, _clk_) \
|
||||
read_fast4b_##_dummy_##_size_##_clk_
|
||||
#define read_dual(_dummy_, _size_, _clk_) read_dual_##_dummy_##_size_##_clk_
|
||||
#define read_dual4b(_dummy_, _size_, _clk_) \
|
||||
read_dual4b_##_dummy_##_size_##_clk_
|
||||
#define read_dual_addr(_dummy_, _size_, _clk_) \
|
||||
read_dual_addr_##_dummy_##_size_##_clk_
|
||||
#define read_dual_addr4b(_dummy_, _size_, _clk_) \
|
||||
read_dual_addr4b_##_dummy_##_size_##_clk_
|
||||
#define read_quad(_dummy_, _size_, _clk_) read_quad_##_dummy_##_size_##_clk_
|
||||
#define read_quad4b(_dummy_, _size_, _clk_) \
|
||||
read_quad4b_##_dummy_##_size_##_clk_
|
||||
#define read_quad_addr(_dummy_, _size_, _clk_) \
|
||||
read_quad_addr_##_dummy_##_size_##_clk_
|
||||
#define read_quad_addr4b(_dummy_, _size_, _clk_) \
|
||||
read_quad_addr4b_##_dummy_##_size_##_clk_
|
||||
#ifdef CONFIG_DTR_MODE_SUPPORT
|
||||
#define read_quad_dtr(_dummy_, _size_, _clk_) \
|
||||
read_quad_dtr_##_dummy_##_size_##_clk_
|
||||
#define read_quad_dtr4b(_dummy_, _size_, _clk_) \
|
||||
read_quad_dtr4b_##_dummy_##_size_##_clk_
|
||||
#define read_quad_dtr4b_winbond(_dummy_, _size_, _clk_) \
|
||||
read_quad_dtr4b_winbond_##_dummy_##_size_##_clk_
|
||||
#endif
|
||||
|
||||
/*****************************************************************************/
|
||||
#define write_std(_dummy_, _size_, _clk_) write_std_##_dummy_##_size_##_clk_
|
||||
#define write_std4b(_dummy_, _size_, _clk_) \
|
||||
write_std4b_##_dummy_##_size_##_clk_
|
||||
#define write_dual(_dummy_, _size_, _clk_) write_dual_##_dummy_##_size_##_clk_
|
||||
#define write_dual4b(_dummy_, _size_, _clk_) \
|
||||
write_dual4b_##_dummy_##_size_##_clk_
|
||||
#define write_dual_addr(_dummy_, _size_, _clk_) \
|
||||
write_dual_addr_##_dummy_##_size_##_clk_
|
||||
#define write_dual_addr4b(_dummy_, _size_, _clk_) \
|
||||
write_dual_addr4b_##_dummy_##_size_##_clk_
|
||||
#define write_quad(_dummy_, _size_, _clk_) write_quad_##_dummy_##_size_##_clk_
|
||||
#define write_quad4b(_dummy_, _size_, _clk_) \
|
||||
write_quad4b_##_dummy_##_size_##_clk_
|
||||
#define write_quad_addr(_dummy_, _size_, _clk_) \
|
||||
write_quad_addr_##_dummy_##_size_##_clk_
|
||||
#define write_quad_addr4b(_dummy_, _size_, _clk_) \
|
||||
write_quad_addr4b_##_dummy_##_size_##_clk_
|
||||
|
||||
/*****************************************************************************/
|
||||
#define erase_sector_4k(_dummy_, _size_, _clk_) \
|
||||
erase_sector_4k_##_dummy_##_size_##_clk_
|
||||
#define erase_sector_4k4b(_dummy_, _size_, _clk_) \
|
||||
erase_sector_4k4b_##_dummy_##_size_##_clk_
|
||||
#define erase_sector_32k(_dummy_, _size_, _clk_) \
|
||||
erase_sector_32k_##_dummy_##_size_##_clk_
|
||||
#define erase_sector_32k4b(_dummy_, _size_, _clk_) \
|
||||
erase_sector_32k4b_##_dummy_##_size_##_clk_
|
||||
#define erase_sector_64k(_dummy_, _size_, _clk_) \
|
||||
erase_sector_64k_##_dummy_##_size_##_clk_
|
||||
#define erase_sector_64k4b(_dummy_, _size_, _clk_) \
|
||||
erase_sector_64k4b_##_dummy_##_size_##_clk_
|
||||
#define erase_sector_128k(_dummy_, _size_, _clk_) \
|
||||
erase_sector_128k_##_dummy_##_size_##_clk_
|
||||
#define erase_sector_128k4b(_dummy_, _size_, _clk_) \
|
||||
erase_sector_128k4b_##_dummy_##_size_##_clk_
|
||||
#define erase_sector_256k(_dummy_, _size_, _clk_) \
|
||||
erase_sector_256k_##_dummy_##_size_##_clk_
|
||||
#define erase_sector_256k4b(_dummy_, _size_, _clk_) \
|
||||
erase_sector_256k4b_##_dummy_##_size_##_clk_
|
||||
|
||||
/*****************************************************************************/
|
||||
#define SPI_CMD_WREN 0x06 /* Write Enable */
|
||||
#define SPI_CMD_WRDI 0x04 /* Write Disable */
|
||||
|
||||
/*****************************************************************************/
|
||||
#define SPI_CMD_WRSR 0x01 /* Write Status Register */
|
||||
#define SPI_CMD_WRSR2 0x31 /* Write Status Register-2 */
|
||||
#define SPI_CMD_WRSR3 0x11 /* Write Status Register-3 */
|
||||
|
||||
#define SPI_CMD_RDSR 0x05 /* Read Status Register */
|
||||
#define SPI_CMD_RDSR2 0x35 /* Read Status Register-2 */
|
||||
#define SPI_CMD_RDSR3 0x15 /* Read Status Register-3 */
|
||||
|
||||
#define SPI_CMD_RDCR 0x35 /* Read Config Register */
|
||||
|
||||
#define SPI_CMD_RDID 0x9F /* Read Identification */
|
||||
|
||||
#define SPI_CMD_RD_SFDP 0x5A /* Read SFDP */
|
||||
/*****************************************************************************/
|
||||
#define SPI_CMD_GET_FEATURES 0x0F /* Get Features */
|
||||
#define SPI_CMD_SET_FEATURE 0x1F /* Set Feature */
|
||||
|
||||
#define SPI_CMD_PAGE_READ 0x13 /* Page Read to Cache */
|
||||
|
||||
#define SPI_CMD_RESET 0xff /* Reset the device */
|
||||
|
||||
/*****************************************************************************/
|
||||
#define SPI_CMD_EN4B 0xB7 /* enter 4 bytes mode and set 4 byte bit as '1' */
|
||||
#define SPI_CMD_EX4B 0xE9 /* exit 4 bytes mode and clear 4 byte bit */
|
||||
|
||||
/*****************************************************************************/
|
||||
#define MAX_SPI_OP 8
|
||||
|
||||
/*****************************************************************************/
|
||||
/* SPI general operation parameter */
|
||||
struct spi_op {
|
||||
unsigned char iftype;
|
||||
unsigned char cmd;
|
||||
unsigned char dummy;
|
||||
unsigned int size;
|
||||
unsigned int clock;
|
||||
};
|
||||
|
||||
struct spi_drv;
|
||||
|
||||
/* SPI interface all operation */
|
||||
struct fmc_spi {
|
||||
char *name;
|
||||
unsigned int chipselect;
|
||||
unsigned long long chipsize;
|
||||
unsigned int erasesize;
|
||||
#define SPI_NOR_3BYTE_ADDR_LEN 3 /* address len 3Bytes */
|
||||
#define SPI_NOR_4BYTE_ADDR_LEN 4 /* address len 4Bytes for 32MB */
|
||||
unsigned int addrcycle;
|
||||
|
||||
struct spi_op read[1];
|
||||
struct spi_op write[1];
|
||||
struct spi_op erase[MAX_SPI_OP];
|
||||
|
||||
void *host;
|
||||
|
||||
struct spi_drv *driver;
|
||||
#ifdef CONFIG_DTR_MODE_SUPPORT
|
||||
unsigned int dtr_mode_support;
|
||||
/* @dtr_cookie: Some device must set some registers when wants to
|
||||
* work on DTR mode, so this cookie tells us to set s.th */
|
||||
unsigned int dtr_cookie;
|
||||
#define DTR_MODE_SET_NONE 0x0 /* Need not set anything */
|
||||
#define DTR_MODE_SET_ODS 0x1 /* Need to set output driver strength */
|
||||
#endif
|
||||
};
|
||||
|
||||
/* SPI interface special operation function hook */
|
||||
struct spi_drv {
|
||||
int (*wait_ready)(struct fmc_spi *spi);
|
||||
int (*write_enable)(struct fmc_spi *spi);
|
||||
int (*qe_enable)(struct fmc_spi *spi);
|
||||
int (*bus_prepare)(struct fmc_spi *spi, int op);
|
||||
int (*entry_4addr)(struct fmc_spi *spi, int en);
|
||||
#ifdef CONFIG_DTR_MODE_SUPPORT
|
||||
int (*dtr_set_device)(struct fmc_spi *spi, int dtr_en);
|
||||
#endif
|
||||
};
|
||||
#ifndef MAX_SPI_NAND_ID_LEN
|
||||
#define MAX_SPI_NAND_ID_LEN 1
|
||||
#endif
|
||||
struct spi_nand_info {
|
||||
char *name;
|
||||
unsigned char id[MAX_SPI_NAND_ID_LEN];
|
||||
unsigned char id_len;
|
||||
unsigned long long chipsize;
|
||||
unsigned int erasesize;
|
||||
unsigned int pagesize;
|
||||
unsigned int oobsize;
|
||||
#define BBP_LAST_PAGE 0x01
|
||||
#define BBP_FIRST_PAGE 0x02
|
||||
unsigned int badblock_pos;
|
||||
struct spi_op *read[MAX_SPI_OP];
|
||||
struct spi_op *write[MAX_SPI_OP];
|
||||
struct spi_op *erase[MAX_SPI_OP];
|
||||
struct spi_drv *driver;
|
||||
};
|
||||
|
||||
#ifndef MAX_SPI_NOR_ID_LEN
|
||||
#define MAX_SPI_NOR_ID_LEN 8
|
||||
#endif
|
||||
|
||||
struct spi_nor_info {
|
||||
char *name;
|
||||
unsigned char id[MAX_SPI_NOR_ID_LEN];
|
||||
unsigned int id_len;
|
||||
unsigned long chipsize;
|
||||
unsigned int erasesize;
|
||||
unsigned int addrcycle;
|
||||
struct spi_op *read[MAX_SPI_OP];
|
||||
struct spi_op *write[MAX_SPI_OP];
|
||||
struct spi_op *erase[MAX_SPI_OP];
|
||||
struct spi_drv *driver;
|
||||
};
|
||||
|
||||
/*****************************************************************************/
|
||||
void fmc_set_fmc_system_clock(struct spi_op *op, int clk_en);
|
||||
|
||||
void fmc_get_fmc_best_2x_clock(unsigned int *clock);
|
||||
#ifdef CONFIG_DTR_MODE_SUPPORT
|
||||
void fmc_get_fmc_best_4x_clock(unsigned int *clock);
|
||||
#endif
|
||||
/*****************************************************************************/
|
||||
|
||||
#endif /* End of __FMC_SPI_IDS_H__ */
|
||||
@@ -0,0 +1,128 @@
|
||||
/*
|
||||
* Copyright (c) LOTUS. All rights reserved.
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <asm/io.h>
|
||||
#include <asm/arch/platform.h>
|
||||
#include <linux/lotus/fmc_common.h>
|
||||
|
||||
#include "fmc_spi_ids.h"
|
||||
|
||||
#define REG_IO_BASE 0x100c0000
|
||||
static void xmfalcon_io_config_spi_sfc(void)
|
||||
{
|
||||
/* set pad ctrl reg for spi */
|
||||
//writel(0x401, REG_IO_BASE + 0x14); /* sfc_clk */
|
||||
//writel(0x461, REG_IO_BASE + 0x18); /* sfc_hold_io0 */
|
||||
//writel(0x461, REG_IO_BASE + 0x1c); /* sfc_miso_io1 */
|
||||
//writel(0x461, REG_IO_BASE + 0x20); /* sfc_wp_io2 */
|
||||
//writel(0x461, REG_IO_BASE + 0x24); /* sfc_mosi_io3 */
|
||||
//writel(0x461, REG_IO_BASE + 0x28); /* sfc_csn */
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
void fmc_set_fmc_system_clock(struct spi_op *op, int clk_en)
|
||||
{
|
||||
unsigned int old_val;
|
||||
unsigned int regval;
|
||||
|
||||
old_val = regval = readl(CRG_REG_BASE + REG_FMC_CRG);
|
||||
|
||||
regval &= ~FMC_CLK_SEL_MASK;
|
||||
|
||||
if (op && op->clock) {
|
||||
regval |= op->clock & FMC_CLK_SEL_MASK;
|
||||
fmc_pr(DTR_DB, "\t|||*-get the setting clock value: %#x\n",
|
||||
op->clock);
|
||||
} else {
|
||||
regval |= fmc_clk_sel(FMC_CLK_24M); /* Default Clock */
|
||||
xmfalcon_io_config_spi_sfc();
|
||||
}
|
||||
if (clk_en)
|
||||
regval |= FMC_CLK_ENABLE;
|
||||
else
|
||||
regval &= ~FMC_CLK_ENABLE;
|
||||
|
||||
if (regval != old_val) {
|
||||
fmc_pr(DTR_DB, "\t|||*-setting system clock [%#x]%#x\n",
|
||||
REG_FMC_CRG, regval);
|
||||
writel(regval, (CRG_REG_BASE + REG_FMC_CRG));
|
||||
}
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
void fmc_get_fmc_best_2x_clock(unsigned int *clock)
|
||||
{
|
||||
int ix;
|
||||
unsigned int clk_reg;
|
||||
unsigned int clk_type;
|
||||
const char *str[] = {"12", "50", "75", "100"};
|
||||
|
||||
unsigned int sys_2x_clk[] = {
|
||||
clk_2x(24), fmc_clk_sel(FMC_CLK_24M),
|
||||
clk_2x(100), fmc_clk_sel(FMC_CLK_100M),
|
||||
clk_2x(150), fmc_clk_sel(FMC_CLK_150M),
|
||||
clk_2x(200), fmc_clk_sel(FMC_CLK_200M),
|
||||
0, 0,
|
||||
};
|
||||
if (!clock)
|
||||
return;
|
||||
clk_type = FMC_CLK_24M;
|
||||
clk_reg = fmc_clk_sel(clk_type);
|
||||
fmc_pr(QE_DBG, "\t|||*-matching flash clock %d\n", *clock);
|
||||
for (ix = 0; (sys_2x_clk[ix] && ((ix + 1) < sizeof(sys_2x_clk)));
|
||||
ix += _2B) {
|
||||
if (*clock < sys_2x_clk[ix])
|
||||
break;
|
||||
clk_reg = sys_2x_clk[ix + 1];
|
||||
clk_type = get_fmc_clk_type(clk_reg);
|
||||
fmc_pr(QE_DBG, "\t||||-select system clock: %sMHz\n",
|
||||
str[clk_type]);
|
||||
}
|
||||
#ifdef CONFIG_DTR_MODE_SUPPORT
|
||||
fmc_pr(DTR_DB, "best system clock for SDR.\n");
|
||||
#endif
|
||||
fmc_pr(QE_DBG, "\t|||*-matched best system clock: %sMHz\n",
|
||||
str[clk_type]);
|
||||
*clock = clk_reg;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_DTR_MODE_SUPPORT
|
||||
/*****************************************************************************/
|
||||
void fmc_get_fmc_best_4x_clock(unsigned int *clock)
|
||||
{
|
||||
int ix;
|
||||
unsigned int clk_reg;
|
||||
unsigned int clk_type;
|
||||
char *const str[] = {"6", "25", "37.5", "50", "75", "90"};
|
||||
|
||||
unsigned int sys_4x_clk[] = {
|
||||
clk_4x(24), fmc_clk_sel(FMC_CLK_24M),
|
||||
clk_4x(100), fmc_clk_sel(FMC_CLK_100M),
|
||||
clk_4x(150), fmc_clk_sel(FMC_CLK_150M),
|
||||
clk_4x(200), fmc_clk_sel(FMC_CLK_200M),
|
||||
clk_4x(300), fmc_clk_sel(FMC_CLK_300M),
|
||||
clk_4x(360), fmc_clk_sel(FMC_CLK_360M),
|
||||
0, 0,
|
||||
};
|
||||
if (!clock)
|
||||
return;
|
||||
clk_type = FMC_CLK_24M;
|
||||
clk_reg = fmc_clk_sel(clk_type);
|
||||
fmc_pr(DTR_DB, "\t|||*-matching flash clock %d\n", *clock);
|
||||
for (ix = 0; (sys_4x_clk[ix] && ((ix + 1) < sizeof(sys_4x_clk))); ix += _2B) {
|
||||
if (*clock < sys_4x_clk[ix])
|
||||
break;
|
||||
clk_reg = sys_4x_clk[ix + 1];
|
||||
clk_type = get_fmc_clk_type(clk_reg);
|
||||
fmc_pr(DTR_DB, "\t||||-select system clock: %sMHz\n",
|
||||
str[clk_type]);
|
||||
}
|
||||
fmc_pr(DTR_DB, "best system clock for DTR.\n");
|
||||
fmc_pr(DTR_DB, "\t|||*-matched best system clock: %sMHz\n",
|
||||
str[clk_type]);
|
||||
*clock = clk_reg;
|
||||
}
|
||||
/*****************************************************************************/
|
||||
#endif/* CONFIG_DTR_MODE_SUPPORT */
|
||||
@@ -0,0 +1,127 @@
|
||||
/*
|
||||
* Copyright (c) LOTUS. All rights reserved.
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <asm/io.h>
|
||||
#include <asm/arch/platform.h>
|
||||
#include <linux/lotus/fmc_common.h>
|
||||
|
||||
#include "fmc_spi_ids.h"
|
||||
|
||||
#define REG_IO_BASE 0x100c0000
|
||||
static void xmorca_io_config_spi_sfc(void)
|
||||
{
|
||||
/* set pad ctrl reg for spi */
|
||||
//writel(0x401, REG_IO_BASE + 0x14); /* sfc_clk */
|
||||
//writel(0x461, REG_IO_BASE + 0x18); /* sfc_hold_io0 */
|
||||
//writel(0x461, REG_IO_BASE + 0x1c); /* sfc_miso_io1 */
|
||||
//writel(0x461, REG_IO_BASE + 0x20); /* sfc_wp_io2 */
|
||||
//writel(0x461, REG_IO_BASE + 0x24); /* sfc_mosi_io3 */
|
||||
//writel(0x461, REG_IO_BASE + 0x28); /* sfc_csn */
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
void fmc_set_fmc_system_clock(struct spi_op *op, int clk_en)
|
||||
{
|
||||
unsigned int old_val;
|
||||
unsigned int regval;
|
||||
|
||||
old_val = regval = readl(CRG_REG_BASE + REG_FMC_CRG);
|
||||
|
||||
regval &= ~FMC_CLK_SEL_MASK;
|
||||
|
||||
if (op && op->clock) {
|
||||
regval |= op->clock & FMC_CLK_SEL_MASK;
|
||||
fmc_pr(DTR_DB, "\t|||*-get the setting clock value: %#x\n",
|
||||
op->clock);
|
||||
} else {
|
||||
regval |= fmc_clk_sel(FMC_CLK_24M); /* Default Clock */
|
||||
xmorca_io_config_spi_sfc();
|
||||
}
|
||||
if (clk_en)
|
||||
regval |= FMC_CLK_ENABLE;
|
||||
else
|
||||
regval &= ~FMC_CLK_ENABLE;
|
||||
|
||||
if (regval != old_val) {
|
||||
fmc_pr(DTR_DB, "\t|||*-setting system clock [%#x]%#x\n",
|
||||
REG_FMC_CRG, regval);
|
||||
writel(regval, (CRG_REG_BASE + REG_FMC_CRG));
|
||||
}
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
void fmc_get_fmc_best_2x_clock(unsigned int *clock)
|
||||
{
|
||||
int ix;
|
||||
unsigned int clk_reg;
|
||||
unsigned int clk_type;
|
||||
const char *str[] = {"12","NULL", "NULL", "100", "75", "50"};
|
||||
|
||||
unsigned int sys_2x_clk[] = {
|
||||
clk_2x(24), fmc_clk_sel(FMC_CLK_24M),
|
||||
clk_2x(100), fmc_clk_sel(FMC_CLK_100M),
|
||||
clk_2x(150), fmc_clk_sel(FMC_CLK_150M),
|
||||
clk_2x(200), fmc_clk_sel(FMC_CLK_200M),
|
||||
0, 0,
|
||||
};
|
||||
if (!clock)
|
||||
return;
|
||||
clk_type = FMC_CLK_24M;
|
||||
clk_reg = fmc_clk_sel(clk_type);
|
||||
fmc_pr(QE_DBG, "\t|||*-matching flash clock %d\n", *clock);
|
||||
for (ix = 0; (sys_2x_clk[ix] && ((ix + 1) < sizeof(sys_2x_clk)));
|
||||
ix += _2B) {
|
||||
if (*clock < sys_2x_clk[ix])
|
||||
break;
|
||||
clk_reg = sys_2x_clk[ix + 1];
|
||||
clk_type = get_fmc_clk_type(clk_reg);
|
||||
fmc_pr(QE_DBG, "\t||||-select system clock: %sMHz\n",
|
||||
str[clk_type]);
|
||||
}
|
||||
#ifdef CONFIG_DTR_MODE_SUPPORT
|
||||
fmc_pr(DTR_DB, "best system clock for SDR.\n");
|
||||
#endif
|
||||
fmc_pr(QE_DBG, "\t|||*-matched best system clock: %sMHz\n",
|
||||
str[clk_type]);
|
||||
*clock = clk_reg;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_DTR_MODE_SUPPORT
|
||||
/*****************************************************************************/
|
||||
void fmc_get_fmc_best_4x_clock(unsigned int *clock)
|
||||
{
|
||||
int ix;
|
||||
unsigned int clk_reg;
|
||||
unsigned int clk_type;
|
||||
char *const str[] = {"6", "NULL", "75", "50", "37.5", "25"};
|
||||
|
||||
unsigned int sys_4x_clk[] = {
|
||||
clk_4x(24), fmc_clk_sel(FMC_CLK_24M),
|
||||
clk_4x(100), fmc_clk_sel(FMC_CLK_100M),
|
||||
clk_4x(150), fmc_clk_sel(FMC_CLK_150M),
|
||||
clk_4x(200), fmc_clk_sel(FMC_CLK_200M),
|
||||
clk_4x(300), fmc_clk_sel(FMC_CLK_300M),
|
||||
0, 0,
|
||||
};
|
||||
if (!clock)
|
||||
return;
|
||||
clk_type = FMC_CLK_24M;
|
||||
clk_reg = fmc_clk_sel(clk_type);
|
||||
fmc_pr(DTR_DB, "\t|||*-matching flash clock %d\n", *clock);
|
||||
for (ix = 0; (sys_4x_clk[ix] && ((ix + 1) < sizeof(sys_4x_clk))); ix += _2B) {
|
||||
if (*clock < sys_4x_clk[ix])
|
||||
break;
|
||||
clk_reg = sys_4x_clk[ix + 1];
|
||||
clk_type = get_fmc_clk_type(clk_reg);
|
||||
fmc_pr(DTR_DB, "\t||||-select system clock: %sMHz\n",
|
||||
str[clk_type]);
|
||||
}
|
||||
fmc_pr(DTR_DB, "best system clock for DTR.\n");
|
||||
fmc_pr(DTR_DB, "\t|||*-matched best system clock: %sMHz\n",
|
||||
str[clk_type]);
|
||||
*clock = clk_reg;
|
||||
}
|
||||
/*****************************************************************************/
|
||||
#endif/* CONFIG_DTR_MODE_SUPPORT */
|
||||
@@ -0,0 +1,56 @@
|
||||
|
||||
config FMC_SPI_NAND
|
||||
bool "lotus SPI Nand Flash Interface support"
|
||||
depends on FMC
|
||||
help
|
||||
Enable the lotus SPI Nand flash support.
|
||||
|
||||
If unsure, say N
|
||||
|
||||
config SPI_NAND_MAX_CHIP_NUM
|
||||
int "Support max number of SPI Nand flash chip (1, 2)"
|
||||
depends on FMC_SPI_NAND
|
||||
default 1
|
||||
help
|
||||
flash memory controller v100 device only support 1 or 2 SPI nand flash
|
||||
chip, your should not config other value.
|
||||
|
||||
config NAND_MAX_CHIP_NUM
|
||||
int "Support max number of Nand flash chip (1, 2)"
|
||||
depends on FMC_NAND
|
||||
default 1
|
||||
help
|
||||
flash memory controller v100 device only support 1 or 2 nand flash
|
||||
chip, your should not config other value.
|
||||
|
||||
if FMC_SPI_NAND || FMC_NAND
|
||||
|
||||
choice
|
||||
prompt "Page Size and Ecc Type Select"
|
||||
default FMC100_AUTO_PAGESIZE_ECC
|
||||
|
||||
config FMC100_HARDWARE_PAGESIZE_ECC
|
||||
bool "Hardware"
|
||||
help
|
||||
the configure of page size and ecc type lie on switch on the board.
|
||||
so the page size and ecc type is controlled by Hardware see demo
|
||||
board of SOC.
|
||||
|
||||
config FMC100_AUTO_PAGESIZE_ECC
|
||||
bool "Auto"
|
||||
help
|
||||
auto-sensed the page size and ecc type value. driver will try each of
|
||||
page size and ecc type one by one till flash can be read and wrote
|
||||
accurately. so the page size and ecc type is match adaptively without
|
||||
switch on the board
|
||||
|
||||
config FMC100_PAGESIZE_AUTO_ECC_NONE
|
||||
bool "Pagesize Auto, Ecc None"
|
||||
help
|
||||
auto-sensed the page size and select ecc none. driver will try each
|
||||
of page size one by one till flash can be read and wrote accurately.
|
||||
so the page size is match adaptively without switch on the board
|
||||
|
||||
endchoice
|
||||
|
||||
endif
|
||||
@@ -0,0 +1,5 @@
|
||||
|
||||
obj-$(CONFIG_CMD_NAND) += nfc_common.o
|
||||
obj-$(CONFIG_CMD_NAND) +=match_table.o
|
||||
|
||||
obj-$(CONFIG_FMC_SPI_NAND) += fmc100/
|
||||
@@ -0,0 +1,4 @@
|
||||
|
||||
ccflags-y += -I$(srctree)/lotus/drivers/mtd
|
||||
|
||||
obj-y += fmc100.o fmc100_os.o fmc_spi_nand_ids.o
|
||||
@@ -0,0 +1,965 @@
|
||||
/*
|
||||
* Copyright (c) LOTUS. All rights reserved.
|
||||
*/
|
||||
|
||||
#include "fmc100.h"
|
||||
#include <common.h>
|
||||
#include <asm/io.h>
|
||||
#include <errno.h>
|
||||
#include <malloc.h>
|
||||
#include <linux/lotus/match_table.h>
|
||||
#include <asm/arch/platform.h>
|
||||
|
||||
void fmc100_ecc0_switch(struct fmc_host* const host, unsigned char op)
|
||||
{
|
||||
unsigned int config;
|
||||
#if EC_DBG
|
||||
unsigned int cmp_cfg;
|
||||
|
||||
config = fmc_read(host, FMC_CFG);
|
||||
fmc_pr(EC_DBG, "\t *-Get CFG[%#x]%#x\n", FMC_CFG, config);
|
||||
|
||||
if (op)
|
||||
cmp_cfg = host->fmc_cfg;
|
||||
else
|
||||
cmp_cfg = host->fmc_cfg_ecc0;
|
||||
|
||||
if (cmp_cfg != config)
|
||||
db_msg("Warning: FMC config[%#x] is different.\n",
|
||||
cmp_cfg);
|
||||
#endif
|
||||
|
||||
if (op == ENABLE) {
|
||||
config = host->fmc_cfg_ecc0;
|
||||
} else if (op == DISABLE) {
|
||||
config = host->fmc_cfg;
|
||||
} else {
|
||||
db_msg("Error: Invalid opcode: %d\n", op);
|
||||
return;
|
||||
}
|
||||
|
||||
fmc_write(host, FMC_CFG, config);
|
||||
fmc_pr(EC_DBG, "\t *-Set CFG[%#x]%#x\n", FMC_CFG, config);
|
||||
}
|
||||
|
||||
static void set_dma_addr_reg(struct fmc_host *host)
|
||||
{
|
||||
unsigned int reg;
|
||||
|
||||
reg = host->dma_buffer;
|
||||
fmc_write(host, FMC_DMA_SADDR_D0, reg);
|
||||
fmc_pr(DMA_DB, "\t|-Set DMA_SADDR_D0[%#x]%#x\n", FMC_DMA_SADDR_D0, reg);
|
||||
/* get hight 32 bits */
|
||||
reg = ((unsigned long)host->dma_buffer & FMC_DMA_SADDRH_MASK) >> 32;
|
||||
fmc_write(host, FMC_DMA_SADDRH_D0, reg);
|
||||
fmc_pr(DMA_DB, "\t|-Set DMA_SADDRH_D0[%#x]%#x\n", FMC_DMA_SADDRH_D0, reg);
|
||||
reg = host->dma_oob;
|
||||
fmc_write(host, FMC_DMA_SADDR_OOB, reg);
|
||||
fmc_pr(DMA_DB, "\t|-Set DMA_SADDR_OOB[%#x]%#x\n", FMC_DMA_SADDR_OOB,
|
||||
reg);
|
||||
/* get hight 32 bits */
|
||||
reg = ((unsigned long)host->dma_oob & FMC_DMA_SADDRH_MASK) >> 32;
|
||||
fmc_write(host, FMC_DMA_SADDRH_OOB, reg);
|
||||
fmc_pr(DMA_DB, "\t|-Set DMA_SADDRH_OOB[%#x]%#x\n", FMC_DMA_SADDRH_OOB,
|
||||
reg);
|
||||
}
|
||||
|
||||
static void set_addr_reg(struct fmc_host *host)
|
||||
{
|
||||
unsigned int reg;
|
||||
struct nand_chip *chip = host->chip;
|
||||
unsigned char pages_per_block_shift;
|
||||
unsigned int block_num;
|
||||
unsigned int block_num_h;
|
||||
unsigned int page_num;
|
||||
|
||||
pages_per_block_shift = chip->phys_erase_shift - chip->page_shift;
|
||||
block_num = host->addr_value[1] >> pages_per_block_shift;
|
||||
block_num_h = block_num >> REG_CNT_HIGH_BLOCK_NUM_SHIFT;
|
||||
reg = fmc_addrh_set(block_num_h);
|
||||
fmc_write(host, FMC_ADDRH, reg);
|
||||
fmc_pr(REG_DB, "|-Set ADDRH[%#x]%#x\n", FMC_ADDRH, reg);
|
||||
|
||||
page_num = host->addr_value[1] - (block_num << pages_per_block_shift);
|
||||
reg = ((block_num & REG_CNT_BLOCK_NUM_MASK) << REG_CNT_BLOCK_NUM_SHIFT) |
|
||||
((page_num & REG_CNT_PAGE_NUM_MASK) << REG_CNT_PAGE_NUM_SHIFT);
|
||||
fmc_write(host, FMC_ADDRL, reg);
|
||||
fmc_pr(REG_DB, "|-Set ADDRL[%#x]%#x\n", FMC_ADDRL, reg);
|
||||
}
|
||||
|
||||
/****************************************************************************/
|
||||
static void set_cs_addr_reg(enum OP op, struct fmc_host *host)
|
||||
{
|
||||
unsigned int reg;
|
||||
struct fmc_spi *spi = host->spi;
|
||||
unsigned char iftype = 0;
|
||||
unsigned char dummy = 0;
|
||||
|
||||
reg = FMC_INT_CLR_ALL;
|
||||
fmc_write(host, FMC_INT_CLR, reg);
|
||||
fmc_pr(WR_DBG, "|-Set INT_CLR[%#x]%#x\n", FMC_INT_CLR, reg);
|
||||
|
||||
if (op == READ) {
|
||||
iftype = spi->read->iftype;
|
||||
dummy = spi->read->dummy;
|
||||
} else if (op == WRITE) {
|
||||
iftype = spi->write->iftype;
|
||||
} else {
|
||||
iftype = spi->erase->iftype;
|
||||
}
|
||||
|
||||
reg = op_cfg_fm_cs(host->cmd_op.cs) |
|
||||
OP_CFG_OEN_EN |
|
||||
op_cfg_mem_if_type(iftype) |
|
||||
op_cfg_dummy_num(dummy);
|
||||
fmc_write(host, FMC_OP_CFG, reg);
|
||||
fmc_pr(REG_DB, "|-Set OP_CFG[%#x]%#x\n", FMC_OP_CFG, reg);
|
||||
|
||||
set_addr_reg(host);
|
||||
}
|
||||
|
||||
static void pageprog_fmc_op(struct fmc_host* const host, struct fmc_spi* const spi)
|
||||
{
|
||||
unsigned int reg;
|
||||
|
||||
reg = op_ctrl_wr_opcode(spi->write->cmd) | op_ctrl_dma_op(OP_TYPE_DMA) |
|
||||
op_ctrl_rw_op(RW_OP_WRITE) | OP_CTRL_DMA_OP_READY;
|
||||
fmc_write(host, FMC_OP_CTRL, reg);
|
||||
fmc_pr(WR_DBG, "|-Set OP_CTRL[%#x]%#x\n", FMC_OP_CTRL, reg);
|
||||
fmc_dma_wait_int_finish(host);
|
||||
}
|
||||
|
||||
static void fmc100_send_cmd_pageprog(struct fmc_host *host)
|
||||
{
|
||||
struct fmc_spi *spi = host->spi;
|
||||
unsigned char *fmc_ip = NULL;
|
||||
int ret;
|
||||
#ifndef CONFIG_SYS_DCACHE_OFF
|
||||
unsigned int dma_align_len;
|
||||
#endif
|
||||
fmc_pr(WR_DBG, "\n*-Enter Dma page program!\n");
|
||||
fmc_ip = get_fmc_ip();
|
||||
if (*fmc_ip) {
|
||||
printf("Warning: FMC IP is busy, Please try again.\n");
|
||||
udelay(1); /* delay 1 us */
|
||||
return;
|
||||
} else {
|
||||
fmc_dev_type_switch(FLASH_TYPE_SPI_NAND);
|
||||
(*fmc_ip)++;
|
||||
}
|
||||
|
||||
ret = spi->driver->write_enable(spi);
|
||||
if (ret) {
|
||||
db_msg("Error: Dma program write enable failed! ret: %#x\n", ret);
|
||||
goto end;
|
||||
}
|
||||
|
||||
host->set_system_clock(spi->write, ENABLE);
|
||||
|
||||
if (ecc0_flag == 1) {
|
||||
fmc100_ecc0_switch(host, ENABLE);
|
||||
fmc_write(host, FMC_DMA_LEN, host->oobsize);
|
||||
}
|
||||
|
||||
set_cs_addr_reg(WRITE, host);
|
||||
|
||||
if (ecc0_flag != 1)
|
||||
*host->epm = 0x0000;
|
||||
|
||||
#ifndef CONFIG_SYS_DCACHE_OFF
|
||||
dma_align_len = ((host->pagesize + host->oobsize +
|
||||
CONFIG_SYS_CACHELINE_SIZE - 1) & ~(CONFIG_SYS_CACHELINE_SIZE - 1));
|
||||
flush_dcache_range(host->dma_buffer, host->dma_buffer + dma_align_len);
|
||||
#endif
|
||||
set_dma_addr_reg(host);
|
||||
|
||||
pageprog_fmc_op(host, spi);
|
||||
|
||||
if (ecc0_flag == 1)
|
||||
fmc100_ecc0_switch(host, DISABLE);
|
||||
ret = spi->driver->wait_ready(spi);
|
||||
if (ret)
|
||||
db_msg("Error: Dma program wait ready failed! status: %#x\n", ret);
|
||||
end:
|
||||
(*fmc_ip)--;
|
||||
fmc_pr(WR_DBG, "*-End Dma page program!\n");
|
||||
}
|
||||
|
||||
static void fmc100_send_cmd_readstart(struct fmc_host *host)
|
||||
{
|
||||
unsigned int reg;
|
||||
struct fmc_spi *spi = host->spi;
|
||||
unsigned char *fmc_ip = NULL;
|
||||
#ifndef CONFIG_SYS_DCACHE_OFF
|
||||
unsigned int dma_align_len;
|
||||
#endif
|
||||
fmc_pr(RD_DBG, "\n\t*-Start Dma page read\n");
|
||||
|
||||
fmc_ip = get_fmc_ip();
|
||||
if (*fmc_ip) {
|
||||
printf("Warning: FMC IP is busy, Please try again.\n");
|
||||
udelay(1); /* delay 1 us */
|
||||
return;
|
||||
} else {
|
||||
fmc_dev_type_switch(FLASH_TYPE_SPI_NAND);
|
||||
(*fmc_ip)++;
|
||||
}
|
||||
|
||||
host->set_system_clock(spi->read, ENABLE);
|
||||
|
||||
if (ecc0_flag == 1 && (host->cmd_op.l_cmd != NAND_CMD_READOOB)) {
|
||||
fmc100_ecc0_switch(host, ENABLE);
|
||||
fmc_write(host, FMC_DMA_LEN, host->oobsize);
|
||||
}
|
||||
|
||||
if (host->cmd_op.l_cmd == NAND_CMD_READOOB)
|
||||
host->cmd_op.op_cfg = op_ctrl_rd_op_sel(RD_OP_READ_OOB);
|
||||
else
|
||||
host->cmd_op.op_cfg = op_ctrl_rd_op_sel(RD_OP_READ_ALL_PAGE);
|
||||
|
||||
set_cs_addr_reg(READ, host);
|
||||
|
||||
#ifndef CONFIG_SYS_DCACHE_OFF
|
||||
dma_align_len = ((host->pagesize + host->oobsize +
|
||||
CONFIG_SYS_CACHELINE_SIZE - 1) & ~(CONFIG_SYS_CACHELINE_SIZE - 1));
|
||||
invalidate_dcache_range(host->dma_buffer, host->dma_buffer + dma_align_len);
|
||||
#endif
|
||||
set_dma_addr_reg(host);
|
||||
|
||||
reg = op_ctrl_rd_opcode(spi->read->cmd) |
|
||||
host->cmd_op.op_cfg | op_ctrl_dma_op(OP_TYPE_DMA) |
|
||||
op_ctrl_rw_op(RW_OP_READ) | OP_CTRL_DMA_OP_READY;
|
||||
fmc_write(host, FMC_OP_CTRL, reg);
|
||||
fmc_pr(RD_DBG, "\t|-Set OP_CTRL[%#x]%#x\n", FMC_OP_CTRL, reg);
|
||||
fmc_dma_wait_int_finish(host);
|
||||
|
||||
if (ecc0_flag == 1 && (host->cmd_op.l_cmd != NAND_CMD_READOOB))
|
||||
fmc100_ecc0_switch(host, DISABLE);
|
||||
|
||||
#ifndef CONFIG_SYS_DCACHE_OFF
|
||||
invalidate_dcache_range(host->dma_buffer, host->dma_buffer + dma_align_len);
|
||||
#endif
|
||||
|
||||
(*fmc_ip)--;
|
||||
fmc_pr(RD_DBG, "\t*-End Dma page read\n");
|
||||
}
|
||||
|
||||
static void erase_fmc_op(struct fmc_host* const host, struct fmc_spi* const spi)
|
||||
{
|
||||
unsigned int reg;
|
||||
|
||||
reg = FMC_INT_CLR_ALL;
|
||||
fmc_write(host, FMC_INT_CLR, reg);
|
||||
fmc_pr(ER_DBG, "\t|-Set INT_CLR[%#x]%#x\n", FMC_INT_CLR, reg);
|
||||
|
||||
reg = spi->erase->cmd;
|
||||
fmc_write(host, FMC_CMD, fmc_cmd_cmd1(reg));
|
||||
fmc_pr(ER_DBG, "\t|-Set CMD[%#x]%#x\n", FMC_CMD, reg);
|
||||
|
||||
reg = fmc_addrl_block_h_mask(host->addr_value[1]) |
|
||||
fmc_addrl_block_l_mask(host->addr_value[0]);
|
||||
fmc_write(host, FMC_ADDRL, reg);
|
||||
fmc_pr(ER_DBG, "\t|-Set ADDRL[%#x]%#x\n", FMC_ADDRL, reg);
|
||||
|
||||
reg = op_cfg_fm_cs(host->cmd_op.cs) | OP_CFG_OEN_EN |
|
||||
op_cfg_mem_if_type(spi->erase->iftype) |
|
||||
op_cfg_addr_num(STD_OP_ADDR_NUM) |
|
||||
op_cfg_dummy_num(spi->erase->dummy);
|
||||
|
||||
fmc_write(host, FMC_OP_CFG, reg);
|
||||
fmc_pr(ER_DBG, "\t|-Set OP_CFG[%#x]%#x\n", FMC_OP_CFG, reg);
|
||||
|
||||
reg = fmc_op_cmd1_en(ENABLE) | fmc_op_addr_en(ENABLE) |
|
||||
FMC_OP_REG_OP_START;
|
||||
fmc_write(host, FMC_OP, reg);
|
||||
fmc_pr(ER_DBG, "\t|-Set OP[%#x]%#x\n", FMC_OP, reg);
|
||||
|
||||
fmc_cmd_wait_cpu_finish(host);
|
||||
}
|
||||
|
||||
static void fmc100_send_cmd_erase(struct fmc_host *host)
|
||||
{
|
||||
struct fmc_spi *spi = host->spi;
|
||||
unsigned char *fmc_ip = NULL;
|
||||
int ret;
|
||||
|
||||
if (ER_DBG)
|
||||
printf("\n");
|
||||
fmc_pr(ER_DBG, "\t*-Start send cmd erase!\n");
|
||||
|
||||
fmc_ip = get_fmc_ip();
|
||||
if (*fmc_ip) {
|
||||
printf("Warning: FMC IP is busy, Please try again.\n");
|
||||
udelay(1); /* delay 1 us */
|
||||
return;
|
||||
} else {
|
||||
fmc_dev_type_switch(FLASH_TYPE_SPI_NAND);
|
||||
(*fmc_ip)++;
|
||||
}
|
||||
|
||||
ret = spi->driver->write_enable(spi);
|
||||
if (ret) {
|
||||
db_msg("Error: Erase write enable failed! ret: %#x\n", ret);
|
||||
goto end;
|
||||
}
|
||||
|
||||
host->set_system_clock(spi->erase, ENABLE);
|
||||
|
||||
erase_fmc_op(host, spi);
|
||||
|
||||
ret = spi->driver->wait_ready(spi);
|
||||
fmc_pr(ER_DBG, "\t|-Erase wait ready, ret: %#x\n", ret);
|
||||
if (ret)
|
||||
db_msg("Error: Erase wait ready fail! status: %#x\n", ret);
|
||||
|
||||
end:
|
||||
(*fmc_ip)--;
|
||||
|
||||
fmc_pr(ER_DBG, "\t*-End send cmd erase!\n");
|
||||
}
|
||||
|
||||
static void fmc100_send_cmd_status(struct fmc_host* const host)
|
||||
{
|
||||
unsigned int status;
|
||||
unsigned char addr = STATUS_ADDR;
|
||||
struct fmc_spi *spi = host->spi;
|
||||
unsigned char *fmc_ip = get_fmc_ip();
|
||||
|
||||
if (*fmc_ip) {
|
||||
printf("Warning: FMC IP is busy, Please try again.\n");
|
||||
udelay(1); /* delay 1 us */
|
||||
return;
|
||||
} else {
|
||||
fmc_dev_type_switch(FLASH_TYPE_SPI_NAND);
|
||||
(*fmc_ip)++;
|
||||
}
|
||||
|
||||
if (host->cmd_op.l_cmd == NAND_CMD_GET_FEATURES)
|
||||
addr = PROTECT_ADDR;
|
||||
|
||||
if (spi_nand_feature_op(spi, GET_OP, addr, &status)) {
|
||||
printf("get protect addr failed!\n");
|
||||
(*fmc_ip)--;
|
||||
return;
|
||||
}
|
||||
|
||||
fmc_pr((ER_DBG || WR_DBG), "\t*-Get status[%#x]: %#x\n", addr, status);
|
||||
|
||||
(*fmc_ip)--;
|
||||
}
|
||||
|
||||
static void fmc100_send_cmd_readid(struct fmc_host *host)
|
||||
{
|
||||
unsigned int reg;
|
||||
|
||||
fmc_pr(BT_DBG, "\t|*-Start send cmd read ID\n");
|
||||
|
||||
fmc100_ecc0_switch(host, ENABLE);
|
||||
|
||||
reg = fmc_cmd_cmd1(SPI_CMD_RDID);
|
||||
fmc_write(host, FMC_CMD, reg);
|
||||
fmc_pr(BT_DBG, "\t||-Set CMD[%#x]%#x\n", FMC_CMD, reg);
|
||||
|
||||
reg = READ_ID_ADDR;
|
||||
fmc_write(host, FMC_ADDRL, reg);
|
||||
fmc_pr(BT_DBG, "\t||-Set ADDRL[%#x]%#x\n", FMC_ADDRL, reg);
|
||||
|
||||
reg = op_cfg_fm_cs(host->cmd_op.cs) | OP_CFG_OEN_EN |
|
||||
op_cfg_addr_num(READ_ID_ADDR_NUM);
|
||||
fmc_write(host, FMC_OP_CFG, reg);
|
||||
fmc_pr(BT_DBG, "\t||-Set OP_CFG[%#x]%#x\n", FMC_OP_CFG, reg);
|
||||
|
||||
reg = fmc_data_num_cnt(MAX_SPI_NAND_ID_LEN);
|
||||
fmc_write(host, FMC_DATA_NUM, reg);
|
||||
fmc_pr(BT_DBG, "\t||-Set DATA_NUM[%#x]%#x\n", FMC_DATA_NUM, reg);
|
||||
|
||||
reg = fmc_op_cmd1_en(ENABLE) | fmc_op_addr_en(ENABLE) |
|
||||
fmc_op_read_data_en(ENABLE) | FMC_OP_REG_OP_START;
|
||||
fmc_write(host, FMC_OP, reg);
|
||||
fmc_pr(BT_DBG, "\t||-Set OP[%#x]%#x\n", FMC_OP, reg);
|
||||
|
||||
host->addr_cycle = 0x0;
|
||||
|
||||
fmc_cmd_wait_cpu_finish(host);
|
||||
|
||||
fmc100_ecc0_switch(host, DISABLE);
|
||||
|
||||
fmc_pr(BT_DBG, "\t|*-End read flash ID\n");
|
||||
}
|
||||
|
||||
static void fmc100_send_cmd_reset(struct fmc_host* const host)
|
||||
{
|
||||
unsigned int reg;
|
||||
|
||||
fmc_pr(BT_DBG, "\t|*-Start send cmd reset\n");
|
||||
|
||||
reg = fmc_cmd_cmd1(SPI_CMD_RESET);
|
||||
fmc_write(host, FMC_CMD, reg);
|
||||
fmc_pr(BT_DBG, "\t||-Set CMD[%#x]%#x\n", FMC_CMD, reg);
|
||||
|
||||
reg = op_cfg_fm_cs(host->cmd_op.cs) | OP_CFG_OEN_EN;
|
||||
fmc_write(host, FMC_OP_CFG, reg);
|
||||
fmc_pr(BT_DBG, "\t||-Set OP_CFG[%#x]%#x\n", FMC_OP_CFG, reg);
|
||||
|
||||
reg = fmc_op_cmd1_en(ENABLE) | FMC_OP_REG_OP_START;
|
||||
fmc_write(host, FMC_OP, reg);
|
||||
fmc_pr(BT_DBG, "\t||-Set OP[%#x]%#x\n", FMC_OP, reg);
|
||||
|
||||
fmc_cmd_wait_cpu_finish(host);
|
||||
|
||||
fmc_pr(BT_DBG, "\t|*-End send cmd reset\n");
|
||||
}
|
||||
|
||||
static unsigned char fmc100_read_byte(struct mtd_info* const mtd)
|
||||
{
|
||||
struct nand_chip *chip = mtd_to_nand(mtd);
|
||||
struct fmc_host *host = chip->priv;
|
||||
unsigned char value;
|
||||
unsigned char ret_val = 0;
|
||||
|
||||
if (host->cmd_op.l_cmd == NAND_CMD_READID) {
|
||||
value = readb(host->iobase + host->offset);
|
||||
host->offset++;
|
||||
if (host->cmd_op.data_no == host->offset)
|
||||
host->cmd_op.l_cmd = 0;
|
||||
return value;
|
||||
}
|
||||
|
||||
if (host->cmd_op.cmd == NAND_CMD_STATUS) {
|
||||
value = fmc_read(host, FMC_STATUS);
|
||||
if (host->cmd_op.l_cmd == NAND_CMD_GET_FEATURES) {
|
||||
fmc_pr((ER_DBG || WR_DBG), "\t\tRead BP status: %#x\n",
|
||||
value);
|
||||
if (any_bp_enable(value))
|
||||
ret_val |= NAND_STATUS_WP;
|
||||
|
||||
host->cmd_op.l_cmd = NAND_CMD_STATUS;
|
||||
}
|
||||
|
||||
if (!(value & STATUS_OIP_MASK))
|
||||
ret_val |= NAND_STATUS_READY;
|
||||
|
||||
if ((chip->state == FL_ERASING) &&
|
||||
(value & STATUS_E_FAIL_MASK)) {
|
||||
fmc_pr(ER_DBG, "\t\tGet erase status: %#x\n", value);
|
||||
ret_val |= NAND_STATUS_FAIL;
|
||||
}
|
||||
|
||||
if ((chip->state == FL_WRITING) &&
|
||||
(value & STATUS_P_FAIL_MASK)) {
|
||||
fmc_pr(WR_DBG, "\t\tGet write status: %#x\n", value);
|
||||
ret_val |= NAND_STATUS_FAIL;
|
||||
}
|
||||
|
||||
return ret_val;
|
||||
}
|
||||
|
||||
if (host->cmd_op.l_cmd == NAND_CMD_READOOB) {
|
||||
value = readb((unsigned char *)
|
||||
((unsigned char *)(uintptr_t)host->dma_oob + host->offset));
|
||||
host->offset++;
|
||||
return value;
|
||||
}
|
||||
|
||||
host->offset++;
|
||||
|
||||
return readb(host->buffer + host->column + host->offset - 1);
|
||||
}
|
||||
|
||||
static unsigned short fmc100_read_word(struct mtd_info *mtd)
|
||||
{
|
||||
struct nand_chip *chip = mtd_to_nand(mtd);
|
||||
struct fmc_host *host = chip->priv;
|
||||
|
||||
return readw(host->buffer + host->column + host->offset);
|
||||
}
|
||||
|
||||
static void fmc100_write_buf(struct mtd_info* const mtd, const u_char* const buf, int len)
|
||||
{
|
||||
struct nand_chip *chip = mtd_to_nand(mtd);
|
||||
struct fmc_host *host = chip->priv;
|
||||
|
||||
if (buf == chip->oob_poi)
|
||||
memcpy((unsigned char *)(uintptr_t)host->dma_oob, buf, len);
|
||||
else
|
||||
memcpy((unsigned char *)(uintptr_t)host->dma_buffer, buf, len);
|
||||
return;
|
||||
}
|
||||
|
||||
static void fmc100_read_buf(struct mtd_info* const mtd, u_char* const buf, int len)
|
||||
{
|
||||
struct nand_chip *chip = mtd_to_nand(mtd);
|
||||
struct fmc_host *host = chip->priv;
|
||||
|
||||
if (buf == chip->oob_poi)
|
||||
memcpy(buf, (unsigned char *)(uintptr_t)host->dma_oob, len);
|
||||
else
|
||||
memcpy(buf, (unsigned char *)(uintptr_t)host->dma_buffer, len);
|
||||
return;
|
||||
}
|
||||
|
||||
static void fmc100_select_chip(struct mtd_info* const mtd, int chipselect)
|
||||
{
|
||||
struct nand_chip *chip = mtd_to_nand(mtd);
|
||||
struct fmc_host *host = chip->priv;
|
||||
|
||||
if (chipselect < 0)
|
||||
return;
|
||||
|
||||
if (chipselect > CONFIG_SPI_NAND_MAX_CHIP_NUM)
|
||||
db_bug("Error: Invalid chipselect: %d\n", chipselect);
|
||||
|
||||
if (host->mtd != mtd)
|
||||
host->mtd = mtd;
|
||||
|
||||
if (!(chip->options & NAND_BROKEN_XD))
|
||||
if ((chip->state == FL_ERASING) || (chip->state == FL_WRITING))
|
||||
host->cmd_op.l_cmd = NAND_CMD_GET_FEATURES;
|
||||
}
|
||||
|
||||
static void read_nand_id_op(struct fmc_host *host, unsigned int command)
|
||||
{
|
||||
host->offset = 0;
|
||||
host->cmd_op.l_cmd = command & 0xff;
|
||||
memset((u_char *)(host->iobase), 0, MAX_SPI_NAND_ID_LEN);
|
||||
host->cmd_op.data_no = MAX_SPI_NAND_ID_LEN;
|
||||
host->send_cmd_readid(host);
|
||||
}
|
||||
|
||||
static void fmc100_cmdfunc(struct mtd_info* const mtd,
|
||||
unsigned int command,
|
||||
int column, int page_addr)
|
||||
{
|
||||
struct nand_chip *chip = mtd_to_nand(mtd);
|
||||
struct fmc_host *host = chip->priv;
|
||||
|
||||
switch (command) {
|
||||
case NAND_CMD_RESET:
|
||||
host->send_cmd_reset(host);
|
||||
chip->dev_ready(mtd);
|
||||
break;
|
||||
case NAND_CMD_READID:
|
||||
read_nand_id_op(host, command);
|
||||
break;
|
||||
case NAND_CMD_GET_FEATURES:
|
||||
case NAND_CMD_STATUS:
|
||||
host->cmd_op.l_cmd = command & 0xff;
|
||||
host->cmd_op.cmd = NAND_CMD_STATUS;
|
||||
host->send_cmd_status(host);
|
||||
break;
|
||||
case NAND_CMD_READOOB:
|
||||
host->offset = 0;
|
||||
host->cmd_op.l_cmd = command & 0xff;
|
||||
/* use same command as normal read */
|
||||
host->cmd_op.cmd = command & 0xff;
|
||||
case NAND_CMD_READ0:
|
||||
if (command == NAND_CMD_READ0)
|
||||
host->cmd_op.l_cmd = command & 0xff;
|
||||
host->addr_value[1] = page_addr;
|
||||
host->send_cmd_readstart(host);
|
||||
break;
|
||||
case NAND_CMD_SEQIN:
|
||||
host->addr_value[1] = page_addr;
|
||||
break;
|
||||
case NAND_CMD_PAGEPROG:
|
||||
host->offset = 0;
|
||||
host->send_cmd_pageprog(host);
|
||||
break;
|
||||
case NAND_CMD_ERASE1:
|
||||
host->cmd_op.l_cmd = command & 0xff;
|
||||
host->addr_value[0] = page_addr;
|
||||
/* page_addr to block_addr, move right 16 bits */
|
||||
host->addr_value[1] = (unsigned int)page_addr >> 16;
|
||||
/* erase operation need a seral of command sequences */
|
||||
host->send_cmd_erase(host);
|
||||
break;
|
||||
case NAND_CMD_ERASE2:
|
||||
case NAND_CMD_READSTART:
|
||||
break;
|
||||
default:
|
||||
printf("%s not support command 0x%08x:\n", mtd->name, command);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static int fmc100_dev_ready(struct mtd_info* const mtd)
|
||||
{
|
||||
unsigned int reg;
|
||||
/* just a big number, so move 12 bits */
|
||||
unsigned long deadline = 1 << 12;
|
||||
struct nand_chip *chip = mtd_to_nand(mtd);
|
||||
struct fmc_host *host = chip->priv;
|
||||
|
||||
do {
|
||||
reg = op_cfg_fm_cs(host->cmd_op.cs) | OP_CFG_OEN_EN;
|
||||
fmc_write(host, FMC_OP_CFG, reg);
|
||||
|
||||
reg = fmc_op_read_status_en(ENABLE) | FMC_OP_REG_OP_START;
|
||||
fmc_write(host, FMC_OP, reg);
|
||||
|
||||
fmc_cmd_wait_cpu_finish(host);
|
||||
|
||||
reg = fmc_read(host, FMC_STATUS);
|
||||
if (!(reg & STATUS_OIP_MASK))
|
||||
return 1;
|
||||
|
||||
udelay(1); /* delay 1 us */
|
||||
} while (deadline--);
|
||||
|
||||
#ifndef CONFIG_SYS_NAND_QUIET_TEST
|
||||
printf("Warning: Wait SPI nand ready timeout, status: %#x\n", reg);
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* 'host->epm' only use the first oobfree[0] field, it looks very simple, But..
|
||||
*/
|
||||
static struct nand_ecclayout nand_ecc_default = {
|
||||
.oobfree = {{2, 30} }
|
||||
};
|
||||
|
||||
#ifdef CONFIG_FS_MAY_NOT_YAFFS2
|
||||
static struct nand_ecclayout nand_ecc_2k16bit = {
|
||||
.oobfree = {{2, 6} }
|
||||
};
|
||||
|
||||
static struct nand_ecclayout nand_ecc_4k16bit = {
|
||||
.oobfree = {{2, 14} }
|
||||
};
|
||||
#endif
|
||||
|
||||
static struct nand_config_info fmc_spi_nand_config_table[] = {
|
||||
{NAND_PAGE_4K, NAND_ECC_24BIT, 200, &nand_ecc_default},
|
||||
#ifdef CONFIG_FS_MAY_NOT_YAFFS2
|
||||
{NAND_PAGE_4K, NAND_ECC_16BIT, 128, &nand_ecc_4k16bit},
|
||||
#endif
|
||||
{NAND_PAGE_4K, NAND_ECC_8BIT, 128, &nand_ecc_default},
|
||||
{NAND_PAGE_4K, NAND_ECC_0BIT, 32, &nand_ecc_default},
|
||||
|
||||
{NAND_PAGE_2K, NAND_ECC_24BIT, 128, &nand_ecc_default},
|
||||
#ifdef CONFIG_FS_MAY_NOT_YAFFS2
|
||||
{NAND_PAGE_2K, NAND_ECC_16BIT, 64, &nand_ecc_2k16bit},
|
||||
#endif
|
||||
{NAND_PAGE_2K, NAND_ECC_8BIT, 64, &nand_ecc_default},
|
||||
{NAND_PAGE_2K, NAND_ECC_0BIT, 32, &nand_ecc_default},
|
||||
|
||||
{0, 0, 0, NULL},
|
||||
};
|
||||
|
||||
/*
|
||||
* Auto-sensed the page size and ecc type value. driver will try each of page
|
||||
* size and ecc type one by one till flash can be read and wrote accurately.
|
||||
* so the page size and ecc type is match adaptively without switch on the board
|
||||
*/
|
||||
static struct nand_config_info *fmc100_get_config_type_info(struct mtd_info *mtd)
|
||||
{
|
||||
struct nand_config_info *best = NULL;
|
||||
struct nand_config_info *info = fmc_spi_nand_config_table;
|
||||
struct nand_chip *chip = mtd_to_nand(mtd);
|
||||
for (; info->layout; info++) {
|
||||
if (match_page_type_to_size(info->pagetype) != mtd->writesize)
|
||||
continue;
|
||||
|
||||
if (mtd->oobsize < info->oobsize)
|
||||
continue;
|
||||
|
||||
if (!best || (best->ecctype < info->ecctype))
|
||||
best = info;
|
||||
}
|
||||
/* All SPI NAND are small-page,SLC */
|
||||
chip->bits_per_cell = 1;
|
||||
return best;
|
||||
}
|
||||
|
||||
static void fmc100_set_oob_info(struct mtd_info* const mtd,
|
||||
struct nand_config_info* const info)
|
||||
{
|
||||
struct nand_chip *chip = mtd_to_nand(mtd);
|
||||
struct fmc_host *host = chip->priv;
|
||||
|
||||
if (info->ecctype != NAND_ECC_0BIT)
|
||||
mtd->oobsize = info->oobsize;
|
||||
|
||||
host->oobsize = mtd->oobsize;
|
||||
host->dma_oob = host->dma_buffer + host->pagesize;
|
||||
host->bbm = (u_char *)(host->buffer + host->pagesize +
|
||||
FMC_BAD_BLOCK_POS);
|
||||
|
||||
chip->ecc.layout = info->layout;
|
||||
|
||||
/* EB bytes locate in the bottom two of CTRL(30) */
|
||||
host->epm = (u_short *)(host->buffer + host->pagesize +
|
||||
chip->ecc.layout->oobfree[0].offset + EB_NORMAL);
|
||||
|
||||
#ifdef CONFIG_FS_MAY_NOT_YAFFS2
|
||||
|
||||
if (info->ecctype == NAND_ECC_16BIT) {
|
||||
if (host->pagesize == _2K)
|
||||
/* EB bits locate in the bottom two of CTRL(6) */
|
||||
host->epm = (u_short *)(host->buffer + host->pagesize +
|
||||
chip->ecc.layout->oobfree[0].offset + EB_2K_16_BIT);
|
||||
else if (host->pagesize == _4K)
|
||||
/* EB bit locate in the bottom two of CTRL(14) */
|
||||
host->epm = (u_short *)(host->buffer + host->pagesize +
|
||||
chip->ecc.layout->oobfree[0].offset + EB_4K_16_BIT);
|
||||
}
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
static unsigned int fmc100_get_ecc_reg(struct fmc_host* const host,
|
||||
struct nand_config_info* const info)
|
||||
{
|
||||
host->ecctype = info->ecctype;
|
||||
|
||||
return fmc_cfg_ecc_type(match_ecc_type_to_reg(info->ecctype));
|
||||
}
|
||||
|
||||
static unsigned int fmc100_get_page_reg(struct fmc_host* const host,
|
||||
struct nand_config_info* const info)
|
||||
{
|
||||
host->pagesize = match_page_type_to_size(info->pagetype);
|
||||
|
||||
return fmc_cfg_page_size(match_page_type_to_reg(info->pagetype));
|
||||
}
|
||||
|
||||
static int fmc100_get_block_reg(struct fmc_host* const host,
|
||||
struct nand_config_info* const info, unsigned int* const block_val)
|
||||
{
|
||||
unsigned int block_reg = 0;
|
||||
unsigned int page_per_block;
|
||||
struct mtd_info *mtd = NULL;
|
||||
|
||||
if (info == NULL || host == NULL || host->mtd == NULL) {
|
||||
printf("para err!\n");
|
||||
return -1;
|
||||
}
|
||||
mtd = host->mtd;
|
||||
host->block_page_mask = ((mtd->erasesize / mtd->writesize) - 1);
|
||||
page_per_block = mtd->erasesize / match_page_type_to_size(info->pagetype);
|
||||
switch (page_per_block) {
|
||||
case _64_PAGES:
|
||||
block_reg = BLOCK_SIZE_64_PAGE;
|
||||
break;
|
||||
case _128_PAGES:
|
||||
block_reg = BLOCK_SIZE_128_PAGE;
|
||||
break;
|
||||
case _256_PAGES:
|
||||
block_reg = BLOCK_SIZE_256_PAGE;
|
||||
break;
|
||||
case _512_PAGES:
|
||||
block_reg = BLOCK_SIZE_512_PAGE;
|
||||
break;
|
||||
default:
|
||||
db_msg("Can't support block %#x and page %#x size\n",
|
||||
mtd->erasesize, mtd->writesize);
|
||||
}
|
||||
*block_val = fmc_cfg_block_size(block_reg);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void fmc100_set_fmc_cfg_reg(struct mtd_info* const mtd,
|
||||
struct nand_config_info* const type_info)
|
||||
{
|
||||
struct nand_chip *chip = NULL;
|
||||
struct fmc_host *host = NULL;
|
||||
unsigned int page_reg;
|
||||
unsigned int ecc_reg;
|
||||
unsigned int block_reg;
|
||||
unsigned int reg_fmc_cfg;
|
||||
if (mtd == NULL || type_info == NULL)
|
||||
return;
|
||||
chip = mtd_to_nand(mtd);
|
||||
if (chip == NULL || chip->priv == NULL)
|
||||
return;
|
||||
host = chip->priv;
|
||||
ecc_reg = fmc100_get_ecc_reg(host, type_info);
|
||||
page_reg = fmc100_get_page_reg(host, type_info);
|
||||
if (fmc100_get_block_reg(host, type_info, &block_reg))
|
||||
return;
|
||||
|
||||
reg_fmc_cfg = fmc_read(host, FMC_CFG);
|
||||
reg_fmc_cfg &= ~(PAGE_SIZE_MASK | ECC_TYPE_MASK | BLOCK_SIZE_MASK);
|
||||
reg_fmc_cfg |= ecc_reg | page_reg | block_reg;
|
||||
fmc_write(host, FMC_CFG, reg_fmc_cfg);
|
||||
|
||||
/* max number of correctible bit errors per ecc step */
|
||||
mtd->ecc_strength = host->ecctype;
|
||||
|
||||
/* Save value of FMC_CFG and FMC_CFG_ECC0 to turn on/off ECC */
|
||||
host->fmc_cfg = reg_fmc_cfg;
|
||||
host->fmc_cfg_ecc0 = (host->fmc_cfg & ~ECC_TYPE_MASK) | ECC_TYPE_0BIT;
|
||||
fmc_pr(BT_DBG, "\t|-Save FMC_CFG[%#x]: %#x and FMC_CFG_ECC0: %#x\n",
|
||||
FMC_CFG, host->fmc_cfg, host->fmc_cfg_ecc0);
|
||||
}
|
||||
|
||||
static int fmc100_set_config_info(struct mtd_info* const mtd)
|
||||
{
|
||||
struct nand_config_info *type_info = NULL;
|
||||
|
||||
fmc_pr(BT_DBG, "\t*-Start match PageSize and EccType\n");
|
||||
|
||||
type_info = fmc100_get_config_type_info(mtd);
|
||||
if (!type_info)
|
||||
db_bug(ERR_STR_DRIVER "pagesize: %d and oobsize: %d.\n",
|
||||
mtd->writesize, mtd->oobsize);
|
||||
|
||||
/* Set the page_size, ecc_type, block_size of FMC_CFG[0x0] register */
|
||||
fmc100_set_fmc_cfg_reg(mtd, type_info);
|
||||
|
||||
fmc_pr(BT_DBG, "\t|- PageSize %s EccType %s OOB Size %d\n",
|
||||
nand_page_name(type_info->pagetype),
|
||||
nand_ecc_name(type_info->ecctype), type_info->oobsize);
|
||||
|
||||
fmc100_set_oob_info(mtd, type_info);
|
||||
|
||||
fmc_pr(BT_DBG, "\t*-End match PageSize and EccType\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void fmc100_chip_init(struct nand_chip* const chip)
|
||||
{
|
||||
if (!chip->IO_ADDR_R)
|
||||
chip->IO_ADDR_R = (void __iomem *)CONFIG_FMC_BUFFER_BASE;
|
||||
chip->IO_ADDR_W = chip->IO_ADDR_R;
|
||||
memset((char *)chip->IO_ADDR_R, 0xff, FMC100_BUFFER_LEN);
|
||||
|
||||
chip->read_byte = fmc100_read_byte;
|
||||
chip->read_word = fmc100_read_word;
|
||||
chip->write_buf = fmc100_write_buf;
|
||||
chip->read_buf = fmc100_read_buf;
|
||||
|
||||
chip->select_chip = fmc100_select_chip;
|
||||
|
||||
chip->cmdfunc = fmc100_cmdfunc;
|
||||
chip->dev_ready = fmc100_dev_ready;
|
||||
|
||||
chip->chip_delay = FMC_CHIP_DELAY;
|
||||
|
||||
chip->options = NAND_BBT_SCANNED | NAND_BROKEN_XD;
|
||||
|
||||
chip->ecc.layout = NULL;
|
||||
chip->ecc.mode = NAND_ECC_NONE;
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
int host_data_init(struct fmc_host *host)
|
||||
{
|
||||
unsigned long align_mask;
|
||||
|
||||
host->addr_cycle = 0;
|
||||
host->addr_value[0] = 0;
|
||||
host->addr_value[1] = 0;
|
||||
host->cache_addr_value[0] = ~0;
|
||||
host->cache_addr_value[1] = ~0;
|
||||
|
||||
fmc_pr(BT_DBG, "\t|||-Malloc memory for dma buffer\n");
|
||||
host->buforg = kmalloc((FMC100_BUFFER_LEN + FMC_DMA_ALIGN),
|
||||
GFP_KERNEL);
|
||||
if (!host->buforg) {
|
||||
db_msg("Error: Can't malloc memory for SPI Nand driver.\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
memset(host->buforg, 0xff, FMC100_BUFFER_LEN + FMC_DMA_ALIGN);
|
||||
|
||||
/* DMA need 32 bytes alignment */
|
||||
align_mask = FMC_DMA_ALIGN - 1;
|
||||
host->dma_buffer = (uintptr_t)(host->buforg + align_mask) & ~align_mask;
|
||||
|
||||
host->buffer = (char *)(uintptr_t)host->dma_buffer;
|
||||
memset(host->buffer, 0xff, FMC100_BUFFER_LEN);
|
||||
|
||||
host->send_cmd_pageprog = fmc100_send_cmd_pageprog;
|
||||
host->send_cmd_status = fmc100_send_cmd_status;
|
||||
host->send_cmd_readstart = fmc100_send_cmd_readstart;
|
||||
host->send_cmd_erase = fmc100_send_cmd_erase;
|
||||
host->send_cmd_readid = fmc100_send_cmd_readid;
|
||||
host->send_cmd_reset = fmc100_send_cmd_reset;
|
||||
host->set_system_clock = fmc_set_fmc_system_clock;
|
||||
|
||||
return 0;
|
||||
}
|
||||
/*****************************************************************************/
|
||||
int fmc100_host_init(struct fmc_host *host)
|
||||
{
|
||||
unsigned int reg;
|
||||
unsigned int flash_type;
|
||||
int ret;
|
||||
|
||||
if (!host)
|
||||
return -1;
|
||||
fmc_pr(BT_DBG, "\t||*-Start SPI Nand host init\n");
|
||||
host->iobase = (void __iomem *)CONFIG_FMC_BUFFER_BASE;
|
||||
host->regbase = (void __iomem *)CONFIG_FMC_REG_BASE;
|
||||
if (!host->iobase || !host->regbase)
|
||||
return -1;
|
||||
reg = fmc_read(host, FMC_CFG);
|
||||
flash_type = (reg & FLASH_SEL_MASK) >> FLASH_SEL_SHIFT;
|
||||
if (flash_type != FLASH_TYPE_SPI_NAND) {
|
||||
db_msg("Error: Flash type isn't SPI Nand. reg: %#x\n", reg);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
if ((reg & OP_MODE_MASK) == OP_MODE_BOOT) {
|
||||
reg |= fmc_cfg_op_mode(OP_MODE_NORMAL);
|
||||
fmc_write(host, FMC_CFG, reg);
|
||||
fmc_pr(BT_DBG, "\t|||-Set CFG[%#x]%#x\n", FMC_CFG, reg);
|
||||
}
|
||||
|
||||
host->fmc_cfg = reg;
|
||||
host->fmc_cfg_ecc0 = (reg & ~ECC_TYPE_MASK) | ECC_TYPE_0BIT;
|
||||
|
||||
reg = fmc_read(host, FMC_GLOBAL_CFG);
|
||||
if (reg & FMC_GLOBAL_CFG_WP_ENABLE) {
|
||||
reg &= ~FMC_GLOBAL_CFG_WP_ENABLE;
|
||||
fmc_write(host, FMC_GLOBAL_CFG, reg);
|
||||
}
|
||||
|
||||
ret = host_data_init(host);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* ecc0_flag for ecc0 read/write */
|
||||
ecc0_flag = 0;
|
||||
|
||||
reg = timing_cfg_tcsh(CS_HOLD_TIME) |
|
||||
timing_cfg_tcss(CS_SETUP_TIME) |
|
||||
timing_cfg_tshsl(CS_DESELECT_TIME);
|
||||
fmc_write(host, FMC_SPI_TIMING_CFG, reg);
|
||||
fmc_pr(BT_DBG, "\t|||-Set TIMING[%#x]%#x\n", FMC_SPI_TIMING_CFG, reg);
|
||||
|
||||
reg = ALL_BURST_ENABLE;
|
||||
fmc_write(host, FMC_DMA_AHB_CTRL, reg);
|
||||
fmc_pr(BT_DBG, "\t|||-Set DMA_AHB[%#x]%#x\n", FMC_DMA_AHB_CTRL, reg);
|
||||
|
||||
fmc_pr(BT_DBG, "\t|||-Register SPI Nand ID table and ecc probe\n");
|
||||
fmc_spi_nand_ids_register();
|
||||
nand_oob_resize = fmc100_set_config_info;
|
||||
|
||||
fmc_pr(BT_DBG, "\t||*-End SPI Nand host init\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
void fmc100_spi_nand_init(struct fmc_host *host)
|
||||
{
|
||||
struct nand_chip *chip = host->chip;
|
||||
|
||||
fmc_pr(BT_DBG, "\t|*-Start fmc100 SPI Nand init\n");
|
||||
|
||||
/* Set system clock and enable controller */
|
||||
fmc_pr(BT_DBG, "\t||-Set system clock and Enable Controller\n");
|
||||
if (host->set_system_clock)
|
||||
host->set_system_clock(NULL, ENABLE);
|
||||
|
||||
/* Fmc nand_chip struct init */
|
||||
fmc_pr(BT_DBG, "\t||-fmc100 struct nand_chip init\n");
|
||||
chip->priv = host;
|
||||
fmc100_chip_init(chip);
|
||||
|
||||
fmc_pr(BT_DBG, "\t|*-End fmc100 SPI Nand init\n");
|
||||
}
|
||||
@@ -0,0 +1,109 @@
|
||||
/*
|
||||
* Copyright (c) LOTUS. All rights reserved.
|
||||
*/
|
||||
|
||||
#ifndef __FMC100_H__
|
||||
#define __FMC100_H__
|
||||
|
||||
#include <nand.h>
|
||||
#include <linux/lotus/fmc_common.h>
|
||||
#include <linux/lotus/nfc_common.h>
|
||||
#include "fmc_spi_ids.h"
|
||||
#include <cpu_func.h>
|
||||
/*****************************************************************************/
|
||||
/* These macroes are for debug only, reg option is slower then dma option */
|
||||
#undef FMC100_SPI_NAND_SUPPORT_REG_READ
|
||||
#undef FMC100_SPI_NAND_SUPPORT_REG_WRITE
|
||||
|
||||
/*****************************************************************************/
|
||||
#define REG_CNT_HIGH_BLOCK_NUM_SHIFT 10
|
||||
|
||||
#define REG_CNT_BLOCK_NUM_MASK 0x3ff
|
||||
#define REG_CNT_BLOCK_NUM_SHIFT 22
|
||||
|
||||
#define REG_CNT_PAGE_NUM_MASK 0x3f
|
||||
#define REG_CNT_PAGE_NUM_SHIFT 16
|
||||
|
||||
#define REG_CNT_WRAP_MASK 0xf
|
||||
#define REG_CNT_WRAP_SHIFT 12
|
||||
|
||||
#define REG_CNT_ECC_OFFSET_MASK 0xfff
|
||||
#define REG_CNT_ECC_8BIT_OFFSET 1054
|
||||
#define REG_CNT_ECC_16BIT_OFFSET 1056
|
||||
#define REG_CNT_ECC_24BIT_OFFSET 1082
|
||||
|
||||
#define ERR_STR_DRIVER "Driver does not support this configure "
|
||||
#define ERR_STR_CHECK "Please make sure the hardware configuration is correct"
|
||||
|
||||
/*****************************************************************************/
|
||||
#define SPI_NAND_MAX_PAGESIZE 4096
|
||||
#define SPI_NAND_MAX_OOBSIZE 256
|
||||
|
||||
#define FMC100_BUFFER_LEN (SPI_NAND_MAX_PAGESIZE + SPI_NAND_MAX_OOBSIZE)
|
||||
|
||||
#define FMC100_ADDR_CYCLE_MASK 0x2
|
||||
|
||||
/*****************************************************************************/
|
||||
struct fmc_host {
|
||||
struct mtd_info *mtd;
|
||||
struct nand_chip *chip;
|
||||
struct fmc_spi spi[CONFIG_SPI_NAND_MAX_CHIP_NUM];
|
||||
struct fmc_cmd_op cmd_op;
|
||||
|
||||
void __iomem *iobase;
|
||||
void __iomem *regbase;
|
||||
|
||||
unsigned int fmc_cfg;
|
||||
unsigned int fmc_cfg_ecc0;
|
||||
|
||||
unsigned int offset;
|
||||
|
||||
struct device *dev;
|
||||
|
||||
/* This is maybe an un-aligment address, only for malloc or free */
|
||||
char *buforg;
|
||||
char *buffer;
|
||||
|
||||
unsigned long dma_buffer;
|
||||
unsigned long dma_oob;
|
||||
|
||||
unsigned int addr_cycle;
|
||||
unsigned int addr_value[2]; /* 2 addr */
|
||||
unsigned int cache_addr_value[2]; /* 2 addr */
|
||||
|
||||
unsigned int column;
|
||||
unsigned int block_page_mask;
|
||||
|
||||
unsigned int ecctype;
|
||||
unsigned int pagesize;
|
||||
unsigned int oobsize;
|
||||
|
||||
int add_partition;
|
||||
|
||||
/* BOOTROM read two bytes to detect the bad block flag */
|
||||
#define FMC_BAD_BLOCK_POS 0
|
||||
unsigned char *bbm; /* nand bad block mark */
|
||||
unsigned short *epm; /* nand empty page mark */
|
||||
|
||||
unsigned int uc_er;
|
||||
|
||||
void (*send_cmd_pageprog)(struct fmc_host *host);
|
||||
void (*send_cmd_status)(struct fmc_host *host);
|
||||
void (*send_cmd_readstart)(struct fmc_host *host);
|
||||
void (*send_cmd_erase)(struct fmc_host *host);
|
||||
void (*send_cmd_readid)(struct fmc_host *host);
|
||||
void (*send_cmd_reset)(struct fmc_host *host);
|
||||
void (*set_system_clock)(struct spi_op *op, int clk_en);
|
||||
};
|
||||
|
||||
void fmc100_ecc0_switch(struct fmc_host *host, unsigned char op);
|
||||
|
||||
int fmc100_host_init(struct fmc_host *host);
|
||||
|
||||
void fmc100_spi_nand_init(struct fmc_host *host);
|
||||
|
||||
void fmc_spi_nand_ids_register(void);
|
||||
char spi_nand_feature_op(struct fmc_spi* const spi, unsigned char op,
|
||||
unsigned char addr, unsigned int* const val);
|
||||
|
||||
#endif /* End of __FMC100_H__ */
|
||||
+124
@@ -0,0 +1,124 @@
|
||||
/*
|
||||
* Copyright (c) LOTUS. All rights reserved.
|
||||
*/
|
||||
|
||||
#include "fmc100_os.h"
|
||||
#include <common.h>
|
||||
|
||||
/*****************************************************************************/
|
||||
static struct fmc_host fmc100_host = {
|
||||
.chip = NULL,
|
||||
};
|
||||
|
||||
/*****************************************************************************/
|
||||
static void fmc100_driver_probe(struct nand_chip *chip, unsigned char cs)
|
||||
{
|
||||
int ret = 0;
|
||||
struct fmc_host *host = &fmc100_host;
|
||||
|
||||
fmc_pr(BT_DBG, "\t*-Start SPI Nand flash driver probe\n");
|
||||
|
||||
if (!host->chip) {
|
||||
/* Fmc ip version check */
|
||||
if (fmc_ip_ver_check())
|
||||
db_bug("Error: fmc IP version unknown!\n");
|
||||
|
||||
/* Fmc current SPI device type check */
|
||||
fmc_dev_type_switch(FLASH_TYPE_SPI_NAND);
|
||||
|
||||
/* Fmc SPI nand init */
|
||||
memset((char *)host, 0, sizeof(struct fmc_host));
|
||||
ret = fmc100_host_init(host);
|
||||
if (ret) {
|
||||
db_msg("Error: Host init failed, result: %d\n", ret);
|
||||
/* Change SPI device type to default */
|
||||
fmc_dev_type_switch(FLASH_TYPE_DEFAULT);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
fmc_pr(BT_DBG, "\t*-SPI Nand host is initialized.\n");
|
||||
}
|
||||
|
||||
host->cmd_op.cs = cs;
|
||||
host->chip = chip;
|
||||
fmc100_spi_nand_init(host);
|
||||
|
||||
fmc_pr(BT_DBG, "\t*-End SPI Nand flash driver probe.\n");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
static int fmc100_spi_nand_pre_probe(struct nand_chip *chip)
|
||||
{
|
||||
uint8_t nand_maf_id;
|
||||
struct mtd_info *mtd = nand_to_mtd(chip);
|
||||
struct fmc_host *host = chip->priv;
|
||||
|
||||
/* Reset the chip first */
|
||||
host->send_cmd_reset(host);
|
||||
chip->dev_ready(mtd);
|
||||
|
||||
/* Check the ID */
|
||||
host->offset = 0;
|
||||
memset((unsigned char *)(chip->IO_ADDR_R), 0, 0x10);
|
||||
host->send_cmd_readid(host);
|
||||
nand_maf_id = readb(chip->IO_ADDR_R);
|
||||
if (nand_maf_id == 0x00 || nand_maf_id == 0xff) {
|
||||
printf("Cannot found a valid SPI Nand Device\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
int board_nand_init(struct nand_chip *chip)
|
||||
{
|
||||
unsigned char chip_num = CONFIG_SPI_NAND_MAX_CHIP_NUM;
|
||||
static unsigned char cs = 0;
|
||||
unsigned char *fmc_cs = NULL;
|
||||
|
||||
if (get_boot_media() != BOOT_MEDIA_NAND) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
for (cs = 0; chip_num && (cs < CONFIG_FMC_MAX_CS_NUM); cs++) {
|
||||
fmc_cs = get_cs_number(cs);
|
||||
if (*fmc_cs) {
|
||||
fmc_pr(BT_DBG, "\t\t*-Current CS(%d) is occupied.\n",
|
||||
cs);
|
||||
continue;
|
||||
}
|
||||
|
||||
fmc100_driver_probe(chip, cs);
|
||||
chip_num--;
|
||||
}
|
||||
|
||||
if (chip_num)
|
||||
return 1;
|
||||
|
||||
if (fmc100_spi_nand_pre_probe(chip))
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
static int fmc100_spi_nand_get_ecctype(void)
|
||||
{
|
||||
struct fmc_host *host = &fmc100_host;
|
||||
|
||||
if (!host->chip) {
|
||||
printf("SPI Nand flash uninitialized.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return match_ecc_type_to_yaffs(fmc100_host.ecctype);
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
int nand_get_ecctype(void)
|
||||
{
|
||||
return fmc100_spi_nand_get_ecctype();
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
/*
|
||||
* Copyright (c) LOTUS. All rights reserved.
|
||||
*/
|
||||
|
||||
#ifndef __FMC100_OS_H__
|
||||
#define __FMC100_OS_H__
|
||||
|
||||
#include "fmc100.h"
|
||||
|
||||
/*****************************************************************************/
|
||||
int board_nand_init(struct nand_chip *chip);
|
||||
|
||||
#endif /* End of __FMC100_OS_H__ */
|
||||
+276
@@ -0,0 +1,276 @@
|
||||
/*
|
||||
* Copyright (c) LOTUS. All rights reserved.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Send set/get features command to SPI Nand flash
|
||||
*/
|
||||
|
||||
void spi_nand_set_cmd(struct fmc_host* const host, u_char op, u_char addr,
|
||||
u_char val)
|
||||
{
|
||||
unsigned int reg;
|
||||
|
||||
reg = fmc_cmd_cmd1(op ? SPI_CMD_SET_FEATURE : SPI_CMD_GET_FEATURES);
|
||||
fmc_write(host, FMC_CMD, reg);
|
||||
fmc_pr(FT_DBG, "\t||||-Set CMD[%#x]%#x\n", FMC_CMD, reg);
|
||||
|
||||
fmc_write(host, FMC_ADDRL, addr);
|
||||
fmc_pr(FT_DBG, "\t||||-Set ADDRL[%#x]%#x\n", FMC_ADDRL, addr);
|
||||
|
||||
reg = op_cfg_fm_cs(host->cmd_op.cs) | OP_CFG_OEN_EN |
|
||||
op_cfg_addr_num(FEATURES_OP_ADDR_NUM);
|
||||
fmc_write(host, FMC_OP_CFG, reg);
|
||||
fmc_pr(FT_DBG, "\t||||-Set OP_CFG[%#x]%#x\n", FMC_OP_CFG, reg);
|
||||
|
||||
reg = fmc_data_num_cnt(FEATURES_DATA_LEN);
|
||||
fmc_write(host, FMC_DATA_NUM, reg);
|
||||
fmc_pr(FT_DBG, "\t||||-Set DATA_NUM[%#x]%#x\n", FMC_DATA_NUM, reg);
|
||||
|
||||
reg = fmc_op_cmd1_en(ENABLE) | fmc_op_addr_en(ENABLE) |
|
||||
FMC_OP_REG_OP_START;
|
||||
|
||||
if (op == SET_OP) {
|
||||
reg |= fmc_op_write_data_en(ENABLE);
|
||||
writeb(val, host->iobase);
|
||||
fmc_pr(FT_DBG, "\t||||-Write IO[%p]%#x\n", host->iobase,
|
||||
*(u_char *)host->iobase);
|
||||
} else {
|
||||
reg |= fmc_op_read_data_en(ENABLE);
|
||||
}
|
||||
|
||||
fmc_write(host, FMC_OP, reg);
|
||||
fmc_pr(FT_DBG, "\t||||-Set OP[%#x]%#x\n", FMC_OP, reg);
|
||||
|
||||
fmc_cmd_wait_cpu_finish(host);
|
||||
}
|
||||
|
||||
char spi_nand_feature_op(struct fmc_spi* const spi, unsigned char op,
|
||||
unsigned char addr, unsigned int* const val)
|
||||
{
|
||||
unsigned int reg;
|
||||
const char *str[] = {"Get", "Set"};
|
||||
struct fmc_host *host = NULL;
|
||||
unsigned char regval;
|
||||
|
||||
if (spi == NULL || spi->host == NULL || val == NULL) {
|
||||
printf("para err!\n");
|
||||
return -1;
|
||||
}
|
||||
host = (struct fmc_host *)spi->host;
|
||||
if ((op == GET_OP) && (addr == STATUS_ADDR)) {
|
||||
fmc_pr(SR_DBG, "\n\t\t|*-Start Get Status\n");
|
||||
reg = op_cfg_fm_cs(host->cmd_op.cs) | OP_CFG_OEN_EN;
|
||||
fmc_write(host, FMC_OP_CFG, reg);
|
||||
fmc_pr(SR_DBG, "\t\t||-Set OP_CFG[%#x]%#x\n", FMC_OP_CFG, reg);
|
||||
reg = fmc_op_read_status_en(ENABLE) | FMC_OP_REG_OP_START;
|
||||
fmc_write(host, FMC_OP, reg);
|
||||
fmc_pr(SR_DBG, "\t\t||-Set OP[%#x]%#x\n", FMC_OP, reg);
|
||||
fmc_cmd_wait_cpu_finish(host);
|
||||
*val = fmc_read(host, FMC_STATUS);
|
||||
fmc_pr(SR_DBG, "\t\t|*-End Get Status, result: %#x\n", *val);
|
||||
return 0;
|
||||
}
|
||||
|
||||
fmc_pr(FT_DBG, "\t|||*-Start %s feature, addr[%#x]\n", str[op], addr);
|
||||
fmc100_ecc0_switch(host, ENABLE);
|
||||
regval = *val & 0xff;
|
||||
spi_nand_set_cmd(host, op, addr, regval);
|
||||
if (op == GET_OP) {
|
||||
if (host->iobase == NULL) {
|
||||
printf("para err!");
|
||||
return -1;
|
||||
}
|
||||
*val = readb(host->iobase);
|
||||
fmc_pr(FT_DBG, "\t||||-Read IO[%p]%#x\n", host->iobase,
|
||||
*(u_char *)host->iobase);
|
||||
}
|
||||
fmc100_ecc0_switch(host, DISABLE);
|
||||
fmc_pr(FT_DBG, "\t|||*-End %s Feature[%#x]:%#x\n", str[op], addr, *val);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Read status[C0H]:[0]bit OIP, judge whether the device is busy or not
|
||||
*/
|
||||
static int spi_general_wait_ready(struct fmc_spi* const spi)
|
||||
{
|
||||
unsigned int status;
|
||||
/* just get a big number, so move left 12 bits */
|
||||
unsigned int deadline = 1 << 12;
|
||||
struct fmc_host *host = (struct fmc_host *)spi->host;
|
||||
|
||||
do {
|
||||
if (spi_nand_feature_op(spi, GET_OP, STATUS_ADDR, &status)) {
|
||||
printf("get feature failed!\n");
|
||||
return 1;
|
||||
}
|
||||
if (!(status & STATUS_OIP_MASK)) {
|
||||
if ((host->cmd_op.l_cmd == NAND_CMD_ERASE2) && (status & STATUS_E_FAIL_MASK))
|
||||
return status;
|
||||
|
||||
if ((host->cmd_op.l_cmd == NAND_CMD_PAGEPROG) && (status & STATUS_P_FAIL_MASK))
|
||||
return status;
|
||||
return 0;
|
||||
}
|
||||
|
||||
udelay(1); /* delay 1 us */
|
||||
} while (deadline--);
|
||||
|
||||
db_msg("Error: SPI Nand wait ready timeout, status: %#x\n", status);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void write_enable_fmc_op(struct fmc_host* const host)
|
||||
{
|
||||
unsigned int reg;
|
||||
|
||||
reg = fmc_read(host, FMC_GLOBAL_CFG);
|
||||
fmc_pr(WE_DBG, "\t||-Get GLOBAL_CFG[%#x]%#x\n", FMC_GLOBAL_CFG, reg);
|
||||
if (reg & FMC_GLOBAL_CFG_WP_ENABLE) {
|
||||
reg &= ~FMC_GLOBAL_CFG_WP_ENABLE;
|
||||
fmc_write(host, FMC_GLOBAL_CFG, reg);
|
||||
fmc_pr(WE_DBG, "\t||-Set GLOBAL_CFG[%#x]%#x\n",
|
||||
FMC_GLOBAL_CFG, reg);
|
||||
}
|
||||
|
||||
reg = fmc_cmd_cmd1(SPI_CMD_WREN);
|
||||
fmc_write(host, FMC_CMD, reg);
|
||||
fmc_pr(WE_DBG, "\t||-Set CMD[%#x]%#x\n", FMC_CMD, reg);
|
||||
|
||||
reg = op_cfg_fm_cs(host->cmd_op.cs) | OP_CFG_OEN_EN;
|
||||
fmc_write(host, FMC_OP_CFG, reg);
|
||||
fmc_pr(WE_DBG, "\t||-Set OP_CFG[%#x]%#x\n", FMC_OP_CFG, reg);
|
||||
|
||||
reg = fmc_op_cmd1_en(ENABLE) | FMC_OP_REG_OP_START;
|
||||
fmc_write(host, FMC_OP, reg);
|
||||
fmc_pr(WE_DBG, "\t||-Set OP[%#x]%#x\n", FMC_OP, reg);
|
||||
|
||||
fmc_cmd_wait_cpu_finish(host);
|
||||
}
|
||||
|
||||
/*
|
||||
* Send write enable cmd to SPI Nand, status[C0H]:[2]bit WEL must be set 1
|
||||
*/
|
||||
static int spi_general_write_enable(struct fmc_spi *spi)
|
||||
{
|
||||
unsigned int reg;
|
||||
struct fmc_host *host = (struct fmc_host *)spi->host;
|
||||
int ret;
|
||||
|
||||
if (WE_DBG)
|
||||
printf("\n");
|
||||
fmc_pr(WE_DBG, "\t|*-Start Write Enable\n");
|
||||
|
||||
ret = spi_nand_feature_op(spi, GET_OP, STATUS_ADDR, ®);
|
||||
if (ret) {
|
||||
db_msg("Error: Get status reg failed,line:%d\n", __LINE__);
|
||||
return ret;
|
||||
}
|
||||
if (reg & STATUS_WEL_MASK) {
|
||||
fmc_pr(WE_DBG, "\t||-Write Enable was opened! reg: %#x\n", reg);
|
||||
return 0;
|
||||
}
|
||||
write_enable_fmc_op(host);
|
||||
#if WE_DBG
|
||||
spi->driver->wait_ready(spi);
|
||||
|
||||
ret = spi_nand_feature_op(spi, GET_OP, STATUS_ADDR, ®);
|
||||
if (ret) {
|
||||
db_msg("Error: Get status reg failed,line:%d\n", __LINE__);
|
||||
return ret;
|
||||
}
|
||||
if (reg & STATUS_WEL_MASK) {
|
||||
fmc_pr(WE_DBG, "\t||-Write Enable success. reg: %#x\n", reg);
|
||||
} else {
|
||||
db_msg("Error: Write Enable failed! reg: %#x\n", reg);
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
|
||||
fmc_pr(WE_DBG, "\t|*-End Write Enable\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* judge whether SPI Nand support QUAD read/write or not
|
||||
*/
|
||||
static int spi_is_quad(struct fmc_spi *spi)
|
||||
{
|
||||
const char *if_str[] = {"STD", "DUAL", "DIO", "QUAD", "QIO"};
|
||||
|
||||
fmc_pr(QE_DBG, "\t\t|||*-SPI read iftype: %s write iftype: %s\n",
|
||||
if_str[spi->read->iftype], if_str[spi->write->iftype]);
|
||||
|
||||
if ((spi->read->iftype == IF_TYPE_QUAD) ||
|
||||
(spi->read->iftype == IF_TYPE_QIO) ||
|
||||
(spi->write->iftype == IF_TYPE_QUAD) ||
|
||||
(spi->write->iftype == IF_TYPE_QIO))
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Send set features cmd to SPI Nand, feature[B0H]:[0]bit QE would be set
|
||||
*/
|
||||
static int spi_general_qe_enable(struct fmc_spi *spi)
|
||||
{
|
||||
unsigned int reg;
|
||||
int op;
|
||||
int ret;
|
||||
|
||||
const char *str[] = {"Disable", "Enable"};
|
||||
|
||||
fmc_pr(QE_DBG, "\t||*-Start SPI Nand flash QE\n");
|
||||
|
||||
op = spi_is_quad(spi);
|
||||
|
||||
fmc_pr(QE_DBG, "\t|||*-End Quad check, SPI Nand %s Quad.\n", str[op]);
|
||||
|
||||
ret = spi_nand_feature_op(spi, GET_OP, FEATURE_ADDR, ®);
|
||||
if (ret) {
|
||||
db_msg("Error: Get feature reg failed,line:%d\n", __LINE__);
|
||||
return ret;
|
||||
}
|
||||
fmc_pr(QE_DBG, "\t|||-Get [%#x]feature: %#x\n", FEATURE_ADDR, reg);
|
||||
if ((reg & FEATURE_QE_ENABLE) == op) {
|
||||
fmc_pr(QE_DBG, "\t||*-SPI Nand quad was %sd!\n", str[op]);
|
||||
return op;
|
||||
}
|
||||
|
||||
if (op == ENABLE)
|
||||
reg |= FEATURE_QE_ENABLE;
|
||||
else
|
||||
reg &= ~FEATURE_QE_ENABLE;
|
||||
|
||||
ret = spi_nand_feature_op(spi, SET_OP, FEATURE_ADDR, ®);
|
||||
if (ret) {
|
||||
db_msg("Error: set feature reg failed,line:%d\n", __LINE__);
|
||||
return ret;
|
||||
}
|
||||
fmc_pr(QE_DBG, "\t|||-SPI Nand %s Quad\n", str[op]);
|
||||
|
||||
spi->driver->wait_ready(spi);
|
||||
|
||||
ret = spi_nand_feature_op(spi, GET_OP, FEATURE_ADDR, ®);
|
||||
if (ret) {
|
||||
db_msg("Error: Get feature reg failed,line:%d\n", __LINE__);
|
||||
return ret;
|
||||
}
|
||||
if ((reg & FEATURE_QE_ENABLE) == op)
|
||||
fmc_pr(QE_DBG, "\t|||-SPI Nand %s Quad succeed!\n", str[op]);
|
||||
else
|
||||
db_msg("Error: %s Quad failed! reg: %#x\n", str[op], reg);
|
||||
|
||||
fmc_pr(QE_DBG, "\t||*-End SPI Nand %s Quad.\n", str[op]);
|
||||
|
||||
return op;
|
||||
}
|
||||
|
||||
/* some spi nand flash don't QUAD enable */
|
||||
static int spi_do_not_qe_enable(struct fmc_spi *spi)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,92 @@
|
||||
/*
|
||||
* Copyright (c) LOTUS. All rights reserved.
|
||||
*/
|
||||
|
||||
#include <linux/string.h>
|
||||
|
||||
#include <linux/lotus/match_table.h>
|
||||
|
||||
int reg2type(const struct match_reg_type *table, int length, int reg, int def)
|
||||
{
|
||||
while (length-- > 0) {
|
||||
if (table->reg == reg)
|
||||
return table->type;
|
||||
table++;
|
||||
}
|
||||
return def;
|
||||
}
|
||||
|
||||
int type2reg(const struct match_reg_type *table, int length, int type, int def)
|
||||
{
|
||||
while (length-- > 0) {
|
||||
if (table->type == type)
|
||||
return table->reg;
|
||||
table++;
|
||||
}
|
||||
return def;
|
||||
}
|
||||
|
||||
int str2type(const struct match_type_str *table, int length, const char *str,
|
||||
int size, int def)
|
||||
{
|
||||
while (length-- > 0) {
|
||||
if (!strncmp(table->str, str, size))
|
||||
return table->type;
|
||||
table++;
|
||||
}
|
||||
return def;
|
||||
}
|
||||
|
||||
const char *type2str(const struct match_type_str *table, int length, int type,
|
||||
const char *def)
|
||||
{
|
||||
while (length-- > 0) {
|
||||
if (table->type == type)
|
||||
return table->str;
|
||||
table++;
|
||||
}
|
||||
return def;
|
||||
}
|
||||
|
||||
int match_reg_to_type(const struct match_t *table, int nr_table, int reg, int def)
|
||||
{
|
||||
while (nr_table-- > 0) {
|
||||
if (table->reg == reg)
|
||||
return table->type;
|
||||
table++;
|
||||
}
|
||||
return def;
|
||||
}
|
||||
|
||||
int match_type_to_reg(const struct match_t *table, int nr_table, int type, int def)
|
||||
{
|
||||
while (nr_table-- > 0) {
|
||||
if (table->type == type)
|
||||
return table->reg;
|
||||
table++;
|
||||
}
|
||||
return def;
|
||||
}
|
||||
|
||||
int match_data_to_type(const struct match_t *table, int nr_table, const char *data,
|
||||
int size, int def)
|
||||
{
|
||||
while (nr_table-- > 0) {
|
||||
if (!memcmp(table->data, data, size))
|
||||
return table->type;
|
||||
table++;
|
||||
}
|
||||
return def;
|
||||
}
|
||||
|
||||
void *match_type_to_data(const struct match_t *table, int nr_table, int type,
|
||||
void *def)
|
||||
{
|
||||
while (nr_table-- > 0) {
|
||||
if (table->type == type)
|
||||
return table->data;
|
||||
table++;
|
||||
}
|
||||
return def;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,165 @@
|
||||
/*
|
||||
* Copyright (c) LOTUS. All rights reserved.
|
||||
*/
|
||||
|
||||
#include <linux/lotus/nfc_common.h>
|
||||
#include <linux/lotus/match_table.h>
|
||||
/*****************************************************************************/
|
||||
struct nand_flash_dev *(*get_flash_type)(
|
||||
struct mtd_info *mtd,
|
||||
struct nand_chip *chip,
|
||||
unsigned char *id) = NULL;
|
||||
|
||||
int (*nand_oob_resize)(struct mtd_info *mtd) = NULL;
|
||||
|
||||
/*****************************************************************************/
|
||||
#if defined(CONFIG_FMC_SPI_NAND) || defined(CONFIG_FMC_NAND)
|
||||
static struct match_type_str ecc2name[] = {
|
||||
{NAND_ECC_0BIT, "none" },
|
||||
{NAND_ECC_8BIT, "4bit/512" },
|
||||
{NAND_ECC_16BIT, "8bit/512" },
|
||||
{NAND_ECC_24BIT, "24bit/1K" },
|
||||
{NAND_ECC_28BIT, "28bit/1K" },
|
||||
{NAND_ECC_40BIT, "40bit/1K" },
|
||||
{NAND_ECC_42BIT, "42bit/1K" },
|
||||
{NAND_ECC_64BIT, "64bit/1K" },
|
||||
};
|
||||
|
||||
const char *nand_ecc_name(int type)
|
||||
{
|
||||
return type2str(ecc2name, ARRAY_SIZE(ecc2name), type, "unknown");
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
static struct match_type_str page2name[] = {
|
||||
{ NAND_PAGE_512B, "512" },
|
||||
{ NAND_PAGE_2K, "2K" },
|
||||
{ NAND_PAGE_4K, "4K" },
|
||||
{ NAND_PAGE_8K, "8K" },
|
||||
{ NAND_PAGE_16K, "16K" },
|
||||
{ NAND_PAGE_32K, "32K" },
|
||||
};
|
||||
|
||||
const char *nand_page_name(int type)
|
||||
{
|
||||
return type2str(page2name, ARRAY_SIZE(page2name), type, "unknown");
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
static struct match_reg_type page2size[] = {
|
||||
{ _512B, NAND_PAGE_512B },
|
||||
{ _2K, NAND_PAGE_2K },
|
||||
{ _4K, NAND_PAGE_4K },
|
||||
{ _8K, NAND_PAGE_8K },
|
||||
{ _16K, NAND_PAGE_16K },
|
||||
{ _32K, NAND_PAGE_32K },
|
||||
};
|
||||
|
||||
int nandpage_size2type(int size)
|
||||
{
|
||||
return reg2type(page2size, ARRAY_SIZE(page2size), size, NAND_PAGE_2K);
|
||||
}
|
||||
|
||||
int nandpage_type2size(int size)
|
||||
{
|
||||
return type2reg(page2size, ARRAY_SIZE(page2size), size, NAND_PAGE_2K);
|
||||
}
|
||||
#endif
|
||||
|
||||
/*****************************************************************************/
|
||||
#define ET_ECC_NONE 0x00
|
||||
#define ET_ECC_4BIT 0x02
|
||||
#define ET_ECC_8BIT 0x03
|
||||
#define ET_ECC_24BIT 0x04
|
||||
#define ET_ECC_40BIT1K 0x05
|
||||
#define ET_ECC_64BIT1K 0x06
|
||||
|
||||
static struct match_reg_type ecc_yaffs_type_t[] = {
|
||||
{ET_ECC_NONE, NAND_ECC_0BIT},
|
||||
{ET_ECC_4BIT, NAND_ECC_8BIT},
|
||||
{ET_ECC_8BIT, NAND_ECC_16BIT},
|
||||
{ET_ECC_24BIT, NAND_ECC_24BIT},
|
||||
{ET_ECC_40BIT1K, NAND_ECC_40BIT},
|
||||
{ET_ECC_64BIT1K, NAND_ECC_64BIT}
|
||||
};
|
||||
|
||||
unsigned char match_ecc_type_to_yaffs(unsigned char type)
|
||||
{
|
||||
return type2reg(ecc_yaffs_type_t, ARRAY_SIZE(ecc_yaffs_type_t), type,
|
||||
ET_ECC_4BIT);
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
static struct match_t page_table[] = {
|
||||
{NAND_PAGE_2K, PAGE_SIZE_2KB, "2K"},
|
||||
{NAND_PAGE_4K, PAGE_SIZE_4KB, "4K"},
|
||||
{NAND_PAGE_8K, PAGE_SIZE_8KB, "8K"},
|
||||
{NAND_PAGE_16K, PAGE_SIZE_16KB, "16K"},
|
||||
};
|
||||
|
||||
unsigned char match_page_reg_to_type(unsigned char reg)
|
||||
{
|
||||
return match_reg_to_type(page_table, ARRAY_SIZE(page_table), reg,
|
||||
NAND_PAGE_2K);
|
||||
}
|
||||
|
||||
unsigned char match_page_type_to_reg(unsigned char type)
|
||||
{
|
||||
return match_type_to_reg(page_table, ARRAY_SIZE(page_table), type,
|
||||
PAGE_SIZE_2KB);
|
||||
}
|
||||
|
||||
const char *match_page_type_to_str(unsigned char type)
|
||||
{
|
||||
return match_type_to_data(page_table, ARRAY_SIZE(page_table), type,
|
||||
"unknown");
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
static struct match_t ecc_table[] = {
|
||||
{NAND_ECC_0BIT, ECC_TYPE_0BIT, "none"},
|
||||
{NAND_ECC_8BIT, ECC_TYPE_8BIT, "4bit/512"},
|
||||
{NAND_ECC_16BIT, ECC_TYPE_16BIT, "8bit/512"},
|
||||
{NAND_ECC_24BIT, ECC_TYPE_24BIT, "24bit/1K"},
|
||||
{NAND_ECC_28BIT, ECC_TYPE_28BIT, "28bit/1K"},
|
||||
{NAND_ECC_40BIT, ECC_TYPE_40BIT, "40bit/1K"},
|
||||
{NAND_ECC_64BIT, ECC_TYPE_64BIT, "64bit/1K"},
|
||||
};
|
||||
|
||||
unsigned char match_ecc_reg_to_type(unsigned char reg)
|
||||
{
|
||||
return match_reg_to_type(ecc_table, ARRAY_SIZE(ecc_table), reg,
|
||||
NAND_ECC_8BIT);
|
||||
}
|
||||
|
||||
unsigned char match_ecc_type_to_reg(unsigned char type)
|
||||
{
|
||||
return match_type_to_reg(ecc_table, ARRAY_SIZE(ecc_table), type,
|
||||
ECC_TYPE_8BIT);
|
||||
}
|
||||
|
||||
const char *match_ecc_type_to_str(unsigned char type)
|
||||
{
|
||||
return match_type_to_data(ecc_table, ARRAY_SIZE(ecc_table), type,
|
||||
"unknown");
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
static struct match_t page_type_size_table[] = {
|
||||
{NAND_PAGE_2K, _2K, NULL},
|
||||
{NAND_PAGE_4K, _4K, NULL},
|
||||
{NAND_PAGE_8K, _8K, NULL},
|
||||
{NAND_PAGE_16K, _16K, NULL},
|
||||
};
|
||||
|
||||
unsigned char match_page_size_to_type(unsigned int size)
|
||||
{
|
||||
return match_reg_to_type(page_type_size_table,
|
||||
ARRAY_SIZE(page_type_size_table), size, NAND_PAGE_2K);
|
||||
}
|
||||
|
||||
unsigned int match_page_type_to_size(unsigned char type)
|
||||
{
|
||||
return match_type_to_reg(page_type_size_table,
|
||||
ARRAY_SIZE(page_type_size_table), type, _2K);
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
|
||||
config FMC_SPI_NOR
|
||||
bool "lotus SPI Nor Flash Interface support"
|
||||
depends on FMC
|
||||
help
|
||||
Enable the lotus SPI Nor flash support.
|
||||
|
||||
If unsure, say N
|
||||
|
||||
config SPI_BLOCK_PROTECT
|
||||
bool "Spi Nor Device BP(Block Protect) Support"
|
||||
depends on FMC_SPI_NOR
|
||||
help
|
||||
SFC supports BP(Block Protect) feature to preestablish a series
|
||||
area to avoid writing and erasing, except to reading. With this macro
|
||||
definition we can get the BP info which was setted before. The
|
||||
BOTTOM/TOP bit is setted to BOTTOM, it means the lock area starts
|
||||
from 0 address.
|
||||
|
||||
If unsure, say N
|
||||
|
||||
config DTR_MODE_SUPPORT
|
||||
bool "Spi Nor Device DTR mode Support"
|
||||
depends on FMC_SPI_NOR
|
||||
default n
|
||||
help
|
||||
To support DTR mode
|
||||
|
||||
If unsure, say N
|
||||
@@ -0,0 +1,4 @@
|
||||
|
||||
obj-y = spi_nor.o
|
||||
|
||||
obj-$(CONFIG_FMC_SPI_NOR) += fmc100/
|
||||
@@ -0,0 +1,4 @@
|
||||
|
||||
ccflags-y += -I$(srctree)/lotus/drivers/mtd
|
||||
|
||||
obj-y += fmc100.o fmc100_os.o fmc_spi_nor_ids.o
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,169 @@
|
||||
/*
|
||||
* Copyright (c) LOTUS. All rights reserved.
|
||||
*/
|
||||
|
||||
#ifndef __FMC100_H__
|
||||
#define __FMC100_H__
|
||||
|
||||
#include <spi_flash.h>
|
||||
#include <linux/lotus/fmc_common.h>
|
||||
#include "fmc_spi_ids.h"
|
||||
#include <cpu_func.h>
|
||||
|
||||
/* These macroes are for debug only, reg read is slower then dma read,
|
||||
so we don't define it */
|
||||
#undef FMC100_SPI_NOR_SUPPORT_REG_READ
|
||||
#undef FMC100_SPI_NOR_SUPPORT_REG_WRITE
|
||||
|
||||
#define FMC100_DMA_WR_MAX_SIZE 4096
|
||||
#define FMC100_DMA_WR_MASK (FMC100_DMA_WR_MAX_SIZE - 1)
|
||||
#define FMC100_DMA_RD_MAX_SIZE (_2M)
|
||||
#define FMC100_DMA_RD_MASK (FMC100_DMA_RD_MAX_SIZE - 1)
|
||||
#define FMC100_REG_RD_MAX_SIZE (_16K)
|
||||
#define FMC100_REG_RD_MASK (FMC100_REG_RD_MAX_SIZE - 1)
|
||||
|
||||
#define SPI_NOR_CR_SHIFT 8 /* Config Register shift(bit) */
|
||||
|
||||
#define SPI_NOR_CR_4BYTE_SHIFT 5
|
||||
#define SPI_NOR_CR_4BYTE_MASK (1 << SPI_NOR_CR_4BYTE_SHIFT)
|
||||
#define spi_nor_get_4byte_by_cr(cr) (((cr) & SPI_NOR_CR_4BYTE_MASK) \
|
||||
>> SPI_NOR_CR_4BYTE_SHIFT)
|
||||
|
||||
#define SPI_NOR_CR_QE_SHIFT 1
|
||||
#define SPI_NOR_CR_QE_MASK (1 << SPI_NOR_CR_QE_SHIFT)
|
||||
#define spi_nor_get_qe_by_cr(cr) (((cr) & SPI_NOR_CR_QE_MASK) \
|
||||
>> SPI_NOR_CR_QE_SHIFT)
|
||||
|
||||
#define SPI_NOR_CR_RST_HOLD_SHIFT 7
|
||||
#define SPI_NOR_CR_RST_HOLD_MASK (1 << SPI_NOR_CR_RST_HOLD_SHIFT)
|
||||
#define SPI_NOR_CR_HOLD_MASK (~(1 << SPI_NOR_CR_RST_HOLD_SHIFT))
|
||||
#define spi_nor_get_rst_hold_by_cr(cr) (((cr) & SPI_NOR_CR_RST_HOLD_MASK) \
|
||||
>> SPI_NOR_CR_RST_HOLD_SHIFT)
|
||||
#define spi_nor_set_rst_by_cr(cr) ((cr) | SPI_NOR_CR_RST_HOLD_MASK)
|
||||
#define spi_nor_set_hold_by_cr(cr) ((cr) & SPI_NOR_CR_HOLD_MASK)
|
||||
|
||||
#ifdef CONFIG_SPI_BLOCK_PROTECT
|
||||
#define DEBUG_SPI_NOR_BP 0
|
||||
|
||||
#define SPI_NOR_SR_SRWD_SHIFT 7
|
||||
#define SPI_NOR_SR_SRWD_MASK (1 << SPI_NOR_SR_SRWD_SHIFT)
|
||||
|
||||
#define SPI_NOR_SR_BP0_SHIFT 2
|
||||
#define SPI_NOR_SR_BP_WIDTH_4 0xf
|
||||
#define SPI_NOR_SR_BP_MASK_4 (SPI_NOR_SR_BP_WIDTH_4 << SPI_NOR_SR_BP0_SHIFT)
|
||||
|
||||
#define SPI_NOR_SR_BP_WIDTH_3 0x7
|
||||
#define SPI_NOR_SR_BP_MASK_3 (SPI_NOR_SR_BP_WIDTH_3 << SPI_NOR_SR_BP0_SHIFT)
|
||||
|
||||
#define SPI_NOR_SR_TB_SHIFT 3
|
||||
#define SPI_NOR_SR_TB_MASK (1 << SPI_NOR_SR_TB_SHIFT)
|
||||
|
||||
#define SPI_NOR_SR_TB_SHIFT_S 5
|
||||
#define SPI_NOR_SR_TB_MASK_S (1 << SPI_NOR_SR_TB_SHIFT_S)
|
||||
|
||||
#define spi_bp_bottom_rdcr_set_s(config) ((config) | \
|
||||
(0x01 << SPI_NOR_SR_TB_SHIFT_S))
|
||||
#define spi_bp_bottom_rdcr_set(config) ((config) | \
|
||||
(0x01 << SPI_NOR_SR_TB_SHIFT))
|
||||
|
||||
#define spi_bp_bottom_rdsr_set_1(bp_num) (0x1 << (2 + bp_num))
|
||||
#define spi_bp_bottom_rdsr_set_0(bp_num) (~(0x1 << (2 + bp_num)))
|
||||
|
||||
#define lock_level_max(bp_num) (((0x01) << bp_num) - 1)
|
||||
|
||||
#endif /* CONFIG_SPI_BLOCK_PROTECT */
|
||||
|
||||
#ifdef CONFIG_DTR_MODE_SUPPORT
|
||||
#define DTR_DUMMY_CYCLES_4 4
|
||||
#define DTR_DUMMY_CYCLES_6 6
|
||||
#define DTR_DUMMY_CYCLES_8 8
|
||||
#define DTR_DUMMY_CYCLES_10 10
|
||||
#define dtr_rdcr_dc_mask(_val) (_val)
|
||||
#define DTR_RDSR_DC_SHIFT 14
|
||||
#define DTR_RDCR_DC_SHIFT 6
|
||||
#define dtr_rdcr_dc_bit_clr(_reg) ((_reg) & (~(3 << DTR_RDSR_DC_SHIFT)))
|
||||
#define dtr_gd_dc_bit_clr(_reg) ((_reg) & (0xfe))
|
||||
#define dtr_py_dc_bit_clr(_reg) ((_reg) & (0xf7))
|
||||
#define dtr_xmc_dc_bit_clr(_reg) ((_reg) & (~(3)))
|
||||
#define DTR_MODE_REQUEST_SHIFT 2
|
||||
|
||||
#define DTR_TRAINING_POINT_NUM 12
|
||||
#define DTR_TRAINING_POINT_MASK 12
|
||||
#define dtr_training_point_clr(_reg) ((_reg) & (~(0xf << 12)))
|
||||
#define DTR_TRAINING_CMP_ADDR_SHIFT (CONFIG_BOOT_HEAD_SIZE)
|
||||
#define DTR_TRAINING_CMP_ADDR_S (CONFIG_SYS_TEXT_BASE_ORI + \
|
||||
DTR_TRAINING_CMP_ADDR_SHIFT)
|
||||
#define DTR_TRAINING_CMP_LEN 0x100
|
||||
#define SFDP_BUF_LEN 0x33
|
||||
#define SFDP_DTR_BIT_SHIFT 3
|
||||
#define SFDP_DTR_BYTE_SHIFT 0x32
|
||||
#define SFDP_DTR_BIT_MASK 0x1
|
||||
#define DEVICE_ID_SUPPORT_DTR_WINBOND 0x70
|
||||
#endif /* CONFIG_DTR_MODE_SUPPORT */
|
||||
|
||||
/* MXIC Config Register's dummy cycle bits */
|
||||
#define CR_DUMMY_CYCLE (0x03 << 6)
|
||||
#define SPI_CMD_RDCR_MX 0x15 /* MXIC Read Config Register */
|
||||
#define DTR_MODE_REQUEST_SHIFT 2
|
||||
#define SPI_NOR_SR_WIP_MASK (1 << 0)
|
||||
|
||||
struct fmc_host {
|
||||
struct spi_flash spi_nor_flash[1];
|
||||
struct mtd_info_ex *spi_nor_info;
|
||||
struct fmc_spi spi[CONFIG_SPI_NOR_MAX_CHIP_NUM];
|
||||
|
||||
void *regbase;
|
||||
void *iobase;
|
||||
|
||||
void (*set_system_clock)(struct spi_op *op, int clk_en);
|
||||
void (*set_host_addr_mode)(struct fmc_host *host, int enable);
|
||||
|
||||
#ifdef CONFIG_SPI_BLOCK_PROTECT
|
||||
unsigned int start_addr;
|
||||
unsigned int end_addr;
|
||||
unsigned char cmp;
|
||||
unsigned int bp_num;
|
||||
/* the BT bit location, decide the data num count */
|
||||
unsigned int bt_loc;
|
||||
unsigned char level;
|
||||
#endif
|
||||
#ifdef CONFIG_DTR_MODE_SUPPORT
|
||||
unsigned int dtr_mode_en;
|
||||
unsigned int dtr_training_flag;
|
||||
#endif
|
||||
};
|
||||
|
||||
#ifdef CONFIG_SPI_BLOCK_PROTECT
|
||||
unsigned short fmc100_set_spi_lock_info(struct fmc_host *host);
|
||||
void fmc100_get_bp_lock_level(struct fmc_host *host);
|
||||
void fmc100_spi_lock(struct fmc_host *host, unsigned char level);
|
||||
void fmc100_spi_flash_lock(unsigned char cmp, unsigned char level,
|
||||
unsigned char op);
|
||||
unsigned short fmc100_handle_bp_rdcr_info(struct fmc_host *host,
|
||||
u_char cmd);
|
||||
unsigned char fmc100_bp_to_level(struct fmc_host *host);
|
||||
unsigned short fmc100_handle_bp_rdsr_info(struct fmc_host *host,
|
||||
u_char cmd);
|
||||
#endif
|
||||
unsigned char spi_general_get_flash_register(struct fmc_spi *spi,
|
||||
u_char cmd);
|
||||
|
||||
#define spiflash_to_host(_spiflash) ((struct fmc_host *)(_spiflash))
|
||||
|
||||
#ifdef CONFIG_DTR_MODE_SUPPORT
|
||||
void fmc_dtr_mode_ctrl(struct fmc_spi *spi, int dtr_en);
|
||||
unsigned int spi_dtr_training(struct fmc_host *host);
|
||||
void spi_dtr_to_sdr_switch(struct fmc_spi *spi);
|
||||
int spi_dtr_dummy_training_set(struct fmc_host *host, int dtr_en);
|
||||
void fmc_check_spi_dtr_support(struct fmc_spi *spi, u_char *ids, int len);
|
||||
unsigned int spi_mxic_check_spi_dtr_support(struct fmc_spi *spi);
|
||||
#endif
|
||||
|
||||
void fmc100_read_ids(const struct fmc_spi *, u_char, u_char* const);
|
||||
void fmc100_op_reg(struct fmc_spi *spi, unsigned char opcode,
|
||||
unsigned int len, unsigned char optype);
|
||||
int fmc_spi_nor_probe(struct mtd_info_ex *mtd, struct fmc_spi *spi);
|
||||
int fmc100_spi_nor_init(struct fmc_host *);
|
||||
struct spi_flash *fmc100_spi_nor_scan(struct fmc_host *host);
|
||||
|
||||
#endif /* End of __FMC100_H__ */
|
||||
+183
@@ -0,0 +1,183 @@
|
||||
/*
|
||||
* Copyright (c) LOTUS. All rights reserved.
|
||||
*/
|
||||
|
||||
#include "fmc100_os.h"
|
||||
#include <common.h>
|
||||
#include <linux/mtd/mtd.h>
|
||||
#include <asm/io.h>
|
||||
|
||||
static struct fmc_host fmc100_host;
|
||||
static struct mtd_info_ex fmc100_spi_nor_info = {.type = 0, };
|
||||
|
||||
static void fmc100_driver_shutdown(void)
|
||||
{
|
||||
unsigned int start_up_addr_mode = get_fmc_boot_mode();
|
||||
if (start_up_addr_mode == SPI_NOR_ADDR_MODE_3_BYTES) {
|
||||
int ix;
|
||||
struct fmc_host *host = &fmc100_host;
|
||||
struct fmc_spi *spi = host->spi;
|
||||
struct mtd_info_ex *spi_nor_info = &fmc100_spi_nor_info;
|
||||
|
||||
fmc_dev_type_switch(FLASH_TYPE_SPI_NOR);
|
||||
|
||||
for (ix = 0; ix < spi_nor_info->numchips; ix++, spi++) {
|
||||
/* 4 byte addr mode */
|
||||
if (spi->addrcycle == 4) {
|
||||
spi->driver->wait_ready(spi);
|
||||
spi->driver->entry_4addr(spi, DISABLE);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int fmc100_driver_probe(void)
|
||||
{
|
||||
int ret;
|
||||
struct fmc_host *host = &fmc100_host;
|
||||
|
||||
fmc_pr(BT_DBG, "\t|*-Start SPI nor flash driver probe\n");
|
||||
|
||||
/* FMC ip version check */
|
||||
ret = fmc_ip_ver_check();
|
||||
if (ret) {
|
||||
fmc_pr(BT_DBG, "\t|*-IP version unknown, result: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
fmc_pr(BT_DBG, "\t||-SPI nor host init\n");
|
||||
memset((char *)host, 0, sizeof(struct fmc_host));
|
||||
ret = fmc100_spi_nor_init(host);
|
||||
if (ret) {
|
||||
fmc_pr(BT_DBG, "Error: SPI Nor init failed, ret: %d\n", ret);
|
||||
goto end;
|
||||
}
|
||||
|
||||
end:
|
||||
fmc_pr(BT_DBG, "\t|*-End SPI nor flash driver probe\n");
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
struct mtd_info_ex *fmc100_get_spi_nor_info(struct spi_flash *spi_nor_flash)
|
||||
{
|
||||
if (fmc100_spi_nor_info.type == 0) {
|
||||
if (fmc100_spi_nor_probe(NULL) == NULL)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return &fmc100_spi_nor_info;
|
||||
}
|
||||
|
||||
static void fmc100_probe_spi_size(struct spi_flash* const spi_nor_flash)
|
||||
{
|
||||
struct fmc_host *host = &fmc100_host;
|
||||
struct fmc_spi *spi = host->spi;
|
||||
unsigned int ix;
|
||||
unsigned int total = 0;
|
||||
struct mtd_info_ex *spi_nor_info = host->spi_nor_info;
|
||||
|
||||
fmc_pr(BT_DBG, "\t|*-Start probe SPI nor flash total size\n");
|
||||
for (ix = 0; ix < spi_nor_info->numchips; ix++, spi++) {
|
||||
fmc_pr(BT_DBG, "\t||-SPI nor flash[%d]: %dMB\n", ix,
|
||||
(u_int)byte_to_mb(spi->chipsize));
|
||||
total += spi->chipsize;
|
||||
}
|
||||
|
||||
spi_nor_flash->size = total;
|
||||
|
||||
fmc_pr(BT_DBG, "\t|*-Probe SPI nor total size: %dMB, chip num: %d\n",
|
||||
byte_to_mb(spi_nor_flash->size), spi_nor_info->numchips);
|
||||
}
|
||||
|
||||
struct spi_flash *fmc100_spi_nor_probe(struct mtd_info_ex **spi_nor_info)
|
||||
{
|
||||
static struct spi_flash *spi_nor_flash = NULL;
|
||||
|
||||
fmc_pr(BT_DBG, "\t*-Start SPI Nor flash probe\n");
|
||||
|
||||
if (spi_nor_flash) {
|
||||
fmc_pr(BT_DBG, "\t*-SPI Nor flash is initialized.\n");
|
||||
return spi_nor_flash;
|
||||
}
|
||||
|
||||
/* Check current SPI device type whether SPI nor */
|
||||
fmc_dev_type_switch(FLASH_TYPE_SPI_NOR);
|
||||
|
||||
fmc_pr(BT_DBG, "\t|-SPI Nor flash driver probe\n");
|
||||
if (!fmc100_driver_probe()) {
|
||||
struct fmc_host *host = &fmc100_host;
|
||||
|
||||
fmc_pr(BT_DBG, "\t|-SPI nor flash scanning\n");
|
||||
host->spi_nor_info = &fmc100_spi_nor_info;
|
||||
spi_nor_flash = fmc100_spi_nor_scan(host);
|
||||
if (spi_nor_flash) {
|
||||
*spi_nor_info =
|
||||
fmc100_get_spi_nor_info(spi_nor_flash);
|
||||
if (*spi_nor_info == NULL)
|
||||
return NULL;
|
||||
|
||||
fmc100_probe_spi_size(spi_nor_flash);
|
||||
printf("SPI Nor total size: %uMB\n",
|
||||
byte_to_mb(spi_nor_flash->size));
|
||||
fmc_pr(BT_DBG, "\t|-Add func hook for Reset cmd\n");
|
||||
add_shutdown(fmc100_driver_shutdown);
|
||||
|
||||
goto end;
|
||||
}
|
||||
}
|
||||
|
||||
spi_nor_flash = NULL;
|
||||
fmc100_spi_nor_info.type = 0;
|
||||
|
||||
end:
|
||||
/* Change SPI device type to default */
|
||||
fmc_dev_type_switch(FLASH_TYPE_DEFAULT);
|
||||
|
||||
fmc_pr(BT_DBG, "\t*-End SPI Nor flash probe\n");
|
||||
|
||||
return spi_nor_flash;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_SPI_BLOCK_PROTECT
|
||||
void fmc100_spi_flash_lock(unsigned char cmp, unsigned char level,
|
||||
unsigned char op)
|
||||
{
|
||||
struct fmc_host *host = &fmc100_host;
|
||||
struct spi_flash *nor = host->spi_nor_flash;
|
||||
|
||||
host->cmp = cmp;
|
||||
|
||||
if (op == BP_OP_GET) {
|
||||
puts("Get spi lock information\n");
|
||||
if (host->level) {
|
||||
if (host->level == nor->bp_level_max)
|
||||
puts("all blocks are locked.\n");
|
||||
else
|
||||
printf("level: %d\n", host->level);
|
||||
printf("Spi is locked. lock address[0 => %#x]\n",
|
||||
host->end_addr);
|
||||
} else {
|
||||
puts("all blocks are unlocked.\n");
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (op == BP_OP_SET) {
|
||||
if (level) {
|
||||
if (level == nor->bp_level_max)
|
||||
puts("lock all blocks.\n");
|
||||
else
|
||||
printf("lock level: %d\n", level);
|
||||
} else {
|
||||
puts("unlock all block.\n");
|
||||
}
|
||||
|
||||
fmc100_spi_lock(host, level);
|
||||
return;
|
||||
}
|
||||
|
||||
printf("%s ERROR: Invalid optin argument!", __func__);
|
||||
}
|
||||
#endif /* CONFIG_SPI_BLOCK_PROTECT */
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
/*
|
||||
* Copyright (c) LOTUS. All rights reserved.
|
||||
*/
|
||||
|
||||
#ifndef __FMC100_OS_H__
|
||||
#define __FMC100_OS_H__
|
||||
|
||||
#include <linux/lotus/fmc.h>
|
||||
|
||||
#include "fmc100.h"
|
||||
|
||||
/*****************************************************************************/
|
||||
#endif /* End of __FMC100_OS_H__ */
|
||||
|
||||
+280
@@ -0,0 +1,280 @@
|
||||
/*
|
||||
* Copyright (c) LOTUS. All rights reserved.
|
||||
*/
|
||||
|
||||
#define GD_SPI_CMD_RDSR1 0x35 /* Read Status Register-1 */
|
||||
|
||||
/*
|
||||
* enable QE bit if QUAD read write is supported by GD "25qxxx" SPI
|
||||
*/
|
||||
#ifndef CONFIG_DTR_MODE_SUPPORT
|
||||
static void clear_dtr_mode_gd(struct fmc_spi *spi)
|
||||
{
|
||||
unsigned int regval;
|
||||
struct fmc_host *host = (struct fmc_host *)spi->host;
|
||||
unsigned char config;
|
||||
unsigned short reg;
|
||||
|
||||
config = spi_general_get_flash_register(spi, SPI_CMD_RDSR3);
|
||||
fmc_pr(DTR_DB, "Get Status Register-3[%#x]\n", config);
|
||||
regval = fmc_read(host, FMC_GLOBAL_CFG);
|
||||
if ((regval >> DTR_MODE_REQUEST_SHIFT) & 0x1) {
|
||||
regval &= (~(1 << DTR_MODE_REQUEST_SHIFT));
|
||||
regval = fmc_write(host, FMC_GLOBAL_CFG, regval);
|
||||
}
|
||||
|
||||
if (config & 0x1) {
|
||||
config &= (0xfe);
|
||||
reg = ((unsigned short)config);
|
||||
writew(reg, host->iobase);
|
||||
spi->driver->write_enable(spi);
|
||||
fmc100_op_reg(spi, SPI_CMD_WRSR3, sizeof(unsigned char),
|
||||
fmc_op_write_data_en(ENABLE));
|
||||
}
|
||||
}
|
||||
#endif
|
||||
static void set_cmd(struct fmc_spi* const spi, u8 cmd, u8 len)
|
||||
{
|
||||
struct fmc_host *host = (struct fmc_host *)spi->host;
|
||||
unsigned int regval;
|
||||
|
||||
regval = fmc_cmd_cmd1(cmd);
|
||||
fmc_write(host, FMC_CMD, regval);
|
||||
fmc_pr(QE_DBG, "\t|-Set CMD[%#x]%#x\n", FMC_CMD, regval);
|
||||
|
||||
regval = op_cfg_fm_cs(spi->chipselect) | OP_CFG_OEN_EN;
|
||||
fmc_write(host, FMC_OP_CFG, regval);
|
||||
fmc_pr(QE_DBG, "\t|-Set OP_CFG[%#x]%#x\n", FMC_OP_CFG, regval);
|
||||
|
||||
regval = fmc_data_num_cnt(len);
|
||||
fmc_write(host, FMC_DATA_NUM, regval);
|
||||
fmc_pr(QE_DBG, "\t|-Set DATA_NUM[%#x]%#x\n", FMC_DATA_NUM, regval);
|
||||
|
||||
regval = fmc_op_cmd1_en(ENABLE) |
|
||||
fmc_op_write_data_en(ENABLE) |
|
||||
FMC_OP_REG_OP_START;
|
||||
fmc_write(host, FMC_OP, regval);
|
||||
fmc_pr(QE_DBG, "\t|-Set OP[%#x]%#x\n", FMC_OP, regval);
|
||||
|
||||
fmc_cmd_wait_cpu_finish(host);
|
||||
|
||||
spi->driver->wait_ready(spi);
|
||||
}
|
||||
|
||||
static int spi_gd25q256_entry_4addr(struct fmc_spi *spi, int enable)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int gd_16pin_qe_enable(struct fmc_spi * const spi, int op)
|
||||
{
|
||||
struct fmc_host *host = (struct fmc_host *)spi->host;
|
||||
unsigned char config;
|
||||
unsigned char status;
|
||||
const char *str[] = {"Disable", "Enable"};
|
||||
|
||||
#ifndef CONFIG_DTR_MODE_SUPPORT
|
||||
clear_dtr_mode_gd(spi);
|
||||
#endif
|
||||
config = spi_general_get_flash_register(spi, GD_SPI_CMD_RDSR1);
|
||||
fmc_pr(QE_DBG, "\t|-Read GD SR-1[%#x], val: %#x\n", GD_SPI_CMD_RDSR1,
|
||||
config);
|
||||
if (op && (op == spi_nor_get_qe_by_cr(config))) {
|
||||
fmc_pr(QE_DBG, "\t* Quad was %sd, status:%#x\n", str[op],
|
||||
config);
|
||||
return op;
|
||||
}
|
||||
|
||||
/* First, we enable/disable QE for 16Pin GD flash, use WRSR[01h] cmd */
|
||||
fmc_pr(QE_DBG, "\t|-First, 16Pin GD flash %s Quad.\n", str[op]);
|
||||
|
||||
status = spi_general_get_flash_register(spi, SPI_CMD_RDSR);
|
||||
fmc_pr(QE_DBG, "\t|-Read Status Register[%#x]%#x\n", SPI_CMD_RDSR,
|
||||
status);
|
||||
|
||||
spi->driver->write_enable(spi);
|
||||
|
||||
if (op)
|
||||
config |= SPI_NOR_CR_QE_MASK;
|
||||
else
|
||||
config &= ~SPI_NOR_CR_QE_MASK;
|
||||
writeb(status, host->iobase);
|
||||
writeb(config, host->iobase + SPI_NOR_SR_LEN);
|
||||
fmc_pr(QE_DBG, "\t|-Write IO[%p]%#x\n", host->iobase,
|
||||
*(unsigned short *)host->iobase);
|
||||
|
||||
set_cmd(spi, SPI_CMD_WRSR, SPI_NOR_SR_LEN + SPI_NOR_CR_LEN);
|
||||
|
||||
config = spi_general_get_flash_register(spi, GD_SPI_CMD_RDSR1);
|
||||
fmc_pr(QE_DBG, "\t|-Read GD SR-1[%#x], val: %#x\n", GD_SPI_CMD_RDSR1,
|
||||
config);
|
||||
if (op == spi_nor_get_qe_by_cr(config)) {
|
||||
fmc_pr(QE_DBG, "\t|-16P %s Quad success reg: %#x\n", str[op],
|
||||
config);
|
||||
return op;
|
||||
} else {
|
||||
fmc_pr(QE_DBG, "\t|-16P %s Quad failed, reg: %#x\n", str[op],
|
||||
config);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void gd_8pin_qe_enable(struct fmc_spi * const spi, int op)
|
||||
{
|
||||
unsigned char config;
|
||||
unsigned char status;
|
||||
const char *str[] = {"Disable", "Enable"};
|
||||
struct fmc_host *host = (struct fmc_host *)spi->host;
|
||||
|
||||
fmc_pr(QE_DBG, "\t|-Second, 8Pin GD flash %s Quad.\n", str[op]);
|
||||
|
||||
status = spi_general_get_flash_register(spi, SPI_CMD_RDSR);
|
||||
fmc_pr(QE_DBG, "\t|-Read Status Register[%#x]:%#x\n", SPI_CMD_RDSR,
|
||||
status);
|
||||
if (!(status & STATUS_WEL_MASK))
|
||||
spi->driver->write_enable(spi);
|
||||
|
||||
config = spi_general_get_flash_register(spi, SPI_CMD_RDSR2);
|
||||
fmc_pr(QE_DBG, "\t|-Read SR-2[%#x], val: %#x\n", SPI_CMD_RDSR2,
|
||||
config);
|
||||
|
||||
if (op && (op == spi_nor_get_qe_by_cr(config))) {
|
||||
fmc_pr(QE_DBG, "\t* Quad was %sd, status:%#x\n", str[op],
|
||||
config);
|
||||
return;
|
||||
}
|
||||
|
||||
if (op)
|
||||
config |= SPI_NOR_CR_QE_MASK;
|
||||
else
|
||||
config &= ~SPI_NOR_CR_QE_MASK;
|
||||
|
||||
writeb(config, host->iobase);
|
||||
fmc_pr(QE_DBG, "\t|-Write IO[%p]%#x\n", host->iobase,
|
||||
*(unsigned char *)host->iobase);
|
||||
|
||||
set_cmd(spi, SPI_CMD_WRSR2, SPI_NOR_CR_LEN);
|
||||
|
||||
config = spi_general_get_flash_register(spi, SPI_CMD_RDSR2);
|
||||
fmc_pr(QE_DBG, "\t|-Read GD SR-2[%#x], val: %#x\n", SPI_CMD_RDSR2,
|
||||
config);
|
||||
if (op == spi_nor_get_qe_by_cr(config))
|
||||
fmc_pr(QE_DBG, "\t|-8P %s Quad success, reg: %#x.\n", str[op],
|
||||
config);
|
||||
else
|
||||
db_msg("Error: %s Quad failed, reg: %#x\n", str[op], config);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
static int spi_gd25qxxx_qe_enable(struct fmc_spi *spi)
|
||||
{
|
||||
unsigned char op;
|
||||
const char *str[] = {"Disable", "Enable"};
|
||||
if (!spi || !spi->host)
|
||||
return -1;
|
||||
op = spi_is_quad(spi);
|
||||
|
||||
fmc_pr(QE_DBG, "\t*-Start GD SPI nor %s Quad.\n", str[op]);
|
||||
|
||||
/* First, we enable/disable QE for 16Pin GD flash, use WRSR[01h] cmd */
|
||||
if (gd_16pin_qe_enable(spi, op))
|
||||
goto qe_end;
|
||||
|
||||
/* Second, we enable/disable QE for 8Pin GD flash, use WRSR2[31h] cmd */
|
||||
gd_8pin_qe_enable(spi, op);
|
||||
|
||||
qe_end:
|
||||
/* Enable the reset pin when working on dual mode for 8PIN */
|
||||
if (!op)
|
||||
spi_nor_reset_pin_enable(spi, ENABLE);
|
||||
|
||||
fmc_pr(QE_DBG, "\t*-End GD SPI nor %s Quad end.\n", str[op]);
|
||||
return op;
|
||||
}
|
||||
#ifdef CONFIG_DTR_MODE_SUPPORT
|
||||
void spi_gd_set_reg(struct fmc_spi *spi)
|
||||
{
|
||||
unsigned int regval;
|
||||
struct fmc_host *host = (struct fmc_host *)spi->host;
|
||||
|
||||
regval = fmc_cmd_cmd1(SPI_CMD_WRSR3);
|
||||
fmc_write(host, FMC_CMD, regval);
|
||||
fmc_pr(DTR_DB, " Set CMD[%#x]%#x\n", FMC_CMD, regval);
|
||||
|
||||
regval = op_cfg_fm_cs(spi->chipselect) | OP_CFG_OEN_EN;
|
||||
fmc_write(host, FMC_OP_CFG, regval);
|
||||
fmc_pr(DTR_DB, " Set OP_CFG[%#x]%#x\n", FMC_OP_CFG, regval);
|
||||
|
||||
regval = fmc_data_num_cnt(SPI_NOR_SR_LEN);
|
||||
fmc_write(host, FMC_DATA_NUM, regval);
|
||||
fmc_pr(DTR_DB, " Set DATA_NUM[%#x]%#x\n", FMC_DATA_NUM, regval);
|
||||
|
||||
regval = fmc_op_cmd1_en(ENABLE) |
|
||||
fmc_op_write_data_en(ENABLE) |
|
||||
FMC_OP_REG_OP_START;
|
||||
fmc_write(host, FMC_OP, regval);
|
||||
fmc_pr(DTR_DB, " Set OP[%#x]%#x\n", FMC_OP, regval);
|
||||
}
|
||||
|
||||
int spi_gd_output_driver_strength_set(struct fmc_spi *spi, int dtr_en)
|
||||
{
|
||||
unsigned char config;
|
||||
unsigned char reg;
|
||||
unsigned char val;
|
||||
unsigned int ix;
|
||||
struct fmc_host *host = NULL;
|
||||
|
||||
/* DC | Numbers of Dummy clock cycles| Quad IO DTR Read */
|
||||
/* 0(default)| 8 | 66 */
|
||||
/* 1 | 10 | 80 */
|
||||
unsigned int str_dummy[] = {
|
||||
DTR_DUMMY_CYCLES_8, dtr_rdcr_dc_mask(0),
|
||||
DTR_DUMMY_CYCLES_10, dtr_rdcr_dc_mask(1),
|
||||
0, 0,
|
||||
};
|
||||
val = 0;
|
||||
if (!spi || !spi->driver)
|
||||
return -1;
|
||||
host = (struct fmc_host *)spi->host;
|
||||
if (!host)
|
||||
return -1;
|
||||
/* get the RDCR and RDSR */
|
||||
spi->driver->wait_ready(spi);
|
||||
/* setting the DC value to match high system clock */
|
||||
config = spi_general_get_flash_register(spi, SPI_CMD_RDSR3);
|
||||
fmc_pr(DTR_DB, "Get Status Register-3[%#x]\n", config);
|
||||
|
||||
if (dtr_en == ENABLE) {
|
||||
/* setting DC value */
|
||||
fmc_pr(DTR_DB, "Get the dummy value[%#x]\n", spi->read->dummy);
|
||||
/* Only the element with an even number of arrays is required, so increase is 2 */
|
||||
for (ix = 0; str_dummy[ix]; ix += _2B) {
|
||||
if (spi->read->dummy < str_dummy[ix])
|
||||
break;
|
||||
val = (unsigned char)str_dummy[ix + 1];
|
||||
}
|
||||
} else {
|
||||
val = dtr_rdcr_dc_mask(0);
|
||||
}
|
||||
|
||||
reg = dtr_gd_dc_bit_clr(config) | val;
|
||||
fmc_pr(DTR_DB, "Get the reg value[%#x]\n", reg);
|
||||
|
||||
spi->driver->write_enable(spi);
|
||||
writew(reg, host->iobase);
|
||||
fmc_pr(DTR_DB, "Write IO[%p]%#x\n", host->iobase,
|
||||
*(unsigned short *)host->iobase);
|
||||
spi_gd_set_reg(spi);
|
||||
fmc_cmd_wait_cpu_finish(host);
|
||||
|
||||
config = spi_general_get_flash_register(spi, SPI_CMD_RDSR3);
|
||||
fmc_pr(DTR_DB, "Get Status Register-3[%#x]\n", config);
|
||||
if ((config & 0x1) != (unsigned char)val) {
|
||||
printf("* Set DC dummy fail.\n");
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
#endif /* CONFIG_DTR_MODE_SUPPORT */
|
||||
+401
@@ -0,0 +1,401 @@
|
||||
/*
|
||||
* Copyright (c) LOTUS. All rights reserved.
|
||||
*/
|
||||
|
||||
/*
|
||||
Get status/config register value from SPI Nor flash
|
||||
*/
|
||||
unsigned char spi_general_get_flash_register(struct fmc_spi * const spi, u_char cmd)
|
||||
{
|
||||
unsigned char status;
|
||||
unsigned int reg;
|
||||
|
||||
struct fmc_host *host = (struct fmc_host *)spi->host;
|
||||
|
||||
host->set_system_clock(NULL, ENABLE);
|
||||
|
||||
fmc_pr(SR_DBG, "\t * Start get flash Register[%#x]\n", cmd);
|
||||
|
||||
reg = op_cfg_fm_cs(spi->chipselect) | OP_CFG_OEN_EN;
|
||||
fmc_write(host, FMC_OP_CFG, reg);
|
||||
|
||||
if (cmd == SPI_CMD_RDSR) {
|
||||
reg = fmc_op_read_status_en(ENABLE) | FMC_OP_REG_OP_START;
|
||||
goto cmd_config_done;
|
||||
}
|
||||
|
||||
fmc_write(host, FMC_CMD, cmd);
|
||||
fmc_pr(SR_DBG, "\t Set CMD[%#x]%#x\n", FMC_CMD, cmd);
|
||||
|
||||
reg = fmc_data_num_cnt(SPI_NOR_CR_LEN);
|
||||
fmc_write(host, FMC_DATA_NUM, reg);
|
||||
fmc_pr(SR_DBG, "\t Set DATA_NUM[%#x]%#x\n", FMC_DATA_NUM, reg);
|
||||
|
||||
reg = fmc_op_cmd1_en(ENABLE) | fmc_op_read_data_en(ENABLE) |
|
||||
FMC_OP_REG_OP_START;
|
||||
|
||||
cmd_config_done:
|
||||
fmc_write(host, FMC_OP, reg);
|
||||
fmc_pr(SR_DBG, "\t Set OP[%#x]%#x\n", FMC_OP, reg);
|
||||
|
||||
fmc_cmd_wait_cpu_finish(host);
|
||||
|
||||
if (cmd == SPI_CMD_RDSR)
|
||||
status = fmc_read(host, FMC_STATUS);
|
||||
else
|
||||
status = readb(host->iobase);
|
||||
fmc_pr(SR_DBG, "\t * End get flash Register[%#x], val: %#x\n", cmd,
|
||||
status);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
/*
|
||||
Read status[C0H]:[0]bit OIP, judge whether the device is busy or not
|
||||
*/
|
||||
static int spi_general_wait_ready(struct fmc_spi * const spi)
|
||||
{
|
||||
unsigned char status;
|
||||
/* need a big number,so move left 20 bit */
|
||||
unsigned int deadline = 1 << 20;
|
||||
if (!spi || !spi->host)
|
||||
return -1;
|
||||
do {
|
||||
status = spi_general_get_flash_register(spi, SPI_CMD_RDSR);
|
||||
if (!(status & SPI_NOR_SR_WIP_MASK))
|
||||
return 0;
|
||||
|
||||
udelay(1); /* delay 1 us */
|
||||
} while (deadline--);
|
||||
|
||||
db_msg("Error: SPI nor wait ready timeout, status[%#x]\n", status);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
Send write enable cmd to SPI Nor, status[C0H]:[2]bit WEL must be set 1
|
||||
*/
|
||||
static int spi_general_write_enable(struct fmc_spi *spi)
|
||||
{
|
||||
unsigned char status;
|
||||
unsigned int reg;
|
||||
if (!spi || !spi->driver || !spi->host) {
|
||||
printf("%s:spi data is NULL, please check input parameter\n", __func__);
|
||||
return -1;
|
||||
}
|
||||
struct fmc_host *host = (struct fmc_host *)spi->host;
|
||||
|
||||
if (WE_DBG)
|
||||
printf("\n");
|
||||
fmc_pr(WE_DBG, "\t * Start Write Enable\n");
|
||||
|
||||
status = spi_general_get_flash_register(spi, SPI_CMD_RDSR);
|
||||
fmc_pr(WE_DBG, "\t Read Status Register[%#x]:%#x\n", SPI_CMD_RDSR,
|
||||
status);
|
||||
if (status & STATUS_WEL_MASK) {
|
||||
fmc_pr(WE_DBG, "\t Write Enable was opened! reg: %#x\n",
|
||||
status);
|
||||
return 0;
|
||||
}
|
||||
|
||||
reg = fmc_read(host, FMC_GLOBAL_CFG);
|
||||
if (reg & FMC_GLOBAL_CFG_WP_ENABLE) {
|
||||
reg &= ~FMC_GLOBAL_CFG_WP_ENABLE;
|
||||
fmc_write(host, FMC_GLOBAL_CFG, reg);
|
||||
fmc_pr(WE_DBG, "\t Set GLOBAL_CFG[%#x]%#x\n",
|
||||
FMC_GLOBAL_CFG, reg);
|
||||
}
|
||||
|
||||
reg = fmc_cmd_cmd1(SPI_CMD_WREN);
|
||||
fmc_write(host, FMC_CMD, reg);
|
||||
fmc_pr(WE_DBG, "\t Set CMD[%#x]%#x\n", FMC_CMD, reg);
|
||||
|
||||
reg = op_cfg_fm_cs(spi->chipselect) | OP_CFG_OEN_EN;
|
||||
fmc_write(host, FMC_OP_CFG, reg);
|
||||
fmc_pr(WE_DBG, "\t Set OP_CFG[%#x]%#x\n", FMC_OP_CFG, reg);
|
||||
|
||||
reg = fmc_op_cmd1_en(ENABLE) | FMC_OP_REG_OP_START;
|
||||
fmc_write(host, FMC_OP, reg);
|
||||
fmc_pr(WE_DBG, "\t Set OP[%#x]%#x\n", FMC_OP, reg);
|
||||
|
||||
fmc_cmd_wait_cpu_finish(host);
|
||||
|
||||
spi->driver->wait_ready(spi);
|
||||
|
||||
reg = spi_general_get_flash_register(spi, SPI_CMD_RDSR);
|
||||
if (reg & STATUS_WEL_MASK) {
|
||||
fmc_pr(WE_DBG, "\t Write Enable success.reg: %#x\n", reg);
|
||||
} else {
|
||||
db_msg("Error: Write Enable failed! status: %#x\n", reg);
|
||||
return status;
|
||||
}
|
||||
|
||||
fmc_pr(WE_DBG, "\t * End Write Enable\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
enable 4byte address for SPI which memory more than 16M
|
||||
*/
|
||||
static int spi_general_entry_4addr(struct fmc_spi *spi, int enable)
|
||||
{
|
||||
unsigned char status;
|
||||
unsigned int reg;
|
||||
const char *str[] = {"Disable", "Enable"};
|
||||
struct fmc_host *host = NULL;
|
||||
if (!spi || !spi->driver)
|
||||
return -1;
|
||||
host = (struct fmc_host*)spi->host;
|
||||
if (!host)
|
||||
return -1;
|
||||
fmc_pr(AC_DBG, "\t* Start SPI Nor flash %s 4-byte mode.\n",
|
||||
str[enable]);
|
||||
|
||||
if (spi->addrcycle != SPI_NOR_4BYTE_ADDR_LEN) {
|
||||
fmc_pr(AC_DBG, "\t* Flash isn't support entry 4-byte mode.\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
status = spi_general_get_flash_register(spi, SPI_CMD_RDSR3);
|
||||
fmc_pr(AC_DBG, "\t Read Status Register-3[%#x]:%#x\n", SPI_CMD_RDSR3,
|
||||
status);
|
||||
if (spi_nor_get_4byte_by_cr(status) == enable) {
|
||||
fmc_pr(AC_DBG, "\t* 4-byte was %sd, reg:%#x\n", str[enable],
|
||||
status);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (enable)
|
||||
reg = SPI_CMD_EN4B;
|
||||
else
|
||||
reg = SPI_CMD_EX4B;
|
||||
fmc_write(host, FMC_CMD, fmc_cmd_cmd1(reg));
|
||||
fmc_pr(AC_DBG, "\t Set CMD[%#x]%#x\n", FMC_CMD, reg);
|
||||
|
||||
reg = op_cfg_fm_cs(spi->chipselect) | OP_CFG_OEN_EN;
|
||||
fmc_write(host, FMC_OP_CFG, reg);
|
||||
fmc_pr(AC_DBG, "\t Set OP_CFG[%#x]%#x\n", FMC_OP_CFG, reg);
|
||||
|
||||
reg = fmc_op_cmd1_en(ENABLE) | FMC_OP_REG_OP_START;
|
||||
fmc_write(host, FMC_OP, reg);
|
||||
fmc_pr(AC_DBG, "\t Set OP[%#x]%#x\n", FMC_OP, reg);
|
||||
|
||||
fmc_cmd_wait_cpu_finish(host);
|
||||
|
||||
spi->driver->wait_ready(spi);
|
||||
|
||||
status = spi_general_get_flash_register(spi, SPI_CMD_RDSR3);
|
||||
fmc_pr(AC_DBG, "\t Read SR-3[%#x]:%#x\n", SPI_CMD_RDSR3,
|
||||
status);
|
||||
if (spi_nor_get_4byte_by_cr(status) != enable) {
|
||||
db_msg("Error: %s 4-byte failed! SR3:%#x\n",
|
||||
str[enable], status);
|
||||
return status;
|
||||
}
|
||||
|
||||
fmc_pr(AC_DBG, "\t %s 4-byte success, SR3:%#x\n", str[enable], status);
|
||||
fmc_pr(AC_DBG, "\t* End SPI Nor flash %s 4-byte mode.\n", str[enable]);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
judge whether SPI Nor support QUAD read write or not
|
||||
*/
|
||||
unsigned char spi_is_quad(const struct fmc_spi * const spi)
|
||||
{
|
||||
char *const if_str[] = {"STD", "DUAL", "DIO", "QUAD", "QIO", "DTR"};
|
||||
if (!spi) {
|
||||
printf("%s:spi is NULL, please check input parameter\n", __func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
fmc_pr(QE_DBG, "\t\t|*-SPI read iftype: %s write iftype: %s\n",
|
||||
if_str[spi->read->iftype], if_str[spi->write->iftype]);
|
||||
|
||||
if ((spi->read->iftype == IF_TYPE_QUAD) ||
|
||||
(spi->read->iftype == IF_TYPE_QIO) ||
|
||||
(spi->write->iftype == IF_TYPE_QUAD) ||
|
||||
#ifdef CONFIG_DTR_MODE_SUPPORT
|
||||
(spi->read->iftype == IF_TYPE_DTR) ||
|
||||
#endif
|
||||
(spi->write->iftype == IF_TYPE_QIO)
|
||||
)
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void spi_general_set_cmd(struct fmc_spi * const spi)
|
||||
{
|
||||
unsigned int reg;
|
||||
struct fmc_host *host = (struct fmc_host *)spi->host;
|
||||
|
||||
reg = fmc_cmd_cmd1(SPI_CMD_WRSR);
|
||||
fmc_write(host, FMC_CMD, reg);
|
||||
fmc_pr(QE_DBG, "\t|-Set CMD[%#x]%#x\n", FMC_CMD, reg);
|
||||
|
||||
reg = op_cfg_fm_cs(spi->chipselect) | OP_CFG_OEN_EN;
|
||||
fmc_write(host, FMC_OP_CFG, reg);
|
||||
fmc_pr(QE_DBG, "\t|-Set OP_CFG[%#x]%#x\n", FMC_OP_CFG, reg);
|
||||
|
||||
reg = fmc_data_num_cnt(SPI_NOR_SR_LEN + SPI_NOR_CR_LEN);
|
||||
fmc_write(host, FMC_DATA_NUM, reg);
|
||||
fmc_pr(QE_DBG, "\t|-Set DATA_NUM[%#x]%#x\n", FMC_DATA_NUM, reg);
|
||||
|
||||
reg = fmc_op_cmd1_en(ENABLE) |
|
||||
fmc_op_write_data_en(ENABLE) |
|
||||
FMC_OP_REG_OP_START;
|
||||
fmc_write(host, FMC_OP, reg);
|
||||
fmc_pr(QE_DBG, "\t|-Set OP[%#x]%#x\n", FMC_OP, reg);
|
||||
|
||||
fmc_cmd_wait_cpu_finish(host);
|
||||
}
|
||||
/*
|
||||
* enable QE bit if QUAD read write is supported by SPI
|
||||
*/
|
||||
static int spi_general_qe_enable(struct fmc_spi *spi)
|
||||
{
|
||||
unsigned char status;
|
||||
unsigned char config;
|
||||
unsigned char op;
|
||||
|
||||
const char *str[] = {"Disable", "Enable"};
|
||||
struct fmc_host *host = NULL;
|
||||
if (!spi || !spi->driver)
|
||||
return -1;
|
||||
host = (struct fmc_host *)spi->host;
|
||||
if (!host || !host->iobase)
|
||||
return -1;
|
||||
op = spi_is_quad(spi);
|
||||
|
||||
fmc_pr(QE_DBG, "\t*-Start SPI Nor %s Quad.\n", str[op]);
|
||||
|
||||
config = spi_general_get_flash_register(spi, SPI_CMD_RDCR);
|
||||
fmc_pr(QE_DBG, "\t|-Read Config Register[%#x]%#x\n", SPI_CMD_RDCR,
|
||||
config);
|
||||
if (op == spi_nor_get_qe_by_cr(config)) {
|
||||
fmc_pr(QE_DBG, "\t* Quad was %sd, config:%#x\n", str[op],
|
||||
config);
|
||||
return op;
|
||||
}
|
||||
|
||||
status = spi_general_get_flash_register(spi, SPI_CMD_RDSR);
|
||||
fmc_pr(QE_DBG, "\t|-Read Status Register[%#x]%#x\n", SPI_CMD_RDSR,
|
||||
status);
|
||||
|
||||
spi->driver->write_enable(spi);
|
||||
|
||||
if (op)
|
||||
config |= SPI_NOR_CR_QE_MASK;
|
||||
else
|
||||
config &= ~SPI_NOR_CR_QE_MASK;
|
||||
writeb(status, host->iobase);
|
||||
writeb(config, host->iobase + SPI_NOR_SR_LEN);
|
||||
fmc_pr(QE_DBG, "\t|-Write IO[%p]%#x\n", host->iobase,
|
||||
*(unsigned short *)host->iobase);
|
||||
|
||||
spi_general_set_cmd(spi);
|
||||
|
||||
spi->driver->wait_ready(spi);
|
||||
|
||||
config = spi_general_get_flash_register(spi, SPI_CMD_RDCR);
|
||||
if (op == spi_nor_get_qe_by_cr(config)) {
|
||||
fmc_pr(QE_DBG, "\t|-%s Quad success, config: %#x\n", str[op],
|
||||
config);
|
||||
} else {
|
||||
db_msg("Error: %s Quad failed! reg: %#x\n", str[op], config);
|
||||
}
|
||||
|
||||
fmc_pr(QE_DBG, "\t* End SPI Nor %s Quad.\n", str[op]);
|
||||
|
||||
return op;
|
||||
}
|
||||
|
||||
/*
|
||||
some chip don't QUAD enable
|
||||
*/
|
||||
static int spi_do_not_qe_enable(struct fmc_spi *spi)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void reset_pin_enable_fmc_op(struct fmc_host* const host,
|
||||
struct fmc_spi* const spi)
|
||||
{
|
||||
unsigned int regval;
|
||||
|
||||
regval = fmc_cmd_cmd1(SPI_CMD_WRSR3);
|
||||
fmc_write(host, FMC_CMD, regval);
|
||||
fmc_pr(RST_DB, "\t|-Set CMD[%#x]%#x\n", FMC_CMD, regval);
|
||||
|
||||
regval = op_cfg_fm_cs(spi->chipselect) | OP_CFG_OEN_EN;
|
||||
fmc_write(host, FMC_OP_CFG, regval);
|
||||
fmc_pr(RST_DB, "\t|-Set OP_CFG[%#x]%#x\n",
|
||||
FMC_OP_CFG, regval);
|
||||
|
||||
regval = fmc_data_num_cnt(SPI_NOR_CR_LEN);
|
||||
fmc_write(host, FMC_DATA_NUM, regval);
|
||||
fmc_pr(RST_DB, "\t|-Set DATA_NUM[%#x]%#x\n",
|
||||
FMC_DATA_NUM, regval);
|
||||
|
||||
regval = fmc_op_cmd1_en(ENABLE) |
|
||||
fmc_op_write_data_en(ENABLE) |
|
||||
FMC_OP_REG_OP_START;
|
||||
fmc_write(host, FMC_OP, regval);
|
||||
fmc_pr(RST_DB, "\t|-Set OP[%#x]%#x\n", FMC_OP, regval);
|
||||
|
||||
fmc_cmd_wait_cpu_finish(host);
|
||||
}
|
||||
|
||||
/*
|
||||
* some chip set the mux HOLD#/RESET#/IO3 pin to RESET#, as it is HOLD# default.
|
||||
*/
|
||||
static void spi_nor_reset_pin_enable(struct fmc_spi *spi, int enable)
|
||||
{
|
||||
unsigned char config;
|
||||
const char *str[] = {"HOLD#", "RESET#"};
|
||||
struct fmc_host *host = NULL;
|
||||
|
||||
if (!spi || !spi->driver)
|
||||
return;
|
||||
|
||||
host = (struct fmc_host *)spi->host;
|
||||
if (!host || !host->iobase)
|
||||
return;
|
||||
|
||||
config = spi_general_get_flash_register(spi, SPI_CMD_RDSR3);
|
||||
fmc_pr(RST_DB, "\t|-Read SR-3[%#x], val: %#x\n",
|
||||
SPI_CMD_RDSR3, config);
|
||||
|
||||
if (enable == spi_nor_get_rst_hold_by_cr(config)) {
|
||||
fmc_pr(RST_DB, " Device has worked on %s.\n", str[enable]);
|
||||
return;
|
||||
}
|
||||
|
||||
fmc_pr(RST_DB, " Start to enable %s function.\n", str[enable]);
|
||||
spi->driver->write_enable(spi);
|
||||
|
||||
if (enable)
|
||||
config = spi_nor_set_rst_by_cr(config);
|
||||
else
|
||||
config = spi_nor_set_hold_by_cr(config);
|
||||
|
||||
writeb(config, host->iobase);
|
||||
fmc_pr(RST_DB, "\t|-Write IO[%p]%#x\n", host->iobase,
|
||||
*(unsigned char *)host->iobase);
|
||||
|
||||
reset_pin_enable_fmc_op(host, spi);
|
||||
|
||||
spi->driver->wait_ready(spi);
|
||||
|
||||
config = spi_general_get_flash_register(spi, SPI_CMD_RDSR3);
|
||||
fmc_pr(RST_DB, "\t|-Read SR-3[%#x], val: %#x\n",
|
||||
SPI_CMD_RDSR3, config);
|
||||
if (enable == spi_nor_get_rst_hold_by_cr(config))
|
||||
fmc_pr(RST_DB, "\t|- Set the MUX pin to RESET# success!\n");
|
||||
else
|
||||
fmc_pr(RST_DB, "\t|- The MUX pin works on HOLD# or DNU!\n");
|
||||
}
|
||||
+91
@@ -0,0 +1,91 @@
|
||||
/*
|
||||
* Copyright (c) LOTUS. All rights reserved.
|
||||
*/
|
||||
|
||||
#define SPI_NOR_ISSI_QE_SHIFT 6
|
||||
#define SPI_NOR_ISSI_SRWD_MASK (1 << 7)
|
||||
#define SPI_NOR_ISSI_QE_MASK (1 << SPI_NOR_ISSI_QE_SHIFT)
|
||||
#define spi_nor_issi_get_qe(sr) (((sr) & SPI_NOR_ISSI_QE_MASK) \
|
||||
>> SPI_NOR_ISSI_QE_SHIFT)
|
||||
|
||||
static void spi_issi_set_cmd(struct fmc_spi * const spi)
|
||||
{
|
||||
unsigned int reg;
|
||||
struct fmc_host *host = (struct fmc_host *)spi->host;
|
||||
|
||||
reg = fmc_cmd_cmd1(SPI_CMD_WRSR);
|
||||
fmc_write(host, FMC_CMD, reg);
|
||||
fmc_pr(QE_DBG, "\t|-Set CMD[%#x]%#x\n", FMC_CMD, reg);
|
||||
|
||||
reg = op_cfg_fm_cs(spi->chipselect) | OP_CFG_OEN_EN;
|
||||
fmc_write(host, FMC_OP_CFG, reg);
|
||||
fmc_pr(QE_DBG, "\t|-Set OP_CFG[%#x]%#x\n", FMC_OP_CFG, reg);
|
||||
|
||||
reg = fmc_data_num_cnt(SPI_NOR_SR_LEN);
|
||||
fmc_write(host, FMC_DATA_NUM, reg);
|
||||
fmc_pr(QE_DBG, "\t|-Set DATA_NUM[%#x]%#x\n", FMC_DATA_NUM, reg);
|
||||
|
||||
reg = fmc_op_cmd1_en(ENABLE) |
|
||||
fmc_op_write_data_en(ENABLE) |
|
||||
FMC_OP_REG_OP_START;
|
||||
fmc_write(host, FMC_OP, reg);
|
||||
fmc_pr(QE_DBG, "\t|-Set OP[%#x]%#x\n", FMC_OP, reg);
|
||||
|
||||
fmc_cmd_wait_cpu_finish(host);
|
||||
}
|
||||
|
||||
static int spi_issi_qe_enable(struct fmc_spi *spi)
|
||||
{
|
||||
unsigned char config;
|
||||
unsigned char op;
|
||||
|
||||
const char *str[] = {"Disable", "Enable"};
|
||||
struct fmc_host *host = NULL;
|
||||
if (!spi || !spi->driver)
|
||||
return -1;
|
||||
host = (struct fmc_host *)spi->host;
|
||||
if (!host || !host->iobase)
|
||||
return -1;
|
||||
op = spi_is_quad(spi);
|
||||
|
||||
fmc_pr(QE_DBG, "\t*-Start SPI Nor %s Quad.\n", str[op]);
|
||||
|
||||
config = spi_general_get_flash_register(spi, SPI_CMD_RDSR);
|
||||
fmc_pr(QE_DBG, "\t|-Read Config Register[%#x]%#x\n", SPI_CMD_RDSR,
|
||||
config);
|
||||
if (op == spi_nor_issi_get_qe(config)) {
|
||||
fmc_pr(QE_DBG, "\t* Quad was %sd, config:%#x\n", str[op],
|
||||
config);
|
||||
return op;
|
||||
}
|
||||
|
||||
spi->driver->write_enable(spi);
|
||||
|
||||
config = spi_general_get_flash_register(spi, SPI_CMD_RDSR);
|
||||
config &= ~SPI_NOR_ISSI_SRWD_MASK;
|
||||
|
||||
if (op)
|
||||
config |= SPI_NOR_ISSI_QE_MASK;
|
||||
else
|
||||
config &= ~SPI_NOR_ISSI_QE_MASK;
|
||||
writeb(config, host->iobase);
|
||||
fmc_pr(QE_DBG, "\t|-Write IO[%p]%#x\n", host->iobase,
|
||||
*(unsigned short *)host->iobase);
|
||||
|
||||
spi_issi_set_cmd(spi);
|
||||
|
||||
spi->driver->wait_ready(spi);
|
||||
|
||||
config = spi_general_get_flash_register(spi, SPI_CMD_RDSR);
|
||||
if (op == spi_nor_issi_get_qe(config)) {
|
||||
fmc_pr(QE_DBG, "\t|-%s Quad success, config: %#x\n", str[op],
|
||||
config);
|
||||
} else {
|
||||
db_msg("Error: %s Quad failed! reg: %#x\n", str[op], config);
|
||||
}
|
||||
|
||||
fmc_pr(QE_DBG, "\t* End SPI Nor %s Quad.\n", str[op]);
|
||||
|
||||
return op;
|
||||
}
|
||||
|
||||
+91
@@ -0,0 +1,91 @@
|
||||
/*
|
||||
* Copyright (c) LOTUS. All rights reserved.
|
||||
*/
|
||||
|
||||
#define SPI_CMD_FIRST_RESET_4ADDR 0x66
|
||||
#define SPI_CMD_SECOND_RESET_4ADDR 0x99
|
||||
|
||||
#define SPI_CMD_FLAG_SR_MICRON 0x70 /* READ FLAG STATUS REGISTER */
|
||||
#define SPI_CMD_RD_RDCR_MICRON 0xB5 /* READ NONVOLATILE CONFIGURATION
|
||||
REGISTER */
|
||||
#define SPI_CMD_WR_RDCR_MICRON 0xB1 /* WRITE NONVOLATILE CONFIGURATION
|
||||
REGISTER */
|
||||
#define SPI_NOR_ADS_MASK 0x1
|
||||
#define spi_nor_get_4byte_by_flag_sr(sr) ((sr) & SPI_NOR_ADS_MASK)
|
||||
|
||||
#define spi_nor_ads_set_4byte(cr) ((cr) & (~SPI_NOR_ADS_MASK))
|
||||
#define spi_nor_ads_get_4byte(cr) ((cr) & SPI_NOR_ADS_MASK)
|
||||
|
||||
static void entry_4addr_fmc_op(struct fmc_host* const host,
|
||||
struct fmc_spi* const spi,
|
||||
int enable)
|
||||
{
|
||||
unsigned int reg;
|
||||
|
||||
if (enable)
|
||||
reg = SPI_CMD_EN4B;
|
||||
else
|
||||
reg = SPI_CMD_EX4B;
|
||||
fmc_write(host, FMC_CMD, fmc_cmd_cmd1(reg));
|
||||
fmc_pr(AC_DBG, "\t Set CMD[%#x]%#x\n", FMC_CMD, reg);
|
||||
|
||||
reg = op_cfg_fm_cs(spi->chipselect) | OP_CFG_OEN_EN;
|
||||
fmc_write(host, FMC_OP_CFG, reg);
|
||||
fmc_pr(AC_DBG, "\t Set OP_CFG[%#x]%#x\n", FMC_OP_CFG, reg);
|
||||
|
||||
reg = fmc_op_cmd1_en(ENABLE) | FMC_OP_REG_OP_START;
|
||||
fmc_write(host, FMC_OP, reg);
|
||||
fmc_pr(AC_DBG, "\t Set OP[%#x]%#x\n", FMC_OP, reg);
|
||||
|
||||
fmc_cmd_wait_cpu_finish(host);
|
||||
}
|
||||
|
||||
static int spi_micron_entry_4addr(struct fmc_spi *spi, int enable)
|
||||
{
|
||||
unsigned char status;
|
||||
const char *str[] = {"Disable", "Enable"};
|
||||
struct fmc_host *host = NULL;
|
||||
|
||||
if (!spi || !spi->driver)
|
||||
return -1;
|
||||
host = (struct fmc_host *)spi->host;
|
||||
if (!host)
|
||||
return -1;
|
||||
fmc_pr(AC_DBG, "\t* Start SPI Nor %s 4-byte mode.\n",
|
||||
str[enable]);
|
||||
|
||||
if (spi->addrcycle != SPI_NOR_4BYTE_ADDR_LEN) {
|
||||
fmc_pr(AC_DBG, "\t* Not support 4B mode.\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
status = spi_general_get_flash_register(spi, SPI_CMD_FLAG_SR_MICRON);
|
||||
fmc_pr(AC_DBG, "\t Read flag status register[%#x]:%#x\n",
|
||||
SPI_CMD_FLAG_SR_MICRON, status);
|
||||
if (spi_nor_get_4byte_by_flag_sr(status) == enable) {
|
||||
fmc_pr(AC_DBG, "\t* 4-byte was %sd, reg:%#x\n", str[enable],
|
||||
status);
|
||||
return 0;
|
||||
}
|
||||
|
||||
spi->driver->write_enable(spi);
|
||||
|
||||
entry_4addr_fmc_op(host, spi, enable);
|
||||
|
||||
spi->driver->wait_ready(spi);
|
||||
|
||||
status = spi_general_get_flash_register(spi,
|
||||
SPI_CMD_FLAG_SR_MICRON);
|
||||
fmc_pr(AC_DBG, "\t Read flag status register[%#x]:%#x\n",
|
||||
SPI_CMD_FLAG_SR_MICRON, status);
|
||||
if (spi_nor_get_4byte_by_flag_sr(status) != enable) {
|
||||
db_msg("Error: %s 4-byte failed! SR3:%#x\n",
|
||||
str[enable], status);
|
||||
return status;
|
||||
}
|
||||
|
||||
fmc_pr(AC_DBG, "\t %s 4-byte success, SR3:%#x\n", str[enable], status);
|
||||
fmc_pr(AC_DBG, "\t* End SPI Nor flash %s 4-byte mode.\n", str[enable]);
|
||||
|
||||
return 0;
|
||||
}
|
||||
Executable
+259
@@ -0,0 +1,259 @@
|
||||
/*
|
||||
* Copyright (c) LOTUS. All rights reserved.
|
||||
*/
|
||||
|
||||
/* MXIC QE(bit) include in Status Register */
|
||||
#define MX_SPI_NOR_SR_QE_SHIFT 6
|
||||
#define MX_SPI_NOR_SR_QE_MASK (1 << MX_SPI_NOR_SR_QE_SHIFT)
|
||||
#define mx_spi_nor_get_qe_by_sr(sr) (((sr) & MX_SPI_NOR_SR_QE_MASK) >> MX_SPI_NOR_SR_QE_SHIFT)
|
||||
|
||||
/*
|
||||
* enable QE bit if 4X R/W is supported by MXIC "25L(256/257)35(E/F)" SPI
|
||||
*/
|
||||
#ifndef CONFIG_DTR_MODE_SUPPORT
|
||||
static void clear_dtr_mode(struct fmc_spi *spi, unsigned char status)
|
||||
{
|
||||
unsigned int regval;
|
||||
struct fmc_host *host = (struct fmc_host *)spi->host;
|
||||
unsigned char config;
|
||||
unsigned short reg;
|
||||
|
||||
config = spi_general_get_flash_register(spi, SPI_CMD_RDCR_MX);
|
||||
|
||||
regval = fmc_read(host, FMC_GLOBAL_CFG);
|
||||
if ((regval >> DTR_MODE_REQUEST_SHIFT) & 0x1) {
|
||||
regval &= (~(1 << DTR_MODE_REQUEST_SHIFT));
|
||||
regval = fmc_write(host, FMC_GLOBAL_CFG, regval);
|
||||
}
|
||||
|
||||
if (config & CR_DUMMY_CYCLE) {
|
||||
config &= (~CR_DUMMY_CYCLE);
|
||||
reg = ((unsigned short)config << SPI_NOR_CR_SHIFT) | status;
|
||||
writew(reg, host->iobase);
|
||||
spi->driver->write_enable(spi);
|
||||
fmc100_op_reg(spi, SPI_CMD_WRSR, sizeof(unsigned short), fmc_op_write_data_en(ENABLE));
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
static void spi_mx25l25635e_set_cmd(struct fmc_spi * const spi)
|
||||
{
|
||||
unsigned int regval;
|
||||
struct fmc_host *host = (struct fmc_host *)spi->host;
|
||||
|
||||
regval = fmc_cmd_cmd1(SPI_CMD_WRSR);
|
||||
fmc_write(host, FMC_CMD, regval);
|
||||
fmc_pr(QE_DBG, "\t||-Set CMD[%#x]%#x\n", FMC_CMD, regval);
|
||||
|
||||
regval = op_cfg_fm_cs(spi->chipselect) | OP_CFG_OEN_EN;
|
||||
fmc_write(host, FMC_OP_CFG, regval);
|
||||
fmc_pr(QE_DBG, "\t||-Set OP_CFG[%#x]%#x\n", FMC_OP_CFG, regval);
|
||||
|
||||
regval = fmc_data_num_cnt(SPI_NOR_SR_LEN);
|
||||
fmc_write(host, FMC_DATA_NUM, regval);
|
||||
fmc_pr(QE_DBG, "\t||-Set DATA_NUM[%#x]%#x\n", FMC_DATA_NUM, regval);
|
||||
|
||||
regval = fmc_op_cmd1_en(ENABLE) | fmc_op_write_data_en(ENABLE) |
|
||||
FMC_OP_REG_OP_START;
|
||||
fmc_write(host, FMC_OP, regval);
|
||||
fmc_pr(QE_DBG, "\t||-Set OP[%#x]%#x\n", FMC_OP, regval);
|
||||
|
||||
fmc_cmd_wait_cpu_finish(host);
|
||||
}
|
||||
|
||||
static int spi_mx25l25635e_qe_enable(struct fmc_spi *spi)
|
||||
{
|
||||
unsigned char status;
|
||||
unsigned char op;
|
||||
const char *str[] = {"Disable", "Enable"};
|
||||
struct fmc_host *host = NULL;
|
||||
if (!spi || !spi->driver)
|
||||
return -1;
|
||||
host = (struct fmc_host *)spi->host;
|
||||
if (!host || !host->iobase)
|
||||
return -1;
|
||||
op = spi_is_quad(spi);
|
||||
|
||||
fmc_pr(QE_DBG, "\t|*-Start MXIC SPI Nor %s Quad.\n", str[op]);
|
||||
|
||||
status = spi_general_get_flash_register(spi, SPI_CMD_RDSR);
|
||||
fmc_pr(QE_DBG, "\t||-Read Status Register[%#x]%#x\n", SPI_CMD_RDSR,
|
||||
status);
|
||||
|
||||
#ifndef CONFIG_DTR_MODE_SUPPORT
|
||||
clear_dtr_mode(spi, status);
|
||||
#endif
|
||||
|
||||
if (mx_spi_nor_get_qe_by_sr(status) == op) {
|
||||
fmc_pr(QE_DBG, "\t|*-Quad was %sd, status:%#x\n", str[op],
|
||||
status);
|
||||
return op;
|
||||
}
|
||||
|
||||
spi->driver->write_enable(spi);
|
||||
|
||||
if (op)
|
||||
status |= MX_SPI_NOR_SR_QE_MASK;
|
||||
else
|
||||
status &= ~MX_SPI_NOR_SR_QE_MASK;
|
||||
|
||||
writeb(status, host->iobase);
|
||||
fmc_pr(QE_DBG, "\t||-Write IO[%p]%#x\n", host->iobase,
|
||||
*(unsigned char *)host->iobase);
|
||||
|
||||
spi_mx25l25635e_set_cmd(spi);
|
||||
|
||||
spi->driver->wait_ready(spi);
|
||||
|
||||
status = spi_general_get_flash_register(spi, SPI_CMD_RDSR);
|
||||
if (mx_spi_nor_get_qe_by_sr(status) == op)
|
||||
fmc_pr(QE_DBG, "\t||-%s Quad success, status:%#x.\n", str[op],
|
||||
status);
|
||||
else
|
||||
db_msg("Error: %s Quad failed! reg: %#x\n", str[op], status);
|
||||
|
||||
fmc_pr(QE_DBG, "\t|*-End MXIC SPI Nor %s Quad.\n", str[op]);
|
||||
|
||||
return op;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_DTR_MODE_SUPPORT
|
||||
void spi_mxic_set_reg(struct fmc_spi *spi)
|
||||
{
|
||||
unsigned int regval;
|
||||
struct fmc_host *host = (struct fmc_host *)spi->host;
|
||||
|
||||
regval = fmc_cmd_cmd1(SPI_CMD_WRSR);
|
||||
fmc_write(host, FMC_CMD, regval);
|
||||
fmc_pr(DTR_DB, " Set CMD[%#x]%#x\n", FMC_CMD, regval);
|
||||
|
||||
regval = op_cfg_fm_cs(spi->chipselect) | OP_CFG_OEN_EN;
|
||||
fmc_write(host, FMC_OP_CFG, regval);
|
||||
fmc_pr(DTR_DB, " Set OP_CFG[%#x]%#x\n", FMC_OP_CFG, regval);
|
||||
|
||||
regval = fmc_data_num_cnt(SPI_NOR_SR_LEN + SPI_NOR_CR_LEN);
|
||||
fmc_write(host, FMC_DATA_NUM, regval);
|
||||
fmc_pr(DTR_DB, " Set DATA_NUM[%#x]%#x\n", FMC_DATA_NUM, regval);
|
||||
|
||||
regval = fmc_op_cmd1_en(ENABLE) |
|
||||
fmc_op_write_data_en(ENABLE) |
|
||||
FMC_OP_REG_OP_START;
|
||||
fmc_write(host, FMC_OP, regval);
|
||||
fmc_pr(DTR_DB, " Set OP[%#x]%#x\n", FMC_OP, regval);
|
||||
}
|
||||
|
||||
int spi_mxic_output_driver_strength_set(struct fmc_spi *spi, int dtr_en)
|
||||
{
|
||||
unsigned char status;
|
||||
unsigned char config;
|
||||
unsigned short reg;
|
||||
unsigned short val;
|
||||
unsigned int ix;
|
||||
struct fmc_host *host = NULL;
|
||||
|
||||
/* DC[1:0] | Numbers of Dummy clock cycles| Quad IO DTR Read */
|
||||
/* 00(default)| 6 | 54 */
|
||||
/* 01 | 6 | 54 */
|
||||
/* 10 | 8 | 70/80R */
|
||||
/* 11 | 10 | 84/100R */
|
||||
unsigned int str_dummy[] = {
|
||||
DTR_DUMMY_CYCLES_6, dtr_rdcr_dc_mask(0),
|
||||
DTR_DUMMY_CYCLES_6, dtr_rdcr_dc_mask(1),
|
||||
DTR_DUMMY_CYCLES_8, dtr_rdcr_dc_mask(2),
|
||||
DTR_DUMMY_CYCLES_10, dtr_rdcr_dc_mask(3),
|
||||
0, 0,
|
||||
};
|
||||
val = 0;
|
||||
if (!spi || !spi->driver)
|
||||
return -1;
|
||||
host = (struct fmc_host *)spi->host;
|
||||
if (!host)
|
||||
return -1;
|
||||
/* get the RDCR and RDSR */
|
||||
spi->driver->wait_ready(spi);
|
||||
|
||||
/* setting the DC value to match high system clock */
|
||||
config = spi_general_get_flash_register(spi, SPI_CMD_RDCR_MX);
|
||||
fmc_pr(DTR_DB, "Get Config Register[%#x]\n", config);
|
||||
|
||||
/* check the QE value */
|
||||
status = spi_general_get_flash_register(spi, SPI_CMD_RDSR);
|
||||
fmc_pr(DTR_DB, "Get Status Register[%#x]\n", status);
|
||||
|
||||
reg = ((unsigned short)config << SPI_NOR_CR_SHIFT) | status;
|
||||
|
||||
if (dtr_en == ENABLE) {
|
||||
/* setting DC value */
|
||||
fmc_pr(DTR_DB, "Get the dummy value[%#x]\n", spi->read->dummy);
|
||||
/* Only the element with an even number of arrays is required, so increase is 2 */
|
||||
for (ix = 0; str_dummy[ix]; ix += _2B) {
|
||||
if (spi->read->dummy < str_dummy[ix])
|
||||
break;
|
||||
val = (unsigned short)str_dummy[ix + 1];
|
||||
}
|
||||
} else {
|
||||
val = dtr_rdcr_dc_mask(0);
|
||||
}
|
||||
|
||||
reg = dtr_rdcr_dc_bit_clr(reg) | (val << DTR_RDSR_DC_SHIFT);
|
||||
|
||||
spi->driver->write_enable(spi);
|
||||
writew(reg, host->iobase);
|
||||
fmc_pr(DTR_DB, "Write IO[%p]%#x\n", host->iobase,
|
||||
*(unsigned short *)host->iobase);
|
||||
spi_mxic_set_reg(spi);
|
||||
fmc_cmd_wait_cpu_finish(host);
|
||||
|
||||
config = spi_general_get_flash_register(spi, SPI_CMD_RDCR_MX);
|
||||
if ((config >> DTR_RDCR_DC_SHIFT) != (unsigned char)val) {
|
||||
printf("* Set DC dummy fail.\n");
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
unsigned int spi_mxic_check_spi_dtr_support(struct fmc_spi *spi)
|
||||
{
|
||||
unsigned int regval;
|
||||
unsigned int rd_sfdp_dummy = 1;
|
||||
unsigned int sfdp_addrcycle = 3;
|
||||
struct fmc_host *host = (struct fmc_host *)spi->host;
|
||||
|
||||
/* get the RDCR and RDSR */
|
||||
spi->driver->wait_ready(spi);
|
||||
|
||||
/* Read the Serial Flash Discoverable Parameter (SFDP) */
|
||||
fmc_write(host, FMC_CMD, SPI_CMD_RD_SFDP);
|
||||
fmc_pr(DTR_DB, "\t Set CMD[%#x]%#x\n", FMC_CMD, SPI_CMD_RD_SFDP);
|
||||
|
||||
regval = op_cfg_fm_cs(spi->chipselect) |
|
||||
OP_CFG_OEN_EN |
|
||||
op_cfg_addr_num(sfdp_addrcycle) |
|
||||
op_cfg_dummy_num(rd_sfdp_dummy);
|
||||
fmc_write(host, FMC_OP_CFG, regval);
|
||||
fmc_pr(DTR_DB, "\t\t Set OP_CFG[%#x]%#x\n", FMC_OP_CFG, regval);
|
||||
|
||||
regval = fmc_data_num_cnt(SFDP_BUF_LEN);
|
||||
fmc_write(host, FMC_DATA_NUM, regval);
|
||||
fmc_pr(DTR_DB, "\t Set DATA_NUM[%#x]%#x\n", FMC_DATA_NUM, regval);
|
||||
|
||||
regval = fmc_op_dummy_en(ENABLE) |
|
||||
fmc_op_cmd1_en(ENABLE) |
|
||||
fmc_op_addr_en(ENABLE) |
|
||||
fmc_op_read_data_en(ENABLE) |
|
||||
FMC_OP_REG_OP_START;
|
||||
fmc_write(host, FMC_OP, regval);
|
||||
fmc_pr(DTR_DB, "\t Set OP[%#x]%#x\n", FMC_OP, regval);
|
||||
|
||||
fmc_cmd_wait_cpu_finish(host);
|
||||
|
||||
regval = readb((char *)host->iobase + SFDP_DTR_BYTE_SHIFT);
|
||||
fmc_pr(DTR_DB, "\t the dtr_mode_support is: %#x\n", regval);
|
||||
|
||||
/* get the DTR mode support bit */
|
||||
spi->dtr_mode_support = (regval >> SFDP_DTR_BIT_SHIFT)
|
||||
& SFDP_DTR_BIT_MASK;
|
||||
|
||||
return spi->dtr_mode_support;
|
||||
}
|
||||
#endif /* CONFIG_DTR_MODE_SUPPORT */
|
||||
+102
@@ -0,0 +1,102 @@
|
||||
/*
|
||||
* Copyright (c) LOTUS. All rights reserved.
|
||||
*/
|
||||
|
||||
#define SPI_NOR_NM_QE_SHIFT 2
|
||||
#define SPI_NOR_NM_QE_MASK (1 << SPI_NOR_NM_QE_SHIFT)
|
||||
#define spi_nor_get_qe_by_nm(cr) (((cr) & SPI_NOR_NM_QE_MASK) \
|
||||
>> SPI_NOR_NM_QE_SHIFT)
|
||||
|
||||
static int spi_nm25q128_entry_4addr(struct fmc_spi *spi, int enable)
|
||||
{
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void spi_nm25q128_set_op(const struct fmc_spi *spi)
|
||||
{
|
||||
unsigned int regval;
|
||||
struct fmc_host *host = (struct fmc_host *)spi->host;
|
||||
|
||||
regval = fmc_cmd_cmd1(SPI_CMD_WRSR2);
|
||||
fmc_write(host, FMC_CMD, regval);
|
||||
fmc_pr(QE_DBG, "\t Set CMD[%#x]%#x\n", FMC_CMD, regval);
|
||||
|
||||
regval = op_cfg_fm_cs(spi->chipselect) | OP_CFG_OEN_EN;
|
||||
fmc_write(host, FMC_OP_CFG, regval);
|
||||
fmc_pr(QE_DBG, "\t Set OP_CFG[%#x]%#x\n", FMC_OP_CFG, regval);
|
||||
|
||||
regval = fmc_data_num_cnt(SPI_NOR_SR_LEN);
|
||||
fmc_write(host, FMC_DATA_NUM, regval);
|
||||
fmc_pr(QE_DBG, "\t Set DATA_NUM[%#x]%#x\n", FMC_DATA_NUM, regval);
|
||||
|
||||
regval = fmc_op_cmd1_en(ENABLE) |
|
||||
fmc_op_write_data_en(ENABLE) |
|
||||
FMC_OP_REG_OP_START;
|
||||
fmc_write(host, FMC_OP, regval);
|
||||
fmc_pr(QE_DBG, "\t Set OP[%#x]%#x\n", FMC_OP, regval);
|
||||
|
||||
fmc_cmd_wait_cpu_finish(host);
|
||||
}
|
||||
|
||||
/*
|
||||
enable QE bit if QUAD read write is supported by NM25Q128EVBSIG
|
||||
*/
|
||||
static int spi_nm25q128_qe_enable(struct fmc_spi *spi)
|
||||
{
|
||||
unsigned char status, op;
|
||||
const char *str[] = {"Disable", "Enable"};
|
||||
struct fmc_host *host = NULL;
|
||||
if (!spi || !spi->driver)
|
||||
return -1;
|
||||
host = (struct fmc_host *)spi->host;
|
||||
if (!host || !host->iobase)
|
||||
return -1;
|
||||
op = spi_is_quad(spi);
|
||||
|
||||
fmc_pr(QE_DBG, "\t* Start SPI Nor NM25Q(128/64)EVBSIG %s Quad.\n", str[op]);
|
||||
|
||||
status = spi_general_get_flash_register(spi, SPI_CMD_RDSR2);
|
||||
fmc_pr(QE_DBG, "\t Read Status Register-2[%#x]%#x\n", SPI_CMD_RDSR2,
|
||||
status);
|
||||
if (op == spi_nor_get_qe_by_nm(status)) {
|
||||
fmc_pr(QE_DBG, "\t* Quad was %s status:%#x\n", str[op], status);
|
||||
goto QE_END;
|
||||
}
|
||||
|
||||
spi->driver->write_enable(spi);
|
||||
|
||||
if (op)
|
||||
status |= SPI_NOR_NM_QE_MASK;
|
||||
else
|
||||
status &= ~SPI_NOR_NM_QE_MASK;
|
||||
|
||||
writeb(status, host->iobase);
|
||||
fmc_pr(QE_DBG, "\t Write IO[%p]%#x\n", host->iobase,
|
||||
*(unsigned char *)host->iobase);
|
||||
|
||||
/* There is new cmd for Write Status Register 2 by NM25Q(128/64)EVBSIG */
|
||||
spi_nm25q128_set_op(spi);
|
||||
|
||||
/* wait the flash have switched quad mode success */
|
||||
spi->driver->wait_ready(spi);
|
||||
|
||||
status = spi_general_get_flash_register(spi, SPI_CMD_RDSR2);
|
||||
fmc_pr(QE_DBG, "\t Read Status Register-2[%#x]:%#x\n",
|
||||
SPI_CMD_RDSR2, status);
|
||||
if (op == spi_nor_get_qe_by_nm(status)) {
|
||||
fmc_pr(QE_DBG, "\t %s Quad success. status:%#x\n",
|
||||
str[op], status);
|
||||
} else {
|
||||
db_msg("Error: %s Quad failed! reg:%#x\n", str[op],
|
||||
status);
|
||||
}
|
||||
QE_END:
|
||||
/* Enable the reset pin when working on dual mode for 8PIN */
|
||||
if (!op)
|
||||
spi_nor_reset_pin_enable(spi, ENABLE);
|
||||
|
||||
fmc_pr(QE_DBG, "\t* End SPI Nor NM25Q(128/64)EVBSIG %s Quad.\n", str[op]);
|
||||
|
||||
return op;
|
||||
}
|
||||
+202
@@ -0,0 +1,202 @@
|
||||
/*
|
||||
* Copyright (c) LOTUS. All rights reserved.
|
||||
*/
|
||||
|
||||
static void puya_qe_enable_fmc_op(struct fmc_host * const host, struct fmc_spi * const spi)
|
||||
{
|
||||
unsigned int regval;
|
||||
|
||||
regval = fmc_cmd_cmd1(SPI_CMD_WRSR2);
|
||||
fmc_write(host, FMC_CMD, regval);
|
||||
fmc_pr(QE_DBG, "\t Set CMD[%#x]%#x\n", FMC_CMD, regval);
|
||||
|
||||
regval = op_cfg_fm_cs(spi->chipselect);
|
||||
fmc_write(host, FMC_OP_CFG, regval);
|
||||
fmc_pr(QE_DBG, "\t Set OP_CFG[%#x]%#x\n", FMC_OP_CFG, regval);
|
||||
|
||||
regval = fmc_data_num_cnt(SPI_NOR_SR_LEN);
|
||||
fmc_write(host, FMC_DATA_NUM, regval);
|
||||
fmc_pr(QE_DBG, "\t Set DATA_NUM[%#x]%#x\n", FMC_DATA_NUM, regval);
|
||||
|
||||
regval = fmc_op_cmd1_en(ENABLE) |
|
||||
fmc_op_write_data_en(ENABLE) | FMC_OP_REG_OP_START;
|
||||
fmc_write(host, FMC_OP, regval);
|
||||
fmc_pr(QE_DBG, "\t Set OP[%#x]%#x\n", FMC_OP, regval);
|
||||
|
||||
fmc_cmd_wait_cpu_finish(host);
|
||||
}
|
||||
|
||||
/*
|
||||
* enable QE bit if QUAD read write is supported by puya'a P25Q128H,it is as same as W25Q(128/256)FV
|
||||
* opcode type name
|
||||
* 0x35 RD SR(S15-S8)
|
||||
* 0x31 WR SR(S15-S8)
|
||||
*/
|
||||
static int spi_puya_qe_enable(struct fmc_spi *spi)
|
||||
{
|
||||
unsigned char status;
|
||||
unsigned char op;
|
||||
const char *str[] = {"Disable", "Enable"};
|
||||
struct fmc_host *host = NULL;
|
||||
if (!spi || !spi->driver)
|
||||
return -1;
|
||||
host = (struct fmc_host *)spi->host;
|
||||
if (!host || !host->iobase)
|
||||
return -1;
|
||||
|
||||
op = spi_is_quad(spi);
|
||||
|
||||
fmc_pr(QE_DBG, "\t* Start SPI Nor %s Quad.\n", str[op]);
|
||||
|
||||
status = spi_general_get_flash_register(spi, SPI_CMD_RDSR2);
|
||||
fmc_pr(QE_DBG, "\t Read Status Register-2[%#x]%#x\n", SPI_CMD_RDSR2,
|
||||
status);
|
||||
if (spi_nor_get_qe_by_cr(status) == op) {
|
||||
fmc_pr(QE_DBG, "\t* Quad was %s status:%#x\n", str[op], status);
|
||||
goto QE_END;
|
||||
}
|
||||
|
||||
spi->driver->write_enable(spi);
|
||||
if (op)
|
||||
status |= SPI_NOR_CR_QE_MASK;
|
||||
else
|
||||
status &= ~SPI_NOR_CR_QE_MASK;
|
||||
|
||||
writeb(status, host->iobase);
|
||||
fmc_pr(QE_DBG, "\t Write IO[%#lx]%#x\n", (uintptr_t)host->iobase,
|
||||
*(unsigned char *)host->iobase);
|
||||
|
||||
/* There is new cmd for Write Status Register 2 by W25Q(128/256)FV */
|
||||
puya_qe_enable_fmc_op(host, spi);
|
||||
|
||||
/* wait the flash have switched quad mode success */
|
||||
spi->driver->wait_ready(spi);
|
||||
|
||||
status = spi_general_get_flash_register(spi, SPI_CMD_RDSR2);
|
||||
fmc_pr(QE_DBG, "\t Read Status Register-2[%#x]:%#x\n",
|
||||
SPI_CMD_RDSR2, status);
|
||||
if (spi_nor_get_qe_by_cr(status) == op)
|
||||
fmc_pr(QE_DBG, "\t %s Quad success. status:%#x\n",
|
||||
str[op], status);
|
||||
else
|
||||
db_msg("Error: %s Quad failed! reg:%#x\n", str[op], status);
|
||||
QE_END:
|
||||
return op;
|
||||
}
|
||||
|
||||
static int spi_puya_entry_4addr(struct fmc_spi *spi, int enable)
|
||||
{
|
||||
const char *str[] = {"Disable", "Enable"};
|
||||
struct fmc_host *host = NULL;
|
||||
if (!spi || !spi->driver)
|
||||
return -1;
|
||||
host = (struct fmc_host*)spi->host;
|
||||
if (!host)
|
||||
return -1;
|
||||
fmc_pr(AC_DBG, "\t* Start SPI Nor flash %s 4-byte mode.\n",
|
||||
str[enable]);
|
||||
|
||||
if (spi->addrcycle != SPI_NOR_4BYTE_ADDR_LEN) {
|
||||
fmc_pr(AC_DBG, "\t* Flash isn't support entry 4-byte mode.\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!enable)
|
||||
{
|
||||
/* reset cmd same sa w25q256fv */
|
||||
spi_w25q256fv_set_cmd(spi, SPI_CMD_FIRST_RESET_4ADDR);
|
||||
spi_w25q256fv_set_cmd(spi, SPI_CMD_SECOND_RESET_4ADDR);
|
||||
fmc_pr(AC_DBG, "\tnow PY25Q256HB start software reset\n");
|
||||
udelay(30); /* delay 30 us */
|
||||
}
|
||||
|
||||
fmc_pr(AC_DBG, "\t* End SPI Nor flash %s 4-byte mode.\n", str[enable]);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_DTR_MODE_SUPPORT
|
||||
void spi_py_set_reg(struct fmc_spi *spi)
|
||||
{
|
||||
unsigned int regval;
|
||||
struct fmc_host *host = (struct fmc_host *)spi->host;
|
||||
|
||||
regval = fmc_cmd_cmd1(SPI_CMD_WRSR3);
|
||||
fmc_write(host, FMC_CMD, regval);
|
||||
fmc_pr(DTR_DB, " Set CMD[%#x]%#x\n", FMC_CMD, regval);
|
||||
|
||||
regval = op_cfg_fm_cs(spi->chipselect) | OP_CFG_OEN_EN;
|
||||
fmc_write(host, FMC_OP_CFG, regval);
|
||||
fmc_pr(DTR_DB, " Set OP_CFG[%#x]%#x\n", FMC_OP_CFG, regval);
|
||||
|
||||
regval = fmc_data_num_cnt(SPI_NOR_SR_LEN);
|
||||
fmc_write(host, FMC_DATA_NUM, regval);
|
||||
fmc_pr(DTR_DB, " Set DATA_NUM[%#x]%#x\n", FMC_DATA_NUM, regval);
|
||||
|
||||
regval = fmc_op_cmd1_en(ENABLE) |
|
||||
fmc_op_write_data_en(ENABLE) |
|
||||
FMC_OP_REG_OP_START;
|
||||
fmc_write(host, FMC_OP, regval);
|
||||
fmc_pr(DTR_DB, " Set OP[%#x]%#x\n", FMC_OP, regval);
|
||||
}
|
||||
|
||||
int spi_py_output_driver_strength_set(struct fmc_spi *spi, int dtr_en)
|
||||
{
|
||||
unsigned char config;
|
||||
unsigned char reg;
|
||||
unsigned char val;
|
||||
unsigned int ix;
|
||||
struct fmc_host *host = NULL;
|
||||
|
||||
/* DC | Numbers of Dummy clock cycles| Quad IO DTR Read */
|
||||
/* 0(default)| 8 | 80 */
|
||||
/* 1 | 10 | 100 */
|
||||
unsigned int str_dummy[] = {
|
||||
DTR_DUMMY_CYCLES_8, dtr_rdcr_dc_mask(0),
|
||||
DTR_DUMMY_CYCLES_10, dtr_rdcr_dc_mask(1),
|
||||
0, 0,
|
||||
};
|
||||
val = 0;
|
||||
if (!spi || !spi->driver)
|
||||
return -1;
|
||||
host = (struct fmc_host *)spi->host;
|
||||
if (!host)
|
||||
return -1;
|
||||
/* get the RDCR and RDSR */
|
||||
spi->driver->wait_ready(spi);
|
||||
/* setting the DC value to match high system clock */
|
||||
config = spi_general_get_flash_register(spi, SPI_CMD_RDSR3);
|
||||
fmc_pr(DTR_DB, "Get Status Register-3[%#x]\n", config);
|
||||
|
||||
if (dtr_en == ENABLE) {
|
||||
/* setting DC value */
|
||||
fmc_pr(DTR_DB, "Get the dummy value[%#x]\n", spi->read->dummy);
|
||||
/* Only the element with an even number of arrays is required, so increase is 2 */
|
||||
for (ix = 0; str_dummy[ix]; ix += _2B) {
|
||||
if (spi->read->dummy < str_dummy[ix])
|
||||
break;
|
||||
val = (unsigned char)str_dummy[ix + 1];
|
||||
}
|
||||
} else {
|
||||
val = dtr_rdcr_dc_mask(0);
|
||||
}
|
||||
|
||||
reg = dtr_py_dc_bit_clr(config) | (val << 3);
|
||||
fmc_pr(DTR_DB, "Get the reg value[%#x]\n", reg);
|
||||
|
||||
spi->driver->write_enable(spi);
|
||||
writew(reg, host->iobase);
|
||||
fmc_pr(DTR_DB, "Write IO[%p]%#x\n", host->iobase,
|
||||
*(unsigned short *)host->iobase);
|
||||
spi_py_set_reg(spi);
|
||||
fmc_cmd_wait_cpu_finish(host);
|
||||
|
||||
config = spi_general_get_flash_register(spi, SPI_CMD_RDSR3);
|
||||
fmc_pr(DTR_DB, "Get Status Register-3[%#x]\n", config);
|
||||
if (((config & 0x8) >> 3) != (unsigned char)val) {
|
||||
printf("* Set DC dummy fail.\n");
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
#endif /* CONFIG_DTR_MODE_SUPPORT */
|
||||
+100
@@ -0,0 +1,100 @@
|
||||
/*
|
||||
* Copyright (c) LOTUS. All rights reserved.
|
||||
*/
|
||||
|
||||
/* SpanSion SPI Nor Flash "S25FL256S" Bank Address Register command */
|
||||
#define SS_SPI_CMD_BRRD 0x16 /* Read Bank Register */
|
||||
#define SS_SPI_CMD_BRWR 0x17 /* Write Bank Register */
|
||||
|
||||
/* Bank Address Register length(byte) */
|
||||
#define SS_SPI_NOR_BR_LEN 1
|
||||
|
||||
/* Extended Address Enable bit[7] include in Bank Address Register */
|
||||
#define SS_SPI_NOR_BR_EAE_SHIFT 7
|
||||
#define SS_SPI_NOR_BR_EAE_MASK (1 << SS_SPI_NOR_BR_EAE_SHIFT)
|
||||
#define ss_spi_nor_get_eae_by_br(br) (((br) & SS_SPI_NOR_BR_EAE_MASK) >> SS_SPI_NOR_BR_EAE_SHIFT)
|
||||
|
||||
static void spi_s25fl256s_set_cmd(const struct fmc_spi *spi)
|
||||
{
|
||||
unsigned int regval;
|
||||
struct fmc_host *host = (struct fmc_host *)spi->host;
|
||||
|
||||
regval = fmc_cmd_cmd1(SS_SPI_CMD_BRWR);
|
||||
fmc_write(host, FMC_CMD, regval);
|
||||
fmc_pr(AC_DBG, "\t Set CMD[%#x]%#x\n", FMC_CMD, regval);
|
||||
|
||||
regval = op_cfg_fm_cs(spi->chipselect);
|
||||
fmc_write(host, FMC_OP_CFG, regval);
|
||||
fmc_pr(AC_DBG, "\t Set OP_CFG[%#x]%#x\n", FMC_OP_CFG, regval);
|
||||
|
||||
regval = fmc_data_num_cnt(SS_SPI_NOR_BR_LEN);
|
||||
fmc_write(host, FMC_DATA_NUM, regval);
|
||||
fmc_pr(AC_DBG, "\t Set DATA_NUM[%#x]%#x\n", FMC_DATA_NUM, regval);
|
||||
|
||||
regval = fmc_op_cmd1_en(ENABLE) |
|
||||
fmc_op_write_data_en(ENABLE) |
|
||||
FMC_OP_REG_OP_START;
|
||||
fmc_write(host, FMC_OP, regval);
|
||||
fmc_pr(AC_DBG, "\t Set OP[%#x]%#x\n", FMC_OP, regval);
|
||||
|
||||
fmc_cmd_wait_cpu_finish(host);
|
||||
}
|
||||
/*
|
||||
enable 4byte address mode for SpanSion "s25fl256" SPI Nor
|
||||
*/
|
||||
static int spi_s25fl256s_entry_4addr(struct fmc_spi *spi, int enable)
|
||||
{
|
||||
unsigned char bank;
|
||||
const char *str[] = {"Disable", "Enable"};
|
||||
struct fmc_host *host = NULL;
|
||||
|
||||
if (!spi || !spi->driver)
|
||||
return -1;
|
||||
host = (struct fmc_host *)spi->host;
|
||||
if (!host || !host->iobase)
|
||||
return -1;
|
||||
fmc_pr(AC_DBG, "\t* Start SpanSion SPI Nor %s 4-byte mode.\n",
|
||||
str[enable]);
|
||||
|
||||
if (spi->addrcycle != SPI_NOR_4BYTE_ADDR_LEN) {
|
||||
fmc_pr(AC_DBG, "\t* Flash isn't support 4-byte mode.\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Read old Bank Register value */
|
||||
bank = spi_general_get_flash_register(spi, SS_SPI_CMD_BRRD);
|
||||
fmc_pr(AC_DBG, "\t Read Bank Register[%#x]%#x\n", SS_SPI_CMD_BRRD,
|
||||
bank);
|
||||
if (ss_spi_nor_get_eae_by_br(bank) == enable) {
|
||||
fmc_pr(AC_DBG, "\t* 4-byte was %sd, bank:%#x\n", str[enable], bank);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Write new Bank Register value */
|
||||
if (enable)
|
||||
bank |= SS_SPI_NOR_BR_EAE_MASK;
|
||||
else
|
||||
bank &= ~SS_SPI_NOR_BR_EAE_MASK;
|
||||
writeb(bank, host->iobase);
|
||||
fmc_pr(AC_DBG, "\t Write IO[%p]%#x\n", host->iobase,
|
||||
*(unsigned char *)host->iobase);
|
||||
|
||||
spi_s25fl256s_set_cmd(spi);
|
||||
|
||||
spi->driver->wait_ready(spi);
|
||||
|
||||
/* Check out Bank Register value */
|
||||
bank = spi_general_get_flash_register(spi, SS_SPI_CMD_BRRD);
|
||||
fmc_pr(AC_DBG, "\t Read Bank Register[%#x]%#x\n", SS_SPI_CMD_BRRD,
|
||||
bank);
|
||||
if (ss_spi_nor_get_eae_by_br(bank) != enable) {
|
||||
db_msg("Error: %s 4bytes failed! bank: %#x\n", str[enable], bank);
|
||||
return bank;
|
||||
}
|
||||
|
||||
fmc_pr(AC_DBG, "\t %s 4byte success, bank:%#x.\n", str[enable], bank);
|
||||
fmc_pr(AC_DBG, "\t* End SpanSion SPI Nor %s 4-byte mode.\n",
|
||||
str[enable]);
|
||||
|
||||
return 0;
|
||||
}
|
||||
+181
@@ -0,0 +1,181 @@
|
||||
/*
|
||||
* Copyright (c) LOTUS. All rights reserved.
|
||||
*/
|
||||
#define W25Q256FV_CR_4BYTE_MASK 0x1
|
||||
|
||||
#define WB_SPI_NOR_SR_ADS_MASK 1
|
||||
#define wb_spi_nor_get_4byte_by_sr(sr) ((sr) & WB_SPI_NOR_SR_ADS_MASK)
|
||||
|
||||
#define SPI_CMD_FIRST_RESET_4ADDR 0x66
|
||||
#define SPI_CMD_SECOND_RESET_4ADDR 0x99
|
||||
|
||||
static void spi_w25q256fv_set_cmd(const struct fmc_spi *spi, u8 cmd)
|
||||
{
|
||||
unsigned int regval;
|
||||
struct fmc_host *host = (struct fmc_host *)spi->host;
|
||||
|
||||
regval = fmc_cmd_cmd1(cmd);
|
||||
fmc_write(host, FMC_CMD, regval);
|
||||
fmc_pr(AC_DBG, "\t Set CMD[%#x]%#x\n", FMC_CMD, regval);
|
||||
|
||||
regval = op_cfg_fm_cs(spi->chipselect) | OP_CFG_OEN_EN;
|
||||
fmc_write(host, FMC_OP_CFG, regval);
|
||||
fmc_pr(AC_DBG, "\t Set OP_CFG[%#x]%#x\n", FMC_OP_CFG, regval);
|
||||
|
||||
regval = fmc_op_cmd1_en(ENABLE) | FMC_OP_REG_OP_START;
|
||||
fmc_write(host, FMC_OP, regval);
|
||||
fmc_pr(AC_DBG, "\t Set OP[%#x]%#x\n", FMC_OP, regval);
|
||||
|
||||
fmc_cmd_wait_cpu_finish(host);
|
||||
}
|
||||
|
||||
static int spi_w25q256jv_entry_4addr(struct fmc_spi *spi, int enable)
|
||||
{
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int spi_w25q256fv_entry_4addr(struct fmc_spi *spi, int enable)
|
||||
{
|
||||
unsigned char status;
|
||||
const char *str[] = {"Disable", "Enable"};
|
||||
|
||||
if (!spi || !spi->host)
|
||||
return -1;
|
||||
|
||||
fmc_pr(AC_DBG, "\t* Start W25Q256FV SPI Nor %s 4-byte mode.\n",
|
||||
str[enable]);
|
||||
|
||||
if (spi->addrcycle != SPI_NOR_4BYTE_ADDR_LEN) {
|
||||
fmc_pr(AC_DBG, "\t* W25Q(128/256)FV not support 4B mode.\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
status = spi_general_get_flash_register(spi, SPI_CMD_RDSR3);
|
||||
fmc_pr(AC_DBG, "\t Read Status Register-3[%#x]:%#x\n", SPI_CMD_RDSR3,
|
||||
status);
|
||||
if (wb_spi_nor_get_4byte_by_sr(status) == enable) {
|
||||
fmc_pr(AC_DBG, "\t* 4-byte was %sd, reg:%#x\n", str[enable],
|
||||
status);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (enable) {
|
||||
spi_w25q256fv_set_cmd(spi, SPI_CMD_EN4B);
|
||||
if (!spi->driver)
|
||||
return -1;
|
||||
spi->driver->wait_ready(spi);
|
||||
|
||||
status = spi_general_get_flash_register(spi, SPI_CMD_RDSR3);
|
||||
fmc_pr(AC_DBG, "\t Get Status Register 3[%#x]:%#x\n",
|
||||
SPI_CMD_RDSR3, status);
|
||||
if (status & W25Q256FV_CR_4BYTE_MASK) {
|
||||
fmc_pr(AC_DBG, "\t Enter 4-byte success, reg[%#x]\n",
|
||||
status);
|
||||
} else {
|
||||
db_msg("Error: Enter 4-byte failed! [%#x]\n", status);
|
||||
}
|
||||
} else {
|
||||
/* reset cmd */
|
||||
spi_w25q256fv_set_cmd(spi, SPI_CMD_FIRST_RESET_4ADDR);
|
||||
|
||||
spi_w25q256fv_set_cmd(spi, SPI_CMD_SECOND_RESET_4ADDR);
|
||||
|
||||
fmc_pr(AC_DBG, "\tnow W25Q256FV start software reset\n");
|
||||
|
||||
udelay(30); /* delay 30 us */
|
||||
}
|
||||
|
||||
fmc_pr(AC_DBG, "\t* End W25Q256FV enter 4-byte mode.\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void spi_w25q256fv_set_op(const struct fmc_spi *spi)
|
||||
{
|
||||
unsigned int regval;
|
||||
struct fmc_host *host = (struct fmc_host *)spi->host;
|
||||
|
||||
regval = fmc_cmd_cmd1(SPI_CMD_WRSR2);
|
||||
fmc_write(host, FMC_CMD, regval);
|
||||
fmc_pr(QE_DBG, "\t Set CMD[%#x]%#x\n", FMC_CMD, regval);
|
||||
|
||||
regval = op_cfg_fm_cs(spi->chipselect) | OP_CFG_OEN_EN;
|
||||
fmc_write(host, FMC_OP_CFG, regval);
|
||||
fmc_pr(QE_DBG, "\t Set OP_CFG[%#x]%#x\n", FMC_OP_CFG, regval);
|
||||
|
||||
regval = fmc_data_num_cnt(SPI_NOR_SR_LEN);
|
||||
fmc_write(host, FMC_DATA_NUM, regval);
|
||||
fmc_pr(QE_DBG, "\t Set DATA_NUM[%#x]%#x\n", FMC_DATA_NUM, regval);
|
||||
|
||||
regval = fmc_op_cmd1_en(ENABLE) |
|
||||
fmc_op_write_data_en(ENABLE) |
|
||||
FMC_OP_REG_OP_START;
|
||||
fmc_write(host, FMC_OP, regval);
|
||||
fmc_pr(QE_DBG, "\t Set OP[%#x]%#x\n", FMC_OP, regval);
|
||||
|
||||
fmc_cmd_wait_cpu_finish(host);
|
||||
}
|
||||
|
||||
/*
|
||||
enable QE bit if QUAD read write is supported by W25Q(128/256)FV
|
||||
*/
|
||||
static int spi_w25q256fv_qe_enable(struct fmc_spi *spi)
|
||||
{
|
||||
unsigned char status, op;
|
||||
const char *str[] = {"Disable", "Enable"};
|
||||
struct fmc_host *host = NULL;
|
||||
if (!spi || !spi->driver)
|
||||
return -1;
|
||||
host = (struct fmc_host *)spi->host;
|
||||
if (!host || !host->iobase)
|
||||
return -1;
|
||||
op = spi_is_quad(spi);
|
||||
|
||||
fmc_pr(QE_DBG, "\t* Start SPI Nor W25Q(128/256)FV %s Quad.\n", str[op]);
|
||||
|
||||
status = spi_general_get_flash_register(spi, SPI_CMD_RDSR2);
|
||||
fmc_pr(QE_DBG, "\t Read Status Register-2[%#x]%#x\n", SPI_CMD_RDSR2,
|
||||
status);
|
||||
if (op == spi_nor_get_qe_by_cr(status)) {
|
||||
fmc_pr(QE_DBG, "\t* Quad was %s status:%#x\n", str[op], status);
|
||||
goto QE_END;
|
||||
}
|
||||
|
||||
spi->driver->write_enable(spi);
|
||||
|
||||
if (op)
|
||||
status |= SPI_NOR_CR_QE_MASK;
|
||||
else
|
||||
status &= ~SPI_NOR_CR_QE_MASK;
|
||||
|
||||
writeb(status, host->iobase);
|
||||
fmc_pr(QE_DBG, "\t Write IO[%p]%#x\n", host->iobase,
|
||||
*(unsigned char *)host->iobase);
|
||||
|
||||
/* There is new cmd for Write Status Register 2 by W25Q(128/256)FV */
|
||||
spi_w25q256fv_set_op(spi);
|
||||
|
||||
/* wait the flash have switched quad mode success */
|
||||
spi->driver->wait_ready(spi);
|
||||
|
||||
status = spi_general_get_flash_register(spi, SPI_CMD_RDSR2);
|
||||
fmc_pr(QE_DBG, "\t Read Status Register-2[%#x]:%#x\n",
|
||||
SPI_CMD_RDSR2, status);
|
||||
if (op == spi_nor_get_qe_by_cr(status)) {
|
||||
fmc_pr(QE_DBG, "\t %s Quad success. status:%#x\n",
|
||||
str[op], status);
|
||||
} else {
|
||||
db_msg("Error: %s Quad failed! reg:%#x\n", str[op],
|
||||
status);
|
||||
}
|
||||
QE_END:
|
||||
/* Enable the reset pin when working on dual mode for 8PIN */
|
||||
if (!op)
|
||||
spi_nor_reset_pin_enable(spi, ENABLE);
|
||||
|
||||
fmc_pr(QE_DBG, "\t* End SPI Nor W25Q(128/256)FV %s Quad.\n", str[op]);
|
||||
|
||||
return op;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,90 @@
|
||||
/*
|
||||
* Copyright (c) LOTUS. All rights reserved.
|
||||
*/
|
||||
#ifdef CONFIG_DTR_MODE_SUPPORT
|
||||
void spi_xmc_set_reg(struct fmc_spi *spi)
|
||||
{
|
||||
unsigned int regval;
|
||||
struct fmc_host *host = (struct fmc_host *)spi->host;
|
||||
|
||||
regval = fmc_cmd_cmd1(SPI_CMD_WRSR3);
|
||||
fmc_write(host, FMC_CMD, regval);
|
||||
fmc_pr(DTR_DB, " Set CMD[%#x]%#x\n", FMC_CMD, regval);
|
||||
|
||||
regval = op_cfg_fm_cs(spi->chipselect) | OP_CFG_OEN_EN;
|
||||
fmc_write(host, FMC_OP_CFG, regval);
|
||||
fmc_pr(DTR_DB, " Set OP_CFG[%#x]%#x\n", FMC_OP_CFG, regval);
|
||||
|
||||
regval = fmc_data_num_cnt(SPI_NOR_SR_LEN);
|
||||
fmc_write(host, FMC_DATA_NUM, regval);
|
||||
fmc_pr(DTR_DB, " Set DATA_NUM[%#x]%#x\n", FMC_DATA_NUM, regval);
|
||||
|
||||
regval = fmc_op_cmd1_en(ENABLE) |
|
||||
fmc_op_write_data_en(ENABLE) |
|
||||
FMC_OP_REG_OP_START;
|
||||
fmc_write(host, FMC_OP, regval);
|
||||
fmc_pr(DTR_DB, " Set OP[%#x]%#x\n", FMC_OP, regval);
|
||||
}
|
||||
|
||||
int spi_xmc_output_driver_strength_set(struct fmc_spi *spi, int dtr_en)
|
||||
{
|
||||
unsigned char config;
|
||||
unsigned short val;
|
||||
unsigned int ix;
|
||||
struct fmc_host *host = NULL;
|
||||
|
||||
/* DC[1:0] | Numbers of Dummy clock cycles| Quad IO DTR Read */
|
||||
/* 00(default)| 8 | 90 */
|
||||
/* 01 | 4 | 66 */
|
||||
/* 10 | 6 | 66 */
|
||||
/* 11 | 10 | 108 */
|
||||
unsigned int str_dummy[] = {
|
||||
DTR_DUMMY_CYCLES_4, dtr_rdcr_dc_mask(1),
|
||||
DTR_DUMMY_CYCLES_6, dtr_rdcr_dc_mask(2),
|
||||
DTR_DUMMY_CYCLES_8, dtr_rdcr_dc_mask(0),
|
||||
DTR_DUMMY_CYCLES_10, dtr_rdcr_dc_mask(3),
|
||||
0, 0,
|
||||
};
|
||||
val = 0;
|
||||
if (!spi || !spi->driver)
|
||||
return -1;
|
||||
host = (struct fmc_host *)spi->host;
|
||||
if (!host)
|
||||
return -1;
|
||||
/* get the RDCR and RDSR */
|
||||
spi->driver->wait_ready(spi);
|
||||
|
||||
/* setting the DC value to match high system clock */
|
||||
config = spi_general_get_flash_register(spi, SPI_CMD_RDCR_MX);
|
||||
fmc_pr(DTR_DB, "Get Config Register[%#x]\n", config);
|
||||
|
||||
if (dtr_en == ENABLE) {
|
||||
/* setting DC value */
|
||||
fmc_pr(DTR_DB, "Get the dummy value[%#x]\n", spi->read->dummy);
|
||||
/* Only the element with an even number of arrays is required, so increase is 2 */
|
||||
for (ix = 0; str_dummy[ix]; ix += _2B) {
|
||||
if (spi->read->dummy < str_dummy[ix])
|
||||
break;
|
||||
val = (unsigned short)str_dummy[ix + 1];
|
||||
}
|
||||
} else {
|
||||
val = dtr_rdcr_dc_mask(0);
|
||||
}
|
||||
|
||||
config = dtr_xmc_dc_bit_clr(config) | val;
|
||||
|
||||
spi->driver->write_enable(spi);
|
||||
writeb(config, host->iobase);
|
||||
fmc_pr(DTR_DB, "Write IO[%p]%#x\n", host->iobase,
|
||||
*(unsigned short *)host->iobase);
|
||||
spi_xmc_set_reg(spi);
|
||||
fmc_cmd_wait_cpu_finish(host);
|
||||
|
||||
config = spi_general_get_flash_register(spi, SPI_CMD_RDCR_MX);
|
||||
if ((config & 3) != (unsigned char)val) {
|
||||
printf("* Set DC dummy fail.\n");
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
#endif /* CONFIG_DTR_MODE_SUPPORT */
|
||||
+92
@@ -0,0 +1,92 @@
|
||||
/*
|
||||
* Copyright (c) LOTUS. All rights reserved.
|
||||
*/
|
||||
|
||||
#define XTX_READ_SR_H 0x35
|
||||
#define XTX_READ_SR_L 0x05
|
||||
|
||||
static void spi_xtx_set_op(const struct fmc_spi *spi)
|
||||
{
|
||||
unsigned int regval;
|
||||
struct fmc_host *host = (struct fmc_host *)spi->host;
|
||||
|
||||
regval = fmc_cmd_cmd1(SPI_CMD_WRSR);
|
||||
fmc_write(host, FMC_CMD, regval);
|
||||
fmc_pr(QE_DBG, "\t Set CMD[%#x]%#x\n", FMC_CMD, regval);
|
||||
|
||||
regval = op_cfg_fm_cs(spi->chipselect) | OP_CFG_OEN_EN;
|
||||
fmc_write(host, FMC_OP_CFG, regval);
|
||||
fmc_pr(QE_DBG, "\t Set OP_CFG[%#x]%#x\n", FMC_OP_CFG, regval);
|
||||
|
||||
regval = fmc_data_num_cnt(sizeof(unsigned short));
|
||||
fmc_write(host, FMC_DATA_NUM, regval);
|
||||
fmc_pr(QE_DBG, "\t Set DATA_NUM[%#x]%#x\n", FMC_DATA_NUM, regval);
|
||||
|
||||
regval = fmc_op_cmd1_en(ENABLE) | fmc_op_write_data_en(ENABLE) |
|
||||
FMC_OP_REG_OP_START;
|
||||
fmc_write(host, FMC_OP, regval);
|
||||
fmc_pr(QE_DBG, "\t Set OP[%#x]%#x\n", FMC_OP, regval);
|
||||
|
||||
fmc_cmd_wait_cpu_finish(host);
|
||||
}
|
||||
/*
|
||||
enable QE bit if QUAD read write is supported by xtx's flash
|
||||
*/
|
||||
static int spi_xtx_qe_enable(struct fmc_spi *spi)
|
||||
{
|
||||
unsigned char status_h;
|
||||
unsigned char status_l;
|
||||
unsigned char op;
|
||||
unsigned short reg;
|
||||
const char *str[] = {"Disable", "Enable"};
|
||||
struct fmc_host *host = NULL;
|
||||
if (!spi || !spi->driver)
|
||||
return -1;
|
||||
host = (struct fmc_host *)spi->host;
|
||||
if (!host || !host->iobase)
|
||||
return -1;
|
||||
|
||||
op = spi_is_quad(spi);
|
||||
|
||||
fmc_pr(QE_DBG, "\t* Start SPI Nor xtx %s Quad.\n", str[op]);
|
||||
|
||||
status_h = spi_general_get_flash_register(spi, XTX_READ_SR_H);
|
||||
|
||||
fmc_pr(QE_DBG, "\t Read Status Register-h[%#x]%#x\n", XTX_READ_SR_H,
|
||||
status_h);
|
||||
if (op == spi_nor_get_qe_by_cr(status_h)) {
|
||||
fmc_pr(QE_DBG, "\t* Quad was %s status:%#x\n", str[op], status_h);
|
||||
goto QE_END;
|
||||
}
|
||||
|
||||
spi->driver->write_enable(spi);
|
||||
status_l = spi_general_get_flash_register(spi, XTX_READ_SR_L);
|
||||
|
||||
if (op)
|
||||
status_h |= SPI_NOR_CR_QE_MASK;
|
||||
else
|
||||
status_h &= ~SPI_NOR_CR_QE_MASK;
|
||||
/* Move left to 8 bit to assign a value to the upper bits */
|
||||
reg = ((unsigned short)status_h << 8) | status_l;
|
||||
writew(reg, host->iobase);
|
||||
fmc_pr(QE_DBG, "\t Write IO[%p]%#x\n", host->iobase,
|
||||
*(unsigned short *)host->iobase);
|
||||
|
||||
spi_xtx_set_op(spi);
|
||||
|
||||
/* wait the flash have switched quad mode success */
|
||||
spi->driver->wait_ready(spi);
|
||||
|
||||
status_h = spi_general_get_flash_register(spi, XTX_READ_SR_H);
|
||||
fmc_pr(QE_DBG, "\t Read Status Register-h[%#x]:%#x\n",
|
||||
XTX_READ_SR_H, status_h);
|
||||
if (op == spi_nor_get_qe_by_cr(status_h)) {
|
||||
fmc_pr(QE_DBG, "\t %s Quad success. status_h:%#x\n",
|
||||
str[op], status_h);
|
||||
} else {
|
||||
db_msg("Error: %s Quad failed! reg:%#x\n", str[op],
|
||||
status_h);
|
||||
}
|
||||
QE_END:
|
||||
return status_h;
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
* Copyright (c) LOTUS. All rights reserved.
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <malloc.h>
|
||||
#include <asm/io.h>
|
||||
#include <spi_flash.h>
|
||||
#include <errno.h>
|
||||
#include <linux/mtd/mtd.h>
|
||||
#include <linux/lotus/fmc.h>
|
||||
|
||||
|
||||
/*****************************************************************************/
|
||||
static struct spi_flash *spiflash;
|
||||
static struct mtd_info_ex *spiinfo_ex;
|
||||
/*****************************************************************************/
|
||||
|
||||
struct spi_flash *spi_flash_probe(unsigned int bus, unsigned int cs,
|
||||
unsigned int max_hz, unsigned int spi_mode)
|
||||
{
|
||||
if (get_boot_media() != BOOT_MEDIA_SPIFLASH) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (spiflash)
|
||||
return spiflash;
|
||||
|
||||
#ifdef CONFIG_FMC_SPI_NOR
|
||||
spiflash = fmc100_spi_nor_probe(&spiinfo_ex);
|
||||
spiflash->erase_size = spiinfo_ex->erasesize;
|
||||
#endif
|
||||
|
||||
return spiflash;
|
||||
}
|
||||
/*****************************************************************************/
|
||||
|
||||
struct mtd_info_ex *get_spiflash_info(void)
|
||||
{
|
||||
if (spiinfo_ex)
|
||||
return spiinfo_ex;
|
||||
|
||||
#ifdef CONFIG_FMC_SPI_NOR
|
||||
spiinfo_ex = fmc100_get_spi_nor_info(spiflash);
|
||||
#endif
|
||||
|
||||
return spiinfo_ex;
|
||||
}
|
||||
/*****************************************************************************/
|
||||
|
||||
void spi_flash_free(struct spi_flash *flash)
|
||||
{
|
||||
}
|
||||
/*****************************************************************************/
|
||||
|
||||
#ifdef CONFIG_SPI_BLOCK_PROTECT
|
||||
void spi_flash_lock(unsigned char cmp, unsigned char level, unsigned char op)
|
||||
{
|
||||
cmp = BP_CMP_BOTTOM;
|
||||
|
||||
if (spiflash->lock)
|
||||
spiflash->lock(cmp, level, op);
|
||||
|
||||
return;
|
||||
}
|
||||
/*****************************************************************************/
|
||||
#endif /* CONFIG_SPI_BLOCK_PROTECT */
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
menuconfig LOTUS_ETH
|
||||
bool "Ethernet Support"
|
||||
default y
|
||||
|
||||
if LOTUS_ETH
|
||||
|
||||
choice
|
||||
prompt "Ethernet select"
|
||||
default NET_FEMAC
|
||||
|
||||
config NET_FEMAC
|
||||
bool "Fast Ethernet Controller"
|
||||
help
|
||||
This driver supports Fast Ethernet controller.
|
||||
|
||||
config FTGMAC030
|
||||
bool "Ftgmac030 Ethernet Support"
|
||||
help
|
||||
This driver supports Ftgmac030 Ethernet controller.
|
||||
|
||||
endchoice
|
||||
|
||||
if FTGMAC030
|
||||
choice
|
||||
prompt "FTGMAC030 Soft Version"
|
||||
default FTGMAC030_V1
|
||||
|
||||
config FTGMAC030_V1
|
||||
bool "v1.0"
|
||||
help
|
||||
This driver supports Ftgmac030 Ethernet controller.
|
||||
|
||||
config FTGMAC030_V2
|
||||
bool "v2.0"
|
||||
select PHYLIB
|
||||
select MII
|
||||
help
|
||||
This driver supports Ftgmac030 Ethernet controller.
|
||||
|
||||
endchoice
|
||||
endif
|
||||
|
||||
endif
|
||||
@@ -0,0 +1,3 @@
|
||||
|
||||
obj-$(CONFIG_NET_FEMAC) += femac/
|
||||
obj-$(CONFIG_FTGMAC030) += ftgmac030/
|
||||
@@ -0,0 +1 @@
|
||||
obj-y += net_drv.o mii_drv.o ctrl.o glb.o mac.o mdio.o sys.o
|
||||
@@ -0,0 +1,139 @@
|
||||
/*
|
||||
* Copyright (c) LOTUS. All rights reserved.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef __BSPETH_H__
|
||||
#define __BSPETH_H__
|
||||
|
||||
#include <config.h>
|
||||
#include <common.h>
|
||||
#include <command.h>
|
||||
|
||||
#include <linux/list.h>
|
||||
#include <asm/io.h>
|
||||
|
||||
#include <malloc.h> /* malloc, free, realloc */
|
||||
|
||||
#include <net.h>
|
||||
#include <miiphy.h>
|
||||
#include <asm/arch/platform.h>
|
||||
|
||||
#define OSDRV_MODULE_VERSION_STRING "ETH net controler"
|
||||
|
||||
#ifndef bit
|
||||
#define bit(nr) (1UL << (nr))
|
||||
#endif
|
||||
#define mdelay(n) udelay((n)*1000)
|
||||
/* ***********************************************************
|
||||
*
|
||||
* Global varibles and defintions
|
||||
*
|
||||
* ***********************************************************
|
||||
*/
|
||||
|
||||
/* configuerable values */
|
||||
|
||||
#ifdef BSPETH_RX_QUEUE_MULTI_DESC
|
||||
#define BSPETH_HW_DESC_DEPTH 72
|
||||
#endif
|
||||
|
||||
#define ETH_MDIO_FRQDIV 2
|
||||
|
||||
/* mdiobus device name, such as platform device name */
|
||||
#define BSPETH_MDIOBUS_NAME "bspeth_mdiobus"
|
||||
|
||||
/* eth device name, such as platform device name */
|
||||
#define BSPETH_FEMAC_NAME "lotus_femac"
|
||||
#define MAX_PHY_NAME_LEN 16
|
||||
#define BSPETH_MAX_QUEUE_DEPTH 64
|
||||
#ifdef BSPETH_RX_QUEUE_MULTI_DESC
|
||||
#define BSPETH_HW_RXQ_DEPTH 52 /* uboot */
|
||||
#else
|
||||
#define BSPETH_HW_RXQ_DEPTH 1 /* uboot */
|
||||
#endif
|
||||
#define BSPETH_HW_TXQ_DEPTH 1 /* uboot */
|
||||
|
||||
#define BSPETH_MAX_FRAME_SIZE PKTSIZE_ALIGN /* 1536 */
|
||||
|
||||
#define BSPETH_TRACE_ETH 2
|
||||
#define BSPETH_TRACE_MDIO 4
|
||||
#define BSPETH_TRACE_DRV 7
|
||||
#define BSPETH_TRACE_LEVEL 8
|
||||
|
||||
/* Error number */
|
||||
#define BSPETH_E_QUEUE (-1)
|
||||
#define BSPETH_E_BUSY (-2)
|
||||
#define BSPETH_E_FULL (-3)
|
||||
#define BSPETH_E_EMPTY (-4)
|
||||
|
||||
struct bspeth_frame_desc {
|
||||
unsigned long frm_addr; /* required by the controler */
|
||||
unsigned int frm_len : 11; /* required by the controler */
|
||||
};
|
||||
|
||||
#define bspeth_trace_fd(level, fd) bspeth_trace(level, \
|
||||
#fd "<%p>={ .frm_addr=%08lx, .frm_len=%d}", \
|
||||
&(fd), (fd).frm_addr, (fd).frm_len)
|
||||
|
||||
/* port */
|
||||
#define UP_PORT 0
|
||||
#define DOWN_PORT 1
|
||||
#define MAX_PORT 2
|
||||
|
||||
enum if_mode {
|
||||
INTERFACE_MODE_MII,
|
||||
INTERFACE_MODE_RMII
|
||||
};
|
||||
|
||||
struct bspeth_netdev_local {
|
||||
unsigned long iobase_phys; /* physical io addr */
|
||||
int port : 1; /* 0 => up port, 1 => down port */
|
||||
|
||||
#ifdef BSPETH_RX_QUEUE_MULTI_DESC
|
||||
int desc_hw_offset; /* the offset where we feed hw */
|
||||
int desc_rec_offset; /* the offset where we receve the package */
|
||||
struct bspeth_frame_desc *bspeth_desc_head;
|
||||
#endif
|
||||
|
||||
u32 link_stat;
|
||||
char *mii_name;
|
||||
unsigned char phy_addr;
|
||||
enum if_mode phy_intf;
|
||||
|
||||
/* mdio_bus freq-div, 1 for 1/100, 0 for 1/50 */
|
||||
u32 mdio_frqdiv;
|
||||
};
|
||||
|
||||
/* ***********************************************************
|
||||
*
|
||||
* Only for internal used!
|
||||
*
|
||||
* ***********************************************************
|
||||
*/
|
||||
|
||||
/* read/write IO */
|
||||
|
||||
#define _readl(c) ({ u32 __v = le32_to_cpu(__raw_readl(c)); __v; })
|
||||
#define _writel(v, c) __raw_writel(cpu_to_le32(v), c)
|
||||
|
||||
#define mk_bits(shift, nbits) ((((shift) & 0x1F) << 16) | ((nbits) & 0x1F))
|
||||
|
||||
u32 bspeth_readl(struct bspeth_netdev_local *ld, u32 ofs);
|
||||
void bspeth_writel(struct bspeth_netdev_local *ld, u32 v, u32 ofs);
|
||||
u32 bspeth_readl_bits(struct bspeth_netdev_local *ld, u32 ofs, u32 bits_desc);
|
||||
void bspeth_writel_bits(struct bspeth_netdev_local *ld, u32 v, u32 ofs, u32 bits_desc);
|
||||
|
||||
void bspeth_trace(int level, const char *fmt, ...);
|
||||
void bspeth_error(const char *fmt, ...);
|
||||
void bspeth_assert(bool cond);
|
||||
|
||||
#define local_lock_init(ld)
|
||||
#define local_lock_exit(ld)
|
||||
#define local_lock(ld)
|
||||
#define local_unlock(ld)
|
||||
|
||||
#define ud_reg_name(name) ((ld->port == UP_PORT) ? U_##name : D_##name)
|
||||
#define ud_bit_name(name) ((ld->port == UP_PORT) ? name##_U : name##_D)
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,172 @@
|
||||
/*
|
||||
* Copyright (c) LOTUS. All rights reserved.
|
||||
*/
|
||||
|
||||
|
||||
#define BSPETH_FEMAC
|
||||
|
||||
#include "bspeth.h"
|
||||
#include "ctrl.h"
|
||||
|
||||
static inline u32 _bspeth_irq_enable(struct bspeth_netdev_local *ld, u32 irqs)
|
||||
{
|
||||
u32 old = bspeth_readl(ld, GLB_RW_IRQ_ENA);
|
||||
|
||||
bspeth_writel(ld, old | irqs, GLB_RW_IRQ_ENA);
|
||||
|
||||
return old;
|
||||
}
|
||||
|
||||
static inline u32 _bspeth_irq_disable(struct bspeth_netdev_local *ld, u32 irqs)
|
||||
{
|
||||
u32 old = bspeth_readl(ld, GLB_RW_IRQ_ENA);
|
||||
|
||||
bspeth_writel(ld, old & (~irqs), GLB_RW_IRQ_ENA);
|
||||
|
||||
return old;
|
||||
}
|
||||
|
||||
static inline u32 _bspeth_read_irqstatus(struct bspeth_netdev_local *ld)
|
||||
{
|
||||
u32 status;
|
||||
|
||||
status = bspeth_readl(ld, GLB_RO_IRQ_STAT);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
u32 bspeth_irq_enable(struct bspeth_netdev_local *ld, u32 irqs)
|
||||
{
|
||||
u32 old;
|
||||
|
||||
local_lock(ld);
|
||||
old = _bspeth_irq_enable(ld, irqs);
|
||||
local_unlock(ld);
|
||||
|
||||
return old;
|
||||
}
|
||||
|
||||
u32 bspeth_irq_disable(struct bspeth_netdev_local *ld, u32 irqs)
|
||||
{
|
||||
u32 old;
|
||||
|
||||
local_lock(ld);
|
||||
old = _bspeth_irq_disable(ld, irqs);
|
||||
local_unlock(ld);
|
||||
|
||||
return old;
|
||||
}
|
||||
|
||||
u32 bspeth_read_irqstatus(struct bspeth_netdev_local *ld)
|
||||
{
|
||||
u32 status;
|
||||
|
||||
local_lock(ld);
|
||||
status = _bspeth_read_irqstatus(ld);
|
||||
local_unlock(ld);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
u32 bspeth_read_raw_irqstatus(struct bspeth_netdev_local *ld)
|
||||
{
|
||||
u32 status;
|
||||
|
||||
local_lock(ld);
|
||||
status = bspeth_readl(ld, GLB_RO_IRQ_STAT);
|
||||
local_unlock(ld);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
u32 bspeth_clear_irqstatus(struct bspeth_netdev_local *ld, u32 irqs)
|
||||
{
|
||||
u32 status;
|
||||
|
||||
local_lock(ld);
|
||||
bspeth_writel(ld, irqs, GLB_RW_IRQ_RAW);
|
||||
status = _bspeth_read_irqstatus(ld);
|
||||
local_unlock(ld);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
u32 bspeth_set_endian_mode(struct bspeth_netdev_local *ld, u32 mode)
|
||||
{
|
||||
u32 old;
|
||||
|
||||
local_lock(ld);
|
||||
old = bspeth_readl_bits(ld, GLB_ENDIAN_MOD, BITS_ENDIAN);
|
||||
bspeth_writel_bits(ld, mode, GLB_ENDIAN_MOD, BITS_ENDIAN);
|
||||
local_unlock(ld);
|
||||
|
||||
return old;
|
||||
}
|
||||
|
||||
void hw_xmitq_setfd(struct bspeth_netdev_local *ld, struct bspeth_frame_desc fd)
|
||||
{
|
||||
bspeth_writel(ld, fd.frm_addr, ud_reg_name(GLB_EQ_ADDR));
|
||||
bspeth_writel_bits(ld, fd.frm_len, ud_reg_name(GLB_EQFRM_LEN), BITS_TXINQ_LEN);
|
||||
}
|
||||
|
||||
u32 bspeth_readl(struct bspeth_netdev_local *ld, u32 ofs)
|
||||
{
|
||||
u32 reg = _readl((uintptr_t)(ld->iobase_phys + ofs));
|
||||
bspeth_trace(BSPETH_TRACE_ETH, "_readl(0x%08X) = 0x%08X",
|
||||
(u32)(ld->iobase_phys + ofs), reg);
|
||||
return reg;
|
||||
}
|
||||
|
||||
void bspeth_writel(struct bspeth_netdev_local *ld, u32 v, u32 ofs)
|
||||
{
|
||||
_writel(v, (uintptr_t)(ld->iobase_phys + ofs));
|
||||
bspeth_trace(BSPETH_TRACE_ETH, "_writel(0x%08X) = 0x%08X",
|
||||
(u32)(ld->iobase_phys + ofs), v);
|
||||
}
|
||||
|
||||
|
||||
u32 bspeth_readl_bits(struct bspeth_netdev_local *ld, u32 ofs, u32 bits_desc)
|
||||
{
|
||||
u32 _bits_desc = bits_desc;
|
||||
u32 _shift = _bits_desc >> 16; /* shift 16 bit */
|
||||
u32 _mask = ((1 << (_bits_desc & 0x1F)) - 1) << _shift;
|
||||
u32 reg = (bspeth_readl(ld, ofs) & _mask) >> _shift;
|
||||
return reg;
|
||||
}
|
||||
|
||||
void bspeth_writel_bits(struct bspeth_netdev_local *ld, u32 v, u32 ofs, u32 bits_desc)
|
||||
{
|
||||
u32 _bits_desc = bits_desc;
|
||||
u32 _shift = _bits_desc >> 16; /* shift 16 bit */
|
||||
u32 _reg = bspeth_readl(ld, ofs);
|
||||
u32 _mask = ((1 << (_bits_desc & 0x1F)) - 1) << _shift;
|
||||
bspeth_writel(ld, (_reg & (~_mask)) | ((v << _shift) & _mask), ofs);
|
||||
}
|
||||
|
||||
void bspeth_trace(int level, const char *fmt, ...)
|
||||
{
|
||||
if (level >= BSPETH_TRACE_LEVEL) {
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
printf("bspeth_trace:");
|
||||
printf(fmt, args);
|
||||
printf("\n");
|
||||
va_end(args);
|
||||
}
|
||||
}
|
||||
|
||||
void bspeth_error(const char *fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
printf("bspeth:");
|
||||
printf(fmt, args);
|
||||
printf("\n");
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
void bspeth_assert(bool cond)
|
||||
{
|
||||
if (!cond)
|
||||
printf("Assert:bspeth:%s:%d\n", __FILE__, __LINE__);
|
||||
}
|
||||
@@ -0,0 +1,148 @@
|
||||
/*
|
||||
* Copyright (c) LOTUS. All rights reserved.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef __BSPETH_CTRL_H__
|
||||
#define __BSPETH_CTRL_H__
|
||||
|
||||
#include "bspeth.h"
|
||||
|
||||
#ifdef BSPETH_FEMAC
|
||||
|
||||
/* ENDIAN */
|
||||
#define GLB_ENDIAN_MOD 0x1318
|
||||
#define BITS_ENDIAN mk_bits(0, 2)
|
||||
#define BSPETH_BIG_ENDIAN 0
|
||||
#define BSPETH_LITTLE_ENDIAN 3
|
||||
|
||||
/* IRQs */
|
||||
#define GLB_RO_IRQ_STAT 0x1330
|
||||
#define GLB_RW_IRQ_ENA 0x1334
|
||||
#define GLB_RW_IRQ_RAW 0x1338
|
||||
|
||||
/* IRQs mask bits */
|
||||
#define BITS_IRQS_U mk_bits(0, 8)
|
||||
#define BITS_VLAN_IRQS mk_bits(11, 1)
|
||||
#define BITS_MDIO_IRQS mk_bits(13, 2)
|
||||
#define BITS_IRQS_ENA_D mk_bits(17, 1)
|
||||
#define BITS_IRQS_ENA_U mk_bits(18, 1)
|
||||
#define BITS_IRQS_ENA_ALLPORT mk_bits(19, 1)
|
||||
#define BITS_IRQS_D mk_bits(20, 8)
|
||||
|
||||
#define BITS_IRQS_MASK_U (0xFF)
|
||||
#define BITS_IRQS_MASK_D (0xFF << 20)
|
||||
|
||||
/* IRQs bit name */
|
||||
#define BSPETH_INT_RX_RDY_U bit(0)
|
||||
#define BSPETH_INT_RX_RDY_D bit(20)
|
||||
#define BSPETH_INT_TX_FIN_U bit(1)
|
||||
#define BSPETH_INT_TX_FIN_D bit(21)
|
||||
#define BSPETH_INT_LINK_CH_U bit(2)
|
||||
#define BSPETH_INT_LINK_CH_D bit(22)
|
||||
#define BSPETH_INT_SPEED_CH_U bit(3)
|
||||
#define BSPETH_INT_SPEED_CH_D bit(23)
|
||||
#define BSPETH_INT_DUPLEX_CH_U bit(4)
|
||||
#define BSPETH_INT_DUPLEX_CH_D bit(24)
|
||||
#define BSPETH_INT_STATE_CH_U bit(5)
|
||||
#define BSPETH_INT_STATE_CH_D bit(25)
|
||||
#define BSPETH_INT_TXQUE_RDY_U bit(6)
|
||||
#define BSPETH_INT_TXQUE_RDY_D bit(26)
|
||||
#define BSPETH_INT_MULTI_RXRDY_U bit(7)
|
||||
#define BSPETH_INT_MULTI_RXRDY_D bit(27)
|
||||
|
||||
#define BSPETH_INT_MDIO_FINISH bit(12)
|
||||
#define BSPETH_INT_UNKNOW_VLANID bit(13)
|
||||
#define BSPETH_INT_UNKNOW_VLANM bit(14)
|
||||
|
||||
/* Tx/Rx Queue depth */
|
||||
#define U_GLB_QLEN_SET 0x0344
|
||||
#define D_GLB_QLEN_SET 0x2344
|
||||
#define BITS_TXQ_DEP mk_bits(0, 6)
|
||||
#define BITS_RXQ_DEP mk_bits(8, 6)
|
||||
|
||||
/* Rx (read only) Queue-ID and LEN */
|
||||
#define U_GLB_RO_IQFRM_DES 0x0354
|
||||
#define D_GLB_RO_IQFRM_DES 0x2354
|
||||
/* bits of UD_GLB_RO_IQFRM_DES */
|
||||
#define BITS_RXPKG_LEN mk_bits(0, 11)
|
||||
#define BITS_RXPKG_ID mk_bits(12, 6)
|
||||
#define BITS_FRM_VLAN_VID mk_bits(18, 1)
|
||||
#define BITS_FD_VID_VID mk_bits(19, 1)
|
||||
#define BITS_FD_VLANID mk_bits(20, 12)
|
||||
|
||||
/* Rx ADDR */
|
||||
#define U_GLB_IQ_ADDR 0x0358
|
||||
#define D_GLB_IQ_ADDR 0x2358
|
||||
|
||||
/* Tx ADDR and LEN */
|
||||
#define U_GLB_EQ_ADDR 0x0360
|
||||
#define D_GLB_EQ_ADDR 0x2360
|
||||
#define U_GLB_EQFRM_LEN 0x0364
|
||||
#define D_GLB_EQFRM_LEN 0x2364
|
||||
/* bits of UD_GLB_EQFRM_LEN */
|
||||
#define BITS_TXINQ_LEN mk_bits(0, 11)
|
||||
|
||||
/* Rx/Tx Queue ID */
|
||||
#define U_GLB_RO_QUEUE_ID 0x0368
|
||||
#define D_GLB_RO_QUEUE_ID 0x2368
|
||||
/* bits of UD_GLB_RO_QUEUE_ID */
|
||||
#define BITS_TXOUTQ_ID mk_bits(0, 6)
|
||||
#define BITS_TXINQ_ID mk_bits(8, 6)
|
||||
#define BITS_RXINQ_ID mk_bits(16, 6)
|
||||
|
||||
/* Rx/Tx Queue staus */
|
||||
#define U_GLB_RO_QUEUE_STAT 0x036C
|
||||
#define D_GLB_RO_QUEUE_STAT 0x236C
|
||||
/* bits of UD_GLB_RO_QUEUE_STAT */
|
||||
/* check this bit to see if we can add a Tx package */
|
||||
#define BITS_XMITQ_RDY mk_bits(24, 1)
|
||||
/* check this bit to see if we can add a Rx addr */
|
||||
#define BITS_RECVQ_RDY mk_bits(25, 1)
|
||||
/* counts in queue, include currently sending */
|
||||
#define BITS_XMITQ_CNT_INUSE mk_bits(0, 6)
|
||||
/* counts in queue, include currently receving */
|
||||
#define BITS_RECVQ_CNT_RXOK mk_bits(8, 6)
|
||||
|
||||
#define is_recv_packet(ld) (bspeth_readl(ld, GLB_RW_IRQ_RAW) & (ud_bit_name(BSPETH_INT_RX_RDY)))
|
||||
#define is_recv_packet_rx(ld) ((bspeth_readl(ld, ud_reg_name(GLB_RO_QUEUE_STAT)) >> 8) & 0x3F)
|
||||
#define hw_set_rxpkg_finish(ld) bspeth_writel(ld, ud_bit_name(BSPETH_INT_RX_RDY), GLB_RW_IRQ_RAW)
|
||||
|
||||
// //////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#define hw_get_rxpkg_id(ld) bspeth_readl_bits(ld, ud_reg_name(GLB_RO_IQFRM_DES), BITS_RXPKG_ID)
|
||||
#define hw_get_rxpkg_len(ld) bspeth_readl_bits(ld, ud_reg_name(GLB_RO_IQFRM_DES), BITS_RXPKG_LEN)
|
||||
|
||||
#define hw_get_txqid(ld) bspeth_readl_bits(ld, ud_reg_name(GLB_RO_QUEUE_ID), BITS_TXINQ_ID)
|
||||
#define hw_get_rxqid(ld) bspeth_readl_bits(ld, ud_reg_name(GLB_RO_QUEUE_ID), BITS_RXINQ_ID)
|
||||
|
||||
#define hw_xmitq_cnt_inuse(ld) bspeth_readl_bits(ld, ud_reg_name(GLB_RO_QUEUE_STAT), BITS_XMITQ_CNT_INUSE)
|
||||
#define hw_recvq_cnt_rxok(ld) bspeth_readl_bits(ld, ud_reg_name(GLB_RO_QUEUE_STAT), BITS_RECVQ_CNT_RXOK)
|
||||
|
||||
#define hw_recvq_setfd(ld, fd) bspeth_writel(ld, (fd).frm_addr, ud_reg_name(GLB_IQ_ADDR))
|
||||
|
||||
#endif
|
||||
|
||||
/* for each bits, set '1' enable the intterrupt, and '0' takes no effects */
|
||||
/* return last irq_enable status */
|
||||
u32 bspeth_irq_enable(struct bspeth_netdev_local *ld, u32 irqs);
|
||||
u32 bspeth_irq_disable(struct bspeth_netdev_local *ld, u32 irqs);
|
||||
/* return irqstatus */
|
||||
u32 bspeth_read_irqstatus(struct bspeth_netdev_local *ld);
|
||||
u32 bspeth_read_raw_irqstatus(struct bspeth_netdev_local *ld);
|
||||
/* return irqstatus after clean */
|
||||
u32 bspeth_clear_irqstatus(struct bspeth_netdev_local *ld, u32 irqs);
|
||||
|
||||
u32 bspeth_set_endian_mode(struct bspeth_netdev_local *ld, u32 mode);
|
||||
void hw_xmitq_setfd(struct bspeth_netdev_local *ld, struct bspeth_frame_desc fd);
|
||||
|
||||
/* Tx/Rx queue operation */
|
||||
int bspeth_set_hwq_depth(struct bspeth_netdev_local *ld);
|
||||
int bspeth_get_hwq_xmit_depth(struct bspeth_netdev_local *ld);
|
||||
int bspeth_get_hwq_recv_depth(struct bspeth_netdev_local *ld);
|
||||
|
||||
#define bspeth_invalid_txqfd_addr(addr) ((addr) & 0x3)
|
||||
#define bspeth_invalid_rxqfd_addr(addr) ((addr) & 0x3)
|
||||
#define bspeth_invalid_rxpkg_len(len) (!((len) >= 42 && (len) <= BSPETH_MAX_FRAME_SIZE))
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
* Copyright (c) LOTUS. All rights reserved.
|
||||
*/
|
||||
|
||||
|
||||
#define BSPETH_FEMAC
|
||||
|
||||
#include <config.h>
|
||||
#include "bspeth.h"
|
||||
#include "mac.h"
|
||||
#include "ctrl.h"
|
||||
#include "glb.h"
|
||||
|
||||
int bspeth_glb_preinit_dummy(struct bspeth_netdev_local *ld)
|
||||
{
|
||||
local_lock_init(ld);
|
||||
|
||||
/*
|
||||
* bspeth_glb_preinit_dummy
|
||||
* HW MAX DEFAULT RX-PKT-LEN [42,1518]
|
||||
* HW MAC FILTER TABLE DISABLE
|
||||
*/
|
||||
|
||||
/* soft reset */
|
||||
bspeth_writel_bits(ld, 1, GLB_SOFT_RESET, BITS_ETH_SOFT_RESET);
|
||||
mdelay(1);
|
||||
bspeth_writel_bits(ld, 0, GLB_SOFT_RESET, BITS_ETH_SOFT_RESET);
|
||||
|
||||
bspeth_set_endian_mode(ld, BSPETH_LITTLE_ENDIAN);
|
||||
|
||||
bspeth_set_linkstat(ld, 0);
|
||||
|
||||
bspeth_set_negmode(ld, BSPETH_NEGMODE_CPUSET);
|
||||
|
||||
/* RMII mode */
|
||||
bspeth_set_mii_mode(ld, ud_bit_name(BSPETH_MII_RMII_MODE));
|
||||
|
||||
bspeth_writel_bits(ld, ~0, GLB_RW_IRQ_ENA, ud_bit_name(BITS_IRQS));
|
||||
bspeth_writel_bits(ld, ~0, GLB_RW_IRQ_ENA, ud_bit_name(BITS_IRQS_ENA));
|
||||
bspeth_writel_bits(ld, ~0, GLB_RW_IRQ_ENA, BITS_IRQS_ENA_ALLPORT);
|
||||
bspeth_irq_disable(ld, ~0);
|
||||
|
||||
/* init */
|
||||
bspeth_writel(ld, 0, GLB_FWCTRL);
|
||||
bspeth_writel(ld, 0, GLB_MACTCTRL);
|
||||
|
||||
/* disable vlan func */
|
||||
bspeth_writel_bits(ld, 0, GLB_FWCTRL, BITS_VLAN_ENABLE);
|
||||
|
||||
/* enable UpEther<->CPU */
|
||||
bspeth_writel_bits(ld, 1, GLB_FWCTRL, BITS_FW2CPU_ENA_UP);
|
||||
bspeth_writel_bits(ld, 0, GLB_FWCTRL, BITS_FWALL2CPU_UP);
|
||||
bspeth_writel_bits(ld, 0, GLB_MACTCTRL, BITS_BROAD2CPU_UP);
|
||||
bspeth_writel_bits(ld, 1, GLB_MACTCTRL, BITS_MACT_ENA_UP);
|
||||
|
||||
/* enable DownEther<->CPU and UpEther<->CPU */
|
||||
bspeth_writel_bits(ld, 1, GLB_FWCTRL, BITS_FW2CPU_ENA_DOWN);
|
||||
bspeth_writel_bits(ld, 0, GLB_FWCTRL, BITS_FWALL2CPU_DOWN);
|
||||
bspeth_writel_bits(ld, 0, GLB_MACTCTRL, BITS_BROAD2CPU_DOWN);
|
||||
bspeth_writel_bits(ld, 1, GLB_MACTCTRL, BITS_MACT_ENA_DOWN);
|
||||
|
||||
bspeth_set_mac_leadcode_cnt_limit(ld, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* Copyright (c) LOTUS. All rights reserved.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef __BSPETH_GLB_H__
|
||||
#define __BSPETH_GLB_H__
|
||||
|
||||
#include "bspeth.h"
|
||||
|
||||
#define GLB_HOSTMAC_L32 0x1300
|
||||
#define BITS_HOSTMAC_L32 mk_bits(0, 32)
|
||||
#define GLB_HOSTMAC_H16 0x1304
|
||||
#define BITS_HOSTMAC_H16 mk_bits(0, 16)
|
||||
|
||||
#define GLB_SOFT_RESET 0x1308
|
||||
#define BITS_ETH_SOFT_RESET mk_bits(0, 1)
|
||||
|
||||
#define GLB_FWCTRL 0x1310
|
||||
#define BITS_VLAN_ENABLE mk_bits(0, 1)
|
||||
#define BITS_FW2CPU_ENA_UP mk_bits(5, 1)
|
||||
#define BITS_FWALL2CPU_UP mk_bits(7, 1)
|
||||
#define BITS_FW2CPU_ENA_DOWN mk_bits(9, 1)
|
||||
#define BITS_FWALL2CPU_DOWN mk_bits(11, 1)
|
||||
|
||||
#define GLB_MACTCTRL 0x1314
|
||||
#define BITS_BROAD2CPU_UP mk_bits(5, 1)
|
||||
#define BITS_BROAD2CPU_DOWN mk_bits(13, 1)
|
||||
#define BITS_MACT_ENA_DOWN mk_bits(15, 1)
|
||||
#define BITS_MACT_ENA_UP mk_bits(7, 1)
|
||||
|
||||
#define GLB_MAC0_L32 0x1400
|
||||
#define GLB_MAC0_H16 0x1404
|
||||
#define BITS_MAC0_H16 mk_bits(0, 16)
|
||||
#define BITS_PKT2CPU_UP mk_bits(21, 1)
|
||||
#define BITS_PKT2CPU_DOWN mk_bits(19, 1)
|
||||
|
||||
int bspeth_glb_preinit_dummy(struct bspeth_netdev_local *ld);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,117 @@
|
||||
/*
|
||||
* Copyright (c) LOTUS. All rights reserved.
|
||||
*/
|
||||
|
||||
|
||||
#define BSPETH_FEMAC
|
||||
|
||||
#include "bspeth.h"
|
||||
#include "mac.h"
|
||||
|
||||
static u32 _set_linkstat(struct bspeth_netdev_local *ld, u32 mode)
|
||||
{
|
||||
u32 old;
|
||||
|
||||
old = bspeth_readl_bits(ld, ud_reg_name(MAC_PORTSET), BITS_MACSTAT);
|
||||
bspeth_writel_bits(ld, mode, ud_reg_name(MAC_PORTSET), BITS_MACSTAT);
|
||||
|
||||
return old;
|
||||
}
|
||||
|
||||
static u32 _set_negmode(struct bspeth_netdev_local *ld, u32 mode)
|
||||
{
|
||||
u32 old;
|
||||
|
||||
old = bspeth_readl_bits(ld, ud_reg_name(MAC_PORTSEL), BITS_NEGMODE);
|
||||
bspeth_writel_bits(ld, mode, ud_reg_name(MAC_PORTSEL), BITS_NEGMODE);
|
||||
|
||||
return old;
|
||||
}
|
||||
|
||||
static u32 _get_negmode(struct bspeth_netdev_local *ld)
|
||||
{
|
||||
u32 val;
|
||||
|
||||
val = bspeth_readl_bits(ld, ud_reg_name(MAC_PORTSEL), BITS_NEGMODE);
|
||||
return val;
|
||||
}
|
||||
|
||||
void bspeth_set_linkstat(struct bspeth_netdev_local *ld, u32 mode)
|
||||
{
|
||||
local_lock(ld);
|
||||
(void)_set_linkstat(ld, mode);
|
||||
local_unlock(ld);
|
||||
}
|
||||
|
||||
u32 bspeth_get_linkstat(struct bspeth_netdev_local *ld)
|
||||
{
|
||||
u32 val;
|
||||
|
||||
local_lock(ld);
|
||||
val = bspeth_readl_bits(ld, ud_reg_name(MAC_RO_STAT), BITS_MACSTAT);
|
||||
local_unlock(ld);
|
||||
return val;
|
||||
}
|
||||
|
||||
void bspeth_set_mac_leadcode_cnt_limit(struct bspeth_netdev_local *ld, u32 cnt)
|
||||
{
|
||||
local_lock(ld);
|
||||
(void)bspeth_readl_bits(ld, ud_reg_name(MAC_TX_IPGCTRL),
|
||||
BITS_PRE_CNT_LIMIT);
|
||||
bspeth_writel_bits(ld, cnt, ud_reg_name(MAC_TX_IPGCTRL),
|
||||
BITS_PRE_CNT_LIMIT);
|
||||
local_unlock(ld);
|
||||
}
|
||||
|
||||
void bspeth_set_mac_trans_interval_bits(struct bspeth_netdev_local *ld, u32 nbits)
|
||||
{
|
||||
u32 linkstat, negmode;
|
||||
|
||||
local_lock(ld);
|
||||
|
||||
negmode = _set_negmode(ld, BSPETH_NEGMODE_CPUSET);
|
||||
linkstat = _set_linkstat(ld, 0);
|
||||
mdelay(1);
|
||||
|
||||
(void)bspeth_readl_bits(ld, ud_reg_name(MAC_TX_IPGCTRL), BITS_IPG);
|
||||
bspeth_writel_bits(ld, nbits, ud_reg_name(MAC_TX_IPGCTRL), BITS_IPG);
|
||||
udelay(100); /* delay 100us */
|
||||
|
||||
_set_negmode(ld, negmode);
|
||||
_set_linkstat(ld, linkstat);
|
||||
|
||||
local_unlock(ld);
|
||||
}
|
||||
|
||||
void bspeth_set_mac_fc_interval(struct bspeth_netdev_local *ld, u32 para)
|
||||
{
|
||||
local_lock(ld);
|
||||
(void)bspeth_readl_bits(ld, ud_reg_name(MAC_TX_IPGCTRL), BITS_FC_INTER);
|
||||
bspeth_writel_bits(ld, para, ud_reg_name(MAC_TX_IPGCTRL), BITS_FC_INTER);
|
||||
local_unlock(ld);
|
||||
}
|
||||
|
||||
void bspeth_set_negmode(struct bspeth_netdev_local *ld, u32 mode)
|
||||
{
|
||||
local_lock(ld);
|
||||
_set_negmode(ld, mode);
|
||||
local_unlock(ld);
|
||||
}
|
||||
|
||||
u32 bspeth_get_negmode(struct bspeth_netdev_local *ld)
|
||||
{
|
||||
u32 val;
|
||||
|
||||
local_lock(ld);
|
||||
val = _get_negmode(ld);
|
||||
local_unlock(ld);
|
||||
return val;
|
||||
}
|
||||
|
||||
void bspeth_set_mii_mode(struct bspeth_netdev_local *ld, u32 mode)
|
||||
{
|
||||
local_lock(ld);
|
||||
(void)bspeth_readl_bits(ld, ud_reg_name(MAC_PORTSEL), BITS_MII_MODE);
|
||||
bspeth_writel_bits(ld, mode, ud_reg_name(MAC_PORTSEL), BITS_MII_MODE);
|
||||
local_unlock(ld);
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
* Copyright (c) LOTUS. All rights reserved.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef __BSPETH_MAC_H__
|
||||
#define __BSPETH_MAC_H__
|
||||
|
||||
#include "bspeth.h"
|
||||
|
||||
#ifdef BSPETH_FEMAC
|
||||
|
||||
#define U_MAC_PORTSEL 0x0200
|
||||
#define D_MAC_PORTSEL 0x2200
|
||||
#define U_MAC_RO_STAT 0x0204
|
||||
#define D_MAC_RO_STAT 0x2204
|
||||
#define U_MAC_PORTSET 0x0208
|
||||
#define D_MAC_PORTSET 0x2208
|
||||
#define U_MAC_STAT_CHANGE 0x020C
|
||||
#define D_MAC_STAT_CHANGE 0x220C
|
||||
#define U_MAC_SET 0x0210
|
||||
#define D_MAC_SET 0x2210
|
||||
#define U_MAC_RX_IPGCTRL 0x0214
|
||||
#define D_MAC_RX_IPGCTRL 0x2214
|
||||
#define U_MAC_TX_IPGCTRL 0x0218
|
||||
#define D_MAC_TX_IPGCTRL 0x2218
|
||||
|
||||
/* bits of UD_MAC_PORTSET and UD_MAC_RO_STAT */
|
||||
#define BITS_MACSTAT mk_bits(0, 3)
|
||||
|
||||
/* bits of U_MAC_PORTSEL and D_MAC_PORTSEL */
|
||||
#define BITS_NEGMODE mk_bits(0, 1)
|
||||
#define BITS_MII_MODE mk_bits(1, 1)
|
||||
|
||||
/* bits of U_MAC_TX_IPGCTRL and D_MAC_TX_IPGCTRL */
|
||||
#define BITS_PRE_CNT_LIMIT mk_bits(23, 3)
|
||||
#define BITS_IPG mk_bits(16, 7)
|
||||
#define BITS_FC_INTER mk_bits(0, 16)
|
||||
|
||||
#endif
|
||||
|
||||
#define BSPETH_SPD_100M bit(2)
|
||||
#define BSPETH_LINKED bit(1)
|
||||
#define BSPETH_DUP_FULL bit(0)
|
||||
|
||||
void bspeth_set_mac_leadcode_cnt_limit(struct bspeth_netdev_local *ld, u32 cnt);
|
||||
void bspeth_set_mac_trans_interval_bits(struct bspeth_netdev_local *ld, u32 nbits);
|
||||
void bspeth_set_mac_fc_interval(struct bspeth_netdev_local *ld, u32 para);
|
||||
|
||||
void bspeth_set_linkstat(struct bspeth_netdev_local *ld, u32 mode);
|
||||
u32 bspeth_get_linkstat(struct bspeth_netdev_local *ld);
|
||||
|
||||
#define BSPETH_NEGMODE_CPUSET 1
|
||||
#define BSPETH_NEGMODE_AUTO 0
|
||||
|
||||
void bspeth_set_negmode(struct bspeth_netdev_local *ld, u32 mode);
|
||||
u32 bspeth_get_negmode(struct bspeth_netdev_local *ld);
|
||||
|
||||
#define BSPETH_MII_MODE 0
|
||||
#define BSPETH_RMII_MODE 1
|
||||
|
||||
void bspeth_set_mii_mode(struct bspeth_netdev_local *ld, u32 mode);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,104 @@
|
||||
/*
|
||||
* Copyright (c) LOTUS. All rights reserved.
|
||||
*/
|
||||
|
||||
#define BSPETH_FEMAC
|
||||
|
||||
#include "bspeth.h"
|
||||
#include "mdio.h"
|
||||
|
||||
/* write mdio registers reset value */
|
||||
static void mdio_reg_reset(struct bspeth_netdev_local *ld)
|
||||
{
|
||||
bspeth_writel(ld, 0x00008000, MDIO_RWCTRL);
|
||||
bspeth_writel(ld, 0x00000001, U_MDIO_PHYADDR);
|
||||
bspeth_writel(ld, 0x00000001, D_MDIO_PHYADDR);
|
||||
bspeth_writel(ld, 0x04631EA9, U_MDIO_ANEG_CTRL);
|
||||
bspeth_writel(ld, 0x04631EA9, D_MDIO_ANEG_CTRL);
|
||||
bspeth_writel(ld, 0x00000000, U_MDIO_IRQENA);
|
||||
bspeth_writel(ld, 0x00000000, D_MDIO_IRQENA);
|
||||
}
|
||||
|
||||
static int wait_mdio_ready(struct bspeth_netdev_local *ld)
|
||||
{
|
||||
int timeout_us = 5000;
|
||||
|
||||
while (--timeout_us && !test_mdio_ready(ld))
|
||||
udelay(50); /* delay 50us */
|
||||
|
||||
return timeout_us;
|
||||
}
|
||||
|
||||
int bspeth_mdio_read(struct bspeth_netdev_local *ld, int phy_addr, unsigned int regnum)
|
||||
{
|
||||
int val = 0;
|
||||
bspeth_assert((!((unsigned int)phy_addr & (~0x1F))) && (!(regnum & (~0x1F))));
|
||||
|
||||
local_lock(ld);
|
||||
|
||||
if (!wait_mdio_ready(ld)) {
|
||||
bspeth_error("mdio busy");
|
||||
goto error_exit;
|
||||
}
|
||||
|
||||
mdio_start_phyread(ld, (unsigned int)phy_addr, regnum);
|
||||
|
||||
if (wait_mdio_ready(ld) != 0) {
|
||||
val = mdio_get_phyread_val(ld);
|
||||
} else {
|
||||
bspeth_error("read timeout");
|
||||
}
|
||||
|
||||
error_exit:
|
||||
|
||||
local_unlock(ld);
|
||||
|
||||
bspeth_trace(BSPETH_TRACE_MDIO, "phy_addr = %d, regnum = %d, val = 0x%04x", phy_addr,
|
||||
(int)regnum, val);
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
int bspeth_mdio_write(struct bspeth_netdev_local *ld, int phy_addr, int regnum, int val)
|
||||
{
|
||||
bspeth_assert((!((unsigned int)phy_addr & (~0x1F))) && (!((unsigned int)regnum & (~0x1F))));
|
||||
|
||||
bspeth_trace(BSPETH_TRACE_MDIO, "phy_addr = %d, regnum = %d", phy_addr, regnum);
|
||||
|
||||
local_lock(ld);
|
||||
|
||||
if (!wait_mdio_ready(ld)) {
|
||||
bspeth_error("mdio busy");
|
||||
val = -1;
|
||||
goto error_exit;
|
||||
}
|
||||
|
||||
mdio_phywrite(ld, (unsigned int)phy_addr, (unsigned int)regnum, (unsigned int)val);
|
||||
|
||||
error_exit:
|
||||
|
||||
local_unlock(ld);
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
int bspeth_mdio_reset(struct bspeth_netdev_local *ld)
|
||||
{
|
||||
mdio_reg_reset(ld);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int bspeth_mdio_init(struct bspeth_netdev_local *ld)
|
||||
{
|
||||
local_lock_init(ld);
|
||||
|
||||
bspeth_mdio_reset(ld);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void bspeth_mdio_exit(struct bspeth_netdev_local *ld)
|
||||
{
|
||||
local_lock_exit(ld);
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* Copyright (c) LOTUS. All rights reserved.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef __BSPETH_MDIO_H__
|
||||
#define __BSPETH_MDIO_H__
|
||||
|
||||
#include "bspeth.h"
|
||||
|
||||
#ifdef BSPETH_FEMAC
|
||||
|
||||
#define MDIO_RWCTRL 0x1100
|
||||
#define MDIO_RO_DATA 0x1104
|
||||
#define U_MDIO_PHYADDR 0x0108
|
||||
#define D_MDIO_PHYADDR 0x2108
|
||||
#define U_MDIO_RO_STAT 0x010C
|
||||
#define D_MDIO_RO_STAT 0x210C
|
||||
#define U_MDIO_ANEG_CTRL 0x0110
|
||||
#define D_MDIO_ANEG_CTRL 0x2110
|
||||
#define U_MDIO_IRQENA 0x0114
|
||||
#define D_MDIO_IRQENA 0x2114
|
||||
|
||||
#define mdio_mk_rwctl(cpu_data_in, finish, rw, phy_exaddr, frq_div, phy_regnum) \
|
||||
(((cpu_data_in) << 16) | (((finish) & 0x01) << 15) | (((rw) & 0x01) << 13) | \
|
||||
(((phy_exaddr) & 0x1F) << 8) | (((frq_div) & 0x7) << 5) | ((phy_regnum) & 0x1F))
|
||||
|
||||
/* hardware set bit'15 of MDIO_REG(0) if mdio ready */
|
||||
#define test_mdio_ready(ld) (bspeth_readl(ld, MDIO_RWCTRL) & bit(15))
|
||||
|
||||
#define mdio_start_phyread(ld, phy_addr, regnum) \
|
||||
bspeth_writel(ld, mdio_mk_rwctl(0, 0, 0, phy_addr, (ld)->mdio_frqdiv, regnum), MDIO_RWCTRL)
|
||||
|
||||
#define mdio_get_phyread_val(ld) (bspeth_readl(ld, MDIO_RO_DATA) & 0xFFFF)
|
||||
|
||||
#define mdio_phywrite(ld, phy_addr, regnum, val) \
|
||||
bspeth_writel(ld, mdio_mk_rwctl(val, 0, 1, phy_addr, (ld)->mdio_frqdiv, regnum), MDIO_RWCTRL)
|
||||
|
||||
#endif
|
||||
|
||||
/* APIs */
|
||||
int bspeth_mdio_read(struct bspeth_netdev_local *ld, int phy_addr, unsigned int regnum);
|
||||
int bspeth_mdio_write(struct bspeth_netdev_local *ld, int phy_addr, int regnum, int val);
|
||||
int bspeth_mdio_reset(struct bspeth_netdev_local *ld);
|
||||
int bspeth_mdio_init(struct bspeth_netdev_local *ld);
|
||||
void bspeth_mdio_exit(struct bspeth_netdev_local *ld);
|
||||
#endif
|
||||
@@ -0,0 +1,152 @@
|
||||
/*
|
||||
* Copyright (c) LOTUS. All rights reserved.
|
||||
*/
|
||||
|
||||
|
||||
#include "bspeth.h"
|
||||
#include "mdio.h"
|
||||
#include "mii_drv.h"
|
||||
#include <config.h>
|
||||
|
||||
/* MDIO Bus Interface */
|
||||
static int bspeth_mdiobus_read(struct mii_dev *bus, int addr, int devad, int reg)
|
||||
{
|
||||
struct bspeth_netdev_local *ld = (struct bspeth_netdev_local *)bus->priv;
|
||||
|
||||
return bspeth_mdio_read(ld, addr, reg);
|
||||
}
|
||||
|
||||
static int bspeth_mdiobus_write(struct mii_dev *bus, int addr, int devad,
|
||||
int reg, u16 value)
|
||||
{
|
||||
struct bspeth_netdev_local *ld = (struct bspeth_netdev_local *)bus->priv;
|
||||
|
||||
bspeth_mdio_write(ld, addr, reg, value);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define PHY_ID_KSZ8051 0x00221550
|
||||
#define PHY_ID_KSZ8081 0x00221560
|
||||
#define PHY_ID_MASK 0xFFFFFFF0
|
||||
|
||||
static bool get_fephy_id(const char *devname, unsigned char phyaddr, u32 *phy_id)
|
||||
{
|
||||
u16 id1 = 0;
|
||||
u16 id2 = 0;
|
||||
|
||||
if (miiphy_read(devname, phyaddr, MII_PHYSID1, &id1)) {
|
||||
printf("%s,%d:PHY_PHYIDR1 read failed!\n", __func__, __LINE__);
|
||||
return false;
|
||||
}
|
||||
if (miiphy_read(devname, phyaddr, MII_PHYSID2, &id2)) {
|
||||
printf("%s,%d:PHY_PHYIDR2 read failed!\n", __func__, __LINE__);
|
||||
return false;
|
||||
}
|
||||
|
||||
*phy_id = (id1 & 0xffff) << 16; /* high 16 bit */
|
||||
*phy_id |= (id2 & 0xffff);
|
||||
|
||||
/* If the phy_id is all Fs, there is no device there */
|
||||
if (*phy_id == 0xffffffff || *phy_id == 0 || *phy_id == 0xFFFF || *phy_id == 0xFFFF0000) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool phy_detected(const char *devname, unsigned char phyaddr)
|
||||
{
|
||||
u32 phy_id = 0;
|
||||
|
||||
if (!get_fephy_id(devname, phyaddr, &phy_id)) return false;
|
||||
|
||||
/* run this at RMII mode */
|
||||
if (BSPETH_MII_RMII_MODE_U == 1) {
|
||||
/* PHY-KSZ8051RNL */
|
||||
if ((phy_id & PHY_ID_MASK) == PHY_ID_KSZ8051) {
|
||||
unsigned short reg = 0;
|
||||
|
||||
if (miiphy_read(devname, phyaddr, 0x1F, ®)) {
|
||||
printf("PHY 0x1F read failed\n");
|
||||
return false;
|
||||
}
|
||||
reg |= bit(7); /* bit7:set phy RMII 50MHz clk; */
|
||||
if (miiphy_write(devname, phyaddr, 0x1F, reg)) {
|
||||
printf("PHY 0x1F write failed\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (miiphy_read(devname, phyaddr, 0x16, ®)) {
|
||||
printf("PHY 0x16 read failed\n");
|
||||
return false;
|
||||
}
|
||||
reg |= bit(1); /* set phy RMII override; */
|
||||
if (miiphy_write(devname, phyaddr, 0x16, reg)) {
|
||||
printf("PHY 0x16 write failed\n");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/* PHY-KSZ8081 */
|
||||
if ((phy_id & PHY_ID_MASK) == PHY_ID_KSZ8081) {
|
||||
unsigned short val = 0;
|
||||
|
||||
if (miiphy_read(devname, phyaddr, 0x1F, &val) != 0) {
|
||||
printf("PHY 0x1F read failed\n");
|
||||
return false;
|
||||
};
|
||||
val |= bit(7); /* bit7:set phy RMII 50MHz clk; */
|
||||
if (miiphy_write(devname, phyaddr, 0x1F, val) != 0) {
|
||||
printf("PHY 0x1F write failed\n");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static int g_mdio_registered;
|
||||
|
||||
int bspeth_mdiobus_driver_init(struct bspeth_netdev_local *ld)
|
||||
{
|
||||
memset(ld->mii_name, 0, MAX_PHY_NAME_LEN);
|
||||
snprintf(ld->mii_name, MAX_PHY_NAME_LEN, "mii_bspeth");
|
||||
ld->mdio_frqdiv = ETH_MDIO_FRQDIV;
|
||||
#if defined(CONFIG_MII) || defined(CONFIG_CMD_MII)
|
||||
if (!g_mdio_registered) {
|
||||
struct mii_dev *bus = mdio_alloc();
|
||||
|
||||
if (!bus) {
|
||||
printf("Failed to allocate MDIO bus\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
bus->priv = ld;
|
||||
bus->read = bspeth_mdiobus_read;
|
||||
bus->write = bspeth_mdiobus_write;
|
||||
snprintf(bus->name, sizeof(bus->name), ld->mii_name);
|
||||
|
||||
bspeth_mdio_init(ld);
|
||||
|
||||
if (mdio_register(bus)) {
|
||||
mdio_free(bus);
|
||||
return -1;
|
||||
}
|
||||
|
||||
miiphy_set_current_dev(ld->mii_name);
|
||||
g_mdio_registered = 1;
|
||||
}
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
void bspeth_mdiobus_driver_exit(struct bspeth_netdev_local *ld)
|
||||
{
|
||||
/* add this to avoid the first time to use eth will print 'No such device: XXXXX' message. */
|
||||
if (!miiphy_get_current_dev()) {
|
||||
return;
|
||||
}
|
||||
|
||||
bspeth_mdio_exit(ld);
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
/*
|
||||
* Copyright (c) LOTUS. All rights reserved.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef __BSPETH_MII_DRV_H__
|
||||
#define __BSPETH_MII_DRV_H__
|
||||
|
||||
bool phy_detected(const char *devname, unsigned char phyaddr);
|
||||
#endif
|
||||
@@ -0,0 +1,536 @@
|
||||
/*
|
||||
* Copyright (c) LOTUS. All rights reserved.
|
||||
*/
|
||||
|
||||
|
||||
#define BSPETH_FEMAC
|
||||
#include "bspeth.h"
|
||||
#include "mac.h"
|
||||
#include "ctrl.h"
|
||||
#include "glb.h"
|
||||
#include "sys.h"
|
||||
#include "mii_drv.h"
|
||||
#include <config.h>
|
||||
#include <miiphy.h>
|
||||
#include <net.h>
|
||||
#include <cpu_func.h>
|
||||
/*************************************************************************/
|
||||
int bspeth_mdiobus_driver_init(struct bspeth_netdev_local *ld);
|
||||
void bspeth_mdiobus_driver_exit(struct bspeth_netdev_local *ld);
|
||||
|
||||
/* Used when trying to connect to a specific phy (mii bus id:phy device id) */
|
||||
/* refer linux-2.6.23 ./include/linux/phy.h line 76 */
|
||||
char mdio_bus_name[MAX_PHY_NAME_LEN];
|
||||
|
||||
#define MAC_LEN 6
|
||||
|
||||
struct bspeth_netdev_local bspeth_devs_priv[MAX_PORT] = {
|
||||
{
|
||||
.port = UP_PORT,
|
||||
.mii_name = mdio_bus_name,
|
||||
.phy_addr = SFV_PHY_U,
|
||||
.phy_intf = BSPETH_MII_RMII_MODE_U,
|
||||
},
|
||||
{
|
||||
.port = DOWN_PORT,
|
||||
.mii_name = mdio_bus_name,
|
||||
.phy_addr = SFV_PHY_D,
|
||||
.phy_intf = BSPETH_MII_RMII_MODE_D,
|
||||
},
|
||||
};
|
||||
|
||||
static int set_mac_address(const char *mac)
|
||||
{
|
||||
u32 val;
|
||||
|
||||
val = ((u32)mac[0] << 8) | (u32)mac[1]; /* mac 0 [8:15], mac 1 [0:7] */
|
||||
writel(val, REG_BASE_SF + GLB_HOSTMAC_H16);
|
||||
/* mac 2 [24:31], mac 3 [16:23], mac 4 [8:15], mac 5 [0:7] */
|
||||
val = ((u32)mac[2] << 24) | ((u32)mac[3] << 16) | ((u32)mac[4] << 8) | (u32)mac[5];
|
||||
writel(val, REG_BASE_SF + GLB_HOSTMAC_L32);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef BSPETH_RX_QUEUE_MULTI_DESC
|
||||
static int bspeth_init_hw_desc_queue(struct bspeth_netdev_local *ld)
|
||||
{
|
||||
struct bspeth_frame_desc *queue_phy_addr = NULL;
|
||||
int i;
|
||||
|
||||
const int size = BSPETH_HW_DESC_DEPTH * sizeof(struct bspeth_frame_desc);
|
||||
/* init rx fq */
|
||||
queue_phy_addr = (struct bspeth_frame_desc *)malloc(size);
|
||||
if (queue_phy_addr == NULL) {
|
||||
printf("alloc rx fq error!\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
memset((void *)queue_phy_addr, 0, size);
|
||||
ld->bspeth_desc_head = queue_phy_addr;
|
||||
ld->desc_hw_offset = 0;
|
||||
ld->desc_rec_offset = 0;
|
||||
|
||||
for (i = 0; i < BSPETH_HW_DESC_DEPTH; i++) {
|
||||
queue_phy_addr[i].frm_addr = (unsigned long)memalign(PKTALIGN, PKTSIZE_ALIGN);
|
||||
queue_phy_addr[i].frm_len = 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef BSPETH_RX_QUEUE_MULTI_DESC
|
||||
static int bspeth_destroy_hw_desc_queue(struct bspeth_netdev_local *ld)
|
||||
{
|
||||
struct bspeth_frame_desc *queue_phy_addr = ld->bspeth_desc_head;
|
||||
int i;
|
||||
|
||||
if (queue_phy_addr != NULL) {
|
||||
for (i = 0; i < BSPETH_HW_DESC_DEPTH; i++)
|
||||
free((void *)queue_phy_addr[i].frm_addr);
|
||||
|
||||
free(ld->bspeth_desc_head);
|
||||
ld->bspeth_desc_head = NULL;
|
||||
}
|
||||
ld->desc_hw_offset = 0;
|
||||
ld->desc_rec_offset = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
int eth_set_host_mac_address(struct eth_device *dev)
|
||||
{
|
||||
unsigned char mac[MAC_LEN];
|
||||
|
||||
memset(mac, 0, sizeof(mac));
|
||||
|
||||
if (!eth_env_get_enetaddr("ethaddr", mac)) {
|
||||
printf("MAC address invalid!\n");
|
||||
#ifdef CONFIG_NET_RANDOM_ETHADDR
|
||||
net_random_ethaddr(mac);
|
||||
printf("Set Random MAC address!\n");
|
||||
eth_env_set_enetaddr("ethaddr", mac);
|
||||
#endif
|
||||
}
|
||||
|
||||
set_mac_address((char *)mac);
|
||||
memcpy(dev->enetaddr, mac, MAC_LEN);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void phy_print_status(struct bspeth_netdev_local *ld, unsigned int stat)
|
||||
{
|
||||
printf("%s : phy status change : LINK=%s : DUPLEX=%s : SPEED=%s\n",
|
||||
(ld->port == UP_PORT) ? "eth0" : "eth1",
|
||||
(stat & BSPETH_LINKED) ? "UP" : "DOWN",
|
||||
(stat & BSPETH_DUP_FULL) ? "FULL" : "HALF",
|
||||
(stat & BSPETH_SPD_100M) ? "100M" : "10M");
|
||||
}
|
||||
|
||||
static void bspeth_adjust_link(struct bspeth_netdev_local *ld)
|
||||
{
|
||||
u32 stat = 0;
|
||||
int timeout_us = 1000;
|
||||
/* this env phy_link_time used to solve the difference phy auto-negotiation time of various phys */
|
||||
char *timeout = env_get("phy_link_time");
|
||||
if (timeout != NULL) {
|
||||
timeout_us = simple_strtol(timeout, 0, 10); /* Base10 */
|
||||
if (timeout_us < 0)
|
||||
timeout_us = 1000; /* delay 1000us */
|
||||
}
|
||||
retry:
|
||||
udelay(1);
|
||||
|
||||
stat |= miiphy_link(ld->mii_name, ld->phy_addr) ? BSPETH_LINKED : 0;
|
||||
stat |= miiphy_duplex(ld->mii_name, ld->phy_addr) == FULL ? BSPETH_DUP_FULL : 0;
|
||||
stat |= miiphy_speed(ld->mii_name, ld->phy_addr) == _100BASET ? BSPETH_SPD_100M : 0;
|
||||
if (--timeout_us && !(stat & BSPETH_LINKED))
|
||||
goto retry;
|
||||
|
||||
if (stat != ld->link_stat) {
|
||||
bspeth_set_linkstat(ld, stat);
|
||||
phy_print_status(ld, stat);
|
||||
ld->link_stat = stat;
|
||||
bspeth_set_mii_mode(ld, ld->phy_intf);
|
||||
}
|
||||
|
||||
set_phy_valtage();
|
||||
}
|
||||
|
||||
static int bspeth_net_open(struct bspeth_netdev_local *ld)
|
||||
{
|
||||
/* enable sys-ctrl-en and clk-en */
|
||||
bspeth_sys_startup();
|
||||
|
||||
/* setup hardware tx dep */
|
||||
bspeth_writel_bits(ld, BSPETH_HW_TXQ_DEPTH, ud_reg_name(GLB_QLEN_SET), BITS_TXQ_DEP);
|
||||
|
||||
/* setup hardware rx dep */
|
||||
bspeth_writel_bits(ld, BSPETH_HW_RXQ_DEPTH, ud_reg_name(GLB_QLEN_SET), BITS_RXQ_DEP);
|
||||
|
||||
ld->link_stat = 0;
|
||||
bspeth_adjust_link(ld);
|
||||
|
||||
bspeth_irq_enable(ld, ud_bit_name(BSPETH_INT_RX_RDY));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int bspeth_net_close(struct bspeth_netdev_local *ld)
|
||||
{
|
||||
bspeth_glb_preinit_dummy(ld);
|
||||
|
||||
bspeth_sys_allstop();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int bspeth_dev_probe_init(struct bspeth_netdev_local *ld)
|
||||
{
|
||||
ld->iobase_phys = REG_BASE_SF;
|
||||
|
||||
bspeth_glb_preinit_dummy(ld);
|
||||
|
||||
#ifdef BSPETH_RX_QUEUE_MULTI_DESC
|
||||
bspeth_init_hw_desc_queue(ld);
|
||||
#endif
|
||||
|
||||
bspeth_sys_allstop();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int bspeth_dev_remove(struct bspeth_netdev_local *ld)
|
||||
{
|
||||
#ifdef BSPETH_RX_QUEUE_MULTI_DESC
|
||||
bspeth_destroy_hw_desc_queue(ld);
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void bspeth_get_phy_intf(struct bspeth_netdev_local *ld)
|
||||
{
|
||||
char *mdio_intf = NULL;
|
||||
|
||||
/* get mdio interface from env.FORMAT: mdio_intf=mii or mdio_intf=rmii */
|
||||
mdio_intf = env_get("mdio_intf");
|
||||
if (mdio_intf != NULL) {
|
||||
if (!strncmp(mdio_intf, "mii", strlen("mii"))) {
|
||||
ld->phy_intf = INTERFACE_MODE_MII;
|
||||
} else if (!strncmp(mdio_intf, "rmii", strlen("rmii"))) {
|
||||
ld->phy_intf = INTERFACE_MODE_RMII;
|
||||
} else {
|
||||
printf("Invalid mdio_intf, should be mii or rmii.\n");
|
||||
ld->phy_intf = INTERFACE_MODE_RMII;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void bspeth_get_phy_addr(struct bspeth_netdev_local *ld)
|
||||
{
|
||||
char *phyaddr = NULL;
|
||||
char addr_name[MAX_PORT][10] = { "phyaddru", "phyaddrd" }; /* max name size is 10 */
|
||||
|
||||
if (ld->port >= MAX_PORT)
|
||||
return;
|
||||
|
||||
/* get phy addr of up port */
|
||||
phyaddr = env_get(addr_name[ld->port]);
|
||||
if (phyaddr != NULL) {
|
||||
unsigned long tmp = simple_strtoul(phyaddr, 0, 10); /* use Base10 */
|
||||
/* check phyaddr > 0x1f */
|
||||
if (tmp >= 0x1f) {
|
||||
printf("Detected env '%s' had been set greater"
|
||||
"than 0x1f,this may not correct.\n",
|
||||
addr_name[ld->port]);
|
||||
return;
|
||||
}
|
||||
ld->phy_addr = (unsigned char)tmp;
|
||||
} else {
|
||||
#if defined(INNER_PHY)
|
||||
if (ld->port == UP_PORT) {
|
||||
#if defined(INNER_PHY_ADDR_U)
|
||||
ld->phy_addr = INNER_PHY_ADDR_U;
|
||||
#endif
|
||||
} else {
|
||||
#if defined(INNER_PHY_ADDR_D)
|
||||
ld->phy_addr = INNER_PHY_ADDR_D;
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
static void bspeth_exit(struct bspeth_netdev_local *ld)
|
||||
{
|
||||
bspeth_mdiobus_driver_exit(ld);
|
||||
|
||||
bspeth_sys_exit();
|
||||
}
|
||||
|
||||
int bspeth_init(struct eth_device *dev, bd_t *bd)
|
||||
{
|
||||
struct bspeth_netdev_local *ld = (struct bspeth_netdev_local *)dev->priv;
|
||||
int ret;
|
||||
int count = 30;
|
||||
|
||||
bspeth_get_phy_intf(ld);
|
||||
|
||||
bspeth_get_phy_addr(ld);
|
||||
#ifdef INNER_PHY
|
||||
if (ld->port == UP_PORT)
|
||||
set_inner_phy_addr(ld->phy_addr);
|
||||
#endif
|
||||
|
||||
printf(OSDRV_MODULE_VERSION_STRING "\n");
|
||||
|
||||
set_efuse_unread();
|
||||
|
||||
/* sys-func-sel */
|
||||
bspeth_sys_init();
|
||||
|
||||
/* register MDIO bus to uboot */
|
||||
if (bspeth_mdiobus_driver_init(ld) != 0) {
|
||||
goto _error_bspeth_init;
|
||||
}
|
||||
|
||||
if (phy_detected(ld->mii_name, ld->phy_addr) == false) {
|
||||
goto _error_bspeth_init;
|
||||
}
|
||||
|
||||
miiphy_reset(ld->mii_name, ld->phy_addr);
|
||||
|
||||
ret = bspeth_dev_probe_init(ld);
|
||||
if (ret) {
|
||||
bspeth_error("register Ether netdevice"
|
||||
" driver failed!");
|
||||
goto _error_bspeth_init;
|
||||
}
|
||||
|
||||
eth_set_host_mac_address(dev);
|
||||
|
||||
while (--count >= 0) {
|
||||
/* open UpEther net dev */
|
||||
bspeth_net_open(ld);
|
||||
if (ld->link_stat & BSPETH_LINKED) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
printf("PHY not link.\n");
|
||||
|
||||
_error_bspeth_init:
|
||||
bspeth_mdiobus_driver_exit(ld);
|
||||
bspeth_net_close(ld);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
#ifdef BSPETH_RX_QUEUE_MULTI_DESC
|
||||
static int bspeth_recv(struct eth_device *dev)
|
||||
{
|
||||
struct bspeth_netdev_local *ld = (struct bspeth_netdev_local *)dev->priv;
|
||||
int recvq_ready, hw_offset, rec_offset;
|
||||
int timeout_us = 10000;
|
||||
struct bspeth_frame_desc *fd;
|
||||
struct bspeth_frame_desc receive_fd;
|
||||
|
||||
fd = ld->bspeth_desc_head;
|
||||
hw_offset = ld->desc_hw_offset;
|
||||
rec_offset = ld->desc_rec_offset;
|
||||
|
||||
/* check this we can add a Rx addr */
|
||||
recvq_ready = bspeth_readl_bits(ld, ud_reg_name(GLB_RO_QUEUE_STAT), BITS_RECVQ_RDY);
|
||||
if (!recvq_ready)
|
||||
bspeth_trace(BSPETH_TRACE_DRV, "hw can't add a rx addr.");
|
||||
|
||||
while (recvq_ready &&
|
||||
((hw_offset + 1) % BSPETH_HW_DESC_DEPTH != rec_offset)) {
|
||||
receive_fd = fd[hw_offset];
|
||||
invalidate_dcache_range(receive_fd.frm_addr,
|
||||
ALIGN(receive_fd.frm_addr + PKTSIZE_ALIGN, ARCH_DMA_MINALIGN));
|
||||
hw_recvq_setfd(ld, receive_fd);
|
||||
|
||||
hw_offset = (hw_offset + 1) % BSPETH_HW_DESC_DEPTH;
|
||||
|
||||
recvq_ready =
|
||||
bspeth_readl_bits(ld, ud_reg_name(GLB_RO_QUEUE_STAT),
|
||||
BITS_RECVQ_RDY);
|
||||
}
|
||||
ld->desc_hw_offset = hw_offset;
|
||||
|
||||
/* receive packed, loop in NetLoop */
|
||||
while (--timeout_us && !is_recv_packet_rx(ld))
|
||||
udelay(1);
|
||||
|
||||
if (is_recv_packet_rx(ld)) {
|
||||
receive_fd = fd[rec_offset];
|
||||
|
||||
receive_fd.frm_len = hw_get_rxpkg_len(ld);
|
||||
hw_set_rxpkg_finish(ld);
|
||||
|
||||
rec_offset = (rec_offset + 1) % BSPETH_HW_DESC_DEPTH;
|
||||
ld->desc_rec_offset = rec_offset;
|
||||
|
||||
if (bspeth_invalid_rxpkg_len(receive_fd.frm_len)) {
|
||||
bspeth_error("frm_len invalid (%u)", receive_fd.frm_len);
|
||||
goto _error_exit;
|
||||
}
|
||||
|
||||
invalidate_dcache_range(receive_fd.frm_addr,
|
||||
ALIGN(receive_fd.frm_addr + receive_fd.frm_len, ARCH_DMA_MINALIGN));
|
||||
memcpy((void *)net_rx_packets[0], (void *)receive_fd.frm_addr,
|
||||
receive_fd.frm_len);
|
||||
|
||||
/* Pass the packet up to the protocol layers. */
|
||||
net_process_received_packet(net_rx_packets[0], receive_fd.frm_len);
|
||||
|
||||
return 0;
|
||||
} else {
|
||||
bspeth_trace(BSPETH_TRACE_DRV, "hw rx timeout.");
|
||||
}
|
||||
|
||||
_error_exit:
|
||||
return -1;
|
||||
}
|
||||
#else
|
||||
static int bspeth_recv(struct eth_device *dev)
|
||||
{
|
||||
struct bspeth_netdev_local *ld = (struct bspeth_netdev_local *)dev->priv;
|
||||
int recvq_ready;
|
||||
int timeout_us = 10000;
|
||||
struct bspeth_frame_desc fd = { 0 };
|
||||
|
||||
/* check this we can add a Rx addr */
|
||||
recvq_ready = bspeth_readl_bits(ld, ud_reg_name(GLB_RO_QUEUE_STAT), BITS_RECVQ_RDY);
|
||||
if (!recvq_ready)
|
||||
bspeth_trace(BSPETH_TRACE_DRV, "hw can't add a rx addr.");
|
||||
|
||||
/* enable rx int */
|
||||
bspeth_irq_enable(ld, ud_bit_name(BSPETH_INT_RX_RDY));
|
||||
|
||||
/* fill rx hwq fd */
|
||||
fd.frm_addr = (uintptr_t)net_rx_packets[0];
|
||||
fd.frm_len = 0;
|
||||
/* recv data will be put into DDR , in case of get the cache data, mush be invalid Dcache */
|
||||
invalidate_dcache_range(fd.frm_addr, ALIGN(fd.frm_addr + PKTSIZE_ALIGN, ARCH_DMA_MINALIGN));
|
||||
|
||||
hw_recvq_setfd(ld, fd);
|
||||
|
||||
/* receive packed, loop in NetLoop */
|
||||
while (--timeout_us && !is_recv_packet(ld))
|
||||
udelay(1);
|
||||
|
||||
if (is_recv_packet(ld)) {
|
||||
fd.frm_len = hw_get_rxpkg_len(ld);
|
||||
hw_set_rxpkg_finish(ld);
|
||||
|
||||
if (bspeth_invalid_rxpkg_len(fd.frm_len)) {
|
||||
bspeth_error("frm_len invalid (%u)", fd.frm_len);
|
||||
goto _error_exit;
|
||||
}
|
||||
|
||||
invalidate_dcache_range(fd.frm_addr,
|
||||
ALIGN(fd.frm_addr + fd.frm_len, ARCH_DMA_MINALIGN));
|
||||
/* Pass the packet up to the protocol layers. */
|
||||
net_process_received_packet(net_rx_packets[0], fd.frm_len);
|
||||
|
||||
return 0;
|
||||
} else {
|
||||
bspeth_trace(BSPETH_TRACE_DRV, "hw rx timeout.");
|
||||
}
|
||||
|
||||
_error_exit:
|
||||
return -1;
|
||||
}
|
||||
#endif
|
||||
|
||||
#define ETH_FCS_LEN 4 /* Octets in the FCS */
|
||||
static int bspeth_send(struct eth_device *dev, void *packet, int length)
|
||||
{
|
||||
struct bspeth_netdev_local *ld = (struct bspeth_netdev_local *)dev->priv;
|
||||
unsigned int ints, xmitq_ready;
|
||||
unsigned int timeout_us = 3000;
|
||||
struct bspeth_frame_desc fd;
|
||||
|
||||
/* check this we can add a Tx addr */
|
||||
xmitq_ready = bspeth_readl_bits(ld, ud_reg_name(GLB_RO_QUEUE_STAT), BITS_XMITQ_RDY);
|
||||
if (!xmitq_ready) {
|
||||
bspeth_error("hw can't add a tx addr");
|
||||
goto _error_exit;
|
||||
}
|
||||
|
||||
/* enable tx int */
|
||||
bspeth_irq_enable(ld, ud_bit_name(BSPETH_INT_TXQUE_RDY));
|
||||
|
||||
flush_cache((uintptr_t)packet, ALIGN((unsigned int)length + ETH_FCS_LEN, ARCH_DMA_MINALIGN));
|
||||
/* fill tx hwq fd */
|
||||
fd.frm_addr = (uintptr_t)packet;
|
||||
fd.frm_len = length + ETH_FCS_LEN;
|
||||
hw_xmitq_setfd(ld, fd);
|
||||
|
||||
do {
|
||||
udelay(1);
|
||||
ints = bspeth_read_irqstatus(ld);
|
||||
} while (--timeout_us && !(ints & ud_bit_name(BSPETH_INT_TXQUE_RDY)));
|
||||
|
||||
bspeth_clear_irqstatus(ld, ints);
|
||||
|
||||
if (!timeout_us) {
|
||||
bspeth_error("hw tx timeout");
|
||||
goto _error_exit;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
_error_exit:
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void bspeth_halt(struct eth_device *dev)
|
||||
{
|
||||
struct bspeth_netdev_local *ld = (struct bspeth_netdev_local *)dev->priv;
|
||||
|
||||
bspeth_net_close(ld);
|
||||
|
||||
bspeth_dev_remove(ld);
|
||||
|
||||
bspeth_exit(ld);
|
||||
}
|
||||
|
||||
static int bspeth_register_dev(unsigned char port_id)
|
||||
{
|
||||
struct eth_device *dev;
|
||||
|
||||
dev = malloc(sizeof(*dev));
|
||||
if (dev == NULL)
|
||||
return -1;
|
||||
memset(dev, 0, sizeof(*dev));
|
||||
|
||||
dev->iobase = REG_BASE_SF;
|
||||
dev->init = bspeth_init;
|
||||
dev->halt = bspeth_halt;
|
||||
dev->send = bspeth_send;
|
||||
dev->recv = bspeth_recv;
|
||||
dev->priv = &bspeth_devs_priv[port_id];
|
||||
bspeth_devs_priv[port_id].iobase_phys = REG_BASE_SF;
|
||||
snprintf(dev->name, sizeof(dev->name) - 1, "eth%d", port_id);
|
||||
|
||||
eth_register(dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int bspeth_initialize(bd_t *bis)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = bspeth_register_dev(UP_PORT);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,252 @@
|
||||
/*
|
||||
* Copyright (c) LOTUS. All rights reserved.
|
||||
*/
|
||||
|
||||
|
||||
#include <config.h>
|
||||
#include "bspeth.h"
|
||||
#include "sys.h"
|
||||
|
||||
#define BSPETH_CRG_REG (CRG_REG_BASE + REG_ETH_CRG)
|
||||
|
||||
|
||||
#define ETH_SOFT_RESET bit(0)
|
||||
#define ETH_CLK_ENABLE bit(1)
|
||||
|
||||
|
||||
#if defined(SFV_RESET_PHY_BY_CRG)
|
||||
#define ETH_EXTERNAL_PHY_RESET bit(3)
|
||||
#endif
|
||||
|
||||
#define ETH_CORE_CLK_SELECT_54M bit(7)
|
||||
|
||||
#if (defined CONFIG_TARGET_XMFALCON)
|
||||
#define BSPETH_SYSCTL_REG (SYS_CTRL_REG_BASE + MISC_CTRL9)
|
||||
#define ETH_INTERNAL_PHY_RESET bit(3)
|
||||
#define ETH_INTERNAL_PHY_CLK_EN bit(2)
|
||||
#elif (defined CONFIG_TARGET_XMORCA)
|
||||
#define BSPETH_SYSCTL_REG (SYS_CTRL_REG_BASE + MISC_CTRL9)
|
||||
#define ETH_INTERNAL_PHY_RESET bit(3)
|
||||
#define ETH_INTERNAL_PHY_CLK_EN bit(2)
|
||||
#endif
|
||||
|
||||
#define BSPETH_CRG_INPHY_CLK_REG BSPETH_CRG_REG
|
||||
#define BSPETH_CRG_EXTPHY_CLK_REG BSPETH_CRG_REG
|
||||
|
||||
|
||||
#if 0
|
||||
void set_efuse_unread(void)
|
||||
{
|
||||
u32 reg_value;
|
||||
|
||||
reg_value = readl(BSPETH_EFUSE_REG);
|
||||
reg_value |= 0x1;
|
||||
writel(reg_value, BSPETH_EFUSE_REG);
|
||||
|
||||
reg_value = readl(BSPETH_EFUSE_DATA_REG);
|
||||
reg_value |= 0x1;
|
||||
writel(reg_value, BSPETH_EFUSE_DATA_REG);
|
||||
|
||||
mdelay(300); /* delay 300ms */
|
||||
|
||||
reg_value = readl(BSPETH_EFUSE_REG);
|
||||
reg_value &= ~0x1;
|
||||
writel(reg_value, BSPETH_EFUSE_REG);
|
||||
}
|
||||
#else
|
||||
void set_efuse_unread(void)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef INNER_PHY
|
||||
void set_inner_phy_addr(u32 phy_addr)
|
||||
{
|
||||
u32 reg_value;
|
||||
|
||||
reg_value = readl(MISC_REG_BASE + MISC_CTRL9);
|
||||
reg_value &= ~0x1f;
|
||||
phy_addr &= 0x1f;
|
||||
reg_value |= phy_addr;
|
||||
writel(reg_value, (MISC_REG_BASE + MISC_CTRL9));
|
||||
}
|
||||
#else
|
||||
void set_inner_phy_addr(u32 phyaddr)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
void revise_led_shine(void)
|
||||
{
|
||||
}
|
||||
|
||||
void set_phy_valtage(void)
|
||||
{
|
||||
}
|
||||
|
||||
static void bspeth_reset(int rst)
|
||||
{
|
||||
u32 val;
|
||||
|
||||
val = _readl(BSPETH_CRG_REG);
|
||||
if (rst) {
|
||||
val |= ETH_SOFT_RESET;
|
||||
} else {
|
||||
val &= ~ETH_SOFT_RESET;
|
||||
}
|
||||
_writel(val, BSPETH_CRG_REG);
|
||||
|
||||
udelay(100); /* delay 100us */
|
||||
}
|
||||
|
||||
static inline void bspeth_clk_ena(void)
|
||||
{
|
||||
u32 val = _readl(BSPETH_CRG_REG);
|
||||
val |= (ETH_CORE_CLK_SELECT_54M | ETH_CLK_ENABLE);
|
||||
_writel(val, BSPETH_CRG_REG);
|
||||
}
|
||||
|
||||
static inline void bspeth_clk_dis(void)
|
||||
{
|
||||
u32 val = _readl(BSPETH_CRG_REG);
|
||||
val &= ~ETH_CLK_ENABLE;
|
||||
_writel(val, BSPETH_CRG_REG);
|
||||
}
|
||||
|
||||
#if defined(INNER_PHY)
|
||||
static void bspeth_fephy_trim(void)
|
||||
{
|
||||
/* To simplify internal FEPHY trim process,
|
||||
* we just delay 300ms to wait FEPHY auto-trim completed.
|
||||
* Not read trim data from EFUSE register.
|
||||
*/
|
||||
mdelay(350); /* delay 350ms */
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
static void bspeth_reset_internal_phy(void)
|
||||
{
|
||||
#ifdef INNER_PHY
|
||||
u32 rst;
|
||||
|
||||
/* disable MDCK clock to make sure FEPHY reset success */
|
||||
bspeth_clk_dis();
|
||||
|
||||
rst = readl(BSPETH_CRG_INPHY_CLK_REG);
|
||||
rst |= ETH_INTERNAL_PHY_CLK_EN;
|
||||
/* internal FEPHY only support MII mode */
|
||||
|
||||
writel(rst, BSPETH_CRG_INPHY_CLK_REG);
|
||||
udelay(10); /* delay 10us */
|
||||
|
||||
rst = _readl(BSPETH_CRG_INPHY_CLK_REG);
|
||||
rst |= ETH_INTERNAL_PHY_RESET;
|
||||
_writel(rst, BSPETH_CRG_INPHY_CLK_REG);
|
||||
/* delay at least 10ms */
|
||||
mdelay(15); /* delay 15ms */
|
||||
|
||||
rst = _readl(BSPETH_CRG_INPHY_CLK_REG);
|
||||
rst &= ~ETH_INTERNAL_PHY_RESET;
|
||||
_writel(rst, BSPETH_CRG_INPHY_CLK_REG);
|
||||
/* delay at least 15ms for MDIO operation */
|
||||
mdelay(20); /* delay 20ms */
|
||||
|
||||
bspeth_clk_ena();
|
||||
/* delay 5ms after enable MDCK to make sure FEPHY trim safe */
|
||||
mdelay(5); /* delay 5ms */
|
||||
bspeth_fephy_trim();
|
||||
#endif
|
||||
}
|
||||
|
||||
static void bspeth_reset_external_phy_by_crg(void)
|
||||
{
|
||||
#if defined(SFV_RESET_PHY_BY_CRG)
|
||||
u32 v;
|
||||
|
||||
/************************************************/
|
||||
/* reset external phy with default reset pin */
|
||||
v = readl(BSPETH_CRG_EXTPHY_CLK_REG);
|
||||
v |= ETH_EXTERNAL_PHY_RESET;
|
||||
writel(v, BSPETH_CRG_EXTPHY_CLK_REG);
|
||||
|
||||
mdelay(50); /* delay 50ms */
|
||||
|
||||
/* then, cancel reset, and should delay some time */
|
||||
v = readl(BSPETH_CRG_EXTPHY_CLK_REG);
|
||||
v &= ~ETH_EXTERNAL_PHY_RESET;
|
||||
writel(v, BSPETH_CRG_EXTPHY_CLK_REG);
|
||||
|
||||
mdelay(50); /* delay 50ms */
|
||||
#endif
|
||||
}
|
||||
|
||||
static void bspeth_reset_external_phy_by_gpio(void)
|
||||
{
|
||||
#ifdef SFV_RESET_GPIO_EN
|
||||
unsigned int val;
|
||||
/* gpiox[x] set to reset, then delay 200ms */
|
||||
val = __raw_readw(SFV_RESET_GPIO_BASE + SFV_RESET_GPIO_DIR);
|
||||
val |= (SFV_RESET_GPIO_DIR_OUT << SFV_RESET_GPIO_BIT);
|
||||
__raw_writew(val, SFV_RESET_GPIO_BASE + SFV_RESET_GPIO_DIR);
|
||||
__raw_writew(SFV_RESET_GPIO_DATA,
|
||||
SFV_RESET_GPIO_BASE +
|
||||
(4 << SFV_RESET_GPIO_BIT)); /* offset addr 4 */
|
||||
|
||||
mdelay(200); /* delay 200ms */
|
||||
|
||||
/* then,cancel reset,and should delay 200ms */
|
||||
val = __raw_readw(SFV_RESET_GPIO_BASE + SFV_RESET_GPIO_DIR);
|
||||
val |= (SFV_RESET_GPIO_DIR_OUT << SFV_RESET_GPIO_BIT);
|
||||
__raw_writew(val, SFV_RESET_GPIO_BASE + SFV_RESET_GPIO_DIR);
|
||||
__raw_writew(((!SFV_RESET_GPIO_DATA) << SFV_RESET_GPIO_BIT),
|
||||
SFV_RESET_GPIO_BASE +
|
||||
(4 << SFV_RESET_GPIO_BIT)); /* offset addr 4 */
|
||||
|
||||
mdelay(20); /* delay 20ms */
|
||||
#endif
|
||||
}
|
||||
|
||||
static void bspeth_phy_reset(void)
|
||||
{
|
||||
bspeth_reset_internal_phy();
|
||||
bspeth_reset_external_phy_by_crg();
|
||||
bspeth_reset_external_phy_by_gpio();
|
||||
}
|
||||
|
||||
static void bspeth_funsel_config(void)
|
||||
{
|
||||
}
|
||||
|
||||
static void bspeth_funsel_restore(void)
|
||||
{
|
||||
}
|
||||
|
||||
/**************************************************/
|
||||
void bspeth_sys_startup(void)
|
||||
{
|
||||
bspeth_clk_ena();
|
||||
/* undo reset */
|
||||
bspeth_reset(0);
|
||||
}
|
||||
|
||||
void bspeth_sys_allstop(void)
|
||||
{
|
||||
}
|
||||
|
||||
void bspeth_sys_init(void)
|
||||
{
|
||||
bspeth_funsel_config();
|
||||
bspeth_sys_allstop();
|
||||
bspeth_clk_ena();
|
||||
bspeth_reset(1);
|
||||
bspeth_reset(0);
|
||||
bspeth_phy_reset();
|
||||
revise_led_shine();
|
||||
}
|
||||
|
||||
void bspeth_sys_exit(void)
|
||||
{
|
||||
bspeth_funsel_restore();
|
||||
bspeth_sys_allstop();
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
/*
|
||||
* Copyright (c) LOTUS. All rights reserved.
|
||||
*/
|
||||
|
||||
#ifndef __BSPETH_SYS_H__
|
||||
#define __BSPETH_SYS_H__
|
||||
|
||||
void bspeth_sys_init(void);
|
||||
void bspeth_sys_exit(void);
|
||||
|
||||
void bspeth_set_crg_phy_mode(unsigned char is_rmii_mode);
|
||||
void set_inner_phy_addr(u32 phyaddr);
|
||||
void set_efuse_unread(void);
|
||||
|
||||
void bspeth_sys_startup(void);
|
||||
void bspeth_sys_allstop(void);
|
||||
|
||||
void set_phy_valtage(void);
|
||||
#endif
|
||||
@@ -0,0 +1,3 @@
|
||||
obj-y += sys.o
|
||||
obj-$(CONFIG_FTGMAC030_V1) += ftgmac030.o marvell_phy.o realtek_phy.o
|
||||
obj-$(CONFIG_FTGMAC030_V2) += ftgmac030_v2.o jlsemi_phy.o icplus_phy.o ephy_phy.o
|
||||
@@ -0,0 +1,62 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0+ */
|
||||
|
||||
#ifndef __BSPETH_H__
|
||||
#define __BSPETH_H__
|
||||
|
||||
#include <config.h>
|
||||
#include <common.h>
|
||||
#include <command.h>
|
||||
|
||||
#include <linux/list.h>
|
||||
#include <linux/io.h>
|
||||
|
||||
#include <malloc.h> /* malloc, free, realloc */
|
||||
|
||||
#include <net.h>
|
||||
#include <miiphy.h>
|
||||
#include <asm/arch/platform.h>
|
||||
|
||||
#ifndef bit
|
||||
#define bit(nr) (1UL << (nr))
|
||||
#endif
|
||||
#define mdelay(n) udelay((n)*1000)
|
||||
|
||||
/* ***********************************************************
|
||||
*
|
||||
* Global varibles
|
||||
*
|
||||
* ***********************************************************
|
||||
*/
|
||||
#define BSPETH_PHY_CRG_REG (CRG_REG_BASE + 0x16c)
|
||||
#define BSPETH_MAC_CRG_REG (CRG_REG_BASE + 0x170)
|
||||
#define BSPETH_CFG_CRG_REG (SYS_CTRL_REG_BASE + 0x134)
|
||||
#define BSPETH_DLY_CRG_REG (SYS_CTRL_REG_BASE + 0x8940)
|
||||
#define BSPETH_DLY_STAT_REG (CRG_REG_BASE + 0x1a8)
|
||||
|
||||
#define ETH_DLY_SEL bit(0)
|
||||
#define ETH_DLY_STAT bit(0)
|
||||
|
||||
#define ETH_SOFT_RESET bit(1)
|
||||
|
||||
#define ETH_EXTERNAL_PHY_RESET bit(1)
|
||||
|
||||
#define ETH_PHY_INTF (bit(0) | bit(3))
|
||||
#define ETH_PHY_RST_N bit(1)
|
||||
|
||||
/* configuerable values */
|
||||
|
||||
/* ***********************************************************
|
||||
*
|
||||
* Only for internal used!
|
||||
*
|
||||
* ***********************************************************
|
||||
*/
|
||||
|
||||
/* read/write IO */
|
||||
|
||||
#define _readl(c) ({ u32 __v = le32_to_cpu(__raw_readl(c)); __v; })
|
||||
#define _writel(v, c) __raw_writel(cpu_to_le32(v), c)
|
||||
|
||||
#define mk_bits(shift, nbits) ((((shift) & 0x1F) << 16) | ((nbits) & 0x1F))
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,149 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
|
||||
#include <common.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/bitops.h>
|
||||
#include <phy.h>
|
||||
|
||||
#define MIIM_EPHY_SUB_CFG 0x12
|
||||
#define MIIM_EPHY_SUB_WDATA 0x13
|
||||
#define MIIM_EPHY_SUB_RDATA 0x14
|
||||
|
||||
#define EPHY_TRIM_REG_BASE (0x12020000 + 0x8a2c)
|
||||
|
||||
typedef union { /* FEPHY TRIM */
|
||||
uint32_t all;
|
||||
struct {
|
||||
uint32_t efuse_tx_100_fs :4;
|
||||
uint32_t efuse_tx_10_outr :4;
|
||||
uint32_t efuse_tx_10_fs :4;
|
||||
uint32_t efuse_rcal_code_rt :4;
|
||||
uint32_t reserved :16;
|
||||
} bitc;
|
||||
} EPHY_TRIM_REG;
|
||||
|
||||
|
||||
static int ephy_phy_extread(struct phy_device *phydev, int addr,
|
||||
int devaddr, int regnum)
|
||||
{
|
||||
int val;
|
||||
int sub_cfg;
|
||||
|
||||
sub_cfg = (0<<15) | (devaddr << 8) | (regnum << 0);
|
||||
phy_write(phydev, MDIO_DEVAD_NONE, MIIM_EPHY_SUB_CFG, sub_cfg);
|
||||
|
||||
val = phy_read(phydev, MDIO_DEVAD_NONE, MIIM_EPHY_SUB_RDATA);
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
static int ephy_phy_extwrite(struct phy_device *phydev, int addr,
|
||||
int devaddr, int regnum, u16 val)
|
||||
{
|
||||
int sub_cfg;
|
||||
|
||||
phy_write(phydev, MDIO_DEVAD_NONE, MIIM_EPHY_SUB_WDATA, val);
|
||||
|
||||
sub_cfg = (1<<15) | (devaddr << 8) | (regnum << 0);
|
||||
phy_write(phydev, MDIO_DEVAD_NONE, MIIM_EPHY_SUB_CFG, sub_cfg);
|
||||
|
||||
return 0;
|
||||
}
|
||||
static int ephy_config(struct phy_device *phydev)
|
||||
{
|
||||
unsigned int reg;
|
||||
EPHY_TRIM_REG ephy_trim_reg;
|
||||
|
||||
/* Set green LED for Link, yellow LED for Active */
|
||||
//set LED to 0 (page 25 reg 0 3:2:0)
|
||||
reg = phydev->drv->readext(phydev, 0, 0x19, 0x00);
|
||||
reg &= ~(0x07);
|
||||
reg |= 0x03;
|
||||
phydev->drv->writeext(phydev, 0, 0x19, 0x00, reg);
|
||||
|
||||
/* Set RG_EPHY_TX_100_LPF fix 4 to Improve the waveform */
|
||||
//set RG_EPHY_TX_100_LPF(page 0 reg 33 7:4)
|
||||
reg = phydev->drv->readext(phydev, 0, 0x0, 0x21);
|
||||
reg &= ~(0xf0);
|
||||
reg |= 0x40;
|
||||
phydev->drv->writeext(phydev, 0, 0x0, 0x21, reg);
|
||||
|
||||
//page 12 reg 4
|
||||
phydev->drv->writeext(phydev, 0, 0x0c, 0x4, 0x10);
|
||||
|
||||
ephy_trim_reg.all = readl(EPHY_TRIM_REG_BASE);
|
||||
|
||||
if (!ephy_trim_reg.all) {
|
||||
ephy_trim_reg.bitc.efuse_tx_10_outr = 0;
|
||||
ephy_trim_reg.bitc.efuse_tx_10_fs = 0;
|
||||
ephy_trim_reg.bitc.efuse_tx_100_fs = 4;
|
||||
ephy_trim_reg.bitc.efuse_rcal_code_rt = 8;
|
||||
}
|
||||
|
||||
pr_info("trim_reg:0x%x\n", ephy_trim_reg.all);
|
||||
pr_info("tx_100_fs:0x%x tx_10_outr:0x%x\n", ephy_trim_reg.bitc.efuse_tx_100_fs, ephy_trim_reg.bitc.efuse_tx_10_outr);
|
||||
pr_info("tx_10_fs:0x%x rcal_code:0x%x\n", ephy_trim_reg.bitc.efuse_tx_10_fs, ephy_trim_reg.bitc.efuse_rcal_code_rt);
|
||||
|
||||
|
||||
if (ephy_trim_reg.all) {
|
||||
//set EPHY_TX_10_OUTR to 0 (page 0 reg 9 11:8)
|
||||
reg = phydev->drv->readext(phydev, 0, 0, 0x09);
|
||||
reg &= ~(0xf << 8);
|
||||
reg |= (ephy_trim_reg.bitc.efuse_tx_10_outr << 8);
|
||||
phydev->drv->writeext(phydev, 0, 0, 0x09, reg);
|
||||
|
||||
//set EPHY_TX_10_FS to 0 (page 0 reg 9 7:4)
|
||||
reg = phydev->drv->readext(phydev, 0, 0, 0x09);
|
||||
reg &= ~(0xf << 4);
|
||||
reg |= (ephy_trim_reg.bitc.efuse_tx_10_fs << 4);
|
||||
phydev->drv->writeext(phydev, 0, 0, 0x09, reg);
|
||||
|
||||
//set EPHY_TX_100_FS to 4 (page 0 reg 33 3:0)
|
||||
reg = phydev->drv->readext(phydev, 0, 0, 0x21);
|
||||
reg &= ~(0xf << 0);
|
||||
reg |= (ephy_trim_reg.bitc.efuse_tx_100_fs << 0);
|
||||
phydev->drv->writeext(phydev, 0, 0, 0x21, reg);
|
||||
|
||||
// set EPHY_RCAL_CODE_FT to 8 (page 0 reg 34 7:4)
|
||||
reg = phydev->drv->readext(phydev, 0, 0, 0x22);
|
||||
reg &= ~(0xf << 4);
|
||||
reg |= (ephy_trim_reg.bitc.efuse_rcal_code_rt << 4);
|
||||
phydev->drv->writeext(phydev, 0, 0, 0x22, reg);
|
||||
}
|
||||
|
||||
genphy_config_aneg(phydev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ephy_aneg_done(struct phy_device *phydev)
|
||||
{
|
||||
unsigned int reg;
|
||||
|
||||
reg = phydev->drv->readext(phydev, 0, 0x11, 0x20);
|
||||
|
||||
pr_info("[EPHY]:0x%x\n", reg);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Support for ePHY PHY */
|
||||
static struct phy_driver ephy_driver = {
|
||||
.name = "ePHY",
|
||||
.uid = 0x0243991f,
|
||||
.mask = 0xffffffff,
|
||||
.features = PHY_BASIC_FEATURES,
|
||||
.config = &ephy_config,
|
||||
.startup = &genphy_startup,
|
||||
.shutdown = &genphy_shutdown,
|
||||
.readext = &ephy_phy_extread,
|
||||
.writeext = &ephy_phy_extwrite,
|
||||
.aneg_done = &ephy_aneg_done,
|
||||
};
|
||||
|
||||
int phy_ephy_init(void)
|
||||
{
|
||||
phy_register(&ephy_driver);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,677 @@
|
||||
/*
|
||||
* Copyright (c) LOTUS. All rights reserved.
|
||||
*/
|
||||
//#define DEBUG
|
||||
#include <config.h>
|
||||
#include <common.h>
|
||||
#include <malloc.h>
|
||||
#include <net.h>
|
||||
#include <wait_bit.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/mii.h>
|
||||
#include <linux/iopoll.h>
|
||||
#include <cpu_func.h>
|
||||
#include <miiphy.h>
|
||||
#include <hexdump.h>
|
||||
|
||||
#include "ftgmac030.h"
|
||||
#include "marvell_phy.h"
|
||||
#include "realtek_phy.h"
|
||||
#include "sys.h"
|
||||
|
||||
#define ETH_ZLEN 60
|
||||
|
||||
/* Receive Buffer Size Register - HW default is 0x640 */
|
||||
#define FTGMAC030_RBSR_DEFAULT_VALUE 0x640
|
||||
|
||||
/* PKTBUFSTX/PKTBUFSRX must both be power of 2 */
|
||||
#define FTGMAC030_PKTBUFSTX 4 /* must be power of 2 */
|
||||
#define FTGMAC030_PKTBUFSRX PKTBUFSRX /* must be power of 2 */
|
||||
|
||||
/* Timeout for transmit */
|
||||
#define FTGMAC030_TX_TIMEOUT_MS 1000
|
||||
|
||||
/* Timeout for a mdio read/write operation */
|
||||
#define FTGMAC030_MDIO_TIMEOUT_USEC 10000
|
||||
|
||||
#define MAC_LEN 6
|
||||
|
||||
struct ftgmac030_data {
|
||||
struct ftgmac030_txdes txdes[FTGMAC030_PKTBUFSTX] __attribute__((aligned(ARCH_DMA_MINALIGN)));
|
||||
struct ftgmac030_rxdes rxdes[FTGMAC030_PKTBUFSRX] __attribute__((aligned(ARCH_DMA_MINALIGN)));
|
||||
int rsvd1 __attribute__((aligned(ARCH_DMA_MINALIGN)));
|
||||
int tx_index;
|
||||
int rx_index;
|
||||
int phy_addr;
|
||||
enum ftgmac030_if_mode phy_intf;
|
||||
};
|
||||
|
||||
/*
|
||||
* struct mdio functions
|
||||
*/
|
||||
int ftgmac030_mdio_read(struct eth_device *dev, int phy_addr,
|
||||
int regnum)
|
||||
{
|
||||
struct ftgmac030 *ftgmac030 = (struct ftgmac030 *)dev->iobase;
|
||||
int phycr;
|
||||
int data;
|
||||
int ret;
|
||||
|
||||
phycr = readl(&ftgmac030->phycr);
|
||||
|
||||
/* preserve MDC cycle threshold */
|
||||
phycr &= FTGMAC030_PHYCR_MDC_CYCTHR_MASK;
|
||||
|
||||
phycr |= FTGMAC030_PHYCR_PHYAD(phy_addr)
|
||||
| FTGMAC030_PHYCR_REGAD(regnum)
|
||||
| FTGMAC030_PHYCR_OP(FTGMAC030_MDIO_OP_RD)
|
||||
| FTGMAC030_PHYCR_SOF(FTGMAC030_MDIO_SOF)
|
||||
| FTGMAC030_PHYCR_PHYRD;
|
||||
|
||||
writel(phycr, &ftgmac030->phycr);
|
||||
|
||||
ret = readl_poll_timeout(&ftgmac030->phycr, phycr,
|
||||
!(phycr & FTGMAC030_PHYCR_PHYRD),
|
||||
FTGMAC030_MDIO_TIMEOUT_USEC);
|
||||
if (ret) {
|
||||
pr_err("mdio read failed (phy:%d reg:%x)\n", phy_addr, regnum);
|
||||
return ret;
|
||||
}
|
||||
|
||||
data = readl(&ftgmac030->phydata);
|
||||
|
||||
return FTGMAC030_PHYDATA_MIIRDATA(data);
|
||||
}
|
||||
|
||||
int ftgmac030_mdio_write(struct eth_device *dev, int phy_addr,
|
||||
int regnum, u16 value)
|
||||
{
|
||||
struct ftgmac030 *ftgmac030 = (struct ftgmac030 *)dev->iobase;
|
||||
int phycr;
|
||||
int data;
|
||||
int ret;
|
||||
|
||||
phycr = readl(&ftgmac030->phycr);
|
||||
|
||||
/* preserve MDC cycle threshold */
|
||||
phycr &= FTGMAC030_PHYCR_MDC_CYCTHR_MASK;
|
||||
|
||||
phycr |= FTGMAC030_PHYCR_PHYAD(phy_addr)
|
||||
| FTGMAC030_PHYCR_REGAD(regnum)
|
||||
| FTGMAC030_PHYCR_OP(FTGMAC030_MDIO_OP_WR)
|
||||
| FTGMAC030_PHYCR_SOF(FTGMAC030_MDIO_SOF)
|
||||
| FTGMAC030_PHYCR_PHYWR;
|
||||
|
||||
data = FTGMAC030_PHYDATA_MIIWDATA(value);
|
||||
|
||||
writel(data, &ftgmac030->phydata);
|
||||
writel(phycr, &ftgmac030->phycr);
|
||||
|
||||
ret = readl_poll_timeout(&ftgmac030->phycr, phycr,
|
||||
!(phycr & FTGMAC030_PHYCR_PHYWR),
|
||||
FTGMAC030_MDIO_TIMEOUT_USEC);
|
||||
if (ret) {
|
||||
pr_err("mdio write failed (phy:%d reg:%x)\n", phy_addr, regnum);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* MDIO Bus Interface */
|
||||
static int ftgmac030_mdiobus_read(struct mii_dev *bus, int addr, int devad, int reg)
|
||||
{
|
||||
struct eth_device *dev = (struct eth_device *)bus->priv;
|
||||
|
||||
return ftgmac030_mdio_read(dev, addr, reg);
|
||||
}
|
||||
|
||||
static int ftgmac030_mdiobus_write(struct mii_dev *bus, int addr, int devad,
|
||||
int reg, u16 value)
|
||||
{
|
||||
struct eth_device *dev = (struct eth_device *)bus->priv;
|
||||
|
||||
return ftgmac030_mdio_write(dev, addr, reg, value);
|
||||
}
|
||||
|
||||
/*
|
||||
* Init MIDO MDC CYCTHR
|
||||
*/
|
||||
static void ftgmac030_mdc_cycthr(struct eth_device *dev, unsigned int mdc_cycthr)
|
||||
{
|
||||
struct ftgmac030 *ftgmac030 = (struct ftgmac030 *)dev->iobase;
|
||||
int phycr;
|
||||
|
||||
phycr = readl(&ftgmac030->phycr);
|
||||
phycr &= ~FTGMAC030_PHYCR_MDC_CYCTHR_MASK;
|
||||
phycr |= FTGMAC030_PHYCR_MDC_CYCTHR(mdc_cycthr);
|
||||
writel(phycr, &ftgmac030->phycr);
|
||||
}
|
||||
|
||||
static void ftgmac030_set_phy_intf(struct eth_device *dev)
|
||||
{
|
||||
struct ftgmac030 *ftgmac030 = (struct ftgmac030 *)dev->iobase;
|
||||
struct ftgmac030_data *priv = dev->priv;
|
||||
|
||||
priv->phy_intf = (bspeth_get_phy_intf() ? FTGMAC030_MODE_RGMII : FTGMAC030_MODE_RMII);
|
||||
|
||||
printf("%s (%s)\n", __func__, (FTGMAC030_MODE_RMII == priv->phy_intf) ? "rmii" : "rgmii");
|
||||
|
||||
writel(priv->phy_intf, &ftgmac030->gisr);
|
||||
|
||||
/* set the mdc cycthr */
|
||||
/* set the mdc cycthr sys_clk=200M*/
|
||||
ftgmac030_mdc_cycthr(dev, 0xc8);
|
||||
}
|
||||
|
||||
int ftgmac030_mdiobus_init(struct eth_device *dev)
|
||||
{
|
||||
#if defined(CONFIG_MII) || defined(CONFIG_CMD_MII)
|
||||
struct mii_dev *bus = mdio_alloc();
|
||||
|
||||
if (!bus) {
|
||||
printf("Failed to allocate MDIO bus\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
bus->priv = dev;
|
||||
bus->read = ftgmac030_mdiobus_read;
|
||||
bus->write = ftgmac030_mdiobus_write;
|
||||
snprintf(bus->name, sizeof(bus->name), dev->name);
|
||||
|
||||
if (mdio_register(bus)) {
|
||||
mdio_free(bus);
|
||||
return -1;
|
||||
}
|
||||
|
||||
miiphy_set_current_dev(dev->name);
|
||||
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ftgmac030_phy_reset(struct eth_device *dev)
|
||||
{
|
||||
struct ftgmac030_data *priv = dev->priv;
|
||||
int i;
|
||||
u16 status, adv;
|
||||
|
||||
adv = ADVERTISE_CSMA | ADVERTISE_ALL;
|
||||
|
||||
ftgmac030_mdio_write(dev, priv->phy_addr, MII_ADVERTISE, adv);
|
||||
|
||||
printf("%s: Starting autonegotiation...\n", dev->name);
|
||||
|
||||
ftgmac030_mdio_write(dev, priv->phy_addr,
|
||||
MII_BMCR, (BMCR_ANENABLE | BMCR_ANRESTART));
|
||||
|
||||
for (i = 0; i < 10000; i++) {
|
||||
status = ftgmac030_mdio_read(dev, priv->phy_addr, MII_BMSR);
|
||||
if (status & BMSR_ANEGCOMPLETE)
|
||||
break;
|
||||
mdelay(1);
|
||||
}
|
||||
|
||||
if (status & BMSR_ANEGCOMPLETE) {
|
||||
printf("%s: Autonegotiation complete\n", dev->name);
|
||||
} else {
|
||||
printf("%s: Autonegotiation timed out (status=0x%04x)\n",
|
||||
dev->name, status);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int ftgmac030_phy_init(struct eth_device *dev)
|
||||
{
|
||||
struct ftgmac030_data *priv = dev->priv;
|
||||
|
||||
int phy_addr;
|
||||
u16 phy_id, status, adv, lpa;
|
||||
u16 stat_ge;
|
||||
int media, speed, duplex;
|
||||
int i;
|
||||
|
||||
/* Check if the PHY is up to snuff... */
|
||||
for (phy_addr = 0; phy_addr < 32; phy_addr++) {
|
||||
phy_id = ftgmac030_mdio_read(dev, phy_addr, MII_PHYSID1);
|
||||
/*
|
||||
* When it is unable to found PHY,
|
||||
* the interface usually return 0xffff or 0x0000
|
||||
*/
|
||||
if (phy_id != 0xffff && phy_id != 0x0) {
|
||||
printf("%s: found PHY(0x%x) at 0x%02x\n",
|
||||
dev->name, phy_id, phy_addr);
|
||||
priv->phy_addr = phy_addr;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (phy_id == 0xffff || phy_id == 0x0) {
|
||||
printf("%s: no PHY present\n", dev->name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (phy_id == PHY_MARVELL)
|
||||
marvell_phy_init(dev, phy_addr);
|
||||
|
||||
if (phy_id == PHY_REALTEK)
|
||||
realtek_phy_init(dev, phy_addr);
|
||||
|
||||
status = ftgmac030_mdio_read(dev, priv->phy_addr, MII_BMSR);
|
||||
if (!(status & BMSR_LSTATUS)) {
|
||||
/* Try to re-negotiate if we don't have link already. */
|
||||
ftgmac030_phy_reset(dev);
|
||||
|
||||
for (i = 0; i < 100000 / 100; i++) {
|
||||
status = ftgmac030_mdio_read(dev, priv->phy_addr, MII_BMSR);
|
||||
if (status & BMSR_LSTATUS)
|
||||
break;
|
||||
udelay(100);
|
||||
}
|
||||
}
|
||||
|
||||
if (!(status & BMSR_LSTATUS)) {
|
||||
printf("%s: link down\n", dev->name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (FTGMAC030_MODE_RGMII == priv->phy_intf) {
|
||||
/* 1000 Base-T Status Register */
|
||||
stat_ge = ftgmac030_mdio_read(dev, priv->phy_addr, MII_STAT1000);
|
||||
speed = (stat_ge & (LPA_1000FULL | LPA_1000HALF)
|
||||
? 1 : 0);
|
||||
|
||||
duplex = ((stat_ge & LPA_1000FULL)
|
||||
? 1 : 0);
|
||||
|
||||
if (speed) { /* Speed is 1000 */
|
||||
printf("%s: link up, 1000bps %s-duplex\n",
|
||||
dev->name, duplex ? "full" : "half");
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
adv = ftgmac030_mdio_read(dev, priv->phy_addr, MII_ADVERTISE);
|
||||
lpa = ftgmac030_mdio_read(dev, priv->phy_addr, MII_LPA);
|
||||
|
||||
media = mii_nway_result(lpa & adv);
|
||||
speed = (media & (ADVERTISE_100FULL | ADVERTISE_100HALF) ? 1 : 0);
|
||||
duplex = (media & ADVERTISE_FULL) ? 1 : 0;
|
||||
|
||||
printf("%s: link up, %sMbps %s-duplex\n",
|
||||
dev->name, speed ? "100" : "10", duplex ? "full" : "half");
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int ftgmac030_update_link_speed(struct eth_device *dev)
|
||||
{
|
||||
struct ftgmac030 *ftgmac030 = (struct ftgmac030 *)dev->iobase;
|
||||
struct ftgmac030_data *priv = dev->priv;
|
||||
|
||||
unsigned short stat_fe;
|
||||
unsigned short stat_ge = 0 ;
|
||||
unsigned int maccr;
|
||||
|
||||
if (FTGMAC030_MODE_RGMII == priv->phy_intf) {
|
||||
/* 1000 Base-T Status Register */
|
||||
stat_ge = ftgmac030_mdio_read(dev, priv->phy_addr, MII_STAT1000);
|
||||
}
|
||||
|
||||
stat_fe = ftgmac030_mdio_read(dev, priv->phy_addr, MII_BMSR);
|
||||
|
||||
if (!(stat_fe & BMSR_LSTATUS)) /* link status up? */
|
||||
return 0;
|
||||
|
||||
/* read MAC control register and clear related bits */
|
||||
maccr = readl(&ftgmac030->maccr) &
|
||||
~(FTGMAC030_MACCR_SPEED_1000 |
|
||||
FTGMAC030_MACCR_SPEED_100 |
|
||||
FTGMAC030_MACCR_FULLDUP);
|
||||
|
||||
if (FTGMAC030_MODE_RGMII == priv->phy_intf) {
|
||||
if (stat_ge & LPA_1000FULL) {
|
||||
/* set gmac for 1000BaseTX and Full Duplex */
|
||||
maccr |= FTGMAC030_MACCR_SPEED_1000 | FTGMAC030_MACCR_FULLDUP;
|
||||
}
|
||||
|
||||
if (stat_ge & LPA_1000HALF) {
|
||||
/* set gmac for 1000BaseTX and Half Duplex */
|
||||
maccr |= FTGMAC030_MACCR_SPEED_1000;
|
||||
}
|
||||
}
|
||||
|
||||
if (!(maccr & FTGMAC030_MACCR_SPEED_1000)) {
|
||||
if (stat_fe & BMSR_100FULL) {
|
||||
/* set MII for 100BaseTX and Full Duplex */
|
||||
maccr |= FTGMAC030_MACCR_SPEED_100 | FTGMAC030_MACCR_FULLDUP;
|
||||
}
|
||||
|
||||
if (stat_fe & BMSR_10FULL) {
|
||||
/* set MII for 10BaseT and Full Duplex */
|
||||
maccr |= FTGMAC030_MACCR_FULLDUP;
|
||||
}
|
||||
|
||||
if (stat_fe & BMSR_100HALF) {
|
||||
/* set MII for 100BaseTX and Half Duplex */
|
||||
maccr |= FTGMAC030_MACCR_SPEED_100;
|
||||
}
|
||||
|
||||
if (stat_fe & BMSR_10HALF) {
|
||||
/* set MII for 10BaseT and Half Duplex */
|
||||
/* we have already clear these bits, do nothing */
|
||||
;
|
||||
}
|
||||
}
|
||||
|
||||
/* update MII config into maccr */
|
||||
writel(maccr, &ftgmac030->maccr);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Reset MAC
|
||||
*/
|
||||
static void ftgmac030_reset(struct eth_device *dev)
|
||||
{
|
||||
struct ftgmac030 *ftgmac030 = (struct ftgmac030 *)dev->iobase;
|
||||
|
||||
debug("%s()\n", __func__);
|
||||
|
||||
writel(FTGMAC030_MACCR_SW_RST, &ftgmac030->maccr);
|
||||
|
||||
while (readl(&ftgmac030->maccr) & FTGMAC030_MACCR_SW_RST)
|
||||
;
|
||||
}
|
||||
|
||||
/*
|
||||
* Set MAC address
|
||||
*/
|
||||
static void ftgmac030_set_mac(struct eth_device *dev,
|
||||
const unsigned char *mac)
|
||||
{
|
||||
struct ftgmac030 *ftgmac030 = (struct ftgmac030 *)dev->iobase;
|
||||
unsigned int maddr = mac[0] << 8 | mac[1];
|
||||
unsigned int laddr = mac[2] << 24 | mac[3] << 16 | mac[4] << 8 | mac[5];
|
||||
|
||||
debug("%s(%x %x)\n", __func__, maddr, laddr);
|
||||
|
||||
writel(maddr, &ftgmac030->mac_madr);
|
||||
writel(laddr, &ftgmac030->mac_ladr);
|
||||
}
|
||||
|
||||
static void ftgmac030_set_mac_from_env(struct eth_device *dev)
|
||||
{
|
||||
unsigned char mac[MAC_LEN];
|
||||
|
||||
memset(mac, 0, sizeof(mac));
|
||||
|
||||
if (!eth_env_get_enetaddr("ethaddr", mac)) {
|
||||
printf("MAC address invalid!\n");
|
||||
#ifdef CONFIG_NET_RANDOM_ETHADDR
|
||||
net_random_ethaddr(mac);
|
||||
printf("Set Random MAC address!\n");
|
||||
eth_env_set_enetaddr("ethaddr", mac);
|
||||
#endif
|
||||
}
|
||||
|
||||
memcpy(dev->enetaddr, mac, MAC_LEN);
|
||||
|
||||
ftgmac030_set_mac(dev, dev->enetaddr);
|
||||
}
|
||||
|
||||
/*
|
||||
* disable transmitter, receiver
|
||||
*/
|
||||
static void ftgmac030_halt(struct eth_device *dev)
|
||||
{
|
||||
struct ftgmac030 *ftgmac030 = (struct ftgmac030 *)dev->iobase;
|
||||
|
||||
debug("%s()\n", __func__);
|
||||
|
||||
writel(0, &ftgmac030->maccr);
|
||||
|
||||
bspeth_sys_exit();
|
||||
}
|
||||
|
||||
static int ftgmac030_init(struct eth_device *dev, bd_t *bd)
|
||||
{
|
||||
struct ftgmac030 *ftgmac030 = (struct ftgmac030 *)dev->iobase;
|
||||
struct ftgmac030_data *priv = dev->priv;
|
||||
unsigned int maccr;
|
||||
ulong start, end;
|
||||
int i;
|
||||
|
||||
debug("%s()\n", __func__);
|
||||
|
||||
/* sys-func-sel */
|
||||
bspeth_sys_init();
|
||||
|
||||
ftgmac030_reset(dev);
|
||||
|
||||
/* set the MII interface */
|
||||
ftgmac030_set_phy_intf(dev);
|
||||
|
||||
/* set the ethernet address */
|
||||
ftgmac030_set_mac_from_env(dev);
|
||||
|
||||
/* disable all interrupts */
|
||||
writel(0, &ftgmac030->ier);
|
||||
|
||||
/* initialize descriptors */
|
||||
priv->tx_index = 0;
|
||||
priv->rx_index = 0;
|
||||
|
||||
for (i = 0; i < FTGMAC030_PKTBUFSTX; i++) {
|
||||
/* TXBUF_BADR */
|
||||
priv->txdes[i].txdes3 = 0;
|
||||
priv->txdes[i].txdes0 = 0;
|
||||
}
|
||||
priv->txdes[FTGMAC030_PKTBUFSTX - 1].txdes0 = FTGMAC030_TXDES0_EDOTR;
|
||||
|
||||
start = ((ulong)&priv->txdes[0]) & ~(ARCH_DMA_MINALIGN - 1);
|
||||
end = start + roundup(sizeof(priv->txdes), ARCH_DMA_MINALIGN);
|
||||
flush_dcache_range(start, end);
|
||||
|
||||
for (i = 0; i < FTGMAC030_PKTBUFSRX; i++) {
|
||||
/* RXBUF_BADR */
|
||||
priv->rxdes[i].rxdes3 = (unsigned int)net_rx_packets[i];
|
||||
priv->rxdes[i].rxdes0 = 0;
|
||||
}
|
||||
priv->rxdes[FTGMAC030_PKTBUFSRX - 1].rxdes0 = FTGMAC030_RXDES0_EDORR;
|
||||
|
||||
start = ((ulong)&priv->rxdes[0]) & ~(ARCH_DMA_MINALIGN - 1);
|
||||
end = start + roundup(sizeof(priv->rxdes), ARCH_DMA_MINALIGN);
|
||||
flush_dcache_range(start, end);
|
||||
|
||||
/* transmit ring */
|
||||
writel((unsigned int)priv->txdes, &ftgmac030->nptxdesc_addr);
|
||||
|
||||
/* receive ring */
|
||||
writel((unsigned int)priv->rxdes, &ftgmac030->rxdesc_addr);
|
||||
|
||||
/* poll receive descriptor automatically */
|
||||
writel(FTGMAC030_APTC_DEFAULT, &ftgmac030->aptc);
|
||||
|
||||
/* config receive buffer size register */
|
||||
writel(FTGMAC030_RBSR_RXBUF_SIZE(FTGMAC030_RBSR_DEFAULT_VALUE), &ftgmac030->rbsr);
|
||||
|
||||
/* enable transmitter, receiver */
|
||||
maccr = FTGMAC030_MACCR_DEFAULT;
|
||||
writel(maccr, &ftgmac030->maccr);
|
||||
|
||||
if (ftgmac030_phy_init(dev)) {
|
||||
if (!ftgmac030_update_link_speed(dev))
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Get a data block via Ethernet
|
||||
*/
|
||||
static int ftgmac030_recv(struct eth_device *dev)
|
||||
{
|
||||
struct ftgmac030_data *priv = dev->priv;
|
||||
struct ftgmac030_rxdes *curr_des = &priv->rxdes[priv->rx_index];
|
||||
unsigned short rxlen;
|
||||
ulong des_start = ((ulong)curr_des) & ~(ARCH_DMA_MINALIGN - 1);
|
||||
ulong des_end = des_start +
|
||||
roundup(sizeof(*curr_des), ARCH_DMA_MINALIGN);
|
||||
ulong data_start = curr_des->rxdes3;
|
||||
ulong data_end;
|
||||
|
||||
invalidate_dcache_range(des_start, des_end);
|
||||
|
||||
if (!(curr_des->rxdes0 & FTGMAC030_RXDES0_RXPKT_RDY))
|
||||
return -EAGAIN;
|
||||
|
||||
if (curr_des->rxdes0 & (FTGMAC030_RXDES0_RX_ERR |
|
||||
FTGMAC030_RXDES0_CRC_ERR |
|
||||
FTGMAC030_RXDES0_FTL |
|
||||
FTGMAC030_RXDES0_RUNT |
|
||||
FTGMAC030_RXDES0_RX_ODD_NB)) {
|
||||
return -EAGAIN;
|
||||
}
|
||||
|
||||
rxlen = FTGMAC030_RXDES0_VDBC(curr_des->rxdes0);
|
||||
|
||||
debug("%s(): RX buffer %d, %x received\n",
|
||||
__func__, priv->rx_index, rxlen);
|
||||
|
||||
/* Invalidate received data */
|
||||
data_end = data_start + roundup(rxlen, ARCH_DMA_MINALIGN);
|
||||
invalidate_dcache_range(data_start, data_end);
|
||||
|
||||
/* pass the packet up to the protocol layers. */
|
||||
net_process_received_packet((void *)curr_des->rxdes3, rxlen);
|
||||
|
||||
/* Release buffer to DMA and flush descriptor */
|
||||
curr_des->rxdes0 &= FTGMAC030_RXDES0_EDORR;
|
||||
curr_des->rxdes1 = 0x0;
|
||||
flush_dcache_range(des_start, des_end);
|
||||
|
||||
/* Move to next descriptor */
|
||||
priv->rx_index = (priv->rx_index + 1) % FTGMAC030_PKTBUFSRX;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static u32 ftgmac030_read_txdesc(const void *desc)
|
||||
{
|
||||
const struct ftgmac030_txdes *txdes = desc;
|
||||
ulong des_start = ((ulong)txdes) & ~(ARCH_DMA_MINALIGN - 1);
|
||||
ulong des_end = des_start + roundup(sizeof(*txdes), ARCH_DMA_MINALIGN);
|
||||
|
||||
invalidate_dcache_range(des_start, des_end);
|
||||
|
||||
return txdes->txdes0;
|
||||
}
|
||||
|
||||
BUILD_WAIT_FOR_BIT(ftgmac030_txdone, u32, ftgmac030_read_txdesc)
|
||||
|
||||
/*
|
||||
* Send a data block via Ethernet
|
||||
*/
|
||||
static int ftgmac030_send(struct eth_device *dev, void *packet, int length)
|
||||
{
|
||||
struct ftgmac030_data *priv = dev->priv;
|
||||
struct ftgmac030 *ftgmac030 = (struct ftgmac030 *)dev->iobase;
|
||||
struct ftgmac030_txdes *curr_des = &priv->txdes[priv->tx_index];
|
||||
ulong des_start = ((ulong)curr_des) & ~(ARCH_DMA_MINALIGN - 1);
|
||||
ulong des_end = des_start +
|
||||
roundup(sizeof(*curr_des), ARCH_DMA_MINALIGN);
|
||||
ulong data_start;
|
||||
ulong data_end;
|
||||
int rc;
|
||||
|
||||
invalidate_dcache_range(des_start, des_end);
|
||||
|
||||
if (curr_des->txdes0 & FTGMAC030_TXDES0_TXDMA_OWN) {
|
||||
debug("%s(): no TX descriptor available\n", __func__);
|
||||
return -1;
|
||||
}
|
||||
|
||||
debug("%s(%x, %x)\n", __func__, (int)packet, length);
|
||||
|
||||
length = (length < ETH_ZLEN) ? ETH_ZLEN : length;
|
||||
|
||||
/* initiate a transmit sequence */
|
||||
curr_des->txdes3 = (unsigned int)packet; /* TXBUF_BADR */
|
||||
|
||||
/* Flush data to be sent */
|
||||
data_start = curr_des->txdes3;
|
||||
data_end = data_start + roundup(length, ARCH_DMA_MINALIGN);
|
||||
flush_dcache_range(data_start, data_end);
|
||||
|
||||
/* only one descriptor on TXBUF */
|
||||
curr_des->txdes0 &= FTGMAC030_TXDES0_EDOTR;
|
||||
curr_des->txdes0 |= FTGMAC030_TXDES0_FTS |
|
||||
FTGMAC030_TXDES0_LTS |
|
||||
FTGMAC030_TXDES0_BUF_SIZE(length) |
|
||||
FTGMAC030_TXDES0_TXDMA_OWN ;
|
||||
|
||||
/* Flush modified buffer descriptor */
|
||||
flush_dcache_range(des_start, des_end);
|
||||
|
||||
/* start transmit */
|
||||
writel(1, &ftgmac030->nptxpd);
|
||||
|
||||
/* wait for transfer to succeed */
|
||||
rc = wait_for_bit_ftgmac030_txdone(curr_des,
|
||||
FTGMAC030_TXDES0_TXDMA_OWN, false,
|
||||
FTGMAC030_TX_TIMEOUT_MS, true);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
debug("%s(): packet sent\n", __func__);
|
||||
|
||||
/* Move to next descriptor */
|
||||
priv->tx_index = (priv->tx_index + 1) % FTGMAC030_PKTBUFSTX;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ftgmac030_initialize(bd_t *bd)
|
||||
{
|
||||
struct eth_device *dev;
|
||||
struct ftgmac030_data *priv;
|
||||
|
||||
dev = malloc(sizeof *dev);
|
||||
if (!dev) {
|
||||
printf("%s(): failed to allocate dev\n", __func__);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Transmit and receive descriptors should align to 16 bytes */
|
||||
priv = memalign(ARCH_DMA_MINALIGN, sizeof(struct ftgmac030_data));
|
||||
if (!priv) {
|
||||
printf("%s(): failed to allocate priv\n", __func__);
|
||||
goto free_dev;
|
||||
}
|
||||
|
||||
memset(dev, 0, sizeof(*dev));
|
||||
memset(priv, 0, sizeof(*priv));
|
||||
|
||||
sprintf(dev->name, "FTGMAC030");
|
||||
dev->iobase = GMAC_REG_BASE;
|
||||
dev->init = ftgmac030_init;
|
||||
dev->halt = ftgmac030_halt;
|
||||
dev->send = ftgmac030_send;
|
||||
dev->recv = ftgmac030_recv;
|
||||
dev->priv = priv;
|
||||
|
||||
ftgmac030_mdiobus_init(dev);
|
||||
|
||||
eth_register(dev);
|
||||
|
||||
return 1;
|
||||
|
||||
free_dev:
|
||||
free(dev);
|
||||
out:
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,459 @@
|
||||
/*
|
||||
* Copyright (c) LOTUS. All rights reserved.
|
||||
*/
|
||||
|
||||
#ifndef __FTGMAC030_H
|
||||
#define __FTGMAC030_H
|
||||
|
||||
/* The registers offset table of ftgmac030 */
|
||||
struct ftgmac030 {
|
||||
volatile unsigned int isr; /* 0x00 */
|
||||
volatile unsigned int ier; /* 0x04 */
|
||||
volatile unsigned int mac_madr; /* 0x08 */
|
||||
volatile unsigned int mac_ladr; /* 0x0C */
|
||||
volatile unsigned int maht0; /* 0x10 */
|
||||
volatile unsigned int maht1; /* 0x14 */
|
||||
volatile unsigned int nptxpd; /* 0x18 */
|
||||
volatile unsigned int rxpd; /*0x1C */
|
||||
volatile unsigned int nptxdesc_addr; /* 0x20 */
|
||||
volatile unsigned int rxdesc_addr; /* 0x24 */
|
||||
volatile unsigned int hptxpd; /* 0x28 */
|
||||
volatile unsigned int hptxdesc_addr; /*0x2C */
|
||||
volatile unsigned int txitc; /* 0x30 */
|
||||
volatile unsigned int rxitc; /* 0x34 */
|
||||
volatile unsigned int aptc; /* 0x38 */
|
||||
volatile unsigned int dblac; /* 0x3C */
|
||||
volatile unsigned int dmafifos; /* 0x40 */
|
||||
volatile unsigned int rsvd1; /* 0x44 */
|
||||
volatile unsigned int tfafcr; /* 0x48 */
|
||||
volatile unsigned int rbsr; /* 0x4C */
|
||||
volatile unsigned int maccr; /* 0x50 */
|
||||
volatile unsigned int macsr; /* 0x54 */
|
||||
volatile unsigned int tm; /* 0x58 */
|
||||
volatile unsigned int rsvd2; /* 0x5C */
|
||||
volatile unsigned int phycr; /* 0x60 */
|
||||
volatile unsigned int phydata;/* 0x64 */
|
||||
volatile unsigned int flow_cntrl; /* 0x68 */
|
||||
volatile unsigned int back_pressure; /* 0x6C */
|
||||
volatile unsigned int wakeOnLan_cntrl; /* 0x70 */
|
||||
volatile unsigned int wakeOnLan_sts; /* 0x74 */
|
||||
volatile unsigned int wfc_crc; /* 0x78 */
|
||||
volatile unsigned int rsvd3; /* 0x7C */
|
||||
volatile unsigned int wfbm1; /* 0x80 */
|
||||
volatile unsigned int wfbm2; /* 0x84 */
|
||||
volatile unsigned int wfbm3; /* 0x88 */
|
||||
volatile unsigned int wfbm4; /* 0x8C */
|
||||
volatile unsigned int nptxr_ptr; /* 0x90 */
|
||||
volatile unsigned int hptxr_prt; /* 0x94 */
|
||||
volatile unsigned int rxr_ptr; /* 0x98 */
|
||||
volatile unsigned int rsvd4; /* 0x9C */
|
||||
volatile unsigned int tpkt_cnt; /* 0xA0 */
|
||||
volatile unsigned int tx_m_s_col; /* 0xA4 */
|
||||
volatile unsigned int tx_e_f_cnt; /* 0xA8 */
|
||||
volatile unsigned int tx_l_u_cnt; /* 0xAC */
|
||||
volatile unsigned int rpkt_cnt; /* 0xB0 */
|
||||
volatile unsigned int bropkt_cnt; /* 0xB4 */
|
||||
volatile unsigned int mulpkt_cnt; /* 0xB8 */
|
||||
volatile unsigned int rpf_aep_cnt; /* 0xBC */
|
||||
volatile unsigned int runt; /* 0xC0 */
|
||||
volatile unsigned int crcer_ftl_cnt; /* 0xC4 */
|
||||
volatile unsigned int rcol_rlost_cnt; /* 0xC8 */
|
||||
volatile unsigned int bistr; /* 0xCC */
|
||||
volatile unsigned int bmrcr; /* 0xD0 */
|
||||
volatile unsigned int rsvd5[4]; /* 0xD4 ~ 0xE3 */
|
||||
volatile unsigned int acir; /* 0xE4 AXI additional Control Information */
|
||||
volatile unsigned int gisr; /* 0xE8 GMAC Interface Selection */
|
||||
volatile unsigned int rsvd6;/* 0xEC */
|
||||
volatile unsigned int eee; /* 0xF0 */
|
||||
volatile unsigned int revr; /* 0xF4 */
|
||||
volatile unsigned int fear; /* 0xF8 */
|
||||
volatile unsigned int rsvd7[1]; /* 0xFC */
|
||||
volatile unsigned int ptp_rx_addr; /* 0x100 PTP Destination Unicast IP Address on Rx path */
|
||||
volatile unsigned int ptp_tx_addr; /* 0x104 PTP Destination Unicast IP Address on Tx path */
|
||||
volatile unsigned int rsvd8[2]; /* 0x108 ~ 0x10C*/
|
||||
volatile unsigned int ptp_tx_sec; /* 0x110 PTP TX Event Frame Time-stamp for seconds */
|
||||
volatile unsigned int ptp_tx_nsec; /* 0x114 PTP TX Event Frame Time-stamp for nanoseconds */
|
||||
volatile unsigned int ptp_rx_sec; /* 0x118 PTP RX Event Frame Time-stamp for seconds */
|
||||
volatile unsigned int ptp_rx_nsec; /* 0x11C PTP RX Event Frame Time-stamp for nanoseconds */
|
||||
volatile unsigned int ptp_tx_p_sec; /* 0x120 PTP TX Peer Frame Time-stamp for seconds */
|
||||
volatile unsigned int ptp_tx_p_nsec; /* 0x124 PTP TX Peer Frame Time-stamp for nanoseconds */
|
||||
volatile unsigned int ptp_rx_p_sec; /* 0x128 PTP RX Peer Frame Time-stamp for seconds */
|
||||
volatile unsigned int ptp_rx_p_nsec; /* 0x12C PTP RX Peer Frame Time-stamp for nanoseconds */
|
||||
volatile unsigned int ptp_nnsec_tmr; /* 0x130 PTP Timer Nano-nansecond */
|
||||
volatile unsigned int ptp_nsec_tmr; /* 0x134 PTP Timer nanosecond */
|
||||
volatile unsigned int ptp_sec_tmr; /* 0x138 PTP Second */
|
||||
volatile unsigned int ptp_ns_period; /* 0x13C PTP period increment, max is 255 ns */
|
||||
volatile unsigned int ptp_nns_period; /* 0x140 PTP period increment, max is 0.999999999 ns */
|
||||
volatile unsigned int ptp_offset; /* 0x144 PTP period offset increment */
|
||||
volatile unsigned int ptp_tmr_adj; /* 0x148 PTP Timer Adjustment */
|
||||
};
|
||||
|
||||
/*
|
||||
* Interrupt Status Register 0x00 and 0x04
|
||||
*/
|
||||
#define FTGMAC030_INT_PDELAY_RESP_OUT (1 << 24)
|
||||
#define FTGMAC030_INT_PDELAY_RESP_IN (1 << 23)
|
||||
#define FTGMAC030_INT_PDELAY_REQ_OUT (1 << 22)
|
||||
#define FTGMAC030_INT_PDELAY_REQ_IN (1 << 21)
|
||||
#define FTGMAC030_INT_DELAY_REQ_OUT (1 << 20)
|
||||
#define FTGMAC030_INT_DELAY_REQ_IN (1 << 19)
|
||||
#define FTGMAC030_INT_SYNC_OUT (1 << 18)
|
||||
#define FTGMAC030_INT_SYNC_IN (1 << 17)
|
||||
#define FTGMAC030_INT_TSU_SEC_INC (1 << 16)
|
||||
#define FTGMAC030_INT_RX_LPI_IN (1 << 12)
|
||||
#define FTGMAC030_INT_RX_LPI_EXIT (1 << 11)
|
||||
#define FTGMAC030_INT_HPTXBUF_UNAVA (1 << 10)
|
||||
#define FTGMAC030_INT_PHYSTS_CHG (1 << 9)
|
||||
#define FTGMAC030_INT_AHB_ERR (1 << 8)
|
||||
#define FTGMAC030_INT_TPKT_LOST (1 << 7)
|
||||
#define FTGMAC030_INT_NPTXBUF_UNAVA (1 << 6)
|
||||
#define FTGMAC030_INT_TPKT2F (1 << 5)
|
||||
#define FTGMAC030_INT_TPKT2E (1 << 4)
|
||||
#define FTGMAC030_INT_RPKT_LOST (1 << 3)
|
||||
#define FTGMAC030_INT_RXBUF_UNAVA (1 << 2)
|
||||
#define FTGMAC030_INT_RPKT2F (1 << 1)
|
||||
#define FTGMAC030_INT_RPKT2B (1 << 0)
|
||||
|
||||
#define FTGMAC030_INT_DEFAULT ( \
|
||||
FTGMAC030_INT_RPKT2B | \
|
||||
FTGMAC030_INT_RXBUF_UNAVA | \
|
||||
FTGMAC030_INT_RPKT_LOST | \
|
||||
FTGMAC030_INT_TPKT2E | \
|
||||
FTGMAC030_INT_TPKT2F | \
|
||||
FTGMAC030_INT_TPKT_LOST | \
|
||||
FTGMAC030_INT_AHB_ERR | \
|
||||
FTGMAC030_INT_PHYSTS_CHG \
|
||||
)
|
||||
|
||||
#define FTGMAC030_INT_ALL ( \
|
||||
FTGMAC030_INT_RPKT2B | \
|
||||
FTGMAC030_INT_RPKT2F | \
|
||||
FTGMAC030_INT_RXBUF_UNAVA | \
|
||||
FTGMAC030_INT_RPKT_LOST | \
|
||||
FTGMAC030_INT_TPKT2E | \
|
||||
FTGMAC030_INT_TPKT2F | \
|
||||
FTGMAC030_INT_NPTXBUF_UNAVA | \
|
||||
FTGMAC030_INT_TPKT_LOST | \
|
||||
FTGMAC030_INT_AHB_ERR | \
|
||||
FTGMAC030_INT_PHYSTS_CHG | \
|
||||
FTGMAC030_INT_HPTXBUF_UNAVA \
|
||||
)
|
||||
/*
|
||||
* MAC Most Significant Address Register 0x08
|
||||
*/
|
||||
#define FTGMAC030_MADR(x) ((x & 0xffff) << 0)
|
||||
|
||||
/*
|
||||
* MAC Least Siginificant Address Register 0x0C
|
||||
*/
|
||||
#define FTGMAC030_LADR(x) ((x & 0xffffffff) << 0)
|
||||
|
||||
/*
|
||||
* TX Interrupt Timer Control Register 0x30
|
||||
*/
|
||||
#define FTGMAC030_TXITC_TIME_SEL (1 << 16)
|
||||
#define FTGMAC030_TXITC_CYL(x) ((x & 0xff) << 8)
|
||||
#define FTGMAC030_TXITC_THR(x) ((x & 0x7) << 4)
|
||||
#define FTGMAC030_TXITC_THR_UNIT(x) ((x & 0x3) << 0)
|
||||
|
||||
#define FTGMAC030_TXITC_DEFAULT FTGMAC030_TXITC_THR(1)
|
||||
|
||||
/*
|
||||
* RX Interrupt Timer Control Register 0x34
|
||||
*/
|
||||
#define FTGMAC030_RXITC_RST(x) ((x & 0xff) << 16)
|
||||
#define FTGMAC030_RXITC_TIME_SEL (1 << 16)
|
||||
#define FTGMAC030_RXITC_CYL(x) ((x & 0xff) << 8)
|
||||
#define FTGMAC030_RXITC_THR(x) ((x & 0x7) << 4)
|
||||
#define FTGMAC030_RXITC_THR_UNIT(x) ((x & 0x3) << 0)
|
||||
|
||||
#define FTGMAC030_RXITC_DEFAULT FTGMAC030_RXITC_THR(1)
|
||||
|
||||
/*
|
||||
* Automatic Polling Timer Control Register 0x34
|
||||
*/
|
||||
#define FTGMAC030_APTC_TX_TIME (1 << 12)
|
||||
#define FTGMAC030_APTC_TX_CNT(x) ((x & 0xf) << 8)
|
||||
#define FTGMAC030_APTC_RX_TIME (1 << 4)
|
||||
#define FTGMAC030_APTC_RX_CNT(x) ((x & 0xf) << 0)
|
||||
|
||||
#define FTGMAC030_APTC_DEFAULT FTGMAC030_APTC_RX_CNT(1)
|
||||
|
||||
/*
|
||||
* DMA Burst Length and Arbitration Control Register 0x38
|
||||
*/
|
||||
#define FTGMAC030_DBLAC_IFG_INC (1 << 23)
|
||||
#define FTGMAC030_DBLAC_IFG_CNT(x) ((x & 0x7) << 20)
|
||||
#define FTGMAC030_DBLAC_TXDES_SIZE(x) ((x & 0xf) << 16)
|
||||
#define FTGMAC030_DBLAC_RXDES_SIZE(x) ((x & 0xf) << 12)
|
||||
#define FTGMAC030_DBLAC_TXBST_SIZE(x) ((x & 0x3) << 10)
|
||||
#define FTGMAC030_DBLAC_RXBST_SIZE(x) ((x & 0x3) << 8)
|
||||
#define FTGMAC030_DBLAC_RX_THR_EN (1 << 6)
|
||||
#define FTGMAC030_DBLAC_RXFIFO_HTHR(x) ((x & 0x7) << 3)
|
||||
#define FTGMAC030_DBLAC_RXFIFO_LTHR(x) ((x & 0x7) << 0)
|
||||
|
||||
#define FTGMAC030_DBLAC_DEFAULT FTGMAC030_DBLAC_RXFIFO_LTHR(2) | \
|
||||
FTGMAC030_DBLAC_RXFIFO_HTHR(6) | \
|
||||
FTGMAC030_DBLAC_RX_THR_EN | \
|
||||
FTGMAC030_DBLAC_RXBST_SIZE(3) | \
|
||||
FTGMAC030_DBLAC_TXBST_SIZE(3)
|
||||
/*
|
||||
* DMA/FIFO State Register 0x3c
|
||||
*/
|
||||
#define FTGMAC030_DMAFIFOS_TXD_REQ (1 << 31)
|
||||
#define FTGMAC030_DMAFIFOS_RXD_REQ (1 << 30)
|
||||
#define FTGMAC030_DMAFIFOS_DARB_TXGNT (1 << 29)
|
||||
#define FTGMAC030_DMAFIFOS_DARB_RXGNT (1 << 28)
|
||||
#define FTGMAC030_DMAFIFOS_TXFIFO_EMPTY (1 << 27)
|
||||
#define FTGMAC030_DMAFIFOS_RXFIFO_EMPTY (1 << 26)
|
||||
#define FTGMAC030_DMAFIFOS_TXDMA3_SM(x) ((x & 0xf) << 18)
|
||||
#define FTGMAC030_DMAFIFOS_TXDMA2_SM(x) ((x & 0xf) << 16)
|
||||
#define FTGMAC030_DMAFIFOS_TXDMA1_SM(x) ((x & 0xf) << 12)
|
||||
#define FTGMAC030_DMAFIFOS_RXDMA3_SM(x) ((x & 0xf) << 8)
|
||||
#define FTGMAC030_DMAFIFOS_RXDMA2_SM(x) ((x & 0xf) << 4)
|
||||
#define FTGMAC030_DMAFIFOS_RXDMA1_SM(x) ((x & 0xf) << 0)
|
||||
|
||||
/*
|
||||
* Revision Register 0x40
|
||||
*/
|
||||
#define FTGMAC030_REV_B1(x) ((x >> 16) & 0xff)
|
||||
#define FTGMAC030_REV_B2(x) ((x >> 8) & 0xff)
|
||||
#define FTGMAC030_REV_B3(x) (x & 0xff)
|
||||
|
||||
/*
|
||||
* Feature Register 0xF8
|
||||
*/
|
||||
#define FTGMAC030_FEAR_TFIFO_RSIZE(x) ((x & 0x7) >> 4)
|
||||
#define FTGMAC030_FEAR_RFIFO_RSIZE(x) ((x & 0x7) >> 0)
|
||||
|
||||
/*
|
||||
* Transmit Priority Arbitration and FIFO Control Register 0x48
|
||||
*/
|
||||
#define FTGMAC030_TPAFCR_TFIFO_SIZE(x) ((x & 0x7) << 27)
|
||||
#define FTGMAC030_TPAFCR_RFIFO_SIZE(x) ((x & 0x7) << 24)
|
||||
#define FTGMAC030_TPAFCR_EARLY_TXTHR(x) ((x & 0xff) << 16)
|
||||
#define FTGMAC030_TPAFCR_EARLY_RXTHR(x) ((x & 0xff) << 8)
|
||||
#define FTGMAC030_TPAFCR_HPKT_THR(x) ((x & 0xf) << 4)
|
||||
#define FTGMAC030_TPAFCR_NPKT_THR(x) ((x & 0xf) << 0)
|
||||
|
||||
/*
|
||||
* Receive Buffer Size Register 0x4c
|
||||
*/
|
||||
#define FTGMAC030_RBSR_RXBUF_SIZE(x) ((x & 0x3fff) << 0)
|
||||
|
||||
/*
|
||||
* MAC Control Register 0x50
|
||||
*/
|
||||
#define FTGMAC030_MACCR_SW_RST (1 << 31)
|
||||
#define FTGMAC030_MACCR_FULLDUP (1 << 26)
|
||||
#define FTGMAC030_MACCR_SPEED_MASK (0x3 << 24)
|
||||
#define FTGMAC030_MACCR_SPEED_1000 (2 << 24)
|
||||
#define FTGMAC030_MACCR_SPEED_100 (1 << 24)
|
||||
#define FTGMAC030_MACCR_SPEED_10 (0 << 24)
|
||||
#define FTGMAC030_MACCR_HPTXR (1 << 22)
|
||||
#define FTGMAC030_MACCR_LOOPBACK (1 << 21)
|
||||
#define FTGMAC030_MACCR_PTP_EN (1 << 20)
|
||||
#define FTGMAC030_MACCR_REMOVE_VLAN (1 << 18)
|
||||
#define FTGMAC030_MACCR_CRC_APD (1 << 17)
|
||||
#define FTGMAC030_MACCR_DROP_CRC_ERR (1 << 16)
|
||||
#define FTGMAC030_MACCR_ENRX_IN_HALFTX (1 << 14)
|
||||
#define FTGMAC030_MACCR_JUMBO_LF (1 << 13)
|
||||
#define FTGMAC030_MACCR_RX_RUNT (1 << 12)
|
||||
#define FTGMAC030_MACCR_BROADPKT (1 << 11)
|
||||
#define FTGMAC030_MACCR_MULTIPKT (1 << 10)
|
||||
#define FTGMAC030_MACCR_HT_EN (1 << 9)
|
||||
#define FTGMAC030_MACCR_ALLADDR (1 << 8)
|
||||
#define FTGMAC030_MACCR_RXMAC (1 << 3)
|
||||
#define FTGMAC030_MACCR_TXMAC (1 << 2)
|
||||
#define FTGMAC030_MACCR_RXDMA (1 << 1)
|
||||
#define FTGMAC030_MACCR_TXDMA (1 << 0)
|
||||
|
||||
#define FTGMAC030_MACCR_DEFAULT ( \
|
||||
FTGMAC030_MACCR_RX_RUNT | \
|
||||
FTGMAC030_MACCR_CRC_APD | \
|
||||
FTGMAC030_MACCR_FULLDUP | \
|
||||
FTGMAC030_MACCR_TXDMA | \
|
||||
FTGMAC030_MACCR_RXMAC | \
|
||||
FTGMAC030_MACCR_RXDMA | \
|
||||
FTGMAC030_MACCR_TXMAC)
|
||||
|
||||
/*
|
||||
* MAC Status Register 0x54
|
||||
*/
|
||||
#define FTGMAC030_MACSR_COL_EXCEED
|
||||
#define FTGMAC030_MACSR_LATE_COL
|
||||
#define FTGMAC030_MACSR_XPKT_LOST
|
||||
#define FTGMAC030_MACSR_XPKT_OK
|
||||
#define FTGMAC030_MACSR_RUNT
|
||||
#define FTGMAC030_MACSR_FTL
|
||||
#define FTGMAC030_MACSR_CRC_ERR
|
||||
#define FTGMAC030_MACSR_RPKT_LOST
|
||||
#define FTGMAC030_MACSR_RPKT_SAVE
|
||||
#define FTGMAC030_MACSR_COL
|
||||
#define FTGMAC030_MACSR_BROADCAST
|
||||
#define FTGMAC030_MACSR_MULTICAST
|
||||
|
||||
/*
|
||||
* Test Mode Register 0x58
|
||||
*/
|
||||
#define FTGMAC030_TM_PTIMER_TEST (1 << 20)
|
||||
#define FTGMAC030_TM_ITIMER_TEST (1 << 19)
|
||||
#define FTGMAC030_TM_TEST_COL (1 << 15)
|
||||
#define FTGMAC030_TM_TEST_BKOFF(x) ((x & 0x3ff) << 5)
|
||||
#define FTGMAC030_TM_TEST_EXSTHR(x) ((x & 0x1f) << 0)
|
||||
|
||||
/*
|
||||
* PHY Control Register 0x60
|
||||
*/
|
||||
#define FTGMAC030_MDIO_SOF 1
|
||||
#define FTGMAC030_MDIO_EXT_SOF 0
|
||||
#define FTGMAC030_MDIO_OP_RD 2
|
||||
#define FTGMAC030_MDIO_OP_WR 1
|
||||
|
||||
#define FTGMAC030_PHYCR_PHYWR (1 << 27)
|
||||
#define FTGMAC030_PHYCR_PHYRD (1 << 26)
|
||||
#define FTGMAC030_PHYCR_REGAD(x) ((x & 0x1f) << 21)
|
||||
#define FTGMAC030_PHYCR_PHYAD(x) ((x & 0x1f) << 16)
|
||||
#define FTGMAC030_PHYCR_OP(x) ((x & 0x3) << 14)
|
||||
#define FTGMAC030_PHYCR_SOF(x) ((x & 0x3) << 12)
|
||||
|
||||
#define FTGMAC030_PHYCR_MDC_CYCTHR(x) ((x) & 0xff)
|
||||
#define FTGMAC030_PHYCR_MDC_CYCTHR_MASK 0xff
|
||||
|
||||
/*
|
||||
* PHY Data Register 0x64
|
||||
*/
|
||||
#define FTGMAC030_PHYDATA_MIIRDATA(x) ((x & 0xffff0000) >> 16)
|
||||
#define FTGMAC030_PHYDATA_MIIWDATA(x) ((x & 0xffff) >> 0)
|
||||
|
||||
/*
|
||||
* Flow Control Register 0x68
|
||||
*/
|
||||
#define FTGMAC030_FCR_PAUSE_TIME(x) ((x & 0xffff) << 16)
|
||||
#define FTGMAC030_FCR_FC_H_L(x) ((x & 0x7f) << 9)
|
||||
#define FTGMAC030_FCR_HTHR (1 << 8)
|
||||
#define FTGMAC030_FCR_RX_PAUSE (1 << 4)
|
||||
#define FTGMAC030_FCR_TXPAUSED (1 << 3)
|
||||
#define FTGMAC030_FCR_FCTHR_EN (1 << 2)
|
||||
#define FTGMAC030_FCR_TX_PAUSE (1 << 1)
|
||||
#define FTGMAC030_FCR_FC_EN (1 << 0)
|
||||
|
||||
/*
|
||||
* Back Pressure Register 0x6c
|
||||
*/
|
||||
#define ftgmac030_BPR_BK_LOW(x) ((x & 0x7f) << 8)
|
||||
#define ftgmac030_BPR_BKJAM_LEN(x) ((x & 0xf) << 4)
|
||||
#define ftgmac030_BPR_BKADR_MODE (1 << 1)
|
||||
#define ftgmac030_BPR_BKEN (1 << 0)
|
||||
|
||||
/*
|
||||
* Wake-On-LAN Control Register 0x70
|
||||
*/
|
||||
#define FTGMAC030_WOLCR_WOL_TYPE(x) ((x & 0x3) << 24)
|
||||
#define FTGMAC030_WOLCR_SW_PDNPHY (1 << 18)
|
||||
#define FTGMAC030_WOLCR_WAKEUP_SEL(x) ((x & 0x3) << 16)
|
||||
#define FTGMAC030_WOLCR_WAKEUP4 (1 << 6)
|
||||
#define FTGMAC030_WOLCR_WAKEUP3 (1 << 5)
|
||||
#define FTGMAC030_WOLCR_WAKEUP2 (1 << 4)
|
||||
#define FTGMAC030_WOLCR_WAKEUP1 (1 << 3)
|
||||
#define FTGMAC030_WOLCR_MAGICPKT (1 << 2)
|
||||
#define FTGMAC030_WOLCR_LINKCHG1 (1 << 1)
|
||||
#define FTGMAC030_WOLCR_LINKCHG0 (1 << 0)
|
||||
|
||||
/*
|
||||
* Wake-On-LAN Status Register 0x74
|
||||
*/
|
||||
#define FTGMAC030_WOLSR_WAKEUP4 (1 << 6)
|
||||
#define FTGMAC030_WOLSR_WAKEUP3 (1 << 5)
|
||||
#define FTGMAC030_WOLSR_WAKEUP2 (1 << 4)
|
||||
#define FTGMAC030_WOLSR_WAKEUP1 (1 << 3)
|
||||
#define FTGMAC030_WOLSR_MAGICPKT (1 << 2)
|
||||
#define FTGMAC030_WOLSR_LINKCHG1 (1 << 1)
|
||||
#define FTGMAC030_WOLSR_LINKCHG0 (1 << 0)
|
||||
|
||||
/*
|
||||
* Transmit descriptor, aligned to 16 bytes
|
||||
*/
|
||||
struct ftgmac030_txdes {
|
||||
volatile unsigned int txdes0;
|
||||
volatile unsigned int txdes1;
|
||||
volatile unsigned int txdes2; /* not used by HW */
|
||||
volatile unsigned int txdes3; /* TXBUF_BADR */
|
||||
} __attribute__ ((aligned(16)));
|
||||
|
||||
#define FTGMAC030_TXDES0_TXDMA_OWN (1 << 31)
|
||||
#define FTGMAC030_TXDES0_FTS (1 << 29)
|
||||
#define FTGMAC030_TXDES0_LTS (1 << 28)
|
||||
#define FTGMAC030_TXDES0_CRC_ERR (1 << 19)
|
||||
#define FTGMAC030_TXDES0_EDOTR (1 << 15)
|
||||
#define FTGMAC030_TXDES0_BUF_SIZE(x) ((x & 0x3fff) << 0)
|
||||
|
||||
#define FTGMAC030_TXDES1_TXIC (1 << 31)
|
||||
#define FTGMAC030_TXDES1_TX2FIC (1 << 30)
|
||||
#define FTGMAC030_TXDES1_LLC_PKT (1 << 22)
|
||||
#define FTGMAC030_TXDES1_IPV6_PKT (1 << 20)
|
||||
#define FTGMAC030_TXDES1_OTHER_PKT (2 << 20)
|
||||
#define FTGMAC030_TXDES1_IPCS_EN (1 << 19)
|
||||
#define FTGMAC030_TXDES1_UDPCS_EN (1 << 18)
|
||||
#define FTGMAC030_TXDES1_TCPCS_EN (1 << 17)
|
||||
#define FTGMAC030_TXDES1_INS_VLAN (1 << 16)
|
||||
#define FTGMAC030_TXDES1_VLAN_TAGC(x) ((x & 0xffff) << 0)
|
||||
|
||||
#define FTGMAC030_TXDES1_DEFAULT (FTGMAC030_TXDES1_TXIC | FTGMAC030_TXDES1_TX2FIC)
|
||||
|
||||
/*
|
||||
* Receive descriptor, aligned to 16 bytes
|
||||
*/
|
||||
struct ftgmac030_rxdes {
|
||||
volatile unsigned int rxdes0;
|
||||
volatile unsigned int rxdes1;
|
||||
volatile unsigned int rxdes2; /* not used by HW */
|
||||
volatile unsigned int rxdes3; /* RXBUF_BADR */
|
||||
} __attribute__ ((aligned(16)));
|
||||
|
||||
#define FTGMAC030_RXDES0_RXPKT_RDY (1 << 31)
|
||||
#define FTGMAC030_RXDES0_FRS (1 << 29)
|
||||
#define FTGMAC030_RXDES0_LRS (1 << 28)
|
||||
#define FTGMAC030_RXDES0_PAUSE_FRAME (1 << 25)
|
||||
#define FTGMAC030_RXDES0_PAUSE_OPCODE (1 << 24)
|
||||
#define FTGMAC030_RXDES0_FIFO_FULL (1 << 23)
|
||||
#define FTGMAC030_RXDES0_RX_ODD_NB (1 << 22)
|
||||
#define FTGMAC030_RXDES0_RUNT (1 << 21)
|
||||
#define FTGMAC030_RXDES0_FTL (1 << 20)
|
||||
#define FTGMAC030_RXDES0_CRC_ERR (1 << 19)
|
||||
#define FTGMAC030_RXDES0_RX_ERR (1 << 18)
|
||||
#define FTGMAC030_RXDES0_BROADCAST (1 << 17)
|
||||
#define FTGMAC030_RXDES0_MULTICAST (1 << 16)
|
||||
#define FTGMAC030_RXDES0_EDORR (1 << 15)
|
||||
#define FTGMAC030_RXDES0_VDBC(x) ((x & 0x3fff) << 0)
|
||||
|
||||
#define FTGMAC030_RXDES1_IPCS_FAIL (1 << 27)
|
||||
#define FTGMAC030_RXDES1_UDPCS_FAIL (1 << 26)
|
||||
#define FTGMAC030_RXDES1_TCPCS_FAIL (1 << 25)
|
||||
#define FTGMAC030_RXDES1_VLAN_AVA (1 << 24)
|
||||
#define FTGMAC030_RXDES1_DF (1 << 23)
|
||||
#define FTGMAC030_RXDES1_LLC_PKT (1 << 22)
|
||||
#define FTGMAC030_RXDES1_PROTL_TYPE(x) ((x >> 20) & 0x3)
|
||||
#define FTGMAC030_RXDES1_IP6_TYPE(x) ((x >> 19) & 0x1)
|
||||
#define FTGMAC030_RXDES1_VLAN_PRIO(x) ((x & 7) << 13)
|
||||
#define FTGMAC030_RXDES1_VLAN_CFI(x) ((x & 1) << 12)
|
||||
#define FTGMAC030_RXDES1_VLAN_VID(x) (x & 0x0fff)
|
||||
#define FTGMAC030_RXDES1_VLAN_TAGC(x) (x & 0xffff)
|
||||
|
||||
#define FTGMAC030_RXDES1_PROT_MASK (0x3 << 20)
|
||||
#define FTGMAC030_RXDES1_PROTL_NOTIP 0
|
||||
#define FTGMAC030_RXDES1_PROTL_IP4 1
|
||||
#define FTGMAC030_RXDES1_PROTL_TCPIP 2
|
||||
#define FTGMAC030_RXDES1_PROTL_UDPIP 3
|
||||
|
||||
/* 0:MII or GMII,(don't suport) 1:RMII, 2: RGMII */
|
||||
enum ftgmac030_if_mode {
|
||||
FTGMAC030_MODE_MII_GMII,
|
||||
FTGMAC030_MODE_RMII,
|
||||
FTGMAC030_MODE_RGMII,
|
||||
};
|
||||
|
||||
int ftgmac030_mdio_read(struct eth_device *dev, int phy_addr, int regnum);
|
||||
int ftgmac030_mdio_write(struct eth_device *dev, int phy_addr, int regnum, u16 value);
|
||||
|
||||
#endif /* __FTGMAC030_H */
|
||||
@@ -0,0 +1,611 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
//#define DEBUG
|
||||
#include <config.h>
|
||||
#include <common.h>
|
||||
#include <malloc.h>
|
||||
#include <net.h>
|
||||
#include <wait_bit.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/mii.h>
|
||||
#include <linux/iopoll.h>
|
||||
#include <cpu_func.h>
|
||||
#include <miiphy.h>
|
||||
#include <hexdump.h>
|
||||
|
||||
#include "ftgmac030.h"
|
||||
#include "sys.h"
|
||||
|
||||
#define ETH_ZLEN 60
|
||||
|
||||
/* Receive Buffer Size Register - HW default is 0x640 */
|
||||
#define FTGMAC030_RBSR_DEFAULT_VALUE 0x640
|
||||
|
||||
/* PKTBUFSTX/PKTBUFSRX must both be power of 2 */
|
||||
#define FTGMAC030_PKTBUFSTX 4 /* must be power of 2 */
|
||||
#define FTGMAC030_PKTBUFSRX PKTBUFSRX /* must be power of 2 */
|
||||
|
||||
/* Timeout for transmit */
|
||||
#define FTGMAC030_TX_TIMEOUT_MS 1000
|
||||
|
||||
/* Timeout for a mdio read/write operation */
|
||||
#define FTGMAC030_MDIO_TIMEOUT_USEC 10000
|
||||
|
||||
#define MAC_LEN 6
|
||||
|
||||
struct ftgmac030_data {
|
||||
struct ftgmac030_txdes txdes[FTGMAC030_PKTBUFSTX] __aligned(ARCH_DMA_MINALIGN);
|
||||
struct ftgmac030_rxdes rxdes[FTGMAC030_PKTBUFSRX] __aligned(ARCH_DMA_MINALIGN);
|
||||
char rsvd1[0] __aligned(ARCH_DMA_MINALIGN);
|
||||
int tx_index;
|
||||
int rx_index;
|
||||
int phy_addr;
|
||||
enum ftgmac030_if_mode phy_intf;
|
||||
struct mii_dev *bus;
|
||||
struct phy_device *phydev;
|
||||
u32 max_speed;
|
||||
u8 mdc_cycthr;
|
||||
};
|
||||
|
||||
/*
|
||||
* struct mdio functions
|
||||
*/
|
||||
int ftgmac030_mdio_read(struct eth_device *dev, int phy_addr,
|
||||
int regnum)
|
||||
{
|
||||
struct ftgmac030 *ftgmac030 = (struct ftgmac030 *)dev->iobase;
|
||||
struct ftgmac030_data *priv = dev->priv;
|
||||
int phycr;
|
||||
int data;
|
||||
int ret;
|
||||
|
||||
phycr = FTGMAC030_PHYCR_PHYAD(phy_addr)
|
||||
| FTGMAC030_PHYCR_REGAD(regnum)
|
||||
| FTGMAC030_PHYCR_OP(FTGMAC030_MDIO_OP_RD)
|
||||
| FTGMAC030_PHYCR_SOF(FTGMAC030_MDIO_SOF)
|
||||
| FTGMAC030_PHYCR_PHYRD
|
||||
| priv->mdc_cycthr;
|
||||
|
||||
writel(phycr, &ftgmac030->phycr);
|
||||
|
||||
ret = readl_poll_timeout(&ftgmac030->phycr, phycr,
|
||||
!(phycr & FTGMAC030_PHYCR_PHYRD),
|
||||
FTGMAC030_MDIO_TIMEOUT_USEC);
|
||||
if (ret) {
|
||||
pr_err("mdio read failed (phy:%d reg:%x)\n", phy_addr, regnum);
|
||||
return ret;
|
||||
}
|
||||
|
||||
data = readl(&ftgmac030->phydata);
|
||||
|
||||
return FTGMAC030_PHYDATA_MIIRDATA(data);
|
||||
}
|
||||
|
||||
int ftgmac030_mdio_write(struct eth_device *dev, int phy_addr,
|
||||
int regnum, u16 value)
|
||||
{
|
||||
struct ftgmac030 *ftgmac030 = (struct ftgmac030 *)dev->iobase;
|
||||
struct ftgmac030_data *priv = dev->priv;
|
||||
int phycr;
|
||||
int data;
|
||||
int ret;
|
||||
|
||||
phycr = FTGMAC030_PHYCR_PHYAD(phy_addr)
|
||||
| FTGMAC030_PHYCR_REGAD(regnum)
|
||||
| FTGMAC030_PHYCR_OP(FTGMAC030_MDIO_OP_WR)
|
||||
| FTGMAC030_PHYCR_SOF(FTGMAC030_MDIO_SOF)
|
||||
| FTGMAC030_PHYCR_PHYWR
|
||||
| priv->mdc_cycthr;
|
||||
|
||||
data = FTGMAC030_PHYDATA_MIIWDATA(value);
|
||||
|
||||
writel(data, &ftgmac030->phydata);
|
||||
writel(phycr, &ftgmac030->phycr);
|
||||
|
||||
ret = readl_poll_timeout(&ftgmac030->phycr, phycr,
|
||||
!(phycr & FTGMAC030_PHYCR_PHYWR),
|
||||
FTGMAC030_MDIO_TIMEOUT_USEC);
|
||||
if (ret)
|
||||
pr_err("mdio write failed (phy:%d reg:%x)\n", phy_addr, regnum);
|
||||
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* MDIO Bus Interface */
|
||||
static int ftgmac030_mdiobus_read(struct mii_dev *bus, int addr, int devad, int reg)
|
||||
{
|
||||
struct eth_device *dev = (struct eth_device *)bus->priv;
|
||||
|
||||
return ftgmac030_mdio_read(dev, addr, reg);
|
||||
}
|
||||
|
||||
static int ftgmac030_mdiobus_write(struct mii_dev *bus, int addr, int devad,
|
||||
int reg, u16 value)
|
||||
{
|
||||
struct eth_device *dev = (struct eth_device *)bus->priv;
|
||||
|
||||
return ftgmac030_mdio_write(dev, addr, reg, value);
|
||||
}
|
||||
|
||||
static void ftgmac030_set_phy_intf(struct eth_device *dev)
|
||||
{
|
||||
struct ftgmac030 *ftgmac030 = (struct ftgmac030 *)dev->iobase;
|
||||
struct ftgmac030_data *priv = dev->priv;
|
||||
|
||||
priv->phy_intf = bspeth_get_phy_intf();
|
||||
|
||||
printf("%s (%s)\n", __func__, (priv->phy_intf == FTGMAC030_MODE_MII_GMII) ?
|
||||
"mii" : ((priv->phy_intf == FTGMAC030_MODE_RMII) ? "rmii" : "rgmii"));
|
||||
|
||||
writel(priv->phy_intf, &ftgmac030->gisr);
|
||||
|
||||
if (priv->phy_intf == FTGMAC030_MODE_RGMII) {
|
||||
/* Set rgmii tx delay for 2ns */
|
||||
bspeth_set_tx_delay();
|
||||
}
|
||||
|
||||
/* set the mdc cycthr sys_clk mpw:200M pliot:297*/
|
||||
priv->mdc_cycthr = 0xc8;
|
||||
|
||||
/* update max_speed*/
|
||||
priv->max_speed = (priv->phy_intf == FTGMAC030_MODE_RGMII) ?
|
||||
SPEED_1000 : SPEED_100;
|
||||
|
||||
/* update phy_intf to libphy */
|
||||
priv->phy_intf = (priv->phy_intf == FTGMAC030_MODE_MII_GMII) ?
|
||||
PHY_INTERFACE_MODE_MII : ((priv->phy_intf == FTGMAC030_MODE_RMII) ?
|
||||
PHY_INTERFACE_MODE_RMII :PHY_INTERFACE_MODE_RGMII_RXID);
|
||||
|
||||
}
|
||||
|
||||
int ftgmac030_mdiobus_init(struct eth_device *dev)
|
||||
{
|
||||
#if defined(CONFIG_MII) || defined(CONFIG_CMD_MII)
|
||||
struct ftgmac030_data *priv = dev->priv;
|
||||
struct mii_dev *bus = mdio_alloc();
|
||||
|
||||
if (!bus) {
|
||||
printf("Failed to allocate MDIO bus\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
bus->priv = dev;
|
||||
bus->read = ftgmac030_mdiobus_read;
|
||||
bus->write = ftgmac030_mdiobus_write;
|
||||
snprintf(bus->name, sizeof(bus->name), dev->name);
|
||||
|
||||
if (mdio_register(bus)) {
|
||||
mdio_free(bus);
|
||||
return -1;
|
||||
}
|
||||
|
||||
miiphy_set_current_dev(dev->name);
|
||||
|
||||
priv->bus = bus;
|
||||
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ftgmac030_phy_init(struct eth_device *dev)
|
||||
{
|
||||
int ret;
|
||||
struct ftgmac030_data *priv = dev->priv;
|
||||
struct phy_device *phydev;
|
||||
|
||||
int phy_addr;
|
||||
u32 phy_id;
|
||||
|
||||
/* Check if the PHY is up to snuff... */
|
||||
for (phy_addr = 0; phy_addr < 32; phy_addr++) {
|
||||
phy_id = ftgmac030_mdio_read(dev, phy_addr, MII_PHYSID1);
|
||||
/*
|
||||
* When it is unable to found PHY,
|
||||
* the interface usually return 0xffff or 0x0000
|
||||
*/
|
||||
if (phy_id != 0xffff && phy_id != 0x0) {
|
||||
|
||||
priv->phy_addr = phy_addr;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (phy_id == 0xffff || phy_id == 0x0) {
|
||||
printf("%s: no PHY present\n", dev->name);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
phy_id = ((phy_id & 0xffff) << 16) |
|
||||
ftgmac030_mdio_read(dev, phy_addr, MII_PHYSID2);
|
||||
|
||||
printf("%s: found PHY(0x%x) at 0x%02x\n",
|
||||
dev->name, phy_id, phy_addr);
|
||||
|
||||
phydev = phy_connect(priv->bus, priv->phy_addr, dev, priv->phy_intf);
|
||||
if (!phydev)
|
||||
return -ENODEV;
|
||||
|
||||
printf("%s connected to %s\n", dev->name, phydev->drv->name);
|
||||
|
||||
phydev->supported &= PHY_GBIT_FEATURES;
|
||||
|
||||
if (priv->max_speed) {
|
||||
ret = phy_set_supported(phydev, priv->max_speed);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
phydev->advertising = phydev->supported;
|
||||
priv->phydev = phydev;
|
||||
phy_config(phydev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ftgmac030_phy_link_speed(struct eth_device *dev)
|
||||
{
|
||||
struct ftgmac030 *ftgmac030 = (struct ftgmac030 *)dev->iobase;
|
||||
struct ftgmac030_data *priv = dev->priv;
|
||||
struct phy_device *phydev = priv->phydev;
|
||||
unsigned int maccr;
|
||||
|
||||
if (!phydev->link) {
|
||||
dev_err(phydev->dev, "No link\n");
|
||||
return -EREMOTEIO;
|
||||
}
|
||||
|
||||
/* read MAC control register and clear related bits */
|
||||
maccr = readl(&ftgmac030->maccr) &
|
||||
~(FTGMAC030_MACCR_SPEED_1000 |
|
||||
FTGMAC030_MACCR_SPEED_100 |
|
||||
FTGMAC030_MACCR_FULLDUP);
|
||||
|
||||
if (phy_interface_is_rgmii(phydev) && phydev->speed == 1000)
|
||||
maccr |= FTGMAC030_MACCR_SPEED_1000;
|
||||
|
||||
if (phydev->speed == 100)
|
||||
maccr |= FTGMAC030_MACCR_SPEED_100;
|
||||
|
||||
if (phydev->duplex)
|
||||
maccr |= FTGMAC030_MACCR_FULLDUP;
|
||||
|
||||
/* update MII config into maccr */
|
||||
writel(maccr, &ftgmac030->maccr);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Reset MAC
|
||||
*/
|
||||
static void ftgmac030_reset(struct eth_device *dev)
|
||||
{
|
||||
struct ftgmac030 *ftgmac030 = (struct ftgmac030 *)dev->iobase;
|
||||
|
||||
debug("%s()\n", __func__);
|
||||
|
||||
writel(FTGMAC030_MACCR_SW_RST, &ftgmac030->maccr);
|
||||
|
||||
while (readl(&ftgmac030->maccr) & FTGMAC030_MACCR_SW_RST)
|
||||
;
|
||||
}
|
||||
|
||||
/*
|
||||
* Set MAC address
|
||||
*/
|
||||
static void ftgmac030_set_mac(struct eth_device *dev,
|
||||
const unsigned char *mac)
|
||||
{
|
||||
struct ftgmac030 *ftgmac030 = (struct ftgmac030 *)dev->iobase;
|
||||
unsigned int maddr = mac[0] << 8 | mac[1];
|
||||
unsigned int laddr = mac[2] << 24 | mac[3] << 16 | mac[4] << 8 | mac[5];
|
||||
|
||||
debug("%s(%x %x)\n", __func__, maddr, laddr);
|
||||
|
||||
writel(maddr, &ftgmac030->mac_madr);
|
||||
writel(laddr, &ftgmac030->mac_ladr);
|
||||
}
|
||||
|
||||
static void ftgmac030_set_mac_from_env(struct eth_device *dev)
|
||||
{
|
||||
unsigned char mac[MAC_LEN];
|
||||
|
||||
memset(mac, 0, sizeof(mac));
|
||||
|
||||
if (!eth_env_get_enetaddr("ethaddr", mac)) {
|
||||
printf("MAC address invalid!\n");
|
||||
#ifdef CONFIG_NET_RANDOM_ETHADDR
|
||||
net_random_ethaddr(mac);
|
||||
printf("Set Random MAC address!\n");
|
||||
eth_env_set_enetaddr("ethaddr", mac);
|
||||
#endif
|
||||
}
|
||||
|
||||
memcpy(dev->enetaddr, mac, MAC_LEN);
|
||||
|
||||
ftgmac030_set_mac(dev, dev->enetaddr);
|
||||
}
|
||||
|
||||
/*
|
||||
* disable transmitter, receiver
|
||||
*/
|
||||
static void ftgmac030_halt(struct eth_device *dev)
|
||||
{
|
||||
struct ftgmac030 *ftgmac030 = (struct ftgmac030 *)dev->iobase;
|
||||
|
||||
debug("%s()\n", __func__);
|
||||
|
||||
writel(0, &ftgmac030->maccr);
|
||||
|
||||
bspeth_sys_exit();
|
||||
}
|
||||
|
||||
static int ftgmac030_init(struct eth_device *dev, bd_t *bd)
|
||||
{
|
||||
struct ftgmac030 *ftgmac030 = (struct ftgmac030 *)dev->iobase;
|
||||
struct ftgmac030_data *priv = dev->priv;
|
||||
struct phy_device *phydev = priv->phydev;
|
||||
unsigned int maccr;
|
||||
ulong start, end;
|
||||
int ret;
|
||||
int i;
|
||||
|
||||
debug("%s()\n", __func__);
|
||||
|
||||
if (phydev == NULL) {
|
||||
/* sys-func-sel */
|
||||
bspeth_sys_init();
|
||||
|
||||
/* set the MII interface */
|
||||
ftgmac030_set_phy_intf(dev);
|
||||
|
||||
ftgmac030_mdiobus_init(dev);
|
||||
|
||||
ret = ftgmac030_phy_init(dev);
|
||||
if (ret) {
|
||||
dev_err(dev, "Failed to initialize PHY\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
phydev = priv->phydev;
|
||||
}
|
||||
|
||||
ftgmac030_reset(dev);
|
||||
|
||||
/* set the ethernet address */
|
||||
ftgmac030_set_mac_from_env(dev);
|
||||
|
||||
/* disable all interrupts */
|
||||
writel(0, &ftgmac030->ier);
|
||||
|
||||
/* initialize descriptors */
|
||||
priv->tx_index = 0;
|
||||
priv->rx_index = 0;
|
||||
|
||||
for (i = 0; i < FTGMAC030_PKTBUFSTX; i++) {
|
||||
/* TXBUF_BADR */
|
||||
priv->txdes[i].txdes3 = 0;
|
||||
priv->txdes[i].txdes0 = 0;
|
||||
}
|
||||
priv->txdes[FTGMAC030_PKTBUFSTX - 1].txdes0 = FTGMAC030_TXDES0_EDOTR;
|
||||
|
||||
start = ((ulong)&priv->txdes[0]) & ~(ARCH_DMA_MINALIGN - 1);
|
||||
end = start + roundup(sizeof(priv->txdes), ARCH_DMA_MINALIGN);
|
||||
flush_dcache_range(start, end);
|
||||
|
||||
for (i = 0; i < FTGMAC030_PKTBUFSRX; i++) {
|
||||
/* RXBUF_BADR */
|
||||
priv->rxdes[i].rxdes3 = (unsigned int)net_rx_packets[i];
|
||||
priv->rxdes[i].rxdes0 = 0;
|
||||
}
|
||||
priv->rxdes[FTGMAC030_PKTBUFSRX - 1].rxdes0 = FTGMAC030_RXDES0_EDORR;
|
||||
|
||||
start = ((ulong)&priv->rxdes[0]) & ~(ARCH_DMA_MINALIGN - 1);
|
||||
end = start + roundup(sizeof(priv->rxdes), ARCH_DMA_MINALIGN);
|
||||
flush_dcache_range(start, end);
|
||||
|
||||
/* transmit ring */
|
||||
writel((unsigned int)priv->txdes, &ftgmac030->nptxdesc_addr);
|
||||
|
||||
/* receive ring */
|
||||
writel((unsigned int)priv->rxdes, &ftgmac030->rxdesc_addr);
|
||||
|
||||
/* poll receive descriptor automatically */
|
||||
writel(FTGMAC030_APTC_DEFAULT, &ftgmac030->aptc);
|
||||
|
||||
/* config receive buffer size register */
|
||||
writel(FTGMAC030_RBSR_RXBUF_SIZE(FTGMAC030_RBSR_DEFAULT_VALUE), &ftgmac030->rbsr);
|
||||
|
||||
/* enable transmitter, receiver */
|
||||
maccr = FTGMAC030_MACCR_DEFAULT;
|
||||
writel(maccr, &ftgmac030->maccr);
|
||||
|
||||
|
||||
ret = phy_startup(phydev);
|
||||
if (ret) {
|
||||
dev_err(phydev->dev, "Could not start PHY\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = ftgmac030_phy_link_speed(dev);
|
||||
if (ret) {
|
||||
dev_err(phydev->dev, "Could not adjust link\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
printf("%s: link up, %d Mbps %s-duplex\n", phydev->dev->name,
|
||||
phydev->speed, phydev->duplex ? "full" : "half");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Get a data block via Ethernet
|
||||
*/
|
||||
static int ftgmac030_recv(struct eth_device *dev)
|
||||
{
|
||||
struct ftgmac030_data *priv = dev->priv;
|
||||
struct ftgmac030_rxdes *curr_des = &priv->rxdes[priv->rx_index];
|
||||
unsigned short rxlen;
|
||||
ulong des_start = ((ulong)curr_des) & ~(ARCH_DMA_MINALIGN - 1);
|
||||
ulong des_end = des_start +
|
||||
roundup(sizeof(*curr_des), ARCH_DMA_MINALIGN);
|
||||
ulong data_start = curr_des->rxdes3;
|
||||
ulong data_end;
|
||||
|
||||
invalidate_dcache_range(des_start, des_end);
|
||||
|
||||
if (!(curr_des->rxdes0 & FTGMAC030_RXDES0_RXPKT_RDY))
|
||||
return -EAGAIN;
|
||||
|
||||
if (curr_des->rxdes0 & (FTGMAC030_RXDES0_RX_ERR |
|
||||
FTGMAC030_RXDES0_CRC_ERR |
|
||||
FTGMAC030_RXDES0_FTL |
|
||||
FTGMAC030_RXDES0_RUNT |
|
||||
FTGMAC030_RXDES0_RX_ODD_NB)) {
|
||||
if (curr_des->rxdes0 & FTGMAC030_RXDES0_CRC_ERR) {
|
||||
genphy_restart_aneg(priv->phydev);
|
||||
genphy_update_link(priv->phydev);
|
||||
}
|
||||
return -EAGAIN;
|
||||
}
|
||||
|
||||
rxlen = FTGMAC030_RXDES0_VDBC(curr_des->rxdes0);
|
||||
|
||||
debug("%s(): RX buffer %d, %x received(%x)\n",
|
||||
__func__, priv->rx_index, rxlen, curr_des->rxdes3);
|
||||
|
||||
/* Invalidate received data */
|
||||
data_end = data_start + roundup(rxlen, ARCH_DMA_MINALIGN);
|
||||
invalidate_dcache_range(data_start, data_end);
|
||||
|
||||
/* pass the packet up to the protocol layers. */
|
||||
net_process_received_packet((void *)curr_des->rxdes3, rxlen);
|
||||
|
||||
/* Release buffer to DMA*/
|
||||
curr_des->rxdes0 &= FTGMAC030_RXDES0_EDORR;
|
||||
curr_des->rxdes1 = 0x0;
|
||||
flush_dcache_range(des_start, des_end);
|
||||
|
||||
/* Move to next descriptor */
|
||||
priv->rx_index = (priv->rx_index + 1) % FTGMAC030_PKTBUFSRX;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static u32 ftgmac030_read_txdesc(const void *desc)
|
||||
{
|
||||
const struct ftgmac030_txdes *txdes = desc;
|
||||
ulong des_start = ((ulong)txdes) & ~(ARCH_DMA_MINALIGN - 1);
|
||||
ulong des_end = des_start +
|
||||
roundup(sizeof(*txdes), ARCH_DMA_MINALIGN);
|
||||
|
||||
invalidate_dcache_range(des_start, des_end);
|
||||
|
||||
return txdes->txdes0;
|
||||
}
|
||||
|
||||
BUILD_WAIT_FOR_BIT(ftgmac030_txdone, u32, ftgmac030_read_txdesc)
|
||||
|
||||
/*
|
||||
* Send a data block via Ethernet
|
||||
*/
|
||||
static int ftgmac030_send(struct eth_device *dev, void *packet, int length)
|
||||
{
|
||||
struct ftgmac030_data *priv = dev->priv;
|
||||
struct ftgmac030 *ftgmac030 = (struct ftgmac030 *)dev->iobase;
|
||||
struct ftgmac030_txdes *curr_des = &priv->txdes[priv->tx_index];
|
||||
ulong des_start = ((ulong)curr_des) & ~(ARCH_DMA_MINALIGN - 1);
|
||||
ulong des_end = des_start +
|
||||
roundup(sizeof(*curr_des), ARCH_DMA_MINALIGN);
|
||||
ulong data_start;
|
||||
ulong data_end;
|
||||
int rc;
|
||||
|
||||
invalidate_dcache_range(des_start, des_end);
|
||||
|
||||
if (curr_des->txdes0 & FTGMAC030_TXDES0_TXDMA_OWN) {
|
||||
debug("%s(): no TX descriptor available\n", __func__);
|
||||
return -1;
|
||||
}
|
||||
|
||||
debug("%s(%x, %x)\n", __func__, (int)packet, length);
|
||||
|
||||
length = (length < ETH_ZLEN) ? ETH_ZLEN : length;
|
||||
|
||||
/* initiate a transmit sequence */
|
||||
curr_des->txdes3 = (unsigned int)packet; /* TXBUF_BADR */
|
||||
|
||||
/* Flush data to be sent */
|
||||
data_start = curr_des->txdes3;
|
||||
data_end = data_start + roundup(length, ARCH_DMA_MINALIGN);
|
||||
flush_dcache_range(data_start, data_end);
|
||||
|
||||
/* only one descriptor on TXBUF */
|
||||
curr_des->txdes0 &= FTGMAC030_TXDES0_EDOTR;
|
||||
curr_des->txdes0 |= FTGMAC030_TXDES0_FTS |
|
||||
FTGMAC030_TXDES0_LTS |
|
||||
FTGMAC030_TXDES0_BUF_SIZE(length) |
|
||||
FTGMAC030_TXDES0_TXDMA_OWN;
|
||||
|
||||
/* Flush modified buffer descriptor */
|
||||
flush_dcache_range(des_start, des_end);
|
||||
|
||||
/* start transmit */
|
||||
writel(1, &ftgmac030->nptxpd);
|
||||
|
||||
/* wait for transfer to succeed */
|
||||
rc = wait_for_bit_ftgmac030_txdone(curr_des,
|
||||
FTGMAC030_TXDES0_TXDMA_OWN, false,
|
||||
FTGMAC030_TX_TIMEOUT_MS, true);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
debug("%s(): packet sent\n", __func__);
|
||||
|
||||
/* Move to next descriptor */
|
||||
priv->tx_index = (priv->tx_index + 1) % FTGMAC030_PKTBUFSTX;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ftgmac030_initialize(bd_t *bd)
|
||||
{
|
||||
struct eth_device *dev;
|
||||
struct ftgmac030_data *priv;
|
||||
|
||||
dev = malloc(sizeof(*dev));
|
||||
if (!dev) {
|
||||
printf("%s(): failed to allocate dev\n", __func__);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Transmit and receive descriptors should align to 16 bytes */
|
||||
priv = (struct ftgmac030_data *)noncached_alloc(sizeof(struct ftgmac030_data),
|
||||
ARCH_DMA_MINALIGN);
|
||||
if (!priv) {
|
||||
printf("%s(): failed to allocate priv\n", __func__);
|
||||
goto free_dev;
|
||||
}
|
||||
|
||||
memset(dev, 0, sizeof(*dev));
|
||||
memset(priv, 0, sizeof(*priv));
|
||||
|
||||
sprintf(dev->name, "FTGMAC030");
|
||||
dev->iobase = GMAC_REG_BASE;
|
||||
dev->init = ftgmac030_init;
|
||||
dev->halt = ftgmac030_halt;
|
||||
dev->send = ftgmac030_send;
|
||||
dev->recv = ftgmac030_recv;
|
||||
dev->priv = priv;
|
||||
|
||||
eth_register(dev);
|
||||
|
||||
return 1;
|
||||
|
||||
free_dev:
|
||||
free(dev);
|
||||
out:
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,106 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
|
||||
#include <common.h>
|
||||
#include <linux/bitops.h>
|
||||
#include <phy.h>
|
||||
|
||||
#define MIIM_ICPLUS_PAGE_SELECT 0x14
|
||||
|
||||
static int icplus_phy_extread(struct phy_device *phydev, int addr,
|
||||
int devaddr, int regnum)
|
||||
{
|
||||
int oldpage = phy_read(phydev, MDIO_DEVAD_NONE,
|
||||
MIIM_ICPLUS_PAGE_SELECT);
|
||||
int val;
|
||||
|
||||
phy_write(phydev, MDIO_DEVAD_NONE, MIIM_ICPLUS_PAGE_SELECT, devaddr);
|
||||
val = phy_read(phydev, MDIO_DEVAD_NONE, regnum);
|
||||
phy_write(phydev, MDIO_DEVAD_NONE, MIIM_ICPLUS_PAGE_SELECT, oldpage);
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
static int icplus_phy_extwrite(struct phy_device *phydev, int addr,
|
||||
int devaddr, int regnum, u16 val)
|
||||
{
|
||||
int oldpage = phy_read(phydev, MDIO_DEVAD_NONE,
|
||||
MIIM_ICPLUS_PAGE_SELECT);
|
||||
|
||||
phy_write(phydev, MDIO_DEVAD_NONE, MIIM_ICPLUS_PAGE_SELECT, devaddr);
|
||||
phy_write(phydev, MDIO_DEVAD_NONE, regnum, val);
|
||||
phy_write(phydev, MDIO_DEVAD_NONE, MIIM_ICPLUS_PAGE_SELECT, oldpage);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int icplus_config(struct phy_device *phydev)
|
||||
{
|
||||
unsigned int reg;
|
||||
|
||||
/* Digital IO Pin Driving Control Register */
|
||||
|
||||
/*RXC DRIVE*/
|
||||
reg = phydev->drv->readext(phydev, 0, 4, 22);
|
||||
reg &= ~(0x7 << 13);
|
||||
#ifdef CONFIG_LOTUS_FPGA
|
||||
reg |= (0x6 << 13);
|
||||
#else
|
||||
reg |= (0x4 << 13);
|
||||
#endif
|
||||
phydev->drv->writeext(phydev, 0, 4, 22, reg);
|
||||
|
||||
/*RXDx DRIVE*/
|
||||
reg = phydev->drv->readext(phydev, 0, 16, 26);
|
||||
reg &= ~(0x7FFF);
|
||||
#ifdef CONFIG_LOTUS_FPGA
|
||||
reg |= (0x6DB6);
|
||||
#else
|
||||
reg |= (0x36DB);
|
||||
#endif
|
||||
phydev->drv->writeext(phydev, 0, 16, 26, reg);
|
||||
|
||||
/*RXDx DRIVE*/
|
||||
reg = phydev->drv->readext(phydev, 0, 16, 27);
|
||||
reg &= ~(0x7FC7);
|
||||
#ifdef CONFIG_LOTUS_FPGA
|
||||
reg |= (0x6D86);
|
||||
#else
|
||||
reg |= (0x2484);
|
||||
#endif
|
||||
phydev->drv->writeext(phydev, 0, 16, 27, reg);
|
||||
|
||||
genphy_config_aneg(phydev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int icplus_startup(struct phy_device *phydev)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = genphy_update_link(phydev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return genphy_parse_link(phydev);
|
||||
}
|
||||
|
||||
/* Support for JL11x1 PHY */
|
||||
static struct phy_driver icplus_driver = {
|
||||
.name = "ICPlus IP101A/G",
|
||||
.uid = 0x02430c54,
|
||||
.mask = 0x0ffffff0,
|
||||
.features = PHY_BASIC_FEATURES,
|
||||
.config = &icplus_config,
|
||||
.startup = &icplus_startup,
|
||||
.shutdown = &genphy_shutdown,
|
||||
.readext = &icplus_phy_extread,
|
||||
.writeext = &icplus_phy_extwrite,
|
||||
};
|
||||
|
||||
int phy_icplus_init(void)
|
||||
{
|
||||
phy_register(&icplus_driver);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,91 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
|
||||
#include <common.h>
|
||||
#include <linux/bitops.h>
|
||||
#include <phy.h>
|
||||
|
||||
#define MIIM_JL11X1_PAGE_SELECT 0x1f
|
||||
|
||||
static int jl11x1_phy_extread(struct phy_device *phydev, int addr,
|
||||
int devaddr, int regnum)
|
||||
{
|
||||
int oldpage = phy_read(phydev, MDIO_DEVAD_NONE,
|
||||
MIIM_JL11X1_PAGE_SELECT);
|
||||
int val;
|
||||
|
||||
phy_write(phydev, MDIO_DEVAD_NONE, MIIM_JL11X1_PAGE_SELECT, devaddr);
|
||||
val = phy_read(phydev, MDIO_DEVAD_NONE, regnum);
|
||||
phy_write(phydev, MDIO_DEVAD_NONE, MIIM_JL11X1_PAGE_SELECT, oldpage);
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
static int jl11x1_phy_extwrite(struct phy_device *phydev, int addr,
|
||||
int devaddr, int regnum, u16 val)
|
||||
{
|
||||
int oldpage = phy_read(phydev, MDIO_DEVAD_NONE,
|
||||
MIIM_JL11X1_PAGE_SELECT);
|
||||
|
||||
phy_write(phydev, MDIO_DEVAD_NONE, MIIM_JL11X1_PAGE_SELECT, devaddr);
|
||||
phy_write(phydev, MDIO_DEVAD_NONE, regnum, val);
|
||||
phy_write(phydev, MDIO_DEVAD_NONE, MIIM_JL11X1_PAGE_SELECT, oldpage);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int jl11x1_config(struct phy_device *phydev)
|
||||
{
|
||||
/* Set green LED for Link, yellow LED for Active */
|
||||
phydev->drv->writeext(phydev, 0, 7, 19, 0x00);
|
||||
|
||||
genphy_config_aneg(phydev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int jl11x1_startup(struct phy_device *phydev)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = genphy_update_link(phydev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = genphy_parse_link(phydev);
|
||||
|
||||
if (phydev->speed == SPEED_100) {
|
||||
unsigned int reg;
|
||||
|
||||
reg = phydev->drv->readext(phydev, 0, 24, 24);
|
||||
reg &= ~(0x7 << 10);
|
||||
reg |= (0x5 << 10);
|
||||
phydev->drv->writeext(phydev, 0, 24, 24, reg);
|
||||
// set page 7 reg 16 tx 3
|
||||
reg = phydev->drv->readext(phydev, 0, 7, 16);
|
||||
reg &= ~(0xf << 8);
|
||||
reg |= (0x3 << 8);
|
||||
phydev->drv->writeext(phydev, 0, 7, 16, reg);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Support for JL11x1 PHY */
|
||||
static struct phy_driver JL11x1_driver = {
|
||||
.name = "JLSemi JL11x1",
|
||||
.uid = 0x937c4024,
|
||||
.mask = 0xffffffff,
|
||||
.features = PHY_BASIC_FEATURES,
|
||||
.config = &jl11x1_config,
|
||||
.startup = &jl11x1_startup,
|
||||
.shutdown = &genphy_shutdown,
|
||||
.readext = &jl11x1_phy_extread,
|
||||
.writeext = &jl11x1_phy_extwrite,
|
||||
};
|
||||
|
||||
int phy_jlsemi_init(void)
|
||||
{
|
||||
phy_register(&JL11x1_driver);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,81 @@
|
||||
/*
|
||||
* Copyright (c) LOTUS. All rights reserved.
|
||||
*/
|
||||
//#define DEBUG
|
||||
#include <config.h>
|
||||
#include <common.h>
|
||||
#include <malloc.h>
|
||||
#include <net.h>
|
||||
#include <wait_bit.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/mii.h>
|
||||
#include <linux/iopoll.h>
|
||||
#include <cpu_func.h>
|
||||
#include <miiphy.h>
|
||||
#include <hexdump.h>
|
||||
|
||||
#include "ftgmac030.h"
|
||||
#include "marvell_phy.h"
|
||||
|
||||
static int m88e1116r_config_init(struct eth_device *dev, int phy_addr)
|
||||
{
|
||||
int temp;
|
||||
int err;
|
||||
|
||||
temp = ftgmac030_mdio_read(dev, phy_addr, MII_BMCR);
|
||||
temp |= BMCR_RESET;
|
||||
err = ftgmac030_mdio_write(dev, phy_addr, MII_BMCR, temp);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
mdelay(500);
|
||||
|
||||
err = ftgmac030_mdio_write(dev, phy_addr, MII_MARVELL_PHY_PAGE, 0);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
temp = ftgmac030_mdio_read(dev, phy_addr, MII_M1011_PHY_SCR);
|
||||
temp |= (7 << 12); /* max number of gigabit attempts */
|
||||
temp |= (1 << 11); /* enable downshift */
|
||||
temp |= MII_M1011_PHY_SCR_AUTO_CROSS;
|
||||
|
||||
debug("(%s) REG_PHY(%d) = 0x%x\n", __func__, MII_M1011_PHY_SCR, temp);
|
||||
|
||||
err = ftgmac030_mdio_write(dev, phy_addr, MII_M1011_PHY_SCR, temp);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
err = ftgmac030_mdio_write(dev, phy_addr, MII_MARVELL_PHY_PAGE, 2);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
temp = ftgmac030_mdio_read(dev, phy_addr, MII_M1116R_CONTROL_REG_MAC);
|
||||
temp &= ~(1 << 5);/*care is bit*/
|
||||
temp |= (1 << 4);
|
||||
|
||||
debug("(%s) REG_PHY(%d) = 0x%x\n", __func__, MII_M1116R_CONTROL_REG_MAC, temp);
|
||||
|
||||
err = ftgmac030_mdio_write(dev, phy_addr, MII_M1116R_CONTROL_REG_MAC, temp);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
err = ftgmac030_mdio_write(dev, phy_addr, MII_MARVELL_PHY_PAGE, 0);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
temp = ftgmac030_mdio_read(dev, phy_addr, MII_BMCR);
|
||||
temp |= BMCR_RESET;
|
||||
err = ftgmac030_mdio_write(dev, phy_addr, MII_BMCR, temp);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
mdelay(500);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int marvell_phy_init(struct eth_device *dev, int phy_addr)
|
||||
{
|
||||
return m88e1116r_config_init(dev, phy_addr);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
/*
|
||||
* Copyright (c) LOTUS. All rights reserved.
|
||||
*/
|
||||
|
||||
#ifndef __MARVELL_PHY_H
|
||||
#define __MARVELL_PHY_H
|
||||
|
||||
#define PHY_MARVELL 0X141
|
||||
|
||||
#define MII_MARVELL_PHY_PAGE 22
|
||||
|
||||
#define MII_M1011_PHY_SCR 0x10
|
||||
#define MII_M1011_PHY_SCR_MDI 0x0000
|
||||
#define MII_M1011_PHY_SCR_MDI_X 0x0020
|
||||
#define MII_M1011_PHY_SCR_AUTO_CROSS 0x0060
|
||||
|
||||
#define MII_M1116R_CONTROL_REG_MAC 21
|
||||
|
||||
int marvell_phy_init(struct eth_device *dev, int phy_addr);
|
||||
|
||||
#endif /* __MARVELL_PHY_H */
|
||||
@@ -0,0 +1,96 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
//#define DEBUG
|
||||
#include <config.h>
|
||||
#include <common.h>
|
||||
#include <malloc.h>
|
||||
#include <net.h>
|
||||
#include <wait_bit.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/mii.h>
|
||||
#include <linux/iopoll.h>
|
||||
#include <cpu_func.h>
|
||||
#include <miiphy.h>
|
||||
#include <hexdump.h>
|
||||
|
||||
#include "ftgmac030.h"
|
||||
#include "realtek_phy.h"
|
||||
|
||||
static int rtl821x_write_page(struct eth_device *dev, int phy_addr, int page)
|
||||
{
|
||||
return ftgmac030_mdio_write(dev, phy_addr, RTL821x_PAGE_SELECT, page);
|
||||
}
|
||||
|
||||
static int rtl8211f_config_init(struct eth_device *dev, int phy_addr)
|
||||
{
|
||||
int temp;
|
||||
int err;
|
||||
|
||||
/* change page to 0xa43 */
|
||||
err = rtl821x_write_page(dev, phy_addr, 0xa43);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
/* config page 0xa43 offset 0x18 */
|
||||
temp = ftgmac030_mdio_read(dev, phy_addr, RTL8211F_PHYCR1);
|
||||
temp |= RTL8211F_ALDPS_ENABLE | RTL8211F_ALDPS_PLL_OFF | RTL8211F_ALDPS_XTAL_OFF;
|
||||
|
||||
debug("(%s) REG_PHY(%d) = 0x%x\n", __func__, RTL8211F_PHYCR1, temp);
|
||||
|
||||
err = ftgmac030_mdio_write(dev, phy_addr, RTL8211F_PHYCR1, temp);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
/* change page to 0xd08 */
|
||||
err = rtl821x_write_page(dev, phy_addr, 0xd08);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
/* config page 0xd08 offset 0x11 */
|
||||
temp = ftgmac030_mdio_read(dev, phy_addr, 0x11);
|
||||
|
||||
temp |= RTL8211F_TX_DELAY;
|
||||
debug("(%s) REG_PHY(%d) = 0x%x\n", __func__, 0x11, temp);
|
||||
|
||||
err = ftgmac030_mdio_write(dev, phy_addr, 0x11, temp);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
/* config page 0xd08 offset 0x15 */
|
||||
temp = ftgmac030_mdio_read(dev, phy_addr, 0x15);
|
||||
|
||||
temp |= RTL8211F_RX_DELAY;
|
||||
debug("(%s) REG_PHY(%d) = 0x%x\n", __func__, 0x15, temp);
|
||||
|
||||
err = ftgmac030_mdio_write(dev, phy_addr, 0x15, temp);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
/* change page to 0x0 */
|
||||
err = rtl821x_write_page(dev, phy_addr, 0);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int realtek_phy_init(struct eth_device *dev, int phy_addr)
|
||||
{
|
||||
int err = 0;
|
||||
u16 phy_id;
|
||||
|
||||
phy_id = ftgmac030_mdio_read(dev, phy_addr, MII_PHYSID2);
|
||||
|
||||
debug("(%s) REG_PHY(%d) = 0x%x\n", __func__, MII_PHYSID2, phy_id);
|
||||
|
||||
switch (phy_id) {
|
||||
case PHY_RTL8211F:
|
||||
err = rtl8211f_config_init(dev, phy_addr);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
|
||||
#ifndef __REALTEK_PHY_H
|
||||
#define __REALTEK_PHY_H
|
||||
|
||||
#define PHY_REALTEK 0X1c
|
||||
|
||||
#define PHY_RTL8211F 0Xc916
|
||||
|
||||
#define RTL821x_PAGE_SELECT 0x1f
|
||||
|
||||
#define RTL8211F_PHYCR1 0x18
|
||||
|
||||
#define RTL8211F_MDI_MODE BIT(8)
|
||||
#define RTL8211F_MDI_MODE_EN BIT(9)
|
||||
|
||||
#define RTL8211F_TX_DELAY BIT(8)
|
||||
#define RTL8211F_RX_DELAY BIT(3)
|
||||
|
||||
#define RTL8211F_ALDPS_PLL_OFF BIT(1)
|
||||
#define RTL8211F_ALDPS_ENABLE BIT(2)
|
||||
#define RTL8211F_ALDPS_XTAL_OFF BIT(12)
|
||||
|
||||
int realtek_phy_init(struct eth_device *dev, int phy_addr);
|
||||
|
||||
#endif /* __REALTEK_PHY_H */
|
||||
@@ -0,0 +1,174 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
|
||||
#include <config.h>
|
||||
#include "bspeth.h"
|
||||
#include "sys.h"
|
||||
#include "ftgmac030.h"
|
||||
|
||||
static void bspeth_reset(int rst)
|
||||
{
|
||||
u32 val;
|
||||
|
||||
val = _readl(BSPETH_MAC_CRG_REG);
|
||||
if (rst)
|
||||
val |= ETH_SOFT_RESET;
|
||||
else
|
||||
val &= ~ETH_SOFT_RESET;
|
||||
|
||||
_writel(val, BSPETH_MAC_CRG_REG);
|
||||
|
||||
udelay(100); /* delay 100us */
|
||||
|
||||
}
|
||||
|
||||
static inline void bspeth_clk_ena(void)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
static inline void bspeth_clk_dis(void)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
static void bspeth_reset_internal_phy(void)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
static void bspeth_reset_external_phy_by_crg(void)
|
||||
{
|
||||
u32 v;
|
||||
u32 phy_reset_n;
|
||||
|
||||
/* bit[1]*/
|
||||
phy_reset_n = readl(BSPETH_CFG_CRG_REG);
|
||||
phy_reset_n &= ETH_PHY_RST_N;
|
||||
|
||||
/************************************************/
|
||||
/* reset external phy with default reset pin */
|
||||
v = readl(BSPETH_PHY_CRG_REG);
|
||||
|
||||
if (phy_reset_n)
|
||||
v |= ETH_EXTERNAL_PHY_RESET;
|
||||
else
|
||||
v &= ~ETH_EXTERNAL_PHY_RESET;
|
||||
|
||||
writel(v, BSPETH_PHY_CRG_REG);
|
||||
mdelay(10); /* delay 10ms */
|
||||
|
||||
/* then, cancel reset, and should delay some time */
|
||||
v = readl(BSPETH_PHY_CRG_REG);
|
||||
|
||||
if (phy_reset_n)
|
||||
v &= ~ETH_EXTERNAL_PHY_RESET;
|
||||
else
|
||||
v |= ETH_EXTERNAL_PHY_RESET;
|
||||
|
||||
writel(v, BSPETH_PHY_CRG_REG);
|
||||
|
||||
mdelay(20); /* delay 20ms */
|
||||
|
||||
/* then, cancel reset, and should delay some time */
|
||||
v = readl(BSPETH_PHY_CRG_REG);
|
||||
|
||||
if (phy_reset_n)
|
||||
v |= ETH_EXTERNAL_PHY_RESET;
|
||||
else
|
||||
v &= ~ETH_EXTERNAL_PHY_RESET;
|
||||
|
||||
writel(v, BSPETH_PHY_CRG_REG);
|
||||
|
||||
mdelay(150); /* delay 150ms */
|
||||
|
||||
}
|
||||
|
||||
static void bspeth_reset_external_phy_by_gpio(void)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
static void bspeth_phy_reset(void)
|
||||
{
|
||||
bspeth_reset_internal_phy();
|
||||
bspeth_reset_external_phy_by_crg();
|
||||
bspeth_reset_external_phy_by_gpio();
|
||||
}
|
||||
|
||||
static void bspeth_funsel_config(void)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
static void bspeth_funsel_restore(void)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
||||
///**************************************************/
|
||||
//void bspeth_sys_startup(void)
|
||||
//{
|
||||
// bspeth_clk_ena();
|
||||
// /* undo reset */
|
||||
// bspeth_reset(0);
|
||||
//}
|
||||
|
||||
void bspeth_sys_allstop(void)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
u32 bspeth_get_phy_intf(void)
|
||||
{
|
||||
u32 phy_intf;
|
||||
|
||||
/* bit[0]: 0 -- rmii,1 -- rgmii bit[3]:1 --mii or gmii*/
|
||||
phy_intf = readl(BSPETH_CFG_CRG_REG);
|
||||
phy_intf &= ETH_PHY_INTF;
|
||||
|
||||
if (phy_intf & bit(3))
|
||||
return FTGMAC030_MODE_MII_GMII;
|
||||
else if (phy_intf & bit(0))
|
||||
return FTGMAC030_MODE_RGMII;
|
||||
else
|
||||
return FTGMAC030_MODE_RMII;
|
||||
}
|
||||
|
||||
/* Set rgmii tx delay for 2ns */
|
||||
void bspeth_set_tx_delay(void)
|
||||
{
|
||||
u32 regval;
|
||||
u32 timeout = 100;//1ms
|
||||
|
||||
regval = readl(BSPETH_DLY_CRG_REG);
|
||||
|
||||
if(!(regval & ETH_DLY_SEL)) {
|
||||
writel(regval | ETH_DLY_SEL, BSPETH_DLY_CRG_REG);
|
||||
|
||||
do {
|
||||
udelay(10);
|
||||
regval = readl(BSPETH_DLY_STAT_REG);
|
||||
--timeout;
|
||||
} while ((!(regval & ETH_DLY_STAT) && timeout));
|
||||
}
|
||||
|
||||
if(!timeout)
|
||||
printf("Error: bspeth_set_tx_delay timeout!\n");
|
||||
}
|
||||
|
||||
void bspeth_sys_init(void)
|
||||
{
|
||||
bspeth_funsel_config();
|
||||
bspeth_sys_allstop();
|
||||
bspeth_clk_ena();
|
||||
bspeth_reset(1);
|
||||
bspeth_reset(0);
|
||||
bspeth_phy_reset();
|
||||
}
|
||||
|
||||
void bspeth_sys_exit(void)
|
||||
{
|
||||
bspeth_funsel_restore();
|
||||
bspeth_sys_allstop();
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0+ */
|
||||
|
||||
#ifndef __BSPETH_SYS_H__
|
||||
#define __BSPETH_SYS_H__
|
||||
|
||||
void bspeth_sys_init(void);
|
||||
void bspeth_sys_exit(void);
|
||||
|
||||
//void bspeth_sys_startup(void);
|
||||
//void bspeth_sys_allstop(void);
|
||||
|
||||
u32 bspeth_get_phy_intf(void);
|
||||
void bspeth_set_tx_delay(void);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,26 @@
|
||||
|
||||
menuconfig LOTUS_USB
|
||||
bool "LOTUS USB support"
|
||||
default y
|
||||
select CMD_USB
|
||||
select USB
|
||||
help
|
||||
LOTUS USB support.
|
||||
|
||||
if LOTUS_USB
|
||||
config USB_PWREN_OFF_DELAY
|
||||
int "usb port power off delay(ms)"
|
||||
default 100
|
||||
help
|
||||
Some usb device can't be recognize in U-boot because of error state, so
|
||||
we power off them and wait a delay to restart them, we call the delay
|
||||
as USB_PWREN_OFF_DELAY.
|
||||
|
||||
config USB_DUAL_PORT_SUPPORT
|
||||
bool "support dual-usb-port(U2 & U3) in host mode"
|
||||
default n
|
||||
help
|
||||
Some lotus chips support 2 usb host ports, contains U2 and U3. If your
|
||||
chip support 2 ports, enable this config could recognize U3 and U2 dev
|
||||
meanwhile, but cause to U3 couldn't be recognized sometimes.
|
||||
endif
|
||||
@@ -0,0 +1,6 @@
|
||||
|
||||
obj-y += phy/
|
||||
|
||||
ccflags-y += -I$(srctree)/cmd
|
||||
|
||||
obj-$(CONFIG_USB_GADGET)+= udc3/
|
||||
@@ -0,0 +1,2 @@
|
||||
obj-$(CONFIG_TARGET_XMFALCON) += phy-xmfalcon-usb.o
|
||||
obj-$(CONFIG_TARGET_XMORCA) += phy-xmorca-usb.o
|
||||
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Copyright (c) LOTUS. All rights reserved.
|
||||
*/
|
||||
|
||||
#ifndef PHY_LOTUS_USB
|
||||
#define PHY_LOTUS_USB
|
||||
|
||||
#define U_LEVEL1 10
|
||||
#define U_LEVEL2 20
|
||||
#define U_LEVEL3 30
|
||||
#define U_LEVEL4 50
|
||||
#define U_LEVEL5 100
|
||||
#define U_LEVEL6 200
|
||||
#define U_LEVEL7 300
|
||||
#define U_LEVEL8 500
|
||||
#define U_LEVEL9 1000
|
||||
#define U_LEVEL10 2000
|
||||
|
||||
enum usb_phy_width_mode {
|
||||
PHY_WIDTH_8BIT = 0,
|
||||
PHY_WIDTH_16BIT,
|
||||
};
|
||||
|
||||
enum usb_scenes {
|
||||
/* U3 CTRL connect U3/U2 PHY */
|
||||
LOTUS_USB_SCENE2 = 0,
|
||||
/* U3 CTRL connect U3 PHY, U2 CTRL connect U2 PHY */
|
||||
LOTUS_USB_SCENE1 = 1,
|
||||
};
|
||||
|
||||
#endif // PHY_LOTUS_USB
|
||||
@@ -0,0 +1,699 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Copyright (c) LOTUS. All rights reserved.
|
||||
*/
|
||||
#include <asm/arch/platform.h>
|
||||
#include <dm.h>
|
||||
#include <linux/lotus/usb.h>
|
||||
#include <usb.h>
|
||||
#include <usb/xhci.h>
|
||||
#include <linux/lotus/chip.h>
|
||||
|
||||
#include "phy-usb.h"
|
||||
|
||||
#ifdef __USB_DEBUG
|
||||
#define USB_DEBUG(...) printf(__VA_ARGS__)
|
||||
#else
|
||||
#define USB_DEBUG(...)
|
||||
#endif
|
||||
|
||||
/* CRG PERI_CRG80: usb clk & reset, offset 0x140 */
|
||||
#define USB2_CTRL 0x140
|
||||
#define OUTER_PHY_CRG_DEF_VAL 0x130d
|
||||
#define LOTUS_PHY_CRG_DEF_VAL 0x3130d
|
||||
#define LOTUS_PHY_REF_CKEN (0x1 << 17)
|
||||
#define LOTUS_PHY_APB_CKEN (0x1 << 16)
|
||||
#define USB2_UTMI_CKEN (0x1 << 12)
|
||||
#define USB2_PHY_APB_CKEN (0x1 << 11)
|
||||
#define USB2_REF_CKEN (0x1 << 9)
|
||||
#define USB2_BUS_CKEN (0x1 << 8)
|
||||
#define USB2_PHY_PLL_CKEN (0x1 << 4)
|
||||
#define USB2_PHY_XTAL_CKEN (0x1 << 2)
|
||||
#define USB2_FREECLK_CKSEL (0x1 << 13)
|
||||
#define USB2_PHY_APB_RST (0x1 << 10)
|
||||
#define USB2_VCC_SRST_REQ (0x1 << 3)
|
||||
#define USB2_PHY_REQ (0x1 << 0)
|
||||
#define USB2_PHY_PORT_TREQ (0x1 << 1)
|
||||
|
||||
#define USB3_CTRL 0x13c
|
||||
#define USB3_PHY_SRST (0x1 << 8)
|
||||
#define USB3_PHY_XTAL_CKEN (0x1 << 7)
|
||||
#define USB3_VCC_SRST (0x1 << 6)
|
||||
#define USB3_PIPE3_RX_CKEN (0x1 << 5)
|
||||
#define USB3_PIPE3_TX_CKEN (0x1 << 4)
|
||||
#define USB3_UTMI_CKEN (0x1 << 3)
|
||||
#define USB3_SUSPEND_CKEN (0x1 << 2)
|
||||
#define USB3_REF_CKEN (0x1 << 1)
|
||||
#define USB3_BUS_CKEN (0x1 << 0)
|
||||
|
||||
#define GTXTHRCFG 0xc108
|
||||
#define GRXTHRCFG 0xc10c
|
||||
#define REG_GCTL 0xc110
|
||||
#define GUSB2PHYCFG 0xc200
|
||||
#define REG_GUSB3PIPECTL0 0xc2c0
|
||||
#define PCS_SSP_SOFT_RESET (0x1 << 31)
|
||||
#define USBTRDTIM_MASK 0x00003c00
|
||||
#define USBTRDTIM_16BIT (0x5 << 10)
|
||||
#define USBTRDTIM_8BIT (0x9 << 10)
|
||||
#define PHYIF (0x1 << 3)
|
||||
#define SUSPENDUSB20 (0x1 << 6)
|
||||
#define ENBLSLPM (0x1 << 8)
|
||||
#define LSIPD_MASK (0x7 << 19)
|
||||
#define LSIPD_3_BIT (0x2 << 19)
|
||||
#define U2_FREECLK_EXISTS (0x1 << 30)
|
||||
|
||||
#define PORT_CAP_DIR (0x3 << 12)
|
||||
#define PORT_SET_HOST (0x1 << 12)
|
||||
#define PORT_DISABLE_SUSPEND (0x1 << 17)
|
||||
|
||||
#define USB2_G_TXTHRCFG 0x23100000
|
||||
#define USB2_G_RXTHRCFG 0x23100000
|
||||
|
||||
/* Lotus phy use reg, reg's misc_base=0x12028000 */
|
||||
#define MISC_LOTUS_PHY2_CTRL_REG 0x28
|
||||
#define MISC_USB_SEL_SCENE1 (0x0 << 0)
|
||||
#define MISC_USB_SEL_SCENE2 (0x1 << 0)
|
||||
#define MISC_USB_SCENE_MASK (0x1 << 0)
|
||||
#define USB2_UTMI_REG_PROTECT1 (0x1 << 8)
|
||||
#define USB2_UTMI_REG_PROTECT2 (0x1 << 9)
|
||||
#define USB2_UTMI_REG_PROTECT3 (0x1 << 10)
|
||||
#define USB2_HW_CHIRP (0x1 << 14)
|
||||
#define USB2_UTMI_IDPULLUP (0x1 << 15)
|
||||
|
||||
/* Outer phy use reg, reg's misc_base=0x12028000 */
|
||||
#define MISC_OUT_PHY2_CTRL_REG 0x2C
|
||||
#define USB2_OSCOUTEN (0x1 << 8)
|
||||
#define USB2_PLL_EN (0x1 << 12)
|
||||
#define USB2_UTMI_DATABUS16_8 (0x1 << 16)
|
||||
#define MISC_OUT_PHY2_XCFGI_63TO32 0x988
|
||||
#define CFG_OUT_PHY2_EYE_VAL (0x1 << 5)
|
||||
|
||||
/* reg base(misc_base): 0x12028000 */
|
||||
#define MISC_USB2_CTRL_REG 0x24
|
||||
#define U2_BUS_FILTER_BYPASS (0xf << 0)
|
||||
#define U2_PWREN_MODE_MASK (0x1 << 4)
|
||||
#define U2_PWREN_MODE_MISC (0x0 << 4)
|
||||
#define U2_PWREN_MODE_CTRL (0x1 << 4)
|
||||
#define U2_OVERCURR_MODE_MASK (0x1 << 5)
|
||||
#define U2_OVERCURR_MODE_DISABLE (0x0 << 5)
|
||||
#define U2_OVERCURR_MODE_ENABLE (0x1 << 5)
|
||||
#define U2_PWREN_MASK (0x1 << 6)
|
||||
#define U2_PWREN_ON (0x0 << 6)
|
||||
#define U2_PWREN_OFF (0x1 << 6)
|
||||
#define U2_OVERCURR_MASK (0x1 << 7)
|
||||
#define U2_OVERCURR_ON (0x0 << 7)
|
||||
#define U2_OVERCURR_OFF (0x1 << 7)
|
||||
#define U2_FLADJ_30MHZ (0x20 << 13)
|
||||
|
||||
#define MISC_USB3_CTRL_REG 0x1110
|
||||
#define U3_BUS_FILTER_BYPASS (0xf << 0)
|
||||
#define U3_FLADJ_30MHZ (0x20 << 4)
|
||||
#define U3_PWREN_MODE_MASK (0x1 << 11)
|
||||
#define U3_PWREN_MODE_MISC (0x0 << 11)
|
||||
#define U3_PWREN_MODE_CTRL (0x1 << 11)
|
||||
#define U3_OVERCURR_MODE_MASK (0x1 << 12)
|
||||
#define U3_OVERCURR_MODE_DISABLE (0x0 << 12)
|
||||
#define U3_OVERCURR_MODE_ENABLE (0x1 << 12)
|
||||
#define U3_PWREN_MASK (0x1 << 13)
|
||||
#define U3_PWREN_ON (0x0 << 13)
|
||||
#define U3_PWREN_OFF (0x1 << 13)
|
||||
#define U3_OVERCURR_MASK (0x1 << 14)
|
||||
#define U3_OVERCURR_ON (0x0 << 14)
|
||||
#define U3_OVERCURR_OFF (0x1 << 14)
|
||||
#define HOST_U2_PORT_MASK (0xf << 20)
|
||||
#define HOST_U2_PORT(n) (n << 20)
|
||||
#define HOST_U3_PORT_MASK (0xf << 24)
|
||||
#define HOST_U3_PORT(n) (n << 24)
|
||||
#define USB2_HOST_VBUS_EN (0x1 << 30)
|
||||
#define USB3_HOST_VBUS_EN (0x1 << 31)
|
||||
|
||||
#define MISC_USB3_PHY_REG 0x112c
|
||||
|
||||
/* SYSBOOT1[2:2]: usb scene select */
|
||||
#define SYSBOOT1 0x12020134
|
||||
#define SYSBOOT1_SEL_USB_SCENE_MASK (1 << 2)
|
||||
|
||||
static uintptr_t xhci_base;
|
||||
|
||||
/**
|
||||
* lotus_chip_u3_ability - whether current chip support u3.
|
||||
*
|
||||
* @return:
|
||||
* 1: support u3.
|
||||
* 0: only support u2.
|
||||
*/
|
||||
static int lotus_chip_u3_ability(void)
|
||||
{
|
||||
u32 id = get_chipid();
|
||||
|
||||
USB_DEBUG("%s: chid id 0x%x\n", __func__, id);
|
||||
|
||||
if (id == 0x76050110 || id == 0x06050110)
|
||||
return 0;
|
||||
|
||||
/* if id == 0, consider chip support U3. */
|
||||
return 1;
|
||||
}
|
||||
|
||||
int xhci_hcd_init(int index, struct xhci_hccr **hccr, struct xhci_hcor **hcor)
|
||||
{
|
||||
#ifdef CONFIG_USB_DUAL_PORT_SUPPORT
|
||||
unsigned int reg;
|
||||
|
||||
reg = readl(MISC_REG_BASE + MISC_LOTUS_PHY2_CTRL_REG);
|
||||
reg &= MISC_USB_SCENE_MASK;
|
||||
|
||||
if (index >= LOTUS_USB_CTRL_UNKNOWN)
|
||||
return -ENODEV;
|
||||
/* 7605v11 only support u2 host */
|
||||
if (!lotus_chip_u3_ability() && index == LOTUS_DWC_U3)
|
||||
return -ENODEV;
|
||||
|
||||
/* Scene2 don't init U2 host. */
|
||||
if (index == LOTUS_DWC_U2 && reg)
|
||||
return -ENODEV;
|
||||
#endif
|
||||
|
||||
if ((hccr == NULL) || (hcor == NULL))
|
||||
return -EINVAL;
|
||||
|
||||
#ifdef CONFIG_USB_DUAL_PORT_SUPPORT
|
||||
if (index == LOTUS_DWC_U3)
|
||||
xhci_base = USB3_CTRL_REG_BASE;
|
||||
else if (index == LOTUS_DWC_U2)
|
||||
xhci_base = USB2_CTRL_REG_BASE;
|
||||
#else
|
||||
/* single usb port, U3 is default */
|
||||
if (index == LOTUS_DWC_U3) {
|
||||
/* 7605v11 only support U2 host */
|
||||
if (!lotus_chip_u3_ability())
|
||||
xhci_base = USB2_CTRL_REG_BASE;
|
||||
else
|
||||
xhci_base = USB3_CTRL_REG_BASE;
|
||||
} else
|
||||
return -ENODEV;
|
||||
#endif
|
||||
*hccr = (struct xhci_hccr *)(xhci_base);
|
||||
*hcor = (struct xhci_hcor *)((uintptr_t) *hccr +
|
||||
HC_LENGTH(xhci_readl(&(*hccr)->cr_capbase)));
|
||||
|
||||
USB_DEBUG("hccr:0x%x, hcor:0x%x.\n", (unsigned int)*hccr, (unsigned int)*hcor);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Get usb scene from SYSBOOT1 and config to MISC
|
||||
* usb_scene: 0-scene1, 1-scene2.
|
||||
* scene1:U3 only + U2.
|
||||
* scene2:U3(compatible to U2).
|
||||
*/
|
||||
#ifdef CONFIG_USB_DUAL_PORT_SUPPORT
|
||||
static void usb_scene_select(int index, int type)
|
||||
#else
|
||||
static void usb_scene_select(int index)
|
||||
#endif
|
||||
{
|
||||
unsigned int reg, usb_scene = LOTUS_USB_SCENE2;
|
||||
|
||||
#ifdef CONFIG_USB_DUAL_PORT_SUPPORT
|
||||
reg = readl(SYSBOOT1);
|
||||
if (reg & SYSBOOT1_SEL_USB_SCENE_MASK)
|
||||
usb_scene = LOTUS_USB_SCENE1;
|
||||
|
||||
/* used by usb download */
|
||||
if (index == LOTUS_DWC_U2 && type == USB_INIT_DEVICE)
|
||||
usb_scene = LOTUS_USB_SCENE1;
|
||||
#else
|
||||
/* used by usb download */
|
||||
if ((index == LOTUS_DWC_U2) ||
|
||||
(index == LOTUS_DWC_U3 && !lotus_chip_u3_ability()))
|
||||
usb_scene = LOTUS_USB_SCENE1;
|
||||
#endif
|
||||
|
||||
reg = 0;
|
||||
if (usb_scene == LOTUS_USB_SCENE1) {
|
||||
/* u3 ctrl connect u3 phy, u2 ctrl connect u2 phy */
|
||||
reg |= MISC_USB_SEL_SCENE1;
|
||||
printf("%s:select U3 only + U2.\n", __func__);
|
||||
} else if (usb_scene == LOTUS_USB_SCENE2) {
|
||||
/* u3 ctrl connect u3 phy and u2 phy */
|
||||
reg |= MISC_USB_SEL_SCENE2;
|
||||
printf("%s:select U3(compatible to U2).\n", __func__);
|
||||
}
|
||||
writel(reg, MISC_REG_BASE + MISC_LOTUS_PHY2_CTRL_REG);
|
||||
|
||||
USB_DEBUG("MISC_LOTUS_PHY2_CTRL_REG(0x12028028):0x%X\n",
|
||||
readl(MISC_REG_BASE + MISC_LOTUS_PHY2_CTRL_REG));
|
||||
}
|
||||
|
||||
static void lotus_u2_phy_eye_config(void)
|
||||
{
|
||||
unsigned int reg;
|
||||
|
||||
/* outer phy eye diagram config */
|
||||
reg = readl(MISC_REG_BASE + MISC_OUT_PHY2_XCFGI_63TO32);
|
||||
reg |= CFG_OUT_PHY2_EYE_VAL;
|
||||
writel(reg, MISC_REG_BASE + MISC_OUT_PHY2_XCFGI_63TO32);
|
||||
}
|
||||
|
||||
static void lotus_dwc_data_width_config(int index,
|
||||
enum usb_phy_width_mode mode)
|
||||
{
|
||||
unsigned int reg, base = 0;
|
||||
|
||||
/* GUSB2PHYCFG register: config val 0x40001548(HS/FS) or
|
||||
* 0x00381548(LS) according to IC.
|
||||
*
|
||||
* USBTRDTIM: USB 2.0 Turnaround Time in PHY clocks.
|
||||
* PHYIF: 1 UTMI+ PHY is 16-bit, 0 UTMI+ PHY is 8-bit.
|
||||
*/
|
||||
reg = 0;
|
||||
switch (mode) {
|
||||
case PHY_WIDTH_8BIT:
|
||||
/* 8-bits usb phy configure */
|
||||
reg |= USBTRDTIM_8BIT;
|
||||
reg &= ~PHYIF;
|
||||
break;
|
||||
case PHY_WIDTH_16BIT:
|
||||
/* 16-bits usb phy configure */
|
||||
reg |= (USBTRDTIM_16BIT | PHYIF);
|
||||
break;
|
||||
default:
|
||||
printf("usb phy width config val error.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_USB_DUAL_PORT_SUPPORT
|
||||
if (index == LOTUS_DWC_U3)
|
||||
base = USB3_CTRL_REG_BASE;
|
||||
else
|
||||
base = USB2_CTRL_REG_BASE;
|
||||
#else
|
||||
if (index == LOTUS_DWC_U3 && lotus_chip_u3_ability())
|
||||
base = USB3_CTRL_REG_BASE;
|
||||
else
|
||||
base = USB2_CTRL_REG_BASE;
|
||||
#endif
|
||||
writel(reg, base + GUSB2PHYCFG);
|
||||
}
|
||||
|
||||
static inline void lotus_usb2_vbus_valid_en(void)
|
||||
{
|
||||
unsigned int reg;
|
||||
|
||||
/* enable U2 vbus valid */
|
||||
reg = readl(MISC_REG_BASE + MISC_USB3_CTRL_REG);
|
||||
reg |= USB2_HOST_VBUS_EN;
|
||||
writel(reg, MISC_REG_BASE + MISC_USB3_CTRL_REG);
|
||||
}
|
||||
|
||||
static inline void lotus_usb3_vbus_valid_en(void)
|
||||
{
|
||||
unsigned int reg;
|
||||
|
||||
/* enable U3 vbus valid */
|
||||
reg = readl(MISC_REG_BASE + MISC_USB3_CTRL_REG);
|
||||
reg |= USB3_HOST_VBUS_EN;
|
||||
writel(reg, MISC_REG_BASE + MISC_USB3_CTRL_REG);
|
||||
}
|
||||
|
||||
static void lotus_u2_phy_clk_init(void)
|
||||
{
|
||||
unsigned int reg;
|
||||
|
||||
/* enable u2 phy clk and do reset */
|
||||
reg = (USB2_PHY_REQ | USB2_PHY_XTAL_CKEN);
|
||||
writel(reg, CRG_REG_BASE + USB2_CTRL);
|
||||
|
||||
udelay(U_LEVEL6);
|
||||
|
||||
/* undo phy reset */
|
||||
reg = readl(CRG_REG_BASE + USB2_CTRL);
|
||||
reg &= ~USB2_PHY_REQ;
|
||||
writel(reg, CRG_REG_BASE + USB2_CTRL);
|
||||
USB_DEBUG("USB2_CRG(0x12010140), expext:0x4,"
|
||||
"acutal:0x%X\n", readl(CRG_REG_BASE + USB2_CTRL));
|
||||
}
|
||||
|
||||
static void lotus_u2_ctrl_clk_init(void)
|
||||
{
|
||||
unsigned int reg;
|
||||
|
||||
/* enable u2 ctrl clk and do reset */
|
||||
reg = readl(CRG_REG_BASE + USB2_CTRL);
|
||||
reg |= (USB2_VCC_SRST_REQ | USB2_BUS_CKEN | USB2_REF_CKEN |
|
||||
USB2_UTMI_CKEN);
|
||||
writel(reg, CRG_REG_BASE + USB2_CTRL);
|
||||
|
||||
udelay(U_LEVEL6);
|
||||
|
||||
/* undo ctrl vcc reset */
|
||||
reg = readl(CRG_REG_BASE + USB2_CTRL);
|
||||
reg &= ~USB2_VCC_SRST_REQ;
|
||||
writel(reg, CRG_REG_BASE + USB2_CTRL);
|
||||
}
|
||||
|
||||
static void inline lotus_usb2_clk_init(void)
|
||||
{
|
||||
/* write CRG default value 0x130d, including:
|
||||
*
|
||||
* USB2_PHY_REQ: reset PHY UTMI
|
||||
* USB2_PHY_XTAL_CKEN: open phy xtal clk
|
||||
* USB2_VCC_SRST_REQ: reset ctrl VCC
|
||||
* USB2_BUS_CKEN: open bus clk
|
||||
* USB2_REF_CKEN: open ctrl ref clk
|
||||
* USB2_UTMI_CKEN: open UTMI clk
|
||||
* LOTUS_PHY_APB_CKEN: open LOTUS phy apb clk
|
||||
* USB2_UTMI_CKEN: open LOTUS phy ref clk
|
||||
*/
|
||||
lotus_u2_phy_clk_init();
|
||||
lotus_u2_ctrl_clk_init();
|
||||
USB_DEBUG("USB2_CRG(0x12010140), expext:0x1304,"
|
||||
"acutal:0x%X\n", readl(CRG_REG_BASE + USB2_CTRL));
|
||||
}
|
||||
|
||||
static void inline lotus_usb2_clk_exit(void)
|
||||
{
|
||||
unsigned int reg;
|
||||
|
||||
/* disable usb2 clk and do reset */
|
||||
reg = 0;
|
||||
reg |= (USB2_VCC_SRST_REQ | USB2_PHY_REQ);
|
||||
writel(reg, CRG_REG_BASE + USB2_CTRL);
|
||||
USB_DEBUG("USB2_CRG(0x12010140), expext:0x9,"
|
||||
"acutal:0x%X\n", readl(CRG_REG_BASE + USB2_CTRL));
|
||||
}
|
||||
|
||||
static void lotus_usb3_clk_init(void)
|
||||
{
|
||||
unsigned int reg;
|
||||
|
||||
/* write CRG default value 0x1ff, including:
|
||||
*
|
||||
* USB3_PHY_SRST: reset U3 PHY UTMI
|
||||
* USB3_PHY_XTAL_CKEN: open U3 phy xtal clk
|
||||
* USB3_VCC_SRST: reset U3 ctrl VCC
|
||||
* USB3_PIPE3_RX_CKEN: open PIPE3 RX clk
|
||||
* USB3_PIPE3_TX_CKEN: open PIPE3 TX clk
|
||||
* USB3_UTMI_CKEN: open UTMI clk
|
||||
* USB3_SUSPEND_CKEN: open U3 ctrl suspend clk
|
||||
* USB3_REF_CKEN: open U3 ctrl ref clk
|
||||
* USB3_BUS_CKEN: open U3 ctrl bus clk
|
||||
*/
|
||||
|
||||
reg = 0;
|
||||
reg |= (USB3_PHY_SRST | USB3_PHY_XTAL_CKEN | USB3_VCC_SRST |
|
||||
USB3_PIPE3_RX_CKEN | USB3_PIPE3_TX_CKEN | USB3_UTMI_CKEN |
|
||||
USB3_SUSPEND_CKEN | USB3_REF_CKEN | USB3_BUS_CKEN);
|
||||
writel(reg, CRG_REG_BASE + USB3_CTRL);
|
||||
USB_DEBUG("USB3_CRG(0x1201013c), expext:0x1ff,"
|
||||
"acutal:0x%X\n", readl(CRG_REG_BASE + USB3_CTRL));
|
||||
|
||||
udelay(U_LEVEL6);
|
||||
|
||||
/* undo usb phy and ctrl reset */
|
||||
reg = readl(CRG_REG_BASE + USB3_CTRL);
|
||||
reg &= ~USB3_PHY_SRST;
|
||||
reg &= ~USB3_VCC_SRST;
|
||||
writel(reg, CRG_REG_BASE + USB3_CTRL);
|
||||
USB_DEBUG("USB3_CRG(0x1201013c), expext:0xbf,"
|
||||
"acutal:0x%X\n", readl(CRG_REG_BASE + USB3_CTRL));
|
||||
}
|
||||
|
||||
static void lotus_usb3_clk_exit(void)
|
||||
{
|
||||
unsigned int reg;
|
||||
|
||||
/* disable usb3 clk and do reset */
|
||||
reg = 0;
|
||||
reg |= (USB3_PHY_SRST | USB3_VCC_SRST);
|
||||
writel(reg, CRG_REG_BASE + USB3_CTRL);
|
||||
USB_DEBUG("USB3_CRG(0x1201013c), expext:0x140,"
|
||||
"acutal:0x%X\n", readl(CRG_REG_BASE + USB3_CTRL));
|
||||
}
|
||||
|
||||
static void lotus_u2_phy_misc_config(void)
|
||||
{
|
||||
unsigned int reg;
|
||||
|
||||
/* MISC_OUT_PHY2_CTRL: config 0x11100 according to IC. */
|
||||
reg = 0;
|
||||
reg |= (USB2_OSCOUTEN | USB2_PLL_EN | USB2_UTMI_DATABUS16_8);
|
||||
writel(reg, MISC_REG_BASE + MISC_OUT_PHY2_CTRL_REG);
|
||||
USB_DEBUG("MISC_OUT_PHY2_CTRL_REG(0x1202802c),"
|
||||
"expext:0x11100, acutal:0x%X\n",
|
||||
readl(MISC_REG_BASE + MISC_OUT_PHY2_CTRL_REG));
|
||||
}
|
||||
|
||||
static void lotus_u2_ctrl_misc_config(void)
|
||||
{
|
||||
unsigned int reg;
|
||||
|
||||
/* MISC_USB2_CTRL_REG: config 0x400ff according to IC. */
|
||||
reg = 0;
|
||||
reg |= (U2_BUS_FILTER_BYPASS | U2_PWREN_MODE_CTRL | U2_OVERCURR_MODE_ENABLE |\
|
||||
U2_PWREN_OFF | U2_OVERCURR_OFF | U2_FLADJ_30MHZ);
|
||||
writel(reg, MISC_REG_BASE + MISC_USB2_CTRL_REG);
|
||||
USB_DEBUG("MISC_USB2_CTRL_REG(0x12028024),"
|
||||
"expext:0x400ff, acutal:0x%X\n",
|
||||
readl(MISC_REG_BASE + MISC_USB2_CTRL_REG));
|
||||
}
|
||||
|
||||
static void lotus_u3_ctrl_misc_config(void)
|
||||
{
|
||||
unsigned int reg;
|
||||
|
||||
/* MISC_USB3_CTRL_REG: config 0xc1107a0f according to IC. */
|
||||
reg = 0;
|
||||
reg |= HOST_U3_PORT(1);
|
||||
reg |= HOST_U2_PORT(1);
|
||||
reg |= (U3_OVERCURR_OFF | U3_PWREN_OFF | U3_OVERCURR_MODE_ENABLE |\
|
||||
U3_PWREN_MODE_CTRL | U3_FLADJ_30MHZ | U3_BUS_FILTER_BYPASS);
|
||||
writel(reg, MISC_REG_BASE + MISC_USB3_CTRL_REG);
|
||||
USB_DEBUG("MISC_USB3_CTRL_REG(0x12029110), acutal:0x%X\n",
|
||||
readl(MISC_REG_BASE + MISC_USB3_CTRL_REG));
|
||||
}
|
||||
|
||||
/* config U3 phy inner reg, insure U3 phy CRG already open. */
|
||||
static void lotus_u3_phy_misc_config(void)
|
||||
{
|
||||
/* adjust pi_current_trim */
|
||||
writel(0x11, MISC_REG_BASE + MISC_USB3_PHY_REG);
|
||||
udelay(1); /* asic say delay 10ns, we delay 1us */
|
||||
writel(0x4211, MISC_REG_BASE + MISC_USB3_PHY_REG);
|
||||
udelay(1);
|
||||
writel(0x11, MISC_REG_BASE + MISC_USB3_PHY_REG);
|
||||
udelay(1);
|
||||
|
||||
/* close slew_assist_dis */
|
||||
writel(0x2, MISC_REG_BASE + MISC_USB3_PHY_REG);
|
||||
udelay(1);
|
||||
writel(0x100202, MISC_REG_BASE + MISC_USB3_PHY_REG);
|
||||
udelay(1);
|
||||
writel(0x2, MISC_REG_BASE + MISC_USB3_PHY_REG);
|
||||
udelay(1);
|
||||
USB_DEBUG("MISC_USB3_PHY_REG(0x1202912c):0x%X\n",
|
||||
readl(MISC_REG_BASE + MISC_USB3_PHY_REG));
|
||||
}
|
||||
|
||||
static void lotus_dwc_usb_config(int index)
|
||||
{
|
||||
unsigned int reg, base = 0;
|
||||
|
||||
#ifdef CONFIG_USB_DUAL_PORT_SUPPORT
|
||||
if (index == LOTUS_DWC_U2)
|
||||
base = USB2_CTRL_REG_BASE;
|
||||
else
|
||||
base = USB3_CTRL_REG_BASE;
|
||||
#else
|
||||
if (index == LOTUS_DWC_U2 ||
|
||||
(index == LOTUS_DWC_U3 && !lotus_chip_u3_ability())) {
|
||||
/* USB2 Controller configs */
|
||||
base = USB2_CTRL_REG_BASE;
|
||||
}
|
||||
|
||||
if ((index == LOTUS_DWC_U3 && lotus_chip_u3_ability())) {
|
||||
/* USB3 Controller configs */
|
||||
base = USB3_CTRL_REG_BASE;
|
||||
}
|
||||
#endif
|
||||
|
||||
reg = readl(base + REG_GUSB3PIPECTL0);
|
||||
reg |= PCS_SSP_SOFT_RESET;
|
||||
writel(reg, base + REG_GUSB3PIPECTL0);
|
||||
udelay(U_LEVEL2);
|
||||
|
||||
reg = readl(base + REG_GCTL);
|
||||
reg &= ~PORT_CAP_DIR;
|
||||
reg |= PORT_SET_HOST; /* [13:12] 01: Host; 10: Device; 11: OTG */
|
||||
writel(reg, base + REG_GCTL);
|
||||
udelay(U_LEVEL2);
|
||||
|
||||
reg = readl(base + REG_GUSB3PIPECTL0);
|
||||
reg &= ~PCS_SSP_SOFT_RESET;
|
||||
reg &= ~PORT_DISABLE_SUSPEND; /* disable suspend */
|
||||
writel(reg, base + REG_GUSB3PIPECTL0);
|
||||
udelay(U_LEVEL2);
|
||||
|
||||
writel(USB2_G_TXTHRCFG, base + GTXTHRCFG);
|
||||
writel(USB2_G_RXTHRCFG, base + GRXTHRCFG);
|
||||
udelay(U_LEVEL2);
|
||||
|
||||
lotus_dwc_data_width_config(index, PHY_WIDTH_16BIT);
|
||||
|
||||
reg = readl(base + GUSB2PHYCFG);
|
||||
reg &= ~SUSPENDUSB20; /* disable suspend */
|
||||
reg &= ~ENBLSLPM; /* suspend not transfer to external PHY */
|
||||
reg &= ~U2_FREECLK_EXISTS;
|
||||
reg &= ~LSIPD_MASK;
|
||||
reg |= LSIPD_3_BIT;
|
||||
writel(reg, base + GUSB2PHYCFG);
|
||||
|
||||
#ifdef CONFIG_USB_DUAL_PORT_SUPPORT
|
||||
if (index == LOTUS_DWC_U2) {
|
||||
#else
|
||||
if (index == LOTUS_DWC_U2 ||
|
||||
(index == LOTUS_DWC_U3 && !lotus_chip_u3_ability())) {
|
||||
#endif
|
||||
USB_DEBUG("U2 GUSB2PHYCFG(0xc200):0x%X\n",
|
||||
readl(base + GUSB2PHYCFG));
|
||||
USB_DEBUG("U2 GUSB3PIPECTL0(0xc2c0):0x%X\n",
|
||||
readl(base + REG_GUSB3PIPECTL0));
|
||||
USB_DEBUG("U2 GCTL(0xc110):0x%X\n",
|
||||
readl(base + REG_GCTL));
|
||||
#ifdef CONFIG_USB_DUAL_PORT_SUPPORT
|
||||
} else if (index == LOTUS_DWC_U3) {
|
||||
#else
|
||||
} else if ((index == LOTUS_DWC_U3 && lotus_chip_u3_ability())) {
|
||||
#endif
|
||||
USB_DEBUG("U3 GUSB2PHYCFG(0xc200):0x%X\n",
|
||||
readl(base + GUSB2PHYCFG));
|
||||
USB_DEBUG("U3 GUSB3PIPECTL0(0xc2c0):0x%X\n",
|
||||
readl(base + REG_GUSB3PIPECTL0));
|
||||
USB_DEBUG("U3 GCTL(0xc110):0x%X\n",
|
||||
readl(base + REG_GCTL));
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef CONFIG_USB_DUAL_PORT_SUPPORT
|
||||
void phy_usb_init(int index, int type)
|
||||
#else
|
||||
void phy_usb_init(int index)
|
||||
#endif
|
||||
{
|
||||
#ifdef CONFIG_USB_DUAL_PORT_SUPPORT
|
||||
unsigned int reg;
|
||||
|
||||
/* 7605v11 only support u2 host */
|
||||
if (!lotus_chip_u3_ability() && index == LOTUS_DWC_U3)
|
||||
return;
|
||||
usb_scene_select(index, type);
|
||||
#else
|
||||
usb_scene_select(index);
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_USB_DUAL_PORT_SUPPORT
|
||||
reg = readl(MISC_REG_BASE + MISC_LOTUS_PHY2_CTRL_REG);
|
||||
reg &= MISC_USB_SCENE_MASK;
|
||||
|
||||
/* Scene2 don't init U2 host. */
|
||||
if (index == LOTUS_DWC_U2 && reg)
|
||||
return;
|
||||
|
||||
if (index == LOTUS_DWC_U3) {
|
||||
#else
|
||||
if (index == LOTUS_DWC_U3 && lotus_chip_u3_ability()) {
|
||||
#endif
|
||||
printf("USB3 init...\n");
|
||||
/* config u3 ctrl/u3 phy/u2 phy */
|
||||
lotus_u3_ctrl_misc_config();
|
||||
lotus_usb3_vbus_valid_en();
|
||||
lotus_u2_phy_misc_config();
|
||||
lotus_usb3_clk_init();
|
||||
lotus_u3_phy_misc_config();
|
||||
lotus_u2_phy_clk_init();
|
||||
lotus_dwc_usb_config(index);
|
||||
#ifdef CONFIG_USB_DUAL_PORT_SUPPORT
|
||||
} else if (index == LOTUS_DWC_U2) {
|
||||
#else
|
||||
} else if ((index == LOTUS_DWC_U2) ||
|
||||
(index == LOTUS_DWC_U3 && !lotus_chip_u3_ability())) {
|
||||
#endif
|
||||
printf("USB2 init...\n");
|
||||
/* config u2 ctrl and u2 phy */
|
||||
lotus_u2_ctrl_misc_config();
|
||||
lotus_usb2_vbus_valid_en();
|
||||
lotus_u2_phy_misc_config();
|
||||
lotus_usb2_clk_init();
|
||||
lotus_dwc_usb_config(index);
|
||||
/* u2 phy eye diagram config */
|
||||
lotus_u2_phy_eye_config();
|
||||
}
|
||||
/* According to IC, usb phy clk init need 1ms delay */
|
||||
mdelay(1);
|
||||
}
|
||||
EXPORT_SYMBOL(phy_usb_init);
|
||||
|
||||
void xhci_hcd_stop(int index)
|
||||
{
|
||||
if (index == LOTUS_DWC_U3 && lotus_chip_u3_ability())
|
||||
lotus_usb3_clk_exit();
|
||||
else if ((index == LOTUS_DWC_U2) ||
|
||||
(index == LOTUS_DWC_U3 && !lotus_chip_u3_ability()))
|
||||
lotus_usb2_clk_exit();
|
||||
}
|
||||
EXPORT_SYMBOL(xhci_hcd_stop);
|
||||
|
||||
/* Some usb device can't be recognize in U-boot because
|
||||
* of error state, so we power off them and wait a delay
|
||||
* to restart them, similar to unplug and then insert.
|
||||
*/
|
||||
void lotus_usb_pwren_control(void)
|
||||
{
|
||||
u32 reg, default_val;
|
||||
int delay __attribute__((unused));
|
||||
|
||||
#ifndef CONFIG_USB_DUAL_PORT_SUPPORT
|
||||
delay = CONFIG_USB_PWREN_OFF_DELAY ?
|
||||
CONFIG_USB_PWREN_OFF_DELAY : 100;
|
||||
#endif
|
||||
reg = readl(MISC_REG_BASE + MISC_USB2_CTRL_REG);
|
||||
default_val = reg;
|
||||
reg &= ~U2_PWREN_MODE_MASK; /* set PWREN_MISC mode */
|
||||
reg |= U2_PWREN_OFF; /* turn off PWREN */
|
||||
writel(reg, MISC_REG_BASE + MISC_USB2_CTRL_REG);
|
||||
|
||||
#ifndef CONFIG_USB_DUAL_PORT_SUPPORT
|
||||
mdelay(delay);
|
||||
#endif
|
||||
|
||||
reg = readl(MISC_REG_BASE + MISC_USB2_CTRL_REG);
|
||||
reg &= ~U2_PWREN_MASK; /* turn on PWREN */
|
||||
writel(reg, MISC_REG_BASE + MISC_USB2_CTRL_REG);
|
||||
|
||||
reg = default_val; /* set PWREN_CTRL mode(default val) */
|
||||
writel(reg, MISC_REG_BASE + MISC_USB2_CTRL_REG);
|
||||
|
||||
/* U3 CTRL PWREN control */
|
||||
reg = readl(MISC_REG_BASE + MISC_USB3_CTRL_REG);
|
||||
default_val = reg;
|
||||
reg &= ~U3_PWREN_MODE_MASK;
|
||||
reg |= U3_PWREN_OFF; /* turn off PWREN */
|
||||
writel(reg, MISC_REG_BASE + MISC_USB3_CTRL_REG);
|
||||
USB_DEBUG("U3 MISC GCTL(0x1110):0x%X\n",
|
||||
readl(MISC_REG_BASE + MISC_USB3_CTRL_REG));
|
||||
|
||||
#ifndef CONFIG_USB_DUAL_PORT_SUPPORT
|
||||
mdelay(delay);
|
||||
#endif
|
||||
|
||||
reg = readl(MISC_REG_BASE + MISC_USB3_CTRL_REG);
|
||||
reg &= ~U3_PWREN_MASK; /* turn on PWREN */
|
||||
writel(reg, MISC_REG_BASE + MISC_USB3_CTRL_REG);
|
||||
USB_DEBUG("U3 MISC GCTL(0x1110):0x%X\n",
|
||||
readl(MISC_REG_BASE + MISC_USB3_CTRL_REG));
|
||||
|
||||
reg = default_val; /* set PWREN_CTRL mode(default val) */
|
||||
writel(reg, MISC_REG_BASE + MISC_USB3_CTRL_REG);
|
||||
USB_DEBUG("U3 MISC GCTL(0x1110):0x%X\n",
|
||||
readl(MISC_REG_BASE + MISC_USB3_CTRL_REG));
|
||||
}
|
||||
@@ -0,0 +1,307 @@
|
||||
/*
|
||||
* Copyright (c) LOTUS. All rights reserved.
|
||||
*/
|
||||
|
||||
#ifdef CONFIG_PHY_LOTUS_USB
|
||||
#include <asm/arch/platform.h>
|
||||
#include <dm.h>
|
||||
#include <linux/lotus/usb.h>
|
||||
#include <usb/xhci.h>
|
||||
#include "phy-usb.h"
|
||||
|
||||
/* offset 0x140 */
|
||||
#define USB2_CTRL 0x140
|
||||
#define USB2_CRG_DEFAULT_VAL 0x3b2f
|
||||
#define USB2_UTMI_CKEN (0x1 << 12)
|
||||
#define USB2_PHY_APB_CKEN (0x1 << 11)
|
||||
#define USB2_REF_CKEN (0x1 << 9)
|
||||
#define USB2_BUS_CKEN (0x1 << 8)
|
||||
#define USB2_PHY_PLL_CKEN (0x1 << 4)
|
||||
#define USB2_PHY_XTAL_CKEN (0x1 << 2)
|
||||
#define USB2_FREECLK_CKSEL (0x1 << 13)
|
||||
#define USB2_PHY_APB_RST (0x1 << 10)
|
||||
#define USB2_VCC_SRST_REQ (0x1 << 3)
|
||||
#define USB2_PHY_REQ (0x1 << 0)
|
||||
#define USB2_PHY_PORT_TREQ (0x1 << 1)
|
||||
|
||||
#define GTXTHRCFG 0xc108
|
||||
#define GRXTHRCFG 0xc10c
|
||||
#define REG_GCTL 0xc110
|
||||
#define REG_GUSB3PIPECTL0 0xc2c0
|
||||
#define PCS_SSP_SOFT_RESET (0x1 << 31)
|
||||
|
||||
#define PORT_CAP_DIR (0x3 << 12)
|
||||
#define PORT_SET_HOST (0x1 << 12)
|
||||
#define PORT_DISABLE_SUSPEND (0x1 << 17)
|
||||
|
||||
#define USB2_G_TXTHRCFG 0x23100000
|
||||
#define USB2_G_RXTHRCFG 0x23100000
|
||||
|
||||
/* PHY base register */
|
||||
#define USB2_PHY_BASE_REG 0x100D0000
|
||||
#define RG_PLL_EN_MASK 0x0003
|
||||
#define RG_PLL_EN_VAL 0x0003
|
||||
#define PHY_PLL_OFFSET 0x0014
|
||||
|
||||
#define USB_VBUS_IO_CONFIG_REG 0x100c007C
|
||||
#define USB_VBUS_IO_CONFIG_VAL 0x0531
|
||||
|
||||
#define USB_PWREN_CONFIG_REG 0x100c0080
|
||||
#define USB_PWREN_CONFIG_VAL 0x1
|
||||
|
||||
/* PHY eye config */
|
||||
#define PHY_ANA_CFG_0_OFFSET 0x00
|
||||
#define PHY_PRE_DRIVE_MASK (0xf << 24)
|
||||
#define PHY_PRE_DRIVE_VAL (0x4 << 24)
|
||||
#define PHY_HSTX_DE_MASK (0xf << 8)
|
||||
#define PHY_HSTX_DE_VAL (0xc << 8)
|
||||
#define PHY_HSTX_DEEN_BIT (0x1 << 5)
|
||||
#define PHY_ANA_CFG_2_OFFSET 0x08
|
||||
#define PHY_TX_TEST_BIT (0x1 << 20)
|
||||
#define PHY_HALF_DEEM_BIT (0x1 << 21)
|
||||
#define PHY_DISCONNECT_REFERENCE_MASK (0x7 << 16)
|
||||
#define PHY_DISCONNECT_REFERENCE_VAL (0x2 << 16)
|
||||
#define PHY_ANA_CFG_4_OFFSET 0x10
|
||||
#define PHY_TX_REFERENCE_MASK (0x7 << 4)
|
||||
#define PHY_TX_REFERENCE_VAL (0x5 << 4)
|
||||
#define PHY_SQUELCH_REFERENCE_MASK (0x7 << 0)
|
||||
#define PHY_SQUELCH_REFERENCE_VAL (0x5 << 0)
|
||||
|
||||
/* PHY trim config */
|
||||
#define USB_TRIM_BASE_REG 0x12028004
|
||||
#define USB_TRIM_VAL_MASK 0x001F
|
||||
#define USB_TRIM_VAL_MIN 0x0009
|
||||
#define USB_TRIM_VAL_MAX 0x001D
|
||||
#define USB2_TRIM_OFFSET 0x0008
|
||||
#define USB2_TRIM_MASK 0x1f00
|
||||
#define usb2_trim_val(a) (((a) << 8) & USB2_TRIM_MASK)
|
||||
#define USB2_TRIM_DEFAULT_VAL 0x000F
|
||||
|
||||
static uintptr_t xhci_base = 0;
|
||||
|
||||
int xhci_hcd_init(int index, struct xhci_hccr **hccr, struct xhci_hcor **hcor)
|
||||
{
|
||||
if ((hccr == NULL) || (hcor == NULL))
|
||||
return -EINVAL;
|
||||
|
||||
xhci_base = USB3_CTRL_REG_BASE;
|
||||
|
||||
*hccr = (struct xhci_hccr *)(xhci_base);
|
||||
*hcor = (struct xhci_hcor *)((uintptr_t) *hccr +
|
||||
HC_LENGTH(xhci_readl(&(*hccr)->cr_capbase)));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void usb2_eye_config(void)
|
||||
{
|
||||
unsigned int reg;
|
||||
/* HSTX pre-drive strength */
|
||||
reg = readl(USB2_PHY_BASE_REG + PHY_ANA_CFG_0_OFFSET);
|
||||
reg &= ~PHY_PRE_DRIVE_MASK;
|
||||
reg |= PHY_PRE_DRIVE_VAL;
|
||||
writel(reg, USB2_PHY_BASE_REG + PHY_ANA_CFG_0_OFFSET);
|
||||
|
||||
/* HSTX de-emphasis strength */
|
||||
reg = readl(USB2_PHY_BASE_REG + PHY_ANA_CFG_0_OFFSET);
|
||||
reg &= ~PHY_HSTX_DE_MASK;
|
||||
reg |= PHY_HSTX_DE_VAL;
|
||||
writel(reg, USB2_PHY_BASE_REG + PHY_ANA_CFG_0_OFFSET);
|
||||
|
||||
/* HSTX de-emphasis enable */
|
||||
reg = readl(USB2_PHY_BASE_REG + PHY_ANA_CFG_0_OFFSET);
|
||||
reg |= PHY_HSTX_DEEN_BIT;
|
||||
writel(reg, USB2_PHY_BASE_REG + PHY_ANA_CFG_0_OFFSET);
|
||||
|
||||
/* TX test bit */
|
||||
reg = readl(USB2_PHY_BASE_REG + PHY_ANA_CFG_2_OFFSET);
|
||||
reg |= PHY_TX_TEST_BIT;
|
||||
writel(reg, USB2_PHY_BASE_REG + PHY_ANA_CFG_2_OFFSET);
|
||||
|
||||
/* TX half de-emphasis bit */
|
||||
reg = readl(USB2_PHY_BASE_REG + PHY_ANA_CFG_2_OFFSET);
|
||||
reg |= PHY_HALF_DEEM_BIT;
|
||||
writel(reg, USB2_PHY_BASE_REG + PHY_ANA_CFG_2_OFFSET);
|
||||
|
||||
/* Disconnect reference voltage sel */
|
||||
reg = readl(USB2_PHY_BASE_REG + PHY_ANA_CFG_2_OFFSET);
|
||||
reg &= ~PHY_DISCONNECT_REFERENCE_MASK;
|
||||
reg |= PHY_DISCONNECT_REFERENCE_VAL;
|
||||
writel(reg, USB2_PHY_BASE_REG + PHY_ANA_CFG_2_OFFSET);
|
||||
|
||||
/* TX reference voltage sel */
|
||||
reg = readl(USB2_PHY_BASE_REG + PHY_ANA_CFG_4_OFFSET);
|
||||
reg &= ~PHY_TX_REFERENCE_MASK;
|
||||
reg |= PHY_TX_REFERENCE_VAL;
|
||||
writel(reg, USB2_PHY_BASE_REG + PHY_ANA_CFG_4_OFFSET);
|
||||
|
||||
/* Squlech reference voltage sel */
|
||||
reg = readl(USB2_PHY_BASE_REG + PHY_ANA_CFG_4_OFFSET);
|
||||
reg &= ~PHY_SQUELCH_REFERENCE_MASK;
|
||||
reg |= PHY_SQUELCH_REFERENCE_VAL;
|
||||
writel(reg, USB2_PHY_BASE_REG + PHY_ANA_CFG_4_OFFSET);
|
||||
}
|
||||
|
||||
void usb2_trim_config(void)
|
||||
{
|
||||
unsigned int ret;
|
||||
unsigned int reg;
|
||||
unsigned int trim_val;
|
||||
|
||||
ret = readl(USB_TRIM_BASE_REG);
|
||||
trim_val = (ret & USB_TRIM_VAL_MASK); /* get usb trim value */
|
||||
reg = readl(USB2_PHY_BASE_REG + USB2_TRIM_OFFSET);
|
||||
reg &= ~USB2_TRIM_MASK;
|
||||
if ((trim_val >= USB_TRIM_VAL_MIN) && (trim_val <= USB_TRIM_VAL_MAX))
|
||||
reg |= usb2_trim_val(trim_val);
|
||||
else
|
||||
reg |= usb2_trim_val(USB2_TRIM_DEFAULT_VAL);
|
||||
|
||||
writel(reg, USB2_PHY_BASE_REG + USB2_TRIM_OFFSET);
|
||||
}
|
||||
|
||||
void phy_usb_init_crg_clk(int index)
|
||||
{
|
||||
unsigned int reg;
|
||||
|
||||
/* set usb2 CRG default val */
|
||||
reg = USB2_CRG_DEFAULT_VAL;
|
||||
writel(reg, CRG_REG_BASE + USB2_CTRL);
|
||||
udelay(U_LEVEL6);
|
||||
|
||||
/* open UTMI clk */
|
||||
reg = readl(CRG_REG_BASE + USB2_CTRL);
|
||||
reg |= USB2_UTMI_CKEN;
|
||||
writel(reg, CRG_REG_BASE + USB2_CTRL);
|
||||
|
||||
/* open phy apb clk */
|
||||
reg = readl(CRG_REG_BASE + USB2_CTRL);
|
||||
reg |= USB2_PHY_APB_CKEN;
|
||||
writel(reg, CRG_REG_BASE + USB2_CTRL);
|
||||
|
||||
/* open ctrl ref clk */
|
||||
reg = readl(CRG_REG_BASE + USB2_CTRL);
|
||||
reg |= USB2_REF_CKEN;
|
||||
writel(reg, CRG_REG_BASE + USB2_CTRL);
|
||||
|
||||
/* open bus clk */
|
||||
reg = readl(CRG_REG_BASE + USB2_CTRL);
|
||||
reg |= USB2_BUS_CKEN;
|
||||
writel(reg, CRG_REG_BASE + USB2_CTRL);
|
||||
|
||||
/* open phy pll clk */
|
||||
reg = readl(CRG_REG_BASE + USB2_CTRL);
|
||||
reg |= USB2_PHY_PLL_CKEN;
|
||||
writel(reg, CRG_REG_BASE + USB2_CTRL);
|
||||
|
||||
/* open phy xtal clk */
|
||||
reg = readl(CRG_REG_BASE + USB2_CTRL);
|
||||
reg |= USB2_PHY_XTAL_CKEN;
|
||||
writel(reg, CRG_REG_BASE + USB2_CTRL);
|
||||
|
||||
/* freeclk_cksel_free */
|
||||
reg = readl(CRG_REG_BASE + USB2_CTRL);
|
||||
reg |= USB2_FREECLK_CKSEL;
|
||||
writel(reg, CRG_REG_BASE + USB2_CTRL);
|
||||
|
||||
udelay(U_LEVEL5);
|
||||
}
|
||||
|
||||
void phy_usb_init(int index)
|
||||
{
|
||||
unsigned int reg;
|
||||
|
||||
/* VBUS config */
|
||||
reg = USB_VBUS_IO_CONFIG_VAL;
|
||||
writel(reg, USB_VBUS_IO_CONFIG_REG);
|
||||
|
||||
reg = USB_PWREN_CONFIG_VAL;
|
||||
writel(reg, USB_PWREN_CONFIG_REG);
|
||||
|
||||
/* init crg and clk */
|
||||
phy_usb_init_crg_clk(index);
|
||||
|
||||
/* release phy apb */
|
||||
reg = readl(CRG_REG_BASE + USB2_CTRL);
|
||||
reg &= ~USB2_PHY_APB_RST;
|
||||
writel(reg, CRG_REG_BASE + USB2_CTRL);
|
||||
|
||||
udelay(U_LEVEL5);
|
||||
|
||||
/* por noreset */
|
||||
reg = readl(CRG_REG_BASE + USB2_CTRL);
|
||||
reg &= ~USB2_PHY_REQ;
|
||||
writel(reg, CRG_REG_BASE + USB2_CTRL);
|
||||
|
||||
reg = readl(USB2_PHY_BASE_REG + PHY_PLL_OFFSET);
|
||||
reg &= ~RG_PLL_EN_MASK;
|
||||
reg |= RG_PLL_EN_VAL;
|
||||
writel(reg, USB2_PHY_BASE_REG + PHY_PLL_OFFSET);
|
||||
|
||||
udelay(U_LEVEL10);
|
||||
|
||||
/* cancel TPOR */
|
||||
reg = readl(CRG_REG_BASE + USB2_CTRL);
|
||||
reg &= ~USB2_PHY_PORT_TREQ;
|
||||
writel(reg, CRG_REG_BASE + USB2_CTRL);
|
||||
udelay(U_LEVEL6);
|
||||
|
||||
/* vcc reset */
|
||||
reg = readl(CRG_REG_BASE + USB2_CTRL);
|
||||
reg &= ~USB2_VCC_SRST_REQ;
|
||||
writel(reg, CRG_REG_BASE + USB2_CTRL);
|
||||
|
||||
/* USB2 Controller configs */
|
||||
reg = readl(USB3_CTRL_REG_BASE + REG_GUSB3PIPECTL0);
|
||||
reg |= PCS_SSP_SOFT_RESET;
|
||||
writel(reg, USB3_CTRL_REG_BASE + REG_GUSB3PIPECTL0);
|
||||
udelay(U_LEVEL2);
|
||||
|
||||
reg = readl(USB3_CTRL_REG_BASE + REG_GCTL);
|
||||
reg &= ~PORT_CAP_DIR;
|
||||
reg |= PORT_SET_HOST; /* [13:12] 01: Host; 10: Device; 11: OTG */
|
||||
writel(reg, USB3_CTRL_REG_BASE + REG_GCTL);
|
||||
udelay(U_LEVEL2);
|
||||
|
||||
reg = readl(USB3_CTRL_REG_BASE + REG_GUSB3PIPECTL0);
|
||||
reg &= ~PCS_SSP_SOFT_RESET;
|
||||
reg &= ~PORT_DISABLE_SUSPEND; /* disable suspend */
|
||||
writel(reg, USB3_CTRL_REG_BASE + REG_GUSB3PIPECTL0);
|
||||
udelay(U_LEVEL2);
|
||||
|
||||
writel(USB2_G_TXTHRCFG, USB3_CTRL_REG_BASE + GTXTHRCFG);
|
||||
writel(USB2_G_RXTHRCFG, USB3_CTRL_REG_BASE + GRXTHRCFG);
|
||||
udelay(U_LEVEL2);
|
||||
|
||||
/* USB2 eye config */
|
||||
usb2_eye_config();
|
||||
|
||||
/* USB2 trim config */
|
||||
usb2_trim_config();
|
||||
}
|
||||
EXPORT_SYMBOL(phy_usb_init);
|
||||
|
||||
void xhci_hcd_stop(int index)
|
||||
{
|
||||
unsigned int reg;
|
||||
|
||||
/* por noreset */
|
||||
reg = readl(CRG_REG_BASE + USB2_CTRL);
|
||||
reg |= USB2_PHY_REQ;
|
||||
writel(reg, CRG_REG_BASE + USB2_CTRL);
|
||||
|
||||
udelay(U_LEVEL10);
|
||||
|
||||
/* cancel TPOR */
|
||||
reg = readl(CRG_REG_BASE + USB2_CTRL);
|
||||
reg |= USB2_PHY_PORT_TREQ;
|
||||
writel(reg, CRG_REG_BASE + USB2_CTRL);
|
||||
udelay(U_LEVEL6);
|
||||
|
||||
/* vcc reset */
|
||||
reg = readl(CRG_REG_BASE + USB2_CTRL);
|
||||
reg |= USB2_VCC_SRST_REQ;
|
||||
writel(reg, CRG_REG_BASE + USB2_CTRL);
|
||||
}
|
||||
EXPORT_SYMBOL(xhci_hcd_stop);
|
||||
#endif
|
||||
@@ -0,0 +1,313 @@
|
||||
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Copyright (c) LOTUS. All rights reserved.
|
||||
*/
|
||||
#include <asm/arch/platform.h>
|
||||
#include <dm.h>
|
||||
#include <linux/lotus/usb.h>
|
||||
#include <usb.h>
|
||||
#include <usb/xhci.h>
|
||||
#include "phy-usb.h"
|
||||
|
||||
#ifdef __USB_DEBUG
|
||||
#define USB_DEBUG(...) printf(__VA_ARGS__)
|
||||
#else
|
||||
#define USB_DEBUG(...)
|
||||
#endif
|
||||
|
||||
#define GTXTHRCFG 0xc108
|
||||
#define GRXTHRCFG 0xc10c
|
||||
#define REG_GCTL 0xc110
|
||||
#define GUSB2PHYCFG 0xc200
|
||||
#define REG_GUSB3PIPECTL0 0xc2c0
|
||||
#define PCS_SSP_SOFT_RESET (0x1 << 31)
|
||||
#define USBTRDTIM_MASK 0x00003c00
|
||||
#define USBTRDTIM_16BIT (0x5 << 10)
|
||||
#define USBTRDTIM_8BIT (0x9 << 10)
|
||||
#define PHYIF (0x1 << 3)
|
||||
#define SUSPENDUSB20 (0x1 << 6)
|
||||
#define ENBLSLPM (0x1 << 8)
|
||||
#define LSIPD_MASK (0x7 << 19)
|
||||
#define LSIPD_3_BIT (0x2 << 19)
|
||||
#define U2_FREECLK_EXISTS (0x1 << 30)
|
||||
|
||||
#define PORT_CAP_DIR (0x3 << 12)
|
||||
#define PORT_SET_HOST (0x1 << 12)
|
||||
#define PORT_DISABLE_SUSPEND (0x1 << 17)
|
||||
|
||||
#define USB2_G_TXTHRCFG 0x23100000
|
||||
#define USB2_G_RXTHRCFG 0x23100000
|
||||
|
||||
/* CRG PERI_CRG80: usb clk & reset, offset 0x140 */
|
||||
#define USB2_CTRL 0x140
|
||||
#define USB2_CRG_DEF_VAL 0x1307
|
||||
#define USB2_PHY_CFG_REQ (0x1 << 0)
|
||||
#define USB2_PHY_PORT_SRST (0x1 << 1)
|
||||
#define USB2_PHY_XTAL_CKEN (0x1 << 2)
|
||||
#define USB2_VCC_SRST_REQ (0x1 << 3)
|
||||
#define USB2_BUS_CKEN (0x1 << 8)
|
||||
#define USB2_REF_CKEN (0x1 << 9)
|
||||
#define USB2_UTMI_CKEN (0x1 << 12)
|
||||
#define USB2_FREECLK_CKSEL (0x1 << 13)
|
||||
|
||||
/* u2 ctrl use reg, reg's misc_base=0x12028000 */
|
||||
#define MISC_USB2_CTRL_REG 0x24
|
||||
#define U2_BUS_FILTER_BYPASS (0xf << 0)
|
||||
#define U2_PWREN_MODE_MASK (0x1 << 4)
|
||||
#define U2_PWREN_MODE_MISC (0x0 << 4)
|
||||
#define U2_PWREN_MODE_CTRL (0x1 << 4)
|
||||
#define U2_OVERCURR_MODE_MASK (0x1 << 5)
|
||||
#define U2_OVERCURR_MODE_DISABLE (0x0 << 5)
|
||||
#define U2_OVERCURR_MODE_ENABLE (0x1 << 5)
|
||||
#define U2_PWREN_MASK (0x1 << 6)
|
||||
#define U2_PWREN_ON (0x0 << 6)
|
||||
#define U2_PWREN_OFF (0x1 << 6)
|
||||
#define U2_OVERCURR_MASK (0x1 << 7)
|
||||
#define U2_OVERCURR_ON (0x0 << 7)
|
||||
#define U2_OVERCURR_OFF (0x1 << 7)
|
||||
#define U2_FLADJ_30MHZ (0x20 << 13)
|
||||
|
||||
/* Outer phy use reg, reg's misc_base=0x12028000 */
|
||||
#define MISC_OUT_PHY2_CTRL_REG 0x2C
|
||||
#define USB2_PLL_EN (0x1 << 0)
|
||||
#define USB2_UTMI_DATABUS16_8 (0x1 << 1)
|
||||
|
||||
#define MISC_USB2_VBUS_REG 0x980
|
||||
#define U2_VBUS_EN (0x1 << 0)
|
||||
|
||||
static uintptr_t xhci_base;
|
||||
|
||||
int xhci_hcd_init(int index, struct xhci_hccr **hccr, struct xhci_hcor **hcor)
|
||||
{
|
||||
if ((hccr == NULL) || (hcor == NULL))
|
||||
return -EINVAL;
|
||||
|
||||
xhci_base = USB2_CTRL_REG_BASE;
|
||||
|
||||
*hccr = (struct xhci_hccr *)(xhci_base);
|
||||
*hcor = (struct xhci_hcor *)((uintptr_t) *hccr +
|
||||
HC_LENGTH(xhci_readl(&(*hccr)->cr_capbase)));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void xhci_hcd_stop(int index)
|
||||
{
|
||||
unsigned int reg;
|
||||
|
||||
/* por reset */
|
||||
reg = readl(CRG_REG_BASE + USB2_CTRL);
|
||||
reg |= USB2_PHY_CFG_REQ;
|
||||
reg |= USB2_PHY_PORT_SRST;
|
||||
writel(reg, CRG_REG_BASE + USB2_CTRL);
|
||||
udelay(U_LEVEL10);
|
||||
|
||||
/* vcc reset */
|
||||
reg = readl(CRG_REG_BASE + USB2_CTRL);
|
||||
reg |= USB2_VCC_SRST_REQ;
|
||||
writel(reg, CRG_REG_BASE + USB2_CTRL);
|
||||
}
|
||||
EXPORT_SYMBOL(xhci_hcd_stop);
|
||||
|
||||
static void usb2_eye_config(void)
|
||||
{
|
||||
}
|
||||
|
||||
static void usb2_trim_config(void)
|
||||
{
|
||||
}
|
||||
|
||||
static void lotus_u2_phy_clk_init(void)
|
||||
{
|
||||
unsigned int reg;
|
||||
|
||||
/* enable u2 phy clk and do reset */
|
||||
reg = (USB2_PHY_CFG_REQ | USB2_PHY_PORT_SRST | \
|
||||
USB2_PHY_XTAL_CKEN);
|
||||
writel(reg, CRG_REG_BASE + USB2_CTRL);
|
||||
USB_DEBUG("USB2_CRG(0x12010140), expext:0x7,"
|
||||
"acutal:0x%X\n", readl(CRG_REG_BASE + USB2_CTRL));
|
||||
|
||||
udelay(1);
|
||||
|
||||
/* undo phy reset */
|
||||
reg = readl(CRG_REG_BASE + USB2_CTRL);
|
||||
reg &= ~USB2_PHY_CFG_REQ;
|
||||
reg &= ~USB2_PHY_PORT_SRST;
|
||||
writel(reg, CRG_REG_BASE + USB2_CTRL);
|
||||
USB_DEBUG("USB2_CRG(0x12010140), expext:0x4,"
|
||||
"acutal:0x%X\n", readl(CRG_REG_BASE + USB2_CTRL));
|
||||
}
|
||||
|
||||
static void lotus_u2_ctrl_clk_init(void)
|
||||
{
|
||||
unsigned int reg;
|
||||
|
||||
/* enable u2 ctrl clk and do reset */
|
||||
reg = readl(CRG_REG_BASE + USB2_CTRL);
|
||||
reg |= (USB2_VCC_SRST_REQ | USB2_BUS_CKEN | USB2_REF_CKEN |
|
||||
USB2_UTMI_CKEN);
|
||||
writel(reg, CRG_REG_BASE + USB2_CTRL);
|
||||
USB_DEBUG("USB2_CRG(0x12010140), expext:0x130c,"
|
||||
"acutal:0x%X\n", readl(CRG_REG_BASE + USB2_CTRL));
|
||||
|
||||
udelay(1);
|
||||
|
||||
/* undo ctrl vcc reset */
|
||||
reg = readl(CRG_REG_BASE + USB2_CTRL);
|
||||
reg &= ~USB2_VCC_SRST_REQ;
|
||||
writel(reg, CRG_REG_BASE + USB2_CTRL);
|
||||
USB_DEBUG("USB2_CRG(0x12010140), expext:0x1304,"
|
||||
"acutal:0x%X\n", readl(CRG_REG_BASE + USB2_CTRL));
|
||||
}
|
||||
|
||||
static void lotus_usb2_clk_init(void)
|
||||
{
|
||||
lotus_u2_phy_clk_init();
|
||||
lotus_u2_ctrl_clk_init();
|
||||
}
|
||||
|
||||
static void lotus_u2_phy_misc_config(void)
|
||||
{
|
||||
unsigned int reg;
|
||||
|
||||
/* MISC_CTRL11: config 0x3 according to IC. */
|
||||
reg = 0;
|
||||
reg |= (USB2_PLL_EN | USB2_UTMI_DATABUS16_8);
|
||||
writel(reg, MISC_REG_BASE + MISC_OUT_PHY2_CTRL_REG);
|
||||
USB_DEBUG("MISC_OUT_PHY2_CTRL_REG(0x1202802c),"
|
||||
"expext:0x3, acutal:0x%X\n",
|
||||
readl(MISC_REG_BASE + MISC_OUT_PHY2_CTRL_REG));
|
||||
}
|
||||
|
||||
static inline void lotus_usb2_vbus_valid_en(void)
|
||||
{
|
||||
unsigned int reg;
|
||||
|
||||
/* enable U2 vbus valid */
|
||||
reg = readl(MISC_REG_BASE + MISC_USB2_VBUS_REG);
|
||||
reg |= U2_VBUS_EN;
|
||||
writel(reg, MISC_REG_BASE + MISC_USB2_VBUS_REG);
|
||||
}
|
||||
|
||||
static void lotus_dwc_data_width_config(enum usb_phy_width_mode mode)
|
||||
{
|
||||
unsigned int reg, base = USB2_CTRL_REG_BASE;
|
||||
|
||||
/* GUSB2PHYCFG register: config val 0x40001548(HS/FS) or
|
||||
* 0x00381548(LS) according to IC.
|
||||
*
|
||||
* USBTRDTIM: USB 2.0 Turnaround Time in PHY clocks.
|
||||
* PHYIF: 1 UTMI+ PHY is 16-bit, 0 UTMI+ PHY is 8-bit.
|
||||
*/
|
||||
reg = 0;
|
||||
switch (mode) {
|
||||
case PHY_WIDTH_8BIT:
|
||||
/* 8-bits usb phy configure */
|
||||
reg |= USBTRDTIM_8BIT;
|
||||
reg &= ~PHYIF;
|
||||
break;
|
||||
case PHY_WIDTH_16BIT:
|
||||
/* 16-bits usb phy configure */
|
||||
reg |= (USBTRDTIM_16BIT | PHYIF);
|
||||
break;
|
||||
default:
|
||||
printf("usb phy width config val error.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
writel(reg, base + GUSB2PHYCFG);
|
||||
}
|
||||
|
||||
static void lotus_dwc_usb_config(void)
|
||||
{
|
||||
unsigned int reg, base = USB2_CTRL_REG_BASE;
|
||||
|
||||
reg = readl(base + REG_GUSB3PIPECTL0);
|
||||
reg |= PCS_SSP_SOFT_RESET;
|
||||
writel(reg, base + REG_GUSB3PIPECTL0);
|
||||
udelay(U_LEVEL2);
|
||||
|
||||
reg = readl(base + REG_GCTL);
|
||||
reg &= ~PORT_CAP_DIR;
|
||||
reg |= PORT_SET_HOST; /* host mode is default. */
|
||||
writel(reg, base + REG_GCTL);
|
||||
udelay(U_LEVEL2);
|
||||
|
||||
reg = readl(base + REG_GUSB3PIPECTL0);
|
||||
reg &= ~PCS_SSP_SOFT_RESET;
|
||||
reg &= ~PORT_DISABLE_SUSPEND; /* disable suspend */
|
||||
writel(reg, base + REG_GUSB3PIPECTL0);
|
||||
udelay(U_LEVEL2);
|
||||
|
||||
writel(USB2_G_TXTHRCFG, base + GTXTHRCFG);
|
||||
writel(USB2_G_RXTHRCFG, base + GRXTHRCFG);
|
||||
udelay(U_LEVEL2);
|
||||
|
||||
lotus_dwc_data_width_config(PHY_WIDTH_16BIT);
|
||||
|
||||
reg = readl(base + GUSB2PHYCFG);
|
||||
reg &= ~SUSPENDUSB20; /* disable suspend */
|
||||
reg &= ~ENBLSLPM; /* suspend not transfer to external PHY */
|
||||
reg &= ~U2_FREECLK_EXISTS;
|
||||
reg &= ~LSIPD_MASK;
|
||||
reg |= LSIPD_3_BIT;
|
||||
writel(reg, base + GUSB2PHYCFG);
|
||||
|
||||
USB_DEBUG("GUSB2PHYCFG(0xc200):0x%X\n",
|
||||
readl(base + GUSB2PHYCFG));
|
||||
USB_DEBUG("GUSB3PIPECTL0(0xc2c0):0x%X\n",
|
||||
readl(base + REG_GUSB3PIPECTL0));
|
||||
USB_DEBUG("GCTL(0xc110):0x%X\n",
|
||||
readl(base + REG_GCTL));
|
||||
}
|
||||
|
||||
void phy_usb_init(int index)
|
||||
{
|
||||
/* u2 utmi data bus config, u2 phy pll en */
|
||||
lotus_u2_phy_misc_config();
|
||||
|
||||
/* init crg and clk */
|
||||
lotus_usb2_clk_init();
|
||||
|
||||
/* enable u2 vbusvalid */
|
||||
lotus_usb2_vbus_valid_en();
|
||||
|
||||
/* USB2 Controller configs */
|
||||
lotus_dwc_usb_config();
|
||||
|
||||
/* lotus phy eye diagram config */
|
||||
usb2_eye_config();
|
||||
|
||||
/* lotus phy trim config */
|
||||
usb2_trim_config();
|
||||
}
|
||||
EXPORT_SYMBOL(phy_usb_init);
|
||||
|
||||
/* Some usb device can't be recognize in U-boot because
|
||||
* of error state, so we power off them and wait a delay
|
||||
* to restart them, similar to unplug and then insert.
|
||||
*/
|
||||
void lotus_usb_pwren_control(void)
|
||||
{
|
||||
u32 reg, default_val, delay;
|
||||
|
||||
delay = CONFIG_USB_PWREN_OFF_DELAY ?
|
||||
CONFIG_USB_PWREN_OFF_DELAY : 100;
|
||||
|
||||
reg = readl(MISC_REG_BASE + MISC_USB2_CTRL_REG);
|
||||
default_val = reg;
|
||||
reg &= ~U2_PWREN_MODE_MASK; /* set PWREN_MISC mode */
|
||||
reg |= U2_PWREN_OFF; /* turn off PWREN */
|
||||
writel(reg, MISC_REG_BASE + MISC_USB2_CTRL_REG);
|
||||
|
||||
mdelay(delay);
|
||||
|
||||
reg = readl(MISC_REG_BASE + MISC_USB2_CTRL_REG);
|
||||
reg &= ~U2_PWREN_MASK; /* turn on PWREN */
|
||||
writel(reg, MISC_REG_BASE + MISC_USB2_CTRL_REG);
|
||||
|
||||
reg = default_val; /* set PWREN_CTRL mode(default val) */
|
||||
writel(reg, MISC_REG_BASE + MISC_USB2_CTRL_REG);
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user