From 9bdcf336d0c061e77f4c45c7b2bc32e3ed6b57e3 Mon Sep 17 00:00:00 2001 From: Manuel Lauss Date: Sun, 4 Oct 2009 14:55:24 +0200 Subject: MIPS: Alchemy: devboard register abstraction All Alchemy development boards have external CPLDs with a few registers in them. They all share an identical register layout with only a few minor differences (except the PB1000) in bit functions and base addresses. This patch - adds a primitive facility to initialize and use these external registers, - replaces all occurrences of bcsr->xxx accesses with calls to the new functions (the pb1200 cascade irq handling code is special). - collects BCSR register information scattered throughout the board headers in a central place. Signed-off-by: Manuel Lauss Signed-off-by: Ralf Baechle --- drivers/mtd/nand/au1550nd.c | 4 ++- drivers/net/irda/au1k_ir.c | 14 +++----- drivers/pcmcia/au1000_db1x00.c | 76 ++++++++++++++++++++++-------------------- 3 files changed, 47 insertions(+), 47 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/au1550nd.c b/drivers/mtd/nand/au1550nd.c index 92c334f..43d46e4 100644 --- a/drivers/mtd/nand/au1550nd.c +++ b/drivers/mtd/nand/au1550nd.c @@ -19,6 +19,7 @@ #include #include +#include /* * MTD structure for NAND controller @@ -475,7 +476,8 @@ static int __init au1xxx_nand_init(void) /* set gpio206 high */ au_writel(au_readl(GPIO2_DIR) & ~(1 << 6), GPIO2_DIR); - boot_swapboot = (au_readl(MEM_STSTAT) & (0x7 << 1)) | ((bcsr->status >> 6) & 0x1); + boot_swapboot = (au_readl(MEM_STSTAT) & (0x7 << 1)) | ((bcsr_read(BCSR_STATUS) >> 6) & 0x1); + switch (boot_swapboot) { case 0: case 2: diff --git a/drivers/net/irda/au1k_ir.c b/drivers/net/irda/au1k_ir.c index 9b2eebd..b5cbd39 100644 --- a/drivers/net/irda/au1k_ir.c +++ b/drivers/net/irda/au1k_ir.c @@ -36,6 +36,7 @@ #include #elif defined(CONFIG_MIPS_DB1000) || defined(CONFIG_MIPS_DB1100) #include +#include #else #error au1k_ir: unsupported board #endif @@ -66,10 +67,6 @@ static char version[] __devinitdata = #define RUN_AT(x) (jiffies + (x)) -#if defined(CONFIG_MIPS_DB1000) || defined(CONFIG_MIPS_DB1100) -static BCSR * const bcsr = (BCSR *)0xAE000000; -#endif - static DEFINE_SPINLOCK(ir_lock); /* @@ -282,9 +279,8 @@ static int au1k_irda_net_init(struct net_device *dev) #if defined(CONFIG_MIPS_DB1000) || defined(CONFIG_MIPS_DB1100) /* power on */ - bcsr->resets &= ~BCSR_RESETS_IRDA_MODE_MASK; - bcsr->resets |= BCSR_RESETS_IRDA_MODE_FULL; - au_sync(); + bcsr_mod(BCSR_RESETS, BCSR_RESETS_IRDA_MODE_MASK, + BCSR_RESETS_IRDA_MODE_FULL); #endif return 0; @@ -720,14 +716,14 @@ au1k_irda_set_speed(struct net_device *dev, int speed) if (speed == 4000000) { #if defined(CONFIG_MIPS_DB1000) || defined(CONFIG_MIPS_DB1100) - bcsr->resets |= BCSR_RESETS_FIR_SEL; + bcsr_mod(BCSR_RESETS, 0, BCSR_RESETS_FIR_SEL); #else /* Pb1000 and Pb1100 */ writel(1<<13, CPLD_AUX1); #endif } else { #if defined(CONFIG_MIPS_DB1000) || defined(CONFIG_MIPS_DB1100) - bcsr->resets &= ~BCSR_RESETS_FIR_SEL; + bcsr_mod(BCSR_RESETS, BCSR_RESETS_FIR_SEL, 0); #else /* Pb1000 and Pb1100 */ writel(readl(CPLD_AUX1) & ~(1<<13), CPLD_AUX1); #endif diff --git a/drivers/pcmcia/au1000_db1x00.c b/drivers/pcmcia/au1000_db1x00.c index c78d77f..3fdd664 100644 --- a/drivers/pcmcia/au1000_db1x00.c +++ b/drivers/pcmcia/au1000_db1x00.c @@ -47,9 +47,9 @@ #include #else #include - static BCSR * const bcsr = (BCSR *)BCSR_KSEG1_ADDR; #endif +#include #include "au1000_generic.h" #if 0 @@ -76,8 +76,8 @@ static int db1x00_pcmcia_hw_init(struct au1000_pcmcia_socket *skt) static void db1x00_pcmcia_shutdown(struct au1000_pcmcia_socket *skt) { - bcsr->pcmcia = 0; /* turn off power */ - au_sync_delay(2); + bcsr_write(BCSR_PCMCIA, 0); /* turn off power */ + msleep(2); } static void @@ -93,19 +93,19 @@ db1x00_pcmcia_socket_state(struct au1000_pcmcia_socket *skt, struct pcmcia_state switch (skt->nr) { case 0: - vs = bcsr->status & 0x3; + vs = bcsr_read(BCSR_STATUS) & 0x3; #if defined(CONFIG_MIPS_DB1200) || defined(CONFIG_MIPS_PB1200) inserted = BOARD_CARD_INSERTED(0); #else - inserted = !(bcsr->status & (1<<4)); + inserted = !(bcsr_read(BCSR_STATUS) & (1 << 4)); #endif break; case 1: - vs = (bcsr->status & 0xC)>>2; + vs = (bcsr_read(BCSR_STATUS) & 0xC) >> 2; #if defined(CONFIG_MIPS_DB1200) || defined(CONFIG_MIPS_PB1200) inserted = BOARD_CARD_INSERTED(1); #else - inserted = !(bcsr->status & (1<<5)); + inserted = !(bcsr_read(BCSR_STATUS) & (1<<5)); #endif break; default:/* should never happen */ @@ -114,7 +114,7 @@ db1x00_pcmcia_socket_state(struct au1000_pcmcia_socket *skt, struct pcmcia_state if (inserted) debug("db1x00 socket %d: inserted %d, vs %d pcmcia %x\n", - skt->nr, inserted, vs, bcsr->pcmcia); + skt->nr, inserted, vs, bcsr_read(BCSR_PCMCIA)); if (inserted) { switch (vs) { @@ -136,19 +136,21 @@ db1x00_pcmcia_socket_state(struct au1000_pcmcia_socket *skt, struct pcmcia_state /* if the card was previously inserted and then ejected, * we should turn off power to it */ - if ((skt->nr == 0) && (bcsr->pcmcia & BCSR_PCMCIA_PC0RST)) { - bcsr->pcmcia &= ~(BCSR_PCMCIA_PC0RST | - BCSR_PCMCIA_PC0DRVEN | - BCSR_PCMCIA_PC0VPP | - BCSR_PCMCIA_PC0VCC); - au_sync_delay(10); + if ((skt->nr == 0) && + (bcsr_read(BCSR_PCMCIA) & BCSR_PCMCIA_PC0RST)) { + bcsr_mod(BCSR_PCMCIA, BCSR_PCMCIA_PC0RST | + BCSR_PCMCIA_PC0DRVEN | + BCSR_PCMCIA_PC0VPP | + BCSR_PCMCIA_PC0VCC, 0); + msleep(10); } - else if ((skt->nr == 1) && bcsr->pcmcia & BCSR_PCMCIA_PC1RST) { - bcsr->pcmcia &= ~(BCSR_PCMCIA_PC1RST | - BCSR_PCMCIA_PC1DRVEN | - BCSR_PCMCIA_PC1VPP | - BCSR_PCMCIA_PC1VCC); - au_sync_delay(10); + else if ((skt->nr == 1) && + (bcsr_read(BCSR_PCMCIA) & BCSR_PCMCIA_PC1RST)) { + bcsr_mod(BCSR_PCMCIA, BCSR_PCMCIA_PC1RST | + BCSR_PCMCIA_PC1DRVEN | + BCSR_PCMCIA_PC1VPP | + BCSR_PCMCIA_PC1VCC, 0); + msleep(10); } } @@ -171,7 +173,7 @@ db1x00_pcmcia_configure_socket(struct au1000_pcmcia_socket *skt, struct socket_s * initializing a socket not to wipe out the settings of the * other socket. */ - pwr = bcsr->pcmcia; + pwr = bcsr_read(BCSR_PCMCIA); pwr &= ~(0xf << sock*8); /* clear voltage settings */ state->Vpp = 0; @@ -228,37 +230,37 @@ db1x00_pcmcia_configure_socket(struct au1000_pcmcia_socket *skt, struct socket_s break; } - bcsr->pcmcia = pwr; - au_sync_delay(300); + bcsr_write(BCSR_PCMCIA, pwr); + msleep(300); if (sock == 0) { if (!(state->flags & SS_RESET)) { pwr |= BCSR_PCMCIA_PC0DRVEN; - bcsr->pcmcia = pwr; - au_sync_delay(300); + bcsr_write(BCSR_PCMCIA, pwr); + msleep(300); pwr |= BCSR_PCMCIA_PC0RST; - bcsr->pcmcia = pwr; - au_sync_delay(100); + bcsr_write(BCSR_PCMCIA, pwr); + msleep(100); } else { pwr &= ~(BCSR_PCMCIA_PC0RST | BCSR_PCMCIA_PC0DRVEN); - bcsr->pcmcia = pwr; - au_sync_delay(100); + bcsr_write(BCSR_PCMCIA, pwr); + msleep(100); } } else { if (!(state->flags & SS_RESET)) { pwr |= BCSR_PCMCIA_PC1DRVEN; - bcsr->pcmcia = pwr; - au_sync_delay(300); + bcsr_write(BCSR_PCMCIA, pwr); + msleep(300); pwr |= BCSR_PCMCIA_PC1RST; - bcsr->pcmcia = pwr; - au_sync_delay(100); + bcsr_write(BCSR_PCMCIA, pwr); + msleep(100); } else { pwr &= ~(BCSR_PCMCIA_PC1RST | BCSR_PCMCIA_PC1DRVEN); - bcsr->pcmcia = pwr; - au_sync_delay(100); + bcsr_write(BCSR_PCMCIA, pwr); + msleep(100); } } return 0; @@ -298,8 +300,8 @@ struct pcmcia_low_level db1x00_pcmcia_ops = { int au1x_board_init(struct device *dev) { int ret = -ENODEV; - bcsr->pcmcia = 0; /* turn off power, if it's not already off */ - au_sync_delay(2); + bcsr_write(BCSR_PCMCIA, 0); /* turn off power, if it's not already off */ + msleep(2); ret = au1x00_pcmcia_socket_probe(dev, &db1x00_pcmcia_ops, 0, 2); return ret; } -- cgit v1.1 From 66213b3ccfc770704025ce9465fa3aaedde21b55 Mon Sep 17 00:00:00 2001 From: Manuel Lauss Date: Sun, 4 Oct 2009 14:55:27 +0200 Subject: MIPS: PCMCIA: new socket driver for Au1000 demoboards. New PCMCIA socket driver for all Db/Pb1xxx boards (except Pb1000), which replaces au1000_db1x00.c and (most of) au1000_pb1x00.c. Notable improvements: - supports Db1000, DB/PB1100/1500/1550/1200. - support for carddetect and statuschange IRQs. - pcmcia socket mem/io/attr areas and irqs passed through platform resource information. - doesn't freeze system during card insertion/ejection like the one it replaces. - boardtype is automatically detected using BCSR ID register. Run-tested on the DB1200. Cc: Linux-PCMCIA Signed-off-by: Manuel Lauss Signed-off-by: Ralf Baechle --- drivers/pcmcia/Kconfig | 11 + drivers/pcmcia/Makefile | 9 +- drivers/pcmcia/au1000_db1x00.c | 307 -------------------- drivers/pcmcia/au1000_generic.h | 12 +- drivers/pcmcia/au1000_pb1x00.c | 119 +------- drivers/pcmcia/db1xxx_ss.c | 630 ++++++++++++++++++++++++++++++++++++++++ 6 files changed, 644 insertions(+), 444 deletions(-) delete mode 100644 drivers/pcmcia/au1000_db1x00.c create mode 100644 drivers/pcmcia/db1xxx_ss.c (limited to 'drivers') diff --git a/drivers/pcmcia/Kconfig b/drivers/pcmcia/Kconfig index 9f3adbd..7c16d35 100644 --- a/drivers/pcmcia/Kconfig +++ b/drivers/pcmcia/Kconfig @@ -174,6 +174,17 @@ config PCMCIA_AU1X00 tristate "Au1x00 pcmcia support" depends on SOC_AU1X00 && PCMCIA +config PCMCIA_ALCHEMY_DEVBOARD + tristate "Alchemy Db/Pb1xxx PCMCIA socket services" + depends on SOC_AU1X00 && PCMCIA + select 64BIT_PHYS_ADDR + help + Enable this driver of you want PCMCIA support on your Alchemy + Db1000, Db/Pb1100, Db/Pb1500, Db/Pb1550, Db/Pb1200 board. + NOT suitable for the PB1000! + + This driver is also available as a module called db1xxx_ss.ko + config PCMCIA_BCM63XX tristate "bcm63xx pcmcia support" depends on BCM63XX && PCMCIA diff --git a/drivers/pcmcia/Makefile b/drivers/pcmcia/Makefile index 83ff802..f128d71 100644 --- a/drivers/pcmcia/Makefile +++ b/drivers/pcmcia/Makefile @@ -35,17 +35,10 @@ obj-$(CONFIG_OMAP_CF) += omap_cf.o obj-$(CONFIG_BFIN_CFPCMCIA) += bfin_cf_pcmcia.o obj-$(CONFIG_AT91_CF) += at91_cf.o obj-$(CONFIG_ELECTRA_CF) += electra_cf.o +obj-$(CONFIG_PCMCIA_ALCHEMY_DEVBOARD) += db1xxx_ss.o au1x00_ss-y += au1000_generic.o au1x00_ss-$(CONFIG_MIPS_PB1000) += au1000_pb1x00.o -au1x00_ss-$(CONFIG_MIPS_PB1100) += au1000_pb1x00.o -au1x00_ss-$(CONFIG_MIPS_PB1200) += au1000_db1x00.o -au1x00_ss-$(CONFIG_MIPS_PB1500) += au1000_pb1x00.o -au1x00_ss-$(CONFIG_MIPS_DB1000) += au1000_db1x00.o -au1x00_ss-$(CONFIG_MIPS_DB1100) += au1000_db1x00.o -au1x00_ss-$(CONFIG_MIPS_DB1200) += au1000_db1x00.o -au1x00_ss-$(CONFIG_MIPS_DB1500) += au1000_db1x00.o -au1x00_ss-$(CONFIG_MIPS_DB1550) += au1000_db1x00.o au1x00_ss-$(CONFIG_MIPS_XXS1500) += au1000_xxs1500.o sa1111_cs-y += sa1111_generic.o diff --git a/drivers/pcmcia/au1000_db1x00.c b/drivers/pcmcia/au1000_db1x00.c deleted file mode 100644 index 3fdd664..0000000 --- a/drivers/pcmcia/au1000_db1x00.c +++ /dev/null @@ -1,307 +0,0 @@ -/* - * - * Alchemy Semi Db1x00 boards specific pcmcia routines. - * - * Copyright 2002 MontaVista Software Inc. - * Author: MontaVista Software, Inc. - * ppopov@mvista.com or source@mvista.com - * - * Copyright 2004 Pete Popov, updated the driver to 2.6. - * Followed the sa11xx API and largely copied many of the hardware - * independent functions. - * - * ######################################################################## - * - * This program is free software; you can distribute 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 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., - * 59 Temple Place - Suite 330, Boston MA 02111-1307, USA. - * - * ######################################################################## - * - * - */ - -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#if defined(CONFIG_MIPS_DB1200) - #include -#elif defined(CONFIG_MIPS_PB1200) - #include -#else - #include -#endif - -#include -#include "au1000_generic.h" - -#if 0 -#define debug(x,args...) printk(KERN_DEBUG "%s: " x, __func__ , ##args) -#else -#define debug(x,args...) -#endif - - -struct au1000_pcmcia_socket au1000_pcmcia_socket[PCMCIA_NUM_SOCKS]; -extern int au1x00_pcmcia_socket_probe(struct device *, struct pcmcia_low_level *, int, int); - -static int db1x00_pcmcia_hw_init(struct au1000_pcmcia_socket *skt) -{ -#ifdef CONFIG_MIPS_DB1550 - skt->irq = skt->nr ? AU1000_GPIO_5 : AU1000_GPIO_3; -#elif defined(CONFIG_MIPS_DB1200) || defined(CONFIG_MIPS_PB1200) - skt->irq = skt->nr ? BOARD_PC1_INT : BOARD_PC0_INT; -#else - skt->irq = skt->nr ? AU1000_GPIO_5 : AU1000_GPIO_2; -#endif - return 0; -} - -static void db1x00_pcmcia_shutdown(struct au1000_pcmcia_socket *skt) -{ - bcsr_write(BCSR_PCMCIA, 0); /* turn off power */ - msleep(2); -} - -static void -db1x00_pcmcia_socket_state(struct au1000_pcmcia_socket *skt, struct pcmcia_state *state) -{ - u32 inserted; - unsigned char vs; - - state->ready = 0; - state->vs_Xv = 0; - state->vs_3v = 0; - state->detect = 0; - - switch (skt->nr) { - case 0: - vs = bcsr_read(BCSR_STATUS) & 0x3; -#if defined(CONFIG_MIPS_DB1200) || defined(CONFIG_MIPS_PB1200) - inserted = BOARD_CARD_INSERTED(0); -#else - inserted = !(bcsr_read(BCSR_STATUS) & (1 << 4)); -#endif - break; - case 1: - vs = (bcsr_read(BCSR_STATUS) & 0xC) >> 2; -#if defined(CONFIG_MIPS_DB1200) || defined(CONFIG_MIPS_PB1200) - inserted = BOARD_CARD_INSERTED(1); -#else - inserted = !(bcsr_read(BCSR_STATUS) & (1<<5)); -#endif - break; - default:/* should never happen */ - return; - } - - if (inserted) - debug("db1x00 socket %d: inserted %d, vs %d pcmcia %x\n", - skt->nr, inserted, vs, bcsr_read(BCSR_PCMCIA)); - - if (inserted) { - switch (vs) { - case 0: - case 2: - state->vs_3v=1; - break; - case 3: /* 5V */ - break; - default: - /* return without setting 'detect' */ - printk(KERN_ERR "db1x00 bad VS (%d)\n", - vs); - } - state->detect = 1; - state->ready = 1; - } - else { - /* if the card was previously inserted and then ejected, - * we should turn off power to it - */ - if ((skt->nr == 0) && - (bcsr_read(BCSR_PCMCIA) & BCSR_PCMCIA_PC0RST)) { - bcsr_mod(BCSR_PCMCIA, BCSR_PCMCIA_PC0RST | - BCSR_PCMCIA_PC0DRVEN | - BCSR_PCMCIA_PC0VPP | - BCSR_PCMCIA_PC0VCC, 0); - msleep(10); - } - else if ((skt->nr == 1) && - (bcsr_read(BCSR_PCMCIA) & BCSR_PCMCIA_PC1RST)) { - bcsr_mod(BCSR_PCMCIA, BCSR_PCMCIA_PC1RST | - BCSR_PCMCIA_PC1DRVEN | - BCSR_PCMCIA_PC1VPP | - BCSR_PCMCIA_PC1VCC, 0); - msleep(10); - } - } - - state->bvd1=1; - state->bvd2=1; - state->wrprot=0; -} - -static int -db1x00_pcmcia_configure_socket(struct au1000_pcmcia_socket *skt, struct socket_state_t *state) -{ - u16 pwr; - int sock = skt->nr; - - debug("config_skt %d Vcc %dV Vpp %dV, reset %d\n", - sock, state->Vcc, state->Vpp, - state->flags & SS_RESET); - - /* pcmcia reg was set to zero at init time. Be careful when - * initializing a socket not to wipe out the settings of the - * other socket. - */ - pwr = bcsr_read(BCSR_PCMCIA); - pwr &= ~(0xf << sock*8); /* clear voltage settings */ - - state->Vpp = 0; - switch(state->Vcc){ - case 0: /* Vcc 0 */ - pwr |= SET_VCC_VPP(0,0,sock); - break; - case 50: /* Vcc 5V */ - switch(state->Vpp) { - case 0: - pwr |= SET_VCC_VPP(2,0,sock); - break; - case 50: - pwr |= SET_VCC_VPP(2,1,sock); - break; - case 12: - pwr |= SET_VCC_VPP(2,2,sock); - break; - case 33: - default: - pwr |= SET_VCC_VPP(0,0,sock); - printk("%s: bad Vcc/Vpp (%d:%d)\n", - __func__, - state->Vcc, - state->Vpp); - break; - } - break; - case 33: /* Vcc 3.3V */ - switch(state->Vpp) { - case 0: - pwr |= SET_VCC_VPP(1,0,sock); - break; - case 12: - pwr |= SET_VCC_VPP(1,2,sock); - break; - case 33: - pwr |= SET_VCC_VPP(1,1,sock); - break; - case 50: - default: - pwr |= SET_VCC_VPP(0,0,sock); - printk("%s: bad Vcc/Vpp (%d:%d)\n", - __func__, - state->Vcc, - state->Vpp); - break; - } - break; - default: /* what's this ? */ - pwr |= SET_VCC_VPP(0,0,sock); - printk(KERN_ERR "%s: bad Vcc %d\n", - __func__, state->Vcc); - break; - } - - bcsr_write(BCSR_PCMCIA, pwr); - msleep(300); - - if (sock == 0) { - if (!(state->flags & SS_RESET)) { - pwr |= BCSR_PCMCIA_PC0DRVEN; - bcsr_write(BCSR_PCMCIA, pwr); - msleep(300); - pwr |= BCSR_PCMCIA_PC0RST; - bcsr_write(BCSR_PCMCIA, pwr); - msleep(100); - } - else { - pwr &= ~(BCSR_PCMCIA_PC0RST | BCSR_PCMCIA_PC0DRVEN); - bcsr_write(BCSR_PCMCIA, pwr); - msleep(100); - } - } - else { - if (!(state->flags & SS_RESET)) { - pwr |= BCSR_PCMCIA_PC1DRVEN; - bcsr_write(BCSR_PCMCIA, pwr); - msleep(300); - pwr |= BCSR_PCMCIA_PC1RST; - bcsr_write(BCSR_PCMCIA, pwr); - msleep(100); - } - else { - pwr &= ~(BCSR_PCMCIA_PC1RST | BCSR_PCMCIA_PC1DRVEN); - bcsr_write(BCSR_PCMCIA, pwr); - msleep(100); - } - } - return 0; -} - -/* - * Enable card status IRQs on (re-)initialisation. This can - * be called at initialisation, power management event, or - * pcmcia event. - */ -void db1x00_socket_init(struct au1000_pcmcia_socket *skt) -{ - /* nothing to do for now */ -} - -/* - * Disable card status IRQs and PCMCIA bus on suspend. - */ -void db1x00_socket_suspend(struct au1000_pcmcia_socket *skt) -{ - /* nothing to do for now */ -} - -struct pcmcia_low_level db1x00_pcmcia_ops = { - .owner = THIS_MODULE, - - .hw_init = db1x00_pcmcia_hw_init, - .hw_shutdown = db1x00_pcmcia_shutdown, - - .socket_state = db1x00_pcmcia_socket_state, - .configure_socket = db1x00_pcmcia_configure_socket, - - .socket_init = db1x00_socket_init, - .socket_suspend = db1x00_socket_suspend -}; - -int au1x_board_init(struct device *dev) -{ - int ret = -ENODEV; - bcsr_write(BCSR_PCMCIA, 0); /* turn off power, if it's not already off */ - msleep(2); - ret = au1x00_pcmcia_socket_probe(dev, &db1x00_pcmcia_ops, 0, 2); - return ret; -} diff --git a/drivers/pcmcia/au1000_generic.h b/drivers/pcmcia/au1000_generic.h index 13a4fbc..aa743f6 100644 --- a/drivers/pcmcia/au1000_generic.h +++ b/drivers/pcmcia/au1000_generic.h @@ -44,22 +44,12 @@ /* pcmcia socket 1 needs external glue logic so the memory map * differs from board to board. */ -#if defined(CONFIG_MIPS_PB1000) || defined(CONFIG_MIPS_PB1100) || \ - defined(CONFIG_MIPS_PB1500) || defined(CONFIG_MIPS_PB1550) || \ - defined(CONFIG_MIPS_PB1200) +#if defined(CONFIG_MIPS_PB1000) #define AU1X_SOCK1_IO 0xF08000000ULL #define AU1X_SOCK1_PHYS_ATTR 0xF48000000ULL #define AU1X_SOCK1_PHYS_MEM 0xF88000000ULL #define AU1X_SOCK1_PSEUDO_PHYS_ATTR 0xF4800000 #define AU1X_SOCK1_PSEUDO_PHYS_MEM 0xF8800000 -#elif defined(CONFIG_MIPS_DB1000) || defined(CONFIG_MIPS_DB1100) || \ - defined(CONFIG_MIPS_DB1500) || defined(CONFIG_MIPS_DB1550) || \ - defined(CONFIG_MIPS_DB1200) -#define AU1X_SOCK1_IO 0xF04000000ULL -#define AU1X_SOCK1_PHYS_ATTR 0xF44000000ULL -#define AU1X_SOCK1_PHYS_MEM 0xF84000000ULL -#define AU1X_SOCK1_PSEUDO_PHYS_ATTR 0xF4400000 -#define AU1X_SOCK1_PSEUDO_PHYS_MEM 0xF8400000 #endif struct pcmcia_state { diff --git a/drivers/pcmcia/au1000_pb1x00.c b/drivers/pcmcia/au1000_pb1x00.c index b1984ed..5a979cb 100644 --- a/drivers/pcmcia/au1000_pb1x00.c +++ b/drivers/pcmcia/au1000_pb1x00.c @@ -1,6 +1,6 @@ /* * - * Alchemy Semi Pb1x00 boards specific pcmcia routines. + * Alchemy Semi Pb1000 boards specific pcmcia routines. * * Copyright 2002 MontaVista Software Inc. * Author: MontaVista Software, Inc. @@ -46,20 +46,11 @@ #define debug(fmt, arg...) do { } while (0) -#ifdef CONFIG_MIPS_PB1000 #include #define PCMCIA_IRQ AU1000_GPIO_15 -#elif defined (CONFIG_MIPS_PB1500) -#include -#define PCMCIA_IRQ AU1500_GPIO_203 -#elif defined (CONFIG_MIPS_PB1100) -#include -#define PCMCIA_IRQ AU1000_GPIO_11 -#endif static int pb1x00_pcmcia_init(struct pcmcia_init *init) { -#ifdef CONFIG_MIPS_PB1000 u16 pcr; pcr = PCR_SLOT_0_RST | PCR_SLOT_1_RST; @@ -74,21 +65,10 @@ static int pb1x00_pcmcia_init(struct pcmcia_init *init) au_sync_delay(20); return PCMCIA_NUM_SOCKS; - -#else /* fixme -- take care of the Pb1500 at some point */ - - u16 pcr; - pcr = au_readw(PCMCIA_BOARD_REG) & ~0xf; /* turn off power */ - pcr &= ~(PC_DEASSERT_RST | PC_DRV_EN); - au_writew(pcr, PCMCIA_BOARD_REG); - au_sync_delay(500); - return PCMCIA_NUM_SOCKS; -#endif } static int pb1x00_pcmcia_shutdown(void) { -#ifdef CONFIG_MIPS_PB1000 u16 pcr; pcr = PCR_SLOT_0_RST | PCR_SLOT_1_RST; pcr |= SET_VCC_VPP(VCC_HIZ,VPP_HIZ,0); @@ -96,14 +76,6 @@ static int pb1x00_pcmcia_shutdown(void) au_writel(pcr, PB1000_PCR); au_sync_delay(20); return 0; -#else - u16 pcr; - pcr = au_readw(PCMCIA_BOARD_REG) & ~0xf; /* turn off power */ - pcr &= ~(PC_DEASSERT_RST | PC_DRV_EN); - au_writew(pcr, PCMCIA_BOARD_REG); - au_sync_delay(2); - return 0; -#endif } static int @@ -112,21 +84,11 @@ pb1x00_pcmcia_socket_state(unsigned sock, struct pcmcia_state *state) u32 inserted0, inserted1; u16 vs0, vs1; -#ifdef CONFIG_MIPS_PB1000 vs0 = vs1 = (u16)au_readl(PB1000_ACR1); inserted0 = !(vs0 & (ACR1_SLOT_0_CD1 | ACR1_SLOT_0_CD2)); inserted1 = !(vs1 & (ACR1_SLOT_1_CD1 | ACR1_SLOT_1_CD2)); vs0 = (vs0 >> 4) & 0x3; vs1 = (vs1 >> 12) & 0x3; -#else - vs0 = (au_readw(BOARD_STATUS_REG) >> 4) & 0x3; -#ifdef CONFIG_MIPS_PB1500 - inserted0 = !((au_readl(GPIO2_PINSTATE) >> 1) & 0x1); /* gpio 201 */ -#else /* Pb1100 */ - inserted0 = !((au_readl(SYS_PINSTATERD) >> 9) & 0x1); /* gpio 9 */ -#endif - inserted1 = 0; -#endif state->ready = 0; state->vs_Xv = 0; @@ -203,7 +165,6 @@ pb1x00_pcmcia_configure_socket(const struct pcmcia_configure *configure) if(configure->sock > PCMCIA_MAX_SOCK) return -1; -#ifdef CONFIG_MIPS_PB1000 pcr = au_readl(PB1000_PCR); if (configure->sock == 0) { @@ -323,84 +284,6 @@ pb1x00_pcmcia_configure_socket(const struct pcmcia_configure *configure) au_writel(pcr, PB1000_PCR); au_sync_delay(300); -#else - - pcr = au_readw(PCMCIA_BOARD_REG) & ~0xf; - - debug("Vcc %dV Vpp %dV, pcr %x, reset %d\n", - configure->vcc, configure->vpp, pcr, configure->reset); - - - switch(configure->vcc){ - case 0: /* Vcc 0 */ - pcr |= SET_VCC_VPP(0,0); - break; - case 50: /* Vcc 5V */ - switch(configure->vpp) { - case 0: - pcr |= SET_VCC_VPP(2,0); - break; - case 50: - pcr |= SET_VCC_VPP(2,1); - break; - case 12: - pcr |= SET_VCC_VPP(2,2); - break; - case 33: - default: - pcr |= SET_VCC_VPP(0,0); - printk("%s: bad Vcc/Vpp (%d:%d)\n", - __func__, - configure->vcc, - configure->vpp); - break; - } - break; - case 33: /* Vcc 3.3V */ - switch(configure->vpp) { - case 0: - pcr |= SET_VCC_VPP(1,0); - break; - case 12: - pcr |= SET_VCC_VPP(1,2); - break; - case 33: - pcr |= SET_VCC_VPP(1,1); - break; - case 50: - default: - pcr |= SET_VCC_VPP(0,0); - printk("%s: bad Vcc/Vpp (%d:%d)\n", - __func__, - configure->vcc, - configure->vpp); - break; - } - break; - default: /* what's this ? */ - pcr |= SET_VCC_VPP(0,0); - printk(KERN_ERR "%s: bad Vcc %d\n", - __func__, configure->vcc); - break; - } - - au_writew(pcr, PCMCIA_BOARD_REG); - au_sync_delay(300); - - if (!configure->reset) { - pcr |= PC_DRV_EN; - au_writew(pcr, PCMCIA_BOARD_REG); - au_sync_delay(100); - pcr |= PC_DEASSERT_RST; - au_writew(pcr, PCMCIA_BOARD_REG); - au_sync_delay(100); - } - else { - pcr &= ~(PC_DEASSERT_RST | PC_DRV_EN); - au_writew(pcr, PCMCIA_BOARD_REG); - au_sync_delay(100); - } -#endif return 0; } diff --git a/drivers/pcmcia/db1xxx_ss.c b/drivers/pcmcia/db1xxx_ss.c new file mode 100644 index 0000000..b35b72b --- /dev/null +++ b/drivers/pcmcia/db1xxx_ss.c @@ -0,0 +1,630 @@ +/* + * PCMCIA socket code for the Alchemy Db1xxx/Pb1xxx boards. + * + * Copyright (c) 2009 Manuel Lauss + * + */ + +/* This is a fairly generic PCMCIA socket driver suitable for the + * following Alchemy Development boards: + * Db1000, Db/Pb1500, Db/Pb1100, Db/Pb1550, Db/Pb1200. + * + * The Db1000 is used as a reference: Per-socket card-, carddetect- and + * statuschange IRQs connected to SoC GPIOs, control and status register + * bits arranged in per-socket groups in an external PLD. All boards + * listed here use this layout, including bit positions and meanings. + * Of course there are exceptions in later boards: + * + * - Pb1100/Pb1500: single socket only; voltage key bits VS are + * at STATUS[5:4] (instead of STATUS[1:0]). + * - Au1200-based: additional card-eject irqs, irqs not gpios! + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +#define MEM_MAP_SIZE 0x400000 +#define IO_MAP_SIZE 0x1000 + +struct db1x_pcmcia_sock { + struct pcmcia_socket socket; + int nr; /* socket number */ + void *virt_io; + + /* the "pseudo" addresses of the PCMCIA space. */ + unsigned long phys_io; + unsigned long phys_attr; + unsigned long phys_mem; + + /* previous flags for set_socket() */ + unsigned int old_flags; + + /* interrupt sources: linux irq numbers! */ + int insert_irq; /* default carddetect irq */ + int stschg_irq; /* card-status-change irq */ + int card_irq; /* card irq */ + int eject_irq; /* db1200/pb1200 have these */ + +#define BOARD_TYPE_DEFAULT 0 /* most boards */ +#define BOARD_TYPE_DB1200 1 /* IRQs aren't gpios */ +#define BOARD_TYPE_PB1100 2 /* VS bits slightly different */ + int board_type; +}; + +#define to_db1x_socket(x) container_of(x, struct db1x_pcmcia_sock, socket) + +/* DB/PB1200: check CPLD SIGSTATUS register bit 10/12 */ +static int db1200_card_inserted(struct db1x_pcmcia_sock *sock) +{ + unsigned short sigstat; + + sigstat = bcsr_read(BCSR_SIGSTAT); + return sigstat & 1 << (8 + 2 * sock->nr); +} + +/* carddetect gpio: low-active */ +static int db1000_card_inserted(struct db1x_pcmcia_sock *sock) +{ + return !gpio_get_value(irq_to_gpio(sock->insert_irq)); +} + +static int db1x_card_inserted(struct db1x_pcmcia_sock *sock) +{ + switch (sock->board_type) { + case BOARD_TYPE_DB1200: + return db1200_card_inserted(sock); + default: + return db1000_card_inserted(sock); + } +} + +/* STSCHG tends to bounce heavily when cards are inserted/ejected. + * To avoid this, the interrupt is normally disabled and only enabled + * after reset to a card has been de-asserted. + */ +static inline void set_stschg(struct db1x_pcmcia_sock *sock, int en) +{ + if (sock->stschg_irq != -1) { + if (en) + enable_irq(sock->stschg_irq); + else + disable_irq(sock->stschg_irq); + } +} + +static irqreturn_t db1000_pcmcia_cdirq(int irq, void *data) +{ + struct db1x_pcmcia_sock *sock = data; + + pcmcia_parse_events(&sock->socket, SS_DETECT); + + return IRQ_HANDLED; +} + +static irqreturn_t db1000_pcmcia_stschgirq(int irq, void *data) +{ + struct db1x_pcmcia_sock *sock = data; + + pcmcia_parse_events(&sock->socket, SS_STSCHG); + + return IRQ_HANDLED; +} + +static irqreturn_t db1200_pcmcia_cdirq(int irq, void *data) +{ + struct db1x_pcmcia_sock *sock = data; + + /* Db/Pb1200 have separate per-socket insertion and ejection + * interrupts which stay asserted as long as the card is + * inserted/missing. The one which caused us to be called + * needs to be disabled and the other one enabled. + */ + if (irq == sock->insert_irq) { + disable_irq_nosync(sock->insert_irq); + enable_irq(sock->eject_irq); + } else { + disable_irq_nosync(sock->eject_irq); + enable_irq(sock->insert_irq); + } + + pcmcia_parse_events(&sock->socket, SS_DETECT); + + return IRQ_HANDLED; +} + +static int db1x_pcmcia_setup_irqs(struct db1x_pcmcia_sock *sock) +{ + int ret; + unsigned long flags; + + if (sock->stschg_irq != -1) { + ret = request_irq(sock->stschg_irq, db1000_pcmcia_stschgirq, + 0, "pcmcia_stschg", sock); + if (ret) + return ret; + } + + /* Db/Pb1200 have separate per-socket insertion and ejection + * interrupts, which should show edge behaviour but don't. + * So interrupts are disabled until both insertion and + * ejection handler have been registered and the currently + * active one disabled. + */ + if (sock->board_type == BOARD_TYPE_DB1200) { + local_irq_save(flags); + + ret = request_irq(sock->insert_irq, db1200_pcmcia_cdirq, + IRQF_DISABLED, "pcmcia_insert", sock); + if (ret) + goto out1; + + ret = request_irq(sock->eject_irq, db1200_pcmcia_cdirq, + IRQF_DISABLED, "pcmcia_eject", sock); + if (ret) { + free_irq(sock->insert_irq, sock); + local_irq_restore(flags); + goto out1; + } + + /* disable the currently active one */ + if (db1200_card_inserted(sock)) + disable_irq_nosync(sock->insert_irq); + else + disable_irq_nosync(sock->eject_irq); + + local_irq_restore(flags); + } else { + /* all other (older) Db1x00 boards use a GPIO to show + * card detection status: use both-edge triggers. + */ + set_irq_type(sock->insert_irq, IRQ_TYPE_EDGE_BOTH); + ret = request_irq(sock->insert_irq, db1000_pcmcia_cdirq, + 0, "pcmcia_carddetect", sock); + + if (ret) + goto out1; + } + + return 0; /* all done */ + +out1: + if (sock->stschg_irq != -1) + free_irq(sock->stschg_irq, sock); + + return ret; +} + +static void db1x_pcmcia_free_irqs(struct db1x_pcmcia_sock *sock) +{ + if (sock->stschg_irq != -1) + free_irq(sock->stschg_irq, sock); + + free_irq(sock->insert_irq, sock); + if (sock->eject_irq != -1) + free_irq(sock->eject_irq, sock); +} + +/* + * configure a PCMCIA socket on the Db1x00 series of boards (and + * compatibles). + * + * 2 external registers are involved: + * pcmcia_status (offset 0x04): bits [0:1/2:3]: read card voltage id + * pcmcia_control(offset 0x10): + * bits[0:1] set vcc for card + * bits[2:3] set vpp for card + * bit 4: enable data buffers + * bit 7: reset# for card + * add 8 for second socket. + */ +static int db1x_pcmcia_configure(struct pcmcia_socket *skt, + struct socket_state_t *state) +{ + struct db1x_pcmcia_sock *sock = to_db1x_socket(skt); + unsigned short cr_clr, cr_set; + unsigned int changed; + int v, p, ret; + + /* card voltage setup */ + cr_clr = (0xf << (sock->nr * 8)); /* clear voltage settings */ + cr_set = 0; + v = p = ret = 0; + + switch (state->Vcc) { + case 50: + ++v; + case 33: + ++v; + case 0: + break; + default: + printk(KERN_INFO "pcmcia%d unsupported Vcc %d\n", + sock->nr, state->Vcc); + } + + switch (state->Vpp) { + case 12: + ++p; + case 33: + case 50: + ++p; + case 0: + break; + default: + printk(KERN_INFO "pcmcia%d unsupported Vpp %d\n", + sock->nr, state->Vpp); + } + + /* sanity check: Vpp must be 0, 12, or Vcc */ + if (((state->Vcc == 33) && (state->Vpp == 50)) || + ((state->Vcc == 50) && (state->Vpp == 33))) { + printk(KERN_INFO "pcmcia%d bad Vcc/Vpp combo (%d %d)\n", + sock->nr, state->Vcc, state->Vpp); + v = p = 0; + ret = -EINVAL; + } + + /* create new voltage code */ + cr_set |= ((v << 2) | p) << (sock->nr * 8); + + changed = state->flags ^ sock->old_flags; + + if (changed & SS_RESET) { + if (state->flags & SS_RESET) { + set_stschg(sock, 0); + /* assert reset, disable io buffers */ + cr_clr |= (1 << (7 + (sock->nr * 8))); + cr_clr |= (1 << (4 + (sock->nr * 8))); + } else { + /* de-assert reset, enable io buffers */ + cr_set |= 1 << (7 + (sock->nr * 8)); + cr_set |= 1 << (4 + (sock->nr * 8)); + } + } + + /* update PCMCIA configuration */ + bcsr_mod(BCSR_PCMCIA, cr_clr, cr_set); + + sock->old_flags = state->flags; + + /* reset was taken away: give card time to initialize properly */ + if ((changed & SS_RESET) && !(state->flags & SS_RESET)) { + msleep(500); + set_stschg(sock, 1); + } + + return ret; +} + +/* VCC bits at [3:2]/[11:10] */ +#define GET_VCC(cr, socknr) \ + ((((cr) >> 2) >> ((socknr) * 8)) & 3) + +/* VS bits at [0:1]/[3:2] */ +#define GET_VS(sr, socknr) \ + (((sr) >> (2 * (socknr))) & 3) + +/* reset bits at [7]/[15] */ +#define GET_RESET(cr, socknr) \ + ((cr) & (1 << (7 + (8 * (socknr))))) + +static int db1x_pcmcia_get_status(struct pcmcia_socket *skt, + unsigned int *value) +{ + struct db1x_pcmcia_sock *sock = to_db1x_socket(skt); + unsigned short cr, sr; + unsigned int status; + + status = db1x_card_inserted(sock) ? SS_DETECT : 0; + + cr = bcsr_read(BCSR_PCMCIA); + sr = bcsr_read(BCSR_STATUS); + + /* PB1100/PB1500: voltage key bits are at [5:4] */ + if (sock->board_type == BOARD_TYPE_PB1100) + sr >>= 4; + + /* determine card type */ + switch (GET_VS(sr, sock->nr)) { + case 0: + case 2: + status |= SS_3VCARD; /* 3V card */ + case 3: + break; /* 5V card: set nothing */ + default: + status |= SS_XVCARD; /* treated as unsupported in core */ + } + + /* if Vcc is not zero, we have applied power to a card */ + status |= GET_VCC(cr, sock->nr) ? SS_POWERON : 0; + + /* reset de-asserted? then we're ready */ + status |= (GET_RESET(cr, sock->nr)) ? SS_READY : SS_RESET; + + *value = status; + + return 0; +} + +static int db1x_pcmcia_sock_init(struct pcmcia_socket *skt) +{ + return 0; +} + +static int db1x_pcmcia_sock_suspend(struct pcmcia_socket *skt) +{ + return 0; +} + +static int au1x00_pcmcia_set_io_map(struct pcmcia_socket *skt, + struct pccard_io_map *map) +{ + struct db1x_pcmcia_sock *sock = to_db1x_socket(skt); + + map->start = (u32)sock->virt_io; + map->stop = map->start + IO_MAP_SIZE; + + return 0; +} + +static int au1x00_pcmcia_set_mem_map(struct pcmcia_socket *skt, + struct pccard_mem_map *map) +{ + struct db1x_pcmcia_sock *sock = to_db1x_socket(skt); + + if (map->flags & MAP_ATTRIB) + map->static_start = sock->phys_attr + map->card_start; + else + map->static_start = sock->phys_mem + map->card_start; + + return 0; +} + +static struct pccard_operations db1x_pcmcia_operations = { + .init = db1x_pcmcia_sock_init, + .suspend = db1x_pcmcia_sock_suspend, + .get_status = db1x_pcmcia_get_status, + .set_socket = db1x_pcmcia_configure, + .set_io_map = au1x00_pcmcia_set_io_map, + .set_mem_map = au1x00_pcmcia_set_mem_map, +}; + +static int __devinit db1x_pcmcia_socket_probe(struct platform_device *pdev) +{ + struct db1x_pcmcia_sock *sock; + struct resource *r; + phys_t physio; + int ret, bid; + + sock = kzalloc(sizeof(struct db1x_pcmcia_sock), GFP_KERNEL); + if (!sock) + return -ENOMEM; + + sock->nr = pdev->id; + + bid = BCSR_WHOAMI_BOARD(bcsr_read(BCSR_WHOAMI)); + switch (bid) { + case BCSR_WHOAMI_PB1500: + case BCSR_WHOAMI_PB1500R2: + case BCSR_WHOAMI_PB1100: + sock->board_type = BOARD_TYPE_PB1100; + break; + case BCSR_WHOAMI_DB1000 ... BCSR_WHOAMI_PB1550_SDR: + sock->board_type = BOARD_TYPE_DEFAULT; + break; + case BCSR_WHOAMI_PB1200 ... BCSR_WHOAMI_DB1200: + sock->board_type = BOARD_TYPE_DB1200; + break; + default: + printk(KERN_INFO "db1xxx-ss: unknown board %d!\n", bid); + ret = -ENODEV; + goto out0; + }; + + /* + * gather resources necessary and optional nice-to-haves to + * operate a socket: + * This includes IRQs for Carddetection/ejection, the card + * itself and optional status change detection. + * Also, the memory areas covered by a socket. For these + * we require the 32bit "pseudo" addresses (see the au1000.h + * header for more information). + */ + + /* card: irq assigned to the card itself. */ + r = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "card"); + sock->card_irq = r ? r->start : 0; + + /* insert: irq which triggers on card insertion/ejection */ + r = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "insert"); + sock->insert_irq = r ? r->start : -1; + + /* stschg: irq which trigger on card status change (optional) */ + r = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "stschg"); + sock->stschg_irq = r ? r->start : -1; + + /* eject: irq which triggers on ejection (DB1200/PB1200 only) */ + r = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "eject"); + sock->eject_irq = r ? r->start : -1; + + ret = -ENODEV; + + /* + * pseudo-attr: The 32bit address of the PCMCIA attribute space + * for this socket (usually the 36bit address shifted 4 to the + * right). + */ + r = platform_get_resource_byname(pdev, IORESOURCE_MEM, "pseudo-attr"); + if (!r) { + printk(KERN_ERR "pcmcia%d has no 'pseudo-attr' resource!\n", + sock->nr); + goto out0; + } + sock->phys_attr = r->start; + + /* + * pseudo-mem: The 32bit address of the PCMCIA memory space for + * this socket (usually the 36bit address shifted 4 to the right) + */ + r = platform_get_resource_byname(pdev, IORESOURCE_MEM, "pseudo-mem"); + if (!r) { + printk(KERN_ERR "pcmcia%d has no 'pseudo-mem' resource!\n", + sock->nr); + goto out0; + } + sock->phys_mem = r->start; + + /* + * pseudo-io: The 32bit address of the PCMCIA IO space for this + * socket (usually the 36bit address shifted 4 to the right). + */ + r = platform_get_resource_byname(pdev, IORESOURCE_MEM, "pseudo-io"); + if (!r) { + printk(KERN_ERR "pcmcia%d has no 'pseudo-io' resource!\n", + sock->nr); + goto out0; + } + sock->phys_io = r->start; + + + /* IO: we must remap the full 36bit address (for reference see + * alchemy/common/setup.c::__fixup_bigphys_addr()) + */ + physio = ((phys_t)sock->phys_io) << 4; + + /* + * PCMCIA client drivers use the inb/outb macros to access + * the IO registers. Since mips_io_port_base is added + * to the access address of the mips implementation of + * inb/outb, we need to subtract it here because we want + * to access the I/O or MEM address directly, without + * going through this "mips_io_port_base" mechanism. + */ + sock->virt_io = (void *)(ioremap(physio, IO_MAP_SIZE) - + mips_io_port_base); + + if (!sock->virt_io) { + printk(KERN_ERR "pcmcia%d: cannot remap IO area\n", + sock->nr); + ret = -ENOMEM; + goto out0; + } + + sock->socket.ops = &db1x_pcmcia_operations; + sock->socket.owner = THIS_MODULE; + sock->socket.pci_irq = sock->card_irq; + sock->socket.features = SS_CAP_STATIC_MAP | SS_CAP_PCCARD; + sock->socket.map_size = MEM_MAP_SIZE; + sock->socket.io_offset = (unsigned long)sock->virt_io; + sock->socket.dev.parent = &pdev->dev; + sock->socket.resource_ops = &pccard_static_ops; + + platform_set_drvdata(pdev, sock); + + ret = db1x_pcmcia_setup_irqs(sock); + if (ret) { + printk(KERN_ERR "pcmcia%d cannot setup interrupts\n", + sock->nr); + goto out1; + } + + set_stschg(sock, 0); + + ret = pcmcia_register_socket(&sock->socket); + if (ret) { + printk(KERN_ERR "pcmcia%d failed to register\n", sock->nr); + goto out2; + } + + printk(KERN_INFO "Alchemy Db/Pb1xxx pcmcia%d @ io/attr/mem %08lx" + "(%p) %08lx %08lx card/insert/stschg/eject irqs @ %d " + "%d %d %d\n", sock->nr, sock->phys_io, sock->virt_io, + sock->phys_attr, sock->phys_mem, sock->card_irq, + sock->insert_irq, sock->stschg_irq, sock->eject_irq); + + return 0; + +out2: + db1x_pcmcia_free_irqs(sock); +out1: + iounmap((void *)(sock->virt_io + (u32)mips_io_port_base)); +out0: + kfree(sock); + return ret; +} + +static int __devexit db1x_pcmcia_socket_remove(struct platform_device *pdev) +{ + struct db1x_pcmcia_sock *sock = platform_get_drvdata(pdev); + + db1x_pcmcia_free_irqs(sock); + pcmcia_unregister_socket(&sock->socket); + iounmap((void *)(sock->virt_io + (u32)mips_io_port_base)); + kfree(sock); + + return 0; +} + +#ifdef CONFIG_PM +static int db1x_pcmcia_suspend(struct device *dev) +{ + return pcmcia_socket_dev_suspend(dev); +} + +static int db1x_pcmcia_resume(struct device *dev) +{ + return pcmcia_socket_dev_resume(dev); +} + +static struct dev_pm_ops db1x_pcmcia_pmops = { + .resume = db1x_pcmcia_resume, + .suspend = db1x_pcmcia_suspend, + .thaw = db1x_pcmcia_resume, + .freeze = db1x_pcmcia_suspend, +}; + +#define DB1XXX_SS_PMOPS &db1x_pcmcia_pmops + +#else + +#define DB1XXX_SS_PMOPS NULL + +#endif + +static struct platform_driver db1x_pcmcia_socket_driver = { + .driver = { + .name = "db1xxx_pcmcia", + .owner = THIS_MODULE, + .pm = DB1XXX_SS_PMOPS + }, + .probe = db1x_pcmcia_socket_probe, + .remove = __devexit_p(db1x_pcmcia_socket_remove), +}; + +int __init db1x_pcmcia_socket_load(void) +{ + return platform_driver_register(&db1x_pcmcia_socket_driver); +} + +void __exit db1x_pcmcia_socket_unload(void) +{ + platform_driver_unregister(&db1x_pcmcia_socket_driver); +} + +module_init(db1x_pcmcia_socket_load); +module_exit(db1x_pcmcia_socket_unload); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("PCMCIA Socket Services for Alchemy Db/Pb1x00 boards"); +MODULE_AUTHOR("Manuel Lauss"); -- cgit v1.1 From 0273b4efccd3bc2b2ef5ea9778e71d8efbbb7ac7 Mon Sep 17 00:00:00 2001 From: Manuel Lauss Date: Sun, 4 Oct 2009 14:55:29 +0200 Subject: MIPS: Alchemy: XXS1500 PCMCIA driver rewrite Rewritten XXS1500 PCMCIA socket driver, standalone (doesn't depend on au1000_generic.c) and added carddetect IRQ support. Signed-off-by: Manuel Lauss Cc: Florian Fainelli Cc: Linux-PCMCIA Signed-off-by: Ralf Baechle --- drivers/pcmcia/Kconfig | 10 ++ drivers/pcmcia/Makefile | 3 +- drivers/pcmcia/au1000_xxs1500.c | 188 --------------------- drivers/pcmcia/xxs1500_ss.c | 357 ++++++++++++++++++++++++++++++++++++++++ 4 files changed, 369 insertions(+), 189 deletions(-) delete mode 100644 drivers/pcmcia/au1000_xxs1500.c create mode 100644 drivers/pcmcia/xxs1500_ss.c (limited to 'drivers') diff --git a/drivers/pcmcia/Kconfig b/drivers/pcmcia/Kconfig index 7c16d35..1e01578 100644 --- a/drivers/pcmcia/Kconfig +++ b/drivers/pcmcia/Kconfig @@ -185,6 +185,16 @@ config PCMCIA_ALCHEMY_DEVBOARD This driver is also available as a module called db1xxx_ss.ko +config PCMCIA_XXS1500 + tristate "MyCable XXS1500 PCMCIA socket support" + depends on PCMCIA && MIPS_XXS1500 + select 64BIT_PHYS_ADDR + help + Support for the PCMCIA/CF socket interface on MyCable XXS1500 + systems. + + This driver is also available as a module called xxs1500_ss.ko + config PCMCIA_BCM63XX tristate "bcm63xx pcmcia support" depends on BCM63XX && PCMCIA diff --git a/drivers/pcmcia/Makefile b/drivers/pcmcia/Makefile index f128d71..21ee964 100644 --- a/drivers/pcmcia/Makefile +++ b/drivers/pcmcia/Makefile @@ -39,7 +39,6 @@ obj-$(CONFIG_PCMCIA_ALCHEMY_DEVBOARD) += db1xxx_ss.o au1x00_ss-y += au1000_generic.o au1x00_ss-$(CONFIG_MIPS_PB1000) += au1000_pb1x00.o -au1x00_ss-$(CONFIG_MIPS_XXS1500) += au1000_xxs1500.o sa1111_cs-y += sa1111_generic.o sa1111_cs-$(CONFIG_ASSABET_NEPONSET) += sa1100_neponset.o @@ -69,3 +68,5 @@ pxa2xx-obj-$(CONFIG_MACH_E740) += pxa2xx_e740.o pxa2xx-obj-$(CONFIG_MACH_STARGATE2) += pxa2xx_stargate2.o obj-$(CONFIG_PCMCIA_PXA2XX) += pxa2xx_base.o $(pxa2xx-obj-y) + +obj-$(CONFIG_PCMCIA_XXS1500) += xxs1500_ss.o diff --git a/drivers/pcmcia/au1000_xxs1500.c b/drivers/pcmcia/au1000_xxs1500.c deleted file mode 100644 index b43d47b..0000000 --- a/drivers/pcmcia/au1000_xxs1500.c +++ /dev/null @@ -1,188 +0,0 @@ -/* - * - * MyCable board specific pcmcia routines. - * - * Copyright 2003 MontaVista Software Inc. - * Author: Pete Popov, MontaVista Software, Inc. - * ppopov@mvista.com or source@mvista.com - * - * ######################################################################## - * - * This program is free software; you can distribute 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 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., - * 59 Temple Place - Suite 330, Boston MA 02111-1307, USA. - * - * ######################################################################## - * - * - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -#include -#include -#include - -#include -#include - -#define PCMCIA_MAX_SOCK 0 -#define PCMCIA_NUM_SOCKS (PCMCIA_MAX_SOCK + 1) -#define PCMCIA_IRQ AU1000_GPIO_4 - -#if 0 -#define DEBUG(x, args...) printk(__func__ ": " x, ##args) -#else -#define DEBUG(x,args...) -#endif - -static int xxs1500_pcmcia_init(struct pcmcia_init *init) -{ - return PCMCIA_NUM_SOCKS; -} - -static int xxs1500_pcmcia_shutdown(void) -{ - /* turn off power */ - au_writel(au_readl(GPIO2_PINSTATE) | (1<<14)|(1<<30), - GPIO2_OUTPUT); - au_sync_delay(100); - - /* assert reset */ - au_writel(au_readl(GPIO2_PINSTATE) | (1<<4)|(1<<20), - GPIO2_OUTPUT); - au_sync_delay(100); - return 0; -} - - -static int -xxs1500_pcmcia_socket_state(unsigned sock, struct pcmcia_state *state) -{ - u32 inserted; u32 vs; - unsigned long gpio, gpio2; - - if(sock > PCMCIA_MAX_SOCK) return -1; - - gpio = au_readl(SYS_PINSTATERD); - gpio2 = au_readl(GPIO2_PINSTATE); - - vs = gpio2 & ((1<<8) | (1<<9)); - inserted = (!(gpio & 0x1) && !(gpio & 0x2)); - - state->ready = 0; - state->vs_Xv = 0; - state->vs_3v = 0; - state->detect = 0; - - if (inserted) { - switch (vs) { - case 0: - case 1: - case 2: - state->vs_3v=1; - break; - case 3: /* 5V */ - default: - /* return without setting 'detect' */ - printk(KERN_ERR "au1x00_cs: unsupported VS\n", - vs); - return; - } - state->detect = 1; - } - - if (state->detect) { - state->ready = 1; - } - - state->bvd1= gpio2 & (1<<10); - state->bvd2 = gpio2 & (1<<11); - state->wrprot=0; - return 1; -} - - -static int xxs1500_pcmcia_get_irq_info(struct pcmcia_irq_info *info) -{ - - if(info->sock > PCMCIA_MAX_SOCK) return -1; - info->irq = PCMCIA_IRQ; - return 0; -} - - -static int -xxs1500_pcmcia_configure_socket(const struct pcmcia_configure *configure) -{ - - if(configure->sock > PCMCIA_MAX_SOCK) return -1; - - DEBUG("Vcc %dV Vpp %dV, reset %d\n", - configure->vcc, configure->vpp, configure->reset); - - switch(configure->vcc){ - case 33: /* Vcc 3.3V */ - /* turn on power */ - DEBUG("turn on power\n"); - au_writel((au_readl(GPIO2_PINSTATE) & ~(1<<14))|(1<<30), - GPIO2_OUTPUT); - au_sync_delay(100); - break; - case 50: /* Vcc 5V */ - default: /* what's this ? */ - printk(KERN_ERR "au1x00_cs: unsupported VCC\n"); - case 0: /* Vcc 0 */ - /* turn off power */ - au_sync_delay(100); - au_writel(au_readl(GPIO2_PINSTATE) | (1<<14)|(1<<30), - GPIO2_OUTPUT); - break; - } - - if (!configure->reset) { - DEBUG("deassert reset\n"); - au_writel((au_readl(GPIO2_PINSTATE) & ~(1<<4))|(1<<20), - GPIO2_OUTPUT); - au_sync_delay(100); - au_writel((au_readl(GPIO2_PINSTATE) & ~(1<<5))|(1<<21), - GPIO2_OUTPUT); - } - else { - DEBUG("assert reset\n"); - au_writel(au_readl(GPIO2_PINSTATE) | (1<<4)|(1<<20), - GPIO2_OUTPUT); - } - au_sync_delay(100); - return 0; -} - -struct pcmcia_low_level xxs1500_pcmcia_ops = { - xxs1500_pcmcia_init, - xxs1500_pcmcia_shutdown, - xxs1500_pcmcia_socket_state, - xxs1500_pcmcia_get_irq_info, - xxs1500_pcmcia_configure_socket -}; diff --git a/drivers/pcmcia/xxs1500_ss.c b/drivers/pcmcia/xxs1500_ss.c new file mode 100644 index 0000000..4e36930 --- /dev/null +++ b/drivers/pcmcia/xxs1500_ss.c @@ -0,0 +1,357 @@ +/* + * PCMCIA socket code for the MyCable XXS1500 system. + * + * Copyright (c) 2009 Manuel Lauss + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include + +#define MEM_MAP_SIZE 0x400000 +#define IO_MAP_SIZE 0x1000 + + +/* + * 3.3V cards only; all interfacing is done via gpios: + * + * 0/1: carddetect (00 = card present, xx = huh) + * 4: card irq + * 204: reset (high-act) + * 205: buffer enable (low-act) + * 208/209: card voltage key (00,01,10,11) + * 210: battwarn + * 211: batdead + * 214: power (low-act) + */ +#define GPIO_CDA 0 +#define GPIO_CDB 1 +#define GPIO_CARDIRQ 4 +#define GPIO_RESET 204 +#define GPIO_OUTEN 205 +#define GPIO_VSL 208 +#define GPIO_VSH 209 +#define GPIO_BATTDEAD 210 +#define GPIO_BATTWARN 211 +#define GPIO_POWER 214 + +struct xxs1500_pcmcia_sock { + struct pcmcia_socket socket; + void *virt_io; + + /* the "pseudo" addresses of the PCMCIA space. */ + unsigned long phys_io; + unsigned long phys_attr; + unsigned long phys_mem; + + /* previous flags for set_socket() */ + unsigned int old_flags; +}; + +#define to_xxs_socket(x) container_of(x, struct xxs1500_pcmcia_sock, socket) + +static irqreturn_t cdirq(int irq, void *data) +{ + struct xxs1500_pcmcia_sock *sock = data; + + pcmcia_parse_events(&sock->socket, SS_DETECT); + + return IRQ_HANDLED; +} + +static int xxs1500_pcmcia_configure(struct pcmcia_socket *skt, + struct socket_state_t *state) +{ + struct xxs1500_pcmcia_sock *sock = to_xxs_socket(skt); + unsigned int changed; + + /* power control */ + switch (state->Vcc) { + case 0: + gpio_set_value(GPIO_POWER, 1); /* power off */ + break; + case 33: + gpio_set_value(GPIO_POWER, 0); /* power on */ + break; + case 50: + default: + return -EINVAL; + } + + changed = state->flags ^ sock->old_flags; + + if (changed & SS_RESET) { + if (state->flags & SS_RESET) { + gpio_set_value(GPIO_RESET, 1); /* assert reset */ + gpio_set_value(GPIO_OUTEN, 1); /* buffers off */ + } else { + gpio_set_value(GPIO_RESET, 0); /* deassert reset */ + gpio_set_value(GPIO_OUTEN, 0); /* buffers on */ + msleep(500); + } + } + + sock->old_flags = state->flags; + + return 0; +} + +static int xxs1500_pcmcia_get_status(struct pcmcia_socket *skt, + unsigned int *value) +{ + unsigned int status; + int i; + + status = 0; + + /* check carddetects: GPIO[0:1] must both be low */ + if (!gpio_get_value(GPIO_CDA) && !gpio_get_value(GPIO_CDB)) + status |= SS_DETECT; + + /* determine card voltage: GPIO[208:209] binary value */ + i = (!!gpio_get_value(GPIO_VSL)) | ((!!gpio_get_value(GPIO_VSH)) << 1); + + switch (i) { + case 0: + case 1: + case 2: + status |= SS_3VCARD; /* 3V card */ + break; + case 3: /* 5V card, unsupported */ + default: + status |= SS_XVCARD; /* treated as unsupported in core */ + } + + /* GPIO214: low active power switch */ + status |= gpio_get_value(GPIO_POWER) ? 0 : SS_POWERON; + + /* GPIO204: high-active reset line */ + status |= gpio_get_value(GPIO_RESET) ? SS_RESET : SS_READY; + + /* other stuff */ + status |= gpio_get_value(GPIO_BATTDEAD) ? 0 : SS_BATDEAD; + status |= gpio_get_value(GPIO_BATTWARN) ? 0 : SS_BATWARN; + + *value = status; + + return 0; +} + +static int xxs1500_pcmcia_sock_init(struct pcmcia_socket *skt) +{ + gpio_direction_input(GPIO_CDA); + gpio_direction_input(GPIO_CDB); + gpio_direction_input(GPIO_VSL); + gpio_direction_input(GPIO_VSH); + gpio_direction_input(GPIO_BATTDEAD); + gpio_direction_input(GPIO_BATTWARN); + gpio_direction_output(GPIO_RESET, 1); /* assert reset */ + gpio_direction_output(GPIO_OUTEN, 1); /* disable buffers */ + gpio_direction_output(GPIO_POWER, 1); /* power off */ + + return 0; +} + +static int xxs1500_pcmcia_sock_suspend(struct pcmcia_socket *skt) +{ + return 0; +} + +static int au1x00_pcmcia_set_io_map(struct pcmcia_socket *skt, + struct pccard_io_map *map) +{ + struct xxs1500_pcmcia_sock *sock = to_xxs_socket(skt); + + map->start = (u32)sock->virt_io; + map->stop = map->start + IO_MAP_SIZE; + + return 0; +} + +static int au1x00_pcmcia_set_mem_map(struct pcmcia_socket *skt, + struct pccard_mem_map *map) +{ + struct xxs1500_pcmcia_sock *sock = to_xxs_socket(skt); + + if (map->flags & MAP_ATTRIB) + map->static_start = sock->phys_attr + map->card_start; + else + map->static_start = sock->phys_mem + map->card_start; + + return 0; +} + +static struct pccard_operations xxs1500_pcmcia_operations = { + .init = xxs1500_pcmcia_sock_init, + .suspend = xxs1500_pcmcia_sock_suspend, + .get_status = xxs1500_pcmcia_get_status, + .set_socket = xxs1500_pcmcia_configure, + .set_io_map = au1x00_pcmcia_set_io_map, + .set_mem_map = au1x00_pcmcia_set_mem_map, +}; + +static int __devinit xxs1500_pcmcia_probe(struct platform_device *pdev) +{ + struct xxs1500_pcmcia_sock *sock; + struct resource *r; + phys_t physio; + int ret, irq; + + sock = kzalloc(sizeof(struct xxs1500_pcmcia_sock), GFP_KERNEL); + if (!sock) + return -ENOMEM; + + ret = -ENODEV; + + /* + * pseudo-attr: The 32bit address of the PCMCIA attribute space + * for this socket (usually the 36bit address shifted 4 to the + * right). + */ + r = platform_get_resource_byname(pdev, IORESOURCE_MEM, "pseudo-attr"); + if (!r) { + dev_err(&pdev->dev, "missing 'pseudo-attr' resource!\n"); + goto out0; + } + sock->phys_attr = r->start; + + /* + * pseudo-mem: The 32bit address of the PCMCIA memory space for + * this socket (usually the 36bit address shifted 4 to the right) + */ + r = platform_get_resource_byname(pdev, IORESOURCE_MEM, "pseudo-mem"); + if (!r) { + dev_err(&pdev->dev, "missing 'pseudo-mem' resource!\n"); + goto out0; + } + sock->phys_mem = r->start; + + /* + * pseudo-io: The 32bit address of the PCMCIA IO space for this + * socket (usually the 36bit address shifted 4 to the right). + */ + r = platform_get_resource_byname(pdev, IORESOURCE_MEM, "pseudo-io"); + if (!r) { + dev_err(&pdev->dev, "missing 'pseudo-io' resource!\n"); + goto out0; + } + sock->phys_io = r->start; + + + /* for io must remap the full 36bit address (for reference see + * alchemy/common/setup.c::__fixup_bigphys_addr) + */ + physio = ((phys_t)sock->phys_io) << 4; + + /* + * PCMCIA client drivers use the inb/outb macros to access + * the IO registers. Since mips_io_port_base is added + * to the access address of the mips implementation of + * inb/outb, we need to subtract it here because we want + * to access the I/O or MEM address directly, without + * going through this "mips_io_port_base" mechanism. + */ + sock->virt_io = (void *)(ioremap(physio, IO_MAP_SIZE) - + mips_io_port_base); + + if (!sock->virt_io) { + dev_err(&pdev->dev, "cannot remap IO area\n"); + ret = -ENOMEM; + goto out0; + } + + sock->socket.ops = &xxs1500_pcmcia_operations; + sock->socket.owner = THIS_MODULE; + sock->socket.pci_irq = gpio_to_irq(GPIO_CARDIRQ); + sock->socket.features = SS_CAP_STATIC_MAP | SS_CAP_PCCARD; + sock->socket.map_size = MEM_MAP_SIZE; + sock->socket.io_offset = (unsigned long)sock->virt_io; + sock->socket.dev.parent = &pdev->dev; + sock->socket.resource_ops = &pccard_static_ops; + + platform_set_drvdata(pdev, sock); + + /* setup carddetect irq: use one of the 2 GPIOs as an + * edge detector. + */ + irq = gpio_to_irq(GPIO_CDA); + set_irq_type(irq, IRQ_TYPE_EDGE_BOTH); + ret = request_irq(irq, cdirq, 0, "pcmcia_carddetect", sock); + if (ret) { + dev_err(&pdev->dev, "cannot setup cd irq\n"); + goto out1; + } + + ret = pcmcia_register_socket(&sock->socket); + if (ret) { + dev_err(&pdev->dev, "failed to register\n"); + goto out2; + } + + printk(KERN_INFO "MyCable XXS1500 PCMCIA socket services\n"); + + return 0; + +out2: + free_irq(gpio_to_irq(GPIO_CDA), sock); +out1: + iounmap((void *)(sock->virt_io + (u32)mips_io_port_base)); +out0: + kfree(sock); + return ret; +} + +static int __devexit xxs1500_pcmcia_remove(struct platform_device *pdev) +{ + struct xxs1500_pcmcia_sock *sock = platform_get_drvdata(pdev); + + pcmcia_unregister_socket(&sock->socket); + free_irq(gpio_to_irq(GPIO_CDA), sock); + iounmap((void *)(sock->virt_io + (u32)mips_io_port_base)); + kfree(sock); + + return 0; +} + +static struct platform_driver xxs1500_pcmcia_socket_driver = { + .driver = { + .name = "xxs1500_pcmcia", + .owner = THIS_MODULE, + }, + .probe = xxs1500_pcmcia_probe, + .remove = __devexit_p(xxs1500_pcmcia_remove), +}; + +int __init xxs1500_pcmcia_socket_load(void) +{ + return platform_driver_register(&xxs1500_pcmcia_socket_driver); +} + +void __exit xxs1500_pcmcia_socket_unload(void) +{ + platform_driver_unregister(&xxs1500_pcmcia_socket_driver); +} + +module_init(xxs1500_pcmcia_socket_load); +module_exit(xxs1500_pcmcia_socket_unload); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("PCMCIA Socket Services for MyCable XXS1500 systems"); +MODULE_AUTHOR("Manuel Lauss"); -- cgit v1.1 From ea071cc705e8bfba0c8bf84be8d4f9f4e9da6962 Mon Sep 17 00:00:00 2001 From: Manuel Lauss Date: Tue, 13 Oct 2009 20:22:34 +0200 Subject: MIPS: Alchemy: remove dbdma compat macros Remove dbdma compat macros, move remaining users over to default queueing functions and -flags. (Queueing function signature has changed in order to give a build failure instead of silent functional changes due to the no longer implicitly specified DDMA_FLAGS_IE flag) Signed-off-by: Manuel Lauss Signed-off-by: Ralf Baechle --- drivers/ide/au1xxx-ide.c | 21 +++++++++------------ drivers/mmc/host/au1xmmc.c | 4 ++-- drivers/spi/au1550_spi.c | 6 ++++-- 3 files changed, 15 insertions(+), 16 deletions(-) (limited to 'drivers') diff --git a/drivers/ide/au1xxx-ide.c b/drivers/ide/au1xxx-ide.c index 87cef0c..377412e 100644 --- a/drivers/ide/au1xxx-ide.c +++ b/drivers/ide/au1xxx-ide.c @@ -56,8 +56,8 @@ static inline void auide_insw(unsigned long port, void *addr, u32 count) chan_tab_t *ctp; au1x_ddma_desc_t *dp; - if(!put_dest_flags(ahwif->rx_chan, (void*)addr, count << 1, - DDMA_FLAGS_NOIE)) { + if (!au1xxx_dbdma_put_dest(ahwif->rx_chan, (void*)addr, + count << 1, DDMA_FLAGS_NOIE)) { printk(KERN_ERR "%s failed %d\n", __func__, __LINE__); return; } @@ -74,8 +74,8 @@ static inline void auide_outsw(unsigned long port, void *addr, u32 count) chan_tab_t *ctp; au1x_ddma_desc_t *dp; - if(!put_source_flags(ahwif->tx_chan, (void*)addr, - count << 1, DDMA_FLAGS_NOIE)) { + if (!au1xxx_dbdma_put_source(ahwif->tx_chan, (void*)addr, + count << 1, DDMA_FLAGS_NOIE)) { printk(KERN_ERR "%s failed %d\n", __func__, __LINE__); return; } @@ -246,17 +246,14 @@ static int auide_build_dmatable(ide_drive_t *drive, struct ide_cmd *cmd) flags = DDMA_FLAGS_NOIE; if (iswrite) { - if(!put_source_flags(ahwif->tx_chan, - (void*) sg_virt(sg), - tc, flags)) { + if (!au1xxx_dbdma_put_source(ahwif->tx_chan, + (void *)sg_virt(sg), tc, flags)) { printk(KERN_ERR "%s failed %d\n", __func__, __LINE__); } - } else - { - if(!put_dest_flags(ahwif->rx_chan, - (void*) sg_virt(sg), - tc, flags)) { + } else { + if (!au1xxx_dbdma_put_dest(ahwif->rx_chan, + (void *)sg_virt(sg), tc, flags)) { printk(KERN_ERR "%s failed %d\n", __func__, __LINE__); } diff --git a/drivers/mmc/host/au1xmmc.c b/drivers/mmc/host/au1xmmc.c index d3f5561..d295dc5 100644 --- a/drivers/mmc/host/au1xmmc.c +++ b/drivers/mmc/host/au1xmmc.c @@ -650,10 +650,10 @@ static int au1xmmc_prepare_data(struct au1xmmc_host *host, flags = DDMA_FLAGS_IE; if (host->flags & HOST_F_XMIT) { - ret = au1xxx_dbdma_put_source_flags(channel, + ret = au1xxx_dbdma_put_source(channel, (void *)sg_virt(sg), len, flags); } else { - ret = au1xxx_dbdma_put_dest_flags(channel, + ret = au1xxx_dbdma_put_dest(channel, (void *)sg_virt(sg), len, flags); } diff --git a/drivers/spi/au1550_spi.c b/drivers/spi/au1550_spi.c index cfd5ff9..22f7d72 100644 --- a/drivers/spi/au1550_spi.c +++ b/drivers/spi/au1550_spi.c @@ -412,11 +412,13 @@ static int au1550_spi_dma_txrxb(struct spi_device *spi, struct spi_transfer *t) } /* put buffers on the ring */ - res = au1xxx_dbdma_put_dest(hw->dma_rx_ch, hw->rx, t->len); + res = au1xxx_dbdma_put_dest(hw->dma_rx_ch, hw->rx, + t->len, DDMA_FLAGS_IE); if (!res) dev_err(hw->dev, "rx dma put dest error\n"); - res = au1xxx_dbdma_put_source(hw->dma_tx_ch, (void *)hw->tx, t->len); + res = au1xxx_dbdma_put_source(hw->dma_tx_ch, (void *)hw->tx, + t->len, DDMA_FLAGS_IE); if (!res) dev_err(hw->dev, "tx dma put source error\n"); -- cgit v1.1 From 963accbc82a0912b39de39d59e2fd6741db3aa4b Mon Sep 17 00:00:00 2001 From: Manuel Lauss Date: Tue, 13 Oct 2009 20:22:35 +0200 Subject: MIPS: Alchemy: change dbdma to accept physical memory addresses DMA can only be done from physical addresses; move the "virt_to_phys" source/destination buffer address translation from the dbdma queueing functions (since the hardware can only DMA to/from physical addresses) to their respective users. Signed-off-by: Manuel Lauss Signed-off-by: Ralf Baechle --- drivers/ide/au1xxx-ide.c | 8 ++++---- drivers/mmc/host/au1xmmc.c | 4 ++-- drivers/spi/au1550_spi.c | 4 ++-- 3 files changed, 8 insertions(+), 8 deletions(-) (limited to 'drivers') diff --git a/drivers/ide/au1xxx-ide.c b/drivers/ide/au1xxx-ide.c index 377412e..349a67b 100644 --- a/drivers/ide/au1xxx-ide.c +++ b/drivers/ide/au1xxx-ide.c @@ -56,7 +56,7 @@ static inline void auide_insw(unsigned long port, void *addr, u32 count) chan_tab_t *ctp; au1x_ddma_desc_t *dp; - if (!au1xxx_dbdma_put_dest(ahwif->rx_chan, (void*)addr, + if (!au1xxx_dbdma_put_dest(ahwif->rx_chan, virt_to_phys(addr), count << 1, DDMA_FLAGS_NOIE)) { printk(KERN_ERR "%s failed %d\n", __func__, __LINE__); return; @@ -74,7 +74,7 @@ static inline void auide_outsw(unsigned long port, void *addr, u32 count) chan_tab_t *ctp; au1x_ddma_desc_t *dp; - if (!au1xxx_dbdma_put_source(ahwif->tx_chan, (void*)addr, + if (!au1xxx_dbdma_put_source(ahwif->tx_chan, virt_to_phys(addr), count << 1, DDMA_FLAGS_NOIE)) { printk(KERN_ERR "%s failed %d\n", __func__, __LINE__); return; @@ -247,13 +247,13 @@ static int auide_build_dmatable(ide_drive_t *drive, struct ide_cmd *cmd) if (iswrite) { if (!au1xxx_dbdma_put_source(ahwif->tx_chan, - (void *)sg_virt(sg), tc, flags)) { + sg_phys(sg), tc, flags)) { printk(KERN_ERR "%s failed %d\n", __func__, __LINE__); } } else { if (!au1xxx_dbdma_put_dest(ahwif->rx_chan, - (void *)sg_virt(sg), tc, flags)) { + sg_phys(sg), tc, flags)) { printk(KERN_ERR "%s failed %d\n", __func__, __LINE__); } diff --git a/drivers/mmc/host/au1xmmc.c b/drivers/mmc/host/au1xmmc.c index d295dc5..c8649df 100644 --- a/drivers/mmc/host/au1xmmc.c +++ b/drivers/mmc/host/au1xmmc.c @@ -651,10 +651,10 @@ static int au1xmmc_prepare_data(struct au1xmmc_host *host, if (host->flags & HOST_F_XMIT) { ret = au1xxx_dbdma_put_source(channel, - (void *)sg_virt(sg), len, flags); + sg_phys(sg), len, flags); } else { ret = au1xxx_dbdma_put_dest(channel, - (void *)sg_virt(sg), len, flags); + sg_phys(sg), len, flags); } if (!ret) diff --git a/drivers/spi/au1550_spi.c b/drivers/spi/au1550_spi.c index 22f7d72..ba8ac4f 100644 --- a/drivers/spi/au1550_spi.c +++ b/drivers/spi/au1550_spi.c @@ -412,12 +412,12 @@ static int au1550_spi_dma_txrxb(struct spi_device *spi, struct spi_transfer *t) } /* put buffers on the ring */ - res = au1xxx_dbdma_put_dest(hw->dma_rx_ch, hw->rx, + res = au1xxx_dbdma_put_dest(hw->dma_rx_ch, virt_to_phys(hw->rx), t->len, DDMA_FLAGS_IE); if (!res) dev_err(hw->dev, "rx dma put dest error\n"); - res = au1xxx_dbdma_put_source(hw->dma_tx_ch, (void *)hw->tx, + res = au1xxx_dbdma_put_source(hw->dma_tx_ch, virt_to_phys(hw->tx), t->len, DDMA_FLAGS_IE); if (!res) dev_err(hw->dev, "tx dma put source error\n"); -- cgit v1.1 From b2b13cdfd05ec331e1be3bbdd593d66840c0b688 Mon Sep 17 00:00:00 2001 From: Manuel Lauss Date: Wed, 28 Oct 2009 21:37:28 +0100 Subject: SERIAL 8250: Fixes for Alchemy UARTs. Limit the amount of address space claimed for Alchemy serial ports to 0x1000. On the Au1300, ports are only 0x1000 apart, and the registers only extend to 0x110 at most on all supported alchemy models. On the Au1300 the autodetect logic no longer works and this makes it necessary to specify the port type through platform data. Because of this the MSR quirk needs to be moved outside the autoconfig() function which will no longer be called when UPF_FIXED_TYPE is specified. Signed-off-by: Manuel Lauss Cc: Andrew Morton , Cc: linux-serial@vger.kernel.org Cc: linux-kernel@vger.kernel.org Cc: linux-mips@linux-mips.org Signed-off-by: Ralf Baechle --- drivers/serial/8250.c | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) (limited to 'drivers') diff --git a/drivers/serial/8250.c b/drivers/serial/8250.c index e9b15c3..a81ff7b 100644 --- a/drivers/serial/8250.c +++ b/drivers/serial/8250.c @@ -1217,12 +1217,6 @@ static void autoconfig(struct uart_8250_port *up, unsigned int probeflags) } #endif -#ifdef CONFIG_SERIAL_8250_AU1X00 - /* if access method is AU, it is a 16550 with a quirk */ - if (up->port.type == PORT_16550A && up->port.iotype == UPIO_AU) - up->bugs |= UART_BUG_NOMSR; -#endif - serial_outp(up, UART_LCR, save_lcr); if (up->capabilities != uart_config[up->port.type].flags) { @@ -2428,7 +2422,7 @@ serial8250_pm(struct uart_port *port, unsigned int state, static unsigned int serial8250_port_size(struct uart_8250_port *pt) { if (pt->port.iotype == UPIO_AU) - return 0x100000; + return 0x1000; #ifdef CONFIG_ARCH_OMAP if (is_omap_port(pt)) return 0x16 << pt->port.regshift; @@ -2585,6 +2579,13 @@ static void serial8250_config_port(struct uart_port *port, int flags) if (flags & UART_CONFIG_TYPE) autoconfig(up, probeflags); + +#ifdef CONFIG_SERIAL_8250_AU1X00 + /* if access method is AU, it is a 16550 with a quirk */ + if (up->port.type == PORT_16550A && up->port.iotype == UPIO_AU) + up->bugs |= UART_BUG_NOMSR; +#endif + if (up->port.type != PORT_UNKNOWN && flags & UART_CONFIG_IRQ) autoconfig_irq(up); -- cgit v1.1 From 206aa6cdadad8bbedee5649f1346fe47e922a039 Mon Sep 17 00:00:00 2001 From: Manuel Lauss Date: Mon, 19 Oct 2009 12:53:37 +0200 Subject: MIPS: Alchemy: physmap-flash for all devboards Replace the devboard NOR MTD mapping driver with physmap-flash support. Also honor the "swapboot" switch settings wrt. to the layout of the NOR partitions. Signed-off-by: Manuel Lauss Cc: Linux-MIPS Acked-By: David Woodhouse Signed-off-by: Ralf Baechle --- drivers/mtd/maps/Kconfig | 6 -- drivers/mtd/maps/Makefile | 1 - drivers/mtd/maps/alchemy-flash.c | 166 --------------------------------------- 3 files changed, 173 deletions(-) delete mode 100644 drivers/mtd/maps/alchemy-flash.c (limited to 'drivers') diff --git a/drivers/mtd/maps/Kconfig b/drivers/mtd/maps/Kconfig index 2de0cc8..2bb03a8 100644 --- a/drivers/mtd/maps/Kconfig +++ b/drivers/mtd/maps/Kconfig @@ -251,12 +251,6 @@ config MTD_NETtel help Support for flash chips on NETtel/SecureEdge/SnapGear boards. -config MTD_ALCHEMY - tristate "AMD Alchemy Pb1xxx/Db1xxx/RDK MTD support" - depends on SOC_AU1X00 && MTD_PARTITIONS && MTD_CFI - help - Flash memory access on AMD Alchemy Pb/Db/RDK Reference Boards - config MTD_DILNETPC tristate "CFI Flash device mapped on DIL/Net PC" depends on X86 && MTD_CONCAT && MTD_PARTITIONS && MTD_CFI_INTELEXT && BROKEN diff --git a/drivers/mtd/maps/Makefile b/drivers/mtd/maps/Makefile index ce31521..a44919f 100644 --- a/drivers/mtd/maps/Makefile +++ b/drivers/mtd/maps/Makefile @@ -40,7 +40,6 @@ obj-$(CONFIG_MTD_SCx200_DOCFLASH)+= scx200_docflash.o obj-$(CONFIG_MTD_DBOX2) += dbox2-flash.o obj-$(CONFIG_MTD_SOLUTIONENGINE)+= solutionengine.o obj-$(CONFIG_MTD_PCI) += pci.o -obj-$(CONFIG_MTD_ALCHEMY) += alchemy-flash.o obj-$(CONFIG_MTD_AUTCPU12) += autcpu12-nvram.o obj-$(CONFIG_MTD_EDB7312) += edb7312.o obj-$(CONFIG_MTD_IMPA7) += impa7.o diff --git a/drivers/mtd/maps/alchemy-flash.c b/drivers/mtd/maps/alchemy-flash.c deleted file mode 100644 index 845ad4f..0000000 --- a/drivers/mtd/maps/alchemy-flash.c +++ /dev/null @@ -1,166 +0,0 @@ -/* - * Flash memory access on AMD Alchemy evaluation boards - * - * (C) 2003, 2004 Pete Popov - */ - -#include -#include -#include -#include - -#include -#include -#include - -#include - -#ifdef CONFIG_MIPS_PB1000 -#define BOARD_MAP_NAME "Pb1000 Flash" -#define BOARD_FLASH_SIZE 0x00800000 /* 8MB */ -#define BOARD_FLASH_WIDTH 4 /* 32-bits */ -#endif - -#ifdef CONFIG_MIPS_PB1500 -#define BOARD_MAP_NAME "Pb1500 Flash" -#define BOARD_FLASH_SIZE 0x04000000 /* 64MB */ -#define BOARD_FLASH_WIDTH 4 /* 32-bits */ -#endif - -#ifdef CONFIG_MIPS_PB1100 -#define BOARD_MAP_NAME "Pb1100 Flash" -#define BOARD_FLASH_SIZE 0x04000000 /* 64MB */ -#define BOARD_FLASH_WIDTH 4 /* 32-bits */ -#endif - -#ifdef CONFIG_MIPS_PB1550 -#define BOARD_MAP_NAME "Pb1550 Flash" -#define BOARD_FLASH_SIZE 0x08000000 /* 128MB */ -#define BOARD_FLASH_WIDTH 4 /* 32-bits */ -#endif - -#ifdef CONFIG_MIPS_PB1200 -#define BOARD_MAP_NAME "Pb1200 Flash" -#define BOARD_FLASH_SIZE 0x08000000 /* 128MB */ -#define BOARD_FLASH_WIDTH 2 /* 16-bits */ -#endif - -#ifdef CONFIG_MIPS_DB1000 -#define BOARD_MAP_NAME "Db1000 Flash" -#define BOARD_FLASH_SIZE 0x02000000 /* 32MB */ -#define BOARD_FLASH_WIDTH 4 /* 32-bits */ -#endif - -#ifdef CONFIG_MIPS_DB1500 -#define BOARD_MAP_NAME "Db1500 Flash" -#define BOARD_FLASH_SIZE 0x02000000 /* 32MB */ -#define BOARD_FLASH_WIDTH 4 /* 32-bits */ -#endif - -#ifdef CONFIG_MIPS_DB1100 -#define BOARD_MAP_NAME "Db1100 Flash" -#define BOARD_FLASH_SIZE 0x02000000 /* 32MB */ -#define BOARD_FLASH_WIDTH 4 /* 32-bits */ -#endif - -#ifdef CONFIG_MIPS_DB1550 -#define BOARD_MAP_NAME "Db1550 Flash" -#define BOARD_FLASH_SIZE 0x08000000 /* 128MB */ -#define BOARD_FLASH_WIDTH 4 /* 32-bits */ -#endif - -#ifdef CONFIG_MIPS_DB1200 -#define BOARD_MAP_NAME "Db1200 Flash" -#define BOARD_FLASH_SIZE 0x04000000 /* 64MB */ -#define BOARD_FLASH_WIDTH 2 /* 16-bits */ -#endif - -#ifdef CONFIG_MIPS_BOSPORUS -#define BOARD_MAP_NAME "Bosporus Flash" -#define BOARD_FLASH_SIZE 0x01000000 /* 16MB */ -#define BOARD_FLASH_WIDTH 2 /* 16-bits */ -#endif - -#ifdef CONFIG_MIPS_MIRAGE -#define BOARD_MAP_NAME "Mirage Flash" -#define BOARD_FLASH_SIZE 0x04000000 /* 64MB */ -#define BOARD_FLASH_WIDTH 4 /* 32-bits */ -#define USE_LOCAL_ACCESSORS /* why? */ -#endif - -static struct map_info alchemy_map = { - .name = BOARD_MAP_NAME, -}; - -static struct mtd_partition alchemy_partitions[] = { - { - .name = "User FS", - .size = BOARD_FLASH_SIZE - 0x00400000, - .offset = 0x0000000 - },{ - .name = "YAMON", - .size = 0x0100000, - .offset = MTDPART_OFS_APPEND, - .mask_flags = MTD_WRITEABLE - },{ - .name = "raw kernel", - .size = (0x300000 - 0x40000), /* last 256KB is yamon env */ - .offset = MTDPART_OFS_APPEND, - } -}; - -static struct mtd_info *mymtd; - -static int __init alchemy_mtd_init(void) -{ - struct mtd_partition *parts; - int nb_parts = 0; - unsigned long window_addr; - unsigned long window_size; - - /* Default flash buswidth */ - alchemy_map.bankwidth = BOARD_FLASH_WIDTH; - - window_addr = 0x20000000 - BOARD_FLASH_SIZE; - window_size = BOARD_FLASH_SIZE; - - /* - * Static partition definition selection - */ - parts = alchemy_partitions; - nb_parts = ARRAY_SIZE(alchemy_partitions); - alchemy_map.size = window_size; - - /* - * Now let's probe for the actual flash. Do it here since - * specific machine settings might have been set above. - */ - printk(KERN_NOTICE BOARD_MAP_NAME ": probing %d-bit flash bus\n", - alchemy_map.bankwidth*8); - alchemy_map.virt = ioremap(window_addr, window_size); - mymtd = do_map_probe("cfi_probe", &alchemy_map); - if (!mymtd) { - iounmap(alchemy_map.virt); - return -ENXIO; - } - mymtd->owner = THIS_MODULE; - - add_mtd_partitions(mymtd, parts, nb_parts); - return 0; -} - -static void __exit alchemy_mtd_cleanup(void) -{ - if (mymtd) { - del_mtd_partitions(mymtd); - map_destroy(mymtd); - iounmap(alchemy_map.virt); - } -} - -module_init(alchemy_mtd_init); -module_exit(alchemy_mtd_cleanup); - -MODULE_AUTHOR("Embedded Alley Solutions, Inc"); -MODULE_DESCRIPTION(BOARD_MAP_NAME " MTD driver"); -MODULE_LICENSE("GPL"); -- cgit v1.1 From bd2302c220566cffd0756e1ac5f65705f9e3d8e7 Mon Sep 17 00:00:00 2001 From: Florian Fainelli Date: Tue, 10 Nov 2009 01:13:38 +0100 Subject: NET: au1000-eth: Convert to platform_driver model This patch converts the au1000-eth driver to become a full platform-driver as it ought to be. We now pass PHY-speficic configurations through platform_data but for compatibility the driver still assumes the default settings (search for PHY1 on MAC0) when no platform_data is passed. Tested on my MTX-1 board. Signed-off-by: Florian Fainelli Cc: linux-mips@linux-mips.org Cc: netdev@vger.kernel.org Acked-by: David S. Miller Patchwork: http://patchwork.linux-mips.org/patch/619/ Patchwork: http://patchwork.linux-mips.org/patch/963/ Signed-off-by: Ralf Baechle --- drivers/net/au1000_eth.c | 441 ++++++++++++++++++++++++----------------------- drivers/net/au1000_eth.h | 9 + 2 files changed, 232 insertions(+), 218 deletions(-) (limited to 'drivers') diff --git a/drivers/net/au1000_eth.c b/drivers/net/au1000_eth.c index 6bac046..6e5a68e 100644 --- a/drivers/net/au1000_eth.c +++ b/drivers/net/au1000_eth.c @@ -55,6 +55,7 @@ #include #include #include +#include #include #include @@ -63,6 +64,7 @@ #include #include +#include #include #include "au1000_eth.h" @@ -112,15 +114,15 @@ struct au1000_private *au_macs[NUM_ETH_INTERFACES]; * * PHY detection algorithm * - * If AU1XXX_PHY_STATIC_CONFIG is undefined, the PHY setup is + * If phy_static_config is undefined, the PHY setup is * autodetected: * * mii_probe() first searches the current MAC's MII bus for a PHY, - * selecting the first (or last, if AU1XXX_PHY_SEARCH_HIGHEST_ADDR is + * selecting the first (or last, if phy_search_highest_addr is * defined) PHY address not already claimed by another netdev. * * If nothing was found that way when searching for the 2nd ethernet - * controller's PHY and AU1XXX_PHY1_SEARCH_ON_MAC0 is defined, then + * controller's PHY and phy1_search_mac0 is defined, then * the first MII bus is searched as well for an unclaimed PHY; this is * needed in case of a dual-PHY accessible only through the MAC0's MII * bus. @@ -129,9 +131,7 @@ struct au1000_private *au_macs[NUM_ETH_INTERFACES]; * controller is not registered to the network subsystem. */ -/* autodetection defaults */ -#undef AU1XXX_PHY_SEARCH_HIGHEST_ADDR -#define AU1XXX_PHY1_SEARCH_ON_MAC0 +/* autodetection defaults: phy1_search_mac0 */ /* static PHY setup * @@ -148,29 +148,6 @@ struct au1000_private *au_macs[NUM_ETH_INTERFACES]; * specific irq-map */ -#if defined(CONFIG_MIPS_BOSPORUS) -/* - * Micrel/Kendin 5 port switch attached to MAC0, - * MAC0 is associated with PHY address 5 (== WAN port) - * MAC1 is not associated with any PHY, since it's connected directly - * to the switch. - * no interrupts are used - */ -# define AU1XXX_PHY_STATIC_CONFIG - -# define AU1XXX_PHY0_ADDR 5 -# define AU1XXX_PHY0_BUSID 0 -# undef AU1XXX_PHY0_IRQ - -# undef AU1XXX_PHY1_ADDR -# undef AU1XXX_PHY1_BUSID -# undef AU1XXX_PHY1_IRQ -#endif - -#if defined(AU1XXX_PHY0_BUSID) && (AU1XXX_PHY0_BUSID > 0) -# error MAC0-associated PHY attached 2nd MACs MII bus not supported yet -#endif - static void enable_mac(struct net_device *dev, int force_reset) { unsigned long flags; @@ -390,67 +367,55 @@ static int mii_probe (struct net_device *dev) struct au1000_private *const aup = netdev_priv(dev); struct phy_device *phydev = NULL; -#if defined(AU1XXX_PHY_STATIC_CONFIG) - BUG_ON(aup->mac_id < 0 || aup->mac_id > 1); + if (aup->phy_static_config) { + BUG_ON(aup->mac_id < 0 || aup->mac_id > 1); - if(aup->mac_id == 0) { /* get PHY0 */ -# if defined(AU1XXX_PHY0_ADDR) - phydev = au_macs[AU1XXX_PHY0_BUSID]->mii_bus->phy_map[AU1XXX_PHY0_ADDR]; -# else - printk (KERN_INFO DRV_NAME ":%s: using PHY-less setup\n", - dev->name); - return 0; -# endif /* defined(AU1XXX_PHY0_ADDR) */ - } else if (aup->mac_id == 1) { /* get PHY1 */ -# if defined(AU1XXX_PHY1_ADDR) - phydev = au_macs[AU1XXX_PHY1_BUSID]->mii_bus->phy_map[AU1XXX_PHY1_ADDR]; -# else - printk (KERN_INFO DRV_NAME ":%s: using PHY-less setup\n", - dev->name); + if (aup->phy_addr) + phydev = aup->mii_bus->phy_map[aup->phy_addr]; + else + printk (KERN_INFO DRV_NAME ":%s: using PHY-less setup\n", + dev->name); return 0; -# endif /* defined(AU1XXX_PHY1_ADDR) */ - } - -#else /* defined(AU1XXX_PHY_STATIC_CONFIG) */ - int phy_addr; - - /* find the first (lowest address) PHY on the current MAC's MII bus */ - for (phy_addr = 0; phy_addr < PHY_MAX_ADDR; phy_addr++) - if (aup->mii_bus->phy_map[phy_addr]) { - phydev = aup->mii_bus->phy_map[phy_addr]; -# if !defined(AU1XXX_PHY_SEARCH_HIGHEST_ADDR) - break; /* break out with first one found */ -# endif - } + } else { + int phy_addr; + + /* find the first (lowest address) PHY on the current MAC's MII bus */ + for (phy_addr = 0; phy_addr < PHY_MAX_ADDR; phy_addr++) + if (aup->mii_bus->phy_map[phy_addr]) { + phydev = aup->mii_bus->phy_map[phy_addr]; + if (!aup->phy_search_highest_addr) + break; /* break out with first one found */ + } -# if defined(AU1XXX_PHY1_SEARCH_ON_MAC0) - /* try harder to find a PHY */ - if (!phydev && (aup->mac_id == 1)) { - /* no PHY found, maybe we have a dual PHY? */ - printk (KERN_INFO DRV_NAME ": no PHY found on MAC1, " - "let's see if it's attached to MAC0...\n"); + if (aup->phy1_search_mac0) { + /* try harder to find a PHY */ + if (!phydev && (aup->mac_id == 1)) { + /* no PHY found, maybe we have a dual PHY? */ + printk (KERN_INFO DRV_NAME ": no PHY found on MAC1, " + "let's see if it's attached to MAC0...\n"); - BUG_ON(!au_macs[0]); + /* find the first (lowest address) non-attached PHY on + * the MAC0 MII bus */ + for (phy_addr = 0; phy_addr < PHY_MAX_ADDR; phy_addr++) { + struct phy_device *const tmp_phydev = + aup->mii_bus->phy_map[phy_addr]; - /* find the first (lowest address) non-attached PHY on - * the MAC0 MII bus */ - for (phy_addr = 0; phy_addr < PHY_MAX_ADDR; phy_addr++) { - struct phy_device *const tmp_phydev = - au_macs[0]->mii_bus->phy_map[phy_addr]; + if (aup->mac_id == 1) + break; - if (!tmp_phydev) - continue; /* no PHY here... */ + if (!tmp_phydev) + continue; /* no PHY here... */ - if (tmp_phydev->attached_dev) - continue; /* already claimed by MAC0 */ + if (tmp_phydev->attached_dev) + continue; /* already claimed by MAC0 */ - phydev = tmp_phydev; - break; /* found it */ + phydev = tmp_phydev; + break; /* found it */ + } + } } } -# endif /* defined(AU1XXX_PHY1_SEARCH_OTHER_BUS) */ -#endif /* defined(AU1XXX_PHY_STATIC_CONFIG) */ if (!phydev) { printk (KERN_ERR DRV_NAME ":%s: no PHY found\n", dev->name); return -1; @@ -578,31 +543,6 @@ setup_hw_rings(struct au1000_private *aup, u32 rx_base, u32 tx_base) } } -static struct { - u32 base_addr; - u32 macen_addr; - int irq; - struct net_device *dev; -} iflist[2] = { -#ifdef CONFIG_SOC_AU1000 - {AU1000_ETH0_BASE, AU1000_MAC0_ENABLE, AU1000_MAC0_DMA_INT}, - {AU1000_ETH1_BASE, AU1000_MAC1_ENABLE, AU1000_MAC1_DMA_INT} -#endif -#ifdef CONFIG_SOC_AU1100 - {AU1100_ETH0_BASE, AU1100_MAC0_ENABLE, AU1100_MAC0_DMA_INT} -#endif -#ifdef CONFIG_SOC_AU1500 - {AU1500_ETH0_BASE, AU1500_MAC0_ENABLE, AU1500_MAC0_DMA_INT}, - {AU1500_ETH1_BASE, AU1500_MAC1_ENABLE, AU1500_MAC1_DMA_INT} -#endif -#ifdef CONFIG_SOC_AU1550 - {AU1550_ETH0_BASE, AU1550_MAC0_ENABLE, AU1550_MAC0_DMA_INT}, - {AU1550_ETH1_BASE, AU1550_MAC1_ENABLE, AU1550_MAC1_DMA_INT} -#endif -}; - -static int num_ifs; - /* * ethtool operations */ @@ -711,7 +651,6 @@ static int au1000_init(struct net_device *dev) static inline void update_rx_stats(struct net_device *dev, u32 status) { - struct au1000_private *aup = netdev_priv(dev); struct net_device_stats *ps = &dev->stats; ps->rx_packets++; @@ -969,7 +908,7 @@ static netdev_tx_t au1000_tx(struct sk_buff *skb, struct net_device *dev) } pDB = aup->tx_db_inuse[aup->tx_head]; - skb_copy_from_linear_data(skb, pDB->vaddr, skb->len); + skb_copy_from_linear_data(skb, (void *)pDB->vaddr, skb->len); if (skb->len < ETH_ZLEN) { for (i=skb->len; ivaddr)[i] = 0; @@ -1058,53 +997,59 @@ static const struct net_device_ops au1000_netdev_ops = { .ndo_change_mtu = eth_change_mtu, }; -static struct net_device * au1000_probe(int port_num) +static int __devinit au1000_probe(struct platform_device *pdev) { static unsigned version_printed = 0; struct au1000_private *aup = NULL; + struct au1000_eth_platform_data *pd; struct net_device *dev = NULL; db_dest_t *pDB, *pDBfree; + int irq, i, err = 0; + struct resource *base, *macen; char ethaddr[6]; - int irq, i, err; - u32 base, macen; - if (port_num >= NUM_ETH_INTERFACES) - return NULL; + base = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!base) { + printk(KERN_ERR DRV_NAME ": failed to retrieve base register\n"); + err = -ENODEV; + goto out; + } - base = CPHYSADDR(iflist[port_num].base_addr ); - macen = CPHYSADDR(iflist[port_num].macen_addr); - irq = iflist[port_num].irq; + macen = platform_get_resource(pdev, IORESOURCE_MEM, 1); + if (!macen) { + printk(KERN_ERR DRV_NAME ": failed to retrieve MAC Enable register\n"); + err = -ENODEV; + goto out; + } - if (!request_mem_region( base, MAC_IOSIZE, "Au1x00 ENET") || - !request_mem_region(macen, 4, "Au1x00 ENET")) - return NULL; + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + printk(KERN_ERR DRV_NAME ": failed to retrieve IRQ\n"); + err = -ENODEV; + goto out; + } - if (version_printed++ == 0) - printk("%s version %s %s\n", DRV_NAME, DRV_VERSION, DRV_AUTHOR); + if (!request_mem_region(base->start, resource_size(base), pdev->name)) { + printk(KERN_ERR DRV_NAME ": failed to request memory region for base registers\n"); + err = -ENXIO; + goto out; + } + + if (!request_mem_region(macen->start, resource_size(macen), pdev->name)) { + printk(KERN_ERR DRV_NAME ": failed to request memory region for MAC enable register\n"); + err = -ENXIO; + goto err_request; + } dev = alloc_etherdev(sizeof(struct au1000_private)); if (!dev) { printk(KERN_ERR "%s: alloc_etherdev failed\n", DRV_NAME); - return NULL; + err = -ENOMEM; + goto err_alloc; } - dev->base_addr = base; - dev->irq = irq; - dev->netdev_ops = &au1000_netdev_ops; - SET_ETHTOOL_OPS(dev, &au1000_ethtool_ops); - dev->watchdog_timeo = ETH_TX_TIMEOUT; - - err = register_netdev(dev); - if (err != 0) { - printk(KERN_ERR "%s: Cannot register net device, error %d\n", - DRV_NAME, err); - free_netdev(dev); - return NULL; - } - - printk("%s: Au1xx0 Ethernet found at 0x%x, irq %d\n", - dev->name, base, irq); - + SET_NETDEV_DEV(dev, &pdev->dev); + platform_set_drvdata(pdev, dev); aup = netdev_priv(dev); spin_lock_init(&aup->lock); @@ -1115,21 +1060,29 @@ static struct net_device * au1000_probe(int port_num) (NUM_TX_BUFFS + NUM_RX_BUFFS), &aup->dma_addr, 0); if (!aup->vaddr) { - free_netdev(dev); - release_mem_region( base, MAC_IOSIZE); - release_mem_region(macen, 4); - return NULL; + printk(KERN_ERR DRV_NAME ": failed to allocate data buffers\n"); + err = -ENOMEM; + goto err_vaddr; } /* aup->mac is the base address of the MAC's registers */ - aup->mac = (volatile mac_reg_t *)iflist[port_num].base_addr; + aup->mac = (volatile mac_reg_t *)ioremap_nocache(base->start, resource_size(base)); + if (!aup->mac) { + printk(KERN_ERR DRV_NAME ": failed to ioremap MAC registers\n"); + err = -ENXIO; + goto err_remap1; + } - /* Setup some variables for quick register address access */ - aup->enable = (volatile u32 *)iflist[port_num].macen_addr; - aup->mac_id = port_num; - au_macs[port_num] = aup; + /* Setup some variables for quick register address access */ + aup->enable = (volatile u32 *)ioremap_nocache(macen->start, resource_size(macen)); + if (!aup->enable) { + printk(KERN_ERR DRV_NAME ": failed to ioremap MAC enable register\n"); + err = -ENXIO; + goto err_remap2; + } + aup->mac_id = pdev->id; - if (port_num == 0) { + if (pdev->id == 0) { if (prom_get_ethernet_addr(ethaddr) == 0) memcpy(au1000_mac_addr, ethaddr, sizeof(au1000_mac_addr)); else { @@ -1139,7 +1092,7 @@ static struct net_device * au1000_probe(int port_num) } setup_hw_rings(aup, MAC0_RX_DMA_ADDR, MAC0_TX_DMA_ADDR); - } else if (port_num == 1) + } else if (pdev->id == 1) setup_hw_rings(aup, MAC1_RX_DMA_ADDR, MAC1_TX_DMA_ADDR); /* @@ -1147,14 +1100,37 @@ static struct net_device * au1000_probe(int port_num) * to match those that are printed on their stickers */ memcpy(dev->dev_addr, au1000_mac_addr, sizeof(au1000_mac_addr)); - dev->dev_addr[5] += port_num; + dev->dev_addr[5] += pdev->id; *aup->enable = 0; aup->mac_enabled = 0; + pd = pdev->dev.platform_data; + if (!pd) { + printk(KERN_INFO DRV_NAME ": no platform_data passed, PHY search on MAC0\n"); + aup->phy1_search_mac0 = 1; + } else { + aup->phy_static_config = pd->phy_static_config; + aup->phy_search_highest_addr = pd->phy_search_highest_addr; + aup->phy1_search_mac0 = pd->phy1_search_mac0; + aup->phy_addr = pd->phy_addr; + aup->phy_busid = pd->phy_busid; + aup->phy_irq = pd->phy_irq; + } + + if (aup->phy_busid && aup->phy_busid > 0) { + printk(KERN_ERR DRV_NAME ": MAC0-associated PHY attached 2nd MACs MII" + "bus not supported yet\n"); + err = -ENODEV; + goto err_mdiobus_alloc; + } + aup->mii_bus = mdiobus_alloc(); - if (aup->mii_bus == NULL) - goto err_out; + if (aup->mii_bus == NULL) { + printk(KERN_ERR DRV_NAME ": failed to allocate mdiobus structure\n"); + err = -ENOMEM; + goto err_mdiobus_alloc; + } aup->mii_bus->priv = dev; aup->mii_bus->read = au1000_mdiobus_read; @@ -1168,23 +1144,19 @@ static struct net_device * au1000_probe(int port_num) for(i = 0; i < PHY_MAX_ADDR; ++i) aup->mii_bus->irq[i] = PHY_POLL; - /* if known, set corresponding PHY IRQs */ -#if defined(AU1XXX_PHY_STATIC_CONFIG) -# if defined(AU1XXX_PHY0_IRQ) - if (AU1XXX_PHY0_BUSID == aup->mac_id) - aup->mii_bus->irq[AU1XXX_PHY0_ADDR] = AU1XXX_PHY0_IRQ; -# endif -# if defined(AU1XXX_PHY1_IRQ) - if (AU1XXX_PHY1_BUSID == aup->mac_id) - aup->mii_bus->irq[AU1XXX_PHY1_ADDR] = AU1XXX_PHY1_IRQ; -# endif -#endif - mdiobus_register(aup->mii_bus); + if (aup->phy_static_config) + if (aup->phy_irq && aup->phy_busid == aup->mac_id) + aup->mii_bus->irq[aup->phy_addr] = aup->phy_irq; + + err = mdiobus_register(aup->mii_bus); + if (err) { + printk(KERN_ERR DRV_NAME " failed to register MDIO bus\n"); + goto err_mdiobus_reg; + } - if (mii_probe(dev) != 0) { + if (mii_probe(dev) != 0) goto err_out; - } pDBfree = NULL; /* setup the data buffer descriptors and attach a buffer to each one */ @@ -1216,19 +1188,35 @@ static struct net_device * au1000_probe(int port_num) aup->tx_db_inuse[i] = pDB; } + dev->base_addr = base->start; + dev->irq = irq; + dev->netdev_ops = &au1000_netdev_ops; + SET_ETHTOOL_OPS(dev, &au1000_ethtool_ops); + dev->watchdog_timeo = ETH_TX_TIMEOUT; + /* * The boot code uses the ethernet controller, so reset it to start * fresh. au1000_init() expects that the device is in reset state. */ reset_mac(dev); - return dev; + err = register_netdev(dev); + if (err) { + printk(KERN_ERR DRV_NAME "%s: Cannot register net device, aborting.\n", + dev->name); + goto err_out; + } + + printk("%s: Au1xx0 Ethernet found at 0x%lx, irq %d\n", + dev->name, (unsigned long)base->start, irq); + if (version_printed++ == 0) + printk("%s version %s %s\n", DRV_NAME, DRV_VERSION, DRV_AUTHOR); + + return 0; err_out: - if (aup->mii_bus != NULL) { + if (aup->mii_bus != NULL) mdiobus_unregister(aup->mii_bus); - mdiobus_free(aup->mii_bus); - } /* here we should have a valid dev plus aup-> register addresses * so we can reset the mac properly.*/ @@ -1242,67 +1230,84 @@ err_out: if (aup->tx_db_inuse[i]) ReleaseDB(aup, aup->tx_db_inuse[i]); } +err_mdiobus_reg: + mdiobus_free(aup->mii_bus); +err_mdiobus_alloc: + iounmap(aup->enable); +err_remap2: + iounmap(aup->mac); +err_remap1: dma_free_noncoherent(NULL, MAX_BUF_SIZE * (NUM_TX_BUFFS + NUM_RX_BUFFS), (void *)aup->vaddr, aup->dma_addr); - unregister_netdev(dev); +err_vaddr: free_netdev(dev); - release_mem_region( base, MAC_IOSIZE); - release_mem_region(macen, 4); - return NULL; +err_alloc: + release_mem_region(macen->start, resource_size(macen)); +err_request: + release_mem_region(base->start, resource_size(base)); +out: + return err; } -/* - * Setup the base address and interrupt of the Au1xxx ethernet macs - * based on cpu type and whether the interface is enabled in sys_pinfunc - * register. The last interface is enabled if SYS_PF_NI2 (bit 4) is 0. - */ -static int __init au1000_init_module(void) +static int __devexit au1000_remove(struct platform_device *pdev) { - int ni = (int)((au_readl(SYS_PINFUNC) & (u32)(SYS_PF_NI2)) >> 4); - struct net_device *dev; - int i, found_one = 0; + struct net_device *dev = platform_get_drvdata(pdev); + struct au1000_private *aup = netdev_priv(dev); + int i; + struct resource *base, *macen; - num_ifs = NUM_ETH_INTERFACES - ni; + platform_set_drvdata(pdev, NULL); + + unregister_netdev(dev); + mdiobus_unregister(aup->mii_bus); + mdiobus_free(aup->mii_bus); + + for (i = 0; i < NUM_RX_DMA; i++) + if (aup->rx_db_inuse[i]) + ReleaseDB(aup, aup->rx_db_inuse[i]); + + for (i = 0; i < NUM_TX_DMA; i++) + if (aup->tx_db_inuse[i]) + ReleaseDB(aup, aup->tx_db_inuse[i]); + + dma_free_noncoherent(NULL, MAX_BUF_SIZE * + (NUM_TX_BUFFS + NUM_RX_BUFFS), + (void *)aup->vaddr, aup->dma_addr); + + iounmap(aup->mac); + iounmap(aup->enable); + + base = platform_get_resource(pdev, IORESOURCE_MEM, 0); + release_mem_region(base->start, resource_size(base)); + + macen = platform_get_resource(pdev, IORESOURCE_MEM, 1); + release_mem_region(macen->start, resource_size(macen)); + + free_netdev(dev); - for(i = 0; i < num_ifs; i++) { - dev = au1000_probe(i); - iflist[i].dev = dev; - if (dev) - found_one++; - } - if (!found_one) - return -ENODEV; return 0; } -static void __exit au1000_cleanup_module(void) +static struct platform_driver au1000_eth_driver = { + .probe = au1000_probe, + .remove = __devexit_p(au1000_remove), + .driver = { + .name = "au1000-eth", + .owner = THIS_MODULE, + }, +}; +MODULE_ALIAS("platform:au1000-eth"); + + +static int __init au1000_init_module(void) +{ + return platform_driver_register(&au1000_eth_driver); +} + +static void __exit au1000_exit_module(void) { - int i, j; - struct net_device *dev; - struct au1000_private *aup; - - for (i = 0; i < num_ifs; i++) { - dev = iflist[i].dev; - if (dev) { - aup = netdev_priv(dev); - unregister_netdev(dev); - mdiobus_unregister(aup->mii_bus); - mdiobus_free(aup->mii_bus); - for (j = 0; j < NUM_RX_DMA; j++) - if (aup->rx_db_inuse[j]) - ReleaseDB(aup, aup->rx_db_inuse[j]); - for (j = 0; j < NUM_TX_DMA; j++) - if (aup->tx_db_inuse[j]) - ReleaseDB(aup, aup->tx_db_inuse[j]); - dma_free_noncoherent(NULL, MAX_BUF_SIZE * - (NUM_TX_BUFFS + NUM_RX_BUFFS), - (void *)aup->vaddr, aup->dma_addr); - release_mem_region(dev->base_addr, MAC_IOSIZE); - release_mem_region(CPHYSADDR(iflist[i].macen_addr), 4); - free_netdev(dev); - } - } + platform_driver_unregister(&au1000_eth_driver); } module_init(au1000_init_module); -module_exit(au1000_cleanup_module); +module_exit(au1000_exit_module); diff --git a/drivers/net/au1000_eth.h b/drivers/net/au1000_eth.h index 824ecd5..f9d29a2 100644 --- a/drivers/net/au1000_eth.h +++ b/drivers/net/au1000_eth.h @@ -108,6 +108,15 @@ struct au1000_private { struct phy_device *phy_dev; struct mii_bus *mii_bus; + /* PHY configuration */ + int phy_static_config; + int phy_search_highest_addr; + int phy1_search_mac0; + + int phy_addr; + int phy_busid; + int phy_irq; + /* These variables are just for quick access to certain regs addresses. */ volatile mac_reg_t *mac; /* mac registers */ volatile u32 *enable; /* address of MAC Enable Register */ -- cgit v1.1 From 85660f43a308cc601b243c1f4dc18a63545c5cfa Mon Sep 17 00:00:00 2001 From: Rade Bozic Date: Thu, 28 Jan 2010 12:47:07 -0800 Subject: MIPS: I2C: Add driver for Cavium OCTEON I2C ports. Signed-off-by: Rade Bozic Signed-off-by: David Daney Cc: Michael Lawnick To: linux-mips@linux-mips.org To: linux-i2c@vger.kernel.org To: ben-linux@fluff.org To: khali@linux-fr.org Cc: rade.bozic.ext@nsn.com Cc: Michael Lawnick Patchwork: http://patchwork.linux-mips.org/patch/890/ Signed-off-by: Ralf Baechle --- drivers/i2c/busses/Kconfig | 10 + drivers/i2c/busses/Makefile | 1 + drivers/i2c/busses/i2c-octeon.c | 651 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 662 insertions(+) create mode 100644 drivers/i2c/busses/i2c-octeon.c (limited to 'drivers') diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig index 5f318ce..737f052 100644 --- a/drivers/i2c/busses/Kconfig +++ b/drivers/i2c/busses/Kconfig @@ -564,6 +564,16 @@ config I2C_VERSATILE This driver can also be built as a module. If so, the module will be called i2c-versatile. +config I2C_OCTEON + tristate "Cavium OCTEON I2C bus support" + depends on CPU_CAVIUM_OCTEON + help + Say yes if you want to support the I2C serial bus on Cavium + OCTEON SOC. + + This driver can also be built as a module. If so, the module + will be called i2c-octeon. + comment "External I2C/SMBus adapter drivers" config I2C_PARPORT diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile index 302c551..c2c4ea1 100644 --- a/drivers/i2c/busses/Makefile +++ b/drivers/i2c/busses/Makefile @@ -54,6 +54,7 @@ obj-$(CONFIG_I2C_SH_MOBILE) += i2c-sh_mobile.o obj-$(CONFIG_I2C_SIMTEC) += i2c-simtec.o obj-$(CONFIG_I2C_STU300) += i2c-stu300.o obj-$(CONFIG_I2C_VERSATILE) += i2c-versatile.o +obj-$(CONFIG_I2C_OCTEON) += i2c-octeon.o # External I2C/SMBus adapter drivers obj-$(CONFIG_I2C_PARPORT) += i2c-parport.o diff --git a/drivers/i2c/busses/i2c-octeon.c b/drivers/i2c/busses/i2c-octeon.c new file mode 100644 index 0000000..6037550 --- /dev/null +++ b/drivers/i2c/busses/i2c-octeon.c @@ -0,0 +1,651 @@ +/* + * (C) Copyright 2009-2010 + * Nokia Siemens Networks, michael.lawnick.ext@nsn.com + * + * Portions Copyright (C) 2010 Cavium Networks, Inc. + * + * This is a driver for the i2c adapter in Cavium Networks' OCTEON processors. + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include + +#define DRV_NAME "i2c-octeon" + +/* The previous out-of-tree version was implicitly version 1.0. */ +#define DRV_VERSION "2.0" + +/* register offsets */ +#define SW_TWSI 0x00 +#define TWSI_INT 0x10 + +/* Controller command patterns */ +#define SW_TWSI_V 0x8000000000000000ull +#define SW_TWSI_EOP_TWSI_DATA 0x0C00000100000000ull +#define SW_TWSI_EOP_TWSI_CTL 0x0C00000200000000ull +#define SW_TWSI_EOP_TWSI_CLKCTL 0x0C00000300000000ull +#define SW_TWSI_EOP_TWSI_STAT 0x0C00000300000000ull +#define SW_TWSI_EOP_TWSI_RST 0x0C00000700000000ull +#define SW_TWSI_OP_TWSI_CLK 0x0800000000000000ull +#define SW_TWSI_R 0x0100000000000000ull + +/* Controller command and status bits */ +#define TWSI_CTL_CE 0x80 +#define TWSI_CTL_ENAB 0x40 +#define TWSI_CTL_STA 0x20 +#define TWSI_CTL_STP 0x10 +#define TWSI_CTL_IFLG 0x08 +#define TWSI_CTL_AAK 0x04 + +/* Some status values */ +#define STAT_START 0x08 +#define STAT_RSTART 0x10 +#define STAT_TXADDR_ACK 0x18 +#define STAT_TXDATA_ACK 0x28 +#define STAT_RXADDR_ACK 0x40 +#define STAT_RXDATA_ACK 0x50 +#define STAT_IDLE 0xF8 + +struct octeon_i2c { + wait_queue_head_t queue; + struct i2c_adapter adap; + int irq; + int twsi_freq; + int sys_freq; + resource_size_t twsi_phys; + void __iomem *twsi_base; + resource_size_t regsize; + struct device *dev; +}; + +/** + * octeon_i2c_write_sw - write an I2C core register. + * @i2c: The struct octeon_i2c. + * @eop_reg: Register selector. + * @data: Value to be written. + * + * The I2C core registers are accessed indirectly via the SW_TWSI CSR. + */ +static void octeon_i2c_write_sw(struct octeon_i2c *i2c, + u64 eop_reg, + u8 data) +{ + u64 tmp; + + __raw_writeq(SW_TWSI_V | eop_reg | data, i2c->twsi_base + SW_TWSI); + do { + tmp = __raw_readq(i2c->twsi_base + SW_TWSI); + } while ((tmp & SW_TWSI_V) != 0); +} + +/** + * octeon_i2c_read_sw - write an I2C core register. + * @i2c: The struct octeon_i2c. + * @eop_reg: Register selector. + * + * Returns the data. + * + * The I2C core registers are accessed indirectly via the SW_TWSI CSR. + */ +static u8 octeon_i2c_read_sw(struct octeon_i2c *i2c, u64 eop_reg) +{ + u64 tmp; + + __raw_writeq(SW_TWSI_V | eop_reg | SW_TWSI_R, i2c->twsi_base + SW_TWSI); + do { + tmp = __raw_readq(i2c->twsi_base + SW_TWSI); + } while ((tmp & SW_TWSI_V) != 0); + + return tmp & 0xFF; +} + +/** + * octeon_i2c_write_int - write the TWSI_INT register + * @i2c: The struct octeon_i2c. + * @data: Value to be written. + */ +static void octeon_i2c_write_int(struct octeon_i2c *i2c, u64 data) +{ + u64 tmp; + + __raw_writeq(data, i2c->twsi_base + TWSI_INT); + tmp = __raw_readq(i2c->twsi_base + TWSI_INT); +} + +/** + * octeon_i2c_int_enable - enable the TS interrupt. + * @i2c: The struct octeon_i2c. + * + * The interrupt will be asserted when there is non-STAT_IDLE state in + * the SW_TWSI_EOP_TWSI_STAT register. + */ +static void octeon_i2c_int_enable(struct octeon_i2c *i2c) +{ + octeon_i2c_write_int(i2c, 0x40); +} + +/** + * octeon_i2c_int_disable - disable the TS interrupt. + * @i2c: The struct octeon_i2c. + */ +static void octeon_i2c_int_disable(struct octeon_i2c *i2c) +{ + octeon_i2c_write_int(i2c, 0); +} + +/** + * octeon_i2c_unblock - unblock the bus. + * @i2c: The struct octeon_i2c. + * + * If there was a reset while a device was driving 0 to bus, + * bus is blocked. We toggle it free manually by some clock + * cycles and send a stop. + */ +static void octeon_i2c_unblock(struct octeon_i2c *i2c) +{ + int i; + + dev_dbg(i2c->dev, "%s\n", __func__); + for (i = 0; i < 9; i++) { + octeon_i2c_write_int(i2c, 0x0); + udelay(5); + octeon_i2c_write_int(i2c, 0x200); + udelay(5); + } + octeon_i2c_write_int(i2c, 0x300); + udelay(5); + octeon_i2c_write_int(i2c, 0x100); + udelay(5); + octeon_i2c_write_int(i2c, 0x0); +} + +/** + * octeon_i2c_isr - the interrupt service routine. + * @int: The irq, unused. + * @dev_id: Our struct octeon_i2c. + */ +static irqreturn_t octeon_i2c_isr(int irq, void *dev_id) +{ + struct octeon_i2c *i2c = dev_id; + + octeon_i2c_int_disable(i2c); + wake_up_interruptible(&i2c->queue); + + return IRQ_HANDLED; +} + + +static int octeon_i2c_test_iflg(struct octeon_i2c *i2c) +{ + return (octeon_i2c_read_sw(i2c, SW_TWSI_EOP_TWSI_CTL) & TWSI_CTL_IFLG) != 0; +} + +/** + * octeon_i2c_wait - wait for the IFLG to be set. + * @i2c: The struct octeon_i2c. + * + * Returns 0 on success, otherwise a negative errno. + */ +static int octeon_i2c_wait(struct octeon_i2c *i2c) +{ + int result; + + octeon_i2c_int_enable(i2c); + + result = wait_event_interruptible_timeout(i2c->queue, + octeon_i2c_test_iflg(i2c), + i2c->adap.timeout); + + octeon_i2c_int_disable(i2c); + + if (result < 0) { + dev_dbg(i2c->dev, "%s: wait interrupted\n", __func__); + return result; + } else if (result == 0) { + dev_dbg(i2c->dev, "%s: timeout\n", __func__); + result = -ETIMEDOUT; + } + + return 0; +} + +/** + * octeon_i2c_start - send START to the bus. + * @i2c: The struct octeon_i2c. + * + * Returns 0 on success, otherwise a negative errno. + */ +static int octeon_i2c_start(struct octeon_i2c *i2c) +{ + u8 data; + int result; + + octeon_i2c_write_sw(i2c, SW_TWSI_EOP_TWSI_CTL, + TWSI_CTL_ENAB | TWSI_CTL_STA); + + result = octeon_i2c_wait(i2c); + if (result) { + if (octeon_i2c_read_sw(i2c, SW_TWSI_EOP_TWSI_STAT) == STAT_IDLE) { + /* + * Controller refused to send start flag May + * be a client is holding SDA low - let's try + * to free it. + */ + octeon_i2c_unblock(i2c); + octeon_i2c_write_sw(i2c, SW_TWSI_EOP_TWSI_CTL, + TWSI_CTL_ENAB | TWSI_CTL_STA); + + result = octeon_i2c_wait(i2c); + } + if (result) + return result; + } + + data = octeon_i2c_read_sw(i2c, SW_TWSI_EOP_TWSI_STAT); + if ((data != STAT_START) && (data != STAT_RSTART)) { + dev_err(i2c->dev, "%s: bad status (0x%x)\n", __func__, data); + return -EIO; + } + + return 0; +} + +/** + * octeon_i2c_stop - send STOP to the bus. + * @i2c: The struct octeon_i2c. + * + * Returns 0 on success, otherwise a negative errno. + */ +static int octeon_i2c_stop(struct octeon_i2c *i2c) +{ + u8 data; + + octeon_i2c_write_sw(i2c, SW_TWSI_EOP_TWSI_CTL, + TWSI_CTL_ENAB | TWSI_CTL_STP); + + data = octeon_i2c_read_sw(i2c, SW_TWSI_EOP_TWSI_STAT); + + if (data != STAT_IDLE) { + dev_err(i2c->dev, "%s: bad status(0x%x)\n", __func__, data); + return -EIO; + } + return 0; +} + +/** + * octeon_i2c_write - send data to the bus. + * @i2c: The struct octeon_i2c. + * @target: Target address. + * @data: Pointer to the data to be sent. + * @length: Length of the data. + * + * The address is sent over the bus, then the data. + * + * Returns 0 on success, otherwise a negative errno. + */ +static int octeon_i2c_write(struct octeon_i2c *i2c, int target, + const u8 *data, int length) +{ + int i, result; + u8 tmp; + + result = octeon_i2c_start(i2c); + if (result) + return result; + + octeon_i2c_write_sw(i2c, SW_TWSI_EOP_TWSI_DATA, target << 1); + octeon_i2c_write_sw(i2c, SW_TWSI_EOP_TWSI_CTL, TWSI_CTL_ENAB); + + result = octeon_i2c_wait(i2c); + if (result) + return result; + + for (i = 0; i < length; i++) { + tmp = octeon_i2c_read_sw(i2c, SW_TWSI_EOP_TWSI_STAT); + if ((tmp != STAT_TXADDR_ACK) && (tmp != STAT_TXDATA_ACK)) { + dev_err(i2c->dev, + "%s: bad status before write (0x%x)\n", + __func__, tmp); + return -EIO; + } + + octeon_i2c_write_sw(i2c, SW_TWSI_EOP_TWSI_DATA, data[i]); + octeon_i2c_write_sw(i2c, SW_TWSI_EOP_TWSI_CTL, TWSI_CTL_ENAB); + + result = octeon_i2c_wait(i2c); + if (result) + return result; + } + + return 0; +} + +/** + * octeon_i2c_read - receive data from the bus. + * @i2c: The struct octeon_i2c. + * @target: Target address. + * @data: Pointer to the location to store the datae . + * @length: Length of the data. + * + * The address is sent over the bus, then the data is read. + * + * Returns 0 on success, otherwise a negative errno. + */ +static int octeon_i2c_read(struct octeon_i2c *i2c, int target, + u8 *data, int length) +{ + int i, result; + u8 tmp; + + if (length < 1) + return -EINVAL; + + result = octeon_i2c_start(i2c); + if (result) + return result; + + octeon_i2c_write_sw(i2c, SW_TWSI_EOP_TWSI_DATA, (target<<1) | 1); + octeon_i2c_write_sw(i2c, SW_TWSI_EOP_TWSI_CTL, TWSI_CTL_ENAB); + + result = octeon_i2c_wait(i2c); + if (result) + return result; + + for (i = 0; i < length; i++) { + tmp = octeon_i2c_read_sw(i2c, SW_TWSI_EOP_TWSI_STAT); + if ((tmp != STAT_RXDATA_ACK) && (tmp != STAT_RXADDR_ACK)) { + dev_err(i2c->dev, + "%s: bad status before read (0x%x)\n", + __func__, tmp); + return -EIO; + } + + if (i+1 < length) + octeon_i2c_write_sw(i2c, SW_TWSI_EOP_TWSI_CTL, + TWSI_CTL_ENAB | TWSI_CTL_AAK); + else + octeon_i2c_write_sw(i2c, SW_TWSI_EOP_TWSI_CTL, + TWSI_CTL_ENAB); + + result = octeon_i2c_wait(i2c); + if (result) + return result; + + data[i] = octeon_i2c_read_sw(i2c, SW_TWSI_EOP_TWSI_DATA); + } + return 0; +} + +/** + * octeon_i2c_xfer - The driver's master_xfer function. + * @adap: Pointer to the i2c_adapter structure. + * @msgs: Pointer to the messages to be processed. + * @num: Length of the MSGS array. + * + * Returns the number of messages processed, or a negative errno on + * failure. + */ +static int octeon_i2c_xfer(struct i2c_adapter *adap, + struct i2c_msg *msgs, + int num) +{ + struct i2c_msg *pmsg; + int i; + int ret = 0; + struct octeon_i2c *i2c = i2c_get_adapdata(adap); + + for (i = 0; ret == 0 && i < num; i++) { + pmsg = &msgs[i]; + dev_dbg(i2c->dev, + "Doing %s %d byte(s) to/from 0x%02x - %d of %d messages\n", + pmsg->flags & I2C_M_RD ? "read" : "write", + pmsg->len, pmsg->addr, i + 1, num); + if (pmsg->flags & I2C_M_RD) + ret = octeon_i2c_read(i2c, pmsg->addr, pmsg->buf, + pmsg->len); + else + ret = octeon_i2c_write(i2c, pmsg->addr, pmsg->buf, + pmsg->len); + } + octeon_i2c_stop(i2c); + + return (ret != 0) ? ret : num; +} + +static u32 octeon_i2c_functionality(struct i2c_adapter *adap) +{ + return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; +} + +static const struct i2c_algorithm octeon_i2c_algo = { + .master_xfer = octeon_i2c_xfer, + .functionality = octeon_i2c_functionality, +}; + +static struct i2c_adapter octeon_i2c_ops = { + .owner = THIS_MODULE, + .name = "OCTEON adapter", + .algo = &octeon_i2c_algo, + .timeout = 2, +}; + +/** + * octeon_i2c_setclock - Calculate and set clock divisors. + */ +static int __init octeon_i2c_setclock(struct octeon_i2c *i2c) +{ + int tclk, thp_base, inc, thp_idx, mdiv_idx, ndiv_idx, foscl, diff; + int thp = 0x18, mdiv = 2, ndiv = 0, delta_hz = 1000000; + + for (ndiv_idx = 0; ndiv_idx < 8 && delta_hz != 0; ndiv_idx++) { + /* + * An mdiv value of less than 2 seems to not work well + * with ds1337 RTCs, so we constrain it to larger + * values. + */ + for (mdiv_idx = 15; mdiv_idx >= 2 && delta_hz != 0; mdiv_idx--) { + /* + * For given ndiv and mdiv values check the + * two closest thp values. + */ + tclk = i2c->twsi_freq * (mdiv_idx + 1) * 10; + tclk *= (1 << ndiv_idx); + thp_base = (i2c->sys_freq / (tclk * 2)) - 1; + for (inc = 0; inc <= 1; inc++) { + thp_idx = thp_base + inc; + if (thp_idx < 5 || thp_idx > 0xff) + continue; + + foscl = i2c->sys_freq / (2 * (thp_idx + 1)); + foscl = foscl / (1 << ndiv_idx); + foscl = foscl / (mdiv_idx + 1) / 10; + diff = abs(foscl - i2c->twsi_freq); + if (diff < delta_hz) { + delta_hz = diff; + thp = thp_idx; + mdiv = mdiv_idx; + ndiv = ndiv_idx; + } + } + } + } + octeon_i2c_write_sw(i2c, SW_TWSI_OP_TWSI_CLK, thp); + octeon_i2c_write_sw(i2c, SW_TWSI_EOP_TWSI_CLKCTL, (mdiv << 3) | ndiv); + + return 0; +} + +static int __init octeon_i2c_initlowlevel(struct octeon_i2c *i2c) +{ + u8 status; + int tries; + + /* disable high level controller, enable bus access */ + octeon_i2c_write_sw(i2c, SW_TWSI_EOP_TWSI_CTL, TWSI_CTL_ENAB); + + /* reset controller */ + octeon_i2c_write_sw(i2c, SW_TWSI_EOP_TWSI_RST, 0); + + for (tries = 10; tries; tries--) { + udelay(1); + status = octeon_i2c_read_sw(i2c, SW_TWSI_EOP_TWSI_STAT); + if (status == STAT_IDLE) + return 0; + } + dev_err(i2c->dev, "%s: TWSI_RST failed! (0x%x)\n", __func__, status); + return -EIO; +} + +static int __devinit octeon_i2c_probe(struct platform_device *pdev) +{ + int irq, result = 0; + struct octeon_i2c *i2c; + struct octeon_i2c_data *i2c_data; + struct resource *res_mem; + + /* All adaptors have an irq. */ + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return irq; + + i2c = kzalloc(sizeof(*i2c), GFP_KERNEL); + if (!i2c) { + dev_err(&pdev->dev, "kzalloc failed\n"); + result = -ENOMEM; + goto out; + } + i2c->dev = &pdev->dev; + i2c_data = pdev->dev.platform_data; + + res_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + + if (res_mem == NULL) { + dev_err(i2c->dev, "found no memory resource\n"); + result = -ENXIO; + goto fail_region; + } + + if (i2c_data == NULL) { + dev_err(i2c->dev, "no I2C frequency data\n"); + result = -ENXIO; + goto fail_region; + } + + i2c->twsi_phys = res_mem->start; + i2c->regsize = resource_size(res_mem); + i2c->twsi_freq = i2c_data->i2c_freq; + i2c->sys_freq = i2c_data->sys_freq; + + if (!request_mem_region(i2c->twsi_phys, i2c->regsize, res_mem->name)) { + dev_err(i2c->dev, "request_mem_region failed\n"); + goto fail_region; + } + i2c->twsi_base = ioremap(i2c->twsi_phys, i2c->regsize); + + init_waitqueue_head(&i2c->queue); + + i2c->irq = irq; + + result = request_irq(i2c->irq, octeon_i2c_isr, 0, DRV_NAME, i2c); + if (result < 0) { + dev_err(i2c->dev, "failed to attach interrupt\n"); + goto fail_irq; + } + + result = octeon_i2c_initlowlevel(i2c); + if (result) { + dev_err(i2c->dev, "init low level failed\n"); + goto fail_add; + } + + result = octeon_i2c_setclock(i2c); + if (result) { + dev_err(i2c->dev, "clock init failed\n"); + goto fail_add; + } + + i2c->adap = octeon_i2c_ops; + i2c->adap.dev.parent = &pdev->dev; + i2c->adap.nr = pdev->id >= 0 ? pdev->id : 0; + i2c_set_adapdata(&i2c->adap, i2c); + platform_set_drvdata(pdev, i2c); + + result = i2c_add_numbered_adapter(&i2c->adap); + if (result < 0) { + dev_err(i2c->dev, "failed to add adapter\n"); + goto fail_add; + } + + dev_info(i2c->dev, "version %s\n", DRV_VERSION); + + return result; + +fail_add: + platform_set_drvdata(pdev, NULL); + free_irq(i2c->irq, i2c); +fail_irq: + iounmap(i2c->twsi_base); + release_mem_region(i2c->twsi_phys, i2c->regsize); +fail_region: + kfree(i2c); +out: + return result; +}; + +static int __devexit octeon_i2c_remove(struct platform_device *pdev) +{ + struct octeon_i2c *i2c = platform_get_drvdata(pdev); + + i2c_del_adapter(&i2c->adap); + platform_set_drvdata(pdev, NULL); + free_irq(i2c->irq, i2c); + iounmap(i2c->twsi_base); + release_mem_region(i2c->twsi_phys, i2c->regsize); + kfree(i2c); + return 0; +}; + +static struct platform_driver octeon_i2c_driver = { + .probe = octeon_i2c_probe, + .remove = __devexit_p(octeon_i2c_remove), + .driver = { + .owner = THIS_MODULE, + .name = DRV_NAME, + }, +}; + +static int __init octeon_i2c_init(void) +{ + int rv; + + rv = platform_driver_register(&octeon_i2c_driver); + return rv; +} + +static void __exit octeon_i2c_exit(void) +{ + platform_driver_unregister(&octeon_i2c_driver); +} + +MODULE_AUTHOR("Michael Lawnick "); +MODULE_DESCRIPTION("I2C-Bus adapter for Cavium OCTEON processors"); +MODULE_LICENSE("GPL"); +MODULE_VERSION(DRV_VERSION); +MODULE_ALIAS("platform:" DRV_NAME); + +module_init(octeon_i2c_init); +module_exit(octeon_i2c_exit); -- cgit v1.1 From 6568a234363978e1aebb5b7c9840ed87eed20362 Mon Sep 17 00:00:00 2001 From: David Daney Date: Thu, 7 Jan 2010 11:05:01 -0800 Subject: Staging: Octeon Ethernet: Remove unused code. Remove unused code, reindent, and join some spilt strings. Signed-off-by: David Daney To: linux-mips@linux-mips.org To: gregkh@suse.de Patchwork: http://patchwork.linux-mips.org/patch/842/ Signed-off-by: Ralf Baechle --- drivers/staging/octeon/ethernet-defines.h | 10 ---- drivers/staging/octeon/ethernet-mem.c | 81 ++++++++----------------- drivers/staging/octeon/ethernet-rx.c | 77 ++++++++---------------- drivers/staging/octeon/ethernet-tx.c | 99 ------------------------------- drivers/staging/octeon/ethernet.c | 31 ++-------- drivers/staging/octeon/octeon-ethernet.h | 41 ------------- 6 files changed, 55 insertions(+), 284 deletions(-) (limited to 'drivers') diff --git a/drivers/staging/octeon/ethernet-defines.h b/drivers/staging/octeon/ethernet-defines.h index f13131b..6b8065f 100644 --- a/drivers/staging/octeon/ethernet-defines.h +++ b/drivers/staging/octeon/ethernet-defines.h @@ -41,9 +41,6 @@ * Tells the driver to populate the packet buffers with kernel skbuffs. * This allows the driver to receive packets without copying them. It also * means that 32bit userspace can't access the packet buffers. - * USE_32BIT_SHARED - * This define tells the driver to allocate memory for buffers from the - * 32bit sahred region instead of the kernel memory space. * USE_HW_TCPUDP_CHECKSUM * Controls if the Octeon TCP/UDP checksum engine is used for packet * output. If this is zero, the kernel will perform the checksum in @@ -75,19 +72,12 @@ #define CONFIG_CAVIUM_RESERVE32 0 #endif -#if CONFIG_CAVIUM_RESERVE32 -#define USE_32BIT_SHARED 1 -#define USE_SKBUFFS_IN_HW 0 -#define REUSE_SKBUFFS_WITHOUT_FREE 0 -#else -#define USE_32BIT_SHARED 0 #define USE_SKBUFFS_IN_HW 1 #ifdef CONFIG_NETFILTER #define REUSE_SKBUFFS_WITHOUT_FREE 0 #else #define REUSE_SKBUFFS_WITHOUT_FREE 1 #endif -#endif /* Max interrupts per second per core */ #define INTERRUPT_LIMIT 10000 diff --git a/drivers/staging/octeon/ethernet-mem.c b/drivers/staging/octeon/ethernet-mem.c index b595903..7090521 100644 --- a/drivers/staging/octeon/ethernet-mem.c +++ b/drivers/staging/octeon/ethernet-mem.c @@ -26,8 +26,6 @@ **********************************************************************/ #include #include -#include -#include #include @@ -107,42 +105,17 @@ static int cvm_oct_fill_hw_memory(int pool, int size, int elements) char *memory; int freed = elements; - if (USE_32BIT_SHARED) { - extern uint64_t octeon_reserve32_memory; - - memory = - cvmx_bootmem_alloc_range(elements * size, 128, - octeon_reserve32_memory, - octeon_reserve32_memory + - (CONFIG_CAVIUM_RESERVE32 << 20) - - 1); - if (memory == NULL) - panic("Unable to allocate %u bytes for FPA pool %d\n", - elements * size, pool); - - pr_notice("Memory range %p - %p reserved for " - "hardware\n", memory, - memory + elements * size - 1); - - while (freed) { - cvmx_fpa_free(memory, pool, 0); - memory += size; - freed--; - } - } else { - while (freed) { - /* We need to force alignment to 128 bytes here */ - memory = kmalloc(size + 127, GFP_ATOMIC); - if (unlikely(memory == NULL)) { - pr_warning("Unable to allocate %u bytes for " - "FPA pool %d\n", - elements * size, pool); - break; - } - memory = (char *)(((unsigned long)memory + 127) & -128); - cvmx_fpa_free(memory, pool, 0); - freed--; + while (freed) { + /* We need to force alignment to 128 bytes here */ + memory = kmalloc(size + 127, GFP_ATOMIC); + if (unlikely(memory == NULL)) { + pr_warning("Unable to allocate %u bytes for FPA pool %d\n", + elements * size, pool); + break; } + memory = (char *)(((unsigned long)memory + 127) & -128); + cvmx_fpa_free(memory, pool, 0); + freed--; } return elements - freed; } @@ -156,27 +129,21 @@ static int cvm_oct_fill_hw_memory(int pool, int size, int elements) */ static void cvm_oct_free_hw_memory(int pool, int size, int elements) { - if (USE_32BIT_SHARED) { - pr_warning("Warning: 32 shared memory is not freeable\n"); - } else { - char *memory; - do { - memory = cvmx_fpa_alloc(pool); - if (memory) { - elements--; - kfree(phys_to_virt(cvmx_ptr_to_phys(memory))); - } - } while (memory); + char *memory; + do { + memory = cvmx_fpa_alloc(pool); + if (memory) { + elements--; + kfree(phys_to_virt(cvmx_ptr_to_phys(memory))); + } + } while (memory); - if (elements < 0) - pr_warning("Freeing of pool %u had too many " - "buffers (%d)\n", - pool, elements); - else if (elements > 0) - pr_warning("Warning: Freeing of pool %u is " - "missing %d buffers\n", - pool, elements); - } + if (elements < 0) + pr_warning("Freeing of pool %u had too many buffers (%d)\n", + pool, elements); + else if (elements > 0) + pr_warning("Warning: Freeing of pool %u is missing %d buffers\n", + pool, elements); } int cvm_oct_mem_fill_fpa(int pool, int size, int elements) diff --git a/drivers/staging/octeon/ethernet-rx.c b/drivers/staging/octeon/ethernet-rx.c index 1b237b7..f63459a 100644 --- a/drivers/staging/octeon/ethernet-rx.c +++ b/drivers/staging/octeon/ethernet-rx.c @@ -33,10 +33,6 @@ #include #include #include -#include -#include -#include -#include #include #ifdef CONFIG_XFRM #include @@ -292,39 +288,27 @@ void cvm_oct_tasklet_rx(unsigned long unused) * buffer. */ if (likely(skb_in_hw)) { - /* - * This calculation was changed in case the - * skb header is using a different address - * aliasing type than the buffer. It doesn't - * make any differnece now, but the new one is - * more correct. - */ - skb->data = - skb->head + work->packet_ptr.s.addr - - cvmx_ptr_to_phys(skb->head); + skb->data = skb->head + work->packet_ptr.s.addr - cvmx_ptr_to_phys(skb->head); prefetch(skb->data); skb->len = work->len; skb_set_tail_pointer(skb, skb->len); packet_not_copied = 1; } else { - /* * We have to copy the packet. First allocate * an skbuff for it. */ skb = dev_alloc_skb(work->len); if (!skb) { - DEBUGPRINT("Port %d failed to allocate " - "skbuff, packet dropped\n", - work->ipprt); + DEBUGPRINT("Port %d failed to allocate skbuff, packet dropped\n", + work->ipprt); cvm_oct_free_work(work); continue; } /* * Check if we've received a packet that was - * entirely stored in the work entry. This is - * untested. + * entirely stored in the work entry. */ if (unlikely(work->word2.s.bufs == 0)) { uint8_t *ptr = work->packet_data; @@ -343,15 +327,13 @@ void cvm_oct_tasklet_rx(unsigned long unused) /* No packet buffers to free */ } else { int segments = work->word2.s.bufs; - union cvmx_buf_ptr segment_ptr = - work->packet_ptr; + union cvmx_buf_ptr segment_ptr = work->packet_ptr; int len = work->len; while (segments--) { union cvmx_buf_ptr next_ptr = - *(union cvmx_buf_ptr *) - cvmx_phys_to_ptr(segment_ptr.s. - addr - 8); + *(union cvmx_buf_ptr *)cvmx_phys_to_ptr(segment_ptr.s.addr - 8); + /* * Octeon Errata PKI-100: The segment size is * wrong. Until it is fixed, calculate the @@ -361,22 +343,18 @@ void cvm_oct_tasklet_rx(unsigned long unused) * one: int segment_size = * segment_ptr.s.size; */ - int segment_size = - CVMX_FPA_PACKET_POOL_SIZE - - (segment_ptr.s.addr - - (((segment_ptr.s.addr >> 7) - - segment_ptr.s.back) << 7)); - /* Don't copy more than what is left - in the packet */ + int segment_size = CVMX_FPA_PACKET_POOL_SIZE - + (segment_ptr.s.addr - (((segment_ptr.s.addr >> 7) - segment_ptr.s.back) << 7)); + /* + * Don't copy more than what + * is left in the packet. + */ if (segment_size > len) segment_size = len; /* Copy the data into the packet */ memcpy(skb_put(skb, segment_size), - cvmx_phys_to_ptr(segment_ptr.s. - addr), + cvmx_phys_to_ptr(segment_ptr.s.addr), segment_size); - /* Reduce the amount of bytes left - to copy */ len -= segment_size; segment_ptr = next_ptr; } @@ -389,16 +367,15 @@ void cvm_oct_tasklet_rx(unsigned long unused) struct net_device *dev = cvm_oct_device[work->ipprt]; struct octeon_ethernet *priv = netdev_priv(dev); - /* Only accept packets for devices - that are currently up */ + /* + * Only accept packets for devices that are + * currently up. + */ if (likely(dev->flags & IFF_UP)) { skb->protocol = eth_type_trans(skb, dev); skb->dev = dev; - if (unlikely - (work->word2.s.not_IP - || work->word2.s.IP_exc - || work->word2.s.L4_error)) + if (unlikely(work->word2.s.not_IP || work->word2.s.IP_exc || work->word2.s.L4_error)) skb->ip_summed = CHECKSUM_NONE; else skb->ip_summed = CHECKSUM_UNNECESSARY; @@ -415,14 +392,11 @@ void cvm_oct_tasklet_rx(unsigned long unused) } netif_receive_skb(skb); } else { + /* Drop any packet received for a device that isn't up */ /* - * Drop any packet received for a - * device that isn't up. - */ - /* - DEBUGPRINT("%s: Device not up, packet dropped\n", - dev->name); - */ + DEBUGPRINT("%s: Device not up, packet dropped\n", + dev->name); + */ #ifdef CONFIG_64BIT atomic64_add(1, (atomic64_t *)&priv->stats.rx_dropped); #else @@ -435,9 +409,8 @@ void cvm_oct_tasklet_rx(unsigned long unused) * Drop any packet received for a device that * doesn't exist. */ - DEBUGPRINT("Port %d not controlled by Linux, packet " - "dropped\n", - work->ipprt); + DEBUGPRINT("Port %d not controlled by Linux, packet dropped\n", + work->ipprt); dev_kfree_skb_irq(skb); } /* diff --git a/drivers/staging/octeon/ethernet-tx.c b/drivers/staging/octeon/ethernet-tx.c index 5352941..a3594bb 100644 --- a/drivers/staging/octeon/ethernet-tx.c +++ b/drivers/staging/octeon/ethernet-tx.c @@ -31,10 +31,6 @@ #include #include #include -#include -#include -#include -#include #include #ifdef CONFIG_XFRM #include @@ -529,101 +525,6 @@ int cvm_oct_xmit_pow(struct sk_buff *skb, struct net_device *dev) } /** - * Transmit a work queue entry out of the ethernet port. Both - * the work queue entry and the packet data can optionally be - * freed. The work will be freed on error as well. - * - * @dev: Device to transmit out. - * @work_queue_entry: - * Work queue entry to send - * @do_free: True if the work queue entry and packet data should be - * freed. If false, neither will be freed. - * @qos: Index into the queues for this port to transmit on. This - * is used to implement QoS if their are multiple queues per - * port. This parameter must be between 0 and the number of - * queues per port minus 1. Values outside of this range will - * be change to zero. - * - * Returns Zero on success, negative on failure. - */ -int cvm_oct_transmit_qos(struct net_device *dev, void *work_queue_entry, - int do_free, int qos) -{ - unsigned long flags; - union cvmx_buf_ptr hw_buffer; - cvmx_pko_command_word0_t pko_command; - int dropped; - struct octeon_ethernet *priv = netdev_priv(dev); - cvmx_wqe_t *work = work_queue_entry; - - if (!(dev->flags & IFF_UP)) { - DEBUGPRINT("%s: Device not up\n", dev->name); - if (do_free) - cvm_oct_free_work(work); - return -1; - } - - /* The check on CVMX_PKO_QUEUES_PER_PORT_* is designed to completely - remove "qos" in the event neither interface supports - multiple queues per port */ - if ((CVMX_PKO_QUEUES_PER_PORT_INTERFACE0 > 1) || - (CVMX_PKO_QUEUES_PER_PORT_INTERFACE1 > 1)) { - if (qos <= 0) - qos = 0; - else if (qos >= cvmx_pko_get_num_queues(priv->port)) - qos = 0; - } else - qos = 0; - - /* Start off assuming no drop */ - dropped = 0; - - local_irq_save(flags); - cvmx_pko_send_packet_prepare(priv->port, priv->queue + qos, - CVMX_PKO_LOCK_CMD_QUEUE); - - /* Build the PKO buffer pointer */ - hw_buffer.u64 = 0; - hw_buffer.s.addr = work->packet_ptr.s.addr; - hw_buffer.s.pool = CVMX_FPA_PACKET_POOL; - hw_buffer.s.size = CVMX_FPA_PACKET_POOL_SIZE; - hw_buffer.s.back = work->packet_ptr.s.back; - - /* Build the PKO command */ - pko_command.u64 = 0; - pko_command.s.n2 = 1; /* Don't pollute L2 with the outgoing packet */ - pko_command.s.dontfree = !do_free; - pko_command.s.segs = work->word2.s.bufs; - pko_command.s.total_bytes = work->len; - - /* Check if we can use the hardware checksumming */ - if (unlikely(work->word2.s.not_IP || work->word2.s.IP_exc)) - pko_command.s.ipoffp1 = 0; - else - pko_command.s.ipoffp1 = sizeof(struct ethhdr) + 1; - - /* Send the packet to the output queue */ - if (unlikely - (cvmx_pko_send_packet_finish - (priv->port, priv->queue + qos, pko_command, hw_buffer, - CVMX_PKO_LOCK_CMD_QUEUE))) { - DEBUGPRINT("%s: Failed to send the packet\n", dev->name); - dropped = -1; - } - local_irq_restore(flags); - - if (unlikely(dropped)) { - if (do_free) - cvm_oct_free_work(work); - priv->stats.tx_dropped++; - } else if (do_free) - cvmx_fpa_free(work, CVMX_FPA_WQE_POOL, DONT_WRITEBACK(1)); - - return dropped; -} -EXPORT_SYMBOL(cvm_oct_transmit_qos); - -/** * This function frees all skb that are currently queued for TX. * * @dev: Device being shutdown diff --git a/drivers/staging/octeon/ethernet.c b/drivers/staging/octeon/ethernet.c index 4cfd4b1..4e05426 100644 --- a/drivers/staging/octeon/ethernet.c +++ b/drivers/staging/octeon/ethernet.c @@ -104,14 +104,6 @@ MODULE_PARM_DESC(pow_send_list, "\n" "\t\"eth2,spi3,spi7\" would cause these three devices to transmit\n" "\tusing the pow_send_group."); -static int disable_core_queueing = 1; -module_param(disable_core_queueing, int, 0444); -MODULE_PARM_DESC(disable_core_queueing, "\n" - "\tWhen set the networking core's tx_queue_len is set to zero. This\n" - "\tallows packets to be sent without lock contention in the packet\n" - "\tscheduler resulting in some cases in improved throughput.\n"); - - /* * The offset from mac_addr_base that should be used for the next port * that is configured. By convention, if any mgmt ports exist on the @@ -205,10 +197,6 @@ static __init void cvm_oct_configure_common_hw(void) cvmx_helper_setup_red(num_packet_buffers / 4, num_packet_buffers / 8); - /* Enable the MII interface */ - if (!octeon_is_simulation()) - cvmx_write_csr(CVMX_SMIX_EN(0), 1); - /* Register an IRQ hander for to receive POW interrupts */ r = request_irq(OCTEON_IRQ_WORKQ0 + pow_receive_group, cvm_oct_do_interrupt, IRQF_SHARED, "Ethernet", @@ -689,7 +677,6 @@ static int __init cvm_oct_init_module(void) if (dev) { /* Initialize the device private structure. */ struct octeon_ethernet *priv = netdev_priv(dev); - memset(priv, 0, sizeof(struct octeon_ethernet)); dev->netdev_ops = &cvm_oct_pow_netdev_ops; priv->imode = CVMX_HELPER_INTERFACE_MODE_DISABLED; @@ -700,19 +687,16 @@ static int __init cvm_oct_init_module(void) skb_queue_head_init(&priv->tx_free_list[qos]); if (register_netdev(dev) < 0) { - pr_err("Failed to register ethernet " - "device for POW\n"); + pr_err("Failed to register ethernet device for POW\n"); kfree(dev); } else { cvm_oct_device[CVMX_PIP_NUM_INPUT_PORTS] = dev; - pr_info("%s: POW send group %d, receive " - "group %d\n", - dev->name, pow_send_group, - pow_receive_group); + pr_info("%s: POW send group %d, receive group %d\n", + dev->name, pow_send_group, + pow_receive_group); } } else { - pr_err("Failed to allocate ethernet device " - "for POW\n"); + pr_err("Failed to allocate ethernet device for POW\n"); } } @@ -730,12 +714,9 @@ static int __init cvm_oct_init_module(void) struct net_device *dev = alloc_etherdev(sizeof(struct octeon_ethernet)); if (!dev) { - pr_err("Failed to allocate ethernet device " - "for port %d\n", port); + pr_err("Failed to allocate ethernet device for port %d\n", port); continue; } - if (disable_core_queueing) - dev->tx_queue_len = 0; /* Initialize the device private structure. */ priv = netdev_priv(dev); diff --git a/drivers/staging/octeon/octeon-ethernet.h b/drivers/staging/octeon/octeon-ethernet.h index 402a15b..208da27 100644 --- a/drivers/staging/octeon/octeon-ethernet.h +++ b/drivers/staging/octeon/octeon-ethernet.h @@ -68,47 +68,6 @@ struct octeon_ethernet { */ int cvm_oct_free_work(void *work_queue_entry); -/** - * Transmit a work queue entry out of the ethernet port. Both - * the work queue entry and the packet data can optionally be - * freed. The work will be freed on error as well. - * - * @dev: Device to transmit out. - * @work_queue_entry: - * Work queue entry to send - * @do_free: True if the work queue entry and packet data should be - * freed. If false, neither will be freed. - * @qos: Index into the queues for this port to transmit on. This - * is used to implement QoS if their are multiple queues per - * port. This parameter must be between 0 and the number of - * queues per port minus 1. Values outside of this range will - * be change to zero. - * - * Returns Zero on success, negative on failure. - */ -int cvm_oct_transmit_qos(struct net_device *dev, void *work_queue_entry, - int do_free, int qos); - -/** - * Transmit a work queue entry out of the ethernet port. Both - * the work queue entry and the packet data can optionally be - * freed. The work will be freed on error as well. This simply - * wraps cvmx_oct_transmit_qos() for backwards compatability. - * - * @dev: Device to transmit out. - * @work_queue_entry: - * Work queue entry to send - * @do_free: True if the work queue entry and packet data should be - * freed. If false, neither will be freed. - * - * Returns Zero on success, negative on failure. - */ -static inline int cvm_oct_transmit(struct net_device *dev, - void *work_queue_entry, int do_free) -{ - return cvm_oct_transmit_qos(dev, work_queue_entry, do_free, 0); -} - extern int cvm_oct_rgmii_init(struct net_device *dev); extern void cvm_oct_rgmii_uninit(struct net_device *dev); extern int cvm_oct_rgmii_open(struct net_device *dev); -- cgit v1.1 From 166bdaa9aad9903bf4330ef68feb37f220c9eac8 Mon Sep 17 00:00:00 2001 From: David Daney Date: Wed, 27 Jan 2010 13:22:53 -0800 Subject: Staging: Octeon Ethernet: Fix memory allocation. After aligning the blocks returned by kmalloc, we need to save the original pointer so they can be correctly freed. There are no guarantees about the alignment of SKB data, so we need to handle worst case alignment. Since right shifts over subtraction have no distributive property, we need to fix the back pointer calculation. Signed-off-by: David Daney To: linux-mips@linux-mips.org Patchwork: http://patchwork.linux-mips.org/patch/884/ Signed-off-by: Ralf Baechle --- drivers/staging/octeon/ethernet-mem.c | 45 ++++++++++++++++++++++------------- drivers/staging/octeon/ethernet-tx.c | 6 ++--- 2 files changed, 31 insertions(+), 20 deletions(-) (limited to 'drivers') diff --git a/drivers/staging/octeon/ethernet-mem.c b/drivers/staging/octeon/ethernet-mem.c index 7090521..53ed2f7 100644 --- a/drivers/staging/octeon/ethernet-mem.c +++ b/drivers/staging/octeon/ethernet-mem.c @@ -4,7 +4,7 @@ * Contact: support@caviumnetworks.com * This file is part of the OCTEON SDK * - * Copyright (c) 2003-2007 Cavium Networks + * Copyright (c) 2003-2010 Cavium Networks * * This file is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, Version 2, as @@ -45,7 +45,7 @@ static int cvm_oct_fill_hw_skbuff(int pool, int size, int elements) int freed = elements; while (freed) { - struct sk_buff *skb = dev_alloc_skb(size + 128); + struct sk_buff *skb = dev_alloc_skb(size + 256); if (unlikely(skb == NULL)) { pr_warning ("Failed to allocate skb for hardware pool %d\n", @@ -53,7 +53,7 @@ static int cvm_oct_fill_hw_skbuff(int pool, int size, int elements) break; } - skb_reserve(skb, 128 - (((unsigned long)skb->data) & 0x7f)); + skb_reserve(skb, 256 - (((unsigned long)skb->data) & 0x7f)); *(struct sk_buff **)(skb->data - sizeof(void *)) = skb; cvmx_fpa_free(skb->data, pool, DONT_WRITEBACK(size / 128)); freed--; @@ -91,10 +91,7 @@ static void cvm_oct_free_hw_skbuff(int pool, int size, int elements) } /** - * This function fills a hardware pool with memory. Depending - * on the config defines, this memory might come from the - * kernel or global 32bit memory allocated with - * cvmx_bootmem_alloc. + * This function fills a hardware pool with memory. * * @pool: Pool to populate * @size: Size of each buffer in the pool @@ -103,18 +100,29 @@ static void cvm_oct_free_hw_skbuff(int pool, int size, int elements) static int cvm_oct_fill_hw_memory(int pool, int size, int elements) { char *memory; + char *fpa; int freed = elements; while (freed) { - /* We need to force alignment to 128 bytes here */ - memory = kmalloc(size + 127, GFP_ATOMIC); + /* + * FPA memory must be 128 byte aligned. Since we are + * aligning we need to save the original pointer so we + * can feed it to kfree when the memory is returned to + * the kernel. + * + * We allocate an extra 256 bytes to allow for + * alignment and space for the original pointer saved + * just before the block. + */ + memory = kmalloc(size + 256, GFP_ATOMIC); if (unlikely(memory == NULL)) { pr_warning("Unable to allocate %u bytes for FPA pool %d\n", elements * size, pool); break; } - memory = (char *)(((unsigned long)memory + 127) & -128); - cvmx_fpa_free(memory, pool, 0); + fpa = (char *)(((unsigned long)memory + 256) & ~0x7fUL); + *((char **)fpa - 1) = memory; + cvmx_fpa_free(fpa, pool, 0); freed--; } return elements - freed; @@ -130,13 +138,16 @@ static int cvm_oct_fill_hw_memory(int pool, int size, int elements) static void cvm_oct_free_hw_memory(int pool, int size, int elements) { char *memory; + char *fpa; do { - memory = cvmx_fpa_alloc(pool); - if (memory) { + fpa = cvmx_fpa_alloc(pool); + if (fpa) { elements--; - kfree(phys_to_virt(cvmx_ptr_to_phys(memory))); + fpa = (char *)phys_to_virt(cvmx_ptr_to_phys(fpa)); + memory = *((char **)fpa - 1); + kfree(memory); } - } while (memory); + } while (fpa); if (elements < 0) pr_warning("Freeing of pool %u had too many buffers (%d)\n", @@ -149,7 +160,7 @@ static void cvm_oct_free_hw_memory(int pool, int size, int elements) int cvm_oct_mem_fill_fpa(int pool, int size, int elements) { int freed; - if (USE_SKBUFFS_IN_HW) + if (USE_SKBUFFS_IN_HW && pool == CVMX_FPA_PACKET_POOL) freed = cvm_oct_fill_hw_skbuff(pool, size, elements); else freed = cvm_oct_fill_hw_memory(pool, size, elements); @@ -158,7 +169,7 @@ int cvm_oct_mem_fill_fpa(int pool, int size, int elements) void cvm_oct_mem_empty_fpa(int pool, int size, int elements) { - if (USE_SKBUFFS_IN_HW) + if (USE_SKBUFFS_IN_HW && pool == CVMX_FPA_PACKET_POOL) cvm_oct_free_hw_skbuff(pool, size, elements); else cvm_oct_free_hw_memory(pool, size, elements); diff --git a/drivers/staging/octeon/ethernet-tx.c b/drivers/staging/octeon/ethernet-tx.c index a3594bb..e5695d9 100644 --- a/drivers/staging/octeon/ethernet-tx.c +++ b/drivers/staging/octeon/ethernet-tx.c @@ -4,7 +4,7 @@ * Contact: support@caviumnetworks.com * This file is part of the OCTEON SDK * - * Copyright (c) 2003-2007 Cavium Networks + * Copyright (c) 2003-2010 Cavium Networks * * This file is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, Version 2, as @@ -186,7 +186,7 @@ int cvm_oct_xmit(struct sk_buff *skb, struct net_device *dev) * shown a 25% increase in performance under some loads. */ #if REUSE_SKBUFFS_WITHOUT_FREE - fpa_head = skb->head + 128 - ((unsigned long)skb->head & 0x7f); + fpa_head = skb->head + 256 - ((unsigned long)skb->head & 0x7f); if (unlikely(skb->data < fpa_head)) { /* * printk("TX buffer beginning can't meet FPA @@ -247,7 +247,7 @@ int cvm_oct_xmit(struct sk_buff *skb, struct net_device *dev) pko_command.s.reg0 = 0; pko_command.s.dontfree = 0; - hw_buffer.s.back = (skb->data - fpa_head) >> 7; + hw_buffer.s.back = ((unsigned long)skb->data >> 7) - ((unsigned long)fpa_head >> 7); *(struct sk_buff **)(fpa_head - sizeof(void *)) = skb; /* -- cgit v1.1 From 6888fc87768eaa218b6244f2e78c55416706981a Mon Sep 17 00:00:00 2001 From: David Daney Date: Thu, 7 Jan 2010 11:05:03 -0800 Subject: Staging: Octeon Ethernet: Rewrite transmit code. Stop the queue if too many packets are queued. Restart it from a high resolution timer. Rearrange and simplify locking and SKB freeing code Signed-off-by: David Daney To: linux-mips@linux-mips.org To: gregkh@suse.de Patchwork: http://patchwork.linux-mips.org/patch/843/ Signed-off-by: Ralf Baechle --- drivers/staging/octeon/Kconfig | 1 + drivers/staging/octeon/ethernet-tx.c | 172 ++++++++++++++++++++----------- drivers/staging/octeon/ethernet-tx.h | 27 +---- drivers/staging/octeon/ethernet.c | 69 ++++++------- drivers/staging/octeon/octeon-ethernet.h | 4 + 5 files changed, 150 insertions(+), 123 deletions(-) (limited to 'drivers') diff --git a/drivers/staging/octeon/Kconfig b/drivers/staging/octeon/Kconfig index 638ad6b..579b8f1 100644 --- a/drivers/staging/octeon/Kconfig +++ b/drivers/staging/octeon/Kconfig @@ -3,6 +3,7 @@ config OCTEON_ETHERNET depends on CPU_CAVIUM_OCTEON select PHYLIB select MDIO_OCTEON + select HIGH_RES_TIMERS help This driver supports the builtin ethernet ports on Cavium Networks' products in the Octeon family. This driver supports the diff --git a/drivers/staging/octeon/ethernet-tx.c b/drivers/staging/octeon/ethernet-tx.c index e5695d9..05b58f8 100644 --- a/drivers/staging/octeon/ethernet-tx.c +++ b/drivers/staging/octeon/ethernet-tx.c @@ -64,6 +64,49 @@ #define GET_SKBUFF_QOS(skb) 0 #endif + +static inline int32_t cvm_oct_adjust_skb_to_free(int32_t skb_to_free, int fau) +{ + int32_t undo; + undo = skb_to_free > 0 ? MAX_SKB_TO_FREE : skb_to_free + MAX_SKB_TO_FREE; + if (undo > 0) + cvmx_fau_atomic_add32(fau, -undo); + skb_to_free = -skb_to_free > MAX_SKB_TO_FREE ? MAX_SKB_TO_FREE : -skb_to_free; + return skb_to_free; +} + +void cvm_oct_free_tx_skbs(struct octeon_ethernet *priv) +{ + int32_t skb_to_free; + int qos, queues_per_port; + queues_per_port = cvmx_pko_get_num_queues(priv->port); + /* Drain any pending packets in the free list */ + for (qos = 0; qos < queues_per_port; qos++) { + if (skb_queue_len(&priv->tx_free_list[qos]) == 0) + continue; + skb_to_free = cvmx_fau_fetch_and_add32(priv->fau+qos*4, MAX_SKB_TO_FREE); + skb_to_free = cvm_oct_adjust_skb_to_free(skb_to_free, priv->fau+qos*4); + + while (skb_to_free > 0) { + dev_kfree_skb_any(skb_dequeue(&priv->tx_free_list[qos])); + skb_to_free--; + } + } +} + +enum hrtimer_restart cvm_oct_restart_tx(struct hrtimer *timer) +{ + struct octeon_ethernet *priv = container_of(timer, struct octeon_ethernet, tx_restart_timer); + struct net_device *dev = cvm_oct_device[priv->port]; + + cvm_oct_free_tx_skbs(priv); + + if (netif_queue_stopped(dev)) + netif_wake_queue(dev); + + return HRTIMER_NORESTART; +} + /** * Packet transmit * @@ -77,13 +120,13 @@ int cvm_oct_xmit(struct sk_buff *skb, struct net_device *dev) union cvmx_buf_ptr hw_buffer; uint64_t old_scratch; uint64_t old_scratch2; - int dropped; int qos; - int queue_it_up; + enum {QUEUE_CORE, QUEUE_HW, QUEUE_DROP} queue_type; struct octeon_ethernet *priv = netdev_priv(dev); + struct sk_buff *to_free_list; int32_t skb_to_free; - int32_t undo; int32_t buffers_to_free; + unsigned long flags; #if REUSE_SKBUFFS_WITHOUT_FREE unsigned char *fpa_head; #endif @@ -94,9 +137,6 @@ int cvm_oct_xmit(struct sk_buff *skb, struct net_device *dev) */ prefetch(priv); - /* Start off assuming no drop */ - dropped = 0; - /* * The check on CVMX_PKO_QUEUES_PER_PORT_* is designed to * completely remove "qos" in the event neither interface @@ -268,9 +308,9 @@ int cvm_oct_xmit(struct sk_buff *skb, struct net_device *dev) skb->tc_verd = 0; #endif /* CONFIG_NET_CLS_ACT */ #endif /* CONFIG_NET_SCHED */ +#endif /* REUSE_SKBUFFS_WITHOUT_FREE */ dont_put_skbuff_in_hw: -#endif /* REUSE_SKBUFFS_WITHOUT_FREE */ /* Check if we can use the hardware checksumming */ if (USE_HW_TCPUDP_CHECKSUM && (skb->protocol == htons(ETH_P_IP)) && @@ -295,18 +335,7 @@ dont_put_skbuff_in_hw: cvmx_fau_fetch_and_add32(FAU_NUM_PACKET_BUFFERS_TO_FREE, 0); } - /* - * We try to claim MAX_SKB_TO_FREE buffers. If there were not - * that many available, we have to un-claim (undo) any that - * were in excess. If skb_to_free is positive we will free - * that many buffers. - */ - undo = skb_to_free > 0 ? - MAX_SKB_TO_FREE : skb_to_free + MAX_SKB_TO_FREE; - if (undo > 0) - cvmx_fau_atomic_add32(priv->fau+qos*4, -undo); - skb_to_free = -skb_to_free > MAX_SKB_TO_FREE ? - MAX_SKB_TO_FREE : -skb_to_free; + skb_to_free = cvm_oct_adjust_skb_to_free(skb_to_free, priv->fau+qos*4); /* * If we're sending faster than the receive can free them then @@ -317,60 +346,83 @@ dont_put_skbuff_in_hw: pko_command.s.reg0 = priv->fau + qos * 4; } - cvmx_pko_send_packet_prepare(priv->port, priv->queue + qos, - CVMX_PKO_LOCK_CMD_QUEUE); + if (pko_command.s.dontfree) + queue_type = QUEUE_CORE; + else + queue_type = QUEUE_HW; + + spin_lock_irqsave(&priv->tx_free_list[qos].lock, flags); /* Drop this packet if we have too many already queued to the HW */ - if (unlikely - (skb_queue_len(&priv->tx_free_list[qos]) >= MAX_OUT_QUEUE_DEPTH)) { - /* - DEBUGPRINT("%s: Tx dropped. Too many queued\n", dev->name); - */ - dropped = 1; + if (unlikely(skb_queue_len(&priv->tx_free_list[qos]) >= MAX_OUT_QUEUE_DEPTH)) { + if (dev->tx_queue_len != 0) { + /* Drop the lock when notifying the core. */ + spin_unlock_irqrestore(&priv->tx_free_list[qos].lock, flags); + netif_stop_queue(dev); + hrtimer_start(&priv->tx_restart_timer, + priv->tx_restart_interval, HRTIMER_MODE_REL); + spin_lock_irqsave(&priv->tx_free_list[qos].lock, flags); + + } else { + /* If not using normal queueing. */ + queue_type = QUEUE_DROP; + goto skip_xmit; + } } + + cvmx_pko_send_packet_prepare(priv->port, priv->queue + qos, + CVMX_PKO_LOCK_NONE); + /* Send the packet to the output queue */ - else if (unlikely - (cvmx_pko_send_packet_finish - (priv->port, priv->queue + qos, pko_command, hw_buffer, - CVMX_PKO_LOCK_CMD_QUEUE))) { + if (unlikely(cvmx_pko_send_packet_finish(priv->port, + priv->queue + qos, + pko_command, hw_buffer, + CVMX_PKO_LOCK_NONE))) { DEBUGPRINT("%s: Failed to send the packet\n", dev->name); - dropped = 1; + queue_type = QUEUE_DROP; } +skip_xmit: + to_free_list = NULL; - if (USE_ASYNC_IOBDMA) { - /* Restore the scratch area */ - cvmx_scratch_write64(CVMX_SCR_SCRATCH, old_scratch); - cvmx_scratch_write64(CVMX_SCR_SCRATCH + 8, old_scratch2); + switch (queue_type) { + case QUEUE_DROP: + skb->next = to_free_list; + to_free_list = skb; + priv->stats.tx_dropped++; + break; + case QUEUE_HW: + cvmx_fau_atomic_add32(FAU_NUM_PACKET_BUFFERS_TO_FREE, -1); + break; + case QUEUE_CORE: + __skb_queue_tail(&priv->tx_free_list[qos], skb); + break; + default: + BUG(); } - queue_it_up = 0; - if (unlikely(dropped)) { - dev_kfree_skb_any(skb); - priv->stats.tx_dropped++; - } else { - if (USE_SKBUFFS_IN_HW) { - /* Put this packet on the queue to be freed later */ - if (pko_command.s.dontfree) - queue_it_up = 1; - else - cvmx_fau_atomic_add32 - (FAU_NUM_PACKET_BUFFERS_TO_FREE, -1); - } else { - /* Put this packet on the queue to be freed later */ - queue_it_up = 1; - } + while (skb_to_free > 0) { + struct sk_buff *t = __skb_dequeue(&priv->tx_free_list[qos]); + t->next = to_free_list; + to_free_list = t; + skb_to_free--; } - if (queue_it_up) { - spin_lock(&priv->tx_free_list[qos].lock); - __skb_queue_tail(&priv->tx_free_list[qos], skb); - cvm_oct_free_tx_skbs(priv, skb_to_free, qos, 0); - spin_unlock(&priv->tx_free_list[qos].lock); - } else { - cvm_oct_free_tx_skbs(priv, skb_to_free, qos, 1); + spin_unlock_irqrestore(&priv->tx_free_list[qos].lock, flags); + + /* Do the actual freeing outside of the lock. */ + while (to_free_list) { + struct sk_buff *t = to_free_list; + to_free_list = to_free_list->next; + dev_kfree_skb_any(t); } - return 0; + if (USE_ASYNC_IOBDMA) { + /* Restore the scratch area */ + cvmx_scratch_write64(CVMX_SCR_SCRATCH, old_scratch); + cvmx_scratch_write64(CVMX_SCR_SCRATCH + 8, old_scratch2); + } + + return NETDEV_TX_OK; } /** diff --git a/drivers/staging/octeon/ethernet-tx.h b/drivers/staging/octeon/ethernet-tx.h index c0bebf7..b628d8c 100644 --- a/drivers/staging/octeon/ethernet-tx.h +++ b/drivers/staging/octeon/ethernet-tx.h @@ -30,28 +30,5 @@ int cvm_oct_xmit_pow(struct sk_buff *skb, struct net_device *dev); int cvm_oct_transmit_qos(struct net_device *dev, void *work_queue_entry, int do_free, int qos); void cvm_oct_tx_shutdown(struct net_device *dev); - -/** - * Free dead transmit skbs. - * - * @priv: The driver data - * @skb_to_free: The number of SKBs to free (free none if negative). - * @qos: The queue to free from. - * @take_lock: If true, acquire the skb list lock. - */ -static inline void cvm_oct_free_tx_skbs(struct octeon_ethernet *priv, - int skb_to_free, - int qos, int take_lock) -{ - /* Free skbuffs not in use by the hardware. */ - if (skb_to_free > 0) { - if (take_lock) - spin_lock(&priv->tx_free_list[qos].lock); - while (skb_to_free > 0) { - dev_kfree_skb(__skb_dequeue(&priv->tx_free_list[qos])); - skb_to_free--; - } - if (take_lock) - spin_unlock(&priv->tx_free_list[qos].lock); - } -} +void cvm_oct_free_tx_skbs(struct octeon_ethernet *priv); +enum hrtimer_restart cvm_oct_restart_tx(struct hrtimer *timer); diff --git a/drivers/staging/octeon/ethernet.c b/drivers/staging/octeon/ethernet.c index 4e05426..973178a 100644 --- a/drivers/staging/octeon/ethernet.c +++ b/drivers/staging/octeon/ethernet.c @@ -131,50 +131,29 @@ struct net_device *cvm_oct_device[TOTAL_NUMBER_OF_PORTS]; */ static void cvm_do_timer(unsigned long arg) { - int32_t skb_to_free, undo; - int queues_per_port; - int qos; - struct octeon_ethernet *priv; static int port; - - if (port >= CVMX_PIP_NUM_INPUT_PORTS) { + if (port < CVMX_PIP_NUM_INPUT_PORTS) { + if (cvm_oct_device[port]) { + struct octeon_ethernet *priv = netdev_priv(cvm_oct_device[port]); + if (priv->poll) + priv->poll(cvm_oct_device[port]); + cvm_oct_free_tx_skbs(priv); + cvm_oct_device[port]->netdev_ops->ndo_get_stats(cvm_oct_device[port]); + } + port++; /* - * All ports have been polled. Start the next - * iteration through the ports in one second. + * Poll the next port in a 50th of a second. This + * spreads the polling of ports out a little bit. */ + mod_timer(&cvm_oct_poll_timer, jiffies + HZ/50); + } else { port = 0; + /* + * All ports have been polled. Start the next iteration through + * the ports in one second. + */ mod_timer(&cvm_oct_poll_timer, jiffies + HZ); - return; } - if (!cvm_oct_device[port]) - goto out; - - priv = netdev_priv(cvm_oct_device[port]); - if (priv->poll) - priv->poll(cvm_oct_device[port]); - - queues_per_port = cvmx_pko_get_num_queues(port); - /* Drain any pending packets in the free list */ - for (qos = 0; qos < queues_per_port; qos++) { - if (skb_queue_len(&priv->tx_free_list[qos]) == 0) - continue; - skb_to_free = cvmx_fau_fetch_and_add32(priv->fau + qos * 4, - MAX_SKB_TO_FREE); - undo = skb_to_free > 0 ? - MAX_SKB_TO_FREE : skb_to_free + MAX_SKB_TO_FREE; - if (undo > 0) - cvmx_fau_atomic_add32(priv->fau+qos*4, -undo); - skb_to_free = -skb_to_free > MAX_SKB_TO_FREE ? - MAX_SKB_TO_FREE : -skb_to_free; - cvm_oct_free_tx_skbs(priv, skb_to_free, qos, 1); - } - cvm_oct_device[port]->netdev_ops->ndo_get_stats(cvm_oct_device[port]); - -out: - port++; - /* Poll the next port in a 50th of a second. - This spreads the polling of ports out a little bit */ - mod_timer(&cvm_oct_poll_timer, jiffies + HZ / 50); } /** @@ -678,6 +657,18 @@ static int __init cvm_oct_init_module(void) /* Initialize the device private structure. */ struct octeon_ethernet *priv = netdev_priv(dev); + hrtimer_init(&priv->tx_restart_timer, + CLOCK_MONOTONIC, + HRTIMER_MODE_REL); + priv->tx_restart_timer.function = cvm_oct_restart_tx; + + /* + * Default for 10GE 5000nS enough time to + * transmit about 100 64byte packtes. 1GE + * interfaces will get 50000nS below. + */ + priv->tx_restart_interval = ktime_set(0, 5000); + dev->netdev_ops = &cvm_oct_pow_netdev_ops; priv->imode = CVMX_HELPER_INTERFACE_MODE_DISABLED; priv->port = CVMX_PIP_NUM_INPUT_PORTS; @@ -757,6 +748,7 @@ static int __init cvm_oct_init_module(void) case CVMX_HELPER_INTERFACE_MODE_SGMII: dev->netdev_ops = &cvm_oct_sgmii_netdev_ops; + priv->tx_restart_interval = ktime_set(0, 50000); strcpy(dev->name, "eth%d"); break; @@ -768,6 +760,7 @@ static int __init cvm_oct_init_module(void) case CVMX_HELPER_INTERFACE_MODE_RGMII: case CVMX_HELPER_INTERFACE_MODE_GMII: dev->netdev_ops = &cvm_oct_rgmii_netdev_ops; + priv->tx_restart_interval = ktime_set(0, 50000); strcpy(dev->name, "eth%d"); break; } diff --git a/drivers/staging/octeon/octeon-ethernet.h b/drivers/staging/octeon/octeon-ethernet.h index 208da27..203c6a9 100644 --- a/drivers/staging/octeon/octeon-ethernet.h +++ b/drivers/staging/octeon/octeon-ethernet.h @@ -31,6 +31,8 @@ #ifndef OCTEON_ETHERNET_H #define OCTEON_ETHERNET_H +#include + /** * This is the definition of the Ethernet driver's private * driver state stored in netdev_priv(dev). @@ -57,6 +59,8 @@ struct octeon_ethernet { uint64_t link_info; /* Called periodically to check link status */ void (*poll) (struct net_device *dev); + struct hrtimer tx_restart_timer; + ktime_t tx_restart_interval; }; /** -- cgit v1.1 From 3368c784bcf77124aaf39372e627016c36bd4472 Mon Sep 17 00:00:00 2001 From: David Daney Date: Thu, 7 Jan 2010 11:05:04 -0800 Subject: Staging: Octeon Ethernet: Convert to NAPI. Convert the driver to be a reasonably well behaved NAPI citizen. There is one NAPI instance per CPU shared between all input ports. As receive backlog increases, NAPI is scheduled on additional CPUs. Receive buffer refill code factored out so it can also be called from the periodic timer. This is needed to recover from temporary buffer starvation conditions. Signed-off-by: David Daney To: linux-mips@linux-mips.org To: gregkh@suse.de Patchwork: http://patchwork.linux-mips.org/patch/839/ Signed-off-by: Ralf Baechle --- drivers/staging/octeon/ethernet-defines.h | 18 -- drivers/staging/octeon/ethernet-rx.c | 300 +++++++++++++++++++----------- drivers/staging/octeon/ethernet-rx.h | 25 ++- drivers/staging/octeon/ethernet.c | 52 ++---- drivers/staging/octeon/octeon-ethernet.h | 3 + 5 files changed, 235 insertions(+), 163 deletions(-) (limited to 'drivers') diff --git a/drivers/staging/octeon/ethernet-defines.h b/drivers/staging/octeon/ethernet-defines.h index 6b8065f..9c4910e 100644 --- a/drivers/staging/octeon/ethernet-defines.h +++ b/drivers/staging/octeon/ethernet-defines.h @@ -45,10 +45,6 @@ * Controls if the Octeon TCP/UDP checksum engine is used for packet * output. If this is zero, the kernel will perform the checksum in * software. - * USE_MULTICORE_RECEIVE - * Process receive interrupts on multiple cores. This spreads the network - * load across the first 8 processors. If ths is zero, only one core - * processes incomming packets. * USE_ASYNC_IOBDMA * Use asynchronous IO access to hardware. This uses Octeon's asynchronous * IOBDMAs to issue IO accesses without stalling. Set this to zero @@ -79,15 +75,8 @@ #define REUSE_SKBUFFS_WITHOUT_FREE 1 #endif -/* Max interrupts per second per core */ -#define INTERRUPT_LIMIT 10000 - -/* Don't limit the number of interrupts */ -/*#define INTERRUPT_LIMIT 0 */ #define USE_HW_TCPUDP_CHECKSUM 1 -#define USE_MULTICORE_RECEIVE 1 - /* Enable Random Early Dropping under load */ #define USE_RED 1 #define USE_ASYNC_IOBDMA (CONFIG_CAVIUM_OCTEON_CVMSEG_SIZE > 0) @@ -105,17 +94,10 @@ /* Use this to not have FPA frees control L2 */ /*#define DONT_WRITEBACK(x) 0 */ -/* Maximum number of packets to process per interrupt. */ -#define MAX_RX_PACKETS 120 /* Maximum number of SKBs to try to free per xmit packet. */ #define MAX_SKB_TO_FREE 10 #define MAX_OUT_QUEUE_DEPTH 1000 -#ifndef CONFIG_SMP -#undef USE_MULTICORE_RECEIVE -#define USE_MULTICORE_RECEIVE 0 -#endif - #define IP_PROTOCOL_TCP 6 #define IP_PROTOCOL_UDP 0x11 diff --git a/drivers/staging/octeon/ethernet-rx.c b/drivers/staging/octeon/ethernet-rx.c index f63459a..b2e6ab6 100644 --- a/drivers/staging/octeon/ethernet-rx.c +++ b/drivers/staging/octeon/ethernet-rx.c @@ -4,7 +4,7 @@ * Contact: support@caviumnetworks.com * This file is part of the OCTEON SDK * - * Copyright (c) 2003-2007 Cavium Networks + * Copyright (c) 2003-2010 Cavium Networks * * This file is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, Version 2, as @@ -27,12 +27,14 @@ #include #include #include +#include #include #include #include #include #include #include +#include #include #ifdef CONFIG_XFRM #include @@ -44,8 +46,9 @@ #include #include "ethernet-defines.h" -#include "octeon-ethernet.h" #include "ethernet-mem.h" +#include "ethernet-rx.h" +#include "octeon-ethernet.h" #include "ethernet-util.h" #include "cvmx-helper.h" @@ -57,56 +60,82 @@ #include "cvmx-gmxx-defs.h" -struct cvm_tasklet_wrapper { - struct tasklet_struct t; -}; +struct cvm_napi_wrapper { + struct napi_struct napi; +} ____cacheline_aligned_in_smp; -/* - * Aligning the tasklet_struct on cachline boundries seems to decrease - * throughput even though in theory it would reduce contantion on the - * cache lines containing the locks. - */ +static struct cvm_napi_wrapper cvm_oct_napi[NR_CPUS] __cacheline_aligned_in_smp; -static struct cvm_tasklet_wrapper cvm_oct_tasklet[NR_CPUS]; +struct cvm_oct_core_state { + int baseline_cores; + /* + * The number of additional cores that could be processing + * input packtes. + */ + atomic_t available_cores; + cpumask_t cpu_state; +} ____cacheline_aligned_in_smp; -/** - * Interrupt handler. The interrupt occurs whenever the POW - * transitions from 0->1 packets in our group. - * - * @cpl: - * @dev_id: - * @regs: - * Returns - */ -irqreturn_t cvm_oct_do_interrupt(int cpl, void *dev_id) +static struct cvm_oct_core_state core_state __cacheline_aligned_in_smp; + +static void cvm_oct_enable_napi(void *_) { - /* Acknowledge the interrupt */ - if (INTERRUPT_LIMIT) - cvmx_write_csr(CVMX_POW_WQ_INT, 1 << pow_receive_group); - else - cvmx_write_csr(CVMX_POW_WQ_INT, 0x10001 << pow_receive_group); - preempt_disable(); - tasklet_schedule(&cvm_oct_tasklet[smp_processor_id()].t); - preempt_enable(); - return IRQ_HANDLED; + int cpu = smp_processor_id(); + napi_schedule(&cvm_oct_napi[cpu].napi); +} + +static void cvm_oct_enable_one_cpu(void) +{ + int v; + int cpu; + + /* Check to see if more CPUs are available for receive processing... */ + v = atomic_sub_if_positive(1, &core_state.available_cores); + if (v < 0) + return; + + /* ... if a CPU is available, Turn on NAPI polling for that CPU. */ + for_each_online_cpu(cpu) { + if (!cpu_test_and_set(cpu, core_state.cpu_state)) { + v = smp_call_function_single(cpu, cvm_oct_enable_napi, + NULL, 0); + if (v) + panic("Can't enable NAPI."); + break; + } + } +} + +static void cvm_oct_no_more_work(void) +{ + int cpu = smp_processor_id(); + + /* + * CPU zero is special. It always has the irq enabled when + * waiting for incoming packets. + */ + if (cpu == 0) { + enable_irq(OCTEON_IRQ_WORKQ0 + pow_receive_group); + return; + } + + cpu_clear(cpu, core_state.cpu_state); + atomic_add(1, &core_state.available_cores); } -#ifdef CONFIG_NET_POLL_CONTROLLER /** - * This is called when the kernel needs to manually poll the - * device. For Octeon, this is simply calling the interrupt - * handler. We actually poll all the devices, not just the - * one supplied. + * Interrupt handler. The interrupt occurs whenever the POW + * has packets in our group. * - * @dev: Device to poll. Unused */ -void cvm_oct_poll_controller(struct net_device *dev) +static irqreturn_t cvm_oct_do_interrupt(int cpl, void *dev_id) { - preempt_disable(); - tasklet_schedule(&cvm_oct_tasklet[smp_processor_id()].t); - preempt_enable(); + /* Disable the IRQ and start napi_poll. */ + disable_irq_nosync(OCTEON_IRQ_WORKQ0 + pow_receive_group); + cvm_oct_enable_napi(NULL); + + return IRQ_HANDLED; } -#endif /** * This is called on receive errors, and determines if the packet @@ -195,19 +224,19 @@ static inline int cvm_oct_check_rcv_error(cvmx_wqe_t *work) } /** - * Tasklet function that is scheduled on a core when an interrupt occurs. + * The NAPI poll function. * - * @unused: + * @napi: The NAPI instance, or null if called from cvm_oct_poll_controller + * @budget: Maximum number of packets to receive. */ -void cvm_oct_tasklet_rx(unsigned long unused) +static int cvm_oct_napi_poll(struct napi_struct *napi, int budget) { - const int coreid = cvmx_get_core_num(); - uint64_t old_group_mask; - uint64_t old_scratch; - int rx_count = 0; - int number_to_free; - int num_freed; - int packet_not_copied; + const int coreid = cvmx_get_core_num(); + uint64_t old_group_mask; + uint64_t old_scratch; + int rx_count = 0; + int did_work_request = 0; + int packet_not_copied; /* Prefetch cvm_oct_device since we know we need it soon */ prefetch(cvm_oct_device); @@ -223,59 +252,63 @@ void cvm_oct_tasklet_rx(unsigned long unused) cvmx_write_csr(CVMX_POW_PP_GRP_MSKX(coreid), (old_group_mask & ~0xFFFFull) | 1 << pow_receive_group); - if (USE_ASYNC_IOBDMA) + if (USE_ASYNC_IOBDMA) { cvmx_pow_work_request_async(CVMX_SCR_SCRATCH, CVMX_POW_NO_WAIT); + did_work_request = 1; + } - while (1) { + while (rx_count < budget) { struct sk_buff *skb = NULL; + struct sk_buff **pskb = NULL; int skb_in_hw; cvmx_wqe_t *work; - if (USE_ASYNC_IOBDMA) { + if (USE_ASYNC_IOBDMA && did_work_request) work = cvmx_pow_work_response_async(CVMX_SCR_SCRATCH); - } else { - if ((INTERRUPT_LIMIT == 0) - || likely(rx_count < MAX_RX_PACKETS)) - work = - cvmx_pow_work_request_sync - (CVMX_POW_NO_WAIT); - else - work = NULL; - } + else + work = cvmx_pow_work_request_sync(CVMX_POW_NO_WAIT); + prefetch(work); - if (work == NULL) + did_work_request = 0; + if (work == NULL) { + union cvmx_pow_wq_int wq_int; + wq_int.u64 = 0; + wq_int.s.iq_dis = 1 << pow_receive_group; + wq_int.s.wq_int = 1 << pow_receive_group; + cvmx_write_csr(CVMX_POW_WQ_INT, wq_int.u64); break; + } + pskb = (struct sk_buff **)(cvm_oct_get_buffer_ptr(work->packet_ptr) - sizeof(void *)); + prefetch(pskb); - /* - * Limit each core to processing MAX_RX_PACKETS - * packets without a break. This way the RX can't - * starve the TX task. - */ - if (USE_ASYNC_IOBDMA) { - - if ((INTERRUPT_LIMIT == 0) - || likely(rx_count < MAX_RX_PACKETS)) - cvmx_pow_work_request_async_nocheck - (CVMX_SCR_SCRATCH, CVMX_POW_NO_WAIT); - else { - cvmx_scratch_write64(CVMX_SCR_SCRATCH, - 0x8000000000000000ull); - cvmx_pow_tag_sw_null_nocheck(); - } + if (USE_ASYNC_IOBDMA && rx_count < (budget - 1)) { + cvmx_pow_work_request_async_nocheck(CVMX_SCR_SCRATCH, CVMX_POW_NO_WAIT); + did_work_request = 1; + } + + if (rx_count == 0) { + /* + * First time through, see if there is enough + * work waiting to merit waking another + * CPU. + */ + union cvmx_pow_wq_int_cntx counts; + int backlog; + int cores_in_use = core_state.baseline_cores - atomic_read(&core_state.available_cores); + counts.u64 = cvmx_read_csr(CVMX_POW_WQ_INT_CNTX(pow_receive_group)); + backlog = counts.s.iq_cnt + counts.s.ds_cnt; + if (backlog > budget * cores_in_use && napi != NULL) + cvm_oct_enable_one_cpu(); } skb_in_hw = USE_SKBUFFS_IN_HW && work->word2.s.bufs == 1; if (likely(skb_in_hw)) { - skb = - *(struct sk_buff - **)(cvm_oct_get_buffer_ptr(work->packet_ptr) - - sizeof(void *)); + skb = *pskb; prefetch(&skb->head); prefetch(&skb->len); } prefetch(cvm_oct_device[work->ipprt]); - rx_count++; /* Immediately throw away all packets with receive errors */ if (unlikely(work->word2.snoip.rcv_error)) { if (cvm_oct_check_rcv_error(work)) @@ -391,6 +424,7 @@ void cvm_oct_tasklet_rx(unsigned long unused) #endif } netif_receive_skb(skb); + rx_count++; } else { /* Drop any packet received for a device that isn't up */ /* @@ -432,47 +466,93 @@ void cvm_oct_tasklet_rx(unsigned long unused) cvm_oct_free_work(work); } } - /* Restore the original POW group mask */ cvmx_write_csr(CVMX_POW_PP_GRP_MSKX(coreid), old_group_mask); if (USE_ASYNC_IOBDMA) { /* Restore the scratch area */ cvmx_scratch_write64(CVMX_SCR_SCRATCH, old_scratch); } + cvm_oct_rx_refill_pool(0); - if (USE_SKBUFFS_IN_HW) { - /* Refill the packet buffer pool */ - number_to_free = - cvmx_fau_fetch_and_add32(FAU_NUM_PACKET_BUFFERS_TO_FREE, 0); - - if (number_to_free > 0) { - cvmx_fau_atomic_add32(FAU_NUM_PACKET_BUFFERS_TO_FREE, - -number_to_free); - num_freed = - cvm_oct_mem_fill_fpa(CVMX_FPA_PACKET_POOL, - CVMX_FPA_PACKET_POOL_SIZE, - number_to_free); - if (num_freed != number_to_free) { - cvmx_fau_atomic_add32 - (FAU_NUM_PACKET_BUFFERS_TO_FREE, - number_to_free - num_freed); - } - } + if (rx_count < budget && napi != NULL) { + /* No more work */ + napi_complete(napi); + cvm_oct_no_more_work(); } + return rx_count; +} + +#ifdef CONFIG_NET_POLL_CONTROLLER +/** + * This is called when the kernel needs to manually poll the + * device. + * + * @dev: Device to poll. Unused + */ +void cvm_oct_poll_controller(struct net_device *dev) +{ + cvm_oct_napi_poll(NULL, 16); } +#endif void cvm_oct_rx_initialize(void) { int i; - /* Initialize all of the tasklets */ - for (i = 0; i < NR_CPUS; i++) - tasklet_init(&cvm_oct_tasklet[i].t, cvm_oct_tasklet_rx, 0); + struct net_device *dev_for_napi = NULL; + union cvmx_pow_wq_int_thrx int_thr; + union cvmx_pow_wq_int_pc int_pc; + + for (i = 0; i < TOTAL_NUMBER_OF_PORTS; i++) { + if (cvm_oct_device[i]) { + dev_for_napi = cvm_oct_device[i]; + break; + } + } + + if (NULL == dev_for_napi) + panic("No net_devices were allocated."); + + if (max_rx_cpus > 1 && max_rx_cpus < num_online_cpus()) + atomic_set(&core_state.available_cores, max_rx_cpus); + else + atomic_set(&core_state.available_cores, num_online_cpus()); + core_state.baseline_cores = atomic_read(&core_state.available_cores); + + core_state.cpu_state = CPU_MASK_NONE; + for_each_possible_cpu(i) { + netif_napi_add(dev_for_napi, &cvm_oct_napi[i].napi, + cvm_oct_napi_poll, rx_napi_weight); + napi_enable(&cvm_oct_napi[i].napi); + } + /* Register an IRQ hander for to receive POW interrupts */ + i = request_irq(OCTEON_IRQ_WORKQ0 + pow_receive_group, + cvm_oct_do_interrupt, 0, "Ethernet", cvm_oct_device); + + if (i) + panic("Could not acquire Ethernet IRQ %d\n", + OCTEON_IRQ_WORKQ0 + pow_receive_group); + + disable_irq_nosync(OCTEON_IRQ_WORKQ0 + pow_receive_group); + + int_thr.u64 = 0; + int_thr.s.tc_en = 1; + int_thr.s.tc_thr = 1; + /* Enable POW interrupt when our port has at least one packet */ + cvmx_write_csr(CVMX_POW_WQ_INT_THRX(pow_receive_group), int_thr.u64); + + int_pc.u64 = 0; + int_pc.s.pc_thr = 5; + cvmx_write_csr(CVMX_POW_WQ_INT_PC, int_pc.u64); + + + /* Scheduld NAPI now. This will indirectly enable interrupts. */ + cvm_oct_enable_one_cpu(); } void cvm_oct_rx_shutdown(void) { int i; - /* Shutdown all of the tasklets */ - for (i = 0; i < NR_CPUS; i++) - tasklet_kill(&cvm_oct_tasklet[i].t); + /* Shutdown all of the NAPIs */ + for_each_possible_cpu(i) + netif_napi_del(&cvm_oct_napi[i].napi); } diff --git a/drivers/staging/octeon/ethernet-rx.h b/drivers/staging/octeon/ethernet-rx.h index a9b72b8..a0743b8 100644 --- a/drivers/staging/octeon/ethernet-rx.h +++ b/drivers/staging/octeon/ethernet-rx.h @@ -24,10 +24,29 @@ * This file may also be available under a different license from Cavium. * Contact Cavium Networks for more information *********************************************************************/ +#include "cvmx-fau.h" -irqreturn_t cvm_oct_do_interrupt(int cpl, void *dev_id); void cvm_oct_poll_controller(struct net_device *dev); -void cvm_oct_tasklet_rx(unsigned long unused); - void cvm_oct_rx_initialize(void); void cvm_oct_rx_shutdown(void); + +static inline void cvm_oct_rx_refill_pool(int fill_threshold) +{ + int number_to_free; + int num_freed; + /* Refill the packet buffer pool */ + number_to_free = + cvmx_fau_fetch_and_add32(FAU_NUM_PACKET_BUFFERS_TO_FREE, 0); + + if (number_to_free > fill_threshold) { + cvmx_fau_atomic_add32(FAU_NUM_PACKET_BUFFERS_TO_FREE, + -number_to_free); + num_freed = cvm_oct_mem_fill_fpa(CVMX_FPA_PACKET_POOL, + CVMX_FPA_PACKET_POOL_SIZE, + number_to_free); + if (num_freed != number_to_free) { + cvmx_fau_atomic_add32(FAU_NUM_PACKET_BUFFERS_TO_FREE, + number_to_free - num_freed); + } + } +} diff --git a/drivers/staging/octeon/ethernet.c b/drivers/staging/octeon/ethernet.c index 973178a..9f5b741 100644 --- a/drivers/staging/octeon/ethernet.c +++ b/drivers/staging/octeon/ethernet.c @@ -104,6 +104,16 @@ MODULE_PARM_DESC(pow_send_list, "\n" "\t\"eth2,spi3,spi7\" would cause these three devices to transmit\n" "\tusing the pow_send_group."); +int max_rx_cpus = -1; +module_param(max_rx_cpus, int, 0444); +MODULE_PARM_DESC(max_rx_cpus, "\n" + "\t\tThe maximum number of CPUs to use for packet reception.\n" + "\t\tUse -1 to use all available CPUs."); + +int rx_napi_weight = 32; +module_param(rx_napi_weight, int, 0444); +MODULE_PARM_DESC(rx_napi_weight, "The NAPI WEIGHT parameter."); + /* * The offset from mac_addr_base that should be used for the next port * that is configured. By convention, if any mgmt ports exist on the @@ -149,6 +159,15 @@ static void cvm_do_timer(unsigned long arg) } else { port = 0; /* + * FPA 0 may have been drained, try to refill it if we + * need more than num_packet_buffers / 2, otherwise + * normal receive processing will refill it. If it + * were drained, no packets could be received so + * cvm_oct_napi_poll would never be invoked to do the + * refill. + */ + cvm_oct_rx_refill_pool(num_packet_buffers / 2); + /* * All ports have been polled. Start the next iteration through * the ports in one second. */ @@ -161,7 +180,6 @@ static void cvm_do_timer(unsigned long arg) */ static __init void cvm_oct_configure_common_hw(void) { - int r; /* Setup the FPA */ cvmx_fpa_enable(); cvm_oct_mem_fill_fpa(CVMX_FPA_PACKET_POOL, CVMX_FPA_PACKET_POOL_SIZE, @@ -176,17 +194,6 @@ static __init void cvm_oct_configure_common_hw(void) cvmx_helper_setup_red(num_packet_buffers / 4, num_packet_buffers / 8); - /* Register an IRQ hander for to receive POW interrupts */ - r = request_irq(OCTEON_IRQ_WORKQ0 + pow_receive_group, - cvm_oct_do_interrupt, IRQF_SHARED, "Ethernet", - cvm_oct_device); - -#if defined(CONFIG_SMP) && 0 - if (USE_MULTICORE_RECEIVE) { - irq_set_affinity(OCTEON_IRQ_WORKQ0 + pow_receive_group, - cpu_online_mask); - } -#endif } /** @@ -616,7 +623,6 @@ static int __init cvm_oct_init_module(void) cvm_oct_mac_addr_offset = 0; cvm_oct_proc_initialize(); - cvm_oct_rx_initialize(); cvm_oct_configure_common_hw(); cvmx_helper_initialize_packet_io_global(); @@ -781,25 +787,7 @@ static int __init cvm_oct_init_module(void) } } - if (INTERRUPT_LIMIT) { - /* - * Set the POW timer rate to give an interrupt at most - * INTERRUPT_LIMIT times per second. - */ - cvmx_write_csr(CVMX_POW_WQ_INT_PC, - octeon_bootinfo->eclock_hz / (INTERRUPT_LIMIT * - 16 * 256) << 8); - - /* - * Enable POW timer interrupt. It will count when - * there are packets available. - */ - cvmx_write_csr(CVMX_POW_WQ_INT_THRX(pow_receive_group), - 0x1ful << 24); - } else { - /* Enable POW interrupt when our port has at least one packet */ - cvmx_write_csr(CVMX_POW_WQ_INT_THRX(pow_receive_group), 0x1001); - } + cvm_oct_rx_initialize(); /* Enable the poll timer for checking RGMII status */ init_timer(&cvm_oct_poll_timer); diff --git a/drivers/staging/octeon/octeon-ethernet.h b/drivers/staging/octeon/octeon-ethernet.h index 203c6a9..40b6956 100644 --- a/drivers/staging/octeon/octeon-ethernet.h +++ b/drivers/staging/octeon/octeon-ethernet.h @@ -98,4 +98,7 @@ extern int pow_receive_group; extern char pow_send_list[]; extern struct net_device *cvm_oct_device[]; +extern int max_rx_cpus; +extern int rx_napi_weight; + #endif -- cgit v1.1 From 924cc2680fbe181066ec138d369691d28d913ea2 Mon Sep 17 00:00:00 2001 From: David Daney Date: Thu, 7 Jan 2010 11:05:05 -0800 Subject: Staging: Octeon Ethernet: Enable scatter-gather. Octeon ethernet hardware can handle NETIF_F_SG, so we enable it. A gather list of up to six fragments will fit in the SKB's CB structure, so no extra memory is required. If a SKB has more than six fragments, we must linearize it. Signed-off-by: David Daney To: linux-mips@linux-mips.org To: gregkh@suse.de Patchwork: http://patchwork.linux-mips.org/patch/838/ Signed-off-by: Ralf Baechle --- drivers/staging/octeon/ethernet-tx.c | 57 +++++++++++++++++++++++++++++++----- drivers/staging/octeon/ethernet.c | 7 +++-- 2 files changed, 55 insertions(+), 9 deletions(-) (limited to 'drivers') diff --git a/drivers/staging/octeon/ethernet-tx.c b/drivers/staging/octeon/ethernet-tx.c index 05b58f8..bc67e41 100644 --- a/drivers/staging/octeon/ethernet-tx.c +++ b/drivers/staging/octeon/ethernet-tx.c @@ -53,6 +53,8 @@ #include "cvmx-gmxx-defs.h" +#define CVM_OCT_SKB_CB(skb) ((u64 *)((skb)->cb)) + /* * You can define GET_SKBUFF_QOS() to override how the skbuff output * function determines which output queue is used. The default @@ -121,6 +123,7 @@ int cvm_oct_xmit(struct sk_buff *skb, struct net_device *dev) uint64_t old_scratch; uint64_t old_scratch2; int qos; + int i; enum {QUEUE_CORE, QUEUE_HW, QUEUE_DROP} queue_type; struct octeon_ethernet *priv = netdev_priv(dev); struct sk_buff *to_free_list; @@ -171,6 +174,28 @@ int cvm_oct_xmit(struct sk_buff *skb, struct net_device *dev) } /* + * We have space for 6 segment pointers, If there will be more + * than that, we must linearize. + */ + if (unlikely(skb_shinfo(skb)->nr_frags > 5)) { + if (unlikely(__skb_linearize(skb))) { + queue_type = QUEUE_DROP; + if (USE_ASYNC_IOBDMA) { + /* Get the number of skbuffs in use by the hardware */ + CVMX_SYNCIOBDMA; + skb_to_free = cvmx_scratch_read64(CVMX_SCR_SCRATCH); + } else { + /* Get the number of skbuffs in use by the hardware */ + skb_to_free = cvmx_fau_fetch_and_add32(priv->fau + qos * 4, + MAX_SKB_TO_FREE); + } + skb_to_free = cvm_oct_adjust_skb_to_free(skb_to_free, priv->fau + qos * 4); + spin_lock_irqsave(&priv->tx_free_list[qos].lock, flags); + goto skip_xmit; + } + } + + /* * The CN3XXX series of parts has an errata (GMX-401) which * causes the GMX block to hang if a collision occurs towards * the end of a <68 byte packet. As a workaround for this, we @@ -198,13 +223,6 @@ int cvm_oct_xmit(struct sk_buff *skb, struct net_device *dev) } } - /* Build the PKO buffer pointer */ - hw_buffer.u64 = 0; - hw_buffer.s.addr = cvmx_ptr_to_phys(skb->data); - hw_buffer.s.pool = 0; - hw_buffer.s.size = - (unsigned long)skb_end_pointer(skb) - (unsigned long)skb->head; - /* Build the PKO command */ pko_command.u64 = 0; pko_command.s.n2 = 1; /* Don't pollute L2 with the outgoing packet */ @@ -215,6 +233,31 @@ int cvm_oct_xmit(struct sk_buff *skb, struct net_device *dev) pko_command.s.dontfree = 1; pko_command.s.reg0 = priv->fau + qos * 4; + + /* Build the PKO buffer pointer */ + hw_buffer.u64 = 0; + if (skb_shinfo(skb)->nr_frags == 0) { + hw_buffer.s.addr = XKPHYS_TO_PHYS((u64)skb->data); + hw_buffer.s.pool = 0; + hw_buffer.s.size = skb->len; + } else { + hw_buffer.s.addr = XKPHYS_TO_PHYS((u64)skb->data); + hw_buffer.s.pool = 0; + hw_buffer.s.size = skb_headlen(skb); + CVM_OCT_SKB_CB(skb)[0] = hw_buffer.u64; + for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) { + struct skb_frag_struct *fs = skb_shinfo(skb)->frags + i; + hw_buffer.s.addr = XKPHYS_TO_PHYS((u64)(page_address(fs->page) + fs->page_offset)); + hw_buffer.s.size = fs->size; + CVM_OCT_SKB_CB(skb)[i + 1] = hw_buffer.u64; + } + hw_buffer.s.addr = XKPHYS_TO_PHYS((u64)CVM_OCT_SKB_CB(skb)); + hw_buffer.s.size = skb_shinfo(skb)->nr_frags + 1; + pko_command.s.segs = skb_shinfo(skb)->nr_frags + 1; + pko_command.s.gather = 1; + goto dont_put_skbuff_in_hw; + } + /* * See if we can put this skb in the FPA pool. Any strange * behavior from the Linux networking stack will most likely diff --git a/drivers/staging/octeon/ethernet.c b/drivers/staging/octeon/ethernet.c index 9f5b741..9d63202 100644 --- a/drivers/staging/octeon/ethernet.c +++ b/drivers/staging/octeon/ethernet.c @@ -484,8 +484,11 @@ int cvm_oct_common_init(struct net_device *dev) && (always_use_pow || strstr(pow_send_list, dev->name))) priv->queue = -1; - if (priv->queue != -1 && USE_HW_TCPUDP_CHECKSUM) - dev->features |= NETIF_F_IP_CSUM; + if (priv->queue != -1) { + dev->features |= NETIF_F_SG; + if (USE_HW_TCPUDP_CHECKSUM) + dev->features |= NETIF_F_IP_CSUM; + } /* We do our own locking, Linux doesn't need to */ dev->features |= NETIF_F_LLTX; -- cgit v1.1 From 081f6749ae33f72b4fafea4c02976e163ef6ef37 Mon Sep 17 00:00:00 2001 From: David Daney Date: Thu, 7 Jan 2010 11:05:06 -0800 Subject: Staging: Octeon Ethernet: Use constants from in.h Signed-off-by: David Daney To: linux-mips@linux-mips.org To: gregkh@suse.de Patchwork: http://patchwork.linux-mips.org/patch/837/ Signed-off-by: Ralf Baechle --- drivers/staging/octeon/ethernet-defines.h | 3 --- drivers/staging/octeon/ethernet-tx.c | 8 ++++---- 2 files changed, 4 insertions(+), 7 deletions(-) (limited to 'drivers') diff --git a/drivers/staging/octeon/ethernet-defines.h b/drivers/staging/octeon/ethernet-defines.h index 9c4910e..00a8561 100644 --- a/drivers/staging/octeon/ethernet-defines.h +++ b/drivers/staging/octeon/ethernet-defines.h @@ -98,9 +98,6 @@ #define MAX_SKB_TO_FREE 10 #define MAX_OUT_QUEUE_DEPTH 1000 -#define IP_PROTOCOL_TCP 6 -#define IP_PROTOCOL_UDP 0x11 - #define FAU_NUM_PACKET_BUFFERS_TO_FREE (CVMX_FAU_REG_END - sizeof(uint32_t)) #define TOTAL_NUMBER_OF_PORTS (CVMX_PIP_NUM_INPUT_PORTS+1) diff --git a/drivers/staging/octeon/ethernet-tx.c b/drivers/staging/octeon/ethernet-tx.c index bc67e41..62258bd 100644 --- a/drivers/staging/octeon/ethernet-tx.c +++ b/drivers/staging/octeon/ethernet-tx.c @@ -359,8 +359,8 @@ dont_put_skbuff_in_hw: if (USE_HW_TCPUDP_CHECKSUM && (skb->protocol == htons(ETH_P_IP)) && (ip_hdr(skb)->version == 4) && (ip_hdr(skb)->ihl == 5) && ((ip_hdr(skb)->frag_off == 0) || (ip_hdr(skb)->frag_off == 1 << 14)) - && ((ip_hdr(skb)->protocol == IP_PROTOCOL_TCP) - || (ip_hdr(skb)->protocol == IP_PROTOCOL_UDP))) { + && ((ip_hdr(skb)->protocol == IPPROTO_TCP) + || (ip_hdr(skb)->protocol == IPPROTO_UDP))) { /* Use hardware checksum calc */ pko_command.s.ipoffp1 = sizeof(struct ethhdr) + 1; } @@ -550,8 +550,8 @@ int cvm_oct_xmit_pow(struct sk_buff *skb, struct net_device *dev) work->word2.s.dec_ipcomp = 0; /* FIXME */ #endif work->word2.s.tcp_or_udp = - (ip_hdr(skb)->protocol == IP_PROTOCOL_TCP) - || (ip_hdr(skb)->protocol == IP_PROTOCOL_UDP); + (ip_hdr(skb)->protocol == IPPROTO_TCP) + || (ip_hdr(skb)->protocol == IPPROTO_UDP); #if 0 /* FIXME */ work->word2.s.dec_ipsec = 0; -- cgit v1.1 From f7a904dffe30a02636053d8022498ced7e44d31c Mon Sep 17 00:00:00 2001 From: Wu Zhangjin Date: Mon, 4 Jan 2010 17:16:51 +0800 Subject: MIPS: Loongson: Change the Email address of Wu Zhangjin Currently wuzj@lemote.com is not usable; change it to wuzhangjin@gmail.com. Signed-off-by: Wu Zhangjin Cc: linux-mips@linux-mips.org Cc: yanh@lemote.com Cc: huhb@lemote.com Cc: zhangfx@lemote.com Patchwork: http://patchwork.linux-mips.org/patch/829/ Signed-off-by: Ralf Baechle --- drivers/staging/sm7xx/smtc2d.c | 2 +- drivers/staging/sm7xx/smtc2d.h | 2 +- drivers/staging/sm7xx/smtcfb.c | 2 +- drivers/staging/sm7xx/smtcfb.h | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/staging/sm7xx/smtc2d.c b/drivers/staging/sm7xx/smtc2d.c index 133b86c..2fff0a0 100644 --- a/drivers/staging/sm7xx/smtc2d.c +++ b/drivers/staging/sm7xx/smtc2d.c @@ -5,7 +5,7 @@ * Author: Boyod boyod.yang@siliconmotion.com.cn * * Copyright (C) 2009 Lemote, Inc. - * Author: Wu Zhangjin, wuzj@lemote.com + * Author: Wu Zhangjin, wuzhangjin@gmail.com * * This file is subject to the terms and conditions of the GNU General Public * License. See the file COPYING in the main directory of this archive for diff --git a/drivers/staging/sm7xx/smtc2d.h b/drivers/staging/sm7xx/smtc2d.h index 38d0c33..02b4fa2 100644 --- a/drivers/staging/sm7xx/smtc2d.h +++ b/drivers/staging/sm7xx/smtc2d.h @@ -5,7 +5,7 @@ * Author: Ge Wang, gewang@siliconmotion.com * * Copyright (C) 2009 Lemote, Inc. - * Author: Wu Zhangjin, wuzj@lemote.com + * Author: Wu Zhangjin, wuzhangjin@gmail.com * * This file is subject to the terms and conditions of the GNU General Public * License. See the file COPYING in the main directory of this archive for diff --git a/drivers/staging/sm7xx/smtcfb.c b/drivers/staging/sm7xx/smtcfb.c index 161dbc9..a4f6f49 100644 --- a/drivers/staging/sm7xx/smtcfb.c +++ b/drivers/staging/sm7xx/smtcfb.c @@ -6,7 +6,7 @@ * Boyod boyod.yang@siliconmotion.com.cn * * Copyright (C) 2009 Lemote, Inc. - * Author: Wu Zhangjin, wuzj@lemote.com + * Author: Wu Zhangjin, wuzhangjin@gmail.com * * This file is subject to the terms and conditions of the GNU General Public * License. See the file COPYING in the main directory of this archive for diff --git a/drivers/staging/sm7xx/smtcfb.h b/drivers/staging/sm7xx/smtcfb.h index 7f2c341..7ee565c 100644 --- a/drivers/staging/sm7xx/smtcfb.h +++ b/drivers/staging/sm7xx/smtcfb.h @@ -6,7 +6,7 @@ * Boyod boyod.yang@siliconmotion.com.cn * * Copyright (C) 2009 Lemote, Inc. - * Author: Wu Zhangjin, wuzj@lemote.com + * Author: Wu Zhangjin, wuzhangjin@gmail.com * * This file is subject to the terms and conditions of the GNU General Public * License. See the file COPYING in the main directory of this archive for -- cgit v1.1 From 780019ddf02f214ad61e641b57b8ac30c837e2a7 Mon Sep 17 00:00:00 2001 From: Florian Fainelli Date: Wed, 27 Jan 2010 09:10:06 +0100 Subject: MIPS: AR7: Implement clock API This patch makes the ar7 clock code implement the Linux clk API. Drivers using the various clocks available in the SoC are updated accordingly. Signed-off-by: Florian Fainelli Acked-by: Wim Van Sebroeck To: linux-mips@linux-mips.org Cc: Wim Van Sebroeck Cc: netdev@vger.kernel.org Cc: David Miller Patchwork: http://patchwork.linux-mips.org/patch/881/ Signed-off-by: Ralf Baechle --- drivers/net/cpmac.c | 10 +++++++++- drivers/watchdog/ar7_wdt.c | 18 +++++++++++++++--- 2 files changed, 24 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/net/cpmac.c b/drivers/net/cpmac.c index 8d0be26..bf2072e 100644 --- a/drivers/net/cpmac.c +++ b/drivers/net/cpmac.c @@ -36,6 +36,7 @@ #include #include #include +#include #include #include @@ -294,9 +295,16 @@ static int cpmac_mdio_write(struct mii_bus *bus, int phy_id, static int cpmac_mdio_reset(struct mii_bus *bus) { + struct clk *cpmac_clk; + + cpmac_clk = clk_get(&bus->dev, "cpmac"); + if (IS_ERR(cpmac_clk)) { + printk(KERN_ERR "unable to get cpmac clock\n"); + return -1; + } ar7_device_reset(AR7_RESET_BIT_MDIO); cpmac_write(bus->priv, CPMAC_MDIO_CONTROL, MDIOC_ENABLE | - MDIOC_CLKDIV(ar7_cpmac_freq() / 2200000 - 1)); + MDIOC_CLKDIV(clk_get_rate(cpmac_clk) / 2200000 - 1)); return 0; } diff --git a/drivers/watchdog/ar7_wdt.c b/drivers/watchdog/ar7_wdt.c index 2e94b71..2bb95cd 100644 --- a/drivers/watchdog/ar7_wdt.c +++ b/drivers/watchdog/ar7_wdt.c @@ -34,6 +34,7 @@ #include #include #include +#include #include #include @@ -80,6 +81,8 @@ static struct resource *ar7_regs_wdt; /* Pointer to the remapped WDT IO space */ static struct ar7_wdt *ar7_wdt; +static struct clk *vbus_clk; + static void ar7_wdt_kick(u32 value) { WRITE_REG(ar7_wdt->kick_lock, 0x5555); @@ -138,17 +141,19 @@ static void ar7_wdt_disable(u32 value) static void ar7_wdt_update_margin(int new_margin) { u32 change; + u32 vbus_rate; - change = new_margin * (ar7_vbus_freq() / prescale_value); + vbus_rate = clk_get_rate(vbus_clk); + change = new_margin * (vbus_rate / prescale_value); if (change < 1) change = 1; if (change > 0xffff) change = 0xffff; ar7_wdt_change(change); - margin = change * prescale_value / ar7_vbus_freq(); + margin = change * prescale_value / vbus_rate; printk(KERN_INFO DRVNAME ": timer margin %d seconds (prescale %d, change %d, freq %d)\n", - margin, prescale_value, change, ar7_vbus_freq()); + margin, prescale_value, change, vbus_rate); } static void ar7_wdt_enable_wdt(void) @@ -298,6 +303,13 @@ static int __devinit ar7_wdt_probe(struct platform_device *pdev) goto out_mem_region; } + vbus_clk = clk_get(NULL, "vbus"); + if (IS_ERR(vbus_clk)) { + printk(KERN_ERR DRVNAME ": could not get vbus clock\n"); + rc = PTR_ERR(vbus_clk); + goto out_mem_region; + } + ar7_wdt_disable_wdt(); ar7_wdt_prescale(prescale_value); ar7_wdt_update_margin(margin); -- cgit v1.1 From 3b839070f11295735ce8d9ef580c5eb23417aabf Mon Sep 17 00:00:00 2001 From: Manuel Lauss Date: Wed, 14 Oct 2009 09:38:06 +0200 Subject: MMC: AU1xMMC: Allow platforms to disable host capabilities Although the hardware supports a 4/8bit SD interface and the driver unconditionally advertises all hardware caps to the MMC core, not all datalines may actually be wired up. This patch introduces another field to au1xmmc platform data allowing platforms to disable certain advanced host controller features. Signed-off-by: Manuel Lauss To: linux-mmc@vger.kernel.org CC: Linux-MIPS Patchwork: http://patchwork.linux-mips.org/patch/460/ Signed-off-by: Ralf Baechle --- drivers/mmc/host/au1xmmc.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'drivers') diff --git a/drivers/mmc/host/au1xmmc.c b/drivers/mmc/host/au1xmmc.c index c8649df..57b2119 100644 --- a/drivers/mmc/host/au1xmmc.c +++ b/drivers/mmc/host/au1xmmc.c @@ -1017,6 +1017,10 @@ static int __devinit au1xmmc_probe(struct platform_device *pdev) } else mmc->caps |= MMC_CAP_NEEDS_POLL; + /* platform may not be able to use all advertised caps */ + if (host->platdata) + mmc->caps &= ~(host->platdata->mask_host_caps); + tasklet_init(&host->data_task, au1xmmc_tasklet_data, (unsigned long)host); -- cgit v1.1 From 1d08f00d576c62f1c7a96900a14648df33b3939a Mon Sep 17 00:00:00 2001 From: David Daney Date: Mon, 15 Feb 2010 12:13:16 -0800 Subject: Staging: octeon: remove unneeded includes Signed-off-by: David Daney To: linux-mips@linux-mips.org To: netdev@vger.kernel.org To: gregkh@suse.de Patchwork: http://patchwork.linux-mips.org/patch/964/ Signed-off-by: Ralf Baechle --- drivers/staging/octeon/ethernet-mdio.h | 1 - drivers/staging/octeon/ethernet-rgmii.c | 1 - drivers/staging/octeon/ethernet-sgmii.c | 1 - drivers/staging/octeon/ethernet-spi.c | 1 - drivers/staging/octeon/ethernet-xaui.c | 1 - drivers/staging/octeon/ethernet.c | 1 - 6 files changed, 6 deletions(-) (limited to 'drivers') diff --git a/drivers/staging/octeon/ethernet-mdio.h b/drivers/staging/octeon/ethernet-mdio.h index 55d0614a..a417d4f 100644 --- a/drivers/staging/octeon/ethernet-mdio.h +++ b/drivers/staging/octeon/ethernet-mdio.h @@ -32,7 +32,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/staging/octeon/ethernet-rgmii.c b/drivers/staging/octeon/ethernet-rgmii.c index 3820f1e..f90d46e 100644 --- a/drivers/staging/octeon/ethernet-rgmii.c +++ b/drivers/staging/octeon/ethernet-rgmii.c @@ -26,7 +26,6 @@ **********************************************************************/ #include #include -#include #include #include diff --git a/drivers/staging/octeon/ethernet-sgmii.c b/drivers/staging/octeon/ethernet-sgmii.c index 6061d01..2d8589e 100644 --- a/drivers/staging/octeon/ethernet-sgmii.c +++ b/drivers/staging/octeon/ethernet-sgmii.c @@ -26,7 +26,6 @@ **********************************************************************/ #include #include -#include #include #include diff --git a/drivers/staging/octeon/ethernet-spi.c b/drivers/staging/octeon/ethernet-spi.c index 00dc0f4..b58b897 100644 --- a/drivers/staging/octeon/ethernet-spi.c +++ b/drivers/staging/octeon/ethernet-spi.c @@ -26,7 +26,6 @@ **********************************************************************/ #include #include -#include #include #include diff --git a/drivers/staging/octeon/ethernet-xaui.c b/drivers/staging/octeon/ethernet-xaui.c index ee3dc41..3fca1cc 100644 --- a/drivers/staging/octeon/ethernet-xaui.c +++ b/drivers/staging/octeon/ethernet-xaui.c @@ -26,7 +26,6 @@ **********************************************************************/ #include #include -#include #include #include diff --git a/drivers/staging/octeon/ethernet.c b/drivers/staging/octeon/ethernet.c index 9d63202..5afece0 100644 --- a/drivers/staging/octeon/ethernet.c +++ b/drivers/staging/octeon/ethernet.c @@ -29,7 +29,6 @@ #include #include #include -#include #include #include -- cgit v1.1 From f8c2648666b5a1b5ba9bbb662ae569bafd3cc830 Mon Sep 17 00:00:00 2001 From: David Daney Date: Mon, 15 Feb 2010 12:13:17 -0800 Subject: Staging: Octeon: Run phy bus accesses on a workqueue. When directly accessing a phy, we must acquire the mdio bus lock. To do that we cannot be in interrupt context, so we need to move these operations to a workqueue. Signed-off-by: David Daney To: linux-mips@linux-mips.org To: netdev@vger.kernel.org To: gregkh@suse.de Patchwork: http://patchwork.linux-mips.org/patch/965/ Signed-off-by: Ralf Baechle --- drivers/staging/octeon/ethernet-rgmii.c | 55 +++++++++++---- drivers/staging/octeon/ethernet.c | 113 +++++++++++++++++-------------- drivers/staging/octeon/octeon-ethernet.h | 4 ++ 3 files changed, 109 insertions(+), 63 deletions(-) (limited to 'drivers') diff --git a/drivers/staging/octeon/ethernet-rgmii.c b/drivers/staging/octeon/ethernet-rgmii.c index f90d46e..a0d4d4b 100644 --- a/drivers/staging/octeon/ethernet-rgmii.c +++ b/drivers/staging/octeon/ethernet-rgmii.c @@ -26,6 +26,7 @@ **********************************************************************/ #include #include +#include #include #include @@ -47,14 +48,20 @@ static int number_rgmii_ports; static void cvm_oct_rgmii_poll(struct net_device *dev) { struct octeon_ethernet *priv = netdev_priv(dev); - unsigned long flags; + unsigned long flags = 0; cvmx_helper_link_info_t link_info; + int use_global_register_lock = (priv->phydev == NULL); - /* - * Take the global register lock since we are going to touch - * registers that affect more than one port. - */ - spin_lock_irqsave(&global_register_lock, flags); + BUG_ON(in_interrupt()); + if (use_global_register_lock) { + /* + * Take the global register lock since we are going to + * touch registers that affect more than one port. + */ + spin_lock_irqsave(&global_register_lock, flags); + } else { + mutex_lock(&priv->phydev->bus->mdio_lock); + } link_info = cvmx_helper_link_get(priv->port); if (link_info.u64 == priv->link_info) { @@ -114,7 +121,11 @@ static void cvm_oct_rgmii_poll(struct net_device *dev) dev->name); } } - spin_unlock_irqrestore(&global_register_lock, flags); + + if (use_global_register_lock) + spin_unlock_irqrestore(&global_register_lock, flags); + else + mutex_unlock(&priv->phydev->bus->mdio_lock); return; } @@ -150,7 +161,12 @@ static void cvm_oct_rgmii_poll(struct net_device *dev) link_info = cvmx_helper_link_autoconf(priv->port); priv->link_info = link_info.u64; } - spin_unlock_irqrestore(&global_register_lock, flags); + + if (use_global_register_lock) + spin_unlock_irqrestore(&global_register_lock, flags); + else { + mutex_unlock(&priv->phydev->bus->mdio_lock); + } if (priv->phydev == NULL) { /* Tell core. */ @@ -212,8 +228,11 @@ static irqreturn_t cvm_oct_rgmii_rml_interrupt(int cpl, void *dev_id) struct net_device *dev = cvm_oct_device[cvmx_helper_get_ipd_port (interface, index)]; - if (dev) - cvm_oct_rgmii_poll(dev); + struct octeon_ethernet *priv = netdev_priv(dev); + + if (dev && !atomic_read(&cvm_oct_poll_queue_stopping)) + queue_work(cvm_oct_poll_queue, &priv->port_work); + gmx_rx_int_reg.u64 = 0; gmx_rx_int_reg.s.phy_dupx = 1; gmx_rx_int_reg.s.phy_link = 1; @@ -251,8 +270,11 @@ static irqreturn_t cvm_oct_rgmii_rml_interrupt(int cpl, void *dev_id) struct net_device *dev = cvm_oct_device[cvmx_helper_get_ipd_port (interface, index)]; - if (dev) - cvm_oct_rgmii_poll(dev); + struct octeon_ethernet *priv = netdev_priv(dev); + + if (dev && !atomic_read(&cvm_oct_poll_queue_stopping)) + queue_work(cvm_oct_poll_queue, &priv->port_work); + gmx_rx_int_reg.u64 = 0; gmx_rx_int_reg.s.phy_dupx = 1; gmx_rx_int_reg.s.phy_link = 1; @@ -301,6 +323,12 @@ int cvm_oct_rgmii_stop(struct net_device *dev) return 0; } +static void cvm_oct_rgmii_immediate_poll(struct work_struct *work) +{ + struct octeon_ethernet *priv = container_of(work, struct octeon_ethernet, port_work); + cvm_oct_rgmii_poll(cvm_oct_device[priv->port]); +} + int cvm_oct_rgmii_init(struct net_device *dev) { struct octeon_ethernet *priv = netdev_priv(dev); @@ -308,7 +336,7 @@ int cvm_oct_rgmii_init(struct net_device *dev) cvm_oct_common_init(dev); dev->netdev_ops->ndo_stop(dev); - + INIT_WORK(&priv->port_work, cvm_oct_rgmii_immediate_poll); /* * Due to GMX errata in CN3XXX series chips, it is necessary * to take the link down immediately when the PHY changes @@ -396,4 +424,5 @@ void cvm_oct_rgmii_uninit(struct net_device *dev) number_rgmii_ports--; if (number_rgmii_ports == 0) free_irq(OCTEON_IRQ_RML, &number_rgmii_ports); + cancel_work_sync(&priv->port_work); } diff --git a/drivers/staging/octeon/ethernet.c b/drivers/staging/octeon/ethernet.c index 5afece0..1771c10 100644 --- a/drivers/staging/octeon/ethernet.c +++ b/drivers/staging/octeon/ethernet.c @@ -123,9 +123,16 @@ MODULE_PARM_DESC(rx_napi_weight, "The NAPI WEIGHT parameter."); static unsigned int cvm_oct_mac_addr_offset; /** - * Periodic timer to check auto negotiation + * cvm_oct_poll_queue - Workqueue for polling operations. */ -static struct timer_list cvm_oct_poll_timer; +struct workqueue_struct *cvm_oct_poll_queue; + +/** + * cvm_oct_poll_queue_stopping - flag to indicate polling should stop. + * + * Set to one right before cvm_oct_poll_queue is destroyed. + */ +atomic_t cvm_oct_poll_queue_stopping = ATOMIC_INIT(0); /** * Array of every ethernet device owned by this driver indexed by @@ -133,47 +140,39 @@ static struct timer_list cvm_oct_poll_timer; */ struct net_device *cvm_oct_device[TOTAL_NUMBER_OF_PORTS]; -/** - * Periodic timer tick for slow management operations - * - * @arg: Device to check - */ -static void cvm_do_timer(unsigned long arg) +static void cvm_oct_rx_refill_worker(struct work_struct *work); +static DECLARE_DELAYED_WORK(cvm_oct_rx_refill_work, cvm_oct_rx_refill_worker); + +static void cvm_oct_rx_refill_worker(struct work_struct *work) { - static int port; - if (port < CVMX_PIP_NUM_INPUT_PORTS) { - if (cvm_oct_device[port]) { - struct octeon_ethernet *priv = netdev_priv(cvm_oct_device[port]); - if (priv->poll) - priv->poll(cvm_oct_device[port]); - cvm_oct_free_tx_skbs(priv); - cvm_oct_device[port]->netdev_ops->ndo_get_stats(cvm_oct_device[port]); - } - port++; - /* - * Poll the next port in a 50th of a second. This - * spreads the polling of ports out a little bit. - */ - mod_timer(&cvm_oct_poll_timer, jiffies + HZ/50); - } else { - port = 0; - /* - * FPA 0 may have been drained, try to refill it if we - * need more than num_packet_buffers / 2, otherwise - * normal receive processing will refill it. If it - * were drained, no packets could be received so - * cvm_oct_napi_poll would never be invoked to do the - * refill. - */ - cvm_oct_rx_refill_pool(num_packet_buffers / 2); - /* - * All ports have been polled. Start the next iteration through - * the ports in one second. - */ - mod_timer(&cvm_oct_poll_timer, jiffies + HZ); - } + /* + * FPA 0 may have been drained, try to refill it if we need + * more than num_packet_buffers / 2, otherwise normal receive + * processing will refill it. If it were drained, no packets + * could be received so cvm_oct_napi_poll would never be + * invoked to do the refill. + */ + cvm_oct_rx_refill_pool(num_packet_buffers / 2); + + if (!atomic_read(&cvm_oct_poll_queue_stopping)) + queue_delayed_work(cvm_oct_poll_queue, + &cvm_oct_rx_refill_work, HZ); } +static void cvm_oct_tx_clean_worker(struct work_struct *work) +{ + struct octeon_ethernet *priv = container_of(work, + struct octeon_ethernet, + tx_clean_work.work); + + if (priv->poll) + priv->poll(cvm_oct_device[priv->port]); + cvm_oct_free_tx_skbs(priv); + cvm_oct_device[priv->port]->netdev_ops->ndo_get_stats(cvm_oct_device[priv->port]); + if (!atomic_read(&cvm_oct_poll_queue_stopping)) + queue_delayed_work(cvm_oct_poll_queue, &priv->tx_clean_work, HZ); + } + /** * Configure common hardware for all interfaces */ @@ -624,6 +623,12 @@ static int __init cvm_oct_init_module(void) else cvm_oct_mac_addr_offset = 0; + cvm_oct_poll_queue = create_singlethread_workqueue("octeon-ethernet"); + if (cvm_oct_poll_queue == NULL) { + pr_err("octeon-ethernet: Cannot create workqueue"); + return -ENOMEM; + } + cvm_oct_proc_initialize(); cvm_oct_configure_common_hw(); @@ -719,7 +724,9 @@ static int __init cvm_oct_init_module(void) /* Initialize the device private structure. */ priv = netdev_priv(dev); - memset(priv, 0, sizeof(struct octeon_ethernet)); + + INIT_DELAYED_WORK(&priv->tx_clean_work, + cvm_oct_tx_clean_worker); priv->imode = imode; priv->port = port; @@ -785,17 +792,15 @@ static int __init cvm_oct_init_module(void) fau -= cvmx_pko_get_num_queues(priv->port) * sizeof(uint32_t); + queue_delayed_work(cvm_oct_poll_queue, + &priv->tx_clean_work, HZ); } } } cvm_oct_rx_initialize(); - /* Enable the poll timer for checking RGMII status */ - init_timer(&cvm_oct_poll_timer); - cvm_oct_poll_timer.data = 0; - cvm_oct_poll_timer.function = cvm_do_timer; - mod_timer(&cvm_oct_poll_timer, jiffies + HZ); + queue_delayed_work(cvm_oct_poll_queue, &cvm_oct_rx_refill_work, HZ); return 0; } @@ -817,20 +822,28 @@ static void __exit cvm_oct_cleanup_module(void) /* Free the interrupt handler */ free_irq(OCTEON_IRQ_WORKQ0 + pow_receive_group, cvm_oct_device); - del_timer(&cvm_oct_poll_timer); + atomic_inc_return(&cvm_oct_poll_queue_stopping); + cancel_delayed_work_sync(&cvm_oct_rx_refill_work); + cvm_oct_rx_shutdown(); cvmx_pko_disable(); /* Free the ethernet devices */ for (port = 0; port < TOTAL_NUMBER_OF_PORTS; port++) { if (cvm_oct_device[port]) { - cvm_oct_tx_shutdown(cvm_oct_device[port]); - unregister_netdev(cvm_oct_device[port]); - kfree(cvm_oct_device[port]); + struct net_device *dev = cvm_oct_device[port]; + struct octeon_ethernet *priv = netdev_priv(dev); + cancel_delayed_work_sync(&priv->tx_clean_work); + + cvm_oct_tx_shutdown(dev); + unregister_netdev(dev); + kfree(dev); cvm_oct_device[port] = NULL; } } + destroy_workqueue(cvm_oct_poll_queue); + cvmx_pko_shutdown(); cvm_oct_proc_shutdown(); diff --git a/drivers/staging/octeon/octeon-ethernet.h b/drivers/staging/octeon/octeon-ethernet.h index 40b6956..8d09210 100644 --- a/drivers/staging/octeon/octeon-ethernet.h +++ b/drivers/staging/octeon/octeon-ethernet.h @@ -61,6 +61,8 @@ struct octeon_ethernet { void (*poll) (struct net_device *dev); struct hrtimer tx_restart_timer; ktime_t tx_restart_interval; + struct delayed_work tx_clean_work; + struct work_struct port_work; /* may be unused. */ }; /** @@ -97,6 +99,8 @@ extern int pow_send_group; extern int pow_receive_group; extern char pow_send_list[]; extern struct net_device *cvm_oct_device[]; +extern struct workqueue_struct *cvm_oct_poll_queue; +extern atomic_t cvm_oct_poll_queue_stopping; extern int max_rx_cpus; extern int rx_napi_weight; -- cgit v1.1 From 4898c560103fb8075c10a8e9d70e0ca26873075e Mon Sep 17 00:00:00 2001 From: David Daney Date: Mon, 15 Feb 2010 15:06:47 -0800 Subject: Staging: Octeon: Free transmit SKBs in a timely manner If we wait for the once-per-second cleanup to free transmit SKBs, sockets with small transmit buffer sizes might spend most of their time blocked waiting for the cleanup. Normally we do a cleanup for each transmitted packet. We add a watchdog type timer so that we also schedule a timeout for 150uS after a packet is transmitted. The watchdog is reset for each transmitted packet, so for high packet rates, it never expires. At these high rates, the cleanups are done for each packet so the extra watchdog initiated cleanups are neither needed nor triggered. Signed-off-by: David Daney To: linux-mips@linux-mips.org To: netdev@vger.kernel.org To: gregkh@suse.de Cc: Eric Dumazet Patchwork: http://patchwork.linux-mips.org/patch/968/ Signed-off-by: Ralf Baechle This version has spelling and comment changes based on feedback from Eric Dumazet. --- drivers/staging/octeon/Kconfig | 1 - drivers/staging/octeon/ethernet-defines.h | 5 +- drivers/staging/octeon/ethernet-tx.c | 137 ++++++++++++++++++++++++------ drivers/staging/octeon/ethernet-tx.h | 6 +- drivers/staging/octeon/ethernet.c | 47 +++++----- drivers/staging/octeon/octeon-ethernet.h | 9 +- 6 files changed, 142 insertions(+), 63 deletions(-) (limited to 'drivers') diff --git a/drivers/staging/octeon/Kconfig b/drivers/staging/octeon/Kconfig index 579b8f1..638ad6b 100644 --- a/drivers/staging/octeon/Kconfig +++ b/drivers/staging/octeon/Kconfig @@ -3,7 +3,6 @@ config OCTEON_ETHERNET depends on CPU_CAVIUM_OCTEON select PHYLIB select MDIO_OCTEON - select HIGH_RES_TIMERS help This driver supports the builtin ethernet ports on Cavium Networks' products in the Octeon family. This driver supports the diff --git a/drivers/staging/octeon/ethernet-defines.h b/drivers/staging/octeon/ethernet-defines.h index 00a8561..6a2cd50 100644 --- a/drivers/staging/octeon/ethernet-defines.h +++ b/drivers/staging/octeon/ethernet-defines.h @@ -95,10 +95,11 @@ /*#define DONT_WRITEBACK(x) 0 */ /* Maximum number of SKBs to try to free per xmit packet. */ -#define MAX_SKB_TO_FREE 10 #define MAX_OUT_QUEUE_DEPTH 1000 -#define FAU_NUM_PACKET_BUFFERS_TO_FREE (CVMX_FAU_REG_END - sizeof(uint32_t)) +#define FAU_TOTAL_TX_TO_CLEAN (CVMX_FAU_REG_END - sizeof(uint32_t)) +#define FAU_NUM_PACKET_BUFFERS_TO_FREE (FAU_TOTAL_TX_TO_CLEAN - sizeof(uint32_t)) + #define TOTAL_NUMBER_OF_PORTS (CVMX_PIP_NUM_INPUT_PORTS+1) diff --git a/drivers/staging/octeon/ethernet-tx.c b/drivers/staging/octeon/ethernet-tx.c index 62258bd..5175247 100644 --- a/drivers/staging/octeon/ethernet-tx.c +++ b/drivers/staging/octeon/ethernet-tx.c @@ -48,6 +48,7 @@ #include "cvmx-wqe.h" #include "cvmx-fau.h" +#include "cvmx-pip.h" #include "cvmx-pko.h" #include "cvmx-helper.h" @@ -66,6 +67,11 @@ #define GET_SKBUFF_QOS(skb) 0 #endif +static void cvm_oct_tx_do_cleanup(unsigned long arg); +static DECLARE_TASKLET(cvm_oct_tx_cleanup_tasklet, cvm_oct_tx_do_cleanup, 0); + +/* Maximum number of SKBs to try to free per xmit packet. */ +#define MAX_SKB_TO_FREE (MAX_OUT_QUEUE_DEPTH * 2) static inline int32_t cvm_oct_adjust_skb_to_free(int32_t skb_to_free, int fau) { @@ -77,10 +83,24 @@ static inline int32_t cvm_oct_adjust_skb_to_free(int32_t skb_to_free, int fau) return skb_to_free; } -void cvm_oct_free_tx_skbs(struct octeon_ethernet *priv) +static void cvm_oct_kick_tx_poll_watchdog(void) +{ + union cvmx_ciu_timx ciu_timx; + ciu_timx.u64 = 0; + ciu_timx.s.one_shot = 1; + ciu_timx.s.len = cvm_oct_tx_poll_interval; + cvmx_write_csr(CVMX_CIU_TIMX(1), ciu_timx.u64); +} + +void cvm_oct_free_tx_skbs(struct net_device *dev) { int32_t skb_to_free; int qos, queues_per_port; + int total_freed = 0; + int total_remaining = 0; + unsigned long flags; + struct octeon_ethernet *priv = netdev_priv(dev); + queues_per_port = cvmx_pko_get_num_queues(priv->port); /* Drain any pending packets in the free list */ for (qos = 0; qos < queues_per_port; qos++) { @@ -89,24 +109,31 @@ void cvm_oct_free_tx_skbs(struct octeon_ethernet *priv) skb_to_free = cvmx_fau_fetch_and_add32(priv->fau+qos*4, MAX_SKB_TO_FREE); skb_to_free = cvm_oct_adjust_skb_to_free(skb_to_free, priv->fau+qos*4); - while (skb_to_free > 0) { - dev_kfree_skb_any(skb_dequeue(&priv->tx_free_list[qos])); - skb_to_free--; + + total_freed += skb_to_free; + if (skb_to_free > 0) { + struct sk_buff *to_free_list = NULL; + spin_lock_irqsave(&priv->tx_free_list[qos].lock, flags); + while (skb_to_free > 0) { + struct sk_buff *t = __skb_dequeue(&priv->tx_free_list[qos]); + t->next = to_free_list; + to_free_list = t; + skb_to_free--; + } + spin_unlock_irqrestore(&priv->tx_free_list[qos].lock, flags); + /* Do the actual freeing outside of the lock. */ + while (to_free_list) { + struct sk_buff *t = to_free_list; + to_free_list = to_free_list->next; + dev_kfree_skb_any(t); + } } + total_remaining += skb_queue_len(&priv->tx_free_list[qos]); } -} - -enum hrtimer_restart cvm_oct_restart_tx(struct hrtimer *timer) -{ - struct octeon_ethernet *priv = container_of(timer, struct octeon_ethernet, tx_restart_timer); - struct net_device *dev = cvm_oct_device[priv->port]; - - cvm_oct_free_tx_skbs(priv); - - if (netif_queue_stopped(dev)) + if (total_freed >= 0 && netif_queue_stopped(dev)) netif_wake_queue(dev); - - return HRTIMER_NORESTART; + if (total_remaining) + cvm_oct_kick_tx_poll_watchdog(); } /** @@ -129,6 +156,7 @@ int cvm_oct_xmit(struct sk_buff *skb, struct net_device *dev) struct sk_buff *to_free_list; int32_t skb_to_free; int32_t buffers_to_free; + u32 total_to_clean; unsigned long flags; #if REUSE_SKBUFFS_WITHOUT_FREE unsigned char *fpa_head; @@ -232,7 +260,6 @@ int cvm_oct_xmit(struct sk_buff *skb, struct net_device *dev) pko_command.s.subone0 = 1; pko_command.s.dontfree = 1; - pko_command.s.reg0 = priv->fau + qos * 4; /* Build the PKO buffer pointer */ hw_buffer.u64 = 0; @@ -327,7 +354,6 @@ int cvm_oct_xmit(struct sk_buff *skb, struct net_device *dev) * We can use this buffer in the FPA. We don't need the FAU * update anymore */ - pko_command.s.reg0 = 0; pko_command.s.dontfree = 0; hw_buffer.s.back = ((unsigned long)skb->data >> 7) - ((unsigned long)fpa_head >> 7); @@ -384,15 +410,17 @@ dont_put_skbuff_in_hw: * If we're sending faster than the receive can free them then * don't do the HW free. */ - if ((buffers_to_free < -100) && !pko_command.s.dontfree) { + if ((buffers_to_free < -100) && !pko_command.s.dontfree) pko_command.s.dontfree = 1; - pko_command.s.reg0 = priv->fau + qos * 4; - } - if (pko_command.s.dontfree) + if (pko_command.s.dontfree) { queue_type = QUEUE_CORE; - else + pko_command.s.reg0 = priv->fau+qos*4; + } else { queue_type = QUEUE_HW; + } + if (USE_ASYNC_IOBDMA) + cvmx_fau_async_fetch_and_add32(CVMX_SCR_SCRATCH, FAU_TOTAL_TX_TO_CLEAN, 1); spin_lock_irqsave(&priv->tx_free_list[qos].lock, flags); @@ -402,10 +430,7 @@ dont_put_skbuff_in_hw: /* Drop the lock when notifying the core. */ spin_unlock_irqrestore(&priv->tx_free_list[qos].lock, flags); netif_stop_queue(dev); - hrtimer_start(&priv->tx_restart_timer, - priv->tx_restart_interval, HRTIMER_MODE_REL); spin_lock_irqsave(&priv->tx_free_list[qos].lock, flags); - } else { /* If not using normal queueing. */ queue_type = QUEUE_DROP; @@ -460,11 +485,27 @@ skip_xmit: } if (USE_ASYNC_IOBDMA) { + CVMX_SYNCIOBDMA; + total_to_clean = cvmx_scratch_read64(CVMX_SCR_SCRATCH); /* Restore the scratch area */ cvmx_scratch_write64(CVMX_SCR_SCRATCH, old_scratch); cvmx_scratch_write64(CVMX_SCR_SCRATCH + 8, old_scratch2); + } else { + total_to_clean = cvmx_fau_fetch_and_add32(FAU_TOTAL_TX_TO_CLEAN, 1); } + if (total_to_clean & 0x3ff) { + /* + * Schedule the cleanup tasklet every 1024 packets for + * the pathological case of high traffic on one port + * delaying clean up of packets on a different port + * that is blocked waiting for the cleanup. + */ + tasklet_schedule(&cvm_oct_tx_cleanup_tasklet); + } + + cvm_oct_kick_tx_poll_watchdog(); + return NETDEV_TX_OK; } @@ -624,7 +665,7 @@ int cvm_oct_xmit_pow(struct sk_buff *skb, struct net_device *dev) * * @dev: Device being shutdown */ -void cvm_oct_tx_shutdown(struct net_device *dev) +void cvm_oct_tx_shutdown_dev(struct net_device *dev) { struct octeon_ethernet *priv = netdev_priv(dev); unsigned long flags; @@ -638,3 +679,45 @@ void cvm_oct_tx_shutdown(struct net_device *dev) spin_unlock_irqrestore(&priv->tx_free_list[qos].lock, flags); } } + +static void cvm_oct_tx_do_cleanup(unsigned long arg) +{ + int port; + + for (port = 0; port < TOTAL_NUMBER_OF_PORTS; port++) { + if (cvm_oct_device[port]) { + struct net_device *dev = cvm_oct_device[port]; + cvm_oct_free_tx_skbs(dev); + } + } +} + +static irqreturn_t cvm_oct_tx_cleanup_watchdog(int cpl, void *dev_id) +{ + /* Disable the interrupt. */ + cvmx_write_csr(CVMX_CIU_TIMX(1), 0); + /* Do the work in the tasklet. */ + tasklet_schedule(&cvm_oct_tx_cleanup_tasklet); + return IRQ_HANDLED; +} + +void cvm_oct_tx_initialize(void) +{ + int i; + + /* Disable the interrupt. */ + cvmx_write_csr(CVMX_CIU_TIMX(1), 0); + /* Register an IRQ hander for to receive CIU_TIMX(1) interrupts */ + i = request_irq(OCTEON_IRQ_TIMER1, + cvm_oct_tx_cleanup_watchdog, 0, + "Ethernet", cvm_oct_device); + + if (i) + panic("Could not acquire Ethernet IRQ %d\n", OCTEON_IRQ_TIMER1); +} + +void cvm_oct_tx_shutdown(void) +{ + /* Free the interrupt handler */ + free_irq(OCTEON_IRQ_TIMER1, cvm_oct_device); +} diff --git a/drivers/staging/octeon/ethernet-tx.h b/drivers/staging/octeon/ethernet-tx.h index b628d8c..547680c 100644 --- a/drivers/staging/octeon/ethernet-tx.h +++ b/drivers/staging/octeon/ethernet-tx.h @@ -29,6 +29,6 @@ int cvm_oct_xmit(struct sk_buff *skb, struct net_device *dev); int cvm_oct_xmit_pow(struct sk_buff *skb, struct net_device *dev); int cvm_oct_transmit_qos(struct net_device *dev, void *work_queue_entry, int do_free, int qos); -void cvm_oct_tx_shutdown(struct net_device *dev); -void cvm_oct_free_tx_skbs(struct octeon_ethernet *priv); -enum hrtimer_restart cvm_oct_restart_tx(struct hrtimer *timer); +void cvm_oct_tx_initialize(void); +void cvm_oct_tx_shutdown(void); +void cvm_oct_tx_shutdown_dev(struct net_device *dev); diff --git a/drivers/staging/octeon/ethernet.c b/drivers/staging/octeon/ethernet.c index 1771c10..5ee60ab 100644 --- a/drivers/staging/octeon/ethernet.c +++ b/drivers/staging/octeon/ethernet.c @@ -140,6 +140,8 @@ atomic_t cvm_oct_poll_queue_stopping = ATOMIC_INIT(0); */ struct net_device *cvm_oct_device[TOTAL_NUMBER_OF_PORTS]; +u64 cvm_oct_tx_poll_interval; + static void cvm_oct_rx_refill_worker(struct work_struct *work); static DECLARE_DELAYED_WORK(cvm_oct_rx_refill_work, cvm_oct_rx_refill_worker); @@ -159,18 +161,19 @@ static void cvm_oct_rx_refill_worker(struct work_struct *work) &cvm_oct_rx_refill_work, HZ); } -static void cvm_oct_tx_clean_worker(struct work_struct *work) +static void cvm_oct_periodic_worker(struct work_struct *work) { struct octeon_ethernet *priv = container_of(work, struct octeon_ethernet, - tx_clean_work.work); + port_periodic_work.work); if (priv->poll) priv->poll(cvm_oct_device[priv->port]); - cvm_oct_free_tx_skbs(priv); + cvm_oct_device[priv->port]->netdev_ops->ndo_get_stats(cvm_oct_device[priv->port]); + if (!atomic_read(&cvm_oct_poll_queue_stopping)) - queue_delayed_work(cvm_oct_poll_queue, &priv->tx_clean_work, HZ); + queue_delayed_work(cvm_oct_poll_queue, &priv->port_periodic_work, HZ); } /** @@ -662,6 +665,9 @@ static int __init cvm_oct_init_module(void) */ cvmx_fau_atomic_write32(FAU_NUM_PACKET_BUFFERS_TO_FREE, 0); + /* Initialize the FAU used for counting tx SKBs that need to be freed */ + cvmx_fau_atomic_write32(FAU_TOTAL_TX_TO_CLEAN, 0); + if ((pow_send_group != -1)) { struct net_device *dev; pr_info("\tConfiguring device for POW only access\n"); @@ -670,18 +676,6 @@ static int __init cvm_oct_init_module(void) /* Initialize the device private structure. */ struct octeon_ethernet *priv = netdev_priv(dev); - hrtimer_init(&priv->tx_restart_timer, - CLOCK_MONOTONIC, - HRTIMER_MODE_REL); - priv->tx_restart_timer.function = cvm_oct_restart_tx; - - /* - * Default for 10GE 5000nS enough time to - * transmit about 100 64byte packtes. 1GE - * interfaces will get 50000nS below. - */ - priv->tx_restart_interval = ktime_set(0, 5000); - dev->netdev_ops = &cvm_oct_pow_netdev_ops; priv->imode = CVMX_HELPER_INTERFACE_MODE_DISABLED; priv->port = CVMX_PIP_NUM_INPUT_PORTS; @@ -725,9 +719,8 @@ static int __init cvm_oct_init_module(void) /* Initialize the device private structure. */ priv = netdev_priv(dev); - INIT_DELAYED_WORK(&priv->tx_clean_work, - cvm_oct_tx_clean_worker); - + INIT_DELAYED_WORK(&priv->port_periodic_work, + cvm_oct_periodic_worker); priv->imode = imode; priv->port = port; priv->queue = cvmx_pko_get_base_queue(priv->port); @@ -763,7 +756,6 @@ static int __init cvm_oct_init_module(void) case CVMX_HELPER_INTERFACE_MODE_SGMII: dev->netdev_ops = &cvm_oct_sgmii_netdev_ops; - priv->tx_restart_interval = ktime_set(0, 50000); strcpy(dev->name, "eth%d"); break; @@ -775,7 +767,6 @@ static int __init cvm_oct_init_module(void) case CVMX_HELPER_INTERFACE_MODE_RGMII: case CVMX_HELPER_INTERFACE_MODE_GMII: dev->netdev_ops = &cvm_oct_rgmii_netdev_ops; - priv->tx_restart_interval = ktime_set(0, 50000); strcpy(dev->name, "eth%d"); break; } @@ -793,13 +784,19 @@ static int __init cvm_oct_init_module(void) cvmx_pko_get_num_queues(priv->port) * sizeof(uint32_t); queue_delayed_work(cvm_oct_poll_queue, - &priv->tx_clean_work, HZ); + &priv->port_periodic_work, HZ); } } } + cvm_oct_tx_initialize(); cvm_oct_rx_initialize(); + /* + * 150 uS: about 10 1500-byte packtes at 1GE. + */ + cvm_oct_tx_poll_interval = 150 * (octeon_get_clock_rate() / 1000000); + queue_delayed_work(cvm_oct_poll_queue, &cvm_oct_rx_refill_work, HZ); return 0; @@ -826,6 +823,8 @@ static void __exit cvm_oct_cleanup_module(void) cancel_delayed_work_sync(&cvm_oct_rx_refill_work); cvm_oct_rx_shutdown(); + cvm_oct_tx_shutdown(); + cvmx_pko_disable(); /* Free the ethernet devices */ @@ -833,9 +832,9 @@ static void __exit cvm_oct_cleanup_module(void) if (cvm_oct_device[port]) { struct net_device *dev = cvm_oct_device[port]; struct octeon_ethernet *priv = netdev_priv(dev); - cancel_delayed_work_sync(&priv->tx_clean_work); + cancel_delayed_work_sync(&priv->port_periodic_work); - cvm_oct_tx_shutdown(dev); + cvm_oct_tx_shutdown_dev(dev); unregister_netdev(dev); kfree(dev); cvm_oct_device[port] = NULL; diff --git a/drivers/staging/octeon/octeon-ethernet.h b/drivers/staging/octeon/octeon-ethernet.h index 8d09210..db2a3cc 100644 --- a/drivers/staging/octeon/octeon-ethernet.h +++ b/drivers/staging/octeon/octeon-ethernet.h @@ -4,7 +4,7 @@ * Contact: support@caviumnetworks.com * This file is part of the OCTEON SDK * - * Copyright (c) 2003-2007 Cavium Networks + * Copyright (c) 2003-2010 Cavium Networks * * This file is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, Version 2, as @@ -31,8 +31,6 @@ #ifndef OCTEON_ETHERNET_H #define OCTEON_ETHERNET_H -#include - /** * This is the definition of the Ethernet driver's private * driver state stored in netdev_priv(dev). @@ -59,9 +57,7 @@ struct octeon_ethernet { uint64_t link_info; /* Called periodically to check link status */ void (*poll) (struct net_device *dev); - struct hrtimer tx_restart_timer; - ktime_t tx_restart_interval; - struct delayed_work tx_clean_work; + struct delayed_work port_periodic_work; struct work_struct port_work; /* may be unused. */ }; @@ -101,6 +97,7 @@ extern char pow_send_list[]; extern struct net_device *cvm_oct_device[]; extern struct workqueue_struct *cvm_oct_poll_queue; extern atomic_t cvm_oct_poll_queue_stopping; +extern u64 cvm_oct_tx_poll_interval; extern int max_rx_cpus; extern int rx_napi_weight; -- cgit v1.1 From ec977c5b473e29dbfdac8f2c7477eccc2142e3bc Mon Sep 17 00:00:00 2001 From: David Daney Date: Tue, 16 Feb 2010 17:25:32 -0800 Subject: Staging: Octeon: Reformat a bunch of comments. Many of the comments didn't follow kerneldoc guidlines. Signed-off-by: David Daney To: linux-mips@linux-mips.org To: netdev@vger.kernel.org To: gregkh@suse.de Patchwork: http://patchwork.linux-mips.org/patch/971/ Signed-off-by: Ralf Baechle --- drivers/staging/octeon/ethernet-mdio.c | 6 ++--- drivers/staging/octeon/ethernet-mem.c | 16 ++++++------ drivers/staging/octeon/ethernet-rx.c | 17 +++++++------ drivers/staging/octeon/ethernet-tx.c | 14 +++++------ drivers/staging/octeon/ethernet-util.h | 13 +++------- drivers/staging/octeon/ethernet.c | 42 ++++++++++---------------------- drivers/staging/octeon/octeon-ethernet.h | 7 ------ 7 files changed, 44 insertions(+), 71 deletions(-) (limited to 'drivers') diff --git a/drivers/staging/octeon/ethernet-mdio.c b/drivers/staging/octeon/ethernet-mdio.c index 05a5cc0..7e0be8d 100644 --- a/drivers/staging/octeon/ethernet-mdio.c +++ b/drivers/staging/octeon/ethernet-mdio.c @@ -96,11 +96,11 @@ const struct ethtool_ops cvm_oct_ethtool_ops = { }; /** - * IOCTL support for PHY control - * + * cvm_oct_ioctl - IOCTL support for PHY control * @dev: Device to change * @rq: the request * @cmd: the command + * * Returns Zero on success */ int cvm_oct_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) @@ -153,7 +153,7 @@ static void cvm_oct_adjust_link(struct net_device *dev) /** - * Setup the PHY + * cvm_oct_phy_setup_device - setup the PHY * * @dev: Device to setup * diff --git a/drivers/staging/octeon/ethernet-mem.c b/drivers/staging/octeon/ethernet-mem.c index 53ed2f7..00cc91d 100644 --- a/drivers/staging/octeon/ethernet-mem.c +++ b/drivers/staging/octeon/ethernet-mem.c @@ -34,11 +34,12 @@ #include "cvmx-fpa.h" /** - * Fill the supplied hardware pool with skbuffs - * + * cvm_oct_fill_hw_skbuff - fill the supplied hardware pool with skbuffs * @pool: Pool to allocate an skbuff for * @size: Size of the buffer needed for the pool * @elements: Number of buffers to allocate + * + * Returns the actual number of buffers allocated. */ static int cvm_oct_fill_hw_skbuff(int pool, int size, int elements) { @@ -62,8 +63,7 @@ static int cvm_oct_fill_hw_skbuff(int pool, int size, int elements) } /** - * Free the supplied hardware pool of skbuffs - * + * cvm_oct_free_hw_skbuff- free hardware pool skbuffs * @pool: Pool to allocate an skbuff for * @size: Size of the buffer needed for the pool * @elements: Number of buffers to allocate @@ -91,11 +91,12 @@ static void cvm_oct_free_hw_skbuff(int pool, int size, int elements) } /** - * This function fills a hardware pool with memory. - * + * cvm_oct_fill_hw_memory - fill a hardware pool with memory. * @pool: Pool to populate * @size: Size of each buffer in the pool * @elements: Number of buffers to allocate + * + * Returns the actual number of buffers allocated. */ static int cvm_oct_fill_hw_memory(int pool, int size, int elements) { @@ -129,8 +130,7 @@ static int cvm_oct_fill_hw_memory(int pool, int size, int elements) } /** - * Free memory previously allocated with cvm_oct_fill_hw_memory - * + * cvm_oct_free_hw_memory - Free memory allocated by cvm_oct_fill_hw_memory * @pool: FPA pool to free * @size: Size of each buffer in the pool * @elements: Number of buffers that should be in the pool diff --git a/drivers/staging/octeon/ethernet-rx.c b/drivers/staging/octeon/ethernet-rx.c index b2e6ab6..cb38f9e 100644 --- a/drivers/staging/octeon/ethernet-rx.c +++ b/drivers/staging/octeon/ethernet-rx.c @@ -124,8 +124,9 @@ static void cvm_oct_no_more_work(void) } /** - * Interrupt handler. The interrupt occurs whenever the POW - * has packets in our group. + * cvm_oct_do_interrupt - interrupt handler. + * + * The interrupt occurs whenever the POW has packets in our group. * */ static irqreturn_t cvm_oct_do_interrupt(int cpl, void *dev_id) @@ -138,10 +139,9 @@ static irqreturn_t cvm_oct_do_interrupt(int cpl, void *dev_id) } /** - * This is called on receive errors, and determines if the packet - * can be dropped early-on in cvm_oct_tasklet_rx(). - * + * cvm_oct_check_rcv_error - process receive errors * @work: Work queue entry pointing to the packet. + * * Returns Non-zero if the packet can be dropped, zero otherwise. */ static inline int cvm_oct_check_rcv_error(cvmx_wqe_t *work) @@ -224,10 +224,11 @@ static inline int cvm_oct_check_rcv_error(cvmx_wqe_t *work) } /** - * The NAPI poll function. - * + * cvm_oct_napi_poll - the NAPI poll function. * @napi: The NAPI instance, or null if called from cvm_oct_poll_controller * @budget: Maximum number of packets to receive. + * + * Returns the number of packets processed. */ static int cvm_oct_napi_poll(struct napi_struct *napi, int budget) { @@ -484,7 +485,7 @@ static int cvm_oct_napi_poll(struct napi_struct *napi, int budget) #ifdef CONFIG_NET_POLL_CONTROLLER /** - * This is called when the kernel needs to manually poll the + * cvm_oct_poll_controller - poll for receive packets * device. * * @dev: Device to poll. Unused diff --git a/drivers/staging/octeon/ethernet-tx.c b/drivers/staging/octeon/ethernet-tx.c index 5175247..afc2b73 100644 --- a/drivers/staging/octeon/ethernet-tx.c +++ b/drivers/staging/octeon/ethernet-tx.c @@ -137,11 +137,11 @@ void cvm_oct_free_tx_skbs(struct net_device *dev) } /** - * Packet transmit - * + * cvm_oct_xmit - transmit a packet * @skb: Packet to send * @dev: Device info structure - * Returns Always returns zero + * + * Returns Always returns NETDEV_TX_OK */ int cvm_oct_xmit(struct sk_buff *skb, struct net_device *dev) { @@ -510,10 +510,10 @@ skip_xmit: } /** - * Packet transmit to the POW - * + * cvm_oct_xmit_pow - transmit a packet to the POW * @skb: Packet to send * @dev: Device info structure + * Returns Always returns zero */ int cvm_oct_xmit_pow(struct sk_buff *skb, struct net_device *dev) @@ -661,9 +661,9 @@ int cvm_oct_xmit_pow(struct sk_buff *skb, struct net_device *dev) } /** - * This function frees all skb that are currently queued for TX. - * + * cvm_oct_tx_shutdown_dev - free all skb that are currently queued for TX. * @dev: Device being shutdown + * */ void cvm_oct_tx_shutdown_dev(struct net_device *dev) { diff --git a/drivers/staging/octeon/ethernet-util.h b/drivers/staging/octeon/ethernet-util.h index 37b6659..2346756 100644 --- a/drivers/staging/octeon/ethernet-util.h +++ b/drivers/staging/octeon/ethernet-util.h @@ -30,10 +30,9 @@ } while (0) /** - * Given a packet data address, return a pointer to the - * beginning of the packet buffer. - * + * cvm_oct_get_buffer_ptr - convert packet data address to pointer * @packet_ptr: Packet data hardware address + * * Returns Packet buffer pointer */ static inline void *cvm_oct_get_buffer_ptr(union cvmx_buf_ptr packet_ptr) @@ -43,9 +42,7 @@ static inline void *cvm_oct_get_buffer_ptr(union cvmx_buf_ptr packet_ptr) } /** - * Given an IPD/PKO port number, return the logical interface it is - * on. - * + * INTERFACE - convert IPD port to locgical interface * @ipd_port: Port to check * * Returns Logical interface @@ -65,9 +62,7 @@ static inline int INTERFACE(int ipd_port) } /** - * Given an IPD/PKO port number, return the port's index on a - * logical interface. - * + * INDEX - convert IPD/PKO port number to the port's interface index * @ipd_port: Port to check * * Returns Index into interface port list diff --git a/drivers/staging/octeon/ethernet.c b/drivers/staging/octeon/ethernet.c index 5ee60ab..45cb4c7d 100644 --- a/drivers/staging/octeon/ethernet.c +++ b/drivers/staging/octeon/ethernet.c @@ -176,9 +176,6 @@ static void cvm_oct_periodic_worker(struct work_struct *work) queue_delayed_work(cvm_oct_poll_queue, &priv->port_periodic_work, HZ); } -/** - * Configure common hardware for all interfaces - */ static __init void cvm_oct_configure_common_hw(void) { /* Setup the FPA */ @@ -198,10 +195,10 @@ static __init void cvm_oct_configure_common_hw(void) } /** - * Free a work queue entry received in a intercept callback. + * cvm_oct_free_work- Free a work queue entry + * + * @work_queue_entry: Work queue entry to free * - * @work_queue_entry: - * Work queue entry to free * Returns Zero on success, Negative on failure. */ int cvm_oct_free_work(void *work_queue_entry) @@ -228,9 +225,9 @@ int cvm_oct_free_work(void *work_queue_entry) EXPORT_SYMBOL(cvm_oct_free_work); /** - * Get the low level ethernet statistics - * + * cvm_oct_common_get_stats - get the low level ethernet statistics * @dev: Device to get the statistics from + * * Returns Pointer to the statistics */ static struct net_device_stats *cvm_oct_common_get_stats(struct net_device *dev) @@ -274,8 +271,7 @@ static struct net_device_stats *cvm_oct_common_get_stats(struct net_device *dev) } /** - * Change the link MTU. Unimplemented - * + * cvm_oct_common_change_mtu - change the link MTU * @dev: Device to change * @new_mtu: The new MTU * @@ -339,8 +335,7 @@ static int cvm_oct_common_change_mtu(struct net_device *dev, int new_mtu) } /** - * Set the multicast list. Currently unimplemented. - * + * cvm_oct_common_set_multicast_list - set the multicast list * @dev: Device to work on */ static void cvm_oct_common_set_multicast_list(struct net_device *dev) @@ -395,10 +390,10 @@ static void cvm_oct_common_set_multicast_list(struct net_device *dev) } /** - * Set the hardware MAC address for a device - * - * @dev: Device to change the MAC address for - * @addr: Address structure to change it too. MAC address is addr + 2. + * cvm_oct_common_set_mac_address - set the hardware MAC address for a device + * @dev: The device in question. + * @addr: Address structure to change it too. + * Returns Zero on success */ static int cvm_oct_common_set_mac_address(struct net_device *dev, void *addr) @@ -445,9 +440,9 @@ static int cvm_oct_common_set_mac_address(struct net_device *dev, void *addr) } /** - * Per network device initialization - * + * cvm_oct_common_init - per network device initialization * @dev: Device to initialize + * * Returns Zero on success */ int cvm_oct_common_init(struct net_device *dev) @@ -603,12 +598,6 @@ static const struct net_device_ops cvm_oct_pow_netdev_ops = { extern void octeon_mdiobus_force_mod_depencency(void); -/** - * Module/ driver initialization. Creates the linux network - * devices. - * - * Returns Zero on success - */ static int __init cvm_oct_init_module(void) { int num_interfaces; @@ -802,11 +791,6 @@ static int __init cvm_oct_init_module(void) return 0; } -/** - * Module / driver shutdown - * - * Returns Zero on success - */ static void __exit cvm_oct_cleanup_module(void) { int port; diff --git a/drivers/staging/octeon/octeon-ethernet.h b/drivers/staging/octeon/octeon-ethernet.h index db2a3cc..d581925 100644 --- a/drivers/staging/octeon/octeon-ethernet.h +++ b/drivers/staging/octeon/octeon-ethernet.h @@ -61,13 +61,6 @@ struct octeon_ethernet { struct work_struct port_work; /* may be unused. */ }; -/** - * Free a work queue entry received in a intercept callback. - * - * @work_queue_entry: - * Work queue entry to free - * Returns Zero on success, Negative on failure. - */ int cvm_oct_free_work(void *work_queue_entry); extern int cvm_oct_rgmii_init(struct net_device *dev); -- cgit v1.1 From 559e25a5e3efe60a22b7f96ea4ad2eb09d996e97 Mon Sep 17 00:00:00 2001 From: David Daney Date: Tue, 16 Feb 2010 17:25:33 -0800 Subject: Staging: Octeon: Remove /proc/octeon_ethernet_stats This file shouldn't be in /proc, so we remove it. Signed-off-by: David Daney To: linux-mips@linux-mips.org To: netdev@vger.kernel.org To: gregkh@suse.de Patchwork: http://patchwork.linux-mips.org/patch/970/ Signed-off-by: Ralf Baechle --- drivers/staging/octeon/Makefile | 1 - drivers/staging/octeon/ethernet-proc.c | 144 --------------------------------- drivers/staging/octeon/ethernet-proc.h | 29 ------- drivers/staging/octeon/ethernet.c | 4 - 4 files changed, 178 deletions(-) delete mode 100644 drivers/staging/octeon/ethernet-proc.c delete mode 100644 drivers/staging/octeon/ethernet-proc.h (limited to 'drivers') diff --git a/drivers/staging/octeon/Makefile b/drivers/staging/octeon/Makefile index c0a583c..87447c1 100644 --- a/drivers/staging/octeon/Makefile +++ b/drivers/staging/octeon/Makefile @@ -14,7 +14,6 @@ obj-${CONFIG_OCTEON_ETHERNET} := octeon-ethernet.o octeon-ethernet-objs := ethernet.o octeon-ethernet-objs += ethernet-mdio.o octeon-ethernet-objs += ethernet-mem.o -octeon-ethernet-objs += ethernet-proc.o octeon-ethernet-objs += ethernet-rgmii.o octeon-ethernet-objs += ethernet-rx.o octeon-ethernet-objs += ethernet-sgmii.o diff --git a/drivers/staging/octeon/ethernet-proc.c b/drivers/staging/octeon/ethernet-proc.c deleted file mode 100644 index 16308d4..0000000 --- a/drivers/staging/octeon/ethernet-proc.c +++ /dev/null @@ -1,144 +0,0 @@ -/********************************************************************** - * Author: Cavium Networks - * - * Contact: support@caviumnetworks.com - * This file is part of the OCTEON SDK - * - * Copyright (c) 2003-2007 Cavium Networks - * - * This file 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 file is distributed in the hope that it will be useful, but - * AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or - * NONINFRINGEMENT. See the GNU General Public License for more - * details. - * - * You should have received a copy of the GNU General Public License - * along with this file; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * or visit http://www.gnu.org/licenses/. - * - * This file may also be available under a different license from Cavium. - * Contact Cavium Networks for more information -**********************************************************************/ -#include -#include -#include -#include - -#include - -#include "octeon-ethernet.h" -#include "ethernet-defines.h" - -#include "cvmx-helper.h" -#include "cvmx-pip.h" - -/** - * User is reading /proc/octeon_ethernet_stats - * - * @m: - * @v: - * Returns - */ -static int cvm_oct_stats_show(struct seq_file *m, void *v) -{ - struct octeon_ethernet *priv; - int port; - - for (port = 0; port < TOTAL_NUMBER_OF_PORTS; port++) { - - if (cvm_oct_device[port]) { - priv = netdev_priv(cvm_oct_device[port]); - - seq_printf(m, "\nOcteon Port %d (%s)\n", port, - cvm_oct_device[port]->name); - seq_printf(m, - "rx_packets: %12lu\t" - "tx_packets: %12lu\n", - priv->stats.rx_packets, - priv->stats.tx_packets); - seq_printf(m, - "rx_bytes: %12lu\t" - "tx_bytes: %12lu\n", - priv->stats.rx_bytes, priv->stats.tx_bytes); - seq_printf(m, - "rx_errors: %12lu\t" - "tx_errors: %12lu\n", - priv->stats.rx_errors, - priv->stats.tx_errors); - seq_printf(m, - "rx_dropped: %12lu\t" - "tx_dropped: %12lu\n", - priv->stats.rx_dropped, - priv->stats.tx_dropped); - seq_printf(m, - "rx_length_errors: %12lu\t" - "tx_aborted_errors: %12lu\n", - priv->stats.rx_length_errors, - priv->stats.tx_aborted_errors); - seq_printf(m, - "rx_over_errors: %12lu\t" - "tx_carrier_errors: %12lu\n", - priv->stats.rx_over_errors, - priv->stats.tx_carrier_errors); - seq_printf(m, - "rx_crc_errors: %12lu\t" - "tx_fifo_errors: %12lu\n", - priv->stats.rx_crc_errors, - priv->stats.tx_fifo_errors); - seq_printf(m, - "rx_frame_errors: %12lu\t" - "tx_heartbeat_errors: %12lu\n", - priv->stats.rx_frame_errors, - priv->stats.tx_heartbeat_errors); - seq_printf(m, - "rx_fifo_errors: %12lu\t" - "tx_window_errors: %12lu\n", - priv->stats.rx_fifo_errors, - priv->stats.tx_window_errors); - seq_printf(m, - "rx_missed_errors: %12lu\t" - "multicast: %12lu\n", - priv->stats.rx_missed_errors, - priv->stats.multicast); - } - } - - return 0; -} - -/** - * /proc/octeon_ethernet_stats was openned. Use the single_open iterator - * - * @inode: - * @file: - * Returns - */ -static int cvm_oct_stats_open(struct inode *inode, struct file *file) -{ - return single_open(file, cvm_oct_stats_show, NULL); -} - -static const struct file_operations cvm_oct_stats_operations = { - .open = cvm_oct_stats_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, -}; - -void cvm_oct_proc_initialize(void) -{ - struct proc_dir_entry *entry = - create_proc_entry("octeon_ethernet_stats", 0, NULL); - if (entry) - entry->proc_fops = &cvm_oct_stats_operations; -} - -void cvm_oct_proc_shutdown(void) -{ - remove_proc_entry("octeon_ethernet_stats", NULL); -} diff --git a/drivers/staging/octeon/ethernet-proc.h b/drivers/staging/octeon/ethernet-proc.h deleted file mode 100644 index 82c7d9f..0000000 --- a/drivers/staging/octeon/ethernet-proc.h +++ /dev/null @@ -1,29 +0,0 @@ -/********************************************************************* - * Author: Cavium Networks - * - * Contact: support@caviumnetworks.com - * This file is part of the OCTEON SDK - * - * Copyright (c) 2003-2007 Cavium Networks - * - * This file 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 file is distributed in the hope that it will be useful, but - * AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or - * NONINFRINGEMENT. See the GNU General Public License for more - * details. - * - * You should have received a copy of the GNU General Public License - * along with this file; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * or visit http://www.gnu.org/licenses/. - * - * This file may also be available under a different license from Cavium. - * Contact Cavium Networks for more information -*********************************************************************/ - -void cvm_oct_proc_initialize(void); -void cvm_oct_proc_shutdown(void); diff --git a/drivers/staging/octeon/ethernet.c b/drivers/staging/octeon/ethernet.c index 45cb4c7d..02b6367 100644 --- a/drivers/staging/octeon/ethernet.c +++ b/drivers/staging/octeon/ethernet.c @@ -42,8 +42,6 @@ #include "ethernet-tx.h" #include "ethernet-mdio.h" #include "ethernet-util.h" -#include "ethernet-proc.h" - #include "cvmx-pip.h" #include "cvmx-pko.h" @@ -621,7 +619,6 @@ static int __init cvm_oct_init_module(void) return -ENOMEM; } - cvm_oct_proc_initialize(); cvm_oct_configure_common_hw(); cvmx_helper_initialize_packet_io_global(); @@ -828,7 +825,6 @@ static void __exit cvm_oct_cleanup_module(void) destroy_workqueue(cvm_oct_poll_queue); cvmx_pko_shutdown(); - cvm_oct_proc_shutdown(); cvmx_ipd_free_ptr(); -- cgit v1.1 From 11b897cf84c37e6522db914793677e933ef311fb Mon Sep 17 00:00:00 2001 From: Manuel Lauss Date: Wed, 24 Feb 2010 17:40:21 +0100 Subject: MIPS: Alchemy: use 36bit addresses for PCMCIA resources. On Alchemy the PCMCIA area lies at the end of the chips 36bit system bus area. Currently, addresses at the far end of the 32bit area are assumed to belong to the PCMCIA area and fixed up to the real 36bit address before being passed to ioremap(). A previous commit enabled 64 bit physical size for the resource datatype on Alchemy and this allows to use the correct 36bit addresses when registering the PCMCIA sockets. This patch removes the 32-to-36bit address fixup and registers the Alchemy demo board pcmcia socket with the correct 36bit physical addresses. Tested on DB1200, with a CF card (ide-cs driver) and a 3c589 PCMCIA ethernet card. Signed-off-by: Manuel Lauss To: Linux-MIPS Cc: Manuel Lauss Patchwork: http://patchwork.linux-mips.org/patch/994/ Signed-off-by: Ralf Baechle --- drivers/pcmcia/au1000_generic.c | 10 ++++------ drivers/pcmcia/au1000_generic.h | 6 ------ drivers/pcmcia/db1xxx_ss.c | 25 +++++++++---------------- drivers/pcmcia/xxs1500_ss.c | 27 ++++++++++----------------- 4 files changed, 23 insertions(+), 45 deletions(-) (limited to 'drivers') diff --git a/drivers/pcmcia/au1000_generic.c b/drivers/pcmcia/au1000_generic.c index 0208870..171c8a6 100644 --- a/drivers/pcmcia/au1000_generic.c +++ b/drivers/pcmcia/au1000_generic.c @@ -405,18 +405,16 @@ int au1x00_pcmcia_socket_probe(struct device *dev, struct pcmcia_low_level *ops, skt->virt_io = (void *) (ioremap((phys_t)AU1X_SOCK0_IO, 0x1000) - (u32)mips_io_port_base); - skt->phys_attr = AU1X_SOCK0_PSEUDO_PHYS_ATTR; - skt->phys_mem = AU1X_SOCK0_PSEUDO_PHYS_MEM; + skt->phys_attr = AU1X_SOCK0_PHYS_ATTR; + skt->phys_mem = AU1X_SOCK0_PHYS_MEM; } -#ifndef CONFIG_MIPS_XXS1500 else { skt->virt_io = (void *) (ioremap((phys_t)AU1X_SOCK1_IO, 0x1000) - (u32)mips_io_port_base); - skt->phys_attr = AU1X_SOCK1_PSEUDO_PHYS_ATTR; - skt->phys_mem = AU1X_SOCK1_PSEUDO_PHYS_MEM; + skt->phys_attr = AU1X_SOCK1_PHYS_ATTR; + skt->phys_mem = AU1X_SOCK1_PHYS_MEM; } -#endif pcmcia_base_vaddrs[i] = (u32 *)skt->virt_io; ret = ops->hw_init(skt); diff --git a/drivers/pcmcia/au1000_generic.h b/drivers/pcmcia/au1000_generic.h index aa743f6..a324d32 100644 --- a/drivers/pcmcia/au1000_generic.h +++ b/drivers/pcmcia/au1000_generic.h @@ -36,10 +36,6 @@ #define AU1X_SOCK0_IO 0xF00000000ULL #define AU1X_SOCK0_PHYS_ATTR 0xF40000000ULL #define AU1X_SOCK0_PHYS_MEM 0xF80000000ULL -/* pseudo 32 bit phys addresses, which get fixed up to the - * real 36 bit address in fixup_bigphys_addr() */ -#define AU1X_SOCK0_PSEUDO_PHYS_ATTR 0xF4000000 -#define AU1X_SOCK0_PSEUDO_PHYS_MEM 0xF8000000 /* pcmcia socket 1 needs external glue logic so the memory map * differs from board to board. @@ -48,8 +44,6 @@ #define AU1X_SOCK1_IO 0xF08000000ULL #define AU1X_SOCK1_PHYS_ATTR 0xF48000000ULL #define AU1X_SOCK1_PHYS_MEM 0xF88000000ULL -#define AU1X_SOCK1_PSEUDO_PHYS_ATTR 0xF4800000 -#define AU1X_SOCK1_PSEUDO_PHYS_MEM 0xF8800000 #endif struct pcmcia_state { diff --git a/drivers/pcmcia/db1xxx_ss.c b/drivers/pcmcia/db1xxx_ss.c index b35b72b..3889cf07 100644 --- a/drivers/pcmcia/db1xxx_ss.c +++ b/drivers/pcmcia/db1xxx_ss.c @@ -43,9 +43,9 @@ struct db1x_pcmcia_sock { void *virt_io; /* the "pseudo" addresses of the PCMCIA space. */ - unsigned long phys_io; - unsigned long phys_attr; - unsigned long phys_mem; + phys_addr_t phys_io; + phys_addr_t phys_attr; + phys_addr_t phys_mem; /* previous flags for set_socket() */ unsigned int old_flags; @@ -404,7 +404,6 @@ static int __devinit db1x_pcmcia_socket_probe(struct platform_device *pdev) { struct db1x_pcmcia_sock *sock; struct resource *r; - phys_t physio; int ret, bid; sock = kzalloc(sizeof(struct db1x_pcmcia_sock), GFP_KERNEL); @@ -465,7 +464,7 @@ static int __devinit db1x_pcmcia_socket_probe(struct platform_device *pdev) * for this socket (usually the 36bit address shifted 4 to the * right). */ - r = platform_get_resource_byname(pdev, IORESOURCE_MEM, "pseudo-attr"); + r = platform_get_resource_byname(pdev, IORESOURCE_MEM, "pcmcia-attr"); if (!r) { printk(KERN_ERR "pcmcia%d has no 'pseudo-attr' resource!\n", sock->nr); @@ -477,7 +476,7 @@ static int __devinit db1x_pcmcia_socket_probe(struct platform_device *pdev) * pseudo-mem: The 32bit address of the PCMCIA memory space for * this socket (usually the 36bit address shifted 4 to the right) */ - r = platform_get_resource_byname(pdev, IORESOURCE_MEM, "pseudo-mem"); + r = platform_get_resource_byname(pdev, IORESOURCE_MEM, "pcmcia-mem"); if (!r) { printk(KERN_ERR "pcmcia%d has no 'pseudo-mem' resource!\n", sock->nr); @@ -489,7 +488,7 @@ static int __devinit db1x_pcmcia_socket_probe(struct platform_device *pdev) * pseudo-io: The 32bit address of the PCMCIA IO space for this * socket (usually the 36bit address shifted 4 to the right). */ - r = platform_get_resource_byname(pdev, IORESOURCE_MEM, "pseudo-io"); + r = platform_get_resource_byname(pdev, IORESOURCE_MEM, "pcmcia-io"); if (!r) { printk(KERN_ERR "pcmcia%d has no 'pseudo-io' resource!\n", sock->nr); @@ -497,12 +496,6 @@ static int __devinit db1x_pcmcia_socket_probe(struct platform_device *pdev) } sock->phys_io = r->start; - - /* IO: we must remap the full 36bit address (for reference see - * alchemy/common/setup.c::__fixup_bigphys_addr()) - */ - physio = ((phys_t)sock->phys_io) << 4; - /* * PCMCIA client drivers use the inb/outb macros to access * the IO registers. Since mips_io_port_base is added @@ -511,7 +504,7 @@ static int __devinit db1x_pcmcia_socket_probe(struct platform_device *pdev) * to access the I/O or MEM address directly, without * going through this "mips_io_port_base" mechanism. */ - sock->virt_io = (void *)(ioremap(physio, IO_MAP_SIZE) - + sock->virt_io = (void *)(ioremap(sock->phys_io, IO_MAP_SIZE) - mips_io_port_base); if (!sock->virt_io) { @@ -547,8 +540,8 @@ static int __devinit db1x_pcmcia_socket_probe(struct platform_device *pdev) goto out2; } - printk(KERN_INFO "Alchemy Db/Pb1xxx pcmcia%d @ io/attr/mem %08lx" - "(%p) %08lx %08lx card/insert/stschg/eject irqs @ %d " + printk(KERN_INFO "Alchemy Db/Pb1xxx pcmcia%d @ io/attr/mem %09llx" + "(%p) %09llx %09llx card/insert/stschg/eject irqs @ %d " "%d %d %d\n", sock->nr, sock->phys_io, sock->virt_io, sock->phys_attr, sock->phys_mem, sock->card_irq, sock->insert_irq, sock->stschg_irq, sock->eject_irq); diff --git a/drivers/pcmcia/xxs1500_ss.c b/drivers/pcmcia/xxs1500_ss.c index 4e36930..61560cd 100644 --- a/drivers/pcmcia/xxs1500_ss.c +++ b/drivers/pcmcia/xxs1500_ss.c @@ -56,10 +56,9 @@ struct xxs1500_pcmcia_sock { struct pcmcia_socket socket; void *virt_io; - /* the "pseudo" addresses of the PCMCIA space. */ - unsigned long phys_io; - unsigned long phys_attr; - unsigned long phys_mem; + phys_addr_t phys_io; + phys_addr_t phys_attr; + phys_addr_t phys_mem; /* previous flags for set_socket() */ unsigned int old_flags; @@ -211,7 +210,6 @@ static int __devinit xxs1500_pcmcia_probe(struct platform_device *pdev) { struct xxs1500_pcmcia_sock *sock; struct resource *r; - phys_t physio; int ret, irq; sock = kzalloc(sizeof(struct xxs1500_pcmcia_sock), GFP_KERNEL); @@ -225,9 +223,9 @@ static int __devinit xxs1500_pcmcia_probe(struct platform_device *pdev) * for this socket (usually the 36bit address shifted 4 to the * right). */ - r = platform_get_resource_byname(pdev, IORESOURCE_MEM, "pseudo-attr"); + r = platform_get_resource_byname(pdev, IORESOURCE_MEM, "pcmcia-attr"); if (!r) { - dev_err(&pdev->dev, "missing 'pseudo-attr' resource!\n"); + dev_err(&pdev->dev, "missing 'pcmcia-attr' resource!\n"); goto out0; } sock->phys_attr = r->start; @@ -236,9 +234,9 @@ static int __devinit xxs1500_pcmcia_probe(struct platform_device *pdev) * pseudo-mem: The 32bit address of the PCMCIA memory space for * this socket (usually the 36bit address shifted 4 to the right) */ - r = platform_get_resource_byname(pdev, IORESOURCE_MEM, "pseudo-mem"); + r = platform_get_resource_byname(pdev, IORESOURCE_MEM, "pcmcia-mem"); if (!r) { - dev_err(&pdev->dev, "missing 'pseudo-mem' resource!\n"); + dev_err(&pdev->dev, "missing 'pcmcia-mem' resource!\n"); goto out0; } sock->phys_mem = r->start; @@ -247,19 +245,14 @@ static int __devinit xxs1500_pcmcia_probe(struct platform_device *pdev) * pseudo-io: The 32bit address of the PCMCIA IO space for this * socket (usually the 36bit address shifted 4 to the right). */ - r = platform_get_resource_byname(pdev, IORESOURCE_MEM, "pseudo-io"); + r = platform_get_resource_byname(pdev, IORESOURCE_MEM, "pcmcia-io"); if (!r) { - dev_err(&pdev->dev, "missing 'pseudo-io' resource!\n"); + dev_err(&pdev->dev, "missing 'pcmcia-io' resource!\n"); goto out0; } sock->phys_io = r->start; - /* for io must remap the full 36bit address (for reference see - * alchemy/common/setup.c::__fixup_bigphys_addr) - */ - physio = ((phys_t)sock->phys_io) << 4; - /* * PCMCIA client drivers use the inb/outb macros to access * the IO registers. Since mips_io_port_base is added @@ -268,7 +261,7 @@ static int __devinit xxs1500_pcmcia_probe(struct platform_device *pdev) * to access the I/O or MEM address directly, without * going through this "mips_io_port_base" mechanism. */ - sock->virt_io = (void *)(ioremap(physio, IO_MAP_SIZE) - + sock->virt_io = (void *)(ioremap(sock->phys_io, IO_MAP_SIZE) - mips_io_port_base); if (!sock->virt_io) { -- cgit v1.1