From b2ea96b934fcf665b4c0cc844416a7a2618e198e Mon Sep 17 00:00:00 2001 From: John Crispin Date: Mon, 22 Oct 2012 09:52:50 +0200 Subject: [PATCH 106/113] MIPS: lantiq: xway: adds PHY11G platform code Signed-off-by: John Crispin --- arch/mips/lantiq/xway/Makefile | 2 +- arch/mips/lantiq/xway/vr9_switch_regs.h | 425 +++++++++++++++++++++++++++++++ arch/mips/lantiq/xway/xway_phy_fw.c | 146 +++++++++++ 3 files changed, 572 insertions(+), 1 deletion(-) create mode 100644 arch/mips/lantiq/xway/vr9_switch_regs.h create mode 100644 arch/mips/lantiq/xway/xway_phy_fw.c diff --git a/arch/mips/lantiq/xway/Makefile b/arch/mips/lantiq/xway/Makefile index 70a58c7..1998b7c 100644 --- a/arch/mips/lantiq/xway/Makefile +++ b/arch/mips/lantiq/xway/Makefile @@ -1 +1 @@ -obj-y := prom.o sysctrl.o clk.o reset.o dma.o gptu.o +obj-y := prom.o sysctrl.o clk.o reset.o dma.o gptu.o xway_phy_fw.o diff --git a/arch/mips/lantiq/xway/vr9_switch_regs.h b/arch/mips/lantiq/xway/vr9_switch_regs.h new file mode 100644 index 0000000..339e4c1 --- /dev/null +++ b/arch/mips/lantiq/xway/vr9_switch_regs.h @@ -0,0 +1,425 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. + * + * Copyright (C) 2012 Daniel Schwierzeck + */ + +#ifndef __VR9_SWITCH_REGS_H__ +#define __VR9_SWITCH_REGS_H__ + +/* Switch core registers */ +struct vr9_switch_core_regs { + __be32 swres; + /* TODO: implement registers */ + __be32 rsvd0[0x3f]; +}; + +/* Switch buffer management registers */ +struct vr9_switch_bm_regs { + struct bm_core { + __be32 ram_val3; /* RAM value 3 */ + __be32 ram_val2; /* RAM value 2 */ + __be32 ram_val1; /* RAM value 1 */ + __be32 ram_val0; /* RAM value 0 */ + __be32 ram_addr; /* RAM address */ + __be32 ram_ctrl; /* RAM access control */ + __be32 fsqm_gctrl; /* Free segment queue global control */ + __be32 cons_sel; /* Number of consumed segments */ + __be32 cons_pkt; /* Number of consumed packet pointers */ + __be32 gctrl; /* Global control */ + __be32 queue_gctrl; /* Queue manager global control */ + /* TODO: implement registers */ + __be32 rsvd0[0x35]; + } core; + + struct bm_port { + __be32 pcfg; /* Port config */ + __be32 rmon_ctrl; /* RMON control */ + } port[13]; + + __be32 rsvd0[0x66]; + + struct bm_queue { + __be32 rsvd0; + __be32 pqm_rs; /* Packet queue manager rate shape assignment */ + } queue[32]; + + struct bm_shaper { + __be32 ctrl; /* Rate shaper control */ + __be32 cbs; /* Rate shaper committed burst size */ + __be32 ibs; /* Rate shaper instantaneous burst size */ + __be32 cir_ext; /* Rate shaper rate exponent */ + __be32 cir_mant; /* Rate shaper rate mantissa */ + } shaper[16]; + + __be32 rsvd1[0x2a8]; +}; + +/* Switch parser and classification engine registers */ +struct vr9_switch_pce_regs { + struct pce_core { + __be32 tbl_key[16]; /* Table key data */ + __be32 tbl_mask; /* Table mask */ + __be32 tbl_val[5]; /* Table value */ + __be32 tbl_addr; /* Table entry address */ + __be32 tbl_ctrl; /* Table access control */ + __be32 tbl_stat; /* Table general status */ + __be32 age_0; /* Aging counter config 0 */ + __be32 age_1; /* Aging counter config 1 */ + __be32 pmap_1; /* Port map (monitoring) */ + __be32 pmap_2; /* Port map (multicast) */ + __be32 pmap_3; /* Port map (unknown unicast) */ + __be32 gctrl_0; /* Global control 0 */ + __be32 gctrl_1; /* Global control 1 */ + __be32 tcm_gctrl; /* Three-color marker global control */ + __be32 igmp_ctrl; /* IGMP control */ + __be32 igmp_drpm; /* IGMP default router port map */ + __be32 igmp_age_0; /* IGMP aging 0 */ + __be32 igmp_age_1; /* IGMP aging 1 */ + __be32 igmp_stat; /* IGMP status */ + __be32 wol_gctrl; /* Wake-on-LAN control */ + __be32 wol_da_0; /* Wake-on-LAN destination address 0 */ + __be32 wol_da_1; /* Wake-on-LAN destination address 1 */ + __be32 wol_da_2; /* Wake-on-LAN destination address 2 */ + __be32 wol_pw_0; /* Wake-on-LAN password 0 */ + __be32 wol_pw_1; /* Wake-on-LAN password 1 */ + __be32 wol_pw_2; /* Wake-on-LAN password 2 */ + __be32 ier_0; /* PCE global interrupt enable 0 */ + __be32 ier_1; /* PCE global interrupt enable 1 */ + __be32 isr_0; /* PCE global interrupt status 0 */ + __be32 isr_1; /* PCE global interrupt status 1 */ + __be32 parser_stat; /* Parser status */ + __be32 rsvd0[0x6]; + } core; + + __be32 rsvd0[0x10]; + + struct pce_port { + __be32 pctrl_0; /* Port control 0 */ + __be32 pctrl_1; /* Port control 1 */ + __be32 pctrl_2; /* Port control 2 */ + __be32 pctrl_3; /* Port control 3 */ + __be32 wol_ctrl; /* Wake-on-LAN control */ + __be32 vlan_ctrl; /* VLAN control */ + __be32 def_pvid; /* Default port VID */ + __be32 pstat; /* Port status */ + __be32 pier; /* Interrupt enable */ + __be32 pisr; /* Interrupt status */ + } port[13]; + + __be32 rsvd1[0x7e]; + + struct pce_meter { + /* TODO: implement registers */ + __be32 rsvd0[0x7]; + } meter[8]; + + __be32 rsvd2[0x308]; +}; + +static inline unsigned int to_pce_tbl_key_id(unsigned int id) +{ + return 15 - id; +} + +static inline unsigned int to_pce_tbl_value_id(unsigned int id) +{ + return 4 - id; +} + +/* Switch ethernet MAC registers */ +struct vr9_switch_mac_regs { + struct mac_core { + __be32 test; /* MAC test */ + __be32 pfad_cfg; /* Pause frame source address config */ + __be32 pfsa_0; /* Pause frame source address 0 */ + __be32 pfsa_1; /* Pause frame source address 1 */ + __be32 pfsa_2; /* Pause frame source address 2 */ + __be32 flen; /* Frame length */ + __be32 vlan_etype_0; /* VLAN ethertype 0 */ + __be32 vlan_etype_1; /* VLAN ethertype 1 */ + __be32 ier; /* Interrupt enable */ + __be32 isr; /* Interrupt status */ + __be32 rsvd0[0x36]; + } core; + + struct mac_port { + __be32 pstat; /* Port status */ + __be32 pisr; /* Interrupt status */ + __be32 pier; /* Interrupt enable */ + __be32 ctrl_0; /* Control 0 */ + __be32 ctrl_1; /* Control 1 */ + __be32 ctrl_2; /* Control 2 */ + __be32 ctrl_3; /* Control 3 */ + __be32 ctrl_4; /* Control 4 */ + __be32 ctrl_5; /* Control 5 */ + __be32 rsvd0[0x2]; + __be32 testen; /* Test enable */ + } port[13]; + + __be32 rsvd0[0xa4]; +}; + +/* Switch Fetch DMA registers */ +struct vr9_switch_fdma_regs { + struct fdma_core { + __be32 ctrl; /* FDMA control */ + __be32 stetype; /* Special tag ethertype control */ + __be32 vtetype; /* VLAN tag ethertype control */ + __be32 stat; /* FDMA status */ + __be32 ier; /* FDMA interrupt enable */ + __be32 isr; /* FDMA interrupt status */ + } core; + + __be32 rsvd0[0x3a]; + + struct fdma_port { + __be32 pctrl; /* Port control */ + __be32 prio; /* Port priority */ + __be32 pstat_0; /* Port status 0 */ + __be32 pstat_1; /* Port status 1 */ + __be32 tstamp_0; /* Egress time stamp 0 */ + __be32 tstamp_1; /* Egress time stamp 1 */ + } port[13]; + + __be32 rsvd1[0x72]; +}; + +/* Switch Store DMA registers */ +struct vr9_switch_sdma_regs { + struct sdma_core { + __be32 ctrl; /* SDMA Control */ + __be32 fcthr_1; /* Flow control threshold 1 */ + __be32 rsvd0; + __be32 fcthr_3; /* Flow control threshold 3 */ + __be32 fcthr_4; /* Flow control threshold 4 */ + __be32 fcthr_5; /* Flow control threshold 5 */ + __be32 fcthr_6; /* Flow control threshold 6 */ + __be32 fcthr_7; /* Flow control threshold 7 */ + __be32 stat_0; /* SDMA status 0 */ + __be32 stat_1; /* SDMA status 1 */ + __be32 stat_2; /* SDMA status 2 */ + __be32 ier; /* SDMA interrupt enable */ + __be32 isr; /* SDMA interrupt status */ + } core; + + __be32 rsvd0[0x73]; + + struct sdma_port { + __be32 pctrl; /* Port control */ + __be32 prio; /* Port priority */ + __be32 pstat_0; /* Port status 0 */ + __be32 pstat_1; /* Port status 1 */ + __be32 tstamp_0; /* Ingress time stamp 0 */ + __be32 tstamp_1; /* Ingress time stamp 1 */ + } port[13]; + + __be32 rsvd1[0x32]; +}; + +/* Switch MDIO control and status registers */ +struct vr9_switch_mdio_regs { + __be32 glob_ctrl; /* Global control 0 */ + __be32 rsvd0[7]; + __be32 mdio_ctrl; /* MDIO control */ + __be32 mdio_read; /* MDIO read data */ + __be32 mdio_write; /* MDIO write data */ + __be32 mdc_cfg_0; /* MDC clock configuration 0 */ + __be32 mdc_cfg_1; /* MDC clock configuration 1 */ + __be32 rsvd1[0x3]; + __be32 phy_addr[6]; /* PHY address port 5..0 */ + __be32 mdio_stat[6]; /* MDIO PHY polling status port 0..5 */ + __be32 aneg_eee[6]; /* EEE auto-neg overrides port 0..5 */ + __be32 rsvd2[0x14]; +}; + +static inline unsigned int to_mdio_phyaddr_id(unsigned int id) +{ + return 5 - id; +} + +/* Switch xMII control registers */ +struct vr9_switch_mii_regs { + __be32 mii_cfg0; /* xMII port 0 configuration */ + __be32 pcdu0; /* Port 0 clock delay configuration */ + __be32 mii_cfg1; /* xMII port 1 configuration */ + __be32 pcdu1; /* Port 1 clock delay configuration */ + __be32 rsvd0[0x6]; + __be32 mii_cfg5; /* xMII port 5 configuration */ + __be32 pcdu5; /* Port 5 clock delay configuration */ + __be32 rsvd1[0x14]; + __be32 rxb_ctl_0; /* Port 0 receive buffer control */ + __be32 rxb_ctl_1; /* Port 1 receive buffer control */ + __be32 rxb_ctl_5; /* Port 5 receive buffer control */ + __be32 rsvd2[0x28]; + __be32 dbg_ctl; /* Debug control */ +}; + +/* Switch Pseudo-MAC registers */ +struct vr9_switch_pmac_regs { + __be32 hd_ctl; /* PMAC header control */ + __be32 tl; /* PMAC type/length */ + __be32 sa1; /* PMAC source address 1 */ + __be32 sa2; /* PMAC source address 2 */ + __be32 sa3; /* PMAC source address 3 */ + __be32 da1; /* PMAC destination address 1 */ + __be32 da2; /* PMAC destination address 2 */ + __be32 da3; /* PMAC destination address 3 */ + __be32 vlan; /* PMAC VLAN */ + __be32 rx_ipg; /* PMAC interpacket gap in RX direction */ + __be32 st_etype; /* PMAC special tag ethertype */ + __be32 ewan; /* PMAC ethernet WAN group */ + __be32 ctl; /* PMAC control */ + __be32 rsvd0[0x2]; +}; + +struct vr9_switch_regs { + struct vr9_switch_core_regs core; + struct vr9_switch_bm_regs bm; + struct vr9_switch_pce_regs pce; + struct vr9_switch_mac_regs mac; + struct vr9_switch_fdma_regs fdma; + struct vr9_switch_sdma_regs sdma; + struct vr9_switch_mdio_regs mdio; + struct vr9_switch_mii_regs mii; + struct vr9_switch_pmac_regs pmac; +}; + +#define VR9_SWITCH_REG_OFFSET(reg) (4 * (reg)) + +#define BUILD_CHECK_VR9_REG(name, offset) \ + BUILD_BUG_ON(offsetof(struct vr9_switch_regs, name) != (4 * offset)) + +static inline void build_check_vr9_registers(void) +{ + BUILD_CHECK_VR9_REG(core, 0x0); + BUILD_CHECK_VR9_REG(bm.core, 0x40); + BUILD_CHECK_VR9_REG(bm.core.queue_gctrl, 0x4a); + BUILD_CHECK_VR9_REG(bm.port[0], 0x80); + BUILD_CHECK_VR9_REG(bm.queue, 0x100); + BUILD_CHECK_VR9_REG(bm.shaper, 0x140); + BUILD_CHECK_VR9_REG(pce.core, 0x438); + BUILD_CHECK_VR9_REG(pce.core.tbl_ctrl, 0x44f); + BUILD_CHECK_VR9_REG(pce.core.parser_stat, 0x469); + BUILD_CHECK_VR9_REG(pce.port[0], 0x480); + BUILD_CHECK_VR9_REG(pce.meter[0], 0x580); + BUILD_CHECK_VR9_REG(mac.core, 0x8c0); + BUILD_CHECK_VR9_REG(mac.port[0].pstat, 0x900); + BUILD_CHECK_VR9_REG(mac.port[0].ctrl_0, 0x903); + BUILD_CHECK_VR9_REG(mac.port[1].pstat, 0x90c); + BUILD_CHECK_VR9_REG(mac.port[1].ctrl_0, 0x90f); + BUILD_CHECK_VR9_REG(mac.port[2].pstat, 0x918); + BUILD_CHECK_VR9_REG(mac.port[2].ctrl_0, 0x91b); + BUILD_CHECK_VR9_REG(fdma.core, 0xa40); + BUILD_CHECK_VR9_REG(fdma.port[0], 0xa80); + BUILD_CHECK_VR9_REG(sdma.core, 0xb40); + BUILD_CHECK_VR9_REG(sdma.port[0], 0xbc0); + BUILD_CHECK_VR9_REG(mdio, 0xc40); + BUILD_CHECK_VR9_REG(mii, (0xc40 + 0x36)); + BUILD_CHECK_VR9_REG(pmac, (0xc40 + 0x82)); +} + +#define MAC_CTRL0_BM BIT(12) +#define MAC_CTRL0_APADEN BIT(11) +#define MAC_CTRL0_VPAD2EN BIT(10) +#define MAC_CTRL0_VPADEN BIT(9) +#define MAC_CTRL0_PADEN BIT(8) +#define MAC_CTRL0_FCS BIT(7) +#define MAC_CTRL0_FCON_SHIFT 4 +#define MAC_CTRL0_FCON_AUTO (0x0 << MAC_CTRL0_FCON_SHIFT) +#define MAC_CTRL0_FCON_RX (0x1 << MAC_CTRL0_FCON_SHIFT) +#define MAC_CTRL0_FCON_TX (0x2 << MAC_CTRL0_FCON_SHIFT) +#define MAC_CTRL0_FCON_RXTX (0x3 << MAC_CTRL0_FCON_SHIFT) +#define MAC_CTRL0_FCON_NONE (0x4 << MAC_CTRL0_FCON_SHIFT) +#define MAC_CTRL0_FDUP_SHIFT 2 +#define MAC_CTRL0_FDUP_AUTO (0x0 << MAC_CTRL0_FDUP_SHIFT) +#define MAC_CTRL0_FDUP_EN (0x1 << MAC_CTRL0_FDUP_SHIFT) +#define MAC_CTRL0_FDUP_DIS (0x3 << MAC_CTRL0_FDUP_SHIFT) +#define MAC_CTRL0_GMII_AUTO 0x0 +#define MAC_CTRL0_GMII_MII 0x1 +#define MAC_CTRL0_GMII_GMII 0x2 +#define MAC_CTRL0_GMII_GMII_2G 0x3 + +#define MAC_CTRL1_DEFERMODE BIT(15) +#define MAC_CTRL1_SHORTPRE BIT(8) + +#define MAC_CTRL2_MLEN BIT(3) +#define MAC_CTRL2_LCHKL BIT(2) +#define MAC_CTRL2_LCHKS_DIS 0x0 +#define MAC_CTRL2_LCHKS_UNTAG 0x1 +#define MAC_CTRL2_LCHKS_TAG 0x2 + +#define PHY_ADDR_LNKST_SHIFT 13 +#define PHY_ADDR_LNKST_AUTO (0x0 << PHY_ADDR_LNKST_SHIFT) +#define PHY_ADDR_LNKST_UP (0x1 << PHY_ADDR_LNKST_SHIFT) +#define PHY_ADDR_LNKST_DOWN (0x2 << PHY_ADDR_LNKST_SHIFT) +#define PHY_ADDR_SPEED_SHIFT 11 +#define PHY_ADDR_SPEED_M10 (0x0 << PHY_ADDR_SPEED_SHIFT) +#define PHY_ADDR_SPEED_M100 (0x1 << PHY_ADDR_SPEED_SHIFT) +#define PHY_ADDR_SPEED_G1 (0x2 << PHY_ADDR_SPEED_SHIFT) +#define PHY_ADDR_SPEED_AUTO (0x3 << PHY_ADDR_SPEED_SHIFT) +#define PHY_ADDR_FDUP_SHIFT 9 +#define PHY_ADDR_FDUP_AUTO (0x0 << PHY_ADDR_FDUP_SHIFT) +#define PHY_ADDR_FDUP_EN (0x1 << PHY_ADDR_FDUP_SHIFT) +#define PHY_ADDR_FDUP_DIS (0x3 << PHY_ADDR_FDUP_SHIFT) +#define PHY_ADDR_FCONTX_SHIFT 7 +#define PHY_ADDR_FCONTX_AUTO (0x0 << PHY_ADDR_FCONTX_SHIFT) +#define PHY_ADDR_FCONTX_EN (0x1 << PHY_ADDR_FCONTX_SHIFT) +#define PHY_ADDR_FCONTX_DIS (0x3 << PHY_ADDR_FCONTX_SHIFT) +#define PHY_ADDR_FCONRX_SHIFT 5 +#define PHY_ADDR_FCONRX_AUTO (0x0 << PHY_ADDR_FCONRX_SHIFT) +#define PHY_ADDR_FCONRX_EN (0x1 << PHY_ADDR_FCONRX_SHIFT) +#define PHY_ADDR_FCONRX_DIS (0x3 << PHY_ADDR_FCONRX_SHIFT) + +#define MII_CFG_RES BIT(15) +#define MII_CFG_EN BIT(14) +#define MII_CFG_LDCLKDIS BIT(12) +#define MII_CFG_MIIRATE_SHIFT 4 +#define MII_CFG_MIIRATE_MASK (0x7 << MII_CFG_MIIRATE_SHIFT) +#define MII_CFG_MIIRATE_M2P5 (0x0 << MII_CFG_MIIRATE_SHIFT) +#define MII_CFG_MIIRATE_M25 (0x1 << MII_CFG_MIIRATE_SHIFT) +#define MII_CFG_MIIRATE_M125 (0x2 << MII_CFG_MIIRATE_SHIFT) +#define MII_CFG_MIIRATE_M50 (0x3 << MII_CFG_MIIRATE_SHIFT) +#define MII_CFG_MIIRATE_AUTO (0x4 << MII_CFG_MIIRATE_SHIFT) +#define MII_CFG_MIIMODE_MASK 0xf +#define MII_CFG_MIIMODE_MIIP 0x0 +#define MII_CFG_MIIMODE_MIIM 0x1 +#define MII_CFG_MIIMODE_RMIIP 0x2 +#define MII_CFG_MIIMODE_RMIIM 0x3 +#define MII_CFG_MIIMODE_RGMII 0x4 + +#define PMAC_HD_CTL_FC BIT(10) +#define PMAC_HD_CTL_RST BIT(8) +#define PMAC_HD_CTL_AST BIT(7) +#define PMAC_HD_CTL_RXSH BIT(6) +#define PMAC_HD_CTL_RC BIT(4) +#define PMAC_HD_CTL_AS BIT(3) +#define PMAC_HD_CTL_AC BIT(2) + +#define PCE_PCTRL_0_IGSTEN BIT(11) + +#define FDMA_PCTRL_STEN BIT(1) +#define FDMA_PCTRL_EN BIT(0) + +#define SDMA_PCTRL_EN BIT(0) + +#define MDIO_CTRL_MBUSY BIT(12) +#define MDIO_CTRL_OP_READ BIT(11) +#define MDIO_CTRL_OP_WRITE BIT(10) +#define MDIO_CTRL_PHYAD_SHIFT 5 +#define MDIO_CTRL_PHYAD_MASK (0x1f << MDIO_CTRL_PHYAD_SHIFT) +#define MDIO_CTRL_REGAD_MASK 0x1f + +#endif /* __VR9_SWITCH_REGS_H__ */ diff --git a/arch/mips/lantiq/xway/xway_phy_fw.c b/arch/mips/lantiq/xway/xway_phy_fw.c new file mode 100644 index 0000000..97a6d22 --- /dev/null +++ b/arch/mips/lantiq/xway/xway_phy_fw.c @@ -0,0 +1,146 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +#include "vr9_switch_regs.h" + +#define XWAY_GPHY_FW_ALIGN (16 * 1024) +#define XWAY_GPHY_FW_NAME_SIZE 32 + +struct xway_gphy_core { + struct device *dev; + char fw_name[XWAY_GPHY_FW_NAME_SIZE]; + dma_addr_t dev_addr; + void *fw_addr; + size_t fw_size; +}; + +static int xway_gphy_load(struct platform_device *pdev, struct xway_gphy_core *gphy) +{ + const struct firmware *fw; + dma_addr_t dev_addr; + void *fw_addr; + int err; + size_t size; + const char *fw_name; + + err = of_property_read_string(pdev->dev.of_node, "firmware", &fw_name); + if (err) { + dev_err(&pdev->dev, "failed to load firmware filename\n"); + return err; + } + + if (strlen(fw_name) >= sizeof(gphy->fw_name)) { + dev_err(&pdev->dev, "firmware filename too long\n"); + return ENAMETOOLONG; + } + + strncpy(gphy->fw_name, fw_name, sizeof(gphy->fw_name)); + + dev_info(&pdev->dev, "requesting %s\n", gphy->fw_name); + err = request_firmware(&fw, gphy->fw_name, &pdev->dev); + if (err) { + dev_err(&pdev->dev, "failed to load firmware: %s\n", gphy->fw_name); + return err; + } + + /* + * GPHY cores need the firmware code in a persistent and contiguous + * memory area with a 16 kB boundary aligned start address + */ + size = fw->size + XWAY_GPHY_FW_ALIGN; + fw_addr = dma_alloc_coherent(&pdev->dev, size, &dev_addr, GFP_KERNEL); + if (!fw_addr) { + dev_err(&pdev->dev, "failed to alloc firmware memory\n"); + goto err_release; + } + + fw_addr = PTR_ALIGN(fw_addr, XWAY_GPHY_FW_ALIGN); + dev_addr = ALIGN(dev_addr, XWAY_GPHY_FW_ALIGN); + + memcpy(fw_addr, fw->data, fw->size); + release_firmware(fw); + + gphy->dev = &pdev->dev; + gphy->dev_addr = dev_addr; + gphy->fw_addr = fw_addr; + gphy->fw_size = size; + + return 0; + +err_release: + release_firmware(fw); + + return err; +} + +static int __devinit xway_phy_fw_probe(struct platform_device *pdev) +{ + struct xway_gphy_core gphy; + struct property *pp; + unsigned char *phyids; + int i, ret; + + ret = xway_gphy_load(pdev, &gphy); + if (ret) + return ret; + pp = of_find_property(pdev->dev.of_node, "phys", NULL); + if (!pp) + return -ENOENT; + phyids = pp->value; + for (i = 0; i < pp->length && !ret; i++) + ret = xrx200_gphy_boot(&pdev->dev, phyids[i], gphy.dev_addr); + if (!ret) + mdelay(100); + return ret; +} + +static const struct of_device_id xway_phy_match[] = { + { .compatible = "lantiq,phy-xrx200" }, + {}, +}; +MODULE_DEVICE_TABLE(of, xway_phy_match); + +static struct platform_driver xway_phy_driver = { + .probe = xway_phy_fw_probe, + .driver = { + .name = "phy-xrx200", + .owner = THIS_MODULE, + .of_match_table = xway_phy_match, + }, +}; + +module_platform_driver(xway_phy_driver); + +MODULE_AUTHOR("John Crispin "); +MODULE_DESCRIPTION("Lantiq XRX200 PHY Firmware Loader"); +MODULE_LICENSE("GPL"); + -- 1.7.10.4