b53: allow configuration through device tree
Add support for the same binding as upstream b53 to allow an easy switch. Signed-off-by: Jonas Gorski <jonas.gorski@gmail.com>
This commit is contained in:
parent
9ceeaf4c6c
commit
5df15c06cc
3 changed files with 180 additions and 19 deletions
|
@ -24,6 +24,8 @@
|
||||||
#include <linux/kernel.h>
|
#include <linux/kernel.h>
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/switch.h>
|
#include <linux/switch.h>
|
||||||
|
#include <linux/of.h>
|
||||||
|
#include <linux/of_net.h>
|
||||||
#include <linux/platform_data/b53.h>
|
#include <linux/platform_data/b53.h>
|
||||||
|
|
||||||
#include "b53_regs.h"
|
#include "b53_regs.h"
|
||||||
|
@ -478,35 +480,88 @@ static void b53_switch_reset_gpio(struct b53_device *dev)
|
||||||
dev->current_page = 0xff;
|
dev->current_page = 0xff;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int b53_switch_reset(struct b53_device *dev)
|
static int b53_configure_ports_of(struct b53_device *dev)
|
||||||
{
|
{
|
||||||
u8 cpu_port = dev->sw_dev.cpu_port;
|
struct device_node *dn, *pn;
|
||||||
u8 mgmt;
|
u32 port_num;
|
||||||
|
|
||||||
b53_switch_reset_gpio(dev);
|
dn = of_get_child_by_name(dev_of_node(dev->dev), "ports");
|
||||||
|
|
||||||
if (is539x(dev)) {
|
for_each_available_child_of_node(dn, pn) {
|
||||||
b53_write8(dev, B53_CTRL_PAGE, B53_SOFTRESET, 0x83);
|
struct device_node *fixed_link;
|
||||||
b53_write8(dev, B53_CTRL_PAGE, B53_SOFTRESET, 0x00);
|
|
||||||
}
|
|
||||||
|
|
||||||
b53_read8(dev, B53_CTRL_PAGE, B53_SWITCH_MODE, &mgmt);
|
if (of_property_read_u32(pn, "reg", &port_num))
|
||||||
|
continue;
|
||||||
|
|
||||||
if (!(mgmt & SM_SW_FWD_EN)) {
|
if (port_num > B53_CPU_PORT)
|
||||||
mgmt &= ~SM_SW_FWD_MODE;
|
continue;
|
||||||
mgmt |= SM_SW_FWD_EN;
|
|
||||||
|
|
||||||
b53_write8(dev, B53_CTRL_PAGE, B53_SWITCH_MODE, mgmt);
|
fixed_link = of_get_child_by_name(pn, "fixed-link");
|
||||||
b53_read8(dev, B53_CTRL_PAGE, B53_SWITCH_MODE, &mgmt);
|
if (fixed_link) {
|
||||||
|
u32 spd;
|
||||||
|
u8 po = GMII_PO_LINK;
|
||||||
|
int mode = of_get_phy_mode(pn);
|
||||||
|
|
||||||
if (!(mgmt & SM_SW_FWD_EN)) {
|
if (!of_property_read_u32(fixed_link, "speed", &spd)) {
|
||||||
pr_err("Failed to enable switch!\n");
|
switch (spd) {
|
||||||
return -EINVAL;
|
case 10:
|
||||||
|
po |= GMII_PO_SPEED_10M;
|
||||||
|
break;
|
||||||
|
case 100:
|
||||||
|
po |= GMII_PO_SPEED_100M;
|
||||||
|
break;
|
||||||
|
case 2000:
|
||||||
|
if (is_imp_port(dev, port_num))
|
||||||
|
po |= PORT_OVERRIDE_SPEED_2000M;
|
||||||
|
else
|
||||||
|
po |= GMII_PO_SPEED_2000M;
|
||||||
|
/* fall through */
|
||||||
|
case 1000:
|
||||||
|
po |= GMII_PO_SPEED_1000M;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (of_property_read_bool(fixed_link, "full-duplex"))
|
||||||
|
po |= PORT_OVERRIDE_FULL_DUPLEX;
|
||||||
|
if (of_property_read_bool(fixed_link, "pause"))
|
||||||
|
po |= GMII_PO_RX_FLOW;
|
||||||
|
if (of_property_read_bool(fixed_link, "asym-pause"))
|
||||||
|
po |= GMII_PO_TX_FLOW;
|
||||||
|
|
||||||
|
if (is_imp_port(dev, port_num)) {
|
||||||
|
po |= PORT_OVERRIDE_EN;
|
||||||
|
|
||||||
|
if (is5325(dev) &&
|
||||||
|
mode == PHY_INTERFACE_MODE_REVMII)
|
||||||
|
po |= PORT_OVERRIDE_RV_MII_25;
|
||||||
|
|
||||||
|
b53_write8(dev, B53_CTRL_PAGE,
|
||||||
|
B53_PORT_OVERRIDE_CTRL, po);
|
||||||
|
|
||||||
|
if (is5325(dev) &&
|
||||||
|
mode == PHY_INTERFACE_MODE_REVMII) {
|
||||||
|
b53_read8(dev, B53_CTRL_PAGE,
|
||||||
|
B53_PORT_OVERRIDE_CTRL, &po);
|
||||||
|
if (!(po & PORT_OVERRIDE_RV_MII_25))
|
||||||
|
pr_err("Failed to enable reverse MII mode\n");
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
po |= GMII_PO_EN;
|
||||||
|
b53_write8(dev, B53_CTRL_PAGE,
|
||||||
|
B53_GMII_PORT_OVERRIDE_CTRL(port_num),
|
||||||
|
po);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* enable all ports */
|
return 0;
|
||||||
b53_enable_ports(dev);
|
}
|
||||||
|
|
||||||
|
static int b53_configure_ports(struct b53_device *dev)
|
||||||
|
{
|
||||||
|
u8 cpu_port = dev->sw_dev.cpu_port;
|
||||||
|
|
||||||
/* configure MII port if necessary */
|
/* configure MII port if necessary */
|
||||||
if (is5325(dev)) {
|
if (is5325(dev)) {
|
||||||
|
@ -576,6 +631,47 @@ static int b53_switch_reset(struct b53_device *dev)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int b53_switch_reset(struct b53_device *dev)
|
||||||
|
{
|
||||||
|
int ret = 0;
|
||||||
|
u8 mgmt;
|
||||||
|
|
||||||
|
b53_switch_reset_gpio(dev);
|
||||||
|
|
||||||
|
if (is539x(dev)) {
|
||||||
|
b53_write8(dev, B53_CTRL_PAGE, B53_SOFTRESET, 0x83);
|
||||||
|
b53_write8(dev, B53_CTRL_PAGE, B53_SOFTRESET, 0x00);
|
||||||
|
}
|
||||||
|
|
||||||
|
b53_read8(dev, B53_CTRL_PAGE, B53_SWITCH_MODE, &mgmt);
|
||||||
|
|
||||||
|
if (!(mgmt & SM_SW_FWD_EN)) {
|
||||||
|
mgmt &= ~SM_SW_FWD_MODE;
|
||||||
|
mgmt |= SM_SW_FWD_EN;
|
||||||
|
|
||||||
|
b53_write8(dev, B53_CTRL_PAGE, B53_SWITCH_MODE, mgmt);
|
||||||
|
b53_read8(dev, B53_CTRL_PAGE, B53_SWITCH_MODE, &mgmt);
|
||||||
|
|
||||||
|
if (!(mgmt & SM_SW_FWD_EN)) {
|
||||||
|
pr_err("Failed to enable switch!\n");
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* enable all ports */
|
||||||
|
b53_enable_ports(dev);
|
||||||
|
|
||||||
|
if (dev->dev->of_node)
|
||||||
|
ret = b53_configure_ports_of(dev);
|
||||||
|
else
|
||||||
|
ret = b53_configure_ports(dev);
|
||||||
|
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
b53_enable_mib(dev);
|
b53_enable_mib(dev);
|
||||||
|
|
||||||
return b53_flush_arl(dev);
|
return b53_flush_arl(dev);
|
||||||
|
@ -1320,6 +1416,43 @@ static const struct b53_chip_data b53_switch_chips[] = {
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static int b53_switch_init_of(struct b53_device *dev)
|
||||||
|
{
|
||||||
|
struct device_node *dn, *pn;
|
||||||
|
const char *alias;
|
||||||
|
u32 port_num;
|
||||||
|
u16 ports = 0;
|
||||||
|
|
||||||
|
dn = of_get_child_by_name(dev_of_node(dev->dev), "ports");
|
||||||
|
if (!dn)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
for_each_available_child_of_node(dn, pn) {
|
||||||
|
const char *label;
|
||||||
|
int len;
|
||||||
|
|
||||||
|
if (of_property_read_u32(pn, "reg", &port_num))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (port_num > B53_CPU_PORT)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
ports |= BIT(port_num);
|
||||||
|
|
||||||
|
label = of_get_property(pn, "label", &len);
|
||||||
|
if (label && !strcmp(label, "cpu"))
|
||||||
|
dev->sw_dev.cpu_port = port_num;
|
||||||
|
}
|
||||||
|
|
||||||
|
dev->enabled_ports = ports;
|
||||||
|
|
||||||
|
if (!of_property_read_string(dev_of_node(dev->dev), "lede,alias",
|
||||||
|
&alias))
|
||||||
|
dev->sw_dev.alias = devm_kstrdup(dev->dev, alias, GFP_KERNEL);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static int b53_switch_init(struct b53_device *dev)
|
static int b53_switch_init(struct b53_device *dev)
|
||||||
{
|
{
|
||||||
struct switch_dev *sw_dev = &dev->sw_dev;
|
struct switch_dev *sw_dev = &dev->sw_dev;
|
||||||
|
@ -1383,6 +1516,12 @@ static int b53_switch_init(struct b53_device *dev)
|
||||||
sw_dev->cpu_port = 5;
|
sw_dev->cpu_port = 5;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (dev_of_node(dev->dev)) {
|
||||||
|
ret = b53_switch_init_of(dev);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
dev->enabled_ports |= BIT(sw_dev->cpu_port);
|
dev->enabled_ports |= BIT(sw_dev->cpu_port);
|
||||||
sw_dev->ports = fls(dev->enabled_ports);
|
sw_dev->ports = fls(dev->enabled_ports);
|
||||||
|
|
||||||
|
|
|
@ -170,6 +170,14 @@ static inline int is_cpu_port(struct b53_device *dev, int port)
|
||||||
return dev->sw_dev.cpu_port == port;
|
return dev->sw_dev.cpu_port == port;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline int is_imp_port(struct b53_device *dev, int port)
|
||||||
|
{
|
||||||
|
if (is5325(dev) || is5365(dev))
|
||||||
|
return port == B53_CPU_PORT_25;
|
||||||
|
else
|
||||||
|
return port == B53_CPU_PORT;
|
||||||
|
}
|
||||||
|
|
||||||
static inline struct b53_device *sw_to_b53(struct switch_dev *sw)
|
static inline struct b53_device *sw_to_b53(struct switch_dev *sw)
|
||||||
{
|
{
|
||||||
return container_of(sw, struct b53_device, sw_dev);
|
return container_of(sw, struct b53_device, sw_dev);
|
||||||
|
|
|
@ -21,6 +21,7 @@
|
||||||
#include <linux/kernel.h>
|
#include <linux/kernel.h>
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/spi/spi.h>
|
#include <linux/spi/spi.h>
|
||||||
|
#include <linux/of.h>
|
||||||
#include <linux/platform_data/b53.h>
|
#include <linux/platform_data/b53.h>
|
||||||
|
|
||||||
#include "b53_priv.h"
|
#include "b53_priv.h"
|
||||||
|
@ -313,11 +314,24 @@ static int b53_spi_remove(struct spi_device *spi)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static const struct of_device_id b53_of_match[] = {
|
||||||
|
{ .compatible = "brcm,bcm5325" },
|
||||||
|
{ .compatible = "brcm,bcm53115" },
|
||||||
|
{ .compatible = "brcm,bcm53125" },
|
||||||
|
{ .compatible = "brcm,bcm53128" },
|
||||||
|
{ .compatible = "brcm,bcm5365" },
|
||||||
|
{ .compatible = "brcm,bcm5395" },
|
||||||
|
{ .compatible = "brcm,bcm5397" },
|
||||||
|
{ .compatible = "brcm,bcm5398" },
|
||||||
|
{ /* sentinel */ },
|
||||||
|
};
|
||||||
|
|
||||||
static struct spi_driver b53_spi_driver = {
|
static struct spi_driver b53_spi_driver = {
|
||||||
.driver = {
|
.driver = {
|
||||||
.name = "b53-switch",
|
.name = "b53-switch",
|
||||||
.bus = &spi_bus_type,
|
.bus = &spi_bus_type,
|
||||||
.owner = THIS_MODULE,
|
.owner = THIS_MODULE,
|
||||||
|
.of_match_table = b53_of_match,
|
||||||
},
|
},
|
||||||
.probe = b53_spi_probe,
|
.probe = b53_spi_probe,
|
||||||
.remove = b53_spi_remove,
|
.remove = b53_spi_remove,
|
||||||
|
|
Loading…
Reference in a new issue