Commit ee591f46 authored by David S. Miller's avatar David S. Miller

Merge branch 'xgene-fix-mod-crash-and-1g-hotplug'

Iyappan Subramanian says:

====================
drivers: net: xgene: Fix module crash and 1G hot-plug

This patchset addresses the following issues,

1. Fixes the kernel crash when the driver loaded as an kernel module
	- by fixing hardware cleanups and rearrange kernel API calls

2. Hot-plug issue on the SGMII 1G interface
	- by adding a driver for MDIO management
Signed-off-by: default avatarIyappan Subramanian <isubramanian@apm.com>
Tested-by: default avatarFushen Chen <fchen@apm.com>
Tested-by: default avatarToan Le <toanle@apm.com>
---
v7: Address review comments from v6
	- fixed kbuild warnings
	- unmapped DMA memory on xgene_enet_delete_bufpool()
	- delete descriptor rings and buffer pools on cle_init() failure
	- fixed error deconstruction path on probe

v6: Address review comments from v5
	- changed to use devm_ioremap_resource
	- changed to return PTR_ERR(clk) on failure
	- cleaned up and removed indirections
	- exported mdio read/write and phy_register functions
	- changed mii_bus is to indicate interface instance
	- changed to call the exported mdio read/write and phy_register functions

v5: Address review comments from v4
	- Fixed clock reset sequence by adding delay
	- Fixed clock count by adding clk_unprepare_disable() in port shutdown

v4: Address review comments from v3
	- Reorganized into smaller patches
	- Added wrapper functions for sgmii_control_reset and sgmii_tbi_control_reset
	- Removed clk_get warning info
	- mdio: Changed the order of 'if' statements and removed the 'else' statement
	- mdio: Removed the mdio_read(write) indirection wrapper functions
	- ethtool: Fixed SGMII 1G get_settings and set_settings
	- Documentation: dtb: Added MDIO node information
	- MAINTAINERS: Added MDIO driver and documentation path

v3: Address review comments from v2
	- Add comment about hardware clock reset sequence on xgene_mdio_reset

v2: Address review comments from v1
	- Fixed patch 1 compilation error
	- Fixed mdio@1f610000 xge0clk reference
	- Squashed dtb patches
	- Added PORT_OFFSET macro

