[PATCH v2] NET: Add support for ks8851_mll

Sascha Hauer s.hauer at pengutronix.de
Tue Mar 13 08:36:04 EDT 2012


On Fri, Mar 09, 2012 at 10:24:09AM +0100, Jan Weitzel wrote:
> Add support for KS8851 16bit MLL chip from Micrel Inc.
> 
> Signed-off-by: Jan Weitzel <j.weitzel at phytec.de>

Applied, thanks

Sascha

> ---
> 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
> 
> 
> _______________________________________________
> barebox mailing list
> barebox at lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/barebox
> 

-- 
Pengutronix e.K.                           |                             |
Industrial Linux Solutions                 | http://www.pengutronix.de/  |
Peiner Str. 6-8, 31137 Hildesheim, Germany | Phone: +49-5121-206917-0    |
Amtsgericht Hildesheim, HRA 2686           | Fax:   +49-5121-206917-5555 |



More information about the barebox mailing list