ath79: ag71xx: Make builtin switch driver a separated module
This patch did several things: 1. Probe the builtin switch as a separated mdio device. 2. Register a separated mdio bus for builtin switch. 3. Use generic mdio read/write function instead of calling ag71xx_mdio_mii_read/write directly. Signed-off-by: Chuanhong Guo <gch981213@gmail.com>
This commit is contained in:
parent
83d2dbc599
commit
7ae9e63719
2 changed files with 84 additions and 68 deletions
|
@ -6,10 +6,9 @@ ag71xx-y += ag71xx_main.o
|
||||||
ag71xx-y += ag71xx_gmac.o
|
ag71xx-y += ag71xx_gmac.o
|
||||||
ag71xx-y += ag71xx_ethtool.o
|
ag71xx-y += ag71xx_ethtool.o
|
||||||
ag71xx-y += ag71xx_phy.o
|
ag71xx-y += ag71xx_phy.o
|
||||||
ag71xx-y += ag71xx_ar7240.o
|
|
||||||
|
|
||||||
ag71xx-$(CONFIG_AG71XX_DEBUG_FS) += ag71xx_debugfs.o
|
ag71xx-$(CONFIG_AG71XX_DEBUG_FS) += ag71xx_debugfs.o
|
||||||
|
|
||||||
|
obj-$(CONFIG_AG71XX) += ag71xx_ar7240.o
|
||||||
obj-$(CONFIG_AG71XX) += ag71xx_mdio.o
|
obj-$(CONFIG_AG71XX) += ag71xx_mdio.o
|
||||||
obj-$(CONFIG_AG71XX) += ag71xx.o
|
obj-$(CONFIG_AG71XX) += ag71xx.o
|
||||||
|
|
||||||
|
|
|
@ -12,6 +12,8 @@
|
||||||
#include <linux/etherdevice.h>
|
#include <linux/etherdevice.h>
|
||||||
#include <linux/list.h>
|
#include <linux/list.h>
|
||||||
#include <linux/netdevice.h>
|
#include <linux/netdevice.h>
|
||||||
|
#include <linux/of_mdio.h>
|
||||||
|
#include <linux/of_net.h>
|
||||||
#include <linux/phy.h>
|
#include <linux/phy.h>
|
||||||
#include <linux/mii.h>
|
#include <linux/mii.h>
|
||||||
#include <linux/bitops.h>
|
#include <linux/bitops.h>
|
||||||
|
@ -291,7 +293,9 @@ struct ar7240sw_port_stat {
|
||||||
|
|
||||||
struct ar7240sw {
|
struct ar7240sw {
|
||||||
struct mii_bus *mii_bus;
|
struct mii_bus *mii_bus;
|
||||||
struct ag71xx_switch_platform_data *swdata;
|
struct mii_bus *switch_mii_bus;
|
||||||
|
struct device_node *of_node;
|
||||||
|
struct device_node *mdio_node;
|
||||||
struct switch_dev swdev;
|
struct switch_dev swdev;
|
||||||
int num_ports;
|
int num_ports;
|
||||||
u8 ver;
|
u8 ver;
|
||||||
|
@ -366,9 +370,11 @@ static u32 __ar7240sw_reg_read(struct mii_bus *mii, u32 reg)
|
||||||
phy_reg = mk_phy_reg(reg);
|
phy_reg = mk_phy_reg(reg);
|
||||||
|
|
||||||
local_irq_save(flags);
|
local_irq_save(flags);
|
||||||
ag71xx_mdio_mii_write(mii, 0x1f, 0x10, mk_high_addr(reg));
|
mutex_lock(&mii->mdio_lock);
|
||||||
lo = (u32) ag71xx_mdio_mii_read(mii, phy_addr, phy_reg);
|
mii->write(mii, 0x1f, 0x10, mk_high_addr(reg));
|
||||||
hi = (u32) ag71xx_mdio_mii_read(mii, phy_addr, phy_reg + 1);
|
lo = (u32) mii->read(mii, phy_addr, phy_reg);
|
||||||
|
hi = (u32) mii->read(mii, phy_addr, phy_reg + 1);
|
||||||
|
mutex_unlock(&mii->mdio_lock);
|
||||||
local_irq_restore(flags);
|
local_irq_restore(flags);
|
||||||
|
|
||||||
return (hi << 16) | lo;
|
return (hi << 16) | lo;
|
||||||
|
@ -385,9 +391,11 @@ static void __ar7240sw_reg_write(struct mii_bus *mii, u32 reg, u32 val)
|
||||||
phy_reg = mk_phy_reg(reg);
|
phy_reg = mk_phy_reg(reg);
|
||||||
|
|
||||||
local_irq_save(flags);
|
local_irq_save(flags);
|
||||||
ag71xx_mdio_mii_write(mii, 0x1f, 0x10, mk_high_addr(reg));
|
mutex_lock(&mii->mdio_lock);
|
||||||
ag71xx_mdio_mii_write(mii, phy_addr, phy_reg + 1, (val >> 16));
|
mii->write(mii, 0x1f, 0x10, mk_high_addr(reg));
|
||||||
ag71xx_mdio_mii_write(mii, phy_addr, phy_reg, (val & 0xffff));
|
mii->write(mii, phy_addr, phy_reg + 1, (val >> 16));
|
||||||
|
mii->write(mii, phy_addr, phy_reg, (val & 0xffff));
|
||||||
|
mutex_unlock(&mii->mdio_lock);
|
||||||
local_irq_restore(flags);
|
local_irq_restore(flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -463,10 +471,12 @@ static int ar7240sw_reg_wait(struct mii_bus *mii, u32 reg, u32 mask, u32 val,
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
int ar7240sw_phy_read(struct mii_bus *mii, int phy_addr, int reg_addr)
|
int ar7240sw_phy_read(struct mii_bus *bus, int phy_addr, int reg_addr)
|
||||||
{
|
{
|
||||||
u32 t, val = 0xffff;
|
u32 t, val = 0xffff;
|
||||||
int err;
|
int err;
|
||||||
|
struct ar7240sw *as = bus->priv;
|
||||||
|
struct mii_bus *mii = as->mii_bus;
|
||||||
|
|
||||||
if (phy_addr >= AR7240_NUM_PHYS)
|
if (phy_addr >= AR7240_NUM_PHYS)
|
||||||
return 0xffff;
|
return 0xffff;
|
||||||
|
@ -488,11 +498,13 @@ int ar7240sw_phy_read(struct mii_bus *mii, int phy_addr, int reg_addr)
|
||||||
return val & AR7240_MDIO_CTRL_DATA_M;
|
return val & AR7240_MDIO_CTRL_DATA_M;
|
||||||
}
|
}
|
||||||
|
|
||||||
int ar7240sw_phy_write(struct mii_bus *mii, int phy_addr, int reg_addr,
|
int ar7240sw_phy_write(struct mii_bus *bus, int phy_addr, int reg_addr,
|
||||||
u16 reg_val)
|
u16 reg_val)
|
||||||
{
|
{
|
||||||
u32 t;
|
u32 t;
|
||||||
int ret;
|
int ret;
|
||||||
|
struct ar7240sw *as = bus->priv;
|
||||||
|
struct mii_bus *mii = as->mii_bus;
|
||||||
|
|
||||||
if (phy_addr >= AR7240_NUM_PHYS)
|
if (phy_addr >= AR7240_NUM_PHYS)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
@ -646,6 +658,7 @@ ar7240sw_phy_poll_reset(struct mii_bus *bus)
|
||||||
static int ar7240sw_reset(struct ar7240sw *as)
|
static int ar7240sw_reset(struct ar7240sw *as)
|
||||||
{
|
{
|
||||||
struct mii_bus *mii = as->mii_bus;
|
struct mii_bus *mii = as->mii_bus;
|
||||||
|
struct mii_bus *swmii = as->switch_mii_bus;
|
||||||
int ret;
|
int ret;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
|
@ -665,13 +678,13 @@ static int ar7240sw_reset(struct ar7240sw *as)
|
||||||
|
|
||||||
/* setup PHYs */
|
/* setup PHYs */
|
||||||
for (i = 0; i < AR7240_NUM_PHYS; i++) {
|
for (i = 0; i < AR7240_NUM_PHYS; i++) {
|
||||||
ar7240sw_phy_write(mii, i, MII_ADVERTISE,
|
ar7240sw_phy_write(swmii, i, MII_ADVERTISE,
|
||||||
ADVERTISE_ALL | ADVERTISE_PAUSE_CAP |
|
ADVERTISE_ALL | ADVERTISE_PAUSE_CAP |
|
||||||
ADVERTISE_PAUSE_ASYM);
|
ADVERTISE_PAUSE_ASYM);
|
||||||
ar7240sw_phy_write(mii, i, MII_BMCR,
|
ar7240sw_phy_write(swmii, i, MII_BMCR,
|
||||||
BMCR_RESET | BMCR_ANENABLE);
|
BMCR_RESET | BMCR_ANENABLE);
|
||||||
}
|
}
|
||||||
ret = ar7240sw_phy_poll_reset(mii);
|
ret = ar7240sw_phy_poll_reset(swmii);
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
|
@ -1199,31 +1212,22 @@ static const struct switch_dev_ops ar7240_ops = {
|
||||||
.get_port_stats = ar7240_get_port_stats,
|
.get_port_stats = ar7240_get_port_stats,
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct ar7240sw *
|
static int
|
||||||
ar7240_probe(struct ag71xx *ag, struct device_node *np)
|
ag71xx_ar7240_probe(struct mdio_device *mdiodev)
|
||||||
{
|
{
|
||||||
struct mii_bus *mii = ag->mii_bus;
|
struct mii_bus *mii = mdiodev->bus;
|
||||||
struct ar7240sw *as;
|
struct ar7240sw *as;
|
||||||
struct switch_dev *swdev;
|
struct switch_dev *swdev;
|
||||||
u32 ctrl;
|
u32 ctrl;
|
||||||
u16 phy_id1;
|
int phy_if_mode, err, i;
|
||||||
u16 phy_id2;
|
|
||||||
int i;
|
|
||||||
|
|
||||||
phy_id1 = ar7240sw_phy_read(mii, 0, MII_PHYSID1);
|
as = devm_kzalloc(&mdiodev->dev, sizeof(*as), GFP_KERNEL);
|
||||||
phy_id2 = ar7240sw_phy_read(mii, 0, MII_PHYSID2);
|
|
||||||
if ((phy_id1 != AR7240_PHY_ID1 || phy_id2 != AR7240_PHY_ID2) &&
|
|
||||||
(phy_id1 != AR934X_PHY_ID1 || phy_id2 != AR934X_PHY_ID2)) {
|
|
||||||
pr_err("%s: unknown phy id '%04x:%04x'\n",
|
|
||||||
dev_name(&mii->dev), phy_id1, phy_id2);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
as = kzalloc(sizeof(*as), GFP_KERNEL);
|
|
||||||
if (!as)
|
if (!as)
|
||||||
return NULL;
|
return -ENOMEM;
|
||||||
|
|
||||||
as->mii_bus = mii;
|
as->mii_bus = mii;
|
||||||
|
as->of_node = mdiodev->dev.of_node;
|
||||||
|
as->mdio_node = of_get_child_by_name(as->of_node, "mdio-bus");
|
||||||
|
|
||||||
swdev = &as->swdev;
|
swdev = &as->swdev;
|
||||||
|
|
||||||
|
@ -1236,20 +1240,21 @@ ar7240_probe(struct ag71xx *ag, struct device_node *np)
|
||||||
swdev->ports = AR7240_NUM_PORTS - 1;
|
swdev->ports = AR7240_NUM_PORTS - 1;
|
||||||
} else if (sw_is_ar934x(as)) {
|
} else if (sw_is_ar934x(as)) {
|
||||||
swdev->name = "AR934X built-in switch";
|
swdev->name = "AR934X built-in switch";
|
||||||
|
phy_if_mode = of_get_phy_mode(as->of_node);
|
||||||
|
|
||||||
if (ag->phy_if_mode == PHY_INTERFACE_MODE_GMII) {
|
if (phy_if_mode == PHY_INTERFACE_MODE_GMII) {
|
||||||
ar7240sw_reg_set(mii, AR934X_REG_OPER_MODE0,
|
ar7240sw_reg_set(mii, AR934X_REG_OPER_MODE0,
|
||||||
AR934X_OPER_MODE0_MAC_GMII_EN);
|
AR934X_OPER_MODE0_MAC_GMII_EN);
|
||||||
} else if (ag->phy_if_mode == PHY_INTERFACE_MODE_MII) {
|
} else if (phy_if_mode == PHY_INTERFACE_MODE_MII) {
|
||||||
ar7240sw_reg_set(mii, AR934X_REG_OPER_MODE0,
|
ar7240sw_reg_set(mii, AR934X_REG_OPER_MODE0,
|
||||||
AR934X_OPER_MODE0_PHY_MII_EN);
|
AR934X_OPER_MODE0_PHY_MII_EN);
|
||||||
} else {
|
} else {
|
||||||
pr_err("%s: invalid PHY interface mode\n",
|
pr_err("%s: invalid PHY interface mode\n",
|
||||||
dev_name(&mii->dev));
|
dev_name(&mdiodev->dev));
|
||||||
goto err_free;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (of_property_read_bool(np, "phy4-mii-enable")) {
|
if (of_property_read_bool(as->of_node, "phy4-mii-enable")) {
|
||||||
ar7240sw_reg_set(mii, AR934X_REG_OPER_MODE1,
|
ar7240sw_reg_set(mii, AR934X_REG_OPER_MODE1,
|
||||||
AR934X_REG_OPER_MODE1_PHY4_MII_EN);
|
AR934X_REG_OPER_MODE1_PHY4_MII_EN);
|
||||||
swdev->ports = AR7240_NUM_PORTS - 1;
|
swdev->ports = AR7240_NUM_PORTS - 1;
|
||||||
|
@ -1258,57 +1263,69 @@ ar7240_probe(struct ag71xx *ag, struct device_node *np)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
pr_err("%s: unsupported chip, ctrl=%08x\n",
|
pr_err("%s: unsupported chip, ctrl=%08x\n",
|
||||||
dev_name(&mii->dev), ctrl);
|
dev_name(&mdiodev->dev), ctrl);
|
||||||
goto err_free;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
swdev->cpu_port = AR7240_PORT_CPU;
|
swdev->cpu_port = AR7240_PORT_CPU;
|
||||||
swdev->vlans = AR7240_MAX_VLANS;
|
swdev->vlans = AR7240_MAX_VLANS;
|
||||||
swdev->ops = &ar7240_ops;
|
swdev->ops = &ar7240_ops;
|
||||||
|
swdev->alias = dev_name(&mdiodev->dev);
|
||||||
|
|
||||||
if (register_switch(&as->swdev, ag->dev) < 0)
|
if ((err = register_switch(&as->swdev, NULL)) < 0)
|
||||||
goto err_free;
|
return err;
|
||||||
|
|
||||||
pr_info("%s: Found an %s\n", dev_name(&mii->dev), swdev->name);
|
pr_info("%s: Found an %s\n", dev_name(&mdiodev->dev), swdev->name);
|
||||||
|
|
||||||
|
as->switch_mii_bus = devm_mdiobus_alloc(&mdiodev->dev);
|
||||||
|
as->switch_mii_bus->name = "ar7240sw_mdio";
|
||||||
|
as->switch_mii_bus->read = ar7240sw_phy_read;
|
||||||
|
as->switch_mii_bus->write = ar7240sw_phy_write;
|
||||||
|
as->switch_mii_bus->priv = as;
|
||||||
|
as->switch_mii_bus->parent = &mdiodev->dev;
|
||||||
|
snprintf(as->switch_mii_bus->id, MII_BUS_ID_SIZE, "%s", dev_name(&mdiodev->dev));
|
||||||
|
|
||||||
|
if(as->mdio_node) {
|
||||||
|
err = of_mdiobus_register(as->switch_mii_bus, as->mdio_node);
|
||||||
|
if (err)
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
/* initialize defaults */
|
/* initialize defaults */
|
||||||
for (i = 0; i < AR7240_MAX_VLANS; i++)
|
for (i = 0; i < AR7240_MAX_VLANS; i++)
|
||||||
as->vlan_id[i] = i;
|
as->vlan_id[i] = i;
|
||||||
|
|
||||||
as->vlan_table[0] = ar7240sw_port_mask_all(as);
|
as->vlan_table[0] = ar7240sw_port_mask_all(as);
|
||||||
|
|
||||||
return as;
|
|
||||||
|
|
||||||
err_free:
|
|
||||||
kfree(as);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
int ag71xx_ar7240_init(struct ag71xx *ag, struct device_node *np)
|
|
||||||
{
|
|
||||||
struct ar7240sw *as;
|
|
||||||
|
|
||||||
as = ar7240_probe(ag, np);
|
|
||||||
if (!as)
|
|
||||||
return -ENODEV;
|
|
||||||
|
|
||||||
ag->phy_priv = as;
|
|
||||||
ar7240sw_reset(as);
|
ar7240sw_reset(as);
|
||||||
ar7240_hw_apply(&as->swdev);
|
ar7240_hw_apply(&as->swdev);
|
||||||
|
|
||||||
rwlock_init(&as->stats_lock);
|
rwlock_init(&as->stats_lock);
|
||||||
|
dev_set_drvdata(&mdiodev->dev, as);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ag71xx_ar7240_cleanup(struct ag71xx *ag)
|
static void
|
||||||
|
ag71xx_ar7240_remove(struct mdio_device *mdiodev)
|
||||||
{
|
{
|
||||||
struct ar7240sw *as = ag->phy_priv;
|
struct ar7240sw *as = dev_get_drvdata(&mdiodev->dev);
|
||||||
|
if(as->mdio_node)
|
||||||
if (!as)
|
mdiobus_unregister(as->switch_mii_bus);
|
||||||
return;
|
|
||||||
|
|
||||||
unregister_switch(&as->swdev);
|
unregister_switch(&as->swdev);
|
||||||
kfree(as);
|
|
||||||
ag->phy_priv = NULL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static const struct of_device_id ag71xx_sw_of_match[] = {
|
||||||
|
{ .compatible = "qca,ar8216-builtin" },
|
||||||
|
{ .compatible = "qca,ar8229-builtin" },
|
||||||
|
{ /* sentinel */ },
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct mdio_driver ag71xx_sw_driver = {
|
||||||
|
.probe = ag71xx_ar7240_probe,
|
||||||
|
.remove = ag71xx_ar7240_remove,
|
||||||
|
.mdiodrv.driver = {
|
||||||
|
.name = "ag71xx-switch",
|
||||||
|
.of_match_table = ag71xx_sw_of_match,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
mdio_module_driver(ag71xx_sw_driver);
|
||||||
|
MODULE_LICENSE("GPL");
|
||||||
|
|
Loading…
Reference in a new issue