18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci /*
38c2ecf20Sopenharmony_ci * drivers/net/ethernet/ec_bhf.c
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (C) 2014 Darek Marcinkiewicz <reksio@newterm.pl>
68c2ecf20Sopenharmony_ci */
78c2ecf20Sopenharmony_ci
88c2ecf20Sopenharmony_ci/* This is a driver for EtherCAT master module present on CCAT FPGA.
98c2ecf20Sopenharmony_ci * Those can be found on Bechhoff CX50xx industrial PCs.
108c2ecf20Sopenharmony_ci */
118c2ecf20Sopenharmony_ci
128c2ecf20Sopenharmony_ci#include <linux/kernel.h>
138c2ecf20Sopenharmony_ci#include <linux/module.h>
148c2ecf20Sopenharmony_ci#include <linux/moduleparam.h>
158c2ecf20Sopenharmony_ci#include <linux/pci.h>
168c2ecf20Sopenharmony_ci#include <linux/init.h>
178c2ecf20Sopenharmony_ci
188c2ecf20Sopenharmony_ci#include <linux/netdevice.h>
198c2ecf20Sopenharmony_ci#include <linux/etherdevice.h>
208c2ecf20Sopenharmony_ci#include <linux/ip.h>
218c2ecf20Sopenharmony_ci#include <linux/skbuff.h>
228c2ecf20Sopenharmony_ci#include <linux/hrtimer.h>
238c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
248c2ecf20Sopenharmony_ci#include <linux/stat.h>
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_ci#define TIMER_INTERVAL_NSEC	20000
278c2ecf20Sopenharmony_ci
288c2ecf20Sopenharmony_ci#define INFO_BLOCK_SIZE		0x10
298c2ecf20Sopenharmony_ci#define INFO_BLOCK_TYPE		0x0
308c2ecf20Sopenharmony_ci#define INFO_BLOCK_REV		0x2
318c2ecf20Sopenharmony_ci#define INFO_BLOCK_BLK_CNT	0x4
328c2ecf20Sopenharmony_ci#define INFO_BLOCK_TX_CHAN	0x4
338c2ecf20Sopenharmony_ci#define INFO_BLOCK_RX_CHAN	0x5
348c2ecf20Sopenharmony_ci#define INFO_BLOCK_OFFSET	0x8
358c2ecf20Sopenharmony_ci
368c2ecf20Sopenharmony_ci#define EC_MII_OFFSET		0x4
378c2ecf20Sopenharmony_ci#define EC_FIFO_OFFSET		0x8
388c2ecf20Sopenharmony_ci#define EC_MAC_OFFSET		0xc
398c2ecf20Sopenharmony_ci
408c2ecf20Sopenharmony_ci#define MAC_FRAME_ERR_CNT	0x0
418c2ecf20Sopenharmony_ci#define MAC_RX_ERR_CNT		0x1
428c2ecf20Sopenharmony_ci#define MAC_CRC_ERR_CNT		0x2
438c2ecf20Sopenharmony_ci#define MAC_LNK_LST_ERR_CNT	0x3
448c2ecf20Sopenharmony_ci#define MAC_TX_FRAME_CNT	0x10
458c2ecf20Sopenharmony_ci#define MAC_RX_FRAME_CNT	0x14
468c2ecf20Sopenharmony_ci#define MAC_TX_FIFO_LVL		0x20
478c2ecf20Sopenharmony_ci#define MAC_DROPPED_FRMS	0x28
488c2ecf20Sopenharmony_ci#define MAC_CONNECTED_CCAT_FLAG	0x78
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_ci#define MII_MAC_ADDR		0x8
518c2ecf20Sopenharmony_ci#define MII_MAC_FILT_FLAG	0xe
528c2ecf20Sopenharmony_ci#define MII_LINK_STATUS		0xf
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_ci#define FIFO_TX_REG		0x0
558c2ecf20Sopenharmony_ci#define FIFO_TX_RESET		0x8
568c2ecf20Sopenharmony_ci#define FIFO_RX_REG		0x10
578c2ecf20Sopenharmony_ci#define FIFO_RX_ADDR_VALID	(1u << 31)
588c2ecf20Sopenharmony_ci#define FIFO_RX_RESET		0x18
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_ci#define DMA_CHAN_OFFSET		0x1000
618c2ecf20Sopenharmony_ci#define DMA_CHAN_SIZE		0x8
628c2ecf20Sopenharmony_ci
638c2ecf20Sopenharmony_ci#define DMA_WINDOW_SIZE_MASK	0xfffffffc
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_ci#define ETHERCAT_MASTER_ID	0x14
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_cistatic const struct pci_device_id ids[] = {
688c2ecf20Sopenharmony_ci	{ PCI_DEVICE(0x15ec, 0x5000), },
698c2ecf20Sopenharmony_ci	{ 0, }
708c2ecf20Sopenharmony_ci};
718c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(pci, ids);
728c2ecf20Sopenharmony_ci
738c2ecf20Sopenharmony_cistruct rx_header {
748c2ecf20Sopenharmony_ci#define RXHDR_NEXT_ADDR_MASK	0xffffffu
758c2ecf20Sopenharmony_ci#define RXHDR_NEXT_VALID	(1u << 31)
768c2ecf20Sopenharmony_ci	__le32 next;
778c2ecf20Sopenharmony_ci#define RXHDR_NEXT_RECV_FLAG	0x1
788c2ecf20Sopenharmony_ci	__le32 recv;
798c2ecf20Sopenharmony_ci#define RXHDR_LEN_MASK		0xfffu
808c2ecf20Sopenharmony_ci	__le16 len;
818c2ecf20Sopenharmony_ci	__le16 port;
828c2ecf20Sopenharmony_ci	__le32 reserved;
838c2ecf20Sopenharmony_ci	u8 timestamp[8];
848c2ecf20Sopenharmony_ci} __packed;
858c2ecf20Sopenharmony_ci
868c2ecf20Sopenharmony_ci#define PKT_PAYLOAD_SIZE	0x7e8
878c2ecf20Sopenharmony_cistruct rx_desc {
888c2ecf20Sopenharmony_ci	struct rx_header header;
898c2ecf20Sopenharmony_ci	u8 data[PKT_PAYLOAD_SIZE];
908c2ecf20Sopenharmony_ci} __packed;
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_cistruct tx_header {
938c2ecf20Sopenharmony_ci	__le16 len;
948c2ecf20Sopenharmony_ci#define TX_HDR_PORT_0		0x1
958c2ecf20Sopenharmony_ci#define TX_HDR_PORT_1		0x2
968c2ecf20Sopenharmony_ci	u8 port;
978c2ecf20Sopenharmony_ci	u8 ts_enable;
988c2ecf20Sopenharmony_ci#define TX_HDR_SENT		0x1
998c2ecf20Sopenharmony_ci	__le32 sent;
1008c2ecf20Sopenharmony_ci	u8 timestamp[8];
1018c2ecf20Sopenharmony_ci} __packed;
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_cistruct tx_desc {
1048c2ecf20Sopenharmony_ci	struct tx_header header;
1058c2ecf20Sopenharmony_ci	u8 data[PKT_PAYLOAD_SIZE];
1068c2ecf20Sopenharmony_ci} __packed;
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_ci#define FIFO_SIZE		64
1098c2ecf20Sopenharmony_ci
1108c2ecf20Sopenharmony_cistatic long polling_frequency = TIMER_INTERVAL_NSEC;
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_cistruct bhf_dma {
1138c2ecf20Sopenharmony_ci	u8 *buf;
1148c2ecf20Sopenharmony_ci	size_t len;
1158c2ecf20Sopenharmony_ci	dma_addr_t buf_phys;
1168c2ecf20Sopenharmony_ci
1178c2ecf20Sopenharmony_ci	u8 *alloc;
1188c2ecf20Sopenharmony_ci	size_t alloc_len;
1198c2ecf20Sopenharmony_ci	dma_addr_t alloc_phys;
1208c2ecf20Sopenharmony_ci};
1218c2ecf20Sopenharmony_ci
1228c2ecf20Sopenharmony_cistruct ec_bhf_priv {
1238c2ecf20Sopenharmony_ci	struct net_device *net_dev;
1248c2ecf20Sopenharmony_ci	struct pci_dev *dev;
1258c2ecf20Sopenharmony_ci
1268c2ecf20Sopenharmony_ci	void __iomem *io;
1278c2ecf20Sopenharmony_ci	void __iomem *dma_io;
1288c2ecf20Sopenharmony_ci
1298c2ecf20Sopenharmony_ci	struct hrtimer hrtimer;
1308c2ecf20Sopenharmony_ci
1318c2ecf20Sopenharmony_ci	int tx_dma_chan;
1328c2ecf20Sopenharmony_ci	int rx_dma_chan;
1338c2ecf20Sopenharmony_ci	void __iomem *ec_io;
1348c2ecf20Sopenharmony_ci	void __iomem *fifo_io;
1358c2ecf20Sopenharmony_ci	void __iomem *mii_io;
1368c2ecf20Sopenharmony_ci	void __iomem *mac_io;
1378c2ecf20Sopenharmony_ci
1388c2ecf20Sopenharmony_ci	struct bhf_dma rx_buf;
1398c2ecf20Sopenharmony_ci	struct rx_desc *rx_descs;
1408c2ecf20Sopenharmony_ci	int rx_dnext;
1418c2ecf20Sopenharmony_ci	int rx_dcount;
1428c2ecf20Sopenharmony_ci
1438c2ecf20Sopenharmony_ci	struct bhf_dma tx_buf;
1448c2ecf20Sopenharmony_ci	struct tx_desc *tx_descs;
1458c2ecf20Sopenharmony_ci	int tx_dcount;
1468c2ecf20Sopenharmony_ci	int tx_dnext;
1478c2ecf20Sopenharmony_ci
1488c2ecf20Sopenharmony_ci	u64 stat_rx_bytes;
1498c2ecf20Sopenharmony_ci	u64 stat_tx_bytes;
1508c2ecf20Sopenharmony_ci};
1518c2ecf20Sopenharmony_ci
1528c2ecf20Sopenharmony_ci#define PRIV_TO_DEV(priv) (&(priv)->dev->dev)
1538c2ecf20Sopenharmony_ci
1548c2ecf20Sopenharmony_cistatic void ec_bhf_reset(struct ec_bhf_priv *priv)
1558c2ecf20Sopenharmony_ci{
1568c2ecf20Sopenharmony_ci	iowrite8(0, priv->mac_io + MAC_FRAME_ERR_CNT);
1578c2ecf20Sopenharmony_ci	iowrite8(0, priv->mac_io + MAC_RX_ERR_CNT);
1588c2ecf20Sopenharmony_ci	iowrite8(0, priv->mac_io + MAC_CRC_ERR_CNT);
1598c2ecf20Sopenharmony_ci	iowrite8(0, priv->mac_io + MAC_LNK_LST_ERR_CNT);
1608c2ecf20Sopenharmony_ci	iowrite32(0, priv->mac_io + MAC_TX_FRAME_CNT);
1618c2ecf20Sopenharmony_ci	iowrite32(0, priv->mac_io + MAC_RX_FRAME_CNT);
1628c2ecf20Sopenharmony_ci	iowrite8(0, priv->mac_io + MAC_DROPPED_FRMS);
1638c2ecf20Sopenharmony_ci
1648c2ecf20Sopenharmony_ci	iowrite8(0, priv->fifo_io + FIFO_TX_RESET);
1658c2ecf20Sopenharmony_ci	iowrite8(0, priv->fifo_io + FIFO_RX_RESET);
1668c2ecf20Sopenharmony_ci
1678c2ecf20Sopenharmony_ci	iowrite8(0, priv->mac_io + MAC_TX_FIFO_LVL);
1688c2ecf20Sopenharmony_ci}
1698c2ecf20Sopenharmony_ci
1708c2ecf20Sopenharmony_cistatic void ec_bhf_send_packet(struct ec_bhf_priv *priv, struct tx_desc *desc)
1718c2ecf20Sopenharmony_ci{
1728c2ecf20Sopenharmony_ci	u32 len = le16_to_cpu(desc->header.len) + sizeof(desc->header);
1738c2ecf20Sopenharmony_ci	u32 addr = (u8 *)desc - priv->tx_buf.buf;
1748c2ecf20Sopenharmony_ci
1758c2ecf20Sopenharmony_ci	iowrite32((ALIGN(len, 8) << 24) | addr, priv->fifo_io + FIFO_TX_REG);
1768c2ecf20Sopenharmony_ci}
1778c2ecf20Sopenharmony_ci
1788c2ecf20Sopenharmony_cistatic int ec_bhf_desc_sent(struct tx_desc *desc)
1798c2ecf20Sopenharmony_ci{
1808c2ecf20Sopenharmony_ci	return le32_to_cpu(desc->header.sent) & TX_HDR_SENT;
1818c2ecf20Sopenharmony_ci}
1828c2ecf20Sopenharmony_ci
1838c2ecf20Sopenharmony_cistatic void ec_bhf_process_tx(struct ec_bhf_priv *priv)
1848c2ecf20Sopenharmony_ci{
1858c2ecf20Sopenharmony_ci	if (unlikely(netif_queue_stopped(priv->net_dev))) {
1868c2ecf20Sopenharmony_ci		/* Make sure that we perceive changes to tx_dnext. */
1878c2ecf20Sopenharmony_ci		smp_rmb();
1888c2ecf20Sopenharmony_ci
1898c2ecf20Sopenharmony_ci		if (ec_bhf_desc_sent(&priv->tx_descs[priv->tx_dnext]))
1908c2ecf20Sopenharmony_ci			netif_wake_queue(priv->net_dev);
1918c2ecf20Sopenharmony_ci	}
1928c2ecf20Sopenharmony_ci}
1938c2ecf20Sopenharmony_ci
1948c2ecf20Sopenharmony_cistatic int ec_bhf_pkt_received(struct rx_desc *desc)
1958c2ecf20Sopenharmony_ci{
1968c2ecf20Sopenharmony_ci	return le32_to_cpu(desc->header.recv) & RXHDR_NEXT_RECV_FLAG;
1978c2ecf20Sopenharmony_ci}
1988c2ecf20Sopenharmony_ci
1998c2ecf20Sopenharmony_cistatic void ec_bhf_add_rx_desc(struct ec_bhf_priv *priv, struct rx_desc *desc)
2008c2ecf20Sopenharmony_ci{
2018c2ecf20Sopenharmony_ci	iowrite32(FIFO_RX_ADDR_VALID | ((u8 *)(desc) - priv->rx_buf.buf),
2028c2ecf20Sopenharmony_ci		  priv->fifo_io + FIFO_RX_REG);
2038c2ecf20Sopenharmony_ci}
2048c2ecf20Sopenharmony_ci
2058c2ecf20Sopenharmony_cistatic void ec_bhf_process_rx(struct ec_bhf_priv *priv)
2068c2ecf20Sopenharmony_ci{
2078c2ecf20Sopenharmony_ci	struct rx_desc *desc = &priv->rx_descs[priv->rx_dnext];
2088c2ecf20Sopenharmony_ci
2098c2ecf20Sopenharmony_ci	while (ec_bhf_pkt_received(desc)) {
2108c2ecf20Sopenharmony_ci		int pkt_size = (le16_to_cpu(desc->header.len) &
2118c2ecf20Sopenharmony_ci			       RXHDR_LEN_MASK) - sizeof(struct rx_header) - 4;
2128c2ecf20Sopenharmony_ci		u8 *data = desc->data;
2138c2ecf20Sopenharmony_ci		struct sk_buff *skb;
2148c2ecf20Sopenharmony_ci
2158c2ecf20Sopenharmony_ci		skb = netdev_alloc_skb_ip_align(priv->net_dev, pkt_size);
2168c2ecf20Sopenharmony_ci		if (skb) {
2178c2ecf20Sopenharmony_ci			skb_put_data(skb, data, pkt_size);
2188c2ecf20Sopenharmony_ci			skb->protocol = eth_type_trans(skb, priv->net_dev);
2198c2ecf20Sopenharmony_ci			priv->stat_rx_bytes += pkt_size;
2208c2ecf20Sopenharmony_ci
2218c2ecf20Sopenharmony_ci			netif_rx(skb);
2228c2ecf20Sopenharmony_ci		} else {
2238c2ecf20Sopenharmony_ci			dev_err_ratelimited(PRIV_TO_DEV(priv),
2248c2ecf20Sopenharmony_ci					    "Couldn't allocate a skb_buff for a packet of size %u\n",
2258c2ecf20Sopenharmony_ci					    pkt_size);
2268c2ecf20Sopenharmony_ci		}
2278c2ecf20Sopenharmony_ci
2288c2ecf20Sopenharmony_ci		desc->header.recv = 0;
2298c2ecf20Sopenharmony_ci
2308c2ecf20Sopenharmony_ci		ec_bhf_add_rx_desc(priv, desc);
2318c2ecf20Sopenharmony_ci
2328c2ecf20Sopenharmony_ci		priv->rx_dnext = (priv->rx_dnext + 1) % priv->rx_dcount;
2338c2ecf20Sopenharmony_ci		desc = &priv->rx_descs[priv->rx_dnext];
2348c2ecf20Sopenharmony_ci	}
2358c2ecf20Sopenharmony_ci}
2368c2ecf20Sopenharmony_ci
2378c2ecf20Sopenharmony_cistatic enum hrtimer_restart ec_bhf_timer_fun(struct hrtimer *timer)
2388c2ecf20Sopenharmony_ci{
2398c2ecf20Sopenharmony_ci	struct ec_bhf_priv *priv = container_of(timer, struct ec_bhf_priv,
2408c2ecf20Sopenharmony_ci						hrtimer);
2418c2ecf20Sopenharmony_ci	ec_bhf_process_rx(priv);
2428c2ecf20Sopenharmony_ci	ec_bhf_process_tx(priv);
2438c2ecf20Sopenharmony_ci
2448c2ecf20Sopenharmony_ci	if (!netif_running(priv->net_dev))
2458c2ecf20Sopenharmony_ci		return HRTIMER_NORESTART;
2468c2ecf20Sopenharmony_ci
2478c2ecf20Sopenharmony_ci	hrtimer_forward_now(timer, polling_frequency);
2488c2ecf20Sopenharmony_ci	return HRTIMER_RESTART;
2498c2ecf20Sopenharmony_ci}
2508c2ecf20Sopenharmony_ci
2518c2ecf20Sopenharmony_cistatic int ec_bhf_setup_offsets(struct ec_bhf_priv *priv)
2528c2ecf20Sopenharmony_ci{
2538c2ecf20Sopenharmony_ci	struct device *dev = PRIV_TO_DEV(priv);
2548c2ecf20Sopenharmony_ci	unsigned block_count, i;
2558c2ecf20Sopenharmony_ci	void __iomem *ec_info;
2568c2ecf20Sopenharmony_ci
2578c2ecf20Sopenharmony_ci	block_count = ioread8(priv->io + INFO_BLOCK_BLK_CNT);
2588c2ecf20Sopenharmony_ci	for (i = 0; i < block_count; i++) {
2598c2ecf20Sopenharmony_ci		u16 type = ioread16(priv->io + i * INFO_BLOCK_SIZE +
2608c2ecf20Sopenharmony_ci				    INFO_BLOCK_TYPE);
2618c2ecf20Sopenharmony_ci		if (type == ETHERCAT_MASTER_ID)
2628c2ecf20Sopenharmony_ci			break;
2638c2ecf20Sopenharmony_ci	}
2648c2ecf20Sopenharmony_ci	if (i == block_count) {
2658c2ecf20Sopenharmony_ci		dev_err(dev, "EtherCAT master with DMA block not found\n");
2668c2ecf20Sopenharmony_ci		return -ENODEV;
2678c2ecf20Sopenharmony_ci	}
2688c2ecf20Sopenharmony_ci
2698c2ecf20Sopenharmony_ci	ec_info = priv->io + i * INFO_BLOCK_SIZE;
2708c2ecf20Sopenharmony_ci
2718c2ecf20Sopenharmony_ci	priv->tx_dma_chan = ioread8(ec_info + INFO_BLOCK_TX_CHAN);
2728c2ecf20Sopenharmony_ci	priv->rx_dma_chan = ioread8(ec_info + INFO_BLOCK_RX_CHAN);
2738c2ecf20Sopenharmony_ci
2748c2ecf20Sopenharmony_ci	priv->ec_io = priv->io + ioread32(ec_info + INFO_BLOCK_OFFSET);
2758c2ecf20Sopenharmony_ci	priv->mii_io = priv->ec_io + ioread32(priv->ec_io + EC_MII_OFFSET);
2768c2ecf20Sopenharmony_ci	priv->fifo_io = priv->ec_io + ioread32(priv->ec_io + EC_FIFO_OFFSET);
2778c2ecf20Sopenharmony_ci	priv->mac_io = priv->ec_io + ioread32(priv->ec_io + EC_MAC_OFFSET);
2788c2ecf20Sopenharmony_ci
2798c2ecf20Sopenharmony_ci	return 0;
2808c2ecf20Sopenharmony_ci}
2818c2ecf20Sopenharmony_ci
2828c2ecf20Sopenharmony_cistatic netdev_tx_t ec_bhf_start_xmit(struct sk_buff *skb,
2838c2ecf20Sopenharmony_ci				     struct net_device *net_dev)
2848c2ecf20Sopenharmony_ci{
2858c2ecf20Sopenharmony_ci	struct ec_bhf_priv *priv = netdev_priv(net_dev);
2868c2ecf20Sopenharmony_ci	struct tx_desc *desc;
2878c2ecf20Sopenharmony_ci	unsigned len;
2888c2ecf20Sopenharmony_ci
2898c2ecf20Sopenharmony_ci	desc = &priv->tx_descs[priv->tx_dnext];
2908c2ecf20Sopenharmony_ci
2918c2ecf20Sopenharmony_ci	skb_copy_and_csum_dev(skb, desc->data);
2928c2ecf20Sopenharmony_ci	len = skb->len;
2938c2ecf20Sopenharmony_ci
2948c2ecf20Sopenharmony_ci	memset(&desc->header, 0, sizeof(desc->header));
2958c2ecf20Sopenharmony_ci	desc->header.len = cpu_to_le16(len);
2968c2ecf20Sopenharmony_ci	desc->header.port = TX_HDR_PORT_0;
2978c2ecf20Sopenharmony_ci
2988c2ecf20Sopenharmony_ci	ec_bhf_send_packet(priv, desc);
2998c2ecf20Sopenharmony_ci
3008c2ecf20Sopenharmony_ci	priv->tx_dnext = (priv->tx_dnext + 1) % priv->tx_dcount;
3018c2ecf20Sopenharmony_ci
3028c2ecf20Sopenharmony_ci	if (!ec_bhf_desc_sent(&priv->tx_descs[priv->tx_dnext])) {
3038c2ecf20Sopenharmony_ci		/* Make sure that updates to tx_dnext are perceived
3048c2ecf20Sopenharmony_ci		 * by timer routine.
3058c2ecf20Sopenharmony_ci		 */
3068c2ecf20Sopenharmony_ci		smp_wmb();
3078c2ecf20Sopenharmony_ci
3088c2ecf20Sopenharmony_ci		netif_stop_queue(net_dev);
3098c2ecf20Sopenharmony_ci	}
3108c2ecf20Sopenharmony_ci
3118c2ecf20Sopenharmony_ci	priv->stat_tx_bytes += len;
3128c2ecf20Sopenharmony_ci
3138c2ecf20Sopenharmony_ci	dev_kfree_skb(skb);
3148c2ecf20Sopenharmony_ci
3158c2ecf20Sopenharmony_ci	return NETDEV_TX_OK;
3168c2ecf20Sopenharmony_ci}
3178c2ecf20Sopenharmony_ci
3188c2ecf20Sopenharmony_cistatic int ec_bhf_alloc_dma_mem(struct ec_bhf_priv *priv,
3198c2ecf20Sopenharmony_ci				struct bhf_dma *buf,
3208c2ecf20Sopenharmony_ci				int channel,
3218c2ecf20Sopenharmony_ci				int size)
3228c2ecf20Sopenharmony_ci{
3238c2ecf20Sopenharmony_ci	int offset = channel * DMA_CHAN_SIZE + DMA_CHAN_OFFSET;
3248c2ecf20Sopenharmony_ci	struct device *dev = PRIV_TO_DEV(priv);
3258c2ecf20Sopenharmony_ci	u32 mask;
3268c2ecf20Sopenharmony_ci
3278c2ecf20Sopenharmony_ci	iowrite32(0xffffffff, priv->dma_io + offset);
3288c2ecf20Sopenharmony_ci
3298c2ecf20Sopenharmony_ci	mask = ioread32(priv->dma_io + offset);
3308c2ecf20Sopenharmony_ci	mask &= DMA_WINDOW_SIZE_MASK;
3318c2ecf20Sopenharmony_ci
3328c2ecf20Sopenharmony_ci	/* We want to allocate a chunk of memory that is:
3338c2ecf20Sopenharmony_ci	 * - aligned to the mask we just read
3348c2ecf20Sopenharmony_ci	 * - is of size 2^mask bytes (at most)
3358c2ecf20Sopenharmony_ci	 * In order to ensure that we will allocate buffer of
3368c2ecf20Sopenharmony_ci	 * 2 * 2^mask bytes.
3378c2ecf20Sopenharmony_ci	 */
3388c2ecf20Sopenharmony_ci	buf->len = min_t(int, ~mask + 1, size);
3398c2ecf20Sopenharmony_ci	buf->alloc_len = 2 * buf->len;
3408c2ecf20Sopenharmony_ci
3418c2ecf20Sopenharmony_ci	buf->alloc = dma_alloc_coherent(dev, buf->alloc_len, &buf->alloc_phys,
3428c2ecf20Sopenharmony_ci					GFP_KERNEL);
3438c2ecf20Sopenharmony_ci	if (buf->alloc == NULL) {
3448c2ecf20Sopenharmony_ci		dev_err(dev, "Failed to allocate buffer\n");
3458c2ecf20Sopenharmony_ci		return -ENOMEM;
3468c2ecf20Sopenharmony_ci	}
3478c2ecf20Sopenharmony_ci
3488c2ecf20Sopenharmony_ci	buf->buf_phys = (buf->alloc_phys + buf->len) & mask;
3498c2ecf20Sopenharmony_ci	buf->buf = buf->alloc + (buf->buf_phys - buf->alloc_phys);
3508c2ecf20Sopenharmony_ci
3518c2ecf20Sopenharmony_ci	iowrite32(0, priv->dma_io + offset + 4);
3528c2ecf20Sopenharmony_ci	iowrite32(buf->buf_phys, priv->dma_io + offset);
3538c2ecf20Sopenharmony_ci
3548c2ecf20Sopenharmony_ci	return 0;
3558c2ecf20Sopenharmony_ci}
3568c2ecf20Sopenharmony_ci
3578c2ecf20Sopenharmony_cistatic void ec_bhf_setup_tx_descs(struct ec_bhf_priv *priv)
3588c2ecf20Sopenharmony_ci{
3598c2ecf20Sopenharmony_ci	int i = 0;
3608c2ecf20Sopenharmony_ci
3618c2ecf20Sopenharmony_ci	priv->tx_dcount = priv->tx_buf.len / sizeof(struct tx_desc);
3628c2ecf20Sopenharmony_ci	priv->tx_descs = (struct tx_desc *)priv->tx_buf.buf;
3638c2ecf20Sopenharmony_ci	priv->tx_dnext = 0;
3648c2ecf20Sopenharmony_ci
3658c2ecf20Sopenharmony_ci	for (i = 0; i < priv->tx_dcount; i++)
3668c2ecf20Sopenharmony_ci		priv->tx_descs[i].header.sent = cpu_to_le32(TX_HDR_SENT);
3678c2ecf20Sopenharmony_ci}
3688c2ecf20Sopenharmony_ci
3698c2ecf20Sopenharmony_cistatic void ec_bhf_setup_rx_descs(struct ec_bhf_priv *priv)
3708c2ecf20Sopenharmony_ci{
3718c2ecf20Sopenharmony_ci	int i;
3728c2ecf20Sopenharmony_ci
3738c2ecf20Sopenharmony_ci	priv->rx_dcount = priv->rx_buf.len / sizeof(struct rx_desc);
3748c2ecf20Sopenharmony_ci	priv->rx_descs = (struct rx_desc *)priv->rx_buf.buf;
3758c2ecf20Sopenharmony_ci	priv->rx_dnext = 0;
3768c2ecf20Sopenharmony_ci
3778c2ecf20Sopenharmony_ci	for (i = 0; i < priv->rx_dcount; i++) {
3788c2ecf20Sopenharmony_ci		struct rx_desc *desc = &priv->rx_descs[i];
3798c2ecf20Sopenharmony_ci		u32 next;
3808c2ecf20Sopenharmony_ci
3818c2ecf20Sopenharmony_ci		if (i != priv->rx_dcount - 1)
3828c2ecf20Sopenharmony_ci			next = (u8 *)(desc + 1) - priv->rx_buf.buf;
3838c2ecf20Sopenharmony_ci		else
3848c2ecf20Sopenharmony_ci			next = 0;
3858c2ecf20Sopenharmony_ci		next |= RXHDR_NEXT_VALID;
3868c2ecf20Sopenharmony_ci		desc->header.next = cpu_to_le32(next);
3878c2ecf20Sopenharmony_ci		desc->header.recv = 0;
3888c2ecf20Sopenharmony_ci		ec_bhf_add_rx_desc(priv, desc);
3898c2ecf20Sopenharmony_ci	}
3908c2ecf20Sopenharmony_ci}
3918c2ecf20Sopenharmony_ci
3928c2ecf20Sopenharmony_cistatic int ec_bhf_open(struct net_device *net_dev)
3938c2ecf20Sopenharmony_ci{
3948c2ecf20Sopenharmony_ci	struct ec_bhf_priv *priv = netdev_priv(net_dev);
3958c2ecf20Sopenharmony_ci	struct device *dev = PRIV_TO_DEV(priv);
3968c2ecf20Sopenharmony_ci	int err = 0;
3978c2ecf20Sopenharmony_ci
3988c2ecf20Sopenharmony_ci	ec_bhf_reset(priv);
3998c2ecf20Sopenharmony_ci
4008c2ecf20Sopenharmony_ci	err = ec_bhf_alloc_dma_mem(priv, &priv->rx_buf, priv->rx_dma_chan,
4018c2ecf20Sopenharmony_ci				   FIFO_SIZE * sizeof(struct rx_desc));
4028c2ecf20Sopenharmony_ci	if (err) {
4038c2ecf20Sopenharmony_ci		dev_err(dev, "Failed to allocate rx buffer\n");
4048c2ecf20Sopenharmony_ci		goto out;
4058c2ecf20Sopenharmony_ci	}
4068c2ecf20Sopenharmony_ci	ec_bhf_setup_rx_descs(priv);
4078c2ecf20Sopenharmony_ci
4088c2ecf20Sopenharmony_ci	err = ec_bhf_alloc_dma_mem(priv, &priv->tx_buf, priv->tx_dma_chan,
4098c2ecf20Sopenharmony_ci				   FIFO_SIZE * sizeof(struct tx_desc));
4108c2ecf20Sopenharmony_ci	if (err) {
4118c2ecf20Sopenharmony_ci		dev_err(dev, "Failed to allocate tx buffer\n");
4128c2ecf20Sopenharmony_ci		goto error_rx_free;
4138c2ecf20Sopenharmony_ci	}
4148c2ecf20Sopenharmony_ci	iowrite8(0, priv->mii_io + MII_MAC_FILT_FLAG);
4158c2ecf20Sopenharmony_ci	ec_bhf_setup_tx_descs(priv);
4168c2ecf20Sopenharmony_ci
4178c2ecf20Sopenharmony_ci	netif_start_queue(net_dev);
4188c2ecf20Sopenharmony_ci
4198c2ecf20Sopenharmony_ci	hrtimer_init(&priv->hrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
4208c2ecf20Sopenharmony_ci	priv->hrtimer.function = ec_bhf_timer_fun;
4218c2ecf20Sopenharmony_ci	hrtimer_start(&priv->hrtimer, polling_frequency, HRTIMER_MODE_REL);
4228c2ecf20Sopenharmony_ci
4238c2ecf20Sopenharmony_ci	return 0;
4248c2ecf20Sopenharmony_ci
4258c2ecf20Sopenharmony_cierror_rx_free:
4268c2ecf20Sopenharmony_ci	dma_free_coherent(dev, priv->rx_buf.alloc_len, priv->rx_buf.alloc,
4278c2ecf20Sopenharmony_ci			  priv->rx_buf.alloc_len);
4288c2ecf20Sopenharmony_ciout:
4298c2ecf20Sopenharmony_ci	return err;
4308c2ecf20Sopenharmony_ci}
4318c2ecf20Sopenharmony_ci
4328c2ecf20Sopenharmony_cistatic int ec_bhf_stop(struct net_device *net_dev)
4338c2ecf20Sopenharmony_ci{
4348c2ecf20Sopenharmony_ci	struct ec_bhf_priv *priv = netdev_priv(net_dev);
4358c2ecf20Sopenharmony_ci	struct device *dev = PRIV_TO_DEV(priv);
4368c2ecf20Sopenharmony_ci
4378c2ecf20Sopenharmony_ci	hrtimer_cancel(&priv->hrtimer);
4388c2ecf20Sopenharmony_ci
4398c2ecf20Sopenharmony_ci	ec_bhf_reset(priv);
4408c2ecf20Sopenharmony_ci
4418c2ecf20Sopenharmony_ci	netif_tx_disable(net_dev);
4428c2ecf20Sopenharmony_ci
4438c2ecf20Sopenharmony_ci	dma_free_coherent(dev, priv->tx_buf.alloc_len,
4448c2ecf20Sopenharmony_ci			  priv->tx_buf.alloc, priv->tx_buf.alloc_phys);
4458c2ecf20Sopenharmony_ci	dma_free_coherent(dev, priv->rx_buf.alloc_len,
4468c2ecf20Sopenharmony_ci			  priv->rx_buf.alloc, priv->rx_buf.alloc_phys);
4478c2ecf20Sopenharmony_ci
4488c2ecf20Sopenharmony_ci	return 0;
4498c2ecf20Sopenharmony_ci}
4508c2ecf20Sopenharmony_ci
4518c2ecf20Sopenharmony_cistatic void
4528c2ecf20Sopenharmony_ciec_bhf_get_stats(struct net_device *net_dev,
4538c2ecf20Sopenharmony_ci		 struct rtnl_link_stats64 *stats)
4548c2ecf20Sopenharmony_ci{
4558c2ecf20Sopenharmony_ci	struct ec_bhf_priv *priv = netdev_priv(net_dev);
4568c2ecf20Sopenharmony_ci
4578c2ecf20Sopenharmony_ci	stats->rx_errors = ioread8(priv->mac_io + MAC_RX_ERR_CNT) +
4588c2ecf20Sopenharmony_ci				ioread8(priv->mac_io + MAC_CRC_ERR_CNT) +
4598c2ecf20Sopenharmony_ci				ioread8(priv->mac_io + MAC_FRAME_ERR_CNT);
4608c2ecf20Sopenharmony_ci	stats->rx_packets = ioread32(priv->mac_io + MAC_RX_FRAME_CNT);
4618c2ecf20Sopenharmony_ci	stats->tx_packets = ioread32(priv->mac_io + MAC_TX_FRAME_CNT);
4628c2ecf20Sopenharmony_ci	stats->rx_dropped = ioread8(priv->mac_io + MAC_DROPPED_FRMS);
4638c2ecf20Sopenharmony_ci
4648c2ecf20Sopenharmony_ci	stats->tx_bytes = priv->stat_tx_bytes;
4658c2ecf20Sopenharmony_ci	stats->rx_bytes = priv->stat_rx_bytes;
4668c2ecf20Sopenharmony_ci}
4678c2ecf20Sopenharmony_ci
4688c2ecf20Sopenharmony_cistatic const struct net_device_ops ec_bhf_netdev_ops = {
4698c2ecf20Sopenharmony_ci	.ndo_start_xmit		= ec_bhf_start_xmit,
4708c2ecf20Sopenharmony_ci	.ndo_open		= ec_bhf_open,
4718c2ecf20Sopenharmony_ci	.ndo_stop		= ec_bhf_stop,
4728c2ecf20Sopenharmony_ci	.ndo_get_stats64	= ec_bhf_get_stats,
4738c2ecf20Sopenharmony_ci	.ndo_validate_addr	= eth_validate_addr,
4748c2ecf20Sopenharmony_ci	.ndo_set_mac_address	= eth_mac_addr
4758c2ecf20Sopenharmony_ci};
4768c2ecf20Sopenharmony_ci
4778c2ecf20Sopenharmony_cistatic int ec_bhf_probe(struct pci_dev *dev, const struct pci_device_id *id)
4788c2ecf20Sopenharmony_ci{
4798c2ecf20Sopenharmony_ci	struct net_device *net_dev;
4808c2ecf20Sopenharmony_ci	struct ec_bhf_priv *priv;
4818c2ecf20Sopenharmony_ci	void __iomem *dma_io;
4828c2ecf20Sopenharmony_ci	void __iomem *io;
4838c2ecf20Sopenharmony_ci	int err = 0;
4848c2ecf20Sopenharmony_ci
4858c2ecf20Sopenharmony_ci	err = pci_enable_device(dev);
4868c2ecf20Sopenharmony_ci	if (err)
4878c2ecf20Sopenharmony_ci		return err;
4888c2ecf20Sopenharmony_ci
4898c2ecf20Sopenharmony_ci	pci_set_master(dev);
4908c2ecf20Sopenharmony_ci
4918c2ecf20Sopenharmony_ci	err = pci_set_dma_mask(dev, DMA_BIT_MASK(32));
4928c2ecf20Sopenharmony_ci	if (err) {
4938c2ecf20Sopenharmony_ci		dev_err(&dev->dev,
4948c2ecf20Sopenharmony_ci			"Required dma mask not supported, failed to initialize device\n");
4958c2ecf20Sopenharmony_ci		err = -EIO;
4968c2ecf20Sopenharmony_ci		goto err_disable_dev;
4978c2ecf20Sopenharmony_ci	}
4988c2ecf20Sopenharmony_ci
4998c2ecf20Sopenharmony_ci	err = pci_set_consistent_dma_mask(dev, DMA_BIT_MASK(32));
5008c2ecf20Sopenharmony_ci	if (err) {
5018c2ecf20Sopenharmony_ci		dev_err(&dev->dev,
5028c2ecf20Sopenharmony_ci			"Required dma mask not supported, failed to initialize device\n");
5038c2ecf20Sopenharmony_ci		goto err_disable_dev;
5048c2ecf20Sopenharmony_ci	}
5058c2ecf20Sopenharmony_ci
5068c2ecf20Sopenharmony_ci	err = pci_request_regions(dev, "ec_bhf");
5078c2ecf20Sopenharmony_ci	if (err) {
5088c2ecf20Sopenharmony_ci		dev_err(&dev->dev, "Failed to request pci memory regions\n");
5098c2ecf20Sopenharmony_ci		goto err_disable_dev;
5108c2ecf20Sopenharmony_ci	}
5118c2ecf20Sopenharmony_ci
5128c2ecf20Sopenharmony_ci	io = pci_iomap(dev, 0, 0);
5138c2ecf20Sopenharmony_ci	if (!io) {
5148c2ecf20Sopenharmony_ci		dev_err(&dev->dev, "Failed to map pci card memory bar 0");
5158c2ecf20Sopenharmony_ci		err = -EIO;
5168c2ecf20Sopenharmony_ci		goto err_release_regions;
5178c2ecf20Sopenharmony_ci	}
5188c2ecf20Sopenharmony_ci
5198c2ecf20Sopenharmony_ci	dma_io = pci_iomap(dev, 2, 0);
5208c2ecf20Sopenharmony_ci	if (!dma_io) {
5218c2ecf20Sopenharmony_ci		dev_err(&dev->dev, "Failed to map pci card memory bar 2");
5228c2ecf20Sopenharmony_ci		err = -EIO;
5238c2ecf20Sopenharmony_ci		goto err_unmap;
5248c2ecf20Sopenharmony_ci	}
5258c2ecf20Sopenharmony_ci
5268c2ecf20Sopenharmony_ci	net_dev = alloc_etherdev(sizeof(struct ec_bhf_priv));
5278c2ecf20Sopenharmony_ci	if (net_dev == NULL) {
5288c2ecf20Sopenharmony_ci		err = -ENOMEM;
5298c2ecf20Sopenharmony_ci		goto err_unmap_dma_io;
5308c2ecf20Sopenharmony_ci	}
5318c2ecf20Sopenharmony_ci
5328c2ecf20Sopenharmony_ci	pci_set_drvdata(dev, net_dev);
5338c2ecf20Sopenharmony_ci	SET_NETDEV_DEV(net_dev, &dev->dev);
5348c2ecf20Sopenharmony_ci
5358c2ecf20Sopenharmony_ci	net_dev->features = 0;
5368c2ecf20Sopenharmony_ci	net_dev->flags |= IFF_NOARP;
5378c2ecf20Sopenharmony_ci
5388c2ecf20Sopenharmony_ci	net_dev->netdev_ops = &ec_bhf_netdev_ops;
5398c2ecf20Sopenharmony_ci
5408c2ecf20Sopenharmony_ci	priv = netdev_priv(net_dev);
5418c2ecf20Sopenharmony_ci	priv->net_dev = net_dev;
5428c2ecf20Sopenharmony_ci	priv->io = io;
5438c2ecf20Sopenharmony_ci	priv->dma_io = dma_io;
5448c2ecf20Sopenharmony_ci	priv->dev = dev;
5458c2ecf20Sopenharmony_ci
5468c2ecf20Sopenharmony_ci	err = ec_bhf_setup_offsets(priv);
5478c2ecf20Sopenharmony_ci	if (err < 0)
5488c2ecf20Sopenharmony_ci		goto err_free_net_dev;
5498c2ecf20Sopenharmony_ci
5508c2ecf20Sopenharmony_ci	memcpy_fromio(net_dev->dev_addr, priv->mii_io + MII_MAC_ADDR, 6);
5518c2ecf20Sopenharmony_ci
5528c2ecf20Sopenharmony_ci	err = register_netdev(net_dev);
5538c2ecf20Sopenharmony_ci	if (err < 0)
5548c2ecf20Sopenharmony_ci		goto err_free_net_dev;
5558c2ecf20Sopenharmony_ci
5568c2ecf20Sopenharmony_ci	return 0;
5578c2ecf20Sopenharmony_ci
5588c2ecf20Sopenharmony_cierr_free_net_dev:
5598c2ecf20Sopenharmony_ci	free_netdev(net_dev);
5608c2ecf20Sopenharmony_cierr_unmap_dma_io:
5618c2ecf20Sopenharmony_ci	pci_iounmap(dev, dma_io);
5628c2ecf20Sopenharmony_cierr_unmap:
5638c2ecf20Sopenharmony_ci	pci_iounmap(dev, io);
5648c2ecf20Sopenharmony_cierr_release_regions:
5658c2ecf20Sopenharmony_ci	pci_release_regions(dev);
5668c2ecf20Sopenharmony_cierr_disable_dev:
5678c2ecf20Sopenharmony_ci	pci_clear_master(dev);
5688c2ecf20Sopenharmony_ci	pci_disable_device(dev);
5698c2ecf20Sopenharmony_ci
5708c2ecf20Sopenharmony_ci	return err;
5718c2ecf20Sopenharmony_ci}
5728c2ecf20Sopenharmony_ci
5738c2ecf20Sopenharmony_cistatic void ec_bhf_remove(struct pci_dev *dev)
5748c2ecf20Sopenharmony_ci{
5758c2ecf20Sopenharmony_ci	struct net_device *net_dev = pci_get_drvdata(dev);
5768c2ecf20Sopenharmony_ci	struct ec_bhf_priv *priv = netdev_priv(net_dev);
5778c2ecf20Sopenharmony_ci
5788c2ecf20Sopenharmony_ci	unregister_netdev(net_dev);
5798c2ecf20Sopenharmony_ci
5808c2ecf20Sopenharmony_ci	pci_iounmap(dev, priv->dma_io);
5818c2ecf20Sopenharmony_ci	pci_iounmap(dev, priv->io);
5828c2ecf20Sopenharmony_ci
5838c2ecf20Sopenharmony_ci	free_netdev(net_dev);
5848c2ecf20Sopenharmony_ci
5858c2ecf20Sopenharmony_ci	pci_release_regions(dev);
5868c2ecf20Sopenharmony_ci	pci_clear_master(dev);
5878c2ecf20Sopenharmony_ci	pci_disable_device(dev);
5888c2ecf20Sopenharmony_ci}
5898c2ecf20Sopenharmony_ci
5908c2ecf20Sopenharmony_cistatic struct pci_driver pci_driver = {
5918c2ecf20Sopenharmony_ci	.name		= "ec_bhf",
5928c2ecf20Sopenharmony_ci	.id_table	= ids,
5938c2ecf20Sopenharmony_ci	.probe		= ec_bhf_probe,
5948c2ecf20Sopenharmony_ci	.remove		= ec_bhf_remove,
5958c2ecf20Sopenharmony_ci};
5968c2ecf20Sopenharmony_cimodule_pci_driver(pci_driver);
5978c2ecf20Sopenharmony_ci
5988c2ecf20Sopenharmony_cimodule_param(polling_frequency, long, 0444);
5998c2ecf20Sopenharmony_ciMODULE_PARM_DESC(polling_frequency, "Polling timer frequency in ns");
6008c2ecf20Sopenharmony_ci
6018c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
6028c2ecf20Sopenharmony_ciMODULE_AUTHOR("Dariusz Marcinkiewicz <reksio@newterm.pl>");
603