[U-Boot] [PATCH 00/18] 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(-)

These should finish with a newline like the others.
Signed-off-by: Simon Glass sjg@chromium.org ---
drivers/core/device.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/drivers/core/device.c b/drivers/core/device.c index 85fd1fc..eebb53c 100644 --- a/drivers/core/device.c +++ b/drivers/core/device.c @@ -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; }

On 6 July 2015 at 16:47, Simon Glass sjg@chromium.org wrote:
These should finish with a newline like the others.
Signed-off-by: Simon Glass sjg@chromium.org
drivers/core/device.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
Applied to u-boot-dm.

Add a hyphen to correct the grammar.
Signed-off-by: Simon Glass sjg@chromium.org ---
drivers/usb/host/ehci-hcd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index bf02221..b040f12 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -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; }

On Tuesday, July 07, 2015 at 12:47:41 AM, Simon Glass wrote:
Add a hyphen to correct the grammar.
Signed-off-by: Simon Glass sjg@chromium.org
Acked by: Marek Vasut marex@denx.de
Best regards, Marek Vasut

On 6 July 2015 at 17:16, Marek Vasut marex@denx.de wrote:
On Tuesday, July 07, 2015 at 12:47:41 AM, Simon Glass wrote:
Add a hyphen to correct the grammar.
Signed-off-by: Simon Glass sjg@chromium.org
Acked by: Marek Vasut marex@denx.de
Best regards, Marek Vasut
Applied to u-boot-dm.

A few drivers still write out the license in full. Fix these.
Signed-off-by: Simon Glass sjg@chromium.org ---
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(-)
diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index b040f12..3a0d32e 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -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> diff --git a/drivers/usb/host/ehci-pci.c b/drivers/usb/host/ehci-pci.c index b9eabc5..3d333bd 100644 --- a/drivers/usb/host/ehci-pci.c +++ b/drivers/usb/host/ehci-pci.c @@ -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> diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h index 774282d..3379c29 100644 --- a/drivers/usb/host/ehci.h +++ b/drivers/usb/host/ehci.h @@ -3,20 +3,7 @@ * Copyright (c) 2008, Michael Trimarchi trimarchimichael@yahoo.it * 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 diff --git a/drivers/usb/host/r8a66597-hcd.c b/drivers/usb/host/r8a66597-hcd.c index 6f33456..373e04c 100644 --- a/drivers/usb/host/r8a66597-hcd.c +++ b/drivers/usb/host/r8a66597-hcd.c @@ -3,19 +3,7 @@ * * Copyright (C) 2008 Yoshihiro Shimoda shimoda.yoshihiro@renesas.com * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; 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> diff --git a/drivers/usb/host/r8a66597.h b/drivers/usb/host/r8a66597.h index ca1b671..67dc3c4 100644 --- a/drivers/usb/host/r8a66597.h +++ b/drivers/usb/host/r8a66597.h @@ -3,19 +3,7 @@ * * Copyright (C) 2008 Yoshihiro Shimoda shimoda.yoshihiro@renesas.com * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; 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__

On Tuesday, July 07, 2015 at 12:47:42 AM, Simon Glass wrote:
A few drivers still write out the license in full. Fix these.
Signed-off-by: Simon Glass sjg@chromium.org
Thanks!
Acked-by: Marek Vasut marex@denx.de
Best regards, Marek Vasut

On 6 July 2015 at 17:17, Marek Vasut marex@denx.de wrote:
On Tuesday, July 07, 2015 at 12:47:42 AM, Simon Glass wrote:
A few drivers still write out the license in full. Fix these.
Signed-off-by: Simon Glass sjg@chromium.org
Thanks!
Acked-by: Marek Vasut marex@denx.de
Best regards, Marek Vasut
Applied to u-boot-dm.

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.
Signed-off-by: Simon Glass sjg@chromium.org ---
arch/x86/cpu/interrupts.c | 2 ++ 1 file changed, 2 insertions(+)
diff --git a/arch/x86/cpu/interrupts.c b/arch/x86/cpu/interrupts.c index c777d36..cff004e 100644 --- a/arch/x86/cpu/interrupts.c +++ b/arch/x86/cpu/interrupts.c @@ -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);

Hi Simon,
On Tue, Jul 7, 2015 at 6:47 AM, Simon Glass sjg@chromium.org wrote:
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.
Signed-off-by: Simon Glass sjg@chromium.org
Some nits below:
arch/x86/cpu/interrupts.c | 2 ++ 1 file changed, 2 insertions(+)
diff --git a/arch/x86/cpu/interrupts.c b/arch/x86/cpu/interrupts.c index c777d36..cff004e 100644 --- a/arch/x86/cpu/interrupts.c +++ b/arch/x86/cpu/interrupts.c @@ -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?
printf("EAX: %08lx EBX: %08lx ECX: %08lx EDX: %08lx\n", regs->eax, regs->ebx, regs->ecx, regs->edx);
--
Regards, Bin

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.
Signed-off-by: Simon Glass sjg@chromium.org ---
drivers/pci/pci-uclass.c | 129 ++++++++++++++++++++++++++++++++++++++++++----- include/pci.h | 79 ++++++++++++++++++++++++++++- 2 files changed, 193 insertions(+), 15 deletions(-)
diff --git a/drivers/pci/pci-uclass.c b/drivers/pci/pci-uclass.c index 5b91fe3..41daa0d 100644 --- a/drivers/pci/pci-uclass.c +++ b/drivers/pci/pci-uclass.c @@ -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; diff --git a/include/pci.h b/include/pci.h index 3af511b..d21fa8b 100644 --- a/include/pci.h +++ b/include/pci.h @@ -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 */ };
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 */
#endif /* __ASSEMBLY__ */ #endif /* _PCI_H */

Hi Simon,
On Mon, Jul 6, 2015 at 5:47 PM, Simon Glass sjg@chromium.org wrote:
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.
Signed-off-by: Simon Glass sjg@chromium.org
Reviewed-by: Joe Hershberger joe.hershberger@ni.com

Hi Simon,
On Tue, Jul 7, 2015 at 6:47 AM, Simon Glass sjg@chromium.org wrote:
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.
Signed-off-by: Simon Glass sjg@chromium.org
drivers/pci/pci-uclass.c | 129 ++++++++++++++++++++++++++++++++++++++++++----- include/pci.h | 79 ++++++++++++++++++++++++++++- 2 files changed, 193 insertions(+), 15 deletions(-)
diff --git a/drivers/pci/pci-uclass.c b/drivers/pci/pci-uclass.c index 5b91fe3..41daa0d 100644 --- a/drivers/pci/pci-uclass.c +++ b/drivers/pci/pci-uclass.c @@ -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;
diff --git a/include/pci.h b/include/pci.h index 3af511b..d21fa8b 100644 --- a/include/pci.h +++ b/include/pci.h @@ -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.
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 */
#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

Hi Bin,
On 21 July 2015 at 10:12, Bin Meng bmeng.cn@gmail.com wrote:
Hi Simon,
On Tue, Jul 7, 2015 at 6:47 AM, Simon Glass sjg@chromium.org wrote:
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.
Signed-off-by: Simon Glass sjg@chromium.org
drivers/pci/pci-uclass.c | 129 ++++++++++++++++++++++++++++++++++++++++++----- include/pci.h | 79 ++++++++++++++++++++++++++++- 2 files changed, 193 insertions(+), 15 deletions(-)
diff --git a/drivers/pci/pci-uclass.c b/drivers/pci/pci-uclass.c index 5b91fe3..41daa0d 100644 --- a/drivers/pci/pci-uclass.c +++ b/drivers/pci/pci-uclass.c @@ -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;
diff --git a/include/pci.h b/include/pci.h index 3af511b..d21fa8b 100644 --- a/include/pci.h +++ b/include/pci.h @@ -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.
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.
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 */
#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

Hi Simon,
On Wed, Jul 22, 2015 at 6:00 AM, Simon Glass sjg@chromium.org wrote:
Hi Bin,
On 21 July 2015 at 10:12, Bin Meng bmeng.cn@gmail.com wrote:
Hi Simon,
On Tue, Jul 7, 2015 at 6:47 AM, Simon Glass sjg@chromium.org wrote:
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.
Signed-off-by: Simon Glass sjg@chromium.org
drivers/pci/pci-uclass.c | 129 ++++++++++++++++++++++++++++++++++++++++++----- include/pci.h | 79 ++++++++++++++++++++++++++++- 2 files changed, 193 insertions(+), 15 deletions(-)
diff --git a/drivers/pci/pci-uclass.c b/drivers/pci/pci-uclass.c index 5b91fe3..41daa0d 100644 --- a/drivers/pci/pci-uclass.c +++ b/drivers/pci/pci-uclass.c @@ -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;
diff --git a/include/pci.h b/include/pci.h index 3af511b..d21fa8b 100644 --- a/include/pci.h +++ b/include/pci.h @@ -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.
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.
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 */
#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

Hi Bin,
On 22 July 2015 at 00:05, Bin Meng bmeng.cn@gmail.com wrote:
Hi Simon,
On Wed, Jul 22, 2015 at 6:00 AM, Simon Glass sjg@chromium.org wrote:
Hi Bin,
On 21 July 2015 at 10:12, Bin Meng bmeng.cn@gmail.com wrote:
Hi Simon,
On Tue, Jul 7, 2015 at 6:47 AM, Simon Glass sjg@chromium.org wrote:
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.
Signed-off-by: Simon Glass sjg@chromium.org
drivers/pci/pci-uclass.c | 129 ++++++++++++++++++++++++++++++++++++++++++----- include/pci.h | 79 ++++++++++++++++++++++++++++- 2 files changed, 193 insertions(+), 15 deletions(-)
diff --git a/drivers/pci/pci-uclass.c b/drivers/pci/pci-uclass.c index 5b91fe3..41daa0d 100644 --- a/drivers/pci/pci-uclass.c +++ b/drivers/pci/pci-uclass.c @@ -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;
diff --git a/include/pci.h b/include/pci.h index 3af511b..d21fa8b 100644 --- a/include/pci.h +++ b/include/pci.h @@ -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.
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.
OK I see. Well I can do this as a follow-on patch.
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 */
#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.
I suspect it can if we put it in the device tree. Is that good enough?
Applied to u-boot-dm.
Regards, Simon

This driver is used by the Intel Minnowmax board. Convert it to driver model so it can use the new Ethernet implementation.
Signed-off-by: Simon Glass sjg@chromium.org ---
drivers/net/rtl8169.c | 236 +++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 187 insertions(+), 49 deletions(-)
diff --git a/drivers/net/rtl8169.c b/drivers/net/rtl8169.c index 958488c..7b6e20f 100644 --- a/drivers/net/rtl8169.c +++ b/drivers/net/rtl8169.c @@ -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

On 6 July 2015 at 16:47, Simon Glass sjg@chromium.org wrote:
This driver is used by the Intel Minnowmax board. Convert it to driver model so it can use the new Ethernet implementation.
Signed-off-by: Simon Glass sjg@chromium.org
drivers/net/rtl8169.c | 236 +++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 187 insertions(+), 49 deletions(-)
Applied to u-boot-dm.

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.
Signed-off-by: Simon Glass sjg@chromium.org ---
drivers/pci/pci-uclass.c | 15 +++++++++------ drivers/pci/pci_compat.c | 8 ++------ include/pci.h | 8 ++++++++ 3 files changed, 19 insertions(+), 12 deletions(-)
diff --git a/drivers/pci/pci-uclass.c b/drivers/pci/pci-uclass.c index 41daa0d..3be76c9 100644 --- a/drivers/pci/pci-uclass.c +++ b/drivers/pci/pci-uclass.c @@ -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__); diff --git a/drivers/pci/pci_compat.c b/drivers/pci/pci_compat.c index d6938c1..05c3510 100644 --- a/drivers/pci/pci_compat.c +++ b/drivers/pci/pci_compat.c @@ -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); } diff --git a/include/pci.h b/include/pci.h index d21fa8b..ae5c854 100644 --- a/include/pci.h +++ b/include/pci.h @@ -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

On 6 July 2015 at 16:47, Simon Glass sjg@chromium.org wrote:
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.
Signed-off-by: Simon Glass sjg@chromium.org
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.

The function documentation is incorrect. Fix it.
Signed-off-by: Simon Glass sjg@chromium.org ---
include/usb.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/include/usb.h b/include/usb.h index dca512d..1b9140f 100644 --- a/include/usb.h +++ b/include/usb.h @@ -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;

On Tuesday, July 07, 2015 at 12:47:47 AM, Simon Glass wrote:
The function documentation is incorrect. Fix it.
Signed-off-by: Simon Glass sjg@chromium.org
Acked-by: Marek Vasut marex@denx.de
Best regards, Marek Vasut

On 6 July 2015 at 17:17, Marek Vasut marex@denx.de wrote:
On Tuesday, July 07, 2015 at 12:47:47 AM, Simon Glass wrote:
The function documentation is incorrect. Fix it.
Signed-off-by: Simon Glass sjg@chromium.org
Acked-by: Marek Vasut marex@denx.de
Best regards, Marek Vasut
Applied to u-boot-dm.

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.
Signed-off-by: Simon Glass sjg@chromium.org ---
common/cmd_usb.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-)
diff --git a/common/cmd_usb.c b/common/cmd_usb.c index eab55cd..e1dba7a 100644 --- a/common/cmd_usb.c +++ b/common/cmd_usb.c @@ -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();

On Tuesday, July 07, 2015 at 12:47:48 AM, Simon Glass wrote:
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.
Signed-off-by: Simon Glass sjg@chromium.org
Well ew, but OK.
Reviewed-by: Marek Vasut marex@denx.de
Best regards, Marek Vasut

On 6 July 2015 at 17:18, Marek Vasut marex@denx.de wrote:
On Tuesday, July 07, 2015 at 12:47:48 AM, Simon Glass wrote:
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.
Signed-off-by: Simon Glass sjg@chromium.org
Well ew, but OK.
Reviewed-by: Marek Vasut marex@denx.de
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.

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.
Signed-off-by: Simon Glass sjg@chromium.org ---
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(-)
diff --git a/drivers/net/designware.c b/drivers/net/designware.c index ae51cf3..c76cc87 100644 --- a/drivers/net/designware.c +++ b/drivers/net/designware.c @@ -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);
diff --git a/drivers/net/sandbox-raw.c b/drivers/net/sandbox-raw.c index 45c3b18..5912427 100644 --- a/drivers/net/sandbox-raw.c +++ b/drivers/net/sandbox-raw.c @@ -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); diff --git a/drivers/net/sandbox.c b/drivers/net/sandbox.c index 4e083d3..6763a24 100644 --- a/drivers/net/sandbox.c +++ b/drivers/net/sandbox.c @@ -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);
diff --git a/drivers/net/sunxi_emac.c b/drivers/net/sunxi_emac.c index e939bf2..11cd0ea 100644 --- a/drivers/net/sunxi_emac.c +++ b/drivers/net/sunxi_emac.c @@ -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; diff --git a/include/net.h b/include/net.h index d17173d..b9c13f2 100644 --- a/include/net.h +++ b/include/net.h @@ -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 diff --git a/net/eth.c b/net/eth.c index 953b731..72ce91c 100644 --- a/net/eth.c +++ b/net/eth.c @@ -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 at 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)

On 6 July 2015 at 16:47, Simon Glass sjg@chromium.org wrote:
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.
Signed-off-by: Simon Glass sjg@chromium.org
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.

Hi Simon,
On Mon, Jul 6, 2015 at 5:47 PM, Simon Glass sjg@chromium.org wrote:
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.
Signed-off-by: Simon Glass sjg@chromium.org
Acked-by: Joe Hershberger joe.hershberger@ni.com

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.
Signed-off-by: Simon Glass sjg@chromium.org ---
common/cmd_usb.c | 2 +- drivers/usb/eth/usb_ether.c | 127 ++++++++++++++++++++++++++++++++++++++++++++ include/usb_ether.h | 89 +++++++++++++++++++++++++++---- 3 files changed, 206 insertions(+), 12 deletions(-)
diff --git a/common/cmd_usb.c b/common/cmd_usb.c index e1dba7a..77cb257 100644 --- a/common/cmd_usb.c +++ b/common/cmd_usb.c @@ -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
diff --git a/drivers/usb/eth/usb_ether.c b/drivers/usb/eth/usb_ether.c index c72b7e4..07d2309 100644 --- a/drivers/usb/eth/usb_ether.c +++ b/drivers/usb/eth/usb_ether.c @@ -6,11 +6,137 @@
#include <common.h> #include <dm.h> +#include <malloc.h> #include <usb.h> #include <dm/device-internal.h>
#include "usb_ether.h"
+#ifdef CONFIG_DM_ETH + +#define USB_BULK_RECV_TIMEOUT 500 + +int usb_ether_register(struct udevice *dev, struct ueth_data *ueth, int rxsize) +{ + struct usb_device *udev = dev_get_parentdata(dev); + struct usb_interface_descriptor *iface_desc; + bool ep_in_found = false, ep_out_found = false; + struct usb_interface *iface; + const int ifnum = 0; /* Always use interface 0 */ + int ret, i; + + iface = &udev->config.if_desc[ifnum]; + iface_desc = &udev->config.if_desc[ifnum].desc; + + /* Initialize the ueth_data structure with some useful info */ + ueth->ifnum = ifnum; + ueth->subclass = iface_desc->bInterfaceSubClass; + ueth->protocol = iface_desc->bInterfaceProtocol; + + /* + * We are expecting a minimum of 3 endpoints - in, out (bulk), and int. + * We will ignore any others. + */ + for (i = 0; i < iface_desc->bNumEndpoints; i++) { + int ep_addr = iface->ep_desc[i].bEndpointAddress; + + /* is it an BULK endpoint? */ + if ((iface->ep_desc[i].bmAttributes & + USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_BULK) { + if (ep_addr & USB_DIR_IN && !ep_in_found) { + ueth->ep_in = ep_addr & + USB_ENDPOINT_NUMBER_MASK; + ep_in_found = true; + } else if (!(ep_addr & USB_DIR_IN) && !ep_out_found) { + ueth->ep_out = ep_addr & + USB_ENDPOINT_NUMBER_MASK; + ep_out_found = true; + } + } + + /* is it an interrupt endpoint? */ + if ((iface->ep_desc[i].bmAttributes & + USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_INT) { + ueth->ep_int = iface->ep_desc[i].bEndpointAddress & + USB_ENDPOINT_NUMBER_MASK; + ueth->irqinterval = iface->ep_desc[i].bInterval; + } + } + debug("Endpoints In %d Out %d Int %d\n", ueth->ep_in, ueth->ep_out, + ueth->ep_int); + + /* Do some basic sanity checks, and bail if we find a problem */ + if (!ueth->ep_in || !ueth->ep_out || !ueth->ep_int) { + debug("%s: %s: Cannot find endpoints\n", __func__, dev->name); + return -ENXIO; + } + + ueth->rxsize = rxsize; + ueth->rxbuf = memalign(rxsize, ARCH_DMA_MINALIGN); + if (!ueth->rxbuf) + return -ENOMEM; + + ret = usb_set_interface(udev, iface_desc->bInterfaceNumber, ifnum); + if (ret) { + debug("%s: %s: Cannot set interface: %d\n", __func__, dev->name, + ret); + return ret; + } + ueth->pusb_dev = udev; + + return 0; +} + +int usb_ether_deregister(struct ueth_data *ueth) +{ + return 0; +} + +int usb_ether_receive(struct ueth_data *ueth, int rxsize) +{ + int actual_len; + int ret; + + if (rxsize > ueth->rxsize) + return -EINVAL; + ret = usb_bulk_msg(ueth->pusb_dev, + usb_rcvbulkpipe(ueth->pusb_dev, ueth->ep_in), + ueth->rxbuf, rxsize, &actual_len, + USB_BULK_RECV_TIMEOUT); + debug("Rx: len = %u, actual = %u, err = %d\n", rxsize, actual_len, ret); + if (ret) { + printf("Rx: failed to receive: %d\n", ret); + return ret; + } + if (actual_len > rxsize) { + debug("Rx: received too many bytes %d\n", actual_len); + return -ENOSPC; + } + ueth->rxlen = actual_len; + ueth->rxptr = 0; + + return actual_len ? 0 : -EAGAIN; +} + +void usb_ether_advance_rxbuf(struct ueth_data *ueth, int num_bytes) +{ + ueth->rxptr += num_bytes; + if (num_bytes < 0 || ueth->rxptr >= ueth->rxlen) + ueth->rxlen = 0; +} + +int usb_ether_get_rx_bytes(struct ueth_data *ueth, uint8_t **ptrp) +{ + if (!ueth->rxlen) + return 0; + + *ptrp = &ueth->rxbuf[ueth->rxptr]; + + return ueth->rxlen - ueth->rxptr; +} + +#else + typedef void (*usb_eth_before_probe)(void); typedef int (*usb_eth_probe)(struct usb_device *dev, unsigned int ifnum, struct ueth_data *ss); @@ -197,3 +323,4 @@ int usb_host_eth_scan(int mode) return 0; return -1; } +#endif diff --git a/include/usb_ether.h b/include/usb_ether.h index 23507e1..c6d1416 100644 --- a/include/usb_ether.h +++ b/include/usb_ether.h @@ -19,25 +19,91 @@ #define ETH_DATA_LEN 1500 /* Max. octets in payload */ #define ETH_FRAME_LEN PKTSIZE_ALIGN /* Max. octets in frame sans FCS */
+/* TODO(sjg@chromium.org): Remove @pusb_dev when all boards use CONFIG_DM_ETH */ struct ueth_data { /* eth info */ - struct eth_device eth_dev; /* used with eth_register */ - int phy_id; /* mii phy id */ +#ifdef CONFIG_DM_ETH + uint8_t *rxbuf; + int rxsize; + int rxlen; /* Total bytes available in rxbuf */ + int rxptr; /* Current position in rxbuf */ +#else + struct eth_device eth_dev; /* used with eth_register */ + /* driver private */ + void *dev_priv; +#endif + int phy_id; /* mii phy id */
/* usb info */ struct usb_device *pusb_dev; /* this usb_device */ - unsigned char ifnum; /* interface number */ - unsigned char ep_in; /* in endpoint */ - unsigned char ep_out; /* out ....... */ - unsigned char ep_int; /* interrupt . */ - unsigned char subclass; /* as in overview */ - unsigned char protocol; /* .............. */ + unsigned char ifnum; /* interface number */ + unsigned char ep_in; /* in endpoint */ + unsigned char ep_out; /* out ....... */ + unsigned char ep_int; /* interrupt . */ + unsigned char subclass; /* as in overview */ + unsigned char protocol; /* .............. */ unsigned char irqinterval; /* Intervall for IRQ Pipe */ - - /* driver private */ - void *dev_priv; };
+#ifdef CONFIG_DM_ETH +/** + * usb_ether_register() - register a new USB ethernet device + * + * This selects the correct USB interface and figures out the endpoints to use. + * + * @dev: USB device + * @ss: Place to put USB ethernet data + * @rxsize: Maximum size to allocate for the receive buffer + * @return 0 if OK, -ve on error + */ +int usb_ether_register(struct udevice *dev, struct ueth_data *ueth, int rxsize); + +/** + * usb_ether_deregister() - deregister a USB ethernet device + * + * @ueth: USB Ethernet device + * @return 0 + */ +int usb_ether_deregister(struct ueth_data *ueth); + +/** + * usb_ether_receive() - recieve a packet from the bulk in endpoint + * + * The packet is stored in the internal buffer ready for processing. + * + * @ueth: USB Ethernet device + * @rxsize: Maximum size to receive + * @return 0 if a packet was received, -EAGAIN if not, -ENOSPC if @rxsize is + * larger than the size passed ot usb_ether_register(), other -ve on error + */ +int usb_ether_receive(struct ueth_data *ueth, int rxsize); + +/** + * usb_ether_get_rx_bytes() - obtain bytes from the internal packet buffer + * + * This should be called repeatedly to obtain packet data until it returns 0. + * After each packet is processed, call usb_ether_advance_rxbuf() to move to + * the next one. + * + * @ueth: USB Ethernet device + * @ptrp: Returns a pointer to the start of the next packet if there is + * one available + * @return number of bytes available, or 0 if none + */ +int usb_ether_get_rx_bytes(struct ueth_data *ueth, uint8_t **ptrp); + +/** + * usb_ether_advance_rxbuf() - Advance to the next packet in the internal buffer + * + * After processing the data returned by usb_ether_get_rx_bytes(), call this + * function to move to the next packet. You must specify the number of bytes + * you have processed in @num_bytes. + * + * @ueth: USB Ethernet device + * @num_bytes: Number of bytes to skip, or -1 to skip all bytes + */ +void usb_ether_advance_rxbuf(struct ueth_data *ueth, int num_bytes); +#else /* * Function definitions for each USB ethernet driver go here * (declaration is unconditional, compilation is conditional) @@ -65,5 +131,6 @@ int smsc95xx_eth_probe(struct usb_device *dev, unsigned int ifnum, struct ueth_data *ss); int smsc95xx_eth_get_info(struct usb_device *dev, struct ueth_data *ss, struct eth_device *eth); +#endif
#endif /* __USB_ETHER_H__ */

On Tuesday, July 07, 2015 at 12:47:50 AM, Simon Glass wrote:
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.
Signed-off-by: Simon Glass sjg@chromium.org
Reviewed-by: Marek Vasut marex@denx.de
Best regards, Marek Vasut

On 6 July 2015 at 17:19, Marek Vasut marex@denx.de wrote:
On Tuesday, July 07, 2015 at 12:47:50 AM, Simon Glass wrote:
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.
Signed-off-by: Simon Glass sjg@chromium.org
Reviewed-by: Marek Vasut marex@denx.de
Best regards, Marek Vasut
Applied to u-boot-dm.

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.
Signed-off-by: Simon Glass sjg@chromium.org ---
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(-)
diff --git a/common/usb_hub.c b/common/usb_hub.c index be01f4f..f621ddb 100644 --- a/common/usb_hub.c +++ b/common/usb_hub.c @@ -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 diff --git a/common/usb_kbd.c b/common/usb_kbd.c index e2af67d..0227024 100644 --- a/common/usb_kbd.c +++ b/common/usb_kbd.c @@ -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; diff --git a/common/usb_storage.c b/common/usb_storage.c index cc9b3e3..b978430 100644 --- a/common/usb_storage.c +++ b/common/usb_storage.c @@ -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 diff --git a/drivers/usb/Kconfig b/drivers/usb/Kconfig index 637ef3d..3fa5b2e 100644 --- a/drivers/usb/Kconfig +++ b/drivers/usb/Kconfig @@ -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"
diff --git a/drivers/usb/eth/usb_ether.c b/drivers/usb/eth/usb_ether.c index 07d2309..63785a9 100644 --- a/drivers/usb/eth/usb_ether.c +++ b/drivers/usb/eth/usb_ether.c @@ -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; diff --git a/include/usb.h b/include/usb.h index 1b9140f..83a8964 100644 --- a/include/usb.h +++ b/include/usb.h @@ -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, \

On Tuesday, July 07, 2015 at 12:47:51 AM, Simon Glass wrote:
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.
Signed-off-by: Simon Glass sjg@chromium.org
Acked-by: Marek Vasut marex@denx.de
Best regards, Marek Vasut

On 6 July 2015 at 17:20, Marek Vasut marex@denx.de wrote:
On Tuesday, July 07, 2015 at 12:47:51 AM, Simon Glass wrote:
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.
Signed-off-by: Simon Glass sjg@chromium.org
Acked-by: Marek Vasut marex@denx.de
Best regards, Marek Vasut
Applied to u-boot-dm.

Now that the RTL8169 driver warning is fixed we can drop this. The incorrect value is causing problems with USB EHCI.
Signed-off-by: Simon Glass sjg@chromium.org ---
include/configs/minnowmax.h | 3 --- 1 file changed, 3 deletions(-)
diff --git a/include/configs/minnowmax.h b/include/configs/minnowmax.h index 41653ba..105b05d 100644 --- a/include/configs/minnowmax.h +++ b/include/configs/minnowmax.h @@ -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

On Tue, Jul 7, 2015 at 6:47 AM, Simon Glass sjg@chromium.org wrote:
Now that the RTL8169 driver warning is fixed we can drop this. The incorrect value is causing problems with USB EHCI.
Signed-off-by: Simon Glass sjg@chromium.org
include/configs/minnowmax.h | 3 --- 1 file changed, 3 deletions(-)
diff --git a/include/configs/minnowmax.h b/include/configs/minnowmax.h index 41653ba..105b05d 100644 --- a/include/configs/minnowmax.h +++ b/include/configs/minnowmax.h @@ -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
--
Reviewed-by: Bin Meng bmeng.cn@gmail.com

On 7 July 2015 at 02:17, Bin Meng bmeng.cn@gmail.com wrote:
On Tue, Jul 7, 2015 at 6:47 AM, Simon Glass sjg@chromium.org wrote:
Now that the RTL8169 driver warning is fixed we can drop this. The incorrect value is causing problems with USB EHCI.
Signed-off-by: Simon Glass sjg@chromium.org
include/configs/minnowmax.h | 3 --- 1 file changed, 3 deletions(-)
diff --git a/include/configs/minnowmax.h b/include/configs/minnowmax.h index 41653ba..105b05d 100644 --- a/include/configs/minnowmax.h +++ b/include/configs/minnowmax.h @@ -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
--
Reviewed-by: Bin Meng bmeng.cn@gmail.com
Applied to u-boot-dm.

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().
Signed-off-by: Simon Glass sjg@chromium.org ---
drivers/usb/host/ehci-pci.c | 97 +++++++++++++++++++++++++++++++++++---------- 1 file changed, 77 insertions(+), 20 deletions(-)
diff --git a/drivers/usb/host/ehci-pci.c b/drivers/usb/host/ehci-pci.c index 3d333bd..0cb9fcc 100644 --- a/drivers/usb/host/ehci-pci.c +++ b/drivers/usb/host/ehci-pci.c @@ -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 */

On Tuesday, July 07, 2015 at 12:47:53 AM, Simon Glass wrote:
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().
Signed-off-by: Simon Glass sjg@chromium.org
Reviewed-by: Marek Vasut marex@denx.de
Best regards, Marek Vasut

On 6 July 2015 at 17:21, Marek Vasut marex@denx.de wrote:
On Tuesday, July 07, 2015 at 12:47:53 AM, Simon Glass wrote:
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().
Signed-off-by: Simon Glass sjg@chromium.org
Reviewed-by: Marek Vasut marex@denx.de
Best regards, Marek Vasut
Applied to u-boot-dm.

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.
Signed-off-by: Simon Glass sjg@chromium.org ---
drivers/usb/eth/asix.c | 237 ++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 216 insertions(+), 21 deletions(-)
diff --git a/drivers/usb/eth/asix.c b/drivers/usb/eth/asix.c index c8697ae..72ec41e 100644 --- a/drivers/usb/eth/asix.c +++ b/drivers/usb/eth/asix.c @@ -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

On 6 July 2015 at 16:47, Simon Glass sjg@chromium.org wrote:
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.
Signed-off-by: Simon Glass sjg@chromium.org
drivers/usb/eth/asix.c | 237 ++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 216 insertions(+), 21 deletions(-)
Applied to u-boot-dm.

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.
Signed-off-by: Simon Glass sjg@chromium.org ---
include/net.h | 4 +++- net/eth.c | 6 ++++++ 2 files changed, 9 insertions(+), 1 deletion(-)
diff --git a/include/net.h b/include/net.h index b9c13f2..d09bec9 100644 --- a/include/net.h +++ b/include/net.h @@ -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 diff --git a/net/eth.c b/net/eth.c index 72ce91c..d3ec8d6 100644 --- a/net/eth.c +++ b/net/eth.c @@ -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);

On 6 July 2015 at 16:47, Simon Glass sjg@chromium.org wrote:
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.
Signed-off-by: Simon Glass sjg@chromium.org
include/net.h | 4 +++- net/eth.c | 6 ++++++ 2 files changed, 9 insertions(+), 1 deletion(-)
Applied to u-boot-dm.

Move to driver model for networking on minnowmax.
Signed-off-by: Simon Glass sjg@chromium.org ---
board/intel/minnowmax/minnowmax.c | 6 ------ configs/minnowmax_defconfig | 1 + 2 files changed, 1 insertion(+), 6 deletions(-)
diff --git a/board/intel/minnowmax/minnowmax.c b/board/intel/minnowmax/minnowmax.c index 383cae0..36f0237 100644 --- a/board/intel/minnowmax/minnowmax.c +++ b/board/intel/minnowmax/minnowmax.c @@ -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); -} diff --git a/configs/minnowmax_defconfig b/configs/minnowmax_defconfig index ff2bfda..bd3701e 100644 --- a/configs/minnowmax_defconfig +++ b/configs/minnowmax_defconfig @@ -13,3 +13,4 @@ CONFIG_CMD_NET=y CONFIG_OF_CONTROL=y CONFIG_CPU=y CONFIG_DM_PCI=y +CONFIG_DM_ETH=y

On Tue, Jul 7, 2015 at 6:47 AM, Simon Glass sjg@chromium.org wrote:
Move to driver model for networking on minnowmax.
Signed-off-by: Simon Glass sjg@chromium.org
board/intel/minnowmax/minnowmax.c | 6 ------ configs/minnowmax_defconfig | 1 + 2 files changed, 1 insertion(+), 6 deletions(-)
diff --git a/board/intel/minnowmax/minnowmax.c b/board/intel/minnowmax/minnowmax.c index 383cae0..36f0237 100644 --- a/board/intel/minnowmax/minnowmax.c +++ b/board/intel/minnowmax/minnowmax.c @@ -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);
-} diff --git a/configs/minnowmax_defconfig b/configs/minnowmax_defconfig index ff2bfda..bd3701e 100644 --- a/configs/minnowmax_defconfig +++ b/configs/minnowmax_defconfig @@ -13,3 +13,4 @@ CONFIG_CMD_NET=y CONFIG_OF_CONTROL=y CONFIG_CPU=y CONFIG_DM_PCI=y
+CONFIG_DM_ETH=y
Reviewed-by: Bin Meng bmeng.cn@gmail.com

Move to driver model for USB on minnowmax. Signed-off-by: Simon Glass sjg@chromium.org ---
configs/minnowmax_defconfig | 2 ++ 1 file changed, 2 insertions(+)
diff --git a/configs/minnowmax_defconfig b/configs/minnowmax_defconfig index bd3701e..e1b9460 100644 --- a/configs/minnowmax_defconfig +++ b/configs/minnowmax_defconfig @@ -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

On Tue, Jul 7, 2015 at 6:47 AM, Simon Glass sjg@chromium.org wrote:
Move to driver model for USB on minnowmax. Signed-off-by: Simon Glass sjg@chromium.org
configs/minnowmax_defconfig | 2 ++ 1 file changed, 2 insertions(+)
diff --git a/configs/minnowmax_defconfig b/configs/minnowmax_defconfig index bd3701e..e1b9460 100644 --- a/configs/minnowmax_defconfig +++ b/configs/minnowmax_defconfig @@ -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
Reviewed-by: Bin Meng bmeng.cn@gmail.com
participants (4)
-
Bin Meng
-
Joe Hershberger
-
Marek Vasut
-
Simon Glass