From mboxrd@z Thu Jan 1 00:00:00 1970 Return-path: Received: from metis.ext.pengutronix.de ([2001:6f8:1178:4:290:27ff:fe1d:cc33]) by casper.infradead.org with esmtps (Exim 4.76 #1 (Red Hat Linux)) id 1S7QxZ-00040Q-RO for barebox@lists.infradead.org; Tue, 13 Mar 2012 12:36:18 +0000 Date: Tue, 13 Mar 2012 13:36:04 +0100 From: Sascha Hauer Message-ID: <20120313123604.GB3852@pengutronix.de> References: <20120308201853.GS3852@pengutronix.de> <1331285049-9736-1-git-send-email-j.weitzel@phytec.de> MIME-Version: 1.0 Content-Disposition: inline In-Reply-To: <1331285049-9736-1-git-send-email-j.weitzel@phytec.de> List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , 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: Re: [PATCH v2] NET: Add support for ks8851_mll To: Jan Weitzel Cc: barebox@lists.infradead.org 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 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 > + * 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 > -- 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