diff options
author | José Pekkarinen <jose.pekkarinen@nokia.com> | 2016-05-18 13:18:31 +0300 |
---|---|---|
committer | José Pekkarinen <jose.pekkarinen@nokia.com> | 2016-05-18 13:42:15 +0300 |
commit | 437fd90c0250dee670290f9b714253671a990160 (patch) | |
tree | b871786c360704244a07411c69fb58da9ead4a06 /qemu/roms/ipxe/src/drivers/net/dm96xx.c | |
parent | 5bbd6fe9b8bab2a93e548c5a53b032d1939eec05 (diff) |
These changes are the raw update to qemu-2.6.
Collission happened in the following patches:
migration: do cleanup operation after completion(738df5b9)
Bug fix.(1750c932f86)
kvmclock: add a new function to update env->tsc.(b52baab2)
The code provided by the patches was already in the upstreamed
version.
Change-Id: I3cc11841a6a76ae20887b2e245710199e1ea7f9a
Signed-off-by: José Pekkarinen <jose.pekkarinen@nokia.com>
Diffstat (limited to 'qemu/roms/ipxe/src/drivers/net/dm96xx.c')
-rw-r--r-- | qemu/roms/ipxe/src/drivers/net/dm96xx.c | 671 |
1 files changed, 671 insertions, 0 deletions
diff --git a/qemu/roms/ipxe/src/drivers/net/dm96xx.c b/qemu/roms/ipxe/src/drivers/net/dm96xx.c new file mode 100644 index 000000000..58d8dd964 --- /dev/null +++ b/qemu/roms/ipxe/src/drivers/net/dm96xx.c @@ -0,0 +1,671 @@ +/* + * Copyright (C) 2015 Michael Brown <mbrown@fensystems.co.uk>. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include <string.h> +#include <unistd.h> +#include <errno.h> +#include <ipxe/ethernet.h> +#include <ipxe/usb.h> +#include <ipxe/usbnet.h> +#include "dm96xx.h" + +/** @file + * + * Davicom DM96xx USB Ethernet driver + * + */ + +/****************************************************************************** + * + * Register operations + * + ****************************************************************************** + */ + +/** + * Reset device + * + * @v dm96xx DM96xx device + * @ret rc Return status code + */ +static int dm96xx_reset ( struct dm96xx_device *dm96xx ) { + int ncr; + int rc; + + /* Reset device */ + if ( ( rc = dm96xx_write_register ( dm96xx, DM96XX_NCR, + DM96XX_NCR_RST ) ) != 0 ) { + DBGC ( dm96xx, "DM96XX %p could not reset: %s\n", + dm96xx, strerror ( rc ) ); + return rc; + } + + /* Wait for reset to complete */ + udelay ( DM96XX_RESET_DELAY_US ); + + /* Check that reset has completed */ + ncr = dm96xx_read_register ( dm96xx, DM96XX_NCR ); + if ( ncr < 0 ) { + rc = ncr; + DBGC ( dm96xx, "DM96XX %p failed to reset: %s\n", + dm96xx, strerror ( rc ) ); + return rc; + } + if ( ncr & DM96XX_NCR_RST ) { + DBGC ( dm96xx, "DM96XX %p failed to reset (NCR=%#02x)\n", + dm96xx, ncr ); + return -EIO; + } + + return 0; +} + +/** + * Read MAC address + * + * @v dm96xx DM96xx device + * @v mac MAC address to fill in + * @ret rc Return status code + */ +static int dm96xx_read_mac ( struct dm96xx_device *dm96xx, uint8_t *mac ) { + int rc; + + /* Read MAC address */ + if ( ( rc = dm96xx_read_registers ( dm96xx, DM96XX_PAR, mac, + ETH_ALEN ) ) != 0 ) { + DBGC ( dm96xx, "DM96XX %p could not read MAC address: %s\n", + dm96xx, strerror ( rc ) ); + return rc; + } + + return 0; +} + +/** + * Write MAC address + * + * @v dm96xx DM96xx device + * @v mac MAC address + * @ret rc Return status code + */ +static int dm96xx_write_mac ( struct dm96xx_device *dm96xx, uint8_t *mac ) { + int rc; + + /* Write MAC address */ + if ( ( rc = dm96xx_write_registers ( dm96xx, DM96XX_PAR, mac, + ETH_ALEN ) ) != 0 ) { + DBGC ( dm96xx, "DM96XX %p could not write MAC address: %s\n", + dm96xx, strerror ( rc ) ); + return rc; + } + + return 0; +} + +/** + * Update link status based on network status register + * + * @v dm96xx DM96xx device + * @v nsr Network status register + */ +static void dm96xx_link_nsr ( struct dm96xx_device *dm96xx, unsigned int nsr ) { + struct net_device *netdev = dm96xx->netdev; + + if ( nsr & DM96XX_NSR_LINKST ) { + if ( ! netdev_link_ok ( netdev ) ) + netdev_link_up ( netdev ); + } else { + if ( netdev_link_ok ( netdev ) ) + netdev_link_down ( netdev ); + } +} + +/** + * Get link status + * + * @v dm96xx DM96xx device + * @ret rc Return status code + */ +static int dm96xx_check_link ( struct dm96xx_device *dm96xx ) { + int nsr; + int rc; + + /* Read network status register */ + nsr = dm96xx_read_register ( dm96xx, DM96XX_NSR ); + if ( nsr < 0 ) { + rc = nsr; + DBGC ( dm96xx, "DM96XX %p could not read network status: %s\n", + dm96xx, strerror ( rc ) ); + return rc; + } + + /* Update link status */ + dm96xx_link_nsr ( dm96xx, nsr ); + + return 0; +} + +/** + * Set DM9601-compatible RX header mode + * + * @v dm96xx DM96xx device + * @ret rc Return status code + */ +static int dm96xx_rx_mode ( struct dm96xx_device *dm96xx ) { + int chipr; + int mode_ctl; + int rc; + + /* Get chip revision */ + chipr = dm96xx_read_register ( dm96xx, DM96XX_CHIPR ); + if ( chipr < 0 ) { + rc = chipr; + DBGC ( dm96xx, "DM96XX %p could not read chip revision: %s\n", + dm96xx, strerror ( rc ) ); + return rc; + } + + /* Do nothing if device is a DM9601 anyway */ + if ( chipr == DM96XX_CHIPR_9601 ) + return 0; + + /* Read current mode control */ + mode_ctl = dm96xx_read_register ( dm96xx, DM96XX_MODE_CTL ); + if ( mode_ctl < 0 ) { + rc = mode_ctl; + DBGC ( dm96xx, "DM96XX %p could not read mode control: %s\n", + dm96xx, strerror ( rc ) ); + return rc; + } + + /* Write mode control */ + mode_ctl &= ~DM96XX_MODE_CTL_MODE; + if ( ( rc = dm96xx_write_register ( dm96xx, DM96XX_MODE_CTL, + mode_ctl ) ) != 0 ) { + DBGC ( dm96xx, "DM96XX %p could not write mode control: %s\n", + dm96xx, strerror ( rc ) ); + return rc; + } + + return 0; +} + +/****************************************************************************** + * + * Endpoint operations + * + ****************************************************************************** + */ + +/** + * Complete interrupt transfer + * + * @v ep USB endpoint + * @v iobuf I/O buffer + * @v rc Completion status code + */ +static void dm96xx_intr_complete ( struct usb_endpoint *ep, + struct io_buffer *iobuf, int rc ) { + struct dm96xx_device *dm96xx = container_of ( ep, struct dm96xx_device, + usbnet.intr ); + struct net_device *netdev = dm96xx->netdev; + struct dm96xx_interrupt *intr; + size_t len = iob_len ( iobuf ); + + /* Ignore packets cancelled when the endpoint closes */ + if ( ! ep->open ) + goto done; + + /* Record USB errors against the network device */ + if ( rc != 0 ) { + DBGC ( dm96xx, "DM96XX %p interrupt failed: %s\n", + dm96xx, strerror ( rc ) ); + DBGC_HDA ( dm96xx, 0, iobuf->data, iob_len ( iobuf ) ); + netdev_rx_err ( netdev, NULL, rc ); + goto done; + } + + /* Extract message header */ + if ( len < sizeof ( *intr ) ) { + DBGC ( dm96xx, "DM96XX %p underlength interrupt:\n", dm96xx ); + DBGC_HDA ( dm96xx, 0, iobuf->data, iob_len ( iobuf ) ); + netdev_rx_err ( netdev, NULL, -EINVAL ); + goto done; + } + intr = iobuf->data; + + /* Update link status */ + dm96xx_link_nsr ( dm96xx, intr->nsr ); + + done: + /* Free I/O buffer */ + free_iob ( iobuf ); +} + +/** Interrupt endpoint operations */ +static struct usb_endpoint_driver_operations dm96xx_intr_operations = { + .complete = dm96xx_intr_complete, +}; + +/** + * Complete bulk IN transfer + * + * @v ep USB endpoint + * @v iobuf I/O buffer + * @v rc Completion status code + */ +static void dm96xx_in_complete ( struct usb_endpoint *ep, + struct io_buffer *iobuf, int rc ) { + struct dm96xx_device *dm96xx = container_of ( ep, struct dm96xx_device, + usbnet.in ); + struct net_device *netdev = dm96xx->netdev; + struct dm96xx_rx_header *header; + + /* Ignore packets cancelled when the endpoint closes */ + if ( ! ep->open ) { + free_iob ( iobuf ); + return; + } + + /* Record USB errors against the network device */ + if ( rc != 0 ) { + DBGC ( dm96xx, "DM96XX %p bulk IN failed: %s\n", + dm96xx, strerror ( rc ) ); + goto err; + } + + /* Sanity check */ + if ( iob_len ( iobuf ) < ( sizeof ( *header ) + 4 /* CRC */ ) ) { + DBGC ( dm96xx, "DM96XX %p underlength bulk IN\n", dm96xx ); + DBGC_HDA ( dm96xx, 0, iobuf->data, iob_len ( iobuf ) ); + rc = -EINVAL; + goto err; + } + + /* Strip header and CRC */ + header = iobuf->data; + iob_pull ( iobuf, sizeof ( *header ) ); + iob_unput ( iobuf, 4 /* CRC */ ); + + /* Check status */ + if ( header->rsr & ~DM96XX_RSR_MF ) { + DBGC ( dm96xx, "DM96XX %p receive error %02x:\n", + dm96xx, header->rsr ); + DBGC_HDA ( dm96xx, 0, iobuf->data, iob_len ( iobuf ) ); + rc = -EIO; + goto err; + } + + /* Hand off to network stack */ + netdev_rx ( netdev, iob_disown ( iobuf ) ); + return; + + err: + /* Hand off to network stack */ + netdev_rx_err ( netdev, iob_disown ( iobuf ), rc ); +} + +/** Bulk IN endpoint operations */ +static struct usb_endpoint_driver_operations dm96xx_in_operations = { + .complete = dm96xx_in_complete, +}; + +/** + * Transmit packet + * + * @v dm96xx DM96xx device + * @v iobuf I/O buffer + * @ret rc Return status code + */ +static int dm96xx_out_transmit ( struct dm96xx_device *dm96xx, + struct io_buffer *iobuf ) { + struct dm96xx_tx_header *header; + size_t len = iob_len ( iobuf ); + int rc; + + /* Prepend header */ + if ( ( rc = iob_ensure_headroom ( iobuf, sizeof ( *header ) ) ) != 0 ) + return rc; + header = iob_push ( iobuf, sizeof ( *header ) ); + header->len = cpu_to_le16 ( len ); + + /* Enqueue I/O buffer */ + if ( ( rc = usb_stream ( &dm96xx->usbnet.out, iobuf, 0 ) ) != 0 ) + return rc; + + return 0; +} + +/** + * Complete bulk OUT transfer + * + * @v ep USB endpoint + * @v iobuf I/O buffer + * @v rc Completion status code + */ +static void dm96xx_out_complete ( struct usb_endpoint *ep, + struct io_buffer *iobuf, int rc ) { + struct dm96xx_device *dm96xx = container_of ( ep, struct dm96xx_device, + usbnet.out ); + struct net_device *netdev = dm96xx->netdev; + + /* Report TX completion */ + netdev_tx_complete_err ( netdev, iobuf, rc ); +} + +/** Bulk OUT endpoint operations */ +static struct usb_endpoint_driver_operations dm96xx_out_operations = { + .complete = dm96xx_out_complete, +}; + +/****************************************************************************** + * + * Network device interface + * + ****************************************************************************** + */ + +/** + * Open network device + * + * @v netdev Network device + * @ret rc Return status code + */ +static int dm96xx_open ( struct net_device *netdev ) { + struct dm96xx_device *dm96xx = netdev->priv; + unsigned int rcr; + int rc; + + /* Set DM9601-compatible RX header mode */ + if ( ( rc = dm96xx_rx_mode ( dm96xx ) ) != 0 ) + goto err_rx_mode; + + /* Write MAC address */ + if ( ( rc = dm96xx_write_mac ( dm96xx, netdev->ll_addr ) ) != 0 ) + goto err_write_mac; + + /* Open USB network device */ + if ( ( rc = usbnet_open ( &dm96xx->usbnet ) ) != 0 ) { + DBGC ( dm96xx, "DM96XX %p could not open: %s\n", + dm96xx, strerror ( rc ) ); + goto err_open; + } + + /* Set receive filters */ + rcr = ( DM96XX_RCR_ALL | DM96XX_RCR_RUNT | DM96XX_RCR_PRMSC | + DM96XX_RCR_RXEN ); + if ( ( rc = dm96xx_write_register ( dm96xx, DM96XX_RCR, rcr ) ) != 0 ) { + DBGC ( dm96xx, "DM96XX %p could not write receive filters: " + "%s\n", dm96xx, strerror ( rc ) ); + goto err_write_rcr; + } + + /* Update link status */ + if ( ( rc = dm96xx_check_link ( dm96xx ) ) != 0 ) + goto err_check_link; + + return 0; + + err_check_link: + err_write_rcr: + usbnet_close ( &dm96xx->usbnet ); + err_open: + err_write_mac: + err_rx_mode: + return rc; +} + +/** + * Close network device + * + * @v netdev Network device + */ +static void dm96xx_close ( struct net_device *netdev ) { + struct dm96xx_device *dm96xx = netdev->priv; + + /* Close USB network device */ + usbnet_close ( &dm96xx->usbnet ); + + /* Reset device */ + dm96xx_reset ( dm96xx ); +} + +/** + * Transmit packet + * + * @v netdev Network device + * @v iobuf I/O buffer + * @ret rc Return status code + */ +static int dm96xx_transmit ( struct net_device *netdev, + struct io_buffer *iobuf ) { + struct dm96xx_device *dm96xx = netdev->priv; + int rc; + + /* Transmit packet */ + if ( ( rc = dm96xx_out_transmit ( dm96xx, iobuf ) ) != 0 ) + return rc; + + return 0; +} + +/** + * Poll for completed and received packets + * + * @v netdev Network device + */ +static void dm96xx_poll ( struct net_device *netdev ) { + struct dm96xx_device *dm96xx = netdev->priv; + int rc; + + /* Poll USB bus */ + usb_poll ( dm96xx->bus ); + + /* Refill endpoints */ + if ( ( rc = usbnet_refill ( &dm96xx->usbnet ) ) != 0 ) + netdev_rx_err ( netdev, NULL, rc ); +} + +/** DM96xx network device operations */ +static struct net_device_operations dm96xx_operations = { + .open = dm96xx_open, + .close = dm96xx_close, + .transmit = dm96xx_transmit, + .poll = dm96xx_poll, +}; + +/****************************************************************************** + * + * USB interface + * + ****************************************************************************** + */ + +/** + * Probe device + * + * @v func USB function + * @v config Configuration descriptor + * @ret rc Return status code + */ +static int dm96xx_probe ( struct usb_function *func, + struct usb_configuration_descriptor *config ) { + struct usb_device *usb = func->usb; + struct net_device *netdev; + struct dm96xx_device *dm96xx; + int rc; + + /* Allocate and initialise structure */ + netdev = alloc_etherdev ( sizeof ( *dm96xx ) ); + if ( ! netdev ) { + rc = -ENOMEM; + goto err_alloc; + } + netdev_init ( netdev, &dm96xx_operations ); + netdev->dev = &func->dev; + dm96xx = netdev->priv; + memset ( dm96xx, 0, sizeof ( *dm96xx ) ); + dm96xx->usb = usb; + dm96xx->bus = usb->port->hub->bus; + dm96xx->netdev = netdev; + usbnet_init ( &dm96xx->usbnet, func, &dm96xx_intr_operations, + &dm96xx_in_operations, &dm96xx_out_operations ); + usb_refill_init ( &dm96xx->usbnet.intr, 0, DM96XX_INTR_MAX_FILL ); + usb_refill_init ( &dm96xx->usbnet.in, DM96XX_IN_MTU, + DM96XX_IN_MAX_FILL ); + DBGC ( dm96xx, "DM96XX %p on %s\n", dm96xx, func->name ); + + /* Describe USB network device */ + if ( ( rc = usbnet_describe ( &dm96xx->usbnet, config ) ) != 0 ) { + DBGC ( dm96xx, "DM96XX %p could not describe: %s\n", + dm96xx, strerror ( rc ) ); + goto err_describe; + } + + /* Reset device */ + if ( ( rc = dm96xx_reset ( dm96xx ) ) != 0 ) + goto err_reset; + + /* Read MAC address */ + if ( ( rc = dm96xx_read_mac ( dm96xx, netdev->hw_addr ) ) != 0 ) + goto err_read_mac; + + /* Get initial link status */ + if ( ( rc = dm96xx_check_link ( dm96xx ) ) != 0 ) + goto err_check_link; + + /* Register network device */ + if ( ( rc = register_netdev ( netdev ) ) != 0 ) + goto err_register; + + usb_func_set_drvdata ( func, netdev ); + return 0; + + unregister_netdev ( netdev ); + err_register: + err_check_link: + err_read_mac: + err_reset: + err_describe: + netdev_nullify ( netdev ); + netdev_put ( netdev ); + err_alloc: + return rc; +} + +/** + * Remove device + * + * @v func USB function + */ +static void dm96xx_remove ( struct usb_function *func ) { + struct net_device *netdev = usb_func_get_drvdata ( func ); + + unregister_netdev ( netdev ); + netdev_nullify ( netdev ); + netdev_put ( netdev ); +} + +/** DM96xx device IDs */ +static struct usb_device_id dm96xx_ids[] = { + { + .name = "dm9601-corega", + .vendor = 0x07aa, + .product = 0x9601, + }, + { + .name = "dm9601", + .vendor = 0x0a46, + .product = 0x9601, + }, + { + .name = "zt6688", + .vendor = 0x0a46, + .product = 0x6688, + }, + { + .name = "st268", + .vendor = 0x0a46, + .product = 0x0268, + }, + { + .name = "adm8515", + .vendor = 0x0a46, + .product = 0x8515, + }, + { + .name = "dm9601-hirose", + .vendor = 0x0a47, + .product = 0x9601, + }, + { + .name = "dm9601-8101", + .vendor = 0x0fe6, + .product = 0x8101, + }, + { + .name = "dm9601-9700", + .vendor = 0x0fe6, + .product = 0x9700, + }, + { + .name = "dm9000e", + .vendor = 0x0a46, + .product = 0x9000, + }, + { + .name = "dm9620", + .vendor = 0x0a46, + .product = 0x9620, + }, + { + .name = "dm9621A", + .vendor = 0x0a46, + .product = 0x9621, + }, + { + .name = "dm9622", + .vendor = 0x0a46, + .product = 0x9622, + }, + { + .name = "dm962Oa", + .vendor = 0x0a46, + .product = 0x0269, + }, + { + .name = "dm9621a", + .vendor = 0x0a46, + .product = 0x1269, + }, +}; + +/** Davicom DM96xx driver */ +struct usb_driver dm96xx_driver __usb_driver = { + .ids = dm96xx_ids, + .id_count = ( sizeof ( dm96xx_ids ) / sizeof ( dm96xx_ids[0] ) ), + .probe = dm96xx_probe, + .remove = dm96xx_remove, +}; |