From mboxrd@z Thu Jan 1 00:00:00 1970 Return-path: Received: from mail.visioncatalog.de ([217.6.246.34] helo=root.phytec.de) by casper.infradead.org with esmtp (Exim 4.76 #1 (Red Hat Linux)) id 1S5w3d-0007Lz-4L for barebox@lists.infradead.org; Fri, 09 Mar 2012 09:24:20 +0000 Received: from idefix.phytec.de (idefix.phytec.de [172.16.0.10]) by root.phytec.de (Postfix) with ESMTP id C5743A30A1 for ; Thu, 8 Mar 2012 10:19:03 +0100 (CET) From: Jan Weitzel Date: Fri, 9 Mar 2012 10:24:09 +0100 Message-Id: <1331285049-9736-1-git-send-email-j.weitzel@phytec.de> In-Reply-To: <20120308201853.GS3852@pengutronix.de> References: <20120308201853.GS3852@pengutronix.de> List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit Sender: barebox-bounces@lists.infradead.org Errors-To: barebox-bounces+u.kleine-koenig=pengutronix.de@lists.infradead.org Subject: [PATCH v2] NET: Add support for ks8851_mll To: barebox@lists.infradead.org Add support for KS8851 16bit MLL chip from Micrel Inc. Signed-off-by: Jan Weitzel --- 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 + * 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 +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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