GK SDK 源码库: XMIPCLinuxV100R005C00SPC030 (kernel/tools/open_source excluded)

This commit is contained in:
lai
2026-09-06 03:52:57 +08:00
commit b1928b41c0
21813 changed files with 4413081 additions and 0 deletions
+163
View File
@@ -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
View File
@@ -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/
+3
View File
@@ -0,0 +1,3 @@
obj-y += param_tags.o
obj-y += atags.o
+144
View File
@@ -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
View File
@@ -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
View File
@@ -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
+7
View File
@@ -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/
+17
View File
@@ -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"
+21
View File
@@ -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__ */
@@ -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__ */
@@ -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, &reg);
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, &reg);
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, &reg);
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, &reg);
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, &reg);
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;
}
@@ -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__ */
@@ -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 */
@@ -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__ */
@@ -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 */
@@ -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");
}
@@ -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;
}
@@ -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;
}
@@ -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 */
@@ -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;
}
@@ -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 */
@@ -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;
}
@@ -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 */
@@ -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;
}
@@ -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, &reg)) {
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, &reg)) {
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 -- rmii1 -- 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
+26
View File
@@ -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