#include <linux/usb/usbnet.h>
#include <linux/slab.h>
#include <linux/of_net.h>
- #include <linux/irq.h>
- #include <linux/irqdomain.h>
#include <linux/mdio.h>
#include <linux/phy.h>
#include "smsc95xx.h"
#define SUSPEND_SUSPEND3 (0x08)
#define SUSPEND_ALLMODES (SUSPEND_SUSPEND0 | SUSPEND_SUSPEND1 | \
SUSPEND_SUSPEND2 | SUSPEND_SUSPEND3)
+#define MAC_ADDR_LEN (6)
- #define SMSC95XX_NR_IRQS (1) /* raise to 12 for GPIOs */
- #define PHY_HWIRQ (SMSC95XX_NR_IRQS - 1)
-
struct smsc95xx_priv {
u32 mac_cr;
u32 hash_hi;
spinlock_t mac_cr_lock;
u8 features;
u8 suspend_flags;
- struct irq_chip irqchip;
- struct irq_domain *irqdomain;
- struct fwnode_handle *irqfwnode;
struct mii_bus *mdiobus;
struct phy_device *phydev;
- struct task_struct *pm_task;
};
static bool turbo_mode = true;
module_param(turbo_mode, bool, 0644);
MODULE_PARM_DESC(turbo_mode, "Enable multiple frames per Rx transaction");
+static bool truesize_mode = false;
+module_param(truesize_mode, bool, 0644);
+MODULE_PARM_DESC(truesize_mode, "Report larger truesize value");
+
+static int packetsize = 2560;
+module_param(packetsize, int, 0644);
+MODULE_PARM_DESC(packetsize, "Override the RX URB packet size");
+
+static char *macaddr = ":";
+module_param(macaddr, charp, 0);
+MODULE_PARM_DESC(macaddr, "MAC address");
+
static int __must_check __smsc95xx_read_reg(struct usbnet *dev, u32 index,
u32 *data, int in_pm)
{
- struct smsc95xx_priv *pdata = dev->driver_priv;
u32 buf;
int ret;
int (*fn)(struct usbnet *, u8, u8, u16, u16, void *, u16);
BUG_ON(!dev);
- if (current != pdata->pm_task)
+ if (!in_pm)
fn = usbnet_read_cmd;
else
fn = usbnet_read_cmd_nopm;
static int __must_check __smsc95xx_write_reg(struct usbnet *dev, u32 index,
u32 data, int in_pm)
{
- struct smsc95xx_priv *pdata = dev->driver_priv;
u32 buf;
int ret;
int (*fn)(struct usbnet *, u8, u8, u16, u16, const void *, u16);
BUG_ON(!dev);
- if (current != pdata->pm_task)
+ if (!in_pm)
fn = usbnet_write_cmd;
else
fn = usbnet_write_cmd_nopm;
static void smsc95xx_status(struct usbnet *dev, struct urb *urb)
{
- struct smsc95xx_priv *pdata = dev->driver_priv;
- unsigned long flags;
u32 intdata;
if (urb->actual_length != 4) {
intdata = get_unaligned_le32(urb->transfer_buffer);
netif_dbg(dev, link, dev->net, "intdata: 0x%08X\n", intdata);
- local_irq_save(flags);
-
if (intdata & INT_ENP_PHY_INT_)
- generic_handle_domain_irq(pdata->irqdomain, PHY_HWIRQ);
+ ;
else
netdev_warn(dev->net, "unexpected interrupt, intdata=0x%08X\n",
intdata);
-
- local_irq_restore(flags);
}
/* Enable or disable Tx & Rx checksum offload engines */
return phy_mii_ioctl(netdev->phydev, rq, cmd);
}
+/* Check the macaddr module parameter for a MAC address */
+static int smsc95xx_is_macaddr_param(struct usbnet *dev, u8 *dev_mac)
+{
+ int i, j, got_num, num;
+ u8 mtbl[MAC_ADDR_LEN];
+
+ if (macaddr[0] == ':')
+ return 0;
+
+ i = 0;
+ j = 0;
+ num = 0;
+ got_num = 0;
+ while (j < MAC_ADDR_LEN) {
+ if (macaddr[i] && macaddr[i] != ':') {
+ got_num++;
+ if ('0' <= macaddr[i] && macaddr[i] <= '9')
+ num = num * 16 + macaddr[i] - '0';
+ else if ('A' <= macaddr[i] && macaddr[i] <= 'F')
+ num = num * 16 + 10 + macaddr[i] - 'A';
+ else if ('a' <= macaddr[i] && macaddr[i] <= 'f')
+ num = num * 16 + 10 + macaddr[i] - 'a';
+ else
+ break;
+ i++;
+ } else if (got_num == 2) {
+ mtbl[j++] = (u8) num;
+ num = 0;
+ got_num = 0;
+ i++;
+ } else {
+ break;
+ }
+ }
+
+ if (j == MAC_ADDR_LEN) {
+ netif_dbg(dev, ifup, dev->net, "Overriding MAC address with: "
+ "%02x:%02x:%02x:%02x:%02x:%02x\n", mtbl[0], mtbl[1], mtbl[2],
+ mtbl[3], mtbl[4], mtbl[5]);
+ for (i = 0; i < MAC_ADDR_LEN; i++)
+ dev_mac[i] = mtbl[i];
+ return 1;
+ } else {
+ return 0;
+ }
+}
+
static void smsc95xx_init_mac_address(struct usbnet *dev)
{
/* maybe the boot loader passed the MAC address in devicetree */
}
}
+ /* Check module parameters */
+ if (smsc95xx_is_macaddr_param(dev, dev->net->dev_addr))
+ return;
+
/* no useful static MAC address found. generate a random one */
eth_hw_addr_random(dev->net);
netif_dbg(dev, ifup, dev->net, "MAC address set to eth_random_addr\n");
if (!turbo_mode) {
burst_cap = 0;
- dev->rx_urb_size = MAX_SINGLE_PACKET_SIZE;
+ dev->rx_urb_size = packetsize ? packetsize : MAX_SINGLE_PACKET_SIZE;
} else if (dev->udev->speed == USB_SPEED_HIGH) {
- burst_cap = DEFAULT_HS_BURST_CAP_SIZE / HS_USB_PKT_SIZE;
- dev->rx_urb_size = DEFAULT_HS_BURST_CAP_SIZE;
+ dev->rx_urb_size = packetsize ? packetsize : DEFAULT_HS_BURST_CAP_SIZE;
+ burst_cap = dev->rx_urb_size / HS_USB_PKT_SIZE;
} else {
- burst_cap = DEFAULT_FS_BURST_CAP_SIZE / FS_USB_PKT_SIZE;
- dev->rx_urb_size = DEFAULT_FS_BURST_CAP_SIZE;
+ dev->rx_urb_size = packetsize ? packetsize : DEFAULT_FS_BURST_CAP_SIZE;
+ burst_cap = dev->rx_urb_size / FS_USB_PKT_SIZE;
}
netif_dbg(dev, ifup, dev->net, "rx_urb_size=%ld\n",
{
struct smsc95xx_priv *pdata;
bool is_internal_phy;
- char usb_path[64];
- int ret, phy_irq;
u32 val;
+ int ret;
printk(KERN_INFO SMSC_CHIPNAME " v" SMSC_DRIVER_VERSION "\n");
if (ret)
goto free_pdata;
- /* create irq domain for use by PHY driver and GPIO consumers */
- usb_make_path(dev->udev, usb_path, sizeof(usb_path));
- pdata->irqfwnode = irq_domain_alloc_named_fwnode(usb_path);
- if (!pdata->irqfwnode) {
- ret = -ENOMEM;
- goto free_pdata;
- }
-
- pdata->irqdomain = irq_domain_create_linear(pdata->irqfwnode,
- SMSC95XX_NR_IRQS,
- &irq_domain_simple_ops,
- pdata);
- if (!pdata->irqdomain) {
- ret = -ENOMEM;
- goto free_irqfwnode;
- }
-
- phy_irq = irq_create_mapping(pdata->irqdomain, PHY_HWIRQ);
- if (!phy_irq) {
- ret = -ENOENT;
- goto remove_irqdomain;
- }
-
- pdata->irqchip = dummy_irq_chip;
- pdata->irqchip.name = SMSC_CHIPNAME;
- irq_set_chip_and_handler_name(phy_irq, &pdata->irqchip,
- handle_simple_irq, "phy");
-
pdata->mdiobus = mdiobus_alloc();
if (!pdata->mdiobus) {
ret = -ENOMEM;
- goto dispose_irq;
+ goto free_pdata;
}
ret = smsc95xx_read_reg(dev, HW_CFG, &val);
goto unregister_mdio;
}
- pdata->phydev->irq = phy_irq;
pdata->phydev->is_internal = is_internal_phy;
/* detect device revision as different features may be available */
free_mdio:
mdiobus_free(pdata->mdiobus);
- dispose_irq:
- irq_dispose_mapping(phy_irq);
-
- remove_irqdomain:
- irq_domain_remove(pdata->irqdomain);
-
- free_irqfwnode:
- irq_domain_free_fwnode(pdata->irqfwnode);
-
free_pdata:
kfree(pdata);
return ret;
phy_disconnect(dev->net->phydev);
mdiobus_unregister(pdata->mdiobus);
mdiobus_free(pdata->mdiobus);
- irq_dispose_mapping(irq_find_mapping(pdata->irqdomain, PHY_HWIRQ));
- irq_domain_remove(pdata->irqdomain);
- irq_domain_free_fwnode(pdata->irqfwnode);
netif_dbg(dev, ifdown, dev->net, "free pdata\n");
kfree(pdata);
}
return crc << ((filter % 2) * 16);
}
+ static int smsc95xx_enable_phy_wakeup_interrupts(struct usbnet *dev, u16 mask)
+ {
+ int ret;
+
+ netdev_dbg(dev->net, "enabling PHY wakeup interrupts\n");
+
+ /* read to clear */
+ ret = smsc95xx_mdio_read_nopm(dev, PHY_INT_SRC);
+ if (ret < 0)
+ return ret;
+
+ /* enable interrupt source */
+ ret = smsc95xx_mdio_read_nopm(dev, PHY_INT_MASK);
+ if (ret < 0)
+ return ret;
+
+ ret |= mask;
+
+ smsc95xx_mdio_write_nopm(dev, PHY_INT_MASK, ret);
+
+ return 0;
+ }
+
static int smsc95xx_link_ok_nopm(struct usbnet *dev)
{
int ret;
static int smsc95xx_autosuspend(struct usbnet *dev, u32 link_up)
{
struct smsc95xx_priv *pdata = dev->driver_priv;
+ int ret;
if (!netif_running(dev->net)) {
/* interface is ifconfig down so fully power down hw */
}
netdev_dbg(dev->net, "autosuspend entering SUSPEND1\n");
+
+ /* enable PHY wakeup events for if cable is attached */
+ ret = smsc95xx_enable_phy_wakeup_interrupts(dev,
+ PHY_INT_MASK_ANEG_COMP_);
+ if (ret < 0) {
+ netdev_warn(dev->net, "error enabling PHY wakeup ints\n");
+ return ret;
+ }
+
netdev_info(dev->net, "entering SUSPEND1 mode\n");
return smsc95xx_enter_suspend1(dev);
}
+ /* enable PHY wakeup events so we remote wakeup if cable is pulled */
+ ret = smsc95xx_enable_phy_wakeup_interrupts(dev,
+ PHY_INT_MASK_LINK_DOWN_);
+ if (ret < 0) {
+ netdev_warn(dev->net, "error enabling PHY wakeup ints\n");
+ return ret;
+ }
+
netdev_dbg(dev->net, "autosuspend entering SUSPEND3\n");
return smsc95xx_enter_suspend3(dev);
}
u32 val, link_up;
int ret;
- pdata->pm_task = current;
-
ret = usbnet_suspend(intf, message);
if (ret < 0) {
netdev_warn(dev->net, "usbnet_suspend error\n");
- pdata->pm_task = NULL;
return ret;
}
}
if (pdata->wolopts & WAKE_PHY) {
+ ret = smsc95xx_enable_phy_wakeup_interrupts(dev,
+ (PHY_INT_MASK_ANEG_COMP_ | PHY_INT_MASK_LINK_DOWN_));
+ if (ret < 0) {
+ netdev_warn(dev->net, "error enabling PHY wakeup ints\n");
+ goto done;
+ }
+
/* if link is down then configure EDPD and enter SUSPEND1,
* otherwise enter SUSPEND0 below
*/
if (ret && PMSG_IS_AUTO(message))
usbnet_resume(intf);
- pdata->pm_task = NULL;
return ret;
}
/* do this first to ensure it's cleared even in error case */
pdata->suspend_flags = 0;
- pdata->pm_task = current;
-
if (suspend_flags & SUSPEND_ALLMODES) {
/* clear wake-up sources */
ret = smsc95xx_read_reg_nopm(dev, WUCSR, &val);
if (ret < 0)
- goto done;
+ return ret;
val &= ~(WUCSR_WAKE_EN_ | WUCSR_MPEN_);
ret = smsc95xx_write_reg_nopm(dev, WUCSR, val);
if (ret < 0)
- goto done;
+ return ret;
/* clear wake-up status */
ret = smsc95xx_read_reg_nopm(dev, PM_CTRL, &val);
if (ret < 0)
- goto done;
+ return ret;
val &= ~PM_CTL_WOL_EN_;
val |= PM_CTL_WUPS_;
ret = smsc95xx_write_reg_nopm(dev, PM_CTRL, val);
if (ret < 0)
- goto done;
+ return ret;
}
- phy_init_hw(pdata->phydev);
-
ret = usbnet_resume(intf);
if (ret < 0)
netdev_warn(dev->net, "usbnet_resume error\n");
- done:
- pdata->pm_task = NULL;
+ phy_init_hw(pdata->phydev);
return ret;
}
static int smsc95xx_reset_resume(struct usb_interface *intf)
{
struct usbnet *dev = usb_get_intfdata(intf);
- struct smsc95xx_priv *pdata = dev->driver_priv;
int ret;
- pdata->pm_task = current;
ret = smsc95xx_reset(dev);
- pdata->pm_task = NULL;
if (ret < 0)
return ret;
if (dev->net->features & NETIF_F_RXCSUM)
smsc95xx_rx_csum_offload(skb);
skb_trim(skb, skb->len - 4); /* remove fcs */
- skb->truesize = size + sizeof(struct sk_buff);
+ if (truesize_mode)
+ skb->truesize = size + sizeof(struct sk_buff);
return 1;
}
if (dev->net->features & NETIF_F_RXCSUM)
smsc95xx_rx_csum_offload(ax_skb);
skb_trim(ax_skb, ax_skb->len - 4); /* remove fcs */
- ax_skb->truesize = size + sizeof(struct sk_buff);
+ if (truesize_mode)
+ ax_skb->truesize = size + sizeof(struct sk_buff);
usbnet_skb_return(dev, ax_skb);
}