From ecc6703cbb2bb648c7345c652a704f7af56322b8 Mon Sep 17 00:00:00 2001 From: Greg Rose Date: Sat, 9 Jan 2010 02:24:50 +0000 Subject: ixgbevf: Kconfig, Makefile and Documentation Modifications for the Kconfig and network device Makefile to add the ixgbevf driver module to the kernel plus basic driver documentation. Signed-off-by: Greg Rose Signed-off-by: Jeff Kirsher Signed-off-by: David S. Miller --- drivers/net/Kconfig | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) (limited to 'drivers/net/Kconfig') diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig index dd9a09c72dff..5be6a2376f4f 100644 --- a/drivers/net/Kconfig +++ b/drivers/net/Kconfig @@ -2618,6 +2618,28 @@ config IXGBE_DCB If unsure, say N. +config IXGBEVF + tristate "Intel(R) 82599 Virtual Function Ethernet support" + depends on PCI_MSI + ---help--- + This driver supports Intel(R) 82599 virtual functions. For more + information on how to identify your adapter, go to the Adapter & + Driver ID Guide at: + + + + For general information and support, go to the Intel support + website at: + + + + More specific information on configuring the driver is in + . + + To compile this driver as a module, choose M here. The module + will be called ixgbevf. MSI-X interrupt support is required + for this driver to work correctly. + config IXGB tristate "Intel(R) PRO/10GbE support" depends on PCI -- cgit v1.2.3 From 0ec00f0392b807d57a2281576a96552d7694b6bb Mon Sep 17 00:00:00 2001 From: Amit Kumar Salecha Date: Wed, 13 Jan 2010 00:37:26 +0000 Subject: NET: Add Qlogic ethernet driver for CNA devices o Separate Ethernet driver for Qlogic CNA devices Signed-off-by: Amit Kumar Salecha Signed-off-by: David S. Miller --- MAINTAINERS | 7 +++++++ drivers/net/Kconfig | 7 +++++++ drivers/net/Makefile | 1 + 3 files changed, 15 insertions(+) (limited to 'drivers/net/Kconfig') diff --git a/MAINTAINERS b/MAINTAINERS index 337dffbe9a47..61367ec865cb 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -4452,6 +4452,13 @@ S: Supported F: Documentation/networking/LICENSE.qla3xxx F: drivers/net/qla3xxx.* +QLOGIC QLCNIC (1/10)Gb ETHERNET DRIVER +M: Amit Kumar Salecha +M: linux-driver@qlogic.com +L: netdev@vger.kernel.org +S: Supported +F: drivers/net/qlcnic/ + QLOGIC QLGE 10Gb ETHERNET DRIVER M: Ron Mercer M: linux-driver@qlogic.com diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig index 5be6a2376f4f..cb0e534418e3 100644 --- a/drivers/net/Kconfig +++ b/drivers/net/Kconfig @@ -2778,6 +2778,13 @@ config BNX2X To compile this driver as a module, choose M here: the module will be called bnx2x. This is recommended. +config QLCNIC + tristate "QLOGIC QLCNIC 1/10Gb Converged Ethernet NIC Support" + depends on PCI + help + This driver supports QLogic QLE8240 and QLE8242 Converged Ethernet + devices. + config QLGE tristate "QLogic QLGE 10Gb Ethernet Driver Support" depends on PCI diff --git a/drivers/net/Makefile b/drivers/net/Makefile index 6746e8b8bdfc..0b763cbe9b1f 100644 --- a/drivers/net/Makefile +++ b/drivers/net/Makefile @@ -149,6 +149,7 @@ ll_temac-objs := ll_temac_main.o ll_temac_mdio.o obj-$(CONFIG_XILINX_LL_TEMAC) += ll_temac.o obj-$(CONFIG_XILINX_EMACLITE) += xilinx_emaclite.o obj-$(CONFIG_QLA3XXX) += qla3xxx.o +obj-$(CONFIG_QLCNIC) += qlcnic/ obj-$(CONFIG_QLGE) += qlge/ obj-$(CONFIG_PPP) += ppp_generic.o -- cgit v1.2.3 From c1596b75c29eb5b32c65ef1e186c8b08c289bf05 Mon Sep 17 00:00:00 2001 From: Hamish Guthrie Date: Thu, 28 Jan 2010 12:54:09 +0100 Subject: ps3_gelic_wireless: Remove PS3 gelic legacy wpa support The current PS3 gelic wireless driver has support for wireless extensions. The original PS3 gelic wireless driver exposed a dedicated API for a dedicated wpa_supplicant driver. This old API could be enabled with CONFIG_GELIC_WIRELESS_OLD_PSK_INTERFACE, however, as this is not being used by any distros, and it is being removed from the driver and from wpa_supplicant. Signed-off-by: Hamish Guthrie Acked-by: Geoff Levand Signed-off-by: John W. Linville --- arch/powerpc/configs/ppc64_defconfig | 1 - arch/powerpc/configs/ps3_defconfig | 1 - drivers/net/Kconfig | 14 ---- drivers/net/ps3_gelic_wireless.c | 137 ----------------------------------- 4 files changed, 153 deletions(-) (limited to 'drivers/net/Kconfig') diff --git a/arch/powerpc/configs/ppc64_defconfig b/arch/powerpc/configs/ppc64_defconfig index 252401824575..070587ae4f74 100644 --- a/arch/powerpc/configs/ppc64_defconfig +++ b/arch/powerpc/configs/ppc64_defconfig @@ -1000,7 +1000,6 @@ CONFIG_TIGON3=y CONFIG_SPIDER_NET=m CONFIG_GELIC_NET=m CONFIG_GELIC_WIRELESS=y -# CONFIG_GELIC_WIRELESS_OLD_PSK_INTERFACE is not set # CONFIG_QLA3XXX is not set # CONFIG_ATL1 is not set # CONFIG_ATL1E is not set diff --git a/arch/powerpc/configs/ps3_defconfig b/arch/powerpc/configs/ps3_defconfig index 7de127e4ceef..32f7058bb173 100644 --- a/arch/powerpc/configs/ps3_defconfig +++ b/arch/powerpc/configs/ps3_defconfig @@ -593,7 +593,6 @@ CONFIG_MII=m CONFIG_NETDEV_1000=y CONFIG_GELIC_NET=y CONFIG_GELIC_WIRELESS=y -# CONFIG_GELIC_WIRELESS_OLD_PSK_INTERFACE is not set # CONFIG_NETDEV_10000 is not set # diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig index 18ff622703be..6460505ab24c 100644 --- a/drivers/net/Kconfig +++ b/drivers/net/Kconfig @@ -2354,20 +2354,6 @@ config GELIC_WIRELESS the driver automatically distinguishes the models, you can safely enable this option even if you have a wireless-less model. -config GELIC_WIRELESS_OLD_PSK_INTERFACE - bool "PS3 Wireless private PSK interface (OBSOLETE)" - depends on GELIC_WIRELESS - select WEXT_PRIV - help - This option retains the obsolete private interface to pass - the PSK from user space programs to the driver. The PSK - stands for 'Pre Shared Key' and is used for WPA[2]-PSK - (WPA-Personal) environment. - If WPA[2]-PSK is used and you need to use old programs that - support only this old interface, say Y. Otherwise N. - - If unsure, say N. - config FSL_PQ_MDIO tristate "Freescale PQ MDIO" depends on FSL_SOC diff --git a/drivers/net/ps3_gelic_wireless.c b/drivers/net/ps3_gelic_wireless.c index 227b141c4fbd..7571dbba6a0a 100644 --- a/drivers/net/ps3_gelic_wireless.c +++ b/drivers/net/ps3_gelic_wireless.c @@ -1389,113 +1389,6 @@ static int gelic_wl_get_mode(struct net_device *netdev, return 0; } -#ifdef CONFIG_GELIC_WIRELESS_OLD_PSK_INTERFACE -/* SIOCIWFIRSTPRIV */ -static int hex2bin(u8 *str, u8 *bin, unsigned int len) -{ - unsigned int i; - static unsigned char *hex = "0123456789ABCDEF"; - unsigned char *p, *q; - u8 tmp; - - if (len != WPA_PSK_LEN * 2) - return -EINVAL; - - for (i = 0; i < WPA_PSK_LEN * 2; i += 2) { - p = strchr(hex, toupper(str[i])); - q = strchr(hex, toupper(str[i + 1])); - if (!p || !q) { - pr_info("%s: unconvertible PSK digit=%d\n", - __func__, i); - return -EINVAL; - } - tmp = ((p - hex) << 4) + (q - hex); - *bin++ = tmp; - } - return 0; -}; - -static int gelic_wl_priv_set_psk(struct net_device *net_dev, - struct iw_request_info *info, - union iwreq_data *data, char *extra) -{ - struct gelic_wl_info *wl = port_wl(netdev_priv(net_dev)); - unsigned int len; - unsigned long irqflag; - int ret = 0; - - pr_debug("%s:<- len=%d\n", __func__, data->data.length); - len = data->data.length - 1; - if (len <= 2) - return -EINVAL; - - spin_lock_irqsave(&wl->lock, irqflag); - if (extra[0] == '"' && extra[len - 1] == '"') { - pr_debug("%s: passphrase mode\n", __func__); - /* pass phrase */ - if (GELIC_WL_EURUS_PSK_MAX_LEN < (len - 2)) { - pr_info("%s: passphrase too long\n", __func__); - ret = -E2BIG; - goto out; - } - memset(wl->psk, 0, sizeof(wl->psk)); - wl->psk_len = len - 2; - memcpy(wl->psk, &(extra[1]), wl->psk_len); - wl->psk_type = GELIC_EURUS_WPA_PSK_PASSPHRASE; - } else { - ret = hex2bin(extra, wl->psk, len); - if (ret) - goto out; - wl->psk_len = WPA_PSK_LEN; - wl->psk_type = GELIC_EURUS_WPA_PSK_BIN; - } - set_bit(GELIC_WL_STAT_WPA_PSK_SET, &wl->stat); -out: - spin_unlock_irqrestore(&wl->lock, irqflag); - pr_debug("%s:->\n", __func__); - return ret; -} - -static int gelic_wl_priv_get_psk(struct net_device *net_dev, - struct iw_request_info *info, - union iwreq_data *data, char *extra) -{ - struct gelic_wl_info *wl = port_wl(netdev_priv(net_dev)); - char *p; - unsigned long irqflag; - unsigned int i; - - pr_debug("%s:<-\n", __func__); - if (!capable(CAP_NET_ADMIN)) - return -EPERM; - - spin_lock_irqsave(&wl->lock, irqflag); - p = extra; - if (test_bit(GELIC_WL_STAT_WPA_PSK_SET, &wl->stat)) { - if (wl->psk_type == GELIC_EURUS_WPA_PSK_BIN) { - for (i = 0; i < wl->psk_len; i++) { - sprintf(p, "%02xu", wl->psk[i]); - p += 2; - } - *p = '\0'; - data->data.length = wl->psk_len * 2; - } else { - *p++ = '"'; - memcpy(p, wl->psk, wl->psk_len); - p += wl->psk_len; - *p++ = '"'; - *p = '\0'; - data->data.length = wl->psk_len + 2; - } - } else - /* no psk set */ - data->data.length = 0; - spin_unlock_irqrestore(&wl->lock, irqflag); - pr_debug("%s:-> %d\n", __func__, data->data.length); - return 0; -} -#endif - /* SIOCGIWNICKN */ static int gelic_wl_get_nick(struct net_device *net_dev, struct iw_request_info *info, @@ -2406,40 +2299,10 @@ static const iw_handler gelic_wl_wext_handler[] = IW_IOCTL(SIOCGIWNICKN) = gelic_wl_get_nick, }; -#ifdef CONFIG_GELIC_WIRELESS_OLD_PSK_INTERFACE -static struct iw_priv_args gelic_wl_private_args[] = -{ - { - .cmd = GELIC_WL_PRIV_SET_PSK, - .set_args = IW_PRIV_TYPE_CHAR | - (GELIC_WL_EURUS_PSK_MAX_LEN + 2), - .name = "set_psk" - }, - { - .cmd = GELIC_WL_PRIV_GET_PSK, - .get_args = IW_PRIV_TYPE_CHAR | - (GELIC_WL_EURUS_PSK_MAX_LEN + 2), - .name = "get_psk" - } -}; - -static const iw_handler gelic_wl_private_handler[] = -{ - gelic_wl_priv_set_psk, - gelic_wl_priv_get_psk, -}; -#endif - static const struct iw_handler_def gelic_wl_wext_handler_def = { .num_standard = ARRAY_SIZE(gelic_wl_wext_handler), .standard = gelic_wl_wext_handler, .get_wireless_stats = gelic_wl_get_wireless_stats, -#ifdef CONFIG_GELIC_WIRELESS_OLD_PSK_INTERFACE - .num_private = ARRAY_SIZE(gelic_wl_private_handler), - .num_private_args = ARRAY_SIZE(gelic_wl_private_args), - .private = gelic_wl_private_handler, - .private_args = gelic_wl_private_args, -#endif }; static struct net_device * __devinit gelic_wl_alloc(struct gelic_card *card) -- cgit v1.2.3 From 20d29d7a916a47bf533b5709437fe735b6b5b79e Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Sat, 30 Jan 2010 12:24:26 +0000 Subject: net: macvtap driver In order to use macvlan with qemu and other tools that require a tap file descriptor, the macvtap driver adds a small backend with a character device with the same interface as the tun driver, with a minimum set of features. Macvtap interfaces are created in the same way as macvlan interfaces using ip link, but the netif is just used as a handle for configuration and accounting, while the data goes through the chardev. Each macvtap interface has its own character device, simplifying permission management significantly over the generic tun/tap driver. Cc: Patrick McHardy Cc: Stephen Hemminger Cc: David S. Miller" Cc: "Michael S. Tsirkin" Cc: Herbert Xu Cc: Or Gerlitz Cc: netdev@vger.kernel.org Cc: bridge@lists.linux-foundation.org Cc: linux-kernel@vger.kernel.org Signed-off-by: Arnd Bergmann Signed-off-by: David S. Miller --- drivers/net/Kconfig | 12 + drivers/net/Makefile | 1 + drivers/net/macvtap.c | 581 +++++++++++++++++++++++++++++++++++++++++++++ include/linux/if_macvlan.h | 1 + 4 files changed, 595 insertions(+) create mode 100644 drivers/net/macvtap.c (limited to 'drivers/net/Kconfig') diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig index cb0e534418e3..411e20703110 100644 --- a/drivers/net/Kconfig +++ b/drivers/net/Kconfig @@ -90,6 +90,18 @@ config MACVLAN To compile this driver as a module, choose M here: the module will be called macvlan. +config MACVTAP + tristate "MAC-VLAN based tap driver (EXPERIMENTAL)" + depends on MACVLAN + help + This adds a specialized tap character device driver that is based + on the MAC-VLAN network interface, called macvtap. A macvtap device + can be added in the same way as a macvlan device, using 'type + macvlan', and then be accessed through the tap user space interface. + + To compile this driver as a module, choose M here: the module + will be called macvtap. + config EQUALIZER tristate "EQL (serial line load balancing) support" ---help--- diff --git a/drivers/net/Makefile b/drivers/net/Makefile index 0b763cbe9b1f..95958032cd31 100644 --- a/drivers/net/Makefile +++ b/drivers/net/Makefile @@ -169,6 +169,7 @@ obj-$(CONFIG_XEN_NETDEV_FRONTEND) += xen-netfront.o obj-$(CONFIG_DUMMY) += dummy.o obj-$(CONFIG_IFB) += ifb.o obj-$(CONFIG_MACVLAN) += macvlan.o +obj-$(CONFIG_MACVTAP) += macvtap.o obj-$(CONFIG_DE600) += de600.o obj-$(CONFIG_DE620) += de620.o obj-$(CONFIG_LANCE) += lance.o diff --git a/drivers/net/macvtap.c b/drivers/net/macvtap.c new file mode 100644 index 000000000000..ad1f6ef89308 --- /dev/null +++ b/drivers/net/macvtap.c @@ -0,0 +1,581 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +/* + * A macvtap queue is the central object of this driver, it connects + * an open character device to a macvlan interface. There can be + * multiple queues on one interface, which map back to queues + * implemented in hardware on the underlying device. + * + * macvtap_proto is used to allocate queues through the sock allocation + * mechanism. + * + * TODO: multiqueue support is currently not implemented, even though + * macvtap is basically prepared for that. We will need to add this + * here as well as in virtio-net and qemu to get line rate on 10gbit + * adapters from a guest. + */ +struct macvtap_queue { + struct sock sk; + struct socket sock; + struct macvlan_dev *vlan; + struct file *file; +}; + +static struct proto macvtap_proto = { + .name = "macvtap", + .owner = THIS_MODULE, + .obj_size = sizeof (struct macvtap_queue), +}; + +/* + * Minor number matches netdev->ifindex, so need a potentially + * large value. This also makes it possible to split the + * tap functionality out again in the future by offering it + * from other drivers besides macvtap. As long as every device + * only has one tap, the interface numbers assure that the + * device nodes are unique. + */ +static unsigned int macvtap_major; +#define MACVTAP_NUM_DEVS 65536 +static struct class *macvtap_class; +static struct cdev macvtap_cdev; + +/* + * RCU usage: + * The macvtap_queue is referenced both from the chardev struct file + * and from the struct macvlan_dev using rcu_read_lock. + * + * We never actually update the contents of a macvtap_queue atomically + * with RCU but it is used for race-free destruction of a queue when + * either the file or the macvlan_dev goes away. Pointers back to + * the dev and the file are implicitly valid as long as the queue + * exists. + * + * The callbacks from macvlan are always done with rcu_read_lock held + * already, while in the file_operations, we get it ourselves. + * + * When destroying a queue, we remove the pointers from the file and + * from the dev and then synchronize_rcu to make sure no thread is + * still using the queue. There may still be references to the struct + * sock inside of the queue from outbound SKBs, but these never + * reference back to the file or the dev. The data structure is freed + * through __sk_free when both our references and any pending SKBs + * are gone. + * + * macvtap_lock is only used to prevent multiple concurrent open() + * calls to assign a new vlan->tap pointer. It could be moved into + * the macvlan_dev itself but is extremely rarely used. + */ +static DEFINE_SPINLOCK(macvtap_lock); + +/* + * Choose the next free queue, for now there is only one + */ +static int macvtap_set_queue(struct net_device *dev, struct file *file, + struct macvtap_queue *q) +{ + struct macvlan_dev *vlan = netdev_priv(dev); + int err = -EBUSY; + + spin_lock(&macvtap_lock); + if (rcu_dereference(vlan->tap)) + goto out; + + err = 0; + q->vlan = vlan; + rcu_assign_pointer(vlan->tap, q); + + q->file = file; + rcu_assign_pointer(file->private_data, q); + +out: + spin_unlock(&macvtap_lock); + return err; +} + +/* + * We must destroy each queue exactly once, when either + * the netdev or the file go away. + * + * Using the spinlock makes sure that we don't get + * to the queue again after destroying it. + * + * synchronize_rcu serializes with the packet flow + * that uses rcu_read_lock. + */ +static void macvtap_del_queue(struct macvtap_queue **qp) +{ + struct macvtap_queue *q; + + spin_lock(&macvtap_lock); + q = rcu_dereference(*qp); + if (!q) { + spin_unlock(&macvtap_lock); + return; + } + + rcu_assign_pointer(q->vlan->tap, NULL); + rcu_assign_pointer(q->file->private_data, NULL); + spin_unlock(&macvtap_lock); + + synchronize_rcu(); + sock_put(&q->sk); +} + +/* + * Since we only support one queue, just dereference the pointer. + */ +static struct macvtap_queue *macvtap_get_queue(struct net_device *dev, + struct sk_buff *skb) +{ + struct macvlan_dev *vlan = netdev_priv(dev); + + return rcu_dereference(vlan->tap); +} + +static void macvtap_del_queues(struct net_device *dev) +{ + struct macvlan_dev *vlan = netdev_priv(dev); + macvtap_del_queue(&vlan->tap); +} + +static inline struct macvtap_queue *macvtap_file_get_queue(struct file *file) +{ + rcu_read_lock_bh(); + return rcu_dereference(file->private_data); +} + +static inline void macvtap_file_put_queue(void) +{ + rcu_read_unlock_bh(); +} + +/* + * Forward happens for data that gets sent from one macvlan + * endpoint to another one in bridge mode. We just take + * the skb and put it into the receive queue. + */ +static int macvtap_forward(struct net_device *dev, struct sk_buff *skb) +{ + struct macvtap_queue *q = macvtap_get_queue(dev, skb); + if (!q) + return -ENOLINK; + + skb_queue_tail(&q->sk.sk_receive_queue, skb); + wake_up(q->sk.sk_sleep); + return 0; +} + +/* + * Receive is for data from the external interface (lowerdev), + * in case of macvtap, we can treat that the same way as + * forward, which macvlan cannot. + */ +static int macvtap_receive(struct sk_buff *skb) +{ + skb_push(skb, ETH_HLEN); + return macvtap_forward(skb->dev, skb); +} + +static int macvtap_newlink(struct net *src_net, + struct net_device *dev, + struct nlattr *tb[], + struct nlattr *data[]) +{ + struct device *classdev; + dev_t devt; + int err; + + err = macvlan_common_newlink(src_net, dev, tb, data, + macvtap_receive, macvtap_forward); + if (err) + goto out; + + devt = MKDEV(MAJOR(macvtap_major), dev->ifindex); + + classdev = device_create(macvtap_class, &dev->dev, devt, + dev, "tap%d", dev->ifindex); + if (IS_ERR(classdev)) { + err = PTR_ERR(classdev); + macvtap_del_queues(dev); + } + +out: + return err; +} + +static void macvtap_dellink(struct net_device *dev, + struct list_head *head) +{ + device_destroy(macvtap_class, + MKDEV(MAJOR(macvtap_major), dev->ifindex)); + + macvtap_del_queues(dev); + macvlan_dellink(dev, head); +} + +static struct rtnl_link_ops macvtap_link_ops __read_mostly = { + .kind = "macvtap", + .newlink = macvtap_newlink, + .dellink = macvtap_dellink, +}; + + +static void macvtap_sock_write_space(struct sock *sk) +{ + if (!sock_writeable(sk) || + !test_and_clear_bit(SOCK_ASYNC_NOSPACE, &sk->sk_socket->flags)) + return; + + if (sk->sk_sleep && waitqueue_active(sk->sk_sleep)) + wake_up_interruptible_sync(sk->sk_sleep); +} + +static int macvtap_open(struct inode *inode, struct file *file) +{ + struct net *net = current->nsproxy->net_ns; + struct net_device *dev = dev_get_by_index(net, iminor(inode)); + struct macvtap_queue *q; + int err; + + err = -ENODEV; + if (!dev) + goto out; + + /* check if this is a macvtap device */ + err = -EINVAL; + if (dev->rtnl_link_ops != &macvtap_link_ops) + goto out; + + err = -ENOMEM; + q = (struct macvtap_queue *)sk_alloc(net, AF_UNSPEC, GFP_KERNEL, + &macvtap_proto); + if (!q) + goto out; + + init_waitqueue_head(&q->sock.wait); + q->sock.type = SOCK_RAW; + q->sock.state = SS_CONNECTED; + sock_init_data(&q->sock, &q->sk); + q->sk.sk_allocation = GFP_ATOMIC; /* for now */ + q->sk.sk_write_space = macvtap_sock_write_space; + + err = macvtap_set_queue(dev, file, q); + if (err) + sock_put(&q->sk); + +out: + if (dev) + dev_put(dev); + + return err; +} + +static int macvtap_release(struct inode *inode, struct file *file) +{ + macvtap_del_queue((struct macvtap_queue **)&file->private_data); + return 0; +} + +static unsigned int macvtap_poll(struct file *file, poll_table * wait) +{ + struct macvtap_queue *q = macvtap_file_get_queue(file); + unsigned int mask = POLLERR; + + if (!q) + goto out; + + mask = 0; + poll_wait(file, &q->sock.wait, wait); + + if (!skb_queue_empty(&q->sk.sk_receive_queue)) + mask |= POLLIN | POLLRDNORM; + + if (sock_writeable(&q->sk) || + (!test_and_set_bit(SOCK_ASYNC_NOSPACE, &q->sock.flags) && + sock_writeable(&q->sk))) + mask |= POLLOUT | POLLWRNORM; + +out: + macvtap_file_put_queue(); + return mask; +} + +/* Get packet from user space buffer */ +static ssize_t macvtap_get_user(struct macvtap_queue *q, + const struct iovec *iv, size_t count, + int noblock) +{ + struct sk_buff *skb; + size_t len = count; + int err; + + if (unlikely(len < ETH_HLEN)) + return -EINVAL; + + skb = sock_alloc_send_skb(&q->sk, NET_IP_ALIGN + len, noblock, &err); + + if (!skb) { + macvlan_count_rx(q->vlan, 0, false, false); + return err; + } + + skb_reserve(skb, NET_IP_ALIGN); + skb_put(skb, count); + + if (skb_copy_datagram_from_iovec(skb, 0, iv, 0, len)) { + macvlan_count_rx(q->vlan, 0, false, false); + kfree_skb(skb); + return -EFAULT; + } + + skb_set_network_header(skb, ETH_HLEN); + + macvlan_start_xmit(skb, q->vlan->dev); + + return count; +} + +static ssize_t macvtap_aio_write(struct kiocb *iocb, const struct iovec *iv, + unsigned long count, loff_t pos) +{ + struct file *file = iocb->ki_filp; + ssize_t result = -ENOLINK; + struct macvtap_queue *q = macvtap_file_get_queue(file); + + if (!q) + goto out; + + result = macvtap_get_user(q, iv, iov_length(iv, count), + file->f_flags & O_NONBLOCK); +out: + macvtap_file_put_queue(); + return result; +} + +/* Put packet to the user space buffer */ +static ssize_t macvtap_put_user(struct macvtap_queue *q, + const struct sk_buff *skb, + const struct iovec *iv, int len) +{ + struct macvlan_dev *vlan = q->vlan; + int ret; + + len = min_t(int, skb->len, len); + + ret = skb_copy_datagram_const_iovec(skb, 0, iv, 0, len); + + macvlan_count_rx(vlan, len, ret == 0, 0); + + return ret ? ret : len; +} + +static ssize_t macvtap_aio_read(struct kiocb *iocb, const struct iovec *iv, + unsigned long count, loff_t pos) +{ + struct file *file = iocb->ki_filp; + struct macvtap_queue *q = macvtap_file_get_queue(file); + + DECLARE_WAITQUEUE(wait, current); + struct sk_buff *skb; + ssize_t len, ret = 0; + + if (!q) { + ret = -ENOLINK; + goto out; + } + + len = iov_length(iv, count); + if (len < 0) { + ret = -EINVAL; + goto out; + } + + add_wait_queue(q->sk.sk_sleep, &wait); + while (len) { + current->state = TASK_INTERRUPTIBLE; + + /* Read frames from the queue */ + skb = skb_dequeue(&q->sk.sk_receive_queue); + if (!skb) { + if (file->f_flags & O_NONBLOCK) { + ret = -EAGAIN; + break; + } + if (signal_pending(current)) { + ret = -ERESTARTSYS; + break; + } + /* Nothing to read, let's sleep */ + schedule(); + continue; + } + ret = macvtap_put_user(q, skb, iv, len); + kfree_skb(skb); + break; + } + + current->state = TASK_RUNNING; + remove_wait_queue(q->sk.sk_sleep, &wait); + +out: + macvtap_file_put_queue(); + return ret; +} + +/* + * provide compatibility with generic tun/tap interface + */ +static long macvtap_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) +{ + struct macvtap_queue *q; + void __user *argp = (void __user *)arg; + struct ifreq __user *ifr = argp; + unsigned int __user *up = argp; + unsigned int u; + char devname[IFNAMSIZ]; + + switch (cmd) { + case TUNSETIFF: + /* ignore the name, just look at flags */ + if (get_user(u, &ifr->ifr_flags)) + return -EFAULT; + if (u != (IFF_TAP | IFF_NO_PI)) + return -EINVAL; + return 0; + + case TUNGETIFF: + q = macvtap_file_get_queue(file); + if (!q) + return -ENOLINK; + memcpy(devname, q->vlan->dev->name, sizeof(devname)); + macvtap_file_put_queue(); + + if (copy_to_user(&ifr->ifr_name, q->vlan->dev->name, IFNAMSIZ) || + put_user((TUN_TAP_DEV | TUN_NO_PI), &ifr->ifr_flags)) + return -EFAULT; + return 0; + + case TUNGETFEATURES: + if (put_user((IFF_TAP | IFF_NO_PI), up)) + return -EFAULT; + return 0; + + case TUNSETSNDBUF: + if (get_user(u, up)) + return -EFAULT; + + q = macvtap_file_get_queue(file); + q->sk.sk_sndbuf = u; + macvtap_file_put_queue(); + return 0; + + case TUNSETOFFLOAD: + /* let the user check for future flags */ + if (arg & ~(TUN_F_CSUM | TUN_F_TSO4 | TUN_F_TSO6 | + TUN_F_TSO_ECN | TUN_F_UFO)) + return -EINVAL; + + /* TODO: add support for these, so far we don't + support any offload */ + if (arg & (TUN_F_CSUM | TUN_F_TSO4 | TUN_F_TSO6 | + TUN_F_TSO_ECN | TUN_F_UFO)) + return -EINVAL; + + return 0; + + default: + return -EINVAL; + } +} + +#ifdef CONFIG_COMPAT +static long macvtap_compat_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) +{ + return macvtap_ioctl(file, cmd, (unsigned long)compat_ptr(arg)); +} +#endif + +static const struct file_operations macvtap_fops = { + .owner = THIS_MODULE, + .open = macvtap_open, + .release = macvtap_release, + .aio_read = macvtap_aio_read, + .aio_write = macvtap_aio_write, + .poll = macvtap_poll, + .llseek = no_llseek, + .unlocked_ioctl = macvtap_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = macvtap_compat_ioctl, +#endif +}; + +static int macvtap_init(void) +{ + int err; + + err = alloc_chrdev_region(&macvtap_major, 0, + MACVTAP_NUM_DEVS, "macvtap"); + if (err) + goto out1; + + cdev_init(&macvtap_cdev, &macvtap_fops); + err = cdev_add(&macvtap_cdev, macvtap_major, MACVTAP_NUM_DEVS); + if (err) + goto out2; + + macvtap_class = class_create(THIS_MODULE, "macvtap"); + if (IS_ERR(macvtap_class)) { + err = PTR_ERR(macvtap_class); + goto out3; + } + + err = macvlan_link_register(&macvtap_link_ops); + if (err) + goto out4; + + return 0; + +out4: + class_unregister(macvtap_class); +out3: + cdev_del(&macvtap_cdev); +out2: + unregister_chrdev_region(macvtap_major, MACVTAP_NUM_DEVS); +out1: + return err; +} +module_init(macvtap_init); + +static void macvtap_exit(void) +{ + rtnl_link_unregister(&macvtap_link_ops); + class_unregister(macvtap_class); + cdev_del(&macvtap_cdev); + unregister_chrdev_region(macvtap_major, MACVTAP_NUM_DEVS); +} +module_exit(macvtap_exit); + +MODULE_ALIAS_RTNL_LINK("macvtap"); +MODULE_AUTHOR("Arnd Bergmann "); +MODULE_LICENSE("GPL"); diff --git a/include/linux/if_macvlan.h b/include/linux/if_macvlan.h index 9a11544bb0b1..51f1512045e9 100644 --- a/include/linux/if_macvlan.h +++ b/include/linux/if_macvlan.h @@ -34,6 +34,7 @@ struct macvlan_dev { enum macvlan_mode mode; int (*receive)(struct sk_buff *skb); int (*forward)(struct net_device *dev, struct sk_buff *skb); + struct macvtap_queue *tap; }; static inline void macvlan_count_rx(const struct macvlan_dev *vlan, -- cgit v1.2.3 From bd011e8826978b0f6cd0f929edec353fd755f49b Mon Sep 17 00:00:00 2001 From: Amit Kucheria Date: Fri, 5 Feb 2010 08:56:22 +0000 Subject: fec: Add ARCH_MX5 as a dependency i.MX51 babbage board has a FEC ethernet controller Signed-off-by: Amit Kucheria Signed-off-by: David S. Miller --- drivers/net/Kconfig | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers/net/Kconfig') diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig index ef662e15c3d3..1bf1987571ff 100644 --- a/drivers/net/Kconfig +++ b/drivers/net/Kconfig @@ -1895,7 +1895,8 @@ config 68360_ENET config FEC bool "FEC ethernet controller (of ColdFire and some i.MX CPUs)" - depends on M523x || M527x || M5272 || M528x || M520x || M532x || MACH_MX27 || ARCH_MX35 || ARCH_MX25 + depends on M523x || M527x || M5272 || M528x || M520x || M532x || \ + MACH_MX27 || ARCH_MX35 || ARCH_MX25 || ARCH_MX5 help Say Y here if you want to use the built-in 10/100 Fast ethernet controller on some Motorola ColdFire and Freescale i.MX processors. -- cgit v1.2.3 From 1eb1cc7a209f9593312ba014f1b1f02c31eda4fb Mon Sep 17 00:00:00 2001 From: Tristram Ha Date: Mon, 8 Feb 2010 11:38:28 +0000 Subject: net: Kconfig change for KSZ884X driver Add Micrel KSZ8841/KSZ8842 PCI Ethernet chip support. Signed-off-by: Tristram Ha Signed-off-by: David S. Miller --- drivers/net/Kconfig | 11 +++++++++++ 1 file changed, 11 insertions(+) (limited to 'drivers/net/Kconfig') diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig index 1bf1987571ff..5f646523c5ae 100644 --- a/drivers/net/Kconfig +++ b/drivers/net/Kconfig @@ -1380,6 +1380,17 @@ config AC3200 To compile this driver as a module, choose M here. The module will be called ac3200. +config KSZ884X_PCI + tristate "Micrel KSZ8841/2 PCI" + depends on NET_PCI && PCI + select MII + select CRC32 + help + This PCI driver is for Micrel KSZ8841/KSZ8842 PCI Ethernet chip. + + To compile this driver as a module, choose M here. The module + will be called ksz884x. + config APRICOT tristate "Apricot Xen-II on board Ethernet" depends on NET_PCI && ISA -- cgit v1.2.3 From 5cdaaa12866e916d0ada8b56c5f0e543cfc7fe3d Mon Sep 17 00:00:00 2001 From: John Linn Date: Mon, 15 Feb 2010 21:51:00 -0800 Subject: net: emaclite: adding MDIO and phy lib support These changes add MDIO and phy lib support to the driver as the IP core now supports the MDIO bus. The MDIO bus and phy are added as a child to the emaclite in the device tree as illustrated below. mdio { #address-cells = <1>; #size-cells = <0>; phy0: phy@7 { compatible = "marvell,88e1111"; reg = <7>; } ; } Signed-off-by: Sadanand Mutyala Signed-off-by: John Linn Acked-by: Grant Likely Signed-off-by: David S. Miller --- drivers/net/Kconfig | 1 + drivers/net/xilinx_emaclite.c | 378 +++++++++++++++++++++++++++++++++++++----- 2 files changed, 336 insertions(+), 43 deletions(-) (limited to 'drivers/net/Kconfig') diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig index 5f646523c5ae..b711d5416100 100644 --- a/drivers/net/Kconfig +++ b/drivers/net/Kconfig @@ -1963,6 +1963,7 @@ config ATL2 config XILINX_EMACLITE tristate "Xilinx 10/100 Ethernet Lite support" depends on PPC32 || MICROBLAZE + select PHYLIB help This driver supports the 10/100 Ethernet Lite from Xilinx. diff --git a/drivers/net/xilinx_emaclite.c b/drivers/net/xilinx_emaclite.c index f7fe1aa03b42..1a74594224b1 100644 --- a/drivers/net/xilinx_emaclite.c +++ b/drivers/net/xilinx_emaclite.c @@ -22,11 +22,17 @@ #include #include +#include +#include #define DRIVER_NAME "xilinx_emaclite" /* Register offsets for the EmacLite Core */ #define XEL_TXBUFF_OFFSET 0x0 /* Transmit Buffer */ +#define XEL_MDIOADDR_OFFSET 0x07E4 /* MDIO Address Register */ +#define XEL_MDIOWR_OFFSET 0x07E8 /* MDIO Write Data Register */ +#define XEL_MDIORD_OFFSET 0x07EC /* MDIO Read Data Register */ +#define XEL_MDIOCTRL_OFFSET 0x07F0 /* MDIO Control Register */ #define XEL_GIER_OFFSET 0x07F8 /* GIE Register */ #define XEL_TSR_OFFSET 0x07FC /* Tx status */ #define XEL_TPLR_OFFSET 0x07F4 /* Tx packet length */ @@ -37,6 +43,22 @@ #define XEL_BUFFER_OFFSET 0x0800 /* Next Tx/Rx buffer's offset */ +/* MDIO Address Register Bit Masks */ +#define XEL_MDIOADDR_REGADR_MASK 0x0000001F /* Register Address */ +#define XEL_MDIOADDR_PHYADR_MASK 0x000003E0 /* PHY Address */ +#define XEL_MDIOADDR_PHYADR_SHIFT 5 +#define XEL_MDIOADDR_OP_MASK 0x00000400 /* RD/WR Operation */ + +/* MDIO Write Data Register Bit Masks */ +#define XEL_MDIOWR_WRDATA_MASK 0x0000FFFF /* Data to be Written */ + +/* MDIO Read Data Register Bit Masks */ +#define XEL_MDIORD_RDDATA_MASK 0x0000FFFF /* Data to be Read */ + +/* MDIO Control Register Bit Masks */ +#define XEL_MDIOCTRL_MDIOSTS_MASK 0x00000001 /* MDIO Status Mask */ +#define XEL_MDIOCTRL_MDIOEN_MASK 0x00000008 /* MDIO Enable */ + /* Global Interrupt Enable Register (GIER) Bit Masks */ #define XEL_GIER_GIE_MASK 0x80000000 /* Global Enable */ @@ -87,6 +109,12 @@ * @reset_lock: lock used for synchronization * @deferred_skb: holds an skb (for transmission at a later time) when the * Tx buffer is not free + * @phy_dev: pointer to the PHY device + * @phy_node: pointer to the PHY device node + * @mii_bus: pointer to the MII bus + * @mdio_irqs: IRQs table for MDIO bus + * @last_link: last link status + * @has_mdio: indicates whether MDIO is included in the HW */ struct net_local { @@ -100,6 +128,15 @@ struct net_local { spinlock_t reset_lock; struct sk_buff *deferred_skb; + + struct phy_device *phy_dev; + struct device_node *phy_node; + + struct mii_bus *mii_bus; + int mdio_irqs[PHY_MAX_ADDR]; + + int last_link; + bool has_mdio; }; @@ -431,7 +468,7 @@ static u16 xemaclite_recv_data(struct net_local *drvdata, u8 *data) } /** - * xemaclite_set_mac_address - Set the MAC address for this device + * xemaclite_update_address - Update the MAC address in the device * @drvdata: Pointer to the Emaclite device private data * @address_ptr:Pointer to the MAC address (MAC address is a 48-bit value) * @@ -441,8 +478,8 @@ static u16 xemaclite_recv_data(struct net_local *drvdata, u8 *data) * The MAC address can be programmed using any of the two transmit * buffers (if configured). */ -static void xemaclite_set_mac_address(struct net_local *drvdata, - u8 *address_ptr) +static void xemaclite_update_address(struct net_local *drvdata, + u8 *address_ptr) { void __iomem *addr; u32 reg_data; @@ -464,6 +501,30 @@ static void xemaclite_set_mac_address(struct net_local *drvdata, ; } +/** + * xemaclite_set_mac_address - Set the MAC address for this device + * @dev: Pointer to the network device instance + * @addr: Void pointer to the sockaddr structure + * + * This function copies the HW address from the sockaddr strucutre to the + * net_device structure and updates the address in HW. + * + * Return: Error if the net device is busy or 0 if the addr is set + * successfully + */ +static int xemaclite_set_mac_address(struct net_device *dev, void *address) +{ + struct net_local *lp = (struct net_local *) netdev_priv(dev); + struct sockaddr *addr = address; + + if (netif_running(dev)) + return -EBUSY; + + memcpy(dev->dev_addr, addr->sa_data, dev->addr_len); + xemaclite_update_address(lp, dev->dev_addr); + return 0; +} + /** * xemaclite_tx_timeout - Callback for Tx Timeout * @dev: Pointer to the network device @@ -641,12 +702,219 @@ static irqreturn_t xemaclite_interrupt(int irq, void *dev_id) return IRQ_HANDLED; } +/**********************/ +/* MDIO Bus functions */ +/**********************/ + +/** + * xemaclite_mdio_wait - Wait for the MDIO to be ready to use + * @lp: Pointer to the Emaclite device private data + * + * This function waits till the device is ready to accept a new MDIO + * request. + * + * Return: 0 for success or ETIMEDOUT for a timeout + */ + +static int xemaclite_mdio_wait(struct net_local *lp) +{ + long end = jiffies + 2; + + /* wait for the MDIO interface to not be busy or timeout + after some time. + */ + while (in_be32(lp->base_addr + XEL_MDIOCTRL_OFFSET) & + XEL_MDIOCTRL_MDIOSTS_MASK) { + if (end - jiffies <= 0) { + WARN_ON(1); + return -ETIMEDOUT; + } + msleep(1); + } + return 0; +} + +/** + * xemaclite_mdio_read - Read from a given MII management register + * @bus: the mii_bus struct + * @phy_id: the phy address + * @reg: register number to read from + * + * This function waits till the device is ready to accept a new MDIO + * request and then writes the phy address to the MDIO Address register + * and reads data from MDIO Read Data register, when its available. + * + * Return: Value read from the MII management register + */ +static int xemaclite_mdio_read(struct mii_bus *bus, int phy_id, int reg) +{ + struct net_local *lp = bus->priv; + u32 ctrl_reg; + u32 rc; + + if (xemaclite_mdio_wait(lp)) + return -ETIMEDOUT; + + /* Write the PHY address, register number and set the OP bit in the + * MDIO Address register. Set the Status bit in the MDIO Control + * register to start a MDIO read transaction. + */ + ctrl_reg = in_be32(lp->base_addr + XEL_MDIOCTRL_OFFSET); + out_be32(lp->base_addr + XEL_MDIOADDR_OFFSET, + XEL_MDIOADDR_OP_MASK | + ((phy_id << XEL_MDIOADDR_PHYADR_SHIFT) | reg)); + out_be32(lp->base_addr + XEL_MDIOCTRL_OFFSET, + ctrl_reg | XEL_MDIOCTRL_MDIOSTS_MASK); + + if (xemaclite_mdio_wait(lp)) + return -ETIMEDOUT; + + rc = in_be32(lp->base_addr + XEL_MDIORD_OFFSET); + + dev_dbg(&lp->ndev->dev, + "xemaclite_mdio_read(phy_id=%i, reg=%x) == %x\n", + phy_id, reg, rc); + + return rc; +} + +/** + * xemaclite_mdio_write - Write to a given MII management register + * @bus: the mii_bus struct + * @phy_id: the phy address + * @reg: register number to write to + * @val: value to write to the register number specified by reg + * + * This fucntion waits till the device is ready to accept a new MDIO + * request and then writes the val to the MDIO Write Data register. + */ +static int xemaclite_mdio_write(struct mii_bus *bus, int phy_id, int reg, + u16 val) +{ + struct net_local *lp = bus->priv; + u32 ctrl_reg; + + dev_dbg(&lp->ndev->dev, + "xemaclite_mdio_write(phy_id=%i, reg=%x, val=%x)\n", + phy_id, reg, val); + + if (xemaclite_mdio_wait(lp)) + return -ETIMEDOUT; + + /* Write the PHY address, register number and clear the OP bit in the + * MDIO Address register and then write the value into the MDIO Write + * Data register. Finally, set the Status bit in the MDIO Control + * register to start a MDIO write transaction. + */ + ctrl_reg = in_be32(lp->base_addr + XEL_MDIOCTRL_OFFSET); + out_be32(lp->base_addr + XEL_MDIOADDR_OFFSET, + ~XEL_MDIOADDR_OP_MASK & + ((phy_id << XEL_MDIOADDR_PHYADR_SHIFT) | reg)); + out_be32(lp->base_addr + XEL_MDIOWR_OFFSET, val); + out_be32(lp->base_addr + XEL_MDIOCTRL_OFFSET, + ctrl_reg | XEL_MDIOCTRL_MDIOSTS_MASK); + + return 0; +} + +/** + * xemaclite_mdio_reset - Reset the mdio bus. + * @bus: Pointer to the MII bus + * + * This function is required(?) as per Documentation/networking/phy.txt. + * There is no reset in this device; this function always returns 0. + */ +static int xemaclite_mdio_reset(struct mii_bus *bus) +{ + return 0; +} + +/** + * xemaclite_mdio_setup - Register mii_bus for the Emaclite device + * @lp: Pointer to the Emaclite device private data + * @ofdev: Pointer to OF device structure + * + * This function enables MDIO bus in the Emaclite device and registers a + * mii_bus. + * + * Return: 0 upon success or a negative error upon failure + */ +static int xemaclite_mdio_setup(struct net_local *lp, struct device *dev) +{ + struct mii_bus *bus; + int rc; + struct resource res; + struct device_node *np = of_get_parent(lp->phy_node); + + /* Don't register the MDIO bus if the phy_node or its parent node + * can't be found. + */ + if (!np) + return -ENODEV; + + /* Enable the MDIO bus by asserting the enable bit in MDIO Control + * register. + */ + out_be32(lp->base_addr + XEL_MDIOCTRL_OFFSET, + XEL_MDIOCTRL_MDIOEN_MASK); + + bus = mdiobus_alloc(); + if (!bus) + return -ENOMEM; + + of_address_to_resource(np, 0, &res); + snprintf(bus->id, MII_BUS_ID_SIZE, "%.8llx", + (unsigned long long)res.start); + bus->priv = lp; + bus->name = "Xilinx Emaclite MDIO"; + bus->read = xemaclite_mdio_read; + bus->write = xemaclite_mdio_write; + bus->reset = xemaclite_mdio_reset; + bus->parent = dev; + bus->irq = lp->mdio_irqs; /* preallocated IRQ table */ + + lp->mii_bus = bus; + + rc = of_mdiobus_register(bus, np); + if (rc) + goto err_register; + + return 0; + +err_register: + mdiobus_free(bus); + return rc; +} + +/** + * xemaclite_adjust_link - Link state callback for the Emaclite device + * @ndev: pointer to net_device struct + * + * There's nothing in the Emaclite device to be configured when the link + * state changes. We just print the status. + */ +void xemaclite_adjust_link(struct net_device *ndev) +{ + struct net_local *lp = netdev_priv(ndev); + struct phy_device *phy = lp->phy_dev; + int link_state; + + /* hash together the state values to decide if something has changed */ + link_state = phy->speed | (phy->duplex << 1) | phy->link; + + if (lp->last_link != link_state) { + lp->last_link = link_state; + phy_print_status(phy); + } +} + /** * xemaclite_open - Open the network device * @dev: Pointer to the network device * * This function sets the MAC address, requests an IRQ and enables interrupts * for the Emaclite device and starts the Tx queue. + * It also connects to the phy device, if MDIO is included in Emaclite device. */ static int xemaclite_open(struct net_device *dev) { @@ -656,14 +924,47 @@ static int xemaclite_open(struct net_device *dev) /* Just to be safe, stop the device first */ xemaclite_disable_interrupts(lp); + if (lp->phy_node) { + u32 bmcr; + + lp->phy_dev = of_phy_connect(lp->ndev, lp->phy_node, + xemaclite_adjust_link, 0, + PHY_INTERFACE_MODE_MII); + if (!lp->phy_dev) { + dev_err(&lp->ndev->dev, "of_phy_connect() failed\n"); + return -ENODEV; + } + + /* EmacLite doesn't support giga-bit speeds */ + lp->phy_dev->supported &= (PHY_BASIC_FEATURES); + lp->phy_dev->advertising = lp->phy_dev->supported; + + /* Don't advertise 1000BASE-T Full/Half duplex speeds */ + phy_write(lp->phy_dev, MII_CTRL1000, 0); + + /* Advertise only 10 and 100mbps full/half duplex speeds */ + phy_write(lp->phy_dev, MII_ADVERTISE, ADVERTISE_ALL); + + /* Restart auto negotiation */ + bmcr = phy_read(lp->phy_dev, MII_BMCR); + bmcr |= (BMCR_ANENABLE | BMCR_ANRESTART); + phy_write(lp->phy_dev, MII_BMCR, bmcr); + + phy_start(lp->phy_dev); + } + /* Set the MAC address each time opened */ - xemaclite_set_mac_address(lp, dev->dev_addr); + xemaclite_update_address(lp, dev->dev_addr); /* Grab the IRQ */ retval = request_irq(dev->irq, xemaclite_interrupt, 0, dev->name, dev); if (retval) { dev_err(&lp->ndev->dev, "Could not allocate interrupt %d\n", dev->irq); + if (lp->phy_dev) + phy_disconnect(lp->phy_dev); + lp->phy_dev = NULL; + return retval; } @@ -682,6 +983,7 @@ static int xemaclite_open(struct net_device *dev) * * This function stops the Tx queue, disables interrupts and frees the IRQ for * the Emaclite device. + * It also disconnects the phy device associated with the Emaclite device. */ static int xemaclite_close(struct net_device *dev) { @@ -691,6 +993,10 @@ static int xemaclite_close(struct net_device *dev) xemaclite_disable_interrupts(lp); free_irq(dev->irq, dev); + if (lp->phy_dev) + phy_disconnect(lp->phy_dev); + lp->phy_dev = NULL; + return 0; } @@ -753,42 +1059,6 @@ static int xemaclite_send(struct sk_buff *orig_skb, struct net_device *dev) return 0; } -/** - * xemaclite_ioctl - Perform IO Control operations on the network device - * @dev: Pointer to the network device - * @rq: Pointer to the interface request structure - * @cmd: IOCTL command - * - * The only IOCTL operation supported by this function is setting the MAC - * address. An error is reported if any other operations are requested. - * - * Return: 0 to indicate success, or a negative error for failure. - */ -static int xemaclite_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) -{ - struct net_local *lp = (struct net_local *) netdev_priv(dev); - struct hw_addr_data *hw_addr = (struct hw_addr_data *) &rq->ifr_hwaddr; - - switch (cmd) { - case SIOCETHTOOL: - return -EIO; - - case SIOCSIFHWADDR: - dev_err(&lp->ndev->dev, "SIOCSIFHWADDR\n"); - - /* Copy MAC address in from user space */ - copy_from_user((void __force *) dev->dev_addr, - (void __user __force *) hw_addr, - IFHWADDRLEN); - xemaclite_set_mac_address(lp, dev->dev_addr); - break; - default: - return -EOPNOTSUPP; - } - - return 0; -} - /** * xemaclite_remove_ndev - Free the network device * @ndev: Pointer to the network device to be freed @@ -840,6 +1110,8 @@ static struct net_device_ops xemaclite_netdev_ops; * This function probes for the Emaclite device in the device tree. * It initializes the driver data structure and the hardware, sets the MAC * address and registers the network device. + * It also registers a mii_bus for the Emaclite device, if MDIO is included + * in the device. * * Return: 0, if the driver is bound to the Emaclite device, or * a negative error if there is failure. @@ -880,6 +1152,7 @@ static int __devinit xemaclite_of_probe(struct of_device *ofdev, } dev_set_drvdata(dev, ndev); + SET_NETDEV_DEV(ndev, &ofdev->dev); ndev->irq = r_irq.start; ndev->mem_start = r_mem.start; @@ -923,7 +1196,12 @@ static int __devinit xemaclite_of_probe(struct of_device *ofdev, out_be32(lp->base_addr + XEL_BUFFER_OFFSET + XEL_TSR_OFFSET, 0); /* Set the MAC address in the EmacLite device */ - xemaclite_set_mac_address(lp, ndev->dev_addr); + xemaclite_update_address(lp, ndev->dev_addr); + + lp->phy_node = of_parse_phandle(ofdev->node, "phy-handle", 0); + rc = xemaclite_mdio_setup(lp, &ofdev->dev); + if (rc) + dev_warn(&ofdev->dev, "error registering MDIO bus\n"); dev_info(dev, "MAC address is now %pM\n", ndev->dev_addr); @@ -968,12 +1246,25 @@ static int __devexit xemaclite_of_remove(struct of_device *of_dev) struct device *dev = &of_dev->dev; struct net_device *ndev = dev_get_drvdata(dev); + struct net_local *lp = (struct net_local *) netdev_priv(ndev); + + /* Un-register the mii_bus, if configured */ + if (lp->has_mdio) { + mdiobus_unregister(lp->mii_bus); + kfree(lp->mii_bus->irq); + mdiobus_free(lp->mii_bus); + lp->mii_bus = NULL; + } + unregister_netdev(ndev); + if (lp->phy_node) + of_node_put(lp->phy_node); + lp->phy_node = NULL; + release_mem_region(ndev->mem_start, ndev->mem_end-ndev->mem_start + 1); xemaclite_remove_ndev(ndev); - dev_set_drvdata(dev, NULL); return 0; @@ -983,7 +1274,7 @@ static struct net_device_ops xemaclite_netdev_ops = { .ndo_open = xemaclite_open, .ndo_stop = xemaclite_close, .ndo_start_xmit = xemaclite_send, - .ndo_do_ioctl = xemaclite_ioctl, + .ndo_set_mac_address = xemaclite_set_mac_address, .ndo_tx_timeout = xemaclite_tx_timeout, .ndo_get_stats = xemaclite_get_stats, }; @@ -995,6 +1286,7 @@ static struct of_device_id xemaclite_of_match[] __devinitdata = { { .compatible = "xlnx,xps-ethernetlite-1.00.a", }, { .compatible = "xlnx,xps-ethernetlite-2.00.a", }, { .compatible = "xlnx,xps-ethernetlite-2.01.a", }, + { .compatible = "xlnx,xps-ethernetlite-3.00.a", }, { /* end of list */ }, }; MODULE_DEVICE_TABLE(of, xemaclite_of_match); -- cgit v1.2.3 From e06d41d09f314a1664ad60e46934b0ac131c6fa2 Mon Sep 17 00:00:00 2001 From: Mike Frysinger Date: Mon, 15 Feb 2010 14:32:50 +0000 Subject: netdev: bfin_mac: drop experimental markings on RMII support The code has been around for a long time now and is known to work on a bunch of different parts/boards. Signed-off-by: Mike Frysinger Signed-off-by: David S. Miller --- drivers/net/Kconfig | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/net/Kconfig') diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig index b711d5416100..069057796bd7 100644 --- a/drivers/net/Kconfig +++ b/drivers/net/Kconfig @@ -880,8 +880,8 @@ config BFIN_RX_DESC_NUM Set the number of buffer packets used in driver. config BFIN_MAC_RMII - bool "RMII PHY Interface (EXPERIMENTAL)" - depends on BFIN_MAC && EXPERIMENTAL + bool "RMII PHY Interface" + depends on BFIN_MAC default y if BFIN527_EZKIT default n if BFIN537_STAMP help -- cgit v1.2.3 From d4c41139df6e74c6fff0cbac43e51cab782133be Mon Sep 17 00:00:00 2001 From: Kristoffer Glembo Date: Mon, 15 Feb 2010 03:33:44 +0000 Subject: net: Add Aeroflex Gaisler 10/100/1G Ethernet MAC driver Adds device driver for Aeroflex Gaisler 10/100 and 10/100/1G Ethernet MAC IP cores. Signed-off-by: Kristoffer Glembo Signed-off-by: David S. Miller --- MAINTAINERS | 6 + drivers/net/Kconfig | 8 + drivers/net/Makefile | 1 + drivers/net/greth.c | 1645 ++++++++++++++++++++++++++++++++++++++++++++++++++ drivers/net/greth.h | 143 +++++ 5 files changed, 1803 insertions(+) create mode 100644 drivers/net/greth.c create mode 100644 drivers/net/greth.h (limited to 'drivers/net/Kconfig') diff --git a/MAINTAINERS b/MAINTAINERS index 64a237bb01ab..32f6915ae0eb 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2373,6 +2373,12 @@ F: Documentation/isdn/README.gigaset F: drivers/isdn/gigaset/ F: include/linux/gigaset_dev.h +GRETH 10/100/1G Ethernet MAC device driver +M: Kristoffer Glembo +L: netdev@vger.kernel.org +S: Maintained +F: drivers/net/greth* + HARD DRIVE ACTIVE PROTECTION SYSTEM (HDAPS) DRIVER M: Frank Seidel L: lm-sensors@lm-sensors.org diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig index 069057796bd7..17ff15f6099a 100644 --- a/drivers/net/Kconfig +++ b/drivers/net/Kconfig @@ -995,6 +995,14 @@ config ETHOC help Say Y here if you want to use the OpenCores 10/100 Mbps Ethernet MAC. +config GRETH + tristate "Aeroflex Gaisler GRETH Ethernet MAC support" + depends on OF + select PHYLIB + select CRC32 + help + Say Y here if you want to use the Aeroflex Gaisler GRETH Ethernet MAC. + config SMC911X tristate "SMSC LAN911[5678] support" select CRC32 diff --git a/drivers/net/Makefile b/drivers/net/Makefile index 622cfd450d48..478886234c28 100644 --- a/drivers/net/Makefile +++ b/drivers/net/Makefile @@ -250,6 +250,7 @@ pasemi_mac_driver-objs := pasemi_mac.o pasemi_mac_ethtool.o obj-$(CONFIG_MLX4_CORE) += mlx4/ obj-$(CONFIG_ENC28J60) += enc28j60.o obj-$(CONFIG_ETHOC) += ethoc.o +obj-$(CONFIG_GRETH) += greth.o obj-$(CONFIG_XTENSA_XT2000_SONIC) += xtsonic.o diff --git a/drivers/net/greth.c b/drivers/net/greth.c new file mode 100644 index 000000000000..457da1c2383c --- /dev/null +++ b/drivers/net/greth.c @@ -0,0 +1,1645 @@ +/* + * Aeroflex Gaisler GRETH 10/100/1G Ethernet MAC. + * + * 2005-2009 (c) Aeroflex Gaisler AB + * + * This driver supports GRETH 10/100 and GRETH 10/100/1G Ethernet MACs + * available in the GRLIB VHDL IP core library. + * + * Full documentation of both cores can be found here: + * http://www.gaisler.com/products/grlib/grip.pdf + * + * The Gigabit version supports scatter/gather DMA, any alignment of + * buffers and checksum offloading. + * + * 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. + * + * Contributors: Kristoffer Glembo + * Daniel Hellstrom + * Marko Isomaki + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef CONFIG_SPARC +#include +#endif + +#include "greth.h" + +#define GRETH_DEF_MSG_ENABLE \ + (NETIF_MSG_DRV | \ + NETIF_MSG_PROBE | \ + NETIF_MSG_LINK | \ + NETIF_MSG_IFDOWN | \ + NETIF_MSG_IFUP | \ + NETIF_MSG_RX_ERR | \ + NETIF_MSG_TX_ERR) + +static int greth_debug = -1; /* -1 == use GRETH_DEF_MSG_ENABLE as value */ +module_param(greth_debug, int, 0); +MODULE_PARM_DESC(greth_debug, "GRETH bitmapped debugging message enable value"); + +/* Accept MAC address of the form macaddr=0x08,0x00,0x20,0x30,0x40,0x50 */ +static int macaddr[6]; +module_param_array(macaddr, int, NULL, 0); +MODULE_PARM_DESC(macaddr, "GRETH Ethernet MAC address"); + +static int greth_edcl = 1; +module_param(greth_edcl, int, 0); +MODULE_PARM_DESC(greth_edcl, "GRETH EDCL usage indicator. Set to 1 if EDCL is used."); + +static int greth_open(struct net_device *dev); +static int greth_start_xmit(struct sk_buff *skb, struct net_device *dev); +static int greth_start_xmit_gbit(struct sk_buff *skb, struct net_device *dev); +static int greth_rx(struct net_device *dev, int limit); +static int greth_rx_gbit(struct net_device *dev, int limit); +static void greth_clean_tx(struct net_device *dev); +static void greth_clean_tx_gbit(struct net_device *dev); +static irqreturn_t greth_interrupt(int irq, void *dev_id); +static int greth_close(struct net_device *dev); +static int greth_set_mac_add(struct net_device *dev, void *p); +static void greth_set_multicast_list(struct net_device *dev); + +#define GRETH_REGLOAD(a) (be32_to_cpu(__raw_readl(&(a)))) +#define GRETH_REGSAVE(a, v) (__raw_writel(cpu_to_be32(v), &(a))) +#define GRETH_REGORIN(a, v) (GRETH_REGSAVE(a, (GRETH_REGLOAD(a) | (v)))) +#define GRETH_REGANDIN(a, v) (GRETH_REGSAVE(a, (GRETH_REGLOAD(a) & (v)))) + +#define NEXT_TX(N) (((N) + 1) & GRETH_TXBD_NUM_MASK) +#define SKIP_TX(N, C) (((N) + C) & GRETH_TXBD_NUM_MASK) +#define NEXT_RX(N) (((N) + 1) & GRETH_RXBD_NUM_MASK) + +static void greth_print_rx_packet(void *addr, int len) +{ + print_hex_dump(KERN_DEBUG, "RX: ", DUMP_PREFIX_OFFSET, 16, 1, + addr, len, true); +} + +static void greth_print_tx_packet(struct sk_buff *skb) +{ + int i; + int length; + + if (skb_shinfo(skb)->nr_frags == 0) + length = skb->len; + else + length = skb_headlen(skb); + + print_hex_dump(KERN_DEBUG, "TX: ", DUMP_PREFIX_OFFSET, 16, 1, + skb->data, length, true); + + for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) { + + print_hex_dump(KERN_DEBUG, "TX: ", DUMP_PREFIX_OFFSET, 16, 1, + phys_to_virt(page_to_phys(skb_shinfo(skb)->frags[i].page)) + + skb_shinfo(skb)->frags[i].page_offset, + length, true); + } +} + +static inline void greth_enable_tx(struct greth_private *greth) +{ + wmb(); + GRETH_REGORIN(greth->regs->control, GRETH_TXEN); +} + +static inline void greth_disable_tx(struct greth_private *greth) +{ + GRETH_REGANDIN(greth->regs->control, ~GRETH_TXEN); +} + +static inline void greth_enable_rx(struct greth_private *greth) +{ + wmb(); + GRETH_REGORIN(greth->regs->control, GRETH_RXEN); +} + +static inline void greth_disable_rx(struct greth_private *greth) +{ + GRETH_REGANDIN(greth->regs->control, ~GRETH_RXEN); +} + +static inline void greth_enable_irqs(struct greth_private *greth) +{ + GRETH_REGORIN(greth->regs->control, GRETH_RXI | GRETH_TXI); +} + +static inline void greth_disable_irqs(struct greth_private *greth) +{ + GRETH_REGANDIN(greth->regs->control, ~(GRETH_RXI|GRETH_TXI)); +} + +static inline void greth_write_bd(u32 *bd, u32 val) +{ + __raw_writel(cpu_to_be32(val), bd); +} + +static inline u32 greth_read_bd(u32 *bd) +{ + return be32_to_cpu(__raw_readl(bd)); +} + +static void greth_clean_rings(struct greth_private *greth) +{ + int i; + struct greth_bd *rx_bdp = greth->rx_bd_base; + struct greth_bd *tx_bdp = greth->tx_bd_base; + + if (greth->gbit_mac) { + + /* Free and unmap RX buffers */ + for (i = 0; i < GRETH_RXBD_NUM; i++, rx_bdp++) { + if (greth->rx_skbuff[i] != NULL) { + dev_kfree_skb(greth->rx_skbuff[i]); + dma_unmap_single(greth->dev, + greth_read_bd(&rx_bdp->addr), + MAX_FRAME_SIZE+NET_IP_ALIGN, + DMA_FROM_DEVICE); + } + } + + /* TX buffers */ + while (greth->tx_free < GRETH_TXBD_NUM) { + + struct sk_buff *skb = greth->tx_skbuff[greth->tx_last]; + int nr_frags = skb_shinfo(skb)->nr_frags; + tx_bdp = greth->tx_bd_base + greth->tx_last; + greth->tx_last = NEXT_TX(greth->tx_last); + + dma_unmap_single(greth->dev, + greth_read_bd(&tx_bdp->addr), + skb_headlen(skb), + DMA_TO_DEVICE); + + for (i = 0; i < nr_frags; i++) { + skb_frag_t *frag = &skb_shinfo(skb)->frags[i]; + tx_bdp = greth->tx_bd_base + greth->tx_last; + + dma_unmap_page(greth->dev, + greth_read_bd(&tx_bdp->addr), + frag->size, + DMA_TO_DEVICE); + + greth->tx_last = NEXT_TX(greth->tx_last); + } + greth->tx_free += nr_frags+1; + dev_kfree_skb(skb); + } + + + } else { /* 10/100 Mbps MAC */ + + for (i = 0; i < GRETH_RXBD_NUM; i++, rx_bdp++) { + kfree(greth->rx_bufs[i]); + dma_unmap_single(greth->dev, + greth_read_bd(&rx_bdp->addr), + MAX_FRAME_SIZE, + DMA_FROM_DEVICE); + } + for (i = 0; i < GRETH_TXBD_NUM; i++, tx_bdp++) { + kfree(greth->tx_bufs[i]); + dma_unmap_single(greth->dev, + greth_read_bd(&tx_bdp->addr), + MAX_FRAME_SIZE, + DMA_TO_DEVICE); + } + } +} + +static int greth_init_rings(struct greth_private *greth) +{ + struct sk_buff *skb; + struct greth_bd *rx_bd, *tx_bd; + u32 dma_addr; + int i; + + rx_bd = greth->rx_bd_base; + tx_bd = greth->tx_bd_base; + + /* Initialize descriptor rings and buffers */ + if (greth->gbit_mac) { + + for (i = 0; i < GRETH_RXBD_NUM; i++) { + skb = netdev_alloc_skb(greth->netdev, MAX_FRAME_SIZE+NET_IP_ALIGN); + if (skb == NULL) { + if (netif_msg_ifup(greth)) + dev_err(greth->dev, "Error allocating DMA ring.\n"); + goto cleanup; + } + skb_reserve(skb, NET_IP_ALIGN); + dma_addr = dma_map_single(greth->dev, + skb->data, + MAX_FRAME_SIZE+NET_IP_ALIGN, + DMA_FROM_DEVICE); + + if (dma_mapping_error(greth->dev, dma_addr)) { + if (netif_msg_ifup(greth)) + dev_err(greth->dev, "Could not create initial DMA mapping\n"); + goto cleanup; + } + greth->rx_skbuff[i] = skb; + greth_write_bd(&rx_bd[i].addr, dma_addr); + greth_write_bd(&rx_bd[i].stat, GRETH_BD_EN | GRETH_BD_IE); + } + + } else { + + /* 10/100 MAC uses a fixed set of buffers and copy to/from SKBs */ + for (i = 0; i < GRETH_RXBD_NUM; i++) { + + greth->rx_bufs[i] = kmalloc(MAX_FRAME_SIZE, GFP_KERNEL); + + if (greth->rx_bufs[i] == NULL) { + if (netif_msg_ifup(greth)) + dev_err(greth->dev, "Error allocating DMA ring.\n"); + goto cleanup; + } + + dma_addr = dma_map_single(greth->dev, + greth->rx_bufs[i], + MAX_FRAME_SIZE, + DMA_FROM_DEVICE); + + if (dma_mapping_error(greth->dev, dma_addr)) { + if (netif_msg_ifup(greth)) + dev_err(greth->dev, "Could not create initial DMA mapping\n"); + goto cleanup; + } + greth_write_bd(&rx_bd[i].addr, dma_addr); + greth_write_bd(&rx_bd[i].stat, GRETH_BD_EN | GRETH_BD_IE); + } + for (i = 0; i < GRETH_TXBD_NUM; i++) { + + greth->tx_bufs[i] = kmalloc(MAX_FRAME_SIZE, GFP_KERNEL); + + if (greth->tx_bufs[i] == NULL) { + if (netif_msg_ifup(greth)) + dev_err(greth->dev, "Error allocating DMA ring.\n"); + goto cleanup; + } + + dma_addr = dma_map_single(greth->dev, + greth->tx_bufs[i], + MAX_FRAME_SIZE, + DMA_TO_DEVICE); + + if (dma_mapping_error(greth->dev, dma_addr)) { + if (netif_msg_ifup(greth)) + dev_err(greth->dev, "Could not create initial DMA mapping\n"); + goto cleanup; + } + greth_write_bd(&tx_bd[i].addr, dma_addr); + greth_write_bd(&tx_bd[i].stat, 0); + } + } + greth_write_bd(&rx_bd[GRETH_RXBD_NUM - 1].stat, + greth_read_bd(&rx_bd[GRETH_RXBD_NUM - 1].stat) | GRETH_BD_WR); + + /* Initialize pointers. */ + greth->rx_cur = 0; + greth->tx_next = 0; + greth->tx_last = 0; + greth->tx_free = GRETH_TXBD_NUM; + + /* Initialize descriptor base address */ + GRETH_REGSAVE(greth->regs->tx_desc_p, greth->tx_bd_base_phys); + GRETH_REGSAVE(greth->regs->rx_desc_p, greth->rx_bd_base_phys); + + return 0; + +cleanup: + greth_clean_rings(greth); + return -ENOMEM; +} + +static int greth_open(struct net_device *dev) +{ + struct greth_private *greth = netdev_priv(dev); + int err; + + err = greth_init_rings(greth); + if (err) { + if (netif_msg_ifup(greth)) + dev_err(&dev->dev, "Could not allocate memory for DMA rings\n"); + return err; + } + + err = request_irq(greth->irq, greth_interrupt, 0, "eth", (void *) dev); + if (err) { + if (netif_msg_ifup(greth)) + dev_err(&dev->dev, "Could not allocate interrupt %d\n", dev->irq); + greth_clean_rings(greth); + return err; + } + + if (netif_msg_ifup(greth)) + dev_dbg(&dev->dev, " starting queue\n"); + netif_start_queue(dev); + + napi_enable(&greth->napi); + + greth_enable_irqs(greth); + greth_enable_tx(greth); + greth_enable_rx(greth); + return 0; + +} + +static int greth_close(struct net_device *dev) +{ + struct greth_private *greth = netdev_priv(dev); + + napi_disable(&greth->napi); + + greth_disable_tx(greth); + + netif_stop_queue(dev); + + free_irq(greth->irq, (void *) dev); + + greth_clean_rings(greth); + + return 0; +} + +static int greth_start_xmit(struct sk_buff *skb, struct net_device *dev) +{ + struct greth_private *greth = netdev_priv(dev); + struct greth_bd *bdp; + int err = NETDEV_TX_OK; + u32 status, dma_addr; + + bdp = greth->tx_bd_base + greth->tx_next; + + if (unlikely(greth->tx_free <= 0)) { + netif_stop_queue(dev); + return NETDEV_TX_BUSY; + } + + if (netif_msg_pktdata(greth)) + greth_print_tx_packet(skb); + + + if (unlikely(skb->len > MAX_FRAME_SIZE)) { + dev->stats.tx_errors++; + goto out; + } + + dma_addr = greth_read_bd(&bdp->addr); + + memcpy((unsigned char *) phys_to_virt(dma_addr), skb->data, skb->len); + + dma_sync_single_for_device(greth->dev, dma_addr, skb->len, DMA_TO_DEVICE); + + status = GRETH_BD_EN | (skb->len & GRETH_BD_LEN); + + /* Wrap around descriptor ring */ + if (greth->tx_next == GRETH_TXBD_NUM_MASK) { + status |= GRETH_BD_WR; + } + + greth->tx_next = NEXT_TX(greth->tx_next); + greth->tx_free--; + + /* No more descriptors */ + if (unlikely(greth->tx_free == 0)) { + + /* Free transmitted descriptors */ + greth_clean_tx(dev); + + /* If nothing was cleaned, stop queue & wait for irq */ + if (unlikely(greth->tx_free == 0)) { + status |= GRETH_BD_IE; + netif_stop_queue(dev); + } + } + + /* Write descriptor control word and enable transmission */ + greth_write_bd(&bdp->stat, status); + greth_enable_tx(greth); + +out: + dev_kfree_skb(skb); + return err; +} + + +static int greth_start_xmit_gbit(struct sk_buff *skb, struct net_device *dev) +{ + struct greth_private *greth = netdev_priv(dev); + struct greth_bd *bdp; + u32 status = 0, dma_addr; + int curr_tx, nr_frags, i, err = NETDEV_TX_OK; + + nr_frags = skb_shinfo(skb)->nr_frags; + + if (greth->tx_free < nr_frags + 1) { + netif_stop_queue(dev); + err = NETDEV_TX_BUSY; + goto out; + } + + if (netif_msg_pktdata(greth)) + greth_print_tx_packet(skb); + + if (unlikely(skb->len > MAX_FRAME_SIZE)) { + dev->stats.tx_errors++; + goto out; + } + + /* Save skb pointer. */ + greth->tx_skbuff[greth->tx_next] = skb; + + /* Linear buf */ + if (nr_frags != 0) + status = GRETH_TXBD_MORE; + + status |= GRETH_TXBD_CSALL; + status |= skb_headlen(skb) & GRETH_BD_LEN; + if (greth->tx_next == GRETH_TXBD_NUM_MASK) + status |= GRETH_BD_WR; + + + bdp = greth->tx_bd_base + greth->tx_next; + greth_write_bd(&bdp->stat, status); + dma_addr = dma_map_single(greth->dev, skb->data, skb_headlen(skb), DMA_TO_DEVICE); + + if (unlikely(dma_mapping_error(greth->dev, dma_addr))) + goto map_error; + + greth_write_bd(&bdp->addr, dma_addr); + + curr_tx = NEXT_TX(greth->tx_next); + + /* Frags */ + for (i = 0; i < nr_frags; i++) { + skb_frag_t *frag = &skb_shinfo(skb)->frags[i]; + greth->tx_skbuff[curr_tx] = NULL; + bdp = greth->tx_bd_base + curr_tx; + + status = GRETH_TXBD_CSALL; + status |= frag->size & GRETH_BD_LEN; + + /* Wrap around descriptor ring */ + if (curr_tx == GRETH_TXBD_NUM_MASK) + status |= GRETH_BD_WR; + + /* More fragments left */ + if (i < nr_frags - 1) + status |= GRETH_TXBD_MORE; + + /* ... last fragment, check if out of descriptors */ + else if (greth->tx_free - nr_frags - 1 < (MAX_SKB_FRAGS + 1)) { + + /* Enable interrupts and stop queue */ + status |= GRETH_BD_IE; + netif_stop_queue(dev); + } + + greth_write_bd(&bdp->stat, status); + + dma_addr = dma_map_page(greth->dev, + frag->page, + frag->page_offset, + frag->size, + DMA_TO_DEVICE); + + if (unlikely(dma_mapping_error(greth->dev, dma_addr))) + goto frag_map_error; + + greth_write_bd(&bdp->addr, dma_addr); + + curr_tx = NEXT_TX(curr_tx); + } + + wmb(); + + /* Enable the descriptors that we configured ... */ + for (i = 0; i < nr_frags + 1; i++) { + bdp = greth->tx_bd_base + greth->tx_next; + greth_write_bd(&bdp->stat, greth_read_bd(&bdp->stat) | GRETH_BD_EN); + greth->tx_next = NEXT_TX(greth->tx_next); + greth->tx_free--; + } + + greth_enable_tx(greth); + + return NETDEV_TX_OK; + +frag_map_error: + /* Unmap SKB mappings that succeeded */ + for (i = 0; greth->tx_next + i != curr_tx; i++) { + bdp = greth->tx_bd_base + greth->tx_next + i; + dma_unmap_single(greth->dev, + greth_read_bd(&bdp->addr), + greth_read_bd(&bdp->stat) & GRETH_BD_LEN, + DMA_TO_DEVICE); + } +map_error: + if (net_ratelimit()) + dev_warn(greth->dev, "Could not create TX DMA mapping\n"); + dev_kfree_skb(skb); + return NETDEV_TX_OK; + +out: + return err; +} + + +static irqreturn_t greth_interrupt(int irq, void *dev_id) +{ + struct net_device *dev = dev_id; + struct greth_private *greth; + u32 status; + irqreturn_t retval = IRQ_NONE; + + greth = netdev_priv(dev); + + spin_lock(&greth->devlock); + + /* Get the interrupt events that caused us to be here. */ + status = GRETH_REGLOAD(greth->regs->status); + + /* Handle rx and tx interrupts through poll */ + if (status & (GRETH_INT_RX | GRETH_INT_TX)) { + + /* Clear interrupt status */ + GRETH_REGORIN(greth->regs->status, + status & (GRETH_INT_RX | GRETH_INT_TX)); + + retval = IRQ_HANDLED; + + /* Disable interrupts and schedule poll() */ + greth_disable_irqs(greth); + napi_schedule(&greth->napi); + } + + mmiowb(); + spin_unlock(&greth->devlock); + + return retval; +} + +static void greth_clean_tx(struct net_device *dev) +{ + struct greth_private *greth; + struct greth_bd *bdp; + u32 stat; + + greth = netdev_priv(dev); + + while (1) { + bdp = greth->tx_bd_base + greth->tx_last; + stat = greth_read_bd(&bdp->stat); + + if (unlikely(stat & GRETH_BD_EN)) + break; + + if (greth->tx_free == GRETH_TXBD_NUM) + break; + + /* Check status for errors */ + if (unlikely(stat & GRETH_TXBD_STATUS)) { + dev->stats.tx_errors++; + if (stat & GRETH_TXBD_ERR_AL) + dev->stats.tx_aborted_errors++; + if (stat & GRETH_TXBD_ERR_UE) + dev->stats.tx_fifo_errors++; + } + dev->stats.tx_packets++; + greth->tx_last = NEXT_TX(greth->tx_last); + greth->tx_free++; + } + + if (greth->tx_free > 0) { + netif_wake_queue(dev); + } + +} + +static inline void greth_update_tx_stats(struct net_device *dev, u32 stat) +{ + /* Check status for errors */ + if (unlikely(stat & GRETH_TXBD_STATUS)) { + dev->stats.tx_errors++; + if (stat & GRETH_TXBD_ERR_AL) + dev->stats.tx_aborted_errors++; + if (stat & GRETH_TXBD_ERR_UE) + dev->stats.tx_fifo_errors++; + if (stat & GRETH_TXBD_ERR_LC) + dev->stats.tx_aborted_errors++; + } + dev->stats.tx_packets++; +} + +static void greth_clean_tx_gbit(struct net_device *dev) +{ + struct greth_private *greth; + struct greth_bd *bdp, *bdp_last_frag; + struct sk_buff *skb; + u32 stat; + int nr_frags, i; + + greth = netdev_priv(dev); + + while (greth->tx_free < GRETH_TXBD_NUM) { + + skb = greth->tx_skbuff[greth->tx_last]; + + nr_frags = skb_shinfo(skb)->nr_frags; + + /* We only clean fully completed SKBs */ + bdp_last_frag = greth->tx_bd_base + SKIP_TX(greth->tx_last, nr_frags); + stat = bdp_last_frag->stat; + + if (stat & GRETH_BD_EN) + break; + + greth->tx_skbuff[greth->tx_last] = NULL; + + greth_update_tx_stats(dev, stat); + + bdp = greth->tx_bd_base + greth->tx_last; + + greth->tx_last = NEXT_TX(greth->tx_last); + + dma_unmap_single(greth->dev, + greth_read_bd(&bdp->addr), + skb_headlen(skb), + DMA_TO_DEVICE); + + for (i = 0; i < nr_frags; i++) { + skb_frag_t *frag = &skb_shinfo(skb)->frags[i]; + bdp = greth->tx_bd_base + greth->tx_last; + + dma_unmap_page(greth->dev, + greth_read_bd(&bdp->addr), + frag->size, + DMA_TO_DEVICE); + + greth->tx_last = NEXT_TX(greth->tx_last); + } + greth->tx_free += nr_frags+1; + dev_kfree_skb(skb); + } + if (greth->tx_free > (MAX_SKB_FRAGS + 1)) { + netif_wake_queue(dev); + } +} + +static int greth_pending_packets(struct greth_private *greth) +{ + struct greth_bd *bdp; + u32 status; + bdp = greth->rx_bd_base + greth->rx_cur; + status = greth_read_bd(&bdp->stat); + if (status & GRETH_BD_EN) + return 0; + else + return 1; +} + +static int greth_rx(struct net_device *dev, int limit) +{ + struct greth_private *greth; + struct greth_bd *bdp; + struct sk_buff *skb; + int pkt_len; + int bad, count; + u32 status, dma_addr; + + greth = netdev_priv(dev); + + for (count = 0; count < limit; ++count) { + + bdp = greth->rx_bd_base + greth->rx_cur; + status = greth_read_bd(&bdp->stat); + dma_addr = greth_read_bd(&bdp->addr); + bad = 0; + + if (unlikely(status & GRETH_BD_EN)) { + break; + } + + /* Check status for errors. */ + if (unlikely(status & GRETH_RXBD_STATUS)) { + if (status & GRETH_RXBD_ERR_FT) { + dev->stats.rx_length_errors++; + bad = 1; + } + if (status & (GRETH_RXBD_ERR_AE | GRETH_RXBD_ERR_OE)) { + dev->stats.rx_frame_errors++; + bad = 1; + } + if (status & GRETH_RXBD_ERR_CRC) { + dev->stats.rx_crc_errors++; + bad = 1; + } + } + if (unlikely(bad)) { + dev->stats.rx_errors++; + + } else { + + pkt_len = status & GRETH_BD_LEN; + + skb = netdev_alloc_skb(dev, pkt_len + NET_IP_ALIGN); + + if (unlikely(skb == NULL)) { + + if (net_ratelimit()) + dev_warn(&dev->dev, "low on memory - " "packet dropped\n"); + + dev->stats.rx_dropped++; + + } else { + skb_reserve(skb, NET_IP_ALIGN); + skb->dev = dev; + + dma_sync_single_for_cpu(greth->dev, + dma_addr, + pkt_len, + DMA_FROM_DEVICE); + + if (netif_msg_pktdata(greth)) + greth_print_rx_packet(phys_to_virt(dma_addr), pkt_len); + + memcpy(skb_put(skb, pkt_len), phys_to_virt(dma_addr), pkt_len); + + skb->protocol = eth_type_trans(skb, dev); + dev->stats.rx_packets++; + netif_receive_skb(skb); + } + } + + status = GRETH_BD_EN | GRETH_BD_IE; + if (greth->rx_cur == GRETH_RXBD_NUM_MASK) { + status |= GRETH_BD_WR; + } + + wmb(); + greth_write_bd(&bdp->stat, status); + + dma_sync_single_for_device(greth->dev, dma_addr, MAX_FRAME_SIZE, DMA_FROM_DEVICE); + + greth_enable_rx(greth); + + greth->rx_cur = NEXT_RX(greth->rx_cur); + } + + return count; +} + +static inline int hw_checksummed(u32 status) +{ + + if (status & GRETH_RXBD_IP_FRAG) + return 0; + + if (status & GRETH_RXBD_IP && status & GRETH_RXBD_IP_CSERR) + return 0; + + if (status & GRETH_RXBD_UDP && status & GRETH_RXBD_UDP_CSERR) + return 0; + + if (status & GRETH_RXBD_TCP && status & GRETH_RXBD_TCP_CSERR) + return 0; + + return 1; +} + +static int greth_rx_gbit(struct net_device *dev, int limit) +{ + struct greth_private *greth; + struct greth_bd *bdp; + struct sk_buff *skb, *newskb; + int pkt_len; + int bad, count = 0; + u32 status, dma_addr; + + greth = netdev_priv(dev); + + for (count = 0; count < limit; ++count) { + + bdp = greth->rx_bd_base + greth->rx_cur; + skb = greth->rx_skbuff[greth->rx_cur]; + status = greth_read_bd(&bdp->stat); + bad = 0; + + if (status & GRETH_BD_EN) + break; + + /* Check status for errors. */ + if (unlikely(status & GRETH_RXBD_STATUS)) { + + if (status & GRETH_RXBD_ERR_FT) { + dev->stats.rx_length_errors++; + bad = 1; + } else if (status & + (GRETH_RXBD_ERR_AE | GRETH_RXBD_ERR_OE | GRETH_RXBD_ERR_LE)) { + dev->stats.rx_frame_errors++; + bad = 1; + } else if (status & GRETH_RXBD_ERR_CRC) { + dev->stats.rx_crc_errors++; + bad = 1; + } + } + + /* Allocate new skb to replace current */ + newskb = netdev_alloc_skb(dev, MAX_FRAME_SIZE + NET_IP_ALIGN); + + if (!bad && newskb) { + skb_reserve(newskb, NET_IP_ALIGN); + + dma_addr = dma_map_single(greth->dev, + newskb->data, + MAX_FRAME_SIZE + NET_IP_ALIGN, + DMA_FROM_DEVICE); + + if (!dma_mapping_error(greth->dev, dma_addr)) { + /* Process the incoming frame. */ + pkt_len = status & GRETH_BD_LEN; + + dma_unmap_single(greth->dev, + greth_read_bd(&bdp->addr), + MAX_FRAME_SIZE + NET_IP_ALIGN, + DMA_FROM_DEVICE); + + if (netif_msg_pktdata(greth)) + greth_print_rx_packet(phys_to_virt(greth_read_bd(&bdp->addr)), pkt_len); + + skb_put(skb, pkt_len); + + if (greth->flags & GRETH_FLAG_RX_CSUM && hw_checksummed(status)) + skb->ip_summed = CHECKSUM_UNNECESSARY; + else + skb->ip_summed = CHECKSUM_NONE; + + skb->dev = dev; + skb->protocol = eth_type_trans(skb, dev); + dev->stats.rx_packets++; + netif_receive_skb(skb); + + greth->rx_skbuff[greth->rx_cur] = newskb; + greth_write_bd(&bdp->addr, dma_addr); + } else { + if (net_ratelimit()) + dev_warn(greth->dev, "Could not create DMA mapping, dropping packet\n"); + dev_kfree_skb(newskb); + dev->stats.rx_dropped++; + } + } else { + if (net_ratelimit()) + dev_warn(greth->dev, "Could not allocate SKB, dropping packet\n"); + dev->stats.rx_dropped++; + } + + status = GRETH_BD_EN | GRETH_BD_IE; + if (greth->rx_cur == GRETH_RXBD_NUM_MASK) { + status |= GRETH_BD_WR; + } + + wmb(); + greth_write_bd(&bdp->stat, status); + greth_enable_rx(greth); + greth->rx_cur = NEXT_RX(greth->rx_cur); + } + + return count; + +} + +static int greth_poll(struct napi_struct *napi, int budget) +{ + struct greth_private *greth; + int work_done = 0; + greth = container_of(napi, struct greth_private, napi); + + if (greth->gbit_mac) { + greth_clean_tx_gbit(greth->netdev); + } else { + greth_clean_tx(greth->netdev); + } + +restart_poll: + if (greth->gbit_mac) { + work_done += greth_rx_gbit(greth->netdev, budget - work_done); + } else { + work_done += greth_rx(greth->netdev, budget - work_done); + } + + if (work_done < budget) { + + napi_complete(napi); + + if (greth_pending_packets(greth)) { + napi_reschedule(napi); + goto restart_poll; + } + } + + greth_enable_irqs(greth); + return work_done; +} + +static int greth_set_mac_add(struct net_device *dev, void *p) +{ + struct sockaddr *addr = p; + struct greth_private *greth; + struct greth_regs *regs; + + greth = (struct greth_private *) netdev_priv(dev); + regs = (struct greth_regs *) greth->regs; + + if (!is_valid_ether_addr(addr->sa_data)) + return -EINVAL; + + memcpy(dev->dev_addr, addr->sa_data, dev->addr_len); + + GRETH_REGSAVE(regs->esa_msb, addr->sa_data[0] << 8 | addr->sa_data[1]); + GRETH_REGSAVE(regs->esa_lsb, + addr->sa_data[2] << 24 | addr-> + sa_data[3] << 16 | addr->sa_data[4] << 8 | addr->sa_data[5]); + return 0; +} + +static u32 greth_hash_get_index(__u8 *addr) +{ + return (ether_crc(6, addr)) & 0x3F; +} + +static void greth_set_hash_filter(struct net_device *dev) +{ + struct dev_mc_list *curr; + struct greth_private *greth = (struct greth_private *) netdev_priv(dev); + struct greth_regs *regs = (struct greth_regs *) greth->regs; + u32 mc_filter[2]; + unsigned int i, bitnr; + + mc_filter[0] = mc_filter[1] = 0; + + curr = dev->mc_list; + + for (i = 0; i < dev->mc_count; i++, curr = curr->next) { + + if (!curr) + break; /* unexpected end of list */ + + bitnr = greth_hash_get_index(curr->dmi_addr); + mc_filter[bitnr >> 5] |= 1 << (bitnr & 31); + } + + GRETH_REGSAVE(regs->hash_msb, mc_filter[1]); + GRETH_REGSAVE(regs->hash_lsb, mc_filter[0]); +} + +static void greth_set_multicast_list(struct net_device *dev) +{ + int cfg; + struct greth_private *greth = netdev_priv(dev); + struct greth_regs *regs = (struct greth_regs *) greth->regs; + + cfg = GRETH_REGLOAD(regs->control); + if (dev->flags & IFF_PROMISC) + cfg |= GRETH_CTRL_PR; + else + cfg &= ~GRETH_CTRL_PR; + + if (greth->multicast) { + if (dev->flags & IFF_ALLMULTI) { + GRETH_REGSAVE(regs->hash_msb, -1); + GRETH_REGSAVE(regs->hash_lsb, -1); + cfg |= GRETH_CTRL_MCEN; + GRETH_REGSAVE(regs->control, cfg); + return; + } + + if (dev->mc_count == 0) { + cfg &= ~GRETH_CTRL_MCEN; + GRETH_REGSAVE(regs->control, cfg); + return; + } + + /* Setup multicast filter */ + greth_set_hash_filter(dev); + cfg |= GRETH_CTRL_MCEN; + } + GRETH_REGSAVE(regs->control, cfg); +} + +static u32 greth_get_msglevel(struct net_device *dev) +{ + struct greth_private *greth = netdev_priv(dev); + return greth->msg_enable; +} + +static void greth_set_msglevel(struct net_device *dev, u32 value) +{ + struct greth_private *greth = netdev_priv(dev); + greth->msg_enable = value; +} +static int greth_get_settings(struct net_device *dev, struct ethtool_cmd *cmd) +{ + struct greth_private *greth = netdev_priv(dev); + struct phy_device *phy = greth->phy; + + if (!phy) + return -ENODEV; + + return phy_ethtool_gset(phy, cmd); +} + +static int greth_set_settings(struct net_device *dev, struct ethtool_cmd *cmd) +{ + struct greth_private *greth = netdev_priv(dev); + struct phy_device *phy = greth->phy; + + if (!phy) + return -ENODEV; + + return phy_ethtool_sset(phy, cmd); +} + +static int greth_get_regs_len(struct net_device *dev) +{ + return sizeof(struct greth_regs); +} + +static void greth_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info) +{ + struct greth_private *greth = netdev_priv(dev); + + strncpy(info->driver, dev_driver_string(greth->dev), 32); + strncpy(info->version, "revision: 1.0", 32); + strncpy(info->bus_info, greth->dev->bus->name, 32); + strncpy(info->fw_version, "N/A", 32); + info->eedump_len = 0; + info->regdump_len = sizeof(struct greth_regs); +} + +static void greth_get_regs(struct net_device *dev, struct ethtool_regs *regs, void *p) +{ + int i; + struct greth_private *greth = netdev_priv(dev); + u32 __iomem *greth_regs = (u32 __iomem *) greth->regs; + u32 *buff = p; + + for (i = 0; i < sizeof(struct greth_regs) / sizeof(u32); i++) + buff[i] = greth_read_bd(&greth_regs[i]); +} + +static u32 greth_get_rx_csum(struct net_device *dev) +{ + struct greth_private *greth = netdev_priv(dev); + return (greth->flags & GRETH_FLAG_RX_CSUM) != 0; +} + +static int greth_set_rx_csum(struct net_device *dev, u32 data) +{ + struct greth_private *greth = netdev_priv(dev); + + spin_lock_bh(&greth->devlock); + + if (data) + greth->flags |= GRETH_FLAG_RX_CSUM; + else + greth->flags &= ~GRETH_FLAG_RX_CSUM; + + spin_unlock_bh(&greth->devlock); + + return 0; +} + +static u32 greth_get_tx_csum(struct net_device *dev) +{ + return (dev->features & NETIF_F_IP_CSUM) != 0; +} + +static int greth_set_tx_csum(struct net_device *dev, u32 data) +{ + netif_tx_lock_bh(dev); + ethtool_op_set_tx_csum(dev, data); + netif_tx_unlock_bh(dev); + return 0; +} + +static const struct ethtool_ops greth_ethtool_ops = { + .get_msglevel = greth_get_msglevel, + .set_msglevel = greth_set_msglevel, + .get_settings = greth_get_settings, + .set_settings = greth_set_settings, + .get_drvinfo = greth_get_drvinfo, + .get_regs_len = greth_get_regs_len, + .get_regs = greth_get_regs, + .get_rx_csum = greth_get_rx_csum, + .set_rx_csum = greth_set_rx_csum, + .get_tx_csum = greth_get_tx_csum, + .set_tx_csum = greth_set_tx_csum, + .get_link = ethtool_op_get_link, +}; + +static struct net_device_ops greth_netdev_ops = { + .ndo_open = greth_open, + .ndo_stop = greth_close, + .ndo_start_xmit = greth_start_xmit, + .ndo_set_mac_address = greth_set_mac_add, +}; + +static inline int wait_for_mdio(struct greth_private *greth) +{ + unsigned long timeout = jiffies + 4*HZ/100; + while (GRETH_REGLOAD(greth->regs->mdio) & GRETH_MII_BUSY) { + if (time_after(jiffies, timeout)) + return 0; + } + return 1; +} + +static int greth_mdio_read(struct mii_bus *bus, int phy, int reg) +{ + struct greth_private *greth = bus->priv; + int data; + + if (!wait_for_mdio(greth)) + return -EBUSY; + + GRETH_REGSAVE(greth->regs->mdio, ((phy & 0x1F) << 11) | ((reg & 0x1F) << 6) | 2); + + if (!wait_for_mdio(greth)) + return -EBUSY; + + if (!(GRETH_REGLOAD(greth->regs->mdio) & GRETH_MII_NVALID)) { + data = (GRETH_REGLOAD(greth->regs->mdio) >> 16) & 0xFFFF; + return data; + + } else { + return -1; + } +} + +static int greth_mdio_write(struct mii_bus *bus, int phy, int reg, u16 val) +{ + struct greth_private *greth = bus->priv; + + if (!wait_for_mdio(greth)) + return -EBUSY; + + GRETH_REGSAVE(greth->regs->mdio, + ((val & 0xFFFF) << 16) | ((phy & 0x1F) << 11) | ((reg & 0x1F) << 6) | 1); + + if (!wait_for_mdio(greth)) + return -EBUSY; + + return 0; +} + +static int greth_mdio_reset(struct mii_bus *bus) +{ + return 0; +} + +static void greth_link_change(struct net_device *dev) +{ + struct greth_private *greth = netdev_priv(dev); + struct phy_device *phydev = greth->phy; + unsigned long flags; + + int status_change = 0; + + spin_lock_irqsave(&greth->devlock, flags); + + if (phydev->link) { + + if ((greth->speed != phydev->speed) || (greth->duplex != phydev->duplex)) { + + GRETH_REGANDIN(greth->regs->control, + ~(GRETH_CTRL_FD | GRETH_CTRL_SP | GRETH_CTRL_GB)); + + if (phydev->duplex) + GRETH_REGORIN(greth->regs->control, GRETH_CTRL_FD); + + if (phydev->speed == SPEED_100) { + + GRETH_REGORIN(greth->regs->control, GRETH_CTRL_SP); + } + + else if (phydev->speed == SPEED_1000) + GRETH_REGORIN(greth->regs->control, GRETH_CTRL_GB); + + greth->speed = phydev->speed; + greth->duplex = phydev->duplex; + status_change = 1; + } + } + + if (phydev->link != greth->link) { + if (!phydev->link) { + greth->speed = 0; + greth->duplex = -1; + } + greth->link = phydev->link; + + status_change = 1; + } + + spin_unlock_irqrestore(&greth->devlock, flags); + + if (status_change) { + if (phydev->link) + pr_debug("%s: link up (%d/%s)\n", + dev->name, phydev->speed, + DUPLEX_FULL == phydev->duplex ? "Full" : "Half"); + else + pr_debug("%s: link down\n", dev->name); + } +} + +static int greth_mdio_probe(struct net_device *dev) +{ + struct greth_private *greth = netdev_priv(dev); + struct phy_device *phy = NULL; + u32 interface; + int i; + + /* Find the first PHY */ + for (i = 0; i < PHY_MAX_ADDR; i++) { + if (greth->mdio->phy_map[i]) { + phy = greth->mdio->phy_map[i]; + break; + } + } + if (!phy) { + if (netif_msg_probe(greth)) + dev_err(&dev->dev, "no PHY found\n"); + return -ENXIO; + } + + if (greth->gbit_mac) + interface = PHY_INTERFACE_MODE_GMII; + else + interface = PHY_INTERFACE_MODE_MII; + + phy = phy_connect(dev, dev_name(&phy->dev), &greth_link_change, 0, interface); + + if (greth->gbit_mac) + phy->supported &= PHY_GBIT_FEATURES; + else + phy->supported &= PHY_BASIC_FEATURES; + + phy->advertising = phy->supported; + + if (IS_ERR(phy)) { + if (netif_msg_ifup(greth)) + dev_err(&dev->dev, "could not attach to PHY\n"); + return PTR_ERR(phy); + } + + greth->link = 0; + greth->speed = 0; + greth->duplex = -1; + greth->phy = phy; + + return 0; +} + +static inline int phy_aneg_done(struct phy_device *phydev) +{ + int retval; + + retval = phy_read(phydev, MII_BMSR); + + return (retval < 0) ? retval : (retval & BMSR_ANEGCOMPLETE); +} + +static int greth_mdio_init(struct greth_private *greth) +{ + int ret, phy; + unsigned long timeout; + + greth->mdio = mdiobus_alloc(); + if (!greth->mdio) { + return -ENOMEM; + } + + greth->mdio->name = "greth-mdio"; + snprintf(greth->mdio->id, MII_BUS_ID_SIZE, "%s-%d", greth->mdio->name, greth->irq); + greth->mdio->read = greth_mdio_read; + greth->mdio->write = greth_mdio_write; + greth->mdio->reset = greth_mdio_reset; + greth->mdio->priv = greth; + + greth->mdio->irq = greth->mdio_irqs; + + for (phy = 0; phy < PHY_MAX_ADDR; phy++) + greth->mdio->irq[phy] = PHY_POLL; + + ret = mdiobus_register(greth->mdio); + if (ret) { + goto error; + } + + ret = greth_mdio_probe(greth->netdev); + if (ret) { + if (netif_msg_probe(greth)) + dev_err(&greth->netdev->dev, "failed to probe MDIO bus\n"); + goto unreg_mdio; + } + + phy_start(greth->phy); + + /* If Ethernet debug link is used make autoneg happen right away */ + if (greth->edcl && greth_edcl == 1) { + phy_start_aneg(greth->phy); + timeout = jiffies + 6*HZ; + while (!phy_aneg_done(greth->phy) && time_before(jiffies, timeout)) { + } + genphy_read_status(greth->phy); + greth_link_change(greth->netdev); + } + + return 0; + +unreg_mdio: + mdiobus_unregister(greth->mdio); +error: + mdiobus_free(greth->mdio); + return ret; +} + +/* Initialize the GRETH MAC */ +static int __devinit greth_of_probe(struct of_device *ofdev, const struct of_device_id *match) +{ + struct net_device *dev; + struct greth_private *greth; + struct greth_regs *regs; + + int i; + int err; + int tmp; + unsigned long timeout; + + dev = alloc_etherdev(sizeof(struct greth_private)); + + if (dev == NULL) + return -ENOMEM; + + greth = netdev_priv(dev); + greth->netdev = dev; + greth->dev = &ofdev->dev; + + if (greth_debug > 0) + greth->msg_enable = greth_debug; + else + greth->msg_enable = GRETH_DEF_MSG_ENABLE; + + spin_lock_init(&greth->devlock); + + greth->regs = of_ioremap(&ofdev->resource[0], 0, + resource_size(&ofdev->resource[0]), + "grlib-greth regs"); + + if (greth->regs == NULL) { + if (netif_msg_probe(greth)) + dev_err(greth->dev, "ioremap failure.\n"); + err = -EIO; + goto error1; + } + + regs = (struct greth_regs *) greth->regs; + greth->irq = ofdev->irqs[0]; + + dev_set_drvdata(greth->dev, dev); + SET_NETDEV_DEV(dev, greth->dev); + + if (netif_msg_probe(greth)) + dev_dbg(greth->dev, "reseting controller.\n"); + + /* Reset the controller. */ + GRETH_REGSAVE(regs->control, GRETH_RESET); + + /* Wait for MAC to reset itself */ + timeout = jiffies + HZ/100; + while (GRETH_REGLOAD(regs->control) & GRETH_RESET) { + if (time_after(jiffies, timeout)) { + err = -EIO; + if (netif_msg_probe(greth)) + dev_err(greth->dev, "timeout when waiting for reset.\n"); + goto error2; + } + } + + /* Get default PHY address */ + greth->phyaddr = (GRETH_REGLOAD(regs->mdio) >> 11) & 0x1F; + + /* Check if we have GBIT capable MAC */ + tmp = GRETH_REGLOAD(regs->control); + greth->gbit_mac = (tmp >> 27) & 1; + + /* Check for multicast capability */ + greth->multicast = (tmp >> 25) & 1; + + greth->edcl = (tmp >> 31) & 1; + + /* If we have EDCL we disable the EDCL speed-duplex FSM so + * it doesn't interfere with the software */ + if (greth->edcl != 0) + GRETH_REGORIN(regs->control, GRETH_CTRL_DISDUPLEX); + + /* Check if MAC can handle MDIO interrupts */ + greth->mdio_int_en = (tmp >> 26) & 1; + + err = greth_mdio_init(greth); + if (err) { + if (netif_msg_probe(greth)) + dev_err(greth->dev, "failed to register MDIO bus\n"); + goto error2; + } + + /* Allocate TX descriptor ring in coherent memory */ + greth->tx_bd_base = (struct greth_bd *) dma_alloc_coherent(greth->dev, + 1024, + &greth->tx_bd_base_phys, + GFP_KERNEL); + + if (!greth->tx_bd_base) { + if (netif_msg_probe(greth)) + dev_err(&dev->dev, "could not allocate descriptor memory.\n"); + err = -ENOMEM; + goto error3; + } + + memset(greth->tx_bd_base, 0, 1024); + + /* Allocate RX descriptor ring in coherent memory */ + greth->rx_bd_base = (struct greth_bd *) dma_alloc_coherent(greth->dev, + 1024, + &greth->rx_bd_base_phys, + GFP_KERNEL); + + if (!greth->rx_bd_base) { + if (netif_msg_probe(greth)) + dev_err(greth->dev, "could not allocate descriptor memory.\n"); + err = -ENOMEM; + goto error4; + } + + memset(greth->rx_bd_base, 0, 1024); + + /* Get MAC address from: module param, OF property or ID prom */ + for (i = 0; i < 6; i++) { + if (macaddr[i] != 0) + break; + } + if (i == 6) { + const unsigned char *addr; + int len; + addr = of_get_property(ofdev->node, "local-mac-address", &len); + if (addr != NULL && len == 6) { + for (i = 0; i < 6; i++) + macaddr[i] = (unsigned int) addr[i]; + } else { +#ifdef CONFIG_SPARC + for (i = 0; i < 6; i++) + macaddr[i] = (unsigned int) idprom->id_ethaddr[i]; +#endif + } + } + + for (i = 0; i < 6; i++) + dev->dev_addr[i] = macaddr[i]; + + macaddr[5]++; + + if (!is_valid_ether_addr(&dev->dev_addr[0])) { + if (netif_msg_probe(greth)) + dev_err(greth->dev, "no valid ethernet address, aborting.\n"); + err = -EINVAL; + goto error5; + } + + GRETH_REGSAVE(regs->esa_msb, dev->dev_addr[0] << 8 | dev->dev_addr[1]); + GRETH_REGSAVE(regs->esa_lsb, dev->dev_addr[2] << 24 | dev->dev_addr[3] << 16 | + dev->dev_addr[4] << 8 | dev->dev_addr[5]); + + /* Clear all pending interrupts except PHY irq */ + GRETH_REGSAVE(regs->status, 0xFF); + + if (greth->gbit_mac) { + dev->features = NETIF_F_SG | NETIF_F_IP_CSUM | NETIF_F_HIGHDMA; + greth_netdev_ops.ndo_start_xmit = greth_start_xmit_gbit; + greth->flags = GRETH_FLAG_RX_CSUM; + } + + if (greth->multicast) { + greth_netdev_ops.ndo_set_multicast_list = greth_set_multicast_list; + dev->flags |= IFF_MULTICAST; + } else { + dev->flags &= ~IFF_MULTICAST; + } + + dev->netdev_ops = &greth_netdev_ops; + dev->ethtool_ops = &greth_ethtool_ops; + + if (register_netdev(dev)) { + if (netif_msg_probe(greth)) + dev_err(greth->dev, "netdevice registration failed.\n"); + err = -ENOMEM; + goto error5; + } + + /* setup NAPI */ + memset(&greth->napi, 0, sizeof(greth->napi)); + netif_napi_add(dev, &greth->napi, greth_poll, 64); + + return 0; + +error5: + dma_free_coherent(greth->dev, 1024, greth->rx_bd_base, greth->rx_bd_base_phys); +error4: + dma_free_coherent(greth->dev, 1024, greth->tx_bd_base, greth->tx_bd_base_phys); +error3: + mdiobus_unregister(greth->mdio); +error2: + of_iounmap(&ofdev->resource[0], greth->regs, resource_size(&ofdev->resource[0])); +error1: + free_netdev(dev); + return err; +} + +static int __devexit greth_of_remove(struct of_device *of_dev) +{ + struct net_device *ndev = dev_get_drvdata(&of_dev->dev); + struct greth_private *greth = netdev_priv(ndev); + + /* Free descriptor areas */ + dma_free_coherent(&of_dev->dev, 1024, greth->rx_bd_base, greth->rx_bd_base_phys); + + dma_free_coherent(&of_dev->dev, 1024, greth->tx_bd_base, greth->tx_bd_base_phys); + + dev_set_drvdata(&of_dev->dev, NULL); + + if (greth->phy) + phy_stop(greth->phy); + mdiobus_unregister(greth->mdio); + + unregister_netdev(ndev); + free_netdev(ndev); + + of_iounmap(&of_dev->resource[0], greth->regs, resource_size(&of_dev->resource[0])); + + return 0; +} + +static struct of_device_id greth_of_match[] = { + { + .name = "GAISLER_ETHMAC", + }, + {}, +}; + +MODULE_DEVICE_TABLE(of, greth_of_match); + +static struct of_platform_driver greth_of_driver = { + .name = "grlib-greth", + .match_table = greth_of_match, + .probe = greth_of_probe, + .remove = __devexit_p(greth_of_remove), + .driver = { + .owner = THIS_MODULE, + .name = "grlib-greth", + }, +}; + +static int __init greth_init(void) +{ + return of_register_platform_driver(&greth_of_driver); +} + +static void __exit greth_cleanup(void) +{ + of_unregister_platform_driver(&greth_of_driver); +} + +module_init(greth_init); +module_exit(greth_cleanup); + +MODULE_AUTHOR("Aeroflex Gaisler AB."); +MODULE_DESCRIPTION("Aeroflex Gaisler Ethernet MAC driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/net/greth.h b/drivers/net/greth.h new file mode 100644 index 000000000000..973388d6abca --- /dev/null +++ b/drivers/net/greth.h @@ -0,0 +1,143 @@ +#ifndef GRETH_H +#define GRETH_H + +#include + +/* Register bits and masks */ +#define GRETH_RESET 0x40 +#define GRETH_MII_BUSY 0x8 +#define GRETH_MII_NVALID 0x10 + +#define GRETH_CTRL_FD 0x10 +#define GRETH_CTRL_PR 0x20 +#define GRETH_CTRL_SP 0x80 +#define GRETH_CTRL_GB 0x100 +#define GRETH_CTRL_PSTATIEN 0x400 +#define GRETH_CTRL_MCEN 0x800 +#define GRETH_CTRL_DISDUPLEX 0x1000 +#define GRETH_STATUS_PHYSTAT 0x100 + +#define GRETH_BD_EN 0x800 +#define GRETH_BD_WR 0x1000 +#define GRETH_BD_IE 0x2000 +#define GRETH_BD_LEN 0x7FF + +#define GRETH_TXEN 0x1 +#define GRETH_INT_TX 0x8 +#define GRETH_TXI 0x4 +#define GRETH_TXBD_STATUS 0x0001C000 +#define GRETH_TXBD_MORE 0x20000 +#define GRETH_TXBD_IPCS 0x40000 +#define GRETH_TXBD_TCPCS 0x80000 +#define GRETH_TXBD_UDPCS 0x100000 +#define GRETH_TXBD_CSALL (GRETH_TXBD_IPCS | GRETH_TXBD_TCPCS | GRETH_TXBD_UDPCS) +#define GRETH_TXBD_ERR_LC 0x10000 +#define GRETH_TXBD_ERR_UE 0x4000 +#define GRETH_TXBD_ERR_AL 0x8000 + +#define GRETH_INT_RX 0x4 +#define GRETH_RXEN 0x2 +#define GRETH_RXI 0x8 +#define GRETH_RXBD_STATUS 0xFFFFC000 +#define GRETH_RXBD_ERR_AE 0x4000 +#define GRETH_RXBD_ERR_FT 0x8000 +#define GRETH_RXBD_ERR_CRC 0x10000 +#define GRETH_RXBD_ERR_OE 0x20000 +#define GRETH_RXBD_ERR_LE 0x40000 +#define GRETH_RXBD_IP 0x80000 +#define GRETH_RXBD_IP_CSERR 0x100000 +#define GRETH_RXBD_UDP 0x200000 +#define GRETH_RXBD_UDP_CSERR 0x400000 +#define GRETH_RXBD_TCP 0x800000 +#define GRETH_RXBD_TCP_CSERR 0x1000000 +#define GRETH_RXBD_IP_FRAG 0x2000000 +#define GRETH_RXBD_MCAST 0x4000000 + +/* Descriptor parameters */ +#define GRETH_TXBD_NUM 128 +#define GRETH_TXBD_NUM_MASK (GRETH_TXBD_NUM-1) +#define GRETH_TX_BUF_SIZE 2048 +#define GRETH_RXBD_NUM 128 +#define GRETH_RXBD_NUM_MASK (GRETH_RXBD_NUM-1) +#define GRETH_RX_BUF_SIZE 2048 + +/* Buffers per page */ +#define GRETH_RX_BUF_PPGAE (PAGE_SIZE/GRETH_RX_BUF_SIZE) +#define GRETH_TX_BUF_PPGAE (PAGE_SIZE/GRETH_TX_BUF_SIZE) + +/* How many pages are needed for buffers */ +#define GRETH_RX_BUF_PAGE_NUM (GRETH_RXBD_NUM/GRETH_RX_BUF_PPGAE) +#define GRETH_TX_BUF_PAGE_NUM (GRETH_TXBD_NUM/GRETH_TX_BUF_PPGAE) + +/* Buffer size. + * Gbit MAC uses tagged maximum frame size which is 1518 excluding CRC. + * Set to 1520 to make all buffers word aligned for non-gbit MAC. + */ +#define MAX_FRAME_SIZE 1520 + +/* Flags */ +#define GRETH_FLAG_RX_CSUM 0x1 + +/* GRETH APB registers */ +struct greth_regs { + u32 control; + u32 status; + u32 esa_msb; + u32 esa_lsb; + u32 mdio; + u32 tx_desc_p; + u32 rx_desc_p; + u32 edclip; + u32 hash_msb; + u32 hash_lsb; +}; + +/* GRETH buffer descriptor */ +struct greth_bd { + u32 stat; + u32 addr; +}; + +struct greth_private { + struct sk_buff *rx_skbuff[GRETH_RXBD_NUM]; + struct sk_buff *tx_skbuff[GRETH_TXBD_NUM]; + + unsigned char *tx_bufs[GRETH_TXBD_NUM]; + unsigned char *rx_bufs[GRETH_RXBD_NUM]; + + u16 tx_next; + u16 tx_last; + u16 tx_free; + u16 rx_cur; + + struct greth_regs *regs; /* Address of controller registers. */ + struct greth_bd *rx_bd_base; /* Address of Rx BDs. */ + struct greth_bd *tx_bd_base; /* Address of Tx BDs. */ + dma_addr_t rx_bd_base_phys; + dma_addr_t tx_bd_base_phys; + + int irq; + + struct device *dev; /* Pointer to of_device->dev */ + struct net_device *netdev; + struct napi_struct napi; + spinlock_t devlock; + + struct phy_device *phy; + struct mii_bus *mdio; + int mdio_irqs[PHY_MAX_ADDR]; + unsigned int link; + unsigned int speed; + unsigned int duplex; + + u32 msg_enable; + u32 flags; + + u8 phyaddr; + u8 multicast; + u8 gbit_mac; + u8 mdio_int_en; + u8 edcl; +}; + +#endif -- cgit v1.2.3 From cf261b2392daa7b1a18b91707485e3648dda34d3 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Thu, 18 Feb 2010 23:32:26 -0800 Subject: net: Make GRETH driver depend on SPARC. Reported by Stephen Rothwell. Signed-off-by: David S. Miller --- drivers/net/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/net/Kconfig') diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig index 17ff15f6099a..46af867af85f 100644 --- a/drivers/net/Kconfig +++ b/drivers/net/Kconfig @@ -997,7 +997,7 @@ config ETHOC config GRETH tristate "Aeroflex Gaisler GRETH Ethernet MAC support" - depends on OF + depends on SPARC select PHYLIB select CRC32 help -- cgit v1.2.3