[PATCH v2] NET: Add support for ks8851_mll

Jan Weitzel j.weitzel at phytec.de
Fri Mar 9 04:24:09 EST 2012


Add support for KS8851 16bit MLL chip from Micrel Inc.

Signed-off-by: Jan Weitzel <j.weitzel at phytec.de>
---
v2: use dev_* for messages

 drivers/base/resource.c  |   33 ++
 drivers/net/Kconfig      |    7 +
 drivers/net/Makefile     |    1 +
 drivers/net/ks8851_mll.c |  892 ++++++++++++++++++++++++++++++++++++++++++++++
 include/driver.h         |   11 +
 5 files changed, 944 insertions(+), 0 deletions(-)
 create mode 100644 drivers/net/ks8851_mll.c

diff --git a/drivers/base/resource.c b/drivers/base/resource.c
index d2f7a07..b31c7d7 100644
--- a/drivers/base/resource.c
+++ b/drivers/base/resource.c
@@ -121,3 +121,36 @@ struct device_d *add_usb_ehci_device(int id, resource_size_t hccr,
 }
 EXPORT_SYMBOL(add_usb_ehci_device);
 #endif
+
+#ifdef CONFIG_DRIVER_NET_KS8851_MLL
+struct device_d *add_ks8851_device(int id, resource_size_t addr,
+		resource_size_t addr_cmd, int flags, void *pdata)
+{
+	struct resource *res;
+	resource_size_t size;
+
+	switch (flags) {
+	case IORESOURCE_MEM_16BIT:
+		size = 2;
+		break;
+	case IORESOURCE_MEM_8BIT:
+		size = 1;
+		break;
+	default:
+		printf("ks8851: memory width flag missing\n");
+		return NULL;
+	}
+
+	res = xzalloc(sizeof(struct resource) * 2);
+
+	res[0].start = addr;
+	res[0].size = size;
+	res[0].flags = IORESOURCE_MEM | flags;
+	res[1].start = addr_cmd;
+	res[1].size = size;
+	res[1].flags = IORESOURCE_MEM | flags;
+
+	return add_generic_device_res("ks8851_mll", id, res, 2, pdata);
+}
+EXPORT_SYMBOL(add_ks8851_device);
+#endif
diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig
index b236d17..4cdb37b 100644
--- a/drivers/net/Kconfig
+++ b/drivers/net/Kconfig
@@ -100,6 +100,13 @@ config TSE_USE_DEDICATED_DESC_MEM
 	  reserved with a malloc but directly mapped to the memory
 	  address (defined in config.h)
 
+config DRIVER_NET_KS8851_MLL
+	bool "ks8851 mll ethernet driver"
+	select MIIDEV
+	help
+	  This option enables support for the Micrel KS8851 MLL
+	  ethernet chip.
+
 source "drivers/net/usb/Kconfig"
 
 endmenu
diff --git a/drivers/net/Makefile b/drivers/net/Makefile
index a84d3dc..34dbee9 100644
--- a/drivers/net/Makefile
+++ b/drivers/net/Makefile
@@ -12,3 +12,4 @@ obj-$(CONFIG_DRIVER_NET_TAP)		+= tap.o
 obj-$(CONFIG_MIIDEV)			+= miidev.o
 obj-$(CONFIG_NET_USB)			+= usb/
 obj-$(CONFIG_DRIVER_NET_TSE)		+= altera_tse.o
+obj-$(CONFIG_DRIVER_NET_KS8851_MLL)	+= ks8851_mll.o
diff --git a/drivers/net/ks8851_mll.c b/drivers/net/ks8851_mll.c
new file mode 100644
index 0000000..aaaf099
--- /dev/null
+++ b/drivers/net/ks8851_mll.c
@@ -0,0 +1,892 @@
+/**
+ * Copyright (c) 2012 Jan Weitzel <j.weitzel at phytec.de>
+ * based on kernel driver drivers/net/ks8851_mll.c
+ * Copyright (c) 2009 Micrel Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that 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, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/**
+ * Supports:
+ * KS8851 16bit MLL chip from Micrel Inc.
+ */
+#include <common.h>
+#include <driver.h>
+
+#include <command.h>
+#include <net.h>
+#include <miidev.h>
+#include <malloc.h>
+#include <init.h>
+#include <xfuncs.h>
+#include <errno.h>
+#include <clock.h>
+#include <io.h>
+
+#define MAX_RECV_FRAMES			32
+#define MAX_BUF_SIZE			2048
+#define TX_BUF_SIZE			2000
+#define RX_BUF_SIZE			2000
+
+#define KS_CCR				0x08
+#define CCR_EEPROM			(1 << 9)
+#define CCR_SPI				(1 << 8)
+#define CCR_8BIT			(1 << 7)
+#define CCR_16BIT			(1 << 6)
+#define CCR_32BIT			(1 << 5)
+#define CCR_SHARED			(1 << 4)
+#define CCR_32PIN			(1 << 0)
+
+/* MAC address registers */
+#define KS_MARL				0x10
+#define KS_MARM				0x12
+#define KS_MARH				0x14
+
+#define KS_OBCR				0x20
+#define OBCR_ODS_16MA			(1 << 6)
+
+#define KS_EEPCR			0x22
+#define EEPCR_EESA			(1 << 4)
+#define EEPCR_EESB			(1 << 3)
+#define EEPCR_EEDO			(1 << 2)
+#define EEPCR_EESCK			(1 << 1)
+#define EEPCR_EECS			(1 << 0)
+
+#define KS_MBIR				0x24
+#define MBIR_TXMBF			(1 << 12)
+#define MBIR_TXMBFA			(1 << 11)
+#define MBIR_RXMBF			(1 << 4)
+#define MBIR_RXMBFA			(1 << 3)
+
+#define KS_GRR				0x26
+#define GRR_QMU				(1 << 1)
+#define GRR_GSR				(1 << 0)
+
+#define KS_WFCR				0x2A
+#define WFCR_MPRXE			(1 << 7)
+#define WFCR_WF3E			(1 << 3)
+#define WFCR_WF2E			(1 << 2)
+#define WFCR_WF1E			(1 << 1)
+#define WFCR_WF0E			(1 << 0)
+
+#define KS_WF0CRC0			0x30
+#define KS_WF0CRC1			0x32
+#define KS_WF0BM0			0x34
+#define KS_WF0BM1			0x36
+#define KS_WF0BM2			0x38
+#define KS_WF0BM3			0x3A
+
+#define KS_WF1CRC0			0x40
+#define KS_WF1CRC1			0x42
+#define KS_WF1BM0			0x44
+#define KS_WF1BM1			0x46
+#define KS_WF1BM2			0x48
+#define KS_WF1BM3			0x4A
+
+#define KS_WF2CRC0			0x50
+#define KS_WF2CRC1			0x52
+#define KS_WF2BM0			0x54
+#define KS_WF2BM1			0x56
+#define KS_WF2BM2			0x58
+#define KS_WF2BM3			0x5A
+
+#define KS_WF3CRC0			0x60
+#define KS_WF3CRC1			0x62
+#define KS_WF3BM0			0x64
+#define KS_WF3BM1			0x66
+#define KS_WF3BM2			0x68
+#define KS_WF3BM3			0x6A
+
+#define KS_TXCR				0x70
+#define TXCR_TCGICMP			(1 << 8)
+#define TXCR_TCGUDP			(1 << 7)
+#define TXCR_TCGTCP			(1 << 6)
+#define TXCR_TCGIP			(1 << 5)
+#define TXCR_FTXQ			(1 << 4)
+#define TXCR_TXFCE			(1 << 3)
+#define TXCR_TXPE			(1 << 2)
+#define TXCR_TXCRC			(1 << 1)
+#define TXCR_TXE			(1 << 0)
+
+#define KS_TXSR				0x72
+#define TXSR_TXLC			(1 << 13)
+#define TXSR_TXMC			(1 << 12)
+#define TXSR_TXFID_MASK			(0x3f << 0)
+#define TXSR_TXFID_SHIFT		(0)
+#define TXSR_TXFID_GET(_v)		(((_v) >> 0) & 0x3f)
+
+
+#define KS_RXCR1			0x74
+#define RXCR1_FRXQ			(1 << 15)
+#define RXCR1_RXUDPFCC			(1 << 14)
+#define RXCR1_RXTCPFCC			(1 << 13)
+#define RXCR1_RXIPFCC			(1 << 12)
+#define RXCR1_RXPAFMA			(1 << 11)
+#define RXCR1_RXFCE			(1 << 10)
+#define RXCR1_RXEFE			(1 << 9)
+#define RXCR1_RXMAFMA			(1 << 8)
+#define RXCR1_RXBE			(1 << 7)
+#define RXCR1_RXME			(1 << 6)
+#define RXCR1_RXUE			(1 << 5)
+#define RXCR1_RXAE			(1 << 4)
+#define RXCR1_RXINVF			(1 << 1)
+#define RXCR1_RXE			(1 << 0)
+#define RXCR1_FILTER_MASK		(RXCR1_RXINVF | RXCR1_RXAE | \
+					 RXCR1_RXMAFMA | RXCR1_RXPAFMA)
+
+#define KS_RXCR2			0x76
+#define RXCR2_SRDBL_MASK		(0x7 << 5)
+#define RXCR2_SRDBL_SHIFT		(5)
+#define RXCR2_SRDBL_4B			(0x0 << 5)
+#define RXCR2_SRDBL_8B			(0x1 << 5)
+#define RXCR2_SRDBL_16B			(0x2 << 5)
+#define RXCR2_SRDBL_32B			(0x3 << 5)
+/* #define RXCR2_SRDBL_FRAME		(0x4 << 5) */
+#define RXCR2_IUFFP			(1 << 4)
+#define RXCR2_RXIUFCEZ			(1 << 3)
+#define RXCR2_UDPLFE			(1 << 2)
+#define RXCR2_RXICMPFCC			(1 << 1)
+#define RXCR2_RXSAF			(1 << 0)
+
+#define KS_TXMIR			0x78
+
+#define KS_RXFHSR			0x7C
+#define RXFSHR_RXFV			(1 << 15)
+#define RXFSHR_RXICMPFCS		(1 << 13)
+#define RXFSHR_RXIPFCS			(1 << 12)
+#define RXFSHR_RXTCPFCS			(1 << 11)
+#define RXFSHR_RXUDPFCS			(1 << 10)
+#define RXFSHR_RXBF			(1 << 7)
+#define RXFSHR_RXMF			(1 << 6)
+#define RXFSHR_RXUF			(1 << 5)
+#define RXFSHR_RXMR			(1 << 4)
+#define RXFSHR_RXFT			(1 << 3)
+#define RXFSHR_RXFTL			(1 << 2)
+#define RXFSHR_RXRF			(1 << 1)
+#define RXFSHR_RXCE			(1 << 0)
+#define	RXFSHR_ERR			(RXFSHR_RXCE | RXFSHR_RXRF |\
+					RXFSHR_RXFTL | RXFSHR_RXMR |\
+					RXFSHR_RXICMPFCS | RXFSHR_RXIPFCS |\
+					RXFSHR_RXTCPFCS)
+#define KS_RXFHBCR			0x7E
+#define RXFHBCR_CNT_MASK		0x0FFF
+
+#define KS_TXQCR			0x80
+#define TXQCR_AETFE			(1 << 2)
+#define TXQCR_TXQMAM			(1 << 1)
+#define TXQCR_METFE			(1 << 0)
+
+#define KS_RXQCR			0x82
+#define RXQCR_RXDTTS			(1 << 12)
+#define RXQCR_RXDBCTS			(1 << 11)
+#define RXQCR_RXFCTS			(1 << 10)
+#define RXQCR_RXIPHTOE			(1 << 9)
+#define RXQCR_RXDTTE			(1 << 7)
+#define RXQCR_RXDBCTE			(1 << 6)
+#define RXQCR_RXFCTE			(1 << 5)
+#define RXQCR_ADRFE			(1 << 4)
+#define RXQCR_SDA			(1 << 3)
+#define RXQCR_RRXEF			(1 << 0)
+#define RXQCR_CMD_CNTL			(RXQCR_RXFCTE|RXQCR_ADRFE)
+
+#define KS_TXFDPR			0x84
+#define TXFDPR_TXFPAI			(1 << 14)
+#define TXFDPR_TXFP_MASK		(0x7ff << 0)
+#define TXFDPR_TXFP_SHIFT		(0)
+
+#define KS_RXFDPR			0x86
+#define RXFDPR_RXFPAI			(1 << 14)
+
+#define KS_RXDTTR			0x8C
+#define KS_RXDBCTR			0x8E
+
+#define KS_IER				0x90
+#define KS_ISR				0x92
+#define IRQ_LCI				(1 << 15)
+#define IRQ_TXI				(1 << 14)
+#define IRQ_RXI				(1 << 13)
+#define IRQ_RXOI			(1 << 11)
+#define IRQ_TXPSI			(1 << 9)
+#define IRQ_RXPSI			(1 << 8)
+#define IRQ_TXSAI			(1 << 6)
+#define IRQ_RXWFDI			(1 << 5)
+#define IRQ_RXMPDI			(1 << 4)
+#define IRQ_LDI				(1 << 3)
+#define IRQ_EDI				(1 << 2)
+#define IRQ_SPIBEI			(1 << 1)
+#define IRQ_DEDI			(1 << 0)
+
+#define KS_RXFCTR			0x9C
+#define RXFCTR_THRESHOLD_MASK		0x00FF
+
+#define KS_RXFC				0x9D
+#define RXFCTR_RXFC_MASK		(0xff << 8)
+#define RXFCTR_RXFC_SHIFT		(8)
+#define RXFCTR_RXFC_GET(_v)		(((_v) >> 8) & 0xff)
+#define RXFCTR_RXFCT_MASK		(0xff << 0)
+#define RXFCTR_RXFCT_SHIFT		(0)
+
+#define KS_TXNTFSR			0x9E
+
+#define KS_MAHTR0			0xA0
+#define KS_MAHTR1			0xA2
+#define KS_MAHTR2			0xA4
+#define KS_MAHTR3			0xA6
+
+#define KS_FCLWR			0xB0
+#define KS_FCHWR			0xB2
+#define KS_FCOWR			0xB4
+
+#define KS_CIDER			0xC0
+#define CIDER_ID			0x8870
+#define CIDER_REV_MASK			(0x7 << 1)
+#define CIDER_REV_SHIFT			(1)
+#define CIDER_REV_GET(_v)		(((_v) >> 1) & 0x7)
+
+#define KS_CGCR				0xC6
+#define KS_IACR				0xC8
+#define IACR_RDEN			(1 << 12)
+#define IACR_TSEL_MASK			(0x3 << 10)
+#define IACR_TSEL_SHIFT			(10)
+#define IACR_TSEL_MIB			(0x3 << 10)
+#define IACR_ADDR_MASK			(0x1f << 0)
+#define IACR_ADDR_SHIFT			(0)
+
+#define KS_IADLR			0xD0
+#define KS_IADHR			0xD2
+
+#define KS_PMECR			0xD4
+#define PMECR_PME_DELAY			(1 << 14)
+#define PMECR_PME_POL			(1 << 12)
+#define PMECR_WOL_WAKEUP		(1 << 11)
+#define PMECR_WOL_MAGICPKT		(1 << 10)
+#define PMECR_WOL_LINKUP		(1 << 9)
+#define PMECR_WOL_ENERGY		(1 << 8)
+#define PMECR_AUTO_WAKE_EN		(1 << 7)
+#define PMECR_WAKEUP_NORMAL		(1 << 6)
+#define PMECR_WKEVT_MASK		(0xf << 2)
+#define PMECR_WKEVT_SHIFT		(2)
+#define PMECR_WKEVT_GET(_v)		(((_v) >> 2) & 0xf)
+#define PMECR_WKEVT_ENERGY		(0x1 << 2)
+#define PMECR_WKEVT_LINK		(0x2 << 2)
+#define PMECR_WKEVT_MAGICPKT		(0x4 << 2)
+#define PMECR_WKEVT_FRAME		(0x8 << 2)
+#define PMECR_PM_MASK			(0x3 << 0)
+#define PMECR_PM_SHIFT			(0)
+#define PMECR_PM_NORMAL			(0x0 << 0)
+#define PMECR_PM_ENERGY			(0x1 << 0)
+#define PMECR_PM_SOFTDOWN		(0x2 << 0)
+#define PMECR_PM_POWERSAVE		(0x3 << 0)
+
+/* Standard MII PHY data */
+#define KS_P1MBCR			0xE4
+#define P1MBCR_FORCE_FDX		(1 << 8)
+
+#define KS_P1MBSR			0xE6
+#define P1MBSR_AN_COMPLETE		(1 << 5)
+#define P1MBSR_AN_CAPABLE		(1 << 3)
+#define P1MBSR_LINK_UP			(1 << 2)
+
+#define KS_PHY1ILR			0xE8
+#define KS_PHY1IHR			0xEA
+#define KS_P1ANAR			0xEC
+#define KS_P1ANLPR			0xEE
+
+#define KS_P1SCLMD			0xF4
+#define P1SCLMD_LEDOFF			(1 << 15)
+#define P1SCLMD_TXIDS			(1 << 14)
+#define P1SCLMD_RESTARTAN		(1 << 13)
+#define P1SCLMD_DISAUTOMDIX		(1 << 10)
+#define P1SCLMD_FORCEMDIX		(1 << 9)
+#define P1SCLMD_AUTONEGEN		(1 << 7)
+#define P1SCLMD_FORCE100		(1 << 6)
+#define P1SCLMD_FORCEFDX		(1 << 5)
+#define P1SCLMD_ADV_FLOW		(1 << 4)
+#define P1SCLMD_ADV_100BT_FDX		(1 << 3)
+#define P1SCLMD_ADV_100BT_HDX		(1 << 2)
+#define P1SCLMD_ADV_10BT_FDX		(1 << 1)
+#define P1SCLMD_ADV_10BT_HDX		(1 << 0)
+
+#define KS_P1CR				0xF6
+#define P1CR_HP_MDIX			(1 << 15)
+#define P1CR_REV_POL			(1 << 13)
+#define P1CR_OP_100M			(1 << 10)
+#define P1CR_OP_FDX			(1 << 9)
+#define P1CR_OP_MDI			(1 << 7)
+#define P1CR_AN_DONE			(1 << 6)
+#define P1CR_LINK_GOOD			(1 << 5)
+#define P1CR_PNTR_FLOW			(1 << 4)
+#define P1CR_PNTR_100BT_FDX		(1 << 3)
+#define P1CR_PNTR_100BT_HDX		(1 << 2)
+#define P1CR_PNTR_10BT_FDX		(1 << 1)
+#define P1CR_PNTR_10BT_HDX		(1 << 0)
+
+/* TX Frame control */
+
+#define TXFR_TXIC			(1 << 15)
+#define TXFR_TXFID_MASK			(0x3f << 0)
+#define TXFR_TXFID_SHIFT		(0)
+
+#define KS_P1SR				0xF8
+#define P1SR_HP_MDIX			(1 << 15)
+#define P1SR_REV_POL			(1 << 13)
+#define P1SR_OP_100M			(1 << 10)
+#define P1SR_OP_FDX			(1 << 9)
+#define P1SR_OP_MDI			(1 << 7)
+#define P1SR_AN_DONE			(1 << 6)
+#define P1SR_LINK_GOOD			(1 << 5)
+#define P1SR_PNTR_FLOW			(1 << 4)
+#define P1SR_PNTR_100BT_FDX		(1 << 3)
+#define P1SR_PNTR_100BT_HDX		(1 << 2)
+#define P1SR_PNTR_10BT_FDX		(1 << 1)
+#define P1SR_PNTR_10BT_HDX		(1 << 0)
+
+#define	ENUM_BUS_NONE			0
+#define	ENUM_BUS_8BIT			1
+#define	ENUM_BUS_16BIT			2
+#define	ENUM_BUS_32BIT			3
+
+#define MAX_MCAST_LST			32
+#define HW_MCAST_SIZE			8
+
+/**
+ * struct ks_net - KS8851 driver private data
+ * @hw_addr	: start address of data register.
+ * @hw_addr_cmd	: start address of command register.
+ * @pdev	: Pointer to platform device.
+ * @bus_width	: i/o bus width.
+ * @extra_byte	: number of extra byte prepended rx pkt.
+ *
+ */
+
+struct ks_net {
+	struct eth_device	edev;
+	struct mii_device	miidev;
+	void __iomem		*hw_addr;
+	void __iomem		*hw_addr_cmd;
+	struct platform_device	*pdev;
+	int			bus_width;
+};
+
+#define BE3             0x8000      /* Byte Enable 3 */
+#define BE2             0x4000      /* Byte Enable 2 */
+#define BE1             0x2000      /* Byte Enable 1 */
+#define BE0             0x1000      /* Byte Enable 0 */
+
+/**
+ * ks_rdreg16 - read 16 bit register from device
+ * @ks	  : The chip information
+ * @offset: The register address
+ *
+ * Read a 16bit register from the chip, returning the result
+ */
+
+static u16 ks_rdreg16(struct ks_net *ks, int offset)
+{
+	u16 value;
+	u16 cmd_reg_cache = (u16)offset | ((BE1 | BE0) << (offset & 0x02));
+
+	writew(cmd_reg_cache, ks->hw_addr_cmd);
+	value = readw(ks->hw_addr);
+
+	return value;
+}
+
+/**
+ * ks_wrreg16 - write 16bit register value to chip
+ * @ks: The chip information
+ * @offset: The register address
+ * @value: The value to write
+ *
+ */
+
+static void ks_wrreg16(struct ks_net *ks, int offset, u16 value)
+{
+	u16 cmd_reg_cache = (u16)offset | ((BE1 | BE0) << (offset & 0x02));
+	writew(cmd_reg_cache, ks->hw_addr_cmd);
+	writew(value, ks->hw_addr);
+}
+
+/**
+ * ks_inblk - read a block of data from QMU.
+ * @ks: The chip state
+ * @wptr: buffer address to save data
+ * @len: length in byte to read
+ *
+ */
+static inline void ks_inblk(struct ks_net *ks, u16 *wptr, u32 len)
+{
+	len >>= 1;
+	while (len--)
+		*wptr++ = (u16)readw(ks->hw_addr);
+}
+
+/**
+ * ks_outblk - write data to QMU.
+ * @ks: The chip information
+ * @wptr: buffer address
+ * @len: length in byte to write
+ *
+ */
+static inline void ks_outblk(struct ks_net *ks, u16 *wptr, u32 len)
+{
+	len >>= 1;
+	while (len--)
+		writew(*wptr++, ks->hw_addr);
+}
+
+void ks_enable_qmu(struct ks_net *ks)
+{
+	u16 w;
+
+	w = ks_rdreg16(ks, KS_TXCR);
+	/* Enables QMU Transmit (TXCR). */
+	ks_wrreg16(ks, KS_TXCR, w | TXCR_TXE);
+
+	/*
+	 * RX Frame Count Threshold Enable and Auto-Dequeue RXQ Frame
+	 * Enable
+	 */
+
+	w = ks_rdreg16(ks, KS_RXQCR);
+	ks_wrreg16(ks, KS_RXQCR, w | RXQCR_RXFCTE);
+
+	/* Enables QMU Receive (RXCR1). */
+	w = ks_rdreg16(ks, KS_RXCR1);
+	ks_wrreg16(ks, KS_RXCR1, w | RXCR1_RXE);
+}  /* ks_enable_qmu */
+
+static void ks_disable_qmu(struct ks_net *ks)
+{
+	u16	w;
+
+	w = ks_rdreg16(ks, KS_TXCR);
+
+	/* Disables QMU Transmit (TXCR). */
+	w  &= ~TXCR_TXE;
+	ks_wrreg16(ks, KS_TXCR, w);
+
+	/* Disables QMU Receive (RXCR1). */
+	w = ks_rdreg16(ks, KS_RXCR1);
+	w &= ~RXCR1_RXE ;
+	ks_wrreg16(ks, KS_RXCR1, w);
+
+}  /* ks_disable_qmu */
+
+/* MII interface controls */
+
+/**
+ * ks_phy_reg - convert MII register into a KS8851 register
+ * @reg: MII register number.
+ *
+ * Return the KS8851 register number for the corresponding MII PHY register
+ * if possible. Return zero if the MII register has no direct mapping to the
+ * KS8851 register set.
+ */
+static int ks_phy_reg(int reg)
+{
+	int retval;
+
+	switch (reg) {
+	case MII_BMCR:
+		retval = KS_P1MBCR;
+		break;
+	case MII_BMSR:
+		retval = KS_P1MBSR;
+		break;
+	case MII_PHYSID1:
+		retval = KS_PHY1ILR;
+		break;
+	case MII_PHYSID2:
+		retval = KS_PHY1IHR;
+		break;
+	case MII_ADVERTISE:
+		retval = KS_P1ANAR;
+		break;
+	case MII_LPA:
+		retval = KS_P1ANLPR;
+		break;
+	default:
+		retval = 0x0;
+	}
+
+	return retval;
+}
+
+/**
+ * ks_phy_read - MII interface PHY register read.
+ *
+ * This call reads data from the PHY register specified in @reg. Since the
+ * device does not support all the MII registers, the non-existent values
+ * are always returned as zero.
+ *
+ * We return zero for unsupported registers as the MII code does not check
+ * the value returned for any error status, and simply returns it to the
+ * caller. The mii-tool that the driver was tested with takes any -ve error
+ * as real PHY capabilities, thus displaying incorrect data to the user.
+ */
+static int ks_phy_read(struct mii_device *mdev, int addr, int reg)
+{
+	struct eth_device *edev = mdev->edev;
+	struct ks_net *priv = (struct ks_net *)edev->priv;
+	int ksreg;
+	int result;
+
+	ksreg = ks_phy_reg(reg);
+	if (!ksreg)
+		return 0x0;	/* no error return allowed, so use zero */
+
+	result = ks_rdreg16(priv, ksreg);
+
+	return result;
+}
+
+static int ks_phy_write(struct mii_device *mdev, int addr, int reg, int val)
+{
+	struct eth_device *edev = mdev->edev;
+	struct ks_net *priv = (struct ks_net *)edev->priv;
+	int ksreg;
+
+	ksreg = ks_phy_reg(reg);
+	if (ksreg)
+		ks_wrreg16(priv, ksreg, val);
+
+	return 0;
+}
+
+static int ks8851_get_ethaddr(struct eth_device *edev, unsigned char *adr)
+{
+	struct ks_net *priv = (struct ks_net *)edev->priv;
+
+	((u16 *) adr)[0] = be16_to_cpu(ks_rdreg16(priv, KS_MARH));
+	((u16 *) adr)[1] = be16_to_cpu(ks_rdreg16(priv, KS_MARM));
+	((u16 *) adr)[2] = be16_to_cpu(ks_rdreg16(priv, KS_MARL));
+
+	return 0;
+}
+
+static int ks8851_set_ethaddr(struct eth_device *edev, unsigned char *adr)
+{
+	struct ks_net *priv = (struct ks_net *)edev->priv;
+
+	ks_wrreg16(priv, KS_MARH, cpu_to_be16(((u16 *) adr)[0]));
+	ks_wrreg16(priv, KS_MARM, cpu_to_be16(((u16 *) adr)[1]));
+	ks_wrreg16(priv, KS_MARL, cpu_to_be16(((u16 *) adr)[2]));
+
+	return 0;
+}
+
+static void ks_soft_reset(struct ks_net *ks, unsigned op)
+{
+	/* Disable interrupt first */
+	ks_wrreg16(ks, KS_IER, 0x0000);
+	ks_wrreg16(ks, KS_GRR, op);
+	mdelay(10);	/* wait a short time to effect reset */
+	ks_wrreg16(ks, KS_GRR, 0);
+	mdelay(1);	/* wait for condition to clear */
+}
+
+/**
+ * ks_read_selftest - read the selftest memory info.
+ * @ks: The device state
+ *
+ * Read and check the TX/RX memory selftest information.
+ */
+static int ks_read_selftest(struct ks_net *ks)
+{
+	struct device_d *dev = &ks->edev.dev;
+	unsigned both_done = MBIR_TXMBF | MBIR_RXMBF;
+	int ret = 0;
+	unsigned rd;
+
+	rd = ks_rdreg16(ks, KS_MBIR);
+
+	if ((rd & both_done) != both_done) {
+		dev_err(dev, "Memory selftest not finished\n");
+		return 0;
+	}
+
+	if (rd & MBIR_TXMBFA) {
+		dev_err(dev, "TX memory selftest fails\n");
+		ret |= 1;
+	}
+
+	if (rd & MBIR_RXMBFA) {
+		dev_err(dev, "RX memory selftest fails\n");
+		ret |= 2;
+	}
+
+	dev_dbg(dev, "the selftest passes\n");
+	return ret;
+}
+
+static void ks_setup(struct ks_net *ks)
+{
+	u16	w;
+
+	/**
+	 * Configure QMU Transmit
+	 */
+
+	/* Setup Transmit Frame Data Pointer Auto-Increment (TXFDPR) */
+	ks_wrreg16(ks, KS_TXFDPR, TXFDPR_TXFPAI);
+
+	/* Setup Receive Frame Data Pointer Auto-Increment */
+	ks_wrreg16(ks, KS_RXFDPR, RXFDPR_RXFPAI);
+
+	/* Setup Receive Frame Threshold - 1 frame (RXFCTFC) */
+	ks_wrreg16(ks, KS_RXFCTR, 1 & RXFCTR_THRESHOLD_MASK);
+
+	/* Setup RxQ Command Control (RXQCR) */
+	ks_wrreg16(ks, KS_RXQCR, RXQCR_CMD_CNTL);
+
+	/**
+	 * set the force mode to half duplex, default is full duplex
+	 *  because if the auto-negotiation fails, most switch uses
+	 *  half-duplex.
+	 */
+
+	w = ks_rdreg16(ks, KS_P1MBCR);
+	w &= ~P1MBCR_FORCE_FDX;
+	ks_wrreg16(ks, KS_P1MBCR, w);
+
+	w = TXCR_TXFCE | TXCR_TXPE | TXCR_TXCRC | TXCR_TCGIP;
+	ks_wrreg16(ks, KS_TXCR, w);
+
+	w = RXCR1_RXFCE | RXCR1_RXBE | RXCR1_RXUE | RXCR1_RXME | RXCR1_RXIPFCC |
+		RXCR1_RXPAFMA;
+	ks_wrreg16(ks, KS_RXCR1, w);
+}  /*ks_setup */
+
+static int ks8851_rx_frame(struct ks_net *ks)
+{
+	struct device_d *dev = &ks->edev.dev;
+	u16 *rdptr = (u16 *) NetRxPackets[0];
+	u16 RxStatus, RxLen = 0;
+	u16 tmp_rxqcr;
+
+	dev_dbg(dev, "receiving packet\n");
+
+	RxStatus = ks_rdreg16(ks, KS_RXFHSR);
+	RxLen = ks_rdreg16(ks, KS_RXFHBCR) & RXFHBCR_CNT_MASK;
+	dev_dbg(dev, "%s RxLen %d (%d) RxStatus 0x%04x\n",
+		__func__, RxLen, ALIGN(RxLen, 4), RxStatus);
+
+	if (RxLen > PKTSIZE)
+		dev_err(dev, "rx length too big\n");
+
+	/* reset Frame pointer */
+	ks_wrreg16(ks, KS_RXFDPR, RXFDPR_RXFPAI);
+
+	tmp_rxqcr = ks_rdreg16(ks, KS_RXQCR);
+	ks_wrreg16(ks, KS_RXQCR, tmp_rxqcr | RXQCR_SDA);
+	/* read 2 bytes for dummy, 2 for status, 2 for len*/
+	ks_inblk(ks, rdptr, 2 + 2 + 2);
+	ks_inblk(ks, rdptr, ALIGN(RxLen, 4));
+	ks_wrreg16(ks, KS_RXQCR, tmp_rxqcr);
+
+	if (RxStatus & RXFSHR_RXFV) {
+		/* Pass to upper layer */
+		dev_dbg(dev, "passing packet to upper layer\n\n");
+		net_receive(NetRxPackets[0], RxLen);
+		return RxLen;
+	} else if (RxStatus & RXFSHR_ERR) {
+		dev_err(dev, "RxStatus error 0x%04x\n", RxStatus & RXFSHR_ERR);
+		if (RxStatus & RXFSHR_RXICMPFCS)
+			dev_dbg(dev, "ICMP frame checksum field is incorrect\n");
+		if (RxStatus & RXFSHR_RXIPFCS)
+			dev_dbg(dev, "IP frame checksum field is incorrect\n");
+		if (RxStatus & RXFSHR_RXTCPFCS)
+			dev_dbg(dev, "TCP frame checksum field is incorrect\n");
+		if (RxStatus & RXFSHR_RXCE)
+			dev_dbg(dev, "CRC Error\n");
+		if (RxStatus & RXFSHR_RXRF)
+			dev_dbg(dev, "frame collision\n");
+		if (RxStatus & RXFSHR_RXFTL)
+			dev_dbg(dev, "frame too long\n");
+		if (RxStatus & RXFSHR_RXMR)
+			dev_dbg(dev, "MII symbol error\n");
+	} else
+		dev_err(dev, "other RxStatus error 0x%04x\n", RxStatus);
+	return 0;
+}
+
+static int ks8851_eth_rx(struct eth_device *edev)
+{
+	struct ks_net *ks = (struct ks_net *)edev->priv;
+	struct device_d *dev = &edev->dev;
+	u16 frame_cnt;
+
+	if (!(ks_rdreg16(ks, KS_ISR) & IRQ_RXI))
+		return 0;
+	ks_wrreg16(ks, KS_ISR, IRQ_RXI);
+
+	frame_cnt = RXFCTR_RXFC_GET(ks_rdreg16(ks, KS_RXFCTR));
+
+	while (frame_cnt--) {
+		dev_dbg(dev, "%s frame %d\n", __func__, frame_cnt);
+		ks8851_rx_frame(ks);
+	}
+
+	return 0;
+}
+
+static int ks8851_eth_send(struct eth_device *edev,
+		void *packet, int length)
+{
+	struct ks_net *ks = (struct ks_net *)edev->priv;
+	struct device_d *dev = &edev->dev;
+	uint64_t tmo;
+	u16 tmp_rxqcr;
+
+	dev_dbg(dev, "%s: length: %d (%d)\n", __func__, length, ALIGN(length, 4));
+
+	/* Enable TXQ write access */
+	tmp_rxqcr = ks_rdreg16(ks, KS_RXQCR);
+	ks_wrreg16(ks, KS_RXQCR, tmp_rxqcr | RXQCR_SDA);
+
+	/* write status/lenth info */
+	writew(0, ks->hw_addr);
+	writew(cpu_to_le16(length), ks->hw_addr);
+
+	/* write pkt data */
+	ks_outblk(ks, (u16 *) packet, ALIGN(length, 4));
+	ks_wrreg16(ks, KS_RXQCR, tmp_rxqcr);
+
+	/* (move the pkt from TX buffer into TXQ) */
+	ks_wrreg16(ks, KS_TXQCR, TXQCR_METFE);
+	/* wait for transmit done */
+	tmo = get_time_ns();
+	while (ks_rdreg16(ks, KS_TXQCR) & TXQCR_METFE) {
+		if (is_timeout(tmo, 5 * SECOND)) {
+			dev_err(dev, "transmission timeout\n");
+			break;
+		}
+	}
+
+	dev_dbg(dev, "transmit done\n\n");
+	return 0;
+}
+
+static int ks8851_eth_open(struct eth_device *edev)
+{
+	struct ks_net *priv = (struct ks_net *)edev->priv;
+	struct device_d *dev = &edev->dev;
+
+	ks_enable_qmu(priv);
+
+	miidev_wait_aneg(&priv->miidev);
+	miidev_print_status(&priv->miidev);
+
+	dev_dbg(dev, "eth_open\n");
+
+	return 0;
+}
+
+static int ks8851_init_dev(struct eth_device *edev)
+{
+	struct ks_net *priv = (struct ks_net *)edev->priv;
+
+	miidev_restart_aneg(&priv->miidev);
+	return 0;
+}
+
+static void ks8851_eth_halt(struct eth_device *edev)
+{
+	struct ks_net *priv = (struct ks_net *)edev->priv;
+	struct device_d *dev = &edev->dev;
+
+	ks_disable_qmu(priv);
+
+	dev_dbg(dev, "eth_halt\n");
+}
+
+static int ks8851_probe(struct device_d *dev)
+{
+	struct eth_device *edev;
+	struct ks_net *ks;
+	u16 id;
+
+	ks = xzalloc(sizeof(struct ks_net));
+	edev = &ks->edev;
+	dev->type_data = edev;
+	edev->priv = ks;
+
+	if (dev->num_resources < 2) {
+		dev_err(dev, "ks8851: need 2 resources addr and addr_cmd");
+		return -ENODEV;
+	}
+
+	ks->hw_addr = dev_request_mem_region(dev, 0);
+	ks->hw_addr_cmd = dev_request_mem_region(dev, 1);
+	ks->bus_width = dev->resource[0].flags & IORESOURCE_MEM_TYPE_MASK;
+
+	edev->init = ks8851_init_dev;
+	edev->open = ks8851_eth_open;
+	edev->send = ks8851_eth_send;
+	edev->recv = ks8851_eth_rx;
+	edev->halt = ks8851_eth_halt;
+	edev->set_ethaddr = ks8851_set_ethaddr;
+	edev->get_ethaddr = ks8851_get_ethaddr;
+	edev->parent = dev;
+
+	/* setup mii state */
+	ks->miidev.read = ks_phy_read;
+	ks->miidev.write = ks_phy_write;
+	ks->miidev.address = 1;
+	ks->miidev.flags = 0;
+	ks->miidev.edev = edev;
+	ks->miidev.parent = dev;
+
+	/* simple check for a valid chip being connected to the bus */
+
+	id = ks_rdreg16(ks, KS_CIDER);
+
+	if ((id & ~CIDER_REV_MASK) != CIDER_ID) {
+		dev_err(dev, "failed to read device ID\n");
+		return -ENODEV;
+	}
+	dev_dbg(dev, "Found chip, family: 0x%x, id: 0x%x, rev: 0x%x\n",
+	     (id >> 8) & 0xff, (id >> 4) & 0xf, (id >> 1) & 0x7);
+
+	if (ks_read_selftest(ks)) {
+		dev_err(dev, "failed to read device ID\n");
+		return -ENODEV;
+	}
+
+	ks_soft_reset(ks, GRR_GSR);
+	ks_setup(ks);
+
+	mii_register(&ks->miidev);
+	eth_register(edev);
+	dev_dbg(dev, "%s MARL 0x%04x MARM 0x%04x MARH 0x%04x\n", __func__,
+		ks_rdreg16(ks, KS_MARL), ks_rdreg16(ks, KS_MARM),
+		ks_rdreg16(ks, KS_MARH));
+
+	return 0;
+}
+
+static struct driver_d ks8851_driver = {
+	.name  = "ks8851_mll",
+	.probe = ks8851_probe,
+};
+
+static int ks8851_init(void)
+{
+	register_driver(&ks8851_driver);
+	return 0;
+}
+
+device_initcall(ks8851_init);
+
diff --git a/include/driver.h b/include/driver.h
index f1964b7..3762027 100644
--- a/include/driver.h
+++ b/include/driver.h
@@ -247,6 +247,17 @@ static inline struct device_d *add_usb_ehci_device(int id, resource_size_t hccr,
 }
 #endif
 
+#ifdef CONFIG_DRIVER_NET_KS8851_MLL
+struct device_d *add_ks8851_device(int id, resource_size_t addr,
+		resource_size_t addr_cmd, int flags, void *pdata);
+#else
+static inline struct device_d *add_ks8851_device(int id, resource_size_t addr,
+		resource_size_t addr_cmd, int flags, void *pdata)
+{
+	return NULL;
+}
+#endif
+
 static inline struct device_d *add_generic_usb_ehci_device(int id,
 		resource_size_t base, void *pdata)
 {
-- 
1.7.0.4




More information about the barebox mailing list