Skip to contentSkip to conversation

x86: minnowmax: Move USB and Ethernet to driver model

PATCH New Simon Glass 4 participants 50 messages Jul 6, 2015 – Aug 7, 2015

Subsystems (7)
Networking Network drivers x86 USB +3 more subsystems
lore
Share & tools
lore
#0 lore raw

cover x86: minnowmax: Move USB and Ethernet to driver model

This series aims to move minnowmax (an x86 board) to use driver model for USB and Ethernet.

Since there is some support lacking, it includes:
- driver-model conversion of asix USB Ethernet driver
- driver-model conversion of Realtek 8169 Ethernet driver
- PCI device matching without using device tree
- driver-model conversion of USB Ethernet layer
- driver-model conversion of ehci_pci driver

Also it includes a proposed change to the Ethernet uclass recv method to support slow devices better.

Simon Glass (18):
dm: core: Add \n to two dm_warn() messages
usb: ehci: Correct a missing hypen in an error message
usb: Update some EHCI driver licenses to use SPDX
x86: Show the un-relocated IP address in exceptions
dm: pci: Add support for PCI driver matching
dm: eth: Add driver-model support to the rtl8169 driver
dm: pci: Add a function to get the BDF for a device
dm: usb: Correct the struct usb_driver_entry comment
dm: usb: Avoid using USB ethernet with CONFIG_DM_USB and no DM_ETH
dm: eth: Avoid blocking on packet reception
dm: usb: eth: Support driver model with USB Ethernet
dm: usb: Adjust the USB_DEVICE() macro naming
x86: minnowmax: Drop the cache line size hack
dm: usb: Add driver-model support to ehci-pci
dm: usb: eth: Add driver-model support to the asix driver
net: Allow drivers to return -ENOSYS with the write_hwaddr() method
x86: Convert minnowmax to use CONFIG_DM_NET
x86: Convert minnowmax to use CONFIG_DM_USB

 arch/x86/cpu/interrupts.c         |   2 +
 board/intel/minnowmax/minnowmax.c |   6 -
 common/cmd_usb.c                  |  14 ++-
 common/usb_hub.c                  |   2 +-
 common/usb_kbd.c                  |   4 +-
 common/usb_storage.c              |   2 +-
 configs/minnowmax_defconfig       |   3 +
 drivers/core/device.c             |   4 +-
 drivers/net/designware.c          |   2 +-
 drivers/net/rtl8169.c             | 236 +++++++++++++++++++++++++++++--------
 drivers/net/sandbox-raw.c         |   2 +-
 drivers/net/sandbox.c             |   2 +-
 drivers/net/sunxi_emac.c          |   2 +-
 drivers/pci/pci-uclass.c          | 144 ++++++++++++++++++++---
 drivers/pci/pci_compat.c          |   8 +-
 drivers/usb/Kconfig               |   4 +-
 drivers/usb/eth/asix.c            | 237 ++++++++++++++++++++++++++++++++++----
 drivers/usb/eth/usb_ether.c       | 131 ++++++++++++++++++++-
 drivers/usb/host/ehci-hcd.c       |  17 +--
 drivers/usb/host/ehci-pci.c       | 112 ++++++++++++------
 drivers/usb/host/ehci.h           |  15 +--
 drivers/usb/host/r8a66597-hcd.c   |  14 +--
 drivers/usb/host/r8a66597.h       |  14 +--
 include/configs/minnowmax.h       |   3 -
 include/net.h                     |  14 ++-
 include/pci.h                     |  87 +++++++++++++-
 include/usb.h                     |  22 +++-
 include/usb_ether.h               |  89 ++++++++++++--
 net/eth.c                         |  11 +-
 29 files changed, 971 insertions(+), 232 deletions(-)
Signature · 1 line
2.4.3.573.g4eafbef
#1 lore raw

PATCH 1/18 dm: core: Add \n to two dm_warn() messages

These should finish with a newline like the others.

---

 drivers/core/device.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)
