[PATCH] NET: Add support for ks8851_mll
Sascha Hauer
s.hauer at pengutronix.de
Thu Mar 8 15:18:53 EST 2012
Hi Jan,
On Wed, Mar 07, 2012 at 01:17:33PM +0100, Jan Weitzel wrote:
> Add support for KS8851 16bit MLL chip from Micrel Inc.
>
> Signed-off-by: Jan Weitzel <j.weitzel at phytec.de>
Looks mostly good. Please use dev_* instead of printf/debug and this is
ready to apply.
Sascha
> ---
> drivers/base/resource.c | 33 ++
> drivers/net/Kconfig | 7 +
> drivers/net/Makefile | 1 +
> drivers/net/ks8851_mll.c | 902 ++++++++++++++++++++++++++++++++++++++++++++++
> include/driver.h | 11 +
> 5 files changed, 954 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..8387950
> --- /dev/null
> +++ b/drivers/net/ks8851_mll.c
> @@ -0,0 +1,902 @@
> +/**
> + * 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 <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;
> + }
> +
> + debug("%s lookup 0x%.2x -> 0x%.2x\n", __func__, reg, retval);
> +
> + 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)
> +{
> + unsigned both_done = MBIR_TXMBF | MBIR_RXMBF;
> + int ret = 0;
> + unsigned rd;
> +
> + rd = ks_rdreg16(ks, KS_MBIR);
> +
> + if ((rd & both_done) != both_done) {
> + printf("Memory selftest not finished\n");
> + return 0;
> + }
> +
> + if (rd & MBIR_TXMBFA) {
> + printf("TX memory selftest fails\n");
> + ret |= 1;
> + }
> +
> + if (rd & MBIR_RXMBFA) {
> + printf("RX memory selftest fails\n");
> + ret |= 2;
> + }
> +
> + debug("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)
> +{
> + u16 *rdptr = (u16 *) NetRxPackets[0];
> + u16 RxStatus, RxLen = 0;
> + u16 tmp_rxqcr;
> + int i;
> +
> + debug("receiving packet\n");
> +
> + RxStatus = ks_rdreg16(ks, KS_RXFHSR);
> + RxLen = ks_rdreg16(ks, KS_RXFHBCR) & RXFHBCR_CNT_MASK;
> + debug("%s RxLen %d (%d) RxStatus 0x%04x",
> + __func__, RxLen, ALIGN(RxLen, 4), RxStatus);
> +
> + if (RxLen > PKTSIZE)
> + printf("\nrx 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);
> +
> + for (i = 0; i < RxLen; i++) {
> + if (i % 16 == 0)
> + debug("\nReceive: %02x: ", i);
> + debug("%02x ", ((unsigned char *) rdptr)[i]);
> + } debug("\n");
> +
> + if (RxStatus & RXFSHR_RXFV) {
> + /* Pass to upper layer */
> + debug("passing packet to upper layer\n\n");
> + net_receive(NetRxPackets[0], RxLen);
> + return RxLen;
> + } else if (RxStatus & RXFSHR_ERR) {
> + printf("RxStatus error 0x%04x\n", RxStatus & RXFSHR_ERR);
> + if (RxStatus & RXFSHR_RXICMPFCS)
> + debug("ICMP frame checksum field is incorrect\n");
> + if (RxStatus & RXFSHR_RXIPFCS)
> + debug("IP frame checksum field is incorrect\n");
> + if (RxStatus & RXFSHR_RXTCPFCS)
> + debug("TCP frame checksum field is incorrect\n");
> + if (RxStatus & RXFSHR_RXCE)
> + debug("CRC Error\n");
> + if (RxStatus & RXFSHR_RXRF)
> + debug("frame collision\n");
> + if (RxStatus & RXFSHR_RXFTL)
> + debug("frame too long\n");
> + if (RxStatus & RXFSHR_RXMR)
> + debug("MII symbol error\n");
> + } else
> + printf("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;
> + 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--) {
> + debug("%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;
> + uint64_t tmo;
> + u16 tmp_rxqcr;
> + int i;
> +
> + debug("eth_send: length: %d (%d)", length, ALIGN(length, 4));
> +
> + for (i = 0; i < length; i++) {
> + if (i % 16 == 0)
> + debug("\nSend: %02x: ", i);
> + debug("%02x ", ((unsigned char *) packet)[i]);
> + } debug("\n");
> +
> + /* 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)) {
> + printf("transmission timeout\n");
> + break;
> + }
> + }
> +
> + debug("transmit done\n\n");
> + return 0;
> +}
> +
> +static int ks8851_eth_open(struct eth_device *edev)
> +{
> + struct ks_net *priv = (struct ks_net *)edev->priv;
> +
> + ks_enable_qmu(priv);
> +
> + miidev_wait_aneg(&priv->miidev);
> + miidev_print_status(&priv->miidev);
> +
> + debug("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;
> +
> + ks_disable_qmu(priv);
> +
> + debug("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) {
> + printf("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) {
> + printf("failed to read device ID\n");
> + return -ENODEV;
> + }
> + debug("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)) {
> + printf("failed to read device ID\n");
> + return -ENODEV;
> + }
> +
> + ks_soft_reset(ks, GRR_GSR);
> + ks_setup(ks);
> +
> + mii_register(&ks->miidev);
> + eth_register(edev);
> + debug("%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