v1:
	- Initial version
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents c43212bb 2efccc60
APM X-Gene SoC MDIO node
MDIO node is defined to describe on-chip MDIO controller.
Required properties:
- compatible: Must be "apm,xgene-mdio-rgmii" or "apm,xgene-mdio-xfi"
- #address-cells: Must be <1>.
- #size-cells: Must be <0>.
- reg: Address and length of the register set
- clocks: Reference to the clock entry
For the phys on the mdio bus, there must be a node with the following fields:
- compatible: PHY identifier. Please refer ./phy.txt for the format.
- reg: The ID number for the phy.
Example:
mdio: mdio@17020000 {
compatible = "apm,xgene-mdio-rgmii";
#address-cells = <1>;
#size-cells = <0>;
reg = <0x0 0x17020000 0x0 0xd100>;
clocks = <&menetclk 0>;
};
/* Board-specific peripheral configurations */
&mdio {
menetphy: phy@3 {
reg = <0x3>;
};
sgenet0phy: phy@4 {
reg = <0x4>;
};
sgenet1phy: phy@5 {
reg = <0x5>;
};
};
......@@ -839,7 +839,9 @@ M: Iyappan Subramanian <isubramanian@apm.com>
M: Keyur Chudgar <kchudgar@apm.com>
S: Supported
F: drivers/net/ethernet/apm/xgene/
F: drivers/net/phy/mdio-xgene.c
F: Documentation/devicetree/bindings/net/apm-xgene-enet.txt
F: Documentation/devicetree/bindings/net/apm-xgene-mdio.txt
APTINA CAMERA SENSOR PLL
M: Laurent Pinchart <Laurent.pinchart@ideasonboard.com>
......
......@@ -83,3 +83,9 @@ rtc68: rtc@68 {
status = "ok";
};
};
&mdio {
sgenet0phy: phy@0 {
reg = <0x0>;
};
};
......@@ -79,3 +79,15 @@ &xgenet {
&mmc0 {
status = "ok";
};
&mdio {
menet0phy: phy@3 {
reg = <0x3>;
};
sgenet0phy: phy@4 {
reg = <0x4>;
};
sgenet1phy: phy@5 {
reg = <0x5>;
};
};
......@@ -625,10 +625,18 @@ sbgpio: gpio@17001000{
apm,irq-start = <8>;
};
mdio: mdio@1f610000 {
compatible = "apm,xgene-mdio-xfi";
#address-cells = <1>;
#size-cells = <0>;
reg = <0x0 0x1f610000 0x0 0xd100>;
clocks = <&xge0clk 0>;
};
sgenet0: ethernet@1f610000 {
compatible = "apm,xgene2-sgenet";
status = "disabled";
reg = <0x0 0x1f610000 0x0 0x10000>,
reg = <0x0 0x1f610000 0x0 0xd100>,
<0x0 0x1f600000 0x0 0Xd100>,
<0x0 0x20000000 0x0 0X20000>;
interrupts = <0 96 4>,
......@@ -637,6 +645,7 @@ sgenet0: ethernet@1f610000 {
clocks = <&xge0clk 0>;
local-mac-address = [00 01 73 00 00 01];
phy-connection-type = "sgmii";
phy-handle = <&sgenet0phy>;
};
xgenet1: ethernet@1f620000 {
......
......@@ -237,20 +237,11 @@ sge0clk: sge0clk@1f21c000 {
clocks = <&socplldiv2 0>;
reg = <0x0 0x1f21c000 0x0 0x1000>;
reg-names = "csr-reg";
csr-mask = <0x3>;
csr-mask = <0xa>;
enable-mask = <0xf>;
clock-output-names = "sge0clk";
};
sge1clk: sge1clk@1f21c000 {
compatible = "apm,xgene-device-clock";
#clock-cells = <1>;
clocks = <&socplldiv2 0>;
reg = <0x0 0x1f21c000 0x0 0x1000>;
reg-names = "csr-reg";
csr-mask = <0xc>;
clock-output-names = "sge1clk";
};
xge0clk: xge0clk@1f61c000 {
compatible = "apm,xgene-device-clock";
#clock-cells = <1>;
......@@ -921,6 +912,14 @@ rtc: rtc@10510000 {
clocks = <&rtcclk 0>;
};
mdio: mdio@17020000 {
compatible = "apm,xgene-mdio-rgmii";
#address-cells = <1>;
#size-cells = <0>;
reg = <0x0 0x17020000 0x0 0xd100>;
clocks = <&menetclk 0>;
};
menet: ethernet@17020000 {
compatible = "apm,xgene-enet";
status = "disabled";
......@@ -934,7 +933,7 @@ menet: ethernet@17020000 {
/* mac address will be overwritten by the bootloader */
local-mac-address = [00 00 00 00 00 00];
phy-connection-type = "rgmii";
phy-handle = <&menetphy>;
phy-handle = <&menet0phy>,<&menetphy>;
mdio {
compatible = "apm,xgene-mdio";
#address-cells = <1>;
......@@ -960,6 +959,7 @@ sgenet0: ethernet@1f210000 {
clocks = <&sge0clk 0>;
local-mac-address = [00 00 00 00 00 00];
phy-connection-type = "sgmii";
phy-handle = <&sgenet0phy>;
};
sgenet1: ethernet@1f210030 {
......@@ -973,9 +973,9 @@ sgenet1: ethernet@1f210030 {
<0x0 0xAD 0x4>;
port-id = <1>;
dma-coherent;
clocks = <&sge1clk 0>;
local-mac-address = [00 00 00 00 00 00];
phy-connection-type = "sgmii";
phy-handle = <&sgenet1phy>;
};
xgenet: ethernet@1f610000 {
......
......@@ -3,6 +3,7 @@ config NET_XGENE
depends on HAS_DMA
depends on ARCH_XGENE || COMPILE_TEST
select PHYLIB
select MDIO_XGENE
help
This is the Ethernet driver for the on-chip ethernet interface on the
APM X-Gene SoC.
......
......@@ -65,8 +65,15 @@ static int xgene_get_settings(struct net_device *ndev, struct ethtool_cmd *cmd)
return phy_ethtool_gset(phydev, cmd);
} else if (pdata->phy_mode == PHY_INTERFACE_MODE_SGMII) {
cmd->supported = SUPPORTED_1000baseT_Full |
SUPPORTED_Autoneg | SUPPORTED_MII;
if (pdata->mdio_driver) {
if (!phydev)
return -ENODEV;
return phy_ethtool_gset(phydev, cmd);
}
cmd->supported = SUPPORTED_1000baseT_Full | SUPPORTED_Autoneg |
SUPPORTED_MII;
cmd->advertising = cmd->supported;
ethtool_cmd_speed_set(cmd, SPEED_1000);
cmd->duplex = DUPLEX_FULL;
......@@ -92,12 +99,21 @@ static int xgene_set_settings(struct net_device *ndev, struct ethtool_cmd *cmd)
struct phy_device *phydev = pdata->phy_dev;
if (pdata->phy_mode == PHY_INTERFACE_MODE_RGMII) {
if (phydev == NULL)
if (!phydev)
return -ENODEV;
return phy_ethtool_sset(phydev, cmd);
}
if (pdata->phy_mode == PHY_INTERFACE_MODE_SGMII) {
if (pdata->mdio_driver) {
if (!phydev)
return -ENODEV;
return phy_ethtool_sset(phydev, cmd);
}
}
return -EINVAL;
}
......
......@@ -381,59 +381,6 @@ static void xgene_enet_rd_mcx_mac(struct xgene_enet_pdata *pdata,
rd_addr);
}
static int xgene_mii_phy_write(struct xgene_enet_pdata *pdata, int phy_id,
u32 reg, u16 data)
{
u32 addr = 0, wr_data = 0;
u32 done;
u8 wait = 10;
PHY_ADDR_SET(&addr, phy_id);
REG_ADDR_SET(&addr, reg);
xgene_enet_wr_mcx_mac(pdata, MII_MGMT_ADDRESS_ADDR, addr);
PHY_CONTROL_SET(&wr_data, data);
xgene_enet_wr_mcx_mac(pdata, MII_MGMT_CONTROL_ADDR, wr_data);
do {
usleep_range(5, 10);
xgene_enet_rd_mcx_mac(pdata, MII_MGMT_INDICATORS_ADDR, &done);
} while ((done & BUSY_MASK) && wait--);
if (done & BUSY_MASK) {
netdev_err(pdata->ndev, "MII_MGMT write failed\n");
return -EBUSY;
}
return 0;
}
static int xgene_mii_phy_read(struct xgene_enet_pdata *pdata,
u8 phy_id, u32 reg)
{
u32 addr = 0;
u32 data, done;
u8 wait = 10;
PHY_ADDR_SET(&addr, phy_id);
REG_ADDR_SET(&addr, reg);
xgene_enet_wr_mcx_mac(pdata, MII_MGMT_ADDRESS_ADDR, addr);
xgene_enet_wr_mcx_mac(pdata, MII_MGMT_COMMAND_ADDR, READ_CYCLE_MASK);
do {
usleep_range(5, 10);
xgene_enet_rd_mcx_mac(pdata, MII_MGMT_INDICATORS_ADDR, &done);
} while ((done & BUSY_MASK) && wait--);
if (done & BUSY_MASK) {
netdev_err(pdata->ndev, "MII_MGMT read failed\n");
return -EBUSY;
}
xgene_enet_rd_mcx_mac(pdata, MII_MGMT_STATUS_ADDR, &data);
xgene_enet_wr_mcx_mac(pdata, MII_MGMT_COMMAND_ADDR, 0);
return data;
}
static void xgene_gmac_set_mac_addr(struct xgene_enet_pdata *pdata)
{
u32 addr0, addr1;
......@@ -512,14 +459,11 @@ static void xgene_enet_configure_clock(struct xgene_enet_pdata *pdata)
#endif
}
static void xgene_gmac_init(struct xgene_enet_pdata *pdata)
static void xgene_gmac_set_speed(struct xgene_enet_pdata *pdata)
{
struct device *dev = &pdata->pdev->dev;
u32 value, mc2;
u32 intf_ctl, rgmii;
u32 icm0, icm2;
xgene_gmac_reset(pdata);
u32 icm0, icm2, mc2;
u32 intf_ctl, rgmii, value;
xgene_enet_rd_mcx_csr(pdata, ICM_CONFIG0_REG_0_ADDR, &icm0);
xgene_enet_rd_mcx_csr(pdata, ICM_CONFIG2_REG_0_ADDR, &icm2);
......@@ -564,7 +508,21 @@ static void xgene_gmac_init(struct xgene_enet_pdata *pdata)
mc2 |= FULL_DUPLEX2 | PAD_CRC;
xgene_enet_wr_mcx_mac(pdata, MAC_CONFIG_2_ADDR, mc2);
xgene_enet_wr_mcx_mac(pdata, INTERFACE_CONTROL_ADDR, intf_ctl);
xgene_enet_wr_csr(pdata, RGMII_REG_0_ADDR, rgmii);
xgene_enet_configure_clock(pdata);
xgene_enet_wr_mcx_csr(pdata, ICM_CONFIG0_REG_0_ADDR, icm0);
xgene_enet_wr_mcx_csr(pdata, ICM_CONFIG2_REG_0_ADDR, icm2);
}
static void xgene_gmac_init(struct xgene_enet_pdata *pdata)
{
u32 value;
if (!pdata->mdio_driver)
xgene_gmac_reset(pdata);
xgene_gmac_set_speed(pdata);
xgene_gmac_set_mac_addr(pdata);
/* Adjust MDC clock frequency */
......@@ -579,15 +537,10 @@ static void xgene_gmac_init(struct xgene_enet_pdata *pdata)
/* Rtype should be copied from FP */
xgene_enet_wr_csr(pdata, RSIF_RAM_DBG_REG0_ADDR, 0);
xgene_enet_wr_csr(pdata, RGMII_REG_0_ADDR, rgmii);
xgene_enet_configure_clock(pdata);
/* Rx-Tx traffic resume */
xgene_enet_wr_csr(pdata, CFG_LINK_AGGR_RESUME_0_ADDR, TX_PORT0);
xgene_enet_wr_mcx_csr(pdata, ICM_CONFIG0_REG_0_ADDR, icm0);
xgene_enet_wr_mcx_csr(pdata, ICM_CONFIG2_REG_0_ADDR, icm2);
xgene_enet_rd_mcx_csr(pdata, RX_DV_GATE_REG_0_ADDR, &value);
value &= ~TX_DV_GATE_EN0;
value &= ~RX_DV_GATE_EN0;
......@@ -671,92 +624,154 @@ bool xgene_ring_mgr_init(struct xgene_enet_pdata *p)
static int xgene_enet_reset(struct xgene_enet_pdata *pdata)
{
u32 val;
struct device *dev = &pdata->pdev->dev;
if (!xgene_ring_mgr_init(pdata))
return -ENODEV;
if (!IS_ERR(pdata->clk)) {
if (pdata->mdio_driver) {
xgene_enet_config_ring_if_assoc(pdata);
return 0;
}
if (dev->of_node) {
clk_prepare_enable(pdata->clk);
udelay(5);
clk_disable_unprepare(pdata->clk);
udelay(5);
clk_prepare_enable(pdata->clk);
xgene_enet_ecc_init(pdata);
udelay(5);
} else {
#ifdef CONFIG_ACPI
if (acpi_has_method(ACPI_HANDLE(&pdata->pdev->dev), "_RST")) {
acpi_evaluate_object(ACPI_HANDLE(&pdata->pdev->dev),
"_RST", NULL, NULL);
} else if (acpi_has_method(ACPI_HANDLE(&pdata->pdev->dev),
"_INI")) {
acpi_evaluate_object(ACPI_HANDLE(&pdata->pdev->dev),
"_INI", NULL, NULL);
}
#endif
}
xgene_enet_config_ring_if_assoc(pdata);
/* Enable auto-incr for scanning */
xgene_enet_rd_mcx_mac(pdata, MII_MGMT_CONFIG_ADDR, &val);
val |= SCAN_AUTO_INCR;
MGMT_CLOCK_SEL_SET(&val, 1);
xgene_enet_wr_mcx_mac(pdata, MII_MGMT_CONFIG_ADDR, val);
xgene_enet_ecc_init(pdata);
xgene_enet_config_ring_if_assoc(pdata);
return 0;
}
static void xgene_gport_shutdown(struct xgene_enet_pdata *pdata)
static void xgene_enet_clear(struct xgene_enet_pdata *pdata,
struct xgene_enet_desc_ring *ring)
{
if (!IS_ERR(pdata->clk))
clk_disable_unprepare(pdata->clk);
}
u32 addr, val, data;
static int xgene_enet_mdio_read(struct mii_bus *bus, int mii_id, int regnum)
{
struct xgene_enet_pdata *pdata = bus->priv;
u32 val;
val = xgene_enet_ring_bufnum(ring->id);
val = xgene_mii_phy_read(pdata, mii_id, regnum);
netdev_dbg(pdata->ndev, "mdio_rd: bus=%d reg=%d val=%x\n",
mii_id, regnum, val);
if (xgene_enet_is_bufpool(ring->id)) {
addr = ENET_CFGSSQMIFPRESET_ADDR;
data = BIT(val - 0x20);
} else {
addr = ENET_CFGSSQMIWQRESET_ADDR;
data = BIT(val);
}
return val;
xgene_enet_wr_ring_if(pdata, addr, data);
}
static int xgene_enet_mdio_write(struct mii_bus *bus, int mii_id, int regnum,
u16 val)
static void xgene_gport_shutdown(struct xgene_enet_pdata *pdata)
{
struct xgene_enet_pdata *pdata = bus->priv;
struct device *dev = &pdata->pdev->dev;
struct xgene_enet_desc_ring *ring;
u32 pb, val;
int i;
pb = 0;
for (i = 0; i < pdata->rxq_cnt; i++) {
ring = pdata->rx_ring[i]->buf_pool;
val = xgene_enet_ring_bufnum(ring->id);
pb |= BIT(val - 0x20);
}
xgene_enet_wr_ring_if(pdata, ENET_CFGSSQMIFPRESET_ADDR, pb);
pb = 0;
for (i = 0; i < pdata->txq_cnt; i++) {
ring = pdata->tx_ring[i];
val = xgene_enet_ring_bufnum(ring->id);
pb |= BIT(val);
}
xgene_enet_wr_ring_if(pdata, ENET_CFGSSQMIWQRESET_ADDR, pb);
netdev_dbg(pdata->ndev, "mdio_wr: bus=%d reg=%d val=%x\n",
mii_id, regnum, val);
return xgene_mii_phy_write(pdata, mii_id, regnum, val);
if (dev->of_node) {
if (!IS_ERR(pdata->clk))
clk_disable_unprepare(pdata->clk);
}
}
static void xgene_enet_adjust_link(struct net_device *ndev)
{
struct xgene_enet_pdata *pdata = netdev_priv(ndev);
const struct xgene_mac_ops *mac_ops = pdata->mac_ops;
struct phy_device *phydev = pdata->phy_dev;
if (phydev->link) {
if (pdata->phy_speed != phydev->speed) {
pdata->phy_speed = phydev->speed;
xgene_gmac_init(pdata);
xgene_gmac_rx_enable(pdata);
xgene_gmac_tx_enable(pdata);
mac_ops->set_speed(pdata);
mac_ops->rx_enable(pdata);
mac_ops->tx_enable(pdata);
phy_print_status(phydev);
}
} else {
xgene_gmac_rx_disable(pdata);
xgene_gmac_tx_disable(pdata);
mac_ops->rx_disable(pdata);
mac_ops->tx_disable(pdata);
pdata->phy_speed = SPEED_UNKNOWN;
phy_print_status(phydev);
}
}
static int xgene_enet_phy_connect(struct net_device *ndev)
#ifdef CONFIG_ACPI
static struct acpi_device *acpi_phy_find_device(struct device *dev)
{
struct acpi_reference_args args;
struct fwnode_handle *fw_node;
int status;
fw_node = acpi_fwnode_handle(ACPI_COMPANION(dev));
status = acpi_node_get_property_reference(fw_node, "phy-handle", 0,
&args);
if (ACPI_FAILURE(status)) {
dev_dbg(dev, "No matching phy in ACPI table\n");
return NULL;
}
return args.adev;
}
#endif
int xgene_enet_phy_connect(struct net_device *ndev)
{
struct xgene_enet_pdata *pdata = netdev_priv(ndev);
struct device_node *phy_np;
struct device_node *np;
struct phy_device *phy_dev;
struct device *dev = &pdata->pdev->dev;
struct acpi_device *adev;
int i;
if (dev->of_node) {
phy_np = of_parse_phandle(dev->of_node, "phy-handle", 0);
if (!phy_np) {
for (i = 0 ; i < 2; i++) {
np = of_parse_phandle(dev->of_node, "phy-handle", i);
if (np)
break;
}
if (!np) {
netdev_dbg(ndev, "No phy-handle found in DT\n");
return -ENODEV;
}
phy_dev = of_phy_connect(ndev, phy_np, &xgene_enet_adjust_link,
phy_dev = of_phy_connect(ndev, np, &xgene_enet_adjust_link,
0, pdata->phy_mode);
if (!phy_dev) {
netdev_err(ndev, "Could not connect to PHY\n");
......@@ -765,6 +780,11 @@ static int xgene_enet_phy_connect(struct net_device *ndev)
pdata->phy_dev = phy_dev;
} else {
#ifdef CONFIG_ACPI
adev = acpi_phy_find_device(dev);
if (adev)
pdata->phy_dev = adev->driver_data;
phy_dev = pdata->phy_dev;
if (!phy_dev ||
......@@ -773,6 +793,7 @@ static int xgene_enet_phy_connect(struct net_device *ndev)
netdev_err(ndev, "Could not connect to PHY\n");
return -ENODEV;
}
#endif
}
pdata->phy_speed = SPEED_UNKNOWN;
......@@ -792,8 +813,8 @@ static int xgene_mdiobus_register(struct xgene_enet_pdata *pdata,
struct phy_device *phy;
struct device_node *child_np;
struct device_node *mdio_np = NULL;
u32 phy_addr;
int ret;
u32 phy_id;
if (dev->of_node) {
for_each_child_of_node(dev->of_node, child_np) {
......@@ -820,21 +841,17 @@ static int xgene_mdiobus_register(struct xgene_enet_pdata *pdata,
if (ret)
return ret;
ret = device_property_read_u32(dev, "phy-channel", &phy_id);
ret = device_property_read_u32(dev, "phy-channel", &phy_addr);
if (ret)
ret = device_property_read_u32(dev, "phy-addr", &phy_id);
ret = device_property_read_u32(dev, "phy-addr", &phy_addr);
if (ret)
return -EINVAL;
phy = get_phy_device(mdio, phy_id, false);
if (IS_ERR(phy))
phy = xgene_enet_phy_register(mdio, phy_addr);
if (!phy)
return -EIO;
ret = phy_device_register(phy);
if (ret)
phy_device_free(phy);
else
pdata->phy_dev = phy;
pdata->phy_dev = phy;
return ret;
}
......@@ -850,13 +867,13 @@ int xgene_enet_mdio_config(struct xgene_enet_pdata *pdata)
return -ENOMEM;
mdio_bus->name = "APM X-Gene MDIO bus";
mdio_bus->read = xgene_enet_mdio_read;
mdio_bus->write = xgene_enet_mdio_write;
mdio_bus->read = xgene_mdio_rgmii_read;
mdio_bus->write = xgene_mdio_rgmii_write;
snprintf(mdio_bus->id, MII_BUS_ID_SIZE, "%s-%s", "xgene-mii",
ndev->name);
mdio_bus->priv = pdata;
mdio_bus->parent = &ndev->dev;
mdio_bus->priv = (void __force *)pdata->mcx_mac_addr;
mdio_bus->parent = &pdata->pdev->dev;
ret = xgene_mdiobus_register(pdata, mdio_bus);
if (ret) {
......@@ -873,6 +890,12 @@ int xgene_enet_mdio_config(struct xgene_enet_pdata *pdata)
return ret;
}
void xgene_enet_phy_disconnect(struct xgene_enet_pdata *pdata)
{
if (pdata->phy_dev)
phy_disconnect(pdata->phy_dev);
}
void xgene_enet_mdio_remove(struct xgene_enet_pdata *pdata)
{
if (pdata->phy_dev)
......@@ -890,11 +913,13 @@ const struct xgene_mac_ops xgene_gmac_ops = {
.tx_enable = xgene_gmac_tx_enable,
.rx_disable = xgene_gmac_rx_disable,
.tx_disable = xgene_gmac_tx_disable,
.set_speed = xgene_gmac_set_speed,
.set_mac_addr = xgene_gmac_set_mac_addr,
};
const struct xgene_port_ops xgene_gport_ops = {
.reset = xgene_enet_reset,
.clear = xgene_enet_clear,
.cle_bypass = xgene_enet_cle_bypass,
.shutdown = xgene_gport_shutdown,
};
......
......@@ -104,6 +104,8 @@ enum xgene_enet_rm {
#define RECOMBBUF BIT(27)
#define MAC_OFFSET 0x30
#define OFFSET_4 0x04
#define OFFSET_8 0x08
#define BLOCK_ETH_CSR_OFFSET 0x2000
#define BLOCK_ETH_CLE_CSR_OFFSET 0x6000
......@@ -165,6 +167,8 @@ enum xgene_enet_rm {
#define TX_DV_GATE_EN0 BIT(2)
#define RX_DV_GATE_EN0 BIT(1)
#define RESUME_RX0 BIT(0)
#define ENET_CFGSSQMIFPRESET_ADDR 0x14
#define ENET_CFGSSQMIWQRESET_ADDR 0x1c
#define ENET_CFGSSQMIWQASSOC_ADDR 0xe0
#define ENET_CFGSSQMIFPQASSOC_ADDR 0xdc
#define ENET_CFGSSQMIQMLITEFPQASSOC_ADDR 0xf0
......@@ -297,11 +301,6 @@ enum xgene_enet_ring_bufnum {
RING_BUFNUM_INVALID
};
enum xgene_enet_cmd {
XGENE_ENET_WR_CMD = BIT(31),
XGENE_ENET_RD_CMD = BIT(30)
};
enum xgene_enet_err_code {
HBF_READ_DATA = 3,
HBF_LL_READ = 4,
......@@ -347,6 +346,8 @@ void xgene_enet_parse_error(struct xgene_enet_desc_ring *ring,
int xgene_enet_mdio_config(struct xgene_enet_pdata *pdata);
void xgene_enet_mdio_remove(struct xgene_enet_pdata *pdata);
bool xgene_ring_mgr_init(struct xgene_enet_pdata *p);
int xgene_enet_phy_connect(struct net_device *ndev);
void xgene_enet_phy_disconnect(struct xgene_enet_pdata *pdata);
extern const struct xgene_mac_ops xgene_gmac_ops;
extern const struct xgene_port_ops xgene_gport_ops;
......
......@@ -102,25 +102,13 @@ static u8 xgene_enet_hdr_len(const void *data)
static void xgene_enet_delete_bufpool(struct xgene_enet_desc_ring *buf_pool)
{
struct xgene_enet_pdata *pdata = netdev_priv(buf_pool->ndev);
struct xgene_enet_raw_desc16 *raw_desc;
u32 slots = buf_pool->slots - 1;
u32 tail = buf_pool->tail;
u32 userinfo;
int i, len;
len = pdata->ring_ops->len(buf_pool);
for (i = 0; i < len; i++) {
tail = (tail - 1) & slots;
raw_desc = &buf_pool->raw_desc16[tail];
int i;
/* Hardware stores descriptor in little endian format */
userinfo = GET_VAL(USERINFO, le64_to_cpu(raw_desc->m0));
dev_kfree_skb_any(buf_pool->rx_skb[userinfo]);
/* Free up the buffers held by hardware */
for (i = 0; i < buf_pool->slots; i++) {
if (buf_pool->rx_skb[i])
dev_kfree_skb_any(buf_pool->rx_skb[i]);
}
pdata->ring_ops->wr_cmd(buf_pool, -len);
buf_pool->tail = tail;
}
static irqreturn_t xgene_enet_rx_irq(const int irq, void *data)
......@@ -481,6 +469,7 @@ static int xgene_enet_rx_frame(struct xgene_enet_desc_ring *rx_ring,
XGENE_ENET_MAX_MTU, DMA_FROM_DEVICE);
skb_index = GET_VAL(USERINFO, le64_to_cpu(raw_desc->m0));
skb = buf_pool->rx_skb[skb_index];
buf_pool->rx_skb[skb_index] = NULL;
/* checking for error */
status = (GET_VAL(ELERR, le64_to_cpu(raw_desc->m0)) << LERR_LEN) ||
......@@ -619,6 +608,30 @@ static void xgene_enet_timeout(struct net_device *ndev)
}
}
static void xgene_enet_set_irq_name(struct net_device *ndev)
{
struct xgene_enet_pdata *pdata = netdev_priv(ndev);
struct xgene_enet_desc_ring *ring;
int i;
for (i = 0; i < pdata->rxq_cnt; i++) {
ring = pdata->rx_ring[i];
if (!pdata->cq_cnt) {
snprintf(ring->irq_name, IRQ_ID_SIZE, "%s-rx-txc",
ndev->name);
} else {
snprintf(ring->irq_name, IRQ_ID_SIZE, "%s-rx-%d",
ndev->name, i);
}
}
for (i = 0; i < pdata->cq_cnt; i++) {
ring = pdata->tx_ring[i]->cp_ring;
snprintf(ring->irq_name, IRQ_ID_SIZE, "%s-txc-%d",
ndev->name, i);
}
}
static int xgene_enet_register_irq(struct net_device *ndev)
{
struct xgene_enet_pdata *pdata = netdev_priv(ndev);
......@@ -626,6 +639,7 @@ static int xgene_enet_register_irq(struct net_device *ndev)
struct xgene_enet_desc_ring *ring;
int ret = 0, i;
xgene_enet_set_irq_name(ndev);
for (i = 0; i < pdata->rxq_cnt; i++) {
ring = pdata->rx_ring[i];
irq_set_status_flags(ring->irq, IRQ_DISABLE_UNLAZY);
......@@ -720,20 +734,21 @@ static int xgene_enet_open(struct net_device *ndev)
if (ret)
return ret;
mac_ops->tx_enable(pdata);
mac_ops->rx_enable(pdata);
xgene_enet_napi_enable(pdata);
ret = xgene_enet_register_irq(ndev);
if (ret)
return ret;
if (pdata->phy_mode == PHY_INTERFACE_MODE_RGMII)
if (pdata->phy_dev) {
phy_start(pdata->phy_dev);
else
} else {
schedule_delayed_work(&pdata->link_work, PHY_POLL_LINK_OFF);
netif_carrier_off(ndev);
}
netif_start_queue(ndev);
mac_ops->tx_enable(pdata);
mac_ops->rx_enable(pdata);
netif_tx_start_all_queues(ndev);
return ret;
}
......@@ -744,16 +759,15 @@ static int xgene_enet_close(struct net_device *ndev)
const struct xgene_mac_ops *mac_ops = pdata->mac_ops;
int i;
netif_stop_queue(ndev);
netif_tx_stop_all_queues(ndev);
mac_ops->tx_disable(pdata);
mac_ops->rx_disable(pdata);
if (pdata->phy_mode == PHY_INTERFACE_MODE_RGMII)
if (pdata->phy_dev)
phy_stop(pdata->phy_dev);
else
cancel_delayed_work_sync(&pdata->link_work);
mac_ops->tx_disable(pdata);
mac_ops->rx_disable(pdata);
xgene_enet_free_irq(ndev);
xgene_enet_napi_disable(pdata);
for (i = 0; i < pdata->rxq_cnt; i++)
......@@ -761,7 +775,6 @@ static int xgene_enet_close(struct net_device *ndev)
return 0;
}
static void xgene_enet_delete_ring(struct xgene_enet_desc_ring *ring)
{
struct xgene_enet_pdata *pdata;
......@@ -771,7 +784,7 @@ static void xgene_enet_delete_ring(struct xgene_enet_desc_ring *ring)
dev = ndev_to_dev(ring->ndev);
pdata->ring_ops->clear(ring);
dma_free_coherent(dev, ring->size, ring->desc_addr, ring->dma);
dmam_free_coherent(dev, ring->size, ring->desc_addr, ring->dma);
}
static void xgene_enet_delete_desc_rings(struct xgene_enet_pdata *pdata)
......@@ -784,6 +797,9 @@ static void xgene_enet_delete_desc_rings(struct xgene_enet_pdata *pdata)
ring = pdata->tx_ring[i];
if (ring) {
xgene_enet_delete_ring(ring);
pdata->port_ops->clear(pdata, ring);
if (pdata->cq_cnt)
xgene_enet_delete_ring(ring->cp_ring);
pdata->tx_ring[i] = NULL;
}
}
......@@ -794,6 +810,7 @@ static void xgene_enet_delete_desc_rings(struct xgene_enet_pdata *pdata)
buf_pool = ring->buf_pool;
xgene_enet_delete_bufpool(buf_pool);
xgene_enet_delete_ring(buf_pool);
pdata->port_ops->clear(pdata, buf_pool);
xgene_enet_delete_ring(ring);
pdata->rx_ring[i] = NULL;
}
......@@ -842,7 +859,7 @@ static void xgene_enet_free_desc_ring(struct xgene_enet_desc_ring *ring)
if (ring->desc_addr) {
pdata->ring_ops->clear(ring);
dma_free_coherent(dev, ring->size, ring->desc_addr, ring->dma);
dmam_free_coherent(dev, ring->size, ring->desc_addr, ring->dma);
}
devm_kfree(dev, ring);
}
......@@ -900,9 +917,10 @@ static struct xgene_enet_desc_ring *xgene_enet_create_desc_ring(
struct net_device *ndev, u32 ring_num,
enum xgene_enet_ring_cfgsize cfgsize, u32 ring_id)
{
struct xgene_enet_desc_ring *ring;
struct xgene_enet_pdata *pdata = netdev_priv(ndev);
struct device *dev = ndev_to_dev(ndev);
struct xgene_enet_desc_ring *ring;
void *irq_mbox_addr;
int size;
size = xgene_enet_get_ring_size(dev, cfgsize);
......@@ -919,8 +937,8 @@ static struct xgene_enet_desc_ring *xgene_enet_create_desc_ring(
ring->cfgsize = cfgsize;
ring->id = ring_id;
ring->desc_addr = dma_zalloc_coherent(dev, size, &ring->dma,
GFP_KERNEL);
ring->desc_addr = dmam_alloc_coherent(dev, size, &ring->dma,
GFP_KERNEL | __GFP_ZERO);
if (!ring->desc_addr) {
devm_kfree(dev, ring);
return NULL;
......@@ -928,14 +946,16 @@ static struct xgene_enet_desc_ring *xgene_enet_create_desc_ring(
ring->size = size;
if (is_irq_mbox_required(pdata, ring)) {
ring->irq_mbox_addr = dma_zalloc_coherent(dev, INTR_MBOX_SIZE,
&ring->irq_mbox_dma, GFP_KERNEL);
if (!ring->irq_mbox_addr) {
dma_free_coherent(dev, size, ring->desc_addr,
ring->dma);
irq_mbox_addr = dmam_alloc_coherent(dev, INTR_MBOX_SIZE,
&ring->irq_mbox_dma,
GFP_KERNEL | __GFP_ZERO);
if (!irq_mbox_addr) {
dmam_free_coherent(dev, size, ring->desc_addr,
ring->dma);
devm_kfree(dev, ring);
return NULL;
}
ring->irq_mbox_addr = irq_mbox_addr;
}
ring->cmd_base = xgene_enet_ring_cmd_base(pdata, ring);
......@@ -996,6 +1016,7 @@ static int xgene_enet_create_desc_rings(struct net_device *ndev)
u8 eth_bufnum = pdata->eth_bufnum;
u8 bp_bufnum = pdata->bp_bufnum;
u16 ring_num = pdata->ring_num;
__le64 *exp_bufs;
u16 ring_id;
int i, ret, size;
......@@ -1027,13 +1048,6 @@ static int xgene_enet_create_desc_rings(struct net_device *ndev)
rx_ring->nbufpool = NUM_BUFPOOL;
rx_ring->buf_pool = buf_pool;
rx_ring->irq = pdata->irqs[i];
if (!pdata->cq_cnt) {
snprintf(rx_ring->irq_name, IRQ_ID_SIZE, "%s-rx-txc",
ndev->name);
} else {
snprintf(rx_ring->irq_name, IRQ_ID_SIZE, "%s-rx%d",
ndev->name, i);
}
buf_pool->rx_skb = devm_kcalloc(dev, buf_pool->slots,
sizeof(struct sk_buff *),
GFP_KERNEL);
......@@ -1060,13 +1074,13 @@ static int xgene_enet_create_desc_rings(struct net_device *ndev)
}
size = (tx_ring->slots / 2) * sizeof(__le64) * MAX_EXP_BUFFS;
tx_ring->exp_bufs = dma_zalloc_coherent(dev, size,
&dma_exp_bufs,
GFP_KERNEL);
if (!tx_ring->exp_bufs) {
exp_bufs = dmam_alloc_coherent(dev, size, &dma_exp_bufs,
GFP_KERNEL | __GFP_ZERO);
if (!exp_bufs) {
ret = -ENOMEM;
goto err;
}
tx_ring->exp_bufs = exp_bufs;
pdata->tx_ring[i] = tx_ring;
......@@ -1086,8 +1100,6 @@ static int xgene_enet_create_desc_rings(struct net_device *ndev)
cp_ring->irq = pdata->irqs[pdata->rxq_cnt + i];
cp_ring->index = i;
snprintf(cp_ring->irq_name, IRQ_ID_SIZE, "%s-txc%d",
ndev->name, i);
}
cp_ring->cp_skb = devm_kcalloc(dev, tx_ring->slots,
......@@ -1283,6 +1295,23 @@ static int xgene_enet_get_irqs(struct xgene_enet_pdata *pdata)
return 0;
}
static int xgene_enet_check_phy_handle(struct xgene_enet_pdata *pdata)
{
int ret;
if (pdata->phy_mode == PHY_INTERFACE_MODE_XGMII)
return 0;
if (!IS_ENABLED(CONFIG_MDIO_XGENE))
return 0;
ret = xgene_enet_phy_connect(pdata->ndev);
if (!ret)
pdata->mdio_driver = true;
return 0;
}
static int xgene_enet_get_resources(struct xgene_enet_pdata *pdata)
{
struct platform_device *pdev;
......@@ -1368,6 +1397,10 @@ static int xgene_enet_get_resources(struct xgene_enet_pdata *pdata)
if (ret)
return ret;
ret = xgene_enet_check_phy_handle(pdata);
if (ret)
return ret;
pdata->clk = devm_clk_get(&pdev->dev, NULL);
if (IS_ERR(pdata->clk)) {
/* Firmware may have set up the clock already. */
......@@ -1447,6 +1480,7 @@ static int xgene_enet_init_hw(struct xgene_enet_pdata *pdata)
pdata->port_ops->cle_bypass(pdata, dst_ring_num, buf_pool->id);
}
pdata->phy_speed = SPEED_UNKNOWN;
pdata->mac_ops->init(pdata);
return ret;
......@@ -1556,28 +1590,12 @@ static void xgene_enet_napi_add(struct xgene_enet_pdata *pdata)
}
}
static void xgene_enet_napi_del(struct xgene_enet_pdata *pdata)
{
struct napi_struct *napi;
int i;
for (i = 0; i < pdata->rxq_cnt; i++) {
napi = &pdata->rx_ring[i]->napi;
netif_napi_del(napi);
}
for (i = 0; i < pdata->cq_cnt; i++) {
napi = &pdata->tx_ring[i]->cp_ring->napi;
netif_napi_del(napi);
}
}
static int xgene_enet_probe(struct platform_device *pdev)
{
struct net_device *ndev;
struct xgene_enet_pdata *pdata;
struct device *dev = &pdev->dev;
const struct xgene_mac_ops *mac_ops;
void (*link_state)(struct work_struct *);
const struct of_device_id *of_id;
int ret;
......@@ -1635,27 +1653,31 @@ static int xgene_enet_probe(struct platform_device *pdev)
goto err;
}
ret = register_netdev(ndev);
if (ret) {
netdev_err(ndev, "Failed to register netdev\n");
goto err;
}
ret = xgene_enet_init_hw(pdata);
if (ret)
goto err_netdev;
mac_ops = pdata->mac_ops;
if (pdata->phy_mode == PHY_INTERFACE_MODE_RGMII) {
ret = xgene_enet_mdio_config(pdata);
if (ret)
goto err_netdev;
} else {
INIT_DELAYED_WORK(&pdata->link_work, mac_ops->link_state);
link_state = pdata->mac_ops->link_state;
if (pdata->phy_mode == PHY_INTERFACE_MODE_XGMII) {
INIT_DELAYED_WORK(&pdata->link_work, link_state);
} else if (!pdata->mdio_driver) {
if (pdata->phy_mode == PHY_INTERFACE_MODE_RGMII)
ret = xgene_enet_mdio_config(pdata);
else
INIT_DELAYED_WORK(&pdata->link_work, link_state);
}
if (ret)
goto err;
xgene_enet_napi_add(pdata);
ret = register_netdev(ndev);
if (ret) {
netdev_err(ndev, "Failed to register netdev\n");
goto err;
}
return 0;
err_netdev:
unregister_netdev(ndev);
err:
......@@ -1673,20 +1695,38 @@ static int xgene_enet_remove(struct platform_device *pdev)
mac_ops = pdata->mac_ops;
ndev = pdata->ndev;
mac_ops->rx_disable(pdata);
mac_ops->tx_disable(pdata);
rtnl_lock();
if (netif_running(ndev))
dev_close(ndev);
rtnl_unlock();
xgene_enet_napi_del(pdata);
if (pdata->phy_mode == PHY_INTERFACE_MODE_RGMII)
if (pdata->mdio_driver)
xgene_enet_phy_disconnect(pdata);
else if (pdata->phy_mode == PHY_INTERFACE_MODE_RGMII)
xgene_enet_mdio_remove(pdata);
unregister_netdev(ndev);
xgene_enet_delete_desc_rings(pdata);
pdata->port_ops->shutdown(pdata);
xgene_enet_delete_desc_rings(pdata);
free_netdev(ndev);
return 0;
}
static void xgene_enet_shutdown(struct platform_device *pdev)
{
struct xgene_enet_pdata *pdata;
pdata = platform_get_drvdata(pdev);
if (!pdata)
return;
if (!pdata->ndev)
return;
xgene_enet_remove(pdev);
}
#ifdef CONFIG_ACPI
static const struct acpi_device_id xgene_enet_acpi_match[] = {
{ "APMC0D05", XGENE_ENET1},
......@@ -1721,6 +1761,7 @@ static struct platform_driver xgene_enet_driver = {
},
.probe = xgene_enet_probe,
.remove = xgene_enet_remove,
.shutdown = xgene_enet_shutdown,
};
module_platform_driver(xgene_enet_driver);
......
......@@ -38,6 +38,7 @@
#include "xgene_enet_hw.h"
#include "xgene_enet_cle.h"
#include "xgene_enet_ring2.h"
#include "../../../phy/mdio-xgene.h"
#define XGENE_DRV_VERSION "v1.0"
#define XGENE_ENET_MAX_MTU 1536
......@@ -140,6 +141,7 @@ struct xgene_mac_ops {
void (*rx_enable)(struct xgene_enet_pdata *pdata);
void (*tx_disable)(struct xgene_enet_pdata *pdata);
void (*rx_disable)(struct xgene_enet_pdata *pdata);
void (*set_speed)(struct xgene_enet_pdata *pdata);
void (*set_mac_addr)(struct xgene_enet_pdata *pdata);
void (*set_mss)(struct xgene_enet_pdata *pdata);
void (*link_state)(struct work_struct *work);
......@@ -147,6 +149,8 @@ struct xgene_mac_ops {
struct xgene_port_ops {
int (*reset)(struct xgene_enet_pdata *pdata);
void (*clear)(struct xgene_enet_pdata *pdata,
struct xgene_enet_desc_ring *ring);
void (*cle_bypass)(struct xgene_enet_pdata *pdata,
u32 dst_ring_num, u16 bufpool_id);
void (*shutdown)(struct xgene_enet_pdata *pdata);
......@@ -211,6 +215,7 @@ struct xgene_enet_pdata {
u32 mss;
u8 tx_delay;
u8 rx_delay;
bool mdio_driver;
};
struct xgene_indirect_ctl {
......@@ -220,34 +225,6 @@ struct xgene_indirect_ctl {
void __iomem *cmd_done;
};
/* Set the specified value into a bit-field defined by its starting position
* and length within a single u64.
*/
static inline u64 xgene_enet_set_field_value(int pos, int len, u64 val)
{
return (val & ((1ULL << len) - 1)) << pos;
}
#define SET_VAL(field, val) \
xgene_enet_set_field_value(field ## _POS, field ## _LEN, val)
#define SET_BIT(field) \
xgene_enet_set_field_value(field ## _POS, 1, 1)
/* Get the value from a bit-field defined by its starting position
* and length within the specified u64.
*/
static inline u64 xgene_enet_get_field_value(int pos, int len, u64 src)
{
return (src >> pos) & ((1ULL << len) - 1);
}
#define GET_VAL(field, src) \
xgene_enet_get_field_value(field ## _POS, field ## _LEN, src)
#define GET_BIT(field, src) \
xgene_enet_get_field_value(field ## _POS, 1, src)
static inline struct device *ndev_to_dev(struct net_device *ndev)
{
return ndev->dev.parent;
......
......@@ -28,6 +28,12 @@ static void xgene_enet_wr_csr(struct xgene_enet_pdata *p, u32 offset, u32 val)
iowrite32(val, p->eth_csr_addr + offset);
}
static void xgene_enet_wr_clkrst_csr(struct xgene_enet_pdata *p, u32 offset,
u32 val)
{
iowrite32(val, p->base_addr + offset);
}
static void xgene_enet_wr_ring_if(struct xgene_enet_pdata *p,
u32 offset, u32 val)
{
......@@ -93,6 +99,11 @@ static u32 xgene_enet_rd_diag_csr(struct xgene_enet_pdata *p, u32 offset)
return ioread32(p->eth_diag_csr_addr + offset);
}
static u32 xgene_enet_rd_mcx_csr(struct xgene_enet_pdata *p, u32 offset)
{
return ioread32(p->mcx_mac_csr_addr + offset);
}
static u32 xgene_enet_rd_indirect(struct xgene_indirect_ctl *ctl, u32 rd_addr)
{
u32 rd_data;
......@@ -132,9 +143,17 @@ static u32 xgene_enet_rd_mac(struct xgene_enet_pdata *p, u32 rd_addr)
static int xgene_enet_ecc_init(struct xgene_enet_pdata *p)
{
struct net_device *ndev = p->ndev;
u32 data;
u32 data, shutdown;
int i = 0;
shutdown = xgene_enet_rd_diag_csr(p, ENET_CFG_MEM_RAM_SHUTDOWN_ADDR);
data = xgene_enet_rd_diag_csr(p, ENET_BLOCK_MEM_RDY_ADDR);
if (!shutdown && data == ~0U) {
netdev_dbg(ndev, "+ ecc_init done, skipping\n");
return 0;
}
xgene_enet_wr_diag_csr(p, ENET_CFG_MEM_RAM_SHUTDOWN_ADDR, 0);
do {
usleep_range(100, 110);
......@@ -230,21 +249,105 @@ static u32 xgene_enet_link_status(struct xgene_enet_pdata *p)
data = xgene_mii_phy_read(p, INT_PHY_ADDR,
SGMII_BASE_PAGE_ABILITY_ADDR >> 2);
if (LINK_SPEED(data) == PHY_SPEED_1000)
p->phy_speed = SPEED_1000;
else if (LINK_SPEED(data) == PHY_SPEED_100)
p->phy_speed = SPEED_100;
else
p->phy_speed = SPEED_10;
return data & LINK_UP;
}
static void xgene_sgmac_init(struct xgene_enet_pdata *p)
static void xgene_sgmii_configure(struct xgene_enet_pdata *p)
{
u32 data, loop = 10;
u32 offset = p->port_id * 4;
u32 enet_spare_cfg_reg, rsif_config_reg;
u32 cfg_bypass_reg, rx_dv_gate_reg;
xgene_sgmac_reset(p);
xgene_mii_phy_write(p, INT_PHY_ADDR, SGMII_TBI_CONTROL_ADDR >> 2,
0x8000);
xgene_mii_phy_write(p, INT_PHY_ADDR, SGMII_CONTROL_ADDR >> 2, 0x9000);
xgene_mii_phy_write(p, INT_PHY_ADDR, SGMII_TBI_CONTROL_ADDR >> 2, 0);
}
/* Enable auto-negotiation */
xgene_mii_phy_write(p, INT_PHY_ADDR, SGMII_CONTROL_ADDR >> 2, 0x1000);
static void xgene_sgmii_tbi_control_reset(struct xgene_enet_pdata *p)
{
xgene_mii_phy_write(p, INT_PHY_ADDR, SGMII_TBI_CONTROL_ADDR >> 2,
0x8000);
xgene_mii_phy_write(p, INT_PHY_ADDR, SGMII_TBI_CONTROL_ADDR >> 2, 0);
}
static void xgene_sgmii_reset(struct xgene_enet_pdata *p)
{
u32 value;
if (p->phy_speed == SPEED_UNKNOWN)
return;
value = xgene_mii_phy_read(p, INT_PHY_ADDR,
SGMII_BASE_PAGE_ABILITY_ADDR >> 2);
if (!(value & LINK_UP))
xgene_sgmii_tbi_control_reset(p);
}
static void xgene_sgmac_set_speed(struct xgene_enet_pdata *p)
{
u32 icm0_addr, icm2_addr, debug_addr;
u32 icm0, icm2, intf_ctl;
u32 mc2, value;
xgene_sgmii_reset(p);
if (p->enet_id == XGENE_ENET1) {
icm0_addr = ICM_CONFIG0_REG_0_ADDR + p->port_id * OFFSET_8;
icm2_addr = ICM_CONFIG2_REG_0_ADDR + p->port_id * OFFSET_4;
debug_addr = DEBUG_REG_ADDR;
} else {
icm0_addr = XG_MCX_ICM_CONFIG0_REG_0_ADDR;
icm2_addr = XG_MCX_ICM_CONFIG2_REG_0_ADDR;
debug_addr = XG_DEBUG_REG_ADDR;
}
icm0 = xgene_enet_rd_mcx_csr(p, icm0_addr);
icm2 = xgene_enet_rd_mcx_csr(p, icm2_addr);
mc2 = xgene_enet_rd_mac(p, MAC_CONFIG_2_ADDR);
intf_ctl = xgene_enet_rd_mac(p, INTERFACE_CONTROL_ADDR);
switch (p->phy_speed) {
case SPEED_10:
ENET_INTERFACE_MODE2_SET(&mc2, 1);
intf_ctl &= ~(ENET_LHD_MODE | ENET_GHD_MODE);
CFG_MACMODE_SET(&icm0, 0);
CFG_WAITASYNCRD_SET(&icm2, 500);
break;
case SPEED_100:
ENET_INTERFACE_MODE2_SET(&mc2, 1);
intf_ctl &= ~ENET_GHD_MODE;
intf_ctl |= ENET_LHD_MODE;
CFG_MACMODE_SET(&icm0, 1);
CFG_WAITASYNCRD_SET(&icm2, 80);
break;
default:
ENET_INTERFACE_MODE2_SET(&mc2, 2);
intf_ctl &= ~ENET_LHD_MODE;
intf_ctl |= ENET_GHD_MODE;
CFG_MACMODE_SET(&icm0, 2);
CFG_WAITASYNCRD_SET(&icm2, 16);
value = xgene_enet_rd_csr(p, debug_addr);
value |= CFG_BYPASS_UNISEC_TX | CFG_BYPASS_UNISEC_RX;
xgene_enet_wr_csr(p, debug_addr, value);
break;
}
mc2 |= FULL_DUPLEX2 | PAD_CRC;
xgene_enet_wr_mac(p, MAC_CONFIG_2_ADDR, mc2);
xgene_enet_wr_mac(p, INTERFACE_CONTROL_ADDR, intf_ctl);
xgene_enet_wr_mcx_csr(p, icm0_addr, icm0);
xgene_enet_wr_mcx_csr(p, icm2_addr, icm2);
}
static void xgene_sgmii_enable_autoneg(struct xgene_enet_pdata *p)
{
u32 data, loop = 10;
xgene_sgmii_configure(p);
while (loop--) {
data = xgene_mii_phy_read(p, INT_PHY_ADDR,
......@@ -255,17 +358,27 @@ static void xgene_sgmac_init(struct xgene_enet_pdata *p)
}
if (!(data & AUTO_NEG_COMPLETE) || !(data & LINK_STATUS))
netdev_err(p->ndev, "Auto-negotiation failed\n");
}
static void xgene_sgmac_init(struct xgene_enet_pdata *p)
{
u32 enet_spare_cfg_reg, rsif_config_reg;
u32 cfg_bypass_reg, rx_dv_gate_reg;
u32 data, offset;
data = xgene_enet_rd_mac(p, MAC_CONFIG_2_ADDR);
ENET_INTERFACE_MODE2_SET(&data, 2);
xgene_enet_wr_mac(p, MAC_CONFIG_2_ADDR, data | FULL_DUPLEX2);
xgene_enet_wr_mac(p, INTERFACE_CONTROL_ADDR, ENET_GHD_MODE);
if (!(p->enet_id == XGENE_ENET2 && p->mdio_driver))
xgene_sgmac_reset(p);
xgene_sgmii_enable_autoneg(p);
xgene_sgmac_set_speed(p);
xgene_sgmac_set_mac_addr(p);
if (p->enet_id == XGENE_ENET1) {
enet_spare_cfg_reg = ENET_SPARE_CFG_REG_ADDR;
rsif_config_reg = RSIF_CONFIG_REG_ADDR;
cfg_bypass_reg = CFG_BYPASS_ADDR;
rx_dv_gate_reg = SG_RX_DV_GATE_REG_0_ADDR;
offset = p->port_id * OFFSET_4;
rx_dv_gate_reg = SG_RX_DV_GATE_REG_0_ADDR + offset;
} else {
enet_spare_cfg_reg = XG_ENET_SPARE_CFG_REG_ADDR;
rsif_config_reg = XG_RSIF_CONFIG_REG_ADDR;
......@@ -277,8 +390,6 @@ static void xgene_sgmac_init(struct xgene_enet_pdata *p)
data |= MPA_IDLE_WITH_QMI_EMPTY;
xgene_enet_wr_csr(p, enet_spare_cfg_reg, data);
xgene_sgmac_set_mac_addr(p);
/* Adjust MDC clock frequency */
data = xgene_enet_rd_mac(p, MII_MGMT_CONFIG_ADDR);
MGMT_CLOCK_SEL_SET(&data, 7);
......@@ -292,7 +403,7 @@ static void xgene_sgmac_init(struct xgene_enet_pdata *p)
/* Bypass traffic gating */
xgene_enet_wr_csr(p, XG_ENET_SPARE_CFG_REG_1_ADDR, 0x84);
xgene_enet_wr_csr(p, cfg_bypass_reg, RESUME_TX);
xgene_enet_wr_mcx_csr(p, rx_dv_gate_reg + offset, RESUME_RX0);
xgene_enet_wr_mcx_csr(p, rx_dv_gate_reg, RESUME_RX0);
}
static void xgene_sgmac_rxtx(struct xgene_enet_pdata *p, u32 bits, bool set)
......@@ -331,17 +442,43 @@ static void xgene_sgmac_tx_disable(struct xgene_enet_pdata *p)
static int xgene_enet_reset(struct xgene_enet_pdata *p)
{
struct device *dev = &p->pdev->dev;
if (!xgene_ring_mgr_init(p))
return -ENODEV;
if (!IS_ERR(p->clk)) {
clk_prepare_enable(p->clk);
clk_disable_unprepare(p->clk);
clk_prepare_enable(p->clk);
if (p->mdio_driver && p->enet_id == XGENE_ENET2) {
xgene_enet_config_ring_if_assoc(p);
return 0;
}
xgene_enet_ecc_init(p);
xgene_enet_config_ring_if_assoc(p);
if (p->enet_id == XGENE_ENET2)
xgene_enet_wr_clkrst_csr(p, XGENET_CONFIG_REG_ADDR, SGMII_EN);
if (dev->of_node) {
if (!IS_ERR(p->clk)) {
clk_prepare_enable(p->clk);
udelay(5);
clk_disable_unprepare(p->clk);
udelay(5);
clk_prepare_enable(p->clk);
udelay(5);
}
} else {
#ifdef CONFIG_ACPI
if (acpi_has_method(ACPI_HANDLE(&p->pdev->dev), "_RST"))
acpi_evaluate_object(ACPI_HANDLE(&p->pdev->dev),
"_RST", NULL, NULL);
else if (acpi_has_method(ACPI_HANDLE(&p->pdev->dev), "_INI"))
acpi_evaluate_object(ACPI_HANDLE(&p->pdev->dev),
"_INI", NULL, NULL);
#endif
}
if (!p->port_id) {
xgene_enet_ecc_init(p);
xgene_enet_config_ring_if_assoc(p);
}
return 0;
}
......@@ -369,10 +506,53 @@ static void xgene_enet_cle_bypass(struct xgene_enet_pdata *p,
xgene_enet_wr_csr(p, cle_bypass_reg1 + offset, data);
}
static void xgene_enet_clear(struct xgene_enet_pdata *pdata,
struct xgene_enet_desc_ring *ring)
{
u32 addr, val, data;
val = xgene_enet_ring_bufnum(ring->id);
if (xgene_enet_is_bufpool(ring->id)) {
addr = ENET_CFGSSQMIFPRESET_ADDR;
data = BIT(val - 0x20);
} else {
addr = ENET_CFGSSQMIWQRESET_ADDR;
data = BIT(val);
}
xgene_enet_wr_ring_if(pdata, addr, data);
}
static void xgene_enet_shutdown(struct xgene_enet_pdata *p)
{
if (!IS_ERR(p->clk))
clk_disable_unprepare(p->clk);
struct device *dev = &p->pdev->dev;
struct xgene_enet_desc_ring *ring;
u32 pb, val;
int i;
pb = 0;
for (i = 0; i < p->rxq_cnt; i++) {
ring = p->rx_ring[i]->buf_pool;
val = xgene_enet_ring_bufnum(ring->id);
pb |= BIT(val - 0x20);
}
xgene_enet_wr_ring_if(p, ENET_CFGSSQMIFPRESET_ADDR, pb);
pb = 0;
for (i = 0; i < p->txq_cnt; i++) {
ring = p->tx_ring[i];
val = xgene_enet_ring_bufnum(ring->id);
pb |= BIT(val);
}
xgene_enet_wr_ring_if(p, ENET_CFGSSQMIWQRESET_ADDR, pb);
if (dev->of_node) {
if (!IS_ERR(p->clk))
clk_disable_unprepare(p->clk);
}
}
static void xgene_enet_link_state(struct work_struct *work)
......@@ -386,10 +566,11 @@ static void xgene_enet_link_state(struct work_struct *work)
if (link) {
if (!netif_carrier_ok(ndev)) {
netif_carrier_on(ndev);
xgene_sgmac_init(p);
xgene_sgmac_set_speed(p);
xgene_sgmac_rx_enable(p);
xgene_sgmac_tx_enable(p);
netdev_info(ndev, "Link is Up - 1Gbps\n");
netdev_info(ndev, "Link is Up - %dMbps\n",
p->phy_speed);
}
poll_interval = PHY_POLL_LINK_ON;
} else {
......@@ -412,12 +593,14 @@ const struct xgene_mac_ops xgene_sgmac_ops = {
.tx_enable = xgene_sgmac_tx_enable,
.rx_disable = xgene_sgmac_rx_disable,
.tx_disable = xgene_sgmac_tx_disable,
.set_speed = xgene_sgmac_set_speed,
.set_mac_addr = xgene_sgmac_set_mac_addr,
.link_state = xgene_enet_link_state
};
const struct xgene_port_ops xgene_sgport_ops = {
.reset = xgene_enet_reset,
.clear = xgene_enet_clear,
.cle_bypass = xgene_enet_cle_bypass,
.shutdown = xgene_enet_shutdown
};
......@@ -24,6 +24,7 @@
#define PHY_ADDR(src) (((src)<<8) & GENMASK(12, 8))
#define REG_ADDR(src) ((src) & GENMASK(4, 0))
#define PHY_CONTROL(src) ((src) & GENMASK(15, 0))
#define LINK_SPEED(src) (((src) & GENMASK(11, 10)) >> 10)
#define INT_PHY_ADDR 0x1e
#define SGMII_TBI_CONTROL_ADDR 0x44
#define SGMII_CONTROL_ADDR 0x00
......@@ -34,6 +35,13 @@
#define LINK_UP BIT(15)
#define MPA_IDLE_WITH_QMI_EMPTY BIT(12)
#define SG_RX_DV_GATE_REG_0_ADDR 0x05fc
#define SGMII_EN 0x1
enum xgene_phy_speed {
PHY_SPEED_10,
PHY_SPEED_100,
PHY_SPEED_1000
};
extern const struct xgene_mac_ops xgene_sgmac_ops;
extern const struct xgene_port_ops xgene_sgport_ops;
......
......@@ -258,13 +258,29 @@ static void xgene_xgmac_tx_disable(struct xgene_enet_pdata *pdata)
static int xgene_enet_reset(struct xgene_enet_pdata *pdata)
{
struct device *dev = &pdata->pdev->dev;
if (!xgene_ring_mgr_init(pdata))
return -ENODEV;
if (!IS_ERR(pdata->clk)) {
if (dev->of_node) {
clk_prepare_enable(pdata->clk);
udelay(5);
clk_disable_unprepare(pdata->clk);
udelay(5);
clk_prepare_enable(pdata->clk);
udelay(5);
} else {
#ifdef CONFIG_ACPI
if (acpi_has_method(ACPI_HANDLE(&pdata->pdev->dev), "_RST")) {
acpi_evaluate_object(ACPI_HANDLE(&pdata->pdev->dev),
"_RST", NULL, NULL);
} else if (acpi_has_method(ACPI_HANDLE(&pdata->pdev->dev),
"_INI")) {
acpi_evaluate_object(ACPI_HANDLE(&pdata->pdev->dev),
"_INI", NULL, NULL);
}
#endif
}
xgene_enet_ecc_init(pdata);
......@@ -292,8 +308,51 @@ static void xgene_enet_xgcle_bypass(struct xgene_enet_pdata *pdata,
static void xgene_enet_shutdown(struct xgene_enet_pdata *pdata)
{
if (!IS_ERR(pdata->clk))
clk_disable_unprepare(pdata->clk);
struct device *dev = &pdata->pdev->dev;
struct xgene_enet_desc_ring *ring;
u32 pb, val;
int i;
pb = 0;
for (i = 0; i < pdata->rxq_cnt; i++) {
ring = pdata->rx_ring[i]->buf_pool;
val = xgene_enet_ring_bufnum(ring->id);
pb |= BIT(val - 0x20);
}
xgene_enet_wr_ring_if(pdata, ENET_CFGSSQMIFPRESET_ADDR, pb);
pb = 0;
for (i = 0; i < pdata->txq_cnt; i++) {
ring = pdata->tx_ring[i];
val = xgene_enet_ring_bufnum(ring->id);
pb |= BIT(val);
}
xgene_enet_wr_ring_if(pdata, ENET_CFGSSQMIWQRESET_ADDR, pb);
if (dev->of_node) {
if (!IS_ERR(pdata->clk))
clk_disable_unprepare(pdata->clk);
}
}
static void xgene_enet_clear(struct xgene_enet_pdata *pdata,
struct xgene_enet_desc_ring *ring)
{
u32 addr, val, data;
val = xgene_enet_ring_bufnum(ring->id);
if (xgene_enet_is_bufpool(ring->id)) {
addr = ENET_CFGSSQMIFPRESET_ADDR;
data = BIT(val - 0x20);
} else {
addr = ENET_CFGSSQMIWQRESET_ADDR;
data = BIT(val);
}
xgene_enet_wr_ring_if(pdata, addr, data);
}
static void xgene_enet_link_state(struct work_struct *work)
......@@ -340,6 +399,7 @@ const struct xgene_mac_ops xgene_xgmac_ops = {
const struct xgene_port_ops xgene_xgport_ops = {
.reset = xgene_enet_reset,
.clear = xgene_enet_clear,
.cle_bypass = xgene_enet_xgcle_bypass,
.shutdown = xgene_enet_shutdown,
};
......@@ -65,9 +65,12 @@
#define XG_CFG_LINK_AGGR_RESUME_0_ADDR 0x0214
#define XG_LINK_STATUS_ADDR 0x0228
#define XG_TSIF_MSS_REG0_ADDR 0x02a4
#define XG_DEBUG_REG_ADDR 0x0400
#define XG_ENET_SPARE_CFG_REG_ADDR 0x040c
#define XG_ENET_SPARE_CFG_REG_1_ADDR 0x0410
#define XGENET_RX_DV_GATE_REG_0_ADDR 0x0804
#define XG_MCX_ICM_CONFIG0_REG_0_ADDR 0x00e0
#define XG_MCX_ICM_CONFIG2_REG_0_ADDR 0x00e8
extern const struct xgene_mac_ops xgene_xgmac_ops;
extern const struct xgene_port_ops xgene_xgport_ops;
......
......@@ -301,6 +301,12 @@ config MDIO_HISI_FEMAC
This module provides a driver for the MDIO busses found in the
Hisilicon SoC that have an Fast Ethernet MAC.
config MDIO_XGENE
tristate "APM X-Gene SoC MDIO bus controller"
help
This module provides a driver for the MDIO busses found in the
APM X-Gene SoC's.
endif # PHYLIB
config MICREL_KS8995MA
......
......@@ -48,3 +48,4 @@ obj-$(CONFIG_MICROCHIP_PHY) += microchip.o
obj-$(CONFIG_MDIO_BCM_IPROC) += mdio-bcm-iproc.o
obj-$(CONFIG_INTEL_XWAY_PHY) += intel-xway.o
obj-$(CONFIG_MDIO_HISI_FEMAC) += mdio-hisi-femac.o
obj-$(CONFIG_MDIO_XGENE) += mdio-xgene.o
/* Applied Micro X-Gene SoC MDIO Driver
*
* Copyright (c) 2016, Applied Micro Circuits Corporation
* Author: Iyappan Subramanian <isubramanian@apm.com>
*
* 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, see <http://www.gnu.org/licenses/>.
*/
#include <linux/acpi.h>
#include <linux/clk.h>
#include <linux/device.h>
#include <linux/efi.h>
#include <linux/if_vlan.h>
#include <linux/io.h>
#include <linux/module.h>
#include <linux/of_platform.h>
#include <linux/of_net.h>
#include <linux/of_mdio.h>
#include <linux/prefetch.h>
#include <linux/phy.h>
#include <net/ip.h>
#include "mdio-xgene.h"
static bool xgene_mdio_status;
static u32 xgene_enet_rd_mac(void __iomem *base_addr, u32 rd_addr)
{
void __iomem *addr, *rd, *cmd, *cmd_done;
u32 done, rd_data = BUSY_MASK;
u8 wait = 10;
addr = base_addr + MAC_ADDR_REG_OFFSET;
rd = base_addr + MAC_READ_REG_OFFSET;
cmd = base_addr + MAC_COMMAND_REG_OFFSET;
cmd_done = base_addr + MAC_COMMAND_DONE_REG_OFFSET;
iowrite32(rd_addr, addr);
iowrite32(XGENE_ENET_RD_CMD, cmd);
while (wait--) {
done = ioread32(cmd_done);
if (done)
break;
udelay(1);
}
if (!done)
return rd_data;
rd_data = ioread32(rd);
iowrite32(0, cmd);
return rd_data;
}
static void xgene_enet_wr_mac(void __iomem *base_addr, u32 wr_addr, u32 wr_data)
{
void __iomem *addr, *wr, *cmd, *cmd_done;
u8 wait = 10;
u32 done;
addr = base_addr + MAC_ADDR_REG_OFFSET;
wr = base_addr + MAC_WRITE_REG_OFFSET;
cmd = base_addr + MAC_COMMAND_REG_OFFSET;
cmd_done = base_addr + MAC_COMMAND_DONE_REG_OFFSET;
iowrite32(wr_addr, addr);
iowrite32(wr_data, wr);
iowrite32(XGENE_ENET_WR_CMD, cmd);
while (wait--) {
done = ioread32(cmd_done);
if (done)
break;
udelay(1);
}
if (!done)
pr_err("MCX mac write failed, addr: 0x%04x\n", wr_addr);
iowrite32(0, cmd);
}
int xgene_mdio_rgmii_read(struct mii_bus *bus, int phy_id, int reg)
{
void __iomem *addr = (void __iomem *)bus->priv;
u32 data, done;
u8 wait = 10;
data = SET_VAL(PHY_ADDR, phy_id) | SET_VAL(REG_ADDR, reg);
xgene_enet_wr_mac(addr, MII_MGMT_ADDRESS_ADDR, data);
xgene_enet_wr_mac(addr, MII_MGMT_COMMAND_ADDR, READ_CYCLE_MASK);
do {
usleep_range(5, 10);
done = xgene_enet_rd_mac(addr, MII_MGMT_INDICATORS_ADDR);
} while ((done & BUSY_MASK) && wait--);
if (done & BUSY_MASK) {
dev_err(&bus->dev, "MII_MGMT read failed\n");
return -EBUSY;
}
data = xgene_enet_rd_mac(addr, MII_MGMT_STATUS_ADDR);
xgene_enet_wr_mac(addr, MII_MGMT_COMMAND_ADDR, 0);
return data;
}
EXPORT_SYMBOL(xgene_mdio_rgmii_read);
int xgene_mdio_rgmii_write(struct mii_bus *bus, int phy_id, int reg, u16 data)
{
void __iomem *addr = (void __iomem *)bus->priv;
u32 val, done;
u8 wait = 10;
val = SET_VAL(PHY_ADDR, phy_id) | SET_VAL(REG_ADDR, reg);
xgene_enet_wr_mac(addr, MII_MGMT_ADDRESS_ADDR, val);
xgene_enet_wr_mac(addr, MII_MGMT_CONTROL_ADDR, data);
do {
usleep_range(5, 10);
done = xgene_enet_rd_mac(addr, MII_MGMT_INDICATORS_ADDR);
} while ((done & BUSY_MASK) && wait--);
if (done & BUSY_MASK) {
dev_err(&bus->dev, "MII_MGMT write failed\n");
return -EBUSY;
}
return 0;
}
EXPORT_SYMBOL(xgene_mdio_rgmii_write);
static u32 xgene_menet_rd_diag_csr(struct xgene_mdio_pdata *pdata, u32 offset)
{
return ioread32(pdata->diag_csr_addr + offset);
}
static void xgene_menet_wr_diag_csr(struct xgene_mdio_pdata *pdata,
u32 offset, u32 val)
{
iowrite32(val, pdata->diag_csr_addr + offset);
}
static int xgene_enet_ecc_init(struct xgene_mdio_pdata *pdata)
{
u32 data;
u8 wait = 10;
xgene_menet_wr_diag_csr(pdata, MENET_CFG_MEM_RAM_SHUTDOWN_ADDR, 0x0);
do {
usleep_range(100, 110);
data = xgene_menet_rd_diag_csr(pdata, MENET_BLOCK_MEM_RDY_ADDR);
} while ((data != 0xffffffff) && wait--);
if (data != 0xffffffff) {
dev_err(pdata->dev, "Failed to release memory from shutdown\n");
return -ENODEV;
}
return 0;
}
static void xgene_gmac_reset(struct xgene_mdio_pdata *pdata)
{
xgene_enet_wr_mac(pdata->mac_csr_addr, MAC_CONFIG_1_ADDR, SOFT_RESET);
xgene_enet_wr_mac(pdata->mac_csr_addr, MAC_CONFIG_1_ADDR, 0);
}
static int xgene_mdio_reset(struct xgene_mdio_pdata *pdata)
{
int ret;
if (pdata->dev->of_node) {
clk_prepare_enable(pdata->clk);
udelay(5);
clk_disable_unprepare(pdata->clk);
udelay(5);
clk_prepare_enable(pdata->clk);
udelay(5);
} else {
#ifdef CONFIG_ACPI
acpi_evaluate_object(ACPI_HANDLE(pdata->dev),
"_RST", NULL, NULL);
#endif
}
ret = xgene_enet_ecc_init(pdata);
if (ret)
return ret;
xgene_gmac_reset(pdata);
return 0;
}
static void xgene_enet_rd_mdio_csr(void __iomem *base_addr,
u32 offset, u32 *val)
{
void __iomem *addr = base_addr + offset;
*val = ioread32(addr);
}
static void xgene_enet_wr_mdio_csr(void __iomem *base_addr,
u32 offset, u32 val)
{
void __iomem *addr = base_addr + offset;
iowrite32(val, addr);
}
static int xgene_xfi_mdio_write(struct mii_bus *bus, int phy_id,
int reg, u16 data)
{
void __iomem *addr = (void __iomem *)bus->priv;
int timeout = 100;
u32 status, val;
val = SET_VAL(HSTPHYADX, phy_id) | SET_VAL(HSTREGADX, reg) |
SET_VAL(HSTMIIMWRDAT, data);
xgene_enet_wr_mdio_csr(addr, MIIM_FIELD_ADDR, data);
val = HSTLDCMD | SET_VAL(HSTMIIMCMD, MIIM_CMD_LEGACY_WRITE);
xgene_enet_wr_mdio_csr(addr, MIIM_COMMAND_ADDR, val);
do {
usleep_range(5, 10);
xgene_enet_rd_mdio_csr(addr, MIIM_INDICATOR_ADDR, &status);
} while ((status & BUSY_MASK) && timeout--);
xgene_enet_wr_mdio_csr(addr, MIIM_COMMAND_ADDR, 0);
return 0;
}
static int xgene_xfi_mdio_read(struct mii_bus *bus, int phy_id, int reg)
{
void __iomem *addr = (void __iomem *)bus->priv;
u32 data, status, val;
int timeout = 100;
val = SET_VAL(HSTPHYADX, phy_id) | SET_VAL(HSTREGADX, reg);
xgene_enet_wr_mdio_csr(addr, MIIM_FIELD_ADDR, val);
val = HSTLDCMD | SET_VAL(HSTMIIMCMD, MIIM_CMD_LEGACY_READ);
xgene_enet_wr_mdio_csr(addr, MIIM_COMMAND_ADDR, val);
do {
usleep_range(5, 10);
xgene_enet_rd_mdio_csr(addr, MIIM_INDICATOR_ADDR, &status);
} while ((status & BUSY_MASK) && timeout--);
if (status & BUSY_MASK) {
pr_err("XGENET_MII_MGMT write failed\n");
return -EBUSY;
}
xgene_enet_rd_mdio_csr(addr, MIIMRD_FIELD_ADDR, &data);
xgene_enet_wr_mdio_csr(addr, MIIM_COMMAND_ADDR, 0);
return data;
}
struct phy_device *xgene_enet_phy_register(struct mii_bus *bus, int phy_addr)
{
struct phy_device *phy_dev;
phy_dev = get_phy_device(bus, phy_addr, false);
if (!phy_dev || IS_ERR(phy_dev))
return NULL;
if (phy_device_register(phy_dev))
phy_device_free(phy_dev);
return phy_dev;
}
EXPORT_SYMBOL(xgene_enet_phy_register);
#ifdef CONFIG_ACPI
static acpi_status acpi_register_phy(acpi_handle handle, u32 lvl,
void *context, void **ret)
{
struct mii_bus *mdio = context;
struct acpi_device *adev;
struct phy_device *phy_dev;
const union acpi_object *obj;
u32 phy_addr;
if (acpi_bus_get_device(handle, &adev))
return AE_OK;
if (acpi_dev_get_property(adev, "phy-channel", ACPI_TYPE_INTEGER, &obj))
return AE_OK;
phy_addr = obj->integer.value;
phy_dev = xgene_enet_phy_register(mdio, phy_addr);
adev->driver_data = phy_dev;
return AE_OK;
}
#endif
static int xgene_mdio_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct mii_bus *mdio_bus;
const struct of_device_id *of_id;
struct resource *res;
struct xgene_mdio_pdata *pdata;
void __iomem *csr_base;
int mdio_id = 0, ret = 0;
of_id = of_match_device(xgene_mdio_of_match, &pdev->dev);
if (of_id) {
mdio_id = (enum xgene_mdio_id)of_id->data;
} else {
#ifdef CONFIG_ACPI
const struct acpi_device_id *acpi_id;
acpi_id = acpi_match_device(xgene_mdio_acpi_match, &pdev->dev);
if (acpi_id)
mdio_id = (enum xgene_mdio_id)acpi_id->driver_data;
#endif
}
if (!mdio_id)
return -ENODEV;
pdata = devm_kzalloc(dev, sizeof(struct xgene_mdio_pdata), GFP_KERNEL);
if (!pdata)
return -ENOMEM;
pdata->mdio_id = mdio_id;
pdata->dev = dev;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
csr_base = devm_ioremap_resource(dev, res);
if (IS_ERR(csr_base)) {
dev_err(dev, "Unable to retrieve mac CSR region\n");
return PTR_ERR(csr_base);
}
pdata->mac_csr_addr = csr_base;
pdata->mdio_csr_addr = csr_base + BLOCK_XG_MDIO_CSR_OFFSET;
pdata->diag_csr_addr = csr_base + BLOCK_DIAG_CSR_OFFSET;
if (dev->of_node) {
pdata->clk = devm_clk_get(dev, NULL);
if (IS_ERR(pdata->clk)) {
dev_err(dev, "Unable to retrieve clk\n");
return PTR_ERR(pdata->clk);
}
}
ret = xgene_mdio_reset(pdata);
if (ret)
return ret;
mdio_bus = mdiobus_alloc();
if (!mdio_bus)
return -ENOMEM;
mdio_bus->name = "APM X-Gene MDIO bus";
if (mdio_id == XGENE_MDIO_RGMII) {
mdio_bus->read = xgene_mdio_rgmii_read;
mdio_bus->write = xgene_mdio_rgmii_write;
mdio_bus->priv = (void __force *)pdata->mac_csr_addr;
snprintf(mdio_bus->id, MII_BUS_ID_SIZE, "%s",
"xgene-mii-rgmii");
} else {
mdio_bus->read = xgene_xfi_mdio_read;
mdio_bus->write = xgene_xfi_mdio_write;
mdio_bus->priv = (void __force *)pdata->mdio_csr_addr;
snprintf(mdio_bus->id, MII_BUS_ID_SIZE, "%s",
"xgene-mii-xfi");
}
mdio_bus->parent = dev;
platform_set_drvdata(pdev, pdata);
if (dev->of_node) {
ret = of_mdiobus_register(mdio_bus, dev->of_node);
} else {
#ifdef CONFIG_ACPI
/* Mask out all PHYs from auto probing. */
mdio_bus->phy_mask = ~0;
ret = mdiobus_register(mdio_bus);
if (ret)
goto out;
acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_HANDLE(dev), 1,
acpi_register_phy, NULL, mdio_bus, NULL);
#endif
}
if (ret)
goto out;
pdata->mdio_bus = mdio_bus;
xgene_mdio_status = true;
return 0;
out:
mdiobus_free(mdio_bus);
return ret;
}
static int xgene_mdio_remove(struct platform_device *pdev)
{
struct xgene_mdio_pdata *pdata = platform_get_drvdata(pdev);
struct mii_bus *mdio_bus = pdata->mdio_bus;
struct device *dev = &pdev->dev;
mdiobus_unregister(mdio_bus);
mdiobus_free(mdio_bus);
if (dev->of_node) {
if (IS_ERR(pdata->clk))
clk_disable_unprepare(pdata->clk);
}
return 0;
}
#ifdef CONFIG_OF
static const struct of_device_id xgene_mdio_of_match[] = {
{
.compatible = "apm,xgene-mdio-rgmii",
.data = (void *)XGENE_MDIO_RGMII
},
{
.compatible = "apm,xgene-mdio-xfi",
.data = (void *)XGENE_MDIO_XFI
},
{},
};
MODULE_DEVICE_TABLE(of, xgene_mdio_of_match);
#endif
#ifdef CONFIG_ACPI
static const struct acpi_device_id xgene_mdio_acpi_match[] = {
{ "APMC0D65", XGENE_MDIO_RGMII },
{ "APMC0D66", XGENE_MDIO_XFI },
{ }
};
MODULE_DEVICE_TABLE(acpi, xgene_mdio_acpi_match);
#endif
static struct platform_driver xgene_mdio_driver = {
.driver = {
.name = "xgene-mdio",
.of_match_table = of_match_ptr(xgene_mdio_of_match),
.acpi_match_table = ACPI_PTR(xgene_mdio_acpi_match),
},
.probe = xgene_mdio_probe,
.remove = xgene_mdio_remove,
};
module_platform_driver(xgene_mdio_driver);
MODULE_DESCRIPTION("APM X-Gene SoC MDIO driver");
MODULE_AUTHOR("Iyappan Subramanian <isubramanian@apm.com>");
MODULE_LICENSE("GPL");
/* Applied Micro X-Gene SoC MDIO Driver
*
* Copyright (c) 2016, Applied Micro Circuits Corporation
* Author: Iyappan Subramanian <isubramanian@apm.com>
*
* 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, see <http://www.gnu.org/licenses/>.
*/
#ifndef __MDIO_XGENE_H__
#define __MDIO_XGENE_H__
#define BLOCK_XG_MDIO_CSR_OFFSET 0x5000
#define BLOCK_DIAG_CSR_OFFSET 0xd000
#define XGENET_CONFIG_REG_ADDR 0x20
#define MAC_ADDR_REG_OFFSET 0x00
#define MAC_COMMAND_REG_OFFSET 0x04
#define MAC_WRITE_REG_OFFSET 0x08
#define MAC_READ_REG_OFFSET 0x0c
#define MAC_COMMAND_DONE_REG_OFFSET 0x10
#define CLKEN_OFFSET 0x08
#define SRST_OFFSET 0x00
#define MENET_CFG_MEM_RAM_SHUTDOWN_ADDR 0x70
#define MENET_BLOCK_MEM_RDY_ADDR 0x74
#define MAC_CONFIG_1_ADDR 0x00
#define MII_MGMT_COMMAND_ADDR 0x24
#define MII_MGMT_ADDRESS_ADDR 0x28
#define MII_MGMT_CONTROL_ADDR 0x2c
#define MII_MGMT_STATUS_ADDR 0x30
#define MII_MGMT_INDICATORS_ADDR 0x34
#define SOFT_RESET BIT(31)
#define MII_MGMT_CONFIG_ADDR 0x20
#define MII_MGMT_COMMAND_ADDR 0x24
#define MII_MGMT_ADDRESS_ADDR 0x28
#define MII_MGMT_CONTROL_ADDR 0x2c
#define MII_MGMT_STATUS_ADDR 0x30
#define MII_MGMT_INDICATORS_ADDR 0x34
#define MIIM_COMMAND_ADDR 0x20
#define MIIM_FIELD_ADDR 0x24
#define MIIM_CONFIGURATION_ADDR 0x28
#define MIIM_LINKFAILVECTOR_ADDR 0x2c
#define MIIM_INDICATOR_ADDR 0x30
#define MIIMRD_FIELD_ADDR 0x34
#define MDIO_CSR_OFFSET 0x5000
#define REG_ADDR_POS 0
#define REG_ADDR_LEN 5
#define PHY_ADDR_POS 8
#define PHY_ADDR_LEN 5
#define HSTMIIMWRDAT_POS 0
#define HSTMIIMWRDAT_LEN 16
#define HSTPHYADX_POS 23
#define HSTPHYADX_LEN 5
#define HSTREGADX_POS 18
#define HSTREGADX_LEN 5
#define HSTLDCMD BIT(3)
#define HSTMIIMCMD_POS 0
#define HSTMIIMCMD_LEN 3
#define BUSY_MASK BIT(0)
#define READ_CYCLE_MASK BIT(0)
enum xgene_enet_cmd {
XGENE_ENET_WR_CMD = BIT(31),
XGENE_ENET_RD_CMD = BIT(30)
};
enum {
MIIM_CMD_IDLE,
MIIM_CMD_LEGACY_WRITE,
MIIM_CMD_LEGACY_READ,
};
enum xgene_mdio_id {
XGENE_MDIO_RGMII = 1,
XGENE_MDIO_XFI
};
struct xgene_mdio_pdata {
struct clk *clk;
struct device *dev;
void __iomem *mac_csr_addr;
void __iomem *diag_csr_addr;
void __iomem *mdio_csr_addr;
struct mii_bus *mdio_bus;
int mdio_id;
};
/* Set the specified value into a bit-field defined by its starting position
* and length within a single u64.
*/
static inline u64 xgene_enet_set_field_value(int pos, int len, u64 val)
{
return (val & ((1ULL << len) - 1)) << pos;
}
#define SET_VAL(field, val) \
xgene_enet_set_field_value(field ## _POS, field ## _LEN, val)
#define SET_BIT(field) \
xgene_enet_set_field_value(field ## _POS, 1, 1)
/* Get the value from a bit-field defined by its starting position
* and length within the specified u64.
*/
static inline u64 xgene_enet_get_field_value(int pos, int len, u64 src)
{
return (src >> pos) & ((1ULL << len) - 1);
}
#define GET_VAL(field, src) \
xgene_enet_get_field_value(field ## _POS, field ## _LEN, src)
#define GET_BIT(field, src) \
xgene_enet_get_field_value(field ## _POS, 1, src)
static const struct of_device_id xgene_mdio_of_match[];
#ifdef CONFIG_ACPI
static const struct acpi_device_id xgene_mdio_acpi_match[];
#endif
int xgene_mdio_rgmii_read(struct mii_bus *bus, int phy_id, int reg);
int xgene_mdio_rgmii_write(struct mii_bus *bus, int phy_id, int reg, u16 data);
struct phy_device *xgene_enet_phy_register(struct mii_bus *bus, int phy_addr);
#endif /* __MDIO_XGENE_H__ */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment