I.MX6 Ar8031 device register hacking
/***************************************************************************** * 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; }