/*
 * Copyright (C) 2014 Lucas Stach <l.stach@pengutronix.de>
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms and conditions of the GNU General Public License,
 * version 2, as published by the Free Software Foundation.
 *
 * This program is distributed in the hope it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
 * more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <common.h>
#include <dma.h>
#include <init.h>
#include <net.h>
#include <malloc.h>
#include <linux/pci.h>

#define NUM_TX_DESC	1
#define NUM_RX_DESC	4
#define PKT_BUF_SIZE	1536
#define ETH_ZLEN	60

struct rtl8169_chip_info {
	const char *name;
	u8 version;
	u32 RxConfigMask;
};

#define BD_STAT_OWN	0x80000000
#define BD_STAT_EOR	0x40000000
#define BD_STAT_FS	0x20000000
#define BD_STAT_LS	0x10000000
#define BD_STAT_RX_RES	0x00200000
struct bufdesc {
	u32 status;
	u32 vlan_tag;
	u32 buf_addr;
	u32 buf_Haddr;
};

struct rtl8169_priv {
	struct eth_device	edev;
	void __iomem		*base;
	struct pci_dev		*pci_dev;
	int			chipset;

	volatile struct bufdesc	*tx_desc;
	void			*tx_buf;
	unsigned int		cur_tx;

	volatile struct bufdesc	*rx_desc;
	void			*rx_buf;
	unsigned int		cur_rx;

	struct mii_bus miibus;
};

#define MAC0 			0x00
#define MAR0			0x08
#define TxDescStartAddrLow	0x20
#define TxDescStartAddrHigh	0x24
#define TxHDescStartAddrLow	0x28
#define TxHDescStartAddrHigh	0x2c
#define FLASH			0x30
#define ERSR			0x36
#define ChipCmd			0x37
#define  CmdReset		0x10
#define  CmdRxEnb		0x08
#define  CmdTxEnb		0x04
#define  RxBufEmpty		0x01
#define TxPoll			0x38
#define IntrMask		0x3c
#define IntrStatus		0x3e
#define  SYSErr			0x8000
#define  PCSTimeout		0x4000
#define  SWInt			0x0100
#define  TxDescUnavail		0x80
#define  RxFIFOOver		0x40
#define  RxUnderrun		0x20
#define  RxOverflow		0x10
#define  TxErr			0x08
#define  TxOK			0x04
#define  RxErr			0x02
#define  RxOK			0x01
#define TxConfig		0x40
#define  TxInterFrameGapShift	24
#define  TxDMAShift		8
#define RxConfig		0x44
#define  AcceptErr		0x20
#define  AcceptRunt		0x10
#define  AcceptBroadcast	0x08
#define  AcceptMulticast	0x04
#define  AcceptMyPhys		0x02
#define  AcceptAllPhys		0x01
#define  RxCfgFIFOShift		13
#define  RxCfgDMAShift		8
#define RxMissed		0x4c
#define Cfg9346			0x50
#define  Cfg9346_Lock		0x00
#define  Cfg9346_Unlock		0xc0
#define Config0			0x51
#define Config1			0x52
#define Config2			0x53
#define Config3			0x54
#define Config4			0x55
#define Config5			0x56
#define MultiIntr		0x5c
#define PHYAR			0x60
#define TBICSR			0x64
#define TBI_ANAR		0x68
#define TBI_LPAR		0x6a
#define PHYstatus		0x6c
#define RxMaxSize		0xda
#define CPlusCmd		0xe0
#define RxDescStartAddrLow	0xe4
#define RxDescStartAddrHigh	0xe8
#define EarlyTxThres		0xec
#define FuncEvent		0xf0
#define FuncEventMask		0xf4
#define FuncPresetState		0xf8
#define FuncForceEvent		0xfc

/* write MMIO register */
#define RTL_W8(priv, reg, val)	writeb(val, ((char *)(priv->base) + reg))
#define RTL_W16(priv, reg, val)	writew(val, ((char *)(priv->base) + reg))
#define RTL_W32(priv, reg, val)	writel(val, ((char *)(priv->base) + reg))

/* read MMIO register */
#define RTL_R8(priv, reg)	readb(((char *)(priv->base) + reg))
#define RTL_R16(priv, reg)	readw(((char *)(priv->base) + reg))
#define RTL_R32(priv, reg)	readl(((char *)(priv->base) + reg))

static const u32 rtl8169_rx_config =
		 (7 << RxCfgFIFOShift) | (6 << RxCfgDMAShift);

static void rtl8169_chip_reset(struct rtl8169_priv *priv)
{
	int i;

	/* Soft reset the chip. */
	RTL_W8(priv, ChipCmd, CmdReset);

	/* Check that the chip has finished the reset. */
	for (i = 1000; i > 0; i--) {
		if ((RTL_R8(priv, ChipCmd) & CmdReset) == 0)
			break;
		udelay(10);
	}
}

static struct rtl8169_chip_info chip_info[] = {
	{"RTL-8169",		0x00,	0xff7e1880},
	{"RTL-8169",		0x04,	0xff7e1880},
	{"RTL-8169",		0x00,	0xff7e1880},
	{"RTL-8169s/8110s",	0x02,	0xff7e1880},
	{"RTL-8169s/8110s",	0x04,	0xff7e1880},
	{"RTL-8169sb/8110sb",	0x10,	0xff7e1880},
	{"RTL-8169sc/8110sc",	0x18,	0xff7e1880},
	{"RTL-8168b/8111sb",	0x30,	0xff7e1880},
	{"RTL-8168b/8111sb",	0x38,	0xff7e1880},
	{"RTL-8168d/8111d",	0x28,	0xff7e1880},
	{"RTL-8168evl/8111evl",	0x2e,	0xff7e1880},
	{"RTL-8168/8111g",	0x4c,	0xff7e1880,},
	{"RTL-8101e",		0x34,	0xff7e1880},
	{"RTL-8100e",		0x32,	0xff7e1880},
};

static void rtl8169_chip_identify(struct rtl8169_priv *priv)
{
	u32 val;
	int i;

	val = RTL_R32(priv, TxConfig);
	val = ((val & 0x7c000000) + ((val & 0x00800000) << 2)) >> 24;

	for (i = ARRAY_SIZE(chip_info) - 1; i >= 0; i--){
		if (val == chip_info[i].version) {
			priv->chipset = i;
			dev_dbg(&priv->pci_dev->dev, "found %s chipset\n",
				chip_info[i].name);
			return;
		}
	}

	dev_dbg(&priv->pci_dev->dev,
		"no matching chip version found, assuming RTL-8169\n");
	priv->chipset = 0;
}

static int rtl8169_init_dev(struct eth_device *edev)
{
	struct rtl8169_priv *priv = edev->priv;

	rtl8169_chip_reset(priv);
	rtl8169_chip_identify(priv);
	pci_set_master(priv->pci_dev);

	return 0;
}

static void __set_rx_mode(struct rtl8169_priv *priv)
{
	u32 mc_filter[2], val;

	/* IFF_ALLMULTI */
	/* Too many to filter perfectly -- accept all multicasts. */
	mc_filter[1] = mc_filter[0] = 0xffffffff;

	val = AcceptBroadcast | AcceptMulticast | AcceptMyPhys |
	      rtl8169_rx_config | (RTL_R32(priv, RxConfig) &
				  chip_info[priv->chipset].RxConfigMask);

	RTL_W32(priv, RxConfig, val);
	RTL_W32(priv, MAR0 + 0, mc_filter[0]);
	RTL_W32(priv, MAR0 + 4, mc_filter[1]);
}

static void rtl8169_init_ring(struct rtl8169_priv *priv)
{
	int i;

	priv->cur_rx = priv->cur_tx = 0;

	priv->tx_desc = dma_alloc_coherent(NUM_TX_DESC *
				sizeof(struct bufdesc), DMA_ADDRESS_BROKEN);
	priv->tx_buf = malloc(NUM_TX_DESC * PKT_BUF_SIZE);
	priv->rx_desc = dma_alloc_coherent(NUM_RX_DESC *
				sizeof(struct bufdesc), DMA_ADDRESS_BROKEN);
	priv->rx_buf = malloc(NUM_RX_DESC * PKT_BUF_SIZE);
	dma_sync_single_for_device((unsigned long)priv->rx_buf,
				   NUM_RX_DESC * PKT_BUF_SIZE, DMA_FROM_DEVICE);

	memset((void *)priv->tx_desc, 0, NUM_TX_DESC * sizeof(struct bufdesc));
	memset((void *)priv->rx_desc, 0, NUM_RX_DESC * sizeof(struct bufdesc));

	for (i = 0; i < NUM_RX_DESC; i++) {
		if (i == (NUM_RX_DESC - 1))
			priv->rx_desc[i].status =
				BD_STAT_OWN | BD_STAT_EOR | PKT_BUF_SIZE;
		else
			priv->rx_desc[i].status =
				BD_STAT_OWN | PKT_BUF_SIZE;

		priv->rx_desc[i].buf_addr =
				virt_to_phys(priv->rx_buf + i * PKT_BUF_SIZE);
	}
}

static void rtl8169_hw_start(struct rtl8169_priv *priv)
{
	u32 val;

	RTL_W8(priv, Cfg9346, Cfg9346_Unlock);

	/* RTL-8169sb/8110sb or previous version */
	if (priv->chipset <= 5)
		RTL_W8(priv, ChipCmd, CmdTxEnb | CmdRxEnb);

	RTL_W8(priv, EarlyTxThres, 0x3f);

	/* For gigabit rtl8169 */
	RTL_W16(priv, RxMaxSize, 0x800);

	/* Set Rx Config register */
	val = rtl8169_rx_config | (RTL_R32(priv, RxConfig) &
	      chip_info[priv->chipset].RxConfigMask);
	RTL_W32(priv, RxConfig, val);

	/* Set DMA burst size and Interframe Gap Time */
	RTL_W32(priv, TxConfig, (6 << TxDMAShift) | (3 << TxInterFrameGapShift));

	RTL_W32(priv, TxDescStartAddrLow, virt_to_phys(priv->tx_desc));
	RTL_W32(priv, TxDescStartAddrHigh, 0);
	RTL_W32(priv, RxDescStartAddrLow, virt_to_phys(priv->rx_desc));
	RTL_W32(priv, RxDescStartAddrHigh, 0);

	/* RTL-8169sc/8110sc or later version */
	if (priv->chipset > 5)
		RTL_W8(priv, ChipCmd, CmdTxEnb | CmdRxEnb);

	RTL_W8(priv, Cfg9346, Cfg9346_Lock);
	udelay(10);

	RTL_W32(priv, RxMissed, 0);

	__set_rx_mode(priv);

	/* no early-rx interrupts */
	RTL_W16(priv, MultiIntr, RTL_R16(priv, MultiIntr) & 0xf000);
}

static int rtl8169_eth_open(struct eth_device *edev)
{
	struct rtl8169_priv *priv = edev->priv;
	int ret;

	rtl8169_init_ring(priv);
	rtl8169_hw_start(priv);

	ret = phy_device_connect(edev, &priv->miibus, 0, NULL, 0,
				 PHY_INTERFACE_MODE_NA);

	return ret;
}

static int rtl8169_phy_write(struct mii_bus *bus, int phy_addr,
	int reg, u16 val)
{
	struct rtl8169_priv *priv = bus->priv;
	int i;

	if (phy_addr != 0)
		return -1;

	RTL_W32(priv, PHYAR, 0x80000000 | (reg & 0xff) << 16 | val);
	mdelay(1);

	for (i = 2000; i > 0; i--) {
		if (!(RTL_R32(priv, PHYAR) & 0x80000000)) {
			return 0;
		} else {
			udelay(100);
		}
	}

	return -1;
}

static int rtl8169_phy_read(struct mii_bus *bus, int phy_addr, int reg)
{
	struct rtl8169_priv *priv = bus->priv;
	int i, val = 0xffff;

	RTL_W32(priv, PHYAR, 0x0 | (reg & 0xff) << 16);
	mdelay(10);

	if (phy_addr != 0)
		return val;

	for (i = 2000; i > 0; i--) {
		if (RTL_R32(priv, PHYAR) & 0x80000000) {
			val = (int) (RTL_R32(priv, PHYAR) & 0xffff);
			break;
		} else {
			udelay(100);
		}
	}
	return val;
}

static int rtl8169_eth_send(struct eth_device *edev, void *packet,
				int packet_length)
{
	struct rtl8169_priv *priv = edev->priv;
	unsigned int entry;

	entry = priv->cur_tx % NUM_TX_DESC;

	if (packet_length < ETH_ZLEN)
		memset(priv->tx_buf + entry * PKT_BUF_SIZE, 0, ETH_ZLEN);
	memcpy(priv->tx_buf + entry * PKT_BUF_SIZE, packet, packet_length);
	dma_sync_single_for_device((unsigned long)priv->tx_buf + entry *
				   PKT_BUF_SIZE, PKT_BUF_SIZE, DMA_TO_DEVICE);

	priv->tx_desc[entry].buf_Haddr = 0;
	priv->tx_desc[entry].buf_addr =
		virt_to_phys(priv->tx_buf + entry * PKT_BUF_SIZE);

	if (entry != (NUM_TX_DESC - 1)) {
		priv->tx_desc[entry].status =
			BD_STAT_OWN | BD_STAT_FS | BD_STAT_LS |
			((packet_length > ETH_ZLEN) ? packet_length : ETH_ZLEN);
	} else {
		priv->tx_desc[entry].status =
			BD_STAT_OWN | BD_STAT_EOR | BD_STAT_FS | BD_STAT_LS |
			((packet_length > ETH_ZLEN) ? packet_length : ETH_ZLEN);
	}

	RTL_W8(priv, TxPoll, 0x40);

	while (priv->tx_desc[entry].status & BD_STAT_OWN)
		;

	dma_sync_single_for_cpu((unsigned long)priv->tx_buf + entry *
				PKT_BUF_SIZE, PKT_BUF_SIZE, DMA_TO_DEVICE);

	priv->cur_tx++;

	return 0;
}

static int rtl8169_eth_rx(struct eth_device *edev)
{
	struct rtl8169_priv *priv = edev->priv;
	unsigned int entry, pkt_size = 0;
	u8 status;

	entry = priv->cur_rx % NUM_RX_DESC;

	if ((priv->rx_desc[entry].status & BD_STAT_OWN) == 0) {
		if (!(priv->rx_desc[entry].status & BD_STAT_RX_RES)) {
			pkt_size = (priv->rx_desc[entry].status & 0x1fff) - 4;

			dma_sync_single_for_cpu((unsigned long)priv->rx_buf
						+ entry * PKT_BUF_SIZE,
						pkt_size, DMA_FROM_DEVICE);

			net_receive(edev, priv->rx_buf + entry * PKT_BUF_SIZE,
			            pkt_size);

			dma_sync_single_for_device((unsigned long)priv->rx_buf
						   + entry * PKT_BUF_SIZE,
						   pkt_size, DMA_FROM_DEVICE);

			if (entry == NUM_RX_DESC - 1)
				priv->rx_desc[entry].status = BD_STAT_OWN |
					BD_STAT_EOR | PKT_BUF_SIZE;
			else
				priv->rx_desc[entry].status =
					BD_STAT_OWN | PKT_BUF_SIZE;
			priv->rx_desc[entry].buf_addr =
				virt_to_phys(priv->rx_buf +
				             entry * PKT_BUF_SIZE);
		} else {
			dev_err(&edev->dev, "rx error\n");
		}

		priv->cur_rx++;

		return pkt_size;

	} else {
		status = RTL_R8(priv, IntrStatus);
		RTL_W8(priv, IntrStatus, status & ~(TxErr | RxErr | SYSErr));
		udelay(100);	/* wait */
	}

	return 0;
}

static int rtl8169_get_ethaddr(struct eth_device *edev, unsigned char *m)
{
	struct rtl8169_priv *priv = edev->priv;
	int i;

	for (i = 0; i < 6; i++) {
		m[i] = RTL_R8(priv, MAC0 + i);
	}

	return 0;
}

static int rtl8169_set_ethaddr(struct eth_device *edev, unsigned char *mac_addr)
{
	struct rtl8169_priv *priv = edev->priv;
	int i;

	RTL_W8(priv, Cfg9346, Cfg9346_Unlock);

	for (i = 0; i < 6; i++) {
		RTL_W8(priv, (MAC0 + i), mac_addr[i]);
		RTL_R8(priv, mac_addr[i]);
	}

	RTL_W8(priv, Cfg9346, Cfg9346_Lock);

	return 0;
}

static void rtl8169_eth_halt(struct eth_device *edev)
{
	struct rtl8169_priv *priv = edev->priv;

	/* Stop the chip's Tx and Rx DMA processes. */
	RTL_W8(priv, ChipCmd, 0x00);

	/* Disable interrupts by clearing the interrupt mask. */
	RTL_W16(priv, IntrMask, 0x0000);
	RTL_W32(priv, RxMissed, 0);

	pci_clear_master(priv->pci_dev);
}

static int rtl8169_probe(struct pci_dev *pdev, const struct pci_device_id *id)
{
	struct device_d *dev = &pdev->dev;
	struct eth_device *edev;
	struct rtl8169_priv *priv;
	int ret;

	/* enable pci device */
	pci_enable_device(pdev);

	priv = xzalloc(sizeof(struct rtl8169_priv));

	edev = &priv->edev;
	dev->type_data = edev;
	edev->priv = priv;

	priv->pci_dev = pdev;

	priv->miibus.read = rtl8169_phy_read;
	priv->miibus.write = rtl8169_phy_write;
	priv->miibus.priv = priv;
	priv->miibus.parent = &edev->dev;

	priv->base = pci_iomap(pdev, pdev->device == 0x8168 ? 2 : 1);

	dev_dbg(dev, "rtl%04x (rev %02x) (base=%p)\n",
		 pdev->device, pdev->revision, priv->base);

	edev->init = rtl8169_init_dev;
	edev->open = rtl8169_eth_open;
	edev->send = rtl8169_eth_send;
	edev->recv = rtl8169_eth_rx;
	edev->get_ethaddr = rtl8169_get_ethaddr;
	edev->set_ethaddr = rtl8169_set_ethaddr;
	edev->halt = rtl8169_eth_halt;
	edev->parent = dev;
	ret = eth_register(edev);
	if (ret)
		goto eth_err;

	ret = mdiobus_register(&priv->miibus);
	if (ret)
		goto mdio_err;

	return 0;

mdio_err:
	eth_unregister(edev);

eth_err:
	free(priv);

	return ret;
}
static DEFINE_PCI_DEVICE_TABLE(rtl8169_pci_tbl) = {
	{ PCI_DEVICE(PCI_VENDOR_ID_REALTEK, 0x8167), },
	{ PCI_DEVICE(PCI_VENDOR_ID_REALTEK, 0x8168), },
	{ PCI_DEVICE(PCI_VENDOR_ID_REALTEK, 0x8169), },
	{ /* sentinel */ }
};

static struct pci_driver rtl8169_eth_driver = {
	.name = "rtl8169_eth",
	.id_table = rtl8169_pci_tbl,
	.probe = rtl8169_probe,
};

static int rtl8169_init(void)
{
	return pci_register_driver(&rtl8169_eth_driver);
}
device_initcall(rtl8169_init);
