Commit 9ba10afe authored by David S. Miller's avatar David S. Miller

Merge branch 'pxa168_eth'

Antoine Tenart says:

====================
ARM: Berlin: Ethernet support

This series introduce support for the Ethernet controller on Berlin SoCs,
using the existing pxa168 Ethernet driver. In order to do this, DT
support is added to the driver alongside some other modifications and
fixes.

This has been tested on a Berlin BG2Q DMP board.

Changes since v5:
	- fixed the build when building the driver as a module

Changes since v4:
        - removed the phy-addr property and added a phy subnode
        - added COMPILE_TEST for the pxa168_eth driver

Changes since v3:
        - moved the addition of pxa168_eth_get_mac_address() to the patch
          using it first

Changes since v2:
        - reworked how the MAC address is configured
        - made the clock anonymous

Changes since v1:
        - removed custom Berlin Ethernet driver
        - used the pxa168 Ethernet driver instead
        - made modifications to the pxa168 driver (DT support, fixes)
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 4cb53f3f 75215193
* Marvell PXA168 Ethernet Controller
Required properties:
- compatible: should be "marvell,pxa168-eth".
- reg: address and length of the register set for the device.
- interrupts: interrupt for the device.
- clocks: pointer to the clock for the device.
Optional properties:
- port-id: Ethernet port number. Should be '0','1' or '2'.
- #address-cells: must be 1 when using sub-nodes.
- #size-cells: must be 0 when using sub-nodes.
- phy-handle: see ethernet.txt file in the same directory.
- local-mac-address: see ethernet.txt file in the same directory.
Sub-nodes:
Each PHY can be represented as a sub-node. This is not mandatory.
Sub-nodes required properties:
- reg: the MDIO address of the PHY.
Example:
eth0: ethernet@f7b90000 {
compatible = "marvell,pxa168-eth";
reg = <0xf7b90000 0x10000>;
clocks = <&chip CLKID_GETH0>;
interrupts = <GIC_SPI 24 IRQ_TYPE_LEVEL_HIGH>;
#address-cells = <1>;
#size-cells = <0>;
phy-handle = <&ethphy0>;
ethphy0: ethernet-phy@0 {
reg = <0>;
};
};
......@@ -45,3 +45,7 @@ &i2c2 {
&uart0 {
status = "okay";
};
&eth0 {
status = "okay";
};
......@@ -114,6 +114,23 @@ gic: interrupt-controller@ad1000 {
#interrupt-cells = <3>;
};
eth0: ethernet@b90000 {
compatible = "marvell,pxa168-eth";
reg = <0xb90000 0x10000>;
clocks = <&chip CLKID_GETH0>;
interrupts = <GIC_SPI 24 IRQ_TYPE_LEVEL_HIGH>;
/* set by bootloader */
local-mac-address = [00 00 00 00 00 00];
#address-cells = <1>;
#size-cells = <0>;
phy-handle = <&ethphy0>;
status = "disabled";
ethphy0: ethernet-phy@0 {
reg = <0>;
};
};
cpu-ctrl@dd0000 {
compatible = "marvell,berlin-cpu-ctrl";
reg = <0xdd0000 0x10000>;
......
......@@ -64,7 +64,7 @@ config MVPP2
config PXA168_ETH
tristate "Marvell pxa168 ethernet support"
depends on CPU_PXA168
depends on CPU_PXA168 || ARCH_BERLIN || COMPILE_TEST
select PHYLIB
---help---
This driver supports the pxa168 Ethernet ports.
......
......@@ -22,27 +22,30 @@
* along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
#include <linux/dma-mapping.h>
#include <linux/in.h>
#include <linux/ip.h>
#include <linux/tcp.h>
#include <linux/udp.h>
#include <linux/etherdevice.h>
#include <linux/bitops.h>
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/dma-mapping.h>
#include <linux/etherdevice.h>
#include <linux/ethtool.h>
#include <linux/platform_device.h>
#include <linux/module.h>
#include <linux/in.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/ip.h>
#include <linux/kernel.h>
#include <linux/workqueue.h>
#include <linux/clk.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_net.h>
#include <linux/phy.h>
#include <linux/io.h>
#include <linux/interrupt.h>
#include <linux/platform_device.h>
#include <linux/pxa168_eth.h>
#include <linux/tcp.h>
#include <linux/types.h>
#include <linux/udp.h>
#include <linux/workqueue.h>
#include <asm/pgtable.h>
#include <asm/cacheflush.h>
#include <linux/pxa168_eth.h>
#define DRIVER_NAME "pxa168-eth"
#define DRIVER_VERSION "0.3"
......@@ -58,6 +61,8 @@
#define PORT_COMMAND 0x0410
#define PORT_STATUS 0x0418
#define HTPR 0x0428
#define MAC_ADDR_LOW 0x0430
#define MAC_ADDR_HIGH 0x0438
#define SDMA_CONFIG 0x0440
#define SDMA_CMD 0x0448
#define INT_CAUSE 0x0450
......@@ -161,7 +166,7 @@
/* Bit definitions for Port status */
#define PORT_SPEED_100 (1 << 0)
#define FULL_DUPLEX (1 << 1)
#define FLOW_CONTROL_ENABLED (1 << 2)
#define FLOW_CONTROL_DISABLED (1 << 2)
#define LINK_UP (1 << 3)
/* Bit definitions for work to be done */
......@@ -191,6 +196,7 @@ struct tx_desc {
struct pxa168_eth_private {
int port_num; /* User Ethernet port number */
int phy_addr;
int rx_resource_err; /* Rx ring resource error flag */
......@@ -296,7 +302,7 @@ static void abort_dma(struct pxa168_eth_private *pep)
} while (max_retries-- > 0 && delay <= 0);
if (max_retries <= 0)
printk(KERN_ERR "%s : DMA Stuck\n", __func__);
netdev_err(pep->dev, "%s : DMA Stuck\n", __func__);
}
static int ethernet_phy_get(struct pxa168_eth_private *pep)
......@@ -507,9 +513,10 @@ static int add_del_hash_entry(struct pxa168_eth_private *pep,
if (i == HOP_NUMBER) {
if (!del) {
printk(KERN_INFO "%s: table section is full, need to "
"move to 16kB implementation?\n",
__FILE__);
netdev_info(pep->dev,
"%s: table section is full, need to "
"move to 16kB implementation?\n",
__FILE__);
return -ENOSPC;
} else
return 0;
......@@ -600,16 +607,42 @@ static void pxa168_eth_set_rx_mode(struct net_device *dev)
update_hash_table_mac_address(pep, NULL, ha->addr);
}
static void pxa168_eth_get_mac_address(struct net_device *dev,
unsigned char *addr)
{
struct pxa168_eth_private *pep = netdev_priv(dev);
unsigned int mac_h = rdl(pep, MAC_ADDR_HIGH);
unsigned int mac_l = rdl(pep, MAC_ADDR_LOW);
addr[0] = (mac_h >> 24) & 0xff;
addr[1] = (mac_h >> 16) & 0xff;
addr[2] = (mac_h >> 8) & 0xff;
addr[3] = mac_h & 0xff;
addr[4] = (mac_l >> 8) & 0xff;
addr[5] = mac_l & 0xff;
}
static int pxa168_eth_set_mac_address(struct net_device *dev, void *addr)
{
struct sockaddr *sa = addr;
struct pxa168_eth_private *pep = netdev_priv(dev);
unsigned char oldMac[ETH_ALEN];
u32 mac_h, mac_l;
if (!is_valid_ether_addr(sa->sa_data))
return -EADDRNOTAVAIL;
memcpy(oldMac, dev->dev_addr, ETH_ALEN);
memcpy(dev->dev_addr, sa->sa_data, ETH_ALEN);
mac_h = sa->sa_data[0] << 24;
mac_h |= sa->sa_data[1] << 16;
mac_h |= sa->sa_data[2] << 8;
mac_h |= sa->sa_data[3];
mac_l = sa->sa_data[4] << 8;
mac_l |= sa->sa_data[5];
wrl(pep, MAC_ADDR_HIGH, mac_h);
wrl(pep, MAC_ADDR_LOW, mac_l);
netif_addr_lock_bh(dev);
update_hash_table_mac_address(pep, oldMac, dev->dev_addr);
netif_addr_unlock_bh(dev);
......@@ -726,7 +759,7 @@ static int txq_reclaim(struct net_device *dev, int force)
if (cmd_sts & TX_ERROR) {
if (net_ratelimit())
printk(KERN_ERR "%s: Error in TX\n", dev->name);
netdev_err(dev, "Error in TX\n");
dev->stats.tx_errors++;
}
dma_unmap_single(NULL, addr, count, DMA_TO_DEVICE);
......@@ -743,8 +776,7 @@ static void pxa168_eth_tx_timeout(struct net_device *dev)
{
struct pxa168_eth_private *pep = netdev_priv(dev);
printk(KERN_INFO "%s: TX timeout desc_count %d\n",
dev->name, pep->tx_desc_count);
netdev_info(dev, "TX timeout desc_count %d\n", pep->tx_desc_count);
schedule_work(&pep->tx_timeout_task);
}
......@@ -814,9 +846,8 @@ static int rxq_process(struct net_device *dev, int budget)
if ((cmd_sts & (RX_FIRST_DESC | RX_LAST_DESC)) !=
(RX_FIRST_DESC | RX_LAST_DESC)) {
if (net_ratelimit())
printk(KERN_ERR
"%s: Rx pkt on multiple desc\n",
dev->name);
netdev_err(dev,
"Rx pkt on multiple desc\n");
}
if (cmd_sts & RX_ERROR)
stats->rx_errors++;
......@@ -871,7 +902,7 @@ static void handle_link_event(struct pxa168_eth_private *pep)
port_status = rdl(pep, PORT_STATUS);
if (!(port_status & LINK_UP)) {
if (netif_carrier_ok(dev)) {
printk(KERN_INFO "%s: link down\n", dev->name);
netdev_info(dev, "link down\n");
netif_carrier_off(dev);
txq_reclaim(dev, 1);
}
......@@ -883,10 +914,9 @@ static void handle_link_event(struct pxa168_eth_private *pep)
speed = 10;
duplex = (port_status & FULL_DUPLEX) ? 1 : 0;
fc = (port_status & FLOW_CONTROL_ENABLED) ? 1 : 0;
printk(KERN_INFO "%s: link up, %d Mb/s, %s duplex, "
"flow control %sabled\n", dev->name,
speed, duplex ? "full" : "half", fc ? "en" : "dis");
fc = (port_status & FLOW_CONTROL_DISABLED) ? 0 : 1;
netdev_info(dev, "link up, %d Mb/s, %s duplex, flow control %sabled\n",
speed, duplex ? "full" : "half", fc ? "en" : "dis");
if (!netif_carrier_ok(dev))
netif_carrier_on(dev);
}
......@@ -1039,9 +1069,8 @@ static void rxq_deinit(struct net_device *dev)
}
}
if (pep->rx_desc_count)
printk(KERN_ERR
"Error in freeing Rx Ring. %d skb's still\n",
pep->rx_desc_count);
netdev_err(dev, "Error in freeing Rx Ring. %d skb's still\n",
pep->rx_desc_count);
/* Free RX ring */
if (pep->p_rx_desc_area)
dma_free_coherent(pep->dev->dev.parent, pep->rx_desc_area_size,
......@@ -1280,15 +1309,15 @@ static int pxa168_smi_read(struct mii_bus *bus, int phy_addr, int regnum)
int val;
if (smi_wait_ready(pep)) {
printk(KERN_WARNING "pxa168_eth: SMI bus busy timeout\n");
netdev_warn(pep->dev, "pxa168_eth: SMI bus busy timeout\n");
return -ETIMEDOUT;
}
wrl(pep, SMI, (phy_addr << 16) | (regnum << 21) | SMI_OP_R);
/* now wait for the data to be valid */
for (i = 0; !((val = rdl(pep, SMI)) & SMI_R_VALID); i++) {
if (i == PHY_WAIT_ITERATIONS) {
printk(KERN_WARNING
"pxa168_eth: SMI bus read not valid\n");
netdev_warn(pep->dev,
"pxa168_eth: SMI bus read not valid\n");
return -ENODEV;
}
msleep(10);
......@@ -1303,7 +1332,7 @@ static int pxa168_smi_write(struct mii_bus *bus, int phy_addr, int regnum,
struct pxa168_eth_private *pep = bus->priv;
if (smi_wait_ready(pep)) {
printk(KERN_WARNING "pxa168_eth: SMI bus busy timeout\n");
netdev_warn(pep->dev, "pxa168_eth: SMI bus busy timeout\n");
return -ETIMEDOUT;
}
......@@ -1311,7 +1340,7 @@ static int pxa168_smi_write(struct mii_bus *bus, int phy_addr, int regnum,
SMI_OP_W | (value & 0xffff));
if (smi_wait_ready(pep)) {
printk(KERN_ERR "pxa168_eth: SMI bus busy timeout\n");
netdev_err(pep->dev, "pxa168_eth: SMI bus busy timeout\n");
return -ETIMEDOUT;
}
......@@ -1361,24 +1390,25 @@ static struct phy_device *phy_scan(struct pxa168_eth_private *pep, int phy_addr)
return phydev;
}
static void phy_init(struct pxa168_eth_private *pep, int speed, int duplex)
static void phy_init(struct pxa168_eth_private *pep)
{
struct phy_device *phy = pep->phy;
phy_attach(pep->dev, dev_name(&phy->dev), PHY_INTERFACE_MODE_MII);
if (speed == 0) {
if (pep->pd && pep->pd->speed != 0) {
phy->autoneg = AUTONEG_DISABLE;
phy->advertising = 0;
phy->speed = pep->pd->speed;
phy->duplex = pep->pd->duplex;
} else {
phy->autoneg = AUTONEG_ENABLE;
phy->speed = 0;
phy->duplex = 0;
phy->supported &= PHY_BASIC_FEATURES;
phy->advertising = phy->supported | ADVERTISED_Autoneg;
} else {
phy->autoneg = AUTONEG_DISABLE;
phy->advertising = 0;
phy->speed = speed;
phy->duplex = duplex;
}
phy_start_aneg(phy);
}
......@@ -1386,11 +1416,13 @@ static int ethernet_phy_setup(struct net_device *dev)
{
struct pxa168_eth_private *pep = netdev_priv(dev);
if (pep->pd->init)
if (pep->pd && pep->pd->init)
pep->pd->init();
pep->phy = phy_scan(pep, pep->pd->phy_addr & 0x1f);
pep->phy = phy_scan(pep, pep->phy_addr & 0x1f);
if (pep->phy != NULL)
phy_init(pep, pep->pd->speed, pep->pd->duplex);
phy_init(pep);
update_hash_table_mac_address(pep, NULL, dev->dev_addr);
return 0;
......@@ -1425,23 +1457,23 @@ static void pxa168_get_drvinfo(struct net_device *dev,
}
static const struct ethtool_ops pxa168_ethtool_ops = {
.get_settings = pxa168_get_settings,
.set_settings = pxa168_set_settings,
.get_drvinfo = pxa168_get_drvinfo,
.get_link = ethtool_op_get_link,
.get_ts_info = ethtool_op_get_ts_info,
.get_settings = pxa168_get_settings,
.set_settings = pxa168_set_settings,
.get_drvinfo = pxa168_get_drvinfo,
.get_link = ethtool_op_get_link,
.get_ts_info = ethtool_op_get_ts_info,
};
static const struct net_device_ops pxa168_eth_netdev_ops = {
.ndo_open = pxa168_eth_open,
.ndo_stop = pxa168_eth_stop,
.ndo_start_xmit = pxa168_eth_start_xmit,
.ndo_set_rx_mode = pxa168_eth_set_rx_mode,
.ndo_set_mac_address = pxa168_eth_set_mac_address,
.ndo_validate_addr = eth_validate_addr,
.ndo_do_ioctl = pxa168_eth_do_ioctl,
.ndo_change_mtu = pxa168_eth_change_mtu,
.ndo_tx_timeout = pxa168_eth_tx_timeout,
.ndo_open = pxa168_eth_open,
.ndo_stop = pxa168_eth_stop,
.ndo_start_xmit = pxa168_eth_start_xmit,
.ndo_set_rx_mode = pxa168_eth_set_rx_mode,
.ndo_set_mac_address = pxa168_eth_set_mac_address,
.ndo_validate_addr = eth_validate_addr,
.ndo_do_ioctl = pxa168_eth_do_ioctl,
.ndo_change_mtu = pxa168_eth_change_mtu,
.ndo_tx_timeout = pxa168_eth_tx_timeout,
};
static int pxa168_eth_probe(struct platform_device *pdev)
......@@ -1450,17 +1482,18 @@ static int pxa168_eth_probe(struct platform_device *pdev)
struct net_device *dev = NULL;
struct resource *res;
struct clk *clk;
struct device_node *np;
const unsigned char *mac_addr = NULL;
int err;
printk(KERN_NOTICE "PXA168 10/100 Ethernet Driver\n");
clk = clk_get(&pdev->dev, "MFUCLK");
clk = devm_clk_get(&pdev->dev, NULL);
if (IS_ERR(clk)) {
printk(KERN_ERR "%s: Fast Ethernet failed to get clock\n",
DRIVER_NAME);
dev_err(&pdev->dev, "Fast Ethernet failed to get clock\n");
return -ENODEV;
}
clk_enable(clk);
clk_prepare_enable(clk);
dev = alloc_etherdev(sizeof(struct pxa168_eth_private));
if (!dev) {
......@@ -1477,8 +1510,8 @@ static int pxa168_eth_probe(struct platform_device *pdev)
err = -ENODEV;
goto err_netdev;
}
pep->base = ioremap(res->start, resource_size(res));
if (pep->base == NULL) {
pep->base = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(pep->base)) {
err = -ENOMEM;
goto err_netdev;
}
......@@ -1492,19 +1525,42 @@ static int pxa168_eth_probe(struct platform_device *pdev)
INIT_WORK(&pep->tx_timeout_task, pxa168_eth_tx_timeout_task);
printk(KERN_INFO "%s:Using random mac address\n", DRIVER_NAME);
eth_hw_addr_random(dev);
if (pdev->dev.of_node)
mac_addr = of_get_mac_address(pdev->dev.of_node);
pep->pd = dev_get_platdata(&pdev->dev);
pep->rx_ring_size = NUM_RX_DESCS;
if (pep->pd->rx_queue_size)
pep->rx_ring_size = pep->pd->rx_queue_size;
if (mac_addr && is_valid_ether_addr(mac_addr)) {
ether_addr_copy(dev->dev_addr, mac_addr);
} else {
/* try reading the mac address, if set by the bootloader */
pxa168_eth_get_mac_address(dev, dev->dev_addr);
if (!is_valid_ether_addr(dev->dev_addr)) {
dev_info(&pdev->dev, "Using random mac address\n");
eth_hw_addr_random(dev);
}
}
pep->rx_ring_size = NUM_RX_DESCS;
pep->tx_ring_size = NUM_TX_DESCS;
if (pep->pd->tx_queue_size)
pep->tx_ring_size = pep->pd->tx_queue_size;
pep->port_num = pep->pd->port_number;
pep->pd = dev_get_platdata(&pdev->dev);
if (pep->pd) {
if (pep->pd->rx_queue_size)
pep->rx_ring_size = pep->pd->rx_queue_size;
if (pep->pd->tx_queue_size)
pep->tx_ring_size = pep->pd->tx_queue_size;
pep->port_num = pep->pd->port_number;
pep->phy_addr = pep->pd->phy_addr;
} else if (pdev->dev.of_node) {
of_property_read_u32(pdev->dev.of_node, "port-id",
&pep->port_num);
np = of_parse_phandle(pdev->dev.of_node, "phy-handle", 0);
if (np)
of_property_read_u32(np, "reg", &pep->phy_addr);
}
/* Hardware supports only 3 ports */
BUG_ON(pep->port_num > 2);
netif_napi_add(dev, &pep->napi, pxa168_rx_poll, pep->rx_ring_size);
......@@ -1605,6 +1661,12 @@ static int pxa168_eth_suspend(struct platform_device *pdev, pm_message_t state)
#define pxa168_eth_suspend NULL
#endif
static const struct of_device_id pxa168_eth_of_match[] = {
{ .compatible = "marvell,pxa168-eth" },
{ },
};
MODULE_DEVICE_TABLE(of, pxa168_eth_of_match);
static struct platform_driver pxa168_eth_driver = {
.probe = pxa168_eth_probe,
.remove = pxa168_eth_remove,
......@@ -1612,8 +1674,9 @@ static struct platform_driver pxa168_eth_driver = {
.resume = pxa168_eth_resume,
.suspend = pxa168_eth_suspend,
.driver = {
.name = DRIVER_NAME,
},
.name = DRIVER_NAME,
.of_match_table = of_match_ptr(pxa168_eth_of_match),
},
};
module_platform_driver(pxa168_eth_driver);
......
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