/*****************************************************************************
* I.MX6 Ar8031 device register hacking
* 声明:
* 主要是为了知道网卡的注册流程,如果需要对网卡中的一些需求进行修改时,能够
* 能够快速的对需求进行分析、修改。
*
* 2015-8-15 雨 深圳 南山区平山村 曾剑锋
****************************************************************************/
/*
* initialize __mach_desc_MX6Q_SABRESD data structure.
*/
MACHINE_START(MX6Q_SABRESD, "Freescale i.MX 6Quad/DualLite/Solo Sabre-SD Board")
/* Maintainer: Freescale Semiconductor, Inc. */
.boot_params = MX6_PHYS_OFFSET + 0x100,
.fixup = fixup_mxc_board,
.map_io = mx6_map_io,
.init_irq = mx6_init_irq,
.init_machine = mx6_sabresd_board_init, ------------+
.timer = &mx6_sabresd_timer, |
.reserve = mx6q_sabresd_reserve, |
MACHINE_END |
|
/*! |
* Board specific initialization. |
*/ |
static void __init mx6_sabresd_board_init(void) <-----+
{
......
imx6_init_fec(fec_data); -----------------------------------------------+
...... | |
} +-------------+ |
| |
/*SBC-7112 NET Driver*/ V |
static struct fec_platform_data fec_data __initdata = { |
.init = mx6q_sabresd_fec_phy_init, -----+ |
.power_hibernate = mx6_sabresd_fec_power_hibernate,--*------+|
.phy = PHY_INTERFACE_MODE_RGMII, | ||
#ifdef CONFIG_MX6_ENET_IRQ_TO_GPIO | ||
.gpio_irq = MX6_ENET_IRQ, | ||
#endif | ||
}; | ||
| ||
static int mx6q_sabresd_fec_phy_init(struct phy_device *phydev) <---+ ||
{ ||
unsigned short val; ||
gpio_request(SABRESD_FEC_PHY_RESET,"phy-rst"); ||
gpio_direction_output(SABRESD_FEC_PHY_RESET, 1); ||
mdelay(1); ||
gpio_direction_output(SABRESD_FEC_PHY_RESET, 0); ||
mdelay(20); ||
gpio_direction_output(SABRESD_FEC_PHY_RESET, 1); ||
mdelay(5); ||
||
/* Ar8031 phy SmartEEE feature cause link status generates glitch, ||
* which cause ethernet link down/up issue, so disable SmartEEE ||
*/ ||
phy_write(phydev, 0xd, 0x3); ||
phy_write(phydev, 0xe, 0x805d); ||
phy_write(phydev, 0xd, 0x4003); ||
val = phy_read(phydev, 0xe); ||
val &= ~(0x1 << 8); ||
phy_write(phydev, 0xe, val); ||
||
/* To enable AR8031 ouput a 125MHz clk from CLK_25M */ ||
phy_write(phydev, 0xd, 0x7); ||
phy_write(phydev, 0xe, 0x8016); ||
phy_write(phydev, 0xd, 0x4007); ||
val = phy_read(phydev, 0xe); ||
||
val &= 0xffe3; ||
val |= 0x18; ||
phy_write(phydev, 0xe, val); ||
||
/* introduce tx clock delay */ ||
phy_write(phydev, 0x1d, 0x5); ||
val = phy_read(phydev, 0x1e); ||
val |= 0x0100; ||
phy_write(phydev, 0x1e, val); ||
||
/*check phy power*/ ||
val = phy_read(phydev, 0x0); ||
if (val & BMCR_PDOWN) ||
phy_write(phydev, 0x0, (val & ~BMCR_PDOWN)); --------+ ||
return 0; | ||
} +------------------------------------+ ||
V ||
static inline void phy_write(struct mii_phy *phy, int reg, int val) ||
{ ||
phy->mdio_write(phy->dev, phy->address, reg, val); ||
} ||
||
static int mx6_sabresd_fec_power_hibernate(struct phy_device *phydev) <---+|
{ |
unsigned short val; |
|
/*set AR8031 debug reg 0xb to hibernate power*/ |
phy_write(phydev, 0x1d, 0xb); |
val = phy_read(phydev, 0x1e); |
|
val |= 0x8000; |
phy_write(phydev, 0x1e, val); |
|
return 0; |
} |
|
void __init imx6_init_fec(struct fec_platform_data fec_data) <---------+
{
fec_get_mac_addr(fec_data.mac); --------+
if (!is_valid_ether_addr(fec_data.mac)) --------*-----------------------+
random_ether_addr(fec_data.mac); --------*----------------------+|
| ||
if (cpu_is_mx6sl()) | ||
imx6sl_add_fec(&fec_data); | ||
else | ||
imx6q_add_fec(&fec_data); --------*---------------------+||
} | |||
| |||
static int fec_get_mac_addr(unsigned char *mac) <----+ |||
{ |||
unsigned int value; |||
|||
value = readl(MX6_IO_ADDRESS(OCOTP_BASE_ADDR) + HW_OCOTP_MACn(0)); |||
printk("<danny debug> vaule = %x\n",value); |||
value = 0x03040506; |||
mac[5] = value & 0xff; |||
mac[4] = (value >> 8) & 0xff; |||
mac[3] = (value >> 16) & 0xff; |||
mac[2] = (value >> 24) & 0xff; |||
value = readl(MX6_IO_ADDRESS(OCOTP_BASE_ADDR) + HW_OCOTP_MACn(1)); |||
printk("<danny debug> vaule = %x\n",value); |||
value = 0x0102; |||
mac[1] = value & 0xff; |||
mac[0] = (value >> 8) & 0xff; |||
|||
return 0; |||
} |||
|||
static inline int is_valid_ether_addr(const u8 *addr) <------------**+
{ ||
/* FF:FF:FF:FF:FF:FF is a multicast address so we don't need to ||
* explicitly check for it here. */ ||
return !is_multicast_ether_addr(addr) && !is_zero_ether_addr(addr); ||
} ^ V ||
V | ||
static inline int is_multicast_ether_addr(const u8 *addr) | ||
{ | ||
return 0x01 & addr[0]; | ||
} | ||
| ||
static inline int is_zero_ether_addr(const u8 *addr) <-----+ ||
{ ||
return !(addr[0] | addr[1] | addr[2] | addr[3] | addr[4] | addr[5]); ||
} ||
||
static inline void random_ether_addr(u8 *addr) <--------------------*+
{ |
get_random_bytes (addr, ETH_ALEN); -----------------------+ |
addr [0] &= 0xfe; /* clear multicast bit */ | |
addr [0] |= 0x02; /* set local assignment bit (IEEE802) */ | |
} | |
| |
void get_random_bytes(void *buf, int nbytes) <--------------+ |
{ |
extract_entropy(&nonblocking_pool, buf, nbytes, 0, 0); -----------+ |
} | |
EXPORT_SYMBOL(get_random_bytes); | |
| |
/********************************************************************* | |
* | |
* Entropy extraction routines | |
* | |
********************************************************************/ | |
| |
static ssize_t extract_entropy(struct entropy_store *r, void *buf, <--+ |
size_t nbytes, int min, int rsvd); |
|
extern const struct imx_fec_data imx6q_fec_data __initconst; ------+ |
#define imx6q_add_fec(pdata) \ <----------------------*-------+
imx_add_fec(&imx6q_fec_data, pdata) -----------*----+
| |
#ifdef CONFIG_SOC_IMX6Q | |
const struct imx_fec_data imx6q_fec_data __initconst = <---------+ |
imx_fec_data_entry_single(MX6Q, "enet"); -----+ |
| |
const struct imx_fec_data imx6sl_fec_data __initconst = | |
imx_fec_data_entry_single(MX6DL, "fec"); | |
#endif | |
| |
#define imx_fec_data_entry_single(soc, _devid) \ <----+ |
{ \ |
.iobase = soc ## _FEC_BASE_ADDR, \ |
.irq = soc ## _INT_FEC, \ |
.devid = _devid, \ |
} |
|
struct platform_device *__init imx_add_fec( <----------------------+
const struct imx_fec_data *data,
const struct fec_platform_data *pdata)
{
struct resource res[] = {
{
.start = data->iobase,
.end = data->iobase + SZ_4K - 1,
.flags = IORESOURCE_MEM,
}, {
.start = data->irq,
.end = data->irq,
.flags = IORESOURCE_IRQ,
},
};
if (!fuse_dev_is_available(MXC_DEV_ENET)) ------+
return ERR_PTR(-ENODEV); |
|
return imx_add_platform_device_dmamask(data->devid, 0, --*-----+
res, ARRAY_SIZE(res), | |
pdata, sizeof(*pdata), DMA_BIT_MASK(32)); | |
} | |
| |
int fuse_dev_is_available(enum mxc_dev_type dev) <-----+ |
{ |
...... |
if (!cpu_is_mx6()) |
return 1; |
|
/* mx6sl is still not supported */ |
if (cpu_is_mx6sl()) |
return 1; |
|
switch (dev) { |
case MXC_DEV_PXP: |
if (cpu_is_mx6q()) |
return 0; |
|
if (cpu_is_mx6dl()) { |
reg = HW_OCOTP_CFGn(2); |
mask = 0x80000000; |
} |
break; |
case MXC_DEV_OVG: |
if (cpu_is_mx6dl()) |
return 0; |
|
if (cpu_is_mx6q()) { |
reg = HW_OCOTP_CFGn(2); |
mask = 0x40000000; |
} |
break; |
case MXC_DEV_DSI_CSI2: |
if (cpu_is_mx6dl() || cpu_is_mx6q()) { |
reg = HW_OCOTP_CFGn(2); |
mask = 0x10000000; |
} |
break; |
case MXC_DEV_ENET: |
if (cpu_is_mx6dl() || cpu_is_mx6q()) { |
reg = HW_OCOTP_CFGn(2); |
mask = 0x08000000; |
} |
break; |
case MXC_DEV_MLB: |
if (cpu_is_mx6dl() || cpu_is_mx6q()) { |
reg = HW_OCOTP_CFGn(2); |
mask = 0x04000000; |
} |
break; |
case MXC_DEV_EPDC: |
if (cpu_is_mx6q()) |
return 0; |
|
if (cpu_is_mx6dl()) { |
reg = HW_OCOTP_CFGn(2); |
mask = 0x02000000; |
} |
break; |
case MXC_DEV_HDMI: |
if (cpu_is_mx6dl() || cpu_is_mx6q()) { |
reg = HW_OCOTP_CFGn(3); |
mask = 0x00000080; |
} |
break; |
case MXC_DEV_PCIE: |
if (cpu_is_mx6dl() || cpu_is_mx6q()) { |
reg = HW_OCOTP_CFGn(3); |
mask = 0x00000040; |
} |
break; |
case MXC_DEV_SATA: |
if (cpu_is_mx6dl()) |
return 0; |
|
if (cpu_is_mx6q()) { |
reg = HW_OCOTP_CFGn(3); |
mask = 0x00000020; |
} |
break; |
case MXC_DEV_DTCP: |
if (cpu_is_mx6dl() || cpu_is_mx6q()) { |
reg = HW_OCOTP_CFGn(3); |
mask = 0x00000010; |
} |
break; |
case MXC_DEV_2D: |
if (cpu_is_mx6dl() || cpu_is_mx6q()) { |
reg = HW_OCOTP_CFGn(3); |
mask = 0x00000008; |
} |
break; |
case MXC_DEV_3D: |
if (cpu_is_mx6dl() || cpu_is_mx6q()) { |
reg = HW_OCOTP_CFGn(3); |
mask = 0x00000004; |
} |
break; |
case MXC_DEV_VPU: |
if (cpu_is_mx6dl() || cpu_is_mx6q()) { |
reg = HW_OCOTP_CFGn(3); |
mask = 0x00008000; |
} |
break; |
case MXC_DEV_DIVX3: |
if (cpu_is_mx6dl() || cpu_is_mx6q()) { |
reg = HW_OCOTP_CFGn(3); |
mask = 0x00000400; |
} |
break; |
case MXC_DEV_RV: |
if (cpu_is_mx6dl() || cpu_is_mx6q()) { |
reg = HW_OCOTP_CFGn(3); |
mask = 0x00000200; |
} |
break; |
case MXC_DEV_SORENSEN: |
if (cpu_is_mx6dl() || cpu_is_mx6q()) { |
reg = HW_OCOTP_CFGn(3); |
mask = 0x00000100; |
} |
break; |
default: |
/* we treat the unkown device is avaiable by default */ |
return 1; |
} |
|
ret = readl(MX6_IO_ADDRESS(OCOTP_BASE_ADDR) + reg) & mask; |
pr_debug("fuse_check: %s is %s\n", names[dev], ret ? |
"unavailable" : "available"); |
|
return !ret; |
} +-------------------------+
V
struct platform_device *__init imx_add_platform_device_dmamask(
const char *name, int id,
const struct resource *res, unsigned int num_resources,
const void *data, size_t size_data, u64 dmamask)
{
int ret = -ENOMEM;
struct platform_device *pdev;
pdev = platform_device_alloc(name, id);
if (!pdev)
goto err;
if (dmamask) {
/*
* This memory isn't freed when the device is put,
* I don't have a nice idea for that though. Conceptually
* dma_mask in struct device should not be a pointer.
* See http://thread.gmane.org/gmane.linux.kernel.pci/9081
*/
pdev->dev.dma_mask =
kmalloc(sizeof(*pdev->dev.dma_mask), GFP_KERNEL);
if (!pdev->dev.dma_mask)
/* ret is still -ENOMEM; */
goto err;
*pdev->dev.dma_mask = dmamask;
pdev->dev.coherent_dma_mask = dmamask;
}
if (res) {
ret = platform_device_add_resources(pdev, res, num_resources);
if (ret)
goto err;
}
if (data) {
ret = platform_device_add_data(pdev, data, size_data);
if (ret)
goto err;
}
ret = platform_device_add(pdev);
if (ret) {
err:
if (dmamask)
kfree(pdev->dev.dma_mask);
platform_device_put(pdev);
return ERR_PTR(ret);
}
return pdev;
}