* [PATCH] pkt_sched: Fix sch_sfq vs tcf_bind_filter oops
From: Jarek Poplawski @ 2010-08-06 10:22 UTC (permalink / raw)
To: Franchoze Eric; +Cc: netdev, David Miller, Patrick McHardy
In-Reply-To: <282881281036255@web101.yandex.ru>
Franchoze Eric wrote:
> Hello, can somebody follow up with approriate fix for that bug? It's reproducible on the latest 2.6.32.17 kernel with Centos5 user land.
> Script to reproduce is bellow. I did quick fix for that to show where is the issue.
I can't test it with 2.6.32 now but it seems the patch below should
help. Please send your "Tested-by" if so.
Thanks,
Jarek P.
------------>
Since there was added ->tcf_chain() method without ->bind_tcf() to
sch_sfq class options, there is oops when a filter is added with
the classid parameter.
Fixes commit 7d2681a6ff4f9ab5e48d02550b4c6338f1638998
netdev thread: null pointer at cls_api.c
Signed-off-by: Jarek Poplawski <jarkao2@gmail.com>
Reported-by: Franchoze Eric <franchoze@yandex.ru>
---
diff --git a/net/sched/sch_sfq.c b/net/sched/sch_sfq.c
index e85352b..534f332 100644
--- a/net/sched/sch_sfq.c
+++ b/net/sched/sch_sfq.c
@@ -513,6 +513,12 @@ static unsigned long sfq_get(struct Qdisc *sch, u32 classid)
return 0;
}
+static unsigned long sfq_bind(struct Qdisc *sch, unsigned long parent,
+ u32 classid)
+{
+ return 0;
+}
+
static struct tcf_proto **sfq_find_tcf(struct Qdisc *sch, unsigned long cl)
{
struct sfq_sched_data *q = qdisc_priv(sch);
@@ -567,6 +573,7 @@ static void sfq_walk(struct Qdisc *sch, struct qdisc_walker *arg)
static const struct Qdisc_class_ops sfq_class_ops = {
.get = sfq_get,
.tcf_chain = sfq_find_tcf,
+ .bind_tcf = sfq_bind,
.dump = sfq_dump_class,
.dump_stats = sfq_dump_class_stats,
.walk = sfq_walk,
^ permalink raw reply related
* Re: [PATCH 2/2] netfilter: xt_condition: change the value from boolean to u32
From: Luciano Coelho @ 2010-08-06 10:34 UTC (permalink / raw)
To: ext Jan Engelhardt
Cc: netfilter-devel@vger.kernel.org, netdev@vger.kernel.org,
kaber@trash.net, sameo@linux.intel.com
In-Reply-To: <alpine.LSU.2.01.1008061044270.8895@obet.zrqbmnf.qr>
On Fri, 2010-08-06 at 10:52 +0200, ext Jan Engelhardt wrote:
> On Friday 2010-08-06 10:00, Luciano Coelho wrote:
> >> >+ buf[length - 1] = '\0';
> >> >+
> >> >+ if (strict_strtoull(buf, 0, &value) != 0)
> >> >+ return -EINVAL;
> >> >+
> >> >+ if (value > (u32) value)
> >> >+ return -EINVAL;
> >>
> >> Is it possible to use just strict_strtoul?
> >
> >Not easily. I found that there is a bug in strtoul (and strtoull for
> >that matter) that causes the long to overflow if there are valid digits
> >after the maximum possible digits for the base. For example if you try
> >to strtoul 0xfffffffff (with 9 f's) the strtoul will overflow and come
> >up with a bogus result.
>
> I see. Strange that no one has adressed this yet - I mean, writing
> a just-too-large value into a procfs/sysfs file and thus effectively
> causing a bogus value to be actually written isn't quite so thrilling
> as things go haywire.
Yes, I was really surprised to see this happening when I was testing the
limits. And I was even more surprised when I checked the strtoull code
and saw that it is broken.
> >I can't easily truncate the string to avoid
> >this problem, because with decimal or octal, the same valid value would
> >take more spaces. I could do some magic here, checking whether it's a
> >hex, dec or oct and truncate appropriately, but that would be very ugly.
> >
> >So the simplest way I came up with was to use strtoull and return
> >-EINVAL if the value exceeds 32 bits. ;)
>
> If I read strtoul(3) right, ERANGE is used for "out of range".
Yes, libc's strtoul returns ERANGE in that case. strict_strtoul() in
the kernel code doesn't. I'll change my code to return -ERANGE here
too, for consistency.
> >> Since the condition value (cdmark) was thought of an nfmark-style thing,
> >> would it perhaps make sense to model it after it
> >>
> >> return (var->value & ~info->mask) ^ info->value;
> >>
> >> Other opinions?
> >
> >I think it's nicer to have it as a normal equals here for now and then
> >extend the match with more operations. We can later add, for example,
> >an --and option to the condition match in order to do other kinds of
> >binary operations. It would be more flexible this way because we could
> >use several different types of comparisons, wouldn't it? And in the
> >target we could have several different types of operations.
>
> Indeed.
--
Cheers,
Luca.
^ permalink raw reply
* [RFC] [PATCH] Don't destroy TCP sockets twice
From: Andi Kleen @ 2010-08-06 11:05 UTC (permalink / raw)
To: netdev
While working on something else I noticed that tcp_v4/6_destroy_sock()
can get called twice on a socket. This happens because when a reset or
similar happens tcp_done destroys the connection socket state, and
then eventually when the socket is released it is destroyed again.
Problem here is that the socket can actually still transmit packets
even after having been destroyed by tcp_done: this happens because
tcp_close is called after tcp_done, tcp_close calls tcp_send_fin which
then sends the FIN at least.
With my local modifications this lead to crashes (I destroyed some
additional state in destroy_sock that is needed for sending any
packets). But I think it's broken even without any changes: tcp_*_destroy
sock removes a lot of state, including MD5 state, congestion control
state and some others.
At least for MD5 it's easy to argue that the the FIN still needs this
state. I haven't tested that, but I think what will happen is that
you'll see FINs which are not MD5 signed after a reset (there is no
crash because zero MD5 state is ignored) There might be other similar
problems with congestion avoidance state and the FIN packet.
Overall it also seems inefficient to destroy twice.
The only thing in tcp_*_destroy sock that I think really needs to be
done is clearing the timers, but tcp_done does this already too.
So this patch changes tcp_done to not call inet_csk_destroy_sock()
and leave the destruction to the later final release or close.
The only drawback is that the buffers could be freed a bit later
for the RST case (e.g. when there is a reference to user space
it will wait for the user close), but that doesn't seem like a big
issue.
I made this a RFC for now because it needs some more review.
Signed-off-by: Andi Kleen <ak@linux.intel.com>
diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c
index 6596b4f..f7cae15 100644
--- a/net/ipv4/tcp.c
+++ b/net/ipv4/tcp.c
@@ -3171,8 +3171,6 @@ void tcp_done(struct sock *sk)
if (!sock_flag(sk, SOCK_DEAD))
sk->sk_state_change(sk);
- else
- inet_csk_destroy_sock(sk);
}
EXPORT_SYMBOL_GPL(tcp_done);
^ permalink raw reply related
* [PATCH v2 1/2] netfilter: xtables: inclusion of xt_condition
From: luciano.coelho @ 2010-08-06 12:21 UTC (permalink / raw)
To: netfilter-devel; +Cc: netdev, kaber, jengelh, sameo
In-Reply-To: <1281097308-1515-1-git-send-email-luciano.coelho@nokia.com>
From: Luciano Coelho <luciano.coelho@nokia.com>
xt_condition can be used by userspace to influence decisions in rules
by means of togglable variables without having to reload the entire
ruleset.
This is a respin of the module in Xtables-addons, with support for
multiple namespaces and other small improvements. Some of the changes
were made by Jan Engelhardt.
Cc: Jan Engelhardt <jengelh@medozas.de>
Signed-off-by: Luciano Coelho <luciano.coelho@nokia.com>
---
include/linux/netfilter/Kbuild | 1 +
include/linux/netfilter/xt_condition.h | 14 ++
net/netfilter/Kconfig | 8 +
net/netfilter/Makefile | 1 +
net/netfilter/xt_condition.c | 265 ++++++++++++++++++++++++++++++++
5 files changed, 289 insertions(+), 0 deletions(-)
create mode 100644 include/linux/netfilter/xt_condition.h
create mode 100644 net/netfilter/xt_condition.c
diff --git a/include/linux/netfilter/Kbuild b/include/linux/netfilter/Kbuild
index edeeabd..60363f5 100644
--- a/include/linux/netfilter/Kbuild
+++ b/include/linux/netfilter/Kbuild
@@ -22,6 +22,7 @@ header-y += xt_TEE.h
header-y += xt_TPROXY.h
header-y += xt_cluster.h
header-y += xt_comment.h
+header-y += xt_condition.h
header-y += xt_connbytes.h
header-y += xt_connlimit.h
header-y += xt_connmark.h
diff --git a/include/linux/netfilter/xt_condition.h b/include/linux/netfilter/xt_condition.h
new file mode 100644
index 0000000..4faf3ca
--- /dev/null
+++ b/include/linux/netfilter/xt_condition.h
@@ -0,0 +1,14 @@
+#ifndef _XT_CONDITION_H
+#define _XT_CONDITION_H
+
+#include <linux/types.h>
+
+struct xt_condition_mtinfo {
+ char name[31];
+ __u8 invert;
+
+ /* Used internally by the kernel */
+ void *condvar __attribute__((aligned(8)));
+};
+
+#endif /* _XT_CONDITION_H */
diff --git a/net/netfilter/Kconfig b/net/netfilter/Kconfig
index 4328825..5044dd6 100644
--- a/net/netfilter/Kconfig
+++ b/net/netfilter/Kconfig
@@ -621,6 +621,14 @@ config NETFILTER_XT_MATCH_COMMENT
If you want to compile it as a module, say M here and read
<file:Documentation/kbuild/modules.txt>. If unsure, say `N'.
+config NETFILTER_XT_MATCH_CONDITION
+ tristate '"condition" match support'
+ depends on NETFILTER_ADVANCED
+ depends on PROC_FS
+ ---help---
+ This option allows you to match firewall rules against condition
+ variables stored in the /proc/net/nf_condition directory.
+
config NETFILTER_XT_MATCH_CONNBYTES
tristate '"connbytes" per-connection counter match support'
depends on NF_CONNTRACK
diff --git a/net/netfilter/Makefile b/net/netfilter/Makefile
index 441050f..bbf72bb 100644
--- a/net/netfilter/Makefile
+++ b/net/netfilter/Makefile
@@ -67,6 +67,7 @@ obj-$(CONFIG_NETFILTER_XT_TARGET_IDLETIMER) += xt_IDLETIMER.o
# matches
obj-$(CONFIG_NETFILTER_XT_MATCH_CLUSTER) += xt_cluster.o
obj-$(CONFIG_NETFILTER_XT_MATCH_COMMENT) += xt_comment.o
+obj-$(CONFIG_NETFILTER_XT_MATCH_CONDITION) += xt_condition.o
obj-$(CONFIG_NETFILTER_XT_MATCH_CONNBYTES) += xt_connbytes.o
obj-$(CONFIG_NETFILTER_XT_MATCH_CONNLIMIT) += xt_connlimit.o
obj-$(CONFIG_NETFILTER_XT_MATCH_CONNTRACK) += xt_conntrack.o
diff --git a/net/netfilter/xt_condition.c b/net/netfilter/xt_condition.c
new file mode 100644
index 0000000..a78d832
--- /dev/null
+++ b/net/netfilter/xt_condition.c
@@ -0,0 +1,265 @@
+/*
+ * "condition" match extension for Xtables
+ *
+ * Description: This module allows firewall rules to match using
+ * condition variables available through procfs.
+ *
+ * Authors:
+ * Stephane Ouellette <ouellettes [at] videotron ca>, 2002-10-22
+ * Massimiliano Hofer <max [at] nucleus it>, 2006-05-15
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License; either version 2
+ * or 3 of the License, as published by the Free Software Foundation.
+ */
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+#include <linux/kernel.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/proc_fs.h>
+#include <linux/spinlock.h>
+#include <linux/string.h>
+#include <linux/version.h>
+#include <linux/netfilter/x_tables.h>
+#include <linux/netfilter/xt_condition.h>
+#include <net/netns/generic.h>
+#include <asm/uaccess.h>
+
+/* Defaults, these can be overridden on the module command-line. */
+static unsigned int condition_list_perms = S_IRUGO | S_IWUSR;
+static unsigned int condition_uid_perms = 0;
+static unsigned int condition_gid_perms = 0;
+
+MODULE_AUTHOR("Stephane Ouellette <ouellettes@videotron.ca>");
+MODULE_AUTHOR("Massimiliano Hofer <max@nucleus.it>");
+MODULE_AUTHOR("Jan Engelhardt <jengelh@medozas.de>");
+MODULE_DESCRIPTION("Allows rules to match against condition variables");
+MODULE_LICENSE("GPL");
+module_param(condition_list_perms, uint, S_IRUSR | S_IWUSR);
+MODULE_PARM_DESC(condition_list_perms, "default permissions on /proc/net/nf_condition/* files");
+module_param(condition_uid_perms, uint, S_IRUSR | S_IWUSR);
+MODULE_PARM_DESC(condition_uid_perms, "default user owner of /proc/net/nf_condition/* files");
+module_param(condition_gid_perms, uint, S_IRUSR | S_IWUSR);
+MODULE_PARM_DESC(condition_gid_perms, "default group owner of /proc/net/nf_condition/* files");
+MODULE_ALIAS("ipt_condition");
+MODULE_ALIAS("ip6t_condition");
+
+struct condition_variable {
+ struct list_head list;
+ struct proc_dir_entry *status_proc;
+ unsigned int refcount;
+ bool enabled;
+};
+
+struct condition_net {
+ struct list_head list;
+ struct proc_dir_entry *proc_dir;
+};
+
+static int condition_net_id;
+static inline struct condition_net *condition_pernet(struct net *net)
+{
+ return net_generic(net, condition_net_id);
+}
+
+/* proc_lock is a user context only semaphore used for write access */
+/* to the conditions' list. */
+static DEFINE_MUTEX(proc_lock);
+
+static int condition_proc_read(char __user *buffer, char **start, off_t offset,
+ int length, int *eof, void *data)
+{
+ const struct condition_variable *var = data;
+
+ buffer[0] = var->enabled ? '1' : '0';
+ buffer[1] = '\n';
+ if (length >= 2)
+ *eof = true;
+ return 2;
+}
+
+static int condition_proc_write(struct file *file, const char __user *buffer,
+ unsigned long length, void *data)
+{
+ struct condition_variable *var = data;
+ char newval;
+
+ if (length > 0) {
+ if (get_user(newval, buffer) != 0)
+ return -EFAULT;
+ /* Match only on the first character */
+ switch (newval) {
+ case '0':
+ var->enabled = false;
+ break;
+ case '1':
+ var->enabled = true;
+ break;
+ }
+ }
+ return length;
+}
+
+static bool
+condition_mt(const struct sk_buff *skb, struct xt_action_param *par)
+{
+ const struct xt_condition_mtinfo *info = par->matchinfo;
+ const struct condition_variable *var = info->condvar;
+
+ return var->enabled ^ info->invert;
+}
+
+static int condition_mt_check(const struct xt_mtchk_param *par)
+{
+ struct xt_condition_mtinfo *info = par->matchinfo;
+ struct condition_variable *var;
+ struct condition_net *cond_net = condition_pernet(par->net);
+
+ /* Forbid certain names */
+ if (*info->name == '\0' || *info->name == '.' ||
+ info->name[sizeof(info->name)-1] != '\0' ||
+ memchr(info->name, '/', sizeof(info->name)) != NULL) {
+ pr_info("name not allowed or too long: \"%.*s\"\n",
+ (unsigned int)sizeof(info->name), info->name);
+ return -EINVAL;
+ }
+
+ /*
+ * Let's acquire the lock, check for the condition and add it
+ * or increase the reference counter.
+ */
+ mutex_lock(&proc_lock);
+ list_for_each_entry(var, &cond_net->list, list) {
+ if (strcmp(info->name, var->status_proc->name) == 0) {
+ ++var->refcount;
+ mutex_unlock(&proc_lock);
+ info->condvar = var;
+ return 0;
+ }
+ }
+
+ /* At this point, we need to allocate a new condition variable. */
+ var = kmalloc(sizeof(struct condition_variable), GFP_KERNEL);
+ if (var == NULL) {
+ mutex_unlock(&proc_lock);
+ return -ENOMEM;
+ }
+
+ /* Create the condition variable's proc file entry. */
+ var->status_proc = create_proc_entry(info->name,
+ condition_list_perms,
+ cond_net->proc_dir);
+ if (var->status_proc == NULL) {
+ kfree(var);
+ mutex_unlock(&proc_lock);
+ return -ENOMEM;
+ }
+
+ var->refcount = 1;
+ var->enabled = false;
+ var->status_proc->data = var;
+ var->status_proc->read_proc = condition_proc_read;
+ var->status_proc->write_proc = condition_proc_write;
+ var->status_proc->uid = condition_uid_perms;
+ var->status_proc->gid = condition_gid_perms;
+ list_add(&var->list, &cond_net->list);
+ mutex_unlock(&proc_lock);
+ info->condvar = var;
+ return 0;
+}
+
+static void condition_mt_destroy(const struct xt_mtdtor_param *par)
+{
+ const struct xt_condition_mtinfo *info = par->matchinfo;
+ struct condition_variable *var = info->condvar;
+ struct condition_net *cond_net = condition_pernet(par->net);
+
+ mutex_lock(&proc_lock);
+ if (--var->refcount == 0) {
+ list_del(&var->list);
+ /* status_proc may be null in case of ns exit */
+ if (var->status_proc)
+ remove_proc_entry(var->status_proc->name,
+ cond_net->proc_dir);
+ mutex_unlock(&proc_lock);
+ kfree(var);
+ return;
+ }
+ mutex_unlock(&proc_lock);
+}
+
+static struct xt_match condition_mt_reg __read_mostly = {
+ .name = "condition",
+ .revision = 1,
+ .family = NFPROTO_UNSPEC,
+ .matchsize = sizeof(struct xt_condition_mtinfo),
+ .match = condition_mt,
+ .checkentry = condition_mt_check,
+ .destroy = condition_mt_destroy,
+ .me = THIS_MODULE,
+};
+
+static const char *const dir_name = "nf_condition";
+
+static int __net_init condnet_mt_init(struct net *net)
+{
+ struct condition_net *cond_net = condition_pernet(net);
+
+ INIT_LIST_HEAD(&cond_net->list);
+
+ cond_net->proc_dir = proc_mkdir(dir_name, net->proc_net);
+
+ return (cond_net->proc_dir == NULL) ? -EACCES : 0;
+}
+
+static void __net_exit condnet_mt_exit(struct net *net)
+{
+ struct condition_net *cond_net = condition_pernet(net);
+ struct condition_variable *var, *tmp;
+
+ mutex_lock(&proc_lock);
+ list_for_each_entry_safe(var, tmp, &cond_net->list, list) {
+ remove_proc_entry(var->status_proc->name,
+ cond_net->proc_dir);
+ /* set to null so we don't double remove in mt_destroy */
+ var->status_proc = NULL;
+ }
+
+ mutex_unlock(&proc_lock);
+
+ remove_proc_entry(dir_name, net->proc_net);
+}
+
+static struct pernet_operations condition_mt_netops = {
+ .init = condnet_mt_init,
+ .exit = condnet_mt_exit,
+ .id = &condition_net_id,
+ .size = sizeof(struct condition_net),
+};
+
+static int __init condition_mt_init(void)
+{
+ int ret;
+
+ mutex_init(&proc_lock);
+ ret = xt_register_match(&condition_mt_reg);
+ if (ret < 0)
+ return ret;
+
+ ret = register_pernet_subsys(&condition_mt_netops);
+ if (ret < 0) {
+ xt_unregister_match(&condition_mt_reg);
+ return ret;
+ }
+
+ return 0;
+}
+
+static void __exit condition_mt_exit(void)
+{
+ unregister_pernet_subsys(&condition_mt_netops);
+ xt_unregister_match(&condition_mt_reg);
+}
+
+module_init(condition_mt_init);
+module_exit(condition_mt_exit);
--
1.7.0.4
^ permalink raw reply related
* [PATCH v2 2/2] netfilter: xt_condition: change the value from boolean to u32
From: luciano.coelho @ 2010-08-06 12:21 UTC (permalink / raw)
To: netfilter-devel; +Cc: netdev, kaber, jengelh, sameo
In-Reply-To: <1281097308-1515-1-git-send-email-luciano.coelho@nokia.com>
From: Luciano Coelho <luciano.coelho@nokia.com>
Previously the condition match was using boolean values for the condition,
which is a bit limited, so this patch changes the value to a u32.
This is not a problem at the moment, since only userspace can set the
condition and it can do more advanced checks before setting the value. But
when the condition target gets implemented, having a u32 value will make it
much more flexible.
Signed-off-by: Luciano Coelho <luciano.coelho@nokia.com>
---
include/linux/netfilter/xt_condition.h | 3 +-
net/netfilter/xt_condition.c | 54 +++++++++++++++++---------------
2 files changed, 31 insertions(+), 26 deletions(-)
diff --git a/include/linux/netfilter/xt_condition.h b/include/linux/netfilter/xt_condition.h
index 4faf3ca..c4fe899 100644
--- a/include/linux/netfilter/xt_condition.h
+++ b/include/linux/netfilter/xt_condition.h
@@ -4,8 +4,9 @@
#include <linux/types.h>
struct xt_condition_mtinfo {
- char name[31];
+ char name[27];
__u8 invert;
+ __u32 value;
/* Used internally by the kernel */
void *condvar __attribute__((aligned(8)));
diff --git a/net/netfilter/xt_condition.c b/net/netfilter/xt_condition.c
index a78d832..cc9ada9 100644
--- a/net/netfilter/xt_condition.c
+++ b/net/netfilter/xt_condition.c
@@ -48,7 +48,7 @@ struct condition_variable {
struct list_head list;
struct proc_dir_entry *status_proc;
unsigned int refcount;
- bool enabled;
+ u32 value;
};
struct condition_net {
@@ -71,32 +71,36 @@ static int condition_proc_read(char __user *buffer, char **start, off_t offset,
{
const struct condition_variable *var = data;
- buffer[0] = var->enabled ? '1' : '0';
- buffer[1] = '\n';
- if (length >= 2)
- *eof = true;
- return 2;
+ return snprintf(buffer, length, "%u\n", var->value);
}
-static int condition_proc_write(struct file *file, const char __user *buffer,
+static int condition_proc_write(struct file *file, const char __user *input,
unsigned long length, void *data)
{
struct condition_variable *var = data;
- char newval;
-
- if (length > 0) {
- if (get_user(newval, buffer) != 0)
- return -EFAULT;
- /* Match only on the first character */
- switch (newval) {
- case '0':
- var->enabled = false;
- break;
- case '1':
- var->enabled = true;
- break;
- }
- }
+ /* the longest possible string is MAX_UINT in octal */
+ char buf[sizeof("+037777777777")];
+ unsigned long long value;
+
+ if (length == 0)
+ return 0;
+
+ if (length > sizeof(buf))
+ return -EINVAL;
+
+ if (copy_from_user(buf, input, length) != 0)
+ return -EFAULT;
+
+ buf[length - 1] = '\0';
+
+ if (strict_strtoull(buf, 0, &value) != 0)
+ return -EINVAL;
+
+ if (value > (u32) value)
+ return -ERANGE;
+
+ var->value = value;
+
return length;
}
@@ -106,7 +110,7 @@ condition_mt(const struct sk_buff *skb, struct xt_action_param *par)
const struct xt_condition_mtinfo *info = par->matchinfo;
const struct condition_variable *var = info->condvar;
- return var->enabled ^ info->invert;
+ return (var->value == info->value) ^ info->invert;
}
static int condition_mt_check(const struct xt_mtchk_param *par)
@@ -156,7 +160,7 @@ static int condition_mt_check(const struct xt_mtchk_param *par)
}
var->refcount = 1;
- var->enabled = false;
+ var->value = 0;
var->status_proc->data = var;
var->status_proc->read_proc = condition_proc_read;
var->status_proc->write_proc = condition_proc_write;
@@ -190,7 +194,7 @@ static void condition_mt_destroy(const struct xt_mtdtor_param *par)
static struct xt_match condition_mt_reg __read_mostly = {
.name = "condition",
- .revision = 1,
+ .revision = 2,
.family = NFPROTO_UNSPEC,
.matchsize = sizeof(struct xt_condition_mtinfo),
.match = condition_mt,
--
1.7.0.4
^ permalink raw reply related
* [PATCH v2 0/2] netfilter: xtables: xt_condition inclusion and change to u32
From: luciano.coelho @ 2010-08-06 12:21 UTC (permalink / raw)
To: netfilter-devel; +Cc: netdev, kaber, jengelh, sameo
From: Luciano Coelho <luciano.coelho@nokia.com>
Hello,
Here v2 of the xt_condition patch, it contains some changes suggested by Jan.
When these patches get accepted, I'll send the implementation of the condition
target, as discussed before.
In RFC v2 I've made a few changes as discussed in the review:
* Removed per-netns module parameters
* Use par->net instead of current->nsproxy->net_ns
* Fix file-leak in procfs when exiting the netns
I didn't get any more comments in RFC v2, so I assume it is okay to send it for
inclusion.
>From [RFC v2] to [PATCH], I've only rebased and added a new patch to support
u32 instead of boolean as the value for the condition.
In PATCH v2:
* Bumped up the revision to 2
* Changed the buffer declaration to have the size of the biggest possible u32 string
* Return -ERANGE instead of -EINVALID when the value passed is too big.
Cheers,
Luca.
Luciano Coelho (2):
netfilter: xtables: inclusion of xt_condition
netfilter: xt_condition: change the value from boolean to u32
include/linux/netfilter/Kbuild | 1 +
include/linux/netfilter/xt_condition.h | 15 ++
net/netfilter/Kconfig | 8 +
net/netfilter/Makefile | 1 +
net/netfilter/xt_condition.c | 269 ++++++++++++++++++++++++++++++++
5 files changed, 294 insertions(+), 0 deletions(-)
create mode 100644 include/linux/netfilter/xt_condition.h
create mode 100644 net/netfilter/xt_condition.c
^ permalink raw reply
* Re: [PATCH] phy: SMSC: fix unused warnings when built into kernel
From: Ben Hutchings @ 2010-08-06 12:57 UTC (permalink / raw)
To: Changli Gao; +Cc: Mike Frysinger, netdev, David S. Miller
In-Reply-To: <AANLkTi=_-ZZ8nyFLiBKSWNTcM_SkwLe2-ZL17zBQb0i4@mail.gmail.com>
On Fri, 2010-08-06 at 14:43 +0800, Changli Gao wrote:
> On Fri, Aug 6, 2010 at 2:27 PM, Mike Frysinger <vapier@gentoo.org> wrote:
> > If the SMSC phy is not built as a module, we get the warning:
> > drivers/net/phy/smsc.c:257: warning: 'smsc_tbl' defined but not used
> >
> > So mark the structure as maybe unused to avoid this.
> >
> > Signed-off-by: Mike Frysinger <vapier@gentoo.org>
> > ---
> > drivers/net/phy/smsc.c | 2 +-
> > 1 files changed, 1 insertions(+), 1 deletions(-)
> >
> > diff --git a/drivers/net/phy/smsc.c b/drivers/net/phy/smsc.c
> > index 78fa988..11944ef 100644
> > --- a/drivers/net/phy/smsc.c
> > +++ b/drivers/net/phy/smsc.c
> > @@ -254,7 +254,7 @@ MODULE_LICENSE("GPL");
> > module_init(smsc_init);
> > module_exit(smsc_exit);
> >
> > -static struct mdio_device_id smsc_tbl[] = {
> > +static __maybe_unused struct mdio_device_id smsc_tbl[] = {
> > { 0x0007c0a0, 0xfffffff0 },
> > { 0x0007c0b0, 0xfffffff0 },
> > { 0x0007c0c0, 0xfffffff0 },
>
> How about adding a #ifdef CONFIG_SMSC_PHY_MODULE clause around these lines?
By default, gcc will discard unused variables and functions declared as
static. So this would not provide any run-time benefit.
Ben.
--
Ben Hutchings, Senior Software Engineer, Solarflare Communications
Not speaking for my employer; that's the marketing department's job.
They asked us to note that Solarflare product names are trademarked.
^ permalink raw reply
* IPVS network namespace aware ?
From: Hans Schillstrom @ 2010-08-06 12:57 UTC (permalink / raw)
To: netfilter-devel; +Cc: netdev
Hello
Is there any one that have made ipvs network namespace aware ?
There is an urgent need of it within Ericsson.
I could not find any trace of it in any of the normal lists.
I will start patching ipvs next week or give a helping hand to any
existing work.
Regards
Hans Schillstrom
^ permalink raw reply
* Re: [ANNOUNCE]: Release of iptables-1.4.9
From: Patrick McHardy @ 2010-08-06 13:15 UTC (permalink / raw)
To: Jan Engelhardt
Cc: Gabor Z. Papp, Netfilter Development Mailinglist,
Linux Netdev List, 'netfilter@vger.kernel.org',
netfilter-announce
In-Reply-To: <alpine.LSU.2.01.1008032001430.1519@obet.zrqbmnf.qr>
Am 03.08.2010 20:04, schrieb Jan Engelhardt:
> The following changes since commit 371cea299f0b2eb100b9fc9fb99089640d2d606f:
>
> xtables: remove unnecessary cast (2010-08-03 19:56:11 +0200)
>
> are available in the git repository at:
> git://dev.medozas.de/iptables master
>
> Jan Engelhardt (1):
> build: fix static linking
>
> extensions/libxt_IDLETIMER.c | 2 +-
> extensions/libxt_TEE.c | 2 +-
> 2 files changed, 2 insertions(+), 2 deletions(-)
> [Actually there's also the "remove unnecessary cast" patch that is not
> included in this listing, but merging that should be ok.]
Pulled, thanks Jan.
^ permalink raw reply
* [PATCH NEXT 1/1] netxen: protect tx timeout recovery by rtnl lock
From: Amit Kumar Salecha @ 2010-08-06 13:33 UTC (permalink / raw)
To: davem; +Cc: netdev, ameen.rahman
Signed-off-by: Amit Kumar Salecha <amit.salecha@qlogic.com>
---
drivers/net/netxen/netxen_nic_main.c | 15 +++++++--------
1 files changed, 7 insertions(+), 8 deletions(-)
diff --git a/drivers/net/netxen/netxen_nic_main.c b/drivers/net/netxen/netxen_nic_main.c
index 6ce6ce1..fd86e18 100644
--- a/drivers/net/netxen/netxen_nic_main.c
+++ b/drivers/net/netxen/netxen_nic_main.c
@@ -2001,27 +2001,26 @@ static void netxen_tx_timeout_task(struct work_struct *work)
if (++adapter->tx_timeo_cnt >= NX_MAX_TX_TIMEOUTS)
goto request_reset;
+ rtnl_lock();
if (NX_IS_REVISION_P2(adapter->ahw.revision_id)) {
/* try to scrub interrupt */
netxen_napi_disable(adapter);
- adapter->netdev->trans_start = jiffies;
-
netxen_napi_enable(adapter);
netif_wake_queue(adapter->netdev);
clear_bit(__NX_RESETTING, &adapter->state);
- return;
} else {
clear_bit(__NX_RESETTING, &adapter->state);
- if (!netxen_nic_reset_context(adapter)) {
- adapter->netdev->trans_start = jiffies;
- return;
+ if (netxen_nic_reset_context(adapter)) {
+ rtnl_unlock();
+ goto request_reset;
}
-
- /* context reset failed, fall through for fw reset */
}
+ adapter->netdev->trans_start = jiffies;
+ rtnl_unlock();
+ return;
request_reset:
adapter->need_fw_reset = 1;
--
1.6.0.2
^ permalink raw reply related
* [PATCH ethtool] ethtool: Fix handling of zero weights for flow hash indirection
From: Ben Hutchings @ 2010-08-06 13:38 UTC (permalink / raw)
To: Jeff Garzik; +Cc: netdev, linux-net-drivers
The loop to generate an indirection table from a list of weights never
advances by more than one weight at a time. Thus, if there is a 0
in the list (except at the end) the corresponding RX ring will be
assigned 1 hash bucket rather than 0. Change 'if' to 'while'.
Signed-off-by: Ben Hutchings <bhutchings@solarflare.com>
---
Please include this in 2.6.35 if you haven't finalised it yet.
Ben.
ethtool.c | 2 +-
1 files changed, 1 insertions(+), 1 deletions(-)
diff --git a/ethtool.c b/ethtool.c
index 0f7dec6..66b5c07 100644
--- a/ethtool.c
+++ b/ethtool.c
@@ -2977,7 +2977,7 @@ static int do_srxfhindir(int fd, struct ifreq *ifr)
j = -1;
for (i = 0; i < indir->size; i++) {
- if (i >= indir->size * partial / sum) {
+ while (i >= indir->size * partial / sum) {
j += 1;
weight = get_u32(rxfhindir_weight[j], 0);
partial += weight;
--
1.6.2.5
--
Ben Hutchings, Senior Software Engineer, Solarflare Communications
Not speaking for my employer; that's the marketing department's job.
They asked us to note that Solarflare product names are trademarked.
^ permalink raw reply related
* [ANNOUNCE]: Release of iptables-1.4.9.1
From: Patrick McHardy @ 2010-08-06 13:51 UTC (permalink / raw)
To: Netfilter Development Mailinglist, NetDev, netfilter-announce,
"'netfilter@vger.kernel.org'" <netfilter
[-- Attachment #1: Type: text/plain, Size: 423 bytes --]
The netfilter coreteam presents:
iptables version 1.4.9.1
the iptables release for the 2.6.35 kernel. This release fixes
a compilation problem with static linking in the 1.4.9 release.
Version 1.4.9.1 can be obtained from:
http://www.netfilter.org/projects/iptables/downloads.html
ftp://ftp.netfilter.org/pub/iptables/
git://git.netfilter.org/iptables.git
On behalf of the Netfilter Core Team.
Happy firewalling!
[-- Attachment #2: changes-iptables-1.4.9.1.txt --]
[-- Type: text/plain, Size: 105 bytes --]
Jan Engelhardt (1):
build: fix static linking
Patrick McHardy (1):
Bump version to 1.4.9.1
^ permalink raw reply
* Re: [PATCH v2 0/2] netfilter: xtables: xt_condition inclusion and change to u32
From: Patrick McHardy @ 2010-08-06 13:54 UTC (permalink / raw)
To: luciano.coelho; +Cc: netfilter-devel, netdev, jengelh, sameo
In-Reply-To: <1281097308-1515-1-git-send-email-luciano.coelho@nokia.com>
Am 06.08.2010 14:21, schrieb luciano.coelho@nokia.com:
> From: Luciano Coelho <luciano.coelho@nokia.com>
>
> Hello,
>
> Here v2 of the xt_condition patch, it contains some changes suggested by Jan.
>
> When these patches get accepted, I'll send the implementation of the condition
> target, as discussed before.
>
> In RFC v2 I've made a few changes as discussed in the review:
>
> * Removed per-netns module parameters
> * Use par->net instead of current->nsproxy->net_ns
> * Fix file-leak in procfs when exiting the netns
>
> I didn't get any more comments in RFC v2, so I assume it is okay to send it for
> inclusion.
The merge window for networking patches is closed, I'm only taking
bugfixes at this time. Please resend once net-next opens up again.
^ permalink raw reply
* Re: IPVS network namespace aware ?
From: Simon Horman @ 2010-08-06 13:59 UTC (permalink / raw)
To: Hans Schillstrom; +Cc: netfilter-devel, netdev, lvs-devel
In-Reply-To: <1281099450.2518.21.camel@seasc0214.dyn.rnd.as.sw.ericsson.se>
[ Cced lvs-devel ]
On Fri, Aug 06, 2010 at 02:57:30PM +0200, Hans Schillstrom wrote:
> Hello
> Is there any one that have made ipvs network namespace aware ?
> There is an urgent need of it within Ericsson.
> I could not find any trace of it in any of the normal lists.
>
> I will start patching ipvs next week or give a helping hand to any
> existing work.
Hi Hans,
I am not aware of any other work in this area.
Please send any patches here :-)
^ permalink raw reply
* Re: [rfc 02/13] [RFC 02/13] netfilter: nf_conntrack_sip: Add callid parser
From: Patrick McHardy @ 2010-08-06 14:00 UTC (permalink / raw)
To: Simon Horman
Cc: lvs-devel, netdev, netfilter, netfilter-devel, Wensong Zhang,
Julian Anastasov
In-Reply-To: <20100805115239.880722744@vergenet.net>
Am 05.08.2010 13:47, schrieb Simon Horman:
> diff --git a/net/netfilter/nf_conntrack_sip.c b/net/netfilter/nf_conntrack_sip.c
> index 2fd1ea2..715ce54 100644
> --- a/net/netfilter/nf_conntrack_sip.c
> +++ b/net/netfilter/nf_conntrack_sip.c
> @@ -130,6 +130,44 @@ static int digits_len(const struct nf_conn *ct, const char *dptr,
> return len;
> }
>
> +static int iswordc(const char c)
> +{
> + if (isalnum(c) || c == '!' || c == '"' || c == '%' ||
> + (c >= '(' && c <= '/') || c == ':' || c == '<' || c == '>' ||
> + c == '?' || (c >= '[' && c <= ']') || c == '_' || c == '`' ||
> + c == '{' || c == '}' || c == '~')
> + return 1;
> + return 0;
> +}
> +
> +static int word_len(const char *dptr, const char *limit)
> +{
> + int len = 0;
> + while (dptr < limit && iswordc(*dptr)) {
> + dptr++;
> + len++;
> + }
> + return len;
> +}
Since the Call-ID can't contain whitespace, couldn't we simply
determine the length by looking for the next newline or whitespace
character?
^ permalink raw reply
* Re: [PATCH v2 0/2] netfilter: xtables: xt_condition inclusion and change to u32
From: Luciano Coelho @ 2010-08-06 14:06 UTC (permalink / raw)
To: ext Patrick McHardy
Cc: netfilter-devel@vger.kernel.org, netdev@vger.kernel.org,
jengelh@medozas.de, sameo@linux.intel.com
In-Reply-To: <4C5C1411.7020709@trash.net>
On Fri, 2010-08-06 at 15:54 +0200, ext Patrick McHardy wrote:
> Am 06.08.2010 14:21, schrieb luciano.coelho@nokia.com:
> > From: Luciano Coelho <luciano.coelho@nokia.com>
> >
> > Hello,
> >
> > Here v2 of the xt_condition patch, it contains some changes suggested by Jan.
> >
> > When these patches get accepted, I'll send the implementation of the condition
> > target, as discussed before.
> >
> > In RFC v2 I've made a few changes as discussed in the review:
> >
> > * Removed per-netns module parameters
> > * Use par->net instead of current->nsproxy->net_ns
> > * Fix file-leak in procfs when exiting the netns
> >
> > I didn't get any more comments in RFC v2, so I assume it is okay to send it for
> > inclusion.
>
> The merge window for networking patches is closed, I'm only taking
> bugfixes at this time. Please resend once net-next opens up again.
Ok, I'll resend once the trees are open again.
I'll probably include some more patches on top of this then.
--
Cheers,
Luca.
^ permalink raw reply
* Re: [RFC PATCH] platform: Faciliatate the creation of pseduo-platform busses
From: Greg KH @ 2010-08-06 14:27 UTC (permalink / raw)
To: Grant Likely
Cc: Patrick Pannuto, Patrick Pannuto, linux-kernel@vger.kernel.org,
linux-arm-msm@vger.kernel.org, linux-omap@vger.kernel.org,
damm@opensource.se, lethal@linux-sh.org, rjw@sisk.pl,
dtor@mail.ru, eric.y.miao@gmail.com, netdev@vger.kernel.org,
Kevin Hilman
In-Reply-To: <AANLkTikVzh9=DvvUPbKmDeo6k2-pPVSnjH+GxAPdQFO4@mail.gmail.com>
On Thu, Aug 05, 2010 at 04:59:35PM -0600, Grant Likely wrote:
> (On that point Greg, what is the reason for even having the
> /sys/devices/platform/ parent? Why not just let the platform devices
> sit at the root of the device tree? In the OF case (granted, I'm
> biased) all of the platform_device registrations reflect the actual
> device hierarchy expressed in the device tree data.)
If we sat them at the "root", there would be a bunch of them there. I
don't know, we could drop the parent, I guess whoever created the
platform device oh so long ago, decided that it would look nicer to be
in this type of structure.
> Now, having gone on this whole long tirade, it looks like having
> separate platform bus types may not be the best approach after all.
I totally agree, and thanks for the detailed explaination, it saved me
from having to write up the same thing :)
greg k-h
^ permalink raw reply
* [PATCH v2 resend] netfilter: ipt_REJECT can't send TCP reset
From: Changli Gao @ 2010-08-06 14:30 UTC (permalink / raw)
To: Patrick McHardy
Cc: David S. Miller, Alexey Kuznetsov, Pekka Savola (ipv6),
James Morris, Hideaki YOSHIFUJI, netfilter-devel, netdev,
Changli Gao
ip_route_me_harder can't create the route cache when the outdev is the same
with the indev for the skbs whichout a valid protocol set.
__mkroute_input functions has this check:
1998 if (skb->protocol != htons(ETH_P_IP)) {
1999 /* Not IP (i.e. ARP). Do not create route, if it is
2000 * invalid for proxy arp. DNAT routes are always valid.
2001 *
2002 * Proxy arp feature have been extended to allow, ARP
2003 * replies back to the same interface, to support
2004 * Private VLAN switch technologies. See arp.c.
2005 */
2006 if (out_dev == in_dev &&
2007 IN_DEV_PROXY_ARP_PVLAN(in_dev) == 0) {
2008 err = -EINVAL;
2009 goto cleanup;
2010 }
2011 }
This patch gives the new skb a valid protocol to bypass this check. In order
to make ipt_REJECT work with bridges, you also need to enable ip_forward.
This patch also fixes a regression. When we used skb_copy_expand(), we
didn't have this issue stated above, as the protocol was properly set.
Signed-off-by: Changli Gao <xiaosuo@gmail.com>
---
v2: updated changelog
net/ipv4/netfilter/ipt_REJECT.c | 1 +
1 file changed, 1 insertion(+)
diff --git a/net/ipv4/netfilter/ipt_REJECT.c b/net/ipv4/netfilter/ipt_REJECT.c
index b254daf..43eec80 100644
--- a/net/ipv4/netfilter/ipt_REJECT.c
+++ b/net/ipv4/netfilter/ipt_REJECT.c
@@ -112,6 +112,7 @@ static void send_reset(struct sk_buff *oldskb, int hook)
/* ip_route_me_harder expects skb->dst to be set */
skb_dst_set_noref(nskb, skb_dst(oldskb));
+ nskb->protocol = htons(ETH_P_IP);
if (ip_route_me_harder(nskb, addr_type))
goto free_nskb;
^ permalink raw reply related
* Re: [rfc 02/13] [RFC 02/13] netfilter: nf_conntrack_sip: Add callid parser
From: Simon Horman @ 2010-08-06 14:31 UTC (permalink / raw)
To: Patrick McHardy
Cc: lvs-devel, netdev, netfilter, netfilter-devel, Wensong Zhang,
Julian Anastasov
In-Reply-To: <4C5C1591.90108@trash.net>
On Fri, Aug 06, 2010 at 04:00:49PM +0200, Patrick McHardy wrote:
> Am 05.08.2010 13:47, schrieb Simon Horman:
> > diff --git a/net/netfilter/nf_conntrack_sip.c b/net/netfilter/nf_conntrack_sip.c
> > index 2fd1ea2..715ce54 100644
> > --- a/net/netfilter/nf_conntrack_sip.c
> > +++ b/net/netfilter/nf_conntrack_sip.c
> > @@ -130,6 +130,44 @@ static int digits_len(const struct nf_conn *ct, const char *dptr,
> > return len;
> > }
> >
> > +static int iswordc(const char c)
> > +{
> > + if (isalnum(c) || c == '!' || c == '"' || c == '%' ||
> > + (c >= '(' && c <= '/') || c == ':' || c == '<' || c == '>' ||
> > + c == '?' || (c >= '[' && c <= ']') || c == '_' || c == '`' ||
> > + c == '{' || c == '}' || c == '~')
> > + return 1;
> > + return 0;
> > +}
> > +
> > +static int word_len(const char *dptr, const char *limit)
> > +{
> > + int len = 0;
> > + while (dptr < limit && iswordc(*dptr)) {
> > + dptr++;
> > + len++;
> > + }
> > + return len;
> > +}
>
> Since the Call-ID can't contain whitespace, couldn't we simply
> determine the length by looking for the next newline or whitespace
> character?
Well, there are other characters (e.g. '#') it can't contain - unless I
read the RFC incorrectly. Are you concerned about speed, code complexity,
or something else?
^ permalink raw reply
* [PATCH] net: add Fast Ethernet driver for PXA168.
From: Sachin Sanap @ 2010-08-06 19:39 UTC (permalink / raw)
To: buytenh; +Cc: netdev, akarkare, sarnaik, eric.y.miao, prakity, markb, ssanap
Signed-off-by: Sachin Sanap <ssanap@marvell.com>
---
arch/arm/mach-mmp/aspenite.c | 3 +-
arch/arm/mach-mmp/pxa168.c | 4 +-
drivers/net/Kconfig | 10 +
drivers/net/Makefile | 1 +
drivers/net/pxa168_eth.c | 1592 ++++++++++++++++++++++++++++++++++++++++++
include/linux/pxa168_eth.h | 20 +
6 files changed, 1626 insertions(+), 4 deletions(-)
create mode 100644 drivers/net/pxa168_eth.c
create mode 100644 include/linux/pxa168_eth.h
diff --git a/arch/arm/mach-mmp/aspenite.c b/arch/arm/mach-mmp/aspenite.c
index 61a6d6a..cec505f 100644
--- a/arch/arm/mach-mmp/aspenite.c
+++ b/arch/arm/mach-mmp/aspenite.c
@@ -200,8 +200,7 @@ static int pxa168_eth_init(void)
}
static struct pxa168_eth_platform_data pxa168_eth_data = {
- .phy_addr = 0, /* phy addr depends on boards */
- .port_number = 0,
+ .phy_addr = 0,
.init = pxa168_eth_init,
};
#endif
diff --git a/arch/arm/mach-mmp/pxa168.c b/arch/arm/mach-mmp/pxa168.c
index f7d1158..6fcaf0c 100644
--- a/arch/arm/mach-mmp/pxa168.c
+++ b/arch/arm/mach-mmp/pxa168.c
@@ -97,7 +97,7 @@ static struct clk_lookup pxa168_clkregs[] = {
INIT_CLKREG(&clk_ssp4, "pxa168-ssp.3", NULL),
INIT_CLKREG(&clk_ssp5, "pxa168-ssp.4", NULL),
INIT_CLKREG(&clk_nand, "pxa3xx-nand", NULL),
- INIT_CLKREG(&clk_mfu, "pxa168-mfu", "MFUCLK"),
+ INIT_CLKREG(&clk_mfu, "pxa168-eth", "MFUCLK"),
};
static int __init pxa168_init(void)
@@ -149,4 +149,4 @@ PXA168_DEVICE(ssp2, "pxa168-ssp", 1, SSP2, 0xd401c000, 0x40, 54, 55);
PXA168_DEVICE(ssp3, "pxa168-ssp", 2, SSP3, 0xd401f000, 0x40, 56, 57);
PXA168_DEVICE(ssp4, "pxa168-ssp", 3, SSP4, 0xd4020000, 0x40, 58, 59);
PXA168_DEVICE(ssp5, "pxa168-ssp", 4, SSP5, 0xd4021000, 0x40, 60, 61);
-PXA168_DEVICE(mfu, "pxa168-mfu", -1, MFU, 0xc0800000, 0x0fff);
+PXA168_DEVICE(mfu, "pxa168-eth", -1, MFU, 0xc0800000, 0x0fff);
diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig
index ce2fcdd..78cd7e8 100644
--- a/drivers/net/Kconfig
+++ b/drivers/net/Kconfig
@@ -927,6 +927,16 @@ config SMC91X
The module will be called smc91x. If you want to compile it as a
module, say M here and read <file:Documentation/kbuild/modules.txt>.
+config PXA168_ETH
+ tristate "Marvell pxa168 ethernet support"
+ depends on CPU_PXA168
+ select PHYLIB
+ help
+ This driver supports the pxa168 Ethernet ports.
+
+ To compile this driver as a module, choose M here. The module
+ will be called pxa168_eth.
+
config NET_NETX
tristate "NetX Ethernet support"
select MII
diff --git a/drivers/net/Makefile b/drivers/net/Makefile
index 0a0512a..a42d437 100644
--- a/drivers/net/Makefile
+++ b/drivers/net/Makefile
@@ -244,6 +244,7 @@ obj-$(CONFIG_MYRI10GE) += myri10ge/
obj-$(CONFIG_SMC91X) += smc91x.o
obj-$(CONFIG_SMC911X) += smc911x.o
obj-$(CONFIG_SMSC911X) += smsc911x.o
+obj-$(CONFIG_PXA168_ETH) += pxa168_eth.o
obj-$(CONFIG_BFIN_MAC) += bfin_mac.o
obj-$(CONFIG_DM9000) += dm9000.o
obj-$(CONFIG_PASEMI_MAC) += pasemi_mac_driver.o
diff --git a/drivers/net/pxa168_eth.c b/drivers/net/pxa168_eth.c
new file mode 100644
index 0000000..c00fc3c
--- /dev/null
+++ b/drivers/net/pxa168_eth.c
@@ -0,0 +1,1592 @@
+/*
+ * PXA168 ethernet driver.
+ * Most of the code is derived from mv643xx ethernet driver.
+ *
+ * Copyright (C) 2010 Marvell International Ltd.
+ * Philip Rakity <prakity@marvell.com>
+ * Mark Brown <markb@marvell.com>
+ * Sachin Sanap <ssanap@marvell.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <linux/init.h>
+#include <linux/dma-mapping.h>
+#include <linux/in.h>
+#include <linux/ip.h>
+#include <linux/tcp.h>
+#include <linux/udp.h>
+#include <linux/etherdevice.h>
+#include <linux/bitops.h>
+#include <linux/delay.h>
+#include <linux/ethtool.h>
+#include <linux/platform_device.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/workqueue.h>
+#include <linux/clk.h>
+#include <linux/phy.h>
+#include <linux/io.h>
+#include <linux/types.h>
+#include <asm/pgtable.h>
+#include <asm/system.h>
+#include <linux/delay.h>
+#include <linux/dma-mapping.h>
+#include <asm/cacheflush.h>
+#include <linux/pxa168_eth.h>
+
+#define DRIVER_NAME "pxa168-eth"
+#define DRIVER_VERSION "0.3"
+
+/*
+ * Registers
+ */
+
+#define PHY_ADDRESS 0x0000
+#define SMI 0x0010
+#define PORT_CONFIG 0x0400
+#define PORT_CONFIG_EXT 0x0408
+#define PORT_COMMAND 0x0410
+#define PORT_STATUS 0x0418
+#define HTPR 0x0428
+#define SDMA_CONFIG 0x0440
+#define SDMA_CMD 0x0448
+#define INT_CAUSE 0x0450
+#define INT_W_CLEAR 0x0454
+#define INT_MASK 0x0458
+#define ETH_F_RX_DESC_0 0x0480
+#define ETH_C_RX_DESC_0 0x04A0
+#define ETH_C_TX_DESC_1 0x04E4
+
+/* smi register */
+#define SMI_BUSY (1 << 28) /* 0 - Write, 1 - Read */
+#define SMI_R_VALID (1 << 27) /* 0 - Write, 1 - Read */
+#define SMI_OP_W (0 << 26) /* Write operation */
+#define SMI_OP_R (1 << 26) /* Read operation */
+
+#define PHY_WAIT_ITERATIONS 500
+
+#define PXA168_ETH_PHY_ADDR_DEFAULT 0
+/* RX & TX descriptor command */
+#define BUF_OWNED_BY_DMA (1 << 31)
+
+/* RX descriptor status */
+#define RX_EN_INT (1 << 23)
+#define RX_FIRST_DESC (1 << 17)
+#define RX_LAST_DESC (1 << 16)
+#define RX_ERROR (1 << 15)
+
+/* TX descriptor command */
+#define TX_EN_INT (1 << 23)
+#define TX_GEN_CRC (1 << 22)
+#define TX_ZERO_PADDING (1 << 18)
+#define TX_FIRST_DESC (1 << 17)
+#define TX_LAST_DESC (1 << 16)
+#define TX_ERROR (1 << 15)
+
+/* SDMA_CMD */
+#define SDMA_CMD_AT (1 << 31)
+#define SDMA_CMD_TXDL (1 << 24)
+#define SDMA_CMD_TXDH (1 << 23)
+#define SDMA_CMD_AR (1 << 15)
+#define SDMA_CMD_ERD (1 << 7)
+
+/* Bit definitions of the Port Config Reg */
+#define PCR_HS (1 << 12)
+#define PCR_EN (1 << 7)
+#define PCR_PM (1 << 0)
+
+/* Bit definitions of the Port Config Extend Reg */
+#define PCXR_2BSM (1 << 28)
+#define PCXR_DSCP_EN (1 << 21)
+#define PCXR_MFL_1518 (0 << 14)
+#define PCXR_MFL_1536 (1 << 14)
+#define PCXR_MFL_2048 (2 << 14)
+#define PCXR_MFL_64K (3 << 14)
+#define PCXR_FLP (1 << 11)
+#define PCXR_PRIO_TX_OFF 3
+#define PCXR_TX_HIGH_PRI (7 << PCXR_PRIO_TX_OFF)
+
+/* Bit definitions of the SDMA Config Reg */
+#define SDCR_BSZ_OFF 12
+#define SDCR_BSZ8 (3 << SDCR_BSZ_OFF)
+#define SDCR_BSZ4 (2 << SDCR_BSZ_OFF)
+#define SDCR_BSZ2 (1 << SDCR_BSZ_OFF)
+#define SDCR_BSZ1 (0 << SDCR_BSZ_OFF)
+#define SDCR_BLMR (1 << 6)
+#define SDCR_BLMT (1 << 7)
+#define SDCR_RIFB (1 << 9)
+#define SDCR_RC_OFF 2
+#define SDCR_RC_MAX_RETRANS (0xf << SDCR_RC_OFF)
+
+/*
+ * Bit definitions of the Interrupt Cause Reg
+ * and Interrupt MASK Reg is the same
+ */
+#define ICR_RXBUF (1 << 0)
+#define ICR_TXBUF_H (1 << 2)
+#define ICR_TXBUF_L (1 << 3)
+#define ICR_TXEND_H (1 << 6)
+#define ICR_TXEND_L (1 << 7)
+#define ICR_RXERR (1 << 8)
+#define ICR_TXERR_H (1 << 10)
+#define ICR_TXERR_L (1 << 11)
+#define ICR_TX_UDR (1 << 13)
+#define ICR_MII_CH (1 << 28)
+
+#define ALL_INTS (ICR_TXBUF_H | ICR_TXBUF_L | ICR_TX_UDR |\
+ ICR_TXERR_H | ICR_TXERR_L |\
+ ICR_TXEND_H | ICR_TXEND_L |\
+ ICR_RXBUF | ICR_RXERR | ICR_MII_CH)
+
+#define ETH_HW_IP_ALIGN 2 /* hw aligns IP header */
+#define ETH_EXTRA_HEADER (6+6+2+4) /* dest+src addr+protocol id+crc */
+#define ETH_DATA_LEN 1500
+#define MAX_PKT_SIZE 1518
+
+#define NUM_RX_DESCS 64
+#define NUM_TX_DESCS 64
+#define MAX_DESCS_PER_HIGH (60)
+#define TX_DESC_COUNT_LOW (10)
+
+#define HASH_ADD 0
+#define HASH_DELETE 1
+#define HASH_ADDR_TABLE_SIZE 0x4000 /* 16K (1/2K address - PCR_HS == 1) */
+#define HOP_NUMBER 12
+
+/* Bit definitions for Port status */
+#define PORT_SPEED_100 (1 << 0)
+#define FULL_DUPLEX (1 << 1)
+#define FLOW_CONTROL_ENABLED (1 << 2)
+#define LINK_UP (1 << 3)
+
+/* Bit definitions for work to be done */
+#define WORK_LINK (1 << 0)
+#define WORK_TX_DONE (1 << 1)
+
+#define TX_DONE_INTERVAL 30
+
+struct rx_desc {
+ u32 cmd_sts; /* Descriptor command status */
+ u16 byte_cnt; /* Descriptor buffer byte count */
+ u16 buf_size; /* Buffer size */
+ u32 buf_ptr; /* Descriptor buffer pointer */
+ u32 next_desc_ptr; /* Next descriptor pointer */
+};
+
+struct tx_desc {
+ u32 cmd_sts; /* Command/status field */
+ u16 reserved;
+ u16 byte_cnt; /* buffer byte count */
+ u32 buf_ptr; /* pointer to buffer for this descriptor */
+ u32 next_desc_ptr; /* Pointer to next descriptor */
+};
+
+struct pxa168_eth_private {
+ int rx_resource_err; /* Rx ring resource error flag */
+
+ /* Next available and first returning Rx resource */
+ int rx_curr_desc_q, rx_used_desc_q;
+
+ /* Next available and first returning Tx resource */
+ int tx_curr_desc_q, tx_used_desc_q;
+
+ struct rx_desc *p_rx_desc_area;
+ dma_addr_t rx_desc_dma;
+ int rx_desc_area_size;
+ struct sk_buff **rx_skb;
+
+ struct tx_desc *p_tx_desc_area;
+ dma_addr_t tx_desc_dma;
+ int tx_desc_area_size;
+ struct sk_buff **tx_skb;
+
+ struct work_struct tx_timeout_task;
+
+ struct net_device *dev;
+ struct napi_struct napi;
+ u8 work_todo;
+
+ struct net_device_stats stats;
+ /* Size of Tx Ring per queue */
+ int tx_ring_size;
+ /* Number of tx descriptors in use */
+ int tx_desc_count;
+ /* Size of Rx Ring per queue */
+ int rx_ring_size;
+ /* Number of rx descriptors in use */
+ int rx_desc_count;
+
+ /*
+ * Used in case RX Ring is empty, which can occur when
+ * system does not have resources (skb's)
+ */
+ struct timer_list timeout;
+ struct mii_bus *smi_bus;
+ struct phy_device *phy;
+
+ /* clock */
+ struct clk *clk;
+ struct pxa168_eth_platform_data *pd;
+ /*
+ * Ethernet controller base address.
+ */
+ void __iomem *base;
+
+ /* Pointer to the hardware address filter table */
+ void *htpr;
+ dma_addr_t htpr_dma;
+};
+
+struct addr_table_entry {
+ u32 lo;
+ u32 hi;
+};
+
+/* Bit fields of a Hash Table Entry */
+enum hash_table_entry {
+ HASH_ENTRY_VALID = 1,
+ SKIP = 2,
+ HASH_ENTRY_RECEIVE_DISCARD = 4,
+ HASH_ENTRY_RECEIVE_DISCARD_BIT = 2
+};
+
+static int pxa168_get_settings(struct net_device *dev, struct ethtool_cmd *cmd);
+static int pxa168_set_settings(struct net_device *dev, struct ethtool_cmd *cmd);
+static int pxa168_init_hw(struct pxa168_eth_private *pep);
+static void eth_port_reset(struct net_device *dev);
+static void eth_port_start(struct net_device *dev);
+static int pxa168_eth_open(struct net_device *dev);
+static int pxa168_eth_stop(struct net_device *dev);
+static int ethernet_phy_setup(struct net_device *dev);
+
+static inline u32 rdl(struct pxa168_eth_private *pep, int offset)
+{
+ return readl(pep->base + offset);
+}
+
+static inline void wrl(struct pxa168_eth_private *pep, int offset, u32 data)
+{
+ writel(data, pep->base + offset);
+}
+
+static void abort_dma(struct pxa168_eth_private *pep)
+{
+ int delay;
+ int max_retries = 40;
+
+ do {
+ wrl(pep, SDMA_CMD, SDMA_CMD_AR | SDMA_CMD_AT);
+ udelay(100);
+
+ delay = 10;
+ while ((rdl(pep, SDMA_CMD) & (SDMA_CMD_AR | SDMA_CMD_AT))
+ && delay-- > 0) {
+ udelay(10);
+ }
+ } while (max_retries-- > 0 && delay <= 0);
+
+ if (max_retries <= 0)
+ printk(KERN_ERR "%s : DMA Stuck\n", __func__);
+}
+
+static int ethernet_phy_get(struct pxa168_eth_private *pep)
+{
+ unsigned int reg_data;
+
+ reg_data = rdl(pep, PHY_ADDRESS);
+
+ return reg_data & 0x1f;
+}
+
+static void ethernet_phy_set_addr(struct pxa168_eth_private *pep, int phy_addr)
+{
+ u32 reg_data;
+
+ reg_data = rdl(pep, PHY_ADDRESS);
+ reg_data &= ~(0x1f);
+ reg_data |= phy_addr & 0x1f;
+ wrl(pep, PHY_ADDRESS, reg_data);
+}
+
+static void ethernet_phy_reset(struct pxa168_eth_private *pep)
+{
+ int data;
+
+ data = phy_read(pep->phy, MII_BMCR);
+ if (data < 0)
+ return;
+
+ data |= BMCR_RESET;
+ if (phy_write(pep->phy, MII_BMCR, data) < 0)
+ return;
+
+ do {
+ data = phy_read(pep->phy, MII_BMCR);
+ } while (data >= 0 && data & BMCR_RESET);
+}
+
+static void rxq_refill(struct net_device *dev)
+{
+ struct pxa168_eth_private *pep = netdev_priv(dev);
+ struct sk_buff *skb;
+ struct rx_desc *p_used_rx_desc;
+ int used_rx_desc;
+
+ while (pep->rx_desc_count < pep->rx_ring_size) {
+ skb = dev_alloc_skb(MAX_PKT_SIZE + ETH_HW_IP_ALIGN);
+ if (!skb)
+ break;
+ pep->rx_desc_count++;
+ /* Get 'used' Rx descriptor */
+ used_rx_desc = pep->rx_used_desc_q;
+ p_used_rx_desc = &pep->p_rx_desc_area[used_rx_desc];
+ p_used_rx_desc->buf_ptr = dma_map_single(NULL,
+ skb->data,
+ MAX_PKT_SIZE +
+ ETH_HW_IP_ALIGN,
+ DMA_FROM_DEVICE);
+ p_used_rx_desc->buf_size = MAX_PKT_SIZE + ETH_HW_IP_ALIGN;
+ pep->rx_skb[used_rx_desc] = skb;
+
+ /* Return the descriptor to DMA ownership */
+ wmb();
+ p_used_rx_desc->cmd_sts = BUF_OWNED_BY_DMA | RX_EN_INT;
+ wmb();
+
+ /* Move the used descriptor pointer to the next descriptor */
+ pep->rx_used_desc_q = (used_rx_desc + 1) % pep->rx_ring_size;
+
+ /* Any Rx return cancels the Rx resource error status */
+ pep->rx_resource_err = 0;
+
+ skb_reserve(skb, ETH_HW_IP_ALIGN);
+ }
+
+ /*
+ * If RX ring is empty of SKB, set a timer to try allocating
+ * again at a later time.
+ */
+ if (pep->rx_desc_count == 0) {
+ pep->timeout.expires = jiffies + (HZ / 10); /* 100 mSec */
+ add_timer(&pep->timeout);
+ }
+}
+
+static inline void rxq_refill_timer_wrapper(unsigned long data)
+{
+ struct pxa168_eth_private *pep = (void *)data;
+ napi_schedule(&pep->napi);
+}
+
+static inline u32 nibble_swapping_32_bit(u32 x)
+{
+ return (((x) & 0xf0f0f0f0) >> 4) | (((x) & 0x0f0f0f0f) << 4);
+}
+
+static inline u32 nibble_swapping_16_bit(u32 x)
+{
+ return (((x) & 0x0000f0f0) >> 4) | (((x) & 0x00000f0f) << 4);
+}
+
+static inline u32 flip_4_bits(u32 x)
+{
+ return (((x) & 0x01) << 3) | (((x) & 0x002) << 1)
+ | (((x) & 0x04) >> 1) | (((x) & 0x008) >> 3);
+}
+
+/*
+ * ----------------------------------------------------------------------------
+ * This function will calculate the hash function of the address.
+ * depends on the hash mode and hash size.
+ * Inputs
+ * mac_high - the 2 most significant bytes of the MAC address.
+ * mac_low - the 4 least significant bytes of the MAC address.
+ * Outputs
+ * return the calculated entry.
+ */
+static u32 hash_function(u32 mac_high, u32 mac_low)
+{
+ u32 hash_result;
+ u32 addr_high;
+ u32 addr_low;
+ u32 addr0;
+ u32 addr1;
+ u32 addr2;
+ u32 addr3;
+ u32 addr_high_swapped;
+ u32 addr_low_swapped;
+
+ addr_high = nibble_swapping_16_bit(mac_high);
+ addr_low = nibble_swapping_32_bit(mac_low);
+
+ addr_high_swapped = flip_4_bits(addr_high & 0xf)
+ + ((flip_4_bits((addr_high >> 4) & 0xf)) << 4)
+ + ((flip_4_bits((addr_high >> 8) & 0xf)) << 8)
+ + ((flip_4_bits((addr_high >> 12) & 0xf)) << 12);
+
+ addr_low_swapped = flip_4_bits(addr_low & 0xf)
+ + ((flip_4_bits((addr_low >> 4) & 0xf)) << 4)
+ + ((flip_4_bits((addr_low >> 8) & 0xf)) << 8)
+ + ((flip_4_bits((addr_low >> 12) & 0xf)) << 12)
+ + ((flip_4_bits((addr_low >> 16) & 0xf)) << 16)
+ + ((flip_4_bits((addr_low >> 20) & 0xf)) << 20)
+ + ((flip_4_bits((addr_low >> 24) & 0xf)) << 24)
+ + ((flip_4_bits((addr_low >> 28) & 0xf)) << 28);
+
+ addr_high = addr_high_swapped;
+ addr_low = addr_low_swapped;
+
+ addr0 = (addr_low >> 2) & 0x03f;
+ addr1 = (addr_low & 0x003) | ((addr_low >> 8) & 0x7f) << 2;
+ addr2 = (addr_low >> 15) & 0x1ff;
+ addr3 = ((addr_low >> 24) & 0x0ff) | ((addr_high & 1) << 8);
+
+ hash_result = (addr0 << 9) | (addr1 ^ addr2 ^ addr3);
+ hash_result = hash_result & 0x07ff;
+ return hash_result;
+}
+
+/*
+ * ----------------------------------------------------------------------------
+ * This function will add/del an entry to the address table.
+ * Inputs
+ * pep - ETHERNET .
+ * mac_high - the 2 most significant bytes of the MAC address.
+ * mac_low - the 4 least significant bytes of the MAC address.
+ * skip - if 1, skip this address.Used in case of deleting an entry which is a
+ * part of chain in the hash table.We cant just delete the entry since
+ * that will break the chain.We need to defragment the tables time to
+ * time.
+ * rd - 0 Discard packet upon match.
+ * - 1 Receive packet upon match.
+ * Outputs
+ * address table entry is added/deleted.
+ * 0 if success.
+ * -ENOSPC if table full
+ */
+static int add_del_hash_entry(struct pxa168_eth_private *pep, u32 mac_high,
+ u32 mac_low, u32 rd, u32 skip, int del)
+{
+ struct addr_table_entry *entry, *start;
+ u32 new_high;
+ u32 new_low;
+ u32 i;
+
+ new_low = (((mac_high >> 4) & 0xf) << 15)
+ | (((mac_high >> 0) & 0xf) << 11)
+ | (((mac_high >> 12) & 0xf) << 7)
+ | (((mac_high >> 8) & 0xf) << 3)
+ | (((mac_low >> 20) & 0x1) << 31)
+ | (((mac_low >> 16) & 0xf) << 27)
+ | (((mac_low >> 28) & 0xf) << 23)
+ | (((mac_low >> 24) & 0xf) << 19)
+ | (skip << SKIP) | (rd << HASH_ENTRY_RECEIVE_DISCARD_BIT)
+ | HASH_ENTRY_VALID;
+
+ new_high = (((mac_low >> 4) & 0xf) << 15)
+ | (((mac_low >> 0) & 0xf) << 11)
+ | (((mac_low >> 12) & 0xf) << 7)
+ | (((mac_low >> 8) & 0xf) << 3)
+ | (((mac_low >> 21) & 0x7) << 0);
+
+ /*
+ * Pick the appropriate table, start scanning for free/reusable
+ * entries at the index obtained by hashing the specified MAC address
+ */
+ start = (struct addr_table_entry *)(pep->htpr);
+ entry = start + hash_function(mac_high, mac_low);
+ for (i = 0; i < HOP_NUMBER; i++) {
+ if (!(entry->lo & HASH_ENTRY_VALID)) {
+ break;
+ } else {
+ /* if same address put in same position */
+ if (((entry->lo & 0xfffffff8) == (new_low & 0xfffffff8))
+ && (entry->hi == new_high)) {
+ break;
+ }
+ }
+ if (entry == start + 0x7ff)
+ entry = start;
+ else
+ entry++;
+ }
+
+ if (((entry->lo & 0xfffffff8) != (new_low & 0xfffffff8)) &&
+ (entry->hi != new_high) && del)
+ return 0;
+
+ if (i == HOP_NUMBER) {
+ if (!del) {
+ printk(KERN_INFO "%s: table section is full\n",
+ __FILE__);
+ return -ENOSPC;
+ } else
+ return 0;
+ }
+
+ /*
+ * Update the selected entry
+ */
+ if (del) {
+ entry->hi = 0;
+ entry->lo = 0;
+ } else {
+ entry->hi = cpu_to_le32(new_high);
+ entry->lo = cpu_to_le32(new_low);
+ }
+
+ return 0;
+}
+
+/*
+ * ----------------------------------------------------------------------------
+ * Create an addressTable entry from MAC address info
+ * found in the specifed net_device struct
+ *
+ * Input : pointer to ethernet interface network device structure
+ * Output : N/A
+ */
+static void update_hash_table_mac_address(struct pxa168_eth_private *pep,
+ u8 *oaddr, u8 *addr)
+{
+ u32 mac_high;
+ u32 mac_low;
+
+ /* Delete old entry */
+ if (oaddr) {
+ mac_high = cpu_to_le32((oaddr[0] << 8) | oaddr[1]);
+ mac_low = cpu_to_le32((oaddr[2] << 24) | (oaddr[3] << 16) |
+ (oaddr[4] << 8) | oaddr[5]);
+ add_del_hash_entry(pep, mac_high, mac_low, 1, 0, HASH_DELETE);
+ }
+ /* Add new entry */
+ mac_high = cpu_to_le32((addr[0] << 8) | addr[1]);
+ mac_low = cpu_to_le32((addr[2] << 24) | (addr[3] << 16) |
+ (addr[4] << 8) | addr[5]);
+ add_del_hash_entry(pep, mac_high, mac_low, 1, 0, HASH_ADD);
+}
+
+static int init_hash_table(struct pxa168_eth_private *pep)
+{
+ /*
+ * Hardware expects CPU to build a hash table based on a predefined
+ * hash function and populate it based on hardware address. The
+ * location of the hash table is identified by 32-bit pointer stored
+ * in HTPR internal register. Two possible sizes exists for the hash
+ * table 256kB and 16kB.We currently only support 16kB.
+ */
+ /* TODO: Add support for 256kB hash table */
+ if (pep->htpr == NULL) {
+ pep->htpr = dma_alloc_coherent(NULL,
+ HASH_ADDR_TABLE_SIZE,
+ &pep->htpr_dma, GFP_KERNEL);
+ if (pep->htpr == NULL)
+ return -ENOMEM;
+ }
+ memset(pep->htpr, 0, HASH_ADDR_TABLE_SIZE);
+ wrl(pep, HTPR, pep->htpr_dma);
+ return 0;
+}
+
+static void pxa168_eth_set_rx_mode(struct net_device *dev)
+{
+ struct pxa168_eth_private *pep = netdev_priv(dev);
+ struct netdev_hw_addr *ha;
+ u32 val;
+
+ val = rdl(pep, PORT_CONFIG);
+ if (dev->flags & IFF_PROMISC)
+ val |= PCR_PM;
+ else
+ val &= ~PCR_PM;
+ wrl(pep, PORT_CONFIG, val);
+ netdev_for_each_mc_addr(ha, dev)
+ update_hash_table_mac_address(pep, NULL, ha->addr);
+}
+
+static int pxa168_eth_set_mac_address(struct net_device *dev, void *addr)
+{
+ struct sockaddr *sa = addr;
+ struct pxa168_eth_private *pep = netdev_priv(dev);
+ unsigned char oldMac[ETH_ALEN];
+
+ if (!is_valid_ether_addr(sa->sa_data))
+ return -EINVAL;
+ memcpy(oldMac, dev->dev_addr, ETH_ALEN);
+ memcpy(dev->dev_addr, sa->sa_data, ETH_ALEN);
+ netif_addr_lock_bh(dev);
+ update_hash_table_mac_address(pep, oldMac, dev->dev_addr);
+ netif_addr_unlock_bh(dev);
+ return 0;
+}
+
+static void eth_port_start(struct net_device *dev)
+{
+ unsigned int val = 0;
+ struct pxa168_eth_private *pep = netdev_priv(dev);
+ int tx_curr_desc, rx_curr_desc;
+
+ /* Perform PHY reset, if there is a PHY. */
+ if (pep->phy != NULL) {
+ struct ethtool_cmd cmd;
+
+ pxa168_get_settings(pep->dev, &cmd);
+ ethernet_phy_reset(pep);
+ pxa168_set_settings(pep->dev, &cmd);
+ }
+
+ /* Assignment of Tx CTRP of given queue */
+ tx_curr_desc = pep->tx_curr_desc_q;
+ wrl(pep, ETH_C_TX_DESC_1,
+ (u32) ((struct tx_desc *)pep->tx_desc_dma + tx_curr_desc));
+
+ /* Assignment of Rx CRDP of given queue */
+ rx_curr_desc = pep->rx_curr_desc_q;
+ wrl(pep, ETH_C_RX_DESC_0,
+ (u32) ((struct rx_desc *)pep->rx_desc_dma + rx_curr_desc));
+
+ wrl(pep, ETH_F_RX_DESC_0,
+ (u32) ((struct rx_desc *)pep->rx_desc_dma + rx_curr_desc));
+
+ /* Clear all interrupts */
+ wrl(pep, INT_CAUSE, 0);
+
+ /* Enable all interrupts for receive, transmit and error. */
+ wrl(pep, INT_MASK, ALL_INTS);
+
+ val = rdl(pep, PORT_CONFIG);
+ val |= PCR_EN;
+ wrl(pep, PORT_CONFIG, val);
+
+ /* Start RX DMA engine */
+ val = rdl(pep, SDMA_CMD);
+ val |= SDMA_CMD_ERD;
+ wrl(pep, SDMA_CMD, val);
+}
+
+static void eth_port_reset(struct net_device *dev)
+{
+ struct pxa168_eth_private *pep = netdev_priv(dev);
+ unsigned int val = 0;
+
+ /* Stop all interrupts for receive, transmit and error. */
+ wrl(pep, INT_MASK, 0);
+
+ /* Clear all interrupts */
+ wrl(pep, INT_CAUSE, 0);
+
+ /* Stop RX DMA */
+ val = rdl(pep, SDMA_CMD);
+ val &= ~SDMA_CMD_ERD; /* abort dma command */
+
+ /* Abort any transmit and receive operations and put DMA
+ * in idle state.
+ */
+ abort_dma(pep);
+
+ /* Disable port */
+ val = rdl(pep, PORT_CONFIG);
+ val &= ~PCR_EN;
+ wrl(pep, PORT_CONFIG, val);
+}
+
+/*
+ * txq_reclaim - Free the tx desc data for completed descriptors
+ * If force is non-zero, frees uncompleted descriptors as well
+ */
+static int txq_reclaim(struct net_device *dev, int force)
+{
+ struct pxa168_eth_private *pep = netdev_priv(dev);
+ struct tx_desc *desc;
+ u32 cmd_sts;
+ struct sk_buff *skb;
+ int tx_index;
+ dma_addr_t addr;
+ int count;
+ int released = 0;
+
+ netif_tx_lock(dev);
+
+ pep->work_todo &= ~(WORK_TX_DONE);
+ while (pep->tx_desc_count > 0) {
+ tx_index = pep->tx_used_desc_q;
+ desc = &pep->p_tx_desc_area[tx_index];
+ cmd_sts = desc->cmd_sts;
+ if (!force && (cmd_sts & BUF_OWNED_BY_DMA)) {
+ if (released > 0) {
+ goto txq_reclaim_end;
+ } else {
+ released = -1;
+ goto txq_reclaim_end;
+ }
+ }
+ pep->tx_used_desc_q = (tx_index + 1) % pep->tx_ring_size;
+ pep->tx_desc_count--;
+ addr = desc->buf_ptr;
+ count = desc->byte_cnt;
+ skb = pep->tx_skb[tx_index];
+ if (skb)
+ pep->tx_skb[tx_index] = NULL;
+
+ if (cmd_sts & TX_ERROR) {
+ if (net_ratelimit())
+ printk(KERN_ERR "%s: Error in TX\n", dev->name);
+ dev->stats.tx_errors++;
+ }
+ dma_unmap_single(NULL, addr, count, DMA_TO_DEVICE);
+ if (skb)
+ dev_kfree_skb_irq(skb);
+ released++;
+ }
+txq_reclaim_end:
+ netif_tx_unlock(dev);
+ return released;
+}
+
+static void pxa168_eth_tx_timeout(struct net_device *dev)
+{
+ struct pxa168_eth_private *pep = netdev_priv(dev);
+
+ printk(KERN_INFO "%s: TX timeout desc_count %d\n",
+ dev->name, pep->tx_desc_count);
+
+ schedule_work(&pep->tx_timeout_task);
+}
+
+static void pxa168_eth_tx_timeout_task(struct work_struct *work)
+{
+ struct pxa168_eth_private *pep = container_of(work,
+ struct pxa168_eth_private,
+ tx_timeout_task);
+ struct net_device *dev = pep->dev;
+ pxa168_eth_stop(dev);
+ pxa168_eth_open(dev);
+}
+
+static int rxq_process(struct net_device *dev, int budget)
+{
+ struct pxa168_eth_private *pep = netdev_priv(dev);
+ struct net_device_stats *stats = &dev->stats;
+ unsigned int received_packets = 0;
+ struct sk_buff *skb;
+
+ while (budget-- > 0) {
+
+ int rx_next_curr_desc, rx_curr_desc, rx_used_desc;
+ struct rx_desc *rx_desc;
+ unsigned int cmd_sts;
+
+ /* Do not process Rx ring in case of Rx ring resource error */
+ if (pep->rx_resource_err)
+ break;
+ rx_curr_desc = pep->rx_curr_desc_q;
+ rx_used_desc = pep->rx_used_desc_q;
+ rx_desc = &pep->p_rx_desc_area[rx_curr_desc];
+ cmd_sts = rx_desc->cmd_sts;
+ rmb();
+ if (cmd_sts & (BUF_OWNED_BY_DMA))
+ break;
+ skb = pep->rx_skb[rx_curr_desc];
+ pep->rx_skb[rx_curr_desc] = NULL;
+
+ rx_next_curr_desc = (rx_curr_desc + 1) % pep->rx_ring_size;
+ pep->rx_curr_desc_q = rx_next_curr_desc;
+
+ /* Rx descriptors exhausted. */
+ /* Set the Rx ring resource error flag */
+ if (rx_next_curr_desc == rx_used_desc)
+ pep->rx_resource_err = 1;
+ pep->rx_desc_count--;
+ dma_unmap_single(NULL, rx_desc->buf_ptr,
+ MAX_PKT_SIZE + ETH_HW_IP_ALIGN,
+ DMA_FROM_DEVICE);
+ received_packets++;
+ /*
+ * Update statistics.
+ * Note byte count includes 4 byte CRC count
+ */
+ stats->rx_packets++;
+ stats->rx_bytes += rx_desc->byte_cnt;
+ /*
+ * In case received a packet without first / last bits on OR
+ * the error summary bit is on, the packets needs to be droped.
+ */
+ if (((cmd_sts & (RX_FIRST_DESC | RX_LAST_DESC)) !=
+ (RX_FIRST_DESC | RX_LAST_DESC))
+ || (cmd_sts & RX_ERROR)) {
+
+ stats->rx_dropped++;
+ if ((cmd_sts & (RX_FIRST_DESC | RX_LAST_DESC)) !=
+ (RX_FIRST_DESC | RX_LAST_DESC)) {
+ if (net_ratelimit())
+ printk(KERN_ERR
+ "%s: Rx pkt on multiple desc\n",
+ dev->name);
+ }
+ if (cmd_sts & RX_ERROR)
+ stats->rx_errors++;
+ dev_kfree_skb_irq(skb);
+ } else {
+ /*
+ * The -4 is for the CRC in the trailer of the
+ * received packet
+ */
+ skb_put(skb, rx_desc->byte_cnt - 4);
+ skb->protocol = eth_type_trans(skb, dev);
+ netif_receive_skb(skb);
+ }
+ dev->last_rx = jiffies;
+ }
+ /* Fill RX ring with skb's */
+ rxq_refill(dev);
+ return received_packets;
+}
+
+static int pxa168_eth_collect_events(struct pxa168_eth_private *pep,
+ struct net_device *dev)
+{
+ u32 icr;
+ int ret = 0;
+
+ icr = rdl(pep, INT_CAUSE);
+ if (0x00 == icr)
+ return IRQ_NONE;
+
+ wrl(pep, INT_CAUSE, icr ^ 0xffffffff);
+ if (icr & (ICR_TXBUF_H | ICR_TXBUF_L)) {
+ pep->work_todo |= WORK_TX_DONE;
+ ret = 1;
+ }
+ if (icr & ICR_RXBUF)
+ ret = 1;
+ if (icr & ICR_MII_CH) {
+ pep->work_todo |= WORK_LINK;
+ ret = 1;
+ }
+ return ret;
+}
+
+static void handle_link_event(struct pxa168_eth_private *pep)
+{
+ struct net_device *dev = pep->dev;
+ u32 port_status;
+ int speed;
+ int duplex;
+ int fc;
+
+ port_status = rdl(pep, PORT_STATUS);
+ if (!(port_status & LINK_UP)) {
+ if (netif_carrier_ok(dev)) {
+ printk(KERN_INFO "%s: link down\n", dev->name);
+ netif_carrier_off(dev);
+ txq_reclaim(dev, 1);
+ }
+ return;
+ }
+ if (port_status & PORT_SPEED_100)
+ speed = 100;
+ else
+ speed = 10;
+
+ duplex = (port_status & FULL_DUPLEX) ? 1 : 0;
+ fc = (port_status & FLOW_CONTROL_ENABLED) ? 1 : 0;
+ printk(KERN_INFO "%s: link up, %d Mb/s, %s duplex, "
+ "flow control %sabled\n", dev->name,
+ speed, duplex ? "full" : "half", fc ? "en" : "dis");
+ if (!netif_carrier_ok(dev))
+ netif_carrier_on(dev);
+}
+
+static irqreturn_t pxa168_eth_int_handler(int irq, void *dev_id)
+{
+ struct net_device *dev = (struct net_device *)dev_id;
+ struct pxa168_eth_private *pep = netdev_priv(dev);
+
+ if (unlikely(!pxa168_eth_collect_events(pep, dev)))
+ return IRQ_NONE;
+ /* Disable interrupts */
+ wrl(pep, INT_MASK, 0);
+ napi_schedule(&pep->napi);
+ return IRQ_HANDLED;
+}
+
+static int set_port_config_ext(struct pxa168_eth_private *pep, int mtu)
+{
+ int mtu_size;
+
+ if ((mtu > ETH_DATA_LEN) || (mtu < 64))
+ return -EINVAL;
+
+ mtu_size = PCXR_MFL_1518;
+ /* Extended Port Configuration */
+ wrl(pep,
+ PORT_CONFIG_EXT, PCXR_2BSM | /* Two byte suffix aligns IP hdr */
+ PCXR_DSCP_EN | /* Enable DSCP in IP */
+ mtu_size | PCXR_FLP | /* do not force link pass */
+ PCXR_TX_HIGH_PRI); /* Transmit - high priority queue */
+
+ (pep->dev)->mtu = mtu;
+ return 0;
+}
+
+static int pxa168_init_hw(struct pxa168_eth_private *pep)
+{
+ int err = 0;
+
+ /* Disable interrupts */
+ wrl(pep, INT_MASK, 0);
+ wrl(pep, INT_CAUSE, 0);
+ /* Write to ICR to clear interrupts. */
+ wrl(pep, INT_W_CLEAR, 0);
+ /* Abort any transmit and receive operations and put DMA
+ * in idle state.
+ */
+ abort_dma(pep);
+ /* Initialize address hash table */
+ err = init_hash_table(pep);
+ if (err)
+ return err;
+ /* SDMA configuration */
+ wrl(pep, SDMA_CONFIG, SDCR_BSZ8 | /* Burst size = 32 bytes */
+ SDCR_RIFB | /* Rx interrupt on frame */
+ SDCR_BLMT | /* Little endian transmit */
+ SDCR_BLMR | /* Little endian receive */
+ SDCR_RC_MAX_RETRANS); /* Max retransmit count */
+ /* Port Configuration */
+ wrl(pep, PORT_CONFIG, PCR_HS); /* Hash size is 1/2kb */
+ set_port_config_ext(pep, (pep->dev)->mtu);
+ return err;
+}
+
+static int rxq_init(struct net_device *dev)
+{
+ struct pxa168_eth_private *pep = netdev_priv(dev);
+ struct rx_desc *p_rx_desc;
+ int size = 0, i = 0;
+ int rx_desc_num = pep->rx_ring_size;
+
+ /* Allocate RX skb rings */
+ pep->rx_skb = kmalloc(sizeof(*pep->rx_skb) * pep->rx_ring_size,
+ GFP_KERNEL);
+ if (!pep->rx_skb) {
+ printk(KERN_ERR "%s: Cannot alloc RX skb ring\n", dev->name);
+ return -ENOMEM;
+ }
+ /* Allocate RX ring */
+ pep->rx_desc_count = 0;
+ size = pep->rx_ring_size * sizeof(struct rx_desc);
+ pep->rx_desc_area_size = size;
+ pep->p_rx_desc_area = dma_alloc_coherent(NULL, size,
+ &pep->rx_desc_dma, GFP_KERNEL);
+ if (!pep->p_rx_desc_area) {
+ printk(KERN_ERR "%s: Cannot alloc RX ring (size %d bytes)\n",
+ dev->name, size);
+ goto out;
+ }
+ memset((void *)pep->p_rx_desc_area, 0, size);
+ /* initialize the next_desc_ptr links in the Rx descriptors ring */
+ p_rx_desc = (struct rx_desc *)pep->p_rx_desc_area;
+ for (i = 0; i < rx_desc_num; i++) {
+ p_rx_desc[i].next_desc_ptr = pep->rx_desc_dma +
+ ((i + 1) % rx_desc_num) * sizeof(struct rx_desc);
+ }
+ /* Save Rx desc pointer to driver struct. */
+ pep->rx_curr_desc_q = 0;
+ pep->rx_used_desc_q = 0;
+ pep->rx_desc_area_size = rx_desc_num * sizeof(struct rx_desc);
+ return 0;
+out:
+ kfree(pep->rx_skb);
+ return -ENOMEM;
+}
+
+static void rxq_deinit(struct net_device *dev)
+{
+ struct pxa168_eth_private *pep = netdev_priv(dev);
+ int curr;
+
+ /* Free preallocated skb's on RX rings */
+ for (curr = 0; pep->rx_desc_count && curr < pep->rx_ring_size; curr++) {
+ if (pep->rx_skb[curr]) {
+ dev_kfree_skb(pep->rx_skb[curr]);
+ pep->rx_desc_count--;
+ }
+ }
+ if (pep->rx_desc_count)
+ printk(KERN_ERR
+ "Error in freeing Rx Ring. %d skb's still\n",
+ pep->rx_desc_count);
+ /* Free RX ring */
+ if (pep->p_rx_desc_area)
+ dma_free_coherent(NULL, pep->rx_desc_area_size,
+ pep->p_rx_desc_area, pep->rx_desc_dma);
+ kfree(pep->rx_skb);
+}
+
+static int txq_init(struct net_device *dev)
+{
+ struct pxa168_eth_private *pep = netdev_priv(dev);
+ struct tx_desc *p_tx_desc;
+ int size = 0, i = 0;
+ int tx_desc_num = pep->tx_ring_size;
+
+ pep->tx_skb = kmalloc(sizeof(*pep->tx_skb) * pep->tx_ring_size,
+ GFP_KERNEL);
+ if (!pep->tx_skb) {
+ printk(KERN_ERR "%s: Cannot alloc TX skb ring\n", dev->name);
+ return -ENOMEM;
+ }
+ /* Allocate TX ring */
+ pep->tx_desc_count = 0;
+ size = pep->tx_ring_size * sizeof(struct tx_desc);
+ pep->tx_desc_area_size = size;
+ pep->p_tx_desc_area = dma_alloc_coherent(NULL, size,
+ &pep->tx_desc_dma, GFP_KERNEL);
+ if (!pep->p_tx_desc_area) {
+ printk(KERN_ERR "%s: Cannot allocate Tx Ring (size %d bytes)\n",
+ dev->name, size);
+ goto out;
+ }
+ memset((void *)pep->p_tx_desc_area, 0, pep->tx_desc_area_size);
+ /* Initialize the next_desc_ptr links in the Tx descriptors ring */
+ p_tx_desc = (struct tx_desc *)pep->p_tx_desc_area;
+ for (i = 0; i < tx_desc_num; i++) {
+ p_tx_desc[i].next_desc_ptr = pep->tx_desc_dma +
+ ((i + 1) % tx_desc_num) * sizeof(struct tx_desc);
+ }
+ pep->tx_curr_desc_q = 0;
+ pep->tx_used_desc_q = 0;
+ pep->tx_desc_area_size = tx_desc_num * sizeof(struct tx_desc);
+ return 0;
+out:
+ kfree(pep->tx_skb);
+ return -ENOMEM;
+}
+
+static void txq_deinit(struct net_device *dev)
+{
+ struct pxa168_eth_private *pep = netdev_priv(dev);
+
+ /* Free outstanding skb's on TX ring */
+ txq_reclaim(dev, 1);
+ BUG_ON(pep->tx_used_desc_q != pep->tx_curr_desc_q);
+ /* Free TX ring */
+ if (pep->p_tx_desc_area)
+ dma_free_coherent(NULL, pep->tx_desc_area_size,
+ pep->p_tx_desc_area, pep->tx_desc_dma);
+ kfree(pep->tx_skb);
+}
+
+static int pxa168_eth_open(struct net_device *dev)
+{
+ struct pxa168_eth_private *pep = netdev_priv(dev);
+ int err;
+
+ err = request_irq(dev->irq, pxa168_eth_int_handler,
+ IRQF_DISABLED , dev->name, dev);
+ if (err) {
+ dev_printk(KERN_ERR, &dev->dev, "can't assign irq\n");
+ return -EAGAIN;
+ }
+ pep->rx_resource_err = 0;
+ err = rxq_init(dev);
+ if (err != 0)
+ goto out_free_irq;
+ err = txq_init(dev);
+ if (err != 0)
+ goto out_free_rx_skb;
+ pep->rx_used_desc_q = 0;
+ pep->rx_curr_desc_q = 0;
+
+ /* Fill RX ring with skb's */
+ rxq_refill(dev);
+ pep->rx_used_desc_q = 0;
+ pep->rx_curr_desc_q = 0;
+ netif_carrier_off(dev);
+ eth_port_start(dev);
+ napi_enable(&pep->napi);
+ return 0;
+out_free_rx_skb:
+ rxq_deinit(dev);
+out_free_irq:
+ free_irq(dev->irq, dev);
+ return err;
+}
+
+static int pxa168_eth_stop(struct net_device *dev)
+{
+ struct pxa168_eth_private *pep = netdev_priv(dev);
+ eth_port_reset(dev);
+
+ /* Disable interrupts */
+ wrl(pep, INT_MASK, 0);
+ wrl(pep, INT_CAUSE, 0);
+ /* Write to ICR to clear interrupts. */
+ wrl(pep, INT_W_CLEAR, 0);
+ napi_disable(&pep->napi);
+ del_timer_sync(&pep->timeout);
+ netif_carrier_off(dev);
+ free_irq(dev->irq, dev);
+ rxq_deinit(dev);
+ txq_deinit(dev);
+ return 0;
+}
+
+static int pxa168_eth_change_mtu(struct net_device *dev, int mtu)
+{
+ if ((mtu > ETH_DATA_LEN) || (mtu < 64))
+ return -EINVAL;
+ dev->mtu = mtu;
+ return 0;
+}
+
+static int eth_alloc_tx_desc_index(struct pxa168_eth_private *pep)
+{
+ int tx_desc_curr;
+
+ tx_desc_curr = pep->tx_curr_desc_q;
+ pep->tx_curr_desc_q = (tx_desc_curr + 1) % pep->tx_ring_size;
+ BUG_ON(pep->tx_curr_desc_q == pep->tx_used_desc_q);
+ pep->tx_desc_count++;
+ return tx_desc_curr;
+}
+
+static void eth_tx_submit_descs_for_skb(struct pxa168_eth_private *pep,
+ struct sk_buff *skb)
+{
+ int tx_index;
+ struct tx_desc *desc;
+ int length;
+
+ tx_index = eth_alloc_tx_desc_index(pep);
+ desc = &pep->p_tx_desc_area[tx_index];
+ length = skb->len;
+ pep->tx_skb[tx_index] = skb;
+ desc->byte_cnt = length;
+ desc->buf_ptr = dma_map_single(NULL, skb->data, length, DMA_TO_DEVICE);
+ wmb();
+ desc->cmd_sts = BUF_OWNED_BY_DMA | TX_GEN_CRC | TX_FIRST_DESC |
+ TX_ZERO_PADDING | TX_LAST_DESC;
+ if (unlikely(!(pep->tx_desc_count % TX_DONE_INTERVAL)))
+ desc->cmd_sts |= TX_EN_INT;
+ wmb();
+ wrl(pep, SDMA_CMD, SDMA_CMD_TXDH | SDMA_CMD_ERD);
+}
+
+static int pxa168_rx_poll(struct napi_struct *napi, int budget)
+{
+ struct pxa168_eth_private *pep =
+ container_of(napi, struct pxa168_eth_private, napi);
+ struct net_device *dev = pep->dev;
+ int work_done = 0;
+
+ if (unlikely(pep->work_todo & WORK_LINK)) {
+ pep->work_todo &= ~(WORK_LINK);
+ handle_link_event(pep);
+ }
+ /*
+ * We call txq_reclaim every time since in NAPI interupts are disabled
+ * and due to this we miss the TX_DONE interrupt,which is not updated in
+ * interrupt status register.
+ */
+ txq_reclaim(dev, 0);
+ if (netif_queue_stopped(dev)
+ && pep->tx_ring_size - pep->tx_desc_count > 1) {
+ netif_wake_queue(dev);
+ }
+ work_done = rxq_process(dev, budget);
+ if (work_done < budget) {
+ napi_complete(napi);
+ wrl(pep, INT_MASK, ALL_INTS);
+ }
+ return work_done;
+}
+
+static int pxa168_eth_start_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+ struct pxa168_eth_private *pep = netdev_priv(dev);
+ struct net_device_stats *stats = &dev->stats;
+
+ eth_tx_submit_descs_for_skb(pep, skb);
+ stats->tx_bytes += skb->len;
+ stats->tx_packets++;
+ dev->trans_start = jiffies;
+ if (pep->tx_ring_size - pep->tx_desc_count <= 1) {
+ /* We handled the current skb, but now we are out of space.*/
+ netif_stop_queue(dev);
+ }
+ return NETDEV_TX_OK;
+}
+
+static int pxa168_smi_read(struct mii_bus *bus, int phy_addr, int regnum)
+{
+ int val;
+ struct pxa168_eth_private *pep = bus->priv;
+ int i = 0;
+
+ /* wait for the SMI register to become available */
+ for (i = 0; (val = rdl(pep, SMI)) & SMI_BUSY; i++) {
+ if (i == PHY_WAIT_ITERATIONS) {
+ printk(KERN_ERR
+ "pxa168 PHY timeout, val=0x%x\n", val);
+ return -ETIMEDOUT;
+ }
+ udelay(1);
+ }
+ wrl(pep, SMI, (phy_addr << 16) | (regnum << 21) | SMI_OP_R);
+ /* now wait for the data to be valid */
+ for (i = 0; !((val = rdl(pep, SMI)) & SMI_R_VALID); i++) {
+ if (i == PHY_WAIT_ITERATIONS) {
+ printk(KERN_ERR
+ "pxa168 PHY RD timeout, val=0x%x\n", val);
+ return -ETIMEDOUT;
+ }
+ udelay(1);
+ }
+ return val & 0xffff;
+}
+
+static int pxa168_smi_write(struct mii_bus *bus, int phy_addr, int regnum,
+ u16 value)
+{
+ struct pxa168_eth_private *pep = bus->priv;
+ int i;
+
+ /* wait for the SMI register to become available */
+ for (i = 0; rdl(pep, SMI) & SMI_BUSY; i++) {
+ if (i == PHY_WAIT_ITERATIONS) {
+ printk(KERN_ERR "pxa168 PHY busy timeout.\n");
+ return -ETIMEDOUT;
+ }
+ udelay(1);
+ }
+ wrl(pep, SMI, (phy_addr << 16) | (regnum << 21) |
+ SMI_OP_W | (value & 0xffff));
+
+ return 0;
+}
+
+static int pxa168_eth_do_ioctl(struct net_device *dev, struct ifreq *ifr,
+ int cmd)
+{
+ struct pxa168_eth_private *pep = netdev_priv(dev);
+ if (pep->phy != NULL)
+ return phy_mii_ioctl(pep->phy, if_mii(ifr), cmd);
+ return -EOPNOTSUPP;
+}
+
+static struct phy_device *phy_scan(struct pxa168_eth_private *pep, int phy_addr)
+{
+ struct mii_bus *bus = pep->smi_bus;
+ struct phy_device *phydev;
+ int start;
+ int num;
+ int i;
+
+ if (phy_addr == PXA168_ETH_PHY_ADDR_DEFAULT) {
+ /* Scan entire range */
+ start = ethernet_phy_get(pep);
+ num = 32;
+ } else {
+ /* Use phy addr specific to platform */
+ start = phy_addr & 0x1f;
+ num = 1;
+ }
+ phydev = NULL;
+ for (i = 0; i < num; i++) {
+ int addr = (start + i) & 0x1f;
+ if (bus->phy_map[addr] == NULL)
+ mdiobus_scan(bus, addr);
+
+ if (phydev == NULL) {
+ phydev = bus->phy_map[addr];
+ if (phydev != NULL)
+ ethernet_phy_set_addr(pep, addr);
+ }
+ }
+
+ return phydev;
+}
+
+static void phy_init(struct pxa168_eth_private *pep, int speed, int duplex)
+{
+ struct phy_device *phy = pep->phy;
+ ethernet_phy_reset(pep);
+
+ phy_attach(pep->dev, dev_name(&phy->dev), 0, PHY_INTERFACE_MODE_MII);
+
+ if (speed == 0) {
+ phy->autoneg = AUTONEG_ENABLE;
+ phy->speed = 0;
+ phy->duplex = 0;
+ phy->supported &= PHY_BASIC_FEATURES;
+ phy->advertising = phy->supported | ADVERTISED_Autoneg;
+ } else {
+ phy->autoneg = AUTONEG_DISABLE;
+ phy->advertising = 0;
+ phy->speed = speed;
+ phy->duplex = duplex;
+ }
+ phy_start_aneg(phy);
+}
+
+static int ethernet_phy_setup(struct net_device *dev)
+{
+ struct pxa168_eth_private *pep = netdev_priv(dev);
+
+ if (pep->pd != NULL) {
+ if (pep->pd->init)
+ pep->pd->init();
+ }
+ pep->phy = phy_scan(pep, pep->pd->phy_addr & 0x1f);
+ if (pep->phy != NULL)
+ phy_init(pep, pep->pd->speed, pep->pd->duplex);
+ update_hash_table_mac_address(pep, NULL, dev->dev_addr);
+ return 0;
+}
+
+static int get_random_mac_addr(struct net_device *dev)
+{
+ printk(KERN_INFO "%s:Using random mac address\n", dev->name);
+ random_ether_addr(dev->dev_addr);
+ return 0;
+}
+
+static int pxa168_get_settings(struct net_device *dev, struct ethtool_cmd *cmd)
+{
+ struct pxa168_eth_private *pep = netdev_priv(dev);
+ int err;
+
+ err = phy_read_status(pep->phy);
+ if (err == 0)
+ err = phy_ethtool_gset(pep->phy, cmd);
+ return err;
+}
+
+static int pxa168_set_settings(struct net_device *dev, struct ethtool_cmd *cmd)
+{
+ struct pxa168_eth_private *pep = netdev_priv(dev);
+
+ return phy_ethtool_sset(pep->phy, cmd);
+}
+
+static void pxa168_get_drvinfo(struct net_device *dev,
+ struct ethtool_drvinfo *info)
+{
+ strncpy(info->driver, DRIVER_NAME, 32);
+ strncpy(info->version, DRIVER_VERSION, 32);
+ strncpy(info->fw_version, "N/A", 32);
+ strncpy(info->bus_info, "N/A", 32);
+}
+
+static u32 pxa168_get_link(struct net_device *dev)
+{
+ return !!netif_carrier_ok(dev);
+}
+
+static const struct ethtool_ops pxa168_ethtool_ops = {
+ .get_settings = pxa168_get_settings,
+ .set_settings = pxa168_set_settings,
+ .get_drvinfo = pxa168_get_drvinfo,
+ .get_link = pxa168_get_link,
+};
+
+static const struct net_device_ops pxa168_eth_netdev_ops = {
+ .ndo_open = pxa168_eth_open,
+ .ndo_stop = pxa168_eth_stop,
+ .ndo_start_xmit = pxa168_eth_start_xmit,
+ .ndo_set_rx_mode = pxa168_eth_set_rx_mode,
+ .ndo_set_mac_address = pxa168_eth_set_mac_address,
+ .ndo_validate_addr = eth_validate_addr,
+ .ndo_do_ioctl = pxa168_eth_do_ioctl,
+ .ndo_change_mtu = pxa168_eth_change_mtu,
+ .ndo_tx_timeout = pxa168_eth_tx_timeout,
+};
+
+static int pxa168_eth_probe(struct platform_device *pdev)
+{
+ struct pxa168_eth_private *pep = NULL;
+ struct net_device *dev = NULL;
+ struct resource *res;
+ struct clk *clk;
+ int err;
+
+ printk(KERN_NOTICE "PXA168 10/100 Ethernet Driver\n");
+
+ clk = clk_get(&pdev->dev, "MFUCLK");
+ if (IS_ERR(clk)) {
+ printk(KERN_ERR "fast Ethernet failed to get clock\n");
+ return -ENODEV;
+ }
+ clk_enable(clk);
+
+ dev = alloc_etherdev(sizeof(struct pxa168_eth_private));
+ if (!dev) {
+ err = -ENOMEM;
+ goto out;
+ }
+
+ platform_set_drvdata(pdev, dev);
+ pep = netdev_priv(dev);
+ pep->dev = dev;
+ pep->clk = clk;
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (res == NULL) {
+ err = -ENODEV;
+ goto out;
+ }
+ pep->base = ioremap(res->start, res->end - res->start + 1);
+ if (pep->base == NULL) {
+ err = -ENOMEM;
+ goto out;
+ }
+ res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+ BUG_ON(!res);
+ dev->irq = res->start;
+ dev->netdev_ops = &pxa168_eth_netdev_ops;
+ dev->watchdog_timeo = 8 * HZ;
+ dev->base_addr = 0;
+ SET_ETHTOOL_OPS(dev, &pxa168_ethtool_ops);
+
+ INIT_WORK(&pep->tx_timeout_task, pxa168_eth_tx_timeout_task);
+
+ pep->rx_ring_size = NUM_RX_DESCS;
+ pep->tx_ring_size = NUM_TX_DESCS;
+ get_random_mac_addr(dev);
+ pep->pd = pdev->dev.platform_data;
+ netif_napi_add(dev, &pep->napi, pxa168_rx_poll, pep->rx_ring_size);
+
+ memset(&pep->timeout, 0, sizeof(struct timer_list));
+ init_timer(&pep->timeout);
+ pep->timeout.function = rxq_refill_timer_wrapper;
+ pep->timeout.data = (unsigned long)pep;
+
+ pep->smi_bus = mdiobus_alloc();
+ if (pep->smi_bus == NULL) {
+ err = -ENOMEM;
+ goto out;
+ }
+ pep->smi_bus->priv = pep;
+ pep->smi_bus->name = "pxa168_eth smi";
+ pep->smi_bus->read = pxa168_smi_read;
+ pep->smi_bus->write = pxa168_smi_write;
+ snprintf(pep->smi_bus->id, MII_BUS_ID_SIZE, "%d", pdev->id);
+ pep->smi_bus->parent = &pdev->dev;
+ pep->smi_bus->phy_mask = 0xffffffff;
+ if (mdiobus_register(pep->smi_bus) < 0) {
+ err = -ENOMEM;
+ goto out;
+ }
+ pxa168_init_hw(pep);
+ err = ethernet_phy_setup(dev);
+ if (err)
+ goto out;
+ SET_NETDEV_DEV(dev, &pdev->dev);
+ err = register_netdev(dev);
+ if (err)
+ goto out;
+ return 0;
+out:
+ if (pep->clk) {
+ clk_disable(pep->clk);
+ clk_put(pep->clk);
+ pep->clk = NULL;
+ }
+ if (pep->base) {
+ iounmap(pep->base);
+ pep->base = NULL;
+ }
+ if (dev)
+ free_netdev(dev);
+ return err;
+}
+
+static int pxa168_eth_remove(struct platform_device *pdev)
+{
+ struct net_device *dev = platform_get_drvdata(pdev);
+ struct pxa168_eth_private *pep = netdev_priv(dev);
+
+ if (pep->htpr) {
+ dma_free_coherent(NULL, HASH_ADDR_TABLE_SIZE + 7,
+ pep->htpr, pep->htpr_dma);
+ pep->htpr = NULL;
+ }
+ if (pep->clk) {
+ clk_disable(pep->clk);
+ clk_put(pep->clk);
+ pep->clk = NULL;
+ }
+ if (pep->phy != NULL)
+ phy_detach(pep->phy);
+
+ iounmap(pep->base);
+ pep->base = NULL;
+ unregister_netdev(dev);
+ flush_scheduled_work();
+ free_netdev(dev);
+ platform_set_drvdata(pdev, NULL);
+ return 0;
+}
+
+static void pxa168_eth_shutdown(struct platform_device *pdev)
+{
+ struct net_device *dev = platform_get_drvdata(pdev);
+ eth_port_reset(dev);
+}
+
+#ifdef CONFIG_PM
+static int pxa168_eth_resume(struct platform_device *pdev)
+{
+ return -ENOSYS;
+}
+
+static int pxa168_eth_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ return -ENOSYS;
+}
+
+#else
+#define pxa168_eth_resume NULL
+#define pxa168_eth_suspend NULL
+#endif
+
+static struct platform_driver pxa168_eth_driver = {
+ .probe = pxa168_eth_probe,
+ .remove = pxa168_eth_remove,
+ .shutdown = pxa168_eth_shutdown,
+ .resume = pxa168_eth_resume,
+ .suspend = pxa168_eth_suspend,
+ .driver = {
+ .name = DRIVER_NAME,
+ },
+};
+
+static int __init pxa168_init_module(void)
+{
+ return platform_driver_register(&pxa168_eth_driver);
+}
+
+static void __exit pxa168_cleanup_module(void)
+{
+ platform_driver_unregister(&pxa168_eth_driver);
+}
+
+module_init(pxa168_init_module);
+module_exit(pxa168_cleanup_module);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Ethernet driver for Marvell PXA168");
+MODULE_ALIAS("platform:pxa168_eth");
diff --git a/include/linux/pxa168_eth.h b/include/linux/pxa168_eth.h
new file mode 100644
index 0000000..152981c
--- /dev/null
+++ b/include/linux/pxa168_eth.h
@@ -0,0 +1,20 @@
+/*
+ *pxa168 ethernet platform device data definition file.
+ */
+#ifndef __LINUX_PXA168_ETH_H
+#define __LINUX_PXA168_ETH_H
+
+struct pxa168_eth_platform_data {
+ int phy_addr;
+
+ /* If speed is 0, then speed and duplex are autonegotiated. */
+ int speed; /* 0, SPEED_10, SPEED_100 */
+ int duplex; /* DUPLEX_HALF or DUPLEX_FULL */
+
+ /* init callback is used for board specific initialization
+ * e.g on Aspenite its used to initialize the PHY transceiver.
+ */
+ int (*init)(void);
+};
+
+#endif /* __LINUX_PXA168_ETH_H */
--
1.5.3.3
^ permalink raw reply related
* Re: [RFC PATCH] platform: Faciliatate the creation of pseduo-platform busses
From: Grant Likely @ 2010-08-06 15:12 UTC (permalink / raw)
To: Greg KH
Cc: Patrick Pannuto, Patrick Pannuto, linux-kernel@vger.kernel.org,
linux-arm-msm@vger.kernel.org, linux-omap@vger.kernel.org,
damm@opensource.se, lethal@linux-sh.org, rjw@sisk.pl,
dtor@mail.ru, eric.y.miao@gmail.com, netdev@vger.kernel.org,
Kevin Hilman
In-Reply-To: <20100806142727.GB4921@suse.de>
On Fri, Aug 6, 2010 at 8:27 AM, Greg KH <gregkh@suse.de> wrote:
> On Thu, Aug 05, 2010 at 04:59:35PM -0600, Grant Likely wrote:
>> (On that point Greg, what is the reason for even having the
>> /sys/devices/platform/ parent? Why not just let the platform devices
>> sit at the root of the device tree? In the OF case (granted, I'm
>> biased) all of the platform_device registrations reflect the actual
>> device hierarchy expressed in the device tree data.)
>
> If we sat them at the "root", there would be a bunch of them there. I
> don't know, we could drop the parent, I guess whoever created the
> platform device oh so long ago, decided that it would look nicer to be
> in this type of structure.
Personally I'd rather see a meaningful structure used here. Maybe
having them all in the root will encourage people to find realistic
parents for their platform devices. :-) Why don't I float a patch to
remove this and see if anybody freaks out. Should I wrap it with a
CONFIG_ so that it can be configurable for a release or to, or just
make it unconditional?
>> Now, having gone on this whole long tirade, it looks like having
>> separate platform bus types may not be the best approach after all.
>
> I totally agree, and thanks for the detailed explaination, it saved me
> from having to write up the same thing :)
:-)
--
Grant Likely, B.Sc., P.Eng.
Secret Lab Technologies Ltd.
^ permalink raw reply
* RE: [PATCH] net: add Fast Ethernet driver for PXA168.
From: Sachin Sanap @ 2010-08-06 15:26 UTC (permalink / raw)
To: buytenh@wantstofly.org
Cc: netdev@vger.kernel.org, Ashish Karkare, Prabhanjan Sarnaik,
eric.y.miao@gmail.com, Philip Rakity, Mark Brown
In-Reply-To: <20100806193931.GA428@pe-dt013.marvell.com>
Sorry for resending the patch as new thread. Discard this patch. I will send the patch again as a reply to previous thread.
-Sachin
> -----Original Message-----
> From: Sachin Sanap [mailto:ssanap@marvell.com]
> Sent: Saturday, August 07, 2010 1:10 AM
> To: buytenh@wantstofly.org
> Cc: netdev@vger.kernel.org; Ashish Karkare; Prabhanjan Sarnaik;
> eric.y.miao@gmail.com; Philip Rakity; Mark Brown; Sachin Sanap
> Subject: [PATCH] net: add Fast Ethernet driver for PXA168.
>
> Signed-off-by: Sachin Sanap <ssanap@marvell.com>
> ---
> arch/arm/mach-mmp/aspenite.c | 3 +-
> arch/arm/mach-mmp/pxa168.c | 4 +-
> drivers/net/Kconfig | 10 +
> drivers/net/Makefile | 1 +
> drivers/net/pxa168_eth.c | 1592
> ++++++++++++++++++++++++++++++++++++++++++
> include/linux/pxa168_eth.h | 20 +
> 6 files changed, 1626 insertions(+), 4 deletions(-)
> create mode 100644 drivers/net/pxa168_eth.c
> create mode 100644 include/linux/pxa168_eth.h
>
> diff --git a/arch/arm/mach-mmp/aspenite.c b/arch/arm/mach-mmp/aspenite.c
> index 61a6d6a..cec505f 100644
> --- a/arch/arm/mach-mmp/aspenite.c
> +++ b/arch/arm/mach-mmp/aspenite.c
> @@ -200,8 +200,7 @@ static int pxa168_eth_init(void)
> }
>
> static struct pxa168_eth_platform_data pxa168_eth_data = {
> - .phy_addr = 0, /* phy addr depends on boards */
> - .port_number = 0,
> + .phy_addr = 0,
> .init = pxa168_eth_init,
> };
> #endif
> diff --git a/arch/arm/mach-mmp/pxa168.c b/arch/arm/mach-mmp/pxa168.c
> index f7d1158..6fcaf0c 100644
> --- a/arch/arm/mach-mmp/pxa168.c
> +++ b/arch/arm/mach-mmp/pxa168.c
> @@ -97,7 +97,7 @@ static struct clk_lookup pxa168_clkregs[] = {
> INIT_CLKREG(&clk_ssp4, "pxa168-ssp.3", NULL),
> INIT_CLKREG(&clk_ssp5, "pxa168-ssp.4", NULL),
> INIT_CLKREG(&clk_nand, "pxa3xx-nand", NULL),
> - INIT_CLKREG(&clk_mfu, "pxa168-mfu", "MFUCLK"),
> + INIT_CLKREG(&clk_mfu, "pxa168-eth", "MFUCLK"),
> };
>
> static int __init pxa168_init(void)
> @@ -149,4 +149,4 @@ PXA168_DEVICE(ssp2, "pxa168-ssp", 1, SSP2, 0xd401c000,
> 0x40, 54, 55);
> PXA168_DEVICE(ssp3, "pxa168-ssp", 2, SSP3, 0xd401f000, 0x40, 56, 57);
> PXA168_DEVICE(ssp4, "pxa168-ssp", 3, SSP4, 0xd4020000, 0x40, 58, 59);
> PXA168_DEVICE(ssp5, "pxa168-ssp", 4, SSP5, 0xd4021000, 0x40, 60, 61);
> -PXA168_DEVICE(mfu, "pxa168-mfu", -1, MFU, 0xc0800000, 0x0fff);
> +PXA168_DEVICE(mfu, "pxa168-eth", -1, MFU, 0xc0800000, 0x0fff);
> diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig
> index ce2fcdd..78cd7e8 100644
> --- a/drivers/net/Kconfig
> +++ b/drivers/net/Kconfig
> @@ -927,6 +927,16 @@ config SMC91X
> The module will be called smc91x. If you want to compile it as a
> module, say M here and read
> <file:Documentation/kbuild/modules.txt>.
>
> +config PXA168_ETH
> + tristate "Marvell pxa168 ethernet support"
> + depends on CPU_PXA168
> + select PHYLIB
> + help
> + This driver supports the pxa168 Ethernet ports.
> +
> + To compile this driver as a module, choose M here. The module
> + will be called pxa168_eth.
> +
> config NET_NETX
> tristate "NetX Ethernet support"
> select MII
> diff --git a/drivers/net/Makefile b/drivers/net/Makefile
> index 0a0512a..a42d437 100644
> --- a/drivers/net/Makefile
> +++ b/drivers/net/Makefile
> @@ -244,6 +244,7 @@ obj-$(CONFIG_MYRI10GE) += myri10ge/
> obj-$(CONFIG_SMC91X) += smc91x.o
> obj-$(CONFIG_SMC911X) += smc911x.o
> obj-$(CONFIG_SMSC911X) += smsc911x.o
> +obj-$(CONFIG_PXA168_ETH) += pxa168_eth.o
> obj-$(CONFIG_BFIN_MAC) += bfin_mac.o
> obj-$(CONFIG_DM9000) += dm9000.o
> obj-$(CONFIG_PASEMI_MAC) += pasemi_mac_driver.o
> diff --git a/drivers/net/pxa168_eth.c b/drivers/net/pxa168_eth.c
> new file mode 100644
> index 0000000..c00fc3c
> --- /dev/null
> +++ b/drivers/net/pxa168_eth.c
> @@ -0,0 +1,1592 @@
> +/*
> + * PXA168 ethernet driver.
> + * Most of the code is derived from mv643xx ethernet driver.
> + *
> + * Copyright (C) 2010 Marvell International Ltd.
> + * Philip Rakity <prakity@marvell.com>
> + * Mark Brown <markb@marvell.com>
> + * Sachin Sanap <ssanap@marvell.com>
> + *
> + * This program is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU General Public License
> + * as published by the Free Software Foundation; either version 2
> + * of the License, or (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, write to the Free Software
> + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
> USA.
> + */
> +
> +#include <linux/init.h>
> +#include <linux/dma-mapping.h>
> +#include <linux/in.h>
> +#include <linux/ip.h>
> +#include <linux/tcp.h>
> +#include <linux/udp.h>
> +#include <linux/etherdevice.h>
> +#include <linux/bitops.h>
> +#include <linux/delay.h>
> +#include <linux/ethtool.h>
> +#include <linux/platform_device.h>
> +#include <linux/module.h>
> +#include <linux/kernel.h>
> +#include <linux/workqueue.h>
> +#include <linux/clk.h>
> +#include <linux/phy.h>
> +#include <linux/io.h>
> +#include <linux/types.h>
> +#include <asm/pgtable.h>
> +#include <asm/system.h>
> +#include <linux/delay.h>
> +#include <linux/dma-mapping.h>
> +#include <asm/cacheflush.h>
> +#include <linux/pxa168_eth.h>
> +
> +#define DRIVER_NAME "pxa168-eth"
> +#define DRIVER_VERSION "0.3"
> +
> +/*
> + * Registers
> + */
> +
> +#define PHY_ADDRESS 0x0000
> +#define SMI 0x0010
> +#define PORT_CONFIG 0x0400
> +#define PORT_CONFIG_EXT 0x0408
> +#define PORT_COMMAND 0x0410
> +#define PORT_STATUS 0x0418
> +#define HTPR 0x0428
> +#define SDMA_CONFIG 0x0440
> +#define SDMA_CMD 0x0448
> +#define INT_CAUSE 0x0450
> +#define INT_W_CLEAR 0x0454
> +#define INT_MASK 0x0458
> +#define ETH_F_RX_DESC_0 0x0480
> +#define ETH_C_RX_DESC_0 0x04A0
> +#define ETH_C_TX_DESC_1 0x04E4
> +
> +/* smi register */
> +#define SMI_BUSY (1 << 28) /* 0 - Write, 1 - Read */
> +#define SMI_R_VALID (1 << 27) /* 0 - Write, 1 - Read */
> +#define SMI_OP_W (0 << 26) /* Write operation */
> +#define SMI_OP_R (1 << 26) /* Read operation */
> +
> +#define PHY_WAIT_ITERATIONS 500
> +
> +#define PXA168_ETH_PHY_ADDR_DEFAULT 0
> +/* RX & TX descriptor command */
> +#define BUF_OWNED_BY_DMA (1 << 31)
> +
> +/* RX descriptor status */
> +#define RX_EN_INT (1 << 23)
> +#define RX_FIRST_DESC (1 << 17)
> +#define RX_LAST_DESC (1 << 16)
> +#define RX_ERROR (1 << 15)
> +
> +/* TX descriptor command */
> +#define TX_EN_INT (1 << 23)
> +#define TX_GEN_CRC (1 << 22)
> +#define TX_ZERO_PADDING (1 << 18)
> +#define TX_FIRST_DESC (1 << 17)
> +#define TX_LAST_DESC (1 << 16)
> +#define TX_ERROR (1 << 15)
> +
> +/* SDMA_CMD */
> +#define SDMA_CMD_AT (1 << 31)
> +#define SDMA_CMD_TXDL (1 << 24)
> +#define SDMA_CMD_TXDH (1 << 23)
> +#define SDMA_CMD_AR (1 << 15)
> +#define SDMA_CMD_ERD (1 << 7)
> +
> +/* Bit definitions of the Port Config Reg */
> +#define PCR_HS (1 << 12)
> +#define PCR_EN (1 << 7)
> +#define PCR_PM (1 << 0)
> +
> +/* Bit definitions of the Port Config Extend Reg */
> +#define PCXR_2BSM (1 << 28)
> +#define PCXR_DSCP_EN (1 << 21)
> +#define PCXR_MFL_1518 (0 << 14)
> +#define PCXR_MFL_1536 (1 << 14)
> +#define PCXR_MFL_2048 (2 << 14)
> +#define PCXR_MFL_64K (3 << 14)
> +#define PCXR_FLP (1 << 11)
> +#define PCXR_PRIO_TX_OFF 3
> +#define PCXR_TX_HIGH_PRI (7 << PCXR_PRIO_TX_OFF)
> +
> +/* Bit definitions of the SDMA Config Reg */
> +#define SDCR_BSZ_OFF 12
> +#define SDCR_BSZ8 (3 << SDCR_BSZ_OFF)
> +#define SDCR_BSZ4 (2 << SDCR_BSZ_OFF)
> +#define SDCR_BSZ2 (1 << SDCR_BSZ_OFF)
> +#define SDCR_BSZ1 (0 << SDCR_BSZ_OFF)
> +#define SDCR_BLMR (1 << 6)
> +#define SDCR_BLMT (1 << 7)
> +#define SDCR_RIFB (1 << 9)
> +#define SDCR_RC_OFF 2
> +#define SDCR_RC_MAX_RETRANS (0xf << SDCR_RC_OFF)
> +
> +/*
> + * Bit definitions of the Interrupt Cause Reg
> + * and Interrupt MASK Reg is the same
> + */
> +#define ICR_RXBUF (1 << 0)
> +#define ICR_TXBUF_H (1 << 2)
> +#define ICR_TXBUF_L (1 << 3)
> +#define ICR_TXEND_H (1 << 6)
> +#define ICR_TXEND_L (1 << 7)
> +#define ICR_RXERR (1 << 8)
> +#define ICR_TXERR_H (1 << 10)
> +#define ICR_TXERR_L (1 << 11)
> +#define ICR_TX_UDR (1 << 13)
> +#define ICR_MII_CH (1 << 28)
> +
> +#define ALL_INTS (ICR_TXBUF_H | ICR_TXBUF_L | ICR_TX_UDR |\
> + ICR_TXERR_H | ICR_TXERR_L |\
> + ICR_TXEND_H | ICR_TXEND_L |\
> + ICR_RXBUF | ICR_RXERR | ICR_MII_CH)
> +
> +#define ETH_HW_IP_ALIGN 2 /* hw aligns IP header */
> +#define ETH_EXTRA_HEADER (6+6+2+4) /* dest+src addr+protocol id+crc
> */
> +#define ETH_DATA_LEN 1500
> +#define MAX_PKT_SIZE 1518
> +
> +#define NUM_RX_DESCS 64
> +#define NUM_TX_DESCS 64
> +#define MAX_DESCS_PER_HIGH (60)
> +#define TX_DESC_COUNT_LOW (10)
> +
> +#define HASH_ADD 0
> +#define HASH_DELETE 1
> +#define HASH_ADDR_TABLE_SIZE 0x4000 /* 16K (1/2K address - PCR_HS ==
> 1) */
> +#define HOP_NUMBER 12
> +
> +/* Bit definitions for Port status */
> +#define PORT_SPEED_100 (1 << 0)
> +#define FULL_DUPLEX (1 << 1)
> +#define FLOW_CONTROL_ENABLED (1 << 2)
> +#define LINK_UP (1 << 3)
> +
> +/* Bit definitions for work to be done */
> +#define WORK_LINK (1 << 0)
> +#define WORK_TX_DONE (1 << 1)
> +
> +#define TX_DONE_INTERVAL 30
> +
> +struct rx_desc {
> + u32 cmd_sts; /* Descriptor command status */
> + u16 byte_cnt; /* Descriptor buffer byte count */
> + u16 buf_size; /* Buffer size */
> + u32 buf_ptr; /* Descriptor buffer pointer */
> + u32 next_desc_ptr; /* Next descriptor pointer */
> +};
> +
> +struct tx_desc {
> + u32 cmd_sts; /* Command/status field */
> + u16 reserved;
> + u16 byte_cnt; /* buffer byte count */
> + u32 buf_ptr; /* pointer to buffer for this descriptor */
> + u32 next_desc_ptr; /* Pointer to next descriptor */
> +};
> +
> +struct pxa168_eth_private {
> + int rx_resource_err; /* Rx ring resource error flag */
> +
> + /* Next available and first returning Rx resource */
> + int rx_curr_desc_q, rx_used_desc_q;
> +
> + /* Next available and first returning Tx resource */
> + int tx_curr_desc_q, tx_used_desc_q;
> +
> + struct rx_desc *p_rx_desc_area;
> + dma_addr_t rx_desc_dma;
> + int rx_desc_area_size;
> + struct sk_buff **rx_skb;
> +
> + struct tx_desc *p_tx_desc_area;
> + dma_addr_t tx_desc_dma;
> + int tx_desc_area_size;
> + struct sk_buff **tx_skb;
> +
> + struct work_struct tx_timeout_task;
> +
> + struct net_device *dev;
> + struct napi_struct napi;
> + u8 work_todo;
> +
> + struct net_device_stats stats;
> + /* Size of Tx Ring per queue */
> + int tx_ring_size;
> + /* Number of tx descriptors in use */
> + int tx_desc_count;
> + /* Size of Rx Ring per queue */
> + int rx_ring_size;
> + /* Number of rx descriptors in use */
> + int rx_desc_count;
> +
> + /*
> + * Used in case RX Ring is empty, which can occur when
> + * system does not have resources (skb's)
> + */
> + struct timer_list timeout;
> + struct mii_bus *smi_bus;
> + struct phy_device *phy;
> +
> + /* clock */
> + struct clk *clk;
> + struct pxa168_eth_platform_data *pd;
> + /*
> + * Ethernet controller base address.
> + */
> + void __iomem *base;
> +
> + /* Pointer to the hardware address filter table */
> + void *htpr;
> + dma_addr_t htpr_dma;
> +};
> +
> +struct addr_table_entry {
> + u32 lo;
> + u32 hi;
> +};
> +
> +/* Bit fields of a Hash Table Entry */
> +enum hash_table_entry {
> + HASH_ENTRY_VALID = 1,
> + SKIP = 2,
> + HASH_ENTRY_RECEIVE_DISCARD = 4,
> + HASH_ENTRY_RECEIVE_DISCARD_BIT = 2
> +};
> +
> +static int pxa168_get_settings(struct net_device *dev, struct ethtool_cmd
> *cmd);
> +static int pxa168_set_settings(struct net_device *dev, struct ethtool_cmd
> *cmd);
> +static int pxa168_init_hw(struct pxa168_eth_private *pep);
> +static void eth_port_reset(struct net_device *dev);
> +static void eth_port_start(struct net_device *dev);
> +static int pxa168_eth_open(struct net_device *dev);
> +static int pxa168_eth_stop(struct net_device *dev);
> +static int ethernet_phy_setup(struct net_device *dev);
> +
> +static inline u32 rdl(struct pxa168_eth_private *pep, int offset)
> +{
> + return readl(pep->base + offset);
> +}
> +
> +static inline void wrl(struct pxa168_eth_private *pep, int offset, u32
> data)
> +{
> + writel(data, pep->base + offset);
> +}
> +
> +static void abort_dma(struct pxa168_eth_private *pep)
> +{
> + int delay;
> + int max_retries = 40;
> +
> + do {
> + wrl(pep, SDMA_CMD, SDMA_CMD_AR | SDMA_CMD_AT);
> + udelay(100);
> +
> + delay = 10;
> + while ((rdl(pep, SDMA_CMD) & (SDMA_CMD_AR | SDMA_CMD_AT))
> + && delay-- > 0) {
> + udelay(10);
> + }
> + } while (max_retries-- > 0 && delay <= 0);
> +
> + if (max_retries <= 0)
> + printk(KERN_ERR "%s : DMA Stuck\n", __func__);
> +}
> +
> +static int ethernet_phy_get(struct pxa168_eth_private *pep)
> +{
> + unsigned int reg_data;
> +
> + reg_data = rdl(pep, PHY_ADDRESS);
> +
> + return reg_data & 0x1f;
> +}
> +
> +static void ethernet_phy_set_addr(struct pxa168_eth_private *pep, int
> phy_addr)
> +{
> + u32 reg_data;
> +
> + reg_data = rdl(pep, PHY_ADDRESS);
> + reg_data &= ~(0x1f);
> + reg_data |= phy_addr & 0x1f;
> + wrl(pep, PHY_ADDRESS, reg_data);
> +}
> +
> +static void ethernet_phy_reset(struct pxa168_eth_private *pep)
> +{
> + int data;
> +
> + data = phy_read(pep->phy, MII_BMCR);
> + if (data < 0)
> + return;
> +
> + data |= BMCR_RESET;
> + if (phy_write(pep->phy, MII_BMCR, data) < 0)
> + return;
> +
> + do {
> + data = phy_read(pep->phy, MII_BMCR);
> + } while (data >= 0 && data & BMCR_RESET);
> +}
> +
> +static void rxq_refill(struct net_device *dev)
> +{
> + struct pxa168_eth_private *pep = netdev_priv(dev);
> + struct sk_buff *skb;
> + struct rx_desc *p_used_rx_desc;
> + int used_rx_desc;
> +
> + while (pep->rx_desc_count < pep->rx_ring_size) {
> + skb = dev_alloc_skb(MAX_PKT_SIZE + ETH_HW_IP_ALIGN);
> + if (!skb)
> + break;
> + pep->rx_desc_count++;
> + /* Get 'used' Rx descriptor */
> + used_rx_desc = pep->rx_used_desc_q;
> + p_used_rx_desc = &pep->p_rx_desc_area[used_rx_desc];
> + p_used_rx_desc->buf_ptr = dma_map_single(NULL,
> + skb->data,
> + MAX_PKT_SIZE +
> + ETH_HW_IP_ALIGN,
> + DMA_FROM_DEVICE);
> + p_used_rx_desc->buf_size = MAX_PKT_SIZE + ETH_HW_IP_ALIGN;
> + pep->rx_skb[used_rx_desc] = skb;
> +
> + /* Return the descriptor to DMA ownership */
> + wmb();
> + p_used_rx_desc->cmd_sts = BUF_OWNED_BY_DMA | RX_EN_INT;
> + wmb();
> +
> + /* Move the used descriptor pointer to the next descriptor */
> + pep->rx_used_desc_q = (used_rx_desc + 1) % pep->rx_ring_size;
> +
> + /* Any Rx return cancels the Rx resource error status */
> + pep->rx_resource_err = 0;
> +
> + skb_reserve(skb, ETH_HW_IP_ALIGN);
> + }
> +
> + /*
> + * If RX ring is empty of SKB, set a timer to try allocating
> + * again at a later time.
> + */
> + if (pep->rx_desc_count == 0) {
> + pep->timeout.expires = jiffies + (HZ / 10); /* 100 mSec */
> + add_timer(&pep->timeout);
> + }
> +}
> +
> +static inline void rxq_refill_timer_wrapper(unsigned long data)
> +{
> + struct pxa168_eth_private *pep = (void *)data;
> + napi_schedule(&pep->napi);
> +}
> +
> +static inline u32 nibble_swapping_32_bit(u32 x)
> +{
> + return (((x) & 0xf0f0f0f0) >> 4) | (((x) & 0x0f0f0f0f) << 4);
> +}
> +
> +static inline u32 nibble_swapping_16_bit(u32 x)
> +{
> + return (((x) & 0x0000f0f0) >> 4) | (((x) & 0x00000f0f) << 4);
> +}
> +
> +static inline u32 flip_4_bits(u32 x)
> +{
> + return (((x) & 0x01) << 3) | (((x) & 0x002) << 1)
> + | (((x) & 0x04) >> 1) | (((x) & 0x008) >> 3);
> +}
> +
> +/*
> + * ----------------------------------------------------------------------
> ------
> + * This function will calculate the hash function of the address.
> + * depends on the hash mode and hash size.
> + * Inputs
> + * mac_high - the 2 most significant bytes of the MAC address.
> + * mac_low - the 4 least significant bytes of the MAC address.
> + * Outputs
> + * return the calculated entry.
> + */
> +static u32 hash_function(u32 mac_high, u32 mac_low)
> +{
> + u32 hash_result;
> + u32 addr_high;
> + u32 addr_low;
> + u32 addr0;
> + u32 addr1;
> + u32 addr2;
> + u32 addr3;
> + u32 addr_high_swapped;
> + u32 addr_low_swapped;
> +
> + addr_high = nibble_swapping_16_bit(mac_high);
> + addr_low = nibble_swapping_32_bit(mac_low);
> +
> + addr_high_swapped = flip_4_bits(addr_high & 0xf)
> + + ((flip_4_bits((addr_high >> 4) & 0xf)) << 4)
> + + ((flip_4_bits((addr_high >> 8) & 0xf)) << 8)
> + + ((flip_4_bits((addr_high >> 12) & 0xf)) << 12);
> +
> + addr_low_swapped = flip_4_bits(addr_low & 0xf)
> + + ((flip_4_bits((addr_low >> 4) & 0xf)) << 4)
> + + ((flip_4_bits((addr_low >> 8) & 0xf)) << 8)
> + + ((flip_4_bits((addr_low >> 12) & 0xf)) << 12)
> + + ((flip_4_bits((addr_low >> 16) & 0xf)) << 16)
> + + ((flip_4_bits((addr_low >> 20) & 0xf)) << 20)
> + + ((flip_4_bits((addr_low >> 24) & 0xf)) << 24)
> + + ((flip_4_bits((addr_low >> 28) & 0xf)) << 28);
> +
> + addr_high = addr_high_swapped;
> + addr_low = addr_low_swapped;
> +
> + addr0 = (addr_low >> 2) & 0x03f;
> + addr1 = (addr_low & 0x003) | ((addr_low >> 8) & 0x7f) << 2;
> + addr2 = (addr_low >> 15) & 0x1ff;
> + addr3 = ((addr_low >> 24) & 0x0ff) | ((addr_high & 1) << 8);
> +
> + hash_result = (addr0 << 9) | (addr1 ^ addr2 ^ addr3);
> + hash_result = hash_result & 0x07ff;
> + return hash_result;
> +}
> +
> +/*
> + * ----------------------------------------------------------------------
> ------
> + * This function will add/del an entry to the address table.
> + * Inputs
> + * pep - ETHERNET .
> + * mac_high - the 2 most significant bytes of the MAC address.
> + * mac_low - the 4 least significant bytes of the MAC address.
> + * skip - if 1, skip this address.Used in case of deleting an entry which
> is a
> + * part of chain in the hash table.We cant just delete the entry
> since
> + * that will break the chain.We need to defragment the tables time to
> + * time.
> + * rd - 0 Discard packet upon match.
> + * - 1 Receive packet upon match.
> + * Outputs
> + * address table entry is added/deleted.
> + * 0 if success.
> + * -ENOSPC if table full
> + */
> +static int add_del_hash_entry(struct pxa168_eth_private *pep, u32
> mac_high,
> + u32 mac_low, u32 rd, u32 skip, int del)
> +{
> + struct addr_table_entry *entry, *start;
> + u32 new_high;
> + u32 new_low;
> + u32 i;
> +
> + new_low = (((mac_high >> 4) & 0xf) << 15)
> + | (((mac_high >> 0) & 0xf) << 11)
> + | (((mac_high >> 12) & 0xf) << 7)
> + | (((mac_high >> 8) & 0xf) << 3)
> + | (((mac_low >> 20) & 0x1) << 31)
> + | (((mac_low >> 16) & 0xf) << 27)
> + | (((mac_low >> 28) & 0xf) << 23)
> + | (((mac_low >> 24) & 0xf) << 19)
> + | (skip << SKIP) | (rd << HASH_ENTRY_RECEIVE_DISCARD_BIT)
> + | HASH_ENTRY_VALID;
> +
> + new_high = (((mac_low >> 4) & 0xf) << 15)
> + | (((mac_low >> 0) & 0xf) << 11)
> + | (((mac_low >> 12) & 0xf) << 7)
> + | (((mac_low >> 8) & 0xf) << 3)
> + | (((mac_low >> 21) & 0x7) << 0);
> +
> + /*
> + * Pick the appropriate table, start scanning for free/reusable
> + * entries at the index obtained by hashing the specified MAC
> address
> + */
> + start = (struct addr_table_entry *)(pep->htpr);
> + entry = start + hash_function(mac_high, mac_low);
> + for (i = 0; i < HOP_NUMBER; i++) {
> + if (!(entry->lo & HASH_ENTRY_VALID)) {
> + break;
> + } else {
> + /* if same address put in same position */
> + if (((entry->lo & 0xfffffff8) == (new_low & 0xfffffff8))
> + && (entry->hi == new_high)) {
> + break;
> + }
> + }
> + if (entry == start + 0x7ff)
> + entry = start;
> + else
> + entry++;
> + }
> +
> + if (((entry->lo & 0xfffffff8) != (new_low & 0xfffffff8)) &&
> + (entry->hi != new_high) && del)
> + return 0;
> +
> + if (i == HOP_NUMBER) {
> + if (!del) {
> + printk(KERN_INFO "%s: table section is full\n",
> + __FILE__);
> + return -ENOSPC;
> + } else
> + return 0;
> + }
> +
> + /*
> + * Update the selected entry
> + */
> + if (del) {
> + entry->hi = 0;
> + entry->lo = 0;
> + } else {
> + entry->hi = cpu_to_le32(new_high);
> + entry->lo = cpu_to_le32(new_low);
> + }
> +
> + return 0;
> +}
> +
> +/*
> + * ----------------------------------------------------------------------
> ------
> + * Create an addressTable entry from MAC address info
> + * found in the specifed net_device struct
> + *
> + * Input : pointer to ethernet interface network device structure
> + * Output : N/A
> + */
> +static void update_hash_table_mac_address(struct pxa168_eth_private *pep,
> + u8 *oaddr, u8 *addr)
> +{
> + u32 mac_high;
> + u32 mac_low;
> +
> + /* Delete old entry */
> + if (oaddr) {
> + mac_high = cpu_to_le32((oaddr[0] << 8) | oaddr[1]);
> + mac_low = cpu_to_le32((oaddr[2] << 24) | (oaddr[3] << 16) |
> + (oaddr[4] << 8) | oaddr[5]);
> + add_del_hash_entry(pep, mac_high, mac_low, 1, 0, HASH_DELETE);
> + }
> + /* Add new entry */
> + mac_high = cpu_to_le32((addr[0] << 8) | addr[1]);
> + mac_low = cpu_to_le32((addr[2] << 24) | (addr[3] << 16) |
> + (addr[4] << 8) | addr[5]);
> + add_del_hash_entry(pep, mac_high, mac_low, 1, 0, HASH_ADD);
> +}
> +
> +static int init_hash_table(struct pxa168_eth_private *pep)
> +{
> + /*
> + * Hardware expects CPU to build a hash table based on a predefined
> + * hash function and populate it based on hardware address. The
> + * location of the hash table is identified by 32-bit pointer stored
> + * in HTPR internal register. Two possible sizes exists for the hash
> + * table 256kB and 16kB.We currently only support 16kB.
> + */
> + /* TODO: Add support for 256kB hash table */
> + if (pep->htpr == NULL) {
> + pep->htpr = dma_alloc_coherent(NULL,
> + HASH_ADDR_TABLE_SIZE,
> + &pep->htpr_dma, GFP_KERNEL);
> + if (pep->htpr == NULL)
> + return -ENOMEM;
> + }
> + memset(pep->htpr, 0, HASH_ADDR_TABLE_SIZE);
> + wrl(pep, HTPR, pep->htpr_dma);
> + return 0;
> +}
> +
> +static void pxa168_eth_set_rx_mode(struct net_device *dev)
> +{
> + struct pxa168_eth_private *pep = netdev_priv(dev);
> + struct netdev_hw_addr *ha;
> + u32 val;
> +
> + val = rdl(pep, PORT_CONFIG);
> + if (dev->flags & IFF_PROMISC)
> + val |= PCR_PM;
> + else
> + val &= ~PCR_PM;
> + wrl(pep, PORT_CONFIG, val);
> + netdev_for_each_mc_addr(ha, dev)
> + update_hash_table_mac_address(pep, NULL, ha->addr);
> +}
> +
> +static int pxa168_eth_set_mac_address(struct net_device *dev, void *addr)
> +{
> + struct sockaddr *sa = addr;
> + struct pxa168_eth_private *pep = netdev_priv(dev);
> + unsigned char oldMac[ETH_ALEN];
> +
> + if (!is_valid_ether_addr(sa->sa_data))
> + return -EINVAL;
> + memcpy(oldMac, dev->dev_addr, ETH_ALEN);
> + memcpy(dev->dev_addr, sa->sa_data, ETH_ALEN);
> + netif_addr_lock_bh(dev);
> + update_hash_table_mac_address(pep, oldMac, dev->dev_addr);
> + netif_addr_unlock_bh(dev);
> + return 0;
> +}
> +
> +static void eth_port_start(struct net_device *dev)
> +{
> + unsigned int val = 0;
> + struct pxa168_eth_private *pep = netdev_priv(dev);
> + int tx_curr_desc, rx_curr_desc;
> +
> + /* Perform PHY reset, if there is a PHY. */
> + if (pep->phy != NULL) {
> + struct ethtool_cmd cmd;
> +
> + pxa168_get_settings(pep->dev, &cmd);
> + ethernet_phy_reset(pep);
> + pxa168_set_settings(pep->dev, &cmd);
> + }
> +
> + /* Assignment of Tx CTRP of given queue */
> + tx_curr_desc = pep->tx_curr_desc_q;
> + wrl(pep, ETH_C_TX_DESC_1,
> + (u32) ((struct tx_desc *)pep->tx_desc_dma + tx_curr_desc));
> +
> + /* Assignment of Rx CRDP of given queue */
> + rx_curr_desc = pep->rx_curr_desc_q;
> + wrl(pep, ETH_C_RX_DESC_0,
> + (u32) ((struct rx_desc *)pep->rx_desc_dma + rx_curr_desc));
> +
> + wrl(pep, ETH_F_RX_DESC_0,
> + (u32) ((struct rx_desc *)pep->rx_desc_dma + rx_curr_desc));
> +
> + /* Clear all interrupts */
> + wrl(pep, INT_CAUSE, 0);
> +
> + /* Enable all interrupts for receive, transmit and error. */
> + wrl(pep, INT_MASK, ALL_INTS);
> +
> + val = rdl(pep, PORT_CONFIG);
> + val |= PCR_EN;
> + wrl(pep, PORT_CONFIG, val);
> +
> + /* Start RX DMA engine */
> + val = rdl(pep, SDMA_CMD);
> + val |= SDMA_CMD_ERD;
> + wrl(pep, SDMA_CMD, val);
> +}
> +
> +static void eth_port_reset(struct net_device *dev)
> +{
> + struct pxa168_eth_private *pep = netdev_priv(dev);
> + unsigned int val = 0;
> +
> + /* Stop all interrupts for receive, transmit and error. */
> + wrl(pep, INT_MASK, 0);
> +
> + /* Clear all interrupts */
> + wrl(pep, INT_CAUSE, 0);
> +
> + /* Stop RX DMA */
> + val = rdl(pep, SDMA_CMD);
> + val &= ~SDMA_CMD_ERD; /* abort dma command */
> +
> + /* Abort any transmit and receive operations and put DMA
> + * in idle state.
> + */
> + abort_dma(pep);
> +
> + /* Disable port */
> + val = rdl(pep, PORT_CONFIG);
> + val &= ~PCR_EN;
> + wrl(pep, PORT_CONFIG, val);
> +}
> +
> +/*
> + * txq_reclaim - Free the tx desc data for completed descriptors
> + * If force is non-zero, frees uncompleted descriptors as well
> + */
> +static int txq_reclaim(struct net_device *dev, int force)
> +{
> + struct pxa168_eth_private *pep = netdev_priv(dev);
> + struct tx_desc *desc;
> + u32 cmd_sts;
> + struct sk_buff *skb;
> + int tx_index;
> + dma_addr_t addr;
> + int count;
> + int released = 0;
> +
> + netif_tx_lock(dev);
> +
> + pep->work_todo &= ~(WORK_TX_DONE);
> + while (pep->tx_desc_count > 0) {
> + tx_index = pep->tx_used_desc_q;
> + desc = &pep->p_tx_desc_area[tx_index];
> + cmd_sts = desc->cmd_sts;
> + if (!force && (cmd_sts & BUF_OWNED_BY_DMA)) {
> + if (released > 0) {
> + goto txq_reclaim_end;
> + } else {
> + released = -1;
> + goto txq_reclaim_end;
> + }
> + }
> + pep->tx_used_desc_q = (tx_index + 1) % pep->tx_ring_size;
> + pep->tx_desc_count--;
> + addr = desc->buf_ptr;
> + count = desc->byte_cnt;
> + skb = pep->tx_skb[tx_index];
> + if (skb)
> + pep->tx_skb[tx_index] = NULL;
> +
> + if (cmd_sts & TX_ERROR) {
> + if (net_ratelimit())
> + printk(KERN_ERR "%s: Error in TX\n", dev->name);
> + dev->stats.tx_errors++;
> + }
> + dma_unmap_single(NULL, addr, count, DMA_TO_DEVICE);
> + if (skb)
> + dev_kfree_skb_irq(skb);
> + released++;
> + }
> +txq_reclaim_end:
> + netif_tx_unlock(dev);
> + return released;
> +}
> +
> +static void pxa168_eth_tx_timeout(struct net_device *dev)
> +{
> + struct pxa168_eth_private *pep = netdev_priv(dev);
> +
> + printk(KERN_INFO "%s: TX timeout desc_count %d\n",
> + dev->name, pep->tx_desc_count);
> +
> + schedule_work(&pep->tx_timeout_task);
> +}
> +
> +static void pxa168_eth_tx_timeout_task(struct work_struct *work)
> +{
> + struct pxa168_eth_private *pep = container_of(work,
> + struct pxa168_eth_private,
> + tx_timeout_task);
> + struct net_device *dev = pep->dev;
> + pxa168_eth_stop(dev);
> + pxa168_eth_open(dev);
> +}
> +
> +static int rxq_process(struct net_device *dev, int budget)
> +{
> + struct pxa168_eth_private *pep = netdev_priv(dev);
> + struct net_device_stats *stats = &dev->stats;
> + unsigned int received_packets = 0;
> + struct sk_buff *skb;
> +
> + while (budget-- > 0) {
> +
> + int rx_next_curr_desc, rx_curr_desc, rx_used_desc;
> + struct rx_desc *rx_desc;
> + unsigned int cmd_sts;
> +
> + /* Do not process Rx ring in case of Rx ring resource error */
> + if (pep->rx_resource_err)
> + break;
> + rx_curr_desc = pep->rx_curr_desc_q;
> + rx_used_desc = pep->rx_used_desc_q;
> + rx_desc = &pep->p_rx_desc_area[rx_curr_desc];
> + cmd_sts = rx_desc->cmd_sts;
> + rmb();
> + if (cmd_sts & (BUF_OWNED_BY_DMA))
> + break;
> + skb = pep->rx_skb[rx_curr_desc];
> + pep->rx_skb[rx_curr_desc] = NULL;
> +
> + rx_next_curr_desc = (rx_curr_desc + 1) % pep->rx_ring_size;
> + pep->rx_curr_desc_q = rx_next_curr_desc;
> +
> + /* Rx descriptors exhausted. */
> + /* Set the Rx ring resource error flag */
> + if (rx_next_curr_desc == rx_used_desc)
> + pep->rx_resource_err = 1;
> + pep->rx_desc_count--;
> + dma_unmap_single(NULL, rx_desc->buf_ptr,
> + MAX_PKT_SIZE + ETH_HW_IP_ALIGN,
> + DMA_FROM_DEVICE);
> + received_packets++;
> + /*
> + * Update statistics.
> + * Note byte count includes 4 byte CRC count
> + */
> + stats->rx_packets++;
> + stats->rx_bytes += rx_desc->byte_cnt;
> + /*
> + * In case received a packet without first / last bits on OR
> + * the error summary bit is on, the packets needs to be droped.
> + */
> + if (((cmd_sts & (RX_FIRST_DESC | RX_LAST_DESC)) !=
> + (RX_FIRST_DESC | RX_LAST_DESC))
> + || (cmd_sts & RX_ERROR)) {
> +
> + stats->rx_dropped++;
> + if ((cmd_sts & (RX_FIRST_DESC | RX_LAST_DESC)) !=
> + (RX_FIRST_DESC | RX_LAST_DESC)) {
> + if (net_ratelimit())
> + printk(KERN_ERR
> + "%s: Rx pkt on multiple desc\n",
> + dev->name);
> + }
> + if (cmd_sts & RX_ERROR)
> + stats->rx_errors++;
> + dev_kfree_skb_irq(skb);
> + } else {
> + /*
> + * The -4 is for the CRC in the trailer of the
> + * received packet
> + */
> + skb_put(skb, rx_desc->byte_cnt - 4);
> + skb->protocol = eth_type_trans(skb, dev);
> + netif_receive_skb(skb);
> + }
> + dev->last_rx = jiffies;
> + }
> + /* Fill RX ring with skb's */
> + rxq_refill(dev);
> + return received_packets;
> +}
> +
> +static int pxa168_eth_collect_events(struct pxa168_eth_private *pep,
> + struct net_device *dev)
> +{
> + u32 icr;
> + int ret = 0;
> +
> + icr = rdl(pep, INT_CAUSE);
> + if (0x00 == icr)
> + return IRQ_NONE;
> +
> + wrl(pep, INT_CAUSE, icr ^ 0xffffffff);
> + if (icr & (ICR_TXBUF_H | ICR_TXBUF_L)) {
> + pep->work_todo |= WORK_TX_DONE;
> + ret = 1;
> + }
> + if (icr & ICR_RXBUF)
> + ret = 1;
> + if (icr & ICR_MII_CH) {
> + pep->work_todo |= WORK_LINK;
> + ret = 1;
> + }
> + return ret;
> +}
> +
> +static void handle_link_event(struct pxa168_eth_private *pep)
> +{
> + struct net_device *dev = pep->dev;
> + u32 port_status;
> + int speed;
> + int duplex;
> + int fc;
> +
> + port_status = rdl(pep, PORT_STATUS);
> + if (!(port_status & LINK_UP)) {
> + if (netif_carrier_ok(dev)) {
> + printk(KERN_INFO "%s: link down\n", dev->name);
> + netif_carrier_off(dev);
> + txq_reclaim(dev, 1);
> + }
> + return;
> + }
> + if (port_status & PORT_SPEED_100)
> + speed = 100;
> + else
> + speed = 10;
> +
> + duplex = (port_status & FULL_DUPLEX) ? 1 : 0;
> + fc = (port_status & FLOW_CONTROL_ENABLED) ? 1 : 0;
> + printk(KERN_INFO "%s: link up, %d Mb/s, %s duplex, "
> + "flow control %sabled\n", dev->name,
> + speed, duplex ? "full" : "half", fc ? "en" : "dis");
> + if (!netif_carrier_ok(dev))
> + netif_carrier_on(dev);
> +}
> +
> +static irqreturn_t pxa168_eth_int_handler(int irq, void *dev_id)
> +{
> + struct net_device *dev = (struct net_device *)dev_id;
> + struct pxa168_eth_private *pep = netdev_priv(dev);
> +
> + if (unlikely(!pxa168_eth_collect_events(pep, dev)))
> + return IRQ_NONE;
> + /* Disable interrupts */
> + wrl(pep, INT_MASK, 0);
> + napi_schedule(&pep->napi);
> + return IRQ_HANDLED;
> +}
> +
> +static int set_port_config_ext(struct pxa168_eth_private *pep, int mtu)
> +{
> + int mtu_size;
> +
> + if ((mtu > ETH_DATA_LEN) || (mtu < 64))
> + return -EINVAL;
> +
> + mtu_size = PCXR_MFL_1518;
> + /* Extended Port Configuration */
> + wrl(pep,
> + PORT_CONFIG_EXT, PCXR_2BSM | /* Two byte suffix aligns IP hdr */
> + PCXR_DSCP_EN | /* Enable DSCP in IP */
> + mtu_size | PCXR_FLP | /* do not force link pass */
> + PCXR_TX_HIGH_PRI); /* Transmit - high priority queue */
> +
> + (pep->dev)->mtu = mtu;
> + return 0;
> +}
> +
> +static int pxa168_init_hw(struct pxa168_eth_private *pep)
> +{
> + int err = 0;
> +
> + /* Disable interrupts */
> + wrl(pep, INT_MASK, 0);
> + wrl(pep, INT_CAUSE, 0);
> + /* Write to ICR to clear interrupts. */
> + wrl(pep, INT_W_CLEAR, 0);
> + /* Abort any transmit and receive operations and put DMA
> + * in idle state.
> + */
> + abort_dma(pep);
> + /* Initialize address hash table */
> + err = init_hash_table(pep);
> + if (err)
> + return err;
> + /* SDMA configuration */
> + wrl(pep, SDMA_CONFIG, SDCR_BSZ8 | /* Burst size = 32 bytes */
> + SDCR_RIFB | /* Rx interrupt on frame */
> + SDCR_BLMT | /* Little endian transmit */
> + SDCR_BLMR | /* Little endian receive */
> + SDCR_RC_MAX_RETRANS); /* Max retransmit count */
> + /* Port Configuration */
> + wrl(pep, PORT_CONFIG, PCR_HS); /* Hash size is 1/2kb */
> + set_port_config_ext(pep, (pep->dev)->mtu);
> + return err;
> +}
> +
> +static int rxq_init(struct net_device *dev)
> +{
> + struct pxa168_eth_private *pep = netdev_priv(dev);
> + struct rx_desc *p_rx_desc;
> + int size = 0, i = 0;
> + int rx_desc_num = pep->rx_ring_size;
> +
> + /* Allocate RX skb rings */
> + pep->rx_skb = kmalloc(sizeof(*pep->rx_skb) * pep->rx_ring_size,
> + GFP_KERNEL);
> + if (!pep->rx_skb) {
> + printk(KERN_ERR "%s: Cannot alloc RX skb ring\n", dev->name);
> + return -ENOMEM;
> + }
> + /* Allocate RX ring */
> + pep->rx_desc_count = 0;
> + size = pep->rx_ring_size * sizeof(struct rx_desc);
> + pep->rx_desc_area_size = size;
> + pep->p_rx_desc_area = dma_alloc_coherent(NULL, size,
> + &pep->rx_desc_dma, GFP_KERNEL);
> + if (!pep->p_rx_desc_area) {
> + printk(KERN_ERR "%s: Cannot alloc RX ring (size %d bytes)\n",
> + dev->name, size);
> + goto out;
> + }
> + memset((void *)pep->p_rx_desc_area, 0, size);
> + /* initialize the next_desc_ptr links in the Rx descriptors ring */
> + p_rx_desc = (struct rx_desc *)pep->p_rx_desc_area;
> + for (i = 0; i < rx_desc_num; i++) {
> + p_rx_desc[i].next_desc_ptr = pep->rx_desc_dma +
> + ((i + 1) % rx_desc_num) * sizeof(struct rx_desc);
> + }
> + /* Save Rx desc pointer to driver struct. */
> + pep->rx_curr_desc_q = 0;
> + pep->rx_used_desc_q = 0;
> + pep->rx_desc_area_size = rx_desc_num * sizeof(struct rx_desc);
> + return 0;
> +out:
> + kfree(pep->rx_skb);
> + return -ENOMEM;
> +}
> +
> +static void rxq_deinit(struct net_device *dev)
> +{
> + struct pxa168_eth_private *pep = netdev_priv(dev);
> + int curr;
> +
> + /* Free preallocated skb's on RX rings */
> + for (curr = 0; pep->rx_desc_count && curr < pep->rx_ring_size;
> curr++) {
> + if (pep->rx_skb[curr]) {
> + dev_kfree_skb(pep->rx_skb[curr]);
> + pep->rx_desc_count--;
> + }
> + }
> + if (pep->rx_desc_count)
> + printk(KERN_ERR
> + "Error in freeing Rx Ring. %d skb's still\n",
> + pep->rx_desc_count);
> + /* Free RX ring */
> + if (pep->p_rx_desc_area)
> + dma_free_coherent(NULL, pep->rx_desc_area_size,
> + pep->p_rx_desc_area, pep->rx_desc_dma);
> + kfree(pep->rx_skb);
> +}
> +
> +static int txq_init(struct net_device *dev)
> +{
> + struct pxa168_eth_private *pep = netdev_priv(dev);
> + struct tx_desc *p_tx_desc;
> + int size = 0, i = 0;
> + int tx_desc_num = pep->tx_ring_size;
> +
> + pep->tx_skb = kmalloc(sizeof(*pep->tx_skb) * pep->tx_ring_size,
> + GFP_KERNEL);
> + if (!pep->tx_skb) {
> + printk(KERN_ERR "%s: Cannot alloc TX skb ring\n", dev->name);
> + return -ENOMEM;
> + }
> + /* Allocate TX ring */
> + pep->tx_desc_count = 0;
> + size = pep->tx_ring_size * sizeof(struct tx_desc);
> + pep->tx_desc_area_size = size;
> + pep->p_tx_desc_area = dma_alloc_coherent(NULL, size,
> + &pep->tx_desc_dma, GFP_KERNEL);
> + if (!pep->p_tx_desc_area) {
> + printk(KERN_ERR "%s: Cannot allocate Tx Ring (size %d
> bytes)\n",
> + dev->name, size);
> + goto out;
> + }
> + memset((void *)pep->p_tx_desc_area, 0, pep->tx_desc_area_size);
> + /* Initialize the next_desc_ptr links in the Tx descriptors ring */
> + p_tx_desc = (struct tx_desc *)pep->p_tx_desc_area;
> + for (i = 0; i < tx_desc_num; i++) {
> + p_tx_desc[i].next_desc_ptr = pep->tx_desc_dma +
> + ((i + 1) % tx_desc_num) * sizeof(struct tx_desc);
> + }
> + pep->tx_curr_desc_q = 0;
> + pep->tx_used_desc_q = 0;
> + pep->tx_desc_area_size = tx_desc_num * sizeof(struct tx_desc);
> + return 0;
> +out:
> + kfree(pep->tx_skb);
> + return -ENOMEM;
> +}
> +
> +static void txq_deinit(struct net_device *dev)
> +{
> + struct pxa168_eth_private *pep = netdev_priv(dev);
> +
> + /* Free outstanding skb's on TX ring */
> + txq_reclaim(dev, 1);
> + BUG_ON(pep->tx_used_desc_q != pep->tx_curr_desc_q);
> + /* Free TX ring */
> + if (pep->p_tx_desc_area)
> + dma_free_coherent(NULL, pep->tx_desc_area_size,
> + pep->p_tx_desc_area, pep->tx_desc_dma);
> + kfree(pep->tx_skb);
> +}
> +
> +static int pxa168_eth_open(struct net_device *dev)
> +{
> + struct pxa168_eth_private *pep = netdev_priv(dev);
> + int err;
> +
> + err = request_irq(dev->irq, pxa168_eth_int_handler,
> + IRQF_DISABLED , dev->name, dev);
> + if (err) {
> + dev_printk(KERN_ERR, &dev->dev, "can't assign irq\n");
> + return -EAGAIN;
> + }
> + pep->rx_resource_err = 0;
> + err = rxq_init(dev);
> + if (err != 0)
> + goto out_free_irq;
> + err = txq_init(dev);
> + if (err != 0)
> + goto out_free_rx_skb;
> + pep->rx_used_desc_q = 0;
> + pep->rx_curr_desc_q = 0;
> +
> + /* Fill RX ring with skb's */
> + rxq_refill(dev);
> + pep->rx_used_desc_q = 0;
> + pep->rx_curr_desc_q = 0;
> + netif_carrier_off(dev);
> + eth_port_start(dev);
> + napi_enable(&pep->napi);
> + return 0;
> +out_free_rx_skb:
> + rxq_deinit(dev);
> +out_free_irq:
> + free_irq(dev->irq, dev);
> + return err;
> +}
> +
> +static int pxa168_eth_stop(struct net_device *dev)
> +{
> + struct pxa168_eth_private *pep = netdev_priv(dev);
> + eth_port_reset(dev);
> +
> + /* Disable interrupts */
> + wrl(pep, INT_MASK, 0);
> + wrl(pep, INT_CAUSE, 0);
> + /* Write to ICR to clear interrupts. */
> + wrl(pep, INT_W_CLEAR, 0);
> + napi_disable(&pep->napi);
> + del_timer_sync(&pep->timeout);
> + netif_carrier_off(dev);
> + free_irq(dev->irq, dev);
> + rxq_deinit(dev);
> + txq_deinit(dev);
> + return 0;
> +}
> +
> +static int pxa168_eth_change_mtu(struct net_device *dev, int mtu)
> +{
> + if ((mtu > ETH_DATA_LEN) || (mtu < 64))
> + return -EINVAL;
> + dev->mtu = mtu;
> + return 0;
> +}
> +
> +static int eth_alloc_tx_desc_index(struct pxa168_eth_private *pep)
> +{
> + int tx_desc_curr;
> +
> + tx_desc_curr = pep->tx_curr_desc_q;
> + pep->tx_curr_desc_q = (tx_desc_curr + 1) % pep->tx_ring_size;
> + BUG_ON(pep->tx_curr_desc_q == pep->tx_used_desc_q);
> + pep->tx_desc_count++;
> + return tx_desc_curr;
> +}
> +
> +static void eth_tx_submit_descs_for_skb(struct pxa168_eth_private *pep,
> + struct sk_buff *skb)
> +{
> + int tx_index;
> + struct tx_desc *desc;
> + int length;
> +
> + tx_index = eth_alloc_tx_desc_index(pep);
> + desc = &pep->p_tx_desc_area[tx_index];
> + length = skb->len;
> + pep->tx_skb[tx_index] = skb;
> + desc->byte_cnt = length;
> + desc->buf_ptr = dma_map_single(NULL, skb->data, length,
> DMA_TO_DEVICE);
> + wmb();
> + desc->cmd_sts = BUF_OWNED_BY_DMA | TX_GEN_CRC | TX_FIRST_DESC |
> + TX_ZERO_PADDING | TX_LAST_DESC;
> + if (unlikely(!(pep->tx_desc_count % TX_DONE_INTERVAL)))
> + desc->cmd_sts |= TX_EN_INT;
> + wmb();
> + wrl(pep, SDMA_CMD, SDMA_CMD_TXDH | SDMA_CMD_ERD);
> +}
> +
> +static int pxa168_rx_poll(struct napi_struct *napi, int budget)
> +{
> + struct pxa168_eth_private *pep =
> + container_of(napi, struct pxa168_eth_private, napi);
> + struct net_device *dev = pep->dev;
> + int work_done = 0;
> +
> + if (unlikely(pep->work_todo & WORK_LINK)) {
> + pep->work_todo &= ~(WORK_LINK);
> + handle_link_event(pep);
> + }
> + /*
> + * We call txq_reclaim every time since in NAPI interupts are
> disabled
> + * and due to this we miss the TX_DONE interrupt,which is not
> updated in
> + * interrupt status register.
> + */
> + txq_reclaim(dev, 0);
> + if (netif_queue_stopped(dev)
> + && pep->tx_ring_size - pep->tx_desc_count > 1) {
> + netif_wake_queue(dev);
> + }
> + work_done = rxq_process(dev, budget);
> + if (work_done < budget) {
> + napi_complete(napi);
> + wrl(pep, INT_MASK, ALL_INTS);
> + }
> + return work_done;
> +}
> +
> +static int pxa168_eth_start_xmit(struct sk_buff *skb, struct net_device
> *dev)
> +{
> + struct pxa168_eth_private *pep = netdev_priv(dev);
> + struct net_device_stats *stats = &dev->stats;
> +
> + eth_tx_submit_descs_for_skb(pep, skb);
> + stats->tx_bytes += skb->len;
> + stats->tx_packets++;
> + dev->trans_start = jiffies;
> + if (pep->tx_ring_size - pep->tx_desc_count <= 1) {
> + /* We handled the current skb, but now we are out of space.*/
> + netif_stop_queue(dev);
> + }
> + return NETDEV_TX_OK;
> +}
> +
> +static int pxa168_smi_read(struct mii_bus *bus, int phy_addr, int regnum)
> +{
> + int val;
> + struct pxa168_eth_private *pep = bus->priv;
> + int i = 0;
> +
> + /* wait for the SMI register to become available */
> + for (i = 0; (val = rdl(pep, SMI)) & SMI_BUSY; i++) {
> + if (i == PHY_WAIT_ITERATIONS) {
> + printk(KERN_ERR
> + "pxa168 PHY timeout, val=0x%x\n", val);
> + return -ETIMEDOUT;
> + }
> + udelay(1);
> + }
> + wrl(pep, SMI, (phy_addr << 16) | (regnum << 21) | SMI_OP_R);
> + /* now wait for the data to be valid */
> + for (i = 0; !((val = rdl(pep, SMI)) & SMI_R_VALID); i++) {
> + if (i == PHY_WAIT_ITERATIONS) {
> + printk(KERN_ERR
> + "pxa168 PHY RD timeout, val=0x%x\n", val);
> + return -ETIMEDOUT;
> + }
> + udelay(1);
> + }
> + return val & 0xffff;
> +}
> +
> +static int pxa168_smi_write(struct mii_bus *bus, int phy_addr, int regnum,
> + u16 value)
> +{
> + struct pxa168_eth_private *pep = bus->priv;
> + int i;
> +
> + /* wait for the SMI register to become available */
> + for (i = 0; rdl(pep, SMI) & SMI_BUSY; i++) {
> + if (i == PHY_WAIT_ITERATIONS) {
> + printk(KERN_ERR "pxa168 PHY busy timeout.\n");
> + return -ETIMEDOUT;
> + }
> + udelay(1);
> + }
> + wrl(pep, SMI, (phy_addr << 16) | (regnum << 21) |
> + SMI_OP_W | (value & 0xffff));
> +
> + return 0;
> +}
> +
> +static int pxa168_eth_do_ioctl(struct net_device *dev, struct ifreq *ifr,
> + int cmd)
> +{
> + struct pxa168_eth_private *pep = netdev_priv(dev);
> + if (pep->phy != NULL)
> + return phy_mii_ioctl(pep->phy, if_mii(ifr), cmd);
> + return -EOPNOTSUPP;
> +}
> +
> +static struct phy_device *phy_scan(struct pxa168_eth_private *pep, int
> phy_addr)
> +{
> + struct mii_bus *bus = pep->smi_bus;
> + struct phy_device *phydev;
> + int start;
> + int num;
> + int i;
> +
> + if (phy_addr == PXA168_ETH_PHY_ADDR_DEFAULT) {
> + /* Scan entire range */
> + start = ethernet_phy_get(pep);
> + num = 32;
> + } else {
> + /* Use phy addr specific to platform */
> + start = phy_addr & 0x1f;
> + num = 1;
> + }
> + phydev = NULL;
> + for (i = 0; i < num; i++) {
> + int addr = (start + i) & 0x1f;
> + if (bus->phy_map[addr] == NULL)
> + mdiobus_scan(bus, addr);
> +
> + if (phydev == NULL) {
> + phydev = bus->phy_map[addr];
> + if (phydev != NULL)
> + ethernet_phy_set_addr(pep, addr);
> + }
> + }
> +
> + return phydev;
> +}
> +
> +static void phy_init(struct pxa168_eth_private *pep, int speed, int
> duplex)
> +{
> + struct phy_device *phy = pep->phy;
> + ethernet_phy_reset(pep);
> +
> + phy_attach(pep->dev, dev_name(&phy->dev), 0,
> PHY_INTERFACE_MODE_MII);
> +
> + if (speed == 0) {
> + phy->autoneg = AUTONEG_ENABLE;
> + phy->speed = 0;
> + phy->duplex = 0;
> + phy->supported &= PHY_BASIC_FEATURES;
> + phy->advertising = phy->supported | ADVERTISED_Autoneg;
> + } else {
> + phy->autoneg = AUTONEG_DISABLE;
> + phy->advertising = 0;
> + phy->speed = speed;
> + phy->duplex = duplex;
> + }
> + phy_start_aneg(phy);
> +}
> +
> +static int ethernet_phy_setup(struct net_device *dev)
> +{
> + struct pxa168_eth_private *pep = netdev_priv(dev);
> +
> + if (pep->pd != NULL) {
> + if (pep->pd->init)
> + pep->pd->init();
> + }
> + pep->phy = phy_scan(pep, pep->pd->phy_addr & 0x1f);
> + if (pep->phy != NULL)
> + phy_init(pep, pep->pd->speed, pep->pd->duplex);
> + update_hash_table_mac_address(pep, NULL, dev->dev_addr);
> + return 0;
> +}
> +
> +static int get_random_mac_addr(struct net_device *dev)
> +{
> + printk(KERN_INFO "%s:Using random mac address\n", dev->name);
> + random_ether_addr(dev->dev_addr);
> + return 0;
> +}
> +
> +static int pxa168_get_settings(struct net_device *dev, struct ethtool_cmd
> *cmd)
> +{
> + struct pxa168_eth_private *pep = netdev_priv(dev);
> + int err;
> +
> + err = phy_read_status(pep->phy);
> + if (err == 0)
> + err = phy_ethtool_gset(pep->phy, cmd);
> + return err;
> +}
> +
> +static int pxa168_set_settings(struct net_device *dev, struct ethtool_cmd
> *cmd)
> +{
> + struct pxa168_eth_private *pep = netdev_priv(dev);
> +
> + return phy_ethtool_sset(pep->phy, cmd);
> +}
> +
> +static void pxa168_get_drvinfo(struct net_device *dev,
> + struct ethtool_drvinfo *info)
> +{
> + strncpy(info->driver, DRIVER_NAME, 32);
> + strncpy(info->version, DRIVER_VERSION, 32);
> + strncpy(info->fw_version, "N/A", 32);
> + strncpy(info->bus_info, "N/A", 32);
> +}
> +
> +static u32 pxa168_get_link(struct net_device *dev)
> +{
> + return !!netif_carrier_ok(dev);
> +}
> +
> +static const struct ethtool_ops pxa168_ethtool_ops = {
> + .get_settings = pxa168_get_settings,
> + .set_settings = pxa168_set_settings,
> + .get_drvinfo = pxa168_get_drvinfo,
> + .get_link = pxa168_get_link,
> +};
> +
> +static const struct net_device_ops pxa168_eth_netdev_ops = {
> + .ndo_open = pxa168_eth_open,
> + .ndo_stop = pxa168_eth_stop,
> + .ndo_start_xmit = pxa168_eth_start_xmit,
> + .ndo_set_rx_mode = pxa168_eth_set_rx_mode,
> + .ndo_set_mac_address = pxa168_eth_set_mac_address,
> + .ndo_validate_addr = eth_validate_addr,
> + .ndo_do_ioctl = pxa168_eth_do_ioctl,
> + .ndo_change_mtu = pxa168_eth_change_mtu,
> + .ndo_tx_timeout = pxa168_eth_tx_timeout,
> +};
> +
> +static int pxa168_eth_probe(struct platform_device *pdev)
> +{
> + struct pxa168_eth_private *pep = NULL;
> + struct net_device *dev = NULL;
> + struct resource *res;
> + struct clk *clk;
> + int err;
> +
> + printk(KERN_NOTICE "PXA168 10/100 Ethernet Driver\n");
> +
> + clk = clk_get(&pdev->dev, "MFUCLK");
> + if (IS_ERR(clk)) {
> + printk(KERN_ERR "fast Ethernet failed to get clock\n");
> + return -ENODEV;
> + }
> + clk_enable(clk);
> +
> + dev = alloc_etherdev(sizeof(struct pxa168_eth_private));
> + if (!dev) {
> + err = -ENOMEM;
> + goto out;
> + }
> +
> + platform_set_drvdata(pdev, dev);
> + pep = netdev_priv(dev);
> + pep->dev = dev;
> + pep->clk = clk;
> + res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> + if (res == NULL) {
> + err = -ENODEV;
> + goto out;
> + }
> + pep->base = ioremap(res->start, res->end - res->start + 1);
> + if (pep->base == NULL) {
> + err = -ENOMEM;
> + goto out;
> + }
> + res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
> + BUG_ON(!res);
> + dev->irq = res->start;
> + dev->netdev_ops = &pxa168_eth_netdev_ops;
> + dev->watchdog_timeo = 8 * HZ;
> + dev->base_addr = 0;
> + SET_ETHTOOL_OPS(dev, &pxa168_ethtool_ops);
> +
> + INIT_WORK(&pep->tx_timeout_task, pxa168_eth_tx_timeout_task);
> +
> + pep->rx_ring_size = NUM_RX_DESCS;
> + pep->tx_ring_size = NUM_TX_DESCS;
> + get_random_mac_addr(dev);
> + pep->pd = pdev->dev.platform_data;
> + netif_napi_add(dev, &pep->napi, pxa168_rx_poll, pep->rx_ring_size);
> +
> + memset(&pep->timeout, 0, sizeof(struct timer_list));
> + init_timer(&pep->timeout);
> + pep->timeout.function = rxq_refill_timer_wrapper;
> + pep->timeout.data = (unsigned long)pep;
> +
> + pep->smi_bus = mdiobus_alloc();
> + if (pep->smi_bus == NULL) {
> + err = -ENOMEM;
> + goto out;
> + }
> + pep->smi_bus->priv = pep;
> + pep->smi_bus->name = "pxa168_eth smi";
> + pep->smi_bus->read = pxa168_smi_read;
> + pep->smi_bus->write = pxa168_smi_write;
> + snprintf(pep->smi_bus->id, MII_BUS_ID_SIZE, "%d", pdev->id);
> + pep->smi_bus->parent = &pdev->dev;
> + pep->smi_bus->phy_mask = 0xffffffff;
> + if (mdiobus_register(pep->smi_bus) < 0) {
> + err = -ENOMEM;
> + goto out;
> + }
> + pxa168_init_hw(pep);
> + err = ethernet_phy_setup(dev);
> + if (err)
> + goto out;
> + SET_NETDEV_DEV(dev, &pdev->dev);
> + err = register_netdev(dev);
> + if (err)
> + goto out;
> + return 0;
> +out:
> + if (pep->clk) {
> + clk_disable(pep->clk);
> + clk_put(pep->clk);
> + pep->clk = NULL;
> + }
> + if (pep->base) {
> + iounmap(pep->base);
> + pep->base = NULL;
> + }
> + if (dev)
> + free_netdev(dev);
> + return err;
> +}
> +
> +static int pxa168_eth_remove(struct platform_device *pdev)
> +{
> + struct net_device *dev = platform_get_drvdata(pdev);
> + struct pxa168_eth_private *pep = netdev_priv(dev);
> +
> + if (pep->htpr) {
> + dma_free_coherent(NULL, HASH_ADDR_TABLE_SIZE + 7,
> + pep->htpr, pep->htpr_dma);
> + pep->htpr = NULL;
> + }
> + if (pep->clk) {
> + clk_disable(pep->clk);
> + clk_put(pep->clk);
> + pep->clk = NULL;
> + }
> + if (pep->phy != NULL)
> + phy_detach(pep->phy);
> +
> + iounmap(pep->base);
> + pep->base = NULL;
> + unregister_netdev(dev);
> + flush_scheduled_work();
> + free_netdev(dev);
> + platform_set_drvdata(pdev, NULL);
> + return 0;
> +}
> +
> +static void pxa168_eth_shutdown(struct platform_device *pdev)
> +{
> + struct net_device *dev = platform_get_drvdata(pdev);
> + eth_port_reset(dev);
> +}
> +
> +#ifdef CONFIG_PM
> +static int pxa168_eth_resume(struct platform_device *pdev)
> +{
> + return -ENOSYS;
> +}
> +
> +static int pxa168_eth_suspend(struct platform_device *pdev, pm_message_t
> state)
> +{
> + return -ENOSYS;
> +}
> +
> +#else
> +#define pxa168_eth_resume NULL
> +#define pxa168_eth_suspend NULL
> +#endif
> +
> +static struct platform_driver pxa168_eth_driver = {
> + .probe = pxa168_eth_probe,
> + .remove = pxa168_eth_remove,
> + .shutdown = pxa168_eth_shutdown,
> + .resume = pxa168_eth_resume,
> + .suspend = pxa168_eth_suspend,
> + .driver = {
> + .name = DRIVER_NAME,
> + },
> +};
> +
> +static int __init pxa168_init_module(void)
> +{
> + return platform_driver_register(&pxa168_eth_driver);
> +}
> +
> +static void __exit pxa168_cleanup_module(void)
> +{
> + platform_driver_unregister(&pxa168_eth_driver);
> +}
> +
> +module_init(pxa168_init_module);
> +module_exit(pxa168_cleanup_module);
> +
> +MODULE_LICENSE("GPL");
> +MODULE_DESCRIPTION("Ethernet driver for Marvell PXA168");
> +MODULE_ALIAS("platform:pxa168_eth");
> diff --git a/include/linux/pxa168_eth.h b/include/linux/pxa168_eth.h
> new file mode 100644
> index 0000000..152981c
> --- /dev/null
> +++ b/include/linux/pxa168_eth.h
> @@ -0,0 +1,20 @@
> +/*
> + *pxa168 ethernet platform device data definition file.
> + */
> +#ifndef __LINUX_PXA168_ETH_H
> +#define __LINUX_PXA168_ETH_H
> +
> +struct pxa168_eth_platform_data {
> + int phy_addr;
> +
> + /* If speed is 0, then speed and duplex are autonegotiated. */
> + int speed; /* 0, SPEED_10, SPEED_100 */
> + int duplex; /* DUPLEX_HALF or DUPLEX_FULL */
> +
> + /* init callback is used for board specific initialization
> + * e.g on Aspenite its used to initialize the PHY transceiver.
> + */
> + int (*init)(void);
> +};
> +
> +#endif /* __LINUX_PXA168_ETH_H */
> --
> 1.5.3.3
^ permalink raw reply
* RE: [PATCH] net: add Fast Ethernet driver for PXA168.
From: Sachin Sanap @ 2010-08-06 15:14 UTC (permalink / raw)
To: Lennert Buytenhek
Cc: netdev@vger.kernel.org, Ashish Karkare, Prabhanjan Sarnaik,
eric.y.miao@gmail.com, Philip Rakity, Mark Brown
In-Reply-To: <20100804065818.GI21121@mail.wantstofly.org>
Thanks Lennert,Eric for your review. Comments inline. I will resend the modified patch as a reply to this email.
>
> On Wed, Aug 04, 2010 at 04:17:50PM +0530, Sachin Sanap wrote:
>
> > This patch adds support for PXA168 Fast Ethernet on Aspenite
> > board.
>
> There's nothing Aspenite-specific in this patch (nor should there be),
> so you can leave that part out.
Removed.
>
>
> > Patch generated against Linux 2.6.35-rc5
> > commit cd5b8f8755a89a57fc8c408d284b8b613f090345
>
> This might be interesting to know but shouldn't be part of the commit
> message.
Removed.
>
>
> > diff --git a/arch/arm/mach-mmp/include/mach/pxa168_eth.h
> b/arch/arm/mach-mmp/include/mach/pxa168_eth.h
> > new file mode 100644
> > index 0000000..abfd335
> > --- /dev/null
> > +++ b/arch/arm/mach-mmp/include/mach/pxa168_eth.h
> > @@ -0,0 +1,18 @@
> > +/*
> > + *pxa168 ethernet platform device data definition file.
> > + */
> > +#ifndef __LINUX_PXA168_ETH_H
> > +#define __LINUX_PXA168_ETH_H
>
> This should just be in include/linux, IMHO, as this unit isn't only used
> in the PXA168, for one.
I have moved it to include/linux but the PXA168 name is still there.
>
>
> > +struct pxa168_eth_platform_data {
> > + int port_number;
> > + u16 phy_addr;
> > +
> > + /* If speed is 0, then speed and duplex are autonegotiated. */
> > + u32 speed; /* 0, SPEED_10, SPEED_100 */
> > + u32 duplex; /* DUPLEX_HALF or DUPLEX_FULL */
>
> phylib treats these three (phy address, speed and duplex) as ints, is
> there any reason you need these to be of different types?
Used int at all three places.
>
>
> > + int (*init)(void);
>
> What's this needed for? The name of this callback is entirely
> nondescriptive, there's no comment as to when it's called, etc.
Added a comment for it.
>
>
> > diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig
> > index ce2fcdd..5ebf287 100644
> > --- a/drivers/net/Kconfig
> > +++ b/drivers/net/Kconfig
> > @@ -927,6 +927,16 @@ config SMC91X
> > The module will be called smc91x. If you want to compile it as a
> > module, say M here and read
> <file:Documentation/kbuild/modules.txt>.
> >
> > +config PXA168_ETH
> > + tristate "Marvell pxa168 ethernet support"
> > + depends on MACH_ASPENITE
>
> You can have it depend on ARM or PXA168, but having it depend on support
> for one specific board support file is almost certainly wrong.
Depends on CPU_PXA168.
>
>
> > diff --git a/drivers/net/pxa168_eth.c b/drivers/net/pxa168_eth.c
> > new file mode 100644
> > index 0000000..618e558
> > --- /dev/null
> > +++ b/drivers/net/pxa168_eth.c
> > @@ -0,0 +1,1723 @@
> > +/*
> > + * Driver for PXA168 based boards.
>
> Why not call this "PXA168 ethernet driver"?
Change done.
>
>
> > + * Based on MV643XX driver.
>
> The mv643xx ethernet driver, that is.
Change done.
>
>
> > +#define DRIVER_NAME "pxa168-mfu"
>
> mfu?
Its was for multi function unit on Aspenite, changed it to pxa168-eth
>
>
> > +/* smi register */
> > +#define SMI_BUSY (1<<28) /* 0 - Write, 1 - Read */
> > +#define SMI_R_VALID (1<<27) /* 0 - Write, 1 - Read */
> > +#define SMI_OP_W (0<<26) /* Write operation */
> > +#define SMI_OP_R (1<<26) /* Read operation */
>
> (1 << 28)
> (1 << 27)
> (0 << 26)
>
> etc (thoughout the file)
Changed.
>
>
> > +struct pxa168_private {
>
> ITYM pxa168_eth_private
Yes, changed.
>
>
> > + /*
> > + * Used in case RX Ring is empty, which can be caused when
> > + * system does not have resources (skb's)
> > + */
>
> can be caused by
> can occur when
Changed.
>
>
> > + struct timer_list timeout;
> > + struct mii_bus *smi_bus;
> > + struct phy_device *phy;
> > +
> > + /* clock */
> > + struct clk *clk;
> > + struct pxa168_eth_platform_data *pd;
> > + /*
> > + * Ethernet controller base address.
> > + */
> > + void __iomem *base;
> > +
> > + u8 *htpr; /* hash pointer */
>
> IMHO it'd look better if you'd either always put the comment on the same
> line or put the comment above the member (within the same struct).
>
> Also, 'hash pointer' as a comment isn't very helpful. Making it
> 'hardware address filter table' or so would be more descriptive.
Changed.
>
>
> > +struct addr_table_entry {
> > + u32 lo;
> > + u32 hi;
> > +};
>
> If the address filter table consists of these structures, it's
> confusing to make htpr a u8 * and then cast things around. Better make
> htpr a void * then.
Changed.
>
> Also, what about endian-cleanness? If you run the CPU core in big
> endian, will the address filter table handling logic in this driver
> still work?
Added cpu_to_le32 for the mac_high and mac_low variables before passing them to the add_del_hash_entry. I have not tested it on the Big endian but I think it should now work.
>
>
> > +static char marvell_OUI[3] = { 0x02, 0x50, 0x43 };
>
> This isn't even a marvell OUI -- 00:50:43 is a marvell OUI, and
> anything with 02: is a locally generated address.
>
> Why don't you just use random_ether_addr()?
Changed to use random_ether_addr.
>
>
> > +static inline u32 rdl(struct pxa168_private *mp, int offset)
> > +{
> > + return readl(mp->base + offset);
> > +}
>
> In mv643xx_eth, 'mp' refers to mv643xx_eth_private, it's funny that
> you've changed the name of the struct but not that of the variable.
>
> As the rest of the driver looks very very similar to mv643xx_eth, if
> not pretty much entirely identical in a lot of places, you should give
> more credit to mv643xx_eth than 'based on', in my opinion.
Changed the credit line for the same.
>
>
> > +static int ethernet_phy_get(struct pxa168_private *mp)
> > +{
> > + unsigned int reg_data;
> > +
> > + /* only support 3 ports */
> > + BUG_ON(mp->port_num > 2);
>
> Hm, does it actually support 3 ports?
No. changed the code.
>
>
> > +static void ethernet_phy_set_addr(struct pxa168_private *mp, int
> phy_addr)
> > +{
> > + u32 reg_data;
> > + int addr_shift = 5 * mp->port_num;
> > +
> > + /* only support 3 ports */
> > + BUG_ON(mp->port_num > 2);
> > +
> > + reg_data = rdl(mp, PHY_ADDRESS);
> > + reg_data &= ~(0x1f << addr_shift);
> > + reg_data |= (phy_addr & 0x1f) << addr_shift;
> > + wrl(mp, PHY_ADDRESS, reg_data);
> > +}
> > +static void ethernet_phy_reset(struct pxa168_private *mp)
> > +{
>
> Blank line between functions.
>
>
> > + do {
> > + data = phy_read(mp->phy, MII_BMCR);
> > + } while (data >= 0 && data & BMCR_RESET);
> > +
> > +}
>
> No blank line here.
>
>
> > +static void rxq_refill(struct net_device *dev)
> > +{
> > + struct pxa168_private *mp = netdev_priv(dev);
> > + struct sk_buff *skb;
> > + struct rx_desc *p_used_rx_desc;
> > + int used_rx_desc;
> > +
> > + while (mp->rx_desc_count < mp->rx_ring_size) {
> > +
> > + skb = dev_alloc_skb(MAX_PKT_SIZE + ETH_HW_IP_ALIGN);
>
> No blank line here.
>
>
> > + if (mp->rx_desc_count == 0) {
> > + printk(KERN_INFO "%s: Rx ring is empty\n", dev->name);
>
> I wouldn't printk for this (and certainly not without a rate limit) --
> going OOM will then just mean that you'll get endless console spam as
> well, which isn't helpful.
Hm, removed the printk.
>
>
> > +/*
> > + * --------------------------------------------------------------------
> --------
> > + * This function will calculate the hash function of the address.
> > + * depends on the hash mode and hash size.
>
> Where are the hash mode and size configured?
>
>
> > +static u32 hash_function(u32 mac_high, u32 mac_low)
> > +{
> > + u32 hash_result;
> > + u32 addr_high;
> > + u32 addr_low;
> > + u32 addr0;
> > + u32 addr1;
> > + u32 addr2;
> > + u32 addr3;
> > + u32 addr_high_swapped;
> > + u32 addr_low_swapped;
> > +
> > + addr_high = nibble_swapping_16_bit(mac_high);
> > + addr_low = nibble_swapping_32_bit(mac_low);
> > +
> > + addr_high_swapped = flip_4_bits(addr_high & 0xf)
> > + + ((flip_4_bits((addr_high >> 4) & 0xf)) << 4)
> > + + ((flip_4_bits((addr_high >> 8) & 0xf)) << 8)
> > + + ((flip_4_bits((addr_high >> 12) & 0xf)) << 12);
> > +
> > + addr_low_swapped = flip_4_bits(addr_low & 0xf)
> > + + ((flip_4_bits((addr_low >> 4) & 0xf)) << 4)
> > + + ((flip_4_bits((addr_low >> 8) & 0xf)) << 8)
> > + + ((flip_4_bits((addr_low >> 12) & 0xf)) << 12)
> > + + ((flip_4_bits((addr_low >> 16) & 0xf)) << 16)
> > + + ((flip_4_bits((addr_low >> 20) & 0xf)) << 20)
> > + + ((flip_4_bits((addr_low >> 24) & 0xf)) << 24)
> > + + ((flip_4_bits((addr_low >> 28) & 0xf)) << 28);
> > +
> > + addr_high = addr_high_swapped;
> > + addr_low = addr_low_swapped;
> > +
> > + addr0 = (addr_low >> 2) & 0x03f;
> > + addr1 = (addr_low & 0x003) | ((addr_low >> 8) & 0x7f) << 2;
> > + addr2 = (addr_low >> 15) & 0x1ff;
> > + addr3 = ((addr_low >> 24) & 0x0ff) | ((addr_high & 1) << 8);
> > +
> > + hash_result = (addr0 << 9) | (addr1 ^ addr2 ^ addr3);
> > + hash_result = hash_result & 0x07ff;
> > + return hash_result;
>
> As it's only a bunch of XORs, can you not calculate the hash function
> first and only then do the bit reversal?
>
>
> > + * --------------------------------------------------------------------
> --------
> > + * This function will add an entry to the address table.
> > + * depends on the hash mode and hash size that was initialized.
> > + * Inputs
> > + * mp - ETHERNET .
> > + * mac_high - the 2 most significant bytes of the MAC address.
> > + * mac_low - the 4 least significant bytes of the MAC address.
> > + * skip - if 1, skip this address.
>
> What does 'skip' do?
>
>
> > + * rd - the RD field in the address table.
>
> And what does that do?
Added more comments regarding the address filtering and the hash function.
>
> If one has hardware docs, this comment doesn't really add anything,
> while if one doesn't have hardware docs, this comment doesn't really
> explain anything -- so the comment isn't very useful.
>
>
> > +static void update_hash_table_mac_address(struct pxa168_private *mp,
> > + u8 *oaddr, u8 *addr)
> > [...]
> > +static int init_hashtable(struct pxa168_private *mp)
>
> hash_table?
>
>
> > +{
> > + u8 *addr;
> > + dma_addr_t reg_dma;
> > +
> > + if (mp->htpr == NULL) {
> > + mp->htpr = dma_alloc_coherent(NULL,
> > + HASH_ADDR_TABLE_SIZE + 7,
> > + &mp->htpr_dma, GFP_KERNEL);
> > + if (mp->htpr == NULL)
> > + return -ENOMEM;
> > + }
> > +
> > + /* align to 8 byte boundary */
> > + addr = (u8 *) (((u32) mp->htpr + 7) & ~0x7);
> > + reg_dma = (dma_addr_t) (((u32) mp->htpr_dma + 7) & ~0x7);
>
> DMA-API-HOWTO.txt says about dma_alloc_coherent():
>
> The cpu return address and the DMA bus master address are both
> guaranteed to be aligned to the smallest PAGE_SIZE order which
> is greater than or equal to the requested size.
>
> So this shuffling isn't needed.
Hm, removed the shuffling code.
>
>
> etc etc.
^ permalink raw reply
* Re: Using virtio as a physical (wire-level) transport
From: Ira W. Snyder @ 2010-08-06 15:34 UTC (permalink / raw)
To: Michael S. Tsirkin; +Cc: Rusty Russell, virtualization, Zang Roy, netdev
In-Reply-To: <20100805232042.GA27651@redhat.com>
On Fri, Aug 06, 2010 at 02:20:42AM +0300, Michael S. Tsirkin wrote:
> On Thu, Aug 05, 2010 at 04:01:03PM -0700, Ira W. Snyder wrote:
> > On Fri, Aug 06, 2010 at 12:30:50AM +0300, Michael S. Tsirkin wrote:
> > > Hi Ira,
> > >
> > > > Making my life harder since the last time I tried this, mainline commit
> > > > 7c5e9ed0c (virtio_ring: remove a level of indirection) has removed the
> > > > possibility of using an alternative virtqueue implementation. The commit
> > > > message suggests that you might be willing to add this capability back.
> > > > Would this be an option?
> > >
> > > Sorry about that.
> > >
> > > With respect to this commit, we only had one implementation upstream
> > > and extra levels of indirection made extending the API
> > > much harder for no apparent benefit.
> > >
> > > When there's more than one ring implementation with very small amount of
> > > common code, I think that it might make sense to readd the indirection
> > > back, to separate the code cleanly.
> > >
> > > OTOH if the two implementations share a lot of code, I think that it
> > > might be better to just add a couple of if statements here and there.
> > > This way compiler even might have a chance to compile the code out if
> > > the feature is disabled in kernel config.
> > >
> >
> > The virtqueue implementation I envision will be almost identical to the
> > current virtio_ring virtqueue implementation, with the following
> > exceptions:
> >
> > * the "shared memory" will actually be remote, on the PCI BAR of a device
> > * iowrite32(), ioread32() and friends will be needed to access the memory
> > * there will only be a fixed number of virtqueues available, due to PCI
> > BAR size
> > * cross-endian virtqueues must work
> > * kick needs to be cross-machine (using PCI IRQ's)
> >
> > I don't think it is feasible to add this to the existing implementation.
> > I think the requirement of being cross-endian will be the hardest to
> > overcome. Rusty did not envision the cross-endian use case when he
> > designed this, and it shows, in virtio_ring, virtio_net and vhost. I
> > have no idea what to do about this. Do you have any ideas?
>
> My guess is sticking an if around each access in virtio would hurt,
> if this is what you are asking about.
>
Yes, I think so too. I think using le32 byte order everywhere in virtio
would be a good thing. In addition, it means that on all x86, things
continue to work as-is. It would also have no overhead in the most
common case: x86-on-x86.
This problem is not limited to my new use of virtio. Virtio is
completely useless in a relatively common virtualization scenario:
x86 host with qemu-ppc guest. Or any other big endian guest system.
> Just a crazy idea: vhost already uses wrappers like get_user etc,
> maybe when building kernel for your board you could
> redefine these to also byteswap?
>
I think idea is clever, but also psychotic :) I'm sure it would work,
but that only solves the problem of virtio ring descriptors. The
virtio-net header contains several __u16 fields which would also need
to be fixed-endianness.
Thanks,
Ira
^ permalink raw reply
* RE: [PATCH] net: add Fast Ethernet driver for PXA168.
From: Sachin Sanap @ 2010-08-06 15:18 UTC (permalink / raw)
To: 20100804104750.GA9652@marvell.com, buytenh@wantstofly.org
Cc: netdev@vger.kernel.org, Ashish Karkare, Prabhanjan Sarnaik,
eric.y.miao@gmail.com, Philip Rakity, Mark Brown
In-Reply-To: <20100806193931.GA428@pe-dt013.marvell.com>
Sorry for resending the patch as new thread. Discard this patch. I will send the patch again as a reply to previous thread.
-Sachin
> -----Original Message-----
> From: Sachin Sanap [mailto:ssanap@marvell.com]
> Sent: Saturday, August 07, 2010 1:10 AM
> To: buytenh@wantstofly.org
> Cc: netdev@vger.kernel.org; Ashish Karkare; Prabhanjan Sarnaik;
> eric.y.miao@gmail.com; Philip Rakity; Mark Brown; Sachin Sanap
> Subject: [PATCH] net: add Fast Ethernet driver for PXA168.
>
> Signed-off-by: Sachin Sanap <ssanap@marvell.com>
> ---
> arch/arm/mach-mmp/aspenite.c | 3 +-
> arch/arm/mach-mmp/pxa168.c | 4 +-
> drivers/net/Kconfig | 10 +
> drivers/net/Makefile | 1 +
> drivers/net/pxa168_eth.c | 1592
> ++++++++++++++++++++++++++++++++++++++++++
> include/linux/pxa168_eth.h | 20 +
> 6 files changed, 1626 insertions(+), 4 deletions(-)
> create mode 100644 drivers/net/pxa168_eth.c
> create mode 100644 include/linux/pxa168_eth.h
>
> diff --git a/arch/arm/mach-mmp/aspenite.c b/arch/arm/mach-mmp/aspenite.c
> index 61a6d6a..cec505f 100644
> --- a/arch/arm/mach-mmp/aspenite.c
> +++ b/arch/arm/mach-mmp/aspenite.c
> @@ -200,8 +200,7 @@ static int pxa168_eth_init(void)
> }
>
> static struct pxa168_eth_platform_data pxa168_eth_data = {
> - .phy_addr = 0, /* phy addr depends on boards */
> - .port_number = 0,
> + .phy_addr = 0,
> .init = pxa168_eth_init,
> };
> #endif
> diff --git a/arch/arm/mach-mmp/pxa168.c b/arch/arm/mach-mmp/pxa168.c
> index f7d1158..6fcaf0c 100644
> --- a/arch/arm/mach-mmp/pxa168.c
> +++ b/arch/arm/mach-mmp/pxa168.c
> @@ -97,7 +97,7 @@ static struct clk_lookup pxa168_clkregs[] = {
> INIT_CLKREG(&clk_ssp4, "pxa168-ssp.3", NULL),
> INIT_CLKREG(&clk_ssp5, "pxa168-ssp.4", NULL),
> INIT_CLKREG(&clk_nand, "pxa3xx-nand", NULL),
> - INIT_CLKREG(&clk_mfu, "pxa168-mfu", "MFUCLK"),
> + INIT_CLKREG(&clk_mfu, "pxa168-eth", "MFUCLK"),
> };
>
> static int __init pxa168_init(void)
> @@ -149,4 +149,4 @@ PXA168_DEVICE(ssp2, "pxa168-ssp", 1, SSP2, 0xd401c000,
> 0x40, 54, 55);
> PXA168_DEVICE(ssp3, "pxa168-ssp", 2, SSP3, 0xd401f000, 0x40, 56, 57);
> PXA168_DEVICE(ssp4, "pxa168-ssp", 3, SSP4, 0xd4020000, 0x40, 58, 59);
> PXA168_DEVICE(ssp5, "pxa168-ssp", 4, SSP5, 0xd4021000, 0x40, 60, 61);
> -PXA168_DEVICE(mfu, "pxa168-mfu", -1, MFU, 0xc0800000, 0x0fff);
> +PXA168_DEVICE(mfu, "pxa168-eth", -1, MFU, 0xc0800000, 0x0fff);
> diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig
> index ce2fcdd..78cd7e8 100644
> --- a/drivers/net/Kconfig
> +++ b/drivers/net/Kconfig
> @@ -927,6 +927,16 @@ config SMC91X
> The module will be called smc91x. If you want to compile it as a
> module, say M here and read
> <file:Documentation/kbuild/modules.txt>.
>
> +config PXA168_ETH
> + tristate "Marvell pxa168 ethernet support"
> + depends on CPU_PXA168
> + select PHYLIB
> + help
> + This driver supports the pxa168 Ethernet ports.
> +
> + To compile this driver as a module, choose M here. The module
> + will be called pxa168_eth.
> +
> config NET_NETX
> tristate "NetX Ethernet support"
> select MII
> diff --git a/drivers/net/Makefile b/drivers/net/Makefile
> index 0a0512a..a42d437 100644
> --- a/drivers/net/Makefile
> +++ b/drivers/net/Makefile
> @@ -244,6 +244,7 @@ obj-$(CONFIG_MYRI10GE) += myri10ge/
> obj-$(CONFIG_SMC91X) += smc91x.o
> obj-$(CONFIG_SMC911X) += smc911x.o
> obj-$(CONFIG_SMSC911X) += smsc911x.o
> +obj-$(CONFIG_PXA168_ETH) += pxa168_eth.o
> obj-$(CONFIG_BFIN_MAC) += bfin_mac.o
> obj-$(CONFIG_DM9000) += dm9000.o
> obj-$(CONFIG_PASEMI_MAC) += pasemi_mac_driver.o
> diff --git a/drivers/net/pxa168_eth.c b/drivers/net/pxa168_eth.c
> new file mode 100644
> index 0000000..c00fc3c
> --- /dev/null
> +++ b/drivers/net/pxa168_eth.c
> @@ -0,0 +1,1592 @@
> +/*
> + * PXA168 ethernet driver.
> + * Most of the code is derived from mv643xx ethernet driver.
> + *
> + * Copyright (C) 2010 Marvell International Ltd.
> + * Philip Rakity <prakity@marvell.com>
> + * Mark Brown <markb@marvell.com>
> + * Sachin Sanap <ssanap@marvell.com>
> + *
> + * This program is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU General Public License
> + * as published by the Free Software Foundation; either version 2
> + * of the License, or (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, write to the Free Software
> + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
> USA.
> + */
> +
> +#include <linux/init.h>
> +#include <linux/dma-mapping.h>
> +#include <linux/in.h>
> +#include <linux/ip.h>
> +#include <linux/tcp.h>
> +#include <linux/udp.h>
> +#include <linux/etherdevice.h>
> +#include <linux/bitops.h>
> +#include <linux/delay.h>
> +#include <linux/ethtool.h>
> +#include <linux/platform_device.h>
> +#include <linux/module.h>
> +#include <linux/kernel.h>
> +#include <linux/workqueue.h>
> +#include <linux/clk.h>
> +#include <linux/phy.h>
> +#include <linux/io.h>
> +#include <linux/types.h>
> +#include <asm/pgtable.h>
> +#include <asm/system.h>
> +#include <linux/delay.h>
> +#include <linux/dma-mapping.h>
> +#include <asm/cacheflush.h>
> +#include <linux/pxa168_eth.h>
> +
> +#define DRIVER_NAME "pxa168-eth"
> +#define DRIVER_VERSION "0.3"
> +
> +/*
> + * Registers
> + */
> +
> +#define PHY_ADDRESS 0x0000
> +#define SMI 0x0010
> +#define PORT_CONFIG 0x0400
> +#define PORT_CONFIG_EXT 0x0408
> +#define PORT_COMMAND 0x0410
> +#define PORT_STATUS 0x0418
> +#define HTPR 0x0428
> +#define SDMA_CONFIG 0x0440
> +#define SDMA_CMD 0x0448
> +#define INT_CAUSE 0x0450
> +#define INT_W_CLEAR 0x0454
> +#define INT_MASK 0x0458
> +#define ETH_F_RX_DESC_0 0x0480
> +#define ETH_C_RX_DESC_0 0x04A0
> +#define ETH_C_TX_DESC_1 0x04E4
> +
> +/* smi register */
> +#define SMI_BUSY (1 << 28) /* 0 - Write, 1 - Read */
> +#define SMI_R_VALID (1 << 27) /* 0 - Write, 1 - Read */
> +#define SMI_OP_W (0 << 26) /* Write operation */
> +#define SMI_OP_R (1 << 26) /* Read operation */
> +
> +#define PHY_WAIT_ITERATIONS 500
> +
> +#define PXA168_ETH_PHY_ADDR_DEFAULT 0
> +/* RX & TX descriptor command */
> +#define BUF_OWNED_BY_DMA (1 << 31)
> +
> +/* RX descriptor status */
> +#define RX_EN_INT (1 << 23)
> +#define RX_FIRST_DESC (1 << 17)
> +#define RX_LAST_DESC (1 << 16)
> +#define RX_ERROR (1 << 15)
> +
> +/* TX descriptor command */
> +#define TX_EN_INT (1 << 23)
> +#define TX_GEN_CRC (1 << 22)
> +#define TX_ZERO_PADDING (1 << 18)
> +#define TX_FIRST_DESC (1 << 17)
> +#define TX_LAST_DESC (1 << 16)
> +#define TX_ERROR (1 << 15)
> +
> +/* SDMA_CMD */
> +#define SDMA_CMD_AT (1 << 31)
> +#define SDMA_CMD_TXDL (1 << 24)
> +#define SDMA_CMD_TXDH (1 << 23)
> +#define SDMA_CMD_AR (1 << 15)
> +#define SDMA_CMD_ERD (1 << 7)
> +
> +/* Bit definitions of the Port Config Reg */
> +#define PCR_HS (1 << 12)
> +#define PCR_EN (1 << 7)
> +#define PCR_PM (1 << 0)
> +
> +/* Bit definitions of the Port Config Extend Reg */
> +#define PCXR_2BSM (1 << 28)
> +#define PCXR_DSCP_EN (1 << 21)
> +#define PCXR_MFL_1518 (0 << 14)
> +#define PCXR_MFL_1536 (1 << 14)
> +#define PCXR_MFL_2048 (2 << 14)
> +#define PCXR_MFL_64K (3 << 14)
> +#define PCXR_FLP (1 << 11)
> +#define PCXR_PRIO_TX_OFF 3
> +#define PCXR_TX_HIGH_PRI (7 << PCXR_PRIO_TX_OFF)
> +
> +/* Bit definitions of the SDMA Config Reg */
> +#define SDCR_BSZ_OFF 12
> +#define SDCR_BSZ8 (3 << SDCR_BSZ_OFF)
> +#define SDCR_BSZ4 (2 << SDCR_BSZ_OFF)
> +#define SDCR_BSZ2 (1 << SDCR_BSZ_OFF)
> +#define SDCR_BSZ1 (0 << SDCR_BSZ_OFF)
> +#define SDCR_BLMR (1 << 6)
> +#define SDCR_BLMT (1 << 7)
> +#define SDCR_RIFB (1 << 9)
> +#define SDCR_RC_OFF 2
> +#define SDCR_RC_MAX_RETRANS (0xf << SDCR_RC_OFF)
> +
> +/*
> + * Bit definitions of the Interrupt Cause Reg
> + * and Interrupt MASK Reg is the same
> + */
> +#define ICR_RXBUF (1 << 0)
> +#define ICR_TXBUF_H (1 << 2)
> +#define ICR_TXBUF_L (1 << 3)
> +#define ICR_TXEND_H (1 << 6)
> +#define ICR_TXEND_L (1 << 7)
> +#define ICR_RXERR (1 << 8)
> +#define ICR_TXERR_H (1 << 10)
> +#define ICR_TXERR_L (1 << 11)
> +#define ICR_TX_UDR (1 << 13)
> +#define ICR_MII_CH (1 << 28)
> +
> +#define ALL_INTS (ICR_TXBUF_H | ICR_TXBUF_L | ICR_TX_UDR |\
> + ICR_TXERR_H | ICR_TXERR_L |\
> + ICR_TXEND_H | ICR_TXEND_L |\
> + ICR_RXBUF | ICR_RXERR | ICR_MII_CH)
> +
> +#define ETH_HW_IP_ALIGN 2 /* hw aligns IP header */
> +#define ETH_EXTRA_HEADER (6+6+2+4) /* dest+src addr+protocol id+crc
> */
> +#define ETH_DATA_LEN 1500
> +#define MAX_PKT_SIZE 1518
> +
> +#define NUM_RX_DESCS 64
> +#define NUM_TX_DESCS 64
> +#define MAX_DESCS_PER_HIGH (60)
> +#define TX_DESC_COUNT_LOW (10)
> +
> +#define HASH_ADD 0
> +#define HASH_DELETE 1
> +#define HASH_ADDR_TABLE_SIZE 0x4000 /* 16K (1/2K address - PCR_HS ==
> 1) */
> +#define HOP_NUMBER 12
> +
> +/* Bit definitions for Port status */
> +#define PORT_SPEED_100 (1 << 0)
> +#define FULL_DUPLEX (1 << 1)
> +#define FLOW_CONTROL_ENABLED (1 << 2)
> +#define LINK_UP (1 << 3)
> +
> +/* Bit definitions for work to be done */
> +#define WORK_LINK (1 << 0)
> +#define WORK_TX_DONE (1 << 1)
> +
> +#define TX_DONE_INTERVAL 30
> +
> +struct rx_desc {
> + u32 cmd_sts; /* Descriptor command status */
> + u16 byte_cnt; /* Descriptor buffer byte count */
> + u16 buf_size; /* Buffer size */
> + u32 buf_ptr; /* Descriptor buffer pointer */
> + u32 next_desc_ptr; /* Next descriptor pointer */
> +};
> +
> +struct tx_desc {
> + u32 cmd_sts; /* Command/status field */
> + u16 reserved;
> + u16 byte_cnt; /* buffer byte count */
> + u32 buf_ptr; /* pointer to buffer for this descriptor */
> + u32 next_desc_ptr; /* Pointer to next descriptor */
> +};
> +
> +struct pxa168_eth_private {
> + int rx_resource_err; /* Rx ring resource error flag */
> +
> + /* Next available and first returning Rx resource */
> + int rx_curr_desc_q, rx_used_desc_q;
> +
> + /* Next available and first returning Tx resource */
> + int tx_curr_desc_q, tx_used_desc_q;
> +
> + struct rx_desc *p_rx_desc_area;
> + dma_addr_t rx_desc_dma;
> + int rx_desc_area_size;
> + struct sk_buff **rx_skb;
> +
> + struct tx_desc *p_tx_desc_area;
> + dma_addr_t tx_desc_dma;
> + int tx_desc_area_size;
> + struct sk_buff **tx_skb;
> +
> + struct work_struct tx_timeout_task;
> +
> + struct net_device *dev;
> + struct napi_struct napi;
> + u8 work_todo;
> +
> + struct net_device_stats stats;
> + /* Size of Tx Ring per queue */
> + int tx_ring_size;
> + /* Number of tx descriptors in use */
> + int tx_desc_count;
> + /* Size of Rx Ring per queue */
> + int rx_ring_size;
> + /* Number of rx descriptors in use */
> + int rx_desc_count;
> +
> + /*
> + * Used in case RX Ring is empty, which can occur when
> + * system does not have resources (skb's)
> + */
> + struct timer_list timeout;
> + struct mii_bus *smi_bus;
> + struct phy_device *phy;
> +
> + /* clock */
> + struct clk *clk;
> + struct pxa168_eth_platform_data *pd;
> + /*
> + * Ethernet controller base address.
> + */
> + void __iomem *base;
> +
> + /* Pointer to the hardware address filter table */
> + void *htpr;
> + dma_addr_t htpr_dma;
> +};
> +
> +struct addr_table_entry {
> + u32 lo;
> + u32 hi;
> +};
> +
> +/* Bit fields of a Hash Table Entry */
> +enum hash_table_entry {
> + HASH_ENTRY_VALID = 1,
> + SKIP = 2,
> + HASH_ENTRY_RECEIVE_DISCARD = 4,
> + HASH_ENTRY_RECEIVE_DISCARD_BIT = 2
> +};
> +
> +static int pxa168_get_settings(struct net_device *dev, struct ethtool_cmd
> *cmd);
> +static int pxa168_set_settings(struct net_device *dev, struct ethtool_cmd
> *cmd);
> +static int pxa168_init_hw(struct pxa168_eth_private *pep);
> +static void eth_port_reset(struct net_device *dev);
> +static void eth_port_start(struct net_device *dev);
> +static int pxa168_eth_open(struct net_device *dev);
> +static int pxa168_eth_stop(struct net_device *dev);
> +static int ethernet_phy_setup(struct net_device *dev);
> +
> +static inline u32 rdl(struct pxa168_eth_private *pep, int offset)
> +{
> + return readl(pep->base + offset);
> +}
> +
> +static inline void wrl(struct pxa168_eth_private *pep, int offset, u32
> data)
> +{
> + writel(data, pep->base + offset);
> +}
> +
> +static void abort_dma(struct pxa168_eth_private *pep)
> +{
> + int delay;
> + int max_retries = 40;
> +
> + do {
> + wrl(pep, SDMA_CMD, SDMA_CMD_AR | SDMA_CMD_AT);
> + udelay(100);
> +
> + delay = 10;
> + while ((rdl(pep, SDMA_CMD) & (SDMA_CMD_AR | SDMA_CMD_AT))
> + && delay-- > 0) {
> + udelay(10);
> + }
> + } while (max_retries-- > 0 && delay <= 0);
> +
> + if (max_retries <= 0)
> + printk(KERN_ERR "%s : DMA Stuck\n", __func__);
> +}
> +
> +static int ethernet_phy_get(struct pxa168_eth_private *pep)
> +{
> + unsigned int reg_data;
> +
> + reg_data = rdl(pep, PHY_ADDRESS);
> +
> + return reg_data & 0x1f;
> +}
> +
> +static void ethernet_phy_set_addr(struct pxa168_eth_private *pep, int
> phy_addr)
> +{
> + u32 reg_data;
> +
> + reg_data = rdl(pep, PHY_ADDRESS);
> + reg_data &= ~(0x1f);
> + reg_data |= phy_addr & 0x1f;
> + wrl(pep, PHY_ADDRESS, reg_data);
> +}
> +
> +static void ethernet_phy_reset(struct pxa168_eth_private *pep)
> +{
> + int data;
> +
> + data = phy_read(pep->phy, MII_BMCR);
> + if (data < 0)
> + return;
> +
> + data |= BMCR_RESET;
> + if (phy_write(pep->phy, MII_BMCR, data) < 0)
> + return;
> +
> + do {
> + data = phy_read(pep->phy, MII_BMCR);
> + } while (data >= 0 && data & BMCR_RESET);
> +}
> +
> +static void rxq_refill(struct net_device *dev)
> +{
> + struct pxa168_eth_private *pep = netdev_priv(dev);
> + struct sk_buff *skb;
> + struct rx_desc *p_used_rx_desc;
> + int used_rx_desc;
> +
> + while (pep->rx_desc_count < pep->rx_ring_size) {
> + skb = dev_alloc_skb(MAX_PKT_SIZE + ETH_HW_IP_ALIGN);
> + if (!skb)
> + break;
> + pep->rx_desc_count++;
> + /* Get 'used' Rx descriptor */
> + used_rx_desc = pep->rx_used_desc_q;
> + p_used_rx_desc = &pep->p_rx_desc_area[used_rx_desc];
> + p_used_rx_desc->buf_ptr = dma_map_single(NULL,
> + skb->data,
> + MAX_PKT_SIZE +
> + ETH_HW_IP_ALIGN,
> + DMA_FROM_DEVICE);
> + p_used_rx_desc->buf_size = MAX_PKT_SIZE + ETH_HW_IP_ALIGN;
> + pep->rx_skb[used_rx_desc] = skb;
> +
> + /* Return the descriptor to DMA ownership */
> + wmb();
> + p_used_rx_desc->cmd_sts = BUF_OWNED_BY_DMA | RX_EN_INT;
> + wmb();
> +
> + /* Move the used descriptor pointer to the next descriptor */
> + pep->rx_used_desc_q = (used_rx_desc + 1) % pep->rx_ring_size;
> +
> + /* Any Rx return cancels the Rx resource error status */
> + pep->rx_resource_err = 0;
> +
> + skb_reserve(skb, ETH_HW_IP_ALIGN);
> + }
> +
> + /*
> + * If RX ring is empty of SKB, set a timer to try allocating
> + * again at a later time.
> + */
> + if (pep->rx_desc_count == 0) {
> + pep->timeout.expires = jiffies + (HZ / 10); /* 100 mSec */
> + add_timer(&pep->timeout);
> + }
> +}
> +
> +static inline void rxq_refill_timer_wrapper(unsigned long data)
> +{
> + struct pxa168_eth_private *pep = (void *)data;
> + napi_schedule(&pep->napi);
> +}
> +
> +static inline u32 nibble_swapping_32_bit(u32 x)
> +{
> + return (((x) & 0xf0f0f0f0) >> 4) | (((x) & 0x0f0f0f0f) << 4);
> +}
> +
> +static inline u32 nibble_swapping_16_bit(u32 x)
> +{
> + return (((x) & 0x0000f0f0) >> 4) | (((x) & 0x00000f0f) << 4);
> +}
> +
> +static inline u32 flip_4_bits(u32 x)
> +{
> + return (((x) & 0x01) << 3) | (((x) & 0x002) << 1)
> + | (((x) & 0x04) >> 1) | (((x) & 0x008) >> 3);
> +}
> +
> +/*
> + * ----------------------------------------------------------------------
> ------
> + * This function will calculate the hash function of the address.
> + * depends on the hash mode and hash size.
> + * Inputs
> + * mac_high - the 2 most significant bytes of the MAC address.
> + * mac_low - the 4 least significant bytes of the MAC address.
> + * Outputs
> + * return the calculated entry.
> + */
> +static u32 hash_function(u32 mac_high, u32 mac_low)
> +{
> + u32 hash_result;
> + u32 addr_high;
> + u32 addr_low;
> + u32 addr0;
> + u32 addr1;
> + u32 addr2;
> + u32 addr3;
> + u32 addr_high_swapped;
> + u32 addr_low_swapped;
> +
> + addr_high = nibble_swapping_16_bit(mac_high);
> + addr_low = nibble_swapping_32_bit(mac_low);
> +
> + addr_high_swapped = flip_4_bits(addr_high & 0xf)
> + + ((flip_4_bits((addr_high >> 4) & 0xf)) << 4)
> + + ((flip_4_bits((addr_high >> 8) & 0xf)) << 8)
> + + ((flip_4_bits((addr_high >> 12) & 0xf)) << 12);
> +
> + addr_low_swapped = flip_4_bits(addr_low & 0xf)
> + + ((flip_4_bits((addr_low >> 4) & 0xf)) << 4)
> + + ((flip_4_bits((addr_low >> 8) & 0xf)) << 8)
> + + ((flip_4_bits((addr_low >> 12) & 0xf)) << 12)
> + + ((flip_4_bits((addr_low >> 16) & 0xf)) << 16)
> + + ((flip_4_bits((addr_low >> 20) & 0xf)) << 20)
> + + ((flip_4_bits((addr_low >> 24) & 0xf)) << 24)
> + + ((flip_4_bits((addr_low >> 28) & 0xf)) << 28);
> +
> + addr_high = addr_high_swapped;
> + addr_low = addr_low_swapped;
> +
> + addr0 = (addr_low >> 2) & 0x03f;
> + addr1 = (addr_low & 0x003) | ((addr_low >> 8) & 0x7f) << 2;
> + addr2 = (addr_low >> 15) & 0x1ff;
> + addr3 = ((addr_low >> 24) & 0x0ff) | ((addr_high & 1) << 8);
> +
> + hash_result = (addr0 << 9) | (addr1 ^ addr2 ^ addr3);
> + hash_result = hash_result & 0x07ff;
> + return hash_result;
> +}
> +
> +/*
> + * ----------------------------------------------------------------------
> ------
> + * This function will add/del an entry to the address table.
> + * Inputs
> + * pep - ETHERNET .
> + * mac_high - the 2 most significant bytes of the MAC address.
> + * mac_low - the 4 least significant bytes of the MAC address.
> + * skip - if 1, skip this address.Used in case of deleting an entry which
> is a
> + * part of chain in the hash table.We cant just delete the entry
> since
> + * that will break the chain.We need to defragment the tables time to
> + * time.
> + * rd - 0 Discard packet upon match.
> + * - 1 Receive packet upon match.
> + * Outputs
> + * address table entry is added/deleted.
> + * 0 if success.
> + * -ENOSPC if table full
> + */
> +static int add_del_hash_entry(struct pxa168_eth_private *pep, u32
> mac_high,
> + u32 mac_low, u32 rd, u32 skip, int del)
> +{
> + struct addr_table_entry *entry, *start;
> + u32 new_high;
> + u32 new_low;
> + u32 i;
> +
> + new_low = (((mac_high >> 4) & 0xf) << 15)
> + | (((mac_high >> 0) & 0xf) << 11)
> + | (((mac_high >> 12) & 0xf) << 7)
> + | (((mac_high >> 8) & 0xf) << 3)
> + | (((mac_low >> 20) & 0x1) << 31)
> + | (((mac_low >> 16) & 0xf) << 27)
> + | (((mac_low >> 28) & 0xf) << 23)
> + | (((mac_low >> 24) & 0xf) << 19)
> + | (skip << SKIP) | (rd << HASH_ENTRY_RECEIVE_DISCARD_BIT)
> + | HASH_ENTRY_VALID;
> +
> + new_high = (((mac_low >> 4) & 0xf) << 15)
> + | (((mac_low >> 0) & 0xf) << 11)
> + | (((mac_low >> 12) & 0xf) << 7)
> + | (((mac_low >> 8) & 0xf) << 3)
> + | (((mac_low >> 21) & 0x7) << 0);
> +
> + /*
> + * Pick the appropriate table, start scanning for free/reusable
> + * entries at the index obtained by hashing the specified MAC
> address
> + */
> + start = (struct addr_table_entry *)(pep->htpr);
> + entry = start + hash_function(mac_high, mac_low);
> + for (i = 0; i < HOP_NUMBER; i++) {
> + if (!(entry->lo & HASH_ENTRY_VALID)) {
> + break;
> + } else {
> + /* if same address put in same position */
> + if (((entry->lo & 0xfffffff8) == (new_low & 0xfffffff8))
> + && (entry->hi == new_high)) {
> + break;
> + }
> + }
> + if (entry == start + 0x7ff)
> + entry = start;
> + else
> + entry++;
> + }
> +
> + if (((entry->lo & 0xfffffff8) != (new_low & 0xfffffff8)) &&
> + (entry->hi != new_high) && del)
> + return 0;
> +
> + if (i == HOP_NUMBER) {
> + if (!del) {
> + printk(KERN_INFO "%s: table section is full\n",
> + __FILE__);
> + return -ENOSPC;
> + } else
> + return 0;
> + }
> +
> + /*
> + * Update the selected entry
> + */
> + if (del) {
> + entry->hi = 0;
> + entry->lo = 0;
> + } else {
> + entry->hi = cpu_to_le32(new_high);
> + entry->lo = cpu_to_le32(new_low);
> + }
> +
> + return 0;
> +}
> +
> +/*
> + * ----------------------------------------------------------------------
> ------
> + * Create an addressTable entry from MAC address info
> + * found in the specifed net_device struct
> + *
> + * Input : pointer to ethernet interface network device structure
> + * Output : N/A
> + */
> +static void update_hash_table_mac_address(struct pxa168_eth_private *pep,
> + u8 *oaddr, u8 *addr)
> +{
> + u32 mac_high;
> + u32 mac_low;
> +
> + /* Delete old entry */
> + if (oaddr) {
> + mac_high = cpu_to_le32((oaddr[0] << 8) | oaddr[1]);
> + mac_low = cpu_to_le32((oaddr[2] << 24) | (oaddr[3] << 16) |
> + (oaddr[4] << 8) | oaddr[5]);
> + add_del_hash_entry(pep, mac_high, mac_low, 1, 0, HASH_DELETE);
> + }
> + /* Add new entry */
> + mac_high = cpu_to_le32((addr[0] << 8) | addr[1]);
> + mac_low = cpu_to_le32((addr[2] << 24) | (addr[3] << 16) |
> + (addr[4] << 8) | addr[5]);
> + add_del_hash_entry(pep, mac_high, mac_low, 1, 0, HASH_ADD);
> +}
> +
> +static int init_hash_table(struct pxa168_eth_private *pep)
> +{
> + /*
> + * Hardware expects CPU to build a hash table based on a predefined
> + * hash function and populate it based on hardware address. The
> + * location of the hash table is identified by 32-bit pointer stored
> + * in HTPR internal register. Two possible sizes exists for the hash
> + * table 256kB and 16kB.We currently only support 16kB.
> + */
> + /* TODO: Add support for 256kB hash table */
> + if (pep->htpr == NULL) {
> + pep->htpr = dma_alloc_coherent(NULL,
> + HASH_ADDR_TABLE_SIZE,
> + &pep->htpr_dma, GFP_KERNEL);
> + if (pep->htpr == NULL)
> + return -ENOMEM;
> + }
> + memset(pep->htpr, 0, HASH_ADDR_TABLE_SIZE);
> + wrl(pep, HTPR, pep->htpr_dma);
> + return 0;
> +}
> +
> +static void pxa168_eth_set_rx_mode(struct net_device *dev)
> +{
> + struct pxa168_eth_private *pep = netdev_priv(dev);
> + struct netdev_hw_addr *ha;
> + u32 val;
> +
> + val = rdl(pep, PORT_CONFIG);
> + if (dev->flags & IFF_PROMISC)
> + val |= PCR_PM;
> + else
> + val &= ~PCR_PM;
> + wrl(pep, PORT_CONFIG, val);
> + netdev_for_each_mc_addr(ha, dev)
> + update_hash_table_mac_address(pep, NULL, ha->addr);
> +}
> +
> +static int pxa168_eth_set_mac_address(struct net_device *dev, void *addr)
> +{
> + struct sockaddr *sa = addr;
> + struct pxa168_eth_private *pep = netdev_priv(dev);
> + unsigned char oldMac[ETH_ALEN];
> +
> + if (!is_valid_ether_addr(sa->sa_data))
> + return -EINVAL;
> + memcpy(oldMac, dev->dev_addr, ETH_ALEN);
> + memcpy(dev->dev_addr, sa->sa_data, ETH_ALEN);
> + netif_addr_lock_bh(dev);
> + update_hash_table_mac_address(pep, oldMac, dev->dev_addr);
> + netif_addr_unlock_bh(dev);
> + return 0;
> +}
> +
> +static void eth_port_start(struct net_device *dev)
> +{
> + unsigned int val = 0;
> + struct pxa168_eth_private *pep = netdev_priv(dev);
> + int tx_curr_desc, rx_curr_desc;
> +
> + /* Perform PHY reset, if there is a PHY. */
> + if (pep->phy != NULL) {
> + struct ethtool_cmd cmd;
> +
> + pxa168_get_settings(pep->dev, &cmd);
> + ethernet_phy_reset(pep);
> + pxa168_set_settings(pep->dev, &cmd);
> + }
> +
> + /* Assignment of Tx CTRP of given queue */
> + tx_curr_desc = pep->tx_curr_desc_q;
> + wrl(pep, ETH_C_TX_DESC_1,
> + (u32) ((struct tx_desc *)pep->tx_desc_dma + tx_curr_desc));
> +
> + /* Assignment of Rx CRDP of given queue */
> + rx_curr_desc = pep->rx_curr_desc_q;
> + wrl(pep, ETH_C_RX_DESC_0,
> + (u32) ((struct rx_desc *)pep->rx_desc_dma + rx_curr_desc));
> +
> + wrl(pep, ETH_F_RX_DESC_0,
> + (u32) ((struct rx_desc *)pep->rx_desc_dma + rx_curr_desc));
> +
> + /* Clear all interrupts */
> + wrl(pep, INT_CAUSE, 0);
> +
> + /* Enable all interrupts for receive, transmit and error. */
> + wrl(pep, INT_MASK, ALL_INTS);
> +
> + val = rdl(pep, PORT_CONFIG);
> + val |= PCR_EN;
> + wrl(pep, PORT_CONFIG, val);
> +
> + /* Start RX DMA engine */
> + val = rdl(pep, SDMA_CMD);
> + val |= SDMA_CMD_ERD;
> + wrl(pep, SDMA_CMD, val);
> +}
> +
> +static void eth_port_reset(struct net_device *dev)
> +{
> + struct pxa168_eth_private *pep = netdev_priv(dev);
> + unsigned int val = 0;
> +
> + /* Stop all interrupts for receive, transmit and error. */
> + wrl(pep, INT_MASK, 0);
> +
> + /* Clear all interrupts */
> + wrl(pep, INT_CAUSE, 0);
> +
> + /* Stop RX DMA */
> + val = rdl(pep, SDMA_CMD);
> + val &= ~SDMA_CMD_ERD; /* abort dma command */
> +
> + /* Abort any transmit and receive operations and put DMA
> + * in idle state.
> + */
> + abort_dma(pep);
> +
> + /* Disable port */
> + val = rdl(pep, PORT_CONFIG);
> + val &= ~PCR_EN;
> + wrl(pep, PORT_CONFIG, val);
> +}
> +
> +/*
> + * txq_reclaim - Free the tx desc data for completed descriptors
> + * If force is non-zero, frees uncompleted descriptors as well
> + */
> +static int txq_reclaim(struct net_device *dev, int force)
> +{
> + struct pxa168_eth_private *pep = netdev_priv(dev);
> + struct tx_desc *desc;
> + u32 cmd_sts;
> + struct sk_buff *skb;
> + int tx_index;
> + dma_addr_t addr;
> + int count;
> + int released = 0;
> +
> + netif_tx_lock(dev);
> +
> + pep->work_todo &= ~(WORK_TX_DONE);
> + while (pep->tx_desc_count > 0) {
> + tx_index = pep->tx_used_desc_q;
> + desc = &pep->p_tx_desc_area[tx_index];
> + cmd_sts = desc->cmd_sts;
> + if (!force && (cmd_sts & BUF_OWNED_BY_DMA)) {
> + if (released > 0) {
> + goto txq_reclaim_end;
> + } else {
> + released = -1;
> + goto txq_reclaim_end;
> + }
> + }
> + pep->tx_used_desc_q = (tx_index + 1) % pep->tx_ring_size;
> + pep->tx_desc_count--;
> + addr = desc->buf_ptr;
> + count = desc->byte_cnt;
> + skb = pep->tx_skb[tx_index];
> + if (skb)
> + pep->tx_skb[tx_index] = NULL;
> +
> + if (cmd_sts & TX_ERROR) {
> + if (net_ratelimit())
> + printk(KERN_ERR "%s: Error in TX\n", dev->name);
> + dev->stats.tx_errors++;
> + }
> + dma_unmap_single(NULL, addr, count, DMA_TO_DEVICE);
> + if (skb)
> + dev_kfree_skb_irq(skb);
> + released++;
> + }
> +txq_reclaim_end:
> + netif_tx_unlock(dev);
> + return released;
> +}
> +
> +static void pxa168_eth_tx_timeout(struct net_device *dev)
> +{
> + struct pxa168_eth_private *pep = netdev_priv(dev);
> +
> + printk(KERN_INFO "%s: TX timeout desc_count %d\n",
> + dev->name, pep->tx_desc_count);
> +
> + schedule_work(&pep->tx_timeout_task);
> +}
> +
> +static void pxa168_eth_tx_timeout_task(struct work_struct *work)
> +{
> + struct pxa168_eth_private *pep = container_of(work,
> + struct pxa168_eth_private,
> + tx_timeout_task);
> + struct net_device *dev = pep->dev;
> + pxa168_eth_stop(dev);
> + pxa168_eth_open(dev);
> +}
> +
> +static int rxq_process(struct net_device *dev, int budget)
> +{
> + struct pxa168_eth_private *pep = netdev_priv(dev);
> + struct net_device_stats *stats = &dev->stats;
> + unsigned int received_packets = 0;
> + struct sk_buff *skb;
> +
> + while (budget-- > 0) {
> +
> + int rx_next_curr_desc, rx_curr_desc, rx_used_desc;
> + struct rx_desc *rx_desc;
> + unsigned int cmd_sts;
> +
> + /* Do not process Rx ring in case of Rx ring resource error */
> + if (pep->rx_resource_err)
> + break;
> + rx_curr_desc = pep->rx_curr_desc_q;
> + rx_used_desc = pep->rx_used_desc_q;
> + rx_desc = &pep->p_rx_desc_area[rx_curr_desc];
> + cmd_sts = rx_desc->cmd_sts;
> + rmb();
> + if (cmd_sts & (BUF_OWNED_BY_DMA))
> + break;
> + skb = pep->rx_skb[rx_curr_desc];
> + pep->rx_skb[rx_curr_desc] = NULL;
> +
> + rx_next_curr_desc = (rx_curr_desc + 1) % pep->rx_ring_size;
> + pep->rx_curr_desc_q = rx_next_curr_desc;
> +
> + /* Rx descriptors exhausted. */
> + /* Set the Rx ring resource error flag */
> + if (rx_next_curr_desc == rx_used_desc)
> + pep->rx_resource_err = 1;
> + pep->rx_desc_count--;
> + dma_unmap_single(NULL, rx_desc->buf_ptr,
> + MAX_PKT_SIZE + ETH_HW_IP_ALIGN,
> + DMA_FROM_DEVICE);
> + received_packets++;
> + /*
> + * Update statistics.
> + * Note byte count includes 4 byte CRC count
> + */
> + stats->rx_packets++;
> + stats->rx_bytes += rx_desc->byte_cnt;
> + /*
> + * In case received a packet without first / last bits on OR
> + * the error summary bit is on, the packets needs to be droped.
> + */
> + if (((cmd_sts & (RX_FIRST_DESC | RX_LAST_DESC)) !=
> + (RX_FIRST_DESC | RX_LAST_DESC))
> + || (cmd_sts & RX_ERROR)) {
> +
> + stats->rx_dropped++;
> + if ((cmd_sts & (RX_FIRST_DESC | RX_LAST_DESC)) !=
> + (RX_FIRST_DESC | RX_LAST_DESC)) {
> + if (net_ratelimit())
> + printk(KERN_ERR
> + "%s: Rx pkt on multiple desc\n",
> + dev->name);
> + }
> + if (cmd_sts & RX_ERROR)
> + stats->rx_errors++;
> + dev_kfree_skb_irq(skb);
> + } else {
> + /*
> + * The -4 is for the CRC in the trailer of the
> + * received packet
> + */
> + skb_put(skb, rx_desc->byte_cnt - 4);
> + skb->protocol = eth_type_trans(skb, dev);
> + netif_receive_skb(skb);
> + }
> + dev->last_rx = jiffies;
> + }
> + /* Fill RX ring with skb's */
> + rxq_refill(dev);
> + return received_packets;
> +}
> +
> +static int pxa168_eth_collect_events(struct pxa168_eth_private *pep,
> + struct net_device *dev)
> +{
> + u32 icr;
> + int ret = 0;
> +
> + icr = rdl(pep, INT_CAUSE);
> + if (0x00 == icr)
> + return IRQ_NONE;
> +
> + wrl(pep, INT_CAUSE, icr ^ 0xffffffff);
> + if (icr & (ICR_TXBUF_H | ICR_TXBUF_L)) {
> + pep->work_todo |= WORK_TX_DONE;
> + ret = 1;
> + }
> + if (icr & ICR_RXBUF)
> + ret = 1;
> + if (icr & ICR_MII_CH) {
> + pep->work_todo |= WORK_LINK;
> + ret = 1;
> + }
> + return ret;
> +}
> +
> +static void handle_link_event(struct pxa168_eth_private *pep)
> +{
> + struct net_device *dev = pep->dev;
> + u32 port_status;
> + int speed;
> + int duplex;
> + int fc;
> +
> + port_status = rdl(pep, PORT_STATUS);
> + if (!(port_status & LINK_UP)) {
> + if (netif_carrier_ok(dev)) {
> + printk(KERN_INFO "%s: link down\n", dev->name);
> + netif_carrier_off(dev);
> + txq_reclaim(dev, 1);
> + }
> + return;
> + }
> + if (port_status & PORT_SPEED_100)
> + speed = 100;
> + else
> + speed = 10;
> +
> + duplex = (port_status & FULL_DUPLEX) ? 1 : 0;
> + fc = (port_status & FLOW_CONTROL_ENABLED) ? 1 : 0;
> + printk(KERN_INFO "%s: link up, %d Mb/s, %s duplex, "
> + "flow control %sabled\n", dev->name,
> + speed, duplex ? "full" : "half", fc ? "en" : "dis");
> + if (!netif_carrier_ok(dev))
> + netif_carrier_on(dev);
> +}
> +
> +static irqreturn_t pxa168_eth_int_handler(int irq, void *dev_id)
> +{
> + struct net_device *dev = (struct net_device *)dev_id;
> + struct pxa168_eth_private *pep = netdev_priv(dev);
> +
> + if (unlikely(!pxa168_eth_collect_events(pep, dev)))
> + return IRQ_NONE;
> + /* Disable interrupts */
> + wrl(pep, INT_MASK, 0);
> + napi_schedule(&pep->napi);
> + return IRQ_HANDLED;
> +}
> +
> +static int set_port_config_ext(struct pxa168_eth_private *pep, int mtu)
> +{
> + int mtu_size;
> +
> + if ((mtu > ETH_DATA_LEN) || (mtu < 64))
> + return -EINVAL;
> +
> + mtu_size = PCXR_MFL_1518;
> + /* Extended Port Configuration */
> + wrl(pep,
> + PORT_CONFIG_EXT, PCXR_2BSM | /* Two byte suffix aligns IP hdr */
> + PCXR_DSCP_EN | /* Enable DSCP in IP */
> + mtu_size | PCXR_FLP | /* do not force link pass */
> + PCXR_TX_HIGH_PRI); /* Transmit - high priority queue */
> +
> + (pep->dev)->mtu = mtu;
> + return 0;
> +}
> +
> +static int pxa168_init_hw(struct pxa168_eth_private *pep)
> +{
> + int err = 0;
> +
> + /* Disable interrupts */
> + wrl(pep, INT_MASK, 0);
> + wrl(pep, INT_CAUSE, 0);
> + /* Write to ICR to clear interrupts. */
> + wrl(pep, INT_W_CLEAR, 0);
> + /* Abort any transmit and receive operations and put DMA
> + * in idle state.
> + */
> + abort_dma(pep);
> + /* Initialize address hash table */
> + err = init_hash_table(pep);
> + if (err)
> + return err;
> + /* SDMA configuration */
> + wrl(pep, SDMA_CONFIG, SDCR_BSZ8 | /* Burst size = 32 bytes */
> + SDCR_RIFB | /* Rx interrupt on frame */
> + SDCR_BLMT | /* Little endian transmit */
> + SDCR_BLMR | /* Little endian receive */
> + SDCR_RC_MAX_RETRANS); /* Max retransmit count */
> + /* Port Configuration */
> + wrl(pep, PORT_CONFIG, PCR_HS); /* Hash size is 1/2kb */
> + set_port_config_ext(pep, (pep->dev)->mtu);
> + return err;
> +}
> +
> +static int rxq_init(struct net_device *dev)
> +{
> + struct pxa168_eth_private *pep = netdev_priv(dev);
> + struct rx_desc *p_rx_desc;
> + int size = 0, i = 0;
> + int rx_desc_num = pep->rx_ring_size;
> +
> + /* Allocate RX skb rings */
> + pep->rx_skb = kmalloc(sizeof(*pep->rx_skb) * pep->rx_ring_size,
> + GFP_KERNEL);
> + if (!pep->rx_skb) {
> + printk(KERN_ERR "%s: Cannot alloc RX skb ring\n", dev->name);
> + return -ENOMEM;
> + }
> + /* Allocate RX ring */
> + pep->rx_desc_count = 0;
> + size = pep->rx_ring_size * sizeof(struct rx_desc);
> + pep->rx_desc_area_size = size;
> + pep->p_rx_desc_area = dma_alloc_coherent(NULL, size,
> + &pep->rx_desc_dma, GFP_KERNEL);
> + if (!pep->p_rx_desc_area) {
> + printk(KERN_ERR "%s: Cannot alloc RX ring (size %d bytes)\n",
> + dev->name, size);
> + goto out;
> + }
> + memset((void *)pep->p_rx_desc_area, 0, size);
> + /* initialize the next_desc_ptr links in the Rx descriptors ring */
> + p_rx_desc = (struct rx_desc *)pep->p_rx_desc_area;
> + for (i = 0; i < rx_desc_num; i++) {
> + p_rx_desc[i].next_desc_ptr = pep->rx_desc_dma +
> + ((i + 1) % rx_desc_num) * sizeof(struct rx_desc);
> + }
> + /* Save Rx desc pointer to driver struct. */
> + pep->rx_curr_desc_q = 0;
> + pep->rx_used_desc_q = 0;
> + pep->rx_desc_area_size = rx_desc_num * sizeof(struct rx_desc);
> + return 0;
> +out:
> + kfree(pep->rx_skb);
> + return -ENOMEM;
> +}
> +
> +static void rxq_deinit(struct net_device *dev)
> +{
> + struct pxa168_eth_private *pep = netdev_priv(dev);
> + int curr;
> +
> + /* Free preallocated skb's on RX rings */
> + for (curr = 0; pep->rx_desc_count && curr < pep->rx_ring_size;
> curr++) {
> + if (pep->rx_skb[curr]) {
> + dev_kfree_skb(pep->rx_skb[curr]);
> + pep->rx_desc_count--;
> + }
> + }
> + if (pep->rx_desc_count)
> + printk(KERN_ERR
> + "Error in freeing Rx Ring. %d skb's still\n",
> + pep->rx_desc_count);
> + /* Free RX ring */
> + if (pep->p_rx_desc_area)
> + dma_free_coherent(NULL, pep->rx_desc_area_size,
> + pep->p_rx_desc_area, pep->rx_desc_dma);
> + kfree(pep->rx_skb);
> +}
> +
> +static int txq_init(struct net_device *dev)
> +{
> + struct pxa168_eth_private *pep = netdev_priv(dev);
> + struct tx_desc *p_tx_desc;
> + int size = 0, i = 0;
> + int tx_desc_num = pep->tx_ring_size;
> +
> + pep->tx_skb = kmalloc(sizeof(*pep->tx_skb) * pep->tx_ring_size,
> + GFP_KERNEL);
> + if (!pep->tx_skb) {
> + printk(KERN_ERR "%s: Cannot alloc TX skb ring\n", dev->name);
> + return -ENOMEM;
> + }
> + /* Allocate TX ring */
> + pep->tx_desc_count = 0;
> + size = pep->tx_ring_size * sizeof(struct tx_desc);
> + pep->tx_desc_area_size = size;
> + pep->p_tx_desc_area = dma_alloc_coherent(NULL, size,
> + &pep->tx_desc_dma, GFP_KERNEL);
> + if (!pep->p_tx_desc_area) {
> + printk(KERN_ERR "%s: Cannot allocate Tx Ring (size %d
> bytes)\n",
> + dev->name, size);
> + goto out;
> + }
> + memset((void *)pep->p_tx_desc_area, 0, pep->tx_desc_area_size);
> + /* Initialize the next_desc_ptr links in the Tx descriptors ring */
> + p_tx_desc = (struct tx_desc *)pep->p_tx_desc_area;
> + for (i = 0; i < tx_desc_num; i++) {
> + p_tx_desc[i].next_desc_ptr = pep->tx_desc_dma +
> + ((i + 1) % tx_desc_num) * sizeof(struct tx_desc);
> + }
> + pep->tx_curr_desc_q = 0;
> + pep->tx_used_desc_q = 0;
> + pep->tx_desc_area_size = tx_desc_num * sizeof(struct tx_desc);
> + return 0;
> +out:
> + kfree(pep->tx_skb);
> + return -ENOMEM;
> +}
> +
> +static void txq_deinit(struct net_device *dev)
> +{
> + struct pxa168_eth_private *pep = netdev_priv(dev);
> +
> + /* Free outstanding skb's on TX ring */
> + txq_reclaim(dev, 1);
> + BUG_ON(pep->tx_used_desc_q != pep->tx_curr_desc_q);
> + /* Free TX ring */
> + if (pep->p_tx_desc_area)
> + dma_free_coherent(NULL, pep->tx_desc_area_size,
> + pep->p_tx_desc_area, pep->tx_desc_dma);
> + kfree(pep->tx_skb);
> +}
> +
> +static int pxa168_eth_open(struct net_device *dev)
> +{
> + struct pxa168_eth_private *pep = netdev_priv(dev);
> + int err;
> +
> + err = request_irq(dev->irq, pxa168_eth_int_handler,
> + IRQF_DISABLED , dev->name, dev);
> + if (err) {
> + dev_printk(KERN_ERR, &dev->dev, "can't assign irq\n");
> + return -EAGAIN;
> + }
> + pep->rx_resource_err = 0;
> + err = rxq_init(dev);
> + if (err != 0)
> + goto out_free_irq;
> + err = txq_init(dev);
> + if (err != 0)
> + goto out_free_rx_skb;
> + pep->rx_used_desc_q = 0;
> + pep->rx_curr_desc_q = 0;
> +
> + /* Fill RX ring with skb's */
> + rxq_refill(dev);
> + pep->rx_used_desc_q = 0;
> + pep->rx_curr_desc_q = 0;
> + netif_carrier_off(dev);
> + eth_port_start(dev);
> + napi_enable(&pep->napi);
> + return 0;
> +out_free_rx_skb:
> + rxq_deinit(dev);
> +out_free_irq:
> + free_irq(dev->irq, dev);
> + return err;
> +}
> +
> +static int pxa168_eth_stop(struct net_device *dev)
> +{
> + struct pxa168_eth_private *pep = netdev_priv(dev);
> + eth_port_reset(dev);
> +
> + /* Disable interrupts */
> + wrl(pep, INT_MASK, 0);
> + wrl(pep, INT_CAUSE, 0);
> + /* Write to ICR to clear interrupts. */
> + wrl(pep, INT_W_CLEAR, 0);
> + napi_disable(&pep->napi);
> + del_timer_sync(&pep->timeout);
> + netif_carrier_off(dev);
> + free_irq(dev->irq, dev);
> + rxq_deinit(dev);
> + txq_deinit(dev);
> + return 0;
> +}
> +
> +static int pxa168_eth_change_mtu(struct net_device *dev, int mtu)
> +{
> + if ((mtu > ETH_DATA_LEN) || (mtu < 64))
> + return -EINVAL;
> + dev->mtu = mtu;
> + return 0;
> +}
> +
> +static int eth_alloc_tx_desc_index(struct pxa168_eth_private *pep)
> +{
> + int tx_desc_curr;
> +
> + tx_desc_curr = pep->tx_curr_desc_q;
> + pep->tx_curr_desc_q = (tx_desc_curr + 1) % pep->tx_ring_size;
> + BUG_ON(pep->tx_curr_desc_q == pep->tx_used_desc_q);
> + pep->tx_desc_count++;
> + return tx_desc_curr;
> +}
> +
> +static void eth_tx_submit_descs_for_skb(struct pxa168_eth_private *pep,
> + struct sk_buff *skb)
> +{
> + int tx_index;
> + struct tx_desc *desc;
> + int length;
> +
> + tx_index = eth_alloc_tx_desc_index(pep);
> + desc = &pep->p_tx_desc_area[tx_index];
> + length = skb->len;
> + pep->tx_skb[tx_index] = skb;
> + desc->byte_cnt = length;
> + desc->buf_ptr = dma_map_single(NULL, skb->data, length,
> DMA_TO_DEVICE);
> + wmb();
> + desc->cmd_sts = BUF_OWNED_BY_DMA | TX_GEN_CRC | TX_FIRST_DESC |
> + TX_ZERO_PADDING | TX_LAST_DESC;
> + if (unlikely(!(pep->tx_desc_count % TX_DONE_INTERVAL)))
> + desc->cmd_sts |= TX_EN_INT;
> + wmb();
> + wrl(pep, SDMA_CMD, SDMA_CMD_TXDH | SDMA_CMD_ERD);
> +}
> +
> +static int pxa168_rx_poll(struct napi_struct *napi, int budget)
> +{
> + struct pxa168_eth_private *pep =
> + container_of(napi, struct pxa168_eth_private, napi);
> + struct net_device *dev = pep->dev;
> + int work_done = 0;
> +
> + if (unlikely(pep->work_todo & WORK_LINK)) {
> + pep->work_todo &= ~(WORK_LINK);
> + handle_link_event(pep);
> + }
> + /*
> + * We call txq_reclaim every time since in NAPI interupts are
> disabled
> + * and due to this we miss the TX_DONE interrupt,which is not
> updated in
> + * interrupt status register.
> + */
> + txq_reclaim(dev, 0);
> + if (netif_queue_stopped(dev)
> + && pep->tx_ring_size - pep->tx_desc_count > 1) {
> + netif_wake_queue(dev);
> + }
> + work_done = rxq_process(dev, budget);
> + if (work_done < budget) {
> + napi_complete(napi);
> + wrl(pep, INT_MASK, ALL_INTS);
> + }
> + return work_done;
> +}
> +
> +static int pxa168_eth_start_xmit(struct sk_buff *skb, struct net_device
> *dev)
> +{
> + struct pxa168_eth_private *pep = netdev_priv(dev);
> + struct net_device_stats *stats = &dev->stats;
> +
> + eth_tx_submit_descs_for_skb(pep, skb);
> + stats->tx_bytes += skb->len;
> + stats->tx_packets++;
> + dev->trans_start = jiffies;
> + if (pep->tx_ring_size - pep->tx_desc_count <= 1) {
> + /* We handled the current skb, but now we are out of space.*/
> + netif_stop_queue(dev);
> + }
> + return NETDEV_TX_OK;
> +}
> +
> +static int pxa168_smi_read(struct mii_bus *bus, int phy_addr, int regnum)
> +{
> + int val;
> + struct pxa168_eth_private *pep = bus->priv;
> + int i = 0;
> +
> + /* wait for the SMI register to become available */
> + for (i = 0; (val = rdl(pep, SMI)) & SMI_BUSY; i++) {
> + if (i == PHY_WAIT_ITERATIONS) {
> + printk(KERN_ERR
> + "pxa168 PHY timeout, val=0x%x\n", val);
> + return -ETIMEDOUT;
> + }
> + udelay(1);
> + }
> + wrl(pep, SMI, (phy_addr << 16) | (regnum << 21) | SMI_OP_R);
> + /* now wait for the data to be valid */
> + for (i = 0; !((val = rdl(pep, SMI)) & SMI_R_VALID); i++) {
> + if (i == PHY_WAIT_ITERATIONS) {
> + printk(KERN_ERR
> + "pxa168 PHY RD timeout, val=0x%x\n", val);
> + return -ETIMEDOUT;
> + }
> + udelay(1);
> + }
> + return val & 0xffff;
> +}
> +
> +static int pxa168_smi_write(struct mii_bus *bus, int phy_addr, int regnum,
> + u16 value)
> +{
> + struct pxa168_eth_private *pep = bus->priv;
> + int i;
> +
> + /* wait for the SMI register to become available */
> + for (i = 0; rdl(pep, SMI) & SMI_BUSY; i++) {
> + if (i == PHY_WAIT_ITERATIONS) {
> + printk(KERN_ERR "pxa168 PHY busy timeout.\n");
> + return -ETIMEDOUT;
> + }
> + udelay(1);
> + }
> + wrl(pep, SMI, (phy_addr << 16) | (regnum << 21) |
> + SMI_OP_W | (value & 0xffff));
> +
> + return 0;
> +}
> +
> +static int pxa168_eth_do_ioctl(struct net_device *dev, struct ifreq *ifr,
> + int cmd)
> +{
> + struct pxa168_eth_private *pep = netdev_priv(dev);
> + if (pep->phy != NULL)
> + return phy_mii_ioctl(pep->phy, if_mii(ifr), cmd);
> + return -EOPNOTSUPP;
> +}
> +
> +static struct phy_device *phy_scan(struct pxa168_eth_private *pep, int
> phy_addr)
> +{
> + struct mii_bus *bus = pep->smi_bus;
> + struct phy_device *phydev;
> + int start;
> + int num;
> + int i;
> +
> + if (phy_addr == PXA168_ETH_PHY_ADDR_DEFAULT) {
> + /* Scan entire range */
> + start = ethernet_phy_get(pep);
> + num = 32;
> + } else {
> + /* Use phy addr specific to platform */
> + start = phy_addr & 0x1f;
> + num = 1;
> + }
> + phydev = NULL;
> + for (i = 0; i < num; i++) {
> + int addr = (start + i) & 0x1f;
> + if (bus->phy_map[addr] == NULL)
> + mdiobus_scan(bus, addr);
> +
> + if (phydev == NULL) {
> + phydev = bus->phy_map[addr];
> + if (phydev != NULL)
> + ethernet_phy_set_addr(pep, addr);
> + }
> + }
> +
> + return phydev;
> +}
> +
> +static void phy_init(struct pxa168_eth_private *pep, int speed, int
> duplex)
> +{
> + struct phy_device *phy = pep->phy;
> + ethernet_phy_reset(pep);
> +
> + phy_attach(pep->dev, dev_name(&phy->dev), 0,
> PHY_INTERFACE_MODE_MII);
> +
> + if (speed == 0) {
> + phy->autoneg = AUTONEG_ENABLE;
> + phy->speed = 0;
> + phy->duplex = 0;
> + phy->supported &= PHY_BASIC_FEATURES;
> + phy->advertising = phy->supported | ADVERTISED_Autoneg;
> + } else {
> + phy->autoneg = AUTONEG_DISABLE;
> + phy->advertising = 0;
> + phy->speed = speed;
> + phy->duplex = duplex;
> + }
> + phy_start_aneg(phy);
> +}
> +
> +static int ethernet_phy_setup(struct net_device *dev)
> +{
> + struct pxa168_eth_private *pep = netdev_priv(dev);
> +
> + if (pep->pd != NULL) {
> + if (pep->pd->init)
> + pep->pd->init();
> + }
> + pep->phy = phy_scan(pep, pep->pd->phy_addr & 0x1f);
> + if (pep->phy != NULL)
> + phy_init(pep, pep->pd->speed, pep->pd->duplex);
> + update_hash_table_mac_address(pep, NULL, dev->dev_addr);
> + return 0;
> +}
> +
> +static int get_random_mac_addr(struct net_device *dev)
> +{
> + printk(KERN_INFO "%s:Using random mac address\n", dev->name);
> + random_ether_addr(dev->dev_addr);
> + return 0;
> +}
> +
> +static int pxa168_get_settings(struct net_device *dev, struct ethtool_cmd
> *cmd)
> +{
> + struct pxa168_eth_private *pep = netdev_priv(dev);
> + int err;
> +
> + err = phy_read_status(pep->phy);
> + if (err == 0)
> + err = phy_ethtool_gset(pep->phy, cmd);
> + return err;
> +}
> +
> +static int pxa168_set_settings(struct net_device *dev, struct ethtool_cmd
> *cmd)
> +{
> + struct pxa168_eth_private *pep = netdev_priv(dev);
> +
> + return phy_ethtool_sset(pep->phy, cmd);
> +}
> +
> +static void pxa168_get_drvinfo(struct net_device *dev,
> + struct ethtool_drvinfo *info)
> +{
> + strncpy(info->driver, DRIVER_NAME, 32);
> + strncpy(info->version, DRIVER_VERSION, 32);
> + strncpy(info->fw_version, "N/A", 32);
> + strncpy(info->bus_info, "N/A", 32);
> +}
> +
> +static u32 pxa168_get_link(struct net_device *dev)
> +{
> + return !!netif_carrier_ok(dev);
> +}
> +
> +static const struct ethtool_ops pxa168_ethtool_ops = {
> + .get_settings = pxa168_get_settings,
> + .set_settings = pxa168_set_settings,
> + .get_drvinfo = pxa168_get_drvinfo,
> + .get_link = pxa168_get_link,
> +};
> +
> +static const struct net_device_ops pxa168_eth_netdev_ops = {
> + .ndo_open = pxa168_eth_open,
> + .ndo_stop = pxa168_eth_stop,
> + .ndo_start_xmit = pxa168_eth_start_xmit,
> + .ndo_set_rx_mode = pxa168_eth_set_rx_mode,
> + .ndo_set_mac_address = pxa168_eth_set_mac_address,
> + .ndo_validate_addr = eth_validate_addr,
> + .ndo_do_ioctl = pxa168_eth_do_ioctl,
> + .ndo_change_mtu = pxa168_eth_change_mtu,
> + .ndo_tx_timeout = pxa168_eth_tx_timeout,
> +};
> +
> +static int pxa168_eth_probe(struct platform_device *pdev)
> +{
> + struct pxa168_eth_private *pep = NULL;
> + struct net_device *dev = NULL;
> + struct resource *res;
> + struct clk *clk;
> + int err;
> +
> + printk(KERN_NOTICE "PXA168 10/100 Ethernet Driver\n");
> +
> + clk = clk_get(&pdev->dev, "MFUCLK");
> + if (IS_ERR(clk)) {
> + printk(KERN_ERR "fast Ethernet failed to get clock\n");
> + return -ENODEV;
> + }
> + clk_enable(clk);
> +
> + dev = alloc_etherdev(sizeof(struct pxa168_eth_private));
> + if (!dev) {
> + err = -ENOMEM;
> + goto out;
> + }
> +
> + platform_set_drvdata(pdev, dev);
> + pep = netdev_priv(dev);
> + pep->dev = dev;
> + pep->clk = clk;
> + res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> + if (res == NULL) {
> + err = -ENODEV;
> + goto out;
> + }
> + pep->base = ioremap(res->start, res->end - res->start + 1);
> + if (pep->base == NULL) {
> + err = -ENOMEM;
> + goto out;
> + }
> + res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
> + BUG_ON(!res);
> + dev->irq = res->start;
> + dev->netdev_ops = &pxa168_eth_netdev_ops;
> + dev->watchdog_timeo = 8 * HZ;
> + dev->base_addr = 0;
> + SET_ETHTOOL_OPS(dev, &pxa168_ethtool_ops);
> +
> + INIT_WORK(&pep->tx_timeout_task, pxa168_eth_tx_timeout_task);
> +
> + pep->rx_ring_size = NUM_RX_DESCS;
> + pep->tx_ring_size = NUM_TX_DESCS;
> + get_random_mac_addr(dev);
> + pep->pd = pdev->dev.platform_data;
> + netif_napi_add(dev, &pep->napi, pxa168_rx_poll, pep->rx_ring_size);
> +
> + memset(&pep->timeout, 0, sizeof(struct timer_list));
> + init_timer(&pep->timeout);
> + pep->timeout.function = rxq_refill_timer_wrapper;
> + pep->timeout.data = (unsigned long)pep;
> +
> + pep->smi_bus = mdiobus_alloc();
> + if (pep->smi_bus == NULL) {
> + err = -ENOMEM;
> + goto out;
> + }
> + pep->smi_bus->priv = pep;
> + pep->smi_bus->name = "pxa168_eth smi";
> + pep->smi_bus->read = pxa168_smi_read;
> + pep->smi_bus->write = pxa168_smi_write;
> + snprintf(pep->smi_bus->id, MII_BUS_ID_SIZE, "%d", pdev->id);
> + pep->smi_bus->parent = &pdev->dev;
> + pep->smi_bus->phy_mask = 0xffffffff;
> + if (mdiobus_register(pep->smi_bus) < 0) {
> + err = -ENOMEM;
> + goto out;
> + }
> + pxa168_init_hw(pep);
> + err = ethernet_phy_setup(dev);
> + if (err)
> + goto out;
> + SET_NETDEV_DEV(dev, &pdev->dev);
> + err = register_netdev(dev);
> + if (err)
> + goto out;
> + return 0;
> +out:
> + if (pep->clk) {
> + clk_disable(pep->clk);
> + clk_put(pep->clk);
> + pep->clk = NULL;
> + }
> + if (pep->base) {
> + iounmap(pep->base);
> + pep->base = NULL;
> + }
> + if (dev)
> + free_netdev(dev);
> + return err;
> +}
> +
> +static int pxa168_eth_remove(struct platform_device *pdev)
> +{
> + struct net_device *dev = platform_get_drvdata(pdev);
> + struct pxa168_eth_private *pep = netdev_priv(dev);
> +
> + if (pep->htpr) {
> + dma_free_coherent(NULL, HASH_ADDR_TABLE_SIZE + 7,
> + pep->htpr, pep->htpr_dma);
> + pep->htpr = NULL;
> + }
> + if (pep->clk) {
> + clk_disable(pep->clk);
> + clk_put(pep->clk);
> + pep->clk = NULL;
> + }
> + if (pep->phy != NULL)
> + phy_detach(pep->phy);
> +
> + iounmap(pep->base);
> + pep->base = NULL;
> + unregister_netdev(dev);
> + flush_scheduled_work();
> + free_netdev(dev);
> + platform_set_drvdata(pdev, NULL);
> + return 0;
> +}
> +
> +static void pxa168_eth_shutdown(struct platform_device *pdev)
> +{
> + struct net_device *dev = platform_get_drvdata(pdev);
> + eth_port_reset(dev);
> +}
> +
> +#ifdef CONFIG_PM
> +static int pxa168_eth_resume(struct platform_device *pdev)
> +{
> + return -ENOSYS;
> +}
> +
> +static int pxa168_eth_suspend(struct platform_device *pdev, pm_message_t
> state)
> +{
> + return -ENOSYS;
> +}
> +
> +#else
> +#define pxa168_eth_resume NULL
> +#define pxa168_eth_suspend NULL
> +#endif
> +
> +static struct platform_driver pxa168_eth_driver = {
> + .probe = pxa168_eth_probe,
> + .remove = pxa168_eth_remove,
> + .shutdown = pxa168_eth_shutdown,
> + .resume = pxa168_eth_resume,
> + .suspend = pxa168_eth_suspend,
> + .driver = {
> + .name = DRIVER_NAME,
> + },
> +};
> +
> +static int __init pxa168_init_module(void)
> +{
> + return platform_driver_register(&pxa168_eth_driver);
> +}
> +
> +static void __exit pxa168_cleanup_module(void)
> +{
> + platform_driver_unregister(&pxa168_eth_driver);
> +}
> +
> +module_init(pxa168_init_module);
> +module_exit(pxa168_cleanup_module);
> +
> +MODULE_LICENSE("GPL");
> +MODULE_DESCRIPTION("Ethernet driver for Marvell PXA168");
> +MODULE_ALIAS("platform:pxa168_eth");
> diff --git a/include/linux/pxa168_eth.h b/include/linux/pxa168_eth.h
> new file mode 100644
> index 0000000..152981c
> --- /dev/null
> +++ b/include/linux/pxa168_eth.h
> @@ -0,0 +1,20 @@
> +/*
> + *pxa168 ethernet platform device data definition file.
> + */
> +#ifndef __LINUX_PXA168_ETH_H
> +#define __LINUX_PXA168_ETH_H
> +
> +struct pxa168_eth_platform_data {
> + int phy_addr;
> +
> + /* If speed is 0, then speed and duplex are autonegotiated. */
> + int speed; /* 0, SPEED_10, SPEED_100 */
> + int duplex; /* DUPLEX_HALF or DUPLEX_FULL */
> +
> + /* init callback is used for board specific initialization
> + * e.g on Aspenite its used to initialize the PHY transceiver.
> + */
> + int (*init)(void);
> +};
> +
> +#endif /* __LINUX_PXA168_ETH_H */
> --
> 1.5.3.3
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox