18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Ethernet driver for the WIZnet W5300 chip. 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2008-2009 WIZnet Co.,Ltd. 68c2ecf20Sopenharmony_ci * Copyright (C) 2011 Taehun Kim <kth3321 <at> gmail.com> 78c2ecf20Sopenharmony_ci * Copyright (C) 2012 Mike Sinkovsky <msink@permonline.ru> 88c2ecf20Sopenharmony_ci */ 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#include <linux/kernel.h> 118c2ecf20Sopenharmony_ci#include <linux/module.h> 128c2ecf20Sopenharmony_ci#include <linux/netdevice.h> 138c2ecf20Sopenharmony_ci#include <linux/etherdevice.h> 148c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 158c2ecf20Sopenharmony_ci#include <linux/platform_data/wiznet.h> 168c2ecf20Sopenharmony_ci#include <linux/ethtool.h> 178c2ecf20Sopenharmony_ci#include <linux/skbuff.h> 188c2ecf20Sopenharmony_ci#include <linux/types.h> 198c2ecf20Sopenharmony_ci#include <linux/errno.h> 208c2ecf20Sopenharmony_ci#include <linux/delay.h> 218c2ecf20Sopenharmony_ci#include <linux/slab.h> 228c2ecf20Sopenharmony_ci#include <linux/spinlock.h> 238c2ecf20Sopenharmony_ci#include <linux/io.h> 248c2ecf20Sopenharmony_ci#include <linux/ioport.h> 258c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 268c2ecf20Sopenharmony_ci#include <linux/irq.h> 278c2ecf20Sopenharmony_ci#include <linux/gpio.h> 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci#define DRV_NAME "w5300" 308c2ecf20Sopenharmony_ci#define DRV_VERSION "2012-04-04" 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("WIZnet W5300 Ethernet driver v"DRV_VERSION); 338c2ecf20Sopenharmony_ciMODULE_AUTHOR("Mike Sinkovsky <msink@permonline.ru>"); 348c2ecf20Sopenharmony_ciMODULE_ALIAS("platform:"DRV_NAME); 358c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci/* 388c2ecf20Sopenharmony_ci * Registers 398c2ecf20Sopenharmony_ci */ 408c2ecf20Sopenharmony_ci#define W5300_MR 0x0000 /* Mode Register */ 418c2ecf20Sopenharmony_ci#define MR_DBW (1 << 15) /* Data bus width */ 428c2ecf20Sopenharmony_ci#define MR_MPF (1 << 14) /* Mac layer pause frame */ 438c2ecf20Sopenharmony_ci#define MR_WDF(n) (((n)&7)<<11) /* Write data fetch time */ 448c2ecf20Sopenharmony_ci#define MR_RDH (1 << 10) /* Read data hold time */ 458c2ecf20Sopenharmony_ci#define MR_FS (1 << 8) /* FIFO swap */ 468c2ecf20Sopenharmony_ci#define MR_RST (1 << 7) /* S/W reset */ 478c2ecf20Sopenharmony_ci#define MR_PB (1 << 4) /* Ping block */ 488c2ecf20Sopenharmony_ci#define MR_DBS (1 << 2) /* Data bus swap */ 498c2ecf20Sopenharmony_ci#define MR_IND (1 << 0) /* Indirect mode */ 508c2ecf20Sopenharmony_ci#define W5300_IR 0x0002 /* Interrupt Register */ 518c2ecf20Sopenharmony_ci#define W5300_IMR 0x0004 /* Interrupt Mask Register */ 528c2ecf20Sopenharmony_ci#define IR_S0 0x0001 /* S0 interrupt */ 538c2ecf20Sopenharmony_ci#define W5300_SHARL 0x0008 /* Source MAC address (0123) */ 548c2ecf20Sopenharmony_ci#define W5300_SHARH 0x000c /* Source MAC address (45) */ 558c2ecf20Sopenharmony_ci#define W5300_TMSRL 0x0020 /* Transmit Memory Size (0123) */ 568c2ecf20Sopenharmony_ci#define W5300_TMSRH 0x0024 /* Transmit Memory Size (4567) */ 578c2ecf20Sopenharmony_ci#define W5300_RMSRL 0x0028 /* Receive Memory Size (0123) */ 588c2ecf20Sopenharmony_ci#define W5300_RMSRH 0x002c /* Receive Memory Size (4567) */ 598c2ecf20Sopenharmony_ci#define W5300_MTYPE 0x0030 /* Memory Type */ 608c2ecf20Sopenharmony_ci#define W5300_IDR 0x00fe /* Chip ID register */ 618c2ecf20Sopenharmony_ci#define IDR_W5300 0x5300 /* =0x5300 for WIZnet W5300 */ 628c2ecf20Sopenharmony_ci#define W5300_S0_MR 0x0200 /* S0 Mode Register */ 638c2ecf20Sopenharmony_ci#define S0_MR_CLOSED 0x0000 /* Close mode */ 648c2ecf20Sopenharmony_ci#define S0_MR_MACRAW 0x0004 /* MAC RAW mode (promiscuous) */ 658c2ecf20Sopenharmony_ci#define S0_MR_MACRAW_MF 0x0044 /* MAC RAW mode (filtered) */ 668c2ecf20Sopenharmony_ci#define W5300_S0_CR 0x0202 /* S0 Command Register */ 678c2ecf20Sopenharmony_ci#define S0_CR_OPEN 0x0001 /* OPEN command */ 688c2ecf20Sopenharmony_ci#define S0_CR_CLOSE 0x0010 /* CLOSE command */ 698c2ecf20Sopenharmony_ci#define S0_CR_SEND 0x0020 /* SEND command */ 708c2ecf20Sopenharmony_ci#define S0_CR_RECV 0x0040 /* RECV command */ 718c2ecf20Sopenharmony_ci#define W5300_S0_IMR 0x0204 /* S0 Interrupt Mask Register */ 728c2ecf20Sopenharmony_ci#define W5300_S0_IR 0x0206 /* S0 Interrupt Register */ 738c2ecf20Sopenharmony_ci#define S0_IR_RECV 0x0004 /* Receive interrupt */ 748c2ecf20Sopenharmony_ci#define S0_IR_SENDOK 0x0010 /* Send OK interrupt */ 758c2ecf20Sopenharmony_ci#define W5300_S0_SSR 0x0208 /* S0 Socket Status Register */ 768c2ecf20Sopenharmony_ci#define W5300_S0_TX_WRSR 0x0220 /* S0 TX Write Size Register */ 778c2ecf20Sopenharmony_ci#define W5300_S0_TX_FSR 0x0224 /* S0 TX Free Size Register */ 788c2ecf20Sopenharmony_ci#define W5300_S0_RX_RSR 0x0228 /* S0 Received data Size */ 798c2ecf20Sopenharmony_ci#define W5300_S0_TX_FIFO 0x022e /* S0 Transmit FIFO */ 808c2ecf20Sopenharmony_ci#define W5300_S0_RX_FIFO 0x0230 /* S0 Receive FIFO */ 818c2ecf20Sopenharmony_ci#define W5300_REGS_LEN 0x0400 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci/* 848c2ecf20Sopenharmony_ci * Device driver private data structure 858c2ecf20Sopenharmony_ci */ 868c2ecf20Sopenharmony_cistruct w5300_priv { 878c2ecf20Sopenharmony_ci void __iomem *base; 888c2ecf20Sopenharmony_ci spinlock_t reg_lock; 898c2ecf20Sopenharmony_ci bool indirect; 908c2ecf20Sopenharmony_ci u16 (*read) (struct w5300_priv *priv, u16 addr); 918c2ecf20Sopenharmony_ci void (*write)(struct w5300_priv *priv, u16 addr, u16 data); 928c2ecf20Sopenharmony_ci int irq; 938c2ecf20Sopenharmony_ci int link_irq; 948c2ecf20Sopenharmony_ci int link_gpio; 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci struct napi_struct napi; 978c2ecf20Sopenharmony_ci struct net_device *ndev; 988c2ecf20Sopenharmony_ci bool promisc; 998c2ecf20Sopenharmony_ci u32 msg_enable; 1008c2ecf20Sopenharmony_ci}; 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci/************************************************************************ 1038c2ecf20Sopenharmony_ci * 1048c2ecf20Sopenharmony_ci * Lowlevel I/O functions 1058c2ecf20Sopenharmony_ci * 1068c2ecf20Sopenharmony_ci ***********************************************************************/ 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci/* 1098c2ecf20Sopenharmony_ci * In direct address mode host system can directly access W5300 registers 1108c2ecf20Sopenharmony_ci * after mapping to Memory-Mapped I/O space. 1118c2ecf20Sopenharmony_ci * 1128c2ecf20Sopenharmony_ci * 0x400 bytes are required for memory space. 1138c2ecf20Sopenharmony_ci */ 1148c2ecf20Sopenharmony_cistatic inline u16 w5300_read_direct(struct w5300_priv *priv, u16 addr) 1158c2ecf20Sopenharmony_ci{ 1168c2ecf20Sopenharmony_ci return ioread16(priv->base + (addr << CONFIG_WIZNET_BUS_SHIFT)); 1178c2ecf20Sopenharmony_ci} 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_cistatic inline void w5300_write_direct(struct w5300_priv *priv, 1208c2ecf20Sopenharmony_ci u16 addr, u16 data) 1218c2ecf20Sopenharmony_ci{ 1228c2ecf20Sopenharmony_ci iowrite16(data, priv->base + (addr << CONFIG_WIZNET_BUS_SHIFT)); 1238c2ecf20Sopenharmony_ci} 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci/* 1268c2ecf20Sopenharmony_ci * In indirect address mode host system indirectly accesses registers by 1278c2ecf20Sopenharmony_ci * using Indirect Mode Address Register (IDM_AR) and Indirect Mode Data 1288c2ecf20Sopenharmony_ci * Register (IDM_DR), which are directly mapped to Memory-Mapped I/O space. 1298c2ecf20Sopenharmony_ci * Mode Register (MR) is directly accessible. 1308c2ecf20Sopenharmony_ci * 1318c2ecf20Sopenharmony_ci * Only 0x06 bytes are required for memory space. 1328c2ecf20Sopenharmony_ci */ 1338c2ecf20Sopenharmony_ci#define W5300_IDM_AR 0x0002 /* Indirect Mode Address */ 1348c2ecf20Sopenharmony_ci#define W5300_IDM_DR 0x0004 /* Indirect Mode Data */ 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_cistatic u16 w5300_read_indirect(struct w5300_priv *priv, u16 addr) 1378c2ecf20Sopenharmony_ci{ 1388c2ecf20Sopenharmony_ci unsigned long flags; 1398c2ecf20Sopenharmony_ci u16 data; 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci spin_lock_irqsave(&priv->reg_lock, flags); 1428c2ecf20Sopenharmony_ci w5300_write_direct(priv, W5300_IDM_AR, addr); 1438c2ecf20Sopenharmony_ci data = w5300_read_direct(priv, W5300_IDM_DR); 1448c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&priv->reg_lock, flags); 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci return data; 1478c2ecf20Sopenharmony_ci} 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_cistatic void w5300_write_indirect(struct w5300_priv *priv, u16 addr, u16 data) 1508c2ecf20Sopenharmony_ci{ 1518c2ecf20Sopenharmony_ci unsigned long flags; 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci spin_lock_irqsave(&priv->reg_lock, flags); 1548c2ecf20Sopenharmony_ci w5300_write_direct(priv, W5300_IDM_AR, addr); 1558c2ecf20Sopenharmony_ci w5300_write_direct(priv, W5300_IDM_DR, data); 1568c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&priv->reg_lock, flags); 1578c2ecf20Sopenharmony_ci} 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci#if defined(CONFIG_WIZNET_BUS_DIRECT) 1608c2ecf20Sopenharmony_ci#define w5300_read w5300_read_direct 1618c2ecf20Sopenharmony_ci#define w5300_write w5300_write_direct 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ci#elif defined(CONFIG_WIZNET_BUS_INDIRECT) 1648c2ecf20Sopenharmony_ci#define w5300_read w5300_read_indirect 1658c2ecf20Sopenharmony_ci#define w5300_write w5300_write_indirect 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci#else /* CONFIG_WIZNET_BUS_ANY */ 1688c2ecf20Sopenharmony_ci#define w5300_read priv->read 1698c2ecf20Sopenharmony_ci#define w5300_write priv->write 1708c2ecf20Sopenharmony_ci#endif 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_cistatic u32 w5300_read32(struct w5300_priv *priv, u16 addr) 1738c2ecf20Sopenharmony_ci{ 1748c2ecf20Sopenharmony_ci u32 data; 1758c2ecf20Sopenharmony_ci data = w5300_read(priv, addr) << 16; 1768c2ecf20Sopenharmony_ci data |= w5300_read(priv, addr + 2); 1778c2ecf20Sopenharmony_ci return data; 1788c2ecf20Sopenharmony_ci} 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_cistatic void w5300_write32(struct w5300_priv *priv, u16 addr, u32 data) 1818c2ecf20Sopenharmony_ci{ 1828c2ecf20Sopenharmony_ci w5300_write(priv, addr, data >> 16); 1838c2ecf20Sopenharmony_ci w5300_write(priv, addr + 2, data); 1848c2ecf20Sopenharmony_ci} 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_cistatic int w5300_command(struct w5300_priv *priv, u16 cmd) 1878c2ecf20Sopenharmony_ci{ 1888c2ecf20Sopenharmony_ci unsigned long timeout = jiffies + msecs_to_jiffies(100); 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_ci w5300_write(priv, W5300_S0_CR, cmd); 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci while (w5300_read(priv, W5300_S0_CR) != 0) { 1938c2ecf20Sopenharmony_ci if (time_after(jiffies, timeout)) 1948c2ecf20Sopenharmony_ci return -EIO; 1958c2ecf20Sopenharmony_ci cpu_relax(); 1968c2ecf20Sopenharmony_ci } 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_ci return 0; 1998c2ecf20Sopenharmony_ci} 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_cistatic void w5300_read_frame(struct w5300_priv *priv, u8 *buf, int len) 2028c2ecf20Sopenharmony_ci{ 2038c2ecf20Sopenharmony_ci u16 fifo; 2048c2ecf20Sopenharmony_ci int i; 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci for (i = 0; i < len; i += 2) { 2078c2ecf20Sopenharmony_ci fifo = w5300_read(priv, W5300_S0_RX_FIFO); 2088c2ecf20Sopenharmony_ci *buf++ = fifo >> 8; 2098c2ecf20Sopenharmony_ci *buf++ = fifo; 2108c2ecf20Sopenharmony_ci } 2118c2ecf20Sopenharmony_ci fifo = w5300_read(priv, W5300_S0_RX_FIFO); 2128c2ecf20Sopenharmony_ci fifo = w5300_read(priv, W5300_S0_RX_FIFO); 2138c2ecf20Sopenharmony_ci} 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_cistatic void w5300_write_frame(struct w5300_priv *priv, u8 *buf, int len) 2168c2ecf20Sopenharmony_ci{ 2178c2ecf20Sopenharmony_ci u16 fifo; 2188c2ecf20Sopenharmony_ci int i; 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_ci for (i = 0; i < len; i += 2) { 2218c2ecf20Sopenharmony_ci fifo = *buf++ << 8; 2228c2ecf20Sopenharmony_ci fifo |= *buf++; 2238c2ecf20Sopenharmony_ci w5300_write(priv, W5300_S0_TX_FIFO, fifo); 2248c2ecf20Sopenharmony_ci } 2258c2ecf20Sopenharmony_ci w5300_write32(priv, W5300_S0_TX_WRSR, len); 2268c2ecf20Sopenharmony_ci} 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_cistatic void w5300_write_macaddr(struct w5300_priv *priv) 2298c2ecf20Sopenharmony_ci{ 2308c2ecf20Sopenharmony_ci struct net_device *ndev = priv->ndev; 2318c2ecf20Sopenharmony_ci w5300_write32(priv, W5300_SHARL, 2328c2ecf20Sopenharmony_ci ndev->dev_addr[0] << 24 | 2338c2ecf20Sopenharmony_ci ndev->dev_addr[1] << 16 | 2348c2ecf20Sopenharmony_ci ndev->dev_addr[2] << 8 | 2358c2ecf20Sopenharmony_ci ndev->dev_addr[3]); 2368c2ecf20Sopenharmony_ci w5300_write(priv, W5300_SHARH, 2378c2ecf20Sopenharmony_ci ndev->dev_addr[4] << 8 | 2388c2ecf20Sopenharmony_ci ndev->dev_addr[5]); 2398c2ecf20Sopenharmony_ci} 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_cistatic void w5300_hw_reset(struct w5300_priv *priv) 2428c2ecf20Sopenharmony_ci{ 2438c2ecf20Sopenharmony_ci w5300_write_direct(priv, W5300_MR, MR_RST); 2448c2ecf20Sopenharmony_ci mdelay(5); 2458c2ecf20Sopenharmony_ci w5300_write_direct(priv, W5300_MR, priv->indirect ? 2468c2ecf20Sopenharmony_ci MR_WDF(7) | MR_PB | MR_IND : 2478c2ecf20Sopenharmony_ci MR_WDF(7) | MR_PB); 2488c2ecf20Sopenharmony_ci w5300_write(priv, W5300_IMR, 0); 2498c2ecf20Sopenharmony_ci w5300_write_macaddr(priv); 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_ci /* Configure 128K of internal memory 2528c2ecf20Sopenharmony_ci * as 64K RX fifo and 64K TX fifo 2538c2ecf20Sopenharmony_ci */ 2548c2ecf20Sopenharmony_ci w5300_write32(priv, W5300_RMSRL, 64 << 24); 2558c2ecf20Sopenharmony_ci w5300_write32(priv, W5300_RMSRH, 0); 2568c2ecf20Sopenharmony_ci w5300_write32(priv, W5300_TMSRL, 64 << 24); 2578c2ecf20Sopenharmony_ci w5300_write32(priv, W5300_TMSRH, 0); 2588c2ecf20Sopenharmony_ci w5300_write(priv, W5300_MTYPE, 0x00ff); 2598c2ecf20Sopenharmony_ci} 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_cistatic void w5300_hw_start(struct w5300_priv *priv) 2628c2ecf20Sopenharmony_ci{ 2638c2ecf20Sopenharmony_ci w5300_write(priv, W5300_S0_MR, priv->promisc ? 2648c2ecf20Sopenharmony_ci S0_MR_MACRAW : S0_MR_MACRAW_MF); 2658c2ecf20Sopenharmony_ci w5300_command(priv, S0_CR_OPEN); 2668c2ecf20Sopenharmony_ci w5300_write(priv, W5300_S0_IMR, S0_IR_RECV | S0_IR_SENDOK); 2678c2ecf20Sopenharmony_ci w5300_write(priv, W5300_IMR, IR_S0); 2688c2ecf20Sopenharmony_ci} 2698c2ecf20Sopenharmony_ci 2708c2ecf20Sopenharmony_cistatic void w5300_hw_close(struct w5300_priv *priv) 2718c2ecf20Sopenharmony_ci{ 2728c2ecf20Sopenharmony_ci w5300_write(priv, W5300_IMR, 0); 2738c2ecf20Sopenharmony_ci w5300_command(priv, S0_CR_CLOSE); 2748c2ecf20Sopenharmony_ci} 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_ci/*********************************************************************** 2778c2ecf20Sopenharmony_ci * 2788c2ecf20Sopenharmony_ci * Device driver functions / callbacks 2798c2ecf20Sopenharmony_ci * 2808c2ecf20Sopenharmony_ci ***********************************************************************/ 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_cistatic void w5300_get_drvinfo(struct net_device *ndev, 2838c2ecf20Sopenharmony_ci struct ethtool_drvinfo *info) 2848c2ecf20Sopenharmony_ci{ 2858c2ecf20Sopenharmony_ci strlcpy(info->driver, DRV_NAME, sizeof(info->driver)); 2868c2ecf20Sopenharmony_ci strlcpy(info->version, DRV_VERSION, sizeof(info->version)); 2878c2ecf20Sopenharmony_ci strlcpy(info->bus_info, dev_name(ndev->dev.parent), 2888c2ecf20Sopenharmony_ci sizeof(info->bus_info)); 2898c2ecf20Sopenharmony_ci} 2908c2ecf20Sopenharmony_ci 2918c2ecf20Sopenharmony_cistatic u32 w5300_get_link(struct net_device *ndev) 2928c2ecf20Sopenharmony_ci{ 2938c2ecf20Sopenharmony_ci struct w5300_priv *priv = netdev_priv(ndev); 2948c2ecf20Sopenharmony_ci 2958c2ecf20Sopenharmony_ci if (gpio_is_valid(priv->link_gpio)) 2968c2ecf20Sopenharmony_ci return !!gpio_get_value(priv->link_gpio); 2978c2ecf20Sopenharmony_ci 2988c2ecf20Sopenharmony_ci return 1; 2998c2ecf20Sopenharmony_ci} 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_cistatic u32 w5300_get_msglevel(struct net_device *ndev) 3028c2ecf20Sopenharmony_ci{ 3038c2ecf20Sopenharmony_ci struct w5300_priv *priv = netdev_priv(ndev); 3048c2ecf20Sopenharmony_ci 3058c2ecf20Sopenharmony_ci return priv->msg_enable; 3068c2ecf20Sopenharmony_ci} 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_cistatic void w5300_set_msglevel(struct net_device *ndev, u32 value) 3098c2ecf20Sopenharmony_ci{ 3108c2ecf20Sopenharmony_ci struct w5300_priv *priv = netdev_priv(ndev); 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_ci priv->msg_enable = value; 3138c2ecf20Sopenharmony_ci} 3148c2ecf20Sopenharmony_ci 3158c2ecf20Sopenharmony_cistatic int w5300_get_regs_len(struct net_device *ndev) 3168c2ecf20Sopenharmony_ci{ 3178c2ecf20Sopenharmony_ci return W5300_REGS_LEN; 3188c2ecf20Sopenharmony_ci} 3198c2ecf20Sopenharmony_ci 3208c2ecf20Sopenharmony_cistatic void w5300_get_regs(struct net_device *ndev, 3218c2ecf20Sopenharmony_ci struct ethtool_regs *regs, void *_buf) 3228c2ecf20Sopenharmony_ci{ 3238c2ecf20Sopenharmony_ci struct w5300_priv *priv = netdev_priv(ndev); 3248c2ecf20Sopenharmony_ci u8 *buf = _buf; 3258c2ecf20Sopenharmony_ci u16 addr; 3268c2ecf20Sopenharmony_ci u16 data; 3278c2ecf20Sopenharmony_ci 3288c2ecf20Sopenharmony_ci regs->version = 1; 3298c2ecf20Sopenharmony_ci for (addr = 0; addr < W5300_REGS_LEN; addr += 2) { 3308c2ecf20Sopenharmony_ci switch (addr & 0x23f) { 3318c2ecf20Sopenharmony_ci case W5300_S0_TX_FIFO: /* cannot read TX_FIFO */ 3328c2ecf20Sopenharmony_ci case W5300_S0_RX_FIFO: /* cannot read RX_FIFO */ 3338c2ecf20Sopenharmony_ci data = 0xffff; 3348c2ecf20Sopenharmony_ci break; 3358c2ecf20Sopenharmony_ci default: 3368c2ecf20Sopenharmony_ci data = w5300_read(priv, addr); 3378c2ecf20Sopenharmony_ci break; 3388c2ecf20Sopenharmony_ci } 3398c2ecf20Sopenharmony_ci *buf++ = data >> 8; 3408c2ecf20Sopenharmony_ci *buf++ = data; 3418c2ecf20Sopenharmony_ci } 3428c2ecf20Sopenharmony_ci} 3438c2ecf20Sopenharmony_ci 3448c2ecf20Sopenharmony_cistatic void w5300_tx_timeout(struct net_device *ndev, unsigned int txqueue) 3458c2ecf20Sopenharmony_ci{ 3468c2ecf20Sopenharmony_ci struct w5300_priv *priv = netdev_priv(ndev); 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_ci netif_stop_queue(ndev); 3498c2ecf20Sopenharmony_ci w5300_hw_reset(priv); 3508c2ecf20Sopenharmony_ci w5300_hw_start(priv); 3518c2ecf20Sopenharmony_ci ndev->stats.tx_errors++; 3528c2ecf20Sopenharmony_ci netif_trans_update(ndev); 3538c2ecf20Sopenharmony_ci netif_wake_queue(ndev); 3548c2ecf20Sopenharmony_ci} 3558c2ecf20Sopenharmony_ci 3568c2ecf20Sopenharmony_cistatic netdev_tx_t w5300_start_tx(struct sk_buff *skb, struct net_device *ndev) 3578c2ecf20Sopenharmony_ci{ 3588c2ecf20Sopenharmony_ci struct w5300_priv *priv = netdev_priv(ndev); 3598c2ecf20Sopenharmony_ci 3608c2ecf20Sopenharmony_ci netif_stop_queue(ndev); 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_ci w5300_write_frame(priv, skb->data, skb->len); 3638c2ecf20Sopenharmony_ci ndev->stats.tx_packets++; 3648c2ecf20Sopenharmony_ci ndev->stats.tx_bytes += skb->len; 3658c2ecf20Sopenharmony_ci dev_kfree_skb(skb); 3668c2ecf20Sopenharmony_ci netif_dbg(priv, tx_queued, ndev, "tx queued\n"); 3678c2ecf20Sopenharmony_ci 3688c2ecf20Sopenharmony_ci w5300_command(priv, S0_CR_SEND); 3698c2ecf20Sopenharmony_ci 3708c2ecf20Sopenharmony_ci return NETDEV_TX_OK; 3718c2ecf20Sopenharmony_ci} 3728c2ecf20Sopenharmony_ci 3738c2ecf20Sopenharmony_cistatic int w5300_napi_poll(struct napi_struct *napi, int budget) 3748c2ecf20Sopenharmony_ci{ 3758c2ecf20Sopenharmony_ci struct w5300_priv *priv = container_of(napi, struct w5300_priv, napi); 3768c2ecf20Sopenharmony_ci struct net_device *ndev = priv->ndev; 3778c2ecf20Sopenharmony_ci struct sk_buff *skb; 3788c2ecf20Sopenharmony_ci int rx_count; 3798c2ecf20Sopenharmony_ci u16 rx_len; 3808c2ecf20Sopenharmony_ci 3818c2ecf20Sopenharmony_ci for (rx_count = 0; rx_count < budget; rx_count++) { 3828c2ecf20Sopenharmony_ci u32 rx_fifo_len = w5300_read32(priv, W5300_S0_RX_RSR); 3838c2ecf20Sopenharmony_ci if (rx_fifo_len == 0) 3848c2ecf20Sopenharmony_ci break; 3858c2ecf20Sopenharmony_ci 3868c2ecf20Sopenharmony_ci rx_len = w5300_read(priv, W5300_S0_RX_FIFO); 3878c2ecf20Sopenharmony_ci 3888c2ecf20Sopenharmony_ci skb = netdev_alloc_skb_ip_align(ndev, roundup(rx_len, 2)); 3898c2ecf20Sopenharmony_ci if (unlikely(!skb)) { 3908c2ecf20Sopenharmony_ci u32 i; 3918c2ecf20Sopenharmony_ci for (i = 0; i < rx_fifo_len; i += 2) 3928c2ecf20Sopenharmony_ci w5300_read(priv, W5300_S0_RX_FIFO); 3938c2ecf20Sopenharmony_ci ndev->stats.rx_dropped++; 3948c2ecf20Sopenharmony_ci return -ENOMEM; 3958c2ecf20Sopenharmony_ci } 3968c2ecf20Sopenharmony_ci 3978c2ecf20Sopenharmony_ci skb_put(skb, rx_len); 3988c2ecf20Sopenharmony_ci w5300_read_frame(priv, skb->data, rx_len); 3998c2ecf20Sopenharmony_ci skb->protocol = eth_type_trans(skb, ndev); 4008c2ecf20Sopenharmony_ci 4018c2ecf20Sopenharmony_ci netif_receive_skb(skb); 4028c2ecf20Sopenharmony_ci ndev->stats.rx_packets++; 4038c2ecf20Sopenharmony_ci ndev->stats.rx_bytes += rx_len; 4048c2ecf20Sopenharmony_ci } 4058c2ecf20Sopenharmony_ci 4068c2ecf20Sopenharmony_ci if (rx_count < budget) { 4078c2ecf20Sopenharmony_ci napi_complete_done(napi, rx_count); 4088c2ecf20Sopenharmony_ci w5300_write(priv, W5300_IMR, IR_S0); 4098c2ecf20Sopenharmony_ci } 4108c2ecf20Sopenharmony_ci 4118c2ecf20Sopenharmony_ci return rx_count; 4128c2ecf20Sopenharmony_ci} 4138c2ecf20Sopenharmony_ci 4148c2ecf20Sopenharmony_cistatic irqreturn_t w5300_interrupt(int irq, void *ndev_instance) 4158c2ecf20Sopenharmony_ci{ 4168c2ecf20Sopenharmony_ci struct net_device *ndev = ndev_instance; 4178c2ecf20Sopenharmony_ci struct w5300_priv *priv = netdev_priv(ndev); 4188c2ecf20Sopenharmony_ci 4198c2ecf20Sopenharmony_ci int ir = w5300_read(priv, W5300_S0_IR); 4208c2ecf20Sopenharmony_ci if (!ir) 4218c2ecf20Sopenharmony_ci return IRQ_NONE; 4228c2ecf20Sopenharmony_ci w5300_write(priv, W5300_S0_IR, ir); 4238c2ecf20Sopenharmony_ci 4248c2ecf20Sopenharmony_ci if (ir & S0_IR_SENDOK) { 4258c2ecf20Sopenharmony_ci netif_dbg(priv, tx_done, ndev, "tx done\n"); 4268c2ecf20Sopenharmony_ci netif_wake_queue(ndev); 4278c2ecf20Sopenharmony_ci } 4288c2ecf20Sopenharmony_ci 4298c2ecf20Sopenharmony_ci if (ir & S0_IR_RECV) { 4308c2ecf20Sopenharmony_ci if (napi_schedule_prep(&priv->napi)) { 4318c2ecf20Sopenharmony_ci w5300_write(priv, W5300_IMR, 0); 4328c2ecf20Sopenharmony_ci __napi_schedule(&priv->napi); 4338c2ecf20Sopenharmony_ci } 4348c2ecf20Sopenharmony_ci } 4358c2ecf20Sopenharmony_ci 4368c2ecf20Sopenharmony_ci return IRQ_HANDLED; 4378c2ecf20Sopenharmony_ci} 4388c2ecf20Sopenharmony_ci 4398c2ecf20Sopenharmony_cistatic irqreturn_t w5300_detect_link(int irq, void *ndev_instance) 4408c2ecf20Sopenharmony_ci{ 4418c2ecf20Sopenharmony_ci struct net_device *ndev = ndev_instance; 4428c2ecf20Sopenharmony_ci struct w5300_priv *priv = netdev_priv(ndev); 4438c2ecf20Sopenharmony_ci 4448c2ecf20Sopenharmony_ci if (netif_running(ndev)) { 4458c2ecf20Sopenharmony_ci if (gpio_get_value(priv->link_gpio) != 0) { 4468c2ecf20Sopenharmony_ci netif_info(priv, link, ndev, "link is up\n"); 4478c2ecf20Sopenharmony_ci netif_carrier_on(ndev); 4488c2ecf20Sopenharmony_ci } else { 4498c2ecf20Sopenharmony_ci netif_info(priv, link, ndev, "link is down\n"); 4508c2ecf20Sopenharmony_ci netif_carrier_off(ndev); 4518c2ecf20Sopenharmony_ci } 4528c2ecf20Sopenharmony_ci } 4538c2ecf20Sopenharmony_ci 4548c2ecf20Sopenharmony_ci return IRQ_HANDLED; 4558c2ecf20Sopenharmony_ci} 4568c2ecf20Sopenharmony_ci 4578c2ecf20Sopenharmony_cistatic void w5300_set_rx_mode(struct net_device *ndev) 4588c2ecf20Sopenharmony_ci{ 4598c2ecf20Sopenharmony_ci struct w5300_priv *priv = netdev_priv(ndev); 4608c2ecf20Sopenharmony_ci bool set_promisc = (ndev->flags & IFF_PROMISC) != 0; 4618c2ecf20Sopenharmony_ci 4628c2ecf20Sopenharmony_ci if (priv->promisc != set_promisc) { 4638c2ecf20Sopenharmony_ci priv->promisc = set_promisc; 4648c2ecf20Sopenharmony_ci w5300_hw_start(priv); 4658c2ecf20Sopenharmony_ci } 4668c2ecf20Sopenharmony_ci} 4678c2ecf20Sopenharmony_ci 4688c2ecf20Sopenharmony_cistatic int w5300_set_macaddr(struct net_device *ndev, void *addr) 4698c2ecf20Sopenharmony_ci{ 4708c2ecf20Sopenharmony_ci struct w5300_priv *priv = netdev_priv(ndev); 4718c2ecf20Sopenharmony_ci struct sockaddr *sock_addr = addr; 4728c2ecf20Sopenharmony_ci 4738c2ecf20Sopenharmony_ci if (!is_valid_ether_addr(sock_addr->sa_data)) 4748c2ecf20Sopenharmony_ci return -EADDRNOTAVAIL; 4758c2ecf20Sopenharmony_ci memcpy(ndev->dev_addr, sock_addr->sa_data, ETH_ALEN); 4768c2ecf20Sopenharmony_ci w5300_write_macaddr(priv); 4778c2ecf20Sopenharmony_ci return 0; 4788c2ecf20Sopenharmony_ci} 4798c2ecf20Sopenharmony_ci 4808c2ecf20Sopenharmony_cistatic int w5300_open(struct net_device *ndev) 4818c2ecf20Sopenharmony_ci{ 4828c2ecf20Sopenharmony_ci struct w5300_priv *priv = netdev_priv(ndev); 4838c2ecf20Sopenharmony_ci 4848c2ecf20Sopenharmony_ci netif_info(priv, ifup, ndev, "enabling\n"); 4858c2ecf20Sopenharmony_ci w5300_hw_start(priv); 4868c2ecf20Sopenharmony_ci napi_enable(&priv->napi); 4878c2ecf20Sopenharmony_ci netif_start_queue(ndev); 4888c2ecf20Sopenharmony_ci if (!gpio_is_valid(priv->link_gpio) || 4898c2ecf20Sopenharmony_ci gpio_get_value(priv->link_gpio) != 0) 4908c2ecf20Sopenharmony_ci netif_carrier_on(ndev); 4918c2ecf20Sopenharmony_ci return 0; 4928c2ecf20Sopenharmony_ci} 4938c2ecf20Sopenharmony_ci 4948c2ecf20Sopenharmony_cistatic int w5300_stop(struct net_device *ndev) 4958c2ecf20Sopenharmony_ci{ 4968c2ecf20Sopenharmony_ci struct w5300_priv *priv = netdev_priv(ndev); 4978c2ecf20Sopenharmony_ci 4988c2ecf20Sopenharmony_ci netif_info(priv, ifdown, ndev, "shutting down\n"); 4998c2ecf20Sopenharmony_ci w5300_hw_close(priv); 5008c2ecf20Sopenharmony_ci netif_carrier_off(ndev); 5018c2ecf20Sopenharmony_ci netif_stop_queue(ndev); 5028c2ecf20Sopenharmony_ci napi_disable(&priv->napi); 5038c2ecf20Sopenharmony_ci return 0; 5048c2ecf20Sopenharmony_ci} 5058c2ecf20Sopenharmony_ci 5068c2ecf20Sopenharmony_cistatic const struct ethtool_ops w5300_ethtool_ops = { 5078c2ecf20Sopenharmony_ci .get_drvinfo = w5300_get_drvinfo, 5088c2ecf20Sopenharmony_ci .get_msglevel = w5300_get_msglevel, 5098c2ecf20Sopenharmony_ci .set_msglevel = w5300_set_msglevel, 5108c2ecf20Sopenharmony_ci .get_link = w5300_get_link, 5118c2ecf20Sopenharmony_ci .get_regs_len = w5300_get_regs_len, 5128c2ecf20Sopenharmony_ci .get_regs = w5300_get_regs, 5138c2ecf20Sopenharmony_ci}; 5148c2ecf20Sopenharmony_ci 5158c2ecf20Sopenharmony_cistatic const struct net_device_ops w5300_netdev_ops = { 5168c2ecf20Sopenharmony_ci .ndo_open = w5300_open, 5178c2ecf20Sopenharmony_ci .ndo_stop = w5300_stop, 5188c2ecf20Sopenharmony_ci .ndo_start_xmit = w5300_start_tx, 5198c2ecf20Sopenharmony_ci .ndo_tx_timeout = w5300_tx_timeout, 5208c2ecf20Sopenharmony_ci .ndo_set_rx_mode = w5300_set_rx_mode, 5218c2ecf20Sopenharmony_ci .ndo_set_mac_address = w5300_set_macaddr, 5228c2ecf20Sopenharmony_ci .ndo_validate_addr = eth_validate_addr, 5238c2ecf20Sopenharmony_ci}; 5248c2ecf20Sopenharmony_ci 5258c2ecf20Sopenharmony_cistatic int w5300_hw_probe(struct platform_device *pdev) 5268c2ecf20Sopenharmony_ci{ 5278c2ecf20Sopenharmony_ci struct wiznet_platform_data *data = dev_get_platdata(&pdev->dev); 5288c2ecf20Sopenharmony_ci struct net_device *ndev = platform_get_drvdata(pdev); 5298c2ecf20Sopenharmony_ci struct w5300_priv *priv = netdev_priv(ndev); 5308c2ecf20Sopenharmony_ci const char *name = netdev_name(ndev); 5318c2ecf20Sopenharmony_ci struct resource *mem; 5328c2ecf20Sopenharmony_ci int mem_size; 5338c2ecf20Sopenharmony_ci int irq; 5348c2ecf20Sopenharmony_ci int ret; 5358c2ecf20Sopenharmony_ci 5368c2ecf20Sopenharmony_ci if (data && is_valid_ether_addr(data->mac_addr)) { 5378c2ecf20Sopenharmony_ci memcpy(ndev->dev_addr, data->mac_addr, ETH_ALEN); 5388c2ecf20Sopenharmony_ci } else { 5398c2ecf20Sopenharmony_ci eth_hw_addr_random(ndev); 5408c2ecf20Sopenharmony_ci } 5418c2ecf20Sopenharmony_ci 5428c2ecf20Sopenharmony_ci mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); 5438c2ecf20Sopenharmony_ci priv->base = devm_ioremap_resource(&pdev->dev, mem); 5448c2ecf20Sopenharmony_ci if (IS_ERR(priv->base)) 5458c2ecf20Sopenharmony_ci return PTR_ERR(priv->base); 5468c2ecf20Sopenharmony_ci 5478c2ecf20Sopenharmony_ci mem_size = resource_size(mem); 5488c2ecf20Sopenharmony_ci 5498c2ecf20Sopenharmony_ci spin_lock_init(&priv->reg_lock); 5508c2ecf20Sopenharmony_ci priv->indirect = mem_size < W5300_BUS_DIRECT_SIZE; 5518c2ecf20Sopenharmony_ci if (priv->indirect) { 5528c2ecf20Sopenharmony_ci priv->read = w5300_read_indirect; 5538c2ecf20Sopenharmony_ci priv->write = w5300_write_indirect; 5548c2ecf20Sopenharmony_ci } else { 5558c2ecf20Sopenharmony_ci priv->read = w5300_read_direct; 5568c2ecf20Sopenharmony_ci priv->write = w5300_write_direct; 5578c2ecf20Sopenharmony_ci } 5588c2ecf20Sopenharmony_ci 5598c2ecf20Sopenharmony_ci w5300_hw_reset(priv); 5608c2ecf20Sopenharmony_ci if (w5300_read(priv, W5300_IDR) != IDR_W5300) 5618c2ecf20Sopenharmony_ci return -ENODEV; 5628c2ecf20Sopenharmony_ci 5638c2ecf20Sopenharmony_ci irq = platform_get_irq(pdev, 0); 5648c2ecf20Sopenharmony_ci if (irq < 0) 5658c2ecf20Sopenharmony_ci return irq; 5668c2ecf20Sopenharmony_ci ret = request_irq(irq, w5300_interrupt, 5678c2ecf20Sopenharmony_ci IRQ_TYPE_LEVEL_LOW, name, ndev); 5688c2ecf20Sopenharmony_ci if (ret < 0) 5698c2ecf20Sopenharmony_ci return ret; 5708c2ecf20Sopenharmony_ci priv->irq = irq; 5718c2ecf20Sopenharmony_ci 5728c2ecf20Sopenharmony_ci priv->link_gpio = data ? data->link_gpio : -EINVAL; 5738c2ecf20Sopenharmony_ci if (gpio_is_valid(priv->link_gpio)) { 5748c2ecf20Sopenharmony_ci char *link_name = devm_kzalloc(&pdev->dev, 16, GFP_KERNEL); 5758c2ecf20Sopenharmony_ci if (!link_name) 5768c2ecf20Sopenharmony_ci return -ENOMEM; 5778c2ecf20Sopenharmony_ci snprintf(link_name, 16, "%s-link", name); 5788c2ecf20Sopenharmony_ci priv->link_irq = gpio_to_irq(priv->link_gpio); 5798c2ecf20Sopenharmony_ci if (request_any_context_irq(priv->link_irq, w5300_detect_link, 5808c2ecf20Sopenharmony_ci IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, 5818c2ecf20Sopenharmony_ci link_name, priv->ndev) < 0) 5828c2ecf20Sopenharmony_ci priv->link_gpio = -EINVAL; 5838c2ecf20Sopenharmony_ci } 5848c2ecf20Sopenharmony_ci 5858c2ecf20Sopenharmony_ci netdev_info(ndev, "at 0x%llx irq %d\n", (u64)mem->start, irq); 5868c2ecf20Sopenharmony_ci return 0; 5878c2ecf20Sopenharmony_ci} 5888c2ecf20Sopenharmony_ci 5898c2ecf20Sopenharmony_cistatic int w5300_probe(struct platform_device *pdev) 5908c2ecf20Sopenharmony_ci{ 5918c2ecf20Sopenharmony_ci struct w5300_priv *priv; 5928c2ecf20Sopenharmony_ci struct net_device *ndev; 5938c2ecf20Sopenharmony_ci int err; 5948c2ecf20Sopenharmony_ci 5958c2ecf20Sopenharmony_ci ndev = alloc_etherdev(sizeof(*priv)); 5968c2ecf20Sopenharmony_ci if (!ndev) 5978c2ecf20Sopenharmony_ci return -ENOMEM; 5988c2ecf20Sopenharmony_ci SET_NETDEV_DEV(ndev, &pdev->dev); 5998c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, ndev); 6008c2ecf20Sopenharmony_ci priv = netdev_priv(ndev); 6018c2ecf20Sopenharmony_ci priv->ndev = ndev; 6028c2ecf20Sopenharmony_ci 6038c2ecf20Sopenharmony_ci ndev->netdev_ops = &w5300_netdev_ops; 6048c2ecf20Sopenharmony_ci ndev->ethtool_ops = &w5300_ethtool_ops; 6058c2ecf20Sopenharmony_ci ndev->watchdog_timeo = HZ; 6068c2ecf20Sopenharmony_ci netif_napi_add(ndev, &priv->napi, w5300_napi_poll, 16); 6078c2ecf20Sopenharmony_ci 6088c2ecf20Sopenharmony_ci /* This chip doesn't support VLAN packets with normal MTU, 6098c2ecf20Sopenharmony_ci * so disable VLAN for this device. 6108c2ecf20Sopenharmony_ci */ 6118c2ecf20Sopenharmony_ci ndev->features |= NETIF_F_VLAN_CHALLENGED; 6128c2ecf20Sopenharmony_ci 6138c2ecf20Sopenharmony_ci err = register_netdev(ndev); 6148c2ecf20Sopenharmony_ci if (err < 0) 6158c2ecf20Sopenharmony_ci goto err_register; 6168c2ecf20Sopenharmony_ci 6178c2ecf20Sopenharmony_ci err = w5300_hw_probe(pdev); 6188c2ecf20Sopenharmony_ci if (err < 0) 6198c2ecf20Sopenharmony_ci goto err_hw_probe; 6208c2ecf20Sopenharmony_ci 6218c2ecf20Sopenharmony_ci return 0; 6228c2ecf20Sopenharmony_ci 6238c2ecf20Sopenharmony_cierr_hw_probe: 6248c2ecf20Sopenharmony_ci unregister_netdev(ndev); 6258c2ecf20Sopenharmony_cierr_register: 6268c2ecf20Sopenharmony_ci free_netdev(ndev); 6278c2ecf20Sopenharmony_ci return err; 6288c2ecf20Sopenharmony_ci} 6298c2ecf20Sopenharmony_ci 6308c2ecf20Sopenharmony_cistatic int w5300_remove(struct platform_device *pdev) 6318c2ecf20Sopenharmony_ci{ 6328c2ecf20Sopenharmony_ci struct net_device *ndev = platform_get_drvdata(pdev); 6338c2ecf20Sopenharmony_ci struct w5300_priv *priv = netdev_priv(ndev); 6348c2ecf20Sopenharmony_ci 6358c2ecf20Sopenharmony_ci w5300_hw_reset(priv); 6368c2ecf20Sopenharmony_ci free_irq(priv->irq, ndev); 6378c2ecf20Sopenharmony_ci if (gpio_is_valid(priv->link_gpio)) 6388c2ecf20Sopenharmony_ci free_irq(priv->link_irq, ndev); 6398c2ecf20Sopenharmony_ci 6408c2ecf20Sopenharmony_ci unregister_netdev(ndev); 6418c2ecf20Sopenharmony_ci free_netdev(ndev); 6428c2ecf20Sopenharmony_ci return 0; 6438c2ecf20Sopenharmony_ci} 6448c2ecf20Sopenharmony_ci 6458c2ecf20Sopenharmony_ci#ifdef CONFIG_PM_SLEEP 6468c2ecf20Sopenharmony_cistatic int w5300_suspend(struct device *dev) 6478c2ecf20Sopenharmony_ci{ 6488c2ecf20Sopenharmony_ci struct net_device *ndev = dev_get_drvdata(dev); 6498c2ecf20Sopenharmony_ci struct w5300_priv *priv = netdev_priv(ndev); 6508c2ecf20Sopenharmony_ci 6518c2ecf20Sopenharmony_ci if (netif_running(ndev)) { 6528c2ecf20Sopenharmony_ci netif_carrier_off(ndev); 6538c2ecf20Sopenharmony_ci netif_device_detach(ndev); 6548c2ecf20Sopenharmony_ci 6558c2ecf20Sopenharmony_ci w5300_hw_close(priv); 6568c2ecf20Sopenharmony_ci } 6578c2ecf20Sopenharmony_ci return 0; 6588c2ecf20Sopenharmony_ci} 6598c2ecf20Sopenharmony_ci 6608c2ecf20Sopenharmony_cistatic int w5300_resume(struct device *dev) 6618c2ecf20Sopenharmony_ci{ 6628c2ecf20Sopenharmony_ci struct net_device *ndev = dev_get_drvdata(dev); 6638c2ecf20Sopenharmony_ci struct w5300_priv *priv = netdev_priv(ndev); 6648c2ecf20Sopenharmony_ci 6658c2ecf20Sopenharmony_ci if (!netif_running(ndev)) { 6668c2ecf20Sopenharmony_ci w5300_hw_reset(priv); 6678c2ecf20Sopenharmony_ci w5300_hw_start(priv); 6688c2ecf20Sopenharmony_ci 6698c2ecf20Sopenharmony_ci netif_device_attach(ndev); 6708c2ecf20Sopenharmony_ci if (!gpio_is_valid(priv->link_gpio) || 6718c2ecf20Sopenharmony_ci gpio_get_value(priv->link_gpio) != 0) 6728c2ecf20Sopenharmony_ci netif_carrier_on(ndev); 6738c2ecf20Sopenharmony_ci } 6748c2ecf20Sopenharmony_ci return 0; 6758c2ecf20Sopenharmony_ci} 6768c2ecf20Sopenharmony_ci#endif /* CONFIG_PM_SLEEP */ 6778c2ecf20Sopenharmony_ci 6788c2ecf20Sopenharmony_cistatic SIMPLE_DEV_PM_OPS(w5300_pm_ops, w5300_suspend, w5300_resume); 6798c2ecf20Sopenharmony_ci 6808c2ecf20Sopenharmony_cistatic struct platform_driver w5300_driver = { 6818c2ecf20Sopenharmony_ci .driver = { 6828c2ecf20Sopenharmony_ci .name = DRV_NAME, 6838c2ecf20Sopenharmony_ci .pm = &w5300_pm_ops, 6848c2ecf20Sopenharmony_ci }, 6858c2ecf20Sopenharmony_ci .probe = w5300_probe, 6868c2ecf20Sopenharmony_ci .remove = w5300_remove, 6878c2ecf20Sopenharmony_ci}; 6888c2ecf20Sopenharmony_ci 6898c2ecf20Sopenharmony_cimodule_platform_driver(w5300_driver); 690