18c2ecf20Sopenharmony_ci/*
28c2ecf20Sopenharmony_ci * Freescale Ethernet controllers
38c2ecf20Sopenharmony_ci *
48c2ecf20Sopenharmony_ci * Copyright (c) 2005 Intracom S.A.
58c2ecf20Sopenharmony_ci *  by Pantelis Antoniou <panto@intracom.gr>
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci * 2005 (c) MontaVista Software, Inc.
88c2ecf20Sopenharmony_ci * Vitaly Bordug <vbordug@ru.mvista.com>
98c2ecf20Sopenharmony_ci *
108c2ecf20Sopenharmony_ci * This file is licensed under the terms of the GNU General Public License
118c2ecf20Sopenharmony_ci * version 2. This program is licensed "as is" without any warranty of any
128c2ecf20Sopenharmony_ci * kind, whether express or implied.
138c2ecf20Sopenharmony_ci */
148c2ecf20Sopenharmony_ci
158c2ecf20Sopenharmony_ci#include <linux/module.h>
168c2ecf20Sopenharmony_ci#include <linux/kernel.h>
178c2ecf20Sopenharmony_ci#include <linux/types.h>
188c2ecf20Sopenharmony_ci#include <linux/string.h>
198c2ecf20Sopenharmony_ci#include <linux/ptrace.h>
208c2ecf20Sopenharmony_ci#include <linux/errno.h>
218c2ecf20Sopenharmony_ci#include <linux/crc32.h>
228c2ecf20Sopenharmony_ci#include <linux/ioport.h>
238c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
248c2ecf20Sopenharmony_ci#include <linux/delay.h>
258c2ecf20Sopenharmony_ci#include <linux/netdevice.h>
268c2ecf20Sopenharmony_ci#include <linux/etherdevice.h>
278c2ecf20Sopenharmony_ci#include <linux/skbuff.h>
288c2ecf20Sopenharmony_ci#include <linux/spinlock.h>
298c2ecf20Sopenharmony_ci#include <linux/mii.h>
308c2ecf20Sopenharmony_ci#include <linux/ethtool.h>
318c2ecf20Sopenharmony_ci#include <linux/bitops.h>
328c2ecf20Sopenharmony_ci#include <linux/fs.h>
338c2ecf20Sopenharmony_ci#include <linux/platform_device.h>
348c2ecf20Sopenharmony_ci#include <linux/of_address.h>
358c2ecf20Sopenharmony_ci#include <linux/of_device.h>
368c2ecf20Sopenharmony_ci#include <linux/of_irq.h>
378c2ecf20Sopenharmony_ci#include <linux/gfp.h>
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_ci#include <asm/irq.h>
408c2ecf20Sopenharmony_ci#include <linux/uaccess.h>
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_ci#include "fs_enet.h"
438c2ecf20Sopenharmony_ci#include "fec.h"
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_ci/*************************************************/
468c2ecf20Sopenharmony_ci
478c2ecf20Sopenharmony_ci#if defined(CONFIG_CPM1)
488c2ecf20Sopenharmony_ci/* for a CPM1 __raw_xxx's are sufficient */
498c2ecf20Sopenharmony_ci#define __fs_out32(addr, x)	__raw_writel(x, addr)
508c2ecf20Sopenharmony_ci#define __fs_out16(addr, x)	__raw_writew(x, addr)
518c2ecf20Sopenharmony_ci#define __fs_in32(addr)	__raw_readl(addr)
528c2ecf20Sopenharmony_ci#define __fs_in16(addr)	__raw_readw(addr)
538c2ecf20Sopenharmony_ci#else
548c2ecf20Sopenharmony_ci/* for others play it safe */
558c2ecf20Sopenharmony_ci#define __fs_out32(addr, x)	out_be32(addr, x)
568c2ecf20Sopenharmony_ci#define __fs_out16(addr, x)	out_be16(addr, x)
578c2ecf20Sopenharmony_ci#define __fs_in32(addr)	in_be32(addr)
588c2ecf20Sopenharmony_ci#define __fs_in16(addr)	in_be16(addr)
598c2ecf20Sopenharmony_ci#endif
608c2ecf20Sopenharmony_ci
618c2ecf20Sopenharmony_ci/* write */
628c2ecf20Sopenharmony_ci#define FW(_fecp, _reg, _v) __fs_out32(&(_fecp)->fec_ ## _reg, (_v))
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_ci/* read */
658c2ecf20Sopenharmony_ci#define FR(_fecp, _reg)	__fs_in32(&(_fecp)->fec_ ## _reg)
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_ci/* set bits */
688c2ecf20Sopenharmony_ci#define FS(_fecp, _reg, _v) FW(_fecp, _reg, FR(_fecp, _reg) | (_v))
698c2ecf20Sopenharmony_ci
708c2ecf20Sopenharmony_ci/* clear bits */
718c2ecf20Sopenharmony_ci#define FC(_fecp, _reg, _v) FW(_fecp, _reg, FR(_fecp, _reg) & ~(_v))
728c2ecf20Sopenharmony_ci
738c2ecf20Sopenharmony_ci/*
748c2ecf20Sopenharmony_ci * Delay to wait for FEC reset command to complete (in us)
758c2ecf20Sopenharmony_ci */
768c2ecf20Sopenharmony_ci#define FEC_RESET_DELAY		50
778c2ecf20Sopenharmony_ci
788c2ecf20Sopenharmony_cistatic int whack_reset(struct fec __iomem *fecp)
798c2ecf20Sopenharmony_ci{
808c2ecf20Sopenharmony_ci	int i;
818c2ecf20Sopenharmony_ci
828c2ecf20Sopenharmony_ci	FW(fecp, ecntrl, FEC_ECNTRL_PINMUX | FEC_ECNTRL_RESET);
838c2ecf20Sopenharmony_ci	for (i = 0; i < FEC_RESET_DELAY; i++) {
848c2ecf20Sopenharmony_ci		if ((FR(fecp, ecntrl) & FEC_ECNTRL_RESET) == 0)
858c2ecf20Sopenharmony_ci			return 0;	/* OK */
868c2ecf20Sopenharmony_ci		udelay(1);
878c2ecf20Sopenharmony_ci	}
888c2ecf20Sopenharmony_ci
898c2ecf20Sopenharmony_ci	return -1;
908c2ecf20Sopenharmony_ci}
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_cistatic int do_pd_setup(struct fs_enet_private *fep)
938c2ecf20Sopenharmony_ci{
948c2ecf20Sopenharmony_ci	struct platform_device *ofdev = to_platform_device(fep->dev);
958c2ecf20Sopenharmony_ci
968c2ecf20Sopenharmony_ci	fep->interrupt = irq_of_parse_and_map(ofdev->dev.of_node, 0);
978c2ecf20Sopenharmony_ci	if (!fep->interrupt)
988c2ecf20Sopenharmony_ci		return -EINVAL;
998c2ecf20Sopenharmony_ci
1008c2ecf20Sopenharmony_ci	fep->fec.fecp = of_iomap(ofdev->dev.of_node, 0);
1018c2ecf20Sopenharmony_ci	if (!fep->fec.fecp)
1028c2ecf20Sopenharmony_ci		return -EINVAL;
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_ci	return 0;
1058c2ecf20Sopenharmony_ci}
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_ci#define FEC_NAPI_EVENT_MSK	(FEC_ENET_RXF | FEC_ENET_RXB | FEC_ENET_TXF)
1088c2ecf20Sopenharmony_ci#define FEC_EVENT		(FEC_ENET_RXF | FEC_ENET_TXF)
1098c2ecf20Sopenharmony_ci#define FEC_ERR_EVENT_MSK	(FEC_ENET_HBERR | FEC_ENET_BABR | \
1108c2ecf20Sopenharmony_ci				 FEC_ENET_BABT | FEC_ENET_EBERR)
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_cistatic int setup_data(struct net_device *dev)
1138c2ecf20Sopenharmony_ci{
1148c2ecf20Sopenharmony_ci	struct fs_enet_private *fep = netdev_priv(dev);
1158c2ecf20Sopenharmony_ci
1168c2ecf20Sopenharmony_ci	if (do_pd_setup(fep) != 0)
1178c2ecf20Sopenharmony_ci		return -EINVAL;
1188c2ecf20Sopenharmony_ci
1198c2ecf20Sopenharmony_ci	fep->fec.hthi = 0;
1208c2ecf20Sopenharmony_ci	fep->fec.htlo = 0;
1218c2ecf20Sopenharmony_ci
1228c2ecf20Sopenharmony_ci	fep->ev_napi = FEC_NAPI_EVENT_MSK;
1238c2ecf20Sopenharmony_ci	fep->ev = FEC_EVENT;
1248c2ecf20Sopenharmony_ci	fep->ev_err = FEC_ERR_EVENT_MSK;
1258c2ecf20Sopenharmony_ci
1268c2ecf20Sopenharmony_ci	return 0;
1278c2ecf20Sopenharmony_ci}
1288c2ecf20Sopenharmony_ci
1298c2ecf20Sopenharmony_cistatic int allocate_bd(struct net_device *dev)
1308c2ecf20Sopenharmony_ci{
1318c2ecf20Sopenharmony_ci	struct fs_enet_private *fep = netdev_priv(dev);
1328c2ecf20Sopenharmony_ci	const struct fs_platform_info *fpi = fep->fpi;
1338c2ecf20Sopenharmony_ci
1348c2ecf20Sopenharmony_ci	fep->ring_base = (void __force __iomem *)dma_alloc_coherent(fep->dev,
1358c2ecf20Sopenharmony_ci					    (fpi->tx_ring + fpi->rx_ring) *
1368c2ecf20Sopenharmony_ci					    sizeof(cbd_t), &fep->ring_mem_addr,
1378c2ecf20Sopenharmony_ci					    GFP_KERNEL);
1388c2ecf20Sopenharmony_ci	if (fep->ring_base == NULL)
1398c2ecf20Sopenharmony_ci		return -ENOMEM;
1408c2ecf20Sopenharmony_ci
1418c2ecf20Sopenharmony_ci	return 0;
1428c2ecf20Sopenharmony_ci}
1438c2ecf20Sopenharmony_ci
1448c2ecf20Sopenharmony_cistatic void free_bd(struct net_device *dev)
1458c2ecf20Sopenharmony_ci{
1468c2ecf20Sopenharmony_ci	struct fs_enet_private *fep = netdev_priv(dev);
1478c2ecf20Sopenharmony_ci	const struct fs_platform_info *fpi = fep->fpi;
1488c2ecf20Sopenharmony_ci
1498c2ecf20Sopenharmony_ci	if(fep->ring_base)
1508c2ecf20Sopenharmony_ci		dma_free_coherent(fep->dev, (fpi->tx_ring + fpi->rx_ring)
1518c2ecf20Sopenharmony_ci					* sizeof(cbd_t),
1528c2ecf20Sopenharmony_ci					(void __force *)fep->ring_base,
1538c2ecf20Sopenharmony_ci					fep->ring_mem_addr);
1548c2ecf20Sopenharmony_ci}
1558c2ecf20Sopenharmony_ci
1568c2ecf20Sopenharmony_cistatic void cleanup_data(struct net_device *dev)
1578c2ecf20Sopenharmony_ci{
1588c2ecf20Sopenharmony_ci	/* nothing */
1598c2ecf20Sopenharmony_ci}
1608c2ecf20Sopenharmony_ci
1618c2ecf20Sopenharmony_cistatic void set_promiscuous_mode(struct net_device *dev)
1628c2ecf20Sopenharmony_ci{
1638c2ecf20Sopenharmony_ci	struct fs_enet_private *fep = netdev_priv(dev);
1648c2ecf20Sopenharmony_ci	struct fec __iomem *fecp = fep->fec.fecp;
1658c2ecf20Sopenharmony_ci
1668c2ecf20Sopenharmony_ci	FS(fecp, r_cntrl, FEC_RCNTRL_PROM);
1678c2ecf20Sopenharmony_ci}
1688c2ecf20Sopenharmony_ci
1698c2ecf20Sopenharmony_cistatic void set_multicast_start(struct net_device *dev)
1708c2ecf20Sopenharmony_ci{
1718c2ecf20Sopenharmony_ci	struct fs_enet_private *fep = netdev_priv(dev);
1728c2ecf20Sopenharmony_ci
1738c2ecf20Sopenharmony_ci	fep->fec.hthi = 0;
1748c2ecf20Sopenharmony_ci	fep->fec.htlo = 0;
1758c2ecf20Sopenharmony_ci}
1768c2ecf20Sopenharmony_ci
1778c2ecf20Sopenharmony_cistatic void set_multicast_one(struct net_device *dev, const u8 *mac)
1788c2ecf20Sopenharmony_ci{
1798c2ecf20Sopenharmony_ci	struct fs_enet_private *fep = netdev_priv(dev);
1808c2ecf20Sopenharmony_ci	int temp, hash_index;
1818c2ecf20Sopenharmony_ci	u32 crc, csrVal;
1828c2ecf20Sopenharmony_ci
1838c2ecf20Sopenharmony_ci	crc = ether_crc(6, mac);
1848c2ecf20Sopenharmony_ci
1858c2ecf20Sopenharmony_ci	temp = (crc & 0x3f) >> 1;
1868c2ecf20Sopenharmony_ci	hash_index = ((temp & 0x01) << 4) |
1878c2ecf20Sopenharmony_ci		     ((temp & 0x02) << 2) |
1888c2ecf20Sopenharmony_ci		     ((temp & 0x04)) |
1898c2ecf20Sopenharmony_ci		     ((temp & 0x08) >> 2) |
1908c2ecf20Sopenharmony_ci		     ((temp & 0x10) >> 4);
1918c2ecf20Sopenharmony_ci	csrVal = 1 << hash_index;
1928c2ecf20Sopenharmony_ci	if (crc & 1)
1938c2ecf20Sopenharmony_ci		fep->fec.hthi |= csrVal;
1948c2ecf20Sopenharmony_ci	else
1958c2ecf20Sopenharmony_ci		fep->fec.htlo |= csrVal;
1968c2ecf20Sopenharmony_ci}
1978c2ecf20Sopenharmony_ci
1988c2ecf20Sopenharmony_cistatic void set_multicast_finish(struct net_device *dev)
1998c2ecf20Sopenharmony_ci{
2008c2ecf20Sopenharmony_ci	struct fs_enet_private *fep = netdev_priv(dev);
2018c2ecf20Sopenharmony_ci	struct fec __iomem *fecp = fep->fec.fecp;
2028c2ecf20Sopenharmony_ci
2038c2ecf20Sopenharmony_ci	/* if all multi or too many multicasts; just enable all */
2048c2ecf20Sopenharmony_ci	if ((dev->flags & IFF_ALLMULTI) != 0 ||
2058c2ecf20Sopenharmony_ci	    netdev_mc_count(dev) > FEC_MAX_MULTICAST_ADDRS) {
2068c2ecf20Sopenharmony_ci		fep->fec.hthi = 0xffffffffU;
2078c2ecf20Sopenharmony_ci		fep->fec.htlo = 0xffffffffU;
2088c2ecf20Sopenharmony_ci	}
2098c2ecf20Sopenharmony_ci
2108c2ecf20Sopenharmony_ci	FC(fecp, r_cntrl, FEC_RCNTRL_PROM);
2118c2ecf20Sopenharmony_ci	FW(fecp, grp_hash_table_high, fep->fec.hthi);
2128c2ecf20Sopenharmony_ci	FW(fecp, grp_hash_table_low, fep->fec.htlo);
2138c2ecf20Sopenharmony_ci}
2148c2ecf20Sopenharmony_ci
2158c2ecf20Sopenharmony_cistatic void set_multicast_list(struct net_device *dev)
2168c2ecf20Sopenharmony_ci{
2178c2ecf20Sopenharmony_ci	struct netdev_hw_addr *ha;
2188c2ecf20Sopenharmony_ci
2198c2ecf20Sopenharmony_ci	if ((dev->flags & IFF_PROMISC) == 0) {
2208c2ecf20Sopenharmony_ci		set_multicast_start(dev);
2218c2ecf20Sopenharmony_ci		netdev_for_each_mc_addr(ha, dev)
2228c2ecf20Sopenharmony_ci			set_multicast_one(dev, ha->addr);
2238c2ecf20Sopenharmony_ci		set_multicast_finish(dev);
2248c2ecf20Sopenharmony_ci	} else
2258c2ecf20Sopenharmony_ci		set_promiscuous_mode(dev);
2268c2ecf20Sopenharmony_ci}
2278c2ecf20Sopenharmony_ci
2288c2ecf20Sopenharmony_cistatic void restart(struct net_device *dev)
2298c2ecf20Sopenharmony_ci{
2308c2ecf20Sopenharmony_ci	struct fs_enet_private *fep = netdev_priv(dev);
2318c2ecf20Sopenharmony_ci	struct fec __iomem *fecp = fep->fec.fecp;
2328c2ecf20Sopenharmony_ci	const struct fs_platform_info *fpi = fep->fpi;
2338c2ecf20Sopenharmony_ci	dma_addr_t rx_bd_base_phys, tx_bd_base_phys;
2348c2ecf20Sopenharmony_ci	int r;
2358c2ecf20Sopenharmony_ci	u32 addrhi, addrlo;
2368c2ecf20Sopenharmony_ci
2378c2ecf20Sopenharmony_ci	struct mii_bus *mii = dev->phydev->mdio.bus;
2388c2ecf20Sopenharmony_ci	struct fec_info* fec_inf = mii->priv;
2398c2ecf20Sopenharmony_ci
2408c2ecf20Sopenharmony_ci	r = whack_reset(fep->fec.fecp);
2418c2ecf20Sopenharmony_ci	if (r != 0)
2428c2ecf20Sopenharmony_ci		dev_err(fep->dev, "FEC Reset FAILED!\n");
2438c2ecf20Sopenharmony_ci	/*
2448c2ecf20Sopenharmony_ci	 * Set station address.
2458c2ecf20Sopenharmony_ci	 */
2468c2ecf20Sopenharmony_ci	addrhi = ((u32) dev->dev_addr[0] << 24) |
2478c2ecf20Sopenharmony_ci		 ((u32) dev->dev_addr[1] << 16) |
2488c2ecf20Sopenharmony_ci		 ((u32) dev->dev_addr[2] <<  8) |
2498c2ecf20Sopenharmony_ci		  (u32) dev->dev_addr[3];
2508c2ecf20Sopenharmony_ci	addrlo = ((u32) dev->dev_addr[4] << 24) |
2518c2ecf20Sopenharmony_ci		 ((u32) dev->dev_addr[5] << 16);
2528c2ecf20Sopenharmony_ci	FW(fecp, addr_low, addrhi);
2538c2ecf20Sopenharmony_ci	FW(fecp, addr_high, addrlo);
2548c2ecf20Sopenharmony_ci
2558c2ecf20Sopenharmony_ci	/*
2568c2ecf20Sopenharmony_ci	 * Reset all multicast.
2578c2ecf20Sopenharmony_ci	 */
2588c2ecf20Sopenharmony_ci	FW(fecp, grp_hash_table_high, fep->fec.hthi);
2598c2ecf20Sopenharmony_ci	FW(fecp, grp_hash_table_low, fep->fec.htlo);
2608c2ecf20Sopenharmony_ci
2618c2ecf20Sopenharmony_ci	/*
2628c2ecf20Sopenharmony_ci	 * Set maximum receive buffer size.
2638c2ecf20Sopenharmony_ci	 */
2648c2ecf20Sopenharmony_ci	FW(fecp, r_buff_size, PKT_MAXBLR_SIZE);
2658c2ecf20Sopenharmony_ci#ifdef CONFIG_FS_ENET_MPC5121_FEC
2668c2ecf20Sopenharmony_ci	FW(fecp, r_cntrl, PKT_MAXBUF_SIZE << 16);
2678c2ecf20Sopenharmony_ci#else
2688c2ecf20Sopenharmony_ci	FW(fecp, r_hash, PKT_MAXBUF_SIZE);
2698c2ecf20Sopenharmony_ci#endif
2708c2ecf20Sopenharmony_ci
2718c2ecf20Sopenharmony_ci	/* get physical address */
2728c2ecf20Sopenharmony_ci	rx_bd_base_phys = fep->ring_mem_addr;
2738c2ecf20Sopenharmony_ci	tx_bd_base_phys = rx_bd_base_phys + sizeof(cbd_t) * fpi->rx_ring;
2748c2ecf20Sopenharmony_ci
2758c2ecf20Sopenharmony_ci	/*
2768c2ecf20Sopenharmony_ci	 * Set receive and transmit descriptor base.
2778c2ecf20Sopenharmony_ci	 */
2788c2ecf20Sopenharmony_ci	FW(fecp, r_des_start, rx_bd_base_phys);
2798c2ecf20Sopenharmony_ci	FW(fecp, x_des_start, tx_bd_base_phys);
2808c2ecf20Sopenharmony_ci
2818c2ecf20Sopenharmony_ci	fs_init_bds(dev);
2828c2ecf20Sopenharmony_ci
2838c2ecf20Sopenharmony_ci	/*
2848c2ecf20Sopenharmony_ci	 * Enable big endian and don't care about SDMA FC.
2858c2ecf20Sopenharmony_ci	 */
2868c2ecf20Sopenharmony_ci#ifdef CONFIG_FS_ENET_MPC5121_FEC
2878c2ecf20Sopenharmony_ci	FS(fecp, dma_control, 0xC0000000);
2888c2ecf20Sopenharmony_ci#else
2898c2ecf20Sopenharmony_ci	FW(fecp, fun_code, 0x78000000);
2908c2ecf20Sopenharmony_ci#endif
2918c2ecf20Sopenharmony_ci
2928c2ecf20Sopenharmony_ci	/*
2938c2ecf20Sopenharmony_ci	 * Set MII speed.
2948c2ecf20Sopenharmony_ci	 */
2958c2ecf20Sopenharmony_ci	FW(fecp, mii_speed, fec_inf->mii_speed);
2968c2ecf20Sopenharmony_ci
2978c2ecf20Sopenharmony_ci	/*
2988c2ecf20Sopenharmony_ci	 * Clear any outstanding interrupt.
2998c2ecf20Sopenharmony_ci	 */
3008c2ecf20Sopenharmony_ci	FW(fecp, ievent, 0xffc0);
3018c2ecf20Sopenharmony_ci#ifndef CONFIG_FS_ENET_MPC5121_FEC
3028c2ecf20Sopenharmony_ci	FW(fecp, ivec, (virq_to_hw(fep->interrupt) / 2) << 29);
3038c2ecf20Sopenharmony_ci
3048c2ecf20Sopenharmony_ci	FW(fecp, r_cntrl, FEC_RCNTRL_MII_MODE);	/* MII enable */
3058c2ecf20Sopenharmony_ci#else
3068c2ecf20Sopenharmony_ci	/*
3078c2ecf20Sopenharmony_ci	 * Only set MII/RMII mode - do not touch maximum frame length
3088c2ecf20Sopenharmony_ci	 * configured before.
3098c2ecf20Sopenharmony_ci	 */
3108c2ecf20Sopenharmony_ci	FS(fecp, r_cntrl, fpi->use_rmii ?
3118c2ecf20Sopenharmony_ci			FEC_RCNTRL_RMII_MODE : FEC_RCNTRL_MII_MODE);
3128c2ecf20Sopenharmony_ci#endif
3138c2ecf20Sopenharmony_ci	/*
3148c2ecf20Sopenharmony_ci	 * adjust to duplex mode
3158c2ecf20Sopenharmony_ci	 */
3168c2ecf20Sopenharmony_ci	if (dev->phydev->duplex) {
3178c2ecf20Sopenharmony_ci		FC(fecp, r_cntrl, FEC_RCNTRL_DRT);
3188c2ecf20Sopenharmony_ci		FS(fecp, x_cntrl, FEC_TCNTRL_FDEN);	/* FD enable */
3198c2ecf20Sopenharmony_ci	} else {
3208c2ecf20Sopenharmony_ci		FS(fecp, r_cntrl, FEC_RCNTRL_DRT);
3218c2ecf20Sopenharmony_ci		FC(fecp, x_cntrl, FEC_TCNTRL_FDEN);	/* FD disable */
3228c2ecf20Sopenharmony_ci	}
3238c2ecf20Sopenharmony_ci
3248c2ecf20Sopenharmony_ci	/* Restore multicast and promiscuous settings */
3258c2ecf20Sopenharmony_ci	set_multicast_list(dev);
3268c2ecf20Sopenharmony_ci
3278c2ecf20Sopenharmony_ci	/*
3288c2ecf20Sopenharmony_ci	 * Enable interrupts we wish to service.
3298c2ecf20Sopenharmony_ci	 */
3308c2ecf20Sopenharmony_ci	FW(fecp, imask, FEC_ENET_TXF | FEC_ENET_TXB |
3318c2ecf20Sopenharmony_ci	   FEC_ENET_RXF | FEC_ENET_RXB);
3328c2ecf20Sopenharmony_ci
3338c2ecf20Sopenharmony_ci	/*
3348c2ecf20Sopenharmony_ci	 * And last, enable the transmit and receive processing.
3358c2ecf20Sopenharmony_ci	 */
3368c2ecf20Sopenharmony_ci	FW(fecp, ecntrl, FEC_ECNTRL_PINMUX | FEC_ECNTRL_ETHER_EN);
3378c2ecf20Sopenharmony_ci	FW(fecp, r_des_active, 0x01000000);
3388c2ecf20Sopenharmony_ci}
3398c2ecf20Sopenharmony_ci
3408c2ecf20Sopenharmony_cistatic void stop(struct net_device *dev)
3418c2ecf20Sopenharmony_ci{
3428c2ecf20Sopenharmony_ci	struct fs_enet_private *fep = netdev_priv(dev);
3438c2ecf20Sopenharmony_ci	const struct fs_platform_info *fpi = fep->fpi;
3448c2ecf20Sopenharmony_ci	struct fec __iomem *fecp = fep->fec.fecp;
3458c2ecf20Sopenharmony_ci
3468c2ecf20Sopenharmony_ci	struct fec_info *feci = dev->phydev->mdio.bus->priv;
3478c2ecf20Sopenharmony_ci
3488c2ecf20Sopenharmony_ci	int i;
3498c2ecf20Sopenharmony_ci
3508c2ecf20Sopenharmony_ci	if ((FR(fecp, ecntrl) & FEC_ECNTRL_ETHER_EN) == 0)
3518c2ecf20Sopenharmony_ci		return;		/* already down */
3528c2ecf20Sopenharmony_ci
3538c2ecf20Sopenharmony_ci	FW(fecp, x_cntrl, 0x01);	/* Graceful transmit stop */
3548c2ecf20Sopenharmony_ci	for (i = 0; ((FR(fecp, ievent) & 0x10000000) == 0) &&
3558c2ecf20Sopenharmony_ci	     i < FEC_RESET_DELAY; i++)
3568c2ecf20Sopenharmony_ci		udelay(1);
3578c2ecf20Sopenharmony_ci
3588c2ecf20Sopenharmony_ci	if (i == FEC_RESET_DELAY)
3598c2ecf20Sopenharmony_ci		dev_warn(fep->dev, "FEC timeout on graceful transmit stop\n");
3608c2ecf20Sopenharmony_ci	/*
3618c2ecf20Sopenharmony_ci	 * Disable FEC. Let only MII interrupts.
3628c2ecf20Sopenharmony_ci	 */
3638c2ecf20Sopenharmony_ci	FW(fecp, imask, 0);
3648c2ecf20Sopenharmony_ci	FC(fecp, ecntrl, FEC_ECNTRL_ETHER_EN);
3658c2ecf20Sopenharmony_ci
3668c2ecf20Sopenharmony_ci	fs_cleanup_bds(dev);
3678c2ecf20Sopenharmony_ci
3688c2ecf20Sopenharmony_ci	/* shut down FEC1? that's where the mii bus is */
3698c2ecf20Sopenharmony_ci	if (fpi->has_phy) {
3708c2ecf20Sopenharmony_ci		FS(fecp, r_cntrl, fpi->use_rmii ?
3718c2ecf20Sopenharmony_ci				FEC_RCNTRL_RMII_MODE :
3728c2ecf20Sopenharmony_ci				FEC_RCNTRL_MII_MODE);	/* MII/RMII enable */
3738c2ecf20Sopenharmony_ci		FS(fecp, ecntrl, FEC_ECNTRL_PINMUX | FEC_ECNTRL_ETHER_EN);
3748c2ecf20Sopenharmony_ci		FW(fecp, ievent, FEC_ENET_MII);
3758c2ecf20Sopenharmony_ci		FW(fecp, mii_speed, feci->mii_speed);
3768c2ecf20Sopenharmony_ci	}
3778c2ecf20Sopenharmony_ci}
3788c2ecf20Sopenharmony_ci
3798c2ecf20Sopenharmony_cistatic void napi_clear_event_fs(struct net_device *dev)
3808c2ecf20Sopenharmony_ci{
3818c2ecf20Sopenharmony_ci	struct fs_enet_private *fep = netdev_priv(dev);
3828c2ecf20Sopenharmony_ci	struct fec __iomem *fecp = fep->fec.fecp;
3838c2ecf20Sopenharmony_ci
3848c2ecf20Sopenharmony_ci	FW(fecp, ievent, FEC_NAPI_EVENT_MSK);
3858c2ecf20Sopenharmony_ci}
3868c2ecf20Sopenharmony_ci
3878c2ecf20Sopenharmony_cistatic void napi_enable_fs(struct net_device *dev)
3888c2ecf20Sopenharmony_ci{
3898c2ecf20Sopenharmony_ci	struct fs_enet_private *fep = netdev_priv(dev);
3908c2ecf20Sopenharmony_ci	struct fec __iomem *fecp = fep->fec.fecp;
3918c2ecf20Sopenharmony_ci
3928c2ecf20Sopenharmony_ci	FS(fecp, imask, FEC_NAPI_EVENT_MSK);
3938c2ecf20Sopenharmony_ci}
3948c2ecf20Sopenharmony_ci
3958c2ecf20Sopenharmony_cistatic void napi_disable_fs(struct net_device *dev)
3968c2ecf20Sopenharmony_ci{
3978c2ecf20Sopenharmony_ci	struct fs_enet_private *fep = netdev_priv(dev);
3988c2ecf20Sopenharmony_ci	struct fec __iomem *fecp = fep->fec.fecp;
3998c2ecf20Sopenharmony_ci
4008c2ecf20Sopenharmony_ci	FC(fecp, imask, FEC_NAPI_EVENT_MSK);
4018c2ecf20Sopenharmony_ci}
4028c2ecf20Sopenharmony_ci
4038c2ecf20Sopenharmony_cistatic void rx_bd_done(struct net_device *dev)
4048c2ecf20Sopenharmony_ci{
4058c2ecf20Sopenharmony_ci	struct fs_enet_private *fep = netdev_priv(dev);
4068c2ecf20Sopenharmony_ci	struct fec __iomem *fecp = fep->fec.fecp;
4078c2ecf20Sopenharmony_ci
4088c2ecf20Sopenharmony_ci	FW(fecp, r_des_active, 0x01000000);
4098c2ecf20Sopenharmony_ci}
4108c2ecf20Sopenharmony_ci
4118c2ecf20Sopenharmony_cistatic void tx_kickstart(struct net_device *dev)
4128c2ecf20Sopenharmony_ci{
4138c2ecf20Sopenharmony_ci	struct fs_enet_private *fep = netdev_priv(dev);
4148c2ecf20Sopenharmony_ci	struct fec __iomem *fecp = fep->fec.fecp;
4158c2ecf20Sopenharmony_ci
4168c2ecf20Sopenharmony_ci	FW(fecp, x_des_active, 0x01000000);
4178c2ecf20Sopenharmony_ci}
4188c2ecf20Sopenharmony_ci
4198c2ecf20Sopenharmony_cistatic u32 get_int_events(struct net_device *dev)
4208c2ecf20Sopenharmony_ci{
4218c2ecf20Sopenharmony_ci	struct fs_enet_private *fep = netdev_priv(dev);
4228c2ecf20Sopenharmony_ci	struct fec __iomem *fecp = fep->fec.fecp;
4238c2ecf20Sopenharmony_ci
4248c2ecf20Sopenharmony_ci	return FR(fecp, ievent) & FR(fecp, imask);
4258c2ecf20Sopenharmony_ci}
4268c2ecf20Sopenharmony_ci
4278c2ecf20Sopenharmony_cistatic void clear_int_events(struct net_device *dev, u32 int_events)
4288c2ecf20Sopenharmony_ci{
4298c2ecf20Sopenharmony_ci	struct fs_enet_private *fep = netdev_priv(dev);
4308c2ecf20Sopenharmony_ci	struct fec __iomem *fecp = fep->fec.fecp;
4318c2ecf20Sopenharmony_ci
4328c2ecf20Sopenharmony_ci	FW(fecp, ievent, int_events);
4338c2ecf20Sopenharmony_ci}
4348c2ecf20Sopenharmony_ci
4358c2ecf20Sopenharmony_cistatic void ev_error(struct net_device *dev, u32 int_events)
4368c2ecf20Sopenharmony_ci{
4378c2ecf20Sopenharmony_ci	struct fs_enet_private *fep = netdev_priv(dev);
4388c2ecf20Sopenharmony_ci
4398c2ecf20Sopenharmony_ci	dev_warn(fep->dev, "FEC ERROR(s) 0x%x\n", int_events);
4408c2ecf20Sopenharmony_ci}
4418c2ecf20Sopenharmony_ci
4428c2ecf20Sopenharmony_cistatic int get_regs(struct net_device *dev, void *p, int *sizep)
4438c2ecf20Sopenharmony_ci{
4448c2ecf20Sopenharmony_ci	struct fs_enet_private *fep = netdev_priv(dev);
4458c2ecf20Sopenharmony_ci
4468c2ecf20Sopenharmony_ci	if (*sizep < sizeof(struct fec))
4478c2ecf20Sopenharmony_ci		return -EINVAL;
4488c2ecf20Sopenharmony_ci
4498c2ecf20Sopenharmony_ci	memcpy_fromio(p, fep->fec.fecp, sizeof(struct fec));
4508c2ecf20Sopenharmony_ci
4518c2ecf20Sopenharmony_ci	return 0;
4528c2ecf20Sopenharmony_ci}
4538c2ecf20Sopenharmony_ci
4548c2ecf20Sopenharmony_cistatic int get_regs_len(struct net_device *dev)
4558c2ecf20Sopenharmony_ci{
4568c2ecf20Sopenharmony_ci	return sizeof(struct fec);
4578c2ecf20Sopenharmony_ci}
4588c2ecf20Sopenharmony_ci
4598c2ecf20Sopenharmony_cistatic void tx_restart(struct net_device *dev)
4608c2ecf20Sopenharmony_ci{
4618c2ecf20Sopenharmony_ci	/* nothing */
4628c2ecf20Sopenharmony_ci}
4638c2ecf20Sopenharmony_ci
4648c2ecf20Sopenharmony_ci/*************************************************************************/
4658c2ecf20Sopenharmony_ci
4668c2ecf20Sopenharmony_ciconst struct fs_ops fs_fec_ops = {
4678c2ecf20Sopenharmony_ci	.setup_data		= setup_data,
4688c2ecf20Sopenharmony_ci	.cleanup_data		= cleanup_data,
4698c2ecf20Sopenharmony_ci	.set_multicast_list	= set_multicast_list,
4708c2ecf20Sopenharmony_ci	.restart		= restart,
4718c2ecf20Sopenharmony_ci	.stop			= stop,
4728c2ecf20Sopenharmony_ci	.napi_clear_event	= napi_clear_event_fs,
4738c2ecf20Sopenharmony_ci	.napi_enable		= napi_enable_fs,
4748c2ecf20Sopenharmony_ci	.napi_disable		= napi_disable_fs,
4758c2ecf20Sopenharmony_ci	.rx_bd_done		= rx_bd_done,
4768c2ecf20Sopenharmony_ci	.tx_kickstart		= tx_kickstart,
4778c2ecf20Sopenharmony_ci	.get_int_events		= get_int_events,
4788c2ecf20Sopenharmony_ci	.clear_int_events	= clear_int_events,
4798c2ecf20Sopenharmony_ci	.ev_error		= ev_error,
4808c2ecf20Sopenharmony_ci	.get_regs		= get_regs,
4818c2ecf20Sopenharmony_ci	.get_regs_len		= get_regs_len,
4828c2ecf20Sopenharmony_ci	.tx_restart		= tx_restart,
4838c2ecf20Sopenharmony_ci	.allocate_bd		= allocate_bd,
4848c2ecf20Sopenharmony_ci	.free_bd		= free_bd,
4858c2ecf20Sopenharmony_ci};
4868c2ecf20Sopenharmony_ci
487