* RE: [PATCH/2.6.17-rc4 7/10] Powerpc: workaround for tsi108 pci c onfi g read exception
From: Zang Roy-r61911 @ 2006-05-18 5:56 UTC (permalink / raw)
To: Kumar Gala
Cc: linuxppc-dev list, Yang Xin-Xin-r48390, Paul Mackerras,
Alexandre.Bounine
PB and PCI error flags will be set when issuing a PCI/X configuration
read request. The error flags should be cleared or the system will be
halt.
Alex, is it right?
Roy
On May 17, 2006, at 5:14 AM, Zang Roy-r61911 wrote:
> Workaround for Tundra Semiconductor tsi108 host bridge pci config read
> exception
Can you explain further why this is needed?
>
> Signed-off-by: Alexandre Bounine <alexandreb@tundra.com>
> Signed-off-by: Roy Zang <tie-fei.zang@freescale.com>
>
> ---
>
> arch/powerpc/kernel/traps.c | 13 +++++++++++++
> 1 files changed, 13 insertions(+), 0 deletions(-)
>
> 0575fbe21e4f1045528bb91ec4b34bb7955c4a92
> diff --git a/arch/powerpc/kernel/traps.c b/arch/powerpc/kernel/traps.c
> index 064a525..7468d76 100644
> --- a/arch/powerpc/kernel/traps.c
> +++ b/arch/powerpc/kernel/traps.c
> @@ -262,6 +262,19 @@ #if defined(CONFIG_PPC_PMAC) && defined(
> }
> }
> #endif /* CONFIG_PPC_PMAC && CONFIG_PPC32 */
> +
> +#ifdef CONFIG_TSI108_BRIDGE
> + extern void tsi108_clear_pci_cfg_error(void);
> + const struct exception_table_entry *entry;
> +
> + /* Are we prepared to handle this fault? */
> + if ((entry = search_exception_tables(regs->nip)) != NULL) {
> + tsi108_clear_pci_cfg_error();
> + regs->msr |= MSR_RI;
> + regs->nip = entry->fixup;
> + return 1;
> + }
> +#endif /* CONFIG_TSI108_BRIDGE */
> return 0;
> }
>
> --
> 1.3.0
> _______________________________________________
> Linuxppc-dev mailing list
> Linuxppc-dev@ozlabs.org
> https://ozlabs.org/mailman/listinfo/linuxppc-dev
^ permalink raw reply
* Re: [HACK] add sandpoint + flattened dt support to arch/powerpc/boot
From: Benjamin Herrenschmidt @ 2006-05-18 5:32 UTC (permalink / raw)
To: Mark A. Greer; +Cc: linuxppc-dev
In-Reply-To: <20060518002142.GA23182@mag.az.mvista.com>
On Wed, 2006-05-17 at 17:21 -0700, Mark A. Greer wrote:
> Hi,
>
> This is a patch with some very rough code that adds support for the
> sandpoint to arch/powerpc. I'm posting it now in the hope that it
> will spark a deeper discussion on exactly how we're going to support
> flattened device tree code in the bootwrapper for non-OF platforms.
> I'm also hoping that it will help others that are working on the same
> sort of thing so we can help each other.
>
> Again, the code is rough with lots of hacks but it boots and is
> something we can point to during this discussion.
>
> So...I am on the right path?
Well, we'll see, let's have a look :)
> diff --git a/arch/powerpc/Kconfig b/arch/powerpc/Kconfig
> index 6729c98..de09eac 100644
> --- a/arch/powerpc/Kconfig
> +++ b/arch/powerpc/Kconfig
> @@ -323,7 +323,10 @@ config PPC_ISERIES
>
> config EMBEDDED6xx
> bool "Embedded 6xx/7xx/7xxx-based board"
> - depends on PPC32 && BROKEN
> + depends on PPC32
> + select PPC_UDBG_16550
> + select MPIC
> + select MPIC_SERIAL
Not totally related to your patch but I'm tempted to turn that into a
"Generic 6xx/7xx/7xxx" rather than "embedded" if we manage to always
avoid board specific code most of the time, but instead add necessary
bits in the device-tree. We still need a per-board Kconfig option I
think that will just select the necessary bits and pieces (and more than
one can be selected at one time). Also, I think right now, the embedded
stuff is +/- exclusive from the MULTIPLATFORM stuff, that must be fixed
asap. We are all living in the same kernel now :)
> +
> +config MPIC_SERIAL
> + depends on EMBEDDED6xx
> + bool
> + default n
Not sure what the above is, I'll have a look further in the patch but I
can already tell you that it should be your per-board
CONFIG_PPC_SANDPOINT for example that "selects" the various bits you
need, and thus the above should depend on it, not EMBEDDED6xx... In fact
I'm wondering wether we should kill EMBEDDED6xx :) Unless it's purely a
reference to arch/powerpc/platforms/embedded6xx/ as a storage place for
board setup_xxx.c files which is useful for platforms that really don't
need more than one or 2 files in there.
> +ifeq ($(CONFIG_EMBEDDED6xx),y)
> +src-boot += prom_embedded.c ns16550.c dts/sandpoint.S \
> + dt_utils.c sandpoint.c mpc10x.c
> +else
> +src-boot += prom.c
> +endif
Again, I think CONFIG_EMBEDDED6xx is the wrong approach. I'd like to
avoid the word "embedded" altogether anyway :) Thing is, it would be
good if ultimately, embedded boards firmware was capable of passing a
device-tree. Thus, embedded a device-tree in the zImage wrapper is more
like a "fallback" solution which has the side effect of creating machine
specific zImage's.
That is why, I think, what we should do is have rules for building
separate zImage wrappers. The basic OF one (which exists in both real
and virtual mode versions and that I'd like to make capable of also
working if a flat device-tree is passed in), the zImage.sandpoint, the
zImage.prep, and whatever...
Thus enabling CONFIG_PPC_MYBOARD should trigger the build of the
board-flavored version of the wrapper in addition to all the other ones
for the other boards built as part of a given config.
I know it's a bit difficult because the current boot Kbuild rules aren't
really design for multiple targets but I think that's the way to go.
.../...
About the .dts file, we should look into "shipping" a pre-built one so
that dtc is not required for building a kernel. I think we already have
a mecanism for providing "shipped" versions of files and having the
option of rebuilding them from source (though I don't have the details
in mind at the moment)
> diff --git a/arch/powerpc/boot/io.h b/arch/powerpc/boot/io.h
.../.. io stuffs ...
Well... io accessors in here can't harm, the question is more how they
are used ;)
> +#define PCI_DEVFN(slot,func) ((((slot) & 0x1f) << 3) | ((func) & 0x07))
> +
> +/* Indirect PCI config space access routines */
> +static inline void
> +pci_indirect_read_config_byte(u32 *cfg_addr, u32 *cfg_data, int devfn,
> + int offset, u8 *val)
> +{
> + out_be32(cfg_addr,
> + ((offset & 0xfc) << 24) | (devfn << 16) | (0 << 8) | 0x80);
> + *val = in_8((u8 *)(cfg_data + (offset & 3)));
> + return;
> +}
> +
> +static inline void
> +pci_indirect_read_config_dword(u32 *cfg_addr, u32 *cfg_data, int devfn,
> + int offset, u32 *val)
> +{
> + out_be32(cfg_addr,
> + ((offset & 0xfc) << 24) | (devfn << 16) | (0 << 8) | 0x80);
> + *val = in_le32(cfg_data + (offset & 3));
> + return;
> +}
That's more annoying... if we start having PCI config space access in
the boot wrapper how far will it stop ? I'd rather keep that sort of
stuff local to myboard.c file unless it's really worth sharing... Unless
we end up with really common need for them in which case those should be
in separate files, possibly in a separate dir (thinking about it, prep
will probably need that too)
The main problem is how do we do printf kind of things from such a
bootloader. That needs IO, thus uart drivers etc... possibly parsing
from the device-tree etc... I'm tempted to just have a global
"boot_puts" function pointer set by the board and/or OF code to use
through the bootloader, maybe a shared uart.c file with various uart
bits and keep the gory implementation details of mapping the uart and
using those uart bits in the myboard.c file...
> +#ifdef CONFIG_PPC_OF
> typedef void (*kernel_entry_t)( unsigned long,
> unsigned long,
> void *,
> void *);
> +#else
> +void platform_fixups(void *dt_blob);
> +extern void *dt_blob_start;
> +extern void *dt_blob_end;
> +void edit_cmdline(void *dt_blob_start);
> +typedef void (*kernel_entry_t)(void *dtb_addr, void *kernel_entry_paddr,
> + void *must_be_null);
> +#endif
Why 2 different prototypes for kernel_entry ?
I think we need something like CONFIG_PPC_BOOT_HAS_BUILTIN_DT that
enables various DT related stuff in a clean way and have a builtin_dt.h
file with related prototypes & definitions.
> #undef DEBUG
> @@ -218,7 +227,7 @@ void start(unsigned long a1, unsigned lo
> if (getprop(chosen_handle, "stdout", &stdout, sizeof(stdout)) != 4)
> exit();
>
> - printf("\n\rzImage starting: loaded at 0x%p (sp: 0x%p)\n\r", _start, sp);
> + printf("\n\rzImage starting: loaded at 0x%p (sp: 0x%p)\n\r", _start,sp);
Gratuituous uglyfication :)
> /*
> * The first available claim_base must be above the end of the
> @@ -238,7 +247,7 @@ void start(unsigned long a1, unsigned lo
> gunzip(elfheader, sizeof(elfheader),
> (unsigned char *)vmlinuz.addr, &len);
> } else
> - memcpy(elfheader, (const void *)vmlinuz.addr, sizeof(elfheader));
> + memcpy(elfheader, (const void *)vmlinuz.addr,sizeof(elfheader));
Gratuituous uglyfication (bis) :)
> if (!is_elf64(elfheader) && !is_elf32(elfheader)) {
> printf("Error: not a valid PPC32 or PPC64 ELF file!\n\r");
> @@ -263,18 +272,21 @@ void start(unsigned long a1, unsigned lo
> initrd.size = (unsigned long)(_initrd_end - _initrd_start);
> initrd.memsize = initrd.size;
> if ( initrd.size > 0 ) {
> - printf("Allocating 0x%lx bytes for initrd ...\n\r", initrd.size);
> + printf("Allocating 0x%lx bytes for initrd ...\n\r",initrd.size);
Gratuituous uglyfication (ter) :)
> initrd.addr = try_claim(initrd.size);
> if (initrd.addr == 0) {
> - printf("Can't allocate memory for initial ramdisk !\n\r");
> + printf("Can't allocate memory for initial "
> + "ramdisk !\n\r");
> exit();
> }
> a1 = initrd.addr;
> a2 = initrd.size;
> - printf("initial ramdisk moving 0x%lx <- 0x%lx (0x%lx bytes)\n\r",
> - initrd.addr, (unsigned long)_initrd_start, initrd.size);
> - memmove((void *)initrd.addr, (void *)_initrd_start, initrd.size);
> - printf("initrd head: 0x%lx\n\r", *((unsigned long *)initrd.addr));
> + printf("initial ramdisk moving 0x%lx <- 0x%lx "
> + "(0x%lx bytes)\n\r", initrd.addr,
> + (unsigned long)_initrd_start, initrd.size);
> + memmove((void *)initrd.addr, (void *)_initrd_start,initrd.size);
> + printf("initrd head: 0x%lx\n\r",
> + *((unsigned long *)initrd.addr));
> }
>
> /* Eventually gunzip the kernel */
> @@ -299,6 +311,7 @@ #endif
> flush_cache((void *)vmlinux.addr, vmlinux.size);
>
> kernel_entry = (kernel_entry_t)vmlinux.addr;
> +#ifdef CONFIG_PPC_OF
> #ifdef DEBUG
> printf( "kernel:\n\r"
> " entry addr = 0x%lx\n\r"
> @@ -311,9 +324,20 @@ #ifdef DEBUG
> #endif
> kernel_entry(a1, a2, prom, NULL);
> +#else /* !CONFIG_PPC_OF ==> flattened device tree */
> +#ifdef DEBUG
> + printf("kernel:\n\r"
> + " entry addr = 0x%lx\n\r"
> + " flattened dt = 0x%lx\n\r",
> + (unsigned long)kernel_entry, &dt_blob_start);
> +#endif
> + edit_cmdline(&dt_blob_start);
> + platform_fixups(&dt_blob_start);
>
> + kernel_entry(&dt_blob_start, (void *)vmlinux.addr, NULL);
> +#endif
Let's avoid #ifdef's if we can... provide function pointers where it
matter setup by the board/OF init code maybe but not ifdef's. Afaik,
only WD actually _likes_ ifdef's :)
> +++ b/arch/powerpc/boot/mpc10x.c
We'll need some subdirs here or it will get messy quick...
> diff --git a/arch/powerpc/boot/ns16550.c b/arch/powerpc/boot/ns16550.c
Same comment as above
> +/* XXXX Get info from dt instead */
> +static struct {
> + int baud_base;
> + unsigned long com_port;
> + u16 iomem_reg_shift;
> +} rs_table[RS_TABLE_SIZE] = {
> + { BASE_BAUD, SANDPOINT_SERIAL_0, 0 },
> + { BASE_BAUD, SANDPOINT_SERIAL_1, 0 },
> +};
Well, your own comment says it all :) I think we might need an
equivalent of prom_parse.c in the bootloader
+
> +/* #ifndef CONFIG_PPC_OF XXXX */
> +
> +/* Definitions used by the flattened device tree */
> +#define OF_DT_HEADER 0xd00dfeed /* marker */
> +#define OF_DT_BEGIN_NODE 0x1 /* Start of node, full name */
> +#define OF_DT_END_NODE 0x2 /* End node */
> +#define OF_DT_PROP 0x3 /* Property: name off, size,
> + * content */
> +#define OF_DT_NOP 0x4 /* nop */
> +#define OF_DT_END 0x9
> +
> +#define OF_DT_VERSION 0x10
.../... (flat DT definitions)
Can't we share the kernel definition here ? Maybe split the kenrel one
in two if necessary ? I don't like that sort of duplication...
> +unsigned long serial_init(int chan, void *ignored);
> +void serial_putc(unsigned long com_port, unsigned char c);
> +unsigned char serial_getc(unsigned long com_port);
> +int serial_tstc(unsigned long com_port);
Forgot to mention that earlier, but those should be uart_* imho (or even
16550_*) since we'll surely have different species of serial ports to
deal with and I don't like name mixup. Unless we consider that only ever
one of those serial files get built in a given zImage wrapper in which
case it makes some sense...
> +call_prom(const char *service, int nargs, int nret, ...)
> +{
> +
> + static struct {
> + char *service;
> + int ((*rtn)(int nargs, int nret, va_list args));
> + } services[] = {
> + { "exit", service_exit },
> + { "finddevice", service_finddevice },
> + { "getprop", service_getprop },
> + /* { "write", service_write }, */
> + };
> + va_list args;
> + int i, rc = 0;
> +
> + for (i=0; i<ARRAY_SIZE(services); i++)
> + if (!strcmp(service, services[i].service)) {
> + va_start(args, nret);
> + rc = services[i].rtn(nargs, nret, args);
> + va_end(args);
> + }
> +
> + return (nret > 0)? rc : 0;
> +}
I don't think call_prom is the right abstraction :) We have to decide
here, we have two choices:
- Either a given zImage wrapper can be booted from both a real OF and
have an embedded flat DT, in which case we need function pointers and I
think the right abstraction is for individual prom functions to have
their function pointer rathaer than "multiplex" through call_prom
- Or a given zImage wrapper is a single board thingy. That is it is
either the OF wrapper _or_ it can contain an embedded DT. Probably
easier that way and no need for funciton pointers nor mux at all... I
tend to think that might be the right way to go at this point...
Especially since as I wrote before, I'm tempted to think we should just
in that case build multiple zImage wrappers. The only thing there is
that if we go that route, I'd like the "OF" one to also be able to boot
with a flat DT passed on entry to it so it will be useable as a generic
zImage for firmwares that have a flat DT to pass to the kernel. The only
"issue" with that is it might be difficult to have console output unless
we do the putc function pointer thing I described earlier and have
several "uart" impleentations with differnet names and enough
device-tree parsing to be able to instanciate them (almost a copy of
kernel's legacy serial stuff). Another option if we want console output
is to add something to the DT header specifically for use by such early
boot code that specifies a function that can be called back for
displaying thing as long as a given memory range isn't overriden (the FW
itself)
Comments are welcome here, this is I think the most tricky point and
where we need to make a decision...
> diff --git a/arch/powerpc/kernel/legacy_serial.c b/arch/powerpc/kernel/legacy_serial.c
> index 6e67b5b..2feca55 100644
> --- a/arch/powerpc/kernel/legacy_serial.c
> +++ b/arch/powerpc/kernel/legacy_serial.c
> @@ -147,9 +147,11 @@ static int __init add_legacy_isa_port(st
> if (reg == NULL)
> return -1;
>
> +#if 0 /* XXXX */
> /* Verify it's an IO port, we don't support anything else */
> if (!(reg[0] & 0x00000001))
> return -1;
> +#endif
Gack ? Care to explain ?
> +obj-$(CONFIG_EMBEDDED6xx) += embedded6xx/
Hrmph.... (random noises)
> +/*
> + * Define all of the IRQ senses and polarities. Taken from the
> + * Sandpoint X3 User's manual.
> + */
> +static u_char sandpoint_openpic_initsenses[] __initdata = {
> + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* 0: SIOINT */
> + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* 1: XXXX FILL?? */
> + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* 2: PCI Slot 1 */
> + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* 3: PCI Slot 2 */
> + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* 4: PCI Slot 3 */
> + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* 5: PCI Slot 4 */
> + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* 6: XXXX FILL?? */
> + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* 7: XXXX FILL?? */
> + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* 8: IDE (INT C) */
> + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* 9: IDE (INT D) */
> + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* 10: */
> + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* 11: */
> + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* 12: */
> + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* 13: */
> + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* 14: */
> + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* 15: */
> +};
Of course you already know that this has to go into the device-tree :)
> +/*
> + * Interrupt setup and service. Interrrupts on the Sandpoint come
> + * from the four PCI slots plus the 8259 in the Winbond Super I/O (SIO).
> + * The 8259 is cascaded from EPIC IRQ0, IRQ1-4 map to PCI slots 1-4,
> + * IDE is on EPIC 7 and 8.
> + */
> +static void __init
> +sandpoint_init_IRQ(void)
> +{
> + struct mpic *mpic;
> + phys_addr_t openpic_paddr = 0xfc000000 + /* XXXX */
> + MPC10X_EUMB_EPIC_OFFSET;
> +
> + /* This doesn't handle i2c, dma, i2o, or timer intrs right now XXXX */
> + mpic = mpic_alloc(openpic_paddr, MPIC_PRIMARY,
> + 16 /* XXXX otherwise num_sources used */ ,
> + NUM_8259_INTERRUPTS,
> + 0, /* was 16 */
> + NR_IRQS - 4 /* XXXX */,
> + sandpoint_openpic_initsenses,
> + sizeof(sandpoint_openpic_initsenses), " EPIC ");
> +
> + BUG_ON(mpic == NULL); /* XXXX */
> + mpic_assign_isu(mpic, 0, openpic_paddr + 0x10200);
> + mpic_init(mpic);
> + mpic_setup_cascade(NUM_8259_INTERRUPTS, i8259_irq_cascade, NULL);
> + i8259_init(0xfef00000, 0); /* pci iack addr */
> +}
It would be really nice if we could define properties to put in the
"mpic" node that defines the various attributes to pass to it and that
sort of thing so that we can have a generic "mpic_init_IRQ" that walks
all MPICs and instanciate & link them in cascade when necessary... Oh
well, food for thought, no high priority there.
> +/*
> + * Freescale Sandpoint interrupt routing.
> + */
> +static inline int
> +x3_map_irq(struct pci_dev *dev, unsigned char idsel, unsigned char pin)
> +{
> + static char pci_irq_table[][4] =
> + /*
> + * PCI IDSEL/INTPIN->INTLINE
> + * A B C D
> + */
> + {
> + { 16, 0, 0, 0 }, /* IDSEL 11 - i8259 on Winbond */
> + { 0, 0, 0, 0 }, /* IDSEL 12 - unused */
> + { 18, 21, 20, 19 }, /* IDSEL 13 - PCI slot 1 */
> + { 19, 18, 21, 20 }, /* IDSEL 14 - PCI slot 2 */
> + { 20, 19, 18, 21 }, /* IDSEL 15 - PCI slot 3 */
> + { 21, 20, 19, 18 }, /* IDSEL 16 - PCI slot 4 */
> + };
> +
> + const long min_idsel = 11, max_idsel = 16, irqs_per_slot = 4;
> + return PCI_IRQ_TABLE_LOOKUP;
> +}
Of course you also know that the above has to go into the device-tree as
well :)
> +static void __init
> +sandpoint_setup_winbond_83553(struct pci_controller *hose)
> +{
> + int devfn;
> +
> + /*
> + * Route IDE interrupts directly to the 8259's IRQ 14 & 15.
> + * We can't route the IDE interrupt to PCI INTC# or INTD# because those
> + * woule interfere with the PMC's INTC# and INTD# lines.
> + */
> + /*
> + * Winbond Fcn 0
> + */
> + devfn = PCI_DEVFN(11,0);
> +
> + early_write_config_byte(hose,
> + 0,
> + devfn,
> + 0x43, /* IDE Interrupt Routing Control */
> + 0xef);
> + early_write_config_word(hose,
> + 0,
> + devfn,
> + 0x44, /* PCI Interrupt Routing Control */
> + 0x0000);
> +
> + /* Want ISA memory cycles to be forwarded to PCI bus */
> + early_write_config_byte(hose,
> + 0,
> + devfn,
> + 0x48, /* ISA-to-PCI Addr Decoder Control */
> + 0xf0);
> +
> + /* Enable Port 92. */
> + early_write_config_byte(hose,
> + 0,
> + devfn,
> + 0x4e, /* AT System Control Register */
> + 0x06);
> + /*
> + * Winbond Fcn 1
> + */
> + devfn = PCI_DEVFN(11,1);
> +
> + /* Put IDE controller into native mode. */
> + early_write_config_byte(hose,
> + 0,
> + devfn,
> + 0x09, /* Programming interface Register */
> + 0x8f);
> +
> + /* Init IRQ routing, enable both ports, disable fast 16 */
> + early_write_config_dword(hose,
> + 0,
> + devfn,
> + 0x40, /* IDE Control/Status Register */
> + 0x00ff0011);
> + return;
> +}
How much of the above could/should be done by the wrapper since it does
config space access hacks ? My idea is that we might be able to remove
pretty much _everything_ from this sandpoint.c file ... My dream would
be to have a generic board support that matches a list of known boards
for which no special code is needed, only the device-tree (and possibly
the zImage wrapper).
The main things preventing us from doing that at the moment is that we
need to define enough standard nodes/properties to have the ability to
the setup interrupt controller tree entirely from it, and the PCI host
bridges as well. For the later, that means encoding enough of the bridge
type and config access method to allow having a generic device-tree
based piece of code that does what add_bridges() does in a generic way.
> +static int
> +x3_exclude_device(u_char bus, u_char devfn)
> +{
> + if ((bus == 0) && (PCI_SLOT(devfn) == 0))
> + return PCIBIOS_DEVICE_NOT_FOUND;
> + else
> + return PCIBIOS_SUCCESSFUL;
> +}
If we need to exclude devices in a generic way, we could define a
specific exclude list property... or use what I think has already been
defined by OF for indicating which slots should be probed :)
> +static int __init
> +add_bridge(struct device_node *dev)
> +{
> + int len;
> + struct pci_controller *hose;
> + int *bus_range;
> +
> + printk("Adding PCI host bridge %s\n", dev->full_name);
> +
> + bus_range = (int *) get_property(dev, "bus-range", &len);
> + if (bus_range == NULL || len < 2 * sizeof(int))
> + printk(KERN_WARNING "Can't get bus-range for %s, assume"
> + " bus 0\n", dev->full_name);
> +
> + hose = pcibios_alloc_controller();
> + if (hose == NULL)
> + return -ENOMEM;
> + hose->first_busno = bus_range ? bus_range[0] : 0;
> + hose->last_busno = bus_range ? bus_range[1] : 0xff;
> + setup_indirect_pci(hose, 0xfec00000, 0xfee00000);
> +
> + /* Interpret the "ranges" property */
> + /* This also maps the I/O region and sets isa_io/mem_base */
> + pci_process_bridge_OF_ranges(hose, dev, 1);
> +
> + return 0;
> +}
See above comments.... Looks ok but how many boards will end up having
very small variations of that same code and thus can we find a way to
make that totally generic...
> +u32 mag_dbg = 0;
> +
> +static void __init
> +sandpoint_setup_arch(void)
> +{
> + loops_per_jiffy = 100000000 / HZ;
> + isa_io_base = 0xfe000000;
> + isa_mem_base = 0x80000000;
> + pci_dram_offset = 0;
The isa stuff should probably be deduced from the device-tree, the
default LPJ too, probably not even useful anymore since we are using the
timebase nowadays.
> +#ifdef CONFIG_BLK_DEV_INITRD
> + if (initrd_start)
> + ROOT_DEV = Root_RAM0;
> + else
> +#endif
> +#ifdef CONFIG_ROOT_NFS
> + ROOT_DEV = Root_NFS;
> +#else
> + ROOT_DEV = Root_HDA1;
> +#endif
Same comment as above, this is fairly generic and we could even imagine
something like a linux,default-boot-device thing in the DT...
> +#if 1 /* XXXX NEW */
> + {
> + struct device_node *np;
> +
> + mag_dbg = 1;
> + for (np = NULL; (np = of_find_node_by_type(np, "pci")) != NULL;)
> + add_bridge(np);
> + mag_dbg = 0;
> +
> + ppc_md.pci_swizzle = common_swizzle;
> + ppc_md.pci_map_irq = x3_map_irq;
> + ppc_md.pci_exclude_device = x3_exclude_device; /* XXXX */
> + }
> +#endif
The problem of IRQ mapping & swizzling will be solved real soon
(hopefully early 2.6.18) so that we can completely rely on the
device-tree without needing device_node's for every PCI device. Only P2P
bridges that don't do standard swizzling (typically bridges soldered on
the motherboard) will require device nodes. I'm working on it :)
The idea is that the host bridge will need the standard interrupt-map &
interrupt-map-mask which will indicate the interrupt mapping for every
line of every slot/device in the standard way handled by OF (that
already works). And I'll add code that will be able to handle PCI
devices without a node by artificially building their "interrupts"
property based on the interrupt pin config space entry and walking up
the PCI tree, doing standard swizzling when encountering a P2P bridge
until we hit something with a device-node in which case we hook our
result to the interrupt-map of that device-node.
> + if (cpu_has_feature(CPU_FTR_SPEC7450))
> + /* 745x is different. We only want to pass along enable. */
> + _set_L2CR(L2CR_L2E);
> + else if (cpu_has_feature(CPU_FTR_L2CR))
> + /* All modules have 1MB of L2. We also assume that an
> + * L2 divisor of 3 will work.
> + */
> + _set_L2CR(L2CR_L2E | L2CR_L2SIZ_1MB | L2CR_L2CLK_DIV3
> + | L2CR_L2RAM_PIPE | L2CR_L2OH_1_0 | L2CR_L2DF);
Keeping in mind my idea of having as much generic code as possible,
whould we do the above in the boot wrapper or if not, should we provide
some DT entries indicating that L2CR should be reset and to which
values ?
> +#if 0
> + /* Untested right now. */
> + if (cpu_has_feature(CPU_FTR_L3CR)) {
> + /* Magic value. */
> + _set_L3CR(0x8f032000);
> + }
> +#endif
> +}
Same comment as above but for L3CR
.../...
> +/*
> + * Fix IDE interrupts.
> + */
> +static int __init
> +sandpoint_fix_winbond_83553(void)
> +{
> + /* Make some 8259 interrupt level sensitive */
> + outb(0xe0, 0x4d0);
> + outb(0xde, 0x4d1);
> +
> + return 0;
> +}
> +
> +arch_initcall(sandpoint_fix_winbond_83553);
> +
> +/*
> + * Initialize the ISA devices on the Nat'l PC87308VUL SuperIO chip.
> + */
> +static int __init
> +sandpoint_setup_natl_87308(void)
> +{
> + u_char reg;
> +
> + /*
> + * Enable all the devices on the Super I/O chip.
> + */
> + SANDPOINT_87308_SELECT_DEV(0x00); /* Select kbd logical device */
> + SANDPOINT_87308_CFG_OUTB(0xf0, 0x00); /* Set KBC clock to 8 Mhz */
> + SANDPOINT_87308_DEV_ENABLE(0x00); /* Enable keyboard */
> + SANDPOINT_87308_DEV_ENABLE(0x01); /* Enable mouse */
> + SANDPOINT_87308_DEV_ENABLE(0x02); /* Enable rtc */
> + SANDPOINT_87308_DEV_ENABLE(0x03); /* Enable fdc (floppy) */
> + SANDPOINT_87308_DEV_ENABLE(0x04); /* Enable parallel */
> + SANDPOINT_87308_DEV_ENABLE(0x05); /* Enable UART 2 */
> + SANDPOINT_87308_CFG_OUTB(0xf0, 0x82); /* Enable bank select regs */
> + SANDPOINT_87308_DEV_ENABLE(0x06); /* Enable UART 1 */
> + SANDPOINT_87308_CFG_OUTB(0xf0, 0x82); /* Enable bank select regs */
> +
> + /* Set up floppy in PS/2 mode */
> + outb(0x09, SIO_CONFIG_RA);
> + reg = inb(SIO_CONFIG_RD);
> + reg = (reg & 0x3F) | 0x40;
> + outb(reg, SIO_CONFIG_RD);
> + outb(reg, SIO_CONFIG_RD); /* Have to write twice to change! */
> +
> + return 0;
> +}
Looks like zImage wrapper work to me... Unless we ever get booted by a
firmware that both provides a flat DT and doesn't do the above....
Ok, I'm not _ABSOLUTELY_ trying to remove the need for a myboar.c file
here ... but it might be worth doing something generic enough so that
it's either reduced to the strict minimum in many cases _or_ made
completely unnecessary if we can...
> +arch_initcall(sandpoint_setup_natl_87308);
> +
> +static int __init
> +sandpoint_request_io(void)
> +{
> + request_region(0x00,0x20,"dma1");
> + request_region(0x20,0x20,"pic1");
> + request_region(0x40,0x20,"timer");
> + request_region(0x80,0x10,"dma page reg");
> + request_region(0xa0,0x20,"pic2");
> + request_region(0xc0,0x20,"dma2");
> +
> + return 0;
> +}
The above has to go... not sure yet what is the best way but it's just a
pain... Could be made dependent on the OFDT too
> +arch_initcall(sandpoint_request_io);
> +#endif
> +
> +static void __init
> +sandpoint_map_io(void)
> +{
> + io_block_mapping(0xfe000000, 0xfe000000, 0x02000000, _PAGE_IO);
> +}
To kill absolutely. What is it there for ? At the very least, make
io_block_mapping allocate virtual space as discussed earlier. I'd like
to keep BATs for RAM anyway
> +static void
> +sandpoint_restart(char *cmd)
> +{
> + local_irq_disable();
> +
> + /* Set exception prefix high - to the firmware */
> + _nmask_and_or_msr(0, MSR_IP);
> +
> + /* Reset system via Port 92 */
> + outb(0x00, 0x92);
> + outb(0x01, 0x92);
> +
> + for(;;); /* Spin until reset happens */
> +}
The above is fairly common. (Or variations of it). In the context of a
"generic" board, we could imagine having a list of known "reboot
methods" and have a DT node indicating which one to use...
> +static void
> +sandpoint_power_off(void)
> +{
> + local_irq_disable();
> + for(;;); /* No way to shut power off with software */
> + /* NOTREACHED */
> +}
The above is _VERY_ commmon :) probably worth just not having a callback
in ppc_md. at all...
> +static void
> +sandpoint_halt(void)
> +{
> + sandpoint_power_off();
> + /* NOTREACHED */
> +}
Same comment as before.
> +static void
> +sandpoint_show_cpuinfo(struct seq_file *m)
> +{
> + seq_printf(m, "vendor\t\t: Freescale\n");
> + seq_printf(m, "machine\t\t: Sandpoint\n");
> +}
Do we need that ? We could define standard DT properties that get
printed in cpuinfo and have that in setup-common.c ...
.../... (commented out IDE code)
This is a windbond ? Can't it just be probed/detected using normal PCI
probing ?
> +
> +/*
> + * Set BAT 3 to map 0xf8000000 to end of physical memory space 1-to-1.
> + */
> +static __inline__ void
> +sandpoint_set_bat(void)
> +{
> + unsigned long bat3u, bat3l;
> +
> + __asm__ __volatile__(
> + " lis %0,0xf800\n \
> + ori %1,%0,0x002a\n \
> + ori %0,%0,0x0ffe\n \
> + mtspr 0x21e,%0\n \
> + mtspr 0x21f,%1\n \
> + isync\n \
> + sync "
> + : "=r" (bat3u), "=r" (bat3l));
> +}
WTF ?
> +TODC_ALLOC();
> +
> +static int __init
> +sandpoint_probe(void)
> +{
> + return 1;
> +}
WTF (bis) ? :)
> +define_machine(sandpoint) {
> + .name = "Sandpoint",
> + .probe = sandpoint_probe,
> + .setup_arch = sandpoint_setup_arch,
> + .setup_io_mappings = sandpoint_map_io,
> + .init_IRQ = sandpoint_init_IRQ,
> + .show_cpuinfo = sandpoint_show_cpuinfo,
> + .get_irq = mpic_get_irq,
> + .restart = sandpoint_restart,
> + .power_off = sandpoint_power_off,
> + .halt = sandpoint_halt,
> + .calibrate_decr = generic_calibrate_decr,
> + /*
> + .progress = udbg_progress,
> + */
> +};
So on the TODO list, replace as much as we can of the above with
generic_* equivalents until nothing remains :)
> diff --git a/arch/powerpc/sysdev/mpic.c b/arch/powerpc/sysdev/mpic.c
> index 7dcdfcb..1badbec 100644
> --- a/arch/powerpc/sysdev/mpic.c
> +++ b/arch/powerpc/sysdev/mpic.c
> @@ -629,6 +629,13 @@ #endif /* CONFIG_SMP */
> mb();
> }
>
> +#ifdef CONFIG_MPIC_SERIAL
> + /* For serial interrupts & set clock ratio */
> + mpic_write(mpic->gregs, MPIC_GREG_GLOBAL_CONF_1,
> + mpic_read(mpic->gregs, MPIC_GREG_GLOBAL_CONF_1)
> + | (1<<27) | (0x7<<28));
> +#endif
This should not be an #ifdef.... this should be an MPIC init flag passed
when initializing it. (and if we do generic device-tree instanciation of
PIC's, we could define standard properties to put in the MPIC node to
enable those flags).
Keep up the good work ! :)
Cheers,
Ben.
^ permalink raw reply
* RE: [PATCH/2.6.17-rc4 6/10] Add Tundra pci vendor and device ID
From: Zang Roy-r61911 @ 2006-05-18 4:50 UTC (permalink / raw)
To: Kumar Gala
Cc: linuxppc-dev list, Yang Xin-Xin-r48390, Paul Mackerras,
Alexandre.Bounine
VENDOR_ID_TUNDRA and PCI_DEVICE_ID_TUNDRA_CA91C042 have been
in pciids list.
we will summit PCI_DEVICE_ID_TUNDRA_TSI108 and
PCI_DEVICE_ID_TUNDRA_TSI109.
Dose the pci.ids file there will appear in the new kernel release?
Roy
On May 17, 2006, at 5:14 AM, Zang Roy-r61911 wrote:
> Add Tundra Semiconductor pci host bridge vendor ID and device ID
>
> Signed-off-by: Alexandre Bounine <alexandreb@tundra.com>
> Signed-off-by: Roy Zang <tie-fei.zang@freescale.com>
Did you guys submit http://pciids.sourceforge.net/ ?
- k
> ---
>
> include/linux/pci_ids.h | 4 ++++
> 1 files changed, 4 insertions(+), 0 deletions(-)
>
> 14bddf6670ca982d5cee57afa472d2199e2170fc
> diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h
> index d6fe048..c39fcfc 100644
> --- a/include/linux/pci_ids.h
> +++ b/include/linux/pci_ids.h
> @@ -2276,3 +2276,7 @@ #define PCI_DEVICE_ID_RME_DIGI32 0x9896
> #define PCI_DEVICE_ID_RME_DIGI32_PRO 0x9897
> #define PCI_DEVICE_ID_RME_DIGI32_8 0x9898
>
> +#define PCI_VENDOR_ID_TUNDRA 0x10e3
> +#define PCI_DEVICE_ID_TUNDRA_CA91C042 0x0000
> +#define PCI_DEVICE_ID_TUNDRA_TSI108 0x0108
> +#define PCI_DEVICE_ID_TUNDRA_TSI109 0xA108
> --
> 1.3.0
> _______________________________________________
> Linuxppc-dev mailing list
> Linuxppc-dev@ozlabs.org
> https://ozlabs.org/mailman/listinfo/linuxppc-dev
^ permalink raw reply
* RE: [PATCH/2.6.17-rc4 1/10] Powerpc: Add general support for mpc7 448h pc2 (Taiga) platform
From: Zang Roy-r61911 @ 2006-05-18 4:12 UTC (permalink / raw)
To: Kumar Gala
Cc: linuxppc-dev list, Yang Xin-Xin-r48390, Paul Mackerras,
Alexandre.Bounine
On May 17, 2006, at 5:13 AM, Zang Roy-r61911 wrote:
> Add support for Freescale mpcc7448 (Taiga) board support
>
> Signed-off-by: Roy Zang <tie-fei.zang@freescale.com>
>
> ---
>
> arch/powerpc/Kconfig | 24 +++
> arch/powerpc/kernel/legacy_serial.c | 39 ++++
These board port should live in platforms/embedded6xx.
Kumar, what does you mean?
All my board support code should be live in platforms/embedded6xx?
How about the legacy serial support?
> arch/powerpc/platforms/74xx/Makefile | 4
> arch/powerpc/platforms/74xx/misc.c | 38 ++++
> arch/powerpc/platforms/74xx/mpc7448_hpc2.c | 257 +++++++++++++++++
> +++++++++++
> arch/powerpc/platforms/74xx/mpc7448_hpc2.h | 80 +++++++++
> arch/powerpc/platforms/74xx/pci.c | 103 +++++++++++
> arch/powerpc/platforms/Makefile | 1
> 8 files changed, 545 insertions(+), 1 deletions(-)
> create mode 100644 arch/powerpc/platforms/74xx/Makefile
> create mode 100644 arch/powerpc/platforms/74xx/misc.c
> create mode 100644 arch/powerpc/platforms/74xx/mpc7448_hpc2.c
> create mode 100644 arch/powerpc/platforms/74xx/mpc7448_hpc2.h
> create mode 100644 arch/powerpc/platforms/74xx/pci.c
>
> 13c16416da4fa51ec771bd4863d94344d139b2f5
> diff --git a/arch/powerpc/Kconfig b/arch/powerpc/Kconfig
> index 6729c98..abeac1f 100644
> --- a/arch/powerpc/Kconfig
> +++ b/arch/powerpc/Kconfig
> @@ -332,6 +332,27 @@ config APUS
> Select APUS if configuring for a PowerUP Amiga.
> More information is available at:
> <http://linux-apus.sourceforge.net/>.
> +
> +config MPC7448HPC2
> + bool "Freescale MPC7448HPC2(Taiga)"
> + select DEFAULT_UIMAGE
> + select PPC_UDBG_16550
> + help
> + Select MPC7448HPC2 if configuring for Freescale MPC7448HPC2
> (Taiga)
> + platform
> +
> +config 74xx
> + bool
> + depends on MPC7448HPC2
> + default y
> + help
> + Select 74xx support
> +
What is the config option used for? We should get ride of.
> +config TSI108_BRIDGE
> + bool
> + depends on MPC7448HPC2
> + default y
> +
> endchoice
>
> config PPC_PSERIES
> @@ -798,7 +819,8 @@ config MCA
> bool
>
> config PCI
> - bool "PCI support" if 40x || CPM2 || PPC_83xx || PPC_85xx ||
> PPC_MPC52xx || (EMBEDDED && PPC_ISERIES)
> + bool "PCI support" if 40x || CPM2 || PPC_83xx || PPC_85xx ||
> PPC_MPC52xx || (EMBEDDED && PPC_ISERIES) \
> + || MPC7448HPC2
> default y if !40x && !CPM2 && !8xx && !APUS && !PPC_83xx && !
> PPC_85xx
> default PCI_PERMEDIA if !4xx && !CPM2 && !8xx && APUS
> default PCI_QSPAN if !4xx && !CPM2 && 8xx
> diff --git a/arch/powerpc/kernel/legacy_serial.c b/arch/powerpc/
> kernel/legacy_serial.c
> index 6e67b5b..f99238d 100644
> --- a/arch/powerpc/kernel/legacy_serial.c
> +++ b/arch/powerpc/kernel/legacy_serial.c
> @@ -134,6 +134,34 @@ static int __init add_legacy_soc_port(st
> return add_legacy_port(np, -1, UPIO_MEM, addr, addr, NO_IRQ, flags);
> }
>
> +static int __init add_legacy_tsi_port(struct device_node *np,
> + struct device_node *tsi_dev)
> +{
> + phys_addr_t addr;
> + u32 *addrp;
> + upf_t flags = UPF_BOOT_AUTOCONF | UPF_SKIP_TEST | UPF_SHARE_IRQ;
> +
> + /* We only support ports that have a clock frequency properly
> + * encoded in the device-tree.
> + */
> + if (get_property(np, "clock-frequency", NULL) == NULL)
> + return -1;
> +
> + /* Get the address */
> + addrp = of_get_address(tsi_dev, 0, NULL, NULL);
> + if (addrp == NULL)
> + return -1;
> +
> + addr = of_translate_address(tsi_dev, addrp);
> +
> + /* Add port, irq will be dealt with later. We passed a translated
> + * IO port value. It will be fixed up later along with the irq
> + */
> + return add_legacy_port(np, -1, UPIO_MEM, addr, addr, NO_IRQ, flags);
> +}
> +
This looks like its duplicating add_legacy_soc_port, why not use that
instead?
> +
> +
> static int __init add_legacy_isa_port(struct device_node *np,
> struct device_node *isa_brg)
> {
> @@ -302,6 +330,17 @@ void __init find_legacy_serial_ports(voi
> of_node_put(isa);
> }
>
> + /* First fill our array with tsi-bridge ports */
> + for (np = NULL; (np = of_find_compatible_node(np, "serial",
> "ns16550")) != NULL;) {
> + struct device_node *tsi = of_get_parent(np);
> + if (tsi && !strcmp(tsi->type, "tsi-bridge")) {
> + index = add_legacy_tsi_port(np, np);
> + if (index >= 0 && np == stdout)
> + legacy_serial_console = index;
> + }
> + of_node_put(tsi);
> + }
> +
> #ifdef CONFIG_PCI
> /* Next, try to locate PCI ports */
> for (np = NULL; (np = of_find_all_nodes(np));) {
> diff --git a/arch/powerpc/platforms/74xx/Makefile b/arch/powerpc/
> platforms/74xx/Makefile
> new file mode 100644
> index 0000000..ed5648c
> --- /dev/null
> +++ b/arch/powerpc/platforms/74xx/Makefile
> @@ -0,0 +1,4 @@
> +#
> +# Makefile for the PowerPC 74xx linux kernel.
> +#
> +obj-$(CONFIG_MPC7448HPC2) += mpc7448_hpc2.o pci.o misc.o
> diff --git a/arch/powerpc/platforms/74xx/misc.c b/arch/powerpc/
> platforms/74xx/misc.c
> new file mode 100644
> index 0000000..6228875
> --- /dev/null
> +++ b/arch/powerpc/platforms/74xx/misc.c
> @@ -0,0 +1,38 @@
> +
> +/*
> + * MPC74xx generic code.
> + *
> + * Maintained by Roy Zang
> + *
> + * Copyright 2006 Freescale Semiconductor Inc.
> + *
> + * This program is free software; you can redistribute it and/or
> modify it
> + * under the terms of the GNU General Public License as
> published by the
> + * Free Software Foundation; either version 2 of the License, or
> (at your
> + * option) any later version.
> + */
> +#include <linux/irq.h>
> +#include <linux/module.h>
> +#include <asm/irq.h>
> +extern void _nmask_and_or_msr(unsigned long nmask, unsigned long
> or_val);
> +
> +void mpc7448_hpc2_restart(char *cmd)
> +{
> + local_irq_disable();
> +
> + /* Set exception prefix high - to the firmware */
> + _nmask_and_or_msr(0, MSR_IP);
> +
> + for (;;) ; /* Spin until reset happens */
> +}
> +
> +void mpc7448_hpc2_power_off(void)
> +{
> + local_irq_disable();
> + for (;;) ; /* No way to shut power off with software */
> +}
> +
> +void mpc7448_hpc2_halt(void)
> +{
> + mpc7448_hpc2_power_off();
> +}
Move these back into mpc7448_hpc2.c. They are to specific to hpc2.
> diff --git a/arch/powerpc/platforms/74xx/mpc7448_hpc2.c b/arch/
> powerpc/platforms/74xx/mpc7448_hpc2.c
> new file mode 100644
> index 0000000..b060ae1
> --- /dev/null
> +++ b/arch/powerpc/platforms/74xx/mpc7448_hpc2.c
> @@ -0,0 +1,257 @@
> +/*
> + * mpc7448_hpc2.c
> + *
> + * Board setup routines for the Freescale Taiga platform
> + *
> + * Author: Jacob Pan
> + * jacob.pan@freescale.com
> + * Author: Xianghua Xiao
> + * updated for taiga board from emulation platform 2005.5
> + * x.xiao@freescale.com
> + * Maintainer: Roy Zang <tie-fei.zang@freescale.com>
> + *
> + * Copyright 2004-2006 Freescale Semiconductor, Inc.
> + *
> + * 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 <linux/config.h>
> +#include <linux/stddef.h>
> +#include <linux/kernel.h>
> +#include <linux/pci.h>
> +#include <linux/kdev_t.h>
> +#include <linux/console.h>
> +#include <linux/delay.h>
> +#include <linux/irq.h>
> +#include <linux/ide.h>
> +#include <linux/seq_file.h>
> +#include <linux/root_dev.h>
> +#include <linux/serial.h>
> +#include <linux/tty.h>
> +#include <linux/serial_core.h>
> +
> +#include <asm/system.h>
> +#include <asm/time.h>
> +#include <asm/machdep.h>
> +#include <asm/prom.h>
> +#include <asm/udbg.h>
> +#include <asm/tsi108.h>
> +#include <asm/pci-bridge.h>
> +#include <asm/reg.h>
> +#include <mm/mmu_decl.h>
> +#include "mpc7448_hpc2.h"
> +#include <asm/tsi108_pic.h>
> +
> +#ifndef CONFIG_PCI
> +isa_io_base = MPC7448_HPC2_ISA_IO_BASE;
> +isa_mem_base = MPC7448_HPC2_ISA_MEM_BASE;
> +pci_dram_offset = MPC7448_HPC2_PCI_MEM_OFFSET;
> +#endif
> +
> +extern int add_bridge(struct device_node *dev);
> +extern void mpc7448_hpc2_restart(char *cmd);
> +extern void mpc7448_hpc2_power_off(void);
> +extern void mpc7448_hpc2_halt(void);
> +
> +#ifdef TSI108_ETH
> +hw_info hw_info_table[TSI108_ETH_MAX_PORTS + 1] = {
> + {TSI108_CSR_ADDR_PHYS + TSI108_ETH_OFFSET,
> + TSI108_CSR_ADDR_PHYS + TSI108_ETH_OFFSET,
> + TSI108_PHY0_ADDR, IRQ_TSI108_GIGE0},
> +
> + {TSI108_CSR_ADDR_PHYS + TSI108_ETH_OFFSET + 0x400,
> + TSI108_CSR_ADDR_PHYS + TSI108_ETH_OFFSET,
> + TSI108_PHY1_ADDR, IRQ_TSI108_GIGE1},
> +
> + {TBL_END, TBL_END, TBL_END, TBL_END}
> +};
> +#endif
This table looks problematic. Look at using something like a
platform device.
> +
> +/*
> + * Define all of the IRQ senses and polarities. Taken from the
> + * mpc7448hpc manual.
> + * Note: Likely, this table and the following function should be
> + * obtained and derived from the OF Device Tree.
> + */
> +
> +static u_char mpc7448_hpc2_pic_initsenses[] __initdata = {
> + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* INT[0] XINT0 from
> FPGA */
> + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* INT[1] XINT1 from
> FPGA */
> + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* INT[2] PHY_INT from
> both GIGE */
> + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* INT[3] RESERVED */
> +};
> +
> +/*
> + * mpc7448hpc2 PCI interrupt routing. all PCI interrupt comes from
> + * external PCI source at 23. need to program pci interrupt
> control registers
> + * to route per slot IRQs.
> + */
> +
> +static inline int
> +mpc7448_hpc2_map_irq(struct pci_dev *dev, unsigned char idsel,
> + unsigned char pin)
> +{
> + static char pci_irq_table[][4] =
> + /*
> + * PCI IDSEL/INTPIN->INTLINE
> + * A B C D
> + */
> + {
> + {IRQ_PCI_INTA, IRQ_PCI_INTB, IRQ_PCI_INTC, IRQ_PCI_INTD}, /* A
> SLOT 1 IDSEL 17 */
> + {IRQ_PCI_INTB, IRQ_PCI_INTC, IRQ_PCI_INTD, IRQ_PCI_INTA}, /* B
> SLOT 2 IDSEL 18 */
> + {IRQ_PCI_INTC, IRQ_PCI_INTD, IRQ_PCI_INTA, IRQ_PCI_INTB}, /* C
> SATA IDSEL 19 */
> + {IRQ_PCI_INTD, IRQ_PCI_INTA, IRQ_PCI_INTB, IRQ_PCI_INTC}, /* D
> USB IDSEL 20 */
> + };
> +
> + const long min_idsel = 1, max_idsel = 4, irqs_per_slot = 4;
> + return PCI_IRQ_TABLE_LOOKUP;
> +}
> +
> +int mpc7448_hpc2_exclude_device(u_char bus, u_char devfn)
> +{
> + if (bus == 0 && PCI_SLOT(devfn) == 0)
> + return PCIBIOS_DEVICE_NOT_FOUND;
> + else
> + return PCIBIOS_SUCCESSFUL;
> +}
> +
> +static __inline__ void mpc7448_hpc2_l2cr_prefetch_enable(void)
> +{
> + unsigned long msscr0;
> + __asm__ __volatile__("mfspr %0, 0x3f6\n \
> + ori %0,%0,0x3\n \
> + sync \n \
> + mtspr 0x3f6,%0\n \
> + sync\n \
> + isync ":"=r"(msscr0));
> +}
> +
> +static void __init mpc7448_hpc2_setup_arch(void)
> +{
> + struct device_node *cpu;
> + struct device_node *np;
> + if (ppc_md.progress)
> + ppc_md.progress("mpc7448_hpc2_setup_arch():set_bridge", 0);
> +
> + cpu = of_find_node_by_type(NULL, "cpu");
> + if (cpu != 0) {
> + unsigned int *fp;
> +
> + fp = (int *)get_property(cpu, "clock-frequency", NULL);
> + if (fp != 0)
> + loops_per_jiffy = *fp / HZ;
> + else
> + loops_per_jiffy = 50000000 / HZ;
> + of_node_put(cpu);
> + }
> +
> +#ifdef CONFIG_ROOT_NFS
> + ROOT_DEV = Root_NFS;
> +#else
> + ROOT_DEV = Root_HDA1;
> +#endif
> +
> +#ifdef CONFIG_BLK_DEV_INITRD
> + ROOT_DEV = Root_RAM0;
> +#endif
> +
> + /* setup PCI host bridge */
> +
> +#ifdef CONFIG_PCI
> +
> + for (np = NULL; (np = of_find_node_by_type(np, "pci")) != NULL;)
> + add_bridge(np);
> +
> + ppc_md.pci_swizzle = common_swizzle;
> + ppc_md.pci_map_irq = mpc7448_hpc2_map_irq;
> + ppc_md.pci_exclude_device = mpc7448_hpc2_exclude_device;
> + if (ppc_md.progress)
> + ppc_md.progress("tsi108: resources set", 0x100);
> +#endif
> +
> +#ifdef CONFIG_DUMMY_CONSOLE
> + conswitchp = &dummy_con;
> +#endif
> +
> + printk(KERN_INFO "MPC7448HPC2 (TAIGA) Platform\n");
> + printk(KERN_INFO
> + "Jointly ported by Freescale and Tundra Semiconductor\n");
> + printk(KERN_INFO
> + "Enabling L2 cache then enabling the HID0 prefetch engine.
> \n");
> + _set_L2CR(L2CR_L2E);
> + mpc7448_hpc2_l2cr_prefetch_enable();
Hmm, can't remember what happens with the L2 setup on 74xx.
> +}
> +
> +/*
> + * Interrupt setup and service. Interrrupts on the mpc7448_hpc2 come
> + * from the four external INT pins, PCI interrupts are routed via
> + * PCI interrupt control registers, it generates internal IRQ23
> + *
> + * Interrupt routing on the Taiga Board:
> + * TSI108:PB_INT[0] -> CPU0:INT#
> + * TSI108:PB_INT[1] -> CPU0:MCP#
> + * TSI108:PB_INT[2] -> N/C
> + * TSI108:PB_INT[3] -> N/C
> + */
> +static void __init mpc7448_hpc2_init_IRQ(void)
> +{
> +
> + tsi108_pic_init(mpc7448_hpc2_pic_initsenses);
> +
> + /* Configure MPIC outputs to CPU0 */
> + tsi108_pic_set_output(0, IRQ_SENSE_EDGE, IRQ_POLARITY_NEGATIVE);
> +}
> +
> +static void __init mpc7448_hpc2_map_io(void)
> +{
> + /* PCI IO mapping */
> + io_block_mapping(MPC7448_HPC2_PCI_IO_BASE_VIRT,
> + MPC7448_HPC2_PCI_IO_BASE_PHYS, 0x00800000, _PAGE_IO);
> + /* Tsi108 CSR mapping */
> + io_block_mapping(TSI108_CSR_ADDR_VIRT, TSI108_CSR_ADDR_PHYS,
> + 0x100000, _PAGE_IO);
> +
> + /* PCI Config mapping */
> + io_block_mapping(MPC7448_HPC2_PCI_CFG_BASE_VIRT,
> + MPC7448_HPC2_PCI_CFG_BASE_PHYS,
> + MPC7448_HPC2_PCI_CFG_SIZE, _PAGE_IO);
> +
> + tsi108_pci_cfg_base = MPC7448_HPC2_PCI_CFG_BASE_VIRT;
> + /* NVRAM mapping */
> + io_block_mapping(MPC7448_HPC2_NVRAM_BASE_ADDR,
> + MPC7448_HPC2_NVRAM_BASE_ADDR, MPC7448_HPC2_NVRAM_SIZE,
> + _PAGE_IO);
> +}
You need to do this another way, we are trying to avoid using
io_block_mapping()
> +
> +void mpc7448_hpc2_show_cpuinfo(struct seq_file *m)
> +{
> + seq_printf(m, "vendor\t\t: Freescale Semiconductor\n");
> + seq_printf(m, "machine\t\t: MPC7448hpc2\n");
> +}
> +
> +/*
> + * Called very early, device-tree isn't unflattened
> + */
> +static int __init mpc7448_hpc2_probe(void)
> +{
> + /* We always match for now, eventually we should look at the flat
> + dev tree to ensure this is the board we are suppose to run on
> + */
> + return 1;
> +}
> +
> +define_machine(mpc7448_hpc2){
> + .name = "MPC7448 HPC2",
> + .probe = mpc7448_hpc2_probe,
> + .setup_arch = mpc7448_hpc2_setup_arch,
> + .init_IRQ = mpc7448_hpc2_init_IRQ,
> + .show_cpuinfo = mpc7448_hpc2_show_cpuinfo,
> + .get_irq = tsi108_pic_get_irq,
> + .restart = mpc7448_hpc2_restart,
> + .calibrate_decr = generic_calibrate_decr,
> + .setup_io_mappings = mpc7448_hpc2_map_io,
> + .progress = udbg_progress,
> +};
> diff --git a/arch/powerpc/platforms/74xx/mpc7448_hpc2.h b/arch/
> powerpc/platforms/74xx/mpc7448_hpc2.h
> new file mode 100644
> index 0000000..8833520
> --- /dev/null
> +++ b/arch/powerpc/platforms/74xx/mpc7448_hpc2.h
> @@ -0,0 +1,80 @@
> +/*
> + * mpc7448_hpc2.h
> + *
> + * Definitions for Freescale MPC7448_HPC2 platform
> + *
> + * Author: Jacob Pan
> + * jacob.pan@freescale.com
> + * Maintainer: Roy Zang <roy.zang@freescale.com>
> + *
> + * 2006 (c) Freescale Semiconductor, Inc. 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.
> + */
> +
> +#ifndef __PPC_PLATFORMS_MPC7448_HPC2_H
> +#define __PPC_PLATFORMS_MPC7448_HPC2_H
> +
> +#include <asm/ppcboot.h>
> +
> +/* Ethernet defines
> + */
> +#define TSI108_ETH
> +#define TSI108_ETH_MAX_PORTS (2) /* TSI108 Ethernet block has 2
> ports */
> +#define TSI108_PHY0_ADDR (8) /* PHY address for GIGE port 0 */
> +#define TSI108_PHY1_ADDR (9) /* PHY address for GIGE port 1 */
> +
> +/* Base Addresses for the PCI bus
> + * HOST_PCI initiator (outbound) window to PCI bus
> + */
> +#define MPC7448_HPC2_PCI_MEM_OFFSET (0x00000000)
> +#define MPC7448_HPC2_PCI_IO_BASE_PHYS (0xfa000000)
> +#define MPC7448_HPC2_PCI_IO_BASE_VIRT (MPC7448_HPC2_PCI_IO_BASE_PHYS)
> +#define MPC7448_HPC2_PCI_MEM_BASE (0xe0000000)
> +#define MPC7448_HPC2_ISA_IO_BASE (0x00000000)
> +#define MPC7448_HPC2_ISA_MEM_BASE (0x00000000)
> +
> +#define MPC7448_HPC2_PCI_CFG_BASE_PHYS (0xfb000000)
> +#define MPC7448_HPC2_PCI_CFG_BASE_VIRT
> (MPC7448_HPC2_PCI_CFG_BASE_PHYS)
> +#define MPC7448_HPC2_PCI_CFG_SIZE (0x01000000)
> +
> +#define MPC7448_HPC2_PCI_MEM_START (0xE0000000)
> +#define MPC7448_HPC2_PCI_MEM_END (0xF9FFFFFF)
> +
> +#define MPC7448_HPC2_PCI_IO_START (0xFA000000)
> +#define MPC7448_HPC2_PCI_IO_END (0xFA00FFFF)
> +
> +#define MPC7448_HPC2_PCI_CFG_OFFSET (0xFB000000)
> +#define MPC7448_HPC2_PCI_IO_OFFSET (0xFA000000)
> +
> +#define MPC7448_HPC2_PCI_MEM32_OFFSET 0x00000000 /* PCI MEM32
> space offset within
> + the PCI window */
> +#define MPC7448_HPC2_PCI_PFM_OFFSET 0x10000000 /* PCI PFM1 space
> offset within
> + the PCI window */
Can you not determine these from your flat device tree?
> +
> +/* Memory-mapped CIU resources (CPU view)
> + * The memory map is set by initialization code in monitor
> + */
> +
> +#define TSI108_CSR_ADDR_PHYS (0xC0000000) /* Physical Tsi108 CSR
> Base Address */
> +#define TSI108_CSR_ADDR_VIRT (0xF0000000) /* Virtual Tsi108 CSR
> Base Address */
Don't use hard coded VIRT addresses.
> +
> +#define FLASH_BASE_ADDR (0xFF000000) /* Boot FLASH Base Address */
> +
> +#define SDRAM_BASE_ADDR 0x00000000 /* SDRAM base address */
> +#define BOARD_SDRAM_SIZE 0x20000000 /* Default value: 512MB */
> +
> +/* Size of SDRAM space reserved for user space. This limits space
> available
> + * for dynamic allocation.
> + */
> +#define USER_RESERVED_MEM (BOARD_SDRAM_SIZE - 0x02000000)
> +
> +#define USER_RESERVED_BASE \
> + (SDRAM_BASE_ADDR + (BOARD_SDRAM_SIZE - USER_RESERVED_MEM)
> +
> +/* Memory mapped NVRAM/RTC */
> +#define MPC7448_HPC2_NVRAM_BASE_ADDR (0xFC000000)
> +#define MPC7448_HPC2_NVRAM_SIZE (0x8000)
> +
> +#endif /* __PPC_PLATFORMS_MPC7448_HPC2_H */
> diff --git a/arch/powerpc/platforms/74xx/pci.c b/arch/powerpc/
> platforms/74xx/pci.c
> new file mode 100644
> index 0000000..fd822ed
> --- /dev/null
> +++ b/arch/powerpc/platforms/74xx/pci.c
If this is generic to the tsi108 rename to something like
tsi108_pci.c as its not generic to 74xx or embedded6xx.
> @@ -0,0 +1,103 @@
> +/*
> + * TSI 108 PCI hose setup up code
> + *
> + * Maintained by Roy Zang
> + *
> + * This program is free software; you can redistribute it and/or
> modify it
> + * under the terms of the GNU General Public License as
> published by the
> + * Free Software Foundation; either version 2 of the License, or
> (at your
> + * option) any later version.
> + */
> +
> +#include <linux/config.h>
> +#include <linux/stddef.h>
> +#include <linux/kernel.h>
> +#include <linux/init.h>
> +#include <linux/errno.h>
> +#include <linux/pci.h>
> +#include <linux/delay.h>
> +#include <linux/irq.h>
> +#include <linux/module.h>
> +
> +#include <asm/system.h>
> +#include <asm/atomic.h>
> +#include <asm/io.h>
> +#include <asm/pci-bridge.h>
> +#include <asm/prom.h>
> +#include <asm/tsi108.h>
> +#include "mpc7448_hpc2.h"
What's this included needed for?
> +
> +#undef DEBUG
> +
> +#ifdef DEBUG
> +#define DBG(x...) printk(x)
> +#else
> +#define DBG(x...)
> +#endif
> +
> +extern int tsi108_direct_write_config(struct pci_bus *bus,
> unsigned int devfn,
> + int offset, int len, u32 val);
> +extern int tsi108_direct_read_config(struct pci_bus *bus, unsigned
> int devfn,
> + int offset, int len, u32 * val);
> +
Can you not use indirect pci support that already exists?
> +void tsi108_clear_pci_error(u32 pci_cfg_base);
> +
> +extern int
> +tsi108_read_config(int bus, unsigned int devfn, int offset, int
> len, u32 * val);
> +#ifdef CONFIG_PCI
> +static struct pci_ops direct_pci_ops = {
> + tsi108_direct_read_config,
> + tsi108_direct_write_config
> +};
> +
> +void tsi108_clear_pci_cfg_error(void)
> +{
> + tsi108_clear_pci_error(MPC7448_HPC2_PCI_CFG_BASE_PHYS);
> +}
> +
> +int __init add_bridge(struct device_node *dev)
> +{
> + int len;
> + struct pci_controller *hose;
> + struct resource rsrc;
> + int *bus_range;
> + int primary = 0, has_address = 0;
> +
> + DBG("TSI_PCI: %s tsi108_pci_cfg_base=0x%x\n", __FUNCTION__,
> + tsi108_pci_cfg_base);
> +
> + /* Fetch host bridge registers address */
> + has_address = (of_address_to_resource(dev, 0, &rsrc) == 0);
> +
> + /* Get bus range if any */
> + bus_range = (int *)get_property(dev, "bus-range", &len);
> + if (bus_range == NULL || len < 2 * sizeof(int)) {
> + printk(KERN_WARNING "Can't get bus-range for %s, assume"
> + " bus 0\n", dev->full_name);
> + }
> +
> + hose = pcibios_alloc_controller();
> +
> + if (!hose) {
> + printk("PCI Host bridge init failed\n");
> + return -ENOMEM;
> + }
> + hose->arch_data = dev;
> + hose->set_cfg_type = 1;
> +
> + hose->first_busno = bus_range ? bus_range[0] : 0;
> + hose->last_busno = bus_range ? bus_range[1] : 0xff;
> +
> + (hose)->ops = &direct_pci_ops;
> +
> + printk(KERN_INFO "Found tsi108 PCI host bridge at 0x%08lx. "
> + "Firmware bus number: %d->%d\n",
> + rsrc.start, hose->first_busno, hose->last_busno);
> +
> + /* Interpret the "ranges" property */
> + /* This also maps the I/O region and sets isa_io/mem_base */
> + pci_process_bridge_OF_ranges(hose, dev, primary);
> + return 0;
> +
> +}
> +#endif
> diff --git a/arch/powerpc/platforms/Makefile b/arch/powerpc/
> platforms/Makefile
> index c4f6b0d..71ca0e0 100644
> --- a/arch/powerpc/platforms/Makefile
> +++ b/arch/powerpc/platforms/Makefile
> @@ -9,6 +9,7 @@ obj-$(CONFIG_PPC_CHRP) += chrp/
> obj-$(CONFIG_4xx) += 4xx/
> obj-$(CONFIG_PPC_83xx) += 83xx/
> obj-$(CONFIG_PPC_85xx) += 85xx/
> +obj-$(CONFIG_74xx) += 74xx/
> obj-$(CONFIG_PPC_PSERIES) += pseries/
> obj-$(CONFIG_PPC_ISERIES) += iseries/
> obj-$(CONFIG_PPC_MAPLE) += maple/
> --
> 1.3.0
>
>
>
>
> _______________________________________________
> Linuxppc-dev mailing list
> Linuxppc-dev@ozlabs.org
> https://ozlabs.org/mailman/listinfo/linuxppc-dev
^ permalink raw reply
* RE: [PATCH/2.6.17-rc4 10/10] bugs fix for marvell SATA on powerp c pl atform
From: Zang Roy-r61911 @ 2006-05-18 4:03 UTC (permalink / raw)
To: jgarzik
Cc: Alexandre.Bounine, linuxppc-dev list, linux-kernel, linux-ide,
Paul Mackerras, Yang Xin-Xin-r48390
-----Original Message-----
From: Kumar Gala [mailto:galak@kernel.crashing.org]
Sent: 2006=E5=B9=B45=E6=9C=8817=E6=97=A5 21:28
To: Zang Roy-r61911
Cc: Paul Mackerras; linuxppc-dev list; Alexandre.Bounine@tundra.com; =
Yang Xin-Xin-r48390
Subject: Re: [PATCH/2.6.17-rc4 10/10] bugs fix for marvell SATA on =
powerpc pl atform
On May 17, 2006, at 5:14 AM, Zang Roy-r61911 wrote:
> Fix Marvell SATA driver bugs on PowerPC platform:
> SATA device can't work for the problem on little-endian mode.
> U-Boot can't find SATA device after kernel reboots.
>
> Signed-off-by: Hongjun cheng <hong-jun.chen@reescale.com>
> Signed-off-by: Roy Zang <tie-fei.zang@freescale.com>
>
>> From nobody Mon Sep 17 00:00:00 2001
> From: roy zang <tie-fei.zang@freescale.com>
> Date: Tue May 16 15:25:23 2006 +0800
> Subject: [PATCH] Fix bugs on powerpc platform for mv sata driver
This needs to go to Jeff Garzik as SATA driver maintainer.
- kumar
>
> drivers/scsi/sata_mv.c | 10 +++++++++-
> 1 files changed, 9 insertions(+), 1 deletions(-)
>
> d82ac19d259f8487a31105eaf844a93cbd9008e8
> diff --git a/drivers/scsi/sata_mv.c b/drivers/scsi/sata_mv.c
> index d5fdcb9..4166422 100644
> --- a/drivers/scsi/sata_mv.c
> +++ b/drivers/scsi/sata_mv.c
> @@ -1032,6 +1032,9 @@ static inline void mv_crqb_pack_cmd(u16
> {
> *cmdw =3D data | (addr << CRQB_CMD_ADDR_SHIFT) | CRQB_CMD_CS |
> (last ? CRQB_CMD_LAST : 0);
> +#ifdef CONFIG_PPC
> + *cmdw =3D cpu_to_le16(*cmdw);
> +#endif
> }
>
> /**
> @@ -1567,13 +1570,18 @@ static void mv5_read_preamp(struct mv_ho
> static void mv5_enable_leds(struct mv_host_priv *hpriv, void =20
> __iomem *mmio)
> {
> u32 tmp;
> -
> +#ifndef CONFIG_PPC
> writel(0, mmio + MV_GPIO_PORT_CTL);
> +#endif
>
> /* FIXME: handle MV_HP_ERRATA_50XXB2 errata */
>
> tmp =3D readl(mmio + MV_PCI_EXP_ROM_BAR_CTL);
> +#ifdef CONFIG_PPC
> + tmp &=3D ~(1 << 0);
> +#else=09
> tmp |=3D ~(1 << 0);
> +#endif
> writel(tmp, mmio + MV_PCI_EXP_ROM_BAR_CTL);
> }
>
> --=20
> 1.3.0
> _______________________________________________
> Linuxppc-dev mailing list
> Linuxppc-dev@ozlabs.org
> https://ozlabs.org/mailman/listinfo/linuxppc-dev
^ permalink raw reply
* RE: [PATCH/2.6.17-rc4 5/10] Add tsi108 Ethernet support
From: Zang Roy-r61911 @ 2006-05-18 3:58 UTC (permalink / raw)
To: jgarzik
Cc: Alexandre.Bounine, netdev, linux-kernel, linuxppc-dev list,
Paul Mackerras, Yang Xin-Xin-r48390
This patch adds Tundra tsi108 Ethernet driver support.
-----Original Message-----
From: Kumar Gala [mailto:galak@kernel.crashing.org]
Sent: 2006=E5=B9=B45=E6=9C=8817=E6=97=A5 21:24
To: Zang Roy-r61911
Cc: Paul Mackerras; linuxppc-dev list; Alexandre.Bounine@tundra.com; =
Yang Xin-Xin-r48390
Subject: Re: [PATCH/2.6.17-rc4 5/10] Add tsi108 Ethernet support
On May 17, 2006, at 5:14 AM, Zang Roy-r61911 wrote:
> This patch adds a device driver and configuration options for
> Tundra Semiconductor Tsi108 integrated dual port Gigabit Ethernet =20
> controller
>
> Signed-off-by: Alexandre Bounine <alexandreb@tundra.com>
> Signed-off-by: Roy Zang <tie-fei.zang@freescale.com>
This patch needs to go to the netdev list and Jeff Garzik as =20
maintainer for review.
- kumar
> drivers/net/Kconfig | 8
> drivers/net/Makefile | 1
> drivers/net/tsi108_eth.c | 1740 +++++++++++++++++++++++++++++++++++=20
> +++++++++++
> drivers/net/tsi108_eth.h | 404 +++++++++++
> 4 files changed, 2153 insertions(+), 0 deletions(-)
> create mode 100644 drivers/net/tsi108_eth.c
> create mode 100644 drivers/net/tsi108_eth.h
>
> 23ed1c55ab6bc7a4c1c76f646579a1bc195bc7d2
> diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig
> index bdaaad8..8a4ef29 100644
> --- a/drivers/net/Kconfig
> +++ b/drivers/net/Kconfig
> @@ -2177,6 +2177,14 @@ config SPIDER_NET
> This driver supports the Gigabit Ethernet chips present on the
> Cell Processor-Based Blades from IBM.
>
> +config TSI108_ETH
> + tristate "Tundra TSI108 gigabit Ethernet support"
> + depends on TSI108_BRIDGE
> + help
> + This driver supports Tundra TSI108 gigabit Ethernet ports.
> + To compile this driver as a module, choose M here: the module
> + will be called tsi108_eth.
> +
> config GIANFAR
> tristate "Gianfar Ethernet"
> depends on 85xx || 83xx
> diff --git a/drivers/net/Makefile b/drivers/net/Makefile
> index b90468a..5f90b30 100644
> --- a/drivers/net/Makefile
> +++ b/drivers/net/Makefile
> @@ -109,6 +109,7 @@ obj-$(CONFIG_B44) +=3D b44.o
> obj-$(CONFIG_FORCEDETH) +=3D forcedeth.o
> obj-$(CONFIG_NE_H8300) +=3D ne-h8300.o 8390.o
>
> +obj-$(CONFIG_TSI108_ETH) +=3D tsi108_eth.o
> obj-$(CONFIG_MV643XX_ETH) +=3D mv643xx_eth.o
>
> obj-$(CONFIG_PPP) +=3D ppp_generic.o slhc.o
> diff --git a/drivers/net/tsi108_eth.c b/drivers/net/tsi108_eth.c
> new file mode 100644
> index 0000000..cb67dbe
> --- /dev/null
> +++ b/drivers/net/tsi108_eth.c
> @@ -0,0 +1,1740 @@
> +/=20
> =
**********************************************************************=20
> *********
> +
> + Copyright(c) 2005 Tundra Semiconductor Corporation.
> +
> + This program is free software; you can redistribute it and/or =20
> modify it
> + under the terms of the GNU General Public License as published =20
> by the Free
> + Software Foundation; either version 2 of the License, or (at =20
> your option)
> + any later version.
> +
> + This program is distributed in the hope that it will be useful, =20
> but WITHOUT
> + ANY WARRANTY; without even the implied warranty of =20
> MERCHANTABILITY or
> + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public =20
> License for
> + more details.
> +
> + You should have received a copy of the GNU General Public =20
> License along with
> + this program; if not, write to the Free Software Foundation, =20
> Inc., 59
> + Temple Place - Suite 330, Boston, MA 02111-1307, USA.
> +
> =
+*********************************************************************=20
> **********/
> +
> +/* This driver is based on the driver code originally developed=09
> + * for the Intel IOC80314 (ForestLake) Gigabit Ethernet by
> + * scott.wood@timesys.com * Copyright (C) 2003 TimeSys Corporation
> + *
> + * Currently changes from original version are:
> + * - portig to Tsi108-based platform and kernel 2.6
> (kong.lai@tundra.com)
> + * - modifications to handle two ports independently and support for
> + * additional PHY devices (alexandre.bounine@tundra.com)
> + *
> + */
> +
> +#include <linux/config.h>
> +#include <linux/module.h>
> +#include <linux/types.h>
> +#include <linux/init.h>
> +#include <linux/net.h>
> +#include <linux/netdevice.h>
> +#include <linux/etherdevice.h>
> +#include <linux/skbuff.h>
> +#include <linux/slab.h>
> +#include <linux/sched.h>
> +#include <linux/spinlock.h>
> +#include <linux/delay.h>
> +#include <linux/crc32.h>
> +#include <linux/mii.h>
> +#include <linux/device.h>
> +#include <asm/system.h>
> +#include <asm/io.h>
> +#include <linux/pci.h>
> +#include <linux/rtnetlink.h>
> +#include <linux/timer.h>
> +
> +#include <asm/tsi108_irq.h>
> +#include <asm/tsi108.h>
> +#include "tsi108_eth.h"
> +
> +typedef struct net_device net_device;
> +typedef struct sk_buff sk_buff;
> +
> +#define MII_READ_DELAY 10000 /* max link wait time in msec */
> +
> +#define TSI108_RXRING_LEN 256
> +
> +/* NOTE: The driver currently does not support receiving packets
> + * larger than the buffer size, so don't decrease this (unless you
> + * want to add such support).
> + */
> +#define TSI108_RXBUF_SIZE 1536
> +
> +#define TSI108_TXRING_LEN 256
> +
> +#define TSI108_TX_INT_FREQ 64
> +
> +/* Check the phy status every half a second. */
> +#define CHECK_PHY_INTERVAL (HZ/2)
> +
> +extern hw_info hw_info_table[];
> +
> +typedef struct {
> + volatile u32 regs; /* Base of normal regs */
> + volatile u32 phyregs; /* Base of register bank used for PHY =20
> access */
> + int phy; /* Index of PHY for this interface */
> + int irq_num;
> +
> + struct timer_list timer;/* Timer that triggers the check phy =20
> function */
> + int rxtail; /* Next entry in rxring to read */
> + int rxhead; /* Next entry in rxring to give a new buffer */
> + int rxfree; /* Number of free, allocated RX buffers */
> +
> + int rxpending; /* Non-zero if there are still descriptors
> + * to be processed from a previous descriptor
> + * interrupt condition that has been cleared */
> +
> + int txtail; /* Next TX descriptor to check status on */
> + int txhead; /* Next TX descriptor to use */
> +
> + /* Number of free TX descriptors. This could be calculated from
> + * rxhead and rxtail if one descriptor were left unused to =20
> disambiguate
> + * full and empty conditions, but it's simpler to just keep track
> + * explicitly. */
> +
> + int txfree;
> +
> + int phy_ok; /* The PHY is currently powered on. */
> +
> + /* PHY status (duplex is 1 for half, 2 for full,
> + * so that the default 0 indicates that neither has
> + * yet been configured). */
> +
> + int link_up;
> + int speed;
> + int duplex;
> +
> + tx_desc *txring;
> + rx_desc *rxring;
> + sk_buff *txskbs[TSI108_TXRING_LEN];
> + sk_buff *rxskbs[TSI108_RXRING_LEN];
> +
> + dma_addr_t txdma, rxdma;
> +
> + /* txlock nests in misclock and phy_lock */
> +
> + spinlock_t txlock, misclock;
> +
> + /* stats is used to hold the upper bits of each hardware counter,
> + * and tmpstats is used to hold the full values for returning
> + * to the caller of get_stats(). They must be separate in case
> + * an overflow interrupt occurs before the stats are consumed.
> + */
> +
> + struct net_device_stats stats;
> + struct net_device_stats tmpstats;
> +
> + /* These stats are kept separate in hardware, thus require =20
> individual
> + * fields for handling carry. They are combined in get_stats.
> + */
> +
> + unsigned long rx_fcs; /* Add to rx_frame_errors */
> + unsigned long rx_short_fcs; /* Add to rx_frame_errors */
> + unsigned long rx_long_fcs; /* Add to rx_frame_errors */
> + unsigned long rx_underruns; /* Add to rx_length_errors */
> + unsigned long rx_overruns; /* Add to rx_length_errors */
> +
> + unsigned long tx_coll_abort; /* Add to tx_aborted_errors/=20
> collisions */
> + unsigned long tx_pause_drop; /* Add to tx_aborted_errors */
> +
> + unsigned long mc_hash[16];
> +} tsi108_prv_data;
> +static void tsi108_timed_checker(unsigned long dev_ptr);
> +
> +static net_device *tsi108_devs[TSI108_ETH_PORT_NUM];
> +
> +static void dump_eth_one(net_device * dev)
> +{
> + tsi108_prv_data *data =3D netdev_priv(dev);
> +
> + printk("Dumping %s...\n", dev->name);
> + printk("intstat %x intmask %x phy_ok %d"
> + " link %d speed %d duplex %d\n",
> + TSI108_ETH_READ_REG(TSI108_EC_INTSTAT),
> + TSI108_ETH_READ_REG(TSI108_EC_INTMASK), data->phy_ok,
> + data->link_up, data->speed, data->duplex);
> +
> + printk("TX: head %d, tail %d, free %d, stat %x, estat %x, err =
%x\n",
> + data->txhead, data->txtail, data->txfree,
> + TSI108_ETH_READ_REG(TSI108_EC_TXSTAT),
> + TSI108_ETH_READ_REG(TSI108_EC_TXESTAT),
> + TSI108_ETH_READ_REG(TSI108_EC_TXERR));
> +
> + printk("RX: head %d, tail %d, free %d, stat %x,"
> + " estat %x, err %x, pending %d\n\n",
> + data->rxhead, data->rxtail, data->rxfree,
> + TSI108_ETH_READ_REG(TSI108_EC_RXSTAT),
> + TSI108_ETH_READ_REG(TSI108_EC_RXESTAT),
> + TSI108_ETH_READ_REG(TSI108_EC_RXERR), data->rxpending);
> +}
> +
> +void tsi108_dump_eth(void)
> +{
> + dump_eth_one(tsi108_devs[0]);
> + dump_eth_one(tsi108_devs[1]);
> +}
> +
> +/* Synchronization is needed between the thread and up/down events.
> + * Note that the PHY is accessed through the same registers for both
> + * interfaces, so this can't be made interface-specific.
> + */
> +
> +static spinlock_t phy_lock =3D SPIN_LOCK_UNLOCKED;
> +
> +static inline u16 tsi108_read_mii(tsi108_prv_data * data, int reg, =20
> int *status)
> +{
> + int i;
> + u16 ret;
> +
> + TSI108_ETH_WRITE_PHYREG(TSI108_MAC_MII_ADDR,
> + (data->phy << TSI108_MAC_MII_ADDR_PHY) |
> + (reg << TSI108_MAC_MII_ADDR_REG));
> + mb();
> + TSI108_ETH_WRITE_PHYREG(TSI108_MAC_MII_CMD, 0);
> + mb();
> + TSI108_ETH_WRITE_PHYREG(TSI108_MAC_MII_CMD, =20
> TSI108_MAC_MII_CMD_READ);
> + mb();
> + for (i =3D 0; i < 100; i++) {
> + if (!(TSI108_ETH_READ_PHYREG(TSI108_MAC_MII_IND) &
> + (TSI108_MAC_MII_IND_NOTVALID | TSI108_MAC_MII_IND_BUSY)))
> + break;
> + udelay(10);
> + }
> +
> + if (i =3D=3D 100) {
> + if (status)
> + *status =3D -EBUSY;
> +
> + ret =3D 0xffff;
> + } else {
> + if (status)
> + *status =3D 0;
> +
> + ret =3D TSI108_ETH_READ_PHYREG(TSI108_MAC_MII_DATAIN);
> + }
> +
> + return ret;
> +}
> +
> +static inline void tsi108_write_mii(tsi108_prv_data * data, int =20
> reg, u16 val)
> +{
> + TSI108_ETH_WRITE_PHYREG(TSI108_MAC_MII_ADDR,
> + (data->phy << TSI108_MAC_MII_ADDR_PHY) |
> + (reg << TSI108_MAC_MII_ADDR_REG));
> + mb();
> + TSI108_ETH_WRITE_PHYREG(TSI108_MAC_MII_DATAOUT, val);
> + mb();
> + while (TSI108_ETH_READ_PHYREG(TSI108_MAC_MII_IND) &
> + TSI108_MAC_MII_IND_BUSY) ;
> +}
> +
> +static inline void tsi108_write_tbi(tsi108_prv_data * data, int =20
> reg, u16 val)
> +{
> +
> + TSI108_ETH_WRITE_REG(TSI108_MAC_MII_ADDR,
> + (0x1e << TSI108_MAC_MII_ADDR_PHY)
> + | (reg << TSI108_MAC_MII_ADDR_REG));
> + mb();
> +
> + TSI108_ETH_WRITE_REG(TSI108_MAC_MII_DATAOUT, val);
> + mb();
> + while (TSI108_ETH_READ_REG(TSI108_MAC_MII_IND) &
> + TSI108_MAC_MII_IND_BUSY) ;
> +}
> +
> +static void tsi108_check_phy(net_device * dev)
> +{
> + tsi108_prv_data *data =3D netdev_priv(dev);
> + u16 sumstat;
> + u32 mac_cfg2_reg, portctrl_reg;
> + u32 fdx_flag =3D 0, reg_update =3D 0;
> +
> + /* Do a dummy read, as for some reason the first read
> + * after a link becomes up returns link down, even if
> + * it's been a while since the link came up.
> + */
> +
> + spin_lock(&phy_lock);
> +
> + if (!data->phy_ok)
> + goto out;
> +
> + tsi108_read_mii(data, PHY_STAT, NULL);
> +
> + if (!(tsi108_read_mii(data, PHY_STAT, NULL) & PHY_STAT_LINKUP)) {
> + if (data->link_up =3D=3D 1) {
> + netif_stop_queue(dev);
> + data->link_up =3D 0;
> + printk(KERN_NOTICE "%s : link is down\n", dev->name);
> + netif_carrier_off(dev);
> + }
> +
> + goto out;
> + }
> +
> + {
> + mac_cfg2_reg =3D TSI108_ETH_READ_REG(TSI108_MAC_CFG2);
> + portctrl_reg =3D TSI108_ETH_READ_REG(TSI108_EC_PORTCTRL);
> +
> + sumstat =3D tsi108_read_mii(data, PHY_SUM_STAT, NULL);
> +
> + switch (sumstat & PHY_SUM_STAT_SPEED_MASK) {
> + case PHY_SUM_STAT_1000T_FD:
> + fdx_flag++;
> + case PHY_SUM_STAT_1000T_HD:
> + if (data->speed !=3D 1000) {
> + mac_cfg2_reg &=3D ~TSI108_MAC_CFG2_IFACE_MASK;
> + mac_cfg2_reg |=3D TSI108_MAC_CFG2_GIG;
> + portctrl_reg &=3D ~TSI108_EC_PORTCTRL_NOGIG;
> + data->speed =3D 1000;
> + reg_update++;
> + }
> + break;
> + case PHY_SUM_STAT_100TX_FD:
> + fdx_flag++;
> + case PHY_SUM_STAT_100TX_HD:
> + if (data->speed !=3D 100) {
> + mac_cfg2_reg &=3D ~TSI108_MAC_CFG2_IFACE_MASK;
> + mac_cfg2_reg |=3D TSI108_MAC_CFG2_NOGIG;
> + portctrl_reg |=3D TSI108_EC_PORTCTRL_NOGIG;
> + data->speed =3D 100;
> + reg_update++;
> + }
> + break;
> +
> + case PHY_SUM_STAT_10T_FD:
> + fdx_flag++;
> + case PHY_SUM_STAT_10T_HD:
> + if (data->speed !=3D 10) {
> + mac_cfg2_reg &=3D ~TSI108_MAC_CFG2_IFACE_MASK;
> + mac_cfg2_reg |=3D TSI108_MAC_CFG2_NOGIG;
> + portctrl_reg |=3D TSI108_EC_PORTCTRL_NOGIG;
> + data->speed =3D 10;
> + reg_update++;
> + }
> + break;
> +
> + default:
> + if (net_ratelimit())
> + printk(KERN_ERR "PHY reported invalid speed,"
> + KERN_ERR " summary status %x\n",
> + sumstat);
> + goto out;
> + }
> +
> + if (fdx_flag || (sumstat & PHY_SUM_STAT_FULLDUPLEX)) {
> + if (data->duplex !=3D 2) {
> + mac_cfg2_reg |=3D TSI108_MAC_CFG2_FULLDUPLEX;
> + portctrl_reg &=3D ~TSI108_EC_PORTCTRL_HALFDUPLEX;
> + data->duplex =3D 2;
> + reg_update++;
> + }
> + } else {
> + if (data->duplex !=3D 1) {
> + mac_cfg2_reg &=3D ~TSI108_MAC_CFG2_FULLDUPLEX;
> + portctrl_reg |=3D TSI108_EC_PORTCTRL_HALFDUPLEX;
> + data->duplex =3D 1;
> + reg_update++;
> + }
> + }
> +
> + if (reg_update) {
> + TSI108_ETH_WRITE_REG(TSI108_MAC_CFG2, mac_cfg2_reg);
> + mb();
> + TSI108_ETH_WRITE_REG(TSI108_EC_PORTCTRL, portctrl_reg);
> + mb();
> + }
> +
> + }
> +
> + if (data->link_up =3D=3D 0) {
> + /* The manual says it can take 3-4 usecs for the speed change
> + * to take effect.
> + */
> + udelay(5);
> +
> + spin_lock(&data->txlock);
> + if (netif_queue_stopped(dev)
> + && is_valid_ether_addr(dev->dev_addr) && data->txfree)
> + netif_wake_queue(dev);
> +
> + data->link_up =3D 1;
> + spin_unlock(&data->txlock);
> + printk("%s : link is up: %dMb %s-duplex\n",
> + dev->name, data->speed,
> + (data->duplex =3D=3D 2) ? "full" : "half");
> + netif_carrier_on(dev);
> + }
> +
> + out:
> + spin_unlock(&phy_lock);
> +}
> +
> +static inline void
> +tsi108_stat_carry_one(int carry, int carry_bit, int carry_shift,
> + unsigned long *upper)
> +{
> + if (carry & carry_bit)
> + *upper +=3D carry_shift;
> +}
> +
> +static void tsi108_stat_carry(net_device * dev)
> +{
> + tsi108_prv_data *data =3D netdev_priv(dev);
> + u32 carry1, carry2;
> +
> + spin_lock_irq(&data->misclock);
> +
> + carry1 =3D TSI108_ETH_READ_REG(TSI108_STAT_CARRY1);
> + carry2 =3D TSI108_ETH_READ_REG(TSI108_STAT_CARRY2);
> +
> + TSI108_ETH_WRITE_REG(TSI108_STAT_CARRY1, carry1);
> + TSI108_ETH_WRITE_REG(TSI108_STAT_CARRY2, carry2);
> +
> + tsi108_stat_carry_one(carry1, TSI108_STAT_CARRY1_RXBYTES,
> + TSI108_STAT_RXBYTES_CARRY, &data->stats.rx_bytes);
> +
> + tsi108_stat_carry_one(carry1, TSI108_STAT_CARRY1_RXPKTS,
> + TSI108_STAT_RXPKTS_CARRY,
> + &data->stats.rx_packets);
> +
> + tsi108_stat_carry_one(carry1, TSI108_STAT_CARRY1_RXFCS,
> + TSI108_STAT_RXFCS_CARRY, &data->rx_fcs);
> +
> + tsi108_stat_carry_one(carry1, TSI108_STAT_CARRY1_RXMCAST,
> + TSI108_STAT_RXMCAST_CARRY,
> + &data->stats.multicast);
> +
> + tsi108_stat_carry_one(carry1, TSI108_STAT_CARRY1_RXALIGN,
> + TSI108_STAT_RXALIGN_CARRY,
> + &data->stats.rx_frame_errors);
> +
> + tsi108_stat_carry_one(carry1, TSI108_STAT_CARRY1_RXLENGTH,
> + TSI108_STAT_RXLENGTH_CARRY,
> + &data->stats.rx_length_errors);
> +
> + tsi108_stat_carry_one(carry1, TSI108_STAT_CARRY1_RXRUNT,
> + TSI108_STAT_RXRUNT_CARRY, &data->rx_underruns);
> +
> + tsi108_stat_carry_one(carry1, TSI108_STAT_CARRY1_RXJUMBO,
> + TSI108_STAT_RXJUMBO_CARRY, &data->rx_overruns);
> +
> + tsi108_stat_carry_one(carry1, TSI108_STAT_CARRY1_RXFRAG,
> + TSI108_STAT_RXFRAG_CARRY, &data->rx_short_fcs);
> +
> + tsi108_stat_carry_one(carry1, TSI108_STAT_CARRY1_RXJABBER,
> + TSI108_STAT_RXJABBER_CARRY, &data->rx_long_fcs);
> +
> + tsi108_stat_carry_one(carry1, TSI108_STAT_CARRY1_RXDROP,
> + TSI108_STAT_RXDROP_CARRY,
> + &data->stats.rx_missed_errors);
> +
> + tsi108_stat_carry_one(carry2, TSI108_STAT_CARRY2_TXBYTES,
> + TSI108_STAT_TXBYTES_CARRY, &data->stats.tx_bytes);
> +
> + tsi108_stat_carry_one(carry2, TSI108_STAT_CARRY2_TXPKTS,
> + TSI108_STAT_TXPKTS_CARRY,
> + &data->stats.tx_packets);
> +
> + tsi108_stat_carry_one(carry2, TSI108_STAT_CARRY2_TXEXDEF,
> + TSI108_STAT_TXEXDEF_CARRY,
> + &data->stats.tx_aborted_errors);
> +
> + tsi108_stat_carry_one(carry2, TSI108_STAT_CARRY2_TXEXCOL,
> + TSI108_STAT_TXEXCOL_CARRY, &data->tx_coll_abort);
> +
> + tsi108_stat_carry_one(carry2, TSI108_STAT_CARRY2_TXTCOL,
> + TSI108_STAT_TXTCOL_CARRY,
> + &data->stats.collisions);
> +
> + tsi108_stat_carry_one(carry2, TSI108_STAT_CARRY2_TXPAUSE,
> + TSI108_STAT_TXPAUSEDROP_CARRY,
> + &data->tx_pause_drop);
> +
> + spin_unlock_irq(&data->misclock);
> +}
> +
> +/* Read a stat counter atomically with respect to carries.
> + * data->misclock must be held.
> + */
> +static inline unsigned long
> +tsi108_read_stat(tsi108_prv_data * data, int reg, int carry_bit,
> + int carry_shift, unsigned long *upper)
> +{
> + int carryreg;
> + unsigned long val;
> +
> + if (reg < 0xb0)
> + carryreg =3D TSI108_STAT_CARRY1;
> + else
> + carryreg =3D TSI108_STAT_CARRY2;
> +
> + again:
> + val =3D TSI108_ETH_READ_REG(reg) | *upper;
> +
> + rmb();
> +
> + /* Check to see if it overflowed, but the interrupt hasn't
> + * been serviced yet. If so, handle the carry here, and
> + * try again.
> + */
> +
> + if (unlikely(TSI108_ETH_READ_REG(carryreg) & carry_bit)) {
> + *upper +=3D carry_shift;
> + TSI108_ETH_WRITE_REG(carryreg, carry_bit);
> + mb();
> +
> + goto again;
> + }
> +
> + return val;
> +}
> +
> +static struct net_device_stats *tsi108_get_stats(net_device * dev)
> +{
> + unsigned long excol;
> +
> + tsi108_prv_data *data =3D netdev_priv(dev);
> + spin_lock_irq(&data->misclock);
> +
> + data->tmpstats.rx_packets =3D
> + tsi108_read_stat(data, TSI108_STAT_RXPKTS,
> + TSI108_STAT_CARRY1_RXPKTS,
> + TSI108_STAT_RXPKTS_CARRY, &data->stats.rx_packets);
> +
> + data->tmpstats.tx_packets =3D
> + tsi108_read_stat(data, TSI108_STAT_TXPKTS,
> + TSI108_STAT_CARRY2_TXPKTS,
> + TSI108_STAT_TXPKTS_CARRY, &data->stats.tx_packets);
> +
> + data->tmpstats.rx_bytes =3D
> + tsi108_read_stat(data, TSI108_STAT_RXBYTES,
> + TSI108_STAT_CARRY1_RXBYTES,
> + TSI108_STAT_RXBYTES_CARRY, &data->stats.rx_bytes);
> +
> + data->tmpstats.tx_bytes =3D
> + tsi108_read_stat(data, TSI108_STAT_TXBYTES,
> + TSI108_STAT_CARRY2_TXBYTES,
> + TSI108_STAT_TXBYTES_CARRY, &data->stats.tx_bytes);
> +
> + data->tmpstats.multicast =3D
> + tsi108_read_stat(data, TSI108_STAT_RXMCAST,
> + TSI108_STAT_CARRY1_RXMCAST,
> + TSI108_STAT_RXMCAST_CARRY, &data->stats.multicast);
> +
> + excol =3D tsi108_read_stat(data, TSI108_STAT_TXEXCOL,
> + TSI108_STAT_CARRY2_TXEXCOL,
> + TSI108_STAT_TXEXCOL_CARRY,
> + &data->tx_coll_abort);
> +
> + data->tmpstats.collisions =3D
> + tsi108_read_stat(data, TSI108_STAT_TXTCOL,
> + TSI108_STAT_CARRY2_TXTCOL,
> + TSI108_STAT_TXTCOL_CARRY, &data->stats.collisions);
> +
> + data->tmpstats.collisions +=3D excol;
> +
> + data->tmpstats.rx_length_errors =3D
> + tsi108_read_stat(data, TSI108_STAT_RXLENGTH,
> + TSI108_STAT_CARRY1_RXLENGTH,
> + TSI108_STAT_RXLENGTH_CARRY,
> + &data->stats.rx_length_errors);
> +
> + data->tmpstats.rx_length_errors +=3D
> + tsi108_read_stat(data, TSI108_STAT_RXRUNT,
> + TSI108_STAT_CARRY1_RXRUNT,
> + TSI108_STAT_RXRUNT_CARRY, &data->rx_underruns);
> +
> + data->tmpstats.rx_length_errors +=3D
> + tsi108_read_stat(data, TSI108_STAT_RXJUMBO,
> + TSI108_STAT_CARRY1_RXJUMBO,
> + TSI108_STAT_RXJUMBO_CARRY, &data->rx_overruns);
> +
> + data->tmpstats.rx_frame_errors =3D
> + tsi108_read_stat(data, TSI108_STAT_RXALIGN,
> + TSI108_STAT_CARRY1_RXALIGN,
> + TSI108_STAT_RXALIGN_CARRY,
> + &data->stats.rx_frame_errors);
> +
> + data->tmpstats.rx_frame_errors +=3D
> + tsi108_read_stat(data, TSI108_STAT_RXFCS,
> + TSI108_STAT_CARRY1_RXFCS, TSI108_STAT_RXFCS_CARRY,
> + &data->rx_fcs);
> +
> + data->tmpstats.rx_frame_errors +=3D
> + tsi108_read_stat(data, TSI108_STAT_RXFRAG,
> + TSI108_STAT_CARRY1_RXFRAG,
> + TSI108_STAT_RXFRAG_CARRY, &data->rx_short_fcs);
> +
> + data->tmpstats.rx_missed_errors =3D
> + tsi108_read_stat(data, TSI108_STAT_RXDROP,
> + TSI108_STAT_CARRY1_RXDROP,
> + TSI108_STAT_RXDROP_CARRY,
> + &data->stats.rx_missed_errors);
> +
> + /* These three are maintained by software. */
> + data->tmpstats.rx_fifo_errors =3D data->stats.rx_fifo_errors;
> + data->tmpstats.rx_crc_errors =3D data->stats.rx_crc_errors;
> +
> + data->tmpstats.tx_aborted_errors =3D
> + tsi108_read_stat(data, TSI108_STAT_TXEXDEF,
> + TSI108_STAT_CARRY2_TXEXDEF,
> + TSI108_STAT_TXEXDEF_CARRY,
> + &data->stats.tx_aborted_errors);
> +
> + data->tmpstats.tx_aborted_errors +=3D
> + tsi108_read_stat(data, TSI108_STAT_TXPAUSEDROP,
> + TSI108_STAT_CARRY2_TXPAUSE,
> + TSI108_STAT_TXPAUSEDROP_CARRY,
> + &data->tx_pause_drop);
> +
> + data->tmpstats.tx_aborted_errors +=3D excol;
> +
> + data->tmpstats.tx_errors =3D data->tmpstats.tx_aborted_errors;
> + data->tmpstats.rx_errors =3D data->tmpstats.rx_length_errors +
> + data->tmpstats.rx_crc_errors +
> + data->tmpstats.rx_frame_errors +
> + data->tmpstats.rx_fifo_errors + =
data->tmpstats.rx_missed_errors;
> +
> + spin_unlock_irq(&data->misclock);
> + return &data->tmpstats;
> +}
> +
> +static void tsi108_restart_rx(tsi108_prv_data * data, net_device * =20
> dev)
> +{
> + TSI108_ETH_WRITE_REG(TSI108_EC_RXQ_PTRHIGH,
> + TSI108_EC_RXQ_PTRHIGH_VALID);
> +
> + wmb();
> + TSI108_ETH_WRITE_REG(TSI108_EC_RXCTRL, TSI108_EC_RXCTRL_GO
> + | TSI108_EC_RXCTRL_QUEUE0);
> +}
> +
> +static void tsi108_restart_tx(tsi108_prv_data * data)
> +{
> + TSI108_ETH_WRITE_REG(TSI108_EC_TXQ_PTRHIGH,
> + TSI108_EC_TXQ_PTRHIGH_VALID);
> +
> + wmb();
> + TSI108_ETH_WRITE_REG(TSI108_EC_TXCTRL, TSI108_EC_TXCTRL_IDLEINT |
> + TSI108_EC_TXCTRL_GO | TSI108_EC_TXCTRL_QUEUE0);
> +}
> +
> +/* txlock must be held by caller, with IRQs disabled, and
> + * with permission to re-enable them when the lock is dropped.
> + */
> +static void tsi108_check_for_completed_tx(net_device * dev)
> +{
> + tsi108_prv_data *data =3D netdev_priv(dev);
> + int tx;
> + struct sk_buff *skb;
> + int release =3D 0;
> +
> + while (!data->txfree || data->txhead !=3D data->txtail) {
> + tx =3D data->txtail;
> +
> + if (data->txring[tx].misc & TSI108_TX_OWN)
> + break;
> +
> + skb =3D data->txskbs[tx];
> +
> + if (!(data->txring[tx].misc & TSI108_TX_OK))
> + printk("%s: bad tx packet, misc %x\n",
> + dev->name, data->txring[tx].misc);
> +
> + data->txtail =3D (data->txtail + 1) % TSI108_TXRING_LEN;
> + data->txfree++;
> +
> + if (data->txring[tx].misc & TSI108_TX_EOF) {
> + dev_kfree_skb_any(skb);
> + release++;
> + }
> + }
> +
> + if (release) {
> +
> + if (netif_queue_stopped(dev)
> + && is_valid_ether_addr(dev->dev_addr) && data->link_up)
> + netif_wake_queue(dev);
> + }
> +}
> +
> +static int tsi108_send_packet(sk_buff * skb, net_device * dev)
> +{
> + tsi108_prv_data *data =3D netdev_priv(dev);
> + int frags =3D skb_shinfo(skb)->nr_frags + 1;
> + int i;
> +#ifdef FIXME_SG_CSUM_NOT_TESTED /* FIXME: Not supported now. */
> + long csstart;
> + long csum;
> +
> + csstart =3D skb->len - skb->data_len;
> + if (csstart > skb->len - skb->data_len)
> + BUG();
> + csum =3D 0;
> + if (csstart !=3D skb->len)
> + csum =3D skb_checksum(skb, csstart, skb->len - csstart, 0);
> +#endif
> +
> + if (!data->phy_ok && net_ratelimit())
> + printk(KERN_ERR "%s: Transmit while PHY is down!\n", dev->name);
> +
> + if (!data->link_up) {
> + printk(KERN_ERR "%s: Transmit while link is down!\n",
> + dev->name);
> + netif_stop_queue(dev);
> + return 1;
> + }
> +
> + if (data->txfree < MAX_SKB_FRAGS + 1) {
> + netif_stop_queue(dev);
> +
> + if (net_ratelimit())
> + printk(KERN_ERR "%s: Transmit with full tx ring!\n",
> + dev->name);
> + return 1;
> + }
> +
> + if (data->txfree - frags < MAX_SKB_FRAGS + 1) {
> + netif_stop_queue(dev);
> + }
> +
> + spin_lock_irq(&data->txlock);
> +
> + for (i =3D 0; i < frags; i++) {
> + int misc =3D 0;
> + int tx =3D data->txhead;
> +
> + /* This is done to mark every TSI108_TX_INT_FREQ tx buffers with
> + * the interrupt bit. TX descriptor-complete interrupts are
> + * enabled when the queue fills up, and masked when there is
> + * still free space. This way, when saturating the outbound
> + * link, the tx interrupts are kept to a reasonable level.
> + * When the queue is not full, reclamation of skbs still occurs
> + * as new packets are transmitted, or on a queue-empty
> + * interrupt.
> + */
> +
> + if ((tx % TSI108_TX_INT_FREQ =3D=3D 0) &&
> + ((TSI108_TXRING_LEN - data->txfree) >=3D TSI108_TX_INT_FREQ)
> + )
> + misc =3D TSI108_TX_INT;
> +
> + data->txskbs[tx] =3D skb;
> +
> + if (i =3D=3D 0) {
> + data->txring[tx].buf0 =3D virt_to_phys(skb->data);
> + data->txring[tx].len =3D skb->len - skb->data_len;
> + misc |=3D TSI108_TX_SOF;
> + } else {
> + skb_frag_t *frag =3D &skb_shinfo(skb)->frags[i - 1];
> +
> + data->txring[tx].buf0 =3D
> + page_to_phys(frag->page) + frag->page_offset;
> + data->txring[tx].len =3D frag->size;
> + }
> +
> + if (i =3D=3D frags - 1)
> + misc |=3D TSI108_TX_EOF;
> +
> +#ifdef TSI108_PRINT_TX_FRAME
> + {
> + int i;
> + printk("%s: Tx Frame contents (%d)\n", dev->name,
> + skb->len);
> + for (i =3D 0; i < skb->len; i++)
> + printk(" %2.2x", skb->data[i]);
> + printk(".\n");
> + }
> +#endif /* TSI108_PRINT_TX_FRAME */
> +
> + mb();
> + data->txring[tx].misc =3D misc | TSI108_TX_OWN;
> +
> + data->txhead =3D (data->txhead + 1) % TSI108_TXRING_LEN;
> + data->txfree--;
> + }
> +
> + tsi108_check_for_completed_tx(dev);
> +
> + /* This must be done after the check for completed tx descriptors,
> + * so that the tail pointer is correct.
> + */
> +
> + if (!(TSI108_ETH_READ_REG(TSI108_EC_TXSTAT) & =20
> TSI108_EC_TXSTAT_QUEUE0))
> + tsi108_restart_tx(data);
> +
> + spin_unlock_irq(&data->txlock);
> + return 0;
> +}
> +
> +static int tsi108_check_for_completed_rx(net_device * dev, int =20
> budget)
> +{
> + tsi108_prv_data *data =3D netdev_priv(dev);
> + int done =3D 0;
> +
> + while (data->rxfree && done !=3D budget) {
> + int rx =3D data->rxtail;
> + struct sk_buff *skb;
> +
> + if (data->rxring[rx].misc & TSI108_RX_OWN)
> + break;
> +
> + skb =3D data->rxskbs[rx];
> + data->rxtail =3D (data->rxtail + 1) % TSI108_RXRING_LEN;
> + data->rxfree--;
> + done++;
> +
> + if (data->rxring[rx].misc & TSI108_RX_BAD) {
> + spin_lock_irq(&data->misclock);
> +
> + if (data->rxring[rx].misc & TSI108_RX_CRC)
> + data->stats.rx_crc_errors++;
> + if (data->rxring[rx].misc & TSI108_RX_OVER)
> + data->stats.rx_fifo_errors++;
> +
> + spin_unlock_irq(&data->misclock);
> +
> + dev_kfree_skb_any(skb);
> + continue;
> + }
> +#ifdef TSI108_PRINT_RX_FRAME
> + {
> + int i;
> + printk("%s: Rx Frame contents (%d)\n",
> + dev->name, data->rxring[rx].len);
> + for (i =3D 0; i < data->rxring[rx].len; i++)
> + printk(" %2.2x", skb->data[i]);
> + printk(".\n");
> + }
> +#endif /* TSI108_PRINT_RX_FRAME */
> +
> + skb->dev =3D dev;
> + skb_put(skb, data->rxring[rx].len);
> + skb->protocol =3D eth_type_trans(skb, dev);
> + netif_receive_skb(skb);
> + dev->last_rx =3D jiffies;
> + }
> +
> + return done;
> +}
> +
> +static int tsi108_refill_rx(net_device * dev, int budget)
> +{
> + tsi108_prv_data *data =3D netdev_priv(dev);
> + int done =3D 0;
> +
> + while (data->rxfree !=3D TSI108_RXRING_LEN && done !=3D budget) {
> + int rx =3D data->rxhead;
> + sk_buff *skb;
> +
> + data->rxskbs[rx] =3D skb =3D dev_alloc_skb(TSI108_RXBUF_SIZE + 2);
> + if (!skb)
> + break;
> +
> + skb_reserve(skb, 2); /* Align the data on a 4-byte boundary. */
> +
> + data->rxring[rx].buf0 =3D virt_to_phys(skb->data);
> +
> + /* Sometimes the hardware sets blen to zero after packet
> + * reception, even though the manual says that it's only ever
> + * modified by the driver.
> + */
> +
> + data->rxring[rx].blen =3D 1536;
> + mb();
> + data->rxring[rx].misc =3D TSI108_RX_OWN | TSI108_RX_INT;
> +
> + data->rxhead =3D (data->rxhead + 1) % TSI108_RXRING_LEN;
> + data->rxfree++;
> + done++;
> + }
> +
> + mb();
> +
> + if (done !=3D 0 && !(TSI108_ETH_READ_REG(TSI108_EC_RXSTAT) &
> + TSI108_EC_RXSTAT_QUEUE0))
> + tsi108_restart_rx(data, dev);
> +
> + return done;
> +}
> +
> +static int tsi108_poll(net_device * dev, int *budget)
> +{
> + tsi108_prv_data *data =3D netdev_priv(dev);
> + u32 estat =3D TSI108_ETH_READ_REG(TSI108_EC_RXESTAT);
> + u32 intstat =3D TSI108_ETH_READ_REG(TSI108_EC_INTSTAT);
> + int total_budget =3D min(*budget, dev->quota);
> + int num_received =3D 0, num_filled =3D 0, budget_used;
> +
> + intstat &=3D TSI108_INT_RXQUEUE0 | TSI108_INT_RXTHRESH |
> + TSI108_INT_RXOVERRUN | TSI108_INT_RXERROR | TSI108_INT_RXWAIT;
> +
> + TSI108_ETH_WRITE_REG(TSI108_EC_RXESTAT, estat);
> + TSI108_ETH_WRITE_REG(TSI108_EC_INTSTAT, intstat);
> +
> + if (data->rxpending || (estat & TSI108_EC_RXESTAT_Q0_DESCINT))
> + num_received =3D tsi108_check_for_completed_rx(dev, total_budget);
> +
> + /* This should normally fill no more slots than the number of
> + * packets received in tsi108_check_for_completed_rx(). The =20
> exception
> + * is when we previously ran out of memory for RX SKBs. In that
> + * case, it's helpful to obey the budget, not only so that the
> + * CPU isn't hogged, but so that memory (which may still be low)
> + * is not hogged by one device.
> + *
> + * A work unit is considered to be two SKBs to allow us to catch
> + * up when the ring has shrunk due to out-of-memory but we're
> + * still removing the full budget's worth of packets each time.
> + */
> +
> + if (data->rxfree < TSI108_RXRING_LEN)
> + num_filled =3D tsi108_refill_rx(dev, total_budget * 2);
> +
> + if (intstat & TSI108_INT_RXERROR) {
> + u32 err =3D TSI108_ETH_READ_REG(TSI108_EC_RXERR);
> + TSI108_ETH_WRITE_REG(TSI108_EC_RXERR, err);
> +
> + if (err) {
> + if (net_ratelimit())
> + printk(KERN_DEBUG "%s: RX error %x\n",
> + dev->name, err);
> +
> + if (!(TSI108_ETH_READ_REG(TSI108_EC_RXSTAT) &
> + TSI108_EC_RXSTAT_QUEUE0))
> + tsi108_restart_rx(data, dev);
> + }
> + }
> +
> + if (intstat & TSI108_INT_RXOVERRUN) {
> + spin_lock_irq(&data->misclock);
> + data->stats.rx_fifo_errors++;
> + spin_unlock_irq(&data->misclock);
> + }
> +
> + budget_used =3D max(num_received, num_filled / 2);
> +
> + *budget -=3D budget_used;
> + dev->quota -=3D budget_used;
> +
> + if (budget_used !=3D total_budget) {
> + data->rxpending =3D 0;
> + netif_rx_complete(dev);
> +
> + TSI108_ETH_WRITE_REG(TSI108_EC_INTMASK,
> + TSI108_ETH_READ_REG(TSI108_EC_INTMASK)
> + & ~(TSI108_INT_RXQUEUE0
> + | TSI108_INT_RXTHRESH |
> + TSI108_INT_RXOVERRUN |
> + TSI108_INT_RXERROR |
> + TSI108_INT_RXWAIT));
> +
> + mb();
> +
> + /* IRQs are level-triggered, so no need to re-check */
> + return 0;
> + } else {
> + data->rxpending =3D 1;
> + }
> +
> + return 1;
> +}
> +
> +static void tsi108_rx_int(net_device * dev)
> +{
> + tsi108_prv_data *data =3D netdev_priv(dev);
> +
> + /* A race could cause dev to already be scheduled, so it's not an
> + * error if that happens (and interrupts shouldn't be re-masked,
> + * because that can cause harmful races, if poll has already
> + * unmasked them but not cleared LINK_STATE_SCHED).
> + *
> + * This can happen if this code races with tsi108_poll(), which =20
> masks
> + * the interrupts after tsi108_irq_one() read the mask, but before
> + * netif_rx_schedule is called. It could also happen due to calls
> + * from tsi108_check_rxring().
> + */
> +
> + if (netif_rx_schedule_prep(dev)) {
> + /* Mask, rather than ack, the receive interrupts. The ack
> + * will happen in tsi108_poll().
> + */
> +
> + TSI108_ETH_WRITE_REG(TSI108_EC_INTMASK,
> + TSI108_ETH_READ_REG(TSI108_EC_INTMASK) |
> + TSI108_INT_RXQUEUE0
> + | TSI108_INT_RXTHRESH |
> + TSI108_INT_RXOVERRUN | TSI108_INT_RXERROR |
> + TSI108_INT_RXWAIT);
> + mb();
> + __netif_rx_schedule(dev);
> + } else {
> + if (!netif_running(dev)) {
> + /* This can happen if an interrupt occurs while the
> + * interface is being brought down, as the START
> + * bit is cleared before the stop function is called.
> + *
> + * In this case, the interrupts must be masked, or
> + * they will continue indefinitely.
> + *
> + * There's a race here if the interface is brought down
> + * and then up in rapid succession, as the device could
> + * be made running after the above check and before
> + * the masking below. This will only happen if the IRQ
> + * thread has a lower priority than the task brining
> + * up the interface. Fixing this race would likely
> + * require changes in generic code.
> + */
> +
> + TSI108_ETH_WRITE_REG(TSI108_EC_INTMASK,
> + TSI108_ETH_READ_REG
> + (TSI108_EC_INTMASK) |
> + TSI108_INT_RXQUEUE0 |
> + TSI108_INT_RXTHRESH |
> + TSI108_INT_RXOVERRUN |
> + TSI108_INT_RXERROR |
> + TSI108_INT_RXWAIT);
> + mb();
> + }
> + }
> +}
> +
> +/* If the RX ring has run out of memory, try periodically
> + * to allocate some more, as otherwise poll would never
> + * get called (apart from the initial end-of-queue condition).
> + *
> + * This is called once per second (by default) from the thread.
> + */
> +
> +static void tsi108_check_rxring(net_device * dev)
> +{
> + tsi108_prv_data *data =3D netdev_priv(dev);
> +
> + /* A poll is scheduled, as opposed to caling tsi108_refill_rx
> + * directly, so as to keep the receive path single-threaded
> + * (and thus not needing a lock).
> + */
> +
> + if (netif_running(dev) && data->rxfree < TSI108_RXRING_LEN / 4)
> + tsi108_rx_int(dev);
> +}
> +
> +static void tsi108_tx_int(net_device * dev)
> +{
> + tsi108_prv_data *data =3D netdev_priv(dev);
> + u32 estat =3D TSI108_ETH_READ_REG(TSI108_EC_TXESTAT);
> +
> + mb();
> + TSI108_ETH_WRITE_REG(TSI108_EC_TXESTAT, estat);
> + mb();
> + TSI108_ETH_WRITE_REG(TSI108_EC_INTSTAT, TSI108_INT_TXQUEUE0 |
> + TSI108_INT_TXIDLE | TSI108_INT_TXERROR);
> + mb();
> + if (estat & TSI108_EC_TXESTAT_Q0_ERR) {
> + u32 err =3D TSI108_ETH_READ_REG(TSI108_EC_TXERR);
> + TSI108_ETH_WRITE_REG(TSI108_EC_TXERR, err);
> +
> + if (err && net_ratelimit())
> + printk(KERN_ERR "%s: TX error %x\n", dev->name, err);
> + }
> +
> + if (estat & (TSI108_EC_TXESTAT_Q0_DESCINT | =20
> TSI108_EC_TXESTAT_Q0_EOQ)) {
> + spin_lock(&data->txlock);
> + tsi108_check_for_completed_tx(dev);
> + spin_unlock(&data->txlock);
> + }
> +}
> +
> +static irqreturn_t tsi108_irq_one(net_device * dev)
> +{
> + tsi108_prv_data *data =3D netdev_priv(dev);
> + u32 stat =3D TSI108_ETH_READ_REG(TSI108_EC_INTSTAT);
> +
> + if (!(stat & TSI108_INT_ANY))
> + return IRQ_NONE; /* Not our interrupt */
> +
> + stat &=3D ~TSI108_ETH_READ_REG(TSI108_EC_INTMASK);
> +
> + if (stat & (TSI108_INT_TXQUEUE0 | TSI108_INT_TXIDLE |
> + TSI108_INT_TXERROR))
> + tsi108_tx_int(dev);
> + if (stat & (TSI108_INT_RXQUEUE0 | TSI108_INT_RXTHRESH |
> + TSI108_INT_RXWAIT | TSI108_INT_RXOVERRUN |
> + TSI108_INT_RXERROR))
> + tsi108_rx_int(dev);
> +
> + if (stat & TSI108_INT_SFN) {
> + if (net_ratelimit())
> + printk(KERN_DEBUG "%s: SFN error\n", dev->name);
> + TSI108_ETH_WRITE_REG(TSI108_EC_INTSTAT, TSI108_INT_SFN);
> + }
> +
> + if (stat & TSI108_INT_STATCARRY) {
> + tsi108_stat_carry(dev);
> + TSI108_ETH_WRITE_REG(TSI108_EC_INTSTAT, TSI108_INT_STATCARRY);
> + }
> +
> + return IRQ_HANDLED;
> +}
> +
> +static irqreturn_t tsi108_irq(int irq, void *dev_id, struct =20
> pt_regs *regs)
> +{
> + if ((IRQ_TSI108_GIGE0 !=3D irq) && (IRQ_TSI108_GIGE1 !=3D irq))
> + return IRQ_NONE; /* Not our interrupt */
> +
> + return tsi108_irq_one((struct net_device *)dev_id);
> +}
> +
> +static void tsi108_stop_ethernet(net_device * dev)
> +{
> + tsi108_prv_data *data =3D netdev_priv(dev);
> +
> + /* Disable all TX and RX queues ... */
> + TSI108_ETH_WRITE_REG(TSI108_EC_TXCTRL, 0);
> + TSI108_ETH_WRITE_REG(TSI108_EC_RXCTRL, 0);
> +
> + /* ...and wait for them to become idle */
> + mb();
> + while (TSI108_ETH_READ_REG(TSI108_EC_TXSTAT) &
> + TSI108_EC_TXSTAT_ACTIVE) ;
> + while (TSI108_ETH_READ_REG(TSI108_EC_RXSTAT) &
> + TSI108_EC_RXSTAT_ACTIVE) ;
> +}
> +
> +static void tsi108_reset_ether(tsi108_prv_data * data)
> +{
> + TSI108_ETH_WRITE_REG(TSI108_MAC_CFG1, TSI108_MAC_CFG1_SOFTRST);
> + udelay(100);
> + TSI108_ETH_WRITE_REG(TSI108_MAC_CFG1, 0);
> +
> + TSI108_ETH_WRITE_REG(TSI108_EC_PORTCTRL, =20
> TSI108_EC_PORTCTRL_STATRST);
> + udelay(100);
> + TSI108_ETH_WRITE_REG(TSI108_EC_PORTCTRL,
> + TSI108_ETH_READ_REG(TSI108_EC_PORTCTRL) &
> + ~TSI108_EC_PORTCTRL_STATRST);
> +
> + TSI108_ETH_WRITE_REG(TSI108_EC_TXCFG, TSI108_EC_TXCFG_RST);
> + udelay(100);
> + TSI108_ETH_WRITE_REG(TSI108_EC_TXCFG,
> + TSI108_ETH_READ_REG(TSI108_EC_TXCFG) &
> + ~TSI108_EC_TXCFG_RST);
> +
> + TSI108_ETH_WRITE_REG(TSI108_EC_RXCFG, TSI108_EC_RXCFG_RST);
> + udelay(100);
> + TSI108_ETH_WRITE_REG(TSI108_EC_RXCFG,
> + TSI108_ETH_READ_REG(TSI108_EC_RXCFG) &
> + ~TSI108_EC_RXCFG_RST);
> +
> + TSI108_ETH_WRITE_REG(TSI108_MAC_MII_MGMT_CFG,
> + TSI108_ETH_READ_REG(TSI108_MAC_MII_MGMT_CFG) |
> + TSI108_MAC_MII_MGMT_RST);
> + udelay(100);
> + TSI108_ETH_WRITE_REG(TSI108_MAC_MII_MGMT_CFG,
> + TSI108_ETH_READ_REG(TSI108_MAC_MII_MGMT_CFG) &
> + ~(TSI108_MAC_MII_MGMT_RST |
> + TSI108_MAC_MII_MGMT_CLK));
> +
> + TSI108_ETH_WRITE_REG(TSI108_MAC_MII_MGMT_CFG,
> + TSI108_ETH_READ_REG(TSI108_MAC_MII_MGMT_CFG) |
> + TSI108_MAC_MII_MGMT_CLK);
> +}
> +
> +static int tsi108_get_mac(net_device * dev)
> +{
> + tsi108_prv_data *data =3D netdev_priv(dev);
> +
> + u32 word1 =3D TSI108_ETH_READ_REG(TSI108_MAC_ADDR1);
> + u32 word2 =3D TSI108_ETH_READ_REG(TSI108_MAC_ADDR2);
> +
> + /* Note that the octets are reversed from what the manual says,
> + * producing an even weirder ordering...
> + */
> + if (word2 =3D=3D 0 && word1 =3D=3D 0) {
> + dev->dev_addr[0] =3D 0x00;
> + dev->dev_addr[1] =3D 0x06;
> + dev->dev_addr[2] =3D 0xd2;
> + dev->dev_addr[3] =3D 0x00;
> + dev->dev_addr[4] =3D 0x00;
> + if (0x8 =3D=3D data->phy)
> + dev->dev_addr[5] =3D 0x01;
> + else
> + dev->dev_addr[5] =3D 0x02;
> +
> + word2 =3D (dev->dev_addr[0] << 16) | (dev->dev_addr[1] << 24);
> +
> + word1 =3D (dev->dev_addr[2] << 0) | (dev->dev_addr[3] << 8) |
> + (dev->dev_addr[4] << 16) | (dev->dev_addr[5] << 24);
> +
> + TSI108_ETH_WRITE_REG(TSI108_MAC_ADDR1, word1);
> + TSI108_ETH_WRITE_REG(TSI108_MAC_ADDR2, word2);
> + } else {
> + dev->dev_addr[0] =3D (word2 >> 16) & 0xff;
> + dev->dev_addr[1] =3D (word2 >> 24) & 0xff;
> + dev->dev_addr[2] =3D (word1 >> 0) & 0xff;
> + dev->dev_addr[3] =3D (word1 >> 8) & 0xff;
> + dev->dev_addr[4] =3D (word1 >> 16) & 0xff;
> + dev->dev_addr[5] =3D (word1 >> 24) & 0xff;
> + }
> +
> + if (!is_valid_ether_addr(dev->dev_addr)) {
> + printk("KERN_ERR: word1: %08x, word2: %08x\n", word1, word2);
> + return -EINVAL;
> + }
> +
> + return 0;
> +}
> +
> +static int tsi108_set_mac(net_device * dev, void *addr)
> +{
> + tsi108_prv_data *data =3D netdev_priv(dev);
> + u32 word1, word2;
> + int i;
> +
> + if (!is_valid_ether_addr(addr))
> + return -EINVAL;
> +
> + for (i =3D 0; i < 6; i++)
> + /* +2 is for the offset of the HW addr type */
> + dev->dev_addr[i] =3D ((unsigned char *)addr)[i + 2];
> +
> + word2 =3D (dev->dev_addr[0] << 16) | (dev->dev_addr[1] << 24);
> +
> + word1 =3D (dev->dev_addr[2] << 0) | (dev->dev_addr[3] << 8) |
> + (dev->dev_addr[4] << 16) | (dev->dev_addr[5] << 24);
> +
> + spin_lock_irq(&data->misclock);
> + TSI108_ETH_WRITE_REG(TSI108_MAC_ADDR1, word1);
> + TSI108_ETH_WRITE_REG(TSI108_MAC_ADDR2, word2);
> + spin_lock(&data->txlock);
> +
> + if (netif_queue_stopped(dev) && data->txfree && data->link_up)
> + netif_wake_queue(dev);
> +
> + spin_unlock(&data->txlock);
> + spin_unlock_irq(&data->misclock);
> + return 0;
> +}
> +
> +/* Protected by dev->xmit_lock. */
> +static void tsi108_set_rx_mode(net_device * dev)
> +{
> + tsi108_prv_data *data =3D netdev_priv(dev);
> + u32 rxcfg =3D TSI108_ETH_READ_REG(TSI108_EC_RXCFG);
> +
> + if (dev->flags & IFF_PROMISC) {
> + rxcfg &=3D ~(TSI108_EC_RXCFG_UC_HASH | TSI108_EC_RXCFG_MC_HASH);
> + rxcfg |=3D TSI108_EC_RXCFG_UFE | TSI108_EC_RXCFG_MFE;
> + goto out;
> + }
> +
> + rxcfg &=3D ~(TSI108_EC_RXCFG_UFE | TSI108_EC_RXCFG_MFE);
> +
> + if (dev->mc_count) {
> + int i;
> + struct dev_mc_list *mc =3D dev->mc_list;
> + rxcfg |=3D TSI108_EC_RXCFG_MFE | TSI108_EC_RXCFG_MC_HASH;
> +
> + memset(data->mc_hash, 0, sizeof(data->mc_hash));
> +
> + while (mc) {
> + u32 hash, crc;
> +
> + if (mc->dmi_addrlen =3D=3D 6) {
> + crc =3D ether_crc(6, mc->dmi_addr);
> + hash =3D crc >> 23;
> +
> + __set_bit(hash, &data->mc_hash[0]);
> + } else {
> + printk(KERN_ERR
> + "%s: got multicast address of length %d "
> + "instead of 6.\n", dev->name,
> + mc->dmi_addrlen);
> + }
> +
> + mc =3D mc->next;
> + }
> +
> + TSI108_ETH_WRITE_REG(TSI108_EC_HASHADDR,
> + TSI108_EC_HASHADDR_AUTOINC |
> + TSI108_EC_HASHADDR_MCAST);
> +
> + for (i =3D 0; i < 16; i++) {
> + /* The manual says that the hardware may drop
> + * back-to-back writes to the data register.
> + */
> + udelay(1);
> + mb();
> + TSI108_ETH_WRITE_REG(TSI108_EC_HASHDATA,
> + data->mc_hash[i]);
> + mb();
> + }
> + }
> +
> + out:
> + mb();
> + TSI108_ETH_WRITE_REG(TSI108_EC_RXCFG, rxcfg);
> +}
> +
> +static void tsi108_init_phy(net_device * dev)
> +{
> + tsi108_prv_data *data =3D netdev_priv(dev);
> + u32 i =3D 0;
> + u16 phyVal =3D 0;
> +
> + spin_lock_irq(&phy_lock);
> +
> + tsi108_write_mii(data, PHY_CTRL, PHY_CTRL_RESET);
> + mb();
> + while (tsi108_read_mii(data, PHY_CTRL, NULL) & PHY_CTRL_RESET) ;
> +
> +#if (TSI108_PHY_TYPE =3D=3D PHY_BCM54XX) /* Broadcom BCM54xx PHY */
> + tsi108_write_mii(data, 0x09, 0x0300);
> + tsi108_write_mii(data, 0x10, 0x1020);
> + tsi108_write_mii(data, 0x1c, 0x8c00);
> + mb();
> +#endif
> +
> + tsi108_write_mii(data,
> + PHY_CTRL,
> + PHY_CTRL_AUTONEG_EN | PHY_CTRL_AUTONEG_START);
> + mb();
> + while (tsi108_read_mii(data, PHY_CTRL, NULL) & =20
> PHY_CTRL_AUTONEG_START) ;
> +
> + /* Set G/MII mode and receive clock select in TBI control #2. The
> + * second port won't work if this isn't done, even though we don't
> + * use TBI mode.
> + */
> +
> + tsi108_write_tbi(data, 0x11, 0x30);
> +
> + /* FIXME: It seems to take more than 2 back-to-back reads to the
> + * PHY_STAT register before the link up status bit is set.
> + */
> +
> + data->link_up =3D 1;
> +
> + while (!((phyVal =3D tsi108_read_mii(data, PHY_STAT, NULL)) &
> + PHY_STAT_LINKUP)) {
> + if (i++ > (MII_READ_DELAY / 10)) {
> + data->link_up =3D 0;
> + break;
> + }
> + mdelay(10);
> + }
> +
> + printk(KERN_DEBUG "PHY_STAT reg contains %08x\n", phyVal);
> + data->phy_ok =3D 1;
> + spin_unlock_irq(&phy_lock);
> +}
> +
> +static void tsi108_kill_phy(struct net_device *dev)
> +{
> + tsi108_prv_data *data =3D netdev_priv(dev);
> +
> + spin_lock_irq(&phy_lock);
> + tsi108_write_mii(data, PHY_CTRL, PHY_CTRL_POWERDOWN);
> + data->phy_ok =3D 0;
> + spin_unlock_irq(&phy_lock);
> +}
> +
> +static int tsi108_open(struct net_device *dev)
> +{
> + int i;
> + tsi108_prv_data *data =3D netdev_priv(dev);
> + unsigned int rxring_size =3D TSI108_RXRING_LEN * sizeof(rx_desc);
> + unsigned int txring_size =3D TSI108_TXRING_LEN * sizeof(tx_desc);
> +
> + printk(KERN_DEBUG "Inside tsi108_open()!\n");
> +
> + i =3D request_irq(data->irq_num, tsi108_irq, 0, dev->name, dev);
> + if (i !=3D 0) {
> + printk(KERN_ERR "tsi108_eth%d: Could not allocate IRQ%d.\n",
> + data->irq_num % IRQ_TSI108_GIGE0, data->irq_num);
> + return i;
> + } else {
> + dev->irq =3D data->irq_num;
> + printk(KERN_NOTICE
> + "tsi108_open : Port %d Assigned IRQ %d to %s\n",
> + data->irq_num % IRQ_TSI108_GIGE0, dev->irq, dev->name);
> + }
> +
> + data->rxring =3D pci_alloc_consistent(NULL, rxring_size, &data-=20
> >rxdma);
> +
> + if (!data->rxring) {
> + printk(KERN_DEBUG
> + "TSI108_ETH: failed to allocate memory for rxring!\n");
> + return -ENOMEM;
> + } else {
> + memset(data->rxring, 0, rxring_size);
> + }
> +
> + data->txring =3D pci_alloc_consistent(NULL, txring_size, &data-=20
> >txdma);
> +
> + if (!data->txring) {
> + printk(KERN_DEBUG
> + "TSI108_ETH: failed to allocate memory for txring!\n");
> + pci_free_consistent(0, rxring_size, data->rxring, data->rxdma);
> + return -ENOMEM;
> + } else {
> + memset(data->txring, 0, txring_size);
> + }
> +
> + for (i =3D 0; i < TSI108_RXRING_LEN; i++) {
> + data->rxring[i].next0 =3D data->rxdma + (i + 1) * sizeof(rx_desc);
> + data->rxring[i].blen =3D TSI108_RXBUF_SIZE;
> + data->rxring[i].vlan =3D 0;
> + }
> +
> + data->rxring[TSI108_RXRING_LEN - 1].next0 =3D data->rxdma;
> +
> + data->rxtail =3D 0;
> + data->rxhead =3D 0;
> +
> + for (i =3D 0; i < TSI108_RXRING_LEN; i++) {
> + sk_buff *skb =3D dev_alloc_skb(TSI108_RXBUF_SIZE + NET_IP_ALIGN);
> +
> + if (!skb) {
> + /* Bah. No memory for now, but maybe we'll get
> + * some more later.
> + * For now, we'll live with the smaller ring.
> + */
> + printk(KERN_WARNING
> + "%s: Could only allocate %d receive skb(s).\n",
> + dev->name, i);
> + data->rxhead =3D i;
> + break;
> + }
> +
> + data->rxskbs[i] =3D skb;
> + /* Align the payload on a 4-byte boundary */
> + skb_reserve(skb, 2);
> + data->rxskbs[i] =3D skb;
> + data->rxring[i].buf0 =3D virt_to_phys(data->rxskbs[i]->data);
> + data->rxring[i].misc =3D TSI108_RX_OWN | TSI108_RX_INT;
> + }
> +
> + data->rxfree =3D i;
> + TSI108_ETH_WRITE_REG(TSI108_EC_RXQ_PTRLOW, data->rxdma);
> +
> + for (i =3D 0; i < TSI108_TXRING_LEN; i++) {
> + data->txring[i].next0 =3D data->txdma + (i + 1) * sizeof(tx_desc);
> + data->txring[i].misc =3D 0;
> + }
> +
> + data->txring[TSI108_TXRING_LEN - 1].next0 =3D data->txdma;
> + data->txtail =3D 0;
> + data->txhead =3D 0;
> + data->txfree =3D TSI108_TXRING_LEN;
> + TSI108_ETH_WRITE_REG(TSI108_EC_TXQ_PTRLOW, data->txdma);
> + tsi108_init_phy(dev);
> +
> + init_timer(&data->timer);
> + data->timer.expires =3D jiffies + 1;
> + data->timer.data =3D (unsigned long)dev;
> + data->timer.function =3D &tsi108_timed_checker; /* timer handler */
> + add_timer(&data->timer);
> +
> + tsi108_restart_rx(data, dev);
> +
> + TSI108_ETH_WRITE_REG(TSI108_EC_INTSTAT, ~0);
> + mb();
> +
> + TSI108_ETH_WRITE_REG(TSI108_EC_INTMASK,
> + ~(TSI108_INT_TXQUEUE0 | TSI108_INT_RXERROR |
> + TSI108_INT_RXTHRESH | TSI108_INT_RXQUEUE0 |
> + TSI108_INT_RXOVERRUN | TSI108_INT_RXWAIT |
> + TSI108_INT_SFN | TSI108_INT_STATCARRY));
> +
> + TSI108_ETH_WRITE_REG(TSI108_MAC_CFG1,
> + TSI108_MAC_CFG1_RXEN | TSI108_MAC_CFG1_TXEN);
> + netif_start_queue(dev);
> + return 0;
> +}
> +
> +static int tsi108_close(net_device * dev)
> +{
> + tsi108_prv_data *data =3D netdev_priv(dev);
> +
> + netif_stop_queue(dev);
> +
> + del_timer_sync(&data->timer);
> +
> + printk(KERN_DEBUG "Inside tsi108_ifdown!\n");
> +
> + tsi108_stop_ethernet(dev);
> + tsi108_kill_phy(dev);
> + TSI108_ETH_WRITE_REG(TSI108_EC_INTMASK, ~0);
> + TSI108_ETH_WRITE_REG(TSI108_MAC_CFG1, 0);
> +
> + /* Check for any pending TX packets, and drop them. */
> +
> + while (!data->txfree || data->txhead !=3D data->txtail) {
> + int tx =3D data->txtail;
> + struct sk_buff *skb;
> + skb =3D data->txskbs[tx];
> + data->txtail =3D (data->txtail + 1) % TSI108_TXRING_LEN;
> + data->txfree++;
> + dev_kfree_skb(skb);
> + }
> +
> + synchronize_irq(data->irq_num);
> + free_irq(data->irq_num, dev);
> +
> + /* Discard the RX ring. */
> +
> + while (data->rxfree) {
> + int rx =3D data->rxtail;
> + struct sk_buff *skb;
> +
> + skb =3D data->rxskbs[rx];
> + data->rxtail =3D (data->rxtail + 1) % TSI108_RXRING_LEN;
> + data->rxfree--;
> + dev_kfree_skb(skb);
> + }
> +
> + pci_free_consistent(0,
> + TSI108_RXRING_LEN * sizeof(rx_desc),
> + data->rxring, data->rxdma);
> + pci_free_consistent(0,
> + TSI108_TXRING_LEN * sizeof(tx_desc),
> + data->txring, data->txdma);
> +
> + return 0;
> +}
> +
> +static void tsi108_init_mac(net_device * dev)
> +{
> + tsi108_prv_data *data =3D netdev_priv(dev);
> +
> + TSI108_ETH_WRITE_REG(TSI108_MAC_CFG2, =20
> TSI108_MAC_CFG2_DFLT_PREAMBLE |
> + TSI108_MAC_CFG2_PADCRC);
> +
> + TSI108_ETH_WRITE_REG(TSI108_EC_TXTHRESH,
> + (192 << TSI108_EC_TXTHRESH_STARTFILL) |
> + (192 << TSI108_EC_TXTHRESH_STOPFILL));
> +
> + TSI108_ETH_WRITE_REG(TSI108_STAT_CARRYMASK1,
> + ~(TSI108_STAT_CARRY1_RXBYTES |
> + TSI108_STAT_CARRY1_RXPKTS |
> + TSI108_STAT_CARRY1_RXFCS |
> + TSI108_STAT_CARRY1_RXMCAST |
> + TSI108_STAT_CARRY1_RXALIGN |
> + TSI108_STAT_CARRY1_RXLENGTH |
> + TSI108_STAT_CARRY1_RXRUNT |
> + TSI108_STAT_CARRY1_RXJUMBO |
> + TSI108_STAT_CARRY1_RXFRAG |
> + TSI108_STAT_CARRY1_RXJABBER |
> + TSI108_STAT_CARRY1_RXDROP));
> +
> + TSI108_ETH_WRITE_REG(TSI108_STAT_CARRYMASK2,
> + ~(TSI108_STAT_CARRY2_TXBYTES |
> + TSI108_STAT_CARRY2_TXPKTS |
> + TSI108_STAT_CARRY2_TXEXDEF |
> + TSI108_STAT_CARRY2_TXEXCOL |
> + TSI108_STAT_CARRY2_TXTCOL |
> + TSI108_STAT_CARRY2_TXPAUSE));
> +
> + TSI108_ETH_WRITE_REG(TSI108_EC_PORTCTRL, =
TSI108_EC_PORTCTRL_STATEN);
> + TSI108_ETH_WRITE_REG(TSI108_MAC_CFG1, 0);
> +
> + TSI108_ETH_WRITE_REG(TSI108_EC_RXCFG,
> + TSI108_EC_RXCFG_SE | TSI108_EC_RXCFG_BFE);
> +
> + TSI108_ETH_WRITE_REG(TSI108_EC_TXQ_CFG, TSI108_EC_TXQ_CFG_DESC_INT =
|
> + TSI108_EC_TXQ_CFG_EOQ_OWN_INT |
> + TSI108_EC_TXQ_CFG_WSWP | (TSI108_PBM_PORT <<
> + TSI108_EC_TXQ_CFG_SFNPORT));
> +
> + TSI108_ETH_WRITE_REG(TSI108_EC_RXQ_CFG, TSI108_EC_RXQ_CFG_DESC_INT =
|
> + TSI108_EC_RXQ_CFG_EOQ_OWN_INT |
> + TSI108_EC_RXQ_CFG_WSWP | (TSI108_PBM_PORT <<
> + TSI108_EC_RXQ_CFG_SFNPORT));
> +
> + TSI108_ETH_WRITE_REG(TSI108_EC_TXQ_BUFCFG,
> + TSI108_EC_TXQ_BUFCFG_BURST256 |
> + TSI108_EC_TXQ_BUFCFG_BSWP | (TSI108_PBM_PORT <<
> + TSI108_EC_TXQ_BUFCFG_SFNPORT));
> +
> + TSI108_ETH_WRITE_REG(TSI108_EC_RXQ_BUFCFG,
> + TSI108_EC_RXQ_BUFCFG_BURST256 |
> + TSI108_EC_RXQ_BUFCFG_BSWP | (TSI108_PBM_PORT <<
> + TSI108_EC_RXQ_BUFCFG_SFNPORT));
> +
> + TSI108_ETH_WRITE_REG(TSI108_EC_INTMASK, ~0);
> +}
> +
> +static int tsi108_ioctl(net_device * dev, struct ifreq *rq, int cmd)
> +{
> + tsi108_prv_data *data =3D netdev_priv(dev);
> + struct mii_ioctl_data *mii_data =3D
> + (struct mii_ioctl_data *)&rq->ifr_data;
> + int ret;
> +
> + switch (cmd) {
> + case SIOCGMIIPHY:
> + mii_data->phy_id =3D data->phy;
> + ret =3D 0;
> + break;
> +
> + case SIOCGMIIREG:
> + spin_lock_irq(&phy_lock);
> + mii_data->val_out =3D
> + tsi108_read_mii(data, mii_data->reg_num, &ret);
> + spin_unlock_irq(&phy_lock);
> + break;
> +
> + default:
> + ret =3D -EOPNOTSUPP;
> + }
> +
> + return ret;
> +}
> +
> +static int
> +tsi108_init_one(unsigned long regs, unsigned long phyregs, u16 =20
> phy, u16 irq_num)
> +{
> + net_device *dev =3D alloc_etherdev(sizeof(tsi108_prv_data));
> + tsi108_prv_data *data;
> + int ret;
> +
> + if (!dev) {
> + printk("tsi108_eth: Could not allocate a device structure\n");
> + return -ENOMEM;
> + }
> +
> + data =3D netdev_priv(dev);
> + memset(data, 0, sizeof(tsi108_prv_data));
> +
> + data->regs =3D (volatile u32)regs;
> + data->phyregs =3D (volatile u32)phyregs;
> + data->phy =3D phy;
> + data->irq_num =3D irq_num;
> +
> + dev->open =3D tsi108_open;
> + dev->stop =3D tsi108_close;
> + dev->hard_start_xmit =3D tsi108_send_packet;
> + dev->set_mac_address =3D tsi108_set_mac;
> + dev->set_multicast_list =3D tsi108_set_rx_mode;
> + dev->get_stats =3D tsi108_get_stats;
> + dev->poll =3D tsi108_poll;
> + dev->do_ioctl =3D tsi108_ioctl;
> + dev->weight =3D 64; /* 64 is more suitable for GigE interface - =20
> klai */
> +
> + /* Apparently, the Linux networking code won't use scatter-gather
> + * if the hardware doesn't do checksums. However, it's faster
> + * to checksum in place and use SG, as (among other reasons)
> + * the cache won't be dirtied (which then has to be flushed
> + * before DMA). The checksumming is done by the driver (via
> + * a new function skb_csum_dev() in net/core/skbuff.c).
> + */
> +
> +#ifdef FIXME_SG_CSUM_NOT_TESTED /* FIXME: Not supported now. */
> + dev->features =3D NETIF_F_HIGHDMA | NETIF_F_SG | NETIF_F_HW_CSUM;
> +#else
> + dev->features =3D NETIF_F_HIGHDMA;
> +#endif
> + SET_MODULE_OWNER(dev);
> +
> + spin_lock_init(&data->txlock);
> + spin_lock_init(&data->misclock);
> +
> + tsi108_reset_ether(data);
> + tsi108_kill_phy(dev);
> +
> + if (tsi108_get_mac(dev) !=3D 0)
> + printk(KERN_ERR "%s: Invalid MAC address. Please correct.\n",
> + dev->name);
> +
> + tsi108_init_mac(dev);
> +
> + tsi108_devs[irq_num % IRQ_TSI108_GIGE0] =3D dev;
> +
> + ret =3D register_netdev(dev);
> + if (ret < 0) {
> + kfree(dev);
> + return ret;
> + }
> +
> + printk(KERN_INFO "%s: Tsi108 Gigabit Ethernet, MAC: "
> + "%02x:%02x:%02x:%02x:%02x:%02x\n", dev->name,
> + dev->dev_addr[0], dev->dev_addr[1], dev->dev_addr[2],
> + dev->dev_addr[3], dev->dev_addr[4], dev->dev_addr[5]);
> +
> + return 0;
> +}
> +
> +/* There's no way to either get interrupts from the PHY when
> + * something changes, or to have the Tsi108 automatically =
communicate
> + * with the PHY to reconfigure itself.
> + *
> + * Thus, we have to do it using a timer.
> + */
> +
> +static void tsi108_timed_checker(unsigned long dev_ptr)
> +{
> + struct net_device *dev =3D (struct net_device *)dev_ptr;
> + tsi108_prv_data *data =3D netdev_priv(dev);
> +
> + tsi108_check_phy(dev);
> + tsi108_check_rxring(dev);
> + data->timer.expires =3D jiffies + CHECK_PHY_INTERVAL;
> + add_timer(&data->timer);
> +}
> +
> +static int tsi108_ether_init(void)
> +{
> + int ret;
> + int dev_count =3D 0;
> + int i;
> +
> + hw_info_table[0].regs =3D (u32) ioremap(hw_info_table[0].regs, =
0x400);
> + hw_info_table[0].phyregs =3D hw_info_table[0].regs;
> +
> + hw_info_table[1].regs =3D (u32) ioremap(hw_info_table[1].regs, =
0x400);
> + hw_info_table[1].phyregs =3D hw_info_table[0].regs;
> +
> + for (i =3D 0; hw_info_table[i].regs !=3D TBL_END; i++) {
> + ret =3D tsi108_init_one(hw_info_table[i].regs,
> + hw_info_table[i].phyregs,
> + hw_info_table[i].phy,
> + hw_info_table[i].irq_num);
> + if (ret < 0)
> + printk("tsi108_ether_init: error initializing ethernet "
> + "device%d\n", i);
> + else
> + dev_count++;
> + }
> +
> + printk("tsi108_ether_init: found %d device(s)\n", dev_count);
> +
> + return 0;
> +}
> +
> +static void tsi108_ether_exit(void)
> +{
> + int i;
> + net_device *dev;
> +
> + for (i =3D 0; hw_info_table[i].regs !=3D TBL_END; i++) {
> + if ((dev =3D tsi108_devs[i]) !=3D NULL) {
> + unregister_netdev(dev);
> + tsi108_stop_ethernet(dev);
> + kfree(dev);
> + tsi108_devs[i] =3D NULL;
> + }
> + }
> +}
> +
> +module_init(tsi108_ether_init);
> +module_exit(tsi108_ether_exit);
> +
> +MODULE_AUTHOR("Tundra Semiconductor Corporation");
> +MODULE_DESCRIPTION("Tsi108 Gigabit Ethernet driver");
> +MODULE_LICENSE("GPL");
> diff --git a/drivers/net/tsi108_eth.h b/drivers/net/tsi108_eth.h
> new file mode 100644
> index 0000000..cb54f92
> --- /dev/null
> +++ b/drivers/net/tsi108_eth.h
> @@ -0,0 +1,404 @@
> +/*
> + * (C) Copyright 2005 Tundra Semiconductor Corp.
> + * Kong Lai, <kong.lai@tundra.com).
> + *
> + * See file CREDITS for list of people who contributed to this
> + * project.
> + *
> + * This program is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU General Public License as
> + * published by the Free Software Foundation; either version 2 of
> + * the License, or (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, write to the Free Software
> + * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
> + * MA 02111-1307 USA
> + */
> +
> +/*
> + * net/tsi108_eth.h - definitions for Tsi108 GIGE network =
controller.
> + */
> +
> +#ifndef __TSI108_ETH_H
> +#define __TSI108_ETH_H
> +
> +#include <linux/config.h>
> +#include <linux/types.h>
> +
> +#define TSI108_ETH_WRITE_REG(offset, val) \
> + out_be32((volatile u32 *)(data->regs + offset), val)
> +
> +#define TSI108_ETH_READ_REG(offset) \
> + in_be32((volatile u32 *)(data->regs + offset))
> +
> +#define TSI108_ETH_WRITE_PHYREG(offset, val) \
> + out_be32((volatile u32 *)(data->phyregs + offset), val)
> +
> +#define TSI108_ETH_READ_PHYREG(offset) \
> + in_be32((volatile u32 *)(data->phyregs + offset))
> +
> +/*
> + * PHY Configuration Options
> + *
> + * NOTE: Enable set of definitions corresponding to your board type
> + */
> +#define PHY_MV88E 1 /* Marvel 88Exxxx PHY */
> +#define PHY_BCM54XX 2 /* Broardcom BCM54xx PHY */
> +#define TSI108_PHY_TYPE PHY_MV88E
> +
> +/*
> + * TSI108 GIGE port registers
> + */
> +
> +#define TSI108_ETH_PORT_NUM 2
> +#define TSI108_PBM_PORT 2
> +#define TSI108_SDRAM_PORT 4
> +
> +#define TSI108_MAC_CFG1 (0x000)
> +#define TSI108_MAC_CFG1_SOFTRST (1 << 31)
> +#define TSI108_MAC_CFG1_LOOPBACK (1 << 8)
> +#define TSI108_MAC_CFG1_RXEN (1 << 2)
> +#define TSI108_MAC_CFG1_TXEN (1 << 0)
> +
> +#define TSI108_MAC_CFG2 (0x004)
> +#define TSI108_MAC_CFG2_DFLT_PREAMBLE (7 << 12)
> +#define TSI108_MAC_CFG2_IFACE_MASK (3 << 8)
> +#define TSI108_MAC_CFG2_NOGIG (1 << 8)
> +#define TSI108_MAC_CFG2_GIG (2 << 8)
> +#define TSI108_MAC_CFG2_PADCRC (1 << 2)
> +#define TSI108_MAC_CFG2_FULLDUPLEX (1 << 0)
> +
> +#define TSI108_MAC_MII_MGMT_CFG (0x020)
> +#define TSI108_MAC_MII_MGMT_CLK (7 << 0)
> +#define TSI108_MAC_MII_MGMT_RST (1 << 31)
> +
> +#define TSI108_MAC_MII_CMD (0x024)
> +#define TSI108_MAC_MII_CMD_READ (1 << 0)
> +
> +#define TSI108_MAC_MII_ADDR (0x028)
> +#define TSI108_MAC_MII_ADDR_REG 0
> +#define TSI108_MAC_MII_ADDR_PHY 8
> +
> +#define TSI108_MAC_MII_DATAOUT (0x02c)
> +#define TSI108_MAC_MII_DATAIN (0x030)
> +
> +#define TSI108_MAC_MII_IND (0x034)
> +#define TSI108_MAC_MII_IND_NOTVALID (1 << 2)
> +#define TSI108_MAC_MII_IND_SCANNING (1 << 1)
> +#define TSI108_MAC_MII_IND_BUSY (1 << 0)
> +
> +#define TSI108_MAC_IFCTRL (0x038)
> +#define TSI108_MAC_IFCTRL_PHYMODE (1 << 24)
> +
> +#define TSI108_MAC_ADDR1 (0x040)
> +#define TSI108_MAC_ADDR2 (0x044)
> +
> +#define TSI108_STAT_RXBYTES (0x06c)
> +#define TSI108_STAT_RXBYTES_CARRY (1 << 24)
> +
> +#define TSI108_STAT_RXPKTS (0x070)
> +#define TSI108_STAT_RXPKTS_CARRY (1 << 18)
> +
> +#define TSI108_STAT_RXFCS (0x074)
> +#define TSI108_STAT_RXFCS_CARRY (1 << 12)
> +
> +#define TSI108_STAT_RXMCAST (0x078)
> +#define TSI108_STAT_RXMCAST_CARRY (1 << 18)
> +
> +#define TSI108_STAT_RXALIGN (0x08c)
> +#define TSI108_STAT_RXALIGN_CARRY (1 << 12)
> +
> +#define TSI108_STAT_RXLENGTH (0x090)
> +#define TSI108_STAT_RXLENGTH_CARRY (1 << 12)
> +
> +#define TSI108_STAT_RXRUNT (0x09c)
> +#define TSI108_STAT_RXRUNT_CARRY (1 << 12)
> +
> +#define TSI108_STAT_RXJUMBO (0x0a0)
> +#define TSI108_STAT_RXJUMBO_CARRY (1 << 12)
> +
> +#define TSI108_STAT_RXFRAG (0x0a4)
> +#define TSI108_STAT_RXFRAG_CARRY (1 << 12)
> +
> +#define TSI108_STAT_RXJABBER (0x0a8)
> +#define TSI108_STAT_RXJABBER_CARRY (1 << 12)
> +
> +#define TSI108_STAT_RXDROP (0x0ac)
> +#define TSI108_STAT_RXDROP_CARRY (1 << 12)
> +
> +#define TSI108_STAT_TXBYTES (0x0b0)
> +#define TSI108_STAT_TXBYTES_CARRY (1 << 24)
> +
> +#define TSI108_STAT_TXPKTS (0x0b4)
> +#define TSI108_STAT_TXPKTS_CARRY (1 << 18)
> +
> +#define TSI108_STAT_TXEXDEF (0x0c8)
> +#define TSI108_STAT_TXEXDEF_CARRY (1 << 12)
> +
> +#define TSI108_STAT_TXEXCOL (0x0d8)
> +#define TSI108_STAT_TXEXCOL_CARRY (1 << 12)
> +
> +#define TSI108_STAT_TXTCOL (0x0dc)
> +#define TSI108_STAT_TXTCOL_CARRY (1 << 13)
> +
> +#define TSI108_STAT_TXPAUSEDROP (0x0e4)
> +#define TSI108_STAT_TXPAUSEDROP_CARRY (1 << 12)
> +
> +#define TSI108_STAT_CARRY1 (0x100)
> +#define TSI108_STAT_CARRY1_RXBYTES (1 << 16)
> +#define TSI108_STAT_CARRY1_RXPKTS (1 << 15)
> +#define TSI108_STAT_CARRY1_RXFCS (1 << 14)
> +#define TSI108_STAT_CARRY1_RXMCAST (1 << 13)
> +#define TSI108_STAT_CARRY1_RXALIGN (1 << 8)
> +#define TSI108_STAT_CARRY1_RXLENGTH (1 << 7)
> +#define TSI108_STAT_CARRY1_RXRUNT (1 << 4)
> +#define TSI108_STAT_CARRY1_RXJUMBO (1 << 3)
> +#define TSI108_STAT_CARRY1_RXFRAG (1 << 2)
> +#define TSI108_STAT_CARRY1_RXJABBER (1 << 1)
> +#define TSI108_STAT_CARRY1_RXDROP (1 << 0)
> +
> +#define TSI108_STAT_CARRY2 (0x104)
> +#define TSI108_STAT_CARRY2_TXBYTES (1 << 13)
> +#define TSI108_STAT_CARRY2_TXPKTS (1 << 12)
> +#define TSI108_STAT_CARRY2_TXEXDEF (1 << 7)
> +#define TSI108_STAT_CARRY2_TXEXCOL (1 << 3)
> +#define TSI108_STAT_CARRY2_TXTCOL (1 << 2)
> +#define TSI108_STAT_CARRY2_TXPAUSE (1 << 0)
> +
> +#define TSI108_STAT_CARRYMASK1 (0x108)
> +#define TSI108_STAT_CARRYMASK2 (0x10c)
> +
> +#define TSI108_EC_PORTCTRL (0x200)
> +#define TSI108_EC_PORTCTRL_STATRST (1 << 31)
> +#define TSI108_EC_PORTCTRL_STATEN (1 << 28)
> +#define TSI108_EC_PORTCTRL_NOGIG (1 << 18)
> +#define TSI108_EC_PORTCTRL_HALFDUPLEX (1 << 16)
> +
> +#define TSI108_EC_INTSTAT (0x204)
> +#define TSI108_EC_INTMASK (0x208)
> +
> +#define TSI108_INT_ANY (1 << 31)
> +#define TSI108_INT_SFN (1 << 30)
> +#define TSI108_INT_RXIDLE (1 << 29)
> +#define TSI108_INT_RXABORT (1 << 28)
> +#define TSI108_INT_RXERROR (1 << 27)
> +#define TSI108_INT_RXOVERRUN (1 << 26)
> +#define TSI108_INT_RXTHRESH (1 << 25)
> +#define TSI108_INT_RXWAIT (1 << 24)
> +#define TSI108_INT_RXQUEUE0 (1 << 16)
> +#define TSI108_INT_STATCARRY (1 << 15)
> +#define TSI108_INT_TXIDLE (1 << 13)
> +#define TSI108_INT_TXABORT (1 << 12)
> +#define TSI108_INT_TXERROR (1 << 11)
> +#define TSI108_INT_TXUNDERRUN (1 << 10)
> +#define TSI108_INT_TXTHRESH (1 << 9)
> +#define TSI108_INT_TXWAIT (1 << 8)
> +#define TSI108_INT_TXQUEUE0 (1 << 0)
> +
> +#define TSI108_EC_TXCFG (0x220)
> +#define TSI108_EC_TXCFG_RST (1 << 31)
> +
> +#define TSI108_EC_TXCTRL (0x224)
> +#define TSI108_EC_TXCTRL_IDLEINT (1 << 31)
> +#define TSI108_EC_TXCTRL_ABORT (1 << 30)
> +#define TSI108_EC_TXCTRL_GO (1 << 15)
> +#define TSI108_EC_TXCTRL_QUEUE0 (1 << 0)
> +
> +#define TSI108_EC_TXSTAT (0x228)
> +#define TSI108_EC_TXSTAT_ACTIVE (1 << 15)
> +#define TSI108_EC_TXSTAT_QUEUE0 (1 << 0)
> +
> +#define TSI108_EC_TXESTAT (0x22c)
> +#define TSI108_EC_TXESTAT_Q0_ERR (1 << 24)
> +#define TSI108_EC_TXESTAT_Q0_DESCINT (1 << 16)
> +#define TSI108_EC_TXESTAT_Q0_EOF (1 << 8)
> +#define TSI108_EC_TXESTAT_Q0_EOQ (1 << 0)
> +
> +#define TSI108_EC_TXERR (0x278)
> +
> +#define TSI108_EC_TXQ_CFG (0x280)
> +#define TSI108_EC_TXQ_CFG_DESC_INT (1 << 20)
> +#define TSI108_EC_TXQ_CFG_EOQ_OWN_INT (1 << 19)
> +#define TSI108_EC_TXQ_CFG_WSWP (1 << 11)
> +#define TSI108_EC_TXQ_CFG_BSWP (1 << 10)
> +#define TSI108_EC_TXQ_CFG_SFNPORT 0
> +
> +#define TSI108_EC_TXQ_BUFCFG (0x284)
> +#define TSI108_EC_TXQ_BUFCFG_BURST8 (0 << 8)
> +#define TSI108_EC_TXQ_BUFCFG_BURST32 (1 << 8)
> +#define TSI108_EC_TXQ_BUFCFG_BURST128 (2 << 8)
> +#define TSI108_EC_TXQ_BUFCFG_BURST256 (3 << 8)
> +#define TSI108_EC_TXQ_BUFCFG_WSWP (1 << 11)
> +#define TSI108_EC_TXQ_BUFCFG_BSWP (1 << 10)
> +#define TSI108_EC_TXQ_BUFCFG_SFNPORT 0
> +
> +#define TSI108_EC_TXQ_PTRLOW (0x288)
> +
> +#define TSI108_EC_TXQ_PTRHIGH (0x28c)
> +#define TSI108_EC_TXQ_PTRHIGH_VALID (1 << 31)
> +
> +#define TSI108_EC_TXTHRESH (0x230)
> +#define TSI108_EC_TXTHRESH_STARTFILL 0
> +#define TSI108_EC_TXTHRESH_STOPFILL 16
> +
> +#define TSI108_EC_RXCFG (0x320)
> +#define TSI108_EC_RXCFG_RST (1 << 31)
> +
> +#define TSI108_EC_RXSTAT (0x328)
> +#define TSI108_EC_RXSTAT_ACTIVE (1 << 15)
> +#define TSI108_EC_RXSTAT_QUEUE0 (1 << 0)
> +
> +#define TSI108_EC_RXESTAT (0x32c)
> +#define TSI108_EC_RXESTAT_Q0_ERR (1 << 24)
> +#define TSI108_EC_RXESTAT_Q0_DESCINT (1 << 16)
> +#define TSI108_EC_RXESTAT_Q0_EOF (1 << 8)
> +#define TSI108_EC_RXESTAT_Q0_EOQ (1 << 0)
> +
> +#define TSI108_EC_HASHADDR (0x360)
> +#define TSI108_EC_HASHADDR_AUTOINC (1 << 31)
> +#define TSI108_EC_HASHADDR_DO1STREAD (1 << 30)
> +#define TSI108_EC_HASHADDR_UNICAST (0 << 4)
> +#define TSI108_EC_HASHADDR_MCAST (1 << 4)
> +
> +#define TSI108_EC_HASHDATA (0x364)
> +
> +#define TSI108_EC_RXQ_PTRLOW (0x388)
> +
> +#define TSI108_EC_RXQ_PTRHIGH (0x38c)
> +#define TSI108_EC_RXQ_PTRHIGH_VALID (1 << 31)
> +
> +/* Station Enable -- accept packets destined for us */
> +#define TSI108_EC_RXCFG_SE (1 << 13)
> +/* Unicast Frame Enable -- for packets not destined for us */
> +#define TSI108_EC_RXCFG_UFE (1 << 12)
> +/* Multicast Frame Enable */
> +#define TSI108_EC_RXCFG_MFE (1 << 11)
> +/* Broadcast Frame Enable */
> +#define TSI108_EC_RXCFG_BFE (1 << 10)
> +#define TSI108_EC_RXCFG_UC_HASH (1 << 9)
> +#define TSI108_EC_RXCFG_MC_HASH (1 << 8)
> +
> +#define TSI108_EC_RXQ_CFG (0x380)
> +#define TSI108_EC_RXQ_CFG_DESC_INT (1 << 20)
> +#define TSI108_EC_RXQ_CFG_EOQ_OWN_INT (1 << 19)
> +#define TSI108_EC_RXQ_CFG_WSWP (1 << 11)
> +#define TSI108_EC_RXQ_CFG_BSWP (1 << 10)
> +#define TSI108_EC_RXQ_CFG_SFNPORT 0
> +
> +#define TSI108_EC_RXQ_BUFCFG (0x384)
> +#define TSI108_EC_RXQ_BUFCFG_BURST8 (0 << 8)
> +#define TSI108_EC_RXQ_BUFCFG_BURST32 (1 << 8)
> +#define TSI108_EC_RXQ_BUFCFG_BURST128 (2 << 8)
> +#define TSI108_EC_RXQ_BUFCFG_BURST256 (3 << 8)
> +#define TSI108_EC_RXQ_BUFCFG_WSWP (1 << 11)
> +#define TSI108_EC_RXQ_BUFCFG_BSWP (1 << 10)
> +#define TSI108_EC_RXQ_BUFCFG_SFNPORT 0
> +
> +#define TSI108_EC_RXCTRL (0x324)
> +#define TSI108_EC_RXCTRL_ABORT (1 << 30)
> +#define TSI108_EC_RXCTRL_GO (1 << 15)
> +#define TSI108_EC_RXCTRL_QUEUE0 (1 << 0)
> +
> +#define TSI108_EC_RXERR (0x378)
> +
> +#define PHY_CTRL 0
> +#define PHY_CTRL_RESET (1 << 15)
> +#define PHY_CTRL_AUTONEG_EN (1 << 12)
> +#define PHY_CTRL_POWERDOWN (1 << 11)
> +#define PHY_CTRL_AUTONEG_START (1 << 9)
> +
> +#define PHY_STAT 1
> +#define PHY_STAT_LINKUP (1 << 2)
> +
> +#if (TSI108_PHY_TYPE =3D=3D PHY_MV88E)
> +/* Marvel 88E1xxx-specific */
> +#define PHY_SUM_STAT 0x11
> +#define PHY_SUM_STAT_SPEED_MASK (3 << 14)
> +#define PHY_SUM_STAT_SPEED_10 (0 << 14)
> +#define PHY_SUM_STAT_SPEED_100 (1 << 14)
> +#define PHY_SUM_STAT_SPEED_1000 (2 << 14)
> +#define PHY_SUM_STAT_FULLDUPLEX (1 << 13)
> +
> +#define PHY_SUM_STAT_1000T_FD (PHY_SUM_STAT_SPEED_1000 | =20
> PHY_SUM_STAT_FULLDUPLEX)
> +#define PHY_SUM_STAT_1000T_HD (PHY_SUM_STAT_SPEED_1000)
> +#define PHY_SUM_STAT_100TX_FD (PHY_SUM_STAT_SPEED_100 | =20
> PHY_SUM_STAT_FULLDUPLEX)
> +#define PHY_SUM_STAT_100TX_HD (PHY_SUM_STAT_SPEED_100)
> +#define PHY_SUM_STAT_10T_FD (PHY_SUM_STAT_SPEED_10 | =20
> PHY_SUM_STAT_FULLDUPLEX)
> +#define PHY_SUM_STAT_10T_HD (PHY_SUM_STAT_SPEED_10)
> +#elif (TSI108_PHY_TYPE =3D=3D PHY_BCM54XX)
> +/*Broadcom BCM54xx */
> +#define PHY_SUM_STAT 0x19
> +#define PHY_SUM_STAT_SPEED_MASK (7 << 8) /* Auto Negotiation HCD */
> +#define PHY_SUM_STAT_1000T_FD (7 << 8) /* 1000BASE-T Full-Duplex */
> +#define PHY_SUM_STAT_1000T_HD (6 << 8) /* 1000BASE-T Half-Duplex */
> +#define PHY_SUM_STAT_100TX_FD (5 << 8) /* 100BASE-TX Full-Duplex */
> +#define PHY_SUM_STAT_100T4 (4 << 8) /* 100BASE-T4 */
> +#define PHY_SUM_STAT_100TX_HD (3 << 8) /* 100BASE-TX Half-Duplex */
> +#define PHY_SUM_STAT_10T_FD (2 << 8) /* 10BASE-T Full-Duplex */
> +#define PHY_SUM_STAT_10T_HD (1 << 8) /* 10BASE-T Half-Duplex */
> +#define PHY_SUM_STAT_AN_FAIL (0 << 8) /* 1000BASE-T Half-Duplex */
> +#else
> +#error "PHY Device not specified"
> +#endif /* MV88_PHY */
> +
> +#define TSI108_TX_EOF (1 << 0) /* End of frame; last fragment of =20
> packet */
> +#define TSI108_TX_SOF (1 << 1) /* Start of frame; first frag. of =20
> packet */
> +#define TSI108_TX_VLAN (1 << 2) /* Per-frame VLAN: enables VLAN =20
> override */
> +#define TSI108_TX_HUGE (1 << 3) /* Huge frame enable */
> +#define TSI108_TX_PAD (1 << 4) /* Pad the packet if too short */
> +#define TSI108_TX_CRC (1 << 5) /* Generate CRC for this packet */
> +#define TSI108_TX_INT (1 << 14) /* Generate an IRQ after frag. =20
> processed */
> +#define TSI108_TX_RETRY 16 /* 4 bit field indicating num. of =20
> retries */
> +#define TSI108_TX_COL (1 << 20) /* Set if a collision occured */
> +#define TSI108_TX_LCOL (1 << 24) /* Set if a late collision =20
> occured */
> +#define TSI108_TX_UNDER (1 << 25) /* Set if a FIFO underrun =20
> occured */
> +#define TSI108_TX_RLIM (1 << 26) /* Set if the retry limit was =20
> reached */
> +#define TSI108_TX_OK (1 << 30) /* Set if the frame TX was =20
> successful */
> +#define TSI108_TX_OWN (1 << 31) /* Set if the device owns the =20
> descriptor */
> +
> +/* Note: the descriptor layouts assume big-endian byte order. */
> +typedef struct {
> + u32 buf0;
> + u32 buf1; /* Base address of buffer */
> + u32 next0; /* Address of next descriptor, if any */
> + u32 next1;
> + u16 vlan; /* VLAN, if override enabled for this packet */
> + u16 len; /* Length of buffer in bytes */
> + u32 misc; /* See TSI108_TX_* above */
> + u32 reserved0; /*reserved0 and reserved1 are added to make the =20
> desc */
> + u32 reserved1; /* 32-byte aligned */
> +} __attribute__ ((aligned(32))) tx_desc;
> +
> +#define TSI108_RX_EOF (1 << 0) /* End of frame; last fragment of
> packet */
> +#define TSI108_RX_SOF (1 << 1) /* Start of frame; first frag. of =20
> packet */
> +#define TSI108_RX_VLAN (1 << 2) /* Set on SOF if packet has a VLAN =
*/
> +#define TSI108_RX_FTYPE (1 << 3) /* Length/Type field is type, not =20
> length */
> +#define TSI108_RX_RUNT (1 << 4)/* Packet is less than minimum size =
*/
> +#define TSI108_RX_HASH (1 << 7)/* Hash table match */
> +#define TSI108_RX_BAD (1 << 8) /* Bad frame */
> +#define TSI108_RX_OVER (1 << 9) /* FIFO overrun occured */
> +#define TSI108_RX_TRUNC (1 << 11) /* Packet truncated due to =20
> excess length */
> +#define TSI108_RX_CRC (1 << 12) /* Packet had a CRC error */
> +#define TSI108_RX_INT (1 << 13) /* Generate an IRQ after frag. =20
> processed */
> +#define TSI108_RX_OWN (1 << 15) /* Set if the device owns the =20
> descriptor */
> +
> +typedef struct {
> + u32 buf0; /* Base address of buffer */
> + u32 buf1; /* Base address of buffer */
> + u32 next0; /* Address of next descriptor, if any */
> + u32 next1; /* Address of next descriptor, if any */
> + u16 vlan; /* VLAN of received packet, first frag only */
> + u16 len; /* Length of received fragment in bytes */
> + u16 blen; /* Length of buffer in bytes */
> + u16 misc; /* See TSI108_RX_* above */
> + u32 reserved0; /* reserved0 and reserved1 are added to make the =20
> desc */
> + u32 reserved1; /* 32-byte aligned */
> +} __attribute__ ((aligned(32))) rx_desc;
> +
> +#endif /* __TSI108_ETH_H */
> --=20
> 1.3.0
> _______________________________________________
> Linuxppc-dev mailing list
> Linuxppc-dev@ozlabs.org
> https://ozlabs.org/mailman/listinfo/linuxppc-dev
^ permalink raw reply
* [PATCH] dtc: add setting of physical boot cpu
From: Michael Neuling @ 2006-05-18 3:56 UTC (permalink / raw)
To: linuxppc-dev, Jon Loeliger
dtc always sets the physical boot CPU to 0xfeedbeef. Add a -b option to
set this.
---
I've tested the blob output but not asm output.
---
dtc.c | 12 +++++++++---
dtc.h | 6 ++++--
flattree.c | 16 ++++++++++------
3 files changed, 23 insertions(+), 11 deletions(-)
Index: dtc/dtc.c
===================================================================
--- dtc.orig/dtc.c
+++ dtc/dtc.c
@@ -95,6 +95,8 @@ static void usage(void)
fprintf(stderr, "\t\tBlob version to produce, defaults to 3 (relevant for dtb\n\t\tand asm output only)\n");
fprintf(stderr, "\t-R <number>\n");
fprintf(stderr, "\t\tMake space for <number> reserve map entries (relevant for \n\t\tdtb and asm output only)\n");
+ fprintf(stderr, "\t-b <number>\n");
+ fprintf(stderr, "\t\tSet the physical boot cpu\n");
fprintf(stderr, "\t-f\n");
fprintf(stderr, "\t\tForce - try to produce output even if the input tree has errors\n");
exit(2);
@@ -113,8 +115,9 @@ int main(int argc, char *argv[])
FILE *outf = NULL;
int outversion = 3;
int reservenum = 1;
+ int boot_cpuid_phys = 0xfeedbeef;
- while ((opt = getopt(argc, argv, "I:O:o:V:R:f")) != EOF) {
+ while ((opt = getopt(argc, argv, "I:O:o:V:R:fb:")) != EOF) {
switch (opt) {
case 'I':
inform = optarg;
@@ -134,6 +137,9 @@ int main(int argc, char *argv[])
case 'f':
force = 1;
break;
+ case 'b':
+ boot_cpuid_phys = strtol(optarg, NULL, 0);
+ break;
default:
usage();
}
@@ -185,9 +191,9 @@ int main(int argc, char *argv[])
if (streq(outform, "dts")) {
dt_to_source(outf, bi);
} else if (streq(outform, "dtb")) {
- dt_to_blob(outf, bi, outversion);
+ dt_to_blob(outf, bi, outversion, boot_cpuid_phys);
} else if (streq(outform, "asm")) {
- dt_to_asm(outf, bi, outversion);
+ dt_to_asm(outf, bi, outversion, boot_cpuid_phys);
} else if (streq(outform, "null")) {
/* do nothing */
} else {
Index: dtc/dtc.h
===================================================================
--- dtc.orig/dtc.h
+++ dtc/dtc.h
@@ -207,8 +207,10 @@ struct boot_info *build_boot_info(struct
/* Flattened trees */
-void dt_to_blob(FILE *f, struct boot_info *bi, int version);
-void dt_to_asm(FILE *f, struct boot_info *bi, int version);
+void dt_to_blob(FILE *f, struct boot_info *bi, int version,
+ int boot_cpuid_phys);
+void dt_to_asm(FILE *f, struct boot_info *bi, int version,
+ int boot_cpuid_phys);
struct boot_info *dt_from_blob(FILE *f);
Index: dtc/flattree.c
===================================================================
--- dtc.orig/flattree.c
+++ dtc/flattree.c
@@ -301,7 +301,8 @@ static struct data flatten_reserve_list(
}
static void make_bph(struct boot_param_header *bph,
struct version_info *vi,
- int reservesize, int dtsize, int strsize)
+ int reservesize, int dtsize, int strsize,
+ int boot_cpuid_phys)
{
int reserve_off;
@@ -324,12 +325,13 @@ static void make_bph(struct boot_param_h
+ dtsize + strsize);
if (vi->flags & FTF_BOOTCPUID)
- bph->boot_cpuid_phys = 0xfeedbeef;
+ bph->boot_cpuid_phys = cpu_to_be32(boot_cpuid_phys);
if (vi->flags & FTF_STRTABSIZE)
bph->size_dt_strings = cpu_to_be32(strsize);
}
-void dt_to_blob(FILE *f, struct boot_info *bi, int version)
+void dt_to_blob(FILE *f, struct boot_info *bi, int version,
+ int boot_cpuid_phys)
{
struct version_info *vi = NULL;
int i;
@@ -355,7 +357,8 @@ void dt_to_blob(FILE *f, struct boot_inf
reservebuf = flatten_reserve_list(bi->reservelist, vi);
/* Make header */
- make_bph(&bph, vi, reservebuf.len, dtbuf.len, strbuf.len);
+ make_bph(&bph, vi, reservebuf.len, dtbuf.len, strbuf.len,
+ boot_cpuid_phys);
fwrite(&bph, vi->hdr_size, 1, f);
@@ -395,7 +398,7 @@ static void dump_stringtable_asm(FILE *f
}
}
-void dt_to_asm(FILE *f, struct boot_info *bi, int version)
+void dt_to_asm(FILE *f, struct boot_info *bi, int version, int boot_cpuid_phys)
{
struct version_info *vi = NULL;
int i;
@@ -434,7 +437,8 @@ void dt_to_asm(FILE *f, struct boot_info
vi->last_comp_version);
if (vi->flags & FTF_BOOTCPUID)
- fprintf(f, "\t.long\t0xdeadbeef\t/*boot_cpuid_phys*/\n");
+ fprintf(f, "\t.long\t%i\t/*boot_cpuid_phys*/\n",
+ boot_cpuid_phys);
if (vi->flags & FTF_STRTABSIZE)
fprintf(f, "\t.long\t_%s_strings_end - _%s_strings_start\t/* size_dt_strings */\n",
^ permalink raw reply
* RE: [PATCH/2.6.17-rc4 5/10] Add tsi108 Ethernet support
From: Zang Roy-r61911 @ 2006-05-18 3:55 UTC (permalink / raw)
To: jgarzik
Cc: Alexandre.Bounine, netdev, linux-kernel, linuxppc-dev list,
Paul Mackerras, Yang Xin-Xin-r48390
This patch adds Tundra tsi108 Ethernet driver support.
-----Original Message-----
From: Kumar Gala [mailto:galak@kernel.crashing.org]
Sent: 2006=E5=B9=B45=E6=9C=8817=E6=97=A5 21:24
To: Zang Roy-r61911
Cc: Paul Mackerras; linuxppc-dev list; Alexandre.Bounine@tundra.com; =
Yang Xin-Xin-r48390
Subject: Re: [PATCH/2.6.17-rc4 5/10] Add tsi108 Ethernet support
On May 17, 2006, at 5:14 AM, Zang Roy-r61911 wrote:
> This patch adds a device driver and configuration options for
> Tundra Semiconductor Tsi108 integrated dual port Gigabit Ethernet =20
> controller
>
> Signed-off-by: Alexandre Bounine <alexandreb@tundra.com>
> Signed-off-by: Roy Zang <tie-fei.zang@freescale.com>
This patch needs to go to the netdev list and Jeff Garzik as =20
maintainer for review.
- kumar
> drivers/net/Kconfig | 8
> drivers/net/Makefile | 1
> drivers/net/tsi108_eth.c | 1740 +++++++++++++++++++++++++++++++++++=20
> +++++++++++
> drivers/net/tsi108_eth.h | 404 +++++++++++
> 4 files changed, 2153 insertions(+), 0 deletions(-)
> create mode 100644 drivers/net/tsi108_eth.c
> create mode 100644 drivers/net/tsi108_eth.h
>
> 23ed1c55ab6bc7a4c1c76f646579a1bc195bc7d2
> diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig
> index bdaaad8..8a4ef29 100644
> --- a/drivers/net/Kconfig
> +++ b/drivers/net/Kconfig
> @@ -2177,6 +2177,14 @@ config SPIDER_NET
> This driver supports the Gigabit Ethernet chips present on the
> Cell Processor-Based Blades from IBM.
>
> +config TSI108_ETH
> + tristate "Tundra TSI108 gigabit Ethernet support"
> + depends on TSI108_BRIDGE
> + help
> + This driver supports Tundra TSI108 gigabit Ethernet ports.
> + To compile this driver as a module, choose M here: the module
> + will be called tsi108_eth.
> +
> config GIANFAR
> tristate "Gianfar Ethernet"
> depends on 85xx || 83xx
> diff --git a/drivers/net/Makefile b/drivers/net/Makefile
> index b90468a..5f90b30 100644
> --- a/drivers/net/Makefile
> +++ b/drivers/net/Makefile
> @@ -109,6 +109,7 @@ obj-$(CONFIG_B44) +=3D b44.o
> obj-$(CONFIG_FORCEDETH) +=3D forcedeth.o
> obj-$(CONFIG_NE_H8300) +=3D ne-h8300.o 8390.o
>
> +obj-$(CONFIG_TSI108_ETH) +=3D tsi108_eth.o
> obj-$(CONFIG_MV643XX_ETH) +=3D mv643xx_eth.o
>
> obj-$(CONFIG_PPP) +=3D ppp_generic.o slhc.o
> diff --git a/drivers/net/tsi108_eth.c b/drivers/net/tsi108_eth.c
> new file mode 100644
> index 0000000..cb67dbe
> --- /dev/null
> +++ b/drivers/net/tsi108_eth.c
> @@ -0,0 +1,1740 @@
> +/=20
> =
**********************************************************************=20
> *********
> +
> + Copyright(c) 2005 Tundra Semiconductor Corporation.
> +
> + This program is free software; you can redistribute it and/or =20
> modify it
> + under the terms of the GNU General Public License as published =20
> by the Free
> + Software Foundation; either version 2 of the License, or (at =20
> your option)
> + any later version.
> +
> + This program is distributed in the hope that it will be useful, =20
> but WITHOUT
> + ANY WARRANTY; without even the implied warranty of =20
> MERCHANTABILITY or
> + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public =20
> License for
> + more details.
> +
> + You should have received a copy of the GNU General Public =20
> License along with
> + this program; if not, write to the Free Software Foundation, =20
> Inc., 59
> + Temple Place - Suite 330, Boston, MA 02111-1307, USA.
> +
> =
+*********************************************************************=20
> **********/
> +
> +/* This driver is based on the driver code originally developed=09
> + * for the Intel IOC80314 (ForestLake) Gigabit Ethernet by
> + * scott.wood@timesys.com * Copyright (C) 2003 TimeSys Corporation
> + *
> + * Currently changes from original version are:
> + * - portig to Tsi108-based platform and kernel 2.6
> (kong.lai@tundra.com)
> + * - modifications to handle two ports independently and support for
> + * additional PHY devices (alexandre.bounine@tundra.com)
> + *
> + */
> +
> +#include <linux/config.h>
> +#include <linux/module.h>
> +#include <linux/types.h>
> +#include <linux/init.h>
> +#include <linux/net.h>
> +#include <linux/netdevice.h>
> +#include <linux/etherdevice.h>
> +#include <linux/skbuff.h>
> +#include <linux/slab.h>
> +#include <linux/sched.h>
> +#include <linux/spinlock.h>
> +#include <linux/delay.h>
> +#include <linux/crc32.h>
> +#include <linux/mii.h>
> +#include <linux/device.h>
> +#include <asm/system.h>
> +#include <asm/io.h>
> +#include <linux/pci.h>
> +#include <linux/rtnetlink.h>
> +#include <linux/timer.h>
> +
> +#include <asm/tsi108_irq.h>
> +#include <asm/tsi108.h>
> +#include "tsi108_eth.h"
> +
> +typedef struct net_device net_device;
> +typedef struct sk_buff sk_buff;
> +
> +#define MII_READ_DELAY 10000 /* max link wait time in msec */
> +
> +#define TSI108_RXRING_LEN 256
> +
> +/* NOTE: The driver currently does not support receiving packets
> + * larger than the buffer size, so don't decrease this (unless you
> + * want to add such support).
> + */
> +#define TSI108_RXBUF_SIZE 1536
> +
> +#define TSI108_TXRING_LEN 256
> +
> +#define TSI108_TX_INT_FREQ 64
> +
> +/* Check the phy status every half a second. */
> +#define CHECK_PHY_INTERVAL (HZ/2)
> +
> +extern hw_info hw_info_table[];
> +
> +typedef struct {
> + volatile u32 regs; /* Base of normal regs */
> + volatile u32 phyregs; /* Base of register bank used for PHY =20
> access */
> + int phy; /* Index of PHY for this interface */
> + int irq_num;
> +
> + struct timer_list timer;/* Timer that triggers the check phy =20
> function */
> + int rxtail; /* Next entry in rxring to read */
> + int rxhead; /* Next entry in rxring to give a new buffer */
> + int rxfree; /* Number of free, allocated RX buffers */
> +
> + int rxpending; /* Non-zero if there are still descriptors
> + * to be processed from a previous descriptor
> + * interrupt condition that has been cleared */
> +
> + int txtail; /* Next TX descriptor to check status on */
> + int txhead; /* Next TX descriptor to use */
> +
> + /* Number of free TX descriptors. This could be calculated from
> + * rxhead and rxtail if one descriptor were left unused to =20
> disambiguate
> + * full and empty conditions, but it's simpler to just keep track
> + * explicitly. */
> +
> + int txfree;
> +
> + int phy_ok; /* The PHY is currently powered on. */
> +
> + /* PHY status (duplex is 1 for half, 2 for full,
> + * so that the default 0 indicates that neither has
> + * yet been configured). */
> +
> + int link_up;
> + int speed;
> + int duplex;
> +
> + tx_desc *txring;
> + rx_desc *rxring;
> + sk_buff *txskbs[TSI108_TXRING_LEN];
> + sk_buff *rxskbs[TSI108_RXRING_LEN];
> +
> + dma_addr_t txdma, rxdma;
> +
> + /* txlock nests in misclock and phy_lock */
> +
> + spinlock_t txlock, misclock;
> +
> + /* stats is used to hold the upper bits of each hardware counter,
> + * and tmpstats is used to hold the full values for returning
> + * to the caller of get_stats(). They must be separate in case
> + * an overflow interrupt occurs before the stats are consumed.
> + */
> +
> + struct net_device_stats stats;
> + struct net_device_stats tmpstats;
> +
> + /* These stats are kept separate in hardware, thus require =20
> individual
> + * fields for handling carry. They are combined in get_stats.
> + */
> +
> + unsigned long rx_fcs; /* Add to rx_frame_errors */
> + unsigned long rx_short_fcs; /* Add to rx_frame_errors */
> + unsigned long rx_long_fcs; /* Add to rx_frame_errors */
> + unsigned long rx_underruns; /* Add to rx_length_errors */
> + unsigned long rx_overruns; /* Add to rx_length_errors */
> +
> + unsigned long tx_coll_abort; /* Add to tx_aborted_errors/=20
> collisions */
> + unsigned long tx_pause_drop; /* Add to tx_aborted_errors */
> +
> + unsigned long mc_hash[16];
> +} tsi108_prv_data;
> +static void tsi108_timed_checker(unsigned long dev_ptr);
> +
> +static net_device *tsi108_devs[TSI108_ETH_PORT_NUM];
> +
> +static void dump_eth_one(net_device * dev)
> +{
> + tsi108_prv_data *data =3D netdev_priv(dev);
> +
> + printk("Dumping %s...\n", dev->name);
> + printk("intstat %x intmask %x phy_ok %d"
> + " link %d speed %d duplex %d\n",
> + TSI108_ETH_READ_REG(TSI108_EC_INTSTAT),
> + TSI108_ETH_READ_REG(TSI108_EC_INTMASK), data->phy_ok,
> + data->link_up, data->speed, data->duplex);
> +
> + printk("TX: head %d, tail %d, free %d, stat %x, estat %x, err =
%x\n",
> + data->txhead, data->txtail, data->txfree,
> + TSI108_ETH_READ_REG(TSI108_EC_TXSTAT),
> + TSI108_ETH_READ_REG(TSI108_EC_TXESTAT),
> + TSI108_ETH_READ_REG(TSI108_EC_TXERR));
> +
> + printk("RX: head %d, tail %d, free %d, stat %x,"
> + " estat %x, err %x, pending %d\n\n",
> + data->rxhead, data->rxtail, data->rxfree,
> + TSI108_ETH_READ_REG(TSI108_EC_RXSTAT),
> + TSI108_ETH_READ_REG(TSI108_EC_RXESTAT),
> + TSI108_ETH_READ_REG(TSI108_EC_RXERR), data->rxpending);
> +}
> +
> +void tsi108_dump_eth(void)
> +{
> + dump_eth_one(tsi108_devs[0]);
> + dump_eth_one(tsi108_devs[1]);
> +}
> +
> +/* Synchronization is needed between the thread and up/down events.
> + * Note that the PHY is accessed through the same registers for both
> + * interfaces, so this can't be made interface-specific.
> + */
> +
> +static spinlock_t phy_lock =3D SPIN_LOCK_UNLOCKED;
> +
> +static inline u16 tsi108_read_mii(tsi108_prv_data * data, int reg, =20
> int *status)
> +{
> + int i;
> + u16 ret;
> +
> + TSI108_ETH_WRITE_PHYREG(TSI108_MAC_MII_ADDR,
> + (data->phy << TSI108_MAC_MII_ADDR_PHY) |
> + (reg << TSI108_MAC_MII_ADDR_REG));
> + mb();
> + TSI108_ETH_WRITE_PHYREG(TSI108_MAC_MII_CMD, 0);
> + mb();
> + TSI108_ETH_WRITE_PHYREG(TSI108_MAC_MII_CMD, =20
> TSI108_MAC_MII_CMD_READ);
> + mb();
> + for (i =3D 0; i < 100; i++) {
> + if (!(TSI108_ETH_READ_PHYREG(TSI108_MAC_MII_IND) &
> + (TSI108_MAC_MII_IND_NOTVALID | TSI108_MAC_MII_IND_BUSY)))
> + break;
> + udelay(10);
> + }
> +
> + if (i =3D=3D 100) {
> + if (status)
> + *status =3D -EBUSY;
> +
> + ret =3D 0xffff;
> + } else {
> + if (status)
> + *status =3D 0;
> +
> + ret =3D TSI108_ETH_READ_PHYREG(TSI108_MAC_MII_DATAIN);
> + }
> +
> + return ret;
> +}
> +
> +static inline void tsi108_write_mii(tsi108_prv_data * data, int =20
> reg, u16 val)
> +{
> + TSI108_ETH_WRITE_PHYREG(TSI108_MAC_MII_ADDR,
> + (data->phy << TSI108_MAC_MII_ADDR_PHY) |
> + (reg << TSI108_MAC_MII_ADDR_REG));
> + mb();
> + TSI108_ETH_WRITE_PHYREG(TSI108_MAC_MII_DATAOUT, val);
> + mb();
> + while (TSI108_ETH_READ_PHYREG(TSI108_MAC_MII_IND) &
> + TSI108_MAC_MII_IND_BUSY) ;
> +}
> +
> +static inline void tsi108_write_tbi(tsi108_prv_data * data, int =20
> reg, u16 val)
> +{
> +
> + TSI108_ETH_WRITE_REG(TSI108_MAC_MII_ADDR,
> + (0x1e << TSI108_MAC_MII_ADDR_PHY)
> + | (reg << TSI108_MAC_MII_ADDR_REG));
> + mb();
> +
> + TSI108_ETH_WRITE_REG(TSI108_MAC_MII_DATAOUT, val);
> + mb();
> + while (TSI108_ETH_READ_REG(TSI108_MAC_MII_IND) &
> + TSI108_MAC_MII_IND_BUSY) ;
> +}
> +
> +static void tsi108_check_phy(net_device * dev)
> +{
> + tsi108_prv_data *data =3D netdev_priv(dev);
> + u16 sumstat;
> + u32 mac_cfg2_reg, portctrl_reg;
> + u32 fdx_flag =3D 0, reg_update =3D 0;
> +
> + /* Do a dummy read, as for some reason the first read
> + * after a link becomes up returns link down, even if
> + * it's been a while since the link came up.
> + */
> +
> + spin_lock(&phy_lock);
> +
> + if (!data->phy_ok)
> + goto out;
> +
> + tsi108_read_mii(data, PHY_STAT, NULL);
> +
> + if (!(tsi108_read_mii(data, PHY_STAT, NULL) & PHY_STAT_LINKUP)) {
> + if (data->link_up =3D=3D 1) {
> + netif_stop_queue(dev);
> + data->link_up =3D 0;
> + printk(KERN_NOTICE "%s : link is down\n", dev->name);
> + netif_carrier_off(dev);
> + }
> +
> + goto out;
> + }
> +
> + {
> + mac_cfg2_reg =3D TSI108_ETH_READ_REG(TSI108_MAC_CFG2);
> + portctrl_reg =3D TSI108_ETH_READ_REG(TSI108_EC_PORTCTRL);
> +
> + sumstat =3D tsi108_read_mii(data, PHY_SUM_STAT, NULL);
> +
> + switch (sumstat & PHY_SUM_STAT_SPEED_MASK) {
> + case PHY_SUM_STAT_1000T_FD:
> + fdx_flag++;
> + case PHY_SUM_STAT_1000T_HD:
> + if (data->speed !=3D 1000) {
> + mac_cfg2_reg &=3D ~TSI108_MAC_CFG2_IFACE_MASK;
> + mac_cfg2_reg |=3D TSI108_MAC_CFG2_GIG;
> + portctrl_reg &=3D ~TSI108_EC_PORTCTRL_NOGIG;
> + data->speed =3D 1000;
> + reg_update++;
> + }
> + break;
> + case PHY_SUM_STAT_100TX_FD:
> + fdx_flag++;
> + case PHY_SUM_STAT_100TX_HD:
> + if (data->speed !=3D 100) {
> + mac_cfg2_reg &=3D ~TSI108_MAC_CFG2_IFACE_MASK;
> + mac_cfg2_reg |=3D TSI108_MAC_CFG2_NOGIG;
> + portctrl_reg |=3D TSI108_EC_PORTCTRL_NOGIG;
> + data->speed =3D 100;
> + reg_update++;
> + }
> + break;
> +
> + case PHY_SUM_STAT_10T_FD:
> + fdx_flag++;
> + case PHY_SUM_STAT_10T_HD:
> + if (data->speed !=3D 10) {
> + mac_cfg2_reg &=3D ~TSI108_MAC_CFG2_IFACE_MASK;
> + mac_cfg2_reg |=3D TSI108_MAC_CFG2_NOGIG;
> + portctrl_reg |=3D TSI108_EC_PORTCTRL_NOGIG;
> + data->speed =3D 10;
> + reg_update++;
> + }
> + break;
> +
> + default:
> + if (net_ratelimit())
> + printk(KERN_ERR "PHY reported invalid speed,"
> + KERN_ERR " summary status %x\n",
> + sumstat);
> + goto out;
> + }
> +
> + if (fdx_flag || (sumstat & PHY_SUM_STAT_FULLDUPLEX)) {
> + if (data->duplex !=3D 2) {
> + mac_cfg2_reg |=3D TSI108_MAC_CFG2_FULLDUPLEX;
> + portctrl_reg &=3D ~TSI108_EC_PORTCTRL_HALFDUPLEX;
> + data->duplex =3D 2;
> + reg_update++;
> + }
> + } else {
> + if (data->duplex !=3D 1) {
> + mac_cfg2_reg &=3D ~TSI108_MAC_CFG2_FULLDUPLEX;
> + portctrl_reg |=3D TSI108_EC_PORTCTRL_HALFDUPLEX;
> + data->duplex =3D 1;
> + reg_update++;
> + }
> + }
> +
> + if (reg_update) {
> + TSI108_ETH_WRITE_REG(TSI108_MAC_CFG2, mac_cfg2_reg);
> + mb();
> + TSI108_ETH_WRITE_REG(TSI108_EC_PORTCTRL, portctrl_reg);
> + mb();
> + }
> +
> + }
> +
> + if (data->link_up =3D=3D 0) {
> + /* The manual says it can take 3-4 usecs for the speed change
> + * to take effect.
> + */
> + udelay(5);
> +
> + spin_lock(&data->txlock);
> + if (netif_queue_stopped(dev)
> + && is_valid_ether_addr(dev->dev_addr) && data->txfree)
> + netif_wake_queue(dev);
> +
> + data->link_up =3D 1;
> + spin_unlock(&data->txlock);
> + printk("%s : link is up: %dMb %s-duplex\n",
> + dev->name, data->speed,
> + (data->duplex =3D=3D 2) ? "full" : "half");
> + netif_carrier_on(dev);
> + }
> +
> + out:
> + spin_unlock(&phy_lock);
> +}
> +
> +static inline void
> +tsi108_stat_carry_one(int carry, int carry_bit, int carry_shift,
> + unsigned long *upper)
> +{
> + if (carry & carry_bit)
> + *upper +=3D carry_shift;
> +}
> +
> +static void tsi108_stat_carry(net_device * dev)
> +{
> + tsi108_prv_data *data =3D netdev_priv(dev);
> + u32 carry1, carry2;
> +
> + spin_lock_irq(&data->misclock);
> +
> + carry1 =3D TSI108_ETH_READ_REG(TSI108_STAT_CARRY1);
> + carry2 =3D TSI108_ETH_READ_REG(TSI108_STAT_CARRY2);
> +
> + TSI108_ETH_WRITE_REG(TSI108_STAT_CARRY1, carry1);
> + TSI108_ETH_WRITE_REG(TSI108_STAT_CARRY2, carry2);
> +
> + tsi108_stat_carry_one(carry1, TSI108_STAT_CARRY1_RXBYTES,
> + TSI108_STAT_RXBYTES_CARRY, &data->stats.rx_bytes);
> +
> + tsi108_stat_carry_one(carry1, TSI108_STAT_CARRY1_RXPKTS,
> + TSI108_STAT_RXPKTS_CARRY,
> + &data->stats.rx_packets);
> +
> + tsi108_stat_carry_one(carry1, TSI108_STAT_CARRY1_RXFCS,
> + TSI108_STAT_RXFCS_CARRY, &data->rx_fcs);
> +
> + tsi108_stat_carry_one(carry1, TSI108_STAT_CARRY1_RXMCAST,
> + TSI108_STAT_RXMCAST_CARRY,
> + &data->stats.multicast);
> +
> + tsi108_stat_carry_one(carry1, TSI108_STAT_CARRY1_RXALIGN,
> + TSI108_STAT_RXALIGN_CARRY,
> + &data->stats.rx_frame_errors);
> +
> + tsi108_stat_carry_one(carry1, TSI108_STAT_CARRY1_RXLENGTH,
> + TSI108_STAT_RXLENGTH_CARRY,
> + &data->stats.rx_length_errors);
> +
> + tsi108_stat_carry_one(carry1, TSI108_STAT_CARRY1_RXRUNT,
> + TSI108_STAT_RXRUNT_CARRY, &data->rx_underruns);
> +
> + tsi108_stat_carry_one(carry1, TSI108_STAT_CARRY1_RXJUMBO,
> + TSI108_STAT_RXJUMBO_CARRY, &data->rx_overruns);
> +
> + tsi108_stat_carry_one(carry1, TSI108_STAT_CARRY1_RXFRAG,
> + TSI108_STAT_RXFRAG_CARRY, &data->rx_short_fcs);
> +
> + tsi108_stat_carry_one(carry1, TSI108_STAT_CARRY1_RXJABBER,
> + TSI108_STAT_RXJABBER_CARRY, &data->rx_long_fcs);
> +
> + tsi108_stat_carry_one(carry1, TSI108_STAT_CARRY1_RXDROP,
> + TSI108_STAT_RXDROP_CARRY,
> + &data->stats.rx_missed_errors);
> +
> + tsi108_stat_carry_one(carry2, TSI108_STAT_CARRY2_TXBYTES,
> + TSI108_STAT_TXBYTES_CARRY, &data->stats.tx_bytes);
> +
> + tsi108_stat_carry_one(carry2, TSI108_STAT_CARRY2_TXPKTS,
> + TSI108_STAT_TXPKTS_CARRY,
> + &data->stats.tx_packets);
> +
> + tsi108_stat_carry_one(carry2, TSI108_STAT_CARRY2_TXEXDEF,
> + TSI108_STAT_TXEXDEF_CARRY,
> + &data->stats.tx_aborted_errors);
> +
> + tsi108_stat_carry_one(carry2, TSI108_STAT_CARRY2_TXEXCOL,
> + TSI108_STAT_TXEXCOL_CARRY, &data->tx_coll_abort);
> +
> + tsi108_stat_carry_one(carry2, TSI108_STAT_CARRY2_TXTCOL,
> + TSI108_STAT_TXTCOL_CARRY,
> + &data->stats.collisions);
> +
> + tsi108_stat_carry_one(carry2, TSI108_STAT_CARRY2_TXPAUSE,
> + TSI108_STAT_TXPAUSEDROP_CARRY,
> + &data->tx_pause_drop);
> +
> + spin_unlock_irq(&data->misclock);
> +}
> +
> +/* Read a stat counter atomically with respect to carries.
> + * data->misclock must be held.
> + */
> +static inline unsigned long
> +tsi108_read_stat(tsi108_prv_data * data, int reg, int carry_bit,
> + int carry_shift, unsigned long *upper)
> +{
> + int carryreg;
> + unsigned long val;
> +
> + if (reg < 0xb0)
> + carryreg =3D TSI108_STAT_CARRY1;
> + else
> + carryreg =3D TSI108_STAT_CARRY2;
> +
> + again:
> + val =3D TSI108_ETH_READ_REG(reg) | *upper;
> +
> + rmb();
> +
> + /* Check to see if it overflowed, but the interrupt hasn't
> + * been serviced yet. If so, handle the carry here, and
> + * try again.
> + */
> +
> + if (unlikely(TSI108_ETH_READ_REG(carryreg) & carry_bit)) {
> + *upper +=3D carry_shift;
> + TSI108_ETH_WRITE_REG(carryreg, carry_bit);
> + mb();
> +
> + goto again;
> + }
> +
> + return val;
> +}
> +
> +static struct net_device_stats *tsi108_get_stats(net_device * dev)
> +{
> + unsigned long excol;
> +
> + tsi108_prv_data *data =3D netdev_priv(dev);
> + spin_lock_irq(&data->misclock);
> +
> + data->tmpstats.rx_packets =3D
> + tsi108_read_stat(data, TSI108_STAT_RXPKTS,
> + TSI108_STAT_CARRY1_RXPKTS,
> + TSI108_STAT_RXPKTS_CARRY, &data->stats.rx_packets);
> +
> + data->tmpstats.tx_packets =3D
> + tsi108_read_stat(data, TSI108_STAT_TXPKTS,
> + TSI108_STAT_CARRY2_TXPKTS,
> + TSI108_STAT_TXPKTS_CARRY, &data->stats.tx_packets);
> +
> + data->tmpstats.rx_bytes =3D
> + tsi108_read_stat(data, TSI108_STAT_RXBYTES,
> + TSI108_STAT_CARRY1_RXBYTES,
> + TSI108_STAT_RXBYTES_CARRY, &data->stats.rx_bytes);
> +
> + data->tmpstats.tx_bytes =3D
> + tsi108_read_stat(data, TSI108_STAT_TXBYTES,
> + TSI108_STAT_CARRY2_TXBYTES,
> + TSI108_STAT_TXBYTES_CARRY, &data->stats.tx_bytes);
> +
> + data->tmpstats.multicast =3D
> + tsi108_read_stat(data, TSI108_STAT_RXMCAST,
> + TSI108_STAT_CARRY1_RXMCAST,
> + TSI108_STAT_RXMCAST_CARRY, &data->stats.multicast);
> +
> + excol =3D tsi108_read_stat(data, TSI108_STAT_TXEXCOL,
> + TSI108_STAT_CARRY2_TXEXCOL,
> + TSI108_STAT_TXEXCOL_CARRY,
> + &data->tx_coll_abort);
> +
> + data->tmpstats.collisions =3D
> + tsi108_read_stat(data, TSI108_STAT_TXTCOL,
> + TSI108_STAT_CARRY2_TXTCOL,
> + TSI108_STAT_TXTCOL_CARRY, &data->stats.collisions);
> +
> + data->tmpstats.collisions +=3D excol;
> +
> + data->tmpstats.rx_length_errors =3D
> + tsi108_read_stat(data, TSI108_STAT_RXLENGTH,
> + TSI108_STAT_CARRY1_RXLENGTH,
> + TSI108_STAT_RXLENGTH_CARRY,
> + &data->stats.rx_length_errors);
> +
> + data->tmpstats.rx_length_errors +=3D
> + tsi108_read_stat(data, TSI108_STAT_RXRUNT,
> + TSI108_STAT_CARRY1_RXRUNT,
> + TSI108_STAT_RXRUNT_CARRY, &data->rx_underruns);
> +
> + data->tmpstats.rx_length_errors +=3D
> + tsi108_read_stat(data, TSI108_STAT_RXJUMBO,
> + TSI108_STAT_CARRY1_RXJUMBO,
> + TSI108_STAT_RXJUMBO_CARRY, &data->rx_overruns);
> +
> + data->tmpstats.rx_frame_errors =3D
> + tsi108_read_stat(data, TSI108_STAT_RXALIGN,
> + TSI108_STAT_CARRY1_RXALIGN,
> + TSI108_STAT_RXALIGN_CARRY,
> + &data->stats.rx_frame_errors);
> +
> + data->tmpstats.rx_frame_errors +=3D
> + tsi108_read_stat(data, TSI108_STAT_RXFCS,
> + TSI108_STAT_CARRY1_RXFCS, TSI108_STAT_RXFCS_CARRY,
> + &data->rx_fcs);
> +
> + data->tmpstats.rx_frame_errors +=3D
> + tsi108_read_stat(data, TSI108_STAT_RXFRAG,
> + TSI108_STAT_CARRY1_RXFRAG,
> + TSI108_STAT_RXFRAG_CARRY, &data->rx_short_fcs);
> +
> + data->tmpstats.rx_missed_errors =3D
> + tsi108_read_stat(data, TSI108_STAT_RXDROP,
> + TSI108_STAT_CARRY1_RXDROP,
> + TSI108_STAT_RXDROP_CARRY,
> + &data->stats.rx_missed_errors);
> +
> + /* These three are maintained by software. */
> + data->tmpstats.rx_fifo_errors =3D data->stats.rx_fifo_errors;
> + data->tmpstats.rx_crc_errors =3D data->stats.rx_crc_errors;
> +
> + data->tmpstats.tx_aborted_errors =3D
> + tsi108_read_stat(data, TSI108_STAT_TXEXDEF,
> + TSI108_STAT_CARRY2_TXEXDEF,
> + TSI108_STAT_TXEXDEF_CARRY,
> + &data->stats.tx_aborted_errors);
> +
> + data->tmpstats.tx_aborted_errors +=3D
> + tsi108_read_stat(data, TSI108_STAT_TXPAUSEDROP,
> + TSI108_STAT_CARRY2_TXPAUSE,
> + TSI108_STAT_TXPAUSEDROP_CARRY,
> + &data->tx_pause_drop);
> +
> + data->tmpstats.tx_aborted_errors +=3D excol;
> +
> + data->tmpstats.tx_errors =3D data->tmpstats.tx_aborted_errors;
> + data->tmpstats.rx_errors =3D data->tmpstats.rx_length_errors +
> + data->tmpstats.rx_crc_errors +
> + data->tmpstats.rx_frame_errors +
> + data->tmpstats.rx_fifo_errors + =
data->tmpstats.rx_missed_errors;
> +
> + spin_unlock_irq(&data->misclock);
> + return &data->tmpstats;
> +}
> +
> +static void tsi108_restart_rx(tsi108_prv_data * data, net_device * =20
> dev)
> +{
> + TSI108_ETH_WRITE_REG(TSI108_EC_RXQ_PTRHIGH,
> + TSI108_EC_RXQ_PTRHIGH_VALID);
> +
> + wmb();
> + TSI108_ETH_WRITE_REG(TSI108_EC_RXCTRL, TSI108_EC_RXCTRL_GO
> + | TSI108_EC_RXCTRL_QUEUE0);
> +}
> +
> +static void tsi108_restart_tx(tsi108_prv_data * data)
> +{
> + TSI108_ETH_WRITE_REG(TSI108_EC_TXQ_PTRHIGH,
> + TSI108_EC_TXQ_PTRHIGH_VALID);
> +
> + wmb();
> + TSI108_ETH_WRITE_REG(TSI108_EC_TXCTRL, TSI108_EC_TXCTRL_IDLEINT |
> + TSI108_EC_TXCTRL_GO | TSI108_EC_TXCTRL_QUEUE0);
> +}
> +
> +/* txlock must be held by caller, with IRQs disabled, and
> + * with permission to re-enable them when the lock is dropped.
> + */
> +static void tsi108_check_for_completed_tx(net_device * dev)
> +{
> + tsi108_prv_data *data =3D netdev_priv(dev);
> + int tx;
> + struct sk_buff *skb;
> + int release =3D 0;
> +
> + while (!data->txfree || data->txhead !=3D data->txtail) {
> + tx =3D data->txtail;
> +
> + if (data->txring[tx].misc & TSI108_TX_OWN)
> + break;
> +
> + skb =3D data->txskbs[tx];
> +
> + if (!(data->txring[tx].misc & TSI108_TX_OK))
> + printk("%s: bad tx packet, misc %x\n",
> + dev->name, data->txring[tx].misc);
> +
> + data->txtail =3D (data->txtail + 1) % TSI108_TXRING_LEN;
> + data->txfree++;
> +
> + if (data->txring[tx].misc & TSI108_TX_EOF) {
> + dev_kfree_skb_any(skb);
> + release++;
> + }
> + }
> +
> + if (release) {
> +
> + if (netif_queue_stopped(dev)
> + && is_valid_ether_addr(dev->dev_addr) && data->link_up)
> + netif_wake_queue(dev);
> + }
> +}
> +
> +static int tsi108_send_packet(sk_buff * skb, net_device * dev)
> +{
> + tsi108_prv_data *data =3D netdev_priv(dev);
> + int frags =3D skb_shinfo(skb)->nr_frags + 1;
> + int i;
> +#ifdef FIXME_SG_CSUM_NOT_TESTED /* FIXME: Not supported now. */
> + long csstart;
> + long csum;
> +
> + csstart =3D skb->len - skb->data_len;
> + if (csstart > skb->len - skb->data_len)
> + BUG();
> + csum =3D 0;
> + if (csstart !=3D skb->len)
> + csum =3D skb_checksum(skb, csstart, skb->len - csstart, 0);
> +#endif
> +
> + if (!data->phy_ok && net_ratelimit())
> + printk(KERN_ERR "%s: Transmit while PHY is down!\n", dev->name);
> +
> + if (!data->link_up) {
> + printk(KERN_ERR "%s: Transmit while link is down!\n",
> + dev->name);
> + netif_stop_queue(dev);
> + return 1;
> + }
> +
> + if (data->txfree < MAX_SKB_FRAGS + 1) {
> + netif_stop_queue(dev);
> +
> + if (net_ratelimit())
> + printk(KERN_ERR "%s: Transmit with full tx ring!\n",
> + dev->name);
> + return 1;
> + }
> +
> + if (data->txfree - frags < MAX_SKB_FRAGS + 1) {
> + netif_stop_queue(dev);
> + }
> +
> + spin_lock_irq(&data->txlock);
> +
> + for (i =3D 0; i < frags; i++) {
> + int misc =3D 0;
> + int tx =3D data->txhead;
> +
> + /* This is done to mark every TSI108_TX_INT_FREQ tx buffers with
> + * the interrupt bit. TX descriptor-complete interrupts are
> + * enabled when the queue fills up, and masked when there is
> + * still free space. This way, when saturating the outbound
> + * link, the tx interrupts are kept to a reasonable level.
> + * When the queue is not full, reclamation of skbs still occurs
> + * as new packets are transmitted, or on a queue-empty
> + * interrupt.
> + */
> +
> + if ((tx % TSI108_TX_INT_FREQ =3D=3D 0) &&
> + ((TSI108_TXRING_LEN - data->txfree) >=3D TSI108_TX_INT_FREQ)
> + )
> + misc =3D TSI108_TX_INT;
> +
> + data->txskbs[tx] =3D skb;
> +
> + if (i =3D=3D 0) {
> + data->txring[tx].buf0 =3D virt_to_phys(skb->data);
> + data->txring[tx].len =3D skb->len - skb->data_len;
> + misc |=3D TSI108_TX_SOF;
> + } else {
> + skb_frag_t *frag =3D &skb_shinfo(skb)->frags[i - 1];
> +
> + data->txring[tx].buf0 =3D
> + page_to_phys(frag->page) + frag->page_offset;
> + data->txring[tx].len =3D frag->size;
> + }
> +
> + if (i =3D=3D frags - 1)
> + misc |=3D TSI108_TX_EOF;
> +
> +#ifdef TSI108_PRINT_TX_FRAME
> + {
> + int i;
> + printk("%s: Tx Frame contents (%d)\n", dev->name,
> + skb->len);
> + for (i =3D 0; i < skb->len; i++)
> + printk(" %2.2x", skb->data[i]);
> + printk(".\n");
> + }
> +#endif /* TSI108_PRINT_TX_FRAME */
> +
> + mb();
> + data->txring[tx].misc =3D misc | TSI108_TX_OWN;
> +
> + data->txhead =3D (data->txhead + 1) % TSI108_TXRING_LEN;
> + data->txfree--;
> + }
> +
> + tsi108_check_for_completed_tx(dev);
> +
> + /* This must be done after the check for completed tx descriptors,
> + * so that the tail pointer is correct.
> + */
> +
> + if (!(TSI108_ETH_READ_REG(TSI108_EC_TXSTAT) & =20
> TSI108_EC_TXSTAT_QUEUE0))
> + tsi108_restart_tx(data);
> +
> + spin_unlock_irq(&data->txlock);
> + return 0;
> +}
> +
> +static int tsi108_check_for_completed_rx(net_device * dev, int =20
> budget)
> +{
> + tsi108_prv_data *data =3D netdev_priv(dev);
> + int done =3D 0;
> +
> + while (data->rxfree && done !=3D budget) {
> + int rx =3D data->rxtail;
> + struct sk_buff *skb;
> +
> + if (data->rxring[rx].misc & TSI108_RX_OWN)
> + break;
> +
> + skb =3D data->rxskbs[rx];
> + data->rxtail =3D (data->rxtail + 1) % TSI108_RXRING_LEN;
> + data->rxfree--;
> + done++;
> +
> + if (data->rxring[rx].misc & TSI108_RX_BAD) {
> + spin_lock_irq(&data->misclock);
> +
> + if (data->rxring[rx].misc & TSI108_RX_CRC)
> + data->stats.rx_crc_errors++;
> + if (data->rxring[rx].misc & TSI108_RX_OVER)
> + data->stats.rx_fifo_errors++;
> +
> + spin_unlock_irq(&data->misclock);
> +
> + dev_kfree_skb_any(skb);
> + continue;
> + }
> +#ifdef TSI108_PRINT_RX_FRAME
> + {
> + int i;
> + printk("%s: Rx Frame contents (%d)\n",
> + dev->name, data->rxring[rx].len);
> + for (i =3D 0; i < data->rxring[rx].len; i++)
> + printk(" %2.2x", skb->data[i]);
> + printk(".\n");
> + }
> +#endif /* TSI108_PRINT_RX_FRAME */
> +
> + skb->dev =3D dev;
> + skb_put(skb, data->rxring[rx].len);
> + skb->protocol =3D eth_type_trans(skb, dev);
> + netif_receive_skb(skb);
> + dev->last_rx =3D jiffies;
> + }
> +
> + return done;
> +}
> +
> +static int tsi108_refill_rx(net_device * dev, int budget)
> +{
> + tsi108_prv_data *data =3D netdev_priv(dev);
> + int done =3D 0;
> +
> + while (data->rxfree !=3D TSI108_RXRING_LEN && done !=3D budget) {
> + int rx =3D data->rxhead;
> + sk_buff *skb;
> +
> + data->rxskbs[rx] =3D skb =3D dev_alloc_skb(TSI108_RXBUF_SIZE + 2);
> + if (!skb)
> + break;
> +
> + skb_reserve(skb, 2); /* Align the data on a 4-byte boundary. */
> +
> + data->rxring[rx].buf0 =3D virt_to_phys(skb->data);
> +
> + /* Sometimes the hardware sets blen to zero after packet
> + * reception, even though the manual says that it's only ever
> + * modified by the driver.
> + */
> +
> + data->rxring[rx].blen =3D 1536;
> + mb();
> + data->rxring[rx].misc =3D TSI108_RX_OWN | TSI108_RX_INT;
> +
> + data->rxhead =3D (data->rxhead + 1) % TSI108_RXRING_LEN;
> + data->rxfree++;
> + done++;
> + }
> +
> + mb();
> +
> + if (done !=3D 0 && !(TSI108_ETH_READ_REG(TSI108_EC_RXSTAT) &
> + TSI108_EC_RXSTAT_QUEUE0))
> + tsi108_restart_rx(data, dev);
> +
> + return done;
> +}
> +
> +static int tsi108_poll(net_device * dev, int *budget)
> +{
> + tsi108_prv_data *data =3D netdev_priv(dev);
> + u32 estat =3D TSI108_ETH_READ_REG(TSI108_EC_RXESTAT);
> + u32 intstat =3D TSI108_ETH_READ_REG(TSI108_EC_INTSTAT);
> + int total_budget =3D min(*budget, dev->quota);
> + int num_received =3D 0, num_filled =3D 0, budget_used;
> +
> + intstat &=3D TSI108_INT_RXQUEUE0 | TSI108_INT_RXTHRESH |
> + TSI108_INT_RXOVERRUN | TSI108_INT_RXERROR | TSI108_INT_RXWAIT;
> +
> + TSI108_ETH_WRITE_REG(TSI108_EC_RXESTAT, estat);
> + TSI108_ETH_WRITE_REG(TSI108_EC_INTSTAT, intstat);
> +
> + if (data->rxpending || (estat & TSI108_EC_RXESTAT_Q0_DESCINT))
> + num_received =3D tsi108_check_for_completed_rx(dev, total_budget);
> +
> + /* This should normally fill no more slots than the number of
> + * packets received in tsi108_check_for_completed_rx(). The =20
> exception
> + * is when we previously ran out of memory for RX SKBs. In that
> + * case, it's helpful to obey the budget, not only so that the
> + * CPU isn't hogged, but so that memory (which may still be low)
> + * is not hogged by one device.
> + *
> + * A work unit is considered to be two SKBs to allow us to catch
> + * up when the ring has shrunk due to out-of-memory but we're
> + * still removing the full budget's worth of packets each time.
> + */
> +
> + if (data->rxfree < TSI108_RXRING_LEN)
> + num_filled =3D tsi108_refill_rx(dev, total_budget * 2);
> +
> + if (intstat & TSI108_INT_RXERROR) {
> + u32 err =3D TSI108_ETH_READ_REG(TSI108_EC_RXERR);
> + TSI108_ETH_WRITE_REG(TSI108_EC_RXERR, err);
> +
> + if (err) {
> + if (net_ratelimit())
> + printk(KERN_DEBUG "%s: RX error %x\n",
> + dev->name, err);
> +
> + if (!(TSI108_ETH_READ_REG(TSI108_EC_RXSTAT) &
> + TSI108_EC_RXSTAT_QUEUE0))
> + tsi108_restart_rx(data, dev);
> + }
> + }
> +
> + if (intstat & TSI108_INT_RXOVERRUN) {
> + spin_lock_irq(&data->misclock);
> + data->stats.rx_fifo_errors++;
> + spin_unlock_irq(&data->misclock);
> + }
> +
> + budget_used =3D max(num_received, num_filled / 2);
> +
> + *budget -=3D budget_used;
> + dev->quota -=3D budget_used;
> +
> + if (budget_used !=3D total_budget) {
> + data->rxpending =3D 0;
> + netif_rx_complete(dev);
> +
> + TSI108_ETH_WRITE_REG(TSI108_EC_INTMASK,
> + TSI108_ETH_READ_REG(TSI108_EC_INTMASK)
> + & ~(TSI108_INT_RXQUEUE0
> + | TSI108_INT_RXTHRESH |
> + TSI108_INT_RXOVERRUN |
> + TSI108_INT_RXERROR |
> + TSI108_INT_RXWAIT));
> +
> + mb();
> +
> + /* IRQs are level-triggered, so no need to re-check */
> + return 0;
> + } else {
> + data->rxpending =3D 1;
> + }
> +
> + return 1;
> +}
> +
> +static void tsi108_rx_int(net_device * dev)
> +{
> + tsi108_prv_data *data =3D netdev_priv(dev);
> +
> + /* A race could cause dev to already be scheduled, so it's not an
> + * error if that happens (and interrupts shouldn't be re-masked,
> + * because that can cause harmful races, if poll has already
> + * unmasked them but not cleared LINK_STATE_SCHED).
> + *
> + * This can happen if this code races with tsi108_poll(), which =20
> masks
> + * the interrupts after tsi108_irq_one() read the mask, but before
> + * netif_rx_schedule is called. It could also happen due to calls
> + * from tsi108_check_rxring().
> + */
> +
> + if (netif_rx_schedule_prep(dev)) {
> + /* Mask, rather than ack, the receive interrupts. The ack
> + * will happen in tsi108_poll().
> + */
> +
> + TSI108_ETH_WRITE_REG(TSI108_EC_INTMASK,
> + TSI108_ETH_READ_REG(TSI108_EC_INTMASK) |
> + TSI108_INT_RXQUEUE0
> + | TSI108_INT_RXTHRESH |
> + TSI108_INT_RXOVERRUN | TSI108_INT_RXERROR |
> + TSI108_INT_RXWAIT);
> + mb();
> + __netif_rx_schedule(dev);
> + } else {
> + if (!netif_running(dev)) {
> + /* This can happen if an interrupt occurs while the
> + * interface is being brought down, as the START
> + * bit is cleared before the stop function is called.
> + *
> + * In this case, the interrupts must be masked, or
> + * they will continue indefinitely.
> + *
> + * There's a race here if the interface is brought down
> + * and then up in rapid succession, as the device could
> + * be made running after the above check and before
> + * the masking below. This will only happen if the IRQ
> + * thread has a lower priority than the task brining
> + * up the interface. Fixing this race would likely
> + * require changes in generic code.
> + */
> +
> + TSI108_ETH_WRITE_REG(TSI108_EC_INTMASK,
> + TSI108_ETH_READ_REG
> + (TSI108_EC_INTMASK) |
> + TSI108_INT_RXQUEUE0 |
> + TSI108_INT_RXTHRESH |
> + TSI108_INT_RXOVERRUN |
> + TSI108_INT_RXERROR |
> + TSI108_INT_RXWAIT);
> + mb();
> + }
> + }
> +}
> +
> +/* If the RX ring has run out of memory, try periodically
> + * to allocate some more, as otherwise poll would never
> + * get called (apart from the initial end-of-queue condition).
> + *
> + * This is called once per second (by default) from the thread.
> + */
> +
> +static void tsi108_check_rxring(net_device * dev)
> +{
> + tsi108_prv_data *data =3D netdev_priv(dev);
> +
> + /* A poll is scheduled, as opposed to caling tsi108_refill_rx
> + * directly, so as to keep the receive path single-threaded
> + * (and thus not needing a lock).
> + */
> +
> + if (netif_running(dev) && data->rxfree < TSI108_RXRING_LEN / 4)
> + tsi108_rx_int(dev);
> +}
> +
> +static void tsi108_tx_int(net_device * dev)
> +{
> + tsi108_prv_data *data =3D netdev_priv(dev);
> + u32 estat =3D TSI108_ETH_READ_REG(TSI108_EC_TXESTAT);
> +
> + mb();
> + TSI108_ETH_WRITE_REG(TSI108_EC_TXESTAT, estat);
> + mb();
> + TSI108_ETH_WRITE_REG(TSI108_EC_INTSTAT, TSI108_INT_TXQUEUE0 |
> + TSI108_INT_TXIDLE | TSI108_INT_TXERROR);
> + mb();
> + if (estat & TSI108_EC_TXESTAT_Q0_ERR) {
> + u32 err =3D TSI108_ETH_READ_REG(TSI108_EC_TXERR);
> + TSI108_ETH_WRITE_REG(TSI108_EC_TXERR, err);
> +
> + if (err && net_ratelimit())
> + printk(KERN_ERR "%s: TX error %x\n", dev->name, err);
> + }
> +
> + if (estat & (TSI108_EC_TXESTAT_Q0_DESCINT | =20
> TSI108_EC_TXESTAT_Q0_EOQ)) {
> + spin_lock(&data->txlock);
> + tsi108_check_for_completed_tx(dev);
> + spin_unlock(&data->txlock);
> + }
> +}
> +
> +static irqreturn_t tsi108_irq_one(net_device * dev)
> +{
> + tsi108_prv_data *data =3D netdev_priv(dev);
> + u32 stat =3D TSI108_ETH_READ_REG(TSI108_EC_INTSTAT);
> +
> + if (!(stat & TSI108_INT_ANY))
> + return IRQ_NONE; /* Not our interrupt */
> +
> + stat &=3D ~TSI108_ETH_READ_REG(TSI108_EC_INTMASK);
> +
> + if (stat & (TSI108_INT_TXQUEUE0 | TSI108_INT_TXIDLE |
> + TSI108_INT_TXERROR))
> + tsi108_tx_int(dev);
> + if (stat & (TSI108_INT_RXQUEUE0 | TSI108_INT_RXTHRESH |
> + TSI108_INT_RXWAIT | TSI108_INT_RXOVERRUN |
> + TSI108_INT_RXERROR))
> + tsi108_rx_int(dev);
> +
> + if (stat & TSI108_INT_SFN) {
> + if (net_ratelimit())
> + printk(KERN_DEBUG "%s: SFN error\n", dev->name);
> + TSI108_ETH_WRITE_REG(TSI108_EC_INTSTAT, TSI108_INT_SFN);
> + }
> +
> + if (stat & TSI108_INT_STATCARRY) {
> + tsi108_stat_carry(dev);
> + TSI108_ETH_WRITE_REG(TSI108_EC_INTSTAT, TSI108_INT_STATCARRY);
> + }
> +
> + return IRQ_HANDLED;
> +}
> +
> +static irqreturn_t tsi108_irq(int irq, void *dev_id, struct =20
> pt_regs *regs)
> +{
> + if ((IRQ_TSI108_GIGE0 !=3D irq) && (IRQ_TSI108_GIGE1 !=3D irq))
> + return IRQ_NONE; /* Not our interrupt */
> +
> + return tsi108_irq_one((struct net_device *)dev_id);
> +}
> +
> +static void tsi108_stop_ethernet(net_device * dev)
> +{
> + tsi108_prv_data *data =3D netdev_priv(dev);
> +
> + /* Disable all TX and RX queues ... */
> + TSI108_ETH_WRITE_REG(TSI108_EC_TXCTRL, 0);
> + TSI108_ETH_WRITE_REG(TSI108_EC_RXCTRL, 0);
> +
> + /* ...and wait for them to become idle */
> + mb();
> + while (TSI108_ETH_READ_REG(TSI108_EC_TXSTAT) &
> + TSI108_EC_TXSTAT_ACTIVE) ;
> + while (TSI108_ETH_READ_REG(TSI108_EC_RXSTAT) &
> + TSI108_EC_RXSTAT_ACTIVE) ;
> +}
> +
> +static void tsi108_reset_ether(tsi108_prv_data * data)
> +{
> + TSI108_ETH_WRITE_REG(TSI108_MAC_CFG1, TSI108_MAC_CFG1_SOFTRST);
> + udelay(100);
> + TSI108_ETH_WRITE_REG(TSI108_MAC_CFG1, 0);
> +
> + TSI108_ETH_WRITE_REG(TSI108_EC_PORTCTRL, =20
> TSI108_EC_PORTCTRL_STATRST);
> + udelay(100);
> + TSI108_ETH_WRITE_REG(TSI108_EC_PORTCTRL,
> + TSI108_ETH_READ_REG(TSI108_EC_PORTCTRL) &
> + ~TSI108_EC_PORTCTRL_STATRST);
> +
> + TSI108_ETH_WRITE_REG(TSI108_EC_TXCFG, TSI108_EC_TXCFG_RST);
> + udelay(100);
> + TSI108_ETH_WRITE_REG(TSI108_EC_TXCFG,
> + TSI108_ETH_READ_REG(TSI108_EC_TXCFG) &
> + ~TSI108_EC_TXCFG_RST);
> +
> + TSI108_ETH_WRITE_REG(TSI108_EC_RXCFG, TSI108_EC_RXCFG_RST);
> + udelay(100);
> + TSI108_ETH_WRITE_REG(TSI108_EC_RXCFG,
> + TSI108_ETH_READ_REG(TSI108_EC_RXCFG) &
> + ~TSI108_EC_RXCFG_RST);
> +
> + TSI108_ETH_WRITE_REG(TSI108_MAC_MII_MGMT_CFG,
> + TSI108_ETH_READ_REG(TSI108_MAC_MII_MGMT_CFG) |
> + TSI108_MAC_MII_MGMT_RST);
> + udelay(100);
> + TSI108_ETH_WRITE_REG(TSI108_MAC_MII_MGMT_CFG,
> + TSI108_ETH_READ_REG(TSI108_MAC_MII_MGMT_CFG) &
> + ~(TSI108_MAC_MII_MGMT_RST |
> + TSI108_MAC_MII_MGMT_CLK));
> +
> + TSI108_ETH_WRITE_REG(TSI108_MAC_MII_MGMT_CFG,
> + TSI108_ETH_READ_REG(TSI108_MAC_MII_MGMT_CFG) |
> + TSI108_MAC_MII_MGMT_CLK);
> +}
> +
> +static int tsi108_get_mac(net_device * dev)
> +{
> + tsi108_prv_data *data =3D netdev_priv(dev);
> +
> + u32 word1 =3D TSI108_ETH_READ_REG(TSI108_MAC_ADDR1);
> + u32 word2 =3D TSI108_ETH_READ_REG(TSI108_MAC_ADDR2);
> +
> + /* Note that the octets are reversed from what the manual says,
> + * producing an even weirder ordering...
> + */
> + if (word2 =3D=3D 0 && word1 =3D=3D 0) {
> + dev->dev_addr[0] =3D 0x00;
> + dev->dev_addr[1] =3D 0x06;
> + dev->dev_addr[2] =3D 0xd2;
> + dev->dev_addr[3] =3D 0x00;
> + dev->dev_addr[4] =3D 0x00;
> + if (0x8 =3D=3D data->phy)
> + dev->dev_addr[5] =3D 0x01;
> + else
> + dev->dev_addr[5] =3D 0x02;
> +
> + word2 =3D (dev->dev_addr[0] << 16) | (dev->dev_addr[1] << 24);
> +
> + word1 =3D (dev->dev_addr[2] << 0) | (dev->dev_addr[3] << 8) |
> + (dev->dev_addr[4] << 16) | (dev->dev_addr[5] << 24);
> +
> + TSI108_ETH_WRITE_REG(TSI108_MAC_ADDR1, word1);
> + TSI108_ETH_WRITE_REG(TSI108_MAC_ADDR2, word2);
> + } else {
> + dev->dev_addr[0] =3D (word2 >> 16) & 0xff;
> + dev->dev_addr[1] =3D (word2 >> 24) & 0xff;
> + dev->dev_addr[2] =3D (word1 >> 0) & 0xff;
> + dev->dev_addr[3] =3D (word1 >> 8) & 0xff;
> + dev->dev_addr[4] =3D (word1 >> 16) & 0xff;
> + dev->dev_addr[5] =3D (word1 >> 24) & 0xff;
> + }
> +
> + if (!is_valid_ether_addr(dev->dev_addr)) {
> + printk("KERN_ERR: word1: %08x, word2: %08x\n", word1, word2);
> + return -EINVAL;
> + }
> +
> + return 0;
> +}
> +
> +static int tsi108_set_mac(net_device * dev, void *addr)
> +{
> + tsi108_prv_data *data =3D netdev_priv(dev);
> + u32 word1, word2;
> + int i;
> +
> + if (!is_valid_ether_addr(addr))
> + return -EINVAL;
> +
> + for (i =3D 0; i < 6; i++)
> + /* +2 is for the offset of the HW addr type */
> + dev->dev_addr[i] =3D ((unsigned char *)addr)[i + 2];
> +
> + word2 =3D (dev->dev_addr[0] << 16) | (dev->dev_addr[1] << 24);
> +
> + word1 =3D (dev->dev_addr[2] << 0) | (dev->dev_addr[3] << 8) |
> + (dev->dev_addr[4] << 16) | (dev->dev_addr[5] << 24);
> +
> + spin_lock_irq(&data->misclock);
> + TSI108_ETH_WRITE_REG(TSI108_MAC_ADDR1, word1);
> + TSI108_ETH_WRITE_REG(TSI108_MAC_ADDR2, word2);
> + spin_lock(&data->txlock);
> +
> + if (netif_queue_stopped(dev) && data->txfree && data->link_up)
> + netif_wake_queue(dev);
> +
> + spin_unlock(&data->txlock);
> + spin_unlock_irq(&data->misclock);
> + return 0;
> +}
> +
> +/* Protected by dev->xmit_lock. */
> +static void tsi108_set_rx_mode(net_device * dev)
> +{
> + tsi108_prv_data *data =3D netdev_priv(dev);
> + u32 rxcfg =3D TSI108_ETH_READ_REG(TSI108_EC_RXCFG);
> +
> + if (dev->flags & IFF_PROMISC) {
> + rxcfg &=3D ~(TSI108_EC_RXCFG_UC_HASH | TSI108_EC_RXCFG_MC_HASH);
> + rxcfg |=3D TSI108_EC_RXCFG_UFE | TSI108_EC_RXCFG_MFE;
> + goto out;
> + }
> +
> + rxcfg &=3D ~(TSI108_EC_RXCFG_UFE | TSI108_EC_RXCFG_MFE);
> +
> + if (dev->mc_count) {
> + int i;
> + struct dev_mc_list *mc =3D dev->mc_list;
> + rxcfg |=3D TSI108_EC_RXCFG_MFE | TSI108_EC_RXCFG_MC_HASH;
> +
> + memset(data->mc_hash, 0, sizeof(data->mc_hash));
> +
> + while (mc) {
> + u32 hash, crc;
> +
> + if (mc->dmi_addrlen =3D=3D 6) {
> + crc =3D ether_crc(6, mc->dmi_addr);
> + hash =3D crc >> 23;
> +
> + __set_bit(hash, &data->mc_hash[0]);
> + } else {
> + printk(KERN_ERR
> + "%s: got multicast address of length %d "
> + "instead of 6.\n", dev->name,
> + mc->dmi_addrlen);
> + }
> +
> + mc =3D mc->next;
> + }
> +
> + TSI108_ETH_WRITE_REG(TSI108_EC_HASHADDR,
> + TSI108_EC_HASHADDR_AUTOINC |
> + TSI108_EC_HASHADDR_MCAST);
> +
> + for (i =3D 0; i < 16; i++) {
> + /* The manual says that the hardware may drop
> + * back-to-back writes to the data register.
> + */
> + udelay(1);
> + mb();
> + TSI108_ETH_WRITE_REG(TSI108_EC_HASHDATA,
> + data->mc_hash[i]);
> + mb();
> + }
> + }
> +
> + out:
> + mb();
> + TSI108_ETH_WRITE_REG(TSI108_EC_RXCFG, rxcfg);
> +}
> +
> +static void tsi108_init_phy(net_device * dev)
> +{
> + tsi108_prv_data *data =3D netdev_priv(dev);
> + u32 i =3D 0;
> + u16 phyVal =3D 0;
> +
> + spin_lock_irq(&phy_lock);
> +
> + tsi108_write_mii(data, PHY_CTRL, PHY_CTRL_RESET);
> + mb();
> + while (tsi108_read_mii(data, PHY_CTRL, NULL) & PHY_CTRL_RESET) ;
> +
> +#if (TSI108_PHY_TYPE =3D=3D PHY_BCM54XX) /* Broadcom BCM54xx PHY */
> + tsi108_write_mii(data, 0x09, 0x0300);
> + tsi108_write_mii(data, 0x10, 0x1020);
> + tsi108_write_mii(data, 0x1c, 0x8c00);
> + mb();
> +#endif
> +
> + tsi108_write_mii(data,
> + PHY_CTRL,
> + PHY_CTRL_AUTONEG_EN | PHY_CTRL_AUTONEG_START);
> + mb();
> + while (tsi108_read_mii(data, PHY_CTRL, NULL) & =20
> PHY_CTRL_AUTONEG_START) ;
> +
> + /* Set G/MII mode and receive clock select in TBI control #2. The
> + * second port won't work if this isn't done, even though we don't
> + * use TBI mode.
> + */
> +
> + tsi108_write_tbi(data, 0x11, 0x30);
> +
> + /* FIXME: It seems to take more than 2 back-to-back reads to the
> + * PHY_STAT register before the link up status bit is set.
> + */
> +
> + data->link_up =3D 1;
> +
> + while (!((phyVal =3D tsi108_read_mii(data, PHY_STAT, NULL)) &
> + PHY_STAT_LINKUP)) {
> + if (i++ > (MII_READ_DELAY / 10)) {
> + data->link_up =3D 0;
> + break;
> + }
> + mdelay(10);
> + }
> +
> + printk(KERN_DEBUG "PHY_STAT reg contains %08x\n", phyVal);
> + data->phy_ok =3D 1;
> + spin_unlock_irq(&phy_lock);
> +}
> +
> +static void tsi108_kill_phy(struct net_device *dev)
> +{
> + tsi108_prv_data *data =3D netdev_priv(dev);
> +
> + spin_lock_irq(&phy_lock);
> + tsi108_write_mii(data, PHY_CTRL, PHY_CTRL_POWERDOWN);
> + data->phy_ok =3D 0;
> + spin_unlock_irq(&phy_lock);
> +}
> +
> +static int tsi108_open(struct net_device *dev)
> +{
> + int i;
> + tsi108_prv_data *data =3D netdev_priv(dev);
> + unsigned int rxring_size =3D TSI108_RXRING_LEN * sizeof(rx_desc);
> + unsigned int txring_size =3D TSI108_TXRING_LEN * sizeof(tx_desc);
> +
> + printk(KERN_DEBUG "Inside tsi108_open()!\n");
> +
> + i =3D request_irq(data->irq_num, tsi108_irq, 0, dev->name, dev);
> + if (i !=3D 0) {
> + printk(KERN_ERR "tsi108_eth%d: Could not allocate IRQ%d.\n",
> + data->irq_num % IRQ_TSI108_GIGE0, data->irq_num);
> + return i;
> + } else {
> + dev->irq =3D data->irq_num;
> + printk(KERN_NOTICE
> + "tsi108_open : Port %d Assigned IRQ %d to %s\n",
> + data->irq_num % IRQ_TSI108_GIGE0, dev->irq, dev->name);
> + }
> +
> + data->rxring =3D pci_alloc_consistent(NULL, rxring_size, &data-=20
> >rxdma);
> +
> + if (!data->rxring) {
> + printk(KERN_DEBUG
> + "TSI108_ETH: failed to allocate memory for rxring!\n");
> + return -ENOMEM;
> + } else {
> + memset(data->rxring, 0, rxring_size);
> + }
> +
> + data->txring =3D pci_alloc_consistent(NULL, txring_size, &data-=20
> >txdma);
> +
> + if (!data->txring) {
> + printk(KERN_DEBUG
> + "TSI108_ETH: failed to allocate memory for txring!\n");
> + pci_free_consistent(0, rxring_size, data->rxring, data->rxdma);
> + return -ENOMEM;
> + } else {
> + memset(data->txring, 0, txring_size);
> + }
> +
> + for (i =3D 0; i < TSI108_RXRING_LEN; i++) {
> + data->rxring[i].next0 =3D data->rxdma + (i + 1) * sizeof(rx_desc);
> + data->rxring[i].blen =3D TSI108_RXBUF_SIZE;
> + data->rxring[i].vlan =3D 0;
> + }
> +
> + data->rxring[TSI108_RXRING_LEN - 1].next0 =3D data->rxdma;
> +
> + data->rxtail =3D 0;
> + data->rxhead =3D 0;
> +
> + for (i =3D 0; i < TSI108_RXRING_LEN; i++) {
> + sk_buff *skb =3D dev_alloc_skb(TSI108_RXBUF_SIZE + NET_IP_ALIGN);
> +
> + if (!skb) {
> + /* Bah. No memory for now, but maybe we'll get
> + * some more later.
> + * For now, we'll live with the smaller ring.
> + */
> + printk(KERN_WARNING
> + "%s: Could only allocate %d receive skb(s).\n",
> + dev->name, i);
> + data->rxhead =3D i;
> + break;
> + }
> +
> + data->rxskbs[i] =3D skb;
> + /* Align the payload on a 4-byte boundary */
> + skb_reserve(skb, 2);
> + data->rxskbs[i] =3D skb;
> + data->rxring[i].buf0 =3D virt_to_phys(data->rxskbs[i]->data);
> + data->rxring[i].misc =3D TSI108_RX_OWN | TSI108_RX_INT;
> + }
> +
> + data->rxfree =3D i;
> + TSI108_ETH_WRITE_REG(TSI108_EC_RXQ_PTRLOW, data->rxdma);
> +
> + for (i =3D 0; i < TSI108_TXRING_LEN; i++) {
> + data->txring[i].next0 =3D data->txdma + (i + 1) * sizeof(tx_desc);
> + data->txring[i].misc =3D 0;
> + }
> +
> + data->txring[TSI108_TXRING_LEN - 1].next0 =3D data->txdma;
> + data->txtail =3D 0;
> + data->txhead =3D 0;
> + data->txfree =3D TSI108_TXRING_LEN;
> + TSI108_ETH_WRITE_REG(TSI108_EC_TXQ_PTRLOW, data->txdma);
> + tsi108_init_phy(dev);
> +
> + init_timer(&data->timer);
> + data->timer.expires =3D jiffies + 1;
> + data->timer.data =3D (unsigned long)dev;
> + data->timer.function =3D &tsi108_timed_checker; /* timer handler */
> + add_timer(&data->timer);
> +
> + tsi108_restart_rx(data, dev);
> +
> + TSI108_ETH_WRITE_REG(TSI108_EC_INTSTAT, ~0);
> + mb();
> +
> + TSI108_ETH_WRITE_REG(TSI108_EC_INTMASK,
> + ~(TSI108_INT_TXQUEUE0 | TSI108_INT_RXERROR |
> + TSI108_INT_RXTHRESH | TSI108_INT_RXQUEUE0 |
> + TSI108_INT_RXOVERRUN | TSI108_INT_RXWAIT |
> + TSI108_INT_SFN | TSI108_INT_STATCARRY));
> +
> + TSI108_ETH_WRITE_REG(TSI108_MAC_CFG1,
> + TSI108_MAC_CFG1_RXEN | TSI108_MAC_CFG1_TXEN);
> + netif_start_queue(dev);
> + return 0;
> +}
> +
> +static int tsi108_close(net_device * dev)
> +{
> + tsi108_prv_data *data =3D netdev_priv(dev);
> +
> + netif_stop_queue(dev);
> +
> + del_timer_sync(&data->timer);
> +
> + printk(KERN_DEBUG "Inside tsi108_ifdown!\n");
> +
> + tsi108_stop_ethernet(dev);
> + tsi108_kill_phy(dev);
> + TSI108_ETH_WRITE_REG(TSI108_EC_INTMASK, ~0);
> + TSI108_ETH_WRITE_REG(TSI108_MAC_CFG1, 0);
> +
> + /* Check for any pending TX packets, and drop them. */
> +
> + while (!data->txfree || data->txhead !=3D data->txtail) {
> + int tx =3D data->txtail;
> + struct sk_buff *skb;
> + skb =3D data->txskbs[tx];
> + data->txtail =3D (data->txtail + 1) % TSI108_TXRING_LEN;
> + data->txfree++;
> + dev_kfree_skb(skb);
> + }
> +
> + synchronize_irq(data->irq_num);
> + free_irq(data->irq_num, dev);
> +
> + /* Discard the RX ring. */
> +
> + while (data->rxfree) {
> + int rx =3D data->rxtail;
> + struct sk_buff *skb;
> +
> + skb =3D data->rxskbs[rx];
> + data->rxtail =3D (data->rxtail + 1) % TSI108_RXRING_LEN;
> + data->rxfree--;
> + dev_kfree_skb(skb);
> + }
> +
> + pci_free_consistent(0,
> + TSI108_RXRING_LEN * sizeof(rx_desc),
> + data->rxring, data->rxdma);
> + pci_free_consistent(0,
> + TSI108_TXRING_LEN * sizeof(tx_desc),
> + data->txring, data->txdma);
> +
> + return 0;
> +}
> +
> +static void tsi108_init_mac(net_device * dev)
> +{
> + tsi108_prv_data *data =3D netdev_priv(dev);
> +
> + TSI108_ETH_WRITE_REG(TSI108_MAC_CFG2, =20
> TSI108_MAC_CFG2_DFLT_PREAMBLE |
> + TSI108_MAC_CFG2_PADCRC);
> +
> + TSI108_ETH_WRITE_REG(TSI108_EC_TXTHRESH,
> + (192 << TSI108_EC_TXTHRESH_STARTFILL) |
> + (192 << TSI108_EC_TXTHRESH_STOPFILL));
> +
> + TSI108_ETH_WRITE_REG(TSI108_STAT_CARRYMASK1,
> + ~(TSI108_STAT_CARRY1_RXBYTES |
> + TSI108_STAT_CARRY1_RXPKTS |
> + TSI108_STAT_CARRY1_RXFCS |
> + TSI108_STAT_CARRY1_RXMCAST |
> + TSI108_STAT_CARRY1_RXALIGN |
> + TSI108_STAT_CARRY1_RXLENGTH |
> + TSI108_STAT_CARRY1_RXRUNT |
> + TSI108_STAT_CARRY1_RXJUMBO |
> + TSI108_STAT_CARRY1_RXFRAG |
> + TSI108_STAT_CARRY1_RXJABBER |
> + TSI108_STAT_CARRY1_RXDROP));
> +
> + TSI108_ETH_WRITE_REG(TSI108_STAT_CARRYMASK2,
> + ~(TSI108_STAT_CARRY2_TXBYTES |
> + TSI108_STAT_CARRY2_TXPKTS |
> + TSI108_STAT_CARRY2_TXEXDEF |
> + TSI108_STAT_CARRY2_TXEXCOL |
> + TSI108_STAT_CARRY2_TXTCOL |
> + TSI108_STAT_CARRY2_TXPAUSE));
> +
> + TSI108_ETH_WRITE_REG(TSI108_EC_PORTCTRL, =
TSI108_EC_PORTCTRL_STATEN);
> + TSI108_ETH_WRITE_REG(TSI108_MAC_CFG1, 0);
> +
> + TSI108_ETH_WRITE_REG(TSI108_EC_RXCFG,
> + TSI108_EC_RXCFG_SE | TSI108_EC_RXCFG_BFE);
> +
> + TSI108_ETH_WRITE_REG(TSI108_EC_TXQ_CFG, TSI108_EC_TXQ_CFG_DESC_INT =
|
> + TSI108_EC_TXQ_CFG_EOQ_OWN_INT |
> + TSI108_EC_TXQ_CFG_WSWP | (TSI108_PBM_PORT <<
> + TSI108_EC_TXQ_CFG_SFNPORT));
> +
> + TSI108_ETH_WRITE_REG(TSI108_EC_RXQ_CFG, TSI108_EC_RXQ_CFG_DESC_INT =
|
> + TSI108_EC_RXQ_CFG_EOQ_OWN_INT |
> + TSI108_EC_RXQ_CFG_WSWP | (TSI108_PBM_PORT <<
> + TSI108_EC_RXQ_CFG_SFNPORT));
> +
> + TSI108_ETH_WRITE_REG(TSI108_EC_TXQ_BUFCFG,
> + TSI108_EC_TXQ_BUFCFG_BURST256 |
> + TSI108_EC_TXQ_BUFCFG_BSWP | (TSI108_PBM_PORT <<
> + TSI108_EC_TXQ_BUFCFG_SFNPORT));
> +
> + TSI108_ETH_WRITE_REG(TSI108_EC_RXQ_BUFCFG,
> + TSI108_EC_RXQ_BUFCFG_BURST256 |
> + TSI108_EC_RXQ_BUFCFG_BSWP | (TSI108_PBM_PORT <<
> + TSI108_EC_RXQ_BUFCFG_SFNPORT));
> +
> + TSI108_ETH_WRITE_REG(TSI108_EC_INTMASK, ~0);
> +}
> +
> +static int tsi108_ioctl(net_device * dev, struct ifreq *rq, int cmd)
> +{
> + tsi108_prv_data *data =3D netdev_priv(dev);
> + struct mii_ioctl_data *mii_data =3D
> + (struct mii_ioctl_data *)&rq->ifr_data;
> + int ret;
> +
> + switch (cmd) {
> + case SIOCGMIIPHY:
> + mii_data->phy_id =3D data->phy;
> + ret =3D 0;
> + break;
> +
> + case SIOCGMIIREG:
> + spin_lock_irq(&phy_lock);
> + mii_data->val_out =3D
> + tsi108_read_mii(data, mii_data->reg_num, &ret);
> + spin_unlock_irq(&phy_lock);
> + break;
> +
> + default:
> + ret =3D -EOPNOTSUPP;
> + }
> +
> + return ret;
> +}
> +
> +static int
> +tsi108_init_one(unsigned long regs, unsigned long phyregs, u16 =20
> phy, u16 irq_num)
> +{
> + net_device *dev =3D alloc_etherdev(sizeof(tsi108_prv_data));
> + tsi108_prv_data *data;
> + int ret;
> +
> + if (!dev) {
> + printk("tsi108_eth: Could not allocate a device structure\n");
> + return -ENOMEM;
> + }
> +
> + data =3D netdev_priv(dev);
> + memset(data, 0, sizeof(tsi108_prv_data));
> +
> + data->regs =3D (volatile u32)regs;
> + data->phyregs =3D (volatile u32)phyregs;
> + data->phy =3D phy;
> + data->irq_num =3D irq_num;
> +
> + dev->open =3D tsi108_open;
> + dev->stop =3D tsi108_close;
> + dev->hard_start_xmit =3D tsi108_send_packet;
> + dev->set_mac_address =3D tsi108_set_mac;
> + dev->set_multicast_list =3D tsi108_set_rx_mode;
> + dev->get_stats =3D tsi108_get_stats;
> + dev->poll =3D tsi108_poll;
> + dev->do_ioctl =3D tsi108_ioctl;
> + dev->weight =3D 64; /* 64 is more suitable for GigE interface - =20
> klai */
> +
> + /* Apparently, the Linux networking code won't use scatter-gather
> + * if the hardware doesn't do checksums. However, it's faster
> + * to checksum in place and use SG, as (among other reasons)
> + * the cache won't be dirtied (which then has to be flushed
> + * before DMA). The checksumming is done by the driver (via
> + * a new function skb_csum_dev() in net/core/skbuff.c).
> + */
> +
> +#ifdef FIXME_SG_CSUM_NOT_TESTED /* FIXME: Not supported now. */
> + dev->features =3D NETIF_F_HIGHDMA | NETIF_F_SG | NETIF_F_HW_CSUM;
> +#else
> + dev->features =3D NETIF_F_HIGHDMA;
> +#endif
> + SET_MODULE_OWNER(dev);
> +
> + spin_lock_init(&data->txlock);
> + spin_lock_init(&data->misclock);
> +
> + tsi108_reset_ether(data);
> + tsi108_kill_phy(dev);
> +
> + if (tsi108_get_mac(dev) !=3D 0)
> + printk(KERN_ERR "%s: Invalid MAC address. Please correct.\n",
> + dev->name);
> +
> + tsi108_init_mac(dev);
> +
> + tsi108_devs[irq_num % IRQ_TSI108_GIGE0] =3D dev;
> +
> + ret =3D register_netdev(dev);
> + if (ret < 0) {
> + kfree(dev);
> + return ret;
> + }
> +
> + printk(KERN_INFO "%s: Tsi108 Gigabit Ethernet, MAC: "
> + "%02x:%02x:%02x:%02x:%02x:%02x\n", dev->name,
> + dev->dev_addr[0], dev->dev_addr[1], dev->dev_addr[2],
> + dev->dev_addr[3], dev->dev_addr[4], dev->dev_addr[5]);
> +
> + return 0;
> +}
> +
> +/* There's no way to either get interrupts from the PHY when
> + * something changes, or to have the Tsi108 automatically =
communicate
> + * with the PHY to reconfigure itself.
> + *
> + * Thus, we have to do it using a timer.
> + */
> +
> +static void tsi108_timed_checker(unsigned long dev_ptr)
> +{
> + struct net_device *dev =3D (struct net_device *)dev_ptr;
> + tsi108_prv_data *data =3D netdev_priv(dev);
> +
> + tsi108_check_phy(dev);
> + tsi108_check_rxring(dev);
> + data->timer.expires =3D jiffies + CHECK_PHY_INTERVAL;
> + add_timer(&data->timer);
> +}
> +
> +static int tsi108_ether_init(void)
> +{
> + int ret;
> + int dev_count =3D 0;
> + int i;
> +
> + hw_info_table[0].regs =3D (u32) ioremap(hw_info_table[0].regs, =
0x400);
> + hw_info_table[0].phyregs =3D hw_info_table[0].regs;
> +
> + hw_info_table[1].regs =3D (u32) ioremap(hw_info_table[1].regs, =
0x400);
> + hw_info_table[1].phyregs =3D hw_info_table[0].regs;
> +
> + for (i =3D 0; hw_info_table[i].regs !=3D TBL_END; i++) {
> + ret =3D tsi108_init_one(hw_info_table[i].regs,
> + hw_info_table[i].phyregs,
> + hw_info_table[i].phy,
> + hw_info_table[i].irq_num);
> + if (ret < 0)
> + printk("tsi108_ether_init: error initializing ethernet "
> + "device%d\n", i);
> + else
> + dev_count++;
> + }
> +
> + printk("tsi108_ether_init: found %d device(s)\n", dev_count);
> +
> + return 0;
> +}
> +
> +static void tsi108_ether_exit(void)
> +{
> + int i;
> + net_device *dev;
> +
> + for (i =3D 0; hw_info_table[i].regs !=3D TBL_END; i++) {
> + if ((dev =3D tsi108_devs[i]) !=3D NULL) {
> + unregister_netdev(dev);
> + tsi108_stop_ethernet(dev);
> + kfree(dev);
> + tsi108_devs[i] =3D NULL;
> + }
> + }
> +}
> +
> +module_init(tsi108_ether_init);
> +module_exit(tsi108_ether_exit);
> +
> +MODULE_AUTHOR("Tundra Semiconductor Corporation");
> +MODULE_DESCRIPTION("Tsi108 Gigabit Ethernet driver");
> +MODULE_LICENSE("GPL");
> diff --git a/drivers/net/tsi108_eth.h b/drivers/net/tsi108_eth.h
> new file mode 100644
> index 0000000..cb54f92
> --- /dev/null
> +++ b/drivers/net/tsi108_eth.h
> @@ -0,0 +1,404 @@
> +/*
> + * (C) Copyright 2005 Tundra Semiconductor Corp.
> + * Kong Lai, <kong.lai@tundra.com).
> + *
> + * See file CREDITS for list of people who contributed to this
> + * project.
> + *
> + * This program is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU General Public License as
> + * published by the Free Software Foundation; either version 2 of
> + * the License, or (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, write to the Free Software
> + * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
> + * MA 02111-1307 USA
> + */
> +
> +/*
> + * net/tsi108_eth.h - definitions for Tsi108 GIGE network =
controller.
> + */
> +
> +#ifndef __TSI108_ETH_H
> +#define __TSI108_ETH_H
> +
> +#include <linux/config.h>
> +#include <linux/types.h>
> +
> +#define TSI108_ETH_WRITE_REG(offset, val) \
> + out_be32((volatile u32 *)(data->regs + offset), val)
> +
> +#define TSI108_ETH_READ_REG(offset) \
> + in_be32((volatile u32 *)(data->regs + offset))
> +
> +#define TSI108_ETH_WRITE_PHYREG(offset, val) \
> + out_be32((volatile u32 *)(data->phyregs + offset), val)
> +
> +#define TSI108_ETH_READ_PHYREG(offset) \
> + in_be32((volatile u32 *)(data->phyregs + offset))
> +
> +/*
> + * PHY Configuration Options
> + *
> + * NOTE: Enable set of definitions corresponding to your board type
> + */
> +#define PHY_MV88E 1 /* Marvel 88Exxxx PHY */
> +#define PHY_BCM54XX 2 /* Broardcom BCM54xx PHY */
> +#define TSI108_PHY_TYPE PHY_MV88E
> +
> +/*
> + * TSI108 GIGE port registers
> + */
> +
> +#define TSI108_ETH_PORT_NUM 2
> +#define TSI108_PBM_PORT 2
> +#define TSI108_SDRAM_PORT 4
> +
> +#define TSI108_MAC_CFG1 (0x000)
> +#define TSI108_MAC_CFG1_SOFTRST (1 << 31)
> +#define TSI108_MAC_CFG1_LOOPBACK (1 << 8)
> +#define TSI108_MAC_CFG1_RXEN (1 << 2)
> +#define TSI108_MAC_CFG1_TXEN (1 << 0)
> +
> +#define TSI108_MAC_CFG2 (0x004)
> +#define TSI108_MAC_CFG2_DFLT_PREAMBLE (7 << 12)
> +#define TSI108_MAC_CFG2_IFACE_MASK (3 << 8)
> +#define TSI108_MAC_CFG2_NOGIG (1 << 8)
> +#define TSI108_MAC_CFG2_GIG (2 << 8)
> +#define TSI108_MAC_CFG2_PADCRC (1 << 2)
> +#define TSI108_MAC_CFG2_FULLDUPLEX (1 << 0)
> +
> +#define TSI108_MAC_MII_MGMT_CFG (0x020)
> +#define TSI108_MAC_MII_MGMT_CLK (7 << 0)
> +#define TSI108_MAC_MII_MGMT_RST (1 << 31)
> +
> +#define TSI108_MAC_MII_CMD (0x024)
> +#define TSI108_MAC_MII_CMD_READ (1 << 0)
> +
> +#define TSI108_MAC_MII_ADDR (0x028)
> +#define TSI108_MAC_MII_ADDR_REG 0
> +#define TSI108_MAC_MII_ADDR_PHY 8
> +
> +#define TSI108_MAC_MII_DATAOUT (0x02c)
> +#define TSI108_MAC_MII_DATAIN (0x030)
> +
> +#define TSI108_MAC_MII_IND (0x034)
> +#define TSI108_MAC_MII_IND_NOTVALID (1 << 2)
> +#define TSI108_MAC_MII_IND_SCANNING (1 << 1)
> +#define TSI108_MAC_MII_IND_BUSY (1 << 0)
> +
> +#define TSI108_MAC_IFCTRL (0x038)
> +#define TSI108_MAC_IFCTRL_PHYMODE (1 << 24)
> +
> +#define TSI108_MAC_ADDR1 (0x040)
> +#define TSI108_MAC_ADDR2 (0x044)
> +
> +#define TSI108_STAT_RXBYTES (0x06c)
> +#define TSI108_STAT_RXBYTES_CARRY (1 << 24)
> +
> +#define TSI108_STAT_RXPKTS (0x070)
> +#define TSI108_STAT_RXPKTS_CARRY (1 << 18)
> +
> +#define TSI108_STAT_RXFCS (0x074)
> +#define TSI108_STAT_RXFCS_CARRY (1 << 12)
> +
> +#define TSI108_STAT_RXMCAST (0x078)
> +#define TSI108_STAT_RXMCAST_CARRY (1 << 18)
> +
> +#define TSI108_STAT_RXALIGN (0x08c)
> +#define TSI108_STAT_RXALIGN_CARRY (1 << 12)
> +
> +#define TSI108_STAT_RXLENGTH (0x090)
> +#define TSI108_STAT_RXLENGTH_CARRY (1 << 12)
> +
> +#define TSI108_STAT_RXRUNT (0x09c)
> +#define TSI108_STAT_RXRUNT_CARRY (1 << 12)
> +
> +#define TSI108_STAT_RXJUMBO (0x0a0)
> +#define TSI108_STAT_RXJUMBO_CARRY (1 << 12)
> +
> +#define TSI108_STAT_RXFRAG (0x0a4)
> +#define TSI108_STAT_RXFRAG_CARRY (1 << 12)
> +
> +#define TSI108_STAT_RXJABBER (0x0a8)
> +#define TSI108_STAT_RXJABBER_CARRY (1 << 12)
> +
> +#define TSI108_STAT_RXDROP (0x0ac)
> +#define TSI108_STAT_RXDROP_CARRY (1 << 12)
> +
> +#define TSI108_STAT_TXBYTES (0x0b0)
> +#define TSI108_STAT_TXBYTES_CARRY (1 << 24)
> +
> +#define TSI108_STAT_TXPKTS (0x0b4)
> +#define TSI108_STAT_TXPKTS_CARRY (1 << 18)
> +
> +#define TSI108_STAT_TXEXDEF (0x0c8)
> +#define TSI108_STAT_TXEXDEF_CARRY (1 << 12)
> +
> +#define TSI108_STAT_TXEXCOL (0x0d8)
> +#define TSI108_STAT_TXEXCOL_CARRY (1 << 12)
> +
> +#define TSI108_STAT_TXTCOL (0x0dc)
> +#define TSI108_STAT_TXTCOL_CARRY (1 << 13)
> +
> +#define TSI108_STAT_TXPAUSEDROP (0x0e4)
> +#define TSI108_STAT_TXPAUSEDROP_CARRY (1 << 12)
> +
> +#define TSI108_STAT_CARRY1 (0x100)
> +#define TSI108_STAT_CARRY1_RXBYTES (1 << 16)
> +#define TSI108_STAT_CARRY1_RXPKTS (1 << 15)
> +#define TSI108_STAT_CARRY1_RXFCS (1 << 14)
> +#define TSI108_STAT_CARRY1_RXMCAST (1 << 13)
> +#define TSI108_STAT_CARRY1_RXALIGN (1 << 8)
> +#define TSI108_STAT_CARRY1_RXLENGTH (1 << 7)
> +#define TSI108_STAT_CARRY1_RXRUNT (1 << 4)
> +#define TSI108_STAT_CARRY1_RXJUMBO (1 << 3)
> +#define TSI108_STAT_CARRY1_RXFRAG (1 << 2)
> +#define TSI108_STAT_CARRY1_RXJABBER (1 << 1)
> +#define TSI108_STAT_CARRY1_RXDROP (1 << 0)
> +
> +#define TSI108_STAT_CARRY2 (0x104)
> +#define TSI108_STAT_CARRY2_TXBYTES (1 << 13)
> +#define TSI108_STAT_CARRY2_TXPKTS (1 << 12)
> +#define TSI108_STAT_CARRY2_TXEXDEF (1 << 7)
> +#define TSI108_STAT_CARRY2_TXEXCOL (1 << 3)
> +#define TSI108_STAT_CARRY2_TXTCOL (1 << 2)
> +#define TSI108_STAT_CARRY2_TXPAUSE (1 << 0)
> +
> +#define TSI108_STAT_CARRYMASK1 (0x108)
> +#define TSI108_STAT_CARRYMASK2 (0x10c)
> +
> +#define TSI108_EC_PORTCTRL (0x200)
> +#define TSI108_EC_PORTCTRL_STATRST (1 << 31)
> +#define TSI108_EC_PORTCTRL_STATEN (1 << 28)
> +#define TSI108_EC_PORTCTRL_NOGIG (1 << 18)
> +#define TSI108_EC_PORTCTRL_HALFDUPLEX (1 << 16)
> +
> +#define TSI108_EC_INTSTAT (0x204)
> +#define TSI108_EC_INTMASK (0x208)
> +
> +#define TSI108_INT_ANY (1 << 31)
> +#define TSI108_INT_SFN (1 << 30)
> +#define TSI108_INT_RXIDLE (1 << 29)
> +#define TSI108_INT_RXABORT (1 << 28)
> +#define TSI108_INT_RXERROR (1 << 27)
> +#define TSI108_INT_RXOVERRUN (1 << 26)
> +#define TSI108_INT_RXTHRESH (1 << 25)
> +#define TSI108_INT_RXWAIT (1 << 24)
> +#define TSI108_INT_RXQUEUE0 (1 << 16)
> +#define TSI108_INT_STATCARRY (1 << 15)
> +#define TSI108_INT_TXIDLE (1 << 13)
> +#define TSI108_INT_TXABORT (1 << 12)
> +#define TSI108_INT_TXERROR (1 << 11)
> +#define TSI108_INT_TXUNDERRUN (1 << 10)
> +#define TSI108_INT_TXTHRESH (1 << 9)
> +#define TSI108_INT_TXWAIT (1 << 8)
> +#define TSI108_INT_TXQUEUE0 (1 << 0)
> +
> +#define TSI108_EC_TXCFG (0x220)
> +#define TSI108_EC_TXCFG_RST (1 << 31)
> +
> +#define TSI108_EC_TXCTRL (0x224)
> +#define TSI108_EC_TXCTRL_IDLEINT (1 << 31)
> +#define TSI108_EC_TXCTRL_ABORT (1 << 30)
> +#define TSI108_EC_TXCTRL_GO (1 << 15)
> +#define TSI108_EC_TXCTRL_QUEUE0 (1 << 0)
> +
> +#define TSI108_EC_TXSTAT (0x228)
> +#define TSI108_EC_TXSTAT_ACTIVE (1 << 15)
> +#define TSI108_EC_TXSTAT_QUEUE0 (1 << 0)
> +
> +#define TSI108_EC_TXESTAT (0x22c)
> +#define TSI108_EC_TXESTAT_Q0_ERR (1 << 24)
> +#define TSI108_EC_TXESTAT_Q0_DESCINT (1 << 16)
> +#define TSI108_EC_TXESTAT_Q0_EOF (1 << 8)
> +#define TSI108_EC_TXESTAT_Q0_EOQ (1 << 0)
> +
> +#define TSI108_EC_TXERR (0x278)
> +
> +#define TSI108_EC_TXQ_CFG (0x280)
> +#define TSI108_EC_TXQ_CFG_DESC_INT (1 << 20)
> +#define TSI108_EC_TXQ_CFG_EOQ_OWN_INT (1 << 19)
> +#define TSI108_EC_TXQ_CFG_WSWP (1 << 11)
> +#define TSI108_EC_TXQ_CFG_BSWP (1 << 10)
> +#define TSI108_EC_TXQ_CFG_SFNPORT 0
> +
> +#define TSI108_EC_TXQ_BUFCFG (0x284)
> +#define TSI108_EC_TXQ_BUFCFG_BURST8 (0 << 8)
> +#define TSI108_EC_TXQ_BUFCFG_BURST32 (1 << 8)
> +#define TSI108_EC_TXQ_BUFCFG_BURST128 (2 << 8)
> +#define TSI108_EC_TXQ_BUFCFG_BURST256 (3 << 8)
> +#define TSI108_EC_TXQ_BUFCFG_WSWP (1 << 11)
> +#define TSI108_EC_TXQ_BUFCFG_BSWP (1 << 10)
> +#define TSI108_EC_TXQ_BUFCFG_SFNPORT 0
> +
> +#define TSI108_EC_TXQ_PTRLOW (0x288)
> +
> +#define TSI108_EC_TXQ_PTRHIGH (0x28c)
> +#define TSI108_EC_TXQ_PTRHIGH_VALID (1 << 31)
> +
> +#define TSI108_EC_TXTHRESH (0x230)
> +#define TSI108_EC_TXTHRESH_STARTFILL 0
> +#define TSI108_EC_TXTHRESH_STOPFILL 16
> +
> +#define TSI108_EC_RXCFG (0x320)
> +#define TSI108_EC_RXCFG_RST (1 << 31)
> +
> +#define TSI108_EC_RXSTAT (0x328)
> +#define TSI108_EC_RXSTAT_ACTIVE (1 << 15)
> +#define TSI108_EC_RXSTAT_QUEUE0 (1 << 0)
> +
> +#define TSI108_EC_RXESTAT (0x32c)
> +#define TSI108_EC_RXESTAT_Q0_ERR (1 << 24)
> +#define TSI108_EC_RXESTAT_Q0_DESCINT (1 << 16)
> +#define TSI108_EC_RXESTAT_Q0_EOF (1 << 8)
> +#define TSI108_EC_RXESTAT_Q0_EOQ (1 << 0)
> +
> +#define TSI108_EC_HASHADDR (0x360)
> +#define TSI108_EC_HASHADDR_AUTOINC (1 << 31)
> +#define TSI108_EC_HASHADDR_DO1STREAD (1 << 30)
> +#define TSI108_EC_HASHADDR_UNICAST (0 << 4)
> +#define TSI108_EC_HASHADDR_MCAST (1 << 4)
> +
> +#define TSI108_EC_HASHDATA (0x364)
> +
> +#define TSI108_EC_RXQ_PTRLOW (0x388)
> +
> +#define TSI108_EC_RXQ_PTRHIGH (0x38c)
> +#define TSI108_EC_RXQ_PTRHIGH_VALID (1 << 31)
> +
> +/* Station Enable -- accept packets destined for us */
> +#define TSI108_EC_RXCFG_SE (1 << 13)
> +/* Unicast Frame Enable -- for packets not destined for us */
> +#define TSI108_EC_RXCFG_UFE (1 << 12)
> +/* Multicast Frame Enable */
> +#define TSI108_EC_RXCFG_MFE (1 << 11)
> +/* Broadcast Frame Enable */
> +#define TSI108_EC_RXCFG_BFE (1 << 10)
> +#define TSI108_EC_RXCFG_UC_HASH (1 << 9)
> +#define TSI108_EC_RXCFG_MC_HASH (1 << 8)
> +
> +#define TSI108_EC_RXQ_CFG (0x380)
> +#define TSI108_EC_RXQ_CFG_DESC_INT (1 << 20)
> +#define TSI108_EC_RXQ_CFG_EOQ_OWN_INT (1 << 19)
> +#define TSI108_EC_RXQ_CFG_WSWP (1 << 11)
> +#define TSI108_EC_RXQ_CFG_BSWP (1 << 10)
> +#define TSI108_EC_RXQ_CFG_SFNPORT 0
> +
> +#define TSI108_EC_RXQ_BUFCFG (0x384)
> +#define TSI108_EC_RXQ_BUFCFG_BURST8 (0 << 8)
> +#define TSI108_EC_RXQ_BUFCFG_BURST32 (1 << 8)
> +#define TSI108_EC_RXQ_BUFCFG_BURST128 (2 << 8)
> +#define TSI108_EC_RXQ_BUFCFG_BURST256 (3 << 8)
> +#define TSI108_EC_RXQ_BUFCFG_WSWP (1 << 11)
> +#define TSI108_EC_RXQ_BUFCFG_BSWP (1 << 10)
> +#define TSI108_EC_RXQ_BUFCFG_SFNPORT 0
> +
> +#define TSI108_EC_RXCTRL (0x324)
> +#define TSI108_EC_RXCTRL_ABORT (1 << 30)
> +#define TSI108_EC_RXCTRL_GO (1 << 15)
> +#define TSI108_EC_RXCTRL_QUEUE0 (1 << 0)
> +
> +#define TSI108_EC_RXERR (0x378)
> +
> +#define PHY_CTRL 0
> +#define PHY_CTRL_RESET (1 << 15)
> +#define PHY_CTRL_AUTONEG_EN (1 << 12)
> +#define PHY_CTRL_POWERDOWN (1 << 11)
> +#define PHY_CTRL_AUTONEG_START (1 << 9)
> +
> +#define PHY_STAT 1
> +#define PHY_STAT_LINKUP (1 << 2)
> +
> +#if (TSI108_PHY_TYPE =3D=3D PHY_MV88E)
> +/* Marvel 88E1xxx-specific */
> +#define PHY_SUM_STAT 0x11
> +#define PHY_SUM_STAT_SPEED_MASK (3 << 14)
> +#define PHY_SUM_STAT_SPEED_10 (0 << 14)
> +#define PHY_SUM_STAT_SPEED_100 (1 << 14)
> +#define PHY_SUM_STAT_SPEED_1000 (2 << 14)
> +#define PHY_SUM_STAT_FULLDUPLEX (1 << 13)
> +
> +#define PHY_SUM_STAT_1000T_FD (PHY_SUM_STAT_SPEED_1000 | =20
> PHY_SUM_STAT_FULLDUPLEX)
> +#define PHY_SUM_STAT_1000T_HD (PHY_SUM_STAT_SPEED_1000)
> +#define PHY_SUM_STAT_100TX_FD (PHY_SUM_STAT_SPEED_100 | =20
> PHY_SUM_STAT_FULLDUPLEX)
> +#define PHY_SUM_STAT_100TX_HD (PHY_SUM_STAT_SPEED_100)
> +#define PHY_SUM_STAT_10T_FD (PHY_SUM_STAT_SPEED_10 | =20
> PHY_SUM_STAT_FULLDUPLEX)
> +#define PHY_SUM_STAT_10T_HD (PHY_SUM_STAT_SPEED_10)
> +#elif (TSI108_PHY_TYPE =3D=3D PHY_BCM54XX)
> +/*Broadcom BCM54xx */
> +#define PHY_SUM_STAT 0x19
> +#define PHY_SUM_STAT_SPEED_MASK (7 << 8) /* Auto Negotiation HCD */
> +#define PHY_SUM_STAT_1000T_FD (7 << 8) /* 1000BASE-T Full-Duplex */
> +#define PHY_SUM_STAT_1000T_HD (6 << 8) /* 1000BASE-T Half-Duplex */
> +#define PHY_SUM_STAT_100TX_FD (5 << 8) /* 100BASE-TX Full-Duplex */
> +#define PHY_SUM_STAT_100T4 (4 << 8) /* 100BASE-T4 */
> +#define PHY_SUM_STAT_100TX_HD (3 << 8) /* 100BASE-TX Half-Duplex */
> +#define PHY_SUM_STAT_10T_FD (2 << 8) /* 10BASE-T Full-Duplex */
> +#define PHY_SUM_STAT_10T_HD (1 << 8) /* 10BASE-T Half-Duplex */
> +#define PHY_SUM_STAT_AN_FAIL (0 << 8) /* 1000BASE-T Half-Duplex */
> +#else
> +#error "PHY Device not specified"
> +#endif /* MV88_PHY */
> +
> +#define TSI108_TX_EOF (1 << 0) /* End of frame; last fragment of =20
> packet */
> +#define TSI108_TX_SOF (1 << 1) /* Start of frame; first frag. of =20
> packet */
> +#define TSI108_TX_VLAN (1 << 2) /* Per-frame VLAN: enables VLAN =20
> override */
> +#define TSI108_TX_HUGE (1 << 3) /* Huge frame enable */
> +#define TSI108_TX_PAD (1 << 4) /* Pad the packet if too short */
> +#define TSI108_TX_CRC (1 << 5) /* Generate CRC for this packet */
> +#define TSI108_TX_INT (1 << 14) /* Generate an IRQ after frag. =20
> processed */
> +#define TSI108_TX_RETRY 16 /* 4 bit field indicating num. of =20
> retries */
> +#define TSI108_TX_COL (1 << 20) /* Set if a collision occured */
> +#define TSI108_TX_LCOL (1 << 24) /* Set if a late collision =20
> occured */
> +#define TSI108_TX_UNDER (1 << 25) /* Set if a FIFO underrun =20
> occured */
> +#define TSI108_TX_RLIM (1 << 26) /* Set if the retry limit was =20
> reached */
> +#define TSI108_TX_OK (1 << 30) /* Set if the frame TX was =20
> successful */
> +#define TSI108_TX_OWN (1 << 31) /* Set if the device owns the =20
> descriptor */
> +
> +/* Note: the descriptor layouts assume big-endian byte order. */
> +typedef struct {
> + u32 buf0;
> + u32 buf1; /* Base address of buffer */
> + u32 next0; /* Address of next descriptor, if any */
> + u32 next1;
> + u16 vlan; /* VLAN, if override enabled for this packet */
> + u16 len; /* Length of buffer in bytes */
> + u32 misc; /* See TSI108_TX_* above */
> + u32 reserved0; /*reserved0 and reserved1 are added to make the =20
> desc */
> + u32 reserved1; /* 32-byte aligned */
> +} __attribute__ ((aligned(32))) tx_desc;
> +
> +#define TSI108_RX_EOF (1 << 0) /* End of frame; last fragment of
> packet */
> +#define TSI108_RX_SOF (1 << 1) /* Start of frame; first frag. of =20
> packet */
> +#define TSI108_RX_VLAN (1 << 2) /* Set on SOF if packet has a VLAN =
*/
> +#define TSI108_RX_FTYPE (1 << 3) /* Length/Type field is type, not =20
> length */
> +#define TSI108_RX_RUNT (1 << 4)/* Packet is less than minimum size =
*/
> +#define TSI108_RX_HASH (1 << 7)/* Hash table match */
> +#define TSI108_RX_BAD (1 << 8) /* Bad frame */
> +#define TSI108_RX_OVER (1 << 9) /* FIFO overrun occured */
> +#define TSI108_RX_TRUNC (1 << 11) /* Packet truncated due to =20
> excess length */
> +#define TSI108_RX_CRC (1 << 12) /* Packet had a CRC error */
> +#define TSI108_RX_INT (1 << 13) /* Generate an IRQ after frag. =20
> processed */
> +#define TSI108_RX_OWN (1 << 15) /* Set if the device owns the =20
> descriptor */
> +
> +typedef struct {
> + u32 buf0; /* Base address of buffer */
> + u32 buf1; /* Base address of buffer */
> + u32 next0; /* Address of next descriptor, if any */
> + u32 next1; /* Address of next descriptor, if any */
> + u16 vlan; /* VLAN of received packet, first frag only */
> + u16 len; /* Length of received fragment in bytes */
> + u16 blen; /* Length of buffer in bytes */
> + u16 misc; /* See TSI108_RX_* above */
> + u32 reserved0; /* reserved0 and reserved1 are added to make the =20
> desc */
> + u32 reserved1; /* 32-byte aligned */
> +} __attribute__ ((aligned(32))) rx_desc;
> +
> +#endif /* __TSI108_ETH_H */
> --=20
> 1.3.0
> _______________________________________________
> Linuxppc-dev mailing list
> Linuxppc-dev@ozlabs.org
> https://ozlabs.org/mailman/listinfo/linuxppc-dev
^ permalink raw reply
* Re: PowerMac7,3 sound (was: PowerBook5,4 -- no sound?)
From: Benjamin Herrenschmidt @ 2006-05-18 3:13 UTC (permalink / raw)
To: Johannes Berg; +Cc: linuxppc-dev
In-Reply-To: <1147862072.14395.25.camel@johannes>
On Wed, 2006-05-17 at 12:34 +0200, Johannes Berg wrote:
> > > request_mem_region(80000000 - 80000fff, i2sbus control)
> > >
> > > 80000000-8007ffff : 0.80000000:mac-io
> > > 8000002c-8000002f : 0.0000004c:fans
> > > 80000030-80000033 : 0.0000004c:fans
> > > 80000034-80000037 : 0.0000004c:fans
> > > 8000004c-8000004f : 0.0000004c:fans
> > > 80000050-8000008a : 0.00000050:gpio
>
> > Look like the bugs in the device-tree I told you about ...
>
> Ben, is it safe to just hardcode 0x80010000-0x80010fff? Or hardcode
> 0x10000-0x10fff and have it do the translation depending on the
> device-tree node, iow assume that the 'reg' property is bogus and
> essentially replace it with one that contains the values
> 0x00010000 0x00001000 (buggy one seems to be 0x00000000 0x00001000)
> 0x00008000 0x00000100
> 0x00008100 0x00000100
> and then go from there? Even then though, I have no idea how to do the
> ranges translation for the node. Maybe we should just fix up the reg
> property in the device-tree for the affected machine(s)?
I'd rather avoid hard-coding in fact. I'm trying to figure out a better
way.
> Btw, it doesn't look like snd-powermac handles this correctly, it only
> ever handles the rx/tx specially and also takes the control io area
> straight from the device tree.
>
> Andreas, you'll probably be able to get a bit further by inserting the
> following code in front of the loop in i2sbus-core.c line 162, and
> removing the of_address_to_resource call inside the loop.
>
> dev->resources[0].start = 0x80010000;
> dev->resources[0].end = 0x80010fff;
> dev->resources[1].start = 0x80008000;
> dev->resources[1].end = 0x800080ff;
> dev->resources[2].start = 0x80008100;
> dev->resources[2].end = 0x800081ff;
>
> But I'm not sure that is correct on all machines (in fact, I'm not
> perfectly sure it is correct on your machine).
>
> After that, you'll probably still not have usable sound though because I
> know nothing about the layout-id of your machine. If I have support for
> your codec already, that should be easy though.
>
> johannes
^ permalink raw reply
* [PATCH] powerpc: Move crashkernel= handling into the kernel.
From: Michael Ellerman @ 2006-05-18 1:16 UTC (permalink / raw)
To: Paul Mackerras; +Cc: linuxppc-dev
In-Reply-To: <20060517080054.963CC67A0E@ozlabs.org>
This was missing a quilt ref.
---
Currently we parse crashkernel= in prom_init.c, which means for other boot
loaders to support Kdump they also need to parse the command line and setup
the appropriate crash kernel properties.
With early param parsing done earlier we can do crashkernel= parsing in the
early kernel code and avoid the need for every bootloader to do it for us.
We still support the device tree properties if they're specified by firmware,
however the command line overrides anything we find there.
Tested on P5 LPAR.
Signed-off-by: Michael Ellerman <michael@ellerman.id.au>
---
arch/powerpc/kernel/machine_kexec_64.c | 94 +++++++++++++++++++++++++++++++++
arch/powerpc/kernel/prom.c | 1
arch/powerpc/kernel/prom_init.c | 54 ------------------
include/asm-powerpc/kexec.h | 3 +
4 files changed, 98 insertions(+), 54 deletions(-)
Index: to-merge/arch/powerpc/kernel/machine_kexec_64.c
===================================================================
--- to-merge.orig/arch/powerpc/kernel/machine_kexec_64.c
+++ to-merge/arch/powerpc/kernel/machine_kexec_64.c
@@ -21,6 +21,7 @@
#include <asm/machdep.h>
#include <asm/cacheflush.h>
#include <asm/paca.h>
+#include <asm/lmb.h>
#include <asm/mmu.h>
#include <asm/sections.h> /* _end */
#include <asm/prom.h>
@@ -335,9 +336,102 @@ static void __init export_htab_values(vo
of_node_put(node);
}
+static struct property crashk_base_prop = {
+ .name = "linux,crashkernel-base",
+ .length = sizeof(unsigned long),
+ .value = (unsigned char *)&crashk_res.start,
+};
+
+static unsigned long crashk_size;
+
+static struct property crashk_size_prop = {
+ .name = "linux,crashkernel-size",
+ .length = sizeof(unsigned long),
+ .value = (unsigned char *)&crashk_size,
+};
+
+static void __init export_crashk_values(void)
+{
+ struct device_node *node;
+ struct property *prop;
+
+ node = of_find_node_by_path("/chosen");
+ if (!node)
+ return;
+
+ /* There might be existing crash kernel properties, but we can't
+ * be sure what's in them, so remove them. */
+ prop = of_find_property(node, "linux,crashkernel-base", NULL);
+ if (prop)
+ prom_remove_property(node, prop);
+
+ prop = of_find_property(node, "linux,crashkernel-size", NULL);
+ if (prop)
+ prom_remove_property(node, prop);
+
+ if (crashk_res.start != 0) {
+ prom_add_property(node, &crashk_base_prop);
+ crashk_size = crashk_res.end - crashk_res.start + 1;
+ prom_add_property(node, &crashk_size_prop);
+ }
+
+ of_node_put(node);
+}
+
void __init kexec_setup(void)
{
export_htab_values();
+ export_crashk_values();
+}
+
+static int __init early_parse_crashk(char *p)
+{
+ unsigned long size;
+
+ if (!p)
+ return 1;
+
+ size = memparse(p, &p);
+
+ if (*p == '@')
+ crashk_res.start = memparse(p + 1, &p);
+ else
+ crashk_res.start = KDUMP_KERNELBASE;
+
+ crashk_res.end = crashk_res.start + size - 1;
+
+ return 0;
+}
+early_param("crashkernel", early_parse_crashk);
+
+void __init reserve_crashkernel(void)
+{
+ unsigned long size;
+
+ if (crashk_res.start == 0)
+ return;
+
+ /* We might have got these values via the command line or the
+ * device tree, either way sanitise them now. */
+
+ size = crashk_res.end - crashk_res.start + 1;
+
+ if (crashk_res.start != KDUMP_KERNELBASE)
+ printk("Crash kernel location must be 0x%x\n",
+ KDUMP_KERNELBASE);
+
+ crashk_res.start = KDUMP_KERNELBASE;
+ size = PAGE_ALIGN(size);
+ crashk_res.end = crashk_res.start + size - 1;
+
+ /* Crash kernel trumps memory limit */
+ if (memory_limit && memory_limit <= crashk_res.end) {
+ memory_limit = crashk_res.end + 1;
+ printk("Adjusted memory limit for crashkernel, now 0x%lx\n",
+ memory_limit);
+ }
+
+ lmb_reserve(crashk_res.start, size);
}
int overlaps_crashkernel(unsigned long start, unsigned long size)
Index: to-merge/arch/powerpc/kernel/prom.c
===================================================================
--- to-merge.orig/arch/powerpc/kernel/prom.c
+++ to-merge/arch/powerpc/kernel/prom.c
@@ -1327,6 +1327,7 @@ void __init early_init_devtree(void *par
/* Reserve LMB regions used by kernel, initrd, dt, etc... */
lmb_reserve(PHYSICAL_START, __pa(klimit) - PHYSICAL_START);
reserve_kdump_trampoline();
+ reserve_crashkernel();
early_reserve_mem();
lmb_enforce_memory_limit(memory_limit);
Index: to-merge/arch/powerpc/kernel/prom_init.c
===================================================================
--- to-merge.orig/arch/powerpc/kernel/prom_init.c
+++ to-merge/arch/powerpc/kernel/prom_init.c
@@ -200,11 +200,6 @@ static unsigned long __initdata alloc_bo
static unsigned long __initdata rmo_top;
static unsigned long __initdata ram_top;
-#ifdef CONFIG_KEXEC
-static unsigned long __initdata prom_crashk_base;
-static unsigned long __initdata prom_crashk_size;
-#endif
-
static struct mem_map_entry __initdata mem_reserve_map[MEM_RESERVE_MAP_SIZE];
static int __initdata mem_reserve_cnt;
@@ -591,35 +586,6 @@ static void __init early_cmdline_parse(v
RELOC(iommu_force_on) = 1;
}
#endif
-
-#ifdef CONFIG_KEXEC
- /*
- * crashkernel=size@addr specifies the location to reserve for
- * crash kernel.
- */
- opt = strstr(RELOC(prom_cmd_line), RELOC("crashkernel="));
- if (opt) {
- opt += 12;
- RELOC(prom_crashk_size) =
- prom_memparse(opt, (const char **)&opt);
-
- if (ALIGN(RELOC(prom_crashk_size), 0x1000000) !=
- RELOC(prom_crashk_size)) {
- prom_printf("Warning: crashkernel size is not "
- "aligned to 16MB\n");
- }
-
- /*
- * At present, the crash kernel always run at 32MB.
- * Just ignore whatever user passed.
- */
- RELOC(prom_crashk_base) = 0x2000000;
- if (*opt == '@') {
- prom_printf("Warning: PPC64 kdump kernel always runs "
- "at 32 MB\n");
- }
- }
-#endif
}
#ifdef CONFIG_PPC_PSERIES
@@ -1122,12 +1088,6 @@ static void __init prom_init_mem(void)
prom_printf(" alloc_top_hi : %x\n", RELOC(alloc_top_high));
prom_printf(" rmo_top : %x\n", RELOC(rmo_top));
prom_printf(" ram_top : %x\n", RELOC(ram_top));
-#ifdef CONFIG_KEXEC
- if (RELOC(prom_crashk_base)) {
- prom_printf(" crashk_base : %x\n", RELOC(prom_crashk_base));
- prom_printf(" crashk_size : %x\n", RELOC(prom_crashk_size));
- }
-#endif
}
@@ -2187,10 +2147,6 @@ unsigned long __init prom_init(unsigned
*/
prom_init_mem();
-#ifdef CONFIG_KEXEC
- if (RELOC(prom_crashk_base))
- reserve_mem(RELOC(prom_crashk_base), RELOC(prom_crashk_size));
-#endif
/*
* Determine which cpu is actually running right _now_
*/
@@ -2243,16 +2199,6 @@ unsigned long __init prom_init(unsigned
}
#endif
-#ifdef CONFIG_KEXEC
- if (RELOC(prom_crashk_base)) {
- prom_setprop(_prom->chosen, "/chosen", "linux,crashkernel-base",
- PTRRELOC(&prom_crashk_base),
- sizeof(RELOC(prom_crashk_base)));
- prom_setprop(_prom->chosen, "/chosen", "linux,crashkernel-size",
- PTRRELOC(&prom_crashk_size),
- sizeof(RELOC(prom_crashk_size)));
- }
-#endif
/*
* Fixup any known bugs in the device-tree
*/
Index: to-merge/include/asm-powerpc/kexec.h
===================================================================
--- to-merge.orig/include/asm-powerpc/kexec.h
+++ to-merge/include/asm-powerpc/kexec.h
@@ -125,6 +125,7 @@ extern void default_machine_crash_shutdo
extern void machine_kexec_simple(struct kimage *image);
extern int overlaps_crashkernel(unsigned long start, unsigned long size);
+extern void reserve_crashkernel(void);
#else /* !CONFIG_KEXEC */
@@ -133,6 +134,8 @@ static inline int overlaps_crashkernel(u
return 0;
}
+static inline void reserve_crashkernel(void) { ; }
+
#endif /* CONFIG_KEXEC */
#endif /* ! __ASSEMBLY__ */
#endif /* __KERNEL__ */
^ permalink raw reply
* Re: snd-aoa status update / automatic driver loading
From: Benjamin Herrenschmidt @ 2006-05-18 1:08 UTC (permalink / raw)
To: Wolfgang Pfeiffer; +Cc: linuxppc-dev list, Johannes Berg, debian-powerpc
In-Reply-To: <20060517215412.GA4504@localhost>
> /var/log/boot.2.gz:Wed May 17 16:14:04 2006: ^[[9;0]^[[14;0]Setting up
> ALSA...warning: 'alsactl restore' failed with error message 'alsactl:
> set_control:894: warning: name mismatch (Line-out Switch/Headphone
> Switch) for control #2
>
> /var/log/boot.2.gz:Wed May 17 16:14:05 2006: alsactl: set_control:896:
> warning: index mismatch (0/0) for control #2
>
> /var/log/boot.2.gz:Wed May 17 16:14:05 2006: alsactl: set_control:894:
> warning: name mismatch (Master Playback Volume/Capture Source) for
> control #3
>
> /var/log/boot.2.gz:Wed May 17 16:14:05 2006: alsactl: set_control:896:
> warning: index mismatch (0/0) for control #3
That's Alsa being stupid. It stores the values of the control by number
and screws up when the driver is updated and adds/removes controls.
Storing the settings with the new driver should cure that
^ permalink raw reply
* Re: [PATCH 1/5] powerpc: Make early xmon logic immune to location of early parsing
From: Tom Rini @ 2006-05-18 1:08 UTC (permalink / raw)
To: Michael Ellerman; +Cc: linuxppc-dev, Paul Mackerras
In-Reply-To: <1147910586.7360.8.camel@localhost.localdomain>
On Thu, May 18, 2006 at 10:03:05AM +1000, Michael Ellerman wrote:
> On Wed, 2006-05-17 at 14:29 -0700, Tom Rini wrote:
> > On Wed, May 17, 2006 at 06:00:41PM +1000, Michael Ellerman wrote:
> >
> > > Currently early_xmon() calls directly into debugger() if xmon=early is passed.
> > > This ties the invocation of early xmon to the location of parse_early_param(),
> > > which might change.
> > >
> > > Tested on P5 LPAR and F50.
> > >
> > > Signed-off-by: Michael Ellerman <michael@ellerman.id.au>
> >
> > Please no, parse_early_param() is there so things like xmon or kgdb can
> > be dropped into as soon as we're able to parse any params that might be
> > usable early on.
>
> Sure, did you read the rest of the series? I want to parse parameters
> eariler, so early that xmon isn't ready to run when we parse them, so I
> have to defer jumping into xmon until after xmon is initialised. The net
> effect on when xmon runs is zero. Or did I miss your point?
My point would be that xmon should either be fixed to work that early or
parse things a bit later as a regular param. I know the current system
is flawed but I really don't like the idea (especially as a comaintainer
of kgdb) of having to do a special plug here or there for one param
because we parse early stuff too early, but regular stuff not early
enough (which is why Andrew Morton got me to poke at the early param
stuff a while back and then I think Rusty did something better, or
something along those lines, anyhow).
I'm just trying to avoid getting back into the situation we had before
parse_early_param().
--
Tom Rini
^ permalink raw reply
* Re: [PATCH/2.6.17-rc4 10/10] bugs fix for marvell SATA on powerpc pl atform
From: Benjamin Herrenschmidt @ 2006-05-18 1:03 UTC (permalink / raw)
To: Zang Roy-r61911
Cc: linuxppc-dev list, Yang Xin-Xin-r48390, Paul Mackerras,
Alexandre.Bounine
In-Reply-To: <9FCDBA58F226D911B202000BDBAD46730626D628@zch01exm40.ap.freescale.net>
On Wed, 2006-05-17 at 18:14 +0800, Zang Roy-r61911 wrote:
> Fix Marvell SATA driver bugs on PowerPC platform:
> SATA device can't work for the problem on little-endian mode.
> U-Boot can't find SATA device after kernel reboots.
As Kumar says -> Jeff is the maintainer. But I do have a couple of
comments still...
> @@ -1032,6 +1032,9 @@ static inline void mv_crqb_pack_cmd(u16
> {
> *cmdw = data | (addr << CRQB_CMD_ADDR_SHIFT) | CRQB_CMD_CS |
> (last ? CRQB_CMD_LAST : 0);
> +#ifdef CONFIG_PPC
> + *cmdw = cpu_to_le16(*cmdw);
> +#endif
> }
Why an ifdef here ? The cpu_to_le16 should probably be unconditional.
And even if for some weird reason you wanted to ifdef it, why PPC ? and
what about other BE architectures ?
> /**
> @@ -1567,13 +1570,18 @@ static void mv5_read_preamp(struct mv_ho
> static void mv5_enable_leds(struct mv_host_priv *hpriv, void __iomem *mmio)
> {
> u32 tmp;
> -
> +#ifndef CONFIG_PPC
> writel(0, mmio + MV_GPIO_PORT_CTL);
> +#endif
You'll have to do better here too... I don't wee why when compiled on
PPC, this driver should "magically" not clear those bits... At the very
least, you should test the machine type if you want to do something
specific to your platform, but first, you'll have to convince Jeff why
this change has to be done in the first place and if there is a better
way to handle it.
> /* FIXME: handle MV_HP_ERRATA_50XXB2 errata */
>
> tmp = readl(mmio + MV_PCI_EXP_ROM_BAR_CTL);
> +#ifdef CONFIG_PPC
> + tmp &= ~(1 << 0);
> +#else
> tmp |= ~(1 << 0);
> +#endif
> writel(tmp, mmio + MV_PCI_EXP_ROM_BAR_CTL);
> }
Looks to me like the initial code was bogus, thus the #ifdef shouldn't
be necessary neither, and even if it was, an ifdef CONFIG_PPC would be
the wrong approach for what I think should be ovious enough reasons...
Ben.
^ permalink raw reply
* Re: [PATCH/2.6.17-rc4 8/10] Add tsi108 8250 serial support
From: Benjamin Herrenschmidt @ 2006-05-18 1:00 UTC (permalink / raw)
To: Zang Roy-r61911
Cc: linuxppc-dev list, Yang Xin-Xin-r48390, Paul Mackerras,
Alexandre.Bounine
In-Reply-To: <9FCDBA58F226D911B202000BDBAD46730626D625@zch01exm40.ap.freescale.net>
On Wed, 2006-05-17 at 18:14 +0800, Zang Roy-r61911 wrote:
> This patch contains changes to the serial device driver specific for integrated
> serial port in Tsi108 Host Bridge.
You'll have to discuss this patch with Russell as Kumar pointed out,
though I'll predict you'll have a hard time getting it through in its
current... chip specific #ifdef's are bad. If workarounds are needed,
they should be using a softer mecanism, either the capabilities bits or
whatever is the best approach for that driver (talk to Russell). Also
the use of in_be32 will probably not be accepted in there neither... If
you really have to do a 32 bits read, use readl (which will do an endian
conversion that you may have to take into account)
Ben.
> Signed-off-by: Alexandre Bounine <alexandreb@tundra.com>
> Signed-off-by: Roy Zang <tie-fei.zang@freescale.com>
>
> >From nobody Mon Sep 17 00:00:00 2001
> From: roy zang <tie-fei.zang@freescale.com>
> Date: Tue May 16 15:26:02 2006 +0800
> Subject: [PATCH] Add tsi108 serial support
>
> ---
>
> drivers/serial/8250.c | 17 +++++++++++++++++
> 1 files changed, 17 insertions(+), 0 deletions(-)
>
> 6cb950357e9970afa671d59f172dbc4b03f11560
> diff --git a/drivers/serial/8250.c b/drivers/serial/8250.c
> index bbf78aa..c12f516 100644
> --- a/drivers/serial/8250.c
> +++ b/drivers/serial/8250.c
> @@ -723,7 +723,9 @@ static int broken_efr(struct uart_8250_p
> static void autoconfig_16550a(struct uart_8250_port *up)
> {
> unsigned char status1, status2;
> +#ifndef CONFIG_TSI108_BRIDGE
> unsigned int iersave;
> +#endif
>
> up->port.type = PORT_16550A;
> up->capabilities |= UART_CAP_FIFO;
> @@ -833,6 +835,7 @@ static void autoconfig_16550a(struct uar
> * trying to write and read a 1 just to make sure it's not
> * already a 1 and maybe locked there before we even start start.
> */
> +#ifndef CONFIG_TSI108_BRIDGE
> iersave = serial_in(up, UART_IER);
> serial_outp(up, UART_IER, iersave & ~UART_IER_UUE);
> if (!(serial_in(up, UART_IER) & UART_IER_UUE)) {
> @@ -859,6 +862,7 @@ static void autoconfig_16550a(struct uar
> DEBUG_AUTOCONF("Couldn't force IER_UUE to 0 ");
> }
> serial_outp(up, UART_IER, iersave);
> +#endif
> }
>
> /*
> @@ -1348,7 +1352,12 @@ static irqreturn_t serial8250_interrupt(
>
> up = list_entry(l, struct uart_8250_port, list);
>
> +#ifdef CONFIG_TSI108_BRIDGE /* for TSI108_REV_Z1 errata U2 */
> + /* read IIR as part of 32-bit word */
> + iir = (in_be32((u32 *)(up->port.membase + UART_RX)) >> 8) & 0xff;
> +#else
> iir = serial_in(up, UART_IIR);
> +#endif
> if (!(iir & UART_IIR_NO_INT)) {
> serial8250_handle_port(up, regs);
>
> @@ -1529,7 +1538,9 @@ static int serial8250_startup(struct uar
> {
> struct uart_8250_port *up = (struct uart_8250_port *)port;
> unsigned long flags;
> +#ifndef CONFIG_TSI108_BRIDGE
> unsigned char lsr, iir;
> +#endif
> int retval;
>
> up->capabilities = uart_config[up->port.type].flags;
> @@ -1567,7 +1578,9 @@ #endif
> */
> (void) serial_inp(up, UART_LSR);
> (void) serial_inp(up, UART_RX);
> +#ifndef CONFIG_TSI108_BRIDGE /* for TSI108_REV_Z1 errata U2 */
> (void) serial_inp(up, UART_IIR);
> +#endif
> (void) serial_inp(up, UART_MSR);
>
> /*
> @@ -1634,6 +1647,7 @@ #endif
>
> serial8250_set_mctrl(&up->port, up->port.mctrl);
>
> +#ifndef CONFIG_TSI108_BRIDGE
> /*
> * Do a quick test to see if we receive an
> * interrupt when we enable the TX irq.
> @@ -1652,6 +1666,7 @@ #endif
> } else {
> up->bugs &= ~UART_BUG_TXEN;
> }
> +#endif
>
> spin_unlock_irqrestore(&up->port.lock, flags);
>
> @@ -1678,7 +1693,9 @@ #endif
> */
> (void) serial_inp(up, UART_LSR);
> (void) serial_inp(up, UART_RX);
> +#ifndef CONFIG_TSI108_BRIDGE /* for TSI108_REV_Z1 errata U2 */
> (void) serial_inp(up, UART_IIR);
> +#endif
> (void) serial_inp(up, UART_MSR);
>
> return 0;
^ permalink raw reply
* Re: snd-aoa status update / automatic driver loading
From: Wolfgang Pfeiffer @ 2006-05-18 1:01 UTC (permalink / raw)
To: Johannes Berg; +Cc: linuxppc-dev list, debian-powerpc
In-Reply-To: <20060517215412.GA4504@localhost>
On Wed, May 17, 2006 at 11:54:12PM +0200, Wolfgang Pfeiffer wrote:
>
> 4:
>
> I can hear two sound files at the same time: Didn't work at the first
> try. I then installed again /etc/asound.conf, that I copied from my
> old ti-IV-Book (Not being sure tho' whether this really is such a good
> idea .... :
And the file below wasn't written by me. Not sure tho' where I found it
once ...
>
> -------------------------------
> pcm.!default {
> type plug
> slave.pcm asymer
> }
>
> pcm.dmixer {
> type dmix
> ipc_key 1977
> ipc_perm 666
> # ipc_key_add_uid true
> slowptr true
> slave {
> pcm "hw:0"
> [ ... ]
> capture_ports {
> 0 alsa_pcm:capture_1
> 1 alsa_pcm:capture_2
> }
> }
>
> # These are for alsa-oss
> pcm.dsp0 pcm.default
> ctl.mixer0 "hw:0"
> ------------------------------
Wolfgang
--
Wolfgang Pfeiffer: /ICQ: 286585973/ + + + /AIM: crashinglinux/
http://profiles.yahoo.com/wolfgangpfeiffer
Key ID: E3037113
http://keyserver.mine.nu/pks/lookup?search=0xE3037113&fingerprint=on
^ permalink raw reply
* Re: [PATCH/2.6.17-rc4 7/10] Powerpc: workaround for tsi108 pci confi g read exception
From: Benjamin Herrenschmidt @ 2006-05-18 0:56 UTC (permalink / raw)
To: Zang Roy-r61911
Cc: linuxppc-dev list, Yang Xin-Xin-r48390, Paul Mackerras,
Alexandre.Bounine
In-Reply-To: <9FCDBA58F226D911B202000BDBAD46730626D624@zch01exm40.ap.freescale.net>
On Wed, 2006-05-17 at 18:14 +0800, Zang Roy-r61911 wrote:
> Workaround for Tundra Semiconductor tsi108 host bridge pci config read
> exception
Use ppc_md.machine_check_exception instead of modifying the generic
traps.c. Also make sure you can't configure your chip not to issue
machine checks on target/master aborts on config space accesses....
Ben.
> Signed-off-by: Alexandre Bounine <alexandreb@tundra.com>
> Signed-off-by: Roy Zang <tie-fei.zang@freescale.com>
>
> ---
>
> arch/powerpc/kernel/traps.c | 13 +++++++++++++
> 1 files changed, 13 insertions(+), 0 deletions(-)
>
> 0575fbe21e4f1045528bb91ec4b34bb7955c4a92
> diff --git a/arch/powerpc/kernel/traps.c b/arch/powerpc/kernel/traps.c
> index 064a525..7468d76 100644
> --- a/arch/powerpc/kernel/traps.c
> +++ b/arch/powerpc/kernel/traps.c
> @@ -262,6 +262,19 @@ #if defined(CONFIG_PPC_PMAC) && defined(
> }
> }
> #endif /* CONFIG_PPC_PMAC && CONFIG_PPC32 */
> +
> +#ifdef CONFIG_TSI108_BRIDGE
> + extern void tsi108_clear_pci_cfg_error(void);
> + const struct exception_table_entry *entry;
> +
> + /* Are we prepared to handle this fault? */
> + if ((entry = search_exception_tables(regs->nip)) != NULL) {
> + tsi108_clear_pci_cfg_error();
> + regs->msr |= MSR_RI;
> + regs->nip = entry->fixup;
> + return 1;
> + }
> +#endif /* CONFIG_TSI108_BRIDGE */
> return 0;
> }
>
^ permalink raw reply
* Re: [PATCH/2.6.17-rc4 5/10] Add tsi108 Ethernet support
From: Benjamin Herrenschmidt @ 2006-05-18 0:53 UTC (permalink / raw)
To: Zang Roy-r61911
Cc: linuxppc-dev list, Yang Xin-Xin-r48390, Paul Mackerras,
Alexandre.Bounine
In-Reply-To: <9FCDBA58F226D911B202000BDBAD46730626D622@zch01exm40.ap.freescale.net>
On Wed, 2006-05-17 at 18:14 +0800, Zang Roy-r61911 wrote:
> This patch adds a device driver and configuration options for
> Tundra Semiconductor Tsi108 integrated dual port Gigabit Ethernet controller
In addition to what Kumar says, you should kill that table based probing
and use the device-tree instead (which can also provide interrupt
routing and MAC address)
Ben.
> Signed-off-by: Alexandre Bounine <alexandreb@tundra.com>
> Signed-off-by: Roy Zang <tie-fei.zang@freescale.com>
>
> ---
>
> drivers/net/Kconfig | 8
> drivers/net/Makefile | 1
> drivers/net/tsi108_eth.c | 1740 ++++++++++++++++++++++++++++++++++++++++++++++
> drivers/net/tsi108_eth.h | 404 +++++++++++
> 4 files changed, 2153 insertions(+), 0 deletions(-)
> create mode 100644 drivers/net/tsi108_eth.c
> create mode 100644 drivers/net/tsi108_eth.h
>
> 23ed1c55ab6bc7a4c1c76f646579a1bc195bc7d2
> diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig
> index bdaaad8..8a4ef29 100644
> --- a/drivers/net/Kconfig
> +++ b/drivers/net/Kconfig
> @@ -2177,6 +2177,14 @@ config SPIDER_NET
> This driver supports the Gigabit Ethernet chips present on the
> Cell Processor-Based Blades from IBM.
>
> +config TSI108_ETH
> + tristate "Tundra TSI108 gigabit Ethernet support"
> + depends on TSI108_BRIDGE
> + help
> + This driver supports Tundra TSI108 gigabit Ethernet ports.
> + To compile this driver as a module, choose M here: the module
> + will be called tsi108_eth.
> +
> config GIANFAR
> tristate "Gianfar Ethernet"
> depends on 85xx || 83xx
> diff --git a/drivers/net/Makefile b/drivers/net/Makefile
> index b90468a..5f90b30 100644
> --- a/drivers/net/Makefile
> +++ b/drivers/net/Makefile
> @@ -109,6 +109,7 @@ obj-$(CONFIG_B44) += b44.o
> obj-$(CONFIG_FORCEDETH) += forcedeth.o
> obj-$(CONFIG_NE_H8300) += ne-h8300.o 8390.o
>
> +obj-$(CONFIG_TSI108_ETH) += tsi108_eth.o
> obj-$(CONFIG_MV643XX_ETH) += mv643xx_eth.o
>
> obj-$(CONFIG_PPP) += ppp_generic.o slhc.o
> diff --git a/drivers/net/tsi108_eth.c b/drivers/net/tsi108_eth.c
> new file mode 100644
> index 0000000..cb67dbe
> --- /dev/null
> +++ b/drivers/net/tsi108_eth.c
> @@ -0,0 +1,1740 @@
> +/*******************************************************************************
> +
> + Copyright(c) 2005 Tundra Semiconductor Corporation.
> +
> + This program is free software; you can redistribute it and/or modify it
> + under the terms of the GNU General Public License as published by the Free
> + Software Foundation; either version 2 of the License, or (at your option)
> + any later version.
> +
> + This program is distributed in the hope that it will be useful, but WITHOUT
> + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
> + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
> + more details.
> +
> + You should have received a copy of the GNU General Public License along with
> + this program; if not, write to the Free Software Foundation, Inc., 59
> + Temple Place - Suite 330, Boston, MA 02111-1307, USA.
> +
> +*******************************************************************************/
> +
> +/* This driver is based on the driver code originally developed
> + * for the Intel IOC80314 (ForestLake) Gigabit Ethernet by
> + * scott.wood@timesys.com * Copyright (C) 2003 TimeSys Corporation
> + *
> + * Currently changes from original version are:
> + * - portig to Tsi108-based platform and kernel 2.6 (kong.lai@tundra.com)
> + * - modifications to handle two ports independently and support for
> + * additional PHY devices (alexandre.bounine@tundra.com)
> + *
> + */
> +
> +#include <linux/config.h>
> +#include <linux/module.h>
> +#include <linux/types.h>
> +#include <linux/init.h>
> +#include <linux/net.h>
> +#include <linux/netdevice.h>
> +#include <linux/etherdevice.h>
> +#include <linux/skbuff.h>
> +#include <linux/slab.h>
> +#include <linux/sched.h>
> +#include <linux/spinlock.h>
> +#include <linux/delay.h>
> +#include <linux/crc32.h>
> +#include <linux/mii.h>
> +#include <linux/device.h>
> +#include <asm/system.h>
> +#include <asm/io.h>
> +#include <linux/pci.h>
> +#include <linux/rtnetlink.h>
> +#include <linux/timer.h>
> +
> +#include <asm/tsi108_irq.h>
> +#include <asm/tsi108.h>
> +#include "tsi108_eth.h"
> +
> +typedef struct net_device net_device;
> +typedef struct sk_buff sk_buff;
> +
> +#define MII_READ_DELAY 10000 /* max link wait time in msec */
> +
> +#define TSI108_RXRING_LEN 256
> +
> +/* NOTE: The driver currently does not support receiving packets
> + * larger than the buffer size, so don't decrease this (unless you
> + * want to add such support).
> + */
> +#define TSI108_RXBUF_SIZE 1536
> +
> +#define TSI108_TXRING_LEN 256
> +
> +#define TSI108_TX_INT_FREQ 64
> +
> +/* Check the phy status every half a second. */
> +#define CHECK_PHY_INTERVAL (HZ/2)
> +
> +extern hw_info hw_info_table[];
> +
> +typedef struct {
> + volatile u32 regs; /* Base of normal regs */
> + volatile u32 phyregs; /* Base of register bank used for PHY access */
> + int phy; /* Index of PHY for this interface */
> + int irq_num;
> +
> + struct timer_list timer;/* Timer that triggers the check phy function */
> + int rxtail; /* Next entry in rxring to read */
> + int rxhead; /* Next entry in rxring to give a new buffer */
> + int rxfree; /* Number of free, allocated RX buffers */
> +
> + int rxpending; /* Non-zero if there are still descriptors
> + * to be processed from a previous descriptor
> + * interrupt condition that has been cleared */
> +
> + int txtail; /* Next TX descriptor to check status on */
> + int txhead; /* Next TX descriptor to use */
> +
> + /* Number of free TX descriptors. This could be calculated from
> + * rxhead and rxtail if one descriptor were left unused to disambiguate
> + * full and empty conditions, but it's simpler to just keep track
> + * explicitly. */
> +
> + int txfree;
> +
> + int phy_ok; /* The PHY is currently powered on. */
> +
> + /* PHY status (duplex is 1 for half, 2 for full,
> + * so that the default 0 indicates that neither has
> + * yet been configured). */
> +
> + int link_up;
> + int speed;
> + int duplex;
> +
> + tx_desc *txring;
> + rx_desc *rxring;
> + sk_buff *txskbs[TSI108_TXRING_LEN];
> + sk_buff *rxskbs[TSI108_RXRING_LEN];
> +
> + dma_addr_t txdma, rxdma;
> +
> + /* txlock nests in misclock and phy_lock */
> +
> + spinlock_t txlock, misclock;
> +
> + /* stats is used to hold the upper bits of each hardware counter,
> + * and tmpstats is used to hold the full values for returning
> + * to the caller of get_stats(). They must be separate in case
> + * an overflow interrupt occurs before the stats are consumed.
> + */
> +
> + struct net_device_stats stats;
> + struct net_device_stats tmpstats;
> +
> + /* These stats are kept separate in hardware, thus require individual
> + * fields for handling carry. They are combined in get_stats.
> + */
> +
> + unsigned long rx_fcs; /* Add to rx_frame_errors */
> + unsigned long rx_short_fcs; /* Add to rx_frame_errors */
> + unsigned long rx_long_fcs; /* Add to rx_frame_errors */
> + unsigned long rx_underruns; /* Add to rx_length_errors */
> + unsigned long rx_overruns; /* Add to rx_length_errors */
> +
> + unsigned long tx_coll_abort; /* Add to tx_aborted_errors/collisions */
> + unsigned long tx_pause_drop; /* Add to tx_aborted_errors */
> +
> + unsigned long mc_hash[16];
> +} tsi108_prv_data;
> +static void tsi108_timed_checker(unsigned long dev_ptr);
> +
> +static net_device *tsi108_devs[TSI108_ETH_PORT_NUM];
> +
> +static void dump_eth_one(net_device * dev)
> +{
> + tsi108_prv_data *data = netdev_priv(dev);
> +
> + printk("Dumping %s...\n", dev->name);
> + printk("intstat %x intmask %x phy_ok %d"
> + " link %d speed %d duplex %d\n",
> + TSI108_ETH_READ_REG(TSI108_EC_INTSTAT),
> + TSI108_ETH_READ_REG(TSI108_EC_INTMASK), data->phy_ok,
> + data->link_up, data->speed, data->duplex);
> +
> + printk("TX: head %d, tail %d, free %d, stat %x, estat %x, err %x\n",
> + data->txhead, data->txtail, data->txfree,
> + TSI108_ETH_READ_REG(TSI108_EC_TXSTAT),
> + TSI108_ETH_READ_REG(TSI108_EC_TXESTAT),
> + TSI108_ETH_READ_REG(TSI108_EC_TXERR));
> +
> + printk("RX: head %d, tail %d, free %d, stat %x,"
> + " estat %x, err %x, pending %d\n\n",
> + data->rxhead, data->rxtail, data->rxfree,
> + TSI108_ETH_READ_REG(TSI108_EC_RXSTAT),
> + TSI108_ETH_READ_REG(TSI108_EC_RXESTAT),
> + TSI108_ETH_READ_REG(TSI108_EC_RXERR), data->rxpending);
> +}
> +
> +void tsi108_dump_eth(void)
> +{
> + dump_eth_one(tsi108_devs[0]);
> + dump_eth_one(tsi108_devs[1]);
> +}
> +
> +/* Synchronization is needed between the thread and up/down events.
> + * Note that the PHY is accessed through the same registers for both
> + * interfaces, so this can't be made interface-specific.
> + */
> +
> +static spinlock_t phy_lock = SPIN_LOCK_UNLOCKED;
> +
> +static inline u16 tsi108_read_mii(tsi108_prv_data * data, int reg, int *status)
> +{
> + int i;
> + u16 ret;
> +
> + TSI108_ETH_WRITE_PHYREG(TSI108_MAC_MII_ADDR,
> + (data->phy << TSI108_MAC_MII_ADDR_PHY) |
> + (reg << TSI108_MAC_MII_ADDR_REG));
> + mb();
> + TSI108_ETH_WRITE_PHYREG(TSI108_MAC_MII_CMD, 0);
> + mb();
> + TSI108_ETH_WRITE_PHYREG(TSI108_MAC_MII_CMD, TSI108_MAC_MII_CMD_READ);
> + mb();
> + for (i = 0; i < 100; i++) {
> + if (!(TSI108_ETH_READ_PHYREG(TSI108_MAC_MII_IND) &
> + (TSI108_MAC_MII_IND_NOTVALID | TSI108_MAC_MII_IND_BUSY)))
> + break;
> + udelay(10);
> + }
> +
> + if (i == 100) {
> + if (status)
> + *status = -EBUSY;
> +
> + ret = 0xffff;
> + } else {
> + if (status)
> + *status = 0;
> +
> + ret = TSI108_ETH_READ_PHYREG(TSI108_MAC_MII_DATAIN);
> + }
> +
> + return ret;
> +}
> +
> +static inline void tsi108_write_mii(tsi108_prv_data * data, int reg, u16 val)
> +{
> + TSI108_ETH_WRITE_PHYREG(TSI108_MAC_MII_ADDR,
> + (data->phy << TSI108_MAC_MII_ADDR_PHY) |
> + (reg << TSI108_MAC_MII_ADDR_REG));
> + mb();
> + TSI108_ETH_WRITE_PHYREG(TSI108_MAC_MII_DATAOUT, val);
> + mb();
> + while (TSI108_ETH_READ_PHYREG(TSI108_MAC_MII_IND) &
> + TSI108_MAC_MII_IND_BUSY) ;
> +}
> +
> +static inline void tsi108_write_tbi(tsi108_prv_data * data, int reg, u16 val)
> +{
> +
> + TSI108_ETH_WRITE_REG(TSI108_MAC_MII_ADDR,
> + (0x1e << TSI108_MAC_MII_ADDR_PHY)
> + | (reg << TSI108_MAC_MII_ADDR_REG));
> + mb();
> +
> + TSI108_ETH_WRITE_REG(TSI108_MAC_MII_DATAOUT, val);
> + mb();
> + while (TSI108_ETH_READ_REG(TSI108_MAC_MII_IND) &
> + TSI108_MAC_MII_IND_BUSY) ;
> +}
> +
> +static void tsi108_check_phy(net_device * dev)
> +{
> + tsi108_prv_data *data = netdev_priv(dev);
> + u16 sumstat;
> + u32 mac_cfg2_reg, portctrl_reg;
> + u32 fdx_flag = 0, reg_update = 0;
> +
> + /* Do a dummy read, as for some reason the first read
> + * after a link becomes up returns link down, even if
> + * it's been a while since the link came up.
> + */
> +
> + spin_lock(&phy_lock);
> +
> + if (!data->phy_ok)
> + goto out;
> +
> + tsi108_read_mii(data, PHY_STAT, NULL);
> +
> + if (!(tsi108_read_mii(data, PHY_STAT, NULL) & PHY_STAT_LINKUP)) {
> + if (data->link_up == 1) {
> + netif_stop_queue(dev);
> + data->link_up = 0;
> + printk(KERN_NOTICE "%s : link is down\n", dev->name);
> + netif_carrier_off(dev);
> + }
> +
> + goto out;
> + }
> +
> + {
> + mac_cfg2_reg = TSI108_ETH_READ_REG(TSI108_MAC_CFG2);
> + portctrl_reg = TSI108_ETH_READ_REG(TSI108_EC_PORTCTRL);
> +
> + sumstat = tsi108_read_mii(data, PHY_SUM_STAT, NULL);
> +
> + switch (sumstat & PHY_SUM_STAT_SPEED_MASK) {
> + case PHY_SUM_STAT_1000T_FD:
> + fdx_flag++;
> + case PHY_SUM_STAT_1000T_HD:
> + if (data->speed != 1000) {
> + mac_cfg2_reg &= ~TSI108_MAC_CFG2_IFACE_MASK;
> + mac_cfg2_reg |= TSI108_MAC_CFG2_GIG;
> + portctrl_reg &= ~TSI108_EC_PORTCTRL_NOGIG;
> + data->speed = 1000;
> + reg_update++;
> + }
> + break;
> + case PHY_SUM_STAT_100TX_FD:
> + fdx_flag++;
> + case PHY_SUM_STAT_100TX_HD:
> + if (data->speed != 100) {
> + mac_cfg2_reg &= ~TSI108_MAC_CFG2_IFACE_MASK;
> + mac_cfg2_reg |= TSI108_MAC_CFG2_NOGIG;
> + portctrl_reg |= TSI108_EC_PORTCTRL_NOGIG;
> + data->speed = 100;
> + reg_update++;
> + }
> + break;
> +
> + case PHY_SUM_STAT_10T_FD:
> + fdx_flag++;
> + case PHY_SUM_STAT_10T_HD:
> + if (data->speed != 10) {
> + mac_cfg2_reg &= ~TSI108_MAC_CFG2_IFACE_MASK;
> + mac_cfg2_reg |= TSI108_MAC_CFG2_NOGIG;
> + portctrl_reg |= TSI108_EC_PORTCTRL_NOGIG;
> + data->speed = 10;
> + reg_update++;
> + }
> + break;
> +
> + default:
> + if (net_ratelimit())
> + printk(KERN_ERR "PHY reported invalid speed,"
> + KERN_ERR " summary status %x\n",
> + sumstat);
> + goto out;
> + }
> +
> + if (fdx_flag || (sumstat & PHY_SUM_STAT_FULLDUPLEX)) {
> + if (data->duplex != 2) {
> + mac_cfg2_reg |= TSI108_MAC_CFG2_FULLDUPLEX;
> + portctrl_reg &= ~TSI108_EC_PORTCTRL_HALFDUPLEX;
> + data->duplex = 2;
> + reg_update++;
> + }
> + } else {
> + if (data->duplex != 1) {
> + mac_cfg2_reg &= ~TSI108_MAC_CFG2_FULLDUPLEX;
> + portctrl_reg |= TSI108_EC_PORTCTRL_HALFDUPLEX;
> + data->duplex = 1;
> + reg_update++;
> + }
> + }
> +
> + if (reg_update) {
> + TSI108_ETH_WRITE_REG(TSI108_MAC_CFG2, mac_cfg2_reg);
> + mb();
> + TSI108_ETH_WRITE_REG(TSI108_EC_PORTCTRL, portctrl_reg);
> + mb();
> + }
> +
> + }
> +
> + if (data->link_up == 0) {
> + /* The manual says it can take 3-4 usecs for the speed change
> + * to take effect.
> + */
> + udelay(5);
> +
> + spin_lock(&data->txlock);
> + if (netif_queue_stopped(dev)
> + && is_valid_ether_addr(dev->dev_addr) && data->txfree)
> + netif_wake_queue(dev);
> +
> + data->link_up = 1;
> + spin_unlock(&data->txlock);
> + printk("%s : link is up: %dMb %s-duplex\n",
> + dev->name, data->speed,
> + (data->duplex == 2) ? "full" : "half");
> + netif_carrier_on(dev);
> + }
> +
> + out:
> + spin_unlock(&phy_lock);
> +}
> +
> +static inline void
> +tsi108_stat_carry_one(int carry, int carry_bit, int carry_shift,
> + unsigned long *upper)
> +{
> + if (carry & carry_bit)
> + *upper += carry_shift;
> +}
> +
> +static void tsi108_stat_carry(net_device * dev)
> +{
> + tsi108_prv_data *data = netdev_priv(dev);
> + u32 carry1, carry2;
> +
> + spin_lock_irq(&data->misclock);
> +
> + carry1 = TSI108_ETH_READ_REG(TSI108_STAT_CARRY1);
> + carry2 = TSI108_ETH_READ_REG(TSI108_STAT_CARRY2);
> +
> + TSI108_ETH_WRITE_REG(TSI108_STAT_CARRY1, carry1);
> + TSI108_ETH_WRITE_REG(TSI108_STAT_CARRY2, carry2);
> +
> + tsi108_stat_carry_one(carry1, TSI108_STAT_CARRY1_RXBYTES,
> + TSI108_STAT_RXBYTES_CARRY, &data->stats.rx_bytes);
> +
> + tsi108_stat_carry_one(carry1, TSI108_STAT_CARRY1_RXPKTS,
> + TSI108_STAT_RXPKTS_CARRY,
> + &data->stats.rx_packets);
> +
> + tsi108_stat_carry_one(carry1, TSI108_STAT_CARRY1_RXFCS,
> + TSI108_STAT_RXFCS_CARRY, &data->rx_fcs);
> +
> + tsi108_stat_carry_one(carry1, TSI108_STAT_CARRY1_RXMCAST,
> + TSI108_STAT_RXMCAST_CARRY,
> + &data->stats.multicast);
> +
> + tsi108_stat_carry_one(carry1, TSI108_STAT_CARRY1_RXALIGN,
> + TSI108_STAT_RXALIGN_CARRY,
> + &data->stats.rx_frame_errors);
> +
> + tsi108_stat_carry_one(carry1, TSI108_STAT_CARRY1_RXLENGTH,
> + TSI108_STAT_RXLENGTH_CARRY,
> + &data->stats.rx_length_errors);
> +
> + tsi108_stat_carry_one(carry1, TSI108_STAT_CARRY1_RXRUNT,
> + TSI108_STAT_RXRUNT_CARRY, &data->rx_underruns);
> +
> + tsi108_stat_carry_one(carry1, TSI108_STAT_CARRY1_RXJUMBO,
> + TSI108_STAT_RXJUMBO_CARRY, &data->rx_overruns);
> +
> + tsi108_stat_carry_one(carry1, TSI108_STAT_CARRY1_RXFRAG,
> + TSI108_STAT_RXFRAG_CARRY, &data->rx_short_fcs);
> +
> + tsi108_stat_carry_one(carry1, TSI108_STAT_CARRY1_RXJABBER,
> + TSI108_STAT_RXJABBER_CARRY, &data->rx_long_fcs);
> +
> + tsi108_stat_carry_one(carry1, TSI108_STAT_CARRY1_RXDROP,
> + TSI108_STAT_RXDROP_CARRY,
> + &data->stats.rx_missed_errors);
> +
> + tsi108_stat_carry_one(carry2, TSI108_STAT_CARRY2_TXBYTES,
> + TSI108_STAT_TXBYTES_CARRY, &data->stats.tx_bytes);
> +
> + tsi108_stat_carry_one(carry2, TSI108_STAT_CARRY2_TXPKTS,
> + TSI108_STAT_TXPKTS_CARRY,
> + &data->stats.tx_packets);
> +
> + tsi108_stat_carry_one(carry2, TSI108_STAT_CARRY2_TXEXDEF,
> + TSI108_STAT_TXEXDEF_CARRY,
> + &data->stats.tx_aborted_errors);
> +
> + tsi108_stat_carry_one(carry2, TSI108_STAT_CARRY2_TXEXCOL,
> + TSI108_STAT_TXEXCOL_CARRY, &data->tx_coll_abort);
> +
> + tsi108_stat_carry_one(carry2, TSI108_STAT_CARRY2_TXTCOL,
> + TSI108_STAT_TXTCOL_CARRY,
> + &data->stats.collisions);
> +
> + tsi108_stat_carry_one(carry2, TSI108_STAT_CARRY2_TXPAUSE,
> + TSI108_STAT_TXPAUSEDROP_CARRY,
> + &data->tx_pause_drop);
> +
> + spin_unlock_irq(&data->misclock);
> +}
> +
> +/* Read a stat counter atomically with respect to carries.
> + * data->misclock must be held.
> + */
> +static inline unsigned long
> +tsi108_read_stat(tsi108_prv_data * data, int reg, int carry_bit,
> + int carry_shift, unsigned long *upper)
> +{
> + int carryreg;
> + unsigned long val;
> +
> + if (reg < 0xb0)
> + carryreg = TSI108_STAT_CARRY1;
> + else
> + carryreg = TSI108_STAT_CARRY2;
> +
> + again:
> + val = TSI108_ETH_READ_REG(reg) | *upper;
> +
> + rmb();
> +
> + /* Check to see if it overflowed, but the interrupt hasn't
> + * been serviced yet. If so, handle the carry here, and
> + * try again.
> + */
> +
> + if (unlikely(TSI108_ETH_READ_REG(carryreg) & carry_bit)) {
> + *upper += carry_shift;
> + TSI108_ETH_WRITE_REG(carryreg, carry_bit);
> + mb();
> +
> + goto again;
> + }
> +
> + return val;
> +}
> +
> +static struct net_device_stats *tsi108_get_stats(net_device * dev)
> +{
> + unsigned long excol;
> +
> + tsi108_prv_data *data = netdev_priv(dev);
> + spin_lock_irq(&data->misclock);
> +
> + data->tmpstats.rx_packets =
> + tsi108_read_stat(data, TSI108_STAT_RXPKTS,
> + TSI108_STAT_CARRY1_RXPKTS,
> + TSI108_STAT_RXPKTS_CARRY, &data->stats.rx_packets);
> +
> + data->tmpstats.tx_packets =
> + tsi108_read_stat(data, TSI108_STAT_TXPKTS,
> + TSI108_STAT_CARRY2_TXPKTS,
> + TSI108_STAT_TXPKTS_CARRY, &data->stats.tx_packets);
> +
> + data->tmpstats.rx_bytes =
> + tsi108_read_stat(data, TSI108_STAT_RXBYTES,
> + TSI108_STAT_CARRY1_RXBYTES,
> + TSI108_STAT_RXBYTES_CARRY, &data->stats.rx_bytes);
> +
> + data->tmpstats.tx_bytes =
> + tsi108_read_stat(data, TSI108_STAT_TXBYTES,
> + TSI108_STAT_CARRY2_TXBYTES,
> + TSI108_STAT_TXBYTES_CARRY, &data->stats.tx_bytes);
> +
> + data->tmpstats.multicast =
> + tsi108_read_stat(data, TSI108_STAT_RXMCAST,
> + TSI108_STAT_CARRY1_RXMCAST,
> + TSI108_STAT_RXMCAST_CARRY, &data->stats.multicast);
> +
> + excol = tsi108_read_stat(data, TSI108_STAT_TXEXCOL,
> + TSI108_STAT_CARRY2_TXEXCOL,
> + TSI108_STAT_TXEXCOL_CARRY,
> + &data->tx_coll_abort);
> +
> + data->tmpstats.collisions =
> + tsi108_read_stat(data, TSI108_STAT_TXTCOL,
> + TSI108_STAT_CARRY2_TXTCOL,
> + TSI108_STAT_TXTCOL_CARRY, &data->stats.collisions);
> +
> + data->tmpstats.collisions += excol;
> +
> + data->tmpstats.rx_length_errors =
> + tsi108_read_stat(data, TSI108_STAT_RXLENGTH,
> + TSI108_STAT_CARRY1_RXLENGTH,
> + TSI108_STAT_RXLENGTH_CARRY,
> + &data->stats.rx_length_errors);
> +
> + data->tmpstats.rx_length_errors +=
> + tsi108_read_stat(data, TSI108_STAT_RXRUNT,
> + TSI108_STAT_CARRY1_RXRUNT,
> + TSI108_STAT_RXRUNT_CARRY, &data->rx_underruns);
> +
> + data->tmpstats.rx_length_errors +=
> + tsi108_read_stat(data, TSI108_STAT_RXJUMBO,
> + TSI108_STAT_CARRY1_RXJUMBO,
> + TSI108_STAT_RXJUMBO_CARRY, &data->rx_overruns);
> +
> + data->tmpstats.rx_frame_errors =
> + tsi108_read_stat(data, TSI108_STAT_RXALIGN,
> + TSI108_STAT_CARRY1_RXALIGN,
> + TSI108_STAT_RXALIGN_CARRY,
> + &data->stats.rx_frame_errors);
> +
> + data->tmpstats.rx_frame_errors +=
> + tsi108_read_stat(data, TSI108_STAT_RXFCS,
> + TSI108_STAT_CARRY1_RXFCS, TSI108_STAT_RXFCS_CARRY,
> + &data->rx_fcs);
> +
> + data->tmpstats.rx_frame_errors +=
> + tsi108_read_stat(data, TSI108_STAT_RXFRAG,
> + TSI108_STAT_CARRY1_RXFRAG,
> + TSI108_STAT_RXFRAG_CARRY, &data->rx_short_fcs);
> +
> + data->tmpstats.rx_missed_errors =
> + tsi108_read_stat(data, TSI108_STAT_RXDROP,
> + TSI108_STAT_CARRY1_RXDROP,
> + TSI108_STAT_RXDROP_CARRY,
> + &data->stats.rx_missed_errors);
> +
> + /* These three are maintained by software. */
> + data->tmpstats.rx_fifo_errors = data->stats.rx_fifo_errors;
> + data->tmpstats.rx_crc_errors = data->stats.rx_crc_errors;
> +
> + data->tmpstats.tx_aborted_errors =
> + tsi108_read_stat(data, TSI108_STAT_TXEXDEF,
> + TSI108_STAT_CARRY2_TXEXDEF,
> + TSI108_STAT_TXEXDEF_CARRY,
> + &data->stats.tx_aborted_errors);
> +
> + data->tmpstats.tx_aborted_errors +=
> + tsi108_read_stat(data, TSI108_STAT_TXPAUSEDROP,
> + TSI108_STAT_CARRY2_TXPAUSE,
> + TSI108_STAT_TXPAUSEDROP_CARRY,
> + &data->tx_pause_drop);
> +
> + data->tmpstats.tx_aborted_errors += excol;
> +
> + data->tmpstats.tx_errors = data->tmpstats.tx_aborted_errors;
> + data->tmpstats.rx_errors = data->tmpstats.rx_length_errors +
> + data->tmpstats.rx_crc_errors +
> + data->tmpstats.rx_frame_errors +
> + data->tmpstats.rx_fifo_errors + data->tmpstats.rx_missed_errors;
> +
> + spin_unlock_irq(&data->misclock);
> + return &data->tmpstats;
> +}
> +
> +static void tsi108_restart_rx(tsi108_prv_data * data, net_device * dev)
> +{
> + TSI108_ETH_WRITE_REG(TSI108_EC_RXQ_PTRHIGH,
> + TSI108_EC_RXQ_PTRHIGH_VALID);
> +
> + wmb();
> + TSI108_ETH_WRITE_REG(TSI108_EC_RXCTRL, TSI108_EC_RXCTRL_GO
> + | TSI108_EC_RXCTRL_QUEUE0);
> +}
> +
> +static void tsi108_restart_tx(tsi108_prv_data * data)
> +{
> + TSI108_ETH_WRITE_REG(TSI108_EC_TXQ_PTRHIGH,
> + TSI108_EC_TXQ_PTRHIGH_VALID);
> +
> + wmb();
> + TSI108_ETH_WRITE_REG(TSI108_EC_TXCTRL, TSI108_EC_TXCTRL_IDLEINT |
> + TSI108_EC_TXCTRL_GO | TSI108_EC_TXCTRL_QUEUE0);
> +}
> +
> +/* txlock must be held by caller, with IRQs disabled, and
> + * with permission to re-enable them when the lock is dropped.
> + */
> +static void tsi108_check_for_completed_tx(net_device * dev)
> +{
> + tsi108_prv_data *data = netdev_priv(dev);
> + int tx;
> + struct sk_buff *skb;
> + int release = 0;
> +
> + while (!data->txfree || data->txhead != data->txtail) {
> + tx = data->txtail;
> +
> + if (data->txring[tx].misc & TSI108_TX_OWN)
> + break;
> +
> + skb = data->txskbs[tx];
> +
> + if (!(data->txring[tx].misc & TSI108_TX_OK))
> + printk("%s: bad tx packet, misc %x\n",
> + dev->name, data->txring[tx].misc);
> +
> + data->txtail = (data->txtail + 1) % TSI108_TXRING_LEN;
> + data->txfree++;
> +
> + if (data->txring[tx].misc & TSI108_TX_EOF) {
> + dev_kfree_skb_any(skb);
> + release++;
> + }
> + }
> +
> + if (release) {
> +
> + if (netif_queue_stopped(dev)
> + && is_valid_ether_addr(dev->dev_addr) && data->link_up)
> + netif_wake_queue(dev);
> + }
> +}
> +
> +static int tsi108_send_packet(sk_buff * skb, net_device * dev)
> +{
> + tsi108_prv_data *data = netdev_priv(dev);
> + int frags = skb_shinfo(skb)->nr_frags + 1;
> + int i;
> +#ifdef FIXME_SG_CSUM_NOT_TESTED /* FIXME: Not supported now. */
> + long csstart;
> + long csum;
> +
> + csstart = skb->len - skb->data_len;
> + if (csstart > skb->len - skb->data_len)
> + BUG();
> + csum = 0;
> + if (csstart != skb->len)
> + csum = skb_checksum(skb, csstart, skb->len - csstart, 0);
> +#endif
> +
> + if (!data->phy_ok && net_ratelimit())
> + printk(KERN_ERR "%s: Transmit while PHY is down!\n", dev->name);
> +
> + if (!data->link_up) {
> + printk(KERN_ERR "%s: Transmit while link is down!\n",
> + dev->name);
> + netif_stop_queue(dev);
> + return 1;
> + }
> +
> + if (data->txfree < MAX_SKB_FRAGS + 1) {
> + netif_stop_queue(dev);
> +
> + if (net_ratelimit())
> + printk(KERN_ERR "%s: Transmit with full tx ring!\n",
> + dev->name);
> + return 1;
> + }
> +
> + if (data->txfree - frags < MAX_SKB_FRAGS + 1) {
> + netif_stop_queue(dev);
> + }
> +
> + spin_lock_irq(&data->txlock);
> +
> + for (i = 0; i < frags; i++) {
> + int misc = 0;
> + int tx = data->txhead;
> +
> + /* This is done to mark every TSI108_TX_INT_FREQ tx buffers with
> + * the interrupt bit. TX descriptor-complete interrupts are
> + * enabled when the queue fills up, and masked when there is
> + * still free space. This way, when saturating the outbound
> + * link, the tx interrupts are kept to a reasonable level.
> + * When the queue is not full, reclamation of skbs still occurs
> + * as new packets are transmitted, or on a queue-empty
> + * interrupt.
> + */
> +
> + if ((tx % TSI108_TX_INT_FREQ == 0) &&
> + ((TSI108_TXRING_LEN - data->txfree) >= TSI108_TX_INT_FREQ)
> + )
> + misc = TSI108_TX_INT;
> +
> + data->txskbs[tx] = skb;
> +
> + if (i == 0) {
> + data->txring[tx].buf0 = virt_to_phys(skb->data);
> + data->txring[tx].len = skb->len - skb->data_len;
> + misc |= TSI108_TX_SOF;
> + } else {
> + skb_frag_t *frag = &skb_shinfo(skb)->frags[i - 1];
> +
> + data->txring[tx].buf0 =
> + page_to_phys(frag->page) + frag->page_offset;
> + data->txring[tx].len = frag->size;
> + }
> +
> + if (i == frags - 1)
> + misc |= TSI108_TX_EOF;
> +
> +#ifdef TSI108_PRINT_TX_FRAME
> + {
> + int i;
> + printk("%s: Tx Frame contents (%d)\n", dev->name,
> + skb->len);
> + for (i = 0; i < skb->len; i++)
> + printk(" %2.2x", skb->data[i]);
> + printk(".\n");
> + }
> +#endif /* TSI108_PRINT_TX_FRAME */
> +
> + mb();
> + data->txring[tx].misc = misc | TSI108_TX_OWN;
> +
> + data->txhead = (data->txhead + 1) % TSI108_TXRING_LEN;
> + data->txfree--;
> + }
> +
> + tsi108_check_for_completed_tx(dev);
> +
> + /* This must be done after the check for completed tx descriptors,
> + * so that the tail pointer is correct.
> + */
> +
> + if (!(TSI108_ETH_READ_REG(TSI108_EC_TXSTAT) & TSI108_EC_TXSTAT_QUEUE0))
> + tsi108_restart_tx(data);
> +
> + spin_unlock_irq(&data->txlock);
> + return 0;
> +}
> +
> +static int tsi108_check_for_completed_rx(net_device * dev, int budget)
> +{
> + tsi108_prv_data *data = netdev_priv(dev);
> + int done = 0;
> +
> + while (data->rxfree && done != budget) {
> + int rx = data->rxtail;
> + struct sk_buff *skb;
> +
> + if (data->rxring[rx].misc & TSI108_RX_OWN)
> + break;
> +
> + skb = data->rxskbs[rx];
> + data->rxtail = (data->rxtail + 1) % TSI108_RXRING_LEN;
> + data->rxfree--;
> + done++;
> +
> + if (data->rxring[rx].misc & TSI108_RX_BAD) {
> + spin_lock_irq(&data->misclock);
> +
> + if (data->rxring[rx].misc & TSI108_RX_CRC)
> + data->stats.rx_crc_errors++;
> + if (data->rxring[rx].misc & TSI108_RX_OVER)
> + data->stats.rx_fifo_errors++;
> +
> + spin_unlock_irq(&data->misclock);
> +
> + dev_kfree_skb_any(skb);
> + continue;
> + }
> +#ifdef TSI108_PRINT_RX_FRAME
> + {
> + int i;
> + printk("%s: Rx Frame contents (%d)\n",
> + dev->name, data->rxring[rx].len);
> + for (i = 0; i < data->rxring[rx].len; i++)
> + printk(" %2.2x", skb->data[i]);
> + printk(".\n");
> + }
> +#endif /* TSI108_PRINT_RX_FRAME */
> +
> + skb->dev = dev;
> + skb_put(skb, data->rxring[rx].len);
> + skb->protocol = eth_type_trans(skb, dev);
> + netif_receive_skb(skb);
> + dev->last_rx = jiffies;
> + }
> +
> + return done;
> +}
> +
> +static int tsi108_refill_rx(net_device * dev, int budget)
> +{
> + tsi108_prv_data *data = netdev_priv(dev);
> + int done = 0;
> +
> + while (data->rxfree != TSI108_RXRING_LEN && done != budget) {
> + int rx = data->rxhead;
> + sk_buff *skb;
> +
> + data->rxskbs[rx] = skb = dev_alloc_skb(TSI108_RXBUF_SIZE + 2);
> + if (!skb)
> + break;
> +
> + skb_reserve(skb, 2); /* Align the data on a 4-byte boundary. */
> +
> + data->rxring[rx].buf0 = virt_to_phys(skb->data);
> +
> + /* Sometimes the hardware sets blen to zero after packet
> + * reception, even though the manual says that it's only ever
> + * modified by the driver.
> + */
> +
> + data->rxring[rx].blen = 1536;
> + mb();
> + data->rxring[rx].misc = TSI108_RX_OWN | TSI108_RX_INT;
> +
> + data->rxhead = (data->rxhead + 1) % TSI108_RXRING_LEN;
> + data->rxfree++;
> + done++;
> + }
> +
> + mb();
> +
> + if (done != 0 && !(TSI108_ETH_READ_REG(TSI108_EC_RXSTAT) &
> + TSI108_EC_RXSTAT_QUEUE0))
> + tsi108_restart_rx(data, dev);
> +
> + return done;
> +}
> +
> +static int tsi108_poll(net_device * dev, int *budget)
> +{
> + tsi108_prv_data *data = netdev_priv(dev);
> + u32 estat = TSI108_ETH_READ_REG(TSI108_EC_RXESTAT);
> + u32 intstat = TSI108_ETH_READ_REG(TSI108_EC_INTSTAT);
> + int total_budget = min(*budget, dev->quota);
> + int num_received = 0, num_filled = 0, budget_used;
> +
> + intstat &= TSI108_INT_RXQUEUE0 | TSI108_INT_RXTHRESH |
> + TSI108_INT_RXOVERRUN | TSI108_INT_RXERROR | TSI108_INT_RXWAIT;
> +
> + TSI108_ETH_WRITE_REG(TSI108_EC_RXESTAT, estat);
> + TSI108_ETH_WRITE_REG(TSI108_EC_INTSTAT, intstat);
> +
> + if (data->rxpending || (estat & TSI108_EC_RXESTAT_Q0_DESCINT))
> + num_received = tsi108_check_for_completed_rx(dev, total_budget);
> +
> + /* This should normally fill no more slots than the number of
> + * packets received in tsi108_check_for_completed_rx(). The exception
> + * is when we previously ran out of memory for RX SKBs. In that
> + * case, it's helpful to obey the budget, not only so that the
> + * CPU isn't hogged, but so that memory (which may still be low)
> + * is not hogged by one device.
> + *
> + * A work unit is considered to be two SKBs to allow us to catch
> + * up when the ring has shrunk due to out-of-memory but we're
> + * still removing the full budget's worth of packets each time.
> + */
> +
> + if (data->rxfree < TSI108_RXRING_LEN)
> + num_filled = tsi108_refill_rx(dev, total_budget * 2);
> +
> + if (intstat & TSI108_INT_RXERROR) {
> + u32 err = TSI108_ETH_READ_REG(TSI108_EC_RXERR);
> + TSI108_ETH_WRITE_REG(TSI108_EC_RXERR, err);
> +
> + if (err) {
> + if (net_ratelimit())
> + printk(KERN_DEBUG "%s: RX error %x\n",
> + dev->name, err);
> +
> + if (!(TSI108_ETH_READ_REG(TSI108_EC_RXSTAT) &
> + TSI108_EC_RXSTAT_QUEUE0))
> + tsi108_restart_rx(data, dev);
> + }
> + }
> +
> + if (intstat & TSI108_INT_RXOVERRUN) {
> + spin_lock_irq(&data->misclock);
> + data->stats.rx_fifo_errors++;
> + spin_unlock_irq(&data->misclock);
> + }
> +
> + budget_used = max(num_received, num_filled / 2);
> +
> + *budget -= budget_used;
> + dev->quota -= budget_used;
> +
> + if (budget_used != total_budget) {
> + data->rxpending = 0;
> + netif_rx_complete(dev);
> +
> + TSI108_ETH_WRITE_REG(TSI108_EC_INTMASK,
> + TSI108_ETH_READ_REG(TSI108_EC_INTMASK)
> + & ~(TSI108_INT_RXQUEUE0
> + | TSI108_INT_RXTHRESH |
> + TSI108_INT_RXOVERRUN |
> + TSI108_INT_RXERROR |
> + TSI108_INT_RXWAIT));
> +
> + mb();
> +
> + /* IRQs are level-triggered, so no need to re-check */
> + return 0;
> + } else {
> + data->rxpending = 1;
> + }
> +
> + return 1;
> +}
> +
> +static void tsi108_rx_int(net_device * dev)
> +{
> + tsi108_prv_data *data = netdev_priv(dev);
> +
> + /* A race could cause dev to already be scheduled, so it's not an
> + * error if that happens (and interrupts shouldn't be re-masked,
> + * because that can cause harmful races, if poll has already
> + * unmasked them but not cleared LINK_STATE_SCHED).
> + *
> + * This can happen if this code races with tsi108_poll(), which masks
> + * the interrupts after tsi108_irq_one() read the mask, but before
> + * netif_rx_schedule is called. It could also happen due to calls
> + * from tsi108_check_rxring().
> + */
> +
> + if (netif_rx_schedule_prep(dev)) {
> + /* Mask, rather than ack, the receive interrupts. The ack
> + * will happen in tsi108_poll().
> + */
> +
> + TSI108_ETH_WRITE_REG(TSI108_EC_INTMASK,
> + TSI108_ETH_READ_REG(TSI108_EC_INTMASK) |
> + TSI108_INT_RXQUEUE0
> + | TSI108_INT_RXTHRESH |
> + TSI108_INT_RXOVERRUN | TSI108_INT_RXERROR |
> + TSI108_INT_RXWAIT);
> + mb();
> + __netif_rx_schedule(dev);
> + } else {
> + if (!netif_running(dev)) {
> + /* This can happen if an interrupt occurs while the
> + * interface is being brought down, as the START
> + * bit is cleared before the stop function is called.
> + *
> + * In this case, the interrupts must be masked, or
> + * they will continue indefinitely.
> + *
> + * There's a race here if the interface is brought down
> + * and then up in rapid succession, as the device could
> + * be made running after the above check and before
> + * the masking below. This will only happen if the IRQ
> + * thread has a lower priority than the task brining
> + * up the interface. Fixing this race would likely
> + * require changes in generic code.
> + */
> +
> + TSI108_ETH_WRITE_REG(TSI108_EC_INTMASK,
> + TSI108_ETH_READ_REG
> + (TSI108_EC_INTMASK) |
> + TSI108_INT_RXQUEUE0 |
> + TSI108_INT_RXTHRESH |
> + TSI108_INT_RXOVERRUN |
> + TSI108_INT_RXERROR |
> + TSI108_INT_RXWAIT);
> + mb();
> + }
> + }
> +}
> +
> +/* If the RX ring has run out of memory, try periodically
> + * to allocate some more, as otherwise poll would never
> + * get called (apart from the initial end-of-queue condition).
> + *
> + * This is called once per second (by default) from the thread.
> + */
> +
> +static void tsi108_check_rxring(net_device * dev)
> +{
> + tsi108_prv_data *data = netdev_priv(dev);
> +
> + /* A poll is scheduled, as opposed to caling tsi108_refill_rx
> + * directly, so as to keep the receive path single-threaded
> + * (and thus not needing a lock).
> + */
> +
> + if (netif_running(dev) && data->rxfree < TSI108_RXRING_LEN / 4)
> + tsi108_rx_int(dev);
> +}
> +
> +static void tsi108_tx_int(net_device * dev)
> +{
> + tsi108_prv_data *data = netdev_priv(dev);
> + u32 estat = TSI108_ETH_READ_REG(TSI108_EC_TXESTAT);
> +
> + mb();
> + TSI108_ETH_WRITE_REG(TSI108_EC_TXESTAT, estat);
> + mb();
> + TSI108_ETH_WRITE_REG(TSI108_EC_INTSTAT, TSI108_INT_TXQUEUE0 |
> + TSI108_INT_TXIDLE | TSI108_INT_TXERROR);
> + mb();
> + if (estat & TSI108_EC_TXESTAT_Q0_ERR) {
> + u32 err = TSI108_ETH_READ_REG(TSI108_EC_TXERR);
> + TSI108_ETH_WRITE_REG(TSI108_EC_TXERR, err);
> +
> + if (err && net_ratelimit())
> + printk(KERN_ERR "%s: TX error %x\n", dev->name, err);
> + }
> +
> + if (estat & (TSI108_EC_TXESTAT_Q0_DESCINT | TSI108_EC_TXESTAT_Q0_EOQ)) {
> + spin_lock(&data->txlock);
> + tsi108_check_for_completed_tx(dev);
> + spin_unlock(&data->txlock);
> + }
> +}
> +
> +static irqreturn_t tsi108_irq_one(net_device * dev)
> +{
> + tsi108_prv_data *data = netdev_priv(dev);
> + u32 stat = TSI108_ETH_READ_REG(TSI108_EC_INTSTAT);
> +
> + if (!(stat & TSI108_INT_ANY))
> + return IRQ_NONE; /* Not our interrupt */
> +
> + stat &= ~TSI108_ETH_READ_REG(TSI108_EC_INTMASK);
> +
> + if (stat & (TSI108_INT_TXQUEUE0 | TSI108_INT_TXIDLE |
> + TSI108_INT_TXERROR))
> + tsi108_tx_int(dev);
> + if (stat & (TSI108_INT_RXQUEUE0 | TSI108_INT_RXTHRESH |
> + TSI108_INT_RXWAIT | TSI108_INT_RXOVERRUN |
> + TSI108_INT_RXERROR))
> + tsi108_rx_int(dev);
> +
> + if (stat & TSI108_INT_SFN) {
> + if (net_ratelimit())
> + printk(KERN_DEBUG "%s: SFN error\n", dev->name);
> + TSI108_ETH_WRITE_REG(TSI108_EC_INTSTAT, TSI108_INT_SFN);
> + }
> +
> + if (stat & TSI108_INT_STATCARRY) {
> + tsi108_stat_carry(dev);
> + TSI108_ETH_WRITE_REG(TSI108_EC_INTSTAT, TSI108_INT_STATCARRY);
> + }
> +
> + return IRQ_HANDLED;
> +}
> +
> +static irqreturn_t tsi108_irq(int irq, void *dev_id, struct pt_regs *regs)
> +{
> + if ((IRQ_TSI108_GIGE0 != irq) && (IRQ_TSI108_GIGE1 != irq))
> + return IRQ_NONE; /* Not our interrupt */
> +
> + return tsi108_irq_one((struct net_device *)dev_id);
> +}
> +
> +static void tsi108_stop_ethernet(net_device * dev)
> +{
> + tsi108_prv_data *data = netdev_priv(dev);
> +
> + /* Disable all TX and RX queues ... */
> + TSI108_ETH_WRITE_REG(TSI108_EC_TXCTRL, 0);
> + TSI108_ETH_WRITE_REG(TSI108_EC_RXCTRL, 0);
> +
> + /* ...and wait for them to become idle */
> + mb();
> + while (TSI108_ETH_READ_REG(TSI108_EC_TXSTAT) &
> + TSI108_EC_TXSTAT_ACTIVE) ;
> + while (TSI108_ETH_READ_REG(TSI108_EC_RXSTAT) &
> + TSI108_EC_RXSTAT_ACTIVE) ;
> +}
> +
> +static void tsi108_reset_ether(tsi108_prv_data * data)
> +{
> + TSI108_ETH_WRITE_REG(TSI108_MAC_CFG1, TSI108_MAC_CFG1_SOFTRST);
> + udelay(100);
> + TSI108_ETH_WRITE_REG(TSI108_MAC_CFG1, 0);
> +
> + TSI108_ETH_WRITE_REG(TSI108_EC_PORTCTRL, TSI108_EC_PORTCTRL_STATRST);
> + udelay(100);
> + TSI108_ETH_WRITE_REG(TSI108_EC_PORTCTRL,
> + TSI108_ETH_READ_REG(TSI108_EC_PORTCTRL) &
> + ~TSI108_EC_PORTCTRL_STATRST);
> +
> + TSI108_ETH_WRITE_REG(TSI108_EC_TXCFG, TSI108_EC_TXCFG_RST);
> + udelay(100);
> + TSI108_ETH_WRITE_REG(TSI108_EC_TXCFG,
> + TSI108_ETH_READ_REG(TSI108_EC_TXCFG) &
> + ~TSI108_EC_TXCFG_RST);
> +
> + TSI108_ETH_WRITE_REG(TSI108_EC_RXCFG, TSI108_EC_RXCFG_RST);
> + udelay(100);
> + TSI108_ETH_WRITE_REG(TSI108_EC_RXCFG,
> + TSI108_ETH_READ_REG(TSI108_EC_RXCFG) &
> + ~TSI108_EC_RXCFG_RST);
> +
> + TSI108_ETH_WRITE_REG(TSI108_MAC_MII_MGMT_CFG,
> + TSI108_ETH_READ_REG(TSI108_MAC_MII_MGMT_CFG) |
> + TSI108_MAC_MII_MGMT_RST);
> + udelay(100);
> + TSI108_ETH_WRITE_REG(TSI108_MAC_MII_MGMT_CFG,
> + TSI108_ETH_READ_REG(TSI108_MAC_MII_MGMT_CFG) &
> + ~(TSI108_MAC_MII_MGMT_RST |
> + TSI108_MAC_MII_MGMT_CLK));
> +
> + TSI108_ETH_WRITE_REG(TSI108_MAC_MII_MGMT_CFG,
> + TSI108_ETH_READ_REG(TSI108_MAC_MII_MGMT_CFG) |
> + TSI108_MAC_MII_MGMT_CLK);
> +}
> +
> +static int tsi108_get_mac(net_device * dev)
> +{
> + tsi108_prv_data *data = netdev_priv(dev);
> +
> + u32 word1 = TSI108_ETH_READ_REG(TSI108_MAC_ADDR1);
> + u32 word2 = TSI108_ETH_READ_REG(TSI108_MAC_ADDR2);
> +
> + /* Note that the octets are reversed from what the manual says,
> + * producing an even weirder ordering...
> + */
> + if (word2 == 0 && word1 == 0) {
> + dev->dev_addr[0] = 0x00;
> + dev->dev_addr[1] = 0x06;
> + dev->dev_addr[2] = 0xd2;
> + dev->dev_addr[3] = 0x00;
> + dev->dev_addr[4] = 0x00;
> + if (0x8 == data->phy)
> + dev->dev_addr[5] = 0x01;
> + else
> + dev->dev_addr[5] = 0x02;
> +
> + word2 = (dev->dev_addr[0] << 16) | (dev->dev_addr[1] << 24);
> +
> + word1 = (dev->dev_addr[2] << 0) | (dev->dev_addr[3] << 8) |
> + (dev->dev_addr[4] << 16) | (dev->dev_addr[5] << 24);
> +
> + TSI108_ETH_WRITE_REG(TSI108_MAC_ADDR1, word1);
> + TSI108_ETH_WRITE_REG(TSI108_MAC_ADDR2, word2);
> + } else {
> + dev->dev_addr[0] = (word2 >> 16) & 0xff;
> + dev->dev_addr[1] = (word2 >> 24) & 0xff;
> + dev->dev_addr[2] = (word1 >> 0) & 0xff;
> + dev->dev_addr[3] = (word1 >> 8) & 0xff;
> + dev->dev_addr[4] = (word1 >> 16) & 0xff;
> + dev->dev_addr[5] = (word1 >> 24) & 0xff;
> + }
> +
> + if (!is_valid_ether_addr(dev->dev_addr)) {
> + printk("KERN_ERR: word1: %08x, word2: %08x\n", word1, word2);
> + return -EINVAL;
> + }
> +
> + return 0;
> +}
> +
> +static int tsi108_set_mac(net_device * dev, void *addr)
> +{
> + tsi108_prv_data *data = netdev_priv(dev);
> + u32 word1, word2;
> + int i;
> +
> + if (!is_valid_ether_addr(addr))
> + return -EINVAL;
> +
> + for (i = 0; i < 6; i++)
> + /* +2 is for the offset of the HW addr type */
> + dev->dev_addr[i] = ((unsigned char *)addr)[i + 2];
> +
> + word2 = (dev->dev_addr[0] << 16) | (dev->dev_addr[1] << 24);
> +
> + word1 = (dev->dev_addr[2] << 0) | (dev->dev_addr[3] << 8) |
> + (dev->dev_addr[4] << 16) | (dev->dev_addr[5] << 24);
> +
> + spin_lock_irq(&data->misclock);
> + TSI108_ETH_WRITE_REG(TSI108_MAC_ADDR1, word1);
> + TSI108_ETH_WRITE_REG(TSI108_MAC_ADDR2, word2);
> + spin_lock(&data->txlock);
> +
> + if (netif_queue_stopped(dev) && data->txfree && data->link_up)
> + netif_wake_queue(dev);
> +
> + spin_unlock(&data->txlock);
> + spin_unlock_irq(&data->misclock);
> + return 0;
> +}
> +
> +/* Protected by dev->xmit_lock. */
> +static void tsi108_set_rx_mode(net_device * dev)
> +{
> + tsi108_prv_data *data = netdev_priv(dev);
> + u32 rxcfg = TSI108_ETH_READ_REG(TSI108_EC_RXCFG);
> +
> + if (dev->flags & IFF_PROMISC) {
> + rxcfg &= ~(TSI108_EC_RXCFG_UC_HASH | TSI108_EC_RXCFG_MC_HASH);
> + rxcfg |= TSI108_EC_RXCFG_UFE | TSI108_EC_RXCFG_MFE;
> + goto out;
> + }
> +
> + rxcfg &= ~(TSI108_EC_RXCFG_UFE | TSI108_EC_RXCFG_MFE);
> +
> + if (dev->mc_count) {
> + int i;
> + struct dev_mc_list *mc = dev->mc_list;
> + rxcfg |= TSI108_EC_RXCFG_MFE | TSI108_EC_RXCFG_MC_HASH;
> +
> + memset(data->mc_hash, 0, sizeof(data->mc_hash));
> +
> + while (mc) {
> + u32 hash, crc;
> +
> + if (mc->dmi_addrlen == 6) {
> + crc = ether_crc(6, mc->dmi_addr);
> + hash = crc >> 23;
> +
> + __set_bit(hash, &data->mc_hash[0]);
> + } else {
> + printk(KERN_ERR
> + "%s: got multicast address of length %d "
> + "instead of 6.\n", dev->name,
> + mc->dmi_addrlen);
> + }
> +
> + mc = mc->next;
> + }
> +
> + TSI108_ETH_WRITE_REG(TSI108_EC_HASHADDR,
> + TSI108_EC_HASHADDR_AUTOINC |
> + TSI108_EC_HASHADDR_MCAST);
> +
> + for (i = 0; i < 16; i++) {
> + /* The manual says that the hardware may drop
> + * back-to-back writes to the data register.
> + */
> + udelay(1);
> + mb();
> + TSI108_ETH_WRITE_REG(TSI108_EC_HASHDATA,
> + data->mc_hash[i]);
> + mb();
> + }
> + }
> +
> + out:
> + mb();
> + TSI108_ETH_WRITE_REG(TSI108_EC_RXCFG, rxcfg);
> +}
> +
> +static void tsi108_init_phy(net_device * dev)
> +{
> + tsi108_prv_data *data = netdev_priv(dev);
> + u32 i = 0;
> + u16 phyVal = 0;
> +
> + spin_lock_irq(&phy_lock);
> +
> + tsi108_write_mii(data, PHY_CTRL, PHY_CTRL_RESET);
> + mb();
> + while (tsi108_read_mii(data, PHY_CTRL, NULL) & PHY_CTRL_RESET) ;
> +
> +#if (TSI108_PHY_TYPE == PHY_BCM54XX) /* Broadcom BCM54xx PHY */
> + tsi108_write_mii(data, 0x09, 0x0300);
> + tsi108_write_mii(data, 0x10, 0x1020);
> + tsi108_write_mii(data, 0x1c, 0x8c00);
> + mb();
> +#endif
> +
> + tsi108_write_mii(data,
> + PHY_CTRL,
> + PHY_CTRL_AUTONEG_EN | PHY_CTRL_AUTONEG_START);
> + mb();
> + while (tsi108_read_mii(data, PHY_CTRL, NULL) & PHY_CTRL_AUTONEG_START) ;
> +
> + /* Set G/MII mode and receive clock select in TBI control #2. The
> + * second port won't work if this isn't done, even though we don't
> + * use TBI mode.
> + */
> +
> + tsi108_write_tbi(data, 0x11, 0x30);
> +
> + /* FIXME: It seems to take more than 2 back-to-back reads to the
> + * PHY_STAT register before the link up status bit is set.
> + */
> +
> + data->link_up = 1;
> +
> + while (!((phyVal = tsi108_read_mii(data, PHY_STAT, NULL)) &
> + PHY_STAT_LINKUP)) {
> + if (i++ > (MII_READ_DELAY / 10)) {
> + data->link_up = 0;
> + break;
> + }
> + mdelay(10);
> + }
> +
> + printk(KERN_DEBUG "PHY_STAT reg contains %08x\n", phyVal);
> + data->phy_ok = 1;
> + spin_unlock_irq(&phy_lock);
> +}
> +
> +static void tsi108_kill_phy(struct net_device *dev)
> +{
> + tsi108_prv_data *data = netdev_priv(dev);
> +
> + spin_lock_irq(&phy_lock);
> + tsi108_write_mii(data, PHY_CTRL, PHY_CTRL_POWERDOWN);
> + data->phy_ok = 0;
> + spin_unlock_irq(&phy_lock);
> +}
> +
> +static int tsi108_open(struct net_device *dev)
> +{
> + int i;
> + tsi108_prv_data *data = netdev_priv(dev);
> + unsigned int rxring_size = TSI108_RXRING_LEN * sizeof(rx_desc);
> + unsigned int txring_size = TSI108_TXRING_LEN * sizeof(tx_desc);
> +
> + printk(KERN_DEBUG "Inside tsi108_open()!\n");
> +
> + i = request_irq(data->irq_num, tsi108_irq, 0, dev->name, dev);
> + if (i != 0) {
> + printk(KERN_ERR "tsi108_eth%d: Could not allocate IRQ%d.\n",
> + data->irq_num % IRQ_TSI108_GIGE0, data->irq_num);
> + return i;
> + } else {
> + dev->irq = data->irq_num;
> + printk(KERN_NOTICE
> + "tsi108_open : Port %d Assigned IRQ %d to %s\n",
> + data->irq_num % IRQ_TSI108_GIGE0, dev->irq, dev->name);
> + }
> +
> + data->rxring = pci_alloc_consistent(NULL, rxring_size, &data->rxdma);
> +
> + if (!data->rxring) {
> + printk(KERN_DEBUG
> + "TSI108_ETH: failed to allocate memory for rxring!\n");
> + return -ENOMEM;
> + } else {
> + memset(data->rxring, 0, rxring_size);
> + }
> +
> + data->txring = pci_alloc_consistent(NULL, txring_size, &data->txdma);
> +
> + if (!data->txring) {
> + printk(KERN_DEBUG
> + "TSI108_ETH: failed to allocate memory for txring!\n");
> + pci_free_consistent(0, rxring_size, data->rxring, data->rxdma);
> + return -ENOMEM;
> + } else {
> + memset(data->txring, 0, txring_size);
> + }
> +
> + for (i = 0; i < TSI108_RXRING_LEN; i++) {
> + data->rxring[i].next0 = data->rxdma + (i + 1) * sizeof(rx_desc);
> + data->rxring[i].blen = TSI108_RXBUF_SIZE;
> + data->rxring[i].vlan = 0;
> + }
> +
> + data->rxring[TSI108_RXRING_LEN - 1].next0 = data->rxdma;
> +
> + data->rxtail = 0;
> + data->rxhead = 0;
> +
> + for (i = 0; i < TSI108_RXRING_LEN; i++) {
> + sk_buff *skb = dev_alloc_skb(TSI108_RXBUF_SIZE + NET_IP_ALIGN);
> +
> + if (!skb) {
> + /* Bah. No memory for now, but maybe we'll get
> + * some more later.
> + * For now, we'll live with the smaller ring.
> + */
> + printk(KERN_WARNING
> + "%s: Could only allocate %d receive skb(s).\n",
> + dev->name, i);
> + data->rxhead = i;
> + break;
> + }
> +
> + data->rxskbs[i] = skb;
> + /* Align the payload on a 4-byte boundary */
> + skb_reserve(skb, 2);
> + data->rxskbs[i] = skb;
> + data->rxring[i].buf0 = virt_to_phys(data->rxskbs[i]->data);
> + data->rxring[i].misc = TSI108_RX_OWN | TSI108_RX_INT;
> + }
> +
> + data->rxfree = i;
> + TSI108_ETH_WRITE_REG(TSI108_EC_RXQ_PTRLOW, data->rxdma);
> +
> + for (i = 0; i < TSI108_TXRING_LEN; i++) {
> + data->txring[i].next0 = data->txdma + (i + 1) * sizeof(tx_desc);
> + data->txring[i].misc = 0;
> + }
> +
> + data->txring[TSI108_TXRING_LEN - 1].next0 = data->txdma;
> + data->txtail = 0;
> + data->txhead = 0;
> + data->txfree = TSI108_TXRING_LEN;
> + TSI108_ETH_WRITE_REG(TSI108_EC_TXQ_PTRLOW, data->txdma);
> + tsi108_init_phy(dev);
> +
> + init_timer(&data->timer);
> + data->timer.expires = jiffies + 1;
> + data->timer.data = (unsigned long)dev;
> + data->timer.function = &tsi108_timed_checker; /* timer handler */
> + add_timer(&data->timer);
> +
> + tsi108_restart_rx(data, dev);
> +
> + TSI108_ETH_WRITE_REG(TSI108_EC_INTSTAT, ~0);
> + mb();
> +
> + TSI108_ETH_WRITE_REG(TSI108_EC_INTMASK,
> + ~(TSI108_INT_TXQUEUE0 | TSI108_INT_RXERROR |
> + TSI108_INT_RXTHRESH | TSI108_INT_RXQUEUE0 |
> + TSI108_INT_RXOVERRUN | TSI108_INT_RXWAIT |
> + TSI108_INT_SFN | TSI108_INT_STATCARRY));
> +
> + TSI108_ETH_WRITE_REG(TSI108_MAC_CFG1,
> + TSI108_MAC_CFG1_RXEN | TSI108_MAC_CFG1_TXEN);
> + netif_start_queue(dev);
> + return 0;
> +}
> +
> +static int tsi108_close(net_device * dev)
> +{
> + tsi108_prv_data *data = netdev_priv(dev);
> +
> + netif_stop_queue(dev);
> +
> + del_timer_sync(&data->timer);
> +
> + printk(KERN_DEBUG "Inside tsi108_ifdown!\n");
> +
> + tsi108_stop_ethernet(dev);
> + tsi108_kill_phy(dev);
> + TSI108_ETH_WRITE_REG(TSI108_EC_INTMASK, ~0);
> + TSI108_ETH_WRITE_REG(TSI108_MAC_CFG1, 0);
> +
> + /* Check for any pending TX packets, and drop them. */
> +
> + while (!data->txfree || data->txhead != data->txtail) {
> + int tx = data->txtail;
> + struct sk_buff *skb;
> + skb = data->txskbs[tx];
> + data->txtail = (data->txtail + 1) % TSI108_TXRING_LEN;
> + data->txfree++;
> + dev_kfree_skb(skb);
> + }
> +
> + synchronize_irq(data->irq_num);
> + free_irq(data->irq_num, dev);
> +
> + /* Discard the RX ring. */
> +
> + while (data->rxfree) {
> + int rx = data->rxtail;
> + struct sk_buff *skb;
> +
> + skb = data->rxskbs[rx];
> + data->rxtail = (data->rxtail + 1) % TSI108_RXRING_LEN;
> + data->rxfree--;
> + dev_kfree_skb(skb);
> + }
> +
> + pci_free_consistent(0,
> + TSI108_RXRING_LEN * sizeof(rx_desc),
> + data->rxring, data->rxdma);
> + pci_free_consistent(0,
> + TSI108_TXRING_LEN * sizeof(tx_desc),
> + data->txring, data->txdma);
> +
> + return 0;
> +}
> +
> +static void tsi108_init_mac(net_device * dev)
> +{
> + tsi108_prv_data *data = netdev_priv(dev);
> +
> + TSI108_ETH_WRITE_REG(TSI108_MAC_CFG2, TSI108_MAC_CFG2_DFLT_PREAMBLE |
> + TSI108_MAC_CFG2_PADCRC);
> +
> + TSI108_ETH_WRITE_REG(TSI108_EC_TXTHRESH,
> + (192 << TSI108_EC_TXTHRESH_STARTFILL) |
> + (192 << TSI108_EC_TXTHRESH_STOPFILL));
> +
> + TSI108_ETH_WRITE_REG(TSI108_STAT_CARRYMASK1,
> + ~(TSI108_STAT_CARRY1_RXBYTES |
> + TSI108_STAT_CARRY1_RXPKTS |
> + TSI108_STAT_CARRY1_RXFCS |
> + TSI108_STAT_CARRY1_RXMCAST |
> + TSI108_STAT_CARRY1_RXALIGN |
> + TSI108_STAT_CARRY1_RXLENGTH |
> + TSI108_STAT_CARRY1_RXRUNT |
> + TSI108_STAT_CARRY1_RXJUMBO |
> + TSI108_STAT_CARRY1_RXFRAG |
> + TSI108_STAT_CARRY1_RXJABBER |
> + TSI108_STAT_CARRY1_RXDROP));
> +
> + TSI108_ETH_WRITE_REG(TSI108_STAT_CARRYMASK2,
> + ~(TSI108_STAT_CARRY2_TXBYTES |
> + TSI108_STAT_CARRY2_TXPKTS |
> + TSI108_STAT_CARRY2_TXEXDEF |
> + TSI108_STAT_CARRY2_TXEXCOL |
> + TSI108_STAT_CARRY2_TXTCOL |
> + TSI108_STAT_CARRY2_TXPAUSE));
> +
> + TSI108_ETH_WRITE_REG(TSI108_EC_PORTCTRL, TSI108_EC_PORTCTRL_STATEN);
> + TSI108_ETH_WRITE_REG(TSI108_MAC_CFG1, 0);
> +
> + TSI108_ETH_WRITE_REG(TSI108_EC_RXCFG,
> + TSI108_EC_RXCFG_SE | TSI108_EC_RXCFG_BFE);
> +
> + TSI108_ETH_WRITE_REG(TSI108_EC_TXQ_CFG, TSI108_EC_TXQ_CFG_DESC_INT |
> + TSI108_EC_TXQ_CFG_EOQ_OWN_INT |
> + TSI108_EC_TXQ_CFG_WSWP | (TSI108_PBM_PORT <<
> + TSI108_EC_TXQ_CFG_SFNPORT));
> +
> + TSI108_ETH_WRITE_REG(TSI108_EC_RXQ_CFG, TSI108_EC_RXQ_CFG_DESC_INT |
> + TSI108_EC_RXQ_CFG_EOQ_OWN_INT |
> + TSI108_EC_RXQ_CFG_WSWP | (TSI108_PBM_PORT <<
> + TSI108_EC_RXQ_CFG_SFNPORT));
> +
> + TSI108_ETH_WRITE_REG(TSI108_EC_TXQ_BUFCFG,
> + TSI108_EC_TXQ_BUFCFG_BURST256 |
> + TSI108_EC_TXQ_BUFCFG_BSWP | (TSI108_PBM_PORT <<
> + TSI108_EC_TXQ_BUFCFG_SFNPORT));
> +
> + TSI108_ETH_WRITE_REG(TSI108_EC_RXQ_BUFCFG,
> + TSI108_EC_RXQ_BUFCFG_BURST256 |
> + TSI108_EC_RXQ_BUFCFG_BSWP | (TSI108_PBM_PORT <<
> + TSI108_EC_RXQ_BUFCFG_SFNPORT));
> +
> + TSI108_ETH_WRITE_REG(TSI108_EC_INTMASK, ~0);
> +}
> +
> +static int tsi108_ioctl(net_device * dev, struct ifreq *rq, int cmd)
> +{
> + tsi108_prv_data *data = netdev_priv(dev);
> + struct mii_ioctl_data *mii_data =
> + (struct mii_ioctl_data *)&rq->ifr_data;
> + int ret;
> +
> + switch (cmd) {
> + case SIOCGMIIPHY:
> + mii_data->phy_id = data->phy;
> + ret = 0;
> + break;
> +
> + case SIOCGMIIREG:
> + spin_lock_irq(&phy_lock);
> + mii_data->val_out =
> + tsi108_read_mii(data, mii_data->reg_num, &ret);
> + spin_unlock_irq(&phy_lock);
> + break;
> +
> + default:
> + ret = -EOPNOTSUPP;
> + }
> +
> + return ret;
> +}
> +
> +static int
> +tsi108_init_one(unsigned long regs, unsigned long phyregs, u16 phy, u16 irq_num)
> +{
> + net_device *dev = alloc_etherdev(sizeof(tsi108_prv_data));
> + tsi108_prv_data *data;
> + int ret;
> +
> + if (!dev) {
> + printk("tsi108_eth: Could not allocate a device structure\n");
> + return -ENOMEM;
> + }
> +
> + data = netdev_priv(dev);
> + memset(data, 0, sizeof(tsi108_prv_data));
> +
> + data->regs = (volatile u32)regs;
> + data->phyregs = (volatile u32)phyregs;
> + data->phy = phy;
> + data->irq_num = irq_num;
> +
> + dev->open = tsi108_open;
> + dev->stop = tsi108_close;
> + dev->hard_start_xmit = tsi108_send_packet;
> + dev->set_mac_address = tsi108_set_mac;
> + dev->set_multicast_list = tsi108_set_rx_mode;
> + dev->get_stats = tsi108_get_stats;
> + dev->poll = tsi108_poll;
> + dev->do_ioctl = tsi108_ioctl;
> + dev->weight = 64; /* 64 is more suitable for GigE interface - klai */
> +
> + /* Apparently, the Linux networking code won't use scatter-gather
> + * if the hardware doesn't do checksums. However, it's faster
> + * to checksum in place and use SG, as (among other reasons)
> + * the cache won't be dirtied (which then has to be flushed
> + * before DMA). The checksumming is done by the driver (via
> + * a new function skb_csum_dev() in net/core/skbuff.c).
> + */
> +
> +#ifdef FIXME_SG_CSUM_NOT_TESTED /* FIXME: Not supported now. */
> + dev->features = NETIF_F_HIGHDMA | NETIF_F_SG | NETIF_F_HW_CSUM;
> +#else
> + dev->features = NETIF_F_HIGHDMA;
> +#endif
> + SET_MODULE_OWNER(dev);
> +
> + spin_lock_init(&data->txlock);
> + spin_lock_init(&data->misclock);
> +
> + tsi108_reset_ether(data);
> + tsi108_kill_phy(dev);
> +
> + if (tsi108_get_mac(dev) != 0)
> + printk(KERN_ERR "%s: Invalid MAC address. Please correct.\n",
> + dev->name);
> +
> + tsi108_init_mac(dev);
> +
> + tsi108_devs[irq_num % IRQ_TSI108_GIGE0] = dev;
> +
> + ret = register_netdev(dev);
> + if (ret < 0) {
> + kfree(dev);
> + return ret;
> + }
> +
> + printk(KERN_INFO "%s: Tsi108 Gigabit Ethernet, MAC: "
> + "%02x:%02x:%02x:%02x:%02x:%02x\n", dev->name,
> + dev->dev_addr[0], dev->dev_addr[1], dev->dev_addr[2],
> + dev->dev_addr[3], dev->dev_addr[4], dev->dev_addr[5]);
> +
> + return 0;
> +}
> +
> +/* There's no way to either get interrupts from the PHY when
> + * something changes, or to have the Tsi108 automatically communicate
> + * with the PHY to reconfigure itself.
> + *
> + * Thus, we have to do it using a timer.
> + */
> +
> +static void tsi108_timed_checker(unsigned long dev_ptr)
> +{
> + struct net_device *dev = (struct net_device *)dev_ptr;
> + tsi108_prv_data *data = netdev_priv(dev);
> +
> + tsi108_check_phy(dev);
> + tsi108_check_rxring(dev);
> + data->timer.expires = jiffies + CHECK_PHY_INTERVAL;
> + add_timer(&data->timer);
> +}
> +
> +static int tsi108_ether_init(void)
> +{
> + int ret;
> + int dev_count = 0;
> + int i;
> +
> + hw_info_table[0].regs = (u32) ioremap(hw_info_table[0].regs, 0x400);
> + hw_info_table[0].phyregs = hw_info_table[0].regs;
> +
> + hw_info_table[1].regs = (u32) ioremap(hw_info_table[1].regs, 0x400);
> + hw_info_table[1].phyregs = hw_info_table[0].regs;
> +
> + for (i = 0; hw_info_table[i].regs != TBL_END; i++) {
> + ret = tsi108_init_one(hw_info_table[i].regs,
> + hw_info_table[i].phyregs,
> + hw_info_table[i].phy,
> + hw_info_table[i].irq_num);
> + if (ret < 0)
> + printk("tsi108_ether_init: error initializing ethernet "
> + "device%d\n", i);
> + else
> + dev_count++;
> + }
> +
> + printk("tsi108_ether_init: found %d device(s)\n", dev_count);
> +
> + return 0;
> +}
> +
> +static void tsi108_ether_exit(void)
> +{
> + int i;
> + net_device *dev;
> +
> + for (i = 0; hw_info_table[i].regs != TBL_END; i++) {
> + if ((dev = tsi108_devs[i]) != NULL) {
> + unregister_netdev(dev);
> + tsi108_stop_ethernet(dev);
> + kfree(dev);
> + tsi108_devs[i] = NULL;
> + }
> + }
> +}
> +
> +module_init(tsi108_ether_init);
> +module_exit(tsi108_ether_exit);
> +
> +MODULE_AUTHOR("Tundra Semiconductor Corporation");
> +MODULE_DESCRIPTION("Tsi108 Gigabit Ethernet driver");
> +MODULE_LICENSE("GPL");
> diff --git a/drivers/net/tsi108_eth.h b/drivers/net/tsi108_eth.h
> new file mode 100644
> index 0000000..cb54f92
> --- /dev/null
> +++ b/drivers/net/tsi108_eth.h
> @@ -0,0 +1,404 @@
> +/*
> + * (C) Copyright 2005 Tundra Semiconductor Corp.
> + * Kong Lai, <kong.lai@tundra.com).
> + *
> + * See file CREDITS for list of people who contributed to this
> + * project.
> + *
> + * This program is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU General Public License as
> + * published by the Free Software Foundation; either version 2 of
> + * the License, or (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, write to the Free Software
> + * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
> + * MA 02111-1307 USA
> + */
> +
> +/*
> + * net/tsi108_eth.h - definitions for Tsi108 GIGE network controller.
> + */
> +
> +#ifndef __TSI108_ETH_H
> +#define __TSI108_ETH_H
> +
> +#include <linux/config.h>
> +#include <linux/types.h>
> +
> +#define TSI108_ETH_WRITE_REG(offset, val) \
> + out_be32((volatile u32 *)(data->regs + offset), val)
> +
> +#define TSI108_ETH_READ_REG(offset) \
> + in_be32((volatile u32 *)(data->regs + offset))
> +
> +#define TSI108_ETH_WRITE_PHYREG(offset, val) \
> + out_be32((volatile u32 *)(data->phyregs + offset), val)
> +
> +#define TSI108_ETH_READ_PHYREG(offset) \
> + in_be32((volatile u32 *)(data->phyregs + offset))
> +
> +/*
> + * PHY Configuration Options
> + *
> + * NOTE: Enable set of definitions corresponding to your board type
> + */
> +#define PHY_MV88E 1 /* Marvel 88Exxxx PHY */
> +#define PHY_BCM54XX 2 /* Broardcom BCM54xx PHY */
> +#define TSI108_PHY_TYPE PHY_MV88E
> +
> +/*
> + * TSI108 GIGE port registers
> + */
> +
> +#define TSI108_ETH_PORT_NUM 2
> +#define TSI108_PBM_PORT 2
> +#define TSI108_SDRAM_PORT 4
> +
> +#define TSI108_MAC_CFG1 (0x000)
> +#define TSI108_MAC_CFG1_SOFTRST (1 << 31)
> +#define TSI108_MAC_CFG1_LOOPBACK (1 << 8)
> +#define TSI108_MAC_CFG1_RXEN (1 << 2)
> +#define TSI108_MAC_CFG1_TXEN (1 << 0)
> +
> +#define TSI108_MAC_CFG2 (0x004)
> +#define TSI108_MAC_CFG2_DFLT_PREAMBLE (7 << 12)
> +#define TSI108_MAC_CFG2_IFACE_MASK (3 << 8)
> +#define TSI108_MAC_CFG2_NOGIG (1 << 8)
> +#define TSI108_MAC_CFG2_GIG (2 << 8)
> +#define TSI108_MAC_CFG2_PADCRC (1 << 2)
> +#define TSI108_MAC_CFG2_FULLDUPLEX (1 << 0)
> +
> +#define TSI108_MAC_MII_MGMT_CFG (0x020)
> +#define TSI108_MAC_MII_MGMT_CLK (7 << 0)
> +#define TSI108_MAC_MII_MGMT_RST (1 << 31)
> +
> +#define TSI108_MAC_MII_CMD (0x024)
> +#define TSI108_MAC_MII_CMD_READ (1 << 0)
> +
> +#define TSI108_MAC_MII_ADDR (0x028)
> +#define TSI108_MAC_MII_ADDR_REG 0
> +#define TSI108_MAC_MII_ADDR_PHY 8
> +
> +#define TSI108_MAC_MII_DATAOUT (0x02c)
> +#define TSI108_MAC_MII_DATAIN (0x030)
> +
> +#define TSI108_MAC_MII_IND (0x034)
> +#define TSI108_MAC_MII_IND_NOTVALID (1 << 2)
> +#define TSI108_MAC_MII_IND_SCANNING (1 << 1)
> +#define TSI108_MAC_MII_IND_BUSY (1 << 0)
> +
> +#define TSI108_MAC_IFCTRL (0x038)
> +#define TSI108_MAC_IFCTRL_PHYMODE (1 << 24)
> +
> +#define TSI108_MAC_ADDR1 (0x040)
> +#define TSI108_MAC_ADDR2 (0x044)
> +
> +#define TSI108_STAT_RXBYTES (0x06c)
> +#define TSI108_STAT_RXBYTES_CARRY (1 << 24)
> +
> +#define TSI108_STAT_RXPKTS (0x070)
> +#define TSI108_STAT_RXPKTS_CARRY (1 << 18)
> +
> +#define TSI108_STAT_RXFCS (0x074)
> +#define TSI108_STAT_RXFCS_CARRY (1 << 12)
> +
> +#define TSI108_STAT_RXMCAST (0x078)
> +#define TSI108_STAT_RXMCAST_CARRY (1 << 18)
> +
> +#define TSI108_STAT_RXALIGN (0x08c)
> +#define TSI108_STAT_RXALIGN_CARRY (1 << 12)
> +
> +#define TSI108_STAT_RXLENGTH (0x090)
> +#define TSI108_STAT_RXLENGTH_CARRY (1 << 12)
> +
> +#define TSI108_STAT_RXRUNT (0x09c)
> +#define TSI108_STAT_RXRUNT_CARRY (1 << 12)
> +
> +#define TSI108_STAT_RXJUMBO (0x0a0)
> +#define TSI108_STAT_RXJUMBO_CARRY (1 << 12)
> +
> +#define TSI108_STAT_RXFRAG (0x0a4)
> +#define TSI108_STAT_RXFRAG_CARRY (1 << 12)
> +
> +#define TSI108_STAT_RXJABBER (0x0a8)
> +#define TSI108_STAT_RXJABBER_CARRY (1 << 12)
> +
> +#define TSI108_STAT_RXDROP (0x0ac)
> +#define TSI108_STAT_RXDROP_CARRY (1 << 12)
> +
> +#define TSI108_STAT_TXBYTES (0x0b0)
> +#define TSI108_STAT_TXBYTES_CARRY (1 << 24)
> +
> +#define TSI108_STAT_TXPKTS (0x0b4)
> +#define TSI108_STAT_TXPKTS_CARRY (1 << 18)
> +
> +#define TSI108_STAT_TXEXDEF (0x0c8)
> +#define TSI108_STAT_TXEXDEF_CARRY (1 << 12)
> +
> +#define TSI108_STAT_TXEXCOL (0x0d8)
> +#define TSI108_STAT_TXEXCOL_CARRY (1 << 12)
> +
> +#define TSI108_STAT_TXTCOL (0x0dc)
> +#define TSI108_STAT_TXTCOL_CARRY (1 << 13)
> +
> +#define TSI108_STAT_TXPAUSEDROP (0x0e4)
> +#define TSI108_STAT_TXPAUSEDROP_CARRY (1 << 12)
> +
> +#define TSI108_STAT_CARRY1 (0x100)
> +#define TSI108_STAT_CARRY1_RXBYTES (1 << 16)
> +#define TSI108_STAT_CARRY1_RXPKTS (1 << 15)
> +#define TSI108_STAT_CARRY1_RXFCS (1 << 14)
> +#define TSI108_STAT_CARRY1_RXMCAST (1 << 13)
> +#define TSI108_STAT_CARRY1_RXALIGN (1 << 8)
> +#define TSI108_STAT_CARRY1_RXLENGTH (1 << 7)
> +#define TSI108_STAT_CARRY1_RXRUNT (1 << 4)
> +#define TSI108_STAT_CARRY1_RXJUMBO (1 << 3)
> +#define TSI108_STAT_CARRY1_RXFRAG (1 << 2)
> +#define TSI108_STAT_CARRY1_RXJABBER (1 << 1)
> +#define TSI108_STAT_CARRY1_RXDROP (1 << 0)
> +
> +#define TSI108_STAT_CARRY2 (0x104)
> +#define TSI108_STAT_CARRY2_TXBYTES (1 << 13)
> +#define TSI108_STAT_CARRY2_TXPKTS (1 << 12)
> +#define TSI108_STAT_CARRY2_TXEXDEF (1 << 7)
> +#define TSI108_STAT_CARRY2_TXEXCOL (1 << 3)
> +#define TSI108_STAT_CARRY2_TXTCOL (1 << 2)
> +#define TSI108_STAT_CARRY2_TXPAUSE (1 << 0)
> +
> +#define TSI108_STAT_CARRYMASK1 (0x108)
> +#define TSI108_STAT_CARRYMASK2 (0x10c)
> +
> +#define TSI108_EC_PORTCTRL (0x200)
> +#define TSI108_EC_PORTCTRL_STATRST (1 << 31)
> +#define TSI108_EC_PORTCTRL_STATEN (1 << 28)
> +#define TSI108_EC_PORTCTRL_NOGIG (1 << 18)
> +#define TSI108_EC_PORTCTRL_HALFDUPLEX (1 << 16)
> +
> +#define TSI108_EC_INTSTAT (0x204)
> +#define TSI108_EC_INTMASK (0x208)
> +
> +#define TSI108_INT_ANY (1 << 31)
> +#define TSI108_INT_SFN (1 << 30)
> +#define TSI108_INT_RXIDLE (1 << 29)
> +#define TSI108_INT_RXABORT (1 << 28)
> +#define TSI108_INT_RXERROR (1 << 27)
> +#define TSI108_INT_RXOVERRUN (1 << 26)
> +#define TSI108_INT_RXTHRESH (1 << 25)
> +#define TSI108_INT_RXWAIT (1 << 24)
> +#define TSI108_INT_RXQUEUE0 (1 << 16)
> +#define TSI108_INT_STATCARRY (1 << 15)
> +#define TSI108_INT_TXIDLE (1 << 13)
> +#define TSI108_INT_TXABORT (1 << 12)
> +#define TSI108_INT_TXERROR (1 << 11)
> +#define TSI108_INT_TXUNDERRUN (1 << 10)
> +#define TSI108_INT_TXTHRESH (1 << 9)
> +#define TSI108_INT_TXWAIT (1 << 8)
> +#define TSI108_INT_TXQUEUE0 (1 << 0)
> +
> +#define TSI108_EC_TXCFG (0x220)
> +#define TSI108_EC_TXCFG_RST (1 << 31)
> +
> +#define TSI108_EC_TXCTRL (0x224)
> +#define TSI108_EC_TXCTRL_IDLEINT (1 << 31)
> +#define TSI108_EC_TXCTRL_ABORT (1 << 30)
> +#define TSI108_EC_TXCTRL_GO (1 << 15)
> +#define TSI108_EC_TXCTRL_QUEUE0 (1 << 0)
> +
> +#define TSI108_EC_TXSTAT (0x228)
> +#define TSI108_EC_TXSTAT_ACTIVE (1 << 15)
> +#define TSI108_EC_TXSTAT_QUEUE0 (1 << 0)
> +
> +#define TSI108_EC_TXESTAT (0x22c)
> +#define TSI108_EC_TXESTAT_Q0_ERR (1 << 24)
> +#define TSI108_EC_TXESTAT_Q0_DESCINT (1 << 16)
> +#define TSI108_EC_TXESTAT_Q0_EOF (1 << 8)
> +#define TSI108_EC_TXESTAT_Q0_EOQ (1 << 0)
> +
> +#define TSI108_EC_TXERR (0x278)
> +
> +#define TSI108_EC_TXQ_CFG (0x280)
> +#define TSI108_EC_TXQ_CFG_DESC_INT (1 << 20)
> +#define TSI108_EC_TXQ_CFG_EOQ_OWN_INT (1 << 19)
> +#define TSI108_EC_TXQ_CFG_WSWP (1 << 11)
> +#define TSI108_EC_TXQ_CFG_BSWP (1 << 10)
> +#define TSI108_EC_TXQ_CFG_SFNPORT 0
> +
> +#define TSI108_EC_TXQ_BUFCFG (0x284)
> +#define TSI108_EC_TXQ_BUFCFG_BURST8 (0 << 8)
> +#define TSI108_EC_TXQ_BUFCFG_BURST32 (1 << 8)
> +#define TSI108_EC_TXQ_BUFCFG_BURST128 (2 << 8)
> +#define TSI108_EC_TXQ_BUFCFG_BURST256 (3 << 8)
> +#define TSI108_EC_TXQ_BUFCFG_WSWP (1 << 11)
> +#define TSI108_EC_TXQ_BUFCFG_BSWP (1 << 10)
> +#define TSI108_EC_TXQ_BUFCFG_SFNPORT 0
> +
> +#define TSI108_EC_TXQ_PTRLOW (0x288)
> +
> +#define TSI108_EC_TXQ_PTRHIGH (0x28c)
> +#define TSI108_EC_TXQ_PTRHIGH_VALID (1 << 31)
> +
> +#define TSI108_EC_TXTHRESH (0x230)
> +#define TSI108_EC_TXTHRESH_STARTFILL 0
> +#define TSI108_EC_TXTHRESH_STOPFILL 16
> +
> +#define TSI108_EC_RXCFG (0x320)
> +#define TSI108_EC_RXCFG_RST (1 << 31)
> +
> +#define TSI108_EC_RXSTAT (0x328)
> +#define TSI108_EC_RXSTAT_ACTIVE (1 << 15)
> +#define TSI108_EC_RXSTAT_QUEUE0 (1 << 0)
> +
> +#define TSI108_EC_RXESTAT (0x32c)
> +#define TSI108_EC_RXESTAT_Q0_ERR (1 << 24)
> +#define TSI108_EC_RXESTAT_Q0_DESCINT (1 << 16)
> +#define TSI108_EC_RXESTAT_Q0_EOF (1 << 8)
> +#define TSI108_EC_RXESTAT_Q0_EOQ (1 << 0)
> +
> +#define TSI108_EC_HASHADDR (0x360)
> +#define TSI108_EC_HASHADDR_AUTOINC (1 << 31)
> +#define TSI108_EC_HASHADDR_DO1STREAD (1 << 30)
> +#define TSI108_EC_HASHADDR_UNICAST (0 << 4)
> +#define TSI108_EC_HASHADDR_MCAST (1 << 4)
> +
> +#define TSI108_EC_HASHDATA (0x364)
> +
> +#define TSI108_EC_RXQ_PTRLOW (0x388)
> +
> +#define TSI108_EC_RXQ_PTRHIGH (0x38c)
> +#define TSI108_EC_RXQ_PTRHIGH_VALID (1 << 31)
> +
> +/* Station Enable -- accept packets destined for us */
> +#define TSI108_EC_RXCFG_SE (1 << 13)
> +/* Unicast Frame Enable -- for packets not destined for us */
> +#define TSI108_EC_RXCFG_UFE (1 << 12)
> +/* Multicast Frame Enable */
> +#define TSI108_EC_RXCFG_MFE (1 << 11)
> +/* Broadcast Frame Enable */
> +#define TSI108_EC_RXCFG_BFE (1 << 10)
> +#define TSI108_EC_RXCFG_UC_HASH (1 << 9)
> +#define TSI108_EC_RXCFG_MC_HASH (1 << 8)
> +
> +#define TSI108_EC_RXQ_CFG (0x380)
> +#define TSI108_EC_RXQ_CFG_DESC_INT (1 << 20)
> +#define TSI108_EC_RXQ_CFG_EOQ_OWN_INT (1 << 19)
> +#define TSI108_EC_RXQ_CFG_WSWP (1 << 11)
> +#define TSI108_EC_RXQ_CFG_BSWP (1 << 10)
> +#define TSI108_EC_RXQ_CFG_SFNPORT 0
> +
> +#define TSI108_EC_RXQ_BUFCFG (0x384)
> +#define TSI108_EC_RXQ_BUFCFG_BURST8 (0 << 8)
> +#define TSI108_EC_RXQ_BUFCFG_BURST32 (1 << 8)
> +#define TSI108_EC_RXQ_BUFCFG_BURST128 (2 << 8)
> +#define TSI108_EC_RXQ_BUFCFG_BURST256 (3 << 8)
> +#define TSI108_EC_RXQ_BUFCFG_WSWP (1 << 11)
> +#define TSI108_EC_RXQ_BUFCFG_BSWP (1 << 10)
> +#define TSI108_EC_RXQ_BUFCFG_SFNPORT 0
> +
> +#define TSI108_EC_RXCTRL (0x324)
> +#define TSI108_EC_RXCTRL_ABORT (1 << 30)
> +#define TSI108_EC_RXCTRL_GO (1 << 15)
> +#define TSI108_EC_RXCTRL_QUEUE0 (1 << 0)
> +
> +#define TSI108_EC_RXERR (0x378)
> +
> +#define PHY_CTRL 0
> +#define PHY_CTRL_RESET (1 << 15)
> +#define PHY_CTRL_AUTONEG_EN (1 << 12)
> +#define PHY_CTRL_POWERDOWN (1 << 11)
> +#define PHY_CTRL_AUTONEG_START (1 << 9)
> +
> +#define PHY_STAT 1
> +#define PHY_STAT_LINKUP (1 << 2)
> +
> +#if (TSI108_PHY_TYPE == PHY_MV88E)
> +/* Marvel 88E1xxx-specific */
> +#define PHY_SUM_STAT 0x11
> +#define PHY_SUM_STAT_SPEED_MASK (3 << 14)
> +#define PHY_SUM_STAT_SPEED_10 (0 << 14)
> +#define PHY_SUM_STAT_SPEED_100 (1 << 14)
> +#define PHY_SUM_STAT_SPEED_1000 (2 << 14)
> +#define PHY_SUM_STAT_FULLDUPLEX (1 << 13)
> +
> +#define PHY_SUM_STAT_1000T_FD (PHY_SUM_STAT_SPEED_1000 | PHY_SUM_STAT_FULLDUPLEX)
> +#define PHY_SUM_STAT_1000T_HD (PHY_SUM_STAT_SPEED_1000)
> +#define PHY_SUM_STAT_100TX_FD (PHY_SUM_STAT_SPEED_100 | PHY_SUM_STAT_FULLDUPLEX)
> +#define PHY_SUM_STAT_100TX_HD (PHY_SUM_STAT_SPEED_100)
> +#define PHY_SUM_STAT_10T_FD (PHY_SUM_STAT_SPEED_10 | PHY_SUM_STAT_FULLDUPLEX)
> +#define PHY_SUM_STAT_10T_HD (PHY_SUM_STAT_SPEED_10)
> +#elif (TSI108_PHY_TYPE == PHY_BCM54XX)
> +/*Broadcom BCM54xx */
> +#define PHY_SUM_STAT 0x19
> +#define PHY_SUM_STAT_SPEED_MASK (7 << 8) /* Auto Negotiation HCD */
> +#define PHY_SUM_STAT_1000T_FD (7 << 8) /* 1000BASE-T Full-Duplex */
> +#define PHY_SUM_STAT_1000T_HD (6 << 8) /* 1000BASE-T Half-Duplex */
> +#define PHY_SUM_STAT_100TX_FD (5 << 8) /* 100BASE-TX Full-Duplex */
> +#define PHY_SUM_STAT_100T4 (4 << 8) /* 100BASE-T4 */
> +#define PHY_SUM_STAT_100TX_HD (3 << 8) /* 100BASE-TX Half-Duplex */
> +#define PHY_SUM_STAT_10T_FD (2 << 8) /* 10BASE-T Full-Duplex */
> +#define PHY_SUM_STAT_10T_HD (1 << 8) /* 10BASE-T Half-Duplex */
> +#define PHY_SUM_STAT_AN_FAIL (0 << 8) /* 1000BASE-T Half-Duplex */
> +#else
> +#error "PHY Device not specified"
> +#endif /* MV88_PHY */
> +
> +#define TSI108_TX_EOF (1 << 0) /* End of frame; last fragment of packet */
> +#define TSI108_TX_SOF (1 << 1) /* Start of frame; first frag. of packet */
> +#define TSI108_TX_VLAN (1 << 2) /* Per-frame VLAN: enables VLAN override */
> +#define TSI108_TX_HUGE (1 << 3) /* Huge frame enable */
> +#define TSI108_TX_PAD (1 << 4) /* Pad the packet if too short */
> +#define TSI108_TX_CRC (1 << 5) /* Generate CRC for this packet */
> +#define TSI108_TX_INT (1 << 14) /* Generate an IRQ after frag. processed */
> +#define TSI108_TX_RETRY 16 /* 4 bit field indicating num. of retries */
> +#define TSI108_TX_COL (1 << 20) /* Set if a collision occured */
> +#define TSI108_TX_LCOL (1 << 24) /* Set if a late collision occured */
> +#define TSI108_TX_UNDER (1 << 25) /* Set if a FIFO underrun occured */
> +#define TSI108_TX_RLIM (1 << 26) /* Set if the retry limit was reached */
> +#define TSI108_TX_OK (1 << 30) /* Set if the frame TX was successful */
> +#define TSI108_TX_OWN (1 << 31) /* Set if the device owns the descriptor */
> +
> +/* Note: the descriptor layouts assume big-endian byte order. */
> +typedef struct {
> + u32 buf0;
> + u32 buf1; /* Base address of buffer */
> + u32 next0; /* Address of next descriptor, if any */
> + u32 next1;
> + u16 vlan; /* VLAN, if override enabled for this packet */
> + u16 len; /* Length of buffer in bytes */
> + u32 misc; /* See TSI108_TX_* above */
> + u32 reserved0; /*reserved0 and reserved1 are added to make the desc */
> + u32 reserved1; /* 32-byte aligned */
> +} __attribute__ ((aligned(32))) tx_desc;
> +
> +#define TSI108_RX_EOF (1 << 0) /* End of frame; last fragment of packet */
> +#define TSI108_RX_SOF (1 << 1) /* Start of frame; first frag. of packet */
> +#define TSI108_RX_VLAN (1 << 2) /* Set on SOF if packet has a VLAN */
> +#define TSI108_RX_FTYPE (1 << 3) /* Length/Type field is type, not length */
> +#define TSI108_RX_RUNT (1 << 4)/* Packet is less than minimum size */
> +#define TSI108_RX_HASH (1 << 7)/* Hash table match */
> +#define TSI108_RX_BAD (1 << 8) /* Bad frame */
> +#define TSI108_RX_OVER (1 << 9) /* FIFO overrun occured */
> +#define TSI108_RX_TRUNC (1 << 11) /* Packet truncated due to excess length */
> +#define TSI108_RX_CRC (1 << 12) /* Packet had a CRC error */
> +#define TSI108_RX_INT (1 << 13) /* Generate an IRQ after frag. processed */
> +#define TSI108_RX_OWN (1 << 15) /* Set if the device owns the descriptor */
> +
> +typedef struct {
> + u32 buf0; /* Base address of buffer */
> + u32 buf1; /* Base address of buffer */
> + u32 next0; /* Address of next descriptor, if any */
> + u32 next1; /* Address of next descriptor, if any */
> + u16 vlan; /* VLAN of received packet, first frag only */
> + u16 len; /* Length of received fragment in bytes */
> + u16 blen; /* Length of buffer in bytes */
> + u16 misc; /* See TSI108_RX_* above */
> + u32 reserved0; /* reserved0 and reserved1 are added to make the desc */
> + u32 reserved1; /* 32-byte aligned */
> +} __attribute__ ((aligned(32))) rx_desc;
> +
> +#endif /* __TSI108_ETH_H */
^ permalink raw reply
* Re: [PATCH/2.6.17-rc4 4/10]Powerpc: Add tsi108 pic support
From: Benjamin Herrenschmidt @ 2006-05-18 0:52 UTC (permalink / raw)
To: Zang Roy-r61911
Cc: linuxppc-dev list, Yang Xin-Xin-r48390, Paul Mackerras,
Alexandre.Bounine
In-Reply-To: <9FCDBA58F226D911B202000BDBAD46730626D61F@zch01exm40.ap.freescale.net>
On Wed, 2006-05-17 at 18:14 +0800, Zang Roy-r61911 wrote:
> Add Tundra Semiconductor tsi108 host bridge interrupt controller support.
It looks a bit like an hacked up MPIC... Is it different enough to
justify a separate driver ? Or would it be possible to define a TSI108
flag to pass the current mpic driver and add the necessary bits to it ?
> Signed-off-by: Alexandre Bounine <alexandreb@tundra.com>
> Signed-off-by: Roy Zang <tie-fei.zang@freescale.com>
>
> ---
>
> arch/powerpc/sysdev/tsi108_pic.c | 813 ++++++++++++++++++++++++++++++++++++++
> include/asm-powerpc/tsi108_pic.h | 232 +++++++++++
> 2 files changed, 1045 insertions(+), 0 deletions(-)
> create mode 100644 arch/powerpc/sysdev/tsi108_pic.c
> create mode 100644 include/asm-powerpc/tsi108_pic.h
>
> 7d23f6a0984cd54ca787f880a57067330900abe8
> diff --git a/arch/powerpc/sysdev/tsi108_pic.c b/arch/powerpc/sysdev/tsi108_pic.c
> new file mode 100644
> index 0000000..bbca587
> --- /dev/null
> +++ b/arch/powerpc/sysdev/tsi108_pic.c
> @@ -0,0 +1,813 @@
> +/*
> + * (C) Copyright 2005 Tundra Semiconductor Corp.
> + * Alex Bounine, <alexandreb@tundra.com).
> + *
> + * See file CREDITS for list of people who contributed to this
> + * project.
> + *
> + * This program is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU General Public License as
> + * published by the Free Software Foundation; either version 2 of
> + * the License, or (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, write to the Free Software
> + * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
> + * MA 02111-1307 USA
> + */
> +
> +/*
> + * Tsi108 Interrupt Controller Handling
> + */
> +
> +#include <linux/config.h>
> +#include <linux/types.h>
> +#include <linux/kernel.h>
> +#include <linux/sched.h>
> +#include <linux/init.h>
> +#include <linux/irq.h>
> +#include <linux/interrupt.h>
> +#include <linux/sysdev.h>
> +#include <asm/ptrace.h>
> +#include <asm/signal.h>
> +#include <asm/io.h>
> +#include <asm/irq.h>
> +#include <asm/prom.h>
> +#include <asm/sections.h>
> +#include <asm/hardirq.h>
> +#include <asm/machdep.h>
> +
> +#include <asm/tsi108.h>
> +#include <asm/tsi108_irq.h>
> +#include <asm/tsi108_pic.h>
> +#undef DEBUG
> +
> +#ifdef DEBUG
> +#define DBG(fmt...) do { printk(fmt); } while(0)
> +#else
> +#define DBG(fmt...) do { } while(0)
> +#endif
> +
> +extern u32 get_vir_csrbase(void);
> +extern u32 tsi108_read_reg(u32 reg_offset);
> +extern void tsi108_write_reg(u32 reg_offset, u32 val);
> +
> +static phys_addr_t tsi108_pic_phy_addr;
> +static u32 tsi108_pic_vir_addr;
> +
> +static int tsi108_pic_cascade_irq = -1;
> +static int (*tsi108_pic_cascade_fn) (struct pt_regs *);
> +
> +/* Global Operations */
> +static void tsi108_pic_set_task_priority(u_int pri);
> +static void tsi108_pic_set_spurious(u_int vector);
> +void tsi108_pic_mask_all(void);
> +
> +/* Timer Interrupts */
> +static void tsi108_pic_inittimer(u_int timer, u_int pri, u_int vector);
> +static void tsi108_pic_maptimer(u_int timer, u_int cpumask);
> +
> +/* Interrupt Sources */
> +static void tsi108_pic_enable_irq(u_int irq);
> +static void tsi108_pic_disable_irq(u_int irq);
> +static void tsi108_pic_initirq(u_int irq, u_int pri, u_int vector, int polarity,
> + int is_level);
> +static void tsi108_pic_mapirq(u_int irq, u_int cpumask, u_int keepmask);
> +static void init_pci_source(void);
> +static inline int get_pci_source(int vector);
> +int tsi108_pic_set_irq_sense(int irq, int pol, int sense);
> +
> +/*
> + * tsi108_pic interface routines
> + */
> +static void tsi108_pic_end_irq(unsigned int irq_nr);
> +static void tsi108_pic_ack_irq(unsigned int irq_nr);
> +void tsi108_pic_set_affinity(unsigned int irq_nr, unsigned long cpumask);
> +
> +static struct hw_interrupt_type tsi108_pic_irq = {
> + "tsi108_pic",
> + NULL,
> + NULL,
> + tsi108_pic_enable_irq,
> + tsi108_pic_disable_irq,
> + tsi108_pic_ack_irq,
> + tsi108_pic_end_irq,
> + NULL
> +};
> +
> +static void tsi108_pci_irq_enable(u_int irq);
> +static void tsi108_pci_irq_disable(u_int irq);
> +static void tsi108_pci_irq_ack(u_int irq);
> +static void tsi108_pci_irq_end(u_int irq);
> +
> +static struct hw_interrupt_type tsi108_pci_irq = {
> + "tsi108_PCI_int",
> + NULL,
> + NULL,
> + tsi108_pci_irq_enable,
> + tsi108_pci_irq_disable,
> + tsi108_pci_irq_ack,
> + tsi108_pci_irq_end,
> + NULL
> +};
> +
> +#ifdef DBG_TSI108_INTERRUPT
> +#define ASSERT(expr) if (!(expr)) { \
> + printk("tsi108pic :" \
> + "assertion failed! %s[%d]: %s\n", \
> + __FUNCTION__, __LINE__, #expr); \
> + dump_stack(); \
> + }
> +#else
> +#define ASSERT(expr) do {} while (0)
> +#endif
> +
> +static inline u_int get_vector_offset(u_int irq)
> +{
> + u_int offset;
> +
> + if (irq < TSI108_IRQ_BASE || irq >= IRQ_PCI_INTAD_BASE)
> + return 0;
> +
> + if (irq < IRQ_TSI108_MBOX0)
> + offset = TSI108_INT_IVPR(irq - TSI108_IRQ_BASE);
> + else if (irq < IRQ_TSI108_DBELL0)
> + offset = TSI108_INT_MBVPR(irq - IRQ_TSI108_MBOX0);
> + else if (irq < IRQ_TSI108_TIMER0)
> + offset = TSI108_INT_DVPR(irq - IRQ_TSI108_DBELL0);
> + else
> + offset = TSI108_INT_GTVPR(irq - IRQ_TSI108_TIMER0);
> +
> + return offset;
> +}
> +
> +static inline u_int tsi108_pic_read_reg(u_int reg_offset)
> +{
> + return in_be32((volatile u32 *)(tsi108_pic_vir_addr + reg_offset));
> +}
> +
> +static inline void tsi108_pic_write_reg(u_int reg_offset, u_int val)
> +{
> + out_be32((volatile u32 *)(tsi108_pic_vir_addr + reg_offset), val);
> +}
> +
> +void tsi108_pic_reset(void)
> +{
> + tsi108_pic_write_reg(TSI108_INT_GCR, TSI108PIC_INT_GCR_R);
> + while (tsi108_pic_read_reg(TSI108_INT_GCR) & TSI108PIC_INT_GCR_R)
> + mb();
> +}
> +
> +void tsi108_pic_set_output(int dest_num, u32 sense, u32 polarity)
> +{
> + u32 temp = 0;
> + temp |= (IRQ_SENSE_LEVEL == sense) ?
> + (TSI108PIC_INT_CSR_S_LEVEL) : (TSI108PIC_INT_CSR_S_EDGE);
> + temp |= (IRQ_POLARITY_POSITIVE == polarity) ?
> + (TSI108PIC_INT_CSR_P_HIGH) : (TSI108PIC_INT_CSR_P_LOW);
> + tsi108_pic_write_reg(TSI108_INT_CSR(dest_num), temp);
> + mb();
> +}
> +
> +int tsi108_pic_source_cfg(int src_num, /* interrupt source number */
> + u32 sense, /* interrupt source Sense */
> + u32 polarity, /* interrupt source Polarity */
> + TSI108_IRQ_MODE mode /* interrupt delivery Mode */
> + )
> +{
> + unsigned temp;
> +
> + temp = tsi108_pic_read_reg(TSI108_INT_IVPR(src_num));
> +
> + if (temp & TSI108PIC_ACTIVITY) /* error if source is active */
> + return -1;
> +
> + if (0 == (temp & TSI108PIC_MASK)) {
> + temp |= TSI108PIC_MASK; /* mask IRQ prior making changes */
> + tsi108_pic_write_reg(TSI108_INT_IVPR(src_num), temp);
> + }
> +
> + temp &= ~(TSI108PIC_INT_IVPR_MODE |
> + TSI108PIC_INT_IVPR_S | TSI108PIC_INT_IVPR_P);
> +
> + temp |= (IRQ_SENSE_LEVEL == sense) ?
> + (TSI108PIC_INT_CSR_S_LEVEL) : (TSI108PIC_INT_CSR_S_EDGE);
> + temp |= (IRQ_POLARITY_POSITIVE == polarity) ?
> + (TSI108PIC_INT_CSR_P_HIGH) : (TSI108PIC_INT_CSR_P_LOW);
> +
> + tsi108_pic_write_reg(TSI108_INT_IVPR(src_num),
> + TSI108PIC_MASK | (mode << 29) | temp);
> + return (0);
> +}
> +
> +int tsi108_pic_set_vector(int src_num, /* source number */
> + int vect, /* vector number */
> + int prio /* interrupt source priority */
> + )
> +{
> + unsigned tmp;
> +
> + tmp = tsi108_pic_read_reg(TSI108_INT_IVPR(src_num));
> +
> + if (tmp & TSI108PIC_ACTIVITY) /* error if source is active */
> + return -1;
> +
> + if (0 == (tmp & TSI108PIC_MASK)) {
> + tmp |= TSI108PIC_MASK; /* mask IRQ prior making changes */
> + tsi108_pic_write_reg(TSI108_INT_IVPR(src_num), tmp);
> + }
> +
> + /* clear bits to be changed */
> + tmp &= ~(TSI108PIC_VECTOR_MASK | TSI108PIC_PRIORITY_MASK);
> +
> + tmp |= (prio << 16) | vect;
> + tsi108_pic_write_reg(TSI108_INT_IVPR(src_num), tmp);
> + return 0;
> +}
> +
> +void tsi108_pic_mask_all()
> +{
> + int i;
> + unsigned int vp;
> +
> + /* Mask all external and internal interrupt sources */
> + for (i = 0; i < TSI108PIC_MAX_SOURCES; i++) {
> + vp = tsi108_pic_read_reg(TSI108_INT_IVPR(i));
> + tsi108_pic_write_reg(TSI108_INT_IVPR(i), vp | TSI108PIC_MASK);
> + mb();
> +
> + /* Make sure that irq is masked */
> + do {
> + vp = tsi108_pic_read_reg(TSI108_INT_IVPR(i));
> + } while ((vp & TSI108PIC_ACTIVITY) && !(vp & TSI108PIC_MASK));
> + }
> +
> + /* Mask all timer interrupts */
> + for (i = 0; i < TSI108PIC_NUM_TIMERS; i++) {
> + vp = tsi108_pic_read_reg(TSI108_INT_GTVPR(i));
> + tsi108_pic_write_reg(TSI108_INT_GTVPR(i), vp | TSI108PIC_MASK);
> + mb();
> +
> + do {
> + vp = tsi108_pic_read_reg(TSI108_INT_GTVPR(i));
> + } while ((vp & TSI108PIC_ACTIVITY) && !(vp & TSI108PIC_MASK));
> + }
> +
> + /* Mask all doorbell interrupts */
> + for (i = 0; i < TSI108PIC_NUM_DBELLS; i++) {
> + vp = tsi108_pic_read_reg(TSI108_INT_DVPR(i));
> + tsi108_pic_write_reg(TSI108_INT_IVPR(i), vp | TSI108PIC_MASK);
> + mb();
> +
> + do {
> + vp = tsi108_pic_read_reg(TSI108_INT_DVPR(i));
> + } while ((vp & TSI108PIC_ACTIVITY) && !(vp & TSI108PIC_MASK));
> + }
> +
> + /* Mask all mailbox interrupts */
> + for (i = 0; i < 4; i++) {
> + vp = tsi108_pic_read_reg(TSI108_INT_MBVPR(i));
> + tsi108_pic_write_reg(TSI108_INT_MBVPR(i), vp | TSI108PIC_MASK);
> + mb();
> +
> + do {
> + vp = tsi108_pic_read_reg(TSI108_INT_MBVPR(i));
> + } while ((vp & TSI108PIC_ACTIVITY) && !(vp & TSI108PIC_MASK));
> + }
> +}
> +
> +/*
> + * The Tsi108 PC initialization routine.
> + * A caller routine (usually from platform-specific code has to provide
> + * sense/polarity configuration information for four external interrupt
> + * sources INT0 - INT3. This should be done in form of four-byte array
> + * (one byte per source ) that contains combination of sensitivity/polarity
> + * flags defined in asm-ppc/irq.h.
> + *
> + * Example of PIC initialization call is shown below:
> + *
> + * u_char your_board_pic_initsenses[] __initdata = {
> + * (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), // INT[0]
> + * (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), // INT[1]
> + * (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), // INT[2]
> + * (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE) // INT[3]
> + * };
> + *
> + * tsi108_pic_init(your_board_pic_initsenses);
> + */
> +
> +void __init tsi108_pic_init(u_char * board_init_senses)
> +{
> + u_int i;
> + u32 sense;
> +
> + struct device_node *tsi_pic;
> + tsi_pic = of_find_node_by_type(NULL, "open-pic");
> + if (tsi_pic) {
> + unsigned int size;
> + void *prop = get_property(tsi_pic, "reg", &size);
> + tsi108_pic_phy_addr = of_translate_address(tsi_pic, prop);
> + }
> +
> + DBG("%s: Tsi108 pic phy addr = 0x%x\n", __FUNCTION__,
> + (u32) tsi108_pic_phy_addr);
> + if (tsi108_pic_phy_addr == 0) {
> + printk("No tsi108 PIC found !\n");
> + return;
> + }
> +
> + tsi108_pic_vir_addr = (u32) ioremap(tsi108_pic_phy_addr, 0x400);
> +
> + tsi108_pic_reset();
> +
> + if (ppc_md.progress)
> + ppc_md.progress("tsi108_pic_init: enter", 0x122);
> +
> + /* Initialize timer interrupts */
> + for (i = 0; i < TSI108PIC_NUM_TIMERS; i++) {
> + /* Disabled, Priority 0 */
> + tsi108_pic_inittimer(i, 0, IRQ_TSI108_TIMER0 + i);
> + /* No processor */
> + tsi108_pic_maptimer(i, 0);
> + }
> +
> + /* Init board-specific external sources */
> + for (i = 0; i < 4; i++) {
> + sense = board_init_senses[i];
> +
> + if (sense & IRQ_SENSE_MASK)
> + irq_desc[TSI108_IRQ(i)].status |= IRQ_LEVEL;
> +
> + /* Enabled, Priority 8 */
> + tsi108_pic_initirq(i, 8, TSI108_IRQ(i),
> + (sense & IRQ_POLARITY_MASK),
> + (sense & IRQ_SENSE_MASK));
> + /* Map to CPU #0 */
> + tsi108_pic_mapirq(TSI108_IRQ(i), 1 << 0, 0);
> + }
> +
> + /* Init remaining internal sources. */
> + for (; i < TSI108PIC_MAX_SOURCES; i++) {
> + /* Disabled, Priority 8, by default - Positive Edge */
> + tsi108_pic_initirq(i, 8, TSI108_IRQ(i),
> + IRQ_POLARITY_POSITIVE, IRQ_SENSE_EDGE);
> + /* Map to CPU #0 */
> + tsi108_pic_mapirq(TSI108_IRQ(i), (1 << 0), 0);
> + }
> +
> + /*
> + * Change sensitivity to level for sources that require it.
> + */
> +
> + irq_desc[IRQ_TSI108_GIGE0].status |= IRQ_LEVEL;
> + irq_desc[IRQ_TSI108_GIGE1].status |= IRQ_LEVEL;
> + irq_desc[IRQ_TSI108_PCI].status |= IRQ_LEVEL;
> +
> + /* Init descriptors */
> + for (i = 0; i < TSI108PIC_MAX_SOURCES; i++)
> + irq_desc[i + TSI108_IRQ_BASE].handler = &tsi108_pic_irq;
> +
> + for (i = 0; i < NUM_PCI_IRQS; i++) {
> + irq_desc[i + IRQ_PCI_INTAD_BASE].handler = &tsi108_pci_irq;
> + irq_desc[i + IRQ_PCI_INTAD_BASE].status |= IRQ_LEVEL;
> + }
> +
> + /* Initialize the spurious interrupt */
> + tsi108_pic_set_spurious(TSI108_IRQ_SPURIOUS);
> + tsi108_pic_set_task_priority(0);
> +
> + init_pci_source();
> + tsi108_pic_enable_irq(IRQ_TSI108_PCI);
> +
> + i = tsi108_pic_read_reg(TSI108_INT_VECTOR(0));
> + tsi108_pic_write_reg(TSI108_INT_EOI(0), 0);
> +
> + if (ppc_md.progress)
> + ppc_md.progress("tsi108_pic_init: exit", 0x222);
> +}
> +
> +/*
> + * Find out the current interrupt
> + */
> +static u_int tsi108_pic_get_vect(void)
> +{
> + u_int vec;
> +
> + vec = tsi108_pic_read_reg(TSI108_INT_VECTOR(0)) & TSI108PIC_VECTOR_MASK;
> +
> +#ifdef DBG_TSI108_INTERRUPT
> + if (vec == TSI108_IRQ_SPURIOUS)
> + printk("TSI108: SPURIOUS vec=0x%08x\n", vec);
> + else
> + printk("TSI108: read vec=0x%08x\n", vec);
> +#endif
> + return (vec);
> +}
> +
> +static inline void tsi108_pic_eoi(void)
> +{
> + tsi108_pic_write_reg(TSI108_INT_EOI(0), 0);
> + mb();
> +}
> +
> +static void __init tsi108_pic_set_task_priority(u_int pri)
> +{
> + ASSERT(pri >= 0 && pri < TSI108PIC_NUM_PRI);
> +
> + tsi108_pic_write_reg(TSI108_INT_TASKP(0),
> + pri & TSI108PIC_INT_TASKP_TASKP);
> + mb();
> +}
> +
> +static void tsi108_pic_set_spurious(u_int vec)
> +{
> + ASSERT(vec == TSI108_IRQ_SPURIOUS);
> + tsi108_pic_write_reg(TSI108_INT_SVR, vec);
> + mb();
> +}
> +
> +#ifdef CONFIG_SMP
> +/*
> + * Convert a cpu mask from logical to physical cpu numbers.
> + */
> +static inline u32 physmask(u32 cpumask)
> +{
> + int i;
> + u32 mask = 0;
> +
> + for (i = 0; i < NR_CPUS; ++i, cpumask >>= 1)
> + if (cpu_online(i))
> + mask |= (cpumask & 1) << smp_hw_index[i];
> + return mask;
> +}
> +#else
> +#define physmask(cpumask) (cpumask)
> +#endif
> +
> +/*
> + * Initialize a timer interrupt (and disable it)
> + *
> + * timer: timer number
> + * pri: interrupt source priority
> + * vec: the vector it will produce
> + */
> +static void __init tsi108_pic_inittimer(u_int timer, u_int pri, u_int vec)
> +{
> + unsigned int gtvpr;
> +
> + ASSERT(timer >= 0 && timer < TSI108PIC_NUM_TIMERS);
> + ASSERT(pri >= 0 && pri < TSI108PIC_NUM_PRI);
> + ASSERT(vec >= 0 && vec < TSI108PIC_NUM_VECTORS);
> +
> + gtvpr = tsi108_pic_read_reg(TSI108_INT_GTVPR(timer));
> + gtvpr &= ~(TSI108PIC_PRIORITY_MASK | TSI108PIC_VECTOR_MASK);
> + gtvpr |= (pri << 16) | vec;
> + tsi108_pic_write_reg(TSI108_INT_GTVPR(timer), gtvpr | TSI108PIC_MASK);
> + mb();
> +}
> +
> +/*
> + * Map a timer interrupt to one or more CPUs
> + */
> +static void __init tsi108_pic_maptimer(u_int timer, u_int cpumask)
> +{
> + ASSERT(timer >= 0 && timer < TSI108PIC_NUM_TIMERS);
> +
> + tsi108_pic_write_reg(TSI108_INT_GTDR(timer), physmask(cpumask));
> + mb();
> +}
> +
> +/*
> + * Initalize the interrupt source which will generate an NMI.
> + * This raises the interrupt's priority from 8 to 9.
> + *
> + * irq: The logical IRQ which generates an NMI.
> + */
> +void __init tsi108_pic_init_nmi_irq(u_int irq)
> +{
> + u_int offset = get_vector_offset(irq);
> + u_int vpr = tsi108_pic_read_reg(offset);
> + vpr &= ~TSI108PIC_PRIORITY_MASK;
> + tsi108_pic_write_reg(offset, vpr | (9 << 16));
> + mb();
> +}
> +
> +/*
> + *
> + * All functions below take an offset'ed irq argument
> + *
> + */
> +
> +/*
> + * Hookup a cascade to the tsi108 PIC.
> + */
> +void __init
> +tsi108_pic_hookup_cascade(u_int irq, char *name,
> + int (*cascade_fn) (struct pt_regs *))
> +{
> + tsi108_pic_cascade_irq = irq;
> + tsi108_pic_cascade_fn = cascade_fn;
> + if (request_irq(irq, no_action, SA_INTERRUPT, name, NULL))
> + printk("Unable to get Tsi108 PIC IRQ %d for cascade\n",
> + irq - TSI108_IRQ_BASE);
> +}
> +
> +/*
> + * Enable/disable an external interrupt source
> + *
> + * Externally called, irq is an offseted system-wide interrupt number
> + */
> +static void tsi108_pic_enable_irq(u_int irq)
> +{
> + u32 offset = get_vector_offset(irq);
> + u32 vpr = tsi108_pic_read_reg(offset);
> +
> + /*
> + * Undo sensitivity change (see tsi108_pic_disable_irq())
> + */
> + if (irq_desc[irq].status & IRQ_LEVEL)
> + vpr |= TSI108PIC_INT_IVPR_S;
> +
> + tsi108_pic_write_reg(offset, vpr & ~TSI108PIC_MASK);
> + mb();
> +}
> +
> +static void tsi108_pic_disable_irq(u_int irq)
> +{
> + u32 offset = get_vector_offset(irq);
> + u32 vpr = tsi108_pic_read_reg(offset);
> +
> + /*
> + * Switch level interrupt to edge sensitivity to avoid generation
> + * of spurious interrupt request. See design note in Tsi108 PIC
> + * section of Tsi108 manual.
> + */
> + if (irq_desc[irq].status & IRQ_LEVEL)
> + vpr &= ~TSI108PIC_INT_IVPR_S;
> +
> + tsi108_pic_write_reg(offset, vpr | TSI108PIC_MASK);
> + mb();
> + vpr = tsi108_pic_read_reg(offset);
> + if (!(vpr & TSI108PIC_MASK))
> + printk("TSI108_PIC: Error - Unable disable IRQ %d\n", irq);
> +}
> +
> +/*
> + * Initialize an interrupt source (and disable it!)
> + *
> + * irq: Tsi108 PIC interrupt source number
> + * pri: interrupt source priority
> + * vec: the vector it will produce
> + * pol: polarity (1 for positive, 0 for negative)
> + * sense: 1 for level, 0 for edge
> + */
> +static void __init
> +tsi108_pic_initirq(u_int irq, u_int pri, u_int vec, int pol, int sense)
> +{
> + unsigned int ivpr;
> +
> + ivpr = TSI108PIC_MASK | (pri << 16) | vec;
> + ivpr |= (IRQ_SENSE_LEVEL == sense) ?
> + TSI108PIC_INT_IVPR_S_LEVEL : TSI108PIC_INT_IVPR_S_EDGE;
> + ivpr |= (IRQ_POLARITY_POSITIVE == pol) ?
> + TSI108PIC_INT_IVPR_P_HIGH : TSI108PIC_INT_IVPR_P_LOW;
> + tsi108_pic_write_reg(TSI108_INT_IVPR(irq), ivpr);
> + mb();
> +}
> +
> +int tsi108_pic_set_irq_sense(int irq, /* PIC source number */
> + int pol, /* interrupt source polarity */
> + int sense /* interrupt source sense */
> + )
> +{
> + unsigned int ivpr;
> +
> + ivpr = tsi108_pic_read_reg(TSI108_INT_IVPR(irq));
> +
> + if (ivpr & TSI108PIC_ACTIVITY) /* error if source is active */
> + return -1;
> +
> + if (0 == (ivpr & TSI108PIC_MASK)) {
> + ivpr |= TSI108PIC_MASK; /* mask IRQ prior making changes */
> + tsi108_pic_write_reg(TSI108_INT_IVPR(irq), ivpr);
> + }
> +
> + /* clear bits to be changed */
> + ivpr &= ~(TSI108PIC_INT_IVPR_P | TSI108PIC_INT_IVPR_S);
> +
> + ivpr |= (IRQ_SENSE_LEVEL == sense) ?
> + TSI108PIC_INT_IVPR_S_LEVEL : TSI108PIC_INT_IVPR_S_EDGE;
> + ivpr |= (IRQ_POLARITY_POSITIVE == pol) ?
> + TSI108PIC_INT_IVPR_P_HIGH : TSI108PIC_INT_IVPR_P_LOW;
> +
> + tsi108_pic_write_reg(TSI108_INT_IVPR(irq), ivpr);
> + return 0;
> +}
> +
> +/*
> + * Map an interrupt source to one or more CPUs
> + */
> +static void tsi108_pic_mapirq(u_int irq, u_int physmask, u_int keepmask)
> +{
> + u_int offset = get_vector_offset(irq);
> +
> + if (0 == offset)
> + return;
> + if (keepmask != 0)
> + physmask |= tsi108_pic_read_reg(offset + 4);
> + tsi108_pic_write_reg(offset + 4, physmask);
> + mb();
> +}
> +
> +/* No spinlocks, should not be necessary with the Tsi108 PIC
> + * (1 register = 1 interrupt and we have the desc lock).
> + */
> +static void tsi108_pic_ack_irq(unsigned int irq_nr)
> +{
> + if ((irq_desc[irq_nr].status & IRQ_LEVEL) == 0)
> + tsi108_pic_eoi();
> +}
> +
> +static void tsi108_pic_end_irq(unsigned int irq_nr)
> +{
> + if ((irq_desc[irq_nr].status & IRQ_LEVEL) != 0)
> + tsi108_pic_eoi();
> +}
> +
> +void tsi108_pic_set_affinity(unsigned int irq_nr, unsigned long cpumask)
> +{
> + tsi108_pic_mapirq(irq_nr, physmask(cpumask), 0);
> +}
> +
> +int tsi108_pic_get_irq(struct pt_regs *regs)
> +{
> + int vector = tsi108_pic_get_vect();
> +
> + if (vector == TSI108_IRQ_SPURIOUS) {
> + vector = -1;
> + }
> +
> + if (vector == IRQ_TSI108_PCI) {
> + vector = get_pci_source(vector);
> + }
> +
> + if (vector == -1) {
> + tsi108_pic_write_reg(TSI108_INT_EOI(0), 0);
> + }
> +
> + return vector;
> +}
> +
> +static void tsi108_pci_int_mask(u_int irq)
> +{
> + u_int irp_cfg;
> + int int_line = (irq - IRQ_PCI_INTAD_BASE);
> +
> + irp_cfg = tsi108_read_reg(TSI108_PCI_OFFSET + TSI108_PCI_IRP_CFG_CTL);
> + mb();
> + irp_cfg |= (1 << int_line); /* INTx_DIR = output */
> + irp_cfg &= ~(3 << (8 + (int_line * 2))); /* INTx_TYPE = unused */
> + tsi108_write_reg(TSI108_PCI_OFFSET + TSI108_PCI_IRP_CFG_CTL, irp_cfg);
> + mb();
> + irp_cfg = tsi108_read_reg(TSI108_PCI_OFFSET + TSI108_PCI_IRP_CFG_CTL);
> +}
> +
> +static void tsi108_pci_int_unmask(u_int irq)
> +{
> + u_int irp_cfg;
> + int int_line = (irq - IRQ_PCI_INTAD_BASE);
> +
> + irp_cfg = tsi108_read_reg(TSI108_PCI_OFFSET + TSI108_PCI_IRP_CFG_CTL);
> + mb();
> + irp_cfg &= ~(1 << int_line);
> + irp_cfg |= (3 << (8 + (int_line * 2)));
> + tsi108_write_reg(TSI108_PCI_OFFSET + TSI108_PCI_IRP_CFG_CTL, irp_cfg);
> + mb();
> +}
> +
> +static void tsi108_pci_irq_enable(u_int irq)
> +{
> + tsi108_pci_int_unmask(irq);
> +}
> +
> +static void tsi108_pci_irq_disable(u_int irq)
> +{
> + tsi108_pci_int_mask(irq);
> +}
> +
> +static void tsi108_pci_irq_ack(u_int irq)
> +{
> + tsi108_pci_int_mask(irq);
> +}
> +
> +static void tsi108_pci_irq_end(u_int irq)
> +{
> + tsi108_pic_eoi(); /* eoi IRQ_TSI108_PCI */
> + tsi108_pci_int_unmask(irq);
> +}
> +
> +static inline int get_pci_source(int vector)
> +{
> + u_int temp = 0;
> + int irq = -1;
> + int i;
> + u_int pci_irp_stat;
> + static int mask = 0;
> +
> + /* Read PCI/X block interrupt status register */
> + pci_irp_stat = tsi108_read_reg(TSI108_PCI_OFFSET + TSI108_PCI_IRP_STAT);
> + mb();
> +
> + if (pci_irp_stat & TSI108_PCI_IRP_STAT_P_INT) {
> + /* Process Interrupt from PCI bus INTA# - INTD# lines */
> + temp =
> + tsi108_read_reg(TSI108_PCI_OFFSET +
> + TSI108_PCI_IRP_INTAD) & 0xf;
> + mb();
> + for (i = 0; i < 4; i++, mask++) {
> + if (temp & (1 << mask % 4)) {
> + irq = IRQ_PCI_INTA + mask % 4;
> + mask++;
> + break;
> + }
> + }
> + }
> +#ifdef DBG_TSI108_INTERRUPT
> + else {
> + printk("TSI108_PIC: error in TSI108_PCI_IRP_STAT\n");
> + pci_irp_stat =
> + tsi108_read_reg(TSI108_PCI_OFFSET + TSI108_PCI_IRP_STAT);
> + temp =
> + tsi108_read_reg(TSI108_PCI_OFFSET + TSI108_PCI_IRP_INTAD);
> + mb();
> + printk(">> stat=0x%08x intad=0x%08x ", pci_irp_stat, temp);
> + temp =
> + tsi108_read_reg(TSI108_PCI_OFFSET + TSI108_PCI_IRP_CFG_CTL);
> + mb();
> + printk("cfg_ctl=0x%08x ", temp);
> + temp =
> + tsi108_read_reg(TSI108_PCI_OFFSET + TSI108_PCI_IRP_ENABLE);
> + mb();
> + printk("irp_enable=0x%08x\n", temp);
> + }
> +#endif /* DBG_TSI108_INTERRUPT */
> +
> + return irq;
> +}
> +
> +static void init_pci_source(void)
> +{
> + tsi108_write_reg(TSI108_PCI_OFFSET + TSI108_PCI_IRP_CFG_CTL,
> + 0x0000ff00);
> + tsi108_write_reg(TSI108_PCI_OFFSET + TSI108_PCI_IRP_ENABLE,
> + 0x00400000);
> + mb();
> +}
> +
> +static struct sysdev_class tsi108_pic_sysclass = {
> + set_kset_name("tsi108_pic"),
> +};
> +
> +static struct sys_device device_tsi108_pic = {
> + .id = 0,
> + .cls = &tsi108_pic_sysclass,
> +};
> +
> +static struct sysdev_driver driver_tsi108_pic = {
> +#ifdef CONFIG_PM /* FIXME: placeholder for future development */
> + .suspend = &tsi108_pic_suspend,
> + .resume = &tsi108_pic_resume,
> +#endif /* CONFIG_PM */
> +};
> +
> +static int __init init_tsi108_pic_sysfs(void)
> +{
> + int rc;
> +
> + if (!get_csrbase())
> + return -ENODEV;
> + printk(KERN_DEBUG "Registering tsi108_pic with sysfs...\n");
> + rc = sysdev_class_register(&tsi108_pic_sysclass);
> + if (rc) {
> + printk(KERN_ERR "Failed registering tsi108_pic sys class\n");
> + return -ENODEV;
> + }
> + rc = sysdev_register(&device_tsi108_pic);
> + if (rc) {
> + printk(KERN_ERR "Failed registering tsi108_pic sys device\n");
> + return -ENODEV;
> + }
> + rc = sysdev_driver_register(&tsi108_pic_sysclass, &driver_tsi108_pic);
> + if (rc) {
> + printk(KERN_ERR "Failed registering tsi108_pic sys driver\n");
> + return -ENODEV;
> + }
> + return 0;
> +}
> +
> +subsys_initcall(init_tsi108_pic_sysfs);
> diff --git a/include/asm-powerpc/tsi108_pic.h b/include/asm-powerpc/tsi108_pic.h
> new file mode 100644
> index 0000000..7b23352
> --- /dev/null
> +++ b/include/asm-powerpc/tsi108_pic.h
> @@ -0,0 +1,232 @@
> +/*
> + * (C) Copyright 2005 Tundra Semiconductor Corp.
> + * Alex Bounine, <alexandreb@tundra.com).
> + *
> + * See file CREDITS for list of people who contributed to this
> + * project.
> + *
> + * This program is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU General Public License as
> + * published by the Free Software Foundation; either version 2 of
> + * the License, or (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, write to the Free Software
> + * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
> + * MA 02111-1307 USA
> + */
> +
> +/*
> + * arch/ppc/syslib/tsi108_pic.h - Tsi108 Interrupt Controller definitions
> + */
> +
> +#ifndef _LINUX_TSI108_PIC_H
> +#define _LINUX_TSI108_PIC_H
> +
> +#include <asm/tsi108_irq.h>
> +
> +#ifdef __KERNEL__
> +
> +/*
> + * Tsi108 PIC supports up to 24 interrupt sources and up to 4 processors
> + */
> +
> +#define TSI108PIC_MAX_SOURCES 24
> +#define TSI108PIC_MAX_PROCESSORS 4
> +
> +#define TSI108PIC_NUM_TIMERS 4
> +#define TSI108PIC_NUM_DBELLS 4
> +#define TSI108PIC_NUM_PROC 4
> +#define TSI108PIC_NUM_PRI 16
> +#define TSI108PIC_NUM_VECTORS 256
> +
> +/*
> + * Tsi108 PIC Register offsets within block.
> + */
> +
> +/* Registers controlling sources */
> +#define TSI108_INT_FRR (0x000)
> +#define TSI108_INT_GCR (0x004)
> +#define TSI108_INT_SVR (0x010)
> +#define TSI108_INT_GTVPR(n) (0x38 + 0x10*(n))
> +#define TSI108_INT_GTDR(n) (0x3C + 0x10*(n))
> +#define TSI108_INT_IVPR(n) (0x100 + 0x8*(n))
> +#define TSI108_INT_IDR(n) (0x104 + 0x8*(n))
> +#define TSI108_INT_DVPR(n) (0x204 + 0xC*(n))
> +#define TSI108_INT_DDR(n) (0x208 + 0xC*(n))
> +#define TSI108_INT_MBVPR(n) (0x284 + 0x10*(n))
> +#define TSI108_INT_MBDR(n) (0x288 + 0x10*(n))
> +
> +/* Registers controlling destinations */
> +#define TSI108_INT_TASKP(n) (0x300 + 0x40*(n))
> +#define TSI108_INT_VECTOR(n) (0x304 + 0x40*(n))
> +#define TSI108_INT_EOI(n) (0x308 + 0x40*(n))
> +#define TSI108_INT_CSR(n) (0x30C + 0x40*(n))
> +
> +/*
> + * Generic definitions common for different types of interrupt
> + * sources.
> + */
> +
> +#define TSI108PIC_MASK (0x80000000)
> +#define TSI108PIC_ACTIVITY (0x40000000)
> +#define TSI108PIC_PRIORITY_MASK (0x000f0000)
> +#define TSI108PIC_VECTOR_MASK (0x000000ff)
> +
> +/**********************************************************
> + * Register Bit Masks definitions for every register
> + */
> +
> +/* TSI108PIC_INT_FRR : Register Bits Masks Definitions */
> +#define TSI108PIC_INT_FRR_VID (0x000000ff)
> +#define TSI108PIC_INT_FRR_NCPU (0x00001f00)
> +#define TSI108PIC_INT_FRR_NITM (0x0000e000)
> +#define TSI108PIC_INT_FRR_NIRQ (0x07ff0000)
> +#define TSI108PIC_INT_FRR_NIDOOR (0xe0000000)
> +#define TSI108PIC_INT_FRR_RESERVED (0x18000000)
> +
> +/* TSI108PIC_INT_GCR : Register Bits Masks Definitions */
> +#define TSI108PIC_INT_GCR_R (0x80000000)
> +#define TSI108PIC_INT_GCR_RESERVED (0x7fffffff)
> +
> +/* TSI108PIC_INT_ICR : Register Bits Masks Definitions */
> +#define TSI108PIC_INT_ICR_R (0x0000000f)
> +#define TSI108PIC_INT_ICR_RESERVED (0xfffffff0)
> +
> +/* TSI108PIC_INT_MVI : Register Bits Masks Definitions */
> +#define TSI108PIC_INT_MVI_VID (0x000000ff)
> +#define TSI108PIC_INT_MVI_DID (0x0000ff00)
> +#define TSI108PIC_INT_MVI_STEP (0x00ff0000)
> +#define TSI108PIC_INT_MVI_RESERVED (0xff000000)
> +
> +/* TSI108PIC_INT_SVR : Register Bits Masks Definitions */
> +#define TSI108PIC_INT_SVR_VECTOR (0x000000ff)
> +#define TSI108PIC_INT_SVR_RESERVED (0xffffff00)
> +
> +/* TSI108PIC_INT_TFRR : Register Bits Masks Definitions */
> +#define TSI108PIC_INT_TFRR_TIME_FREQ (0xffffffff)
> +
> +/* TSI108PIC_INT_SOFT_SET : Register Bits Masks Definitions */
> +#define TSI108PIC_INT_SOFT_SET_S (0x00ffffff)
> +#define TSI108PIC_INT_SOFT_SET_RESERVED (0xff000000)
> +
> +/* TSI108PIC_INT_SOFT_ENABLE : Register Bits Masks Definitions */
> +#define TSI108PIC_INT_SOFT_ENABLE_EN (0x00ffffff)
> +#define TSI108PIC_INT_SOFT_ENABLE_RESERVED (0xff000000)
> +
> +/* TSI108PIC_INT_GTCCR(X) : Register Bits Masks Definitions */
> +#define TSI108PIC_INT_GTCCR_COUNT (0x7fffffff)
> +#define TSI108PIC_INT_GTCCR_T (0x80000000)
> +
> +/* TSI108PIC_INT_GTBCR(X) : Register Bits Masks Definitions */
> +#define TSI108PIC_INT_GTBCR_B_COUNT (0x7fffffff)
> +#define TSI108PIC_INT_GTBCR_CI (0x80000000)
> +
> +/* TSI108PIC_INT_GTVPR(X) : Register Bits Masks Definitions */
> +#define TSI108PIC_INT_GTVPR_VECTOR (0x000000ff)
> +#define TSI108PIC_INT_GTVPR_PRIORITY (0x000f0000)
> +#define TSI108PIC_INT_GTVPR_PRESCALE (0x00f00000)
> +#define TSI108PIC_INT_GTVPR_A (0x40000000)
> +#define TSI108PIC_INT_GTVPR_M (0x80000000)
> +#define TSI108PIC_INT_GTVPR_RESERVED (0x3f00ff00)
> +
> +/* TSI108PIC_INT_GTDR(X) : Register Bits Masks Definitions */
> +#define TSI108PIC_INT_GTDR_SEL_OUT (0x0000000f)
> +#define TSI108PIC_INT_GTDR_RESERVED (0xfffffff0)
> +
> +/* TSI108PIC_INT_IVPR(X) : Register Bits Masks Definitions */
> +#define TSI108PIC_INT_IVPR_VECTOR (0x000000ff)
> +#define TSI108PIC_INT_IVPR_PRIORITY (0x000f0000)
> +
> +#define TSI108PIC_INT_IVPR_P (0x01000000)
> +#define TSI108PIC_INT_IVPR_P_LOW (0 << 24)
> +#define TSI108PIC_INT_IVPR_P_HIGH (1 << 24)
> +
> +#define TSI108PIC_INT_IVPR_S (0x02000000)
> +#define TSI108PIC_INT_IVPR_S_EDGE (0 << 25)
> +#define TSI108PIC_INT_IVPR_S_LEVEL (1 << 25)
> +
> +#define TSI108PIC_INT_IVPR_MODE (0x20000000)
> +#define TSI108PIC_INT_IVPR_A (0x40000000)
> +#define TSI108PIC_INT_IVPR_M (0x80000000)
> +#define TSI108PIC_INT_IVPR_RESERVED (0x1cf0ff00)
> +
> +/* TSI108PIC_INT_IDR(X) : Register Bits Masks Definitions */
> +#define TSI108PIC_INT_IDR_SEL_OUT (0x0000000f)
> +#define TSI108PIC_INT_IDR_RESERVED (0xfffffff0)
> +
> +/* TSI108PIC_INT_DAR : Register Bits Masks Definitions */
> +#define TSI108PIC_INT_DAR_A (0x0000000f)
> +#define TSI108PIC_INT_DAR_RESERVED (0xfffffff0)
> +
> +/* TSI108PIC_INT_DVPR(X) : Register Bits Masks Definitions */
> +#define TSI108PIC_INT_DVPR_VECTOR (0x000000ff)
> +#define TSI108PIC_INT_DVPR_PRIORITY (0x000f0000)
> +#define TSI108PIC_INT_DVPR_A (0x40000000)
> +#define TSI108PIC_INT_DVPR_M (0x80000000)
> +#define TSI108PIC_INT_DVPR_RESERVED (0x3ff0ff00)
> +
> +/* TSI108PIC_INT_DDR(X) : Register Bits Masks Definitions */
> +#define TSI108PIC_INT_DDR_SEL_OUT (0x0000000f)
> +#define TSI108PIC_INT_DDR_RESERVED (0xfffffff0)
> +
> +/* TSI108PIC_INT_DMR(X) : Register Bits Masks Definitions */
> +#define TSI108PIC_INT_DMR_M (0xffffffff)
> +
> +/* TSI108PIC_INT_MBR(X) : Register Bits Masks Definitions */
> +#define TSI108PIC_INT_MBR_M (0xffffffff)
> +
> +/* TSI108PIC_INT_MBVPR(X) : Register Bits Masks Definitions */
> +#define TSI108PIC_INT_MBVPR_VECTOR (0x000000ff)
> +#define TSI108PIC_INT_MBVPR_PRIORITY (0x000f0000)
> +#define TSI108PIC_INT_MBVPR_A (0x40000000)
> +#define TSI108PIC_INT_MBVPR_M (0x80000000)
> +#define TSI108PIC_INT_MBVPR_RESERVED (0x3ff0ff00)
> +
> +/* TSI108PIC_INT_MBDR(X) : Register Bits Masks Definitions */
> +#define TSI108PIC_INT_MBDR_SEL_OUT (0x0000000f)
> +#define TSI108PIC_INT_MBDR_RESERVED (0xfffffff0)
> +
> +/* TSI108PIC_INT_TASKP(X) : Register Bits Masks Definitions */
> +#define TSI108PIC_INT_TASKP_TASKP (0x0000000f)
> +#define TSI108PIC_INT_TASKP_RESERVED (0xfffffff0)
> +
> +/* TSI108PIC_INT_VECTOR(X) : Register Bits Masks Definitions */
> +#define TSI108PIC_INT_VECTOR_VECTOR (0x000000ff)
> +#define TSI108PIC_INT_VECTOR_LS_VECTOR (0xff000000)
> +#define TSI108PIC_INT_VECTOR_RESERVED (0x00ffff00)
> +
> +/* TSI108PIC_INT_EOI(X) : Register Bits Masks Definitions */
> +#define TSI108PIC_INT_EOI_EOI (0x000000ff)
> +#define TSI108PIC_INT_EOI_RESERVED (0xffffff00)
> +
> +/* TSI108PIC_INT_CSR(X) : Register Bits Masks Definitions */
> +#define TSI108PIC_INT_CSR_RESERVED (0xfffffffc)
> +
> +#define TSI108PIC_INT_CSR_P (1 << 0)
> +#define TSI108PIC_INT_CSR_P_LOW (0 << 0)
> +#define TSI108PIC_INT_CSR_P_HIGH (1 << 0)
> +
> +#define TSI108PIC_INT_CSR_S (1 << 1)
> +#define TSI108PIC_INT_CSR_S_EDGE (0 << 1)
> +#define TSI108PIC_INT_CSR_S_LEVEL (1 << 1)
> +
> +extern void tsi108_pic_init(u_char * board_init_senses);
> +extern void tsi108_pic_reset(void);
> +extern void tsi108_pic_set_output(int dest_num, u32 sense, u32 polarity);
> +extern int tsi108_pic_source_cfg(int src_num, u32 sense,
> + u32 polarity, TSI108_IRQ_MODE mode);
> +extern int tsi108_pic_set_vector(int src_num, int vect, int prio);
> +extern void tsi108_pic_init_nmi_irq(u_int irq);
> +extern void tsi108_pic_hookup_cascade(u_int irq, char *name,
> + int (*cascade_fn) (struct pt_regs *));
> +extern int tsi108_pic_get_irq(struct pt_regs *regs);
> +
> +#endif /* __KERNEL__ */
> +
> +#endif /* _LINUX_TSI108_PIC_H */
^ permalink raw reply
* Re: [PATCH/2.6.17-rc4 3/10] Powerpc: Add tsi108 common function
From: Benjamin Herrenschmidt @ 2006-05-18 0:49 UTC (permalink / raw)
To: Zang Roy-r61911
Cc: linuxppc-dev list, Yang Xin-Xin-r48390, Paul Mackerras,
Alexandre.Bounine
In-Reply-To: <9FCDBA58F226D911B202000BDBAD46730626D61D@zch01exm40.ap.freescale.net>
On Wed, 2006-05-17 at 18:14 +0800, Zang Roy-r61911 wrote:
> Add Tundra Semiconductor tsi108 host bridge common function support.
As I explained in the first patch, the PCI functions for the tsi108
should be kept together with the pci_ops structure. If you have a few
other functions unrelated to PCI, it's ok to keep them all in the same
file, but you start adding more, you should also think about splitting
tsi108_pci.c from tsi108_whateverelse.c :)
Ben.
> Signed-off-by: Alexandre Bounine <alexandreb@tundra.com>
> Signed-off-by: Roy Zang <tie-fei.zang@freescale.com>
>
> ---
>
> arch/powerpc/sysdev/Makefile | 1
> arch/powerpc/sysdev/tsi108_common.c | 224 +++++++++++++++++++++++++++++++++++
> 2 files changed, 225 insertions(+), 0 deletions(-)
> create mode 100644 arch/powerpc/sysdev/tsi108_common.c
>
> ab3b477b5924d2c9cbf6ba3ae4d95fe333c9bad9
> diff --git a/arch/powerpc/sysdev/Makefile b/arch/powerpc/sysdev/Makefile
> index 4c2b356..8c0afb7 100644
> --- a/arch/powerpc/sysdev/Makefile
> +++ b/arch/powerpc/sysdev/Makefile
> @@ -8,3 +8,4 @@ obj-$(CONFIG_U3_DART) += dart_iommu.o
> obj-$(CONFIG_MMIO_NVRAM) += mmio_nvram.o
> obj-$(CONFIG_PPC_83xx) += ipic.o
> obj-$(CONFIG_FSL_SOC) += fsl_soc.o
> +obj-$(CONFIG_TSI108_BRIDGE) += tsi108_common.o tsi108_pic.o
> diff --git a/arch/powerpc/sysdev/tsi108_common.c b/arch/powerpc/sysdev/tsi108_common.c
> new file mode 100644
> index 0000000..3c55f99
> --- /dev/null
> +++ b/arch/powerpc/sysdev/tsi108_common.c
> @@ -0,0 +1,224 @@
> +/*
> + * arch/ppc/syslib/tsi108_common.c
> + *
> + * Common routines for Tundra Semiconductor TSI108 host bridge.
> + *
> + * 2004-2005 (c) Tundra Semiconductor Corp.
> + * Author: Alex Bounine (alexandreb@tundra.com)
> + *
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms of the GNU General Public License as published by the Free
> + * Software Foundation; either version 2 of the License, or (at your option)
> + * any later version.
> + *
> + * This program is distributed in the hope that it will be useful, but WITHOUT
> + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
> + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
> + * more details.
> + *
> + * You should have received a copy of the GNU General Public License along with
> + * this program; if not, write to the Free Software Foundation, Inc., 59
> + * Temple Place - Suite 330, Boston, MA 02111-1307, USA.
> + */
> +
> +#include <linux/kernel.h>
> +#include <linux/init.h>
> +#include <linux/pci.h>
> +#include <linux/slab.h>
> +
> +#include <asm/byteorder.h>
> +#include <asm/io.h>
> +#include <asm/irq.h>
> +#include <asm/uaccess.h>
> +#include <asm/machdep.h>
> +#include <asm/pci-bridge.h>
> +#include <asm/tsi108.h>
> +#include <asm/prom.h>
> +
> +u32 tsi108_pci_cfg_base;
> +
> +#undef TSI108_PCI_DEBUG
> +
> +#define tsi_mk_config_addr(bus, devfunc, offset) \
> + (((bus)<<16) | ((devfunc)<<8) | (offset & 0xfc)| tsi108_pci_cfg_base)
> +
> +static phys_addr_t tsi108_csr_base = -1;
> +
> +phys_addr_t get_csrbase(void)
> +{
> + struct device_node *tsi;
> +
> + if (tsi108_csr_base != -1)
> + return tsi108_csr_base;
> +
> + tsi = of_find_node_by_type(NULL, "tsi-bridge");
> + if (tsi) {
> + unsigned int size;
> + void *prop = get_property(tsi, "reg", &size);
> + tsi108_csr_base = of_translate_address(tsi, prop);
> + of_node_put(tsi);
> + };
> + return tsi108_csr_base;
> +}
> +
> +u32 get_vir_csrbase(void)
> +{
> + return (u32) (ioremap(get_csrbase(), 0x10000));
> +}
> +
> +EXPORT_SYMBOL(get_csrbase);
> +EXPORT_SYMBOL(get_vir_csrbase);
> +
> +/*
> + * Prosessor Bus Clock (in MHz) defined by CG_PB_SELECT
> + * (based on recommended Tsi108 reference clock 33MHz)
> + */
> +static int pb_clk_sel[8] = { 0, 0, 183, 100, 133, 167, 200, 233 };
> +
> +/*
> + * SDRAM Clock (in MHz) defined by CG_SD_SELECT
> + * (based on recommended Tsi108 reference clock 33MHz)
> + */
> +static int sd_clk_sel[2][8] = {
> + {0, 0, 183, 100, 133, 167, 200, 233}, /* SYNC */
> + {0, 0, 0, 0, 133, 160, 200, 0} /* ASYNC */
> +};
> +
> +int
> +tsi108_direct_write_config(struct pci_bus *bus, unsigned int devfunc,
> + int offset, int len, u32 val)
> +{
> + volatile unsigned char *cfg_addr;
> +
> + cfg_addr = (unsigned char *)(tsi_mk_config_addr(bus->number,
> + devfunc, offset) |
> + (offset & 0x03));
> +
> +#ifdef TSI108_PCI_DEBUG
> + printk("PCI CFG write : ");
> + printk("%d:0x%x:0x%x ", bus->number, devfunc, offset);
> + printk("%d ADDR=0x%08x ", len, (uint) cfg_addr);
> + printk("data = 0x%08x\n", val);
> +#endif
> +
> + switch (len) {
> + case 1:
> + out_8((u8 *) cfg_addr, val);
> + break;
> + case 2:
> + out_le16((u16 *) cfg_addr, val);
> + break;
> + default:
> + out_le32((u32 *) cfg_addr, val);
> + break;
> + }
> +
> + return PCIBIOS_SUCCESSFUL;
> +}
> +
> +void tsi108_clear_pci_error(u32 pci_cfg_base)
> +{
> + u32 err_stat, err_addr, pci_stat;
> +
> + /*
> + * Quietly clear PB and PCI error flags set as result
> + * of PCI/X configuration read requests.
> + */
> +
> + /* Read PB Error Log Registers */
> +
> + err_stat = tsi108_read_reg(TSI108_PB_OFFSET + TSI108_PB_ERRCS);
> + err_addr = tsi108_read_reg(TSI108_PB_OFFSET + TSI108_PB_AERR);
> +
> + if (err_stat & TSI108_PB_ERRCS_ES) {
> + /* Clear error flag */
> + tsi108_write_reg(TSI108_PB_OFFSET + TSI108_PB_ERRCS,
> + TSI108_PB_ERRCS_ES);
> +
> + /* Clear read error reported in PB_ISR */
> + tsi108_write_reg(TSI108_PB_OFFSET + TSI108_PB_ISR,
> + TSI108_PB_ISR_PBS_RD_ERR);
> +
> + /* Clear PCI/X bus cfg errors if applicable */
> + if ((err_addr & 0xFF000000) == pci_cfg_base) {
> + pci_stat =
> + tsi108_read_reg(TSI108_PCI_OFFSET + TSI108_PCI_CSR);
> + tsi108_write_reg(TSI108_PCI_OFFSET + TSI108_PCI_CSR,
> + pci_stat);
> + }
> + }
> +
> + return;
> +}
> +
> +#define __tsi108_read_pci_config(x, addr, op) \
> + __asm__ __volatile__( \
> + " "op" %0,0,%1\n" \
> + "1: eieio\n" \
> + "2:\n" \
> + ".section .fixup,\"ax\"\n" \
> + "3: li %0,-1\n" \
> + " b 2b\n" \
> + ".section __ex_table,\"a\"\n" \
> + " .align 2\n" \
> + " .long 1b,3b\n" \
> + ".text" \
> + : "=r"(x) : "r"(addr))
> +
> +int
> +tsi108_direct_read_config(struct pci_bus *bus, unsigned int devfn, int offset,
> + int len, u32 * val)
> +{
> + volatile unsigned char *cfg_addr;
> + u32 temp;
> +
> + cfg_addr = (unsigned char *)(tsi_mk_config_addr(bus->number,
> + devfn,
> + offset) | (offset &
> + 0x03));
> +
> + switch (len) {
> + case 1:
> + __tsi108_read_pci_config(temp, cfg_addr, "lbzx");
> + break;
> + case 2:
> + __tsi108_read_pci_config(temp, cfg_addr, "lhbrx");
> + break;
> + default:
> + __tsi108_read_pci_config(temp, cfg_addr, "lwbrx");
> + break;
> + }
> +
> + *val = temp;
> +
> +#ifdef TSI108_PCI_DEBUG
> + if ((0xFFFFFFFF != temp) && (0xFFFF != temp) && (0xFF != temp)) {
> + printk("PCI CFG read : ");
> + printk("%d:0x%x:0x%x ", bus->number, devfn, offset);
> + printk("%d ADDR=0x%08x ", len, (uint) cfg_addr);
> + printk("data = 0x%x\n", *val);
> + }
> +#endif
> + return PCIBIOS_SUCCESSFUL;
> +}
> +
> +unsigned long tsi108_get_cpu_clk(void)
> +{
> + /* Detect PB clock freq. */
> + u32 i = tsi108_read_reg(TSI108_CLK_OFFSET + TSI108_CG_PWRUP_STATUS);
> +
> + i = (i >> 16) & 0x07; /* Get PB PLL multiplier */
> + return (pb_clk_sel[i] * 1000000);
> +}
> +
> +unsigned long tsi108_get_sdc_clk(void)
> +{
> + u32 i, k;
> +
> + /* Get SDC/PB clock freq. from CG settings */
> + i = tsi108_read_reg(TSI108_CLK_OFFSET + TSI108_CG_PWRUP_STATUS);
> + k = (i >> 16) & 0x07; /* Get PB PLL multiplier */
> + i = (i >> 20) & 0x07; /* Get SDC PLL multiplier */
> + k = (k == i) ? 0 : 1; /* sync/async configuration */
> + return (sd_clk_sel[k][i] * 1000000);
> +}
^ permalink raw reply
* Re: [PATCH/2.6.17-rc4 2/10] Powerpc: Add Tundra Semiconductor tsi108 macro define
From: Benjamin Herrenschmidt @ 2006-05-18 0:48 UTC (permalink / raw)
To: Zang Roy-r61911
Cc: linuxppc-dev list, Yang Xin-Xin-r48390, Paul Mackerras,
Alexandre.Bounine
In-Reply-To: <9FCDBA58F226D911B202000BDBAD46730626D61C@zch01exm40.ap.freescale.net>
> +/* Mapping of MPIC outputs to processors' interrupt pins */
> +
> +#define IDIR_INT_OUT0 0x1
> +#define IDIR_INT_OUT1 0x2
> +#define IDIR_INT_OUT2 0x4
> +#define IDIR_INT_OUT3 0x8
All this mapping should be provided by the device-tree
> +/* Error codes */
> +
> +#define MPIC_OK 0
> +#define MPIC_ERROR 1
What the heck are these for ?
Ben.
^ permalink raw reply
* Re: [PATCH/2.6.17-rc4 1/10] Powerpc: Add general support for mpc7448h pc2 (Taiga) platform
From: Benjamin Herrenschmidt @ 2006-05-18 0:47 UTC (permalink / raw)
To: Zang Roy-r61911
Cc: linuxppc-dev list, Yang Xin-Xin-r48390, Paul Mackerras,
Alexandre.Bounine
In-Reply-To: <9FCDBA58F226D911B202000BDBAD46730626D619@zch01exm40.ap.freescale.net>
I'm not repeating Kumar's comments about that CONFIG_7xxx thing and that
7xxx/ directory, it should all go.
Some more bits:
> +static u_char mpc7448_hpc2_pic_initsenses[] __initdata = {
> + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* INT[0] XINT0 from FPGA */
> + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* INT[1] XINT1 from FPGA */
> + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* INT[2] PHY_INT from both GIGE */
> + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* INT[3] RESERVED */
> +};
> +
> +/*
> + * mpc7448hpc2 PCI interrupt routing. all PCI interrupt comes from
> + * external PCI source at 23. need to program pci interrupt control registers
> + * to route per slot IRQs.
> + */
> +
> +static inline int
> +mpc7448_hpc2_map_irq(struct pci_dev *dev, unsigned char idsel,
> + unsigned char pin)
> +{
> + static char pci_irq_table[][4] =
> + /*
> + * PCI IDSEL/INTPIN->INTLINE
> + * A B C D
> + */
> + {
> + {IRQ_PCI_INTA, IRQ_PCI_INTB, IRQ_PCI_INTC, IRQ_PCI_INTD}, /* A SLOT 1 IDSEL 17 */
> + {IRQ_PCI_INTB, IRQ_PCI_INTC, IRQ_PCI_INTD, IRQ_PCI_INTA}, /* B SLOT 2 IDSEL 18 */
> + {IRQ_PCI_INTC, IRQ_PCI_INTD, IRQ_PCI_INTA, IRQ_PCI_INTB}, /* C SATA IDSEL 19 */
> + {IRQ_PCI_INTD, IRQ_PCI_INTA, IRQ_PCI_INTB, IRQ_PCI_INTC}, /* D USB IDSEL 20 */
> + };
> +
> + const long min_idsel = 1, max_idsel = 4, irqs_per_slot = 4;
> + return PCI_IRQ_TABLE_LOOKUP;
> +}
This whole irq map stuff is excactly what the device-tree is there for .
Please implement proper interrupt maps in the device-tree and get rid of
those tables.
> +static void __init mpc7448_hpc2_map_io(void)
> +{
> + /* PCI IO mapping */
> + io_block_mapping(MPC7448_HPC2_PCI_IO_BASE_VIRT,
> + MPC7448_HPC2_PCI_IO_BASE_PHYS, 0x00800000, _PAGE_IO);
> + /* Tsi108 CSR mapping */
> + io_block_mapping(TSI108_CSR_ADDR_VIRT, TSI108_CSR_ADDR_PHYS,
> + 0x100000, _PAGE_IO);
> +
> + /* PCI Config mapping */
> + io_block_mapping(MPC7448_HPC2_PCI_CFG_BASE_VIRT,
> + MPC7448_HPC2_PCI_CFG_BASE_PHYS,
> + MPC7448_HPC2_PCI_CFG_SIZE, _PAGE_IO);
> +
> + tsi108_pci_cfg_base = MPC7448_HPC2_PCI_CFG_BASE_VIRT;
> + /* NVRAM mapping */
> + io_block_mapping(MPC7448_HPC2_NVRAM_BASE_ADDR,
> + MPC7448_HPC2_NVRAM_BASE_ADDR, MPC7448_HPC2_NVRAM_SIZE,
> + _PAGE_IO);
> +}
io_block_mapping is bad ! see endless discussions in the archives of
why ... Use ioremap.
> +static int __init mpc7448_hpc2_probe(void)
> +{
> + /* We always match for now, eventually we should look at the flat
> + dev tree to ensure this is the board we are suppose to run on
> + */
> + return 1;
> +}
Yes, please do so, we will not accept a board that does the above :)
> +extern int tsi108_direct_write_config(struct pci_bus *bus, unsigned int devfn,
> + int offset, int len, u32 val);
> +extern int tsi108_direct_read_config(struct pci_bus *bus, unsigned int devfn,
> + int offset, int len, u32 * val);
>
> +void tsi108_clear_pci_error(u32 pci_cfg_base);
> +
> +extern int
> +tsi108_read_config(int bus, unsigned int devfn, int offset, int len, u32 * val);
> +#ifdef CONFIG_PCI
> +static struct pci_ops direct_pci_ops = {
> + tsi108_direct_read_config,
> + tsi108_direct_write_config
> +};
That sounds bogus. You should instead have a tsi108_pci.c file that
contains all of the functions _and_ the pci_ops structure (with a better
name please) and exports some kind of setup_tsi108_pci(device_node *).
> +void tsi108_clear_pci_cfg_error(void)
> +{
> + tsi108_clear_pci_error(MPC7448_HPC2_PCI_CFG_BASE_PHYS);
> +}
> +
> +int __init add_bridge(struct device_node *dev)
> +{
> + int len;
> + struct pci_controller *hose;
> + struct resource rsrc;
> + int *bus_range;
> + int primary = 0, has_address = 0;
> +
> + DBG("TSI_PCI: %s tsi108_pci_cfg_base=0x%x\n", __FUNCTION__,
> + tsi108_pci_cfg_base);
> +
> + /* Fetch host bridge registers address */
> + has_address = (of_address_to_resource(dev, 0, &rsrc) == 0);
> +
> + /* Get bus range if any */
> + bus_range = (int *)get_property(dev, "bus-range", &len);
> + if (bus_range == NULL || len < 2 * sizeof(int)) {
> + printk(KERN_WARNING "Can't get bus-range for %s, assume"
> + " bus 0\n", dev->full_name);
> + }
> +
> + hose = pcibios_alloc_controller();
> +
> + if (!hose) {
> + printk("PCI Host bridge init failed\n");
> + return -ENOMEM;
> + }
> + hose->arch_data = dev;
> + hose->set_cfg_type = 1;
> +
> + hose->first_busno = bus_range ? bus_range[0] : 0;
> + hose->last_busno = bus_range ? bus_range[1] : 0xff;
> +
> + (hose)->ops = &direct_pci_ops;
> +
> + printk(KERN_INFO "Found tsi108 PCI host bridge at 0x%08lx. "
> + "Firmware bus number: %d->%d\n",
> + rsrc.start, hose->first_busno, hose->last_busno);
> +
> + /* Interpret the "ranges" property */
> + /* This also maps the I/O region and sets isa_io/mem_base */
> + pci_process_bridge_OF_ranges(hose, dev, primary);
> + return 0;
> +
> +}
We need a generic add_bridge that goes through PCI bridges and
instanciate based on their type as provided by the device-tree. You
should try to work in that direction...
^ permalink raw reply
* Re: [PATCH/2.6.17-rc4 1/10] Powerpc: Add general support for mpc7448h pc2 (Taiga) platform
From: Benjamin Herrenschmidt @ 2006-05-18 0:41 UTC (permalink / raw)
To: Kumar Gala
Cc: Alexandre.Bounine, linuxppc-dev list, Paul Mackerras,
Yang Xin-Xin-r48390
In-Reply-To: <AF039DA5-BAED-4646-904A-18BE025989AD@kernel.crashing.org>
On Wed, 2006-05-17 at 08:45 -0500, Kumar Gala wrote:
> > +#ifdef TSI108_ETH
> > +hw_info hw_info_table[TSI108_ETH_MAX_PORTS + 1] = {
> > + {TSI108_CSR_ADDR_PHYS + TSI108_ETH_OFFSET,
> > + TSI108_CSR_ADDR_PHYS + TSI108_ETH_OFFSET,
> > + TSI108_PHY0_ADDR, IRQ_TSI108_GIGE0},
> > +
> > + {TSI108_CSR_ADDR_PHYS + TSI108_ETH_OFFSET + 0x400,
> > + TSI108_CSR_ADDR_PHYS + TSI108_ETH_OFFSET,
> > + TSI108_PHY1_ADDR, IRQ_TSI108_GIGE1},
> > +
> > + {TBL_END, TBL_END, TBL_END, TBL_END}
> > +};
> > +#endif
>
> This table looks problematic. Look at using something like a
> platform device.
Bzzzt ... wrong answer :) That's a textbook example of stuff that should
be provided via the device-tree.
^ permalink raw reply
* Re: snd-aoa status update / automatic driver loading
From: Dean Hamstead @ 2006-05-18 0:39 UTC (permalink / raw)
To: Wolfgang Pfeiffer; +Cc: linuxppc-dev list, debian-powerpc
In-Reply-To: <20060518002800.GB2902@localhost>
they are running about a 30 min test with a variety of
audio and so forth so test for distortion.
they are hoping people will use oss and alsa, as well
as oss emulation and report back on what worked and what
didnt etc
Dean
Wolfgang Pfeiffer wrote:
> On Thu, May 18, 2006 at 08:30:23AM +1000, Dean Hamstead wrote:
>> perhaps a comprehensive sound test would be in order, akin perhaps to
>> the one the winehq chaps are doing right now
>
> Not being sure if I got that one ... :)
>
> Regards
> Wolfgang
>
^ permalink raw reply
* Re: snd-aoa status update / automatic driver loading
From: Wolfgang Pfeiffer @ 2006-05-18 0:28 UTC (permalink / raw)
To: Dean Hamstead; +Cc: linuxppc-dev list, debian-powerpc
In-Reply-To: <446BA3FF.5050308@bong.com.au>
On Thu, May 18, 2006 at 08:30:23AM +1000, Dean Hamstead wrote:
> perhaps a comprehensive sound test would be in order, akin perhaps to
> the one the winehq chaps are doing right now
Not being sure if I got that one ... :)
Regards
Wolfgang
--
Wolfgang Pfeiffer: /ICQ: 286585973/ + + + /AIM: crashinglinux/
http://profiles.yahoo.com/wolfgangpfeiffer
Key ID: E3037113
http://keyserver.mine.nu/pks/lookup?search=0xE3037113&fingerprint=on
^ permalink raw reply
* [HACK] add sandpoint + flattened dt support to arch/powerpc/boot
From: Mark A. Greer @ 2006-05-18 0:21 UTC (permalink / raw)
To: linuxppc-dev
Hi,
This is a patch with some very rough code that adds support for the
sandpoint to arch/powerpc. I'm posting it now in the hope that it
will spark a deeper discussion on exactly how we're going to support
flattened device tree code in the bootwrapper for non-OF platforms.
I'm also hoping that it will help others that are working on the same
sort of thing so we can help each other.
Again, the code is rough with lots of hacks but it boots and is
something we can point to during this discussion.
So...I am on the right path?
Mark
---
Kconfig | 10
boot/Makefile | 8
boot/dt_utils.c | 95 +++
boot/dts/sandpoint.dts | 204 +++++++
boot/io.h | 160 +++++
boot/main.c | 42 +
boot/mpc10x.c | 88 +++
boot/ns16550.c | 118 ++++
boot/prom.h | 52 +
boot/prom_embedded.c | 112 ++++
boot/sandpoint.c | 110 ++++
boot/types.h | 28 +
configs/sandpoint_defconfig | 1017 ++++++++++++++++++++++++++++++++++++++
kernel/legacy_serial.c | 2
platforms/Makefile | 1
platforms/embedded6xx/Makefile | 4
platforms/embedded6xx/sandpoint.c | 629 +++++++++++++++++++++++
sysdev/mpic.c | 7
18 files changed, 2676 insertions(+), 11 deletions(-)
---
diff --git a/arch/powerpc/Kconfig b/arch/powerpc/Kconfig
index 6729c98..de09eac 100644
--- a/arch/powerpc/Kconfig
+++ b/arch/powerpc/Kconfig
@@ -323,7 +323,10 @@ config PPC_ISERIES
config EMBEDDED6xx
bool "Embedded 6xx/7xx/7xxx-based board"
- depends on PPC32 && BROKEN
+ depends on PPC32
+ select PPC_UDBG_16550
+ select MPIC
+ select MPIC_SERIAL
config APUS
bool "Amiga-APUS"
@@ -411,6 +414,11 @@ config MPIC
depends on PPC_PSERIES || PPC_PMAC || PPC_MAPLE || PPC_CHRP
bool
default y
+
+config MPIC_SERIAL
+ depends on EMBEDDED6xx
+ bool
+ default n
config PPC_RTAS
bool
diff --git a/arch/powerpc/boot/Makefile b/arch/powerpc/boot/Makefile
index 840ae59..1995544 100644
--- a/arch/powerpc/boot/Makefile
+++ b/arch/powerpc/boot/Makefile
@@ -36,8 +36,14 @@ zliblinuxheader := zlib.h zconf.h zutil.
$(addprefix $(obj)/,$(zlib) main.o): $(addprefix $(obj)/,$(zliblinuxheader)) $(addprefix $(obj)/,$(zlibheader))
#$(addprefix $(obj)/,main.o): $(addprefix $(obj)/,zlib.h)
-src-boot := crt0.S string.S prom.c stdio.c main.c div64.S
+src-boot := crt0.S string.S stdio.c main.c div64.S
src-boot += $(zlib)
+ifeq ($(CONFIG_EMBEDDED6xx),y)
+src-boot += prom_embedded.c ns16550.c dts/sandpoint.S \
+ dt_utils.c sandpoint.c mpc10x.c
+else
+src-boot += prom.c
+endif
src-boot := $(addprefix $(obj)/, $(src-boot))
obj-boot := $(addsuffix .o, $(basename $(src-boot)))
diff --git a/arch/powerpc/boot/dt_utils.c b/arch/powerpc/boot/dt_utils.c
new file mode 100644
index 0000000..c991598
--- /dev/null
+++ b/arch/powerpc/boot/dt_utils.c
@@ -0,0 +1,95 @@
+/*
+ * Simple dtb (binary flattened device tree) search/manipulation routines.
+ *
+ * Author: Mark A. Greer <mgreer@mvista.com>
+ * - The code for strrchr() was copied from lib/string.c and is
+ * copyrighted by Linus Torvalds.
+ * - The smarts for dt_find_prop_by_name() were copied with the author's
+ * permission from u-boot:common/ft_build.c which was written by
+ * Pantelis Antoniou <pantelis@embeddedalley.com>.
+ *
+ * 2006 (c) MontaVista, Software, Inc. 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.
+ */
+
+/* Supports dtb version 0x10 only */
+
+#include <stdarg.h>
+#include "page.h"
+#include "string.h"
+#include "stdio.h"
+#include "prom.h"
+
+struct boot_param_header *
+dt_get_bph(void *dt_blob)
+{
+ return (struct boot_param_header *)dt_blob;
+}
+
+char *
+strrchr(const char *s, int c)
+{
+ const char *p = s + strlen(s);
+
+ do {
+ if (*p == (char)c)
+ return (char *)p;
+ } while (--p >= s);
+ return NULL;
+}
+
+
+void *
+dt_find_prop_by_name(void *dt_blob, char *full_name, u32 *val_sizep)
+{
+ struct boot_param_header *bph = dt_get_bph(dt_blob);
+ u32 *dp, tag, size;
+ char *s, *str_region;
+ static char path[256], prop[256];
+
+ path[0] = '\0';
+
+ if (bph->magic != OF_DT_HEADER) {
+ printf("dt_find_prop_by_name: bogus magic value: 0x%x\n",
+ bph->magic);
+ return NULL;
+ }
+
+ dp = (u32 *)((u32)dt_blob + bph->off_dt_struct);
+ str_region = (char *)((u32)dt_blob + bph->off_dt_strings);
+
+ while ((tag = *dp++) != OF_DT_END) {
+ switch (tag) {
+ case OF_DT_PROP:
+ size = *dp++;
+ s = str_region + *dp++;
+
+ strcpy(prop, path);
+ strcat(prop, s);
+ if (!strcmp(prop, full_name)) {
+ *val_sizep = size;
+ return dp; /* ptr to prop value */
+ }
+
+ dp = (u32 *)_ALIGN_UP((unsigned long)dp + size, 4);
+ break;
+ case OF_DT_BEGIN_NODE:
+ s = (char *)dp;
+ strcat(path, s);
+ strcat(path, "/");
+ dp = (u32 *)_ALIGN_UP((u32)dp + strlen(s) + 1, 4);
+ break;
+ case OF_DT_END_NODE:
+ path[strlen(path) - 1] = '\0';
+ s = strrchr(path, '/');
+ if (s != NULL)
+ s[1] = '\0';
+ break;
+ default:
+ printf("dt_find_prop_by_name: bugus tag: 0x%x\n", tag);
+ }
+ }
+ return NULL;
+}
diff --git a/arch/powerpc/boot/dts/sandpoint.dts b/arch/powerpc/boot/dts/sandpoint.dts
new file mode 100644
index 0000000..93ecf37
--- /dev/null
+++ b/arch/powerpc/boot/dts/sandpoint.dts
@@ -0,0 +1,204 @@
+/*
+ * Device Tree Souce for Freescale Sandpoint
+ *
+ * Author: Mark A. Greer <mgreer@mvista.com>
+ *
+ * 2006 (c) MontaVista, Software, Inc. 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.
+
+XXXX add flash parts, rtc, ??
+
+build with: "dtc -I dts -O asm -o sandpoint.S -V 16 sandpoint.dts"
+
+
+ */
+
+/ {
+ linux,phandle = <1000>;
+ model = "Sandpoint";
+ compatible = "classic+mpc10x";
+ #address-cells = <1>;
+ #size-cells = <1>;
+
+ cpus {
+ linux,phandle = <2000>;
+ #cpus = <1>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ PowerPC,603e { /* Really 75x, 74xx, 824x */
+ linux,phandle = <2100>;
+ linux,boot-cpu;
+ device_type = "cpu";
+ reg = <0>;
+ clock-frequency = <0>; /* Fixed by bootwrapper */
+ timebase-frequency = <0>; /* Fixed by bootwrapper */
+ /* Following required by dtc but not used */
+ i-cache-line-size = <0>;
+ d-cache-line-size = <0>;
+ i-cache-size = <0>;
+ d-cache-size = <0>;
+ };
+ };
+
+ memory {
+ linux,phandle = <3000>;
+ device_type = "memory";
+ reg = <00000000 00000000>; /* Fixed by bootwrapper */
+ };
+
+ mpc10x { /* AFAICT need to make soc for 8245's uarts to be defined */
+ linux,phandle = <4000>;
+ #address-cells = <1>;
+ #size-cells = <1>;
+ #interrupt-cells = <2>;
+ device_type = "mpc10x-bridge";
+ compatible = "mpc10x-bridge";
+ store-gathering = <0>; /* 0 == off, !0 == on */
+ reg = <fc000000 00100000>;
+ ranges = <80000000 80000000 70000000 /* pci mem space */
+ fe000000 fe000000 00c00000 /* pci i/o space */
+ fec00000 fec00000 00300000 /* pci cfg regs */
+ fef00000 fec00000 00100000>; /* pci iack */
+
+ dma@1100 {
+ linux,phandle = <4100>;
+ #interrupt-cells = <1>;
+ #address-cells = <1>;
+ #size-cells = <1>;
+ device_type = "dma";
+ compatible = "fsl-dma";
+ clock-frequency = <0>;
+ reg = <fc001100 00000024>;
+ interrupts = <33 0>;
+ interrupt-parent = <4400>;
+ };
+
+ dma@1200 {
+ linux,phandle = <4200>;
+ #interrupt-cells = <1>;
+ #address-cells = <1>;
+ #size-cells = <1>;
+ device_type = "dma";
+ compatible = "fsl-dma";
+ clock-frequency = <0>;
+ reg = <fc001200 00000024>;
+ interrupts = <34 0>;
+ interrupt-parent = <4400>;
+ };
+
+ i2c {
+ linux,phandle = <4300>;
+ #interrupt-cells = <1>;
+ #address-cells = <1>;
+ #size-cells = <1>;
+ device_type = "i2c";
+ compatible = "fsl-i2c";
+ clock-frequency = <0>;
+ reg = <fc003000 00001000>;
+ interrupts = <32 0>;
+ interrupt-parent = <4400>;
+ };
+
+ pic {
+ linux,phandle = <4400>;
+ clock-frequency = <0>;
+ interrupt-controller;
+ #interrupt-cells = <2>;
+ #address-cells = <0>;
+ device_type = "open-pic";
+ compatible = "chrp,open-pic";
+ reg = <fc040000 00040000>;
+ };
+
+ pci {
+ linux,phandle = <4500>;
+ #address-cells = <3>;
+ #size-cells = <2>;
+ #interrupt-cells = <1>;
+ device_type = "pci";
+ compatible = "mpc10x-pci";
+ reg = <fec00000 00400000>;
+ ranges = <01000000 0 0 fe000000 0 00c00000
+ 02000000 0 80000000 80000000 0 70000000>;
+ bus-range = <0 ff>;
+ clock-frequency = <7f28155>;
+ interrupt-parent = <4400>;
+ interrupt-map-mask = <f800 0 0 7>;
+ interrupt-map = <
+ /* IDSEL 0x11 */
+ 8800 0 0 1 4100 18 0
+ 8800 0 0 2 4100 18 0
+ 8800 0 0 3 4100 18 0
+ 8800 0 0 4 4100 18 0
+ /* IDSEL 0x12 */
+ 9000 0 0 1 4100 19 0
+ 9000 0 0 2 4100 19 0
+ 9000 0 0 3 4100 19 0
+ 9000 0 0 4 4100 19 0
+ /* IDSEL 0x13 */
+ 9800 0 0 1 4100 20 0
+ 9800 0 0 2 4100 20 0
+ 9800 0 0 3 4100 20 0
+ 9800 0 0 4 4100 20 0
+ /* IDSEL 0x14 */
+ a000 0 0 1 4100 21 0
+ a000 0 0 2 4100 21 0
+ a000 0 0 3 4100 21 0
+ a000 0 0 4 4100 21 0
+ /* IDSEL 0x15 */
+ a800 0 0 1 4100 22 0
+ a800 0 0 2 4100 22 0
+ a800 0 0 3 4100 22 0
+ a800 0 0 4 4100 22 0
+ /* IDSEL 0x16 */
+ b000 0 0 1 4100 23 0
+ b000 0 0 2 4100 23 0
+ b000 0 0 3 4100 23 0
+ b000 0 0 4 4100 23 0
+ >;
+
+ isa {
+ linux,phandle = <4510>;
+ #address-cells = <2>;
+ #size-cells = <1>;
+ #interrupt-cells = <2>;
+ device_type = "isa";
+ compatible = "isa";
+ ranges = <0 0 01000000 0 0 00800000>;
+
+ serial@3f8 {
+ linux,phandle = <4511>;
+ device_type = "serial";
+ compatible = "ns16550";
+ reg = <0 3f8 8>;
+ clock-frequency = <1c2000>;
+ current-speed = <2580>;
+ interrupts = <4 0>;
+ interrupt-parent = <4400>;
+ };
+
+ serial@2f8 {
+ linux,phandle = <4512>;
+ device_type = "serial";
+ compatible = "ns16550";
+ reg = <0 2f8 8>;
+ clock-frequency = <1c2000>;
+ current-speed = <2580>;
+ interrupts = <3 0>;
+ interrupt-parent = <4400>;
+ };
+ };
+ };
+ };
+
+ chosen {
+ linux,phandle = <5000>;
+ linux,platform = <1>;
+ bootargs = "ip=on";
+ linux,stdout-path = "/mpc8245/pci/isa/serial@3f8";
+ interrupt-controller = <4100>;
+ };
+};
diff --git a/arch/powerpc/boot/io.h b/arch/powerpc/boot/io.h
new file mode 100644
index 0000000..65c1864
--- /dev/null
+++ b/arch/powerpc/boot/io.h
@@ -0,0 +1,160 @@
+#ifndef _IO_H
+#define __IO_H
+
+#include "types.h"
+
+/*
+ * 8, 16 and 32 bit, big and little endian I/O operations, with barrier.
+ * These routines do not perform EEH-related I/O address translation,
+ * and should not be used directly by device drivers. Use inb/readb
+ * instead.
+ */
+static inline int in_8(const volatile unsigned char *addr)
+{
+ int ret;
+
+ __asm__ __volatile__("lbz%U1%X1 %0,%1; twi 0,%0,0; isync"
+ : "=r" (ret) : "m" (*addr));
+ return ret;
+}
+
+static inline void out_8(volatile unsigned char *addr, int val)
+{
+ __asm__ __volatile__("stb%U0%X0 %1,%0; sync"
+ : "=m" (*addr) : "r" (val));
+}
+
+static inline int in_le16(const volatile unsigned short *addr)
+{
+ int ret;
+
+ __asm__ __volatile__("lhbrx %0,0,%1; twi 0,%0,0; isync"
+ : "=r" (ret) : "r" (addr), "m" (*addr));
+ return ret;
+}
+
+static inline int in_be16(const volatile unsigned short *addr)
+{
+ int ret;
+
+ __asm__ __volatile__("lhz%U1%X1 %0,%1; twi 0,%0,0; isync"
+ : "=r" (ret) : "m" (*addr));
+ return ret;
+}
+
+static inline void out_le16(volatile unsigned short *addr, int val)
+{
+ __asm__ __volatile__("sthbrx %1,0,%2; sync"
+ : "=m" (*addr) : "r" (val), "r" (addr));
+}
+
+static inline void out_be16(volatile unsigned short *addr, int val)
+{
+ __asm__ __volatile__("sth%U0%X0 %1,%0; sync"
+ : "=m" (*addr) : "r" (val));
+}
+
+static inline unsigned in_le32(const volatile unsigned *addr)
+{
+ unsigned ret;
+
+ __asm__ __volatile__("lwbrx %0,0,%1; twi 0,%0,0; isync"
+ : "=r" (ret) : "r" (addr), "m" (*addr));
+ return ret;
+}
+
+static inline unsigned in_be32(const volatile unsigned *addr)
+{
+ unsigned ret;
+
+ __asm__ __volatile__("lwz%U1%X1 %0,%1; twi 0,%0,0; isync"
+ : "=r" (ret) : "m" (*addr));
+ return ret;
+}
+
+static inline void out_le32(volatile unsigned *addr, int val)
+{
+ __asm__ __volatile__("stwbrx %1,0,%2; sync" : "=m" (*addr)
+ : "r" (val), "r" (addr));
+}
+
+static inline void out_be32(volatile unsigned *addr, int val)
+{
+ __asm__ __volatile__("stw%U0%X0 %1,%0; sync"
+ : "=m" (*addr) : "r" (val));
+}
+
+static inline unsigned long in_le64(const volatile unsigned long *addr)
+{
+ unsigned long tmp, ret;
+
+ __asm__ __volatile__(
+ "ld %1,0(%2)\n"
+ "twi 0,%1,0\n"
+ "isync\n"
+ "rldimi %0,%1,5*8,1*8\n"
+ "rldimi %0,%1,3*8,2*8\n"
+ "rldimi %0,%1,1*8,3*8\n"
+ "rldimi %0,%1,7*8,4*8\n"
+ "rldicl %1,%1,32,0\n"
+ "rlwimi %0,%1,8,8,31\n"
+ "rlwimi %0,%1,24,16,23\n"
+ : "=r" (ret) , "=r" (tmp) : "b" (addr) , "m" (*addr));
+ return ret;
+}
+
+static inline unsigned long in_be64(const volatile unsigned long *addr)
+{
+ unsigned long ret;
+
+ __asm__ __volatile__("ld%U1%X1 %0,%1; twi 0,%0,0; isync"
+ : "=r" (ret) : "m" (*addr));
+ return ret;
+}
+
+static inline void out_le64(volatile unsigned long *addr, unsigned long val)
+{
+ unsigned long tmp;
+
+ __asm__ __volatile__(
+ "rldimi %0,%1,5*8,1*8\n"
+ "rldimi %0,%1,3*8,2*8\n"
+ "rldimi %0,%1,1*8,3*8\n"
+ "rldimi %0,%1,7*8,4*8\n"
+ "rldicl %1,%1,32,0\n"
+ "rlwimi %0,%1,8,8,31\n"
+ "rlwimi %0,%1,24,16,23\n"
+ "std %0,0(%3)\n"
+ "sync"
+ : "=&r" (tmp) , "=&r" (val) : "1" (val) , "b" (addr) , "m" (*addr));
+}
+
+static inline void out_be64(volatile unsigned long *addr, unsigned long val)
+{
+ __asm__ __volatile__("std%U0%X0 %1,%0; sync" : "=m" (*addr) : "r" (val));
+}
+
+#define PCI_DEVFN(slot,func) ((((slot) & 0x1f) << 3) | ((func) & 0x07))
+
+/* Indirect PCI config space access routines */
+static inline void
+pci_indirect_read_config_byte(u32 *cfg_addr, u32 *cfg_data, int devfn,
+ int offset, u8 *val)
+{
+ out_be32(cfg_addr,
+ ((offset & 0xfc) << 24) | (devfn << 16) | (0 << 8) | 0x80);
+ *val = in_8((u8 *)(cfg_data + (offset & 3)));
+ return;
+}
+
+static inline void
+pci_indirect_read_config_dword(u32 *cfg_addr, u32 *cfg_data, int devfn,
+ int offset, u32 *val)
+{
+ out_be32(cfg_addr,
+ ((offset & 0xfc) << 24) | (devfn << 16) | (0 << 8) | 0x80);
+ *val = in_le32(cfg_data + (offset & 3));
+ return;
+}
+
+#endif /* _IO_H */
diff --git a/arch/powerpc/boot/main.c b/arch/powerpc/boot/main.c
index 816446f..6acdf66 100644
--- a/arch/powerpc/boot/main.c
+++ b/arch/powerpc/boot/main.c
@@ -48,10 +48,19 @@ static char scratch[46912]; /* scratch s
static char elfheader[256];
+#ifdef CONFIG_PPC_OF
typedef void (*kernel_entry_t)( unsigned long,
unsigned long,
void *,
void *);
+#else
+void platform_fixups(void *dt_blob);
+extern void *dt_blob_start;
+extern void *dt_blob_end;
+void edit_cmdline(void *dt_blob_start);
+typedef void (*kernel_entry_t)(void *dtb_addr, void *kernel_entry_paddr,
+ void *must_be_null);
+#endif
#undef DEBUG
@@ -218,7 +227,7 @@ void start(unsigned long a1, unsigned lo
if (getprop(chosen_handle, "stdout", &stdout, sizeof(stdout)) != 4)
exit();
- printf("\n\rzImage starting: loaded at 0x%p (sp: 0x%p)\n\r", _start, sp);
+ printf("\n\rzImage starting: loaded at 0x%p (sp: 0x%p)\n\r", _start,sp);
/*
* The first available claim_base must be above the end of the
@@ -238,7 +247,7 @@ void start(unsigned long a1, unsigned lo
gunzip(elfheader, sizeof(elfheader),
(unsigned char *)vmlinuz.addr, &len);
} else
- memcpy(elfheader, (const void *)vmlinuz.addr, sizeof(elfheader));
+ memcpy(elfheader, (const void *)vmlinuz.addr,sizeof(elfheader));
if (!is_elf64(elfheader) && !is_elf32(elfheader)) {
printf("Error: not a valid PPC32 or PPC64 ELF file!\n\r");
@@ -263,18 +272,21 @@ void start(unsigned long a1, unsigned lo
initrd.size = (unsigned long)(_initrd_end - _initrd_start);
initrd.memsize = initrd.size;
if ( initrd.size > 0 ) {
- printf("Allocating 0x%lx bytes for initrd ...\n\r", initrd.size);
+ printf("Allocating 0x%lx bytes for initrd ...\n\r",initrd.size);
initrd.addr = try_claim(initrd.size);
if (initrd.addr == 0) {
- printf("Can't allocate memory for initial ramdisk !\n\r");
+ printf("Can't allocate memory for initial "
+ "ramdisk !\n\r");
exit();
}
a1 = initrd.addr;
a2 = initrd.size;
- printf("initial ramdisk moving 0x%lx <- 0x%lx (0x%lx bytes)\n\r",
- initrd.addr, (unsigned long)_initrd_start, initrd.size);
- memmove((void *)initrd.addr, (void *)_initrd_start, initrd.size);
- printf("initrd head: 0x%lx\n\r", *((unsigned long *)initrd.addr));
+ printf("initial ramdisk moving 0x%lx <- 0x%lx "
+ "(0x%lx bytes)\n\r", initrd.addr,
+ (unsigned long)_initrd_start, initrd.size);
+ memmove((void *)initrd.addr, (void *)_initrd_start,initrd.size);
+ printf("initrd head: 0x%lx\n\r",
+ *((unsigned long *)initrd.addr));
}
/* Eventually gunzip the kernel */
@@ -299,6 +311,7 @@ #endif
flush_cache((void *)vmlinux.addr, vmlinux.size);
kernel_entry = (kernel_entry_t)vmlinux.addr;
+#ifdef CONFIG_PPC_OF
#ifdef DEBUG
printf( "kernel:\n\r"
" entry addr = 0x%lx\n\r"
@@ -311,9 +324,20 @@ #ifdef DEBUG
#endif
kernel_entry(a1, a2, prom, NULL);
+#else /* !CONFIG_PPC_OF ==> flattened device tree */
+#ifdef DEBUG
+ printf("kernel:\n\r"
+ " entry addr = 0x%lx\n\r"
+ " flattened dt = 0x%lx\n\r",
+ (unsigned long)kernel_entry, &dt_blob_start);
+#endif
+ edit_cmdline(&dt_blob_start);
+ platform_fixups(&dt_blob_start);
+ kernel_entry(&dt_blob_start, (void *)vmlinux.addr, NULL);
+#endif
+
printf("Error: Linux kernel returned to zImage bootloader!\n\r");
exit();
}
-
diff --git a/arch/powerpc/boot/mpc10x.c b/arch/powerpc/boot/mpc10x.c
new file mode 100644
index 0000000..d9c0e0b
--- /dev/null
+++ b/arch/powerpc/boot/mpc10x.c
@@ -0,0 +1,88 @@
+/*
+ * Freescale mpc10[67] & mpc824[015] specific code.
+ *
+ * Author: Mark A. Greer <mgreer@mvista.com>
+ *
+ * 2001 (c) MontaVista, Software, Inc. 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 <stdarg.h>
+#include <stddef.h>
+#include "types.h"
+#include "string.h"
+#include "stdio.h"
+#include "io.h"
+#include "prom.h"
+
+/* Map B (CHRP Map) Defines */
+#define MPC10X_MAPB_CNFG_ADDR 0xfec00000
+#define MPC10X_MAPB_CNFG_DATA 0xfee00000
+
+/* Define offsets for the memory controller registers in the config space */
+#define MPC10X_MCTLR_MEM_START_1 0x80 /* Banks 0-3 */
+#define MPC10X_MCTLR_MEM_START_2 0x84 /* Banks 4-7 */
+#define MPC10X_MCTLR_EXT_MEM_START_1 0x88 /* Banks 0-3 */
+#define MPC10X_MCTLR_EXT_MEM_START_2 0x8c /* Banks 4-7 */
+
+#define MPC10X_MCTLR_MEM_END_1 0x90 /* Banks 0-3 */
+#define MPC10X_MCTLR_MEM_END_2 0x94 /* Banks 4-7 */
+#define MPC10X_MCTLR_EXT_MEM_END_1 0x98 /* Banks 0-3 */
+#define MPC10X_MCTLR_EXT_MEM_END_2 0x9c /* Banks 4-7 */
+
+#define MPC10X_MCTLR_MEM_BANK_ENABLES 0xa0
+
+
+/*
+ * Read the memory controller registers to determine the amount of memory in
+ * the system. This assumes that the firmware has correctly set up the memory
+ * controller registers.
+ * Assume memory map B (CHRP).
+ */
+u32
+mpc10x_get_mem_size(void)
+{
+ u32 *config_addr, *config_data, val;
+ u32 start, end, total, offset, i;
+ u8 bank_enables;
+
+ config_addr = (u32 *)MPC10X_MAPB_CNFG_ADDR;
+ config_data = (u32 *)MPC10X_MAPB_CNFG_DATA;
+
+ pci_indirect_read_config_byte(config_addr, config_data, PCI_DEVFN(0,0),
+ MPC10X_MCTLR_MEM_BANK_ENABLES, &bank_enables);
+
+ total = 0;
+
+ for (i=0; i<8; i++) {
+ if (bank_enables & (1 << i)) {
+ offset = MPC10X_MCTLR_MEM_START_1 + ((i > 3) ? 4 : 0);
+ pci_indirect_read_config_dword(config_addr, config_data,
+ PCI_DEVFN(0,0), offset, &val);
+ start = (val >> ((i & 3) << 3)) & 0xff;
+
+ offset = MPC10X_MCTLR_EXT_MEM_START_1 + ((i>3) ? 4 : 0);
+ pci_indirect_read_config_dword(config_addr, config_data,
+ PCI_DEVFN(0,0), offset, &val);
+ val = (val >> ((i & 3) << 3)) & 0x03;
+ start = (val << 28) | (start << 20);
+
+ offset = MPC10X_MCTLR_MEM_END_1 + ((i > 3) ? 4 : 0);
+ pci_indirect_read_config_dword(config_addr, config_data,
+ PCI_DEVFN(0,0), offset, &val);
+ end = (val >> ((i & 3) << 3)) & 0xff;
+
+ offset = MPC10X_MCTLR_EXT_MEM_END_1 + ((i > 3) ? 4 : 0);
+ pci_indirect_read_config_dword(config_addr, config_data,
+ PCI_DEVFN(0,0), offset, &val);
+ val = (val >> ((i & 3) << 3)) & 0x03;
+ end = (val << 28) | (end << 20) | 0xfffff;
+
+ total += (end - start + 1);
+ }
+ }
+
+ return total;
+}
diff --git a/arch/powerpc/boot/ns16550.c b/arch/powerpc/boot/ns16550.c
new file mode 100644
index 0000000..f92073a
--- /dev/null
+++ b/arch/powerpc/boot/ns16550.c
@@ -0,0 +1,118 @@
+/*
+ * COM1 NS16550 support
+ */
+
+#include "io.h"
+#include "prom.h"
+
+#define UART_FCR 2 /* Out: FIFO Control Register */
+#define UART_LCR 3 /* Out: Line Control Register */
+#define UART_MCR 4 /* Out: Modem Control Register */
+#define UART_LSR 5 /* In: Line Status Register */
+#define UART_LSR_THRE 0x20 /* Transmit-hold-register empty */
+#define UART_LSR_DR 0x01 /* Receiver data ready */
+#define UART_MSR 6 /* In: Modem Status Register */
+#define UART_SCR 7 /* I/O: Scratch Register */
+#define UART_DLL 0 /* Out: Divisor Latch Low */
+#define UART_DLM 1 /* Out: Divisor Latch High */
+
+unsigned long com_port;
+
+/*
+ * Serial defines.
+ */
+#define SERIAL_BAUD 9600
+
+#define SANDPOINT_SERIAL_0 0xfe0003f8
+#define SANDPOINT_SERIAL_1 0xfe0002f8
+
+#define RS_TABLE_SIZE 2
+
+/* Rate for the 1.8432 Mhz clock for the onboard serial chip */
+#define BASE_BAUD ( 1843200 / 16 )
+#define UART_CLK 1843200
+
+/* XXXX Get info from dt instead */
+static struct {
+ int baud_base;
+ unsigned long com_port;
+ u16 iomem_reg_shift;
+} rs_table[RS_TABLE_SIZE] = {
+ { BASE_BAUD, SANDPOINT_SERIAL_0, 0 },
+ { BASE_BAUD, SANDPOINT_SERIAL_1, 0 },
+};
+
+static int shift;
+
+unsigned long
+serial_init(int chan, void *ignored)
+{
+ unsigned long base_baud;
+ unsigned char lcr, dlm;
+
+ com_port = rs_table[chan].com_port;
+
+ /* How far apart the registers are. */
+ shift = rs_table[chan].iomem_reg_shift;
+ /* Base baud.. */
+ base_baud = rs_table[chan].baud_base;
+
+ /* save the LCR */
+ lcr = in_8((const volatile unsigned char *)com_port
+ + (UART_LCR << shift));
+ /* Access baud rate */
+ out_8((volatile unsigned char *)com_port + (UART_LCR << shift), 0x80);
+ dlm = in_8((const volatile unsigned char *)com_port
+ + (UART_DLM << shift));
+ /*
+ * Test if serial port is unconfigured.
+ * We assume that no-one uses less than 110 baud or
+ * less than 7 bits per character these days.
+ * -- paulus.
+ */
+
+ if ((dlm <= 4) && (lcr & 2))
+ /* port is configured, put the old LCR back */
+ out_8((volatile unsigned char *)com_port
+ + (UART_LCR << shift), lcr);
+ else {
+ /* Input clock. */
+ out_8((volatile unsigned char *)com_port + (UART_DLL << shift),
+ (base_baud / SERIAL_BAUD) & 0xFF);
+ out_8((volatile unsigned char *)com_port + (UART_DLM << shift),
+ (base_baud / SERIAL_BAUD) >> 8);
+ /* 8 data, 1 stop, no parity */
+ out_8((volatile unsigned char *)com_port + (UART_LCR << shift),
+ 0x03);
+ /* RTS/DTR */
+ out_8((volatile unsigned char *)com_port + (UART_MCR << shift),
+ 0x03);
+ }
+ /* Clear & enable FIFOs */
+ out_8((volatile unsigned char *)com_port + (UART_FCR << shift), 0x07);
+
+ return (com_port);
+}
+
+void
+serial_putc(unsigned long com_port, unsigned char c)
+{
+ while ((in_8((const volatile unsigned char *)com_port
+ + (UART_LSR << shift)) & UART_LSR_THRE) == 0);
+ out_8((volatile unsigned char *)com_port, c);
+}
+
+unsigned char
+serial_getc(unsigned long com_port)
+{
+ while ((in_8((const volatile unsigned char *)com_port
+ + (UART_LSR << shift)) & UART_LSR_DR) == 0);
+ return in_8((const volatile unsigned char *)com_port);
+}
+
+int
+serial_tstc(unsigned long com_port)
+{
+ return ((in_8((const volatile unsigned char *)com_port
+ + (UART_LSR << shift)) & UART_LSR_DR) != 0);
+}
diff --git a/arch/powerpc/boot/prom.h b/arch/powerpc/boot/prom.h
index 3e2ddd4..28394f7 100644
--- a/arch/powerpc/boot/prom.h
+++ b/arch/powerpc/boot/prom.h
@@ -1,6 +1,58 @@
#ifndef _PPC_BOOT_PROM_H_
#define _PPC_BOOT_PROM_H_
+#include "types.h"
+
+/* #ifndef CONFIG_PPC_OF XXXX */
+
+/* Definitions used by the flattened device tree */
+#define OF_DT_HEADER 0xd00dfeed /* marker */
+#define OF_DT_BEGIN_NODE 0x1 /* Start of node, full name */
+#define OF_DT_END_NODE 0x2 /* End node */
+#define OF_DT_PROP 0x3 /* Property: name off, size,
+ * content */
+#define OF_DT_NOP 0x4 /* nop */
+#define OF_DT_END 0x9
+
+#define OF_DT_VERSION 0x10
+
+/*
+ * This is what gets passed to the kernel by prom_init or kexec
+ *
+ * The dt struct contains the device tree structure, full pathes and
+ * property contents. The dt strings contain a separate block with just
+ * the strings for the property names, and is fully page aligned and
+ * self contained in a page, so that it can be kept around by the kernel,
+ * each property name appears only once in this page (cheap compression)
+ *
+ * the mem_rsvmap contains a map of reserved ranges of physical memory,
+ * passing it here instead of in the device-tree itself greatly simplifies
+ * the job of everybody. It's just a list of u64 pairs (base/size) that
+ * ends when size is 0
+ */
+
+struct boot_param_header
+{
+ u32 magic; /* magic word OF_DT_HEADER */
+ u32 totalsize; /* total size of DT block */
+ u32 off_dt_struct; /* offset to structure */
+ u32 off_dt_strings; /* offset to strings */
+ u32 off_mem_rsvmap; /* offset to memory reserve map */
+ u32 version; /* format version */
+ u32 last_comp_version; /* last compatible version */
+ /* version 2 fields below */
+ u32 boot_cpuid_phys; /* Physical CPU id we're booting on */
+ /* version 3 fields below */
+ u32 dt_strings_size; /* size of the DT strings block */
+};
+
+unsigned long serial_init(int chan, void *ignored);
+void serial_putc(unsigned long com_port, unsigned char c);
+unsigned char serial_getc(unsigned long com_port);
+int serial_tstc(unsigned long com_port);
+
+/* #endif XXXX */
+
typedef void *phandle;
typedef void *ihandle;
diff --git a/arch/powerpc/boot/prom_embedded.c b/arch/powerpc/boot/prom_embedded.c
new file mode 100644
index 0000000..96971df
--- /dev/null
+++ b/arch/powerpc/boot/prom_embedded.c
@@ -0,0 +1,112 @@
+/*
+ * prom.c routines for non-OF/embedded platforms.
+ *
+ * Author: Mark A. Greer <mgreer@mvista.com>
+ *
+ * 2006 (c) MontaVista Software, Inc. 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 <stdarg.h>
+#include <stddef.h>
+#include "string.h"
+#include "stdio.h"
+#include "prom.h"
+
+int (*prom)(void *);
+phandle chosen_handle;
+ihandle stdout;
+
+int
+service_exit(int nargs, int nret, va_list args)
+{
+ printf("EXIT\n");
+ while (1);
+ /* NOTREACHED */
+}
+
+int
+service_finddevice(int nargs, int nret, va_list args)
+{
+ return 0; /* XXXX */
+}
+
+int
+service_getprop(int nargs, int nret, va_list args)
+{
+ return 4; /* XXXX */
+}
+
+int
+call_prom(const char *service, int nargs, int nret, ...)
+{
+
+ static struct {
+ char *service;
+ int ((*rtn)(int nargs, int nret, va_list args));
+ } services[] = {
+ { "exit", service_exit },
+ { "finddevice", service_finddevice },
+ { "getprop", service_getprop },
+ /* { "write", service_write }, */
+ };
+ va_list args;
+ int i, rc = 0;
+
+ for (i=0; i<ARRAY_SIZE(services); i++)
+ if (!strcmp(service, services[i].service)) {
+ va_start(args, nret);
+ rc = services[i].rtn(nargs, nret, args);
+ va_end(args);
+ }
+
+ return (nret > 0)? rc : 0;
+}
+
+void
+*claim(unsigned long virt, unsigned long size, unsigned long align)
+{
+ return (void *)virt;
+}
+
+int
+write(void *handle, void *ptr, int nb)
+{
+ char c, *s = (char *)ptr;
+ static char first_time = 1;
+ extern unsigned long com_port;
+
+ if (first_time) {
+ serial_init(0, NULL);
+ first_time = 0;
+ }
+
+ while ((c = *s++) != '\0') {
+ serial_putc(com_port, c);
+ if (c == '\n')
+ serial_putc(com_port, '\r');
+ }
+
+ return nb;
+}
+
+void
+edit_cmdline(void *dt_blob)
+{
+ u32 i, *v;
+ extern void *dt_find_prop_by_name(void *dt_blob, char *full_name,
+ u32 *val_sizep);
+
+ if ((v = (u32 *)dt_find_prop_by_name(dt_blob,
+ "/chosen/bootargs", &i)) != NULL) {
+ printf("\nLinux/PPC load: %s", (char *)v);
+ /* edit cmdline & put back into dt (realloc may be necessary) */
+ }
+ printf("\n");
+}
+
+void __attribute__ ((weak))
+platform_fixups(void *dt_blob)
+{
+}
diff --git a/arch/powerpc/boot/sandpoint.c b/arch/powerpc/boot/sandpoint.c
new file mode 100644
index 0000000..283bb66
--- /dev/null
+++ b/arch/powerpc/boot/sandpoint.c
@@ -0,0 +1,110 @@
+/*
+ * Sandpoint specific fixups.
+ *
+ * Author: Mark A. Greer <mgreer@mvista.com>
+ *
+ * 2006 (c) MontaVista, Software, Inc. 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 <stdarg.h>
+#include <stddef.h>
+#include "types.h"
+#include "string.h"
+#include "stdio.h"
+#include "io.h"
+#include "prom.h"
+
+static u32 cpu_824x[32] = {
+ 5, 6, 9, 4, 4, 5, 2, 6, 6, 4, 9, 6, 5, 7, 6, 7,
+ 4, 5, 4, 6, 7, 8, 8, 4, 6, 5, 8, 6, 6, 5, 7, 0
+};
+static u32 cpu_7xx[16] = { /* 750/755 */
+ 0, 15, 14, 2, 4, 13, 20, 9, 6, 11, 8, 10, 16, 12, 7, 0
+};
+static u32 cpu_7457[32] = {
+ 23, 34, 15, 30, 14, 36, 2, 40, 4, 42, 13, 26, 17, 48, 19, 18,
+ 6, 21, 11, 22, 8, 20, 10, 24, 16, 28, 12, 32, 27, 56, 0, 25
+};
+
+static struct processor_info {
+ u32 pvr;
+ u32 mask;
+ u32 bus_freq;
+ u32 hid1_shift;
+ u32 hid1_mask;
+ u32 *pll_tbl;
+ u32 max_mem; /* 7457 flaky with > 64MB of mem */
+} processor_info_tbl[] = { /* From cputable -- MHz are only guesses */
+ /* 824x */
+ { 0x00810000, 0x7fff0000, 100000000, 27, 0x1f, cpu_824x, 0x80000000 },
+ /* 750 */
+ { 0x00084202, 0xffffffff, 100000000, 28, 0xf, cpu_7xx, 0x80000000 },
+ /* 745/755 */
+ { 0x00083000, 0xfffff000, 100000000, 28, 0xf, cpu_7xx, 0x80000000 },
+ /* 7447/7457 Rev 1.0 */
+ { 0x80020100, 0xffffffff, 100000000, 12, 0x1f, cpu_7457, 0x04000000 },
+ /* 7447/7457 Rev 1.1 */
+ { 0x80020101, 0xffffffff, 100000000, 12, 0x1f, cpu_7457, 0x04000000 },
+ /* 7447/7457 Rev 1.2 & up*/
+ { 0x80020000, 0xffff0000, 100000000, 12, 0x1f, cpu_7457, 0x04000000 },
+ /* 7447A */
+ { 0x80030000, 0xffff0000, 100000000, 12, 0x1f, cpu_7457, 0x80000000 },
+};
+
+struct processor_info *
+get_processor_info(u32 pvr)
+{
+ struct processor_info *pit = processor_info_tbl;
+ u32 i;
+
+ for (i=0; i<ARRAY_SIZE(processor_info_tbl); i++, pit++)
+ if (pit->pvr == (pvr & pit->mask))
+ return pit;
+ return NULL;
+}
+
+#define __stringify_1(x) #x
+#define __stringify(x) __stringify_1(x)
+
+
+#define SPRN_PVR 0x11F /* Processor Version Register */
+#define SPRN_HID1 0x3F1 /* Hardware Implementation Register 1 */
+#define mfspr(rn) ({unsigned long rval; \
+ asm volatile("mfspr %0," __stringify(rn) \
+ : "=r" (rval)); rval;})
+
+void
+platform_fixups(void *dt_blob)
+{
+ u32 i, *v, hid1, max_mem = 0xffffffff;
+ struct processor_info *pit;
+ extern void *dt_find_prop_by_name(void *dt_blob, char *full_name,
+ u32 *val_sizep);
+ extern u32 mpc10x_get_mem_size(void);
+
+ if ((pit = get_processor_info(mfspr(SPRN_PVR))) != NULL) {
+ if ((v = (u32 *)dt_find_prop_by_name(dt_blob,
+ "/cpus/PowerPC,603e/clock-frequency", &i)) != NULL) {
+
+ hid1 = (mfspr(SPRN_HID1) >> pit->hid1_shift)
+ & pit->hid1_mask;
+ v[0] = pit->bus_freq * pit->pll_tbl[hid1]/2;
+ }
+
+ if ((v = (u32 *)dt_find_prop_by_name(dt_blob,
+ "/cpus/PowerPC,603e/timebase-frequency", &i)) != NULL)
+ v[0] = pit->bus_freq / 4;
+
+ max_mem = pit->max_mem;
+ }
+
+ /* Get the RAM size from the memory controller */
+ if ((v = (u32 *)dt_find_prop_by_name(dt_blob, "/memory/reg", &i))
+ != NULL) {
+ i = mpc10x_get_mem_size();
+ v[1] = min(i, max_mem);
+ }
+}
diff --git a/arch/powerpc/boot/types.h b/arch/powerpc/boot/types.h
new file mode 100644
index 0000000..7095b54
--- /dev/null
+++ b/arch/powerpc/boot/types.h
@@ -0,0 +1,28 @@
+#ifndef _TYPES_H_
+#define _TYPES_H_
+
+#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
+
+/* XXXX
+#ifdef CONFIG_PPC32
+*/
+typedef unsigned char u8;
+typedef unsigned short u16;
+typedef unsigned int u32;
+/*
+#endif
+*/
+
+#define min(x,y) ({ \
+ typeof(x) _x = (x); \
+ typeof(y) _y = (y); \
+ (void) (&_x == &_y); \
+ _x < _y ? _x : _y; })
+
+#define max(x,y) ({ \
+ typeof(x) _x = (x); \
+ typeof(y) _y = (y); \
+ (void) (&_x == &_y); \
+ _x > _y ? _x : _y; })
+
+#endif /* _TYPES_H_ */
diff --git a/arch/powerpc/configs/sandpoint_defconfig b/arch/powerpc/configs/sandpoint_defconfig
new file mode 100644
index 0000000..3345c8e
--- /dev/null
+++ b/arch/powerpc/configs/sandpoint_defconfig
@@ -0,0 +1,1017 @@
+#
+# Automatically generated make config: don't edit
+# Linux kernel version: 2.6.17-rc3
+# Wed May 17 16:48:20 2006
+#
+# CONFIG_PPC64 is not set
+CONFIG_PPC32=y
+CONFIG_PPC_MERGE=y
+CONFIG_MMU=y
+CONFIG_GENERIC_HARDIRQS=y
+CONFIG_RWSEM_XCHGADD_ALGORITHM=y
+CONFIG_GENERIC_HWEIGHT=y
+CONFIG_GENERIC_CALIBRATE_DELAY=y
+CONFIG_PPC=y
+CONFIG_EARLY_PRINTK=y
+CONFIG_GENERIC_NVRAM=y
+CONFIG_SCHED_NO_NO_OMIT_FRAME_POINTER=y
+CONFIG_ARCH_MAY_HAVE_PC_FDC=y
+CONFIG_PPC_OF=y
+CONFIG_PPC_UDBG_16550=y
+# CONFIG_GENERIC_TBSYNC is not set
+# CONFIG_DEFAULT_UIMAGE is not set
+
+#
+# Processor support
+#
+CONFIG_CLASSIC32=y
+# CONFIG_PPC_52xx is not set
+# CONFIG_PPC_82xx is not set
+# CONFIG_PPC_83xx is not set
+# CONFIG_PPC_85xx is not set
+# CONFIG_40x is not set
+# CONFIG_44x is not set
+# CONFIG_8xx is not set
+# CONFIG_E200 is not set
+CONFIG_6xx=y
+CONFIG_PPC_FPU=y
+CONFIG_ALTIVEC=y
+CONFIG_PPC_STD_MMU=y
+CONFIG_PPC_STD_MMU_32=y
+# CONFIG_SMP is not set
+
+#
+# Code maturity level options
+#
+CONFIG_EXPERIMENTAL=y
+CONFIG_BROKEN_ON_SMP=y
+CONFIG_INIT_ENV_ARG_LIMIT=32
+
+#
+# General setup
+#
+CONFIG_LOCALVERSION=""
+# CONFIG_LOCALVERSION_AUTO is not set
+CONFIG_SWAP=y
+CONFIG_SYSVIPC=y
+CONFIG_POSIX_MQUEUE=y
+# CONFIG_BSD_PROCESS_ACCT is not set
+CONFIG_SYSCTL=y
+# CONFIG_AUDIT is not set
+CONFIG_IKCONFIG=y
+CONFIG_IKCONFIG_PROC=y
+# CONFIG_RELAY is not set
+CONFIG_INITRAMFS_SOURCE=""
+# CONFIG_CC_OPTIMIZE_FOR_SIZE is not set
+CONFIG_EMBEDDED=y
+CONFIG_KALLSYMS=y
+# CONFIG_KALLSYMS_ALL is not set
+# CONFIG_KALLSYMS_EXTRA_PASS is not set
+CONFIG_HOTPLUG=y
+CONFIG_PRINTK=y
+CONFIG_BUG=y
+CONFIG_ELF_CORE=y
+CONFIG_BASE_FULL=y
+CONFIG_FUTEX=y
+CONFIG_EPOLL=y
+CONFIG_SHMEM=y
+CONFIG_SLAB=y
+# CONFIG_TINY_SHMEM is not set
+CONFIG_BASE_SMALL=0
+# CONFIG_SLOB is not set
+
+#
+# Loadable module support
+#
+CONFIG_MODULES=y
+CONFIG_MODULE_UNLOAD=y
+CONFIG_MODULE_FORCE_UNLOAD=y
+# CONFIG_MODVERSIONS is not set
+# CONFIG_MODULE_SRCVERSION_ALL is not set
+CONFIG_KMOD=y
+
+#
+# Block layer
+#
+# CONFIG_LBD is not set
+# CONFIG_BLK_DEV_IO_TRACE is not set
+# CONFIG_LSF is not set
+
+#
+# IO Schedulers
+#
+CONFIG_IOSCHED_NOOP=y
+# CONFIG_IOSCHED_AS is not set
+# CONFIG_IOSCHED_DEADLINE is not set
+# CONFIG_IOSCHED_CFQ is not set
+# CONFIG_DEFAULT_AS is not set
+# CONFIG_DEFAULT_DEADLINE is not set
+# CONFIG_DEFAULT_CFQ is not set
+CONFIG_DEFAULT_NOOP=y
+CONFIG_DEFAULT_IOSCHED="noop"
+
+#
+# Platform support
+#
+# CONFIG_PPC_MULTIPLATFORM is not set
+# CONFIG_PPC_ISERIES is not set
+CONFIG_EMBEDDED6xx=y
+# CONFIG_APUS is not set
+CONFIG_MPIC=y
+CONFIG_MPIC_SERIAL=y
+# CONFIG_PPC_RTAS is not set
+# CONFIG_MMIO_NVRAM is not set
+# CONFIG_PPC_MPC106 is not set
+# CONFIG_PPC_970_NAP is not set
+# CONFIG_CPU_FREQ is not set
+# CONFIG_TAU is not set
+# CONFIG_KATANA is not set
+# CONFIG_WILLOW is not set
+# CONFIG_CPCI690 is not set
+# CONFIG_POWERPMC250 is not set
+# CONFIG_CHESTNUT is not set
+# CONFIG_SPRUCE is not set
+# CONFIG_HDPU is not set
+# CONFIG_EV64260 is not set
+# CONFIG_LOPEC is not set
+# CONFIG_MVME5100 is not set
+# CONFIG_PPLUS is not set
+# CONFIG_PRPMC750 is not set
+# CONFIG_PRPMC800 is not set
+CONFIG_SANDPOINT=y
+# CONFIG_RADSTONE_PPC7D is not set
+# CONFIG_PAL4 is not set
+# CONFIG_GEMINI is not set
+# CONFIG_EST8260 is not set
+# CONFIG_SBC82xx is not set
+# CONFIG_SBS8260 is not set
+# CONFIG_RPX8260 is not set
+# CONFIG_TQM8260 is not set
+# CONFIG_ADS8272 is not set
+# CONFIG_PQ2FADS is not set
+# CONFIG_LITE5200 is not set
+# CONFIG_EV64360 is not set
+CONFIG_PPC_GEN550=y
+CONFIG_EPIC_SERIAL_MODE=y
+CONFIG_MPC10X_BRIDGE=y
+CONFIG_MPC10X_OPENPIC=y
+# CONFIG_MPC10X_STORE_GATHERING is not set
+# CONFIG_SANDPOINT_ENABLE_UART1 is not set
+# CONFIG_WANT_EARLY_SERIAL is not set
+
+#
+# Kernel options
+#
+# CONFIG_HIGHMEM is not set
+CONFIG_HZ_100=y
+# CONFIG_HZ_250 is not set
+# CONFIG_HZ_1000 is not set
+CONFIG_HZ=100
+CONFIG_PREEMPT_NONE=y
+# CONFIG_PREEMPT_VOLUNTARY is not set
+# CONFIG_PREEMPT is not set
+CONFIG_BINFMT_ELF=y
+CONFIG_BINFMT_MISC=y
+CONFIG_ARCH_FLATMEM_ENABLE=y
+CONFIG_SELECT_MEMORY_MODEL=y
+CONFIG_FLATMEM_MANUAL=y
+# CONFIG_DISCONTIGMEM_MANUAL is not set
+# CONFIG_SPARSEMEM_MANUAL is not set
+CONFIG_FLATMEM=y
+CONFIG_FLAT_NODE_MEM_MAP=y
+# CONFIG_SPARSEMEM_STATIC is not set
+CONFIG_SPLIT_PTLOCK_CPUS=4
+CONFIG_PROC_DEVICETREE=y
+# CONFIG_CMDLINE_BOOL is not set
+# CONFIG_PM is not set
+# CONFIG_SOFTWARE_SUSPEND is not set
+# CONFIG_SECCOMP is not set
+CONFIG_ISA_DMA_API=y
+
+#
+# Bus options
+#
+CONFIG_GENERIC_ISA_DMA=y
+CONFIG_PPC_I8259=y
+CONFIG_PPC_INDIRECT_PCI=y
+CONFIG_PCI=y
+CONFIG_PCI_DOMAINS=y
+# CONFIG_PCI_DEBUG is not set
+
+#
+# PCCARD (PCMCIA/CardBus) support
+#
+CONFIG_PCCARD=m
+# CONFIG_PCMCIA_DEBUG is not set
+CONFIG_PCMCIA=m
+CONFIG_PCMCIA_LOAD_CIS=y
+CONFIG_PCMCIA_IOCTL=y
+CONFIG_CARDBUS=y
+
+#
+# PC-card bridges
+#
+CONFIG_YENTA=m
+CONFIG_YENTA_O2=y
+CONFIG_YENTA_RICOH=y
+CONFIG_YENTA_TI=y
+CONFIG_YENTA_ENE_TUNE=y
+CONFIG_YENTA_TOSHIBA=y
+# CONFIG_PD6729 is not set
+# CONFIG_I82092 is not set
+CONFIG_PCCARD_NONSTATIC=m
+
+#
+# PCI Hotplug Support
+#
+# CONFIG_HOTPLUG_PCI is not set
+
+#
+# Advanced setup
+#
+# CONFIG_ADVANCED_OPTIONS is not set
+
+#
+# Default settings for advanced configuration options are used
+#
+CONFIG_HIGHMEM_START=0xfe000000
+CONFIG_LOWMEM_SIZE=0x30000000
+CONFIG_KERNEL_START=0xc0000000
+CONFIG_TASK_SIZE=0x80000000
+CONFIG_BOOT_LOAD=0x00800000
+
+#
+# Networking
+#
+CONFIG_NET=y
+
+#
+# Networking options
+#
+# CONFIG_NETDEBUG is not set
+CONFIG_PACKET=y
+# CONFIG_PACKET_MMAP is not set
+CONFIG_UNIX=y
+# CONFIG_NET_KEY is not set
+CONFIG_INET=y
+# CONFIG_IP_MULTICAST is not set
+# CONFIG_IP_ADVANCED_ROUTER is not set
+CONFIG_IP_FIB_HASH=y
+CONFIG_IP_PNP=y
+CONFIG_IP_PNP_DHCP=y
+# CONFIG_IP_PNP_BOOTP is not set
+# CONFIG_IP_PNP_RARP is not set
+# CONFIG_NET_IPIP is not set
+# CONFIG_NET_IPGRE is not set
+# CONFIG_ARPD is not set
+# CONFIG_SYN_COOKIES is not set
+# CONFIG_INET_AH is not set
+# CONFIG_INET_ESP is not set
+# CONFIG_INET_IPCOMP is not set
+# CONFIG_INET_XFRM_TUNNEL is not set
+# CONFIG_INET_TUNNEL is not set
+CONFIG_INET_DIAG=y
+CONFIG_INET_TCP_DIAG=y
+# CONFIG_TCP_CONG_ADVANCED is not set
+CONFIG_TCP_CONG_BIC=y
+# CONFIG_IPV6 is not set
+# CONFIG_INET6_XFRM_TUNNEL is not set
+# CONFIG_INET6_TUNNEL is not set
+# CONFIG_NETFILTER is not set
+
+#
+# DCCP Configuration (EXPERIMENTAL)
+#
+CONFIG_IP_DCCP=m
+CONFIG_INET_DCCP_DIAG=m
+CONFIG_IP_DCCP_ACKVEC=y
+
+#
+# DCCP CCIDs Configuration (EXPERIMENTAL)
+#
+CONFIG_IP_DCCP_CCID2=m
+CONFIG_IP_DCCP_CCID3=m
+CONFIG_IP_DCCP_TFRC_LIB=m
+
+#
+# DCCP Kernel Hacking
+#
+# CONFIG_IP_DCCP_DEBUG is not set
+
+#
+# SCTP Configuration (EXPERIMENTAL)
+#
+# CONFIG_IP_SCTP is not set
+
+#
+# TIPC Configuration (EXPERIMENTAL)
+#
+# CONFIG_TIPC is not set
+# CONFIG_ATM is not set
+# CONFIG_BRIDGE is not set
+# CONFIG_VLAN_8021Q is not set
+# CONFIG_DECNET is not set
+# CONFIG_LLC2 is not set
+# CONFIG_IPX is not set
+# CONFIG_ATALK is not set
+# CONFIG_X25 is not set
+# CONFIG_LAPB is not set
+# CONFIG_NET_DIVERT is not set
+# CONFIG_ECONET is not set
+# CONFIG_WAN_ROUTER is not set
+
+#
+# QoS and/or fair queueing
+#
+# CONFIG_NET_SCHED is not set
+
+#
+# Network testing
+#
+# CONFIG_NET_PKTGEN is not set
+# CONFIG_HAMRADIO is not set
+# CONFIG_IRDA is not set
+# CONFIG_BT is not set
+# CONFIG_IEEE80211 is not set
+
+#
+# Device Drivers
+#
+
+#
+# Generic Driver Options
+#
+# CONFIG_STANDALONE is not set
+CONFIG_PREVENT_FIRMWARE_BUILD=y
+CONFIG_FW_LOADER=y
+# CONFIG_DEBUG_DRIVER is not set
+
+#
+# Connector - unified userspace <-> kernelspace linker
+#
+# CONFIG_CONNECTOR is not set
+
+#
+# Memory Technology Devices (MTD)
+#
+# CONFIG_MTD is not set
+
+#
+# Parallel port support
+#
+# CONFIG_PARPORT is not set
+
+#
+# Plug and Play support
+#
+
+#
+# Block devices
+#
+# CONFIG_BLK_DEV_FD is not set
+# CONFIG_BLK_CPQ_DA is not set
+# CONFIG_BLK_CPQ_CISS_DA is not set
+# CONFIG_BLK_DEV_DAC960 is not set
+# CONFIG_BLK_DEV_UMEM is not set
+# CONFIG_BLK_DEV_COW_COMMON is not set
+CONFIG_BLK_DEV_LOOP=y
+# CONFIG_BLK_DEV_CRYPTOLOOP is not set
+# CONFIG_BLK_DEV_NBD is not set
+# CONFIG_BLK_DEV_SX8 is not set
+CONFIG_BLK_DEV_RAM=y
+CONFIG_BLK_DEV_RAM_COUNT=16
+CONFIG_BLK_DEV_RAM_SIZE=4096
+CONFIG_BLK_DEV_INITRD=y
+# CONFIG_CDROM_PKTCDVD is not set
+# CONFIG_ATA_OVER_ETH is not set
+
+#
+# ATA/ATAPI/MFM/RLL support
+#
+# CONFIG_IDE is not set
+
+#
+# SCSI device support
+#
+# CONFIG_RAID_ATTRS is not set
+# CONFIG_SCSI is not set
+
+#
+# Multi-device support (RAID and LVM)
+#
+# CONFIG_MD is not set
+
+#
+# Fusion MPT device support
+#
+# CONFIG_FUSION is not set
+
+#
+# IEEE 1394 (FireWire) support
+#
+# CONFIG_IEEE1394 is not set
+
+#
+# I2O device support
+#
+# CONFIG_I2O is not set
+
+#
+# Macintosh device drivers
+#
+# CONFIG_WINDFARM is not set
+
+#
+# Network device support
+#
+CONFIG_NETDEVICES=y
+# CONFIG_DUMMY is not set
+# CONFIG_BONDING is not set
+# CONFIG_EQUALIZER is not set
+# CONFIG_TUN is not set
+
+#
+# ARCnet devices
+#
+# CONFIG_ARCNET is not set
+
+#
+# PHY device support
+#
+# CONFIG_PHYLIB is not set
+
+#
+# Ethernet (10 or 100Mbit)
+#
+CONFIG_NET_ETHERNET=y
+CONFIG_MII=y
+# CONFIG_HAPPYMEAL is not set
+# CONFIG_SUNGEM is not set
+# CONFIG_CASSINI is not set
+# CONFIG_NET_VENDOR_3COM is not set
+
+#
+# Tulip family network device support
+#
+CONFIG_NET_TULIP=y
+# CONFIG_DE2104X is not set
+CONFIG_TULIP=y
+# CONFIG_TULIP_MWI is not set
+# CONFIG_TULIP_MMIO is not set
+# CONFIG_TULIP_NAPI is not set
+# CONFIG_DE4X5 is not set
+# CONFIG_WINBOND_840 is not set
+# CONFIG_DM9102 is not set
+# CONFIG_ULI526X is not set
+# CONFIG_PCMCIA_XIRCOM is not set
+# CONFIG_PCMCIA_XIRTULIP is not set
+# CONFIG_HP100 is not set
+CONFIG_NET_PCI=y
+CONFIG_PCNET32=y
+# CONFIG_AMD8111_ETH is not set
+# CONFIG_ADAPTEC_STARFIRE is not set
+# CONFIG_B44 is not set
+# CONFIG_FORCEDETH is not set
+# CONFIG_DGRS is not set
+# CONFIG_EEPRO100 is not set
+CONFIG_E100=y
+# CONFIG_FEALNX is not set
+# CONFIG_NATSEMI is not set
+# CONFIG_NE2K_PCI is not set
+# CONFIG_8139CP is not set
+CONFIG_8139TOO=y
+CONFIG_8139TOO_PIO=y
+# CONFIG_8139TOO_TUNE_TWISTER is not set
+# CONFIG_8139TOO_8129 is not set
+# CONFIG_8139_OLD_RX_RESET is not set
+# CONFIG_SIS900 is not set
+# CONFIG_EPIC100 is not set
+# CONFIG_SUNDANCE is not set
+# CONFIG_TLAN is not set
+# CONFIG_VIA_RHINE is not set
+
+#
+# Ethernet (1000 Mbit)
+#
+# CONFIG_ACENIC is not set
+# CONFIG_DL2K is not set
+# CONFIG_E1000 is not set
+# CONFIG_NS83820 is not set
+# CONFIG_HAMACHI is not set
+# CONFIG_YELLOWFIN is not set
+# CONFIG_R8169 is not set
+# CONFIG_SIS190 is not set
+# CONFIG_SKGE is not set
+# CONFIG_SKY2 is not set
+# CONFIG_SK98LIN is not set
+# CONFIG_VIA_VELOCITY is not set
+# CONFIG_TIGON3 is not set
+# CONFIG_BNX2 is not set
+
+#
+# Ethernet (10000 Mbit)
+#
+# CONFIG_CHELSIO_T1 is not set
+# CONFIG_IXGB is not set
+# CONFIG_S2IO is not set
+
+#
+# Token Ring devices
+#
+# CONFIG_TR is not set
+
+#
+# Wireless LAN (non-hamradio)
+#
+# CONFIG_NET_RADIO is not set
+
+#
+# PCMCIA network device support
+#
+# CONFIG_NET_PCMCIA is not set
+
+#
+# Wan interfaces
+#
+# CONFIG_WAN is not set
+# CONFIG_FDDI is not set
+# CONFIG_HIPPI is not set
+# CONFIG_PPP is not set
+# CONFIG_SLIP is not set
+# CONFIG_SHAPER is not set
+# CONFIG_NETCONSOLE is not set
+# CONFIG_NETPOLL is not set
+# CONFIG_NET_POLL_CONTROLLER is not set
+
+#
+# ISDN subsystem
+#
+# CONFIG_ISDN is not set
+
+#
+# Telephony Support
+#
+# CONFIG_PHONE is not set
+
+#
+# Input device support
+#
+CONFIG_INPUT=y
+
+#
+# Userland interfaces
+#
+CONFIG_INPUT_MOUSEDEV=y
+# CONFIG_INPUT_MOUSEDEV_PSAUX is not set
+CONFIG_INPUT_MOUSEDEV_SCREEN_X=1024
+CONFIG_INPUT_MOUSEDEV_SCREEN_Y=768
+# CONFIG_INPUT_JOYDEV is not set
+# CONFIG_INPUT_TSDEV is not set
+# CONFIG_INPUT_EVDEV is not set
+# CONFIG_INPUT_EVBUG is not set
+
+#
+# Input Device Drivers
+#
+# CONFIG_INPUT_KEYBOARD is not set
+# CONFIG_INPUT_MOUSE is not set
+# CONFIG_INPUT_JOYSTICK is not set
+# CONFIG_INPUT_TOUCHSCREEN is not set
+# CONFIG_INPUT_MISC is not set
+
+#
+# Hardware I/O ports
+#
+# CONFIG_SERIO is not set
+# CONFIG_GAMEPORT is not set
+
+#
+# Character devices
+#
+# CONFIG_VT is not set
+# CONFIG_SERIAL_NONSTANDARD is not set
+
+#
+# Serial drivers
+#
+CONFIG_SERIAL_8250=y
+CONFIG_SERIAL_8250_CONSOLE=y
+CONFIG_SERIAL_8250_PCI=y
+# CONFIG_SERIAL_8250_CS is not set
+CONFIG_SERIAL_8250_NR_UARTS=4
+CONFIG_SERIAL_8250_RUNTIME_UARTS=4
+# CONFIG_SERIAL_8250_EXTENDED is not set
+
+#
+# Non-8250 serial port support
+#
+CONFIG_SERIAL_CORE=y
+CONFIG_SERIAL_CORE_CONSOLE=y
+# CONFIG_SERIAL_JSM is not set
+CONFIG_UNIX98_PTYS=y
+CONFIG_LEGACY_PTYS=y
+CONFIG_LEGACY_PTY_COUNT=256
+
+#
+# IPMI
+#
+# CONFIG_IPMI_HANDLER is not set
+
+#
+# Watchdog Cards
+#
+# CONFIG_WATCHDOG is not set
+CONFIG_NVRAM=y
+CONFIG_GEN_RTC=y
+# CONFIG_GEN_RTC_X is not set
+# CONFIG_DTLK is not set
+# CONFIG_R3964 is not set
+# CONFIG_APPLICOM is not set
+
+#
+# Ftape, the floppy tape device driver
+#
+# CONFIG_AGP is not set
+# CONFIG_DRM is not set
+
+#
+# PCMCIA character devices
+#
+# CONFIG_SYNCLINK_CS is not set
+# CONFIG_CARDMAN_4000 is not set
+# CONFIG_CARDMAN_4040 is not set
+# CONFIG_RAW_DRIVER is not set
+
+#
+# TPM devices
+#
+# CONFIG_TCG_TPM is not set
+# CONFIG_TELCLOCK is not set
+
+#
+# I2C support
+#
+CONFIG_I2C=y
+CONFIG_I2C_CHARDEV=y
+
+#
+# I2C Algorithms
+#
+CONFIG_I2C_ALGOBIT=y
+# CONFIG_I2C_ALGOPCF is not set
+# CONFIG_I2C_ALGOPCA is not set
+
+#
+# I2C Hardware Bus support
+#
+# CONFIG_I2C_ALI1535 is not set
+# CONFIG_I2C_ALI1563 is not set
+# CONFIG_I2C_ALI15X3 is not set
+# CONFIG_I2C_AMD756 is not set
+# CONFIG_I2C_AMD8111 is not set
+# CONFIG_I2C_I801 is not set
+# CONFIG_I2C_I810 is not set
+# CONFIG_I2C_PIIX4 is not set
+CONFIG_I2C_MPC=y
+# CONFIG_I2C_NFORCE2 is not set
+# CONFIG_I2C_PARPORT_LIGHT is not set
+# CONFIG_I2C_PROSAVAGE is not set
+# CONFIG_I2C_SAVAGE4 is not set
+# CONFIG_I2C_SIS5595 is not set
+# CONFIG_I2C_SIS630 is not set
+# CONFIG_I2C_SIS96X is not set
+# CONFIG_I2C_STUB is not set
+# CONFIG_I2C_VIA is not set
+# CONFIG_I2C_VIAPRO is not set
+# CONFIG_I2C_VOODOO3 is not set
+# CONFIG_I2C_PCA_ISA is not set
+
+#
+# Miscellaneous I2C Chip support
+#
+# CONFIG_SENSORS_DS1337 is not set
+# CONFIG_SENSORS_DS1374 is not set
+# CONFIG_SENSORS_EEPROM is not set
+# CONFIG_SENSORS_PCF8574 is not set
+# CONFIG_SENSORS_PCA9539 is not set
+# CONFIG_SENSORS_PCF8591 is not set
+# CONFIG_SENSORS_M41T00 is not set
+# CONFIG_SENSORS_MAX6875 is not set
+# CONFIG_I2C_DEBUG_CORE is not set
+# CONFIG_I2C_DEBUG_ALGO is not set
+# CONFIG_I2C_DEBUG_BUS is not set
+# CONFIG_I2C_DEBUG_CHIP is not set
+
+#
+# SPI support
+#
+# CONFIG_SPI is not set
+# CONFIG_SPI_MASTER is not set
+
+#
+# Dallas's 1-wire bus
+#
+# CONFIG_W1 is not set
+
+#
+# Hardware Monitoring support
+#
+# CONFIG_HWMON is not set
+# CONFIG_HWMON_VID is not set
+
+#
+# Misc devices
+#
+
+#
+# Multimedia devices
+#
+# CONFIG_VIDEO_DEV is not set
+
+#
+# Digital Video Broadcasting Devices
+#
+# CONFIG_DVB is not set
+
+#
+# Graphics support
+#
+# CONFIG_FB is not set
+
+#
+# Sound
+#
+# CONFIG_SOUND is not set
+
+#
+# USB support
+#
+CONFIG_USB_ARCH_HAS_HCD=y
+CONFIG_USB_ARCH_HAS_OHCI=y
+CONFIG_USB_ARCH_HAS_EHCI=y
+# CONFIG_USB is not set
+
+#
+# NOTE: USB_STORAGE enables SCSI, and 'SCSI disk support'
+#
+
+#
+# USB Gadget Support
+#
+# CONFIG_USB_GADGET is not set
+
+#
+# MMC/SD Card support
+#
+# CONFIG_MMC is not set
+
+#
+# LED devices
+#
+# CONFIG_NEW_LEDS is not set
+
+#
+# LED drivers
+#
+
+#
+# LED Triggers
+#
+
+#
+# InfiniBand support
+#
+# CONFIG_INFINIBAND is not set
+
+#
+# EDAC - error detection and reporting (RAS) (EXPERIMENTAL)
+#
+
+#
+# Real Time Clock
+#
+# CONFIG_RTC_CLASS is not set
+
+#
+# File systems
+#
+CONFIG_EXT2_FS=y
+# CONFIG_EXT2_FS_XATTR is not set
+# CONFIG_EXT2_FS_XIP is not set
+CONFIG_EXT3_FS=y
+CONFIG_EXT3_FS_XATTR=y
+# CONFIG_EXT3_FS_POSIX_ACL is not set
+# CONFIG_EXT3_FS_SECURITY is not set
+CONFIG_JBD=y
+# CONFIG_JBD_DEBUG is not set
+CONFIG_FS_MBCACHE=y
+# CONFIG_REISERFS_FS is not set
+# CONFIG_JFS_FS is not set
+CONFIG_FS_POSIX_ACL=y
+# CONFIG_XFS_FS is not set
+# CONFIG_OCFS2_FS is not set
+# CONFIG_MINIX_FS is not set
+# CONFIG_ROMFS_FS is not set
+# CONFIG_INOTIFY is not set
+# CONFIG_QUOTA is not set
+CONFIG_DNOTIFY=y
+# CONFIG_AUTOFS_FS is not set
+# CONFIG_AUTOFS4_FS is not set
+# CONFIG_FUSE_FS is not set
+
+#
+# CD-ROM/DVD Filesystems
+#
+# CONFIG_ISO9660_FS is not set
+# CONFIG_UDF_FS is not set
+
+#
+# DOS/FAT/NT Filesystems
+#
+# CONFIG_MSDOS_FS is not set
+# CONFIG_VFAT_FS is not set
+# CONFIG_NTFS_FS is not set
+
+#
+# Pseudo filesystems
+#
+CONFIG_PROC_FS=y
+CONFIG_PROC_KCORE=y
+CONFIG_SYSFS=y
+CONFIG_TMPFS=y
+# CONFIG_HUGETLB_PAGE is not set
+CONFIG_RAMFS=y
+# CONFIG_CONFIGFS_FS is not set
+
+#
+# Miscellaneous filesystems
+#
+# CONFIG_ADFS_FS is not set
+# CONFIG_AFFS_FS is not set
+# CONFIG_HFS_FS is not set
+# CONFIG_HFSPLUS_FS is not set
+# CONFIG_BEFS_FS is not set
+# CONFIG_BFS_FS is not set
+# CONFIG_EFS_FS is not set
+# CONFIG_CRAMFS is not set
+# CONFIG_VXFS_FS is not set
+# CONFIG_HPFS_FS is not set
+# CONFIG_QNX4FS_FS is not set
+# CONFIG_SYSV_FS is not set
+# CONFIG_UFS_FS is not set
+
+#
+# Network File Systems
+#
+CONFIG_NFS_FS=y
+CONFIG_NFS_V3=y
+CONFIG_NFS_V3_ACL=y
+CONFIG_NFS_V4=y
+# CONFIG_NFS_DIRECTIO is not set
+# CONFIG_NFSD is not set
+CONFIG_ROOT_NFS=y
+CONFIG_LOCKD=y
+CONFIG_LOCKD_V4=y
+CONFIG_NFS_ACL_SUPPORT=y
+CONFIG_NFS_COMMON=y
+CONFIG_SUNRPC=y
+CONFIG_SUNRPC_GSS=y
+CONFIG_RPCSEC_GSS_KRB5=y
+# CONFIG_RPCSEC_GSS_SPKM3 is not set
+# CONFIG_SMB_FS is not set
+# CONFIG_CIFS is not set
+# CONFIG_NCP_FS is not set
+# CONFIG_CODA_FS is not set
+# CONFIG_AFS_FS is not set
+# CONFIG_9P_FS is not set
+
+#
+# Partition Types
+#
+# CONFIG_PARTITION_ADVANCED is not set
+CONFIG_MSDOS_PARTITION=y
+
+#
+# Native Language Support
+#
+CONFIG_NLS=y
+CONFIG_NLS_DEFAULT="iso8859-1"
+# CONFIG_NLS_CODEPAGE_437 is not set
+# CONFIG_NLS_CODEPAGE_737 is not set
+# CONFIG_NLS_CODEPAGE_775 is not set
+# CONFIG_NLS_CODEPAGE_850 is not set
+# CONFIG_NLS_CODEPAGE_852 is not set
+# CONFIG_NLS_CODEPAGE_855 is not set
+# CONFIG_NLS_CODEPAGE_857 is not set
+# CONFIG_NLS_CODEPAGE_860 is not set
+# CONFIG_NLS_CODEPAGE_861 is not set
+# CONFIG_NLS_CODEPAGE_862 is not set
+# CONFIG_NLS_CODEPAGE_863 is not set
+# CONFIG_NLS_CODEPAGE_864 is not set
+# CONFIG_NLS_CODEPAGE_865 is not set
+# CONFIG_NLS_CODEPAGE_866 is not set
+# CONFIG_NLS_CODEPAGE_869 is not set
+# CONFIG_NLS_CODEPAGE_936 is not set
+# CONFIG_NLS_CODEPAGE_950 is not set
+# CONFIG_NLS_CODEPAGE_932 is not set
+# CONFIG_NLS_CODEPAGE_949 is not set
+# CONFIG_NLS_CODEPAGE_874 is not set
+# CONFIG_NLS_ISO8859_8 is not set
+# CONFIG_NLS_CODEPAGE_1250 is not set
+# CONFIG_NLS_CODEPAGE_1251 is not set
+# CONFIG_NLS_ASCII is not set
+# CONFIG_NLS_ISO8859_1 is not set
+# CONFIG_NLS_ISO8859_2 is not set
+# CONFIG_NLS_ISO8859_3 is not set
+# CONFIG_NLS_ISO8859_4 is not set
+# CONFIG_NLS_ISO8859_5 is not set
+# CONFIG_NLS_ISO8859_6 is not set
+# CONFIG_NLS_ISO8859_7 is not set
+# CONFIG_NLS_ISO8859_9 is not set
+# CONFIG_NLS_ISO8859_13 is not set
+# CONFIG_NLS_ISO8859_14 is not set
+# CONFIG_NLS_ISO8859_15 is not set
+# CONFIG_NLS_KOI8_R is not set
+# CONFIG_NLS_KOI8_U is not set
+# CONFIG_NLS_UTF8 is not set
+
+#
+# Library routines
+#
+CONFIG_CRC_CCITT=y
+CONFIG_CRC16=y
+CONFIG_CRC32=y
+# CONFIG_LIBCRC32C is not set
+
+#
+# Instrumentation Support
+#
+# CONFIG_PROFILING is not set
+
+#
+# Kernel hacking
+#
+# CONFIG_PRINTK_TIME is not set
+# CONFIG_MAGIC_SYSRQ is not set
+CONFIG_DEBUG_KERNEL=y
+CONFIG_LOG_BUF_SHIFT=14
+CONFIG_DETECT_SOFTLOCKUP=y
+# CONFIG_SCHEDSTATS is not set
+# CONFIG_DEBUG_SLAB is not set
+# CONFIG_DEBUG_MUTEXES is not set
+# CONFIG_DEBUG_SPINLOCK is not set
+# CONFIG_DEBUG_SPINLOCK_SLEEP is not set
+# CONFIG_DEBUG_KOBJECT is not set
+# CONFIG_DEBUG_INFO is not set
+# CONFIG_DEBUG_FS is not set
+# CONFIG_DEBUG_VM is not set
+CONFIG_FORCED_INLINING=y
+# CONFIG_RCU_TORTURE_TEST is not set
+# CONFIG_DEBUGGER is not set
+CONFIG_BDI_SWITCH=y
+# CONFIG_BOOTX_TEXT is not set
+# CONFIG_SERIAL_TEXT_DEBUG is not set
+# CONFIG_PPC_EARLY_DEBUG_LPAR is not set
+# CONFIG_PPC_EARLY_DEBUG_G5 is not set
+# CONFIG_PPC_EARLY_DEBUG_RTAS is not set
+# CONFIG_PPC_EARLY_DEBUG_MAPLE is not set
+# CONFIG_PPC_EARLY_DEBUG_ISERIES is not set
+
+#
+# Security options
+#
+# CONFIG_KEYS is not set
+# CONFIG_SECURITY is not set
+
+#
+# Cryptographic options
+#
+CONFIG_CRYPTO=y
+# CONFIG_CRYPTO_HMAC is not set
+# CONFIG_CRYPTO_NULL is not set
+# CONFIG_CRYPTO_MD4 is not set
+CONFIG_CRYPTO_MD5=y
+# CONFIG_CRYPTO_SHA1 is not set
+# CONFIG_CRYPTO_SHA256 is not set
+# CONFIG_CRYPTO_SHA512 is not set
+# CONFIG_CRYPTO_WP512 is not set
+# CONFIG_CRYPTO_TGR192 is not set
+CONFIG_CRYPTO_DES=y
+# CONFIG_CRYPTO_BLOWFISH is not set
+# CONFIG_CRYPTO_TWOFISH is not set
+# CONFIG_CRYPTO_SERPENT is not set
+# CONFIG_CRYPTO_AES is not set
+# CONFIG_CRYPTO_CAST5 is not set
+# CONFIG_CRYPTO_CAST6 is not set
+# CONFIG_CRYPTO_TEA is not set
+# CONFIG_CRYPTO_ARC4 is not set
+# CONFIG_CRYPTO_KHAZAD is not set
+# CONFIG_CRYPTO_ANUBIS is not set
+# CONFIG_CRYPTO_DEFLATE is not set
+# CONFIG_CRYPTO_MICHAEL_MIC is not set
+# CONFIG_CRYPTO_CRC32C is not set
+# CONFIG_CRYPTO_TEST is not set
+
+#
+# Hardware crypto devices
+#
diff --git a/arch/powerpc/kernel/legacy_serial.c b/arch/powerpc/kernel/legacy_serial.c
index 6e67b5b..2feca55 100644
--- a/arch/powerpc/kernel/legacy_serial.c
+++ b/arch/powerpc/kernel/legacy_serial.c
@@ -147,9 +147,11 @@ static int __init add_legacy_isa_port(st
if (reg == NULL)
return -1;
+#if 0 /* XXXX */
/* Verify it's an IO port, we don't support anything else */
if (!(reg[0] & 0x00000001))
return -1;
+#endif
/* Now look for an "ibm,aix-loc" property that gives us ordering
* if any...
diff --git a/arch/powerpc/kernel/setup-common.c b/arch/powerpc/kernel/setup-common.c
diff --git a/arch/powerpc/platforms/Makefile b/arch/powerpc/platforms/Makefile
index c4f6b0d..308fead 100644
--- a/arch/powerpc/platforms/Makefile
+++ b/arch/powerpc/platforms/Makefile
@@ -13,3 +13,4 @@ obj-$(CONFIG_PPC_PSERIES) += pseries/
obj-$(CONFIG_PPC_ISERIES) += iseries/
obj-$(CONFIG_PPC_MAPLE) += maple/
obj-$(CONFIG_PPC_CELL) += cell/
+obj-$(CONFIG_EMBEDDED6xx) += embedded6xx/
diff --git a/arch/powerpc/platforms/embedded6xx/Makefile b/arch/powerpc/platforms/embedded6xx/Makefile
new file mode 100644
index 0000000..3324426
--- /dev/null
+++ b/arch/powerpc/platforms/embedded6xx/Makefile
@@ -0,0 +1,4 @@
+#
+# Makefile for the PowerPC Embedded Platforms
+#
+obj-$(CONFIG_SANDPOINT) += sandpoint.o
diff --git a/arch/powerpc/platforms/embedded6xx/sandpoint.c b/arch/powerpc/platforms/embedded6xx/sandpoint.c
new file mode 100644
index 0000000..9567de8
--- /dev/null
+++ b/arch/powerpc/platforms/embedded6xx/sandpoint.c
@@ -0,0 +1,629 @@
+/*
+ * Board setup routines for the Freescale Sandpoint Test Platform.
+ *
+ * Author: Mark A. Greer
+ * mgreer@mvista.com
+ *
+ * 2000-2003 (c) MontaVista Software, Inc. 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.
+ */
+
+/*
+ * This file adds support for the Freescale Sandpoint Test Platform.
+ * These boards have a PPMC slot for the processor so any combination
+ * of cpu and host bridge can be attached. This port is for an 8240 PPMC
+ * module from Freescale and other closely related cpu/host bridge
+ * combinations (e.g., 750/755/7400 with MPC107 host bridge).
+ * The sandpoint itself has a Windbond 83c553 (PCI-ISA bridge, 2 DMA ctlrs, 2
+ * cascaded 8259 interrupt ctlrs, 8254 Timer/Counter, and an IDE ctlr), a
+ * National 87308 (RTC, 2 UARTs, Keyboard & mouse ctlrs, and a floppy ctlr),
+ * and 4 PCI slots (only 2 of which are usable; the other 2 are keyed for 3.3V
+ * but are really 5V).
+ *
+ * The firmware on the sandpoint is called DINK (not my acronym :). This port
+ * depends on DINK to do some basic initialization (e.g., initialize the memory
+ * ctlr) and to ensure that the processor is using MAP B (CHRP map).
+ *
+ * The switch settings for the Sandpoint board MUST be as follows:
+ * S3: down
+ * S4: up
+ * S5: up
+ * S6: down
+ *
+ * 'down' is in the direction from the PCI slots towards the PPMC slot;
+ * 'up' is in the direction from the PPMC slot towards the PCI slots.
+ * Be careful, the way the sandpoint board is installed in XT chasses will
+ * make the directions reversed.
+ *
+ * Since Freescale listened to our suggestions for improvement, we now have
+ * the Sandpoint X3 board. All of the PCI slots are available, it uses
+ * the serial interrupt interface (just a hardware thing we need to
+ * configure properly).
+ *
+ * Use the default X3 switch settings. The interrupts are then:
+ * EPIC Source
+ * 0 SIOINT (8259, active low)
+ * 1 PCI #1
+ * 2 PCI #2
+ * 3 PCI #3
+ * 4 PCI #4
+ * 7 Winbond INTC (IDE interrupt)
+ * 8 Winbond INTD (IDE interrupt)
+ *
+ *
+ * Freescale has finally released a version of DINK32 that correctly
+ * (seemingly) initalizes the memory controller correctly, regardless
+ * of the amount of memory in the system. Once a method of determining
+ * what version of DINK initializes the system for us, if applicable, is
+ * found, we can hopefully stop hardcoding 32MB of RAM.
+ */
+
+#include <linux/config.h>
+#include <linux/stddef.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/errno.h>
+#include <linux/reboot.h>
+#include <linux/pci.h>
+#include <linux/kdev_t.h>
+#include <linux/major.h>
+#include <linux/initrd.h>
+#include <linux/console.h>
+#include <linux/delay.h>
+#include <linux/ide.h>
+#include <linux/seq_file.h>
+#include <linux/root_dev.h>
+#include <linux/serial.h>
+#include <linux/tty.h> /* for linux/serial_core.h */
+#include <linux/serial_core.h>
+#include <linux/serial_8250.h>
+
+#include <asm/system.h>
+#include <asm/pgtable.h>
+#include <asm/page.h>
+#include <asm/time.h>
+#include <asm/dma.h>
+#include <asm/io.h>
+#include <asm/machdep.h>
+#include <asm/prom.h>
+#include <asm/smp.h>
+#include <asm/vga.h>
+#include <asm/open_pic.h>
+#include <asm/i8259.h>
+#include <asm/todc.h>
+#include <asm/bootinfo.h>
+#include <asm/mpc10x.h>
+#include <asm/pci-bridge.h>
+/*
+#include <asm/kgdb.h>
+*/
+#include <asm/ppc_sys.h>
+#include <asm/mpic.h>
+
+static void sandpoint_halt(void);
+
+/*
+ * Define all of the IRQ senses and polarities. Taken from the
+ * Sandpoint X3 User's manual.
+ */
+static u_char sandpoint_openpic_initsenses[] __initdata = {
+ (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* 0: SIOINT */
+ (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* 1: XXXX FILL?? */
+ (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* 2: PCI Slot 1 */
+ (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* 3: PCI Slot 2 */
+ (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* 4: PCI Slot 3 */
+ (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* 5: PCI Slot 4 */
+ (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* 6: XXXX FILL?? */
+ (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* 7: XXXX FILL?? */
+ (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* 8: IDE (INT C) */
+ (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* 9: IDE (INT D) */
+ (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* 10: */
+ (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* 11: */
+ (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* 12: */
+ (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* 13: */
+ (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* 14: */
+ (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* 15: */
+};
+
+/*
+ * Interrupt setup and service. Interrrupts on the Sandpoint come
+ * from the four PCI slots plus the 8259 in the Winbond Super I/O (SIO).
+ * The 8259 is cascaded from EPIC IRQ0, IRQ1-4 map to PCI slots 1-4,
+ * IDE is on EPIC 7 and 8.
+ */
+static void __init
+sandpoint_init_IRQ(void)
+{
+ struct mpic *mpic;
+ phys_addr_t openpic_paddr = 0xfc000000 + /* XXXX */
+ MPC10X_EUMB_EPIC_OFFSET;
+
+ /* This doesn't handle i2c, dma, i2o, or timer intrs right now XXXX */
+ mpic = mpic_alloc(openpic_paddr, MPIC_PRIMARY,
+ 16 /* XXXX otherwise num_sources used */ ,
+ NUM_8259_INTERRUPTS,
+ 0, /* was 16 */
+ NR_IRQS - 4 /* XXXX */,
+ sandpoint_openpic_initsenses,
+ sizeof(sandpoint_openpic_initsenses), " EPIC ");
+
+ BUG_ON(mpic == NULL); /* XXXX */
+ mpic_assign_isu(mpic, 0, openpic_paddr + 0x10200);
+ mpic_init(mpic);
+ mpic_setup_cascade(NUM_8259_INTERRUPTS, i8259_irq_cascade, NULL);
+ i8259_init(0xfef00000, 0); /* pci iack addr */
+}
+
+/*
+ * Freescale Sandpoint interrupt routing.
+ */
+static inline int
+x3_map_irq(struct pci_dev *dev, unsigned char idsel, unsigned char pin)
+{
+ static char pci_irq_table[][4] =
+ /*
+ * PCI IDSEL/INTPIN->INTLINE
+ * A B C D
+ */
+ {
+ { 16, 0, 0, 0 }, /* IDSEL 11 - i8259 on Winbond */
+ { 0, 0, 0, 0 }, /* IDSEL 12 - unused */
+ { 18, 21, 20, 19 }, /* IDSEL 13 - PCI slot 1 */
+ { 19, 18, 21, 20 }, /* IDSEL 14 - PCI slot 2 */
+ { 20, 19, 18, 21 }, /* IDSEL 15 - PCI slot 3 */
+ { 21, 20, 19, 18 }, /* IDSEL 16 - PCI slot 4 */
+ };
+
+ const long min_idsel = 11, max_idsel = 16, irqs_per_slot = 4;
+ return PCI_IRQ_TABLE_LOOKUP;
+}
+
+static void __init
+sandpoint_setup_winbond_83553(struct pci_controller *hose)
+{
+ int devfn;
+
+ /*
+ * Route IDE interrupts directly to the 8259's IRQ 14 & 15.
+ * We can't route the IDE interrupt to PCI INTC# or INTD# because those
+ * woule interfere with the PMC's INTC# and INTD# lines.
+ */
+ /*
+ * Winbond Fcn 0
+ */
+ devfn = PCI_DEVFN(11,0);
+
+ early_write_config_byte(hose,
+ 0,
+ devfn,
+ 0x43, /* IDE Interrupt Routing Control */
+ 0xef);
+ early_write_config_word(hose,
+ 0,
+ devfn,
+ 0x44, /* PCI Interrupt Routing Control */
+ 0x0000);
+
+ /* Want ISA memory cycles to be forwarded to PCI bus */
+ early_write_config_byte(hose,
+ 0,
+ devfn,
+ 0x48, /* ISA-to-PCI Addr Decoder Control */
+ 0xf0);
+
+ /* Enable Port 92. */
+ early_write_config_byte(hose,
+ 0,
+ devfn,
+ 0x4e, /* AT System Control Register */
+ 0x06);
+ /*
+ * Winbond Fcn 1
+ */
+ devfn = PCI_DEVFN(11,1);
+
+ /* Put IDE controller into native mode. */
+ early_write_config_byte(hose,
+ 0,
+ devfn,
+ 0x09, /* Programming interface Register */
+ 0x8f);
+
+ /* Init IRQ routing, enable both ports, disable fast 16 */
+ early_write_config_dword(hose,
+ 0,
+ devfn,
+ 0x40, /* IDE Control/Status Register */
+ 0x00ff0011);
+ return;
+}
+
+static int
+x3_exclude_device(u_char bus, u_char devfn)
+{
+ if ((bus == 0) && (PCI_SLOT(devfn) == 0))
+ return PCIBIOS_DEVICE_NOT_FOUND;
+ else
+ return PCIBIOS_SUCCESSFUL;
+}
+
+static int __init
+add_bridge(struct device_node *dev)
+{
+ int len;
+ struct pci_controller *hose;
+ int *bus_range;
+
+ printk("Adding PCI host bridge %s\n", dev->full_name);
+
+ bus_range = (int *) get_property(dev, "bus-range", &len);
+ if (bus_range == NULL || len < 2 * sizeof(int))
+ printk(KERN_WARNING "Can't get bus-range for %s, assume"
+ " bus 0\n", dev->full_name);
+
+ hose = pcibios_alloc_controller();
+ if (hose == NULL)
+ return -ENOMEM;
+ hose->first_busno = bus_range ? bus_range[0] : 0;
+ hose->last_busno = bus_range ? bus_range[1] : 0xff;
+ setup_indirect_pci(hose, 0xfec00000, 0xfee00000);
+
+ /* Interpret the "ranges" property */
+ /* This also maps the I/O region and sets isa_io/mem_base */
+ pci_process_bridge_OF_ranges(hose, dev, 1);
+
+ return 0;
+}
+
+u32 mag_dbg = 0;
+
+static void __init
+sandpoint_setup_arch(void)
+{
+ loops_per_jiffy = 100000000 / HZ;
+ isa_io_base = 0xfe000000;
+ isa_mem_base = 0x80000000;
+ pci_dram_offset = 0;
+
+#ifdef CONFIG_BLK_DEV_INITRD
+ if (initrd_start)
+ ROOT_DEV = Root_RAM0;
+ else
+#endif
+#ifdef CONFIG_ROOT_NFS
+ ROOT_DEV = Root_NFS;
+#else
+ ROOT_DEV = Root_HDA1;
+#endif
+
+
+#if 1 /* XXXX NEW */
+ {
+ struct device_node *np;
+
+ mag_dbg = 1;
+ for (np = NULL; (np = of_find_node_by_type(np, "pci")) != NULL;)
+ add_bridge(np);
+ mag_dbg = 0;
+
+ ppc_md.pci_swizzle = common_swizzle;
+ ppc_md.pci_map_irq = x3_map_irq;
+ ppc_md.pci_exclude_device = x3_exclude_device; /* XXXX */
+ }
+#endif
+
+#if 0 /* XXXX */
+ if (strncmp (cur_ppc_sys_spec->ppc_sys_name, "8245", 4) == 0)
+ {
+ bd_t *bp = (bd_t *)__res;
+ struct plat_serial8250_port *pdata;
+
+ pdata = (struct plat_serial8250_port *) ppc_sys_get_pdata(MPC10X_UART0);
+ if (pdata)
+ {
+ pdata[0].uartclk = bp->bi_busfreq;
+ }
+
+#ifdef CONFIG_SANDPOINT_ENABLE_UART1
+ pdata = (struct plat_serial8250_port *) ppc_sys_get_pdata(MPC10X_UART1);
+ if (pdata)
+ {
+ pdata[0].uartclk = bp->bi_busfreq;
+ }
+#else
+ ppc_sys_device_remove(MPC10X_UART1);
+#endif
+ }
+#endif
+
+ printk(KERN_INFO "Freescale Sandpoint Test Platform\n");
+ printk(KERN_INFO "Port by MontaVista Software, Inc. (source@mvista.com)\n");
+
+ /* DINK32 12.3 and below do not correctly enable any caches.
+ * We will do this now with good known values. Future versions
+ * of DINK32 are supposed to get this correct.
+ */
+ if (cpu_has_feature(CPU_FTR_SPEC7450))
+ /* 745x is different. We only want to pass along enable. */
+ _set_L2CR(L2CR_L2E);
+ else if (cpu_has_feature(CPU_FTR_L2CR))
+ /* All modules have 1MB of L2. We also assume that an
+ * L2 divisor of 3 will work.
+ */
+ _set_L2CR(L2CR_L2E | L2CR_L2SIZ_1MB | L2CR_L2CLK_DIV3
+ | L2CR_L2RAM_PIPE | L2CR_L2OH_1_0 | L2CR_L2DF);
+#if 0
+ /* Untested right now. */
+ if (cpu_has_feature(CPU_FTR_L3CR)) {
+ /* Magic value. */
+ _set_L3CR(0x8f032000);
+ }
+#endif
+}
+
+#if 1 /* XXXX */
+#define SANDPOINT_87308_CFG_ADDR 0x15c
+#define SANDPOINT_87308_CFG_DATA 0x15d
+
+#define SANDPOINT_87308_CFG_INB(addr, byte) { \
+ outb((addr), SANDPOINT_87308_CFG_ADDR); \
+ (byte) = inb(SANDPOINT_87308_CFG_DATA); \
+}
+
+#define SANDPOINT_87308_CFG_OUTB(addr, byte) { \
+ outb((addr), SANDPOINT_87308_CFG_ADDR); \
+ outb((byte), SANDPOINT_87308_CFG_DATA); \
+}
+
+#define SANDPOINT_87308_SELECT_DEV(dev_num) { \
+ SANDPOINT_87308_CFG_OUTB(0x07, (dev_num)); \
+}
+
+#define SANDPOINT_87308_DEV_ENABLE(dev_num) { \
+ SANDPOINT_87308_SELECT_DEV(dev_num); \
+ SANDPOINT_87308_CFG_OUTB(0x30, 0x01); \
+}
+
+/*
+ * Fix IDE interrupts.
+ */
+static int __init
+sandpoint_fix_winbond_83553(void)
+{
+ /* Make some 8259 interrupt level sensitive */
+ outb(0xe0, 0x4d0);
+ outb(0xde, 0x4d1);
+
+ return 0;
+}
+
+arch_initcall(sandpoint_fix_winbond_83553);
+
+/*
+ * Initialize the ISA devices on the Nat'l PC87308VUL SuperIO chip.
+ */
+static int __init
+sandpoint_setup_natl_87308(void)
+{
+ u_char reg;
+
+ /*
+ * Enable all the devices on the Super I/O chip.
+ */
+ SANDPOINT_87308_SELECT_DEV(0x00); /* Select kbd logical device */
+ SANDPOINT_87308_CFG_OUTB(0xf0, 0x00); /* Set KBC clock to 8 Mhz */
+ SANDPOINT_87308_DEV_ENABLE(0x00); /* Enable keyboard */
+ SANDPOINT_87308_DEV_ENABLE(0x01); /* Enable mouse */
+ SANDPOINT_87308_DEV_ENABLE(0x02); /* Enable rtc */
+ SANDPOINT_87308_DEV_ENABLE(0x03); /* Enable fdc (floppy) */
+ SANDPOINT_87308_DEV_ENABLE(0x04); /* Enable parallel */
+ SANDPOINT_87308_DEV_ENABLE(0x05); /* Enable UART 2 */
+ SANDPOINT_87308_CFG_OUTB(0xf0, 0x82); /* Enable bank select regs */
+ SANDPOINT_87308_DEV_ENABLE(0x06); /* Enable UART 1 */
+ SANDPOINT_87308_CFG_OUTB(0xf0, 0x82); /* Enable bank select regs */
+
+ /* Set up floppy in PS/2 mode */
+ outb(0x09, SIO_CONFIG_RA);
+ reg = inb(SIO_CONFIG_RD);
+ reg = (reg & 0x3F) | 0x40;
+ outb(reg, SIO_CONFIG_RD);
+ outb(reg, SIO_CONFIG_RD); /* Have to write twice to change! */
+
+ return 0;
+}
+
+arch_initcall(sandpoint_setup_natl_87308);
+
+static int __init
+sandpoint_request_io(void)
+{
+ request_region(0x00,0x20,"dma1");
+ request_region(0x20,0x20,"pic1");
+ request_region(0x40,0x20,"timer");
+ request_region(0x80,0x10,"dma page reg");
+ request_region(0xa0,0x20,"pic2");
+ request_region(0xc0,0x20,"dma2");
+
+ return 0;
+}
+
+arch_initcall(sandpoint_request_io);
+#endif
+
+static void __init
+sandpoint_map_io(void)
+{
+ io_block_mapping(0xfe000000, 0xfe000000, 0x02000000, _PAGE_IO);
+}
+
+static void
+sandpoint_restart(char *cmd)
+{
+ local_irq_disable();
+
+ /* Set exception prefix high - to the firmware */
+ _nmask_and_or_msr(0, MSR_IP);
+
+ /* Reset system via Port 92 */
+ outb(0x00, 0x92);
+ outb(0x01, 0x92);
+
+ for(;;); /* Spin until reset happens */
+}
+
+static void
+sandpoint_power_off(void)
+{
+ local_irq_disable();
+ for(;;); /* No way to shut power off with software */
+ /* NOTREACHED */
+}
+
+static void
+sandpoint_halt(void)
+{
+ sandpoint_power_off();
+ /* NOTREACHED */
+}
+
+static void
+sandpoint_show_cpuinfo(struct seq_file *m)
+{
+ seq_printf(m, "vendor\t\t: Freescale\n");
+ seq_printf(m, "machine\t\t: Sandpoint\n");
+}
+
+#if 0 /* XXXX */
+#if defined(CONFIG_BLK_DEV_IDE) || defined(CONFIG_BLK_DEV_IDE_MODULE)
+/*
+ * IDE support.
+ */
+static int sandpoint_ide_ports_known = 0;
+static unsigned long sandpoint_ide_regbase[MAX_HWIFS];
+static unsigned long sandpoint_ide_ctl_regbase[MAX_HWIFS];
+static unsigned long sandpoint_idedma_regbase;
+
+static void
+sandpoint_ide_probe(void)
+{
+ struct pci_dev *pdev = pci_get_device(PCI_VENDOR_ID_WINBOND,
+ PCI_DEVICE_ID_WINBOND_82C105, NULL);
+
+ if (pdev) {
+ sandpoint_ide_regbase[0]=pdev->resource[0].start;
+ sandpoint_ide_regbase[1]=pdev->resource[2].start;
+ sandpoint_ide_ctl_regbase[0]=pdev->resource[1].start;
+ sandpoint_ide_ctl_regbase[1]=pdev->resource[3].start;
+ sandpoint_idedma_regbase=pdev->resource[4].start;
+ pci_dev_put(pdev);
+ }
+
+ sandpoint_ide_ports_known = 1;
+}
+
+static int
+sandpoint_ide_default_irq(unsigned long base)
+{
+ if (sandpoint_ide_ports_known == 0)
+ sandpoint_ide_probe();
+
+ if (base == sandpoint_ide_regbase[0])
+ return SANDPOINT_IDE_INT0;
+ else if (base == sandpoint_ide_regbase[1])
+ return SANDPOINT_IDE_INT1;
+ else
+ return 0;
+}
+
+static unsigned long
+sandpoint_ide_default_io_base(int index)
+{
+ if (sandpoint_ide_ports_known == 0)
+ sandpoint_ide_probe();
+
+ return sandpoint_ide_regbase[index];
+}
+
+static void __init
+sandpoint_ide_init_hwif_ports(hw_regs_t *hw, unsigned long data_port,
+ unsigned long ctrl_port, int *irq)
+{
+ unsigned long reg = data_port;
+ uint alt_status_base;
+ int i;
+
+ for (i = IDE_DATA_OFFSET; i <= IDE_STATUS_OFFSET; i++) {
+ hw->io_ports[i] = reg++;
+ }
+
+ if (data_port == sandpoint_ide_regbase[0]) {
+ alt_status_base = sandpoint_ide_ctl_regbase[0] + 2;
+ hw->irq = 14;
+ }
+ else if (data_port == sandpoint_ide_regbase[1]) {
+ alt_status_base = sandpoint_ide_ctl_regbase[1] + 2;
+ hw->irq = 15;
+ }
+ else {
+ alt_status_base = 0;
+ hw->irq = 0;
+ }
+
+ if (ctrl_port) {
+ hw->io_ports[IDE_CONTROL_OFFSET] = ctrl_port;
+ } else {
+ hw->io_ports[IDE_CONTROL_OFFSET] = alt_status_base;
+ }
+
+ if (irq != NULL) {
+ *irq = hw->irq;
+ }
+}
+#endif
+#endif
+
+/*
+ * Set BAT 3 to map 0xf8000000 to end of physical memory space 1-to-1.
+ */
+static __inline__ void
+sandpoint_set_bat(void)
+{
+ unsigned long bat3u, bat3l;
+
+ __asm__ __volatile__(
+ " lis %0,0xf800\n \
+ ori %1,%0,0x002a\n \
+ ori %0,%0,0x0ffe\n \
+ mtspr 0x21e,%0\n \
+ mtspr 0x21f,%1\n \
+ isync\n \
+ sync "
+ : "=r" (bat3u), "=r" (bat3l));
+}
+
+TODC_ALLOC();
+
+static int __init
+sandpoint_probe(void)
+{
+ return 1;
+}
+
+define_machine(sandpoint) {
+ .name = "Sandpoint",
+ .probe = sandpoint_probe,
+ .setup_arch = sandpoint_setup_arch,
+ .setup_io_mappings = sandpoint_map_io,
+ .init_IRQ = sandpoint_init_IRQ,
+ .show_cpuinfo = sandpoint_show_cpuinfo,
+ .get_irq = mpic_get_irq,
+ .restart = sandpoint_restart,
+ .power_off = sandpoint_power_off,
+ .halt = sandpoint_halt,
+ .calibrate_decr = generic_calibrate_decr,
+ /*
+ .progress = udbg_progress,
+ */
+};
diff --git a/arch/powerpc/sysdev/mpic.c b/arch/powerpc/sysdev/mpic.c
index 7dcdfcb..1badbec 100644
--- a/arch/powerpc/sysdev/mpic.c
+++ b/arch/powerpc/sysdev/mpic.c
@@ -629,6 +629,13 @@ #endif /* CONFIG_SMP */
mb();
}
+#ifdef CONFIG_MPIC_SERIAL
+ /* For serial interrupts & set clock ratio */
+ mpic_write(mpic->gregs, MPIC_GREG_GLOBAL_CONF_1,
+ mpic_read(mpic->gregs, MPIC_GREG_GLOBAL_CONF_1)
+ | (1<<27) | (0x7<<28));
+#endif
+
/* Read feature register, calculate num CPUs and, for non-ISU
* MPICs, num sources as well. On ISU MPICs, sources are counted
* as ISUs are added
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox