野火和nxp的imx6ull都采用KSZ8081网卡,电路图一致。
ENET1_nRST复位引脚连接在了74lv595上,因此需要先驱动74lv595。
74lv595利用BOOT_MODE0、BOOT_MODE1、SNVS_TAMPER7、SNVS_TAMPER8四个引脚最多可以驱动8个引脚,但是野火的输出端只连接了两个。
BOOT_MODE0、BOOT_MODE1、SNVS_TAMPER8四个引脚的复用功能寄存器如上图。需要将它们复用成如下引脚:
BOOT_MODE0——> GPIO5_IO10、
BOOT_MODE1——> GPIO5_IO11、
SNVS_TAMPER7——> GPIO5_IO07、
SNVS_TAMPER8——> GPIO5_IO08
关于这四个引脚是如何复位的,我参考了正点原子“蜂鸣器实验”章节,其原理图如下:
这里需要将SNVS_TAMPER1复用为——> GPIO5_IO01,其源码如下:
void beep_init(void)
{
/* 1、初始化IO复用,复用为GPIO5_IO01 */
IOMUXC_SetPinMux(IOMUXC_SNVS_SNVS_TAMPER1_GPIO5_IO01,0);
/* 2、、配置GPIO1_IO03的IO属性
*bit 16:0 HYS关闭
*bit [15:14]: 00 默认下拉
*bit [13]: 0 kepper功能
*bit [12]: 1 pull/keeper使能
*bit [11]: 0 关闭开路输出
*bit [7:6]: 10 速度100Mhz
*bit [5:3]: 110 R0/6驱动能力
*bit [0]: 0 低转换率
*/
IOMUXC_SetPinConfig(IOMUXC_SNVS_SNVS_TAMPER1_GPIO5_IO01,0X10B0);
/* 3、初始化GPIO,GPIO5_IO01设置为输出 */
GPIO5->GDIR |= (1 << 1);
/* 4、设置GPIO5_IO01输出高电平,关闭蜂鸣器 */
GPIO5->DR |= (1 << 1);
}
/*
* @description : 蜂鸣器控制函数,控制蜂鸣器打开还是关闭
* @param - status : 0,关闭蜂鸣器,1 打开蜂鸣器
* @return : 无
*/
void beep_switch(int status)
{
if(status == ON)
GPIO5->DR &= ~(1 << 1); /* 打开蜂鸣器 */
else if(status == OFF)
GPIO5->DR |= (1 << 1); /* 关闭蜂鸣器 */
}
这里顺带讲一下DR 寄存器和GDIR 寄存器。
此寄存器是 32 位的,一个 GPIO 组最大只有 32 个 IO,因此 DR 寄存器中的每个位都对应 一个 GPIO。当 GPIO 被配置为输出功能以后,向指定的位写入数据那么相应的 IO 就会输出相 应的高低电平,比如要设置 GPIO1_IO00 输出高电平,那么就应该设置 GPIO1.DR=1。当GPIO被配置为输入模式以后,此寄存器就保存着对应 IO 的电平值,每个位对对应一个 GPIO,例如, 当 GPIO1_IO00 这个引脚接地的话,那么 GPIO1.DR 的 bit0 就是 0。
看完 DR 寄存器,接着看 GDIR 寄存器,这是方向寄存器,用来设置某个 GPIO 的工作方向的,即输入/输出,GDIR 寄存器结构如图 8.1.5.3 所示:
GDIR 寄存器也是 32 位的,此寄存器用来设置某个 IO 的工作方向,是输入还是输出。同样的,每个 IO 对应一个位,如果要设置 GPIO 为输入的话就设置相应的位为 0,如果要设置为
输出的话就设置为 1。比如要设置 GPIO1_IO00 为输入,那么 GPIO1.GDIR=0;
/*!
* @brief Sets the IOMUXC pin mux mode.
* @note The first five parameters can be filled with the pin function ID macros.
*
* This is an example to set the ENET1_RX_DATA0 Pad as FLEXCAN1_TX:
* @code
* IOMUXC_SetPinMux(IOMUXC_ENET1_RX_DATA0_FLEXCAN1_TX, 0);
* @endcode
*
* This is an example to set the GPIO1_IO02 Pad as I2C1_SCL:
* @code
* IOMUXC_SetPinMux(IOMUXC_GPIO1_IO02_I2C1_SCL, 0);
* @endcode
*
* @param muxRegister The pin mux register.
* @param muxMode The pin mux mode.
* @param inputRegister The select input register.
* @param inputDaisy The input daisy.
* @param configRegister The config register.
* @param inputOnfield Software input on field.
*/
static inline void IOMUXC_SetPinMux(uint32_t muxRegister,
uint32_t muxMode,
uint32_t inputRegister,
uint32_t inputDaisy,
uint32_t configRegister,
uint32_t inputOnfield)
{
*((volatile uint32_t *)muxRegister) =
IOMUXC_SW_MUX_CTL_PAD_MUX_MODE(muxMode) | IOMUXC_SW_MUX_CTL_PAD_SION(inputOnfield);
if (inputRegister)
{
*((volatile uint32_t *)inputRegister) = IOMUXC_SELECT_INPUT_DAISY(inputDaisy);
}
}
/*!
* @brief Sets the IOMUXC pin configuration.
* @note The previous five parameters can be filled with the pin function ID macros.
*
* This is an example to set pin configuration for IOMUXC_GPIO1_IO02_I2C1_SCL:
* @code
* IOMUXC_SetPinConfig(IOMUXC_GPIO1_IO02_I2C1_SCL, IOMUXC_SW_PAD_CTL_PAD_PUE_MASK | IOMUXC_SW_PAD_CTL_PAD_PUS(2U));
* @endcode
*
* @param muxRegister The pin mux register.
* @param muxMode The pin mux mode.
* @param inputRegister The select input register.
* @param inputDaisy The input daisy.
* @param configRegister The config register.
* @param configValue The pin config value.
*/
static inline void IOMUXC_SetPinConfig(uint32_t muxRegister,
uint32_t muxMode,
uint32_t inputRegister,
uint32_t inputDaisy,
uint32_t configRegister,
uint32_t configValue)
{
if (configRegister)
{
*((volatile uint32_t *)configRegister) = configValue;
}
}
for example
IOMUXC_SetPinMux(IOMUXC_GPIO1_IO03_GPIO1_IO03, 0);
IOMUXC_SetPinConfig(IOMUXC_GPIO1_IO03_GPIO1_IO03, 0X10B0);
这 里 使 用 了 两 个 函 数 IOMUXC_SetPinMux 和 IOMUXC_SetPinConfig , 其 中 函 数 IOMUXC_SetPinMux 是 用 来 设 置 IO 复 用 功 能 的 , 最 终 肯 定 设 置 的 是 寄 存 器 “IOMUXC_SW_MUX_CTL_PAD_XX”。函数 IOMUXC_SetPinConfig 设置的是 IO 的上下拉、 速度等的,也就是寄存器“IOMUXC_SW_PAD_CTL_PAD_XX”。
函数 IOMUXC_SetPinMux 有 6 个参数,这 6 个参数的函数如下:
muxRegister : IO 的 复 用 寄 存 器 地 址 , 比 如 GPIO1_IO03 的 IO 复 用 寄 存 器 SW_MUX_CTL_PAD_GPIO1_IO03 的地址为 0X020E0068。
muxMode: IO 复用值,也就是 ALT0~ALT8,对应数字 0~8,比如要将 GPIO1_IO03 设置 为 GPIO 功能的话此参数就要设置为 5。
inputRegister:外设输入 IO 选择寄存器地址,有些 IO 在设置为其他的复用功能以后还需 要设置 IO 输入寄存器,比如 GPIO1_IO03 要复用为 UART1_RX 的话还需要设置寄存器 UART1_RX_DATA_SELECT_INPUT,此寄存器地址为 0X020E0624。
inputDaisy:寄存器 inputRegister 的值,比如 GPIO1_IO03 要作为 UART1_RX 引脚的话此 参数就是 1。
configRegister:未使用,函数 IOMUXC_SetPinConfig 会使用这个寄存器。
inputOnfield : IO 软 件 输 入 使 能 , 以 GPIO1_IO03 为 例 就 是 寄 存 器 SW_MUX_CTL_PAD_GPIO1_IO03 的 SION 位(bit4)。如果需要使能 GPIO1_IO03 的软件输入功能的话此参数应该为 1,否则的话就为 0。
函数 IOMUXC_SetPinConfig 有 6 个参数,其中前五个参数和函数 IOMUXC_SetPinMux 一 样,但是此函数只使用了参数 configRegister 和 configValue,cofigRegister 参数是 IO 配置寄存 器地址,比如 GPIO1_IO03 的 IO 配置寄存器为 IOMUXC_SW_PAD_CTL_PAD_GPIO1_IO03, 其地址为 0X020E02F4,参数 configValue 就是要写入到寄存器 configRegister 的值。同理,“示例代码 12.3.3.1”的 43 行展开以后就是:
IOMUXC_SetPinConfig(0x020E0068U, 0x5U, 0x00000000U, 0x0U, 0x020E02F4U, 0X10B0);
根据函数 IOMUXC_SetPinConfig 的源码可以知道,上面函数就是将寄存器 0x020E02F4 的值设置为 0X10B0。函数 IOMUXC_SetPinMux 和 IOMUXC_SetPinConfig 就讲解到这里,我们
以后就可以使用这两个函数来方便的配置 IO 的复用功能和 IO 配置。
art-pi smart里的imx6ull_iomuxc和上面两个函数类似,但也存在一些差别:
struct imx6ull_iomuxc
{
rt_uint32_t muxRegister;
rt_uint32_t muxMode;
rt_uint32_t inputRegister;
rt_uint32_t inputDaisy;
rt_uint32_t configRegister;
rt_uint32_t inputOnfield;
rt_uint32_t configValue;
};
void imx6ull_gpio_init(const struct imx6ull_iomuxc *gpio)
{
rt_uint32_t mux_reg_vaddr = 0;
rt_uint32_t input_reg_vaddr = 0;
rt_uint32_t config_reg_vaddr = 0;
mux_reg_vaddr = (rt_uint32_t)(gpio->muxRegister ? (rt_uint32_t)imx6ull_get_periph_vaddr(gpio->muxRegister) : gpio->muxRegister);
input_reg_vaddr = (rt_uint32_t)(gpio->inputRegister ? (rt_uint32_t)imx6ull_get_periph_vaddr(gpio->inputRegister) : gpio->inputRegister);
config_reg_vaddr = (rt_uint32_t)(gpio->configRegister ? (rt_uint32_t)imx6ull_get_periph_vaddr(gpio->configRegister) : gpio->configRegister);
IOMUXC_SetPinMux(mux_reg_vaddr, gpio->muxMode, input_reg_vaddr, gpio->inputDaisy, config_reg_vaddr, gpio->inputOnfield);
IOMUXC_SetPinConfig(mux_reg_vaddr, gpio->muxMode, input_reg_vaddr, gpio->inputDaisy, config_reg_vaddr, gpio->configValue);
}
#define IOMUXC_GPIO1_IO06_ENET1_MDIO 0x020E0074U, 0x0U, 0x020E0578U, 0x0U, 0x020E0300U
#define IOMUXC_GPIO1_IO07_ENET1_MDC 0x020E0078U, 0x0U, 0x00000000U, 0x0U, 0x020E0304U
static struct imx6ull_iomuxc mdio_gpio[2] =
{
{IOMUXC_GPIO1_IO06_ENET1_MDIO,0U,0xB029},
{IOMUXC_GPIO1_IO07_ENET1_MDC,0U,0xB0E9}
};
完全展开后是:
static struct imx6ull_iomuxc mdio_gpio[2] =
{
{0x020E0074U, 0x0U, 0x020E0578U, 0x0U, 0x020E0300U,0U,0xB029},
{0x020E0078U, 0x0U, 0x00000000U, 0x0U, 0x020E0304U,0U,0xB0E9}
};
注意点:0xB029(configValue)是用于设置0x020E0300U(configRegister)寄存器的。
nxp官方也提供了以太网的IAR工程,里面正是用到了74lv595,可以在官方的sdk里找到代码。其74lv595相关的代码如下:
#define NXP74LV595_INPUT_STCP_GPIO GPIO5
#define NXP74LV595_INPUT_STCP_PIN 7U
#define NXP74LV595_INPUT_OE_GPIO GPIO5
#define NXP74LV595_INPUT_OE_PIN 8U
#define NXP74LV595_INPUT_SDI_GPIO GPIO5
#define NXP74LV595_INPUT_SDI_PIN 10U
#define NXP74LV595_INPUT_SHCP_GPIO GPIO5
#define NXP74LV595_INPUT_SHCP_PIN 11U
typedef enum _NXP74LV595_signal
{
kSignal_NXP74LV595_Low = 0U,
kSignal_NXP74LV595_High = 1U
} NXP74LV595_signal_t;
typedef enum _NXP74LV595_parOutputPins
{
kNXP74LV595_HDMI_nRST = 0U,
kNXP74LV595_ENET1_nRST = 1U,
kNXP74LV595_ENET2_nRST = 2U,
kNXP74LV595_CAN1_2_STBY = 3U,
kNXP74LV595_BT_nPWD = 4U,
kNXP74LV595_CSI_RST = 5U,
kNXP74LV595_CSI_PWDN = 6U,
kNXP74LV595_LCD_nPWREN = 7U
} NXP74LV595_parOutputPins_t;
/*Initial output value of NXP74LV595*/
static uint8_t s_NXP74LV595Output = 0U;
void Delay(uint32_t ticks)
{
while (ticks--)
{
__NOP();
}
}
/* Set parallel pins output value of 74LV595 */
void BOARD_NXP74LV595_SetValue(NXP74LV595_parOutputPins_t pin, NXP74LV595_signal_t value)
{
uint8_t shiftNumber;
uint8_t mask;
/*Init GPIO pins and open output gate of 74LV595*/
NXP74LV595_INPUT_OE_GPIO->DR &= ~(1U << NXP74LV595_INPUT_OE_PIN);
NXP74LV595_INPUT_OE_GPIO->GDIR |= (1U << NXP74LV595_INPUT_OE_PIN);
NXP74LV595_INPUT_STCP_GPIO->GDIR |= (1U << NXP74LV595_INPUT_STCP_PIN);
NXP74LV595_INPUT_SDI_GPIO->GDIR |= (1U << NXP74LV595_INPUT_SDI_PIN);
NXP74LV595_INPUT_SHCP_GPIO->GDIR |= (1U << NXP74LV595_INPUT_SHCP_PIN);
/*Calculate all parallel pins output value that will be set*/
s_NXP74LV595Output = (s_NXP74LV595Output & (~(1U << pin))) | (value << pin);
for (shiftNumber = 0; shiftNumber < 8; shiftNumber++)
{
/*High data bits transfer first*/
mask = (s_NXP74LV595Output >> (7 - shiftNumber)) & 1U;
if (0 == mask)
{
NXP74LV595_INPUT_SDI_GPIO->DR &= ~(1U << NXP74LV595_INPUT_SDI_PIN);
}
else
{
NXP74LV595_INPUT_SDI_GPIO->DR |= (1U << NXP74LV595_INPUT_SDI_PIN);
}
/*Contents of shift register shifted.*/
NXP74LV595_INPUT_SHCP_GPIO->DR &= ~(1U << NXP74LV595_INPUT_SHCP_PIN);
Delay(1000);
NXP74LV595_INPUT_SHCP_GPIO->DR |= (1U << NXP74LV595_INPUT_SHCP_PIN);
Delay(1000);
}
/*Contents of shift register stages are transferred to the storage register and parallel output stages*/
NXP74LV595_INPUT_STCP_GPIO->DR &= ~(1U << NXP74LV595_INPUT_STCP_PIN);
Delay(1000);
NXP74LV595_INPUT_STCP_GPIO->DR |= (1U << NXP74LV595_INPUT_STCP_PIN);
Delay(1000);
}
uboot里也有以太网驱动的代码,其中74lv595相关的代码如下:
我找的是野火的uboot代码,源码路径为board\freescale\mx6ullevk\mx6ullevk.c
#define IOX_SDI IMX_GPIO_NR(5, 10)
#define IOX_STCP IMX_GPIO_NR(5, 7)
#define IOX_SHCP IMX_GPIO_NR(5, 11)
#define IOX_OE IMX_GPIO_NR(5, 8)
static iomux_v3_cfg_t const iox_pads[] = {
/* IOX_SDI */
MX6_PAD_BOOT_MODE0__GPIO5_IO10 | MUX_PAD_CTRL(NO_PAD_CTRL),
/* IOX_SHCP */
MX6_PAD_BOOT_MODE1__GPIO5_IO11 | MUX_PAD_CTRL(NO_PAD_CTRL),
/* IOX_STCP */
MX6_PAD_SNVS_TAMPER7__GPIO5_IO07 | MUX_PAD_CTRL(NO_PAD_CTRL),
/* IOX_nOE */
MX6_PAD_SNVS_TAMPER8__GPIO5_IO08 | MUX_PAD_CTRL(NO_PAD_CTRL),
};
static enum qn_level seq[3][2] = {
{0, 1}, {1, 1}, {0, 0}
};
static enum qn_func qn_output[8] = {
qn_reset, qn_reset, qn_reset, qn_enable, qn_disable, qn_reset,
qn_disable, qn_disable
};
static void iox74lv_init(void)
{
int i;
gpio_direction_output(IOX_OE, 0);
for (i = 7; i >= 0; i--) {
gpio_direction_output(IOX_SHCP, 0);
gpio_direction_output(IOX_SDI, seq[qn_output[i]][0]);
udelay(500);
gpio_direction_output(IOX_SHCP, 1);
udelay(500);
}
gpio_direction_output(IOX_STCP, 0);
udelay(500);
/*
* shift register will be output to pins
*/
gpio_direction_output(IOX_STCP, 1);
for (i = 7; i >= 0; i--) {
gpio_direction_output(IOX_SHCP, 0);
gpio_direction_output(IOX_SDI, seq[qn_output[i]][1]);
udelay(500);
gpio_direction_output(IOX_SHCP, 1);
udelay(500);
}
gpio_direction_output(IOX_STCP, 0);
udelay(500);
/*
* shift register will be output to pins
*/
gpio_direction_output(IOX_STCP, 1);
};
void iox74lv_set(int index)
{
int i;
for (i = 7; i >= 0; i--) {
gpio_direction_output(IOX_SHCP, 0);
if (i == index)
gpio_direction_output(IOX_SDI, seq[qn_output[i]][0]);
else
gpio_direction_output(IOX_SDI, seq[qn_output[i]][1]);
udelay(500);
gpio_direction_output(IOX_SHCP, 1);
udelay(500);
}
gpio_direction_output(IOX_STCP, 0);
udelay(500);
/*
* shift register will be output to pins
*/
gpio_direction_output(IOX_STCP, 1);
for (i = 7; i >= 0; i--) {
gpio_direction_output(IOX_SHCP, 0);
gpio_direction_output(IOX_SDI, seq[qn_output[i]][1]);
udelay(500);
gpio_direction_output(IOX_SHCP, 1);
udelay(500);
}
gpio_direction_output(IOX_STCP, 0);
udelay(500);
/*
* shift register will be output to pins
*/
gpio_direction_output(IOX_STCP, 1);
};
#ifndef __DRV_74LV595_H__
#define __DRV_74LV595_H__
#include
#include "fsl_phy.h"
#include "imx6ull.h"
#include "drv_common.h"
typedef enum _NXP74LV595_signal
{
kSignal_NXP74LV595_Low = 0U,
kSignal_NXP74LV595_High = 1U
} NXP74LV595_signal_t;
typedef enum _NXP74LV595_parOutputPins
{
kNXP74LV595_HDMI_nRST = 0U,
kNXP74LV595_ENET1_nRST = 1U,
kNXP74LV595_ENET2_nRST = 2U,
kNXP74LV595_CAN1_2_STBY = 3U,
kNXP74LV595_BT_nPWD = 4U,
kNXP74LV595_CSI_RST = 5U,
kNXP74LV595_CSI_PWDN = 6U,
kNXP74LV595_LCD_nPWREN = 7U
} NXP74LV595_parOutputPins_t;
void BOARD_NXP74LV595_SetValue_Init(void);
void BOARD_NXP74LV595_SetValue(NXP74LV595_parOutputPins_t pin, NXP74LV595_signal_t value);
#endif /* __DRV_74LV595_H__ */
#include "drv_74lv595.h"
#define NXP74LV595_INPUT_STCP_GPIO GPIO5
#define NXP74LV595_INPUT_STCP_PIN 7U
#define NXP74LV595_INPUT_OE_GPIO GPIO5
#define NXP74LV595_INPUT_OE_PIN 8U
#define NXP74LV595_INPUT_SDI_GPIO GPIO5
#define NXP74LV595_INPUT_SDI_PIN 10U
#define NXP74LV595_INPUT_SHCP_GPIO GPIO5
#define NXP74LV595_INPUT_SHCP_PIN 11U
/*Initial output value of NXP74LV595*/
static uint8_t s_NXP74LV595Output = 0U;
static struct imx6ull_iomuxc _74lv595_gpio[4] =
{
{IOMUXC_SNVS_BOOT_MODE0_GPIO5_IO10,0U,0x110B0},
{IOMUXC_SNVS_BOOT_MODE1_GPIO5_IO11,0U,0x110B0},
{IOMUXC_SNVS_SNVS_TAMPER7_GPIO5_IO07,0U,0x110B0},
{IOMUXC_SNVS_SNVS_TAMPER8_GPIO5_IO08,0U,0x110B0},
};
void Delay(uint32_t ticks)
{
while (ticks--)
{
__NOP();
//__ASM("nop");
}
}
void BOARD_NXP74LV595_SetValue_Init(void)
{
imx6ull_gpio_init(&_74lv595_gpio[0]);
imx6ull_gpio_init(&_74lv595_gpio[1]);
imx6ull_gpio_init(&_74lv595_gpio[2]);
imx6ull_gpio_init(&_74lv595_gpio[3]);
}
/* Set parallel pins output value of 74LV595 */
//GDIR寄存器输入0,输出1;
void BOARD_NXP74LV595_SetValue(NXP74LV595_parOutputPins_t pin, NXP74LV595_signal_t value)
{
uint8_t shiftNumber;
uint8_t mask;
/*Init GPIO pins and open output gate of 74LV595*/
GPIO_Type *OE_GPIO = (GPIO_Type *)rt_ioremap((void *)GPIO5,0x1000);
GPIO_Type *STCP_GPIO = (GPIO_Type *)rt_ioremap((void *)GPIO5,0x1000);
GPIO_Type *SDI_GPIO = (GPIO_Type *)rt_ioremap((void *)GPIO5,0x1000);
GPIO_Type *SHCP_GPIO = (GPIO_Type *)rt_ioremap((void *)GPIO5,0x1000);
OE_GPIO->DR &= ~(1U << NXP74LV595_INPUT_OE_PIN);
OE_GPIO->GDIR |= (1U << NXP74LV595_INPUT_OE_PIN);
STCP_GPIO->GDIR |= (1U << NXP74LV595_INPUT_STCP_PIN);
SDI_GPIO->GDIR |= (1U << NXP74LV595_INPUT_SDI_PIN);
SHCP_GPIO->GDIR |= (1U << NXP74LV595_INPUT_SHCP_PIN);
/*Calculate all parallel pins output value that will be set*/
s_NXP74LV595Output = (s_NXP74LV595Output & (~(1U << pin))) | (value << pin);
for (shiftNumber = 0; shiftNumber < 8; shiftNumber++)
{
/*High data bits transfer first*/
mask = (s_NXP74LV595Output >> (7 - shiftNumber)) & 1U;
if (0 == mask)
{
SDI_GPIO->DR &= ~(1U << NXP74LV595_INPUT_SDI_PIN);
}
else
{
SDI_GPIO->DR |= (1U << NXP74LV595_INPUT_SDI_PIN);
}
/*Contents of shift register shifted.*/
SHCP_GPIO->DR &= ~(1U << NXP74LV595_INPUT_SHCP_PIN);
Delay(1000);
SHCP_GPIO->DR |= (1U << NXP74LV595_INPUT_SHCP_PIN);
Delay(1000);
}
/*Contents of shift register stages are transferred to the storage register and parallel output stages*/
STCP_GPIO->DR &= ~(1U << NXP74LV595_INPUT_STCP_PIN);
Delay(1000);
STCP_GPIO->DR |= (1U << NXP74LV595_INPUT_STCP_PIN);
Delay(1000);
}
/*
* Copyright (c) 2006-2021, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2021-06-16 songchao support emac driver
* 2021-06-29 songchao add phy link detect
* 2021-08-13 songchao support dual mac and reduse copy
*/
#include "drv_74lv595.h"
#include "drv_eth.h"
#define DBG_TAG "drv.enet"
#define DBG_LVL DBG_LOG
#include
// #define BSP_USING_IMX6ULL_ART_PI
#if (defined(RT_USING_ENET1)) || (defined(RT_USING_ENET2))
#ifdef BSP_USING_IMX6ULL_ART_PI
static struct imx6ull_iomuxc mdio_gpio[2] =
{
{IOMUXC_GPIO1_IO06_ENET1_MDIO,0U,0xB029},
{IOMUXC_GPIO1_IO07_ENET1_MDC,0U,0xB0E9}
};
#else
static struct imx6ull_iomuxc mdio_gpio[2] =
{
{IOMUXC_GPIO1_IO06_ENET2_MDIO,0U,0xB029},
{IOMUXC_GPIO1_IO07_ENET2_MDC,0U,0xB0E9},
};
#endif
enum
{
#ifdef RT_USING_ENET1
DEV_ENET1,
#endif
#ifdef RT_USING_ENET2
DEV_ENET2,
#endif
DEV_ENET_MAX,
};
static struct rt_imx6ul_ethps _imx6ul_eth_device[DEV_ENET_MAX] =
{
#ifdef RT_USING_ENET1
{
.dev_addr = {0xa8,0x5e,0x45,0x91,0x92,0x93},
.mac_name = "e1",
.irq_name = "emac1_intr",
.enet_phy_base_addr = ENET1,
.irq_num = IMX_INT_ENET1,
.phy_num = 2,
.mac_num = 1,
.phy_id = 0x22,
.buffConfig =
{
ENET_RXBD_NUM,
ENET_TXBD_NUM,
ENET_RXBUFF_ALIGN_SIZE,
ENET_TXBUFF_ALIGN_SIZE,
RT_NULL,
RT_NULL,
RT_NULL,
RT_NULL,
RT_NULL,
RT_NULL,
RT_NULL,
RT_NULL,
ENET_RXBUFF_TOTAL_SIZE,
ENET_TXBUFF_TOTAL_SIZE
},
.gpio =
{
{IOMUXC_ENET1_RX_DATA0_ENET1_RDATA00,0U,0xB0E9},
{IOMUXC_ENET1_RX_DATA1_ENET1_RDATA01,0U,0xB0E9},
{IOMUXC_ENET1_RX_EN_ENET1_RX_EN,0U,0xB0E9},
{IOMUXC_ENET1_RX_ER_ENET1_RX_ER,0U,0xB0E9},
{IOMUXC_ENET1_TX_CLK_ENET1_REF_CLK1,1U,0x00F0},
{IOMUXC_ENET1_TX_DATA0_ENET1_TDATA00,0U,0xB0E9},
{IOMUXC_ENET1_TX_DATA1_ENET1_TDATA01,0U,0xB0E9},
{IOMUXC_ENET1_TX_EN_ENET1_TX_EN,0U,0xB0E9}
}
},
#endif
#ifdef RT_USING_ENET2
{
.dev_addr = {0xa8,0x5e,0x45,0x01,0x02,0x03},
.mac_name = "e2",
.irq_name = "emac2_intr",
.enet_phy_base_addr = ENET2,
.irq_num = IMX_INT_ENET2,
.phy_num = 1,
.mac_num = 2,
.phy_id = 0x22,
.buffConfig =
{
ENET_RXBD_NUM,
ENET_TXBD_NUM,
ENET_RXBUFF_ALIGN_SIZE,
ENET_TXBUFF_ALIGN_SIZE,
RT_NULL,
RT_NULL,
RT_NULL,
RT_NULL,
RT_NULL,
RT_NULL,
RT_NULL,
RT_NULL,
ENET_RXBUFF_TOTAL_SIZE,
ENET_TXBUFF_TOTAL_SIZE
},
.gpio =
{
{IOMUXC_ENET2_RX_DATA0_ENET2_RDATA00,0U,0xB0E9},
{IOMUXC_ENET2_RX_DATA1_ENET2_RDATA01,0U,0xB0E9},
{IOMUXC_ENET2_RX_EN_ENET2_RX_EN,0U,0xB0E9},
{IOMUXC_ENET2_RX_ER_ENET2_RX_ER,0U,0xB0E9},
{IOMUXC_ENET2_TX_CLK_ENET2_REF_CLK2,1U,0x00F0},
{IOMUXC_ENET2_TX_DATA0_ENET2_TDATA00,0U,0xB0E9},
{IOMUXC_ENET2_TX_DATA1_ENET2_TDATA01,0U,0xB0E9},
{IOMUXC_ENET2_TX_EN_ENET2_TX_EN,0U,0xB0E9}
}
},
#endif
};
void imx6ul_eth_link_change(struct rt_imx6ul_ethps *imx6ul_device,rt_bool_t up)
{
if(up)
{
LOG_D("enet%d link up\n",imx6ul_device->mac_num);
eth_device_linkchange(&imx6ul_device->parent, RT_TRUE);
imx6ul_device->phy_link_status = RT_TRUE;
}
else
{
LOG_D("enet%d link down\n",imx6ul_device->mac_num);
eth_device_linkchange(&imx6ul_device->parent, RT_FALSE);
imx6ul_device->phy_link_status = RT_FALSE;
}
}
void ENET_InitModuleClock(void)
{
const clock_enet_pll_config_t config = {true, true, false, 1, 1};
CLOCK_InitEnetPll(&config);
}
rt_err_t enet_buffer_init(enet_buffer_config_t *buffConfig)
{
void *tx_buff_addr = RT_NULL;
void *rx_buff_addr = RT_NULL;
void *tx_bd_addr = RT_NULL;
void *rx_bd_addr = RT_NULL;
if(((SYS_PAGE_SIZE<<RX_BUFFER_INDEX_NUM)<buffConfig->rxBufferTotalSize)||
((SYS_PAGE_SIZE<<TX_BUFFER_INDEX_NUM)<buffConfig->txBufferTotalSize))
{
LOG_E("ERROR: alloc mem not enough for enet driver\n");
return RT_ERROR;
}
rx_buff_addr = rt_pages_alloc(RX_BUFFER_INDEX_NUM);
if(!rx_buff_addr)
{
LOG_E("ERROR: rx buff page alloc failed\n");
return RT_ERROR;
}
buffConfig->rxBufferAlign = (void *)rt_ioremap_nocache(virtual_to_physical(rx_buff_addr), (SYS_PAGE_SIZE<<RX_BUFFER_INDEX_NUM));
buffConfig->rxPhyBufferAlign = (void *)virtual_to_physical(rx_buff_addr);
tx_buff_addr = rt_pages_alloc(TX_BUFFER_INDEX_NUM);
if(!tx_buff_addr)
{
LOG_E("ERROR: tx buff page alloc failed\n");
return RT_ERROR;
}
buffConfig->txBufferAlign = (void *)rt_ioremap_nocache(virtual_to_physical(tx_buff_addr), (SYS_PAGE_SIZE<<TX_BUFFER_INDEX_NUM));
buffConfig->txPhyBufferAlign = (void *)virtual_to_physical(tx_buff_addr);
rx_bd_addr = rt_pages_alloc(RX_BD_INDEX_NUM);
if(!rx_bd_addr)
{
LOG_E("ERROR: rx bd page alloc failed\n");
return RT_ERROR;
}
buffConfig->rxBdStartAddrAlign = (void *)rt_ioremap_nocache(virtual_to_physical(rx_bd_addr), (SYS_PAGE_SIZE<<RX_BD_INDEX_NUM));
buffConfig->rxPhyBdStartAddrAlign = virtual_to_physical(rx_bd_addr);
tx_bd_addr = rt_pages_alloc(TX_BD_INDEX_NUM);
if(!tx_bd_addr)
{
LOG_E("ERROR: tx bd page alloc failed\n");
return RT_ERROR;
}
buffConfig->txBdStartAddrAlign = (void *)rt_ioremap_nocache(virtual_to_physical(tx_bd_addr), (SYS_PAGE_SIZE<<TX_BD_INDEX_NUM));
buffConfig->txPhyBdStartAddrAlign = virtual_to_physical(tx_bd_addr);
return RT_EOK;
}
/* EMAC initialization function */
static rt_err_t rt_imx6ul_eth_init(rt_device_t dev)
{
rt_err_t state;
struct rt_imx6ul_ethps *imx6ul_device = (struct rt_imx6ul_ethps *)dev;
ENET_Type *base_addr = RT_NULL;
enet_config_t *config;
enet_handle_t *handle;
enet_buffer_config_t *buffConfig;
rt_uint32_t reg_value;
imx6ul_device->enet_virtual_base_addr = (ENET_Type *)rt_ioremap((void *)imx6ul_device->enet_phy_base_addr,SYS_PAGE_SIZE);
base_addr = imx6ul_device->enet_virtual_base_addr;
config = &imx6ul_device->config;
handle = &imx6ul_device->handle;
buffConfig = &imx6ul_device->buffConfig;
for (int i=0; i<GET_ARRAY_NUM(imx6ul_device->gpio); i++)
{
imx6ull_gpio_init(&imx6ul_device->gpio[i]);
}
IOMUXC_GPR_Type *GPR1 = (IOMUXC_GPR_Type *)rt_ioremap((void *)IOMUXC_GPR,0x1000);
if(imx6ul_device->mac_num == 1)
{
reg_value = GPR1->GPR1;
reg_value &= ~(IOMUXC_GPR_GPR1_ENET1_CLK_SEL_MASK
| IOMUXC_GPR_GPR1_ENET1_CLK_SEL_MASK);
reg_value |= IOMUXC_GPR_GPR1_ENET1_TX_CLK_DIR(1);
reg_value |= IOMUXC_GPR_GPR1_ENET1_CLK_SEL(0);
GPR1->GPR1 = reg_value;
}
else if(imx6ul_device->mac_num == 2)
{
reg_value = GPR1->GPR1;
reg_value &= ~(IOMUXC_GPR_GPR1_ENET2_CLK_SEL_MASK
| IOMUXC_GPR_GPR1_ENET2_CLK_SEL_MASK);
reg_value |= IOMUXC_GPR_GPR1_ENET2_TX_CLK_DIR(1);
reg_value |= IOMUXC_GPR_GPR1_ENET2_CLK_SEL(0);
GPR1->GPR1 = reg_value;
}
ENET_InitModuleClock();
ENET_GetDefaultConfig(config);
config->interrupt |= (ENET_RX_INTERRUPT);
state = enet_buffer_init(buffConfig);
if(state != RT_EOK)
{
return state;
}
ENET_Init(base_addr, handle, config, buffConfig, &imx6ul_device->dev_addr[0], SYS_CLOCK_HZ);
ENET_ActiveRead(base_addr);
rt_hw_interrupt_install(imx6ul_device->irq_num, (rt_isr_handler_t)ENET_DriverIRQHandler, (void *)base_addr,imx6ul_device->irq_name);
rt_hw_interrupt_umask(imx6ul_device->irq_num);
return RT_EOK;
}
static rt_err_t rt_imx6ul_eth_open(rt_device_t dev, rt_uint16_t oflag)
{
return RT_EOK;
}
static rt_err_t rt_imx6ul_eth_close(rt_device_t dev)
{
return RT_EOK;
}
static rt_size_t rt_imx6ul_eth_read(rt_device_t dev, rt_off_t pos, void *buffer, rt_size_t size)
{
return 0;
}
static rt_size_t rt_imx6ul_eth_write(rt_device_t dev, rt_off_t pos, const void *buffer, rt_size_t size)
{
return 0;
}
static rt_err_t rt_imx6ul_eth_control(rt_device_t dev, int cmd, void *args)
{
struct rt_imx6ul_ethps *imx6ul_device = (struct rt_imx6ul_ethps *)dev;
switch (cmd)
{
case NIOCTL_GADDR:
/* get MAC address */
if (args)
{
rt_memcpy(args, imx6ul_device->dev_addr, MAX_ADDR_LEN);
}
else
{
return -RT_ERROR;
}
break;
default :
break;
}
return RT_EOK;
}
static status_t read_data_from_eth(rt_device_t dev,void *read_data,uint16_t *read_length)
{
status_t status = 0;
uint16_t length = 0;
ENET_Type *base_addr = RT_NULL;
enet_config_t *config;
enet_handle_t *handle;
enet_buffer_config_t *buffConfig;
struct rt_imx6ul_ethps *imx6ul_device = (struct rt_imx6ul_ethps *)dev;
base_addr = imx6ul_device->enet_virtual_base_addr;
config = &imx6ul_device->config;
handle = &imx6ul_device->handle;
buffConfig = &imx6ul_device->buffConfig;
/* Get the Frame size */
status = ENET_ReadFrame(base_addr,handle,config,read_data,&length);
if((status == kStatus_ENET_RxFrameEmpty)||(status == kStatus_ENET_RxFrameError))
{
ENET_EnableInterrupts(base_addr,ENET_RX_INTERRUPT);
if(status == kStatus_ENET_RxFrameError)
{
/*recv error happend reinitialize mac*/
ENET_Init(base_addr, handle, config, buffConfig, &imx6ul_device->dev_addr[0], SYS_CLOCK_HZ);
ENET_ActiveRead(base_addr);
return kStatus_ENET_RxFrameError;
}
else if(status == kStatus_ENET_RxFrameEmpty)
{
return kStatus_ENET_RxFrameEmpty;
}
}
*read_length = length;
return status;
}
/* transmit data*/
rt_err_t rt_imx6ul_eth_tx(rt_device_t dev, struct pbuf *p)
{
rt_err_t ret = RT_ERROR;
struct pbuf *q = RT_NULL;
uint16_t offset = 0;
uint32_t last_flag = 0;
status_t status;
ENET_Type *base_addr = RT_NULL;
enet_handle_t *handle;
struct rt_imx6ul_ethps *imx6ul_device = (struct rt_imx6ul_ethps *)dev;
base_addr = imx6ul_device->enet_virtual_base_addr;
handle = &imx6ul_device->handle;
RT_ASSERT(p);
for(q = p;q != RT_NULL;q=q->next)
{
if(q->next == RT_NULL)
{
last_flag = 1;
}
else
{
last_flag = 0;
}
status = ENET_SendFrame(base_addr, handle, q->payload, q->len,last_flag);
offset = offset + q->len;
if(status == kStatus_Success)
{
}
else
{
return RT_ERROR;
}
}
if(offset > ENET_FRAME_MAX_FRAMELEN)
{
LOG_E("net error send length %d exceed max length\n",offset);
}
return ret;
}
struct pbuf *rt_imx6ul_eth_rx(rt_device_t dev)
{
static struct pbuf *p_s = RT_NULL;
struct pbuf *p = RT_NULL;
status_t status;
uint16_t length = 0;
if(p_s == RT_NULL)
{
p_s = pbuf_alloc(PBUF_RAW, ENET_FRAME_MAX_FRAMELEN, PBUF_POOL);
if(p_s == RT_NULL)
{
return RT_NULL;
}
}
p = p_s;
status = read_data_from_eth(dev,p->payload,&length);
if(status == kStatus_ENET_RxFrameEmpty)
{
return RT_NULL;
}
else if(status == kStatus_ENET_RxFrameError)
{
return RT_NULL;
}
if(length > ENET_FRAME_MAX_FRAMELEN)
{
LOG_E("net error recv length %d exceed max length\n",length);
return RT_NULL;
}
pbuf_realloc(p, length);
p_s = RT_NULL;
return p;
}
int32_t get_instance_by_base(void *base)
{
int32_t i = 0;
int32_t instance = 0;
for(i = 0; i < DEV_ENET_MAX; i ++)
{
if((void *)_imx6ul_eth_device[i].enet_virtual_base_addr == base)
{
break;
}
}
if(i == DEV_ENET_MAX)
{
return -1;
}
return instance;
}
void rx_enet_callback(void *base)
{
int32_t instance = 0;
instance = get_instance_by_base(base);
if(instance == -1)
{
LOG_E("interrput match base addr error \n");
return;
}
eth_device_ready(&(_imx6ul_eth_device[instance].parent));
ENET_DisableInterrupts(base,ENET_RX_INTERRUPT);
}
void tx_enet_callback(void *base)
{
ENET_DisableInterrupts(base,ENET_TX_INTERRUPT);
}
/*phy link detect thread*/
static void phy_detect_thread_entry(void *param)
{
bool link = false;
phy_speed_t speed;
phy_duplex_t duplex;
ENET_Type *base_addr = RT_NULL;
struct rt_imx6ul_ethps *imx6ul_device = (struct rt_imx6ul_ethps *)param;
base_addr = imx6ul_device->enet_virtual_base_addr;
// phy_reset(imx6ul_device->phy_base_addr,imx6ul_device->phy_gpio_pin);
if(imx6ul_device->mac_num == 1)
{
BOARD_NXP74LV595_SetValue(kNXP74LV595_ENET1_nRST,kSignal_NXP74LV595_Low);
rt_thread_delay(50);
BOARD_NXP74LV595_SetValue(kNXP74LV595_ENET1_nRST,kSignal_NXP74LV595_High);
}
else if(imx6ul_device->mac_num == 2)
{
BOARD_NXP74LV595_SetValue(kNXP74LV595_ENET2_nRST,kSignal_NXP74LV595_Low);
rt_thread_delay(50);
BOARD_NXP74LV595_SetValue(kNXP74LV595_ENET2_nRST,kSignal_NXP74LV595_High);
}
rt_thread_mdelay(100);
PHY_Init(base_addr, imx6ul_device->phy_num, SYS_CLOCK_HZ,imx6ul_device->phy_id);
int i = 0;
for(i = 0;i<100;i++)
{
PHY_GetLinkStatus(base_addr, imx6ul_device->phy_num, &link);
LOG_D("phy %s , link status %d",imx6ul_device->mac_name,link);
if (link)
{
/* Get the actual PHY link speed. */
PHY_GetLinkSpeedDuplex(base_addr, imx6ul_device->phy_num, &speed, &duplex);
/* Change the MII speed and duplex for actual link status. */
imx6ul_device->config.miiSpeed = (enet_mii_speed_t)speed;
imx6ul_device->config.miiDuplex = (enet_mii_duplex_t)duplex;
break;
}
else
{
LOG_E("PHY : %s , Link down, please check the cable connection and link partner setting.\r\n",imx6ul_device->mac_name);
}
}
// PHY_GetLinkStatus(base_addr, imx6ul_device->phy_num, &link);
// if (link)
// {
// /* Get the actual PHY link speed. */
// PHY_GetLinkSpeedDuplex(base_addr, imx6ul_device->phy_num, &speed, &duplex);
// /* Change the MII speed and duplex for actual link status. */
// imx6ul_device->config.miiSpeed = (enet_mii_speed_t)speed;
// imx6ul_device->config.miiDuplex = (enet_mii_duplex_t)duplex;
// }
// else
// {
// LOG_E("\r\nPHY Link down, please check the cable connection and link partner setting.\r\n");
// }
while(1)
{
PHY_GetLinkStatus(base_addr, imx6ul_device->phy_num, &link);
if(link != imx6ul_device->phy_link_status)
{
if(link == true)
{
PHY_StartNegotiation(base_addr,imx6ul_device->phy_num);
}
imx6ul_eth_link_change(imx6ul_device,link);
}
rt_thread_delay(DETECT_DELAY_ONE_SECOND);
}
}
_internal_ro struct rt_device_ops _k_enet_ops =
{
rt_imx6ul_eth_init,
rt_imx6ul_eth_open,
rt_imx6ul_eth_close,
rt_imx6ul_eth_read,
rt_imx6ul_eth_write,
rt_imx6ul_eth_control,
};
static int imx6ul_eth_init(void)
{
rt_err_t state = RT_EOK;
char link_detect[10];
#if 1
imx6ull_gpio_init(&mdio_gpio[0]);
imx6ull_gpio_init(&mdio_gpio[1]);
#endif
BOARD_NXP74LV595_SetValue_Init();
BOARD_NXP74LV595_SetValue(kNXP74LV595_ENET1_nRST,kSignal_NXP74LV595_High);
BOARD_NXP74LV595_SetValue(kNXP74LV595_ENET2_nRST,kSignal_NXP74LV595_High);
for (int idx=0; idx<GET_ARRAY_NUM(_imx6ul_eth_device); idx++)
{
_imx6ul_eth_device[idx].parent.parent.ops = &_k_enet_ops;
_imx6ul_eth_device[idx].parent.eth_rx = rt_imx6ul_eth_rx;
_imx6ul_eth_device[idx].parent.eth_tx = rt_imx6ul_eth_tx;
_imx6ul_eth_device[idx].phy_link_status = RT_FALSE;
/* register eth device */
state = eth_device_init(&(_imx6ul_eth_device[idx].parent), _imx6ul_eth_device[idx].mac_name);
if (RT_EOK == state)
{
LOG_E("emac device init success\n");
}
else
{
LOG_E("emac device init faild: %d", state);
state = -RT_ERROR;
}
rt_sprintf(link_detect,"link_d%d",_imx6ul_eth_device[idx].mac_num);
/* start phy link detect */
rt_thread_t phy_link_tid;
phy_link_tid = rt_thread_create(link_detect,
phy_detect_thread_entry,
&_imx6ul_eth_device[idx],
4096,
RT_THREAD_PRIORITY_MAX - 2,
2);
if (phy_link_tid != RT_NULL)
{
rt_thread_startup(phy_link_tid);
}
memset(link_detect,0,sizeof(link_detect));
}
return state;
}
INIT_DEVICE_EXPORT(imx6ul_eth_init);
#endif
kernel\bsp\yh\libraries\sdk\devices\MCIMX6Y2\drivers\fsl_phy.c添加如下宏定义