drivers/core/device.c +2 -2
index 85fd1fc..eebb53c 100644
@@ -330,7 +330,7 @@ void *dev_get_platdata(struct udevice *dev)
 void *dev_get_parent_platdata(struct udevice *dev)
 {
 	if (!dev) {
-		dm_warn("%s: null device", __func__);
+		dm_warn("%s: null device\n", __func__);
 		return NULL;
 	}
 
@@ -340,7 +340,7 @@ void *dev_get_parent_platdata(struct udevice *dev)
 void *dev_get_uclass_platdata(struct udevice *dev)
 {
 	if (!dev) {
-		dm_warn("%s: null device", __func__);
+		dm_warn("%s: null device\n", __func__);
 		return NULL;
 	}
 
Signature · 1 line
2.4.3.573.g4eafbef
#2 lore raw

PATCH 1/18 dm: core: Add \n to two dm_warn() messages

Quoted 8 lines · Simon Glass

These should finish with a newline like the others.

---

 drivers/core/device.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

Applied to u-boot-dm.

#3 lore raw

PATCH 2/18 usb: ehci: Correct a missing hypen in an error message

In reply to Simon Glass

Add a hyphen to correct the grammar.

---

 drivers/usb/host/ehci-hcd.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/usb/host/ehci-hcd.c +1 -1
index bf02221..b040f12 100644
@@ -321,7 +321,7 @@ static void ehci_update_endpt2_dev_n_port(struct usb_device *udev,
 		struct udevice *dev = parent;
 
 		if (device_get_uclass_id(dev->parent) != UCLASS_USB_HUB) {
-			printf("ehci: Error cannot find high speed parent of usb-1 device\n");
+			printf("ehci: Error cannot find high-speed parent of usb-1 device\n");
 			return;
 		}
 
Signature · 1 line
2.4.3.573.g4eafbef
#6 lore raw

PATCH 3/18 usb: Update some EHCI driver licenses to use SPDX

In reply to Simon Glass

A few drivers still write out the license in full. Fix these.

---

 drivers/usb/host/ehci-hcd.c     | 15 +--------------
 drivers/usb/host/ehci-pci.c     | 15 +--------------
 drivers/usb/host/ehci.h         | 15 +--------------
 drivers/usb/host/r8a66597-hcd.c | 14 +-------------
 drivers/usb/host/r8a66597.h     | 14 +-------------
 5 files changed, 5 insertions(+), 68 deletions(-)
drivers/usb/host/ehci-hcd.c +1 -14
index b040f12..3a0d32e 100644
@@ -5,20 +5,7 @@
  *
  * All rights reserved.
  *
- * 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 version 2 of
- * the License.
- *
- * 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
+ * SPDX-License-Identifier:	GPL-2.0
  */
 #include <common.h>
 #include <dm.h>
drivers/usb/host/ehci-pci.c +1 -14
index b9eabc5..3d333bd 100644
@@ -2,20 +2,7 @@
  * Copyright (c) 2007-2008, Juniper Networks, Inc.
  * All rights reserved.
  *
- * 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 version 2 of
- * the License.
- *
- * 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
+ * SPDX-License-Identifier:	GPL-2.0
  */
 
 #include <common.h>
drivers/usb/host/ehci.h +1 -14
index 774282d..3379c29 100644
@@ -3,20 +3,7 @@
  * Copyright (c) 2008, Michael Trimarchi <[email protected]>
  * All rights reserved.
  *
- * 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 version 2 of
- * the License.
- *
- * 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
+ * SPDX-License-Identifier:	GPL-2.0
  */
 
 #ifndef USB_EHCI_H
drivers/usb/host/r8a66597-hcd.c +1 -13
index 6f33456..373e04c 100644
@@ -3,19 +3,7 @@
  *
  * Copyright (C) 2008  Yoshihiro Shimoda <[email protected]>
  *
- * 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; version 2 of the License.
- *
- * 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., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
- *
+ * SPDX-License-Identifier:	GPL-2.0
  */
 
 #include <common.h>
drivers/usb/host/r8a66597.h +1 -13
index ca1b671..67dc3c4 100644
@@ -3,19 +3,7 @@
  *
  * Copyright (C) 2008  Yoshihiro Shimoda <[email protected]>
  *
- * 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; version 2 of the License.
- *
- * 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., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
- *
+ * SPDX-License-Identifier:	GPL-2.0
  */
 
 #ifndef __R8A66597_H__
Signature · 1 line
2.4.3.573.g4eafbef
#9 lore raw

PATCH 4/18 x86: Show the un-relocated IP address in exceptions

In reply to Simon Glass

When trying to figure out where an exception has occured, the relocated address is not a lot of help. Its value depends on various factors. Show the un-relocated IP as well. This can be looked up in System.map directly.

---

 arch/x86/cpu/interrupts.c | 2 ++
 1 file changed, 2 insertions(+)
arch/x86/cpu/interrupts.c +2 -0
index c777d36..cff004e 100644
@@ -40,6 +40,8 @@ static void dump_regs(struct irq_regs *regs)
 
 	printf("EIP: %04x:[<%08lx>] EFLAGS: %08lx\n",
 			(u16)regs->xcs, regs->eip, regs->eflags);
+	if (gd->flags & GD_FLG_RELOC)
+		printf("reloc EIP : [<%08lx>]\n", regs->eip - gd->reloc_off);
 
 	printf("EAX: %08lx EBX: %08lx ECX: %08lx EDX: %08lx\n",
 		regs->eax, regs->ebx, regs->ecx, regs->edx);
Signature · 1 line
2.4.3.573.g4eafbef
#10 lore raw

PATCH 4/18 x86: Show the un-relocated IP address in exceptions

Hi Simon,

Quoted 7 lines · Simon Glass

When trying to figure out where an exception has occured, the relocated address is not a lot of help. Its value depends on various factors. Show the un-relocated IP as well. This can be looked up in System.map directly.

---

Some nits below:

arch/x86/cpu/interrupts.c static void dump_regs(struct irq_regs *regs) · line 44

Quoted 13 lines
 arch/x86/cpu/interrupts.c | 2 ++
 1 file changed, 2 insertions(+)
arch/x86/cpu/interrupts.c +2 -0
index c777d36..cff004e 100644
@@ -40,6 +40,8 @@ static void dump_regs(struct irq_regs *regs)
 
        printf("EIP: %04x:[<%08lx>] EFLAGS: %08lx\n",
                        (u16)regs->xcs, regs->eip, regs->eflags);
+       if (gd->flags & GD_FLG_RELOC)
+               printf("reloc EIP : [<%08lx>]\n", regs->eip - gd->reloc_off);

Could it be 'Original EIP' instead of 'reloc EIP'? To me, 'reloc EIP' sounds confusing. And the space followed immediately after ':' can be removed?

arch/x86/cpu/interrupts.c static void dump_regs(struct irq_regs *regs)

Quoted 4 lines
arch/x86/cpu/interrupts.c +0 -1
        printf("EAX: %08lx EBX: %08lx ECX: %08lx EDX: %08lx\n",
                regs->eax, regs->ebx, regs->ecx, regs->edx);
--

Regards,
Bin

#11 lore raw

PATCH 5/18 dm: pci: Add support for PCI driver matching

In reply to Simon Glass

At present all PCI devices must be present in the device tree in order to be used. Many or most PCI devices don't require any configuration other than that which is done automatically by U-Boot. It is inefficent to add a node with nothing but a compatible string in order to get a device working.

Add a mechanism whereby PCI drivers can be declared along with the device parameters they support (vendor/device/class). When no suitable driver is found in the device tree the list of such devices is consulted to determine the correct driver. If this also fails, then a generic driver is used as before.

The mechanism used is very similar to that provided by Linux and the header file defintions are copied from Linux 4.1.

---

 drivers/pci/pci-uclass.c | 129 ++++++++++++++++++++++++++++++++++++++++++-----
 include/pci.h            |  79 ++++++++++++++++++++++++++++-
 2 files changed, 193 insertions(+), 15 deletions(-)
drivers/pci/pci-uclass.c +116 -13
index 5b91fe3..41daa0d 100644
@@ -353,6 +353,101 @@ int dm_pci_hose_probe_bus(struct pci_controller *hose, pci_dev_t bdf)
 	return sub_bus;
 }
 
+/**
+ * pci_match_one_device - Tell if a PCI device structure has a matching
+ *                        PCI device id structure
+ * @id: single PCI device id structure to match
+ * @dev: the PCI device structure to match against
+ *
+ * Returns the matching pci_device_id structure or %NULL if there is no match.
+ */
+static bool pci_match_one_id(const struct pci_device_id *id,
+			     const struct pci_device_id *find)
+{
+	if ((id->vendor == PCI_ANY_ID || id->vendor == find->vendor) &&
+	    (id->device == PCI_ANY_ID || id->device == find->device) &&
+	    (id->subvendor == PCI_ANY_ID || id->subvendor == find->subvendor) &&
+	    (id->subdevice == PCI_ANY_ID || id->subdevice == find->subdevice) &&
+	    !((id->class ^ find->class) & id->class_mask))
+		return true;
+
+	return false;
+}
+
+/**
+ * pci_find_and_bind_driver() - Find and bind the right PCI driver
+ *
+ * This only looks at certain fields in the descriptor.
+ */
+static int pci_find_and_bind_driver(struct udevice *parent,
+				    struct pci_device_id *find_id, int devfn,
+				    struct udevice **devp)
+{
+	struct pci_driver_entry *start, *entry;
+	const char *drv;
+	int n_ents;
+	int ret;
+	char name[30], *str;
+
+	*devp = NULL;
+
+	debug("%s: Searching for driver: vendor=%x, device=%x\n", __func__,
+	      find_id->vendor, find_id->device);
+	start = ll_entry_start(struct pci_driver_entry, pci_driver_entry);
+	n_ents = ll_entry_count(struct pci_driver_entry, pci_driver_entry);
+	for (entry = start; entry != start + n_ents; entry++) {
+		const struct pci_device_id *id;
+		struct udevice *dev;
+		const struct driver *drv;
+
+		for (id = entry->match;
+		     id->vendor || id->subvendor || id->class_mask;
+		     id++) {
+			if (!pci_match_one_id(id, find_id))
+				continue;
+
+			drv = entry->driver;
+			/*
+			 * We could pass the descriptor to the driver as
+			 * platdata (instead of NULL) and allow its bind()
+			 * method to return -ENOENT if it doesn't support this
+			 * device. That way we could continue the search to
+			 * find another driver. For now this doesn't seem
+			 * necesssary, so just bind the first match.
+			 */
+			ret = device_bind(parent, drv, drv->name, NULL, -1,
+					  &dev);
+			if (ret)
+				goto error;
+			debug("%s: Match found: %s\n", __func__, drv->name);
+			dev->driver_data = find_id->driver_data;
+			*devp = dev;
+			return 0;
+		}
+	}
+
+	/* Bind a generic driver so that the device can be used */
+	sprintf(name, "pci_%x:%x.%x", parent->seq, PCI_DEV(devfn),
+		PCI_FUNC(devfn));
+	str = strdup(name);
+	if (!str)
+		return -ENOMEM;
+	drv = (find_id->class >> 8) == PCI_CLASS_BRIDGE_PCI ? "pci_bridge_drv" :
+			"pci_generic_drv";
+	ret = device_bind_driver(parent, drv, str, devp);
+	if (ret) {
+		debug("%s: Failed to bind generic driver: %d", __func__, ret);
+		return ret;
+	}
+	debug("%s: No match found: bound generic driver instead\n", __func__);
+
+	return 0;
+
+error:
+	debug("%s: No match found: error %d\n", __func__, ret);
+	return ret;
+}
+
 int pci_bind_bus_devices(struct udevice *bus)
 {
 	ulong vendor, device;
@@ -387,25 +482,33 @@ int pci_bind_bus_devices(struct udevice *bus)
 		      bus->seq, bus->name, PCI_DEV(devfn), PCI_FUNC(devfn));
 		pci_bus_read_config(bus, devfn, PCI_DEVICE_ID, &device,
 				    PCI_SIZE_16);
-		pci_bus_read_config(bus, devfn, PCI_CLASS_DEVICE, &class,
-				    PCI_SIZE_16);
+		pci_bus_read_config(bus, devfn, PCI_CLASS_REVISION, &class,
+				    PCI_SIZE_32);
+		class >>= 8;
 
 		/* Find this device in the device tree */
 		ret = pci_bus_find_devfn(bus, devfn, &dev);
 
+		/* Search for a driver */
+
 		/* If nothing in the device tree, bind a generic device */
 		if (ret == -ENODEV) {
-			char name[30], *str;
-			const char *drv;
-
-			sprintf(name, "pci_%x:%x.%x", bus->seq,
-				PCI_DEV(devfn), PCI_FUNC(devfn));
-			str = strdup(name);
-			if (!str)
-				return -ENOMEM;
-			drv = class == PCI_CLASS_BRIDGE_PCI ?
-				"pci_bridge_drv" : "pci_generic_drv";
-			ret = device_bind_driver(bus, drv, str, &dev);
+			struct pci_device_id find_id;
+			ulong val;
+
+			memset(&find_id, '\0', sizeof(find_id));
+			find_id.vendor = vendor;
+			find_id.device = device;
+			find_id.class = class;
+			if ((header_type & 0x7f) == PCI_HEADER_TYPE_NORMAL) {
+				pci_bus_read_config(bus, devfn,
+						    PCI_SUBSYSTEM_VENDOR_ID,
+						    &val, PCI_SIZE_32);
+				find_id.subvendor = val & 0xffff;
+				find_id.subdevice = val >> 16;
+			}
+			ret = pci_find_and_bind_driver(bus, &find_id, devfn,
+						       &dev);
 		}
 		if (ret)
 			return ret;
include/pci.h +77 -2

91 lines not on this page · raw message

Signature · 1 line
2.4.3.573.g4eafbef
#12 lore raw

PATCH 5/18 dm: pci: Add support for PCI driver matching

Hi Simon,

Quoted 11 lines · Simon Glass

At present all PCI devices must be present in the device tree in order to be used. Many or most PCI devices don't require any configuration other than that which is done automatically by U-Boot. It is inefficent to add a node with nothing but a compatible string in order to get a device working.

Add a mechanism whereby PCI drivers can be declared along with the device parameters they support (vendor/device/class). When no suitable driver is found in the device tree the list of such devices is consulted to determine the correct driver. If this also fails, then a generic driver is used as before.

Quoted 5 lines

The mechanism used is very similar to that provided by Linux and the header file defintions are copied from Linux 4.1.

---

#13 lore raw

PATCH 5/18 dm: pci: Add support for PCI driver matching

In reply to Simon Glass

Hi Simon,

Quoted 179 lines · Simon Glass

At present all PCI devices must be present in the device tree in order to be used. Many or most PCI devices don't require any configuration other than that which is done automatically by U-Boot. It is inefficent to add a node with nothing but a compatible string in order to get a device working.

Add a mechanism whereby PCI drivers can be declared along with the device parameters they support (vendor/device/class). When no suitable driver is found in the device tree the list of such devices is consulted to determine the correct driver. If this also fails, then a generic driver is used as before.

The mechanism used is very similar to that provided by Linux and the header file defintions are copied from Linux 4.1.

---

 drivers/pci/pci-uclass.c | 129 ++++++++++++++++++++++++++++++++++++++++++-----
 include/pci.h            |  79 ++++++++++++++++++++++++++++-
 2 files changed, 193 insertions(+), 15 deletions(-)
drivers/pci/pci-uclass.c +116 -13
index 5b91fe3..41daa0d 100644
@@ -353,6 +353,101 @@ int dm_pci_hose_probe_bus(struct pci_controller *hose, pci_dev_t bdf)
        return sub_bus;
 }
 
+/**
+ * pci_match_one_device - Tell if a PCI device structure has a matching
+ *                        PCI device id structure
+ * @id: single PCI device id structure to match
+ * @dev: the PCI device structure to match against
+ *
+ * Returns the matching pci_device_id structure or %NULL if there is no match.
+ */
+static bool pci_match_one_id(const struct pci_device_id *id,
+                            const struct pci_device_id *find)
+{
+       if ((id->vendor == PCI_ANY_ID || id->vendor == find->vendor) &&
+           (id->device == PCI_ANY_ID || id->device == find->device) &&
+           (id->subvendor == PCI_ANY_ID || id->subvendor == find->subvendor) &&
+           (id->subdevice == PCI_ANY_ID || id->subdevice == find->subdevice) &&
+           !((id->class ^ find->class) & id->class_mask))
+               return true;
+
+       return false;
+}
+
+/**
+ * pci_find_and_bind_driver() - Find and bind the right PCI driver
+ *
+ * This only looks at certain fields in the descriptor.
+ */
+static int pci_find_and_bind_driver(struct udevice *parent,
+                                   struct pci_device_id *find_id, int devfn,
+                                   struct udevice **devp)
+{
+       struct pci_driver_entry *start, *entry;
+       const char *drv;
+       int n_ents;
+       int ret;
+       char name[30], *str;
+
+       *devp = NULL;
+
+       debug("%s: Searching for driver: vendor=%x, device=%x\n", __func__,
+             find_id->vendor, find_id->device);
+       start = ll_entry_start(struct pci_driver_entry, pci_driver_entry);
+       n_ents = ll_entry_count(struct pci_driver_entry, pci_driver_entry);
+       for (entry = start; entry != start + n_ents; entry++) {
+               const struct pci_device_id *id;
+               struct udevice *dev;
+               const struct driver *drv;
+
+               for (id = entry->match;
+                    id->vendor || id->subvendor || id->class_mask;
+                    id++) {
+                       if (!pci_match_one_id(id, find_id))
+                               continue;
+
+                       drv = entry->driver;
+                       /*
+                        * We could pass the descriptor to the driver as
+                        * platdata (instead of NULL) and allow its bind()
+                        * method to return -ENOENT if it doesn't support this
+                        * device. That way we could continue the search to
+                        * find another driver. For now this doesn't seem
+                        * necesssary, so just bind the first match.
+                        */
+                       ret = device_bind(parent, drv, drv->name, NULL, -1,
+                                         &dev);
+                       if (ret)
+                               goto error;
+                       debug("%s: Match found: %s\n", __func__, drv->name);
+                       dev->driver_data = find_id->driver_data;
+                       *devp = dev;
+                       return 0;
+               }
+       }
+
+       /* Bind a generic driver so that the device can be used */
+       sprintf(name, "pci_%x:%x.%x", parent->seq, PCI_DEV(devfn),
+               PCI_FUNC(devfn));
+       str = strdup(name);
+       if (!str)
+               return -ENOMEM;
+       drv = (find_id->class >> 8) == PCI_CLASS_BRIDGE_PCI ? "pci_bridge_drv" :
+                       "pci_generic_drv";
+       ret = device_bind_driver(parent, drv, str, devp);
+       if (ret) {
+               debug("%s: Failed to bind generic driver: %d", __func__, ret);
+               return ret;
+       }
+       debug("%s: No match found: bound generic driver instead\n", __func__);
+
+       return 0;
+
+error:
+       debug("%s: No match found: error %d\n", __func__, ret);
+       return ret;
+}
+
 int pci_bind_bus_devices(struct udevice *bus)
 {
        ulong vendor, device;
@@ -387,25 +482,33 @@ int pci_bind_bus_devices(struct udevice *bus)
                      bus->seq, bus->name, PCI_DEV(devfn), PCI_FUNC(devfn));
                pci_bus_read_config(bus, devfn, PCI_DEVICE_ID, &device,
                                    PCI_SIZE_16);
-               pci_bus_read_config(bus, devfn, PCI_CLASS_DEVICE, &class,
-                                   PCI_SIZE_16);
+               pci_bus_read_config(bus, devfn, PCI_CLASS_REVISION, &class,
+                                   PCI_SIZE_32);
+               class >>= 8;
 
                /* Find this device in the device tree */
                ret = pci_bus_find_devfn(bus, devfn, &dev);
 
+               /* Search for a driver */
+
                /* If nothing in the device tree, bind a generic device */
                if (ret == -ENODEV) {
-                       char name[30], *str;
-                       const char *drv;
-
-                       sprintf(name, "pci_%x:%x.%x", bus->seq,
-                               PCI_DEV(devfn), PCI_FUNC(devfn));
-                       str = strdup(name);
-                       if (!str)
-                               return -ENOMEM;
-                       drv = class == PCI_CLASS_BRIDGE_PCI ?
-                               "pci_bridge_drv" : "pci_generic_drv";
-                       ret = device_bind_driver(bus, drv, str, &dev);
+                       struct pci_device_id find_id;
+                       ulong val;
+
+                       memset(&find_id, '\0', sizeof(find_id));
+                       find_id.vendor = vendor;
+                       find_id.device = device;
+                       find_id.class = class;
+                       if ((header_type & 0x7f) == PCI_HEADER_TYPE_NORMAL) {
+                               pci_bus_read_config(bus, devfn,
+                                                   PCI_SUBSYSTEM_VENDOR_ID,
+                                                   &val, PCI_SIZE_32);
+                               find_id.subvendor = val & 0xffff;
+                               find_id.subdevice = val >> 16;
+                       }
+                       ret = pci_find_and_bind_driver(bus, &find_id, devfn,
+                                                      &dev);
                }
                if (ret)
                        return ret;
include/pci.h +0 -0
index 3af511b..d21fa8b 100644

include/pci.h typedef int pci_dev_t; · line 475

Quoted 9 lines
include/pci.h +4 -1
@@ -468,7 +468,10 @@ typedef int pci_dev_t;
 #define PCI_ANY_ID             (~0)
 
 struct pci_device_id {
-       unsigned int vendor, device;            /* Vendor and device ID or PCI_ANY_ID */
+       unsigned int vendor, device;    /* Vendor and device ID or PCI_ANY_ID */
+       unsigned int subvendor, subdevice; /* Subsystem ID's or PCI_ANY_ID */
+       unsigned int class, class_mask; /* (class,subclass,prog-if) triplet */
+       unsigned long driver_data;      /* Data private to the driver */
 };

Can we create another structure for dm? There are lots of codes which only defines vendor id and device id like below:

static struct pci_device_id mmc_supported[] = {
{ PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_TCF_SDIO_0 }, { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_TCF_SDIO_1 },
};

Although compiler does not generate any error or warning, it is confusing.

Quoted 74 lines
include/pci.h +67 -1
 struct pci_controller;
@@ -1110,7 +1113,79 @@ struct dm_pci_emul_ops {
 int sandbox_pci_get_emul(struct udevice *bus, pci_dev_t find_devfn,
                         struct udevice **emulp);
 
-#endif
+/**
+ * PCI_DEVICE - macro used to describe a specific pci device
+ * @vend: the 16 bit PCI Vendor ID
+ * @dev: the 16 bit PCI Device ID
+ *
+ * This macro is used to create a struct pci_device_id that matches a
+ * specific device.  The subvendor and subdevice fields will be set to
+ * PCI_ANY_ID.
+ */
+#define PCI_DEVICE(vend, dev) \
+       .vendor = (vend), .device = (dev), \
+       .subvendor = PCI_ANY_ID, .subdevice = PCI_ANY_ID
+
+/**
+ * PCI_DEVICE_SUB - macro used to describe a specific pci device with subsystem
+ * @vend: the 16 bit PCI Vendor ID
+ * @dev: the 16 bit PCI Device ID
+ * @subvend: the 16 bit PCI Subvendor ID
+ * @subdev: the 16 bit PCI Subdevice ID
+ *
+ * This macro is used to create a struct pci_device_id that matches a
+ * specific device with subsystem information.
+ */
+#define PCI_DEVICE_SUB(vend, dev, subvend, subdev) \
+       .vendor = (vend), .device = (dev), \
+       .subvendor = (subvend), .subdevice = (subdev)
+
+/**
+ * PCI_DEVICE_CLASS - macro used to describe a specific pci device class
+ * @dev_class: the class, subclass, prog-if triple for this device
+ * @dev_class_mask: the class mask for this device
+ *
+ * This macro is used to create a struct pci_device_id that matches a
+ * specific PCI class.  The vendor, device, subvendor, and subdevice
+ * fields will be set to PCI_ANY_ID.
+ */
+#define PCI_DEVICE_CLASS(dev_class, dev_class_mask) \
+       .class = (dev_class), .class_mask = (dev_class_mask), \
+       .vendor = PCI_ANY_ID, .device = PCI_ANY_ID, \
+       .subvendor = PCI_ANY_ID, .subdevice = PCI_ANY_ID
+
+/**
+ * PCI_VDEVICE - macro used to describe a specific pci device in short form
+ * @vend: the vendor name
+ * @dev: the 16 bit PCI Device ID
+ *
+ * This macro is used to create a struct pci_device_id that matches a
+ * specific PCI device.  The subvendor, and subdevice fields will be set
+ * to PCI_ANY_ID. The macro allows the next field to follow as the device
+ * private data.
+ */
+
+#define PCI_VDEVICE(vend, dev) \
+       .vendor = PCI_VENDOR_ID_##vend, .device = (dev), \
+       .subvendor = PCI_ANY_ID, .subdevice = PCI_ANY_ID, 0, 0
+
+/**
+ * struct pci_driver_entry - Matches a driver to its pci_device_id list
+ * @driver: Driver to use
+ * @match: List of match records for this driver, terminated by {}
+ */
+struct pci_driver_entry {
+       struct driver *driver;
+       const struct pci_device_id *match;
+};
+
+#define U_BOOT_PCI_DEVICE(__name, __match)                             \

include/pci.h struct dm_pci_emul_ops {

Quoted 10 lines
include/pci.h +6 -1
+       ll_entry_declare(struct pci_driver_entry, __name, pci_driver_entry) = {\
+               .driver = llsym(struct driver, __name, driver), \
+               .match = __match, \
+               }
+
+#endif /* CONFIG_DM_PCI */
 
 #endif /* __ASSEMBLY__ */
 #endif /* _PCI_H */
--

Do you have plan to address the issue that dm pci cannot be used in the pre-reloc phase? Like we need pci uart as the U-Boot console.

Regards,
Bin

#14 lore raw

PATCH 5/18 dm: pci: Add support for PCI driver matching

Hi Bin,

Quoted 195 lines · Bin Meng

Hi Simon,

Quoted 179 lines · Simon Glass

At present all PCI devices must be present in the device tree in order to be used. Many or most PCI devices don't require any configuration other than that which is done automatically by U-Boot. It is inefficent to add a node with nothing but a compatible string in order to get a device working.

Add a mechanism whereby PCI drivers can be declared along with the device parameters they support (vendor/device/class). When no suitable driver is found in the device tree the list of such devices is consulted to determine the correct driver. If this also fails, then a generic driver is used as before.

The mechanism used is very similar to that provided by Linux and the header file defintions are copied from Linux 4.1.

---

 drivers/pci/pci-uclass.c | 129 ++++++++++++++++++++++++++++++++++++++++++-----
 include/pci.h            |  79 ++++++++++++++++++++++++++++-
 2 files changed, 193 insertions(+), 15 deletions(-)
drivers/pci/pci-uclass.c +116 -13
index 5b91fe3..41daa0d 100644
@@ -353,6 +353,101 @@ int dm_pci_hose_probe_bus(struct pci_controller *hose, pci_dev_t bdf)
        return sub_bus;
 }
 
+/**
+ * pci_match_one_device - Tell if a PCI device structure has a matching
+ *                        PCI device id structure
+ * @id: single PCI device id structure to match
+ * @dev: the PCI device structure to match against
+ *
+ * Returns the matching pci_device_id structure or %NULL if there is no match.
+ */
+static bool pci_match_one_id(const struct pci_device_id *id,
+                            const struct pci_device_id *find)
+{
+       if ((id->vendor == PCI_ANY_ID || id->vendor == find->vendor) &&
+           (id->device == PCI_ANY_ID || id->device == find->device) &&
+           (id->subvendor == PCI_ANY_ID || id->subvendor == find->subvendor) &&
+           (id->subdevice == PCI_ANY_ID || id->subdevice == find->subdevice) &&
+           !((id->class ^ find->class) & id->class_mask))
+               return true;
+
+       return false;
+}
+
+/**
+ * pci_find_and_bind_driver() - Find and bind the right PCI driver
+ *
+ * This only looks at certain fields in the descriptor.
+ */
+static int pci_find_and_bind_driver(struct udevice *parent,
+                                   struct pci_device_id *find_id, int devfn,
+                                   struct udevice **devp)
+{
+       struct pci_driver_entry *start, *entry;
+       const char *drv;
+       int n_ents;
+       int ret;
+       char name[30], *str;
+
+       *devp = NULL;
+
+       debug("%s: Searching for driver: vendor=%x, device=%x\n", __func__,
+             find_id->vendor, find_id->device);
+       start = ll_entry_start(struct pci_driver_entry, pci_driver_entry);
+       n_ents = ll_entry_count(struct pci_driver_entry, pci_driver_entry);
+       for (entry = start; entry != start + n_ents; entry++) {
+               const struct pci_device_id *id;
+               struct udevice *dev;
+               const struct driver *drv;
+
+               for (id = entry->match;
+                    id->vendor || id->subvendor || id->class_mask;
+                    id++) {
+                       if (!pci_match_one_id(id, find_id))
+                               continue;
+
+                       drv = entry->driver;
+                       /*
+                        * We could pass the descriptor to the driver as
+                        * platdata (instead of NULL) and allow its bind()
+                        * method to return -ENOENT if it doesn't support this
+                        * device. That way we could continue the search to
+                        * find another driver. For now this doesn't seem
+                        * necesssary, so just bind the first match.
+                        */
+                       ret = device_bind(parent, drv, drv->name, NULL, -1,
+                                         &dev);
+                       if (ret)
+                               goto error;
+                       debug("%s: Match found: %s\n", __func__, drv->name);
+                       dev->driver_data = find_id->driver_data;
+                       *devp = dev;
+                       return 0;
+               }
+       }
+
+       /* Bind a generic driver so that the device can be used */
+       sprintf(name, "pci_%x:%x.%x", parent->seq, PCI_DEV(devfn),
+               PCI_FUNC(devfn));
+       str = strdup(name);
+       if (!str)
+               return -ENOMEM;
+       drv = (find_id->class >> 8) == PCI_CLASS_BRIDGE_PCI ? "pci_bridge_drv" :
+                       "pci_generic_drv";
+       ret = device_bind_driver(parent, drv, str, devp);
+       if (ret) {
+               debug("%s: Failed to bind generic driver: %d", __func__, ret);
+               return ret;
+       }
+       debug("%s: No match found: bound generic driver instead\n", __func__);
+
+       return 0;
+
+error:
+       debug("%s: No match found: error %d\n", __func__, ret);
+       return ret;
+}
+
 int pci_bind_bus_devices(struct udevice *bus)
 {
        ulong vendor, device;
@@ -387,25 +482,33 @@ int pci_bind_bus_devices(struct udevice *bus)
                      bus->seq, bus->name, PCI_DEV(devfn), PCI_FUNC(devfn));
                pci_bus_read_config(bus, devfn, PCI_DEVICE_ID, &device,
                                    PCI_SIZE_16);
-               pci_bus_read_config(bus, devfn, PCI_CLASS_DEVICE, &class,
-                                   PCI_SIZE_16);
+               pci_bus_read_config(bus, devfn, PCI_CLASS_REVISION, &class,
+                                   PCI_SIZE_32);
+               class >>= 8;
 
                /* Find this device in the device tree */
                ret = pci_bus_find_devfn(bus, devfn, &dev);
 
+               /* Search for a driver */
+
                /* If nothing in the device tree, bind a generic device */
                if (ret == -ENODEV) {
-                       char name[30], *str;
-                       const char *drv;
-
-                       sprintf(name, "pci_%x:%x.%x", bus->seq,
-                               PCI_DEV(devfn), PCI_FUNC(devfn));
-                       str = strdup(name);
-                       if (!str)
-                               return -ENOMEM;
-                       drv = class == PCI_CLASS_BRIDGE_PCI ?
-                               "pci_bridge_drv" : "pci_generic_drv";
-                       ret = device_bind_driver(bus, drv, str, &dev);
+                       struct pci_device_id find_id;
+                       ulong val;
+
+                       memset(&find_id, '\0', sizeof(find_id));
+                       find_id.vendor = vendor;
+                       find_id.device = device;
+                       find_id.class = class;
+                       if ((header_type & 0x7f) == PCI_HEADER_TYPE_NORMAL) {
+                               pci_bus_read_config(bus, devfn,
+                                                   PCI_SUBSYSTEM_VENDOR_ID,
+                                                   &val, PCI_SIZE_32);
+                               find_id.subvendor = val & 0xffff;
+                               find_id.subdevice = val >> 16;
+                       }
+                       ret = pci_find_and_bind_driver(bus, &find_id, devfn,
+                                                      &dev);
                }
                if (ret)
                        return ret;
include/pci.h +0 -0
index 3af511b..d21fa8b 100644

include/pci.h typedef int pci_dev_t; · line 475

Quoted 9 lines
include/pci.h +4 -1
@@ -468,7 +468,10 @@ typedef int pci_dev_t;
 #define PCI_ANY_ID             (~0)
 
 struct pci_device_id {
-       unsigned int vendor, device;            /* Vendor and device ID or PCI_ANY_ID */
+       unsigned int vendor, device;    /* Vendor and device ID or PCI_ANY_ID */
+       unsigned int subvendor, subdevice; /* Subsystem ID's or PCI_ANY_ID */
+       unsigned int class, class_mask; /* (class,subclass,prog-if) triplet */
+       unsigned long driver_data;      /* Data private to the driver */
 };

Can we create another structure for dm? There are lots of codes which only defines vendor id and device id like below:

Quoted 6 lines

static struct pci_device_id mmc_supported[] = {
{ PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_TCF_SDIO_0 }, { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_TCF_SDIO_1 },
};

Although compiler does not generate any error or warning, it is confusing.

I think the existing structure is for exactly this purpose, - I have just added a few new fields. Why do we want to change it? I also really want to use this name as it matches Linux.

Quoted 82 lines
Quoted 82 lines
include/pci.h +73 -1
 struct pci_controller;
@@ -1110,7 +1113,79 @@ struct dm_pci_emul_ops {
 int sandbox_pci_get_emul(struct udevice *bus, pci_dev_t find_devfn,
                         struct udevice **emulp);
 
-#endif
+/**
+ * PCI_DEVICE - macro used to describe a specific pci device
+ * @vend: the 16 bit PCI Vendor ID
+ * @dev: the 16 bit PCI Device ID
+ *
+ * This macro is used to create a struct pci_device_id that matches a
+ * specific device.  The subvendor and subdevice fields will be set to
+ * PCI_ANY_ID.
+ */
+#define PCI_DEVICE(vend, dev) \
+       .vendor = (vend), .device = (dev), \
+       .subvendor = PCI_ANY_ID, .subdevice = PCI_ANY_ID
+
+/**
+ * PCI_DEVICE_SUB - macro used to describe a specific pci device with subsystem
+ * @vend: the 16 bit PCI Vendor ID
+ * @dev: the 16 bit PCI Device ID
+ * @subvend: the 16 bit PCI Subvendor ID
+ * @subdev: the 16 bit PCI Subdevice ID
+ *
+ * This macro is used to create a struct pci_device_id that matches a
+ * specific device with subsystem information.
+ */
+#define PCI_DEVICE_SUB(vend, dev, subvend, subdev) \
+       .vendor = (vend), .device = (dev), \
+       .subvendor = (subvend), .subdevice = (subdev)
+
+/**
+ * PCI_DEVICE_CLASS - macro used to describe a specific pci device class
+ * @dev_class: the class, subclass, prog-if triple for this device
+ * @dev_class_mask: the class mask for this device
+ *
+ * This macro is used to create a struct pci_device_id that matches a
+ * specific PCI class.  The vendor, device, subvendor, and subdevice
+ * fields will be set to PCI_ANY_ID.
+ */
+#define PCI_DEVICE_CLASS(dev_class, dev_class_mask) \
+       .class = (dev_class), .class_mask = (dev_class_mask), \
+       .vendor = PCI_ANY_ID, .device = PCI_ANY_ID, \
+       .subvendor = PCI_ANY_ID, .subdevice = PCI_ANY_ID
+
+/**
+ * PCI_VDEVICE - macro used to describe a specific pci device in short form
+ * @vend: the vendor name
+ * @dev: the 16 bit PCI Device ID
+ *
+ * This macro is used to create a struct pci_device_id that matches a
+ * specific PCI device.  The subvendor, and subdevice fields will be set
+ * to PCI_ANY_ID. The macro allows the next field to follow as the device
+ * private data.
+ */
+
+#define PCI_VDEVICE(vend, dev) \
+       .vendor = PCI_VENDOR_ID_##vend, .device = (dev), \
+       .subvendor = PCI_ANY_ID, .subdevice = PCI_ANY_ID, 0, 0
+
+/**
+ * struct pci_driver_entry - Matches a driver to its pci_device_id list
+ * @driver: Driver to use
+ * @match: List of match records for this driver, terminated by {}
+ */
+struct pci_driver_entry {
+       struct driver *driver;
+       const struct pci_device_id *match;
+};
+
+#define U_BOOT_PCI_DEVICE(__name, __match)                             \
+       ll_entry_declare(struct pci_driver_entry, __name, pci_driver_entry) = {\
+               .driver = llsym(struct driver, __name, driver), \
+               .match = __match, \
+               }
+
+#endif /* CONFIG_DM_PCI */
 
Quoted 6 lines

include/pci.h struct dm_pci_emul_ops {

Quoted 3 lines
include/pci.h +0 -1
 #endif /* __ASSEMBLY__ */
 #endif /* _PCI_H */
--

Do you have plan to address the issue that dm pci cannot be used in the pre-reloc phase? Like we need pci uart as the U-Boot console.

Regards,
Simon

#15 lore raw

PATCH 5/18 dm: pci: Add support for PCI driver matching

Hi Simon,

Quoted 203 lines · Simon Glass

Hi Bin,

Quoted 200 lines · Bin Meng

Hi Simon,

Quoted 179 lines · Simon Glass

At present all PCI devices must be present in the device tree in order to be used. Many or most PCI devices don't require any configuration other than that which is done automatically by U-Boot. It is inefficent to add a node with nothing but a compatible string in order to get a device working.

Add a mechanism whereby PCI drivers can be declared along with the device parameters they support (vendor/device/class). When no suitable driver is found in the device tree the list of such devices is consulted to determine the correct driver. If this also fails, then a generic driver is used as before.

The mechanism used is very similar to that provided by Linux and the header file defintions are copied from Linux 4.1.

---

 drivers/pci/pci-uclass.c | 129 ++++++++++++++++++++++++++++++++++++++++++-----
 include/pci.h            |  79 ++++++++++++++++++++++++++++-
 2 files changed, 193 insertions(+), 15 deletions(-)
drivers/pci/pci-uclass.c +116 -13
index 5b91fe3..41daa0d 100644
@@ -353,6 +353,101 @@ int dm_pci_hose_probe_bus(struct pci_controller *hose, pci_dev_t bdf)
        return sub_bus;
 }
 
+/**
+ * pci_match_one_device - Tell if a PCI device structure has a matching
+ *                        PCI device id structure
+ * @id: single PCI device id structure to match
+ * @dev: the PCI device structure to match against
+ *
+ * Returns the matching pci_device_id structure or %NULL if there is no match.
+ */
+static bool pci_match_one_id(const struct pci_device_id *id,
+                            const struct pci_device_id *find)
+{
+       if ((id->vendor == PCI_ANY_ID || id->vendor == find->vendor) &&
+           (id->device == PCI_ANY_ID || id->device == find->device) &&
+           (id->subvendor == PCI_ANY_ID || id->subvendor == find->subvendor) &&
+           (id->subdevice == PCI_ANY_ID || id->subdevice == find->subdevice) &&
+           !((id->class ^ find->class) & id->class_mask))
+               return true;
+
+       return false;
+}
+
+/**
+ * pci_find_and_bind_driver() - Find and bind the right PCI driver
+ *
+ * This only looks at certain fields in the descriptor.
+ */
+static int pci_find_and_bind_driver(struct udevice *parent,
+                                   struct pci_device_id *find_id, int devfn,
+                                   struct udevice **devp)
+{
+       struct pci_driver_entry *start, *entry;
+       const char *drv;
+       int n_ents;
+       int ret;
+       char name[30], *str;
+
+       *devp = NULL;
+
+       debug("%s: Searching for driver: vendor=%x, device=%x\n", __func__,
+             find_id->vendor, find_id->device);
+       start = ll_entry_start(struct pci_driver_entry, pci_driver_entry);
+       n_ents = ll_entry_count(struct pci_driver_entry, pci_driver_entry);
+       for (entry = start; entry != start + n_ents; entry++) {
+               const struct pci_device_id *id;
+               struct udevice *dev;
+               const struct driver *drv;
+
+               for (id = entry->match;
+                    id->vendor || id->subvendor || id->class_mask;
+                    id++) {
+                       if (!pci_match_one_id(id, find_id))
+                               continue;
+
+                       drv = entry->driver;
+                       /*
+                        * We could pass the descriptor to the driver as
+                        * platdata (instead of NULL) and allow its bind()
+                        * method to return -ENOENT if it doesn't support this
+                        * device. That way we could continue the search to
+                        * find another driver. For now this doesn't seem
+                        * necesssary, so just bind the first match.
+                        */
+                       ret = device_bind(parent, drv, drv->name, NULL, -1,
+                                         &dev);
+                       if (ret)
+                               goto error;
+                       debug("%s: Match found: %s\n", __func__, drv->name);
+                       dev->driver_data = find_id->driver_data;
+                       *devp = dev;
+                       return 0;
+               }
+       }
+
+       /* Bind a generic driver so that the device can be used */
+       sprintf(name, "pci_%x:%x.%x", parent->seq, PCI_DEV(devfn),
+               PCI_FUNC(devfn));
+       str = strdup(name);
+       if (!str)
+               return -ENOMEM;
+       drv = (find_id->class >> 8) == PCI_CLASS_BRIDGE_PCI ? "pci_bridge_drv" :
+                       "pci_generic_drv";
+       ret = device_bind_driver(parent, drv, str, devp);
+       if (ret) {
+               debug("%s: Failed to bind generic driver: %d", __func__, ret);
+               return ret;
+       }
+       debug("%s: No match found: bound generic driver instead\n", __func__);
+
+       return 0;
+
+error:
+       debug("%s: No match found: error %d\n", __func__, ret);
+       return ret;
+}
+
 int pci_bind_bus_devices(struct udevice *bus)
 {
        ulong vendor, device;
@@ -387,25 +482,33 @@ int pci_bind_bus_devices(struct udevice *bus)
                      bus->seq, bus->name, PCI_DEV(devfn), PCI_FUNC(devfn));
                pci_bus_read_config(bus, devfn, PCI_DEVICE_ID, &device,
                                    PCI_SIZE_16);
-               pci_bus_read_config(bus, devfn, PCI_CLASS_DEVICE, &class,
-                                   PCI_SIZE_16);
+               pci_bus_read_config(bus, devfn, PCI_CLASS_REVISION, &class,
+                                   PCI_SIZE_32);
+               class >>= 8;
 
                /* Find this device in the device tree */
                ret = pci_bus_find_devfn(bus, devfn, &dev);
 
+               /* Search for a driver */
+
                /* If nothing in the device tree, bind a generic device */
                if (ret == -ENODEV) {
-                       char name[30], *str;
-                       const char *drv;
-
-                       sprintf(name, "pci_%x:%x.%x", bus->seq,
-                               PCI_DEV(devfn), PCI_FUNC(devfn));
-                       str = strdup(name);
-                       if (!str)
-                               return -ENOMEM;
-                       drv = class == PCI_CLASS_BRIDGE_PCI ?
-                               "pci_bridge_drv" : "pci_generic_drv";
-                       ret = device_bind_driver(bus, drv, str, &dev);
+                       struct pci_device_id find_id;
+                       ulong val;
+
+                       memset(&find_id, '\0', sizeof(find_id));
+                       find_id.vendor = vendor;
+                       find_id.device = device;
+                       find_id.class = class;
+                       if ((header_type & 0x7f) == PCI_HEADER_TYPE_NORMAL) {
+                               pci_bus_read_config(bus, devfn,
+                                                   PCI_SUBSYSTEM_VENDOR_ID,
+                                                   &val, PCI_SIZE_32);
+                               find_id.subvendor = val & 0xffff;
+                               find_id.subdevice = val >> 16;
+                       }
+                       ret = pci_find_and_bind_driver(bus, &find_id, devfn,
+                                                      &dev);
                }
                if (ret)
                        return ret;
include/pci.h +0 -0
index 3af511b..d21fa8b 100644

include/pci.h typedef int pci_dev_t; · line 475

Quoted 9 lines
include/pci.h +4 -1
@@ -468,7 +468,10 @@ typedef int pci_dev_t;
 #define PCI_ANY_ID             (~0)
 
 struct pci_device_id {
-       unsigned int vendor, device;            /* Vendor and device ID or PCI_ANY_ID */
+       unsigned int vendor, device;    /* Vendor and device ID or PCI_ANY_ID */
+       unsigned int subvendor, subdevice; /* Subsystem ID's or PCI_ANY_ID */
+       unsigned int class, class_mask; /* (class,subclass,prog-if) triplet */
+       unsigned long driver_data;      /* Data private to the driver */
 };

Can we create another structure for dm? There are lots of codes which only defines vendor id and device id like below:

static struct pci_device_id mmc_supported[] = {
{ PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_TCF_SDIO_0 }, { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_TCF_SDIO_1 },
};

Quoted 6 lines
Quoted 1 line

Although compiler does not generate any error or warning, it is confusing.

I think the existing structure is for exactly this purpose, - I have just added a few new fields. Why do we want to change it? I also really want to use this name as it matches Linux.

I mean it creates confusion if existing codes are unchanged, which makes me think it contains only a vendor id and a device id for each structure, but actually it has more members which are omitted. I would do:

static struct pci_device_id mmc_supported[] = {
{ .vendor = PCI_VENDOR_ID_INTEL, .device =
PCI_DEVICE_ID_INTEL_TCF_SDIO_0 },
{ .vendor = PCI_VENDOR_ID_INTEL, .device =
PCI_DEVICE_ID_INTEL_TCF_SDIO_1 },

And one more comment at the end.

Quoted 82 lines
Quoted 82 lines
Quoted 82 lines
include/pci.h +73 -1
 struct pci_controller;
@@ -1110,7 +1113,79 @@ struct dm_pci_emul_ops {
 int sandbox_pci_get_emul(struct udevice *bus, pci_dev_t find_devfn,
                         struct udevice **emulp);
 
-#endif
+/**
+ * PCI_DEVICE - macro used to describe a specific pci device
+ * @vend: the 16 bit PCI Vendor ID
+ * @dev: the 16 bit PCI Device ID
+ *
+ * This macro is used to create a struct pci_device_id that matches a
+ * specific device.  The subvendor and subdevice fields will be set to
+ * PCI_ANY_ID.
+ */
+#define PCI_DEVICE(vend, dev) \
+       .vendor = (vend), .device = (dev), \
+       .subvendor = PCI_ANY_ID, .subdevice = PCI_ANY_ID
+
+/**
+ * PCI_DEVICE_SUB - macro used to describe a specific pci device with subsystem
+ * @vend: the 16 bit PCI Vendor ID
+ * @dev: the 16 bit PCI Device ID
+ * @subvend: the 16 bit PCI Subvendor ID
+ * @subdev: the 16 bit PCI Subdevice ID
+ *
+ * This macro is used to create a struct pci_device_id that matches a
+ * specific device with subsystem information.
+ */
+#define PCI_DEVICE_SUB(vend, dev, subvend, subdev) \
+       .vendor = (vend), .device = (dev), \
+       .subvendor = (subvend), .subdevice = (subdev)
+
+/**
+ * PCI_DEVICE_CLASS - macro used to describe a specific pci device class
+ * @dev_class: the class, subclass, prog-if triple for this device
+ * @dev_class_mask: the class mask for this device
+ *
+ * This macro is used to create a struct pci_device_id that matches a
+ * specific PCI class.  The vendor, device, subvendor, and subdevice
+ * fields will be set to PCI_ANY_ID.
+ */
+#define PCI_DEVICE_CLASS(dev_class, dev_class_mask) \
+       .class = (dev_class), .class_mask = (dev_class_mask), \
+       .vendor = PCI_ANY_ID, .device = PCI_ANY_ID, \
+       .subvendor = PCI_ANY_ID, .subdevice = PCI_ANY_ID
+
+/**
+ * PCI_VDEVICE - macro used to describe a specific pci device in short form
+ * @vend: the vendor name
+ * @dev: the 16 bit PCI Device ID
+ *
+ * This macro is used to create a struct pci_device_id that matches a
+ * specific PCI device.  The subvendor, and subdevice fields will be set
+ * to PCI_ANY_ID. The macro allows the next field to follow as the device
+ * private data.
+ */
+
+#define PCI_VDEVICE(vend, dev) \
+       .vendor = PCI_VENDOR_ID_##vend, .device = (dev), \
+       .subvendor = PCI_ANY_ID, .subdevice = PCI_ANY_ID, 0, 0
+
+/**
+ * struct pci_driver_entry - Matches a driver to its pci_device_id list
+ * @driver: Driver to use
+ * @match: List of match records for this driver, terminated by {}
+ */
+struct pci_driver_entry {
+       struct driver *driver;
+       const struct pci_device_id *match;
+};
+
+#define U_BOOT_PCI_DEVICE(__name, __match)                             \
+       ll_entry_declare(struct pci_driver_entry, __name, pci_driver_entry) = {\
+               .driver = llsym(struct driver, __name, driver), \
+               .match = __match, \
+               }
+
+#endif /* CONFIG_DM_PCI */
 
Quoted 7 lines
Quoted 6 lines

include/pci.h struct dm_pci_emul_ops {

Quoted 3 lines
include/pci.h +0 -1
 #endif /* __ASSEMBLY__ */
 #endif /* _PCI_H */
--

Do you have plan to address the issue that dm pci cannot be used in the pre-reloc phase? Like we need pci uart as the U-Boot console.

Do you have plan to address the issue that dm pci cannot be used in the pre-reloc phase? Like we need pci uart as the U-Boot console.

Regards,
Bin

#16 lore raw

PATCH 5/18 dm: pci: Add support for PCI driver matching

Hi Bin,

Quoted 218 lines · Bin Meng

Hi Simon,

Quoted 203 lines · Simon Glass

Hi Bin,

Quoted 200 lines · Bin Meng

Hi Simon,

Quoted 179 lines · Simon Glass

At present all PCI devices must be present in the device tree in order to be used. Many or most PCI devices don't require any configuration other than that which is done automatically by U-Boot. It is inefficent to add a node with nothing but a compatible string in order to get a device working.

Add a mechanism whereby PCI drivers can be declared along with the device parameters they support (vendor/device/class). When no suitable driver is found in the device tree the list of such devices is consulted to determine the correct driver. If this also fails, then a generic driver is used as before.

The mechanism used is very similar to that provided by Linux and the header file defintions are copied from Linux 4.1.

---

 drivers/pci/pci-uclass.c | 129 ++++++++++++++++++++++++++++++++++++++++++-----
 include/pci.h            |  79 ++++++++++++++++++++++++++++-
 2 files changed, 193 insertions(+), 15 deletions(-)
drivers/pci/pci-uclass.c +116 -13
index 5b91fe3..41daa0d 100644
@@ -353,6 +353,101 @@ int dm_pci_hose_probe_bus(struct pci_controller *hose, pci_dev_t bdf)
        return sub_bus;
 }
 
+/**
+ * pci_match_one_device - Tell if a PCI device structure has a matching
+ *                        PCI device id structure
+ * @id: single PCI device id structure to match
+ * @dev: the PCI device structure to match against
+ *
+ * Returns the matching pci_device_id structure or %NULL if there is no match.
+ */
+static bool pci_match_one_id(const struct pci_device_id *id,
+                            const struct pci_device_id *find)
+{
+       if ((id->vendor == PCI_ANY_ID || id->vendor == find->vendor) &&
+           (id->device == PCI_ANY_ID || id->device == find->device) &&
+           (id->subvendor == PCI_ANY_ID || id->subvendor == find->subvendor) &&
+           (id->subdevice == PCI_ANY_ID || id->subdevice == find->subdevice) &&
+           !((id->class ^ find->class) & id->class_mask))
+               return true;
+
+       return false;
+}
+
+/**
+ * pci_find_and_bind_driver() - Find and bind the right PCI driver
+ *
+ * This only looks at certain fields in the descriptor.
+ */
+static int pci_find_and_bind_driver(struct udevice *parent,
+                                   struct pci_device_id *find_id, int devfn,
+                                   struct udevice **devp)
+{
+       struct pci_driver_entry *start, *entry;
+       const char *drv;
+       int n_ents;
+       int ret;
+       char name[30], *str;
+
+       *devp = NULL;
+
+       debug("%s: Searching for driver: vendor=%x, device=%x\n", __func__,
+             find_id->vendor, find_id->device);
+       start = ll_entry_start(struct pci_driver_entry, pci_driver_entry);
+       n_ents = ll_entry_count(struct pci_driver_entry, pci_driver_entry);
+       for (entry = start; entry != start + n_ents; entry++) {
+               const struct pci_device_id *id;
+               struct udevice *dev;
+               const struct driver *drv;
+
+               for (id = entry->match;
+                    id->vendor || id->subvendor || id->class_mask;
+                    id++) {
+                       if (!pci_match_one_id(id, find_id))
+                               continue;
+
+                       drv = entry->driver;
+                       /*
+                        * We could pass the descriptor to the driver as
+                        * platdata (instead of NULL) and allow its bind()
+                        * method to return -ENOENT if it doesn't support this
+                        * device. That way we could continue the search to
+                        * find another driver. For now this doesn't seem
+                        * necesssary, so just bind the first match.
+                        */
+                       ret = device_bind(parent, drv, drv->name, NULL, -1,
+                                         &dev);
+                       if (ret)
+                               goto error;
+                       debug("%s: Match found: %s\n", __func__, drv->name);
+                       dev->driver_data = find_id->driver_data;
+                       *devp = dev;
+                       return 0;
+               }
+       }
+
+       /* Bind a generic driver so that the device can be used */
+       sprintf(name, "pci_%x:%x.%x", parent->seq, PCI_DEV(devfn),
+               PCI_FUNC(devfn));
+       str = strdup(name);
+       if (!str)
+               return -ENOMEM;
+       drv = (find_id->class >> 8) == PCI_CLASS_BRIDGE_PCI ? "pci_bridge_drv" :
+                       "pci_generic_drv";
+       ret = device_bind_driver(parent, drv, str, devp);
+       if (ret) {
+               debug("%s: Failed to bind generic driver: %d", __func__, ret);
+               return ret;
+       }
+       debug("%s: No match found: bound generic driver instead\n", __func__);
+
+       return 0;
+
+error:
+       debug("%s: No match found: error %d\n", __func__, ret);
+       return ret;
+}
+
 int pci_bind_bus_devices(struct udevice *bus)
 {
        ulong vendor, device;
@@ -387,25 +482,33 @@ int pci_bind_bus_devices(struct udevice *bus)
                      bus->seq, bus->name, PCI_DEV(devfn), PCI_FUNC(devfn));
                pci_bus_read_config(bus, devfn, PCI_DEVICE_ID, &device,
                                    PCI_SIZE_16);
-               pci_bus_read_config(bus, devfn, PCI_CLASS_DEVICE, &class,
-                                   PCI_SIZE_16);
+               pci_bus_read_config(bus, devfn, PCI_CLASS_REVISION, &class,
+                                   PCI_SIZE_32);
+               class >>= 8;
 
                /* Find this device in the device tree */
                ret = pci_bus_find_devfn(bus, devfn, &dev);
 
+               /* Search for a driver */
+
                /* If nothing in the device tree, bind a generic device */
                if (ret == -ENODEV) {
-                       char name[30], *str;
-                       const char *drv;
-
-                       sprintf(name, "pci_%x:%x.%x", bus->seq,
-                               PCI_DEV(devfn), PCI_FUNC(devfn));
-                       str = strdup(name);
-                       if (!str)
-                               return -ENOMEM;
-                       drv = class == PCI_CLASS_BRIDGE_PCI ?
-                               "pci_bridge_drv" : "pci_generic_drv";
-                       ret = device_bind_driver(bus, drv, str, &dev);
+                       struct pci_device_id find_id;
+                       ulong val;
+
+                       memset(&find_id, '\0', sizeof(find_id));
+                       find_id.vendor = vendor;
+                       find_id.device = device;
+                       find_id.class = class;
+                       if ((header_type & 0x7f) == PCI_HEADER_TYPE_NORMAL) {
+                               pci_bus_read_config(bus, devfn,
+                                                   PCI_SUBSYSTEM_VENDOR_ID,
+                                                   &val, PCI_SIZE_32);
+                               find_id.subvendor = val & 0xffff;
+                               find_id.subdevice = val >> 16;
+                       }
+                       ret = pci_find_and_bind_driver(bus, &find_id, devfn,
+                                                      &dev);
                }
                if (ret)
                        return ret;
include/pci.h +0 -0
index 3af511b..d21fa8b 100644

include/pci.h typedef int pci_dev_t; · line 475

Quoted 9 lines
include/pci.h +4 -1
@@ -468,7 +468,10 @@ typedef int pci_dev_t;
 #define PCI_ANY_ID             (~0)
 
 struct pci_device_id {
-       unsigned int vendor, device;            /* Vendor and device ID or PCI_ANY_ID */
+       unsigned int vendor, device;    /* Vendor and device ID or PCI_ANY_ID */
+       unsigned int subvendor, subdevice; /* Subsystem ID's or PCI_ANY_ID */
+       unsigned int class, class_mask; /* (class,subclass,prog-if) triplet */
+       unsigned long driver_data;      /* Data private to the driver */
 };

Can we create another structure for dm? There are lots of codes which only defines vendor id and device id like below:

static struct pci_device_id mmc_supported[] = {
{ PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_TCF_SDIO_0 }, { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_TCF_SDIO_1 },
};

Quoted 6 lines
Quoted 1 line

Although compiler does not generate any error or warning, it is confusing.

I think the existing structure is for exactly this purpose, - I have just added a few new fields. Why do we want to change it? I also really want to use this name as it matches Linux.

I mean it creates confusion if existing codes are unchanged, which makes me think it contains only a vendor id and a device id for each structure, but actually it has more members which are omitted. I would do:

Quoted 7 lines

static struct pci_device_id mmc_supported[] = {
{ .vendor = PCI_VENDOR_ID_INTEL, .device =
PCI_DEVICE_ID_INTEL_TCF_SDIO_0 },
{ .vendor = PCI_VENDOR_ID_INTEL, .device =
PCI_DEVICE_ID_INTEL_TCF_SDIO_1 },

And one more comment at the end.

OK I see. Well I can do this as a follow-on patch.

Quoted 91 lines
Quoted 83 lines
Quoted 83 lines
Quoted 83 lines
include/pci.h +73 -1
 struct pci_controller;
@@ -1110,7 +1113,79 @@ struct dm_pci_emul_ops {
 int sandbox_pci_get_emul(struct udevice *bus, pci_dev_t find_devfn,
                         struct udevice **emulp);
 
-#endif
+/**
+ * PCI_DEVICE - macro used to describe a specific pci device
+ * @vend: the 16 bit PCI Vendor ID
+ * @dev: the 16 bit PCI Device ID
+ *
+ * This macro is used to create a struct pci_device_id that matches a
+ * specific device.  The subvendor and subdevice fields will be set to
+ * PCI_ANY_ID.
+ */
+#define PCI_DEVICE(vend, dev) \
+       .vendor = (vend), .device = (dev), \
+       .subvendor = PCI_ANY_ID, .subdevice = PCI_ANY_ID
+
+/**
+ * PCI_DEVICE_SUB - macro used to describe a specific pci device with subsystem
+ * @vend: the 16 bit PCI Vendor ID
+ * @dev: the 16 bit PCI Device ID
+ * @subvend: the 16 bit PCI Subvendor ID
+ * @subdev: the 16 bit PCI Subdevice ID
+ *
+ * This macro is used to create a struct pci_device_id that matches a
+ * specific device with subsystem information.
+ */
+#define PCI_DEVICE_SUB(vend, dev, subvend, subdev) \
+       .vendor = (vend), .device = (dev), \
+       .subvendor = (subvend), .subdevice = (subdev)
+
+/**
+ * PCI_DEVICE_CLASS - macro used to describe a specific pci device class
+ * @dev_class: the class, subclass, prog-if triple for this device
+ * @dev_class_mask: the class mask for this device
+ *
+ * This macro is used to create a struct pci_device_id that matches a
+ * specific PCI class.  The vendor, device, subvendor, and subdevice
+ * fields will be set to PCI_ANY_ID.
+ */
+#define PCI_DEVICE_CLASS(dev_class, dev_class_mask) \
+       .class = (dev_class), .class_mask = (dev_class_mask), \
+       .vendor = PCI_ANY_ID, .device = PCI_ANY_ID, \
+       .subvendor = PCI_ANY_ID, .subdevice = PCI_ANY_ID
+
+/**
+ * PCI_VDEVICE - macro used to describe a specific pci device in short form
+ * @vend: the vendor name
+ * @dev: the 16 bit PCI Device ID
+ *
+ * This macro is used to create a struct pci_device_id that matches a
+ * specific PCI device.  The subvendor, and subdevice fields will be set
+ * to PCI_ANY_ID. The macro allows the next field to follow as the device
+ * private data.
+ */
+
+#define PCI_VDEVICE(vend, dev) \
+       .vendor = PCI_VENDOR_ID_##vend, .device = (dev), \
+       .subvendor = PCI_ANY_ID, .subdevice = PCI_ANY_ID, 0, 0
+
+/**
+ * struct pci_driver_entry - Matches a driver to its pci_device_id list
+ * @driver: Driver to use
+ * @match: List of match records for this driver, terminated by {}
+ */
+struct pci_driver_entry {
+       struct driver *driver;
+       const struct pci_device_id *match;
+};
+
+#define U_BOOT_PCI_DEVICE(__name, __match)                             \
+       ll_entry_declare(struct pci_driver_entry, __name, pci_driver_entry) = {\
+               .driver = llsym(struct driver, __name, driver), \
+               .match = __match, \
+               }
+
+#endif /* CONFIG_DM_PCI */
 
Quoted 7 lines
Quoted 6 lines

include/pci.h struct dm_pci_emul_ops {

Quoted 3 lines
include/pci.h +0 -1
 #endif /* __ASSEMBLY__ */
 #endif /* _PCI_H */
--

Do you have plan to address the issue that dm pci cannot be used in the pre-reloc phase? Like we need pci uart as the U-Boot console.

Quoted 2 lines

Do you have plan to address the issue that dm pci cannot be used in the pre-reloc phase? Like we need pci uart as the U-Boot console.

I suspect it can if we put it in the device tree. Is that good enough?

Applied to u-boot-dm.

Regards,
Simon

#17 lore raw

PATCH 6/18 dm: eth: Add driver-model support to the rtl8169 driver

In reply to Simon Glass

This driver is used by the Intel Minnowmax board. Convert it to driver model so it can use the new Ethernet implementation.

---

 drivers/net/rtl8169.c | 236 +++++++++++++++++++++++++++++++++++++++-----------
 1 file changed, 187 insertions(+), 49 deletions(-)
drivers/net/rtl8169.c +187 -49
index 958488c..7b6e20f 100644
@@ -41,10 +41,13 @@
  * Modified to use le32_to_cpu and cpu_to_le32 properly
  */
 #include <common.h>
+#include <dm.h>
 #include <errno.h>
 #include <malloc.h>
 #include <net.h>
+#ifndef CONFIG_DM_ETH
 #include <netdev.h>
+#endif
 #include <asm/io.h>
 #include <pci.h>
 
@@ -281,6 +284,8 @@ struct RxDesc {
 	u32 buf_Haddr;
 };
 
+static unsigned char rxdata[RX_BUF_LEN];
+
 #define RTL8169_DESC_SIZE 16
 
 #if ARCH_DMA_MINALIGN > 256
@@ -299,7 +304,8 @@ struct RxDesc {
  * the driver to allocate descriptors from a pool of non-cached memory.
  */
 #if RTL8169_DESC_SIZE < ARCH_DMA_MINALIGN
-#if !defined(CONFIG_SYS_NONCACHED_MEMORY) && !defined(CONFIG_SYS_DCACHE_OFF)
+#if !defined(CONFIG_SYS_NONCACHED_MEMORY) && \
+	!defined(CONFIG_SYS_DCACHE_OFF) && !defined(CONFIG_X86)
 #warning cache-line size is larger than descriptor size
 #endif
 #endif
@@ -317,6 +323,7 @@ DEFINE_ALIGN_BUFFER(u8, txb, NUM_TX_DESC * RX_BUF_SIZE, RTL8169_ALIGN);
 DEFINE_ALIGN_BUFFER(u8, rxb, NUM_RX_DESC * RX_BUF_SIZE, RTL8169_ALIGN);
 
 struct rtl8169_private {
+	ulong iobase;
 	void *mmio_addr;	/* memory map physical address */
 	int chipset;
 	unsigned long cur_rx;	/* Index into the Rx descriptor buffer of next Rx pkt. */
@@ -338,9 +345,9 @@ static const unsigned int rtl8169_rx_config =
     (RX_FIFO_THRESH << RxCfgFIFOShift) | (RX_DMA_BURST << RxCfgDMAShift);
 
 static struct pci_device_id supported[] = {
-	{PCI_VENDOR_ID_REALTEK, 0x8167},
-	{PCI_VENDOR_ID_REALTEK, 0x8168},
-	{PCI_VENDOR_ID_REALTEK, 0x8169},
+	{ PCI_DEVICE(PCI_VENDOR_ID_REALTEK, 0x8167) },
+	{ PCI_DEVICE(PCI_VENDOR_ID_REALTEK, 0x8168) },
+	{ PCI_DEVICE(PCI_VENDOR_ID_REALTEK, 0x8169) },
 	{}
 };
 
@@ -380,7 +387,7 @@ int mdio_read(int RegAddr)
 	return value;
 }
 
-static int rtl8169_init_board(struct eth_device *dev)
+static int rtl8169_init_board(unsigned long dev_iobase, const char *name)
 {
 	int i;
 	u32 tmp;
@@ -388,7 +395,7 @@ static int rtl8169_init_board(struct eth_device *dev)
 #ifdef DEBUG_RTL8169
 	printf ("%s\n", __FUNCTION__);
 #endif
-	ioaddr = dev->iobase;
+	ioaddr = dev_iobase;
 
 	/* Soft reset the chip. */
 	RTL_W8(ChipCmd, CmdReset);
@@ -412,7 +419,8 @@ static int rtl8169_init_board(struct eth_device *dev)
 	}
 
 	/* if unknown chip, assume array element #0, original RTL-8169 in this case */
-	printf("PCI device %s: unknown chip version, assuming RTL-8169\n", dev->name);
+	printf("PCI device %s: unknown chip version, assuming RTL-8169\n",
+	       name);
 	printf("PCI device: TxConfig = 0x%lX\n", (unsigned long) RTL_R32(TxConfig));
 	tpc->chipset = 0;
 
@@ -504,7 +512,8 @@ static void rtl_flush_buffer(void *buf, size_t size)
 /**************************************************************************
 RECV - Receive a frame
 ***************************************************************************/
-static int rtl_recv(struct eth_device *dev)
+static int rtl_recv_common(pci_dev_t bdf, unsigned long dev_iobase,
+			   uchar **packetp)
 {
 	/* return true if there's an ethernet packet ready to read */
 	/* nic->packet should contain data on return */
@@ -515,7 +524,7 @@ static int rtl_recv(struct eth_device *dev)
 #ifdef DEBUG_RTL8169_RX
 	printf ("%s\n", __FUNCTION__);
 #endif
-	ioaddr = dev->iobase;
+	ioaddr = dev_iobase;
 
 	cur_rx = tpc->cur_rx;
 
@@ -523,7 +532,6 @@ static int rtl_recv(struct eth_device *dev)
 
 	if ((le32_to_cpu(tpc->RxDescArray[cur_rx].status) & OWNbit) == 0) {
 		if (!(le32_to_cpu(tpc->RxDescArray[cur_rx].status) & RxRES)) {
-			unsigned char rxdata[RX_BUF_LEN];
 			length = (int) (le32_to_cpu(tpc->RxDescArray[cur_rx].
 						status) & 0x00001FFF) - 4;
 
@@ -536,17 +544,22 @@ static int rtl_recv(struct eth_device *dev)
 			else
 				tpc->RxDescArray[cur_rx].status =
 					cpu_to_le32(OWNbit + RX_BUF_SIZE);
-			tpc->RxDescArray[cur_rx].buf_addr =
-				cpu_to_le32(bus_to_phys(tpc->RxBufferRing[cur_rx]));
+			tpc->RxDescArray[cur_rx].buf_addr = cpu_to_le32(
+				pci_mem_to_phys(bdf, (pci_addr_t)(unsigned long)
+				tpc->RxBufferRing[cur_rx]));
 			rtl_flush_rx_desc(&tpc->RxDescArray[cur_rx]);
-
+#ifdef CONFIG_DM_ETH
+			*packetp = rxdata;
+#else
 			net_process_received_packet(rxdata, length);
+#endif
 		} else {
 			puts("Error Rx");
+			length = -EIO;
 		}
 		cur_rx = (cur_rx + 1) % NUM_RX_DESC;
 		tpc->cur_rx = cur_rx;
-		return 1;
+		return length;
 
 	} else {
 		ushort sts = RTL_R8(IntrStatus);
@@ -557,11 +570,26 @@ static int rtl_recv(struct eth_device *dev)
 	return (0);		/* initially as this is called to flush the input */
 }
 
+#ifdef CONFIG_DM_ETH
+int rtl8169_eth_recv(struct udevice *dev, int flags, uchar **packetp)
+{
+	struct rtl8169_private *priv = dev_get_priv(dev);
+
+	return rtl_recv_common(pci_get_bdf(dev), priv->iobase, packetp);
+}
+#else
+static int rtl_recv(struct eth_device *dev)
+{
+	return rtl_recv_common((pci_dev_t)dev->priv, dev->iobase, NULL);
+}
+#endif /* nCONFIG_DM_ETH */
+
 #define HZ 1000
 /**************************************************************************
 SEND - Transmit a frame
 ***************************************************************************/
-static int rtl_send(struct eth_device *dev, void *packet, int length)
+static int rtl_send_common(pci_dev_t bdf, unsigned long dev_iobase,
+			   void *packet, int length)
 {
 	/* send the packet to destination */
 
@@ -577,7 +605,7 @@ static int rtl_send(struct eth_device *dev, void *packet, int length)
 	printf("sending %d bytes\n", len);
 #endif
 
-	ioaddr = dev->iobase;
+	ioaddr = dev_iobase;
 
 	/* point to the current txb incase multiple tx_rings are used */
 	ptxb = tpc->Tx_skbuff[entry * MAX_ETH_FRAME_SIZE];
@@ -588,7 +616,8 @@ static int rtl_send(struct eth_device *dev, void *packet, int length)
 		ptxb[len++] = '\0';
 
 	tpc->TxDescArray[entry].buf_Haddr = 0;
-	tpc->TxDescArray[entry].buf_addr = cpu_to_le32(bus_to_phys(ptxb));
+	tpc->TxDescArray[entry].buf_addr = cpu_to_le32(
+		pci_mem_to_phys(bdf, (pci_addr_t)(unsigned long)ptxb));
 	if (entry != (NUM_TX_DESC - 1)) {
 		tpc->TxDescArray[entry].status =
 			cpu_to_le32((OWNbit | FSbit | LSbit) |
@@ -625,7 +654,23 @@ static int rtl_send(struct eth_device *dev, void *packet, int length)
 	return ret;
 }
 
-static void rtl8169_set_rx_mode(struct eth_device *dev)
+#ifdef CONFIG_DM_ETH
+int rtl8169_eth_send(struct udevice *dev, void *packet, int length)
+{
+	struct rtl8169_private *priv = dev_get_priv(dev);
+
+	return rtl_send_common(pci_get_bdf(dev), priv->iobase, packet, length);
+}
+
+#else
+static int rtl_send(struct eth_device *dev, void *packet, int length)
+{
+	return rtl_send_common((pci_dev_t)dev->priv, dev->iobase, packet,
+			       length);
+}
+#endif
+
+static void rtl8169_set_rx_mode(void)
 {
 	u32 mc_filter[2];	/* Multicast hash filter */
 	int rx_mode;
@@ -648,7 +693,7 @@ static void rtl8169_set_rx_mode(struct eth_device *dev)
 	RTL_W32(MAR0 + 4, mc_filter[1]);
 }
 
-static void rtl8169_hw_start(struct eth_device *dev)
+static void rtl8169_hw_start(pci_dev_t bdf)
 {
 	u32 i;
 
@@ -693,9 +738,11 @@ static void rtl8169_hw_start(struct eth_device *dev)
 
 	tpc->cur_rx = 0;
 
-	RTL_W32(TxDescStartAddrLow, bus_to_phys(tpc->TxDescArray));
+	RTL_W32(TxDescStartAddrLow, pci_mem_to_phys(bdf,
+			(pci_addr_t)(unsigned long)tpc->TxDescArray));
 	RTL_W32(TxDescStartAddrHigh, (unsigned long)0);
-	RTL_W32(RxDescStartAddrLow, bus_to_phys(tpc->RxDescArray));
+	RTL_W32(RxDescStartAddrLow, pci_mem_to_phys(
+			bdf, (pci_addr_t)(unsigned long)tpc->RxDescArray));
 	RTL_W32(RxDescStartAddrHigh, (unsigned long)0);
 
 	/* RTL-8169sc/8110sc or later version */
@@ -707,7 +754,7 @@ static void rtl8169_hw_start(struct eth_device *dev)
 
 	RTL_W32(RxMissed, 0);
 
-	rtl8169_set_rx_mode(dev);
+	rtl8169_set_rx_mode();
 
 	/* no early-rx interrupts */
 	RTL_W16(MultiIntr, RTL_R16(MultiIntr) & 0xF000);
@@ -717,7 +764,7 @@ static void rtl8169_hw_start(struct eth_device *dev)
 #endif
 }
 
-static void rtl8169_init_ring(struct eth_device *dev)
+static void rtl8169_init_ring(pci_dev_t bdf)
 {
 	int i;
 
@@ -745,8 +792,8 @@ static void rtl8169_init_ring(struct eth_device *dev)
 				cpu_to_le32(OWNbit + RX_BUF_SIZE);
 
 		tpc->RxBufferRing[i] = &rxb[i * RX_BUF_SIZE];
-		tpc->RxDescArray[i].buf_addr =
-			cpu_to_le32(bus_to_phys(tpc->RxBufferRing[i]));
+		tpc->RxDescArray[i].buf_addr = cpu_to_le32(pci_mem_to_phys(
+			bdf, (pci_addr_t)(unsigned long)tpc->RxBufferRing[i]));
 		rtl_flush_rx_desc(&tpc->RxDescArray[i]);
 	}
 
@@ -755,10 +802,7 @@ static void rtl8169_init_ring(struct eth_device *dev)
 #endif
 }
 
-/**************************************************************************
-RESET - Finish setting up the ethernet interface
-***************************************************************************/
-static int rtl_reset(struct eth_device *dev, bd_t *bis)
+static void rtl8169_common_start(pci_dev_t bdf, unsigned char *enetaddr)
 {
 	int i;
 
@@ -767,30 +811,47 @@ static int rtl_reset(struct eth_device *dev, bd_t *bis)
 	printf ("%s\n", __FUNCTION__);
 #endif
 
-	rtl8169_init_ring(dev);
-	rtl8169_hw_start(dev);
+	rtl8169_init_ring(bdf);
+	rtl8169_hw_start(bdf);
 	/* Construct a perfect filter frame with the mac address as first match
 	 * and broadcast for all others */
 	for (i = 0; i < 192; i++)
 		txb[i] = 0xFF;
 
-	txb[0] = dev->enetaddr[0];
-	txb[1] = dev->enetaddr[1];
-	txb[2] = dev->enetaddr[2];
-	txb[3] = dev->enetaddr[3];
-	txb[4] = dev->enetaddr[4];
-	txb[5] = dev->enetaddr[5];
+	txb[0] = enetaddr[0];
+	txb[1] = enetaddr[1];
+	txb[2] = enetaddr[2];
+	txb[3] = enetaddr[3];
+	txb[4] = enetaddr[4];
+	txb[5] = enetaddr[5];
 
 #ifdef DEBUG_RTL8169
 	printf("%s elapsed time : %lu\n", __func__, currticks()-stime);
 #endif
-	return 0;
 }
 
+#ifdef CONFIG_DM_ETH
+static int rtl8169_eth_start(struct udevice *dev)
+{
+	struct eth_pdata *plat = dev_get_platdata(dev);
+
+	rtl8169_common_start(pci_get_bdf(dev), plat->enetaddr);
+
+	return 0;
+}
+#else
 /**************************************************************************
-HALT - Turn off ethernet interface
+RESET - Finish setting up the ethernet interface
 ***************************************************************************/
-static void rtl_halt(struct eth_device *dev)
+static int rtl_reset(struct eth_device *dev, bd_t *bis)
+{
+	rtl8169_common_start((pci_dev_t)dev->priv, dev->enetaddr);
+
+	return 0;
+}
+#endif /* nCONFIG_DM_ETH */
+
+static void rtl_halt_common(unsigned long dev_iobase)
 {
 	int i;
 
@@ -798,7 +859,7 @@ static void rtl_halt(struct eth_device *dev)
 	printf ("%s\n", __FUNCTION__);
 #endif
 
-	ioaddr = dev->iobase;
+	ioaddr = dev_iobase;
 
 	/* Stop the chip's Tx and Rx DMA processes. */
 	RTL_W8(ChipCmd, 0x00);
@@ -813,13 +874,31 @@ static void rtl_halt(struct eth_device *dev)
 	}
 }
 
+#ifdef CONFIG_DM_ETH
+void rtl8169_eth_stop(struct udevice *dev)
+{
+	struct rtl8169_private *priv = dev_get_priv(dev);
+
+	rtl_halt_common(priv->iobase);
+}
+#else
+/**************************************************************************
+HALT - Turn off ethernet interface
+***************************************************************************/
+static void rtl_halt(struct eth_device *dev)
+{
+	rtl_halt_common(dev->iobase);
+}
+#endif
+
 /**************************************************************************
 INIT - Look for an adapter, this routine's visible to the outside
 ***************************************************************************/
 
 #define board_found 1
 #define valid_link 0
-static int rtl_init(struct eth_device *dev, bd_t *bis)
+static int rtl_init(unsigned long dev_ioaddr, const char *name,
+		    unsigned char *enetaddr)
 {
 	static int board_idx = -1;
 	int i, rc;
@@ -828,33 +907,32 @@ static int rtl_init(struct eth_device *dev, bd_t *bis)
 #ifdef DEBUG_RTL8169
 	printf ("%s\n", __FUNCTION__);
 #endif
-
-	ioaddr = dev->iobase;
+	ioaddr = dev_ioaddr;
 
 	board_idx++;
 
 	/* point to private storage */
 	tpc = &tpx;
 
-	rc = rtl8169_init_board(dev);
+	rc = rtl8169_init_board(ioaddr, name);
 	if (rc)
 		return rc;
 
 	/* Get MAC address.  FIXME: read EEPROM */
 	for (i = 0; i < MAC_ADDR_LEN; i++)
-		dev->enetaddr[i] = RTL_R8(MAC0 + i);
+		enetaddr[i] = RTL_R8(MAC0 + i);
 
 #ifdef DEBUG_RTL8169
 	printf("chipset = %d\n", tpc->chipset);
 	printf("MAC Address");
 	for (i = 0; i < MAC_ADDR_LEN; i++)
-		printf(":%02x", dev->enetaddr[i]);
+		printf(":%02x", enetaddr[i]);
 	putc('\n');
 #endif
 
 #ifdef DEBUG_RTL8169
 	/* Print out some hardware info */
-	printf("%s: at ioaddr 0x%lx\n", dev->name, ioaddr);
+	printf("%s: at ioaddr 0x%lx\n", name, ioaddr);
 #endif
 
 	/* if TBI is not endbled */
@@ -964,6 +1042,7 @@ static int rtl_init(struct eth_device *dev, bd_t *bis)
 	return 0;
 }
 
+#ifndef CONFIG_DM_ETH
 int rtl8169_initialize(bd_t *bis)
 {
 	pci_dev_t devno;
@@ -1014,7 +1093,7 @@ int rtl8169_initialize(bd_t *bis)
 		dev->send = rtl_send;
 		dev->recv = rtl_recv;
 
-		err = rtl_init(dev, bis);
+		err = rtl_init(dev->iobase, dev->name, dev->enetaddr);
 		if (err < 0) {
 			printf(pr_fmt("failed to initialize card: %d\n"), err);
 			free(dev);
@@ -1027,3 +1106,62 @@ int rtl8169_initialize(bd_t *bis)
 	}
 	return card_number;
 }
+#endif
+
+#ifdef CONFIG_DM_ETH
+static int rtl8169_eth_probe(struct udevice *dev)
+{
+	struct pci_child_platdata *pplat = dev_get_parent_platdata(dev);
+	struct rtl8169_private *priv = dev_get_priv(dev);
+	struct eth_pdata *plat = dev_get_platdata(dev);
+	u32 iobase;
+	int region;
+	int ret;
+
+	debug("rtl8169: REALTEK RTL8169 @0x%x\n", iobase);
+	switch (pplat->device) {
+	case 0x8168:
+		region = 2;
+		break;
+	default:
+		region = 1;
+		break;
+	}
+	pci_read_config32(pci_get_bdf(dev), PCI_BASE_ADDRESS_0 + region * 4,
+			  &iobase);
+	iobase &= ~0xf;
+	priv->iobase = (int)pci_mem_to_phys(pci_get_bdf(dev), iobase);
+
+	ret = rtl_init(priv->iobase, dev->name, plat->enetaddr);
+	if (ret < 0) {
+		printf(pr_fmt("failed to initialize card: %d\n"), ret);
+		return ret;
+	}
+
+	return 0;
+}
+
+static const struct eth_ops rtl8169_eth_ops = {
+	.start	= rtl8169_eth_start,
+	.send	= rtl8169_eth_send,
+	.recv	= rtl8169_eth_recv,
+	.stop	= rtl8169_eth_stop,
+};
+
+static const struct udevice_id rtl8169_eth_ids[] = {
+	{ .compatible = "realtek,rtl8169" },
+	{ }
+};
+
+U_BOOT_DRIVER(eth_rtl8169) = {
+	.name	= "eth_rtl8169",
+	.id	= UCLASS_ETH,
+	.of_match = rtl8169_eth_ids,
+	.probe	= rtl8169_eth_probe,
+	.ops	= &rtl8169_eth_ops,
+	.priv_auto_alloc_size = sizeof(struct rtl8169_private),
+	.platdata_auto_alloc_size = sizeof(struct eth_pdata),
+};
+
+U_BOOT_PCI_DEVICE(eth_rtl8169, supported);
+#endif
Signature · 1 line
2.4.3.573.g4eafbef
#18 lore raw

PATCH 6/18 dm: eth: Add driver-model support to the rtl8169 driver

Quoted 8 lines · Simon Glass

This driver is used by the Intel Minnowmax board. Convert it to driver model so it can use the new Ethernet implementation.

---

 drivers/net/rtl8169.c | 236 +++++++++++++++++++++++++++++++++++++++-----------
 1 file changed, 187 insertions(+), 49 deletions(-)

Applied to u-boot-dm.

#19 lore raw

PATCH 7/18 dm: pci: Add a function to get the BDF for a device

In reply to Simon Glass

It is useful to be able to find the full PCI address (bus, device and function) for a PCI device. Add a function to provide this.

Adjust the existing code to use this.

---

 drivers/pci/pci-uclass.c | 15 +++++++++------
 drivers/pci/pci_compat.c |  8 ++------
 include/pci.h            |  8 ++++++++
 3 files changed, 19 insertions(+), 12 deletions(-)
drivers/pci/pci-uclass.c +9 -6
index 41daa0d..3be76c9 100644
@@ -30,6 +30,14 @@ struct pci_controller *pci_bus_to_hose(int busnum)
 	return dev_get_uclass_priv(bus);
 }
 
+pci_dev_t pci_get_bdf(struct udevice *dev)
+{
+	struct pci_child_platdata *pplat = dev_get_parent_platdata(dev);
+	struct udevice *bus = dev->parent;
+
+	return PCI_ADD_BUS(bus->seq, pplat->devfn);
+}
+
 /**
  * pci_get_bus_max() - returns the bus number of the last active bus
  *
@@ -295,19 +303,14 @@ int pci_auto_config_devices(struct udevice *bus)
 	for (ret = device_find_first_child(bus, &dev);
 	     !ret && dev;
 	     ret = device_find_next_child(&dev)) {
-		struct pci_child_platdata *pplat;
 		struct pci_controller *ctlr_hose;
-
-		pplat = dev_get_parent_platdata(dev);
 		unsigned int max_bus;
-		pci_dev_t bdf;
 
-		bdf = PCI_ADD_BUS(bus->seq, pplat->devfn);
 		debug("%s: device %s\n", __func__, dev->name);
 
 		/* The root controller has the region information */
 		ctlr_hose = hose->ctlr->uclass_priv;
-		max_bus = pciauto_config_device(ctlr_hose, bdf);
+		max_bus = pciauto_config_device(ctlr_hose, pci_get_bdf(dev));
 		sub_bus = max(sub_bus, max_bus);
 	}
 	debug("%s: done\n", __func__);
drivers/pci/pci_compat.c +2 -6
index d6938c1..05c3510 100644
@@ -31,13 +31,9 @@ PCI_HOSE_OP(write, dword, 32, u32)
 
 pci_dev_t pci_find_devices(struct pci_device_id *ids, int index)
 {
-	struct pci_child_platdata *pplat;
-	struct udevice *bus, *dev;
+	struct udevice *dev;
 
 	if (pci_find_device_id(ids, index, &dev))
 		return -1;
-	bus = dev->parent;
-	pplat = dev_get_parent_platdata(dev);
-
-	return PCI_ADD_BUS(bus->seq, pplat->devfn);
+	return pci_get_bdf(dev);
 }
include/pci.h +8 -0
index d21fa8b..ae5c854 100644
@@ -817,6 +817,14 @@ struct dm_pci_ops {
 #define pci_get_ops(dev)	((struct dm_pci_ops *)(dev)->driver->ops)
 
 /**
+ * pci_get_bdf() - Get the BDF value for a device
+ *
+ * @dev:	Device to check
+ * @return bus/device/function value (see PCI_BDF())
+ */
+pci_dev_t pci_get_bdf(struct udevice *dev);
+
+/**
  * pci_bind_bus_devices() - scan a PCI bus and bind devices
  *
  * Scan a PCI bus looking for devices. Bind each one that is found. If
Signature · 1 line
2.4.3.573.g4eafbef
#20 lore raw

PATCH 7/18 dm: pci: Add a function to get the BDF for a device

Quoted 5 lines · Simon Glass

It is useful to be able to find the full PCI address (bus, device and function) for a PCI device. Add a function to provide this.

Adjust the existing code to use this.

Quoted 7 lines

---

 drivers/pci/pci-uclass.c | 15 +++++++++------
 drivers/pci/pci_compat.c |  8 ++------
 include/pci.h            |  8 ++++++++
 3 files changed, 19 insertions(+), 12 deletions(-)

Applied to u-boot-dm.

#21 lore raw

PATCH 8/18 dm: usb: Correct the struct usb_driver_entry comment

In reply to Simon Glass

The function documentation is incorrect. Fix it.

---

 include/usb.h | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)
include/usb.h +2 -2
index dca512d..1b9140f 100644
@@ -493,8 +493,8 @@ struct usb_device_id {
 
 /**
  * struct usb_driver_entry - Matches a driver to its usb_device_ids
- * @compatible: Compatible string
- * @data: Data for this compatible string
+ * @driver: Driver to use
+ * @match: List of match records for this driver, terminated by {}
  */
 struct usb_driver_entry {
 	struct driver *driver;
Signature · 1 line
2.4.3.573.g4eafbef
#24 lore raw

PATCH 9/18 dm: usb: Avoid using USB ethernet with CONFIG_DM_USB and no DM_ETH

In reply to Simon Glass

If driver model is used for Ethernet then USB Ethernet does not build. This can be made to work with driver model is used for USB also. Add #ifdef logic to make this clear when building.

---

 common/cmd_usb.c | 12 +++++++-----
 1 file changed, 7 insertions(+), 5 deletions(-)
common/cmd_usb.c +7 -5
index eab55cd..e1dba7a 100644
@@ -23,7 +23,7 @@
 static int usb_stor_curr_dev = -1; /* current device */
 #endif
 #ifdef CONFIG_USB_HOST_ETHER
-static int usb_ether_curr_dev = -1; /* current ethernet device */
+static int __maybe_unused usb_ether_curr_dev = -1; /* current ethernet device */
 #endif
 
 /* some display routines (info command) */
@@ -526,14 +526,16 @@ static void do_usb_start(void)
 
 	/* Driver model will probe the devices as they are found */
 #ifndef CONFIG_DM_USB
-#ifdef CONFIG_USB_STORAGE
+# ifdef CONFIG_USB_STORAGE
 	/* try to recognize storage devices immediately */
 	usb_stor_curr_dev = usb_stor_scan(1);
-#endif
-#endif
-#ifdef CONFIG_USB_HOST_ETHER
+# endif
+# ifdef CONFIG_DM_ETH
+#   warning "You must use CONFIG_DM_USB if you want to use CONFIG_USB_HOST_ETHER with CONFIG_DM_ETH"
+# elif defined(CONFIG_USB_HOST_ETHER)
 	/* try to recognize ethernet devices immediately */
 	usb_ether_curr_dev = usb_host_eth_scan(1);
+# endif
 #endif
 #ifdef CONFIG_USB_KEYBOARD
 	drv_usb_kbd_init();
Signature · 1 line
2.4.3.573.g4eafbef
#25 lore raw

PATCH 9/18 dm: usb: Avoid using USB ethernet with CONFIG_DM_USB and no DM_ETH

Quoted 5 lines · Simon Glass

If driver model is used for Ethernet then USB Ethernet does not build. This can be made to work with driver model is used for USB also. Add #ifdef logic to make this clear when building.

Well ew, but OK.

Best regards,
Marek Vasut

#26 lore raw

PATCH 9/18 dm: usb: Avoid using USB ethernet with CONFIG_DM_USB and no DM_ETH

Quoted 5 lines · Marek Vasut
Quoted 4 lines · Simon Glass

If driver model is used for Ethernet then USB Ethernet does not build. This can be made to work with driver model is used for USB also. Add #ifdef logic to make this clear when building.

Quoted 8 lines
Quoted 1 line

Well ew, but OK.

Best regards,
Marek Vasut

I squashed this in with my later patch
http://patchwork.ozlabs.org/patch/492686/ which should reduce the ew. But ultimate we should try to get all of this to driver model.

Applied to u-boot-dm.

#27 lore raw

PATCH 10/18 dm: eth: Avoid blocking on packet reception

In reply to Simon Glass

Some devices can take a long time to work out whether they have a new packet or now. For example the ASIX USB Ethernet dongle can take 5 seconds to do this, since it waits until it gets a new packet on the wire before allowing the USB bulk read packet to be submitted.

At present with driver mode the Ethernet receive code reads 32 packets. This can take a very long time if we must wait for all 32 packets. The old code (before driver model) worked by reading a single set of packets from the USB device, then processing all the packets with in. It would be nice to use the same behaviour with driver model.

Add a flag to the receive method which indicates that the driver should try to find a packet if available, by consulting the hardware. When the flag is not set, it should just return any packet data it has already received. If there is none, it should return -EAGAIN so that the loop will terminate.

---

 drivers/net/designware.c  |  2 +-
 drivers/net/sandbox-raw.c |  2 +-
 drivers/net/sandbox.c     |  2 +-
 drivers/net/sunxi_emac.c  |  2 +-
 include/net.h             | 10 +++++++++-
 net/eth.c                 |  5 ++++-
 6 files changed, 17 insertions(+), 6 deletions(-)
drivers/net/designware.c +1 -1
index ae51cf3..c76cc87 100644
@@ -522,7 +522,7 @@ static int designware_eth_send(struct udevice *dev, void *packet, int length)
 	return _dw_eth_send(priv, packet, length);
 }
 
-static int designware_eth_recv(struct udevice *dev, uchar **packetp)
+static int designware_eth_recv(struct udevice *dev, int flags, uchar **packetp)
 {
 	struct dw_eth_dev *priv = dev_get_priv(dev);
 
drivers/net/sandbox-raw.c +1 -1
index 45c3b18..5912427 100644
@@ -65,7 +65,7 @@ static int sb_eth_raw_send(struct udevice *dev, void *packet, int length)
 	return sandbox_eth_raw_os_send(packet, length, priv);
 }
 
-static int sb_eth_raw_recv(struct udevice *dev, uchar **packetp)
+static int sb_eth_raw_recv(struct udevice *dev, int flags, uchar **packetp)
 {
 	struct eth_pdata *pdata = dev_get_platdata(dev);
 	struct eth_sandbox_raw_priv *priv = dev_get_priv(dev);
drivers/net/sandbox.c +1 -1
index 4e083d3..6763a24 100644
@@ -152,7 +152,7 @@ static int sb_eth_send(struct udevice *dev, void *packet, int length)
 	return 0;
 }
 
-static int sb_eth_recv(struct udevice *dev, uchar **packetp)
+static int sb_eth_recv(struct udevice *dev, int flags, uchar **packetp)
 {
 	struct eth_sandbox_priv *priv = dev_get_priv(dev);
 
drivers/net/sunxi_emac.c +1 -1
index e939bf2..11cd0ea 100644
@@ -527,7 +527,7 @@ static int sunxi_emac_eth_send(struct udevice *dev, void *packet, int length)
 	return _sunxi_emac_eth_send(priv, packet, length);
 }
 
-static int sunxi_emac_eth_recv(struct udevice *dev, uchar **packetp)
+static int sunxi_emac_eth_recv(struct udevice *dev, int flags, uchar **packetp)
 {
 	struct emac_eth_dev *priv = dev_get_priv(dev);
 	int rx_len;
include/net.h +9 -1
index d17173d..b9c13f2 100644
@@ -93,6 +93,14 @@ struct eth_pdata {
 	int phy_interface;
 };
 
+enum eth_recv_flags {
+	/*
+	 * Check hardware device for new packets (otherwise only return those
+	 * which are already in the memory buffer ready to process)
+	 */
+	ETH_RECV_CHECK_DEVICE		= 1 << 0,
+};
+
 /**
  * struct eth_ops - functions of Ethernet MAC controllers
  *
@@ -120,7 +128,7 @@ struct eth_pdata {
 struct eth_ops {
 	int (*start)(struct udevice *dev);
 	int (*send)(struct udevice *dev, void *packet, int length);
-	int (*recv)(struct udevice *dev, uchar **packetp);
+	int (*recv)(struct udevice *dev, int flags, uchar **packetp);
 	int (*free_pkt)(struct udevice *dev, uchar *packet, int length);
 	void (*stop)(struct udevice *dev);
 #ifdef CONFIG_MCAST_TFTP
net/eth.c +4 -1
index 953b731..72ce91c 100644
@@ -404,6 +404,7 @@ int eth_rx(void)
 {
 	struct udevice *current;
 	uchar *packet;
+	int flags;
 	int ret;
 	int i;
 
@@ -415,8 +416,10 @@ int eth_rx(void)
 		return -EINVAL;
 
 	/* Process up to 32 packets@one time */
+	flags = ETH_RECV_CHECK_DEVICE;
 	for (i = 0; i < 32; i++) {
-		ret = eth_get_ops(current)->recv(current, &packet);
+		ret = eth_get_ops(current)->recv(current, flags, &packet);
+		flags = 0;
 		if (ret > 0)
 			net_process_received_packet(packet, ret);
 		if (ret >= 0 && eth_get_ops(current)->free_pkt)
Signature · 1 line
2.4.3.573.g4eafbef
#28 lore raw

PATCH 10/18 dm: eth: Avoid blocking on packet reception

Quoted 19 lines · Simon Glass

Some devices can take a long time to work out whether they have a new packet or now. For example the ASIX USB Ethernet dongle can take 5 seconds to do this, since it waits until it gets a new packet on the wire before allowing the USB bulk read packet to be submitted.

At present with driver mode the Ethernet receive code reads 32 packets. This can take a very long time if we must wait for all 32 packets. The old code (before driver model) worked by reading a single set of packets from the USB device, then processing all the packets with in. It would be nice to use the same behaviour with driver model.

Add a flag to the receive method which indicates that the driver should try to find a packet if available, by consulting the hardware. When the flag is not set, it should just return any packet data it has already received. If there is none, it should return -EAGAIN so that the loop will terminate.

---

Quoted 7 lines
 drivers/net/designware.c  |  2 +-
 drivers/net/sandbox-raw.c |  2 +-
 drivers/net/sandbox.c     |  2 +-
 drivers/net/sunxi_emac.c  |  2 +-
 include/net.h             | 10 +++++++++-
 net/eth.c                 |  5 ++++-
 6 files changed, 17 insertions(+), 6 deletions(-)

Applied to u-boot-dm.

#29 lore raw

PATCH 10/18 dm: eth: Avoid blocking on packet reception

In reply to Simon Glass

Hi Simon,

Quoted 11 lines · Simon Glass

Some devices can take a long time to work out whether they have a new packet or now. For example the ASIX USB Ethernet dongle can take 5 seconds to do this, since it waits until it gets a new packet on the wire before allowing the USB bulk read packet to be submitted.

At present with driver mode the Ethernet receive code reads 32 packets. This can take a very long time if we must wait for all 32 packets. The old code (before driver model) worked by reading a single set of packets from the USB device, then processing all the packets with in. It would be nice to use the same behaviour with driver model.

Quoted 7 lines

Add a flag to the receive method which indicates that the driver should try to find a packet if available, by consulting the hardware. When the flag is not set, it should just return any packet data it has already received. If there is none, it should return -EAGAIN so that the loop will terminate.

---

#30 lore raw

PATCH 11/18 dm: usb: eth: Support driver model with USB Ethernet

In reply to Simon Glass

At present USB Ethernet does not work with CONFIG_DM_ETH. Add driver model support to this feature, so that it can work alongside other Ethernet devices with driver model.

It was found that quite a bit of code is common in most of the USB Ethernet drivers. Add this code to the common layer to reduce the amount of duplicate code needed in USB Ethernet drivers when CONFIG_DM_ETH is used.

---

 common/cmd_usb.c            |   2 +-
 drivers/usb/eth/usb_ether.c | 127 ++++++++++++++++++++++++++++++++++++++++++++
 include/usb_ether.h         |  89 +++++++++++++++++++++++++++----
 3 files changed, 206 insertions(+), 12 deletions(-)
common/cmd_usb.c +1 -1
index e1dba7a..77cb257 100644
@@ -22,7 +22,7 @@
 #ifdef CONFIG_USB_STORAGE
 static int usb_stor_curr_dev = -1; /* current device */
 #endif
-#ifdef CONFIG_USB_HOST_ETHER
+#if defined(CONFIG_USB_HOST_ETHER) && !defined(CONFIG_DM_ETH)
 static int __maybe_unused usb_ether_curr_dev = -1; /* current ethernet device */
 #endif
 
drivers/usb/eth/usb_ether.c +127 -0

141 lines not on this page · raw message

include/usb_ether.h +78 -11

108 lines not on this page · raw message

Signature · 1 line
2.4.3.573.g4eafbef
#31 lore raw

PATCH 11/18 dm: usb: eth: Support driver model with USB Ethernet

Quoted 9 lines · Simon Glass

At present USB Ethernet does not work with CONFIG_DM_ETH. Add driver model support to this feature, so that it can work alongside other Ethernet devices with driver model.

It was found that quite a bit of code is common in most of the USB Ethernet drivers. Add this code to the common layer to reduce the amount of duplicate code needed in USB Ethernet drivers when CONFIG_DM_ETH is used.

Best regards,
Marek Vasut

#32 lore raw

PATCH 11/18 dm: usb: eth: Support driver model with USB Ethernet

Quoted 9 lines · Marek Vasut
Quoted 8 lines · Simon Glass

At present USB Ethernet does not work with CONFIG_DM_ETH. Add driver model support to this feature, so that it can work alongside other Ethernet devices with driver model.

It was found that quite a bit of code is common in most of the USB Ethernet drivers. Add this code to the common layer to reduce the amount of duplicate code needed in USB Ethernet drivers when CONFIG_DM_ETH is used.

Quoted 6 lines
Quoted 1 line

Best regards,
Marek Vasut

Applied to u-boot-dm.

#33 lore raw

PATCH 12/18 dm: usb: Adjust the USB_DEVICE() macro naming

In reply to Simon Glass

In Linux USB_DEVICE() is used to declare a USB device by vendor/device ID. We should follow the same convention in U-Boot. Rename the existing USB_DEVICE() macro to U_BOOT_USB_DEVICE() and bring in the USB_DEVICE() macro from Linux for use in U-Boot.

---

 common/usb_hub.c            |  2 +-
 common/usb_kbd.c            |  4 ++--
 common/usb_storage.c        |  2 +-
 drivers/usb/Kconfig         |  4 ++--
 drivers/usb/eth/usb_ether.c |  4 ++--
 include/usb.h               | 18 +++++++++++++++++-
 6 files changed, 25 insertions(+), 9 deletions(-)
common/usb_hub.c +1 -1
index be01f4f..f621ddb 100644
@@ -652,6 +652,6 @@ static const struct usb_device_id hub_id_table[] = {
 	{ }	/* Terminating entry */
 };
 
-USB_DEVICE(usb_generic_hub, hub_id_table);
+U_BOOT_USB_DEVICE(usb_generic_hub, hub_id_table);
 
 #endif
common/usb_kbd.c +2 -2
index e2af67d..0227024 100644
@@ -540,8 +540,8 @@ int drv_usb_kbd_init(void)
 	debug("%s: Probing for keyboard\n", __func__);
 #ifdef CONFIG_DM_USB
 	/*
-	 * TODO: We should add USB_DEVICE() declarations to each USB ethernet
-	 * driver and then most of this file can be removed.
+	 * TODO: We should add U_BOOT_USB_DEVICE() declarations to each USB
+	 * keyboard driver and then most of this file can be removed.
 	 */
 	struct udevice *bus;
 	struct uclass *uc;
common/usb_storage.c +1 -1
index cc9b3e3..b978430 100644
@@ -1442,6 +1442,6 @@ static const struct usb_device_id mass_storage_id_table[] = {
 	{ }		/* Terminating entry */
 };
 
-USB_DEVICE(usb_mass_storage, mass_storage_id_table);
+U_BOOT_USB_DEVICE(usb_mass_storage, mass_storage_id_table);
 
 #endif
drivers/usb/Kconfig +2 -2
index 637ef3d..3fa5b2e 100644
@@ -46,8 +46,8 @@ config DM_USB
 
 	  Much of the code is shared but with this option enabled the USB
 	  uclass takes care of device enumeration. USB devices can be
-	  declared with the USB_DEVICE() macro and will be automatically
-	  probed when found on the bus.
+	  declared with the U_BOOT_USB_DEVICE() macro and will be
+	  automatically probed when found on the bus.
 
 source "drivers/usb/host/Kconfig"
 
drivers/usb/eth/usb_ether.c +2 -2
index 07d2309..63785a9 100644
@@ -266,8 +266,8 @@ int usb_host_eth_scan(int mode)
 	usb_max_eth_dev = 0;
 #ifdef CONFIG_DM_USB
 	/*
-	 * TODO: We should add USB_DEVICE() declarations to each USB ethernet
-	 * driver and then most of this file can be removed.
+	 * TODO: We should add U_BOOT_USB_DEVICE() declarations to each USB
+	 * Ethernet driver and then most of this file can be removed.
 	 */
 	struct udevice *bus;
 	struct uclass *uc;
include/usb.h +17 -1
index 1b9140f..83a8964 100644
@@ -501,7 +501,23 @@ struct usb_driver_entry {
 	const struct usb_device_id *match;
 };
 
-#define USB_DEVICE(__name, __match)					\
+#define USB_DEVICE_ID_MATCH_DEVICE \
+		(USB_DEVICE_ID_MATCH_VENDOR | USB_DEVICE_ID_MATCH_PRODUCT)
+
+/**
+ * USB_DEVICE - macro used to describe a specific usb device
+ * @vend: the 16 bit USB Vendor ID
+ * @prod: the 16 bit USB Product ID
+ *
+ * This macro is used to create a struct usb_device_id that matches a
+ * specific device.
+ */
+#define USB_DEVICE(vend, prod) \
+	.match_flags = USB_DEVICE_ID_MATCH_DEVICE, \
+	.idVendor = (vend), \
+	.idProduct = (prod)
+
+#define U_BOOT_USB_DEVICE(__name, __match) \
 	ll_entry_declare(struct usb_driver_entry, __name, usb_driver_entry) = {\
 		.driver = llsym(struct driver, __name, driver), \
 		.match = __match, \
Signature · 1 line
2.4.3.573.g4eafbef
#34 lore raw

PATCH 12/18 dm: usb: Adjust the USB_DEVICE() macro naming

Quoted 6 lines · Simon Glass

In Linux USB_DEVICE() is used to declare a USB device by vendor/device ID. We should follow the same convention in U-Boot. Rename the existing USB_DEVICE() macro to U_BOOT_USB_DEVICE() and bring in the USB_DEVICE() macro from Linux for use in U-Boot.

Best regards,
Marek Vasut

#35 lore raw

PATCH 12/18 dm: usb: Adjust the USB_DEVICE() macro naming

Quoted 6 lines · Marek Vasut
Quoted 5 lines · Simon Glass

In Linux USB_DEVICE() is used to declare a USB device by vendor/device ID. We should follow the same convention in U-Boot. Rename the existing USB_DEVICE() macro to U_BOOT_USB_DEVICE() and bring in the USB_DEVICE() macro from Linux for use in U-Boot.

Quoted 6 lines
Quoted 1 line

Best regards,
Marek Vasut

Applied to u-boot-dm.

#36 lore raw

PATCH 13/18 x86: minnowmax: Drop the cache line size hack

In reply to Simon Glass

Now that the RTL8169 driver warning is fixed we can drop this. The incorrect value is causing problems with USB EHCI.

---

 include/configs/minnowmax.h | 3 ---
 1 file changed, 3 deletions(-)
include/configs/minnowmax.h +0 -3
index 41653ba..105b05d 100644
@@ -63,9 +63,6 @@
 #define CONFIG_FIT_SIGNATURE
 #define CONFIG_RSA
 
-/* Avoid a warning in the Realtek Ethernet driver */
-#define CONFIG_SYS_CACHELINE_SIZE 16
-
 #define CONFIG_ENV_SECT_SIZE		0x1000
 #define CONFIG_ENV_OFFSET		0x007fe000
 
Signature · 1 line
2.4.3.573.g4eafbef
#37 lore raw

PATCH 13/18 x86: minnowmax: Drop the cache line size hack

Quoted 14 lines · Simon Glass

Now that the RTL8169 driver warning is fixed we can drop this. The incorrect value is causing problems with USB EHCI.

---

 include/configs/minnowmax.h | 3 ---
 1 file changed, 3 deletions(-)
include/configs/minnowmax.h +0 -0
index 41653ba..105b05d 100644

include/configs/minnowmax.h

Quoted 10 lines
include/configs/minnowmax.h +0 -4
@@ -63,9 +63,6 @@
 #define CONFIG_FIT_SIGNATURE
 #define CONFIG_RSA
 
-/* Avoid a warning in the Realtek Ethernet driver */
-#define CONFIG_SYS_CACHELINE_SIZE 16
-
 #define CONFIG_ENV_SECT_SIZE           0x1000
 #define CONFIG_ENV_OFFSET              0x007fe000
 
--
#38 lore raw

PATCH 13/18 x86: minnowmax: Drop the cache line size hack

Quoted 24 lines · Bin Meng
Quoted 23 lines · Simon Glass

Now that the RTL8169 driver warning is fixed we can drop this. The incorrect value is causing problems with USB EHCI.

---

 include/configs/minnowmax.h | 3 ---
 1 file changed, 3 deletions(-)
include/configs/minnowmax.h +0 -3
index 41653ba..105b05d 100644
@@ -63,9 +63,6 @@
 #define CONFIG_FIT_SIGNATURE
 #define CONFIG_RSA
 
-/* Avoid a warning in the Realtek Ethernet driver */
-#define CONFIG_SYS_CACHELINE_SIZE 16
-
 #define CONFIG_ENV_SECT_SIZE           0x1000
 #define CONFIG_ENV_OFFSET              0x007fe000
 
Quoted 3 lines
Quoted 1 line

--

Applied to u-boot-dm.

#39 lore raw

PATCH 14/18 dm: usb: Add driver-model support to ehci-pci

In reply to Simon Glass

Support driver model in this driver. This uses the normal USB driver search mechanism. Any EHCI controllers will be set up as they are found during usb_init().

---

 drivers/usb/host/ehci-pci.c | 97 +++++++++++++++++++++++++++++++++++----------
 1 file changed, 77 insertions(+), 20 deletions(-)
drivers/usb/host/ehci-pci.c +77 -20
index 3d333bd..0cb9fcc 100644
@@ -6,12 +6,45 @@
  */
 
 #include <common.h>
+#include <dm.h>
 #include <errno.h>
 #include <pci.h>
 #include <usb.h>
 
 #include "ehci.h"
 
+/* Information about a USB port */
+struct ehci_pci_priv {
+	struct ehci_ctrl ehci;
+};
+
+static void ehci_pci_common_init(pci_dev_t pdev, struct ehci_hccr **ret_hccr,
+				 struct ehci_hcor **ret_hcor)
+{
+	struct ehci_hccr *hccr;
+	struct ehci_hcor *hcor;
+	uint32_t cmd;
+
+	hccr = (struct ehci_hccr *)pci_map_bar(pdev,
+			PCI_BASE_ADDRESS_0, PCI_REGION_MEM);
+	hcor = (struct ehci_hcor *)((uint32_t) hccr +
+			HC_LENGTH(ehci_readl(&hccr->cr_capbase)));
+
+	debug("EHCI-PCI init hccr 0x%x and hcor 0x%x hc_length %d\n",
+	      (uint32_t)hccr, (uint32_t)hcor,
+	      (uint32_t)HC_LENGTH(ehci_readl(&hccr->cr_capbase)));
+
+	*ret_hccr = hccr;
+	*ret_hcor = hcor;
+
+	/* enable busmaster */
+	pci_read_config_dword(pdev, PCI_COMMAND, &cmd);
+	cmd |= PCI_COMMAND_MASTER;
+	pci_write_config_dword(pdev, PCI_COMMAND, cmd);
+}
+
+#ifndef CONFIG_DM_USB
+
 #ifdef CONFIG_PCI_EHCI_DEVICE
 static struct pci_device_id ehci_pci_ids[] = {
 	/* Please add supported PCI EHCI controller ids here */
@@ -20,7 +53,6 @@ static struct pci_device_id ehci_pci_ids[] = {
 	{0x12D8, 0x400F},	/* Pericom */
 	{0, 0}
 };
-#else
 #endif
 
 /*
@@ -31,9 +63,6 @@ int ehci_hcd_init(int index, enum usb_init_type init,
 		struct ehci_hccr **ret_hccr, struct ehci_hcor **ret_hcor)
 {
 	pci_dev_t pdev;
-	uint32_t cmd;
-	struct ehci_hccr *hccr;
-	struct ehci_hcor *hcor;
 
 #ifdef CONFIG_PCI_EHCI_DEVICE
 	pdev = pci_find_devices(ehci_pci_ids, CONFIG_PCI_EHCI_DEVICE);
@@ -44,23 +73,8 @@ int ehci_hcd_init(int index, enum usb_init_type init,
 		printf("EHCI host controller not found\n");
 		return -1;
 	}
+	ehci_pci_common_init(pdev, ret_hccr, ret_hcor);
 
-	hccr = (struct ehci_hccr *)pci_map_bar(pdev,
-			PCI_BASE_ADDRESS_0, PCI_REGION_MEM);
-	hcor = (struct ehci_hcor *)((uint32_t) hccr +
-			HC_LENGTH(ehci_readl(&hccr->cr_capbase)));
-
-	debug("EHCI-PCI init hccr 0x%x and hcor 0x%x hc_length %d\n",
-			(uint32_t)hccr, (uint32_t)hcor,
-			(uint32_t)HC_LENGTH(ehci_readl(&hccr->cr_capbase)));
-
-	*ret_hccr = hccr;
-	*ret_hcor = hcor;
-
-	/* enable busmaster */
-	pci_read_config_dword(pdev, PCI_COMMAND, &cmd);
-	cmd |= PCI_COMMAND_MASTER;
-	pci_write_config_dword(pdev, PCI_COMMAND, cmd);
 	return 0;
 }
 
@@ -72,3 +86,46 @@ int ehci_hcd_stop(int index)
 {
 	return 0;
 }
+#endif /* nCONFIG_DM_USB */
+
+#ifdef CONFIG_DM_USB
+static int ehci_pci_probe(struct udevice *dev)
+{
+	struct ehci_hccr *hccr;
+	struct ehci_hcor *hcor;
+
+	ehci_pci_common_init(pci_get_bdf(dev), &hccr, &hcor);
+
+	return ehci_register(dev, hccr, hcor, NULL, 0, USB_INIT_HOST);
+}
+
+static int ehci_pci_remove(struct udevice *dev)
+{
+	int ret;
+
+	ret = ehci_deregister(dev);
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
+U_BOOT_DRIVER(ehci_pci) = {
+	.name	= "ehci_pci",
+	.id	= UCLASS_USB,
+	.probe = ehci_pci_probe,
+	.remove = ehci_pci_remove,
+	.ops	= &ehci_usb_ops,
+	.platdata_auto_alloc_size = sizeof(struct usb_platdata),
+	.priv_auto_alloc_size = sizeof(struct ehci_pci_priv),
+	.flags	= DM_FLAG_ALLOC_PRIV_DMA,
+};
+
+static struct pci_device_id ehci_pci_supported[] = {
+	{ PCI_DEVICE_CLASS(PCI_CLASS_SERIAL_USB_EHCI, ~0) },
+	{},
+};
+
+U_BOOT_PCI_DEVICE(ehci_pci, ehci_pci_supported);
+
+#endif /* CONFIG_DM_USB */
Signature · 1 line
2.4.3.573.g4eafbef
#41 lore raw

PATCH 14/18 dm: usb: Add driver-model support to ehci-pci

Quoted 11 lines · Marek Vasut
Quoted 5 lines · Simon Glass

Support driver model in this driver. This uses the normal USB driver search mechanism. Any EHCI controllers will be set up as they are found during usb_init().

Best regards,
Marek Vasut

Applied to u-boot-dm.

#42 lore raw

PATCH 15/18 dm: usb: eth: Add driver-model support to the asix driver

In reply to Simon Glass

This USB Ethernet driver is quite widely use. Allow it to work with CONFIG_DM_ETH enabled. Most of the code remains common but there is a new packet receive flow which is handled specially.

---

 drivers/usb/eth/asix.c | 237 ++++++++++++++++++++++++++++++++++++++++++++-----
 1 file changed, 216 insertions(+), 21 deletions(-)
drivers/usb/eth/asix.c +216 -21
index c8697ae..72ec41e 100644
@@ -5,11 +5,11 @@
  */
 
 #include <common.h>
+#include <dm.h>
 #include <usb.h>
+#include <malloc.h>
 #include <linux/mii.h>
 #include "usb_ether.h"
-#include <malloc.h>
-
 
 /* ASIX AX8817X based USB 2.0 Ethernet Devices */
 
@@ -92,14 +92,20 @@
 #define FLAG_TYPE_AX88772B	(1U << 2)
 #define FLAG_EEPROM_MAC		(1U << 3) /* initial mac address in eeprom */
 
-/* local vars */
-static int curr_eth_dev; /* index for name of next device detected */
 
 /* driver private */
 struct asix_private {
 	int flags;
+#ifdef CONFIG_DM_ETH
+	struct ueth_data ueth;
+#endif
 };
 
+#ifndef CONFIG_DM_ETH
+/* local vars */
+static int curr_eth_dev; /* index for name of next device detected */
+#endif
+
 /*
  * Asix infrastructure commands
  */
@@ -284,13 +290,12 @@ static int asix_write_gpio(struct ueth_data *dev, u16 value, int sleep)
 	return ret;
 }
 
-static int asix_write_hwaddr(struct eth_device *eth)
+static int asix_write_hwaddr_common(struct ueth_data *dev, uint8_t *enetaddr)
 {
-	struct ueth_data *dev = (struct ueth_data *)eth->priv;
 	int ret;
 	ALLOC_CACHE_ALIGN_BUFFER(unsigned char, buf, ETH_ALEN);
 
-	memcpy(buf, eth->enetaddr, ETH_ALEN);
+	memcpy(buf, enetaddr, ETH_ALEN);
 
 	ret = asix_write_cmd(dev, AX_CMD_WRITE_NODE_ID, 0, 0, ETH_ALEN, buf);
 	if (ret < 0)
@@ -325,12 +330,11 @@ static int mii_nway_restart(struct ueth_data *dev)
 	return r;
 }
 
-static int asix_read_mac(struct eth_device *eth)
+static int asix_read_mac_common(struct ueth_data *dev,
+				struct asix_private *priv, uint8_t *enetaddr)
 {
-	struct ueth_data *dev = (struct ueth_data *)eth->priv;
-	struct asix_private *priv = (struct asix_private *)dev->dev_priv;
-	int i;
 	ALLOC_CACHE_ALIGN_BUFFER(unsigned char, buf, ETH_ALEN);
+	int i;
 
 	if (priv->flags & FLAG_EEPROM_MAC) {
 		for (i = 0; i < (ETH_ALEN >> 1); i++) {
@@ -339,7 +343,7 @@ static int asix_read_mac(struct eth_device *eth)
 				debug("Failed to read SROM address 04h.\n");
 				return -1;
 			}
-			memcpy((eth->enetaddr + i * 2), buf, 2);
+			memcpy(enetaddr + i * 2, buf, 2);
 		}
 	} else {
 		if (asix_read_cmd(dev, AX_CMD_READ_NODE_ID, 0, 0, ETH_ALEN, buf)
@@ -347,7 +351,7 @@ static int asix_read_mac(struct eth_device *eth)
 			debug("Failed to read MAC address.\n");
 			return -1;
 		}
-		memcpy(eth->enetaddr, buf, ETH_ALEN);
+		memcpy(enetaddr, buf, ETH_ALEN);
 	}
 
 	return 0;
@@ -414,12 +418,8 @@ static int asix_basic_reset(struct ueth_data *dev)
 	return 0;
 }
 
-/*
- * Asix callbacks
- */
-static int asix_init(struct eth_device *eth, bd_t *bd)
+static int asix_init_common(struct ueth_data *dev)
 {
-	struct ueth_data	*dev = (struct ueth_data *)eth->priv;
 	int timeout = 0;
 #define TIMEOUT_RESOLUTION 50	/* ms */
 	int link_detected;
@@ -452,9 +452,8 @@ out_err:
 	return -1;
 }
 
-static int asix_send(struct eth_device *eth, void *packet, int length)
+static int asix_send_common(struct ueth_data *dev, void *packet, int length)
 {
-	struct ueth_data *dev = (struct ueth_data *)eth->priv;
 	int err;
 	u32 packet_len;
 	int actual_len;
@@ -481,6 +480,24 @@ static int asix_send(struct eth_device *eth, void *packet, int length)
 	return err;
 }
 
+#ifndef CONFIG_DM_ETH
+/*
+ * Asix callbacks
+ */
+static int asix_init(struct eth_device *eth, bd_t *bd)
+{
+	struct ueth_data *dev = (struct ueth_data *)eth->priv;
+
+	return asix_init_common(dev);
+}
+
+static int asix_send(struct eth_device *eth, void *packet, int length)
+{
+	struct ueth_data *dev = (struct ueth_data *)eth->priv;
+
+	return asix_send_common(dev, packet, length);
+}
+
 static int asix_recv(struct eth_device *eth)
 {
 	struct ueth_data *dev = (struct ueth_data *)eth->priv;
@@ -552,6 +569,13 @@ static void asix_halt(struct eth_device *eth)
 	debug("** %s()\n", __func__);
 }
 
+static int asix_write_hwaddr(struct eth_device *eth)
+{
+	struct ueth_data *dev = (struct ueth_data *)eth->priv;
+
+	return asix_write_hwaddr_common(dev, eth->enetaddr);
+}
+
 /*
  * Asix probing functions
  */
@@ -694,9 +718,180 @@ int asix_eth_get_info(struct usb_device *dev, struct ueth_data *ss,
 		return 0;
 
 	/* Get the MAC address */
-	if (asix_read_mac(eth))
+	if (asix_read_mac_common(ss, priv, eth->enetaddr))
 		return 0;
 	debug("MAC %pM\n", eth->enetaddr);
 
 	return 1;
 }
+#endif
+
+#ifdef CONFIG_DM_ETH
+static int asix_eth_start(struct udevice *dev)
+{
+	struct asix_private *priv = dev_get_priv(dev);
+
+	return asix_init_common(&priv->ueth);
+}
+
+void asix_eth_stop(struct udevice *dev)
+{
+	debug("** %s()\n", __func__);
+}
+
+int asix_eth_send(struct udevice *dev, void *packet, int length)
+{
+	struct asix_private *priv = dev_get_priv(dev);
+
+	return asix_send_common(&priv->ueth, packet, length);
+}
+
+int asix_eth_recv(struct udevice *dev, int flags, uchar **packetp)
+{
+	struct asix_private *priv = dev_get_priv(dev);
+	struct ueth_data *ueth = &priv->ueth;
+	uint8_t *ptr;
+	int ret, len;
+	u32 packet_len;
+
+	len = usb_ether_get_rx_bytes(ueth, &ptr);
+	debug("%s: first try, len=%d\n", __func__, len);
+	if (!len) {
+		if (!(flags & ETH_RECV_CHECK_DEVICE))
+			return -EAGAIN;
+		ret = usb_ether_receive(ueth, AX_RX_URB_SIZE);
+		if (ret == -EAGAIN)
+			return ret;
+
+		len = usb_ether_get_rx_bytes(ueth, &ptr);
+		debug("%s: second try, len=%d\n", __func__, len);
+	}
+
+	/*
+	 * 1st 4 bytes contain the length of the actual data as two
+	 * complementary 16-bit words. Extract the length of the data.
+	 */
+	if (len < sizeof(packet_len)) {
+		debug("Rx: incomplete packet length\n");
+		goto err;
+	}
+	memcpy(&packet_len, ptr, sizeof(packet_len));
+	le32_to_cpus(&packet_len);
+	if (((~packet_len >> 16) & 0x7ff) != (packet_len & 0x7ff)) {
+		debug("Rx: malformed packet length: %#x (%#x:%#x)\n",
+		      packet_len, (~packet_len >> 16) & 0x7ff,
+		      packet_len & 0x7ff);
+		goto err;
+	}
+	packet_len = packet_len & 0x7ff;
+	if (packet_len > len - sizeof(packet_len)) {
+		debug("Rx: too large packet: %d\n", packet_len);
+		goto err;
+	}
+
+	*packetp = ptr + sizeof(packet_len);
+	return packet_len;
+
+err:
+	usb_ether_advance_rxbuf(ueth, -1);
+	return -EINVAL;
+}
+
+static int asix_free_pkt(struct udevice *dev, uchar *packet, int packet_len)
+{
+	struct asix_private *priv = dev_get_priv(dev);
+
+	if (packet_len & 1)
+		packet_len++;
+	usb_ether_advance_rxbuf(&priv->ueth, sizeof(u32) + packet_len);
+
+	return 0;
+}
+
+int asix_write_hwaddr(struct udevice *dev)
+{
+	struct eth_pdata *pdata = dev_get_platdata(dev);
+	struct asix_private *priv = dev_get_priv(dev);
+
+	if (priv->flags & FLAG_TYPE_AX88172)
+		return -ENOSYS;
+
+	return asix_write_hwaddr_common(&priv->ueth, pdata->enetaddr);
+}
+
+static int asix_eth_probe(struct udevice *dev)
+{
+	struct eth_pdata *pdata = dev_get_platdata(dev);
+	struct asix_private *priv = dev_get_priv(dev);
+	struct ueth_data *ss = &priv->ueth;
+	int ret;
+
+	priv->flags = dev->driver_data;
+	ret = usb_ether_register(dev, ss, AX_RX_URB_SIZE);
+	if (ret)
+		return ret;
+
+	ret = asix_basic_reset(ss);
+	if (ret)
+		goto err;
+
+	/* Get the MAC address */
+	ret = asix_read_mac_common(ss, priv, pdata->enetaddr);
+	if (ret)
+		goto err;
+	debug("MAC %pM\n", pdata->enetaddr);
+
+	return 0;
+
+err:
+	return usb_ether_deregister(ss);
+}
+
+static const struct eth_ops asix_eth_ops = {
+	.start	= asix_eth_start,
+	.send	= asix_eth_send,
+	.recv	= asix_eth_recv,
+	.free_pkt = asix_free_pkt,
+	.stop	= asix_eth_stop,
+	.write_hwaddr = asix_write_hwaddr,
+};
+
+U_BOOT_DRIVER(asix_eth) = {
+	.name	= "asix_eth",
+	.id	= UCLASS_ETH,
+	.probe = asix_eth_probe,
+	.ops	= &asix_eth_ops,
+	.priv_auto_alloc_size = sizeof(struct asix_private),
+	.platdata_auto_alloc_size = sizeof(struct eth_pdata),
+};
+
+static const struct usb_device_id asix_eth_id_table[] = {
+	/* Apple USB Ethernet Adapter */
+	{ USB_DEVICE(0x05ac, 0x1402), .driver_info = FLAG_TYPE_AX88772 },
+	/* D-Link DUB-E100 H/W Ver B1 */
+	{ USB_DEVICE(0x07d1, 0x3c05), .driver_info = FLAG_TYPE_AX88772 },
+	/* D-Link DUB-E100 H/W Ver C1 */
+	{ USB_DEVICE(0x2001, 0x1a02), .driver_info = FLAG_TYPE_AX88772 },
+	/* Cables-to-Go USB Ethernet Adapter */
+	{ USB_DEVICE(0x0b95, 0x772a), .driver_info = FLAG_TYPE_AX88772 },
+	/* Trendnet TU2-ET100 V3.0R */
+	{ USB_DEVICE(0x0b95, 0x7720), .driver_info = FLAG_TYPE_AX88772 },
+	/* SMC */
+	{ USB_DEVICE(0x0b95, 0x1720), .driver_info = FLAG_TYPE_AX88172 },
+	/* MSI - ASIX 88772a */
+	{ USB_DEVICE(0x0db0, 0xa877), .driver_info = FLAG_TYPE_AX88772 },
+	/* Linksys 200M v2.1 */
+	{ USB_DEVICE(0x13b1, 0x0018), .driver_info = FLAG_TYPE_AX88172 },
+	/* 0Q0 cable ethernet */
+	{ USB_DEVICE(0x1557, 0x7720), .driver_info = FLAG_TYPE_AX88772 },
+	/* DLink DUB-E100 H/W Ver B1 Alternate */
+	{ USB_DEVICE(0x2001, 0x3c05), .driver_info = FLAG_TYPE_AX88772 },
+	/* ASIX 88772B */
+	{ USB_DEVICE(0x0b95, 0x772b),
+		.driver_info = FLAG_TYPE_AX88772B | FLAG_EEPROM_MAC },
+	{ USB_DEVICE(0x0b95, 0x7e2b), .driver_info = FLAG_TYPE_AX88772B },
+	{ }		/* Terminating entry */
+};
+
+U_BOOT_USB_DEVICE(asix_eth, asix_eth_id_table);
+#endif
Signature · 1 line
2.4.3.573.g4eafbef
#43 lore raw

PATCH 15/18 dm: usb: eth: Add driver-model support to the asix driver

Quoted 9 lines · Simon Glass

This USB Ethernet driver is quite widely use. Allow it to work with CONFIG_DM_ETH enabled. Most of the code remains common but there is a new packet receive flow which is handled specially.

---

 drivers/usb/eth/asix.c | 237 ++++++++++++++++++++++++++++++++++++++++++++-----
 1 file changed, 216 insertions(+), 21 deletions(-)

Applied to u-boot-dm.

#44 lore raw

PATCH 16/18 net: Allow drivers to return -ENOSYS with the write_hwaddr() method

In reply to Simon Glass

Some drivers may want to implement this method for some of their devices but not for others. So it is not possible to just leave the operation out of the table. Drivers could get around this by masquerading as two separate drivers but that seems unpleasant.

Allow the driver to return an error when it does not want to process the write_hwaddr() method.

---

 include/net.h | 4 +++-
 net/eth.c     | 6 ++++++
 2 files changed, 9 insertions(+), 1 deletion(-)
include/net.h +3 -1
index b9c13f2..d09bec9 100644
@@ -119,7 +119,9 @@ enum eth_recv_flags {
  * mcast: Join or leave a multicast group (for TFTP) - optional
  * write_hwaddr: Write a MAC address to the hardware (used to pass it to Linux
  *		 on some platforms like ARM). This function expects the
- *		 eth_pdata::enetaddr field to be populated - optional
+ *		 eth_pdata::enetaddr field to be populated. The method can
+ *		 return -ENOSYS to indicate that this is not implemented for
+		 this hardware - optional.
  * read_rom_hwaddr: Some devices have a backup of the MAC address stored in a
  *		    ROM on the board. This is how the driver should expose it
  *		    to the network stack. This function should fill in the
net/eth.c +6 -0
index 72ce91c..d3ec8d6 100644
@@ -287,7 +287,13 @@ static int eth_write_hwaddr(struct udevice *dev)
 			return -EINVAL;
 		}
 
+		/*
+		 * Drivers are allowed to decide not to implement this at
+		 * run-time. E.g. Some devices may use it and some may not.
+		 */
 		ret = eth_get_ops(dev)->write_hwaddr(dev);
+		if (ret == -ENOSYS)
+			ret = 0;
 		if (ret)
 			printf("\nWarning: %s failed to set MAC address\n",
 			       dev->name);
Signature · 1 line
2.4.3.573.g4eafbef
#45 lore raw

PATCH 16/18 net: Allow drivers to return -ENOSYS with the write_hwaddr() method

Quoted 8 lines · Simon Glass

Some drivers may want to implement this method for some of their devices but not for others. So it is not possible to just leave the operation out of the table. Drivers could get around this by masquerading as two separate drivers but that seems unpleasant.

Allow the driver to return an error when it does not want to process the write_hwaddr() method.

Quoted 6 lines

---

 include/net.h | 4 +++-
 net/eth.c     | 6 ++++++
 2 files changed, 9 insertions(+), 1 deletion(-)

Applied to u-boot-dm.

#46 lore raw

PATCH 17/18 x86: Convert minnowmax to use CONFIG_DM_NET

In reply to Simon Glass

Move to driver model for networking on minnowmax.

---

 board/intel/minnowmax/minnowmax.c | 6 ------
 configs/minnowmax_defconfig       | 1 +
 2 files changed, 1 insertion(+), 6 deletions(-)
board/intel/minnowmax/minnowmax.c +0 -6
index 383cae0..36f0237 100644
@@ -8,7 +8,6 @@
 #include <asm/gpio.h>
 #include <asm/ibmpc.h>
 #include <asm/pnp_def.h>
-#include <netdev.h>
 #include <smsc_lpc47m.h>
 
 #define SERIAL_DEV PNP_DEV(0x2e, 4)
@@ -32,8 +31,3 @@ void setup_pch_gpios(u16 gpiobase, const struct pch_gpio_map *gpio)
 {
 	return;
 }
-
-int board_eth_init(bd_t *bis)
-{
-	return pci_eth_init(bis);
-}
configs/minnowmax_defconfig +1 -0
index ff2bfda..bd3701e 100644
@@ -13,3 +13,4 @@ CONFIG_CMD_NET=y
 CONFIG_OF_CONTROL=y
 CONFIG_CPU=y
 CONFIG_DM_PCI=y
+CONFIG_DM_ETH=y
Signature · 1 line
2.4.3.573.g4eafbef
#47 lore raw

PATCH 17/18 x86: Convert minnowmax to use CONFIG_DM_NET

Quoted 35 lines · Simon Glass

Move to driver model for networking on minnowmax.

---

 board/intel/minnowmax/minnowmax.c | 6 ------
 configs/minnowmax_defconfig       | 1 +
 2 files changed, 1 insertion(+), 6 deletions(-)
board/intel/minnowmax/minnowmax.c +0 -6
index 383cae0..36f0237 100644
@@ -8,7 +8,6 @@
 #include <asm/gpio.h>
 #include <asm/ibmpc.h>
 #include <asm/pnp_def.h>
-#include <netdev.h>
 #include <smsc_lpc47m.h>
 
 #define SERIAL_DEV PNP_DEV(0x2e, 4)
@@ -32,8 +31,3 @@ void setup_pch_gpios(u16 gpiobase, const struct pch_gpio_map *gpio)
 {
        return;
 }
-
-int board_eth_init(bd_t *bis)
-{
-       return pci_eth_init(bis);
-}
configs/minnowmax_defconfig +0 -0
index ff2bfda..bd3701e 100644

configs/minnowmax_defconfig CONFIG_CMD_NET=y

Quoted 5 lines
configs/minnowmax_defconfig +1 -1
@@ -13,3 +13,4 @@ CONFIG_CMD_NET=y
 CONFIG_OF_CONTROL=y
 CONFIG_CPU=y
 CONFIG_DM_PCI=y
+CONFIG_DM_ETH=y
--
#48 lore raw

PATCH 18/18 x86: Convert minnowmax to use CONFIG_DM_USB

In reply to Simon Glass

Move to driver model for USB on minnowmax.

---

 configs/minnowmax_defconfig | 2 ++
 1 file changed, 2 insertions(+)
configs/minnowmax_defconfig +2 -0
index bd3701e..e1b9460 100644
@@ -14,3 +14,5 @@ CONFIG_OF_CONTROL=y
 CONFIG_CPU=y
 CONFIG_DM_PCI=y
 CONFIG_DM_ETH=y
+CONFIG_DM_USB=y
+CONFIG_USB=y
Signature · 1 line
2.4.3.573.g4eafbef
#49 lore raw

PATCH 18/18 x86: Convert minnowmax to use CONFIG_DM_USB

Quoted 12 lines · Simon Glass

Move to driver model for USB on minnowmax.

---

 configs/minnowmax_defconfig | 2 ++
 1 file changed, 2 insertions(+)
configs/minnowmax_defconfig +0 -0
index bd3701e..e1b9460 100644

configs/minnowmax_defconfig CONFIG_OF_CONTROL=y

Quoted 6 lines
configs/minnowmax_defconfig +2 -1
@@ -14,3 +14,5 @@ CONFIG_OF_CONTROL=y
 CONFIG_CPU=y
 CONFIG_DM_PCI=y
 CONFIG_DM_ETH=y
+CONFIG_DM_USB=y
+CONFIG_USB=y
--