* [PATCH] NET: Add support for ks8851_mll @ 2012-03-07 12:17 Jan Weitzel 2012-03-08 20:18 ` Sascha Hauer 0 siblings, 1 reply; 4+ messages in thread From: Jan Weitzel @ 2012-03-07 12:17 UTC (permalink / raw) To: barebox Add support for KS8851 16bit MLL chip from Micrel Inc. Signed-off-by: Jan Weitzel <j.weitzel@phytec.de> --- 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@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@lists.infradead.org http://lists.infradead.org/mailman/listinfo/barebox ^ permalink raw reply [flat|nested] 4+ messages in thread
* Re: [PATCH] NET: Add support for ks8851_mll 2012-03-07 12:17 [PATCH] NET: Add support for ks8851_mll Jan Weitzel @ 2012-03-08 20:18 ` Sascha Hauer 2012-03-09 9:24 ` [PATCH v2] " Jan Weitzel 0 siblings, 1 reply; 4+ messages in thread From: Sascha Hauer @ 2012-03-08 20:18 UTC (permalink / raw) To: Jan Weitzel; +Cc: barebox 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@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@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@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 | _______________________________________________ barebox mailing list barebox@lists.infradead.org http://lists.infradead.org/mailman/listinfo/barebox ^ permalink raw reply [flat|nested] 4+ messages in thread
* [PATCH v2] NET: Add support for ks8851_mll 2012-03-08 20:18 ` Sascha Hauer @ 2012-03-09 9:24 ` Jan Weitzel 2012-03-13 12:36 ` Sascha Hauer 0 siblings, 1 reply; 4+ messages in thread From: Jan Weitzel @ 2012-03-09 9:24 UTC (permalink / raw) To: barebox Add support for KS8851 16bit MLL chip from Micrel Inc. Signed-off-by: Jan Weitzel <j.weitzel@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@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@lists.infradead.org http://lists.infradead.org/mailman/listinfo/barebox ^ permalink raw reply [flat|nested] 4+ messages in thread
* Re: [PATCH v2] NET: Add support for ks8851_mll 2012-03-09 9:24 ` [PATCH v2] " Jan Weitzel @ 2012-03-13 12:36 ` Sascha Hauer 0 siblings, 0 replies; 4+ messages in thread From: Sascha Hauer @ 2012-03-13 12:36 UTC (permalink / raw) To: Jan Weitzel; +Cc: barebox 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@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@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@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 | _______________________________________________ barebox mailing list barebox@lists.infradead.org http://lists.infradead.org/mailman/listinfo/barebox ^ permalink raw reply [flat|nested] 4+ messages in thread
end of thread, other threads:[~2012-03-13 12:36 UTC | newest] Thread overview: 4+ messages (download: mbox.gz / follow: Atom feed) -- links below jump to the message on this page -- 2012-03-07 12:17 [PATCH] NET: Add support for ks8851_mll Jan Weitzel 2012-03-08 20:18 ` Sascha Hauer 2012-03-09 9:24 ` [PATCH v2] " Jan Weitzel 2012-03-13 12:36 ` Sascha Hauer
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox