[U-Boot] [PATCH v3 2/2] usb: eth: add Realtek RTL8152B/RTL8153 driver
Marek Vasut
marex at denx.de
Tue Dec 1 16:10:14 CET 2015
On Tuesday, December 01, 2015 at 12:24:41 PM, Ted Chen wrote:
> This patch adds driver support for the Realtek RTL8152B/RTL8153 USB
> network adapters.
Hi!
looks pretty good, just a few final nitpicks below.
> Signed-off-by: Ted Chen <tedchen at realtek.com>
> [swarren, fixed a few compiler warnings]
> [swarren, with permission, converted license header to SPDX]
> [swarren, removed printf() spew during probe()]
This changelog should be removed, really. It doesn't have to be in the commit
message, right ?
> Signed-off-by: Stephen Warren <swarren at nvidia.com>
[...]
> +#include <common.h>
> +#include <errno.h>
> +#include <malloc.h>
> +#include <usb.h>
> +#include <usb/lin_gadget_compat.h>
> +#include <linux/mii.h>
> +#include <linux/bitops.h>
> +#include "usb_ether.h"
> +#include "r8152.h"
> +
> +/* local vars */
> +static int curr_eth_dev; /* index for name of next device detected */
This $curr_eth_dev doesn't seem very DM friendly. Is this really needed?
> +struct r8152_dongle {
> + unsigned short vendor;
> + unsigned short product;
> +};
> +
> +static const struct r8152_dongle const r8152_dongles[] = {
> + /* Realtek */
> + { 0x0bda, 0x8050 },
> + { 0x0bda, 0x8152 },
> + { 0x0bda, 0x8153 },
> +
> + /* Samsung */
> + { 0x04e8, 0xa101 },
> +
> + /* Lenovo */
> + { 0x17ef, 0x304f },
> + { 0x17ef, 0x3052 },
> + { 0x17ef, 0x3054 },
> + { 0x17ef, 0x3057 },
> + { 0x17ef, 0x7205 },
> + { 0x17ef, 0x720a },
> + { 0x17ef, 0x720b },
> + { 0x17ef, 0x720c },
> +
> + /* TP-LINK */
> + { 0x2357, 0x0601 },
> +
> + /* Nvidia */
> + { 0x0955, 0x09ff },
> +
> + { 0x0000, 0x0000 } /* END - Do not remove */
> +};
> +
> +static
> +int get_registers(struct r8152 *tp, u16 value, u16 index, u16 size, void
> *data) +{
> + int ret;
> +
> + ret = usb_control_msg(tp->udev, usb_rcvctrlpipe(tp->udev, 0),
> + RTL8152_REQ_GET_REGS, RTL8152_REQT_READ,
> + value, index, data, size, 500);
> +
> + return ret;
> +}
> +
> +static
> +int set_registers(struct r8152 *tp, u16 value, u16 index, u16 size, void
> *data) +{
> + int ret;
> +
> + ret = usb_control_msg(tp->udev, usb_sndctrlpipe(tp->udev, 0),
> + RTL8152_REQ_SET_REGS, RTL8152_REQT_WRITE,
> + value, index, data, size, 500);
> +
> + return ret;
> +}
> +
> +int generic_ocp_read(struct r8152 *tp, u16 index, u16 size,
> + void *data, u16 type)
> +{
> + u16 burst_size = 64;
> + int ret = 0;
int txsize;
> + /* both size and index must be 4 bytes align */
> + if ((size & 3) || !size || (index & 3) || !data)
> + return -EINVAL;
> +
> + if (index + size > 0xffff)
> + return -EINVAL;
> +
> + while (size) {
txsize = min(size, burst_size);
ret = get_registers(tp, index, type, txsize, data);
if (ret < 0)
break;
index += txsize;
data += txsize;
size =- txsize;
And you can drop this whole conditional statement. Right ?
> + if (size > burst_size) {
> + ret = get_registers(tp, index, type, burst_size, data);
> + if (ret < 0)
> + break;
> +
> + index += burst_size;
> + data += burst_size;
> + size -= burst_size;
> + } else {
> + ret = get_registers(tp, index, type, size, data);
> + if (ret < 0)
> + break;
> +
> + index += size;
> + data += size;
> + size = 0;
> + break;
> + }
> + }
> +
> + return ret;
> +}
> +
> +int generic_ocp_write(struct r8152 *tp, u16 index, u16 byteen,
> + u16 size, void *data, u16 type)
> +{
> + int ret;
> + u16 byteen_start, byteen_end, byte_en_to_hw;
> + u16 burst_size = 512;
> +
> + /* both size and index must be 4 bytes align */
> + if ((size & 3) || !size || (index & 3) || !data)
> + return -EINVAL;
> +
> + if (index + size > 0xffff)
> + return -EINVAL;
> +
> + byteen_start = byteen & BYTE_EN_START_MASK;
> + byteen_end = byteen & BYTE_EN_END_MASK;
> +
> + byte_en_to_hw = byteen_start | (byteen_start << 4);
> + ret = set_registers(tp, index, type | byte_en_to_hw, 4, data);
> + if (ret < 0)
> + return ret;
> +
> + index += 4;
> + data += 4;
> + size -= 4;
> +
> + if (size) {
> + size -= 4;
> +
> + while (size) {
> + if (size > burst_size) {
> + ret = set_registers(tp, index,
> + type | BYTE_EN_DWORD,
> + burst_size, data);
> + if (ret < 0)
> + return ret;
> +
> + index += burst_size;
> + data += burst_size;
> + size -= burst_size;
> + } else {
> + ret = set_registers(tp, index,
> + type | BYTE_EN_DWORD,
> + size, data);
> + if (ret < 0)
> + return ret;
> +
> + index += size;
> + data += size;
> + size = 0;
> + break;
DTTO here
> + }
> + }
> +
> + byte_en_to_hw = byteen_end | (byteen_end >> 4);
> + ret = set_registers(tp, index, type | byte_en_to_hw, 4, data);
> + if (ret < 0)
> + return ret;
> + }
> +
> + return ret;
> +}
[...]
> +u16 ocp_read_word(struct r8152 *tp, u16 type, u16 index)
> +{
> + u32 data;
> + __le32 tmp;
> + u8 shift = index & 2;
> +
> + index &= ~3;
> +
> + generic_ocp_read(tp, index, sizeof(tmp), &tmp, type);
> +
> + data = __le32_to_cpu(tmp);
> + data >>= (shift * 8);
> + data &= 0xffff;
Can you please review the typecasts in the driver ? I don't think that a
lot of them are really necessary.
> + return (u16)data;
> +}
[...]
> +static void rtl8152_nic_reset(struct r8152 *tp)
> +{
> + int i;
> +
> + ocp_write_byte(tp, MCU_TYPE_PLA, PLA_CR, PLA_CR_RST);
> +
> + for (i = 0; i < 1000; i++) {
> + if (!(ocp_read_byte(tp, MCU_TYPE_PLA, PLA_CR) & PLA_CR_RST))
> + break;
> +
> + udelay(400);
> + }
So what exactly happens if this function fails 1000 times ? The NIC will not
be reset, but the code will proceed with whatever other actions?
> +}
[...]
> +static void rtl_disable(struct r8152 *tp)
> +{
> + u32 ocp_data;
> + int i;
> +
> + ocp_data = ocp_read_dword(tp, MCU_TYPE_PLA, PLA_RCR);
> + ocp_data &= ~RCR_ACPT_ALL;
> + ocp_write_dword(tp, MCU_TYPE_PLA, PLA_RCR, ocp_data);
> +
> + rxdy_gated_en(tp, true);
> +
> + for (i = 0; i < 1000; i++) {
You might want to pull this magic value, 1000, into a macro, since it's being
pretty repetitive in this driver. It seems to have some sort of significance.
> + ocp_data = ocp_read_byte(tp, MCU_TYPE_PLA, PLA_OOB_CTRL);
> + if ((ocp_data & FIFO_EMPTY) == FIFO_EMPTY)
> + break;
> +
> + mdelay(2);
> + }
> +
> + for (i = 0; i < 1000; i++) {
> + if (ocp_read_word(tp, MCU_TYPE_PLA, PLA_TCR0) & TCR0_TX_EMPTY)
> + break;
> + mdelay(2);
> + }
> +
> + rtl8152_nic_reset(tp);
> +}
[...]
> +static int r8152_init(struct eth_device *eth, bd_t *bd)
> +{
> + struct ueth_data *dev = (struct ueth_data *)eth->priv;
ueth_data[SPACE]*dev please.
> + struct r8152 *tp = (struct r8152 *)dev->dev_priv;
> +
> + u8 speed;
> + int timeout = 0;
> +#define TIMEOUT_RESOLUTION 50 /* ms */
> +#define PHY_CONNECT_TIMEOUT 5000
I'd suggest to use const variable instead to leverage the typechecking.
> + int link_detected;
> +
> + debug("** %s()\n", __func__);
> +
> + do {
> + speed = rtl8152_get_speed(tp);
> +
> + link_detected = speed & LINK_STATUS;
> + if (!link_detected) {
> + if (timeout == 0)
> + printf("Waiting for Ethernet connection... ");
> + mdelay(TIMEOUT_RESOLUTION);
> + timeout += TIMEOUT_RESOLUTION;
> + }
> + } while (!link_detected && timeout < PHY_CONNECT_TIMEOUT);
> + if (link_detected) {
> + tp->rtl_ops.enable(tp);
> +
> + if (timeout != 0)
> + printf("done.\n");
> + } else {
> + printf("unable to connect.\n");
> + }
> +
> + return 0;
> +}
> +
> +static int r8152_send(struct eth_device *eth, void *packet, int length)
> +{
> + struct ueth_data *dev = (struct ueth_data *)eth->priv;
> +
> + u32 opts1, opts2 = 0;
> +
> + int err;
> +
> + int actual_len;
> + unsigned char msg[PKTSIZE + sizeof(struct tx_desc)];
> + struct tx_desc *tx_desc = (struct tx_desc *)msg;
> +
> +#define USB_BULK_SEND_TIMEOUT 5000
DTTO here and in all the places where you use #define FOO in a function.
> + debug("** %s(), len %d\n", __func__, length);
> +
> + opts1 = length | TX_FS | TX_LS;
> +
> + tx_desc->opts2 = cpu_to_le32(opts2);
> + tx_desc->opts1 = cpu_to_le32(opts1);
> +
> + memcpy(msg + sizeof(struct tx_desc), (void *)packet, length);
> +
> + err = usb_bulk_msg(dev->pusb_dev,
> + usb_sndbulkpipe(dev->pusb_dev, dev->ep_out),
> + (void *)msg,
> + length + sizeof(struct tx_desc),
> + &actual_len,
> + USB_BULK_SEND_TIMEOUT);
> + debug("Tx: len = %zu, actual = %u, err = %d\n",
> + length + sizeof(struct tx_desc), actual_len, err);
> +
> + return err;
> +}
[...]
> +/* Probe to see if a new device is actually an realtek device */
> +int r8152_eth_probe(struct usb_device *dev, unsigned int ifnum,
> + struct ueth_data *ss)
> +{
> + struct usb_interface *iface;
> + struct usb_interface_descriptor *iface_desc;
> + int ep_in_found = 0, ep_out_found = 0;
> + int i;
> +
> + struct r8152 *tp;
> +
> + /* let's examine the device now */
> + iface = &dev->config.if_desc[ifnum];
> + iface_desc = &dev->config.if_desc[ifnum].desc;
> +
> + for (i = 0; r8152_dongles[i].vendor != 0; i++) {
Maybe you can use ARRAY_SIZE() instead and avoid having 0x00 as a terminating
entry in the array . What do you think ?
> + if (dev->descriptor.idVendor == r8152_dongles[i].vendor &&
> + dev->descriptor.idProduct == r8152_dongles[i].product)
> + /* Found a supported dongle */
> + break;
> + }
> +
> + if (r8152_dongles[i].vendor == 0)
> + return 0;
> +
> + memset(ss, 0, sizeof(struct ueth_data));
> +
> + /* At this point, we know we've got a live one */
> + debug("\n\nUSB Ethernet device detected: %#04x:%#04x\n",
> + dev->descriptor.idVendor, dev->descriptor.idProduct);
> +
> + /* Initialize the ueth_data structure with some useful info */
> + ss->ifnum = ifnum;
> + ss->pusb_dev = dev;
> + ss->subclass = iface_desc->bInterfaceSubClass;
> + ss->protocol = iface_desc->bInterfaceProtocol;
> +
> + /* alloc driver private */
> + ss->dev_priv = calloc(1, sizeof(struct r8152));
> +
> + if (!ss->dev_priv)
> + return 0;
> +
> + /*
> + * 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++) {
> + /* is it an BULK endpoint? */
> + if ((iface->ep_desc[i].bmAttributes &
> + USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_BULK) {
> + u8 ep_addr = iface->ep_desc[i].bEndpointAddress;
> + if (ep_addr & USB_DIR_IN) {
> + if (!ep_in_found) {
You can probably rewrite it this way
if (!ep_in_found && (ep_addr & USB_DIR_IN)) {
do stuff ...
}
This might trim down the indentation, which looks really bad. But if it doesn't,
then don't worry about this.
> + ss->ep_in = ep_addr &
> + USB_ENDPOINT_NUMBER_MASK;
> + ep_in_found = 1;
> + }
> + } else {
> + if (!ep_out_found) {
> + ss->ep_out = ep_addr &
> + USB_ENDPOINT_NUMBER_MASK;
> + ep_out_found = 1;
> + }
> + }
> + }
> +
> + /* is it an interrupt endpoint? */
> + if ((iface->ep_desc[i].bmAttributes &
> + USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_INT) {
> + ss->ep_int = iface->ep_desc[i].bEndpointAddress &
> + USB_ENDPOINT_NUMBER_MASK;
> + ss->irqinterval = iface->ep_desc[i].bInterval;
> + }
> + }
[...]
More information about the U-Boot
mailing list