new file mode 100644
@@ -0,0 +1,40 @@
+/*
+ * eth-mac-platform.h: Enforces platform-defined MAC for Async probed devices
+ */
+
+#ifndef __ETH_NET_MAC_PLATFORM_H__
+#define __ETH_NET_MAC_PLATFORM_H__
+
+#include <linux/if_ether.h>
+
+/**
+ * struct eth_mac_platform - associates asynchronously probed device path with
+ * MAC address to be assigned to the device when it
+ * is created
+ *
+ * @device_path: device path name of network device
+ * @mac: MAC address to assign to network device matching device path
+ * @list: can be left uninitialized when passing from platform
+ */
+
+struct eth_mac_platform {
+ char *device_path;
+ u8 mac[ETH_ALEN];
+ struct list_head list; /* unused in platform data usage */
+};
+
+#ifdef CONFIG_NET
+/**
+ * eth_mac_platform_register_device_macs - add an array of device path to
+ * monitor and MAC to apply when the network
+ * device at the device path appears
+ * @macs: array of struct eth_mac_platform terminated by entry with
+ * NULL device_path
+ */
+int eth_mac_platform_register_device_macs(const struct eth_mac_platform *macs);
+#else
+static inline int eth_mac_platform_register_device_macs(
+ const struct eth_mac_platform *macs) { return 0; }
+#endif /* !CONFIG_NET */
+
+#endif /* __ETH_NET_MAC_PLATFORM_H__ */
@@ -335,9 +335,12 @@ source "net/caif/Kconfig"
source "net/ceph/Kconfig"
source "net/nfc/Kconfig"
-
endif # if NET
+# used by board / dt platform to enforce Ethernet MACs for Async-probed devices
+config ETH_MAC_PLATFORM
+ bool
+
# Used by archs to tell that they support BPF_JIT
config HAVE_BPF_JIT
bool
@@ -5,3 +5,6 @@
obj-y += eth.o
obj-$(subst m,y,$(CONFIG_IPX)) += pe2.o
obj-$(subst m,y,$(CONFIG_ATALK)) += pe2.o
+ifneq ($(CONFIG_NET),)
+obj-$(CONFIG_ETH_MAC_PLATFORM) += eth-mac-platform.o
+endif
new file mode 100644
@@ -0,0 +1,150 @@
+/*
+ * Helper to allow platform code to enforce association of a locally-
+ * administered MAC address automatically on asynchronously probed devices,
+ * such as SDIO and USB based devices.
+ *
+ * Because the "device path" is used for matching, this is only useful for
+ * network assets physcally wired on the board, and also requires any
+ * different drivers that can compete for bus ordinals (eg mUSB vs ehci) to
+ * have fixed initialization ordering, eg, by having ehci in monolithic
+ * kernel
+ *
+ * Neither a driver nor a module as needs to be callable from machine file
+ * before the network devices are registered.
+ *
+ * (c) 2012 Andy Green <andy.green@linaro.org>
+ */
+
+#include <linux/netdevice.h>
+#include <net/eth-mac-platform.h>
+
+static LIST_HEAD(eth_mac_platform_list);
+static DEFINE_MUTEX(eth_mac_platform_mutex);
+
+static struct eth_mac_platform *__eth_mac_platform_check(struct device *dev)
+{
+ const char *path;
+ const char *p;
+ const char *try;
+ int len;
+ struct device *devn;
+ struct eth_mac_platform *tmp;
+ struct list_head *pos;
+
+ list_for_each(pos, ð_mac_platform_list) {
+
+ tmp = list_entry(pos, struct eth_mac_platform, list);
+
+ try = tmp->device_path;
+
+ p = try + strlen(try);
+ devn = dev;
+
+ while (devn) {
+
+ path = dev_name(devn);
+ len = strlen(path);
+
+ if ((p - try) < len) {
+ devn = NULL;
+ continue;
+ }
+
+ p -= len;
+
+ if (strncmp(path, p, len)) {
+ devn = NULL;
+ continue;
+ }
+
+ devn = devn->parent;
+ if (p == try)
+ return tmp;
+
+ if (devn != NULL && (p - try) < 2)
+ devn = NULL;
+
+ p--;
+ if (devn != NULL && *p != '/')
+ devn = NULL;
+ }
+ }
+
+ return NULL;
+}
+
+static int eth_mac_platform_netdev_event(struct notifier_block *this,
+ unsigned long event, void *ptr)
+{
+ struct net_device *dev = ptr;
+ struct sockaddr sa;
+ struct eth_mac_platform *match;
+
+ if (event != NETDEV_REGISTER)
+ return NOTIFY_DONE;
+
+ mutex_lock(ð_mac_platform_mutex);
+
+ match = __eth_mac_platform_check(dev->dev.parent);
+ if (match == NULL)
+ goto bail;
+
+ sa.sa_family = dev->type;
+ memcpy(sa.sa_data, match->mac, sizeof match->mac);
+ dev->netdev_ops->ndo_set_mac_address(dev, &sa);
+
+bail:
+ mutex_unlock(ð_mac_platform_mutex);
+
+ return NOTIFY_DONE;
+}
+
+int eth_mac_platform_register_device_macs(const struct eth_mac_platform *macs)
+{
+ struct eth_mac_platform *next;
+ int ret = 0;
+
+ mutex_lock(ð_mac_platform_mutex);
+
+ while (macs->device_path) {
+
+ next = kmemdup(macs, sizeof(*macs), GFP_KERNEL);
+ if (!next) {
+ ret = -ENOMEM;
+ goto bail;
+ }
+
+ next->device_path = kstrdup(macs->device_path, GFP_KERNEL);
+ if (!next->device_path) {
+ kfree(next);
+ ret = -ENOMEM;
+ goto bail;
+ }
+
+ list_add(&next->list, ð_mac_platform_list);
+
+ macs++;
+ }
+bail:
+ mutex_unlock(ð_mac_platform_mutex);
+
+ return ret;
+}
+
+static struct notifier_block eth_mac_platform_netdev_notifier = {
+ .notifier_call = eth_mac_platform_netdev_event,
+ .priority = 1,
+};
+
+static int __init eth_mac_platform_init(void)
+{
+ int ret;
+
+ ret = register_netdevice_notifier(ð_mac_platform_netdev_notifier);
+ if (ret)
+ pr_err("eth_mac_platform_init: Notifier registration failed\n");
+
+ return ret;
+}
+
+core_initcall(eth_mac_platform_init);