Newer
Older
struct bcma_device *core = bgmac->core;
u8 imode;
if (bgmac_is_bcm4707_family(bgmac)) {
bcma_awrite32(core, BCMA_IOCTL,
bcma_aread32(core, BCMA_IOCTL) | 0x40 |
BGMAC_BCMA_IOCTL_SW_CLKEN);
bgmac->mac_speed = SPEED_2500;
bgmac->mac_duplex = DUPLEX_FULL;
bgmac_mac_speed(bgmac);
} else {
imode = (bgmac_read(bgmac, BGMAC_DEV_STATUS) &
BGMAC_DS_MM_MASK) >> BGMAC_DS_MM_SHIFT;
if (imode == 0 || imode == 1) {
bgmac->mac_speed = SPEED_100;
bgmac->mac_duplex = DUPLEX_FULL;
bgmac_mac_speed(bgmac);
}
}
}
/* http://bcm-v4.sipsolutions.net/mac-gbit/gmac/chipreset */
static void bgmac_chip_reset(struct bgmac *bgmac)
{
struct bcma_device *core = bgmac->core;
struct bcma_bus *bus = core->bus;
struct bcma_chipinfo *ci = &bus->chipinfo;
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
u32 iost;
int i;
if (bcma_core_is_enabled(core)) {
if (!bgmac->stats_grabbed) {
/* bgmac_chip_stats_update(bgmac); */
bgmac->stats_grabbed = true;
}
for (i = 0; i < BGMAC_MAX_TX_RINGS; i++)
bgmac_dma_tx_reset(bgmac, &bgmac->tx_ring[i]);
bgmac_cmdcfg_maskset(bgmac, ~0, BGMAC_CMDCFG_ML, false);
udelay(1);
for (i = 0; i < BGMAC_MAX_RX_RINGS; i++)
bgmac_dma_rx_reset(bgmac, &bgmac->rx_ring[i]);
/* TODO: Clear software multicast filter list */
}
iost = bcma_aread32(core, BCMA_IOST);
if ((ci->id == BCMA_CHIP_ID_BCM5357 && ci->pkg == BCMA_PKG_ID_BCM47186) ||
(ci->id == BCMA_CHIP_ID_BCM4749 && ci->pkg == 10) ||
(ci->id == BCMA_CHIP_ID_BCM53572 && ci->pkg == BCMA_PKG_ID_BCM47188))
iost &= ~BGMAC_BCMA_IOST_ATTACHED;
/* 3GMAC: for BCM4707 & BCM47094, only do core reset at bgmac_probe() */
if (ci->id != BCMA_CHIP_ID_BCM4707 &&
ci->id != BCMA_CHIP_ID_BCM47094) {
flags = 0;
if (iost & BGMAC_BCMA_IOST_ATTACHED) {
flags = BGMAC_BCMA_IOCTL_SW_CLKEN;
if (!bgmac->has_robosw)
flags |= BGMAC_BCMA_IOCTL_SW_RESET;
}
bcma_core_enable(core, flags);
/* Request Misc PLL for corerev > 2 */
if (core->id.rev > 2 && !bgmac_is_bcm4707_family(bgmac)) {
bgmac_set(bgmac, BCMA_CLKCTLST,
BGMAC_BCMA_CLKCTLST_MISC_PLL_REQ);
bgmac_wait_value(bgmac->core, BCMA_CLKCTLST,
BGMAC_BCMA_CLKCTLST_MISC_PLL_ST,
BGMAC_BCMA_CLKCTLST_MISC_PLL_ST,
if (ci->id == BCMA_CHIP_ID_BCM5357 ||
ci->id == BCMA_CHIP_ID_BCM4749 ||
ci->id == BCMA_CHIP_ID_BCM53572) {
struct bcma_drv_cc *cc = &bgmac->core->bus->drv_cc;
u8 et_swtype = 0;
u8 sw_type = BGMAC_CHIPCTL_1_SW_TYPE_EPHY |
if (bcm47xx_nvram_getenv("et_swtype", buf, sizeof(buf)) > 0) {
if (kstrtou8(buf, 0, &et_swtype))
bgmac_err(bgmac, "Failed to parse et_swtype (%s)\n",
buf);
et_swtype &= 0x0f;
et_swtype <<= 4;
sw_type = et_swtype;
} else if (ci->id == BCMA_CHIP_ID_BCM5357 && ci->pkg == BCMA_PKG_ID_BCM5358) {
sw_type = BGMAC_CHIPCTL_1_SW_TYPE_EPHYRMII;
} else if ((ci->id == BCMA_CHIP_ID_BCM5357 && ci->pkg == BCMA_PKG_ID_BCM47186) ||
(ci->id == BCMA_CHIP_ID_BCM4749 && ci->pkg == 10) ||
(ci->id == BCMA_CHIP_ID_BCM53572 && ci->pkg == BCMA_PKG_ID_BCM47188)) {
sw_type = BGMAC_CHIPCTL_1_IF_TYPE_RGMII |
BGMAC_CHIPCTL_1_SW_TYPE_RGMII;
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
}
bcma_chipco_chipctl_maskset(cc, 1,
~(BGMAC_CHIPCTL_1_IF_TYPE_MASK |
BGMAC_CHIPCTL_1_SW_TYPE_MASK),
sw_type);
}
if (iost & BGMAC_BCMA_IOST_ATTACHED && !bgmac->has_robosw)
bcma_awrite32(core, BCMA_IOCTL,
bcma_aread32(core, BCMA_IOCTL) &
~BGMAC_BCMA_IOCTL_SW_RESET);
/* http://bcm-v4.sipsolutions.net/mac-gbit/gmac/gmac_reset
* Specs don't say about using BGMAC_CMDCFG_SR, but in this routine
* BGMAC_CMDCFG is read _after_ putting chip in a reset. So it has to
* be keps until taking MAC out of the reset.
*/
bgmac_cmdcfg_maskset(bgmac,
~(BGMAC_CMDCFG_TE |
BGMAC_CMDCFG_RE |
BGMAC_CMDCFG_RPI |
BGMAC_CMDCFG_TAI |
BGMAC_CMDCFG_HD |
BGMAC_CMDCFG_ML |
BGMAC_CMDCFG_CFE |
BGMAC_CMDCFG_RL |
BGMAC_CMDCFG_RED |
BGMAC_CMDCFG_PE |
BGMAC_CMDCFG_TPI |
BGMAC_CMDCFG_PAD_EN |
BGMAC_CMDCFG_PF),
BGMAC_CMDCFG_PROM |
BGMAC_CMDCFG_NLC |
BGMAC_CMDCFG_CFE |
BGMAC_CMDCFG_SR(core->id.rev),
bgmac->mac_speed = SPEED_UNKNOWN;
bgmac->mac_duplex = DUPLEX_UNKNOWN;
bgmac_clear_mib(bgmac);
if (core->id.id == BCMA_CORE_4706_MAC_GBIT)
bcma_maskset32(bgmac->cmn, BCMA_GMAC_CMN_PHY_CTL, ~0,
BCMA_GMAC_CMN_PC_MTE);
else
bgmac_set(bgmac, BGMAC_PHY_CNTL, BGMAC_PC_MTE);
bgmac_miiconfig(bgmac);
bgmac_phy_init(bgmac);
netdev_reset_queue(bgmac->net_dev);
}
static void bgmac_chip_intrs_on(struct bgmac *bgmac)
{
bgmac_write(bgmac, BGMAC_INT_MASK, bgmac->int_mask);
}
static void bgmac_chip_intrs_off(struct bgmac *bgmac)
{
bgmac_write(bgmac, BGMAC_INT_MASK, 0);
bgmac_read(bgmac, BGMAC_INT_MASK);
}
/* http://bcm-v4.sipsolutions.net/mac-gbit/gmac/gmac_enable */
static void bgmac_enable(struct bgmac *bgmac)
{
struct bcma_chipinfo *ci = &bgmac->core->bus->chipinfo;
u32 cmdcfg;
u32 mode;
u32 rxq_ctl;
u32 fl_ctl;
u16 bp_clk;
u8 mdp;
cmdcfg = bgmac_read(bgmac, BGMAC_CMDCFG);
bgmac_cmdcfg_maskset(bgmac, ~(BGMAC_CMDCFG_TE | BGMAC_CMDCFG_RE),
BGMAC_CMDCFG_SR(bgmac->core->id.rev), true);
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
udelay(2);
cmdcfg |= BGMAC_CMDCFG_TE | BGMAC_CMDCFG_RE;
bgmac_write(bgmac, BGMAC_CMDCFG, cmdcfg);
mode = (bgmac_read(bgmac, BGMAC_DEV_STATUS) & BGMAC_DS_MM_MASK) >>
BGMAC_DS_MM_SHIFT;
if (ci->id != BCMA_CHIP_ID_BCM47162 || mode != 0)
bgmac_set(bgmac, BCMA_CLKCTLST, BCMA_CLKCTLST_FORCEHT);
if (ci->id == BCMA_CHIP_ID_BCM47162 && mode == 2)
bcma_chipco_chipctl_maskset(&bgmac->core->bus->drv_cc, 1, ~0,
BGMAC_CHIPCTL_1_RXC_DLL_BYPASS);
switch (ci->id) {
case BCMA_CHIP_ID_BCM5357:
case BCMA_CHIP_ID_BCM4749:
case BCMA_CHIP_ID_BCM53572:
case BCMA_CHIP_ID_BCM4716:
case BCMA_CHIP_ID_BCM47162:
fl_ctl = 0x03cb04cb;
if (ci->id == BCMA_CHIP_ID_BCM5357 ||
ci->id == BCMA_CHIP_ID_BCM4749 ||
ci->id == BCMA_CHIP_ID_BCM53572)
fl_ctl = 0x2300e1;
bgmac_write(bgmac, BGMAC_FLOW_CTL_THRESH, fl_ctl);
bgmac_write(bgmac, BGMAC_PAUSE_CTL, 0x27fff);
break;
}
if (!bgmac_is_bcm4707_family(bgmac)) {
rxq_ctl = bgmac_read(bgmac, BGMAC_RXQ_CTL);
rxq_ctl &= ~BGMAC_RXQ_CTL_MDP_MASK;
bp_clk = bcma_pmu_get_bus_clock(&bgmac->core->bus->drv_cc) /
1000000;
mdp = (bp_clk * 128 / 1000) - 3;
rxq_ctl |= (mdp << BGMAC_RXQ_CTL_MDP_SHIFT);
bgmac_write(bgmac, BGMAC_RXQ_CTL, rxq_ctl);
}
}
/* http://bcm-v4.sipsolutions.net/mac-gbit/gmac/chipinit */
static void bgmac_chip_init(struct bgmac *bgmac)
{
/* 1 interrupt per received frame */
bgmac_write(bgmac, BGMAC_INT_RECV_LAZY, 1 << BGMAC_IRL_FC_SHIFT);
/* Enable 802.3x tx flow control (honor received PAUSE frames) */
bgmac_cmdcfg_maskset(bgmac, ~BGMAC_CMDCFG_RPI, 0, true);
bgmac_set_rx_mode(bgmac->net_dev);
bgmac_write_mac_address(bgmac, bgmac->net_dev->dev_addr);
bgmac_cmdcfg_maskset(bgmac, ~0, BGMAC_CMDCFG_ML, false);
bgmac_cmdcfg_maskset(bgmac, ~BGMAC_CMDCFG_ML, 0, false);
bgmac_write(bgmac, BGMAC_RXMAX_LENGTH, 32 + ETHER_MAX_LEN);
bgmac_enable(bgmac);
}
static irqreturn_t bgmac_interrupt(int irq, void *dev_id)
{
struct bgmac *bgmac = netdev_priv(dev_id);
u32 int_status = bgmac_read(bgmac, BGMAC_INT_STATUS);
int_status &= bgmac->int_mask;
if (!int_status)
return IRQ_NONE;
int_status &= ~(BGMAC_IS_TX0 | BGMAC_IS_RX);
if (int_status)
bgmac_err(bgmac, "Unknown IRQs: 0x%08X\n", int_status);
/* Disable new interrupts until handling existing ones */
bgmac_chip_intrs_off(bgmac);
napi_schedule(&bgmac->napi);
return IRQ_HANDLED;
}
static int bgmac_poll(struct napi_struct *napi, int weight)
{
struct bgmac *bgmac = container_of(napi, struct bgmac, napi);
int handled = 0;
/* Ack */
bgmac_write(bgmac, BGMAC_INT_STATUS, ~0);
bgmac_dma_tx_free(bgmac, &bgmac->tx_ring[0]);
handled += bgmac_dma_rx_read(bgmac, &bgmac->rx_ring[0], weight);
/* Poll again if more events arrived in the meantime */
if (bgmac_read(bgmac, BGMAC_INT_STATUS) & (BGMAC_IS_TX0 | BGMAC_IS_RX))
if (handled < weight) {
bgmac_chip_intrs_on(bgmac);
}
return handled;
}
/**************************************************
* net_device_ops
**************************************************/
static int bgmac_open(struct net_device *net_dev)
{
struct bgmac *bgmac = netdev_priv(net_dev);
int err = 0;
bgmac_chip_reset(bgmac);
err = bgmac_dma_init(bgmac);
if (err)
return err;
/* Specs say about reclaiming rings here, but we do that in DMA init */
err = request_irq(bgmac->core->irq, bgmac_interrupt, IRQF_SHARED,
KBUILD_MODNAME, net_dev);
if (err < 0) {
bgmac_err(bgmac, "IRQ request error: %d!\n", err);
bgmac_dma_cleanup(bgmac);
return err;
}
napi_enable(&bgmac->napi);
phy_start(bgmac->phy_dev);
netif_start_queue(net_dev);
}
static int bgmac_stop(struct net_device *net_dev)
{
struct bgmac *bgmac = netdev_priv(net_dev);
netif_carrier_off(net_dev);
phy_stop(bgmac->phy_dev);
napi_disable(&bgmac->napi);
bgmac_chip_intrs_off(bgmac);
free_irq(bgmac->core->irq, net_dev);
bgmac_chip_reset(bgmac);
return 0;
}
static netdev_tx_t bgmac_start_xmit(struct sk_buff *skb,
struct net_device *net_dev)
{
struct bgmac *bgmac = netdev_priv(net_dev);
struct bgmac_dma_ring *ring;
/* No QOS support yet */
ring = &bgmac->tx_ring[0];
return bgmac_dma_tx_add(bgmac, ring, skb);
}
static int bgmac_set_mac_address(struct net_device *net_dev, void *addr)
{
struct bgmac *bgmac = netdev_priv(net_dev);
int ret;
ret = eth_prepare_mac_addr_change(net_dev, addr);
if (ret < 0)
return ret;
bgmac_write_mac_address(bgmac, (u8 *)addr);
eth_commit_mac_addr_change(net_dev, addr);
return 0;
}
static int bgmac_ioctl(struct net_device *net_dev, struct ifreq *ifr, int cmd)
{
struct bgmac *bgmac = netdev_priv(net_dev);
if (!netif_running(net_dev))
return -EINVAL;
return phy_mii_ioctl(bgmac->phy_dev, ifr, cmd);
}
static const struct net_device_ops bgmac_netdev_ops = {
.ndo_open = bgmac_open,
.ndo_stop = bgmac_stop,
.ndo_start_xmit = bgmac_start_xmit,
.ndo_set_rx_mode = bgmac_set_rx_mode,
.ndo_set_mac_address = bgmac_set_mac_address,
.ndo_validate_addr = eth_validate_addr,
.ndo_do_ioctl = bgmac_ioctl,
};
/**************************************************
* ethtool_ops
**************************************************/
static int bgmac_get_settings(struct net_device *net_dev,
struct ethtool_cmd *cmd)
{
struct bgmac *bgmac = netdev_priv(net_dev);
return phy_ethtool_gset(bgmac->phy_dev, cmd);
}
static int bgmac_set_settings(struct net_device *net_dev,
struct ethtool_cmd *cmd)
{
struct bgmac *bgmac = netdev_priv(net_dev);
return phy_ethtool_sset(bgmac->phy_dev, cmd);
}
static void bgmac_get_drvinfo(struct net_device *net_dev,
struct ethtool_drvinfo *info)
{
strlcpy(info->driver, KBUILD_MODNAME, sizeof(info->driver));
strlcpy(info->bus_info, "BCMA", sizeof(info->bus_info));
}
static const struct ethtool_ops bgmac_ethtool_ops = {
.get_settings = bgmac_get_settings,
.set_settings = bgmac_set_settings,
.get_drvinfo = bgmac_get_drvinfo,
};
/**************************************************
* MII
**************************************************/
static int bgmac_mii_read(struct mii_bus *bus, int mii_id, int regnum)
{
return bgmac_phy_read(bus->priv, mii_id, regnum);
}
static int bgmac_mii_write(struct mii_bus *bus, int mii_id, int regnum,
u16 value)
{
return bgmac_phy_write(bus->priv, mii_id, regnum, value);
}
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
static void bgmac_adjust_link(struct net_device *net_dev)
{
struct bgmac *bgmac = netdev_priv(net_dev);
struct phy_device *phy_dev = bgmac->phy_dev;
bool update = false;
if (phy_dev->link) {
if (phy_dev->speed != bgmac->mac_speed) {
bgmac->mac_speed = phy_dev->speed;
update = true;
}
if (phy_dev->duplex != bgmac->mac_duplex) {
bgmac->mac_duplex = phy_dev->duplex;
update = true;
}
}
if (update) {
bgmac_mac_speed(bgmac);
phy_print_status(phy_dev);
}
}
static int bgmac_fixed_phy_register(struct bgmac *bgmac)
{
struct fixed_phy_status fphy_status = {
.link = 1,
.speed = SPEED_1000,
.duplex = DUPLEX_FULL,
};
struct phy_device *phy_dev;
int err;
phy_dev = fixed_phy_register(PHY_POLL, &fphy_status, -1, NULL);
if (!phy_dev || IS_ERR(phy_dev)) {
bgmac_err(bgmac, "Failed to register fixed PHY device\n");
return -ENODEV;
}
err = phy_connect_direct(bgmac->net_dev, phy_dev, bgmac_adjust_link,
PHY_INTERFACE_MODE_MII);
if (err) {
bgmac_err(bgmac, "Connecting PHY failed\n");
return err;
}
bgmac->phy_dev = phy_dev;
return err;
}
static int bgmac_mii_register(struct bgmac *bgmac)
{
struct mii_bus *mii_bus;
struct phy_device *phy_dev;
char bus_id[MII_BUS_ID_SIZE + 3];
if (bgmac_is_bcm4707_family(bgmac))
return bgmac_fixed_phy_register(bgmac);
mii_bus = mdiobus_alloc();
if (!mii_bus)
return -ENOMEM;
mii_bus->name = "bgmac mii bus";
sprintf(mii_bus->id, "%s-%d-%d", "bgmac", bgmac->core->bus->num,
bgmac->core->core_unit);
mii_bus->priv = bgmac;
mii_bus->read = bgmac_mii_read;
mii_bus->write = bgmac_mii_write;
mii_bus->parent = &bgmac->core->dev;
mii_bus->phy_mask = ~(1 << bgmac->phyaddr);
err = mdiobus_register(mii_bus);
if (err) {
bgmac_err(bgmac, "Registration of mii bus failed\n");
/* Connect to the PHY */
snprintf(bus_id, sizeof(bus_id), PHY_ID_FMT, mii_bus->id,
bgmac->phyaddr);
phy_dev = phy_connect(bgmac->net_dev, bus_id, &bgmac_adjust_link,
PHY_INTERFACE_MODE_MII);
if (IS_ERR(phy_dev)) {
bgmac_err(bgmac, "PHY connection failed\n");
err = PTR_ERR(phy_dev);
goto err_unregister_bus;
}
bgmac->phy_dev = phy_dev;
err_unregister_bus:
mdiobus_unregister(mii_bus);
err_free_bus:
mdiobus_free(mii_bus);
return err;
}
static void bgmac_mii_unregister(struct bgmac *bgmac)
{
struct mii_bus *mii_bus = bgmac->mii_bus;
mdiobus_unregister(mii_bus);
mdiobus_free(mii_bus);
}
/**************************************************
* BCMA bus ops
**************************************************/
/* http://bcm-v4.sipsolutions.net/mac-gbit/gmac/chipattach */
static int bgmac_probe(struct bcma_device *core)
{
struct net_device *net_dev;
struct bgmac *bgmac;
struct ssb_sprom *sprom = &core->bus->sprom;
switch (core->core_unit) {
case 0:
mac = sprom->et0mac;
break;
case 1:
mac = sprom->et1mac;
break;
case 2:
mac = sprom->et2mac;
break;
default:
pr_err("Unsupported core_unit %d\n", core->core_unit);
return -ENOTSUPP;
}
if (!is_valid_ether_addr(mac)) {
dev_err(&core->dev, "Invalid MAC addr: %pM\n", mac);
eth_random_addr(mac);
dev_warn(&core->dev, "Using random MAC: %pM\n", mac);
}
/* This (reset &) enable is not preset in specs or reference driver but
* Broadcom does it in arch PCI code when enabling fake PCI device.
*/
bcma_core_enable(core, 0);
/* Allocation and references */
net_dev = alloc_etherdev(sizeof(*bgmac));
if (!net_dev)
return -ENOMEM;
net_dev->netdev_ops = &bgmac_netdev_ops;
net_dev->irq = core->irq;
net_dev->ethtool_ops = &bgmac_ethtool_ops;
bgmac = netdev_priv(net_dev);
bgmac->net_dev = net_dev;
bgmac->core = core;
bcma_set_drvdata(core, bgmac);
/* Defaults */
memcpy(bgmac->net_dev->dev_addr, mac, ETH_ALEN);
/* On BCM4706 we need common core to access PHY */
if (core->id.id == BCMA_CORE_4706_MAC_GBIT &&
!core->bus->drv_gmac_cmn.core) {
bgmac_err(bgmac, "GMAC CMN core not found (required for BCM4706)\n");
err = -ENODEV;
goto err_netdev_free;
}
bgmac->cmn = core->bus->drv_gmac_cmn.core;
switch (core->core_unit) {
case 0:
bgmac->phyaddr = sprom->et0phyaddr;
break;
case 1:
bgmac->phyaddr = sprom->et1phyaddr;
break;
case 2:
bgmac->phyaddr = sprom->et2phyaddr;
break;
}
bgmac->phyaddr &= BGMAC_PHY_MASK;
if (bgmac->phyaddr == BGMAC_PHY_MASK) {
bgmac_err(bgmac, "No PHY found\n");
err = -ENODEV;
goto err_netdev_free;
}
bgmac_info(bgmac, "Found PHY addr: %d%s\n", bgmac->phyaddr,
bgmac->phyaddr == BGMAC_PHY_NOREGS ? " (NOREGS)" : "");
if (core->bus->hosttype == BCMA_HOSTTYPE_PCI) {
bgmac_err(bgmac, "PCI setup not implemented\n");
err = -ENOTSUPP;
goto err_netdev_free;
}
bgmac_chip_reset(bgmac);
/* For Northstar, we have to take all GMAC core out of reset */
if (bgmac_is_bcm4707_family(bgmac)) {
struct bcma_device *ns_core;
int ns_gmac;
/* Northstar has 4 GMAC cores */
for (ns_gmac = 0; ns_gmac < 4; ns_gmac++) {
/* As Northstar requirement, we have to reset all GMACs
* before accessing one. bgmac_chip_reset() call
* bcma_core_enable() for this core. Then the other
*/
ns_core = bcma_find_core_unit(core->bus,
BCMA_CORE_MAC_GBIT,
ns_gmac);
if (ns_core && !bcma_core_is_enabled(ns_core))
bcma_core_enable(ns_core, 0);
}
}
err = bgmac_dma_alloc(bgmac);
if (err) {
bgmac_err(bgmac, "Unable to alloc memory for DMA\n");
goto err_netdev_free;
}
bgmac->int_mask = BGMAC_IS_ERRMASK | BGMAC_IS_RX | BGMAC_IS_TX_MASK;
if (bcm47xx_nvram_getenv("et0_no_txint", NULL, 0) == 0)
bgmac->int_mask &= ~BGMAC_IS_TX_MASK;
/* TODO: reset the external phy. Specs are needed */
bgmac_phy_reset(bgmac);
bgmac->has_robosw = !!(core->bus->sprom.boardflags_lo &
BGMAC_BFL_ENETROBO);
if (bgmac->has_robosw)
bgmac_warn(bgmac, "Support for Roboswitch not implemented\n");
if (core->bus->sprom.boardflags_lo & BGMAC_BFL_ENETADM)
bgmac_warn(bgmac, "Support for ADMtek ethernet switch not implemented\n");
netif_napi_add(net_dev, &bgmac->napi, bgmac_poll, BGMAC_WEIGHT);
err = bgmac_mii_register(bgmac);
if (err) {
bgmac_err(bgmac, "Cannot register MDIO\n");
goto err_dma_free;
}
net_dev->features = NETIF_F_SG | NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM;
net_dev->hw_features = net_dev->features;
net_dev->vlan_features = net_dev->features;
err = register_netdev(bgmac->net_dev);
if (err) {
bgmac_err(bgmac, "Cannot register net device\n");
}
netif_carrier_off(net_dev);
return 0;
err_mii_unregister:
bgmac_mii_unregister(bgmac);
err_dma_free:
bgmac_dma_free(bgmac);
err_netdev_free:
bcma_set_drvdata(core, NULL);
free_netdev(net_dev);
return err;
}
static void bgmac_remove(struct bcma_device *core)
{
struct bgmac *bgmac = bcma_get_drvdata(core);
unregister_netdev(bgmac->net_dev);
1716
1717
1718
1719
1720
1721
1722
1723
1724
1725
1726
1727
1728
1729
1730
1731
1732
1733
1734
1735
1736
1737
1738
1739
1740
1741
1742
1743
1744
1745
1746
1747
1748
1749
bgmac_dma_free(bgmac);
bcma_set_drvdata(core, NULL);
free_netdev(bgmac->net_dev);
}
static struct bcma_driver bgmac_bcma_driver = {
.name = KBUILD_MODNAME,
.id_table = bgmac_bcma_tbl,
.probe = bgmac_probe,
.remove = bgmac_remove,
};
static int __init bgmac_init(void)
{
int err;
err = bcma_driver_register(&bgmac_bcma_driver);
if (err)
return err;
pr_info("Broadcom 47xx GBit MAC driver loaded\n");
return 0;
}
static void __exit bgmac_exit(void)
{
bcma_driver_unregister(&bgmac_bcma_driver);
}
module_init(bgmac_init)
module_exit(bgmac_exit)
MODULE_AUTHOR("Rafał Miłecki");
MODULE_LICENSE("GPL");