From b445e26cbf784cdba10f2b6c3e2cd3ee7bab360a Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Mon, 27 Jun 2005 15:42:04 -0700 Subject: [SPARC64]: Avoid membar instructions in delay slots. In particular, avoid membar instructions in the delay slot of a jmpl instruction. UltraSPARC-I, II, IIi, and IIe have a bug, documented in the UltraSPARC-IIi User's Manual, Appendix K, Erratum 51 The long and short of it is that if the IMU unit misses on a branch or jmpl, and there is a store buffer synchronizing membar in the delay slot, the chip can stop fetching instructions. If interrupts are enabled or some other trap is enabled, the chip will unwedge itself, but performance will suffer. We already had a workaround for this bug in a few spots, but it's better to have the entire tree sanitized for this rule. Signed-off-by: David S. Miller --- include/asm-sparc64/rwsem.h | 3 ++- include/asm-sparc64/spinlock.h | 29 +++++++++++++++++++---------- include/asm-sparc64/spitfire.h | 1 - 3 files changed, 21 insertions(+), 12 deletions(-) (limited to 'include') diff --git a/include/asm-sparc64/rwsem.h b/include/asm-sparc64/rwsem.h index bf2ae90ed3d..a1cc94f9598 100644 --- a/include/asm-sparc64/rwsem.h +++ b/include/asm-sparc64/rwsem.h @@ -55,8 +55,9 @@ static __inline__ int rwsem_atomic_update(int delta, struct rw_semaphore *sem) "add %%g1, %1, %%g7\n\t" "cas [%2], %%g1, %%g7\n\t" "cmp %%g1, %%g7\n\t" + "membar #StoreLoad | #StoreStore\n\t" "bne,pn %%icc, 1b\n\t" - " membar #StoreLoad | #StoreStore\n\t" + " nop\n\t" "mov %%g7, %0\n\t" : "=&r" (tmp) : "0" (tmp), "r" (sem) diff --git a/include/asm-sparc64/spinlock.h b/include/asm-sparc64/spinlock.h index db7581bdb53..9cb93a5c2b4 100644 --- a/include/asm-sparc64/spinlock.h +++ b/include/asm-sparc64/spinlock.h @@ -52,12 +52,14 @@ static inline void _raw_spin_lock(spinlock_t *lock) __asm__ __volatile__( "1: ldstub [%1], %0\n" +" membar #StoreLoad | #StoreStore\n" " brnz,pn %0, 2f\n" -" membar #StoreLoad | #StoreStore\n" +" nop\n" " .subsection 2\n" "2: ldub [%1], %0\n" +" membar #LoadLoad\n" " brnz,pt %0, 2b\n" -" membar #LoadLoad\n" +" nop\n" " ba,a,pt %%xcc, 1b\n" " .previous" : "=&r" (tmp) @@ -95,16 +97,18 @@ static inline void _raw_spin_lock_flags(spinlock_t *lock, unsigned long flags) __asm__ __volatile__( "1: ldstub [%2], %0\n" -" brnz,pn %0, 2f\n" " membar #StoreLoad | #StoreStore\n" +" brnz,pn %0, 2f\n" +" nop\n" " .subsection 2\n" "2: rdpr %%pil, %1\n" " wrpr %3, %%pil\n" "3: ldub [%2], %0\n" -" brnz,pt %0, 3b\n" " membar #LoadLoad\n" +" brnz,pt %0, 3b\n" +" nop\n" " ba,pt %%xcc, 1b\n" -" wrpr %1, %%pil\n" +" wrpr %1, %%pil\n" " .previous" : "=&r" (tmp1), "=&r" (tmp2) : "r"(lock), "r"(flags) @@ -162,12 +166,14 @@ static void inline __read_lock(rwlock_t *lock) "4: add %0, 1, %1\n" " cas [%2], %0, %1\n" " cmp %0, %1\n" +" membar #StoreLoad | #StoreStore\n" " bne,pn %%icc, 1b\n" -" membar #StoreLoad | #StoreStore\n" +" nop\n" " .subsection 2\n" "2: ldsw [%2], %0\n" +" membar #LoadLoad\n" " brlz,pt %0, 2b\n" -" membar #LoadLoad\n" +" nop\n" " ba,a,pt %%xcc, 4b\n" " .previous" : "=&r" (tmp1), "=&r" (tmp2) @@ -204,12 +210,14 @@ static void inline __write_lock(rwlock_t *lock) "4: or %0, %3, %1\n" " cas [%2], %0, %1\n" " cmp %0, %1\n" +" membar #StoreLoad | #StoreStore\n" " bne,pn %%icc, 1b\n" -" membar #StoreLoad | #StoreStore\n" +" nop\n" " .subsection 2\n" "2: lduw [%2], %0\n" +" membar #LoadLoad\n" " brnz,pt %0, 2b\n" -" membar #LoadLoad\n" +" nop\n" " ba,a,pt %%xcc, 4b\n" " .previous" : "=&r" (tmp1), "=&r" (tmp2) @@ -240,8 +248,9 @@ static int inline __write_trylock(rwlock_t *lock) " or %0, %4, %1\n" " cas [%3], %0, %1\n" " cmp %0, %1\n" +" membar #StoreLoad | #StoreStore\n" " bne,pn %%icc, 1b\n" -" membar #StoreLoad | #StoreStore\n" +" nop\n" " mov 1, %2\n" "2:" : "=&r" (tmp1), "=&r" (tmp2), "=&r" (result) diff --git a/include/asm-sparc64/spitfire.h b/include/asm-sparc64/spitfire.h index 9d7613eea81..1aa932773af 100644 --- a/include/asm-sparc64/spitfire.h +++ b/include/asm-sparc64/spitfire.h @@ -111,7 +111,6 @@ static __inline__ void spitfire_put_dcache_tag(unsigned long addr, unsigned long "membar #Sync" : /* No outputs */ : "r" (tag), "r" (addr), "i" (ASI_DCACHE_TAG)); - __asm__ __volatile__ ("membar #Sync" : : : "memory"); } /* The instruction cache lines are flushed with this, but note that -- cgit v1.2.3-70-g09d2 From 63b614522cba5a015923c0e8f284be6e01c13f1a Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Mon, 27 Jun 2005 17:04:45 -0700 Subject: [SPARC64]: Get rid of fast IRQ feature. The only real user was the assembler floppy interrupt handler, which does not need to be in assembly. This makes it so that there are less pieces of code which know about the internal layout of ivector_table[] and friends. Signed-off-by: David S. Miller --- arch/sparc64/kernel/auxio.c | 2 +- arch/sparc64/kernel/entry.S | 110 ----------------------- arch/sparc64/kernel/irq.c | 171 +++++++++++------------------------- arch/sparc64/kernel/sparc64_ksyms.c | 1 - include/asm-sparc64/auxio.h | 2 + include/asm-sparc64/floppy.h | 16 ++-- include/asm-sparc64/irq.h | 7 +- 7 files changed, 62 insertions(+), 247 deletions(-) (limited to 'include') diff --git a/arch/sparc64/kernel/auxio.c b/arch/sparc64/kernel/auxio.c index a0716ccc2f4..8852c20c8d9 100644 --- a/arch/sparc64/kernel/auxio.c +++ b/arch/sparc64/kernel/auxio.c @@ -16,7 +16,7 @@ #include #include -/* This cannot be static, as it is referenced in entry.S */ +/* This cannot be static, as it is referenced in irq.c */ void __iomem *auxio_register = NULL; enum auxio_type { diff --git a/arch/sparc64/kernel/entry.S b/arch/sparc64/kernel/entry.S index ffe717ab7f8..eee516a71c1 100644 --- a/arch/sparc64/kernel/entry.S +++ b/arch/sparc64/kernel/entry.S @@ -701,116 +701,6 @@ utrap_ill: ba,pt %xcc, rtrap clr %l6 -#ifdef CONFIG_BLK_DEV_FD - .globl floppy_hardint -floppy_hardint: - wr %g0, (1 << 11), %clear_softint - sethi %hi(doing_pdma), %g1 - ld [%g1 + %lo(doing_pdma)], %g2 - brz,pn %g2, floppy_dosoftint - sethi %hi(fdc_status), %g3 - ldx [%g3 + %lo(fdc_status)], %g3 - sethi %hi(pdma_vaddr), %g5 - ldx [%g5 + %lo(pdma_vaddr)], %g4 - sethi %hi(pdma_size), %g5 - ldx [%g5 + %lo(pdma_size)], %g5 - -next_byte: - lduba [%g3] ASI_PHYS_BYPASS_EC_E, %g7 - andcc %g7, 0x80, %g0 - be,pn %icc, floppy_fifo_emptied - andcc %g7, 0x20, %g0 - be,pn %icc, floppy_overrun - andcc %g7, 0x40, %g0 - be,pn %icc, floppy_write - sub %g5, 1, %g5 - - inc %g3 - lduba [%g3] ASI_PHYS_BYPASS_EC_E, %g7 - dec %g3 - orcc %g0, %g5, %g0 - stb %g7, [%g4] - bne,pn %xcc, next_byte - add %g4, 1, %g4 - - b,pt %xcc, floppy_tdone - nop - -floppy_write: - ldub [%g4], %g7 - orcc %g0, %g5, %g0 - inc %g3 - stba %g7, [%g3] ASI_PHYS_BYPASS_EC_E - dec %g3 - bne,pn %xcc, next_byte - add %g4, 1, %g4 - -floppy_tdone: - sethi %hi(pdma_vaddr), %g1 - stx %g4, [%g1 + %lo(pdma_vaddr)] - sethi %hi(pdma_size), %g1 - stx %g5, [%g1 + %lo(pdma_size)] - sethi %hi(auxio_register), %g1 - ldx [%g1 + %lo(auxio_register)], %g7 - lduba [%g7] ASI_PHYS_BYPASS_EC_E, %g5 - or %g5, AUXIO_AUX1_FTCNT, %g5 -/* andn %g5, AUXIO_AUX1_MASK, %g5 */ - stba %g5, [%g7] ASI_PHYS_BYPASS_EC_E - andn %g5, AUXIO_AUX1_FTCNT, %g5 -/* andn %g5, AUXIO_AUX1_MASK, %g5 */ - - nop; nop; nop; nop; nop; nop; - nop; nop; nop; nop; nop; nop; - - stba %g5, [%g7] ASI_PHYS_BYPASS_EC_E - sethi %hi(doing_pdma), %g1 - b,pt %xcc, floppy_dosoftint - st %g0, [%g1 + %lo(doing_pdma)] - -floppy_fifo_emptied: - sethi %hi(pdma_vaddr), %g1 - stx %g4, [%g1 + %lo(pdma_vaddr)] - sethi %hi(pdma_size), %g1 - stx %g5, [%g1 + %lo(pdma_size)] - sethi %hi(irq_action), %g1 - or %g1, %lo(irq_action), %g1 - ldx [%g1 + (11 << 3)], %g3 ! irqaction[floppy_irq] - ldx [%g3 + 0x08], %g4 ! action->flags>>48==ino - sethi %hi(ivector_table), %g3 - srlx %g4, 48, %g4 - or %g3, %lo(ivector_table), %g3 - sllx %g4, 5, %g4 - ldx [%g3 + %g4], %g4 ! &ivector_table[ino] - ldx [%g4 + 0x10], %g4 ! bucket->iclr - stwa %g0, [%g4] ASI_PHYS_BYPASS_EC_E ! ICLR_IDLE - membar #Sync ! probably not needed... - retry - -floppy_overrun: - sethi %hi(pdma_vaddr), %g1 - stx %g4, [%g1 + %lo(pdma_vaddr)] - sethi %hi(pdma_size), %g1 - stx %g5, [%g1 + %lo(pdma_size)] - sethi %hi(doing_pdma), %g1 - st %g0, [%g1 + %lo(doing_pdma)] - -floppy_dosoftint: - rdpr %pil, %g2 - wrpr %g0, 15, %pil - sethi %hi(109f), %g7 - b,pt %xcc, etrap_irq -109: or %g7, %lo(109b), %g7 - - mov 11, %o0 - mov 0, %o1 - call sparc_floppy_irq - add %sp, PTREGS_OFF, %o2 - - b,pt %xcc, rtrap_irq - nop - -#endif /* CONFIG_BLK_DEV_FD */ - /* XXX Here is stuff we still need to write... -DaveM XXX */ .globl netbsd_syscall netbsd_syscall: diff --git a/arch/sparc64/kernel/irq.c b/arch/sparc64/kernel/irq.c index 4dcb8af9409..42471257730 100644 --- a/arch/sparc64/kernel/irq.c +++ b/arch/sparc64/kernel/irq.c @@ -37,6 +37,7 @@ #include #include #include +#include #ifdef CONFIG_SMP static void distribute_irqs(void); @@ -834,137 +835,65 @@ void handler_irq(int irq, struct pt_regs *regs) } #ifdef CONFIG_BLK_DEV_FD -extern void floppy_interrupt(int irq, void *dev_cookie, struct pt_regs *regs); +extern irqreturn_t floppy_interrupt(int, void *, struct pt_regs *);; -void sparc_floppy_irq(int irq, void *dev_cookie, struct pt_regs *regs) -{ - struct irqaction *action = *(irq + irq_action); - struct ino_bucket *bucket; - int cpu = smp_processor_id(); - - irq_enter(); - kstat_this_cpu.irqs[irq]++; - - *(irq_work(cpu, irq)) = 0; - bucket = get_ino_in_irqaction(action) + ivector_table; - - bucket->flags |= IBF_INPROGRESS; - - floppy_interrupt(irq, dev_cookie, regs); - upa_writel(ICLR_IDLE, bucket->iclr); - - bucket->flags &= ~IBF_INPROGRESS; - - irq_exit(); -} -#endif - -/* The following assumes that the branch lies before the place we - * are branching to. This is the case for a trap vector... - * You have been warned. - */ -#define SPARC_BRANCH(dest_addr, inst_addr) \ - (0x10800000 | ((((dest_addr)-(inst_addr))>>2)&0x3fffff)) - -#define SPARC_NOP (0x01000000) +/* XXX No easy way to include asm/floppy.h XXX */ +extern unsigned char *pdma_vaddr; +extern unsigned long pdma_size; +extern volatile int doing_pdma; +extern unsigned long fdc_status; -static void install_fast_irq(unsigned int cpu_irq, - irqreturn_t (*handler)(int, void *, struct pt_regs *)) +irqreturn_t sparc_floppy_irq(int irq, void *dev_cookie, struct pt_regs *regs) { - extern unsigned long sparc64_ttable_tl0; - unsigned long ttent = (unsigned long) &sparc64_ttable_tl0; - unsigned int *insns; - - ttent += 0x820; - ttent += (cpu_irq - 1) << 5; - insns = (unsigned int *) ttent; - insns[0] = SPARC_BRANCH(((unsigned long) handler), - ((unsigned long)&insns[0])); - insns[1] = SPARC_NOP; - __asm__ __volatile__("membar #StoreStore; flush %0" : : "r" (ttent)); -} - -int request_fast_irq(unsigned int irq, - irqreturn_t (*handler)(int, void *, struct pt_regs *), - unsigned long irqflags, const char *name, void *dev_id) -{ - struct irqaction *action; - struct ino_bucket *bucket = __bucket(irq); - unsigned long flags; - - /* No pil0 dummy buckets allowed here. */ - if (bucket < &ivector_table[0] || - bucket >= &ivector_table[NUM_IVECS]) { - unsigned int *caller; - - __asm__ __volatile__("mov %%i7, %0" : "=r" (caller)); - printk(KERN_CRIT "request_fast_irq: Old style IRQ registry attempt " - "from %p, irq %08x.\n", caller, irq); - return -EINVAL; - } - - if (!handler) - return -EINVAL; + if (likely(doing_pdma)) { + void __iomem *stat = (void __iomem *) fdc_status; + unsigned char *vaddr = pdma_vaddr; + unsigned long size = pdma_size; + u8 val; + + while (size) { + val = readb(stat); + if (unlikely(!(val & 0x80))) { + pdma_vaddr = vaddr; + pdma_size = size; + return IRQ_HANDLED; + } + if (unlikely(!(val & 0x20))) { + pdma_vaddr = vaddr; + pdma_size = size; + doing_pdma = 0; + goto main_interrupt; + } + if (val & 0x40) { + /* read */ + *vaddr++ = readb(stat + 1); + } else { + unsigned char data = *vaddr++; - if ((bucket->pil == 0) || (bucket->pil == 14)) { - printk("request_fast_irq: Trying to register shared IRQ 0 or 14.\n"); - return -EBUSY; - } + /* write */ + writeb(data, stat + 1); + } + size--; + } - spin_lock_irqsave(&irq_action_lock, flags); + pdma_vaddr = vaddr; + pdma_size = size; - action = *(bucket->pil + irq_action); - if (action) { - if (action->flags & SA_SHIRQ) - panic("Trying to register fast irq when already shared.\n"); - if (irqflags & SA_SHIRQ) - panic("Trying to register fast irq as shared.\n"); - printk("request_fast_irq: Trying to register yet already owned.\n"); - spin_unlock_irqrestore(&irq_action_lock, flags); - return -EBUSY; - } + /* Send Terminal Count pulse to floppy controller. */ + val = readb(auxio_register); + val |= AUXIO_AUX1_FTCNT; + writeb(val, auxio_register); + val &= AUXIO_AUX1_FTCNT; + writeb(val, auxio_register); - /* - * We do not check for SA_SAMPLE_RANDOM in this path. Neither do we - * support smp intr affinity in this path. - */ - if (irqflags & SA_STATIC_ALLOC) { - if (static_irq_count < MAX_STATIC_ALLOC) - action = &static_irqaction[static_irq_count++]; - else - printk("Request for IRQ%d (%s) SA_STATIC_ALLOC failed " - "using kmalloc\n", bucket->pil, name); - } - if (action == NULL) - action = (struct irqaction *)kmalloc(sizeof(struct irqaction), - GFP_ATOMIC); - if (!action) { - spin_unlock_irqrestore(&irq_action_lock, flags); - return -ENOMEM; + doing_pdma = 0; } - install_fast_irq(bucket->pil, handler); - bucket->irq_info = action; - bucket->flags |= IBF_ACTIVE; - - action->handler = handler; - action->flags = irqflags; - action->dev_id = NULL; - action->name = name; - action->next = NULL; - put_ino_in_irqaction(action, irq); - put_smpaff_in_irqaction(action, CPU_MASK_NONE); - - *(bucket->pil + irq_action) = action; - enable_irq(irq); - - spin_unlock_irqrestore(&irq_action_lock, flags); - -#ifdef CONFIG_SMP - distribute_irqs(); -#endif - return 0; +main_interrupt: + return floppy_interrupt(irq, dev_cookie, regs); } +EXPORT_SYMBOL(sparc_floppy_irq); +#endif /* We really don't need these at all on the Sparc. We only have * stubs here because they are exported to modules. diff --git a/arch/sparc64/kernel/sparc64_ksyms.c b/arch/sparc64/kernel/sparc64_ksyms.c index e78cc53594f..56cd96f4a5c 100644 --- a/arch/sparc64/kernel/sparc64_ksyms.c +++ b/arch/sparc64/kernel/sparc64_ksyms.c @@ -227,7 +227,6 @@ EXPORT_SYMBOL(__flush_dcache_range); EXPORT_SYMBOL(mostek_lock); EXPORT_SYMBOL(mstk48t02_regs); -EXPORT_SYMBOL(request_fast_irq); #ifdef CONFIG_SUN_AUXIO EXPORT_SYMBOL(auxio_set_led); EXPORT_SYMBOL(auxio_set_lte); diff --git a/include/asm-sparc64/auxio.h b/include/asm-sparc64/auxio.h index 5eb01dd4715..81a590a50a1 100644 --- a/include/asm-sparc64/auxio.h +++ b/include/asm-sparc64/auxio.h @@ -75,6 +75,8 @@ #ifndef __ASSEMBLY__ +extern void __iomem *auxio_register; + #define AUXIO_LTE_ON 1 #define AUXIO_LTE_OFF 0 diff --git a/include/asm-sparc64/floppy.h b/include/asm-sparc64/floppy.h index e071b4b4edf..49d49a28594 100644 --- a/include/asm-sparc64/floppy.h +++ b/include/asm-sparc64/floppy.h @@ -159,7 +159,7 @@ static void sun_82077_fd_outb(unsigned char value, unsigned long port) * underruns. If non-zero, doing_pdma encodes the direction of * the transfer for debugging. 1=read 2=write */ -char *pdma_vaddr; +unsigned char *pdma_vaddr; unsigned long pdma_size; volatile int doing_pdma = 0; @@ -209,8 +209,7 @@ static void sun_fd_enable_dma(void) pdma_areasize = pdma_size; } -/* Our low-level entry point in arch/sparc/kernel/entry.S */ -extern irqreturn_t floppy_hardint(int irq, void *unused, struct pt_regs *regs); +extern irqreturn_t sparc_floppy_irq(int, void *, struct pt_regs *); static int sun_fd_request_irq(void) { @@ -220,8 +219,8 @@ static int sun_fd_request_irq(void) if(!once) { once = 1; - error = request_fast_irq(FLOPPY_IRQ, floppy_hardint, - SA_INTERRUPT, "floppy", NULL); + error = request_irq(FLOPPY_IRQ, sparc_floppy_irq, + SA_INTERRUPT, "floppy", NULL); return ((error == 0) ? 0 : -1); } @@ -615,7 +614,7 @@ static unsigned long __init sun_floppy_init(void) struct linux_ebus *ebus; struct linux_ebus_device *edev = NULL; unsigned long config = 0; - unsigned long auxio_reg; + void __iomem *auxio_reg; for_each_ebus(ebus) { for_each_ebusdev(edev, ebus) { @@ -642,7 +641,7 @@ static unsigned long __init sun_floppy_init(void) /* Make sure the high density bit is set, some systems * (most notably Ultra5/Ultra10) come up with it clear. */ - auxio_reg = edev->resource[2].start; + auxio_reg = (void __iomem *) edev->resource[2].start; writel(readl(auxio_reg)|0x2, auxio_reg); sun_pci_ebus_dev = ebus->self; @@ -650,7 +649,8 @@ static unsigned long __init sun_floppy_init(void) spin_lock_init(&sun_pci_fd_ebus_dma.lock); /* XXX ioremap */ - sun_pci_fd_ebus_dma.regs = edev->resource[1].start; + sun_pci_fd_ebus_dma.regs = (void __iomem *) + edev->resource[1].start; if (!sun_pci_fd_ebus_dma.regs) return 0; diff --git a/include/asm-sparc64/irq.h b/include/asm-sparc64/irq.h index 3aef0ca6775..018e2e46082 100644 --- a/include/asm-sparc64/irq.h +++ b/include/asm-sparc64/irq.h @@ -19,7 +19,7 @@ /* You should not mess with this directly. That's the job of irq.c. * * If you make changes here, please update hand coded assembler of - * SBUS/floppy interrupt handler in entry.S -DaveM + * the vectored interrupt trap handler in entry.S -DaveM * * This is currently one DCACHE line, two buckets per L2 cache * line. Keep this in mind please. @@ -122,11 +122,6 @@ extern void enable_irq(unsigned int); extern unsigned int build_irq(int pil, int inofixup, unsigned long iclr, unsigned long imap); extern unsigned int sbus_build_irq(void *sbus, unsigned int ino); -extern int request_fast_irq(unsigned int irq, - irqreturn_t (*handler)(int, void *, struct pt_regs *), - unsigned long flags, __const__ char *devname, - void *dev_id); - static __inline__ void set_softint(unsigned long bits) { __asm__ __volatile__("wr %0, 0x0, %%set_softint" -- cgit v1.2.3-70-g09d2 From da9091ee3b5f9808c64abb925cefe7b100018614 Mon Sep 17 00:00:00 2001 From: Alan Cox Date: Mon, 27 Jun 2005 15:24:30 -0700 Subject: [PATCH] ide: it8212 backport for Bartlomiej IDE This lets you throw out the iteraid stuff that has ended up back in due to stupid goings on in the IDE world. Its the same heavily tested code shipped in Fedora/Red Hat products but without the other dependancies on the Bartlomiej IDE layer. Pre-requisite: the ide-disk patch I sent to handle pure LBA devices. Obviously you lose things like hot unplug with the Bartlomiej IDE layer at the moment but that won't matter to most users. The patch does the following - Add IT8211/12 to pci_ids.h - Add Makefile/Kconfig entry - Add it8212 driver No core IDE code is touched by this diff Embedded system testing and the ability to force raid mode off by David Howells Made possible by the ite reference code, documentation and also several clarifications and pieces of assistance provided by ITE themselves Signed-off-by: Alan Cox Acked-by: Bartlomiej Zolnierkiewicz Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/ide/Kconfig | 6 + drivers/ide/pci/Makefile | 1 + drivers/ide/pci/it821x.c | 812 +++++++++++++++++++++++++++++++++++++++++++++++ include/linux/pci_ids.h | 2 + 4 files changed, 821 insertions(+) create mode 100644 drivers/ide/pci/it821x.c (limited to 'include') diff --git a/drivers/ide/Kconfig b/drivers/ide/Kconfig index 0273f124a4f..5f33df47aa7 100644 --- a/drivers/ide/Kconfig +++ b/drivers/ide/Kconfig @@ -606,6 +606,12 @@ config BLK_DEV_IT8172 ; picture of the board at . +config BLK_DEV_IT821X + tristate "IT821X IDE support" + help + This driver adds support for the ITE 8211 IDE controller and the + IT 8212 IDE RAID controller in both RAID and pass-through mode. + config BLK_DEV_NS87415 tristate "NS87415 chipset support" help diff --git a/drivers/ide/pci/Makefile b/drivers/ide/pci/Makefile index 55e6e553e49..af46226c179 100644 --- a/drivers/ide/pci/Makefile +++ b/drivers/ide/pci/Makefile @@ -12,6 +12,7 @@ obj-$(CONFIG_BLK_DEV_HPT34X) += hpt34x.o obj-$(CONFIG_BLK_DEV_HPT366) += hpt366.o #obj-$(CONFIG_BLK_DEV_HPT37X) += hpt37x.o obj-$(CONFIG_BLK_DEV_IT8172) += it8172.o +obj-$(CONFIG_BLK_DEV_IT821X) += it821x.o obj-$(CONFIG_BLK_DEV_NS87415) += ns87415.o obj-$(CONFIG_BLK_DEV_OPTI621) += opti621.o obj-$(CONFIG_BLK_DEV_PDC202XX_OLD) += pdc202xx_old.o diff --git a/drivers/ide/pci/it821x.c b/drivers/ide/pci/it821x.c new file mode 100644 index 00000000000..e440036e651 --- /dev/null +++ b/drivers/ide/pci/it821x.c @@ -0,0 +1,812 @@ + +/* + * linux/drivers/ide/pci/it821x.c Version 0.09 December 2004 + * + * Copyright (C) 2004 Red Hat + * + * May be copied or modified under the terms of the GNU General Public License + * Based in part on the ITE vendor provided SCSI driver. + * + * Documentation available from + * http://www.ite.com.tw/pc/IT8212F_V04.pdf + * Some other documents are NDA. + * + * The ITE8212 isn't exactly a standard IDE controller. It has two + * modes. In pass through mode then it is an IDE controller. In its smart + * mode its actually quite a capable hardware raid controller disguised + * as an IDE controller. Smart mode only understands DMA read/write and + * identify, none of the fancier commands apply. The IT8211 is identical + * in other respects but lacks the raid mode. + * + * Errata: + * o Rev 0x10 also requires master/slave hold the same DMA timings and + * cannot do ATAPI MWDMA. + * o The identify data for raid volumes lacks CHS info (technically ok) + * but also fails to set the LBA28 and other bits. We fix these in + * the IDE probe quirk code. + * o If you write LBA48 sized I/O's (ie > 256 sector) in smart mode + * raid then the controller firmware dies + * o Smart mode without RAID doesn't clear all the necessary identify + * bits to reduce the command set to the one used + * + * This has a few impacts on the driver + * - In pass through mode we do all the work you would expect + * - In smart mode the clocking set up is done by the controller generally + * but we must watch the other limits and filter. + * - There are a few extra vendor commands that actually talk to the + * controller but only work PIO with no IRQ. + * + * Vendor areas of the identify block in smart mode are used for the + * timing and policy set up. Each HDD in raid mode also has a serial + * block on the disk. The hardware extra commands are get/set chip status, + * rebuild, get rebuild status. + * + * In Linux the driver supports pass through mode as if the device was + * just another IDE controller. If the smart mode is running then + * volumes are managed by the controller firmware and each IDE "disk" + * is a raid volume. Even more cute - the controller can do automated + * hotplug and rebuild. + * + * The pass through controller itself is a little demented. It has a + * flaw that it has a single set of PIO/MWDMA timings per channel so + * non UDMA devices restrict each others performance. It also has a + * single clock source per channel so mixed UDMA100/133 performance + * isn't perfect and we have to pick a clock. Thankfully none of this + * matters in smart mode. ATAPI DMA is not currently supported. + * + * It seems the smart mode is a win for RAID1/RAID10 but otherwise not. + * + * TODO + * - ATAPI UDMA is ok but not MWDMA it seems + * - RAID configuration ioctls + * - Move to libata once it grows up + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +struct it821x_dev +{ + unsigned int smart:1, /* Are we in smart raid mode */ + timing10:1; /* Rev 0x10 */ + u8 clock_mode; /* 0, ATA_50 or ATA_66 */ + u8 want[2][2]; /* Mode/Pri log for master slave */ + /* We need these for switching the clock when DMA goes on/off + The high byte is the 66Mhz timing */ + u16 pio[2]; /* Cached PIO values */ + u16 mwdma[2]; /* Cached MWDMA values */ + u16 udma[2]; /* Cached UDMA values (per drive) */ +}; + +#define ATA_66 0 +#define ATA_50 1 +#define ATA_ANY 2 + +#define UDMA_OFF 0 +#define MWDMA_OFF 0 + +/* + * We allow users to force the card into non raid mode without + * flashing the alternative BIOS. This is also neccessary right now + * for embedded platforms that cannot run a PC BIOS but are using this + * device. + */ + +static int it8212_noraid; + +/** + * it821x_program - program the PIO/MWDMA registers + * @drive: drive to tune + * + * Program the PIO/MWDMA timing for this channel according to the + * current clock. + */ + +static void it821x_program(ide_drive_t *drive, u16 timing) +{ + ide_hwif_t *hwif = drive->hwif; + struct it821x_dev *itdev = ide_get_hwifdata(hwif); + int channel = hwif->channel; + u8 conf; + + /* Program PIO/MWDMA timing bits */ + if(itdev->clock_mode == ATA_66) + conf = timing >> 8; + else + conf = timing & 0xFF; + pci_write_config_byte(hwif->pci_dev, 0x54 + 4 * channel, conf); +} + +/** + * it821x_program_udma - program the UDMA registers + * @drive: drive to tune + * + * Program the UDMA timing for this drive according to the + * current clock. + */ + +static void it821x_program_udma(ide_drive_t *drive, u16 timing) +{ + ide_hwif_t *hwif = drive->hwif; + struct it821x_dev *itdev = ide_get_hwifdata(hwif); + int channel = hwif->channel; + int unit = drive->select.b.unit; + u8 conf; + + /* Program UDMA timing bits */ + if(itdev->clock_mode == ATA_66) + conf = timing >> 8; + else + conf = timing & 0xFF; + if(itdev->timing10 == 0) + pci_write_config_byte(hwif->pci_dev, 0x56 + 4 * channel + unit, conf); + else { + pci_write_config_byte(hwif->pci_dev, 0x56 + 4 * channel, conf); + pci_write_config_byte(hwif->pci_dev, 0x56 + 4 * channel + 1, conf); + } +} + + +/** + * it821x_clock_strategy + * @hwif: hardware interface + * + * Select between the 50 and 66Mhz base clocks to get the best + * results for this interface. + */ + +static void it821x_clock_strategy(ide_drive_t *drive) +{ + ide_hwif_t *hwif = drive->hwif; + struct it821x_dev *itdev = ide_get_hwifdata(hwif); + + u8 unit = drive->select.b.unit; + ide_drive_t *pair = &hwif->drives[1-unit]; + + int clock, altclock; + u8 v; + int sel = 0; + + if(itdev->want[0][0] > itdev->want[1][0]) { + clock = itdev->want[0][1]; + altclock = itdev->want[1][1]; + } else { + clock = itdev->want[1][1]; + altclock = itdev->want[0][1]; + } + + /* Master doesn't care does the slave ? */ + if(clock == ATA_ANY) + clock = altclock; + + /* Nobody cares - keep the same clock */ + if(clock == ATA_ANY) + return; + /* No change */ + if(clock == itdev->clock_mode) + return; + + /* Load this into the controller ? */ + if(clock == ATA_66) + itdev->clock_mode = ATA_66; + else { + itdev->clock_mode = ATA_50; + sel = 1; + } + pci_read_config_byte(hwif->pci_dev, 0x50, &v); + v &= ~(1 << (1 + hwif->channel)); + v |= sel << (1 + hwif->channel); + pci_write_config_byte(hwif->pci_dev, 0x50, v); + + /* + * Reprogram the UDMA/PIO of the pair drive for the switch + * MWDMA will be dealt with by the dma switcher + */ + if(pair && itdev->udma[1-unit] != UDMA_OFF) { + it821x_program_udma(pair, itdev->udma[1-unit]); + it821x_program(pair, itdev->pio[1-unit]); + } + /* + * Reprogram the UDMA/PIO of our drive for the switch. + * MWDMA will be dealt with by the dma switcher + */ + if(itdev->udma[unit] != UDMA_OFF) { + it821x_program_udma(drive, itdev->udma[unit]); + it821x_program(drive, itdev->pio[unit]); + } +} + +/** + * it821x_ratemask - Compute available modes + * @drive: IDE drive + * + * Compute the available speeds for the devices on the interface. This + * is all modes to ATA133 clipped by drive cable setup. + */ + +static u8 it821x_ratemask (ide_drive_t *drive) +{ + u8 mode = 4; + if (!eighty_ninty_three(drive)) + mode = min(mode, (u8)1); + return mode; +} + +/** + * it821x_tuneproc - tune a drive + * @drive: drive to tune + * @mode_wanted: the target operating mode + * + * Load the timing settings for this device mode into the + * controller. By the time we are called the mode has been + * modified as neccessary to handle the absence of seperate + * master/slave timers for MWDMA/PIO. + * + * This code is only used in pass through mode. + */ + +static void it821x_tuneproc (ide_drive_t *drive, byte mode_wanted) +{ + ide_hwif_t *hwif = drive->hwif; + struct it821x_dev *itdev = ide_get_hwifdata(hwif); + int unit = drive->select.b.unit; + + /* Spec says 89 ref driver uses 88 */ + static u16 pio[] = { 0xAA88, 0xA382, 0xA181, 0x3332, 0x3121 }; + static u8 pio_want[] = { ATA_66, ATA_66, ATA_66, ATA_66, ATA_ANY }; + + if(itdev->smart) + return; + + /* We prefer 66Mhz clock for PIO 0-3, don't care for PIO4 */ + itdev->want[unit][1] = pio_want[mode_wanted]; + itdev->want[unit][0] = 1; /* PIO is lowest priority */ + itdev->pio[unit] = pio[mode_wanted]; + it821x_clock_strategy(drive); + it821x_program(drive, itdev->pio[unit]); +} + +/** + * it821x_tune_mwdma - tune a channel for MWDMA + * @drive: drive to set up + * @mode_wanted: the target operating mode + * + * Load the timing settings for this device mode into the + * controller when doing MWDMA in pass through mode. The caller + * must manage the whole lack of per device MWDMA/PIO timings and + * the shared MWDMA/PIO timing register. + */ + +static void it821x_tune_mwdma (ide_drive_t *drive, byte mode_wanted) +{ + ide_hwif_t *hwif = drive->hwif; + struct it821x_dev *itdev = (void *)ide_get_hwifdata(hwif); + int unit = drive->select.b.unit; + int channel = hwif->channel; + u8 conf; + + static u16 dma[] = { 0x8866, 0x3222, 0x3121 }; + static u8 mwdma_want[] = { ATA_ANY, ATA_66, ATA_ANY }; + + itdev->want[unit][1] = mwdma_want[mode_wanted]; + itdev->want[unit][0] = 2; /* MWDMA is low priority */ + itdev->mwdma[unit] = dma[mode_wanted]; + itdev->udma[unit] = UDMA_OFF; + + /* UDMA bits off - Revision 0x10 do them in pairs */ + pci_read_config_byte(hwif->pci_dev, 0x50, &conf); + if(itdev->timing10) + conf |= channel ? 0x60: 0x18; + else + conf |= 1 << (3 + 2 * channel + unit); + pci_write_config_byte(hwif->pci_dev, 0x50, conf); + + it821x_clock_strategy(drive); + /* FIXME: do we need to program this ? */ + /* it821x_program(drive, itdev->mwdma[unit]); */ +} + +/** + * it821x_tune_udma - tune a channel for UDMA + * @drive: drive to set up + * @mode_wanted: the target operating mode + * + * Load the timing settings for this device mode into the + * controller when doing UDMA modes in pass through. + */ + +static void it821x_tune_udma (ide_drive_t *drive, byte mode_wanted) +{ + ide_hwif_t *hwif = drive->hwif; + struct it821x_dev *itdev = ide_get_hwifdata(hwif); + int unit = drive->select.b.unit; + int channel = hwif->channel; + u8 conf; + + static u16 udma[] = { 0x4433, 0x4231, 0x3121, 0x2121, 0x1111, 0x2211, 0x1111 }; + static u8 udma_want[] = { ATA_ANY, ATA_50, ATA_ANY, ATA_66, ATA_66, ATA_50, ATA_66 }; + + itdev->want[unit][1] = udma_want[mode_wanted]; + itdev->want[unit][0] = 3; /* UDMA is high priority */ + itdev->mwdma[unit] = MWDMA_OFF; + itdev->udma[unit] = udma[mode_wanted]; + if(mode_wanted >= 5) + itdev->udma[unit] |= 0x8080; /* UDMA 5/6 select on */ + + /* UDMA on. Again revision 0x10 must do the pair */ + pci_read_config_byte(hwif->pci_dev, 0x50, &conf); + if(itdev->timing10) + conf &= channel ? 0x9F: 0xE7; + else + conf &= ~ (1 << (3 + 2 * channel + unit)); + pci_write_config_byte(hwif->pci_dev, 0x50, conf); + + it821x_clock_strategy(drive); + it821x_program_udma(drive, itdev->udma[unit]); + +} + +/** + * config_it821x_chipset_for_pio - set drive timings + * @drive: drive to tune + * @speed we want + * + * Compute the best pio mode we can for a given device. We must + * pick a speed that does not cause problems with the other device + * on the cable. + */ + +static void config_it821x_chipset_for_pio (ide_drive_t *drive, byte set_speed) +{ + u8 unit = drive->select.b.unit; + ide_hwif_t *hwif = drive->hwif; + ide_drive_t *pair = &hwif->drives[1-unit]; + u8 speed = 0, set_pio = ide_get_best_pio_mode(drive, 255, 5, NULL); + u8 pair_pio; + + /* We have to deal with this mess in pairs */ + if(pair != NULL) { + pair_pio = ide_get_best_pio_mode(pair, 255, 5, NULL); + /* Trim PIO to the slowest of the master/slave */ + if(pair_pio < set_pio) + set_pio = pair_pio; + } + it821x_tuneproc(drive, set_pio); + speed = XFER_PIO_0 + set_pio; + /* XXX - We trim to the lowest of the pair so the other drive + will always be fine at this point until we do hotplug passthru */ + + if (set_speed) + (void) ide_config_drive_speed(drive, speed); +} + +/** + * it821x_dma_read - DMA hook + * @drive: drive for DMA + * + * The IT821x has a single timing register for MWDMA and for PIO + * operations. As we flip back and forth we have to reload the + * clock. In addition the rev 0x10 device only works if the same + * timing value is loaded into the master and slave UDMA clock + * so we must also reload that. + * + * FIXME: we could figure out in advance if we need to do reloads + */ + +static void it821x_dma_start(ide_drive_t *drive) +{ + ide_hwif_t *hwif = drive->hwif; + struct it821x_dev *itdev = ide_get_hwifdata(hwif); + int unit = drive->select.b.unit; + if(itdev->mwdma[unit] != MWDMA_OFF) + it821x_program(drive, itdev->mwdma[unit]); + else if(itdev->udma[unit] != UDMA_OFF && itdev->timing10) + it821x_program_udma(drive, itdev->udma[unit]); + ide_dma_start(drive); +} + +/** + * it821x_dma_write - DMA hook + * @drive: drive for DMA stop + * + * The IT821x has a single timing register for MWDMA and for PIO + * operations. As we flip back and forth we have to reload the + * clock. + */ + +static int it821x_dma_end(ide_drive_t *drive) +{ + ide_hwif_t *hwif = drive->hwif; + int unit = drive->select.b.unit; + struct it821x_dev *itdev = ide_get_hwifdata(hwif); + int ret = __ide_dma_end(drive); + if(itdev->mwdma[unit] != MWDMA_OFF) + it821x_program(drive, itdev->pio[unit]); + return ret; +} + + +/** + * it821x_tune_chipset - set controller timings + * @drive: Drive to set up + * @xferspeed: speed we want to achieve + * + * Tune the ITE chipset for the desired mode. If we can't achieve + * the desired mode then tune for a lower one, but ultimately + * make the thing work. + */ + +static int it821x_tune_chipset (ide_drive_t *drive, byte xferspeed) +{ + + ide_hwif_t *hwif = drive->hwif; + struct it821x_dev *itdev = ide_get_hwifdata(hwif); + u8 speed = ide_rate_filter(it821x_ratemask(drive), xferspeed); + + if(!itdev->smart) { + switch(speed) { + case XFER_PIO_4: + case XFER_PIO_3: + case XFER_PIO_2: + case XFER_PIO_1: + case XFER_PIO_0: + it821x_tuneproc(drive, (speed - XFER_PIO_0)); + break; + /* MWDMA tuning is really hard because our MWDMA and PIO + timings are kept in the same place. We can switch in the + host dma on/off callbacks */ + case XFER_MW_DMA_2: + case XFER_MW_DMA_1: + case XFER_MW_DMA_0: + it821x_tune_mwdma(drive, (speed - XFER_MW_DMA_0)); + break; + case XFER_UDMA_6: + case XFER_UDMA_5: + case XFER_UDMA_4: + case XFER_UDMA_3: + case XFER_UDMA_2: + case XFER_UDMA_1: + case XFER_UDMA_0: + it821x_tune_udma(drive, (speed - XFER_UDMA_0)); + break; + default: + return 1; + } + } + /* + * In smart mode the clocking is done by the host controller + * snooping the mode we picked. The rest of it is not our problem + */ + return ide_config_drive_speed(drive, speed); +} + +/** + * config_chipset_for_dma - configure for DMA + * @drive: drive to configure + * + * Called by the IDE layer when it wants the timings set up. + */ + +static int config_chipset_for_dma (ide_drive_t *drive) +{ + u8 speed = ide_dma_speed(drive, it821x_ratemask(drive)); + + config_it821x_chipset_for_pio(drive, !speed); + it821x_tune_chipset(drive, speed); + return ide_dma_enable(drive); +} + +/** + * it821x_configure_drive_for_dma - set up for DMA transfers + * @drive: drive we are going to set up + * + * Set up the drive for DMA, tune the controller and drive as + * required. If the drive isn't suitable for DMA or we hit + * other problems then we will drop down to PIO and set up + * PIO appropriately + */ + +static int it821x_config_drive_for_dma (ide_drive_t *drive) +{ + ide_hwif_t *hwif = drive->hwif; + + if (ide_use_dma(drive)) { + if (config_chipset_for_dma(drive)) + return hwif->ide_dma_on(drive); + } + config_it821x_chipset_for_pio(drive, 1); + return hwif->ide_dma_off_quietly(drive); +} + +/** + * ata66_it821x - check for 80 pin cable + * @hwif: interface to check + * + * Check for the presence of an ATA66 capable cable on the + * interface. Problematic as it seems some cards don't have + * the needed logic onboard. + */ + +static unsigned int __devinit ata66_it821x(ide_hwif_t *hwif) +{ + /* The reference driver also only does disk side */ + return 1; +} + +/** + * it821x_fixup - post init callback + * @hwif: interface + * + * This callback is run after the drives have been probed but + * before anything gets attached. It allows drivers to do any + * final tuning that is needed, or fixups to work around bugs. + */ + +static void __devinit it821x_fixups(ide_hwif_t *hwif) +{ + struct it821x_dev *itdev = ide_get_hwifdata(hwif); + int i; + + if(!itdev->smart) { + /* + * If we are in pass through mode then not much + * needs to be done, but we do bother to clear the + * IRQ mask as we may well be in PIO (eg rev 0x10) + * for now and we know unmasking is safe on this chipset. + */ + for (i = 0; i < 2; i++) { + ide_drive_t *drive = &hwif->drives[i]; + if(drive->present) + drive->unmask = 1; + } + return; + } + /* + * Perform fixups on smart mode. We need to "lose" some + * capabilities the firmware lacks but does not filter, and + * also patch up some capability bits that it forgets to set + * in RAID mode. + */ + + for(i = 0; i < 2; i++) { + ide_drive_t *drive = &hwif->drives[i]; + struct hd_driveid *id; + u16 *idbits; + + if(!drive->present) + continue; + id = drive->id; + idbits = (u16 *)drive->id; + + /* Check for RAID v native */ + if(strstr(id->model, "Integrated Technology Express")) { + /* In raid mode the ident block is slightly buggy + We need to set the bits so that the IDE layer knows + LBA28. LBA48 and DMA ar valid */ + id->capability |= 3; /* LBA28, DMA */ + id->command_set_2 |= 0x0400; /* LBA48 valid */ + id->cfs_enable_2 |= 0x0400; /* LBA48 on */ + /* Reporting logic */ + printk(KERN_INFO "%s: IT8212 %sRAID %d volume", + drive->name, + idbits[147] ? "Bootable ":"", + idbits[129]); + if(idbits[129] != 1) + printk("(%dK stripe)", idbits[146]); + printk(".\n"); + /* Now the core code will have wrongly decided no DMA + so we need to fix this */ + hwif->ide_dma_off_quietly(drive); +#ifdef CONFIG_IDEDMA_ONLYDISK + if (drive->media == ide_disk) +#endif + hwif->ide_dma_check(drive); + } else { + /* Non RAID volume. Fixups to stop the core code + doing unsupported things */ + id->field_valid &= 1; + id->queue_depth = 0; + id->command_set_1 = 0; + id->command_set_2 &= 0xC400; + id->cfsse &= 0xC000; + id->cfs_enable_1 = 0; + id->cfs_enable_2 &= 0xC400; + id->csf_default &= 0xC000; + id->word127 = 0; + id->dlf = 0; + id->csfo = 0; + id->cfa_power = 0; + printk(KERN_INFO "%s: Performing identify fixups.\n", + drive->name); + } + } + +} + +/** + * init_hwif_it821x - set up hwif structs + * @hwif: interface to set up + * + * We do the basic set up of the interface structure. The IT8212 + * requires several custom handlers so we override the default + * ide DMA handlers appropriately + */ + +static void __devinit init_hwif_it821x(ide_hwif_t *hwif) +{ + struct it821x_dev *idev = kmalloc(sizeof(struct it821x_dev), GFP_KERNEL); + u8 conf; + + if(idev == NULL) { + printk(KERN_ERR "it821x: out of memory, falling back to legacy behaviour.\n"); + goto fallback; + } + memset(idev, 0, sizeof(struct it821x_dev)); + ide_set_hwifdata(hwif, idev); + + pci_read_config_byte(hwif->pci_dev, 0x50, &conf); + if(conf & 1) { + idev->smart = 1; + hwif->atapi_dma = 0; + /* Long I/O's although allowed in LBA48 space cause the + onboard firmware to enter the twighlight zone */ + hwif->rqsize = 256; + } + + /* Pull the current clocks from 0x50 also */ + if (conf & (1 << (1 + hwif->channel))) + idev->clock_mode = ATA_50; + else + idev->clock_mode = ATA_66; + + idev->want[0][1] = ATA_ANY; + idev->want[1][1] = ATA_ANY; + + /* + * Not in the docs but according to the reference driver + * this is neccessary. + */ + + pci_read_config_byte(hwif->pci_dev, 0x08, &conf); + if(conf == 0x10) { + idev->timing10 = 1; + hwif->atapi_dma = 0; + if(!idev->smart) + printk(KERN_WARNING "it821x: Revision 0x10, workarounds activated.\n"); + } + + hwif->speedproc = &it821x_tune_chipset; + hwif->tuneproc = &it821x_tuneproc; + + /* MWDMA/PIO clock switching for pass through mode */ + if(!idev->smart) { + hwif->dma_start = &it821x_dma_start; + hwif->ide_dma_end = &it821x_dma_end; + } + + hwif->drives[0].autotune = 1; + hwif->drives[1].autotune = 1; + + if (!hwif->dma_base) + goto fallback; + + hwif->ultra_mask = 0x7f; + hwif->mwdma_mask = 0x07; + hwif->swdma_mask = 0x07; + + hwif->ide_dma_check = &it821x_config_drive_for_dma; + if (!(hwif->udma_four)) + hwif->udma_four = ata66_it821x(hwif); + + /* + * The BIOS often doesn't set up DMA on this controller + * so we always do it. + */ + + hwif->autodma = 1; + hwif->drives[0].autodma = hwif->autodma; + hwif->drives[1].autodma = hwif->autodma; + return; +fallback: + hwif->autodma = 0; + return; +} + +static void __devinit it8212_disable_raid(struct pci_dev *dev) +{ + /* Reset local CPU, and set BIOS not ready */ + pci_write_config_byte(dev, 0x5E, 0x01); + + /* Set to bypass mode, and reset PCI bus */ + pci_write_config_byte(dev, 0x50, 0x00); + pci_write_config_word(dev, PCI_COMMAND, + PCI_COMMAND_PARITY | PCI_COMMAND_IO | + PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER); + pci_write_config_word(dev, 0x40, 0xA0F3); + + pci_write_config_dword(dev,0x4C, 0x02040204); + pci_write_config_byte(dev, 0x42, 0x36); + pci_write_config_byte(dev, PCI_LATENCY_TIMER, 0); +} + +static unsigned int __devinit init_chipset_it821x(struct pci_dev *dev, const char *name) +{ + u8 conf; + static char *mode[2] = { "pass through", "smart" }; + + /* Force the card into bypass mode if so requested */ + if (it8212_noraid) { + printk(KERN_INFO "it8212: forcing bypass mode.\n"); + it8212_disable_raid(dev); + } + pci_read_config_byte(dev, 0x50, &conf); + printk(KERN_INFO "it821x: controller in %s mode.\n", mode[conf & 1]); + return 0; +} + + +#define DECLARE_ITE_DEV(name_str) \ + { \ + .name = name_str, \ + .init_chipset = init_chipset_it821x, \ + .init_hwif = init_hwif_it821x, \ + .channels = 2, \ + .autodma = AUTODMA, \ + .bootable = ON_BOARD, \ + .fixup = it821x_fixups \ + } + +static ide_pci_device_t it821x_chipsets[] __devinitdata = { + /* 0 */ DECLARE_ITE_DEV("IT8212"), +}; + +/** + * it821x_init_one - pci layer discovery entry + * @dev: PCI device + * @id: ident table entry + * + * Called by the PCI code when it finds an ITE821x controller. + * We then use the IDE PCI generic helper to do most of the work. + */ + +static int __devinit it821x_init_one(struct pci_dev *dev, const struct pci_device_id *id) +{ + ide_setup_pci_device(dev, &it821x_chipsets[id->driver_data]); + return 0; +} + +static struct pci_device_id it821x_pci_tbl[] = { + { PCI_VENDOR_ID_ITE, PCI_DEVICE_ID_ITE_8211, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, + { PCI_VENDOR_ID_ITE, PCI_DEVICE_ID_ITE_8212, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, + { 0, }, +}; + +MODULE_DEVICE_TABLE(pci, it821x_pci_tbl); + +static struct pci_driver driver = { + .name = "ITE821x IDE", + .id_table = it821x_pci_tbl, + .probe = it821x_init_one, +}; + +static int __init it821x_ide_init(void) +{ + return ide_pci_register_driver(&driver); +} + +module_init(it821x_ide_init); + +module_param_named(noraid, it8212_noraid, int, S_IRUGO); +MODULE_PARM_DESC(it8212_noraid, "Force card into bypass mode"); + +MODULE_AUTHOR("Alan Cox"); +MODULE_DESCRIPTION("PCI driver module for the ITE 821x"); +MODULE_LICENSE("GPL"); diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h index a3961e1d518..1e0bc6a8d65 100644 --- a/include/linux/pci_ids.h +++ b/include/linux/pci_ids.h @@ -1815,6 +1815,8 @@ #define PCI_VENDOR_ID_ITE 0x1283 #define PCI_DEVICE_ID_ITE_IT8172G 0x8172 #define PCI_DEVICE_ID_ITE_IT8172G_AUDIO 0x0801 +#define PCI_DEVICE_ID_ITE_8211 0x8211 +#define PCI_DEVICE_ID_ITE_8212 0x8212 #define PCI_DEVICE_ID_ITE_8872 0x8872 #define PCI_DEVICE_ID_ITE_IT8330G_0 0xe886 -- cgit v1.2.3-70-g09d2 From c47abbbffd17a7e774ec1ef952a1c3621a9cb13b Mon Sep 17 00:00:00 2001 From: Alan Cox Date: Mon, 27 Jun 2005 15:24:31 -0700 Subject: [PATCH] ide: sensible probing for PCI systems Old ISA/VESA systems sometimes put tertiary IDE controllers at addresses 0x1e8, 0x168, 0x1e0 or 0x160. Linux thus probes these addresses on x86 systems. Unfortunately some PCI systems now use these addresses for other purposes which leads to users seeing minute plus hangs during boot or even crashes. The following patch (again has been in Fedora for a while) only probes the obscure legacy ISA ports on machinea that are pre-PCI. This seems to keep everyone happy and if there is someone with that utterly weird corner case the ide= command line still provides a get out of jail card. Unsurprisingly we've not found anyone so affected. Signed-off-by: Alan Cox Acked-by: Bartlomiej Zolnierkiewicz Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/asm-i386/ide.h | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/include/asm-i386/ide.h b/include/asm-i386/ide.h index 859ebf4da63..79dfab87135 100644 --- a/include/asm-i386/ide.h +++ b/include/asm-i386/ide.h @@ -41,13 +41,17 @@ static __inline__ int ide_default_irq(unsigned long base) static __inline__ unsigned long ide_default_io_base(int index) { + if (pci_find_device(PCI_ANY_ID, PCI_ANY_ID, NULL) == NULL) { + switch(index) { + case 2: return 0x1e8; + case 3: return 0x168; + case 4: return 0x1e0; + case 5: return 0x160; + } + } switch (index) { case 0: return 0x1f0; case 1: return 0x170; - case 2: return 0x1e8; - case 3: return 0x168; - case 4: return 0x1e0; - case 5: return 0x160; default: return 0; } -- cgit v1.2.3-70-g09d2 From 1ad275e3e7d253d44f03868e85977c908e334fed Mon Sep 17 00:00:00 2001 From: Dominik Brodowski Date: Mon, 27 Jun 2005 16:28:06 -0700 Subject: [PATCH] pcmcia: device and driver matching The actual matching of pcmcia drivers and pcmcia devices. The original version of this was written by David Woodhouse. Signed-off-by: Dominik Brodowski Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/pcmcia/ds.c | 123 +++++++++++++++++++++++++++- include/linux/mod_devicetable.h | 33 ++++++++ include/pcmcia/device_id.h | 175 ++++++++++++++++++++++++++++++++++++++++ include/pcmcia/ds.h | 7 +- 4 files changed, 336 insertions(+), 2 deletions(-) create mode 100644 include/pcmcia/device_id.h (limited to 'include') diff --git a/drivers/pcmcia/ds.c b/drivers/pcmcia/ds.c index 35d479b0df6..5701b93b2dd 100644 --- a/drivers/pcmcia/ds.c +++ b/drivers/pcmcia/ds.c @@ -101,6 +101,9 @@ struct pcmcia_bus_socket { u8 device_count; /* the number of devices, used * only internally and subject * to incorrectness and change */ + + u8 device_add_pending; + struct work_struct device_add; }; static spinlock_t pcmcia_dev_list_lock; @@ -512,6 +515,10 @@ static struct pcmcia_device * pcmcia_device_add(struct pcmcia_bus_socket *s, uns down(&device_add_lock); + /* max of 2 devices per card */ + if (s->device_count == 2) + goto err_put; + p_dev = kmalloc(sizeof(struct pcmcia_device), GFP_KERNEL); if (!p_dev) goto err_put; @@ -537,6 +544,8 @@ static struct pcmcia_device * pcmcia_device_add(struct pcmcia_bus_socket *s, uns list_add_tail(&p_dev->socket_device_list, &s->devices_list); spin_unlock_irqrestore(&pcmcia_dev_list_lock, flags); + pcmcia_device_query(p_dev); + if (device_register(&p_dev->dev)) { spin_lock_irqsave(&pcmcia_dev_list_lock, flags); list_del(&p_dev->socket_device_list); @@ -591,14 +600,123 @@ static int pcmcia_card_add(struct pcmcia_socket *s) } +static void pcmcia_delayed_add_pseudo_device(void *data) +{ + struct pcmcia_bus_socket *s = data; + pcmcia_device_add(s, 0); + s->device_add_pending = 0; +} + +static inline void pcmcia_add_pseudo_device(struct pcmcia_bus_socket *s) +{ + if (!s->device_add_pending) { + schedule_work(&s->device_add); + s->device_add_pending = 1; + } + return; +} + + +static inline int pcmcia_devmatch(struct pcmcia_device *dev, + struct pcmcia_device_id *did) +{ + if (did->match_flags & PCMCIA_DEV_ID_MATCH_MANF_ID) { + if ((!dev->has_manf_id) || (dev->manf_id != did->manf_id)) + return 0; + } + + if (did->match_flags & PCMCIA_DEV_ID_MATCH_CARD_ID) { + if ((!dev->has_card_id) || (dev->card_id != did->card_id)) + return 0; + } + + if (did->match_flags & PCMCIA_DEV_ID_MATCH_FUNCTION) { + if (dev->func != did->function) + return 0; + } + + if (did->match_flags & PCMCIA_DEV_ID_MATCH_PROD_ID1) { + if (!dev->prod_id[0]) + return 0; + if (strcmp(did->prod_id[0], dev->prod_id[0])) + return 0; + } + + if (did->match_flags & PCMCIA_DEV_ID_MATCH_PROD_ID2) { + if (!dev->prod_id[1]) + return 0; + if (strcmp(did->prod_id[1], dev->prod_id[1])) + return 0; + } + + if (did->match_flags & PCMCIA_DEV_ID_MATCH_PROD_ID3) { + if (!dev->prod_id[2]) + return 0; + if (strcmp(did->prod_id[2], dev->prod_id[2])) + return 0; + } + + if (did->match_flags & PCMCIA_DEV_ID_MATCH_PROD_ID4) { + if (!dev->prod_id[3]) + return 0; + if (strcmp(did->prod_id[3], dev->prod_id[3])) + return 0; + } + + if (did->match_flags & PCMCIA_DEV_ID_MATCH_DEVICE_NO) { + /* handle pseudo multifunction devices: + * there are at most two pseudo multifunction devices. + * if we're matching against the first, schedule a + * call which will then check whether there are two + * pseudo devices, and if not, add the second one. + */ + if (dev->device_no == 0) + pcmcia_add_pseudo_device(dev->socket->pcmcia); + + if (dev->device_no != did->device_no) + return 0; + } + + if (did->match_flags & PCMCIA_DEV_ID_MATCH_FUNC_ID) { + if ((!dev->has_func_id) || (dev->func_id != did->func_id)) + return 0; + + /* if this is a pseudo-multi-function device, + * we need explicit matches */ + if (did->match_flags & PCMCIA_DEV_ID_MATCH_DEVICE_NO) + return 0; + if (dev->device_no) + return 0; + + /* also, FUNC_ID matching needs to be activated by userspace + * after it has re-checked that there is no possible module + * with a prod_id/manf_id/card_id match. + */ + if (!dev->allow_func_id_match) + return 0; + } + + dev->dev.driver_data = (void *) did; + + return 1; +} + + static int pcmcia_bus_match(struct device * dev, struct device_driver * drv) { struct pcmcia_device * p_dev = to_pcmcia_dev(dev); struct pcmcia_driver * p_drv = to_pcmcia_drv(drv); + struct pcmcia_device_id *did = p_drv->id_table; /* matching by cardmgr */ if (p_dev->cardmgr == p_drv) return 1; + while (did && did->match_flags) { + if (pcmcia_devmatch(p_dev, did)) + return 1; + did++; + } + return 0; } @@ -922,7 +1040,9 @@ static int bind_request(struct pcmcia_bus_socket *s, bind_info_t *bind_info) rescan: p_dev->cardmgr = p_drv; - pcmcia_device_query(p_dev); + /* if a driver is already running, we can abort */ + if (p_dev->dev.driver) + goto err_put_module; /* * Prevent this racing with a card insertion. @@ -1595,6 +1715,7 @@ static int __devinit pcmcia_bus_add_socket(struct class_device *class_dev) init_waitqueue_head(&s->queue); INIT_LIST_HEAD(&s->devices_list); + INIT_WORK(&s->device_add, pcmcia_delayed_add_pseudo_device, s); /* Set up hotline to Card Services */ s->callback.owner = THIS_MODULE; diff --git a/include/linux/mod_devicetable.h b/include/linux/mod_devicetable.h index d6eb7b2efc0..e9651cd8310 100644 --- a/include/linux/mod_devicetable.h +++ b/include/linux/mod_devicetable.h @@ -175,4 +175,37 @@ struct serio_device_id { }; +/* PCMCIA */ + +struct pcmcia_device_id { + __u16 match_flags; + + __u16 manf_id; + __u16 card_id; + + __u8 func_id; + + /* for real multi-function devices */ + __u8 function; + + /* for pseude multi-function devices */ + __u8 device_no; + + const char * prod_id[4]; + __u32 prod_id_hash[4]; + + /* not matched against */ + kernel_ulong_t driver_info; +}; + +#define PCMCIA_DEV_ID_MATCH_MANF_ID 0x0001 +#define PCMCIA_DEV_ID_MATCH_CARD_ID 0x0002 +#define PCMCIA_DEV_ID_MATCH_FUNC_ID 0x0004 +#define PCMCIA_DEV_ID_MATCH_FUNCTION 0x0008 +#define PCMCIA_DEV_ID_MATCH_PROD_ID1 0x0010 +#define PCMCIA_DEV_ID_MATCH_PROD_ID2 0x0020 +#define PCMCIA_DEV_ID_MATCH_PROD_ID3 0x0040 +#define PCMCIA_DEV_ID_MATCH_PROD_ID4 0x0080 +#define PCMCIA_DEV_ID_MATCH_DEVICE_NO 0x0100 + #endif /* LINUX_MOD_DEVICETABLE_H */ diff --git a/include/pcmcia/device_id.h b/include/pcmcia/device_id.h new file mode 100644 index 00000000000..acf68656de3 --- /dev/null +++ b/include/pcmcia/device_id.h @@ -0,0 +1,175 @@ +/* + * Copyright (2003-2004) Dominik Brodowski + * David Woodhouse + * + * License: GPL v2 + */ + +#define PCMCIA_DEVICE_MANF_CARD(manf, card) { \ + .match_flags = PCMCIA_DEV_ID_MATCH_MANF_ID| \ + PCMCIA_DEV_ID_MATCH_CARD_ID, \ + .manf_id = (manf), \ + .card_id = (card), } + +#define PCMCIA_DEVICE_FUNC_ID(func) { \ + .match_flags = PCMCIA_DEV_ID_MATCH_FUNC_ID, \ + .func_id = (func), } + +#define PCMCIA_DEVICE_PROD_ID1(v1, vh1) { \ + .match_flags = PCMCIA_DEV_ID_MATCH_PROD_ID1, \ + .prod_id = { (v1), NULL, NULL, NULL }, \ + .prod_id_hash = { (vh1), 0, 0, 0 }, } + +#define PCMCIA_DEVICE_PROD_ID2(v2, vh2) { \ + .match_flags = PCMCIA_DEV_ID_MATCH_PROD_ID2, \ + .prod_id = { NULL, (v2), NULL, NULL }, \ + .prod_id_hash = { 0, (vh2), 0, 0 }, } + +#define PCMCIA_DEVICE_PROD_ID12(v1, v2, vh1, vh2) { \ + .match_flags = PCMCIA_DEV_ID_MATCH_PROD_ID1| \ + PCMCIA_DEV_ID_MATCH_PROD_ID2, \ + .prod_id = { (v1), (v2), NULL, NULL }, \ + .prod_id_hash = { (vh1), (vh2), 0, 0 }, } + +#define PCMCIA_DEVICE_PROD_ID13(v1, v3, vh1, vh3) { \ + .match_flags = PCMCIA_DEV_ID_MATCH_PROD_ID1| \ + PCMCIA_DEV_ID_MATCH_PROD_ID3, \ + .prod_id = { (v1), NULL, (v3), NULL }, \ + .prod_id_hash = { (vh1), 0, (vh3), 0 }, } + +#define PCMCIA_DEVICE_PROD_ID14(v1, v4, vh1, vh4) { \ + .match_flags = PCMCIA_DEV_ID_MATCH_PROD_ID1| \ + PCMCIA_DEV_ID_MATCH_PROD_ID4, \ + .prod_id = { (v1), NULL, NULL, (v4) }, \ + .prod_id_hash = { (vh1), 0, 0, (vh4) }, } + +#define PCMCIA_DEVICE_PROD_ID123(v1, v2, v3, vh1, vh2, vh3) { \ + .match_flags = PCMCIA_DEV_ID_MATCH_PROD_ID1| \ + PCMCIA_DEV_ID_MATCH_PROD_ID2| \ + PCMCIA_DEV_ID_MATCH_PROD_ID3, \ + .prod_id = { (v1), (v2), (v3), NULL },\ + .prod_id_hash = { (vh1), (vh2), (vh3), 0 }, } + +#define PCMCIA_DEVICE_PROD_ID124(v1, v2, v4, vh1, vh2, vh4) { \ + .match_flags = PCMCIA_DEV_ID_MATCH_PROD_ID1| \ + PCMCIA_DEV_ID_MATCH_PROD_ID2| \ + PCMCIA_DEV_ID_MATCH_PROD_ID4, \ + .prod_id = { (v1), (v2), NULL, (v4) }, \ + .prod_id_hash = { (vh1), (vh2), 0, (vh4) }, } + +#define PCMCIA_DEVICE_PROD_ID134(v1, v3, v4, vh1, vh3, vh4) { \ + .match_flags = PCMCIA_DEV_ID_MATCH_PROD_ID1| \ + PCMCIA_DEV_ID_MATCH_PROD_ID3| \ + PCMCIA_DEV_ID_MATCH_PROD_ID4, \ + .prod_id = { (v1), NULL, (v3), (v4) }, \ + .prod_id_hash = { (vh1), 0, (vh3), (vh4) }, } + +#define PCMCIA_DEVICE_PROD_ID1234(v1, v2, v3, v4, vh1, vh2, vh3, vh4) { \ + .match_flags = PCMCIA_DEV_ID_MATCH_PROD_ID1| \ + PCMCIA_DEV_ID_MATCH_PROD_ID2| \ + PCMCIA_DEV_ID_MATCH_PROD_ID3| \ + PCMCIA_DEV_ID_MATCH_PROD_ID4, \ + .prod_id = { (v1), (v2), (v3), (v4) }, \ + .prod_id_hash = { (vh1), (vh2), (vh3), (vh4) }, } + + +/* multi-function devices */ + +#define PCMCIA_MFC_DEVICE_MANF_CARD(mfc, manf, card) { \ + .match_flags = PCMCIA_DEV_ID_MATCH_MANF_ID| \ + PCMCIA_DEV_ID_MATCH_CARD_ID| \ + PCMCIA_DEV_ID_MATCH_FUNCTION, \ + .manf_id = (manf), \ + .card_id = (card), \ + .function = (mfc), } + +#define PCMCIA_MFC_DEVICE_PROD_ID1(mfc, v1, vh1) { \ + .match_flags = PCMCIA_DEV_ID_MATCH_PROD_ID1| \ + PCMCIA_DEV_ID_MATCH_FUNCTION, \ + .prod_id = { (v1), NULL, NULL, NULL }, \ + .prod_id_hash = { (vh1), 0, 0, 0 }, \ + .function = (mfc), } + +#define PCMCIA_MFC_DEVICE_PROD_ID2(mfc, v2, vh2) { \ + .match_flags = PCMCIA_DEV_ID_MATCH_PROD_ID2| \ + PCMCIA_DEV_ID_MATCH_FUNCTION, \ + .prod_id = { NULL, (v2), NULL, NULL }, \ + .prod_id_hash = { 0, (vh2), 0, 0 }, \ + .function = (mfc), } + +#define PCMCIA_MFC_DEVICE_PROD_ID12(mfc, v1, v2, vh1, vh2) { \ + .match_flags = PCMCIA_DEV_ID_MATCH_PROD_ID1| \ + PCMCIA_DEV_ID_MATCH_PROD_ID2| \ + PCMCIA_DEV_ID_MATCH_FUNCTION, \ + .prod_id = { (v1), (v2), NULL, NULL }, \ + .prod_id_hash = { (vh1), (vh2), 0, 0 }, \ + .function = (mfc), } + +#define PCMCIA_MFC_DEVICE_PROD_ID13(mfc, v1, v3, vh1, vh3) { \ + .match_flags = PCMCIA_DEV_ID_MATCH_PROD_ID1| \ + PCMCIA_DEV_ID_MATCH_PROD_ID3| \ + PCMCIA_DEV_ID_MATCH_FUNCTION, \ + .prod_id = { (v1), NULL, (v3), NULL }, \ + .prod_id_hash = { (vh1), 0, (vh3), 0 }, \ + .function = (mfc), } + +#define PCMCIA_MFC_DEVICE_PROD_ID123(mfc, v1, v2, v3, vh1, vh2, vh3) { \ + .match_flags = PCMCIA_DEV_ID_MATCH_PROD_ID1| \ + PCMCIA_DEV_ID_MATCH_PROD_ID2| \ + PCMCIA_DEV_ID_MATCH_PROD_ID3| \ + PCMCIA_DEV_ID_MATCH_FUNCTION, \ + .prod_id = { (v1), (v2), (v3), NULL },\ + .prod_id_hash = { (vh1), (vh2), (vh3), 0 }, \ + .function = (mfc), } + +/* pseudo multi-function devices */ + +#define PCMCIA_PFC_DEVICE_MANF_CARD(mfc, manf, card) { \ + .match_flags = PCMCIA_DEV_ID_MATCH_MANF_ID| \ + PCMCIA_DEV_ID_MATCH_CARD_ID| \ + PCMCIA_DEV_ID_MATCH_DEVICE_NO, \ + .manf_id = (manf), \ + .card_id = (card), \ + .device_no = (mfc), } + +#define PCMCIA_PFC_DEVICE_PROD_ID1(mfc, v1, vh1) { \ + .match_flags = PCMCIA_DEV_ID_MATCH_PROD_ID1| \ + PCMCIA_DEV_ID_MATCH_DEVICE_NO, \ + .prod_id = { (v1), NULL, NULL, NULL }, \ + .prod_id_hash = { (vh1), 0, 0, 0 }, \ + .device_no = (mfc), } + +#define PCMCIA_PFC_DEVICE_PROD_ID2(mfc, v2, vh2) { \ + .match_flags = PCMCIA_DEV_ID_MATCH_PROD_ID2| \ + PCMCIA_DEV_ID_MATCH_DEVICE_NO, \ + .prod_id = { NULL, (v2), NULL, NULL }, \ + .prod_id_hash = { 0, (vh2), 0, 0 }, \ + .device_no = (mfc), } + +#define PCMCIA_PFC_DEVICE_PROD_ID12(mfc, v1, v2, vh1, vh2) { \ + .match_flags = PCMCIA_DEV_ID_MATCH_PROD_ID1| \ + PCMCIA_DEV_ID_MATCH_PROD_ID2| \ + PCMCIA_DEV_ID_MATCH_DEVICE_NO, \ + .prod_id = { (v1), (v2), NULL, NULL }, \ + .prod_id_hash = { (vh1), (vh2), 0, 0 }, \ + .device_no = (mfc), } + +#define PCMCIA_PFC_DEVICE_PROD_ID13(mfc, v1, v3, vh1, vh3) { \ + .match_flags = PCMCIA_DEV_ID_MATCH_PROD_ID1| \ + PCMCIA_DEV_ID_MATCH_PROD_ID3| \ + PCMCIA_DEV_ID_MATCH_DEVICE_NO, \ + .prod_id = { (v1), NULL, (v3), NULL }, \ + .prod_id_hash = { (vh1), 0, (vh3), 0 }, \ + .device_no = (mfc), } + +#define PCMCIA_PFC_DEVICE_PROD_ID123(mfc, v1, v2, v3, vh1, vh2, vh3) { \ + .match_flags = PCMCIA_DEV_ID_MATCH_PROD_ID1| \ + PCMCIA_DEV_ID_MATCH_PROD_ID2| \ + PCMCIA_DEV_ID_MATCH_PROD_ID3| \ + PCMCIA_DEV_ID_MATCH_DEVICE_NO, \ + .prod_id = { (v1), (v2), (v3), NULL },\ + .prod_id_hash = { (vh1), (vh2), (vh3), 0 }, \ + .device_no = (mfc), } + + +#define PCMCIA_DEVICE_NULL { .match_flags = 0, } diff --git a/include/pcmcia/ds.h b/include/pcmcia/ds.h index 312fd958c90..c267edde9d0 100644 --- a/include/pcmcia/ds.h +++ b/include/pcmcia/ds.h @@ -18,6 +18,8 @@ #include #include +#include +#include typedef struct tuple_parse_t { tuple_t tuple; @@ -135,6 +137,7 @@ struct pcmcia_driver { dev_link_t *(*attach)(void); void (*detach)(dev_link_t *); struct module *owner; + struct pcmcia_device_id *id_table; struct device_driver drv; }; @@ -173,7 +176,9 @@ struct pcmcia_device { u8 has_manf_id:1; u8 has_card_id:1; u8 has_func_id:1; - u8 reserved:5; + + u8 allow_func_id_match:1; + u8 reserved:4; u8 func_id; u16 manf_id; -- cgit v1.2.3-70-g09d2 From ea7b38825bba66a81745a706da70a1c81adc95bd Mon Sep 17 00:00:00 2001 From: Dominik Brodowski Date: Mon, 27 Jun 2005 16:28:07 -0700 Subject: [PATCH] pcmcia: match for fake CIS Add another match flag for devices needing a CIS override. The driver will only probe/attach if the CIS has been replaced before. Signed-off-by: Dominik Brodowski Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/pcmcia/ds.c | 8 +++++ include/linux/mod_devicetable.h | 2 ++ include/pcmcia/device_id.h | 74 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 84 insertions(+) (limited to 'include') diff --git a/drivers/pcmcia/ds.c b/drivers/pcmcia/ds.c index 3ac7a443f66..c0611d56eab 100644 --- a/drivers/pcmcia/ds.c +++ b/drivers/pcmcia/ds.c @@ -733,6 +733,14 @@ static inline int pcmcia_devmatch(struct pcmcia_device *dev, return 0; } + if (did->match_flags & PCMCIA_DEV_ID_MATCH_FAKE_CIS) { + if (!dev->socket->fake_cis) { + /* FIXME: evaluate using firmware helpers to + * automagically load it from userspace */ + return 0; + } + } + dev->dev.driver_data = (void *) did; return 1; diff --git a/include/linux/mod_devicetable.h b/include/linux/mod_devicetable.h index e9651cd8310..c0106d68bb6 100644 --- a/include/linux/mod_devicetable.h +++ b/include/linux/mod_devicetable.h @@ -196,6 +196,7 @@ struct pcmcia_device_id { /* not matched against */ kernel_ulong_t driver_info; + char * cisfile; }; #define PCMCIA_DEV_ID_MATCH_MANF_ID 0x0001 @@ -207,5 +208,6 @@ struct pcmcia_device_id { #define PCMCIA_DEV_ID_MATCH_PROD_ID3 0x0040 #define PCMCIA_DEV_ID_MATCH_PROD_ID4 0x0080 #define PCMCIA_DEV_ID_MATCH_DEVICE_NO 0x0100 +#define PCMCIA_DEV_ID_MATCH_FAKE_CIS 0x0200 #endif /* LINUX_MOD_DEVICETABLE_H */ diff --git a/include/pcmcia/device_id.h b/include/pcmcia/device_id.h index acf68656de3..346d81ece28 100644 --- a/include/pcmcia/device_id.h +++ b/include/pcmcia/device_id.h @@ -171,5 +171,79 @@ .prod_id_hash = { (vh1), (vh2), (vh3), 0 }, \ .device_no = (mfc), } +/* cards needing a CIS override */ + +#define PCMCIA_DEVICE_CIS_MANF_CARD(manf, card, _cisfile) { \ + .match_flags = PCMCIA_DEV_ID_MATCH_FAKE_CIS | \ + PCMCIA_DEV_ID_MATCH_MANF_ID| \ + PCMCIA_DEV_ID_MATCH_CARD_ID, \ + .manf_id = (manf), \ + .card_id = (card), \ + .cisfile = (_cisfile)} + +#define PCMCIA_DEVICE_CIS_PROD_ID12(v1, v2, vh1, vh2, _cisfile) { \ + .match_flags = PCMCIA_DEV_ID_MATCH_FAKE_CIS | \ + PCMCIA_DEV_ID_MATCH_PROD_ID1| \ + PCMCIA_DEV_ID_MATCH_PROD_ID2, \ + .prod_id = { (v1), (v2), NULL, NULL }, \ + .prod_id_hash = { (vh1), (vh2), 0, 0 }, \ + .cisfile = (_cisfile)} + +#define PCMCIA_DEVICE_CIS_PROD_ID123(v1, v2, v3, vh1, vh2, vh3, _cisfile) { \ + .match_flags = PCMCIA_DEV_ID_MATCH_FAKE_CIS | \ + PCMCIA_DEV_ID_MATCH_PROD_ID1| \ + PCMCIA_DEV_ID_MATCH_PROD_ID2| \ + PCMCIA_DEV_ID_MATCH_PROD_ID3, \ + .prod_id = { (v1), (v2), (v3), NULL },\ + .prod_id_hash = { (vh1), (vh2), (vh3), 0 }, \ + .cisfile = (_cisfile)} + + +#define PCMCIA_DEVICE_CIS_PROD_ID2(v2, vh2, _cisfile) { \ + .match_flags = PCMCIA_DEV_ID_MATCH_FAKE_CIS | \ + PCMCIA_DEV_ID_MATCH_PROD_ID2, \ + .prod_id = { NULL, (v2), NULL, NULL }, \ + .prod_id_hash = { 0, (vh2), 0, 0 }, \ + .cisfile = (_cisfile)} + +#define PCMCIA_PFC_DEVICE_CIS_PROD_ID12(mfc, v1, v2, vh1, vh2, _cisfile) { \ + .match_flags = PCMCIA_DEV_ID_MATCH_FAKE_CIS | \ + PCMCIA_DEV_ID_MATCH_PROD_ID1| \ + PCMCIA_DEV_ID_MATCH_PROD_ID2| \ + PCMCIA_DEV_ID_MATCH_DEVICE_NO, \ + .prod_id = { (v1), (v2), NULL, NULL }, \ + .prod_id_hash = { (vh1), (vh2), 0, 0 },\ + .device_no = (mfc), \ + .cisfile = (_cisfile)} + +#define PCMCIA_MFC_DEVICE_CIS_MANF_CARD(mfc, manf, card, _cisfile) { \ + .match_flags = PCMCIA_DEV_ID_MATCH_FAKE_CIS | \ + PCMCIA_DEV_ID_MATCH_MANF_ID| \ + PCMCIA_DEV_ID_MATCH_CARD_ID| \ + PCMCIA_DEV_ID_MATCH_FUNCTION, \ + .manf_id = (manf), \ + .card_id = (card), \ + .function = (mfc), \ + .cisfile = (_cisfile)} + +#define PCMCIA_MFC_DEVICE_CIS_PROD_ID12(mfc, v1, v2, vh1, vh2, _cisfile) { \ + .match_flags = PCMCIA_DEV_ID_MATCH_FAKE_CIS | \ + PCMCIA_DEV_ID_MATCH_PROD_ID1| \ + PCMCIA_DEV_ID_MATCH_PROD_ID2| \ + PCMCIA_DEV_ID_MATCH_FUNCTION, \ + .prod_id = { (v1), (v2), NULL, NULL }, \ + .prod_id_hash = { (vh1), (vh2), 0, 0 }, \ + .function = (mfc), \ + .cisfile = (_cisfile)} + +#define PCMCIA_MFC_DEVICE_CIS_PROD_ID4(mfc, v4, vh4, _cisfile) { \ + .match_flags = PCMCIA_DEV_ID_MATCH_FAKE_CIS | \ + PCMCIA_DEV_ID_MATCH_PROD_ID4| \ + PCMCIA_DEV_ID_MATCH_FUNCTION, \ + .prod_id = { NULL, NULL, NULL, (v4) }, \ + .prod_id_hash = { 0, 0, 0, (vh4) }, \ + .function = (mfc), \ + .cisfile = (_cisfile)} + #define PCMCIA_DEVICE_NULL { .match_flags = 0, } -- cgit v1.2.3-70-g09d2 From f602ff7eb4e44e7245bfeeba4d078144703fcd76 Mon Sep 17 00:00:00 2001 From: Dominik Brodowski Date: Mon, 27 Jun 2005 16:28:09 -0700 Subject: [PATCH] pcmcia: match "anonymous" cards If a card doesn't provide _any_ information about itself, assume it is a so-called "anonymous" card. pcmciamtd will bind to it if it is configured to do so. Signed-off-by: Dominik Brodowski Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/pcmcia/ds.c | 9 +++++++++ include/linux/mod_devicetable.h | 1 + 2 files changed, 10 insertions(+) (limited to 'include') diff --git a/drivers/pcmcia/ds.c b/drivers/pcmcia/ds.c index f657a2a77b2..66680699e91 100644 --- a/drivers/pcmcia/ds.c +++ b/drivers/pcmcia/ds.c @@ -746,6 +746,15 @@ static inline int pcmcia_devmatch(struct pcmcia_device *dev, } } + if (did->match_flags & PCMCIA_DEV_ID_MATCH_ANONYMOUS) { + int i; + for (i=0; i<4; i++) + if (dev->prod_id[i]) + return 0; + if (dev->has_manf_id || dev->has_card_id || dev->has_func_id) + return 0; + } + dev->dev.driver_data = (void *) did; return 1; diff --git a/include/linux/mod_devicetable.h b/include/linux/mod_devicetable.h index c0106d68bb6..8a8dc82a941 100644 --- a/include/linux/mod_devicetable.h +++ b/include/linux/mod_devicetable.h @@ -209,5 +209,6 @@ struct pcmcia_device_id { #define PCMCIA_DEV_ID_MATCH_PROD_ID4 0x0080 #define PCMCIA_DEV_ID_MATCH_DEVICE_NO 0x0100 #define PCMCIA_DEV_ID_MATCH_FAKE_CIS 0x0200 +#define PCMCIA_DEV_ID_MATCH_ANONYMOUS 0x0400 #endif /* LINUX_MOD_DEVICETABLE_H */ -- cgit v1.2.3-70-g09d2 From e94e15f70559962f60915a09d44658b73a453fe2 Mon Sep 17 00:00:00 2001 From: Dominik Brodowski Date: Mon, 27 Jun 2005 16:28:15 -0700 Subject: [PATCH] pcmcia: cleanups From: Adrian Bunk This patch contains the following cleanups: - make needlessly global code static - remove the following unneeded EXPORT_SYMBOL's: - ds.c: pcmcia_report_error - ds.c: pcmcia_bus_type Signed-off-by: Adrian Bunk Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/pcmcia/ds.c | 10 +++++----- drivers/pcmcia/rsrc_nonstatic.c | 6 +++--- include/pcmcia/cs.h | 1 - include/pcmcia/ds.h | 2 -- 4 files changed, 8 insertions(+), 11 deletions(-) (limited to 'include') diff --git a/drivers/pcmcia/ds.c b/drivers/pcmcia/ds.c index 5c88ff8e7d7..0de0f230045 100644 --- a/drivers/pcmcia/ds.c +++ b/drivers/pcmcia/ds.c @@ -60,7 +60,7 @@ MODULE_DESCRIPTION("PCMCIA Driver Services"); MODULE_LICENSE("GPL"); #ifdef DEBUG -int ds_pc_debug; +static int ds_pc_debug; module_param_named(pc_debug, ds_pc_debug, int, 0644); @@ -108,6 +108,8 @@ struct pcmcia_bus_socket { }; static spinlock_t pcmcia_dev_list_lock; +static struct bus_type pcmcia_bus_type; + #define DS_SOCKET_PRESENT 0x01 #define DS_SOCKET_BUSY 0x02 #define DS_SOCKET_REMOVAL_PENDING 0x10 @@ -218,7 +220,7 @@ static const lookup_t service_table[] = { }; -int pcmcia_report_error(client_handle_t handle, error_info_t *err) +static int pcmcia_report_error(client_handle_t handle, error_info_t *err) { int i; char *serv; @@ -248,7 +250,6 @@ int pcmcia_report_error(client_handle_t handle, error_info_t *err) return CS_SUCCESS; } /* report_error */ -EXPORT_SYMBOL(pcmcia_report_error); /* end of code which was in cs.c before */ @@ -1901,13 +1902,12 @@ static struct class_interface pcmcia_bus_interface = { }; -struct bus_type pcmcia_bus_type = { +static struct bus_type pcmcia_bus_type = { .name = "pcmcia", .hotplug = pcmcia_bus_hotplug, .match = pcmcia_bus_match, .dev_attrs = pcmcia_dev_attrs, }; -EXPORT_SYMBOL(pcmcia_bus_type); static int __init init_pcmcia_bus(void) diff --git a/drivers/pcmcia/rsrc_nonstatic.c b/drivers/pcmcia/rsrc_nonstatic.c index 5876bab7c14..9a0b835d612 100644 --- a/drivers/pcmcia/rsrc_nonstatic.c +++ b/drivers/pcmcia/rsrc_nonstatic.c @@ -601,7 +601,7 @@ static int nonstatic_adjust_io_region(struct resource *res, unsigned long r_star ======================================================================*/ -struct resource *nonstatic_find_io_region(unsigned long base, int num, +static struct resource *nonstatic_find_io_region(unsigned long base, int num, unsigned long align, struct pcmcia_socket *s) { struct resource *res = make_resource(0, num, IORESOURCE_IO, s->dev.class_id); @@ -635,8 +635,8 @@ struct resource *nonstatic_find_io_region(unsigned long base, int num, return res; } -struct resource * nonstatic_find_mem_region(u_long base, u_long num, u_long align, - int low, struct pcmcia_socket *s) +static struct resource * nonstatic_find_mem_region(u_long base, u_long num, + u_long align, int low, struct pcmcia_socket *s) { struct resource *res = make_resource(0, num, IORESOURCE_MEM, s->dev.class_id); struct socket_data *s_data = s->resource_data; diff --git a/include/pcmcia/cs.h b/include/pcmcia/cs.h index 8d8643adc78..60a3decea82 100644 --- a/include/pcmcia/cs.h +++ b/include/pcmcia/cs.h @@ -417,7 +417,6 @@ int pcmcia_suspend_card(struct pcmcia_socket *skt); int pcmcia_resume_card(struct pcmcia_socket *skt); int pcmcia_eject_card(struct pcmcia_socket *skt); int pcmcia_insert_card(struct pcmcia_socket *skt); -int pcmcia_report_error(client_handle_t handle, error_info_t *err); struct pcmcia_socket * pcmcia_get_socket(struct pcmcia_socket *skt); void pcmcia_put_socket(struct pcmcia_socket *skt); diff --git a/include/pcmcia/ds.h b/include/pcmcia/ds.h index c267edde9d0..2b52553f2d9 100644 --- a/include/pcmcia/ds.h +++ b/include/pcmcia/ds.h @@ -131,8 +131,6 @@ typedef struct dev_link_t { struct pcmcia_socket; -extern struct bus_type pcmcia_bus_type; - struct pcmcia_driver { dev_link_t *(*attach)(void); void (*detach)(dev_link_t *); -- cgit v1.2.3-70-g09d2 From f4d7510d3d3b4501c94b4b00cf42fd58d49aeddd Mon Sep 17 00:00:00 2001 From: Jun Komuro Date: Mon, 27 Jun 2005 16:28:44 -0700 Subject: [PATCH] pcmcia: more IDs for TDK multifunction cards Add new pcmcia id_table for fmvj18x_cs and serial_cs. (TDK multi-function card (NetPartner9610 and MobileNetworker3200)) Signed-off-by: Jun Komuro Signed-off-by: Dominik Brodowski Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/net/pcmcia/fmvj18x_cs.c | 6 +++++- drivers/serial/serial_cs.c | 2 ++ include/pcmcia/ciscode.h | 2 ++ 3 files changed, 9 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/drivers/net/pcmcia/fmvj18x_cs.c b/drivers/net/pcmcia/fmvj18x_cs.c index b17aedd5e5a..917adbbf0b5 100644 --- a/drivers/net/pcmcia/fmvj18x_cs.c +++ b/drivers/net/pcmcia/fmvj18x_cs.c @@ -435,7 +435,9 @@ static void fmvj18x_config(dev_link_t *link) pcmcia_get_status(handle, &status); if (status.CardState & CS_EVENT_3VCARD) link->conf.Vcc = 33; /* inserted in 3.3V slot */ - } else if (le16_to_cpu(buf[1]) == PRODID_TDK_GN3410) { + } else if (le16_to_cpu(buf[1]) == PRODID_TDK_GN3410 + || le16_to_cpu(buf[1]) == PRODID_TDK_NP9610 + || le16_to_cpu(buf[1]) == PRODID_TDK_MN3200) { /* MultiFunction Card */ link->conf.ConfigBase = 0x800; link->conf.ConfigIndex = 0x47; @@ -783,6 +785,8 @@ static struct pcmcia_device_id fmvj18x_ids[] = { PCMCIA_DEVICE_PROD_ID1("PCMCIA MBH10302", 0x8f4005da), PCMCIA_DEVICE_PROD_ID1("UBKK,V2.0", 0x90888080), PCMCIA_PFC_DEVICE_PROD_ID12(0, "TDK", "GlobalNetworker 3410/3412", 0x1eae9475, 0xd9a93bed), + PCMCIA_PFC_DEVICE_MANF_CARD(0, 0x0105, 0x0d0a), + PCMCIA_PFC_DEVICE_MANF_CARD(0, 0x0105, 0x0e0a), PCMCIA_DEVICE_NULL, }; MODULE_DEVICE_TABLE(pcmcia, fmvj18x_ids); diff --git a/drivers/serial/serial_cs.c b/drivers/serial/serial_cs.c index ca86f1f668f..73a34b18866 100644 --- a/drivers/serial/serial_cs.c +++ b/drivers/serial/serial_cs.c @@ -785,6 +785,8 @@ static struct pcmcia_device_id serial_ids[] = { PCMCIA_PFC_DEVICE_MANF_CARD(1, 0x016c, 0x0081), PCMCIA_PFC_DEVICE_MANF_CARD(1, 0x021b, 0x0101), PCMCIA_PFC_DEVICE_MANF_CARD(1, 0x08a1, 0xc0ab), + PCMCIA_PFC_DEVICE_MANF_CARD(1, 0x0105, 0x0d0a), + PCMCIA_PFC_DEVICE_MANF_CARD(1, 0x0105, 0x0e0a), PCMCIA_PFC_DEVICE_PROD_ID123(1, "MEGAHERTZ", "CC/XJEM3288", "DATA/FAX/CELL ETHERNET MODEM", 0xf510db04, 0x04cd2988, 0x46a52d63), PCMCIA_PFC_DEVICE_PROD_ID123(1, "MEGAHERTZ", "CC/XJEM3336", "DATA/FAX/CELL ETHERNET MODEM", 0xf510db04, 0x0143b773, 0x46a52d63), PCMCIA_PFC_DEVICE_PROD_ID123(1, "MEGAHERTZ", "EM1144T", "PCMCIA MODEM", 0xf510db04, 0x856d66c8, 0xbd6c43ef), diff --git a/include/pcmcia/ciscode.h b/include/pcmcia/ciscode.h index 2000b43ece9..da19c297dd6 100644 --- a/include/pcmcia/ciscode.h +++ b/include/pcmcia/ciscode.h @@ -112,6 +112,8 @@ #define MANFID_TDK 0x0105 #define PRODID_TDK_CF010 0x0900 +#define PRODID_TDK_NP9610 0x0d0a +#define PRODID_TDK_MN3200 0x0e0a #define PRODID_TDK_GN3410 0x4815 #define MANFID_TOSHIBA 0x0098 -- cgit v1.2.3-70-g09d2 From 3c29976a6469b81a7858812dc2d4b8430d74004a Mon Sep 17 00:00:00 2001 From: Dominik Brodowski Date: Mon, 27 Jun 2005 16:28:46 -0700 Subject: [PATCH] pcmcia: mark parent bridge windows as resources available for PCMCIA devices Automatically mark the parent PCI-PCI bridge windows as resources available for PCMCIA usage. Signed-off-by: Dominik Brodowski Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/pcmcia/rsrc_mgr.c | 2 +- drivers/pcmcia/rsrc_nonstatic.c | 58 +++++++++++++++++++++++++++++++++++++++++ include/pcmcia/ss.h | 3 ++- 3 files changed, 61 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/drivers/pcmcia/rsrc_mgr.c b/drivers/pcmcia/rsrc_mgr.c index b6843f8d300..b9269e66281 100644 --- a/drivers/pcmcia/rsrc_mgr.c +++ b/drivers/pcmcia/rsrc_mgr.c @@ -72,7 +72,7 @@ int pcmcia_adjust_resource_info(adjust_t *adj) /* you can't use the old interface if the new * one was used before */ spin_lock_irqsave(&s->lock, flags); - if ((s->resource_setup_done) && + if ((s->resource_setup_new) && !(s->resource_setup_old)) { spin_unlock_irqrestore(&s->lock, flags); continue; diff --git a/drivers/pcmcia/rsrc_nonstatic.c b/drivers/pcmcia/rsrc_nonstatic.c index 6b463609a3a..c8f21796c59 100644 --- a/drivers/pcmcia/rsrc_nonstatic.c +++ b/drivers/pcmcia/rsrc_nonstatic.c @@ -768,6 +768,58 @@ static int nonstatic_adjust_resource_info(struct pcmcia_socket *s, adjust_t *adj return CS_UNSUPPORTED_FUNCTION; } +#ifdef CONFIG_PCI +static int nonstatic_autoadd_resources(struct pcmcia_socket *s) +{ + struct resource *res; + int i, done = 0; + + if (!s->cb_dev || !s->cb_dev->bus) + return -ENODEV; + + for (i=0; i < PCI_BUS_NUM_RESOURCES; i++) { + res = s->cb_dev->bus->resource[i]; + if (!res) + continue; + + if (res->flags & IORESOURCE_IO) { + if (res == &ioport_resource) + continue; + printk(KERN_INFO "pcmcia: parent PCI bridge I/O window: 0x%lx - 0x%lx\n", + res->start, res->end); + if (!adjust_io(s, ADD_MANAGED_RESOURCE, res->start, res->end)) + done |= IORESOURCE_IO; + + } + + if (res->flags & IORESOURCE_MEM) { + if (res == &iomem_resource) + continue; + printk(KERN_INFO "pcmcia: parent PCI bridge Memory window: 0x%lx - 0x%lx\n", + res->start, res->end); + if (!adjust_memory(s, ADD_MANAGED_RESOURCE, res->start, res->end)) + done |= IORESOURCE_MEM; + } + } + + /* if we got at least one of IO, and one of MEM, we can be glad and + * activate the PCMCIA subsystem */ + if (done & (IORESOURCE_MEM | IORESOURCE_IO)) + s->resource_setup_done = 1; + + return 0; +} + +#else + +static inline int nonstatic_autoadd_resources(struct pcmcia_socket *s) +{ + return -ENODEV; +} + +#endif + + static int nonstatic_init(struct pcmcia_socket *s) { struct socket_data *data; @@ -782,6 +834,8 @@ static int nonstatic_init(struct pcmcia_socket *s) s->resource_data = (void *) data; + nonstatic_autoadd_resources(s); + return 0; } @@ -862,6 +916,8 @@ static ssize_t store_io_db(struct class_device *class_dev, const char *buf, size return -EINVAL; ret = adjust_io(s, add, start_addr, end_addr); + if (!ret) + s->resource_setup_new = 1; return ret ? ret : count; } @@ -912,6 +968,8 @@ static ssize_t store_mem_db(struct class_device *class_dev, const char *buf, siz return -EINVAL; ret = adjust_memory(s, add, start_addr, end_addr); + if (!ret) + s->resource_setup_new = 1; return ret ? ret : count; } diff --git a/include/pcmcia/ss.h b/include/pcmcia/ss.h index 67b867f31fe..f8797767051 100644 --- a/include/pcmcia/ss.h +++ b/include/pcmcia/ss.h @@ -216,8 +216,9 @@ struct pcmcia_socket { /* is set to one if resource setup is done using adjust_resource_info() */ u8 resource_setup_old:1; + u8 resource_setup_new:1; - u8 reserved:6; + u8 reserved:5; /* socket operations */ struct pccard_operations * ops; -- cgit v1.2.3-70-g09d2 From dc109497bd5799770fedfc6503119808497b3677 Mon Sep 17 00:00:00 2001 From: Dominik Brodowski Date: Mon, 27 Jun 2005 16:28:50 -0700 Subject: [PATCH] pcmcia: merge struct pcmcia_bus_socket into struct pcmcia_socket Merge struct pcmcia_bus_socket into struct pcmcia_socket. Signed-off-by: Dominik Brodowski Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/pcmcia/ds.c | 123 ++++++++++++++---------------------------- drivers/pcmcia/ds_internal.h | 45 ++-------------- drivers/pcmcia/pcmcia_ioctl.c | 97 +++++++++++++++------------------ include/pcmcia/ss.h | 31 +++++++++-- 4 files changed, 118 insertions(+), 178 deletions(-) (limited to 'include') diff --git a/drivers/pcmcia/ds.c b/drivers/pcmcia/ds.c index 85e2cb2b9e9..f796dcd64dc 100644 --- a/drivers/pcmcia/ds.c +++ b/drivers/pcmcia/ds.c @@ -75,7 +75,7 @@ module_param_named(pc_debug, ds_pc_debug, int, 0644); spinlock_t pcmcia_dev_list_lock; -static int unbind_request(struct pcmcia_bus_socket *s); +static int unbind_request(struct pcmcia_socket *s); /*====================================================================*/ @@ -313,24 +313,6 @@ static inline int pcmcia_load_firmware(struct pcmcia_device *dev, char * filenam /*======================================================================*/ -void pcmcia_release_bus_socket(struct kref *refcount) -{ - struct pcmcia_bus_socket *s = container_of(refcount, struct pcmcia_bus_socket, refcount); - pcmcia_put_socket(s->parent); - kfree(s); -} - -void pcmcia_put_bus_socket(struct pcmcia_bus_socket *s) -{ - kref_put(&s->refcount, pcmcia_release_bus_socket); -} - -struct pcmcia_bus_socket *pcmcia_get_bus_socket(struct pcmcia_bus_socket *s) -{ - kref_get(&s->refcount); - return (s); -} - /** * pcmcia_register_driver - register a PCMCIA driver with the bus core * @@ -387,7 +369,7 @@ static void pcmcia_release_dev(struct device *dev) { struct pcmcia_device *p_dev = to_pcmcia_dev(dev); ds_dbg(1, "releasing dev %p\n", p_dev); - pcmcia_put_bus_socket(p_dev->socket->pcmcia); + pcmcia_put_socket(p_dev->socket); kfree(p_dev); } @@ -522,12 +504,12 @@ static int pcmcia_device_query(struct pcmcia_device *p_dev) */ static DECLARE_MUTEX(device_add_lock); -struct pcmcia_device * pcmcia_device_add(struct pcmcia_bus_socket *s, unsigned int function) +struct pcmcia_device * pcmcia_device_add(struct pcmcia_socket *s, unsigned int function) { struct pcmcia_device *p_dev; unsigned long flags; - s = pcmcia_get_bus_socket(s); + s = pcmcia_get_socket(s); if (!s) return NULL; @@ -542,18 +524,18 @@ struct pcmcia_device * pcmcia_device_add(struct pcmcia_bus_socket *s, unsigned i goto err_put; memset(p_dev, 0, sizeof(struct pcmcia_device)); - p_dev->socket = s->parent; + p_dev->socket = s; p_dev->device_no = (s->device_count++); p_dev->func = function; p_dev->dev.bus = &pcmcia_bus_type; - p_dev->dev.parent = s->parent->dev.dev; + p_dev->dev.parent = s->dev.dev; p_dev->dev.release = pcmcia_release_dev; sprintf (p_dev->dev.bus_id, "%d.%d", p_dev->socket->sock, p_dev->device_no); /* compat */ p_dev->client.client_magic = CLIENT_MAGIC; - p_dev->client.Socket = s->parent; + p_dev->client.Socket = s; p_dev->client.Function = function; p_dev->client.state = CLIENT_UNBOUND; @@ -581,7 +563,7 @@ struct pcmcia_device * pcmcia_device_add(struct pcmcia_bus_socket *s, unsigned i s->device_count--; err_put: up(&device_add_lock); - pcmcia_put_bus_socket(s); + pcmcia_put_socket(s); return NULL; } @@ -612,7 +594,7 @@ static int pcmcia_card_add(struct pcmcia_socket *s) /* this doesn't handle multifunction devices on one pcmcia function * yet. */ for (i=0; i < no_funcs; i++) - pcmcia_device_add(s->pcmcia, i); + pcmcia_device_add(s, i); return (ret); } @@ -620,12 +602,12 @@ static int pcmcia_card_add(struct pcmcia_socket *s) static void pcmcia_delayed_add_pseudo_device(void *data) { - struct pcmcia_bus_socket *s = data; + struct pcmcia_socket *s = data; pcmcia_device_add(s, 0); s->pcmcia_state.device_add_pending = 0; } -static inline void pcmcia_add_pseudo_device(struct pcmcia_bus_socket *s) +static inline void pcmcia_add_pseudo_device(struct pcmcia_socket *s) { if (!s->pcmcia_state.device_add_pending) { schedule_work(&s->device_add); @@ -650,7 +632,7 @@ static void pcmcia_bus_rescan(struct pcmcia_socket *skt) /* must be called with skt_sem held */ spin_lock_irqsave(&pcmcia_dev_list_lock, flags); - if (list_empty(&skt->pcmcia->devices_list)) + if (list_empty(&skt->devices_list)) no_devices=1; spin_unlock_irqrestore(&pcmcia_dev_list_lock, flags); @@ -727,7 +709,7 @@ static inline int pcmcia_devmatch(struct pcmcia_device *dev, * pseudo devices, and if not, add the second one. */ if (dev->device_no == 0) - pcmcia_add_pseudo_device(dev->socket->pcmcia); + pcmcia_add_pseudo_device(dev->socket); if (dev->device_no != did->device_no) return 0; @@ -947,21 +929,13 @@ static int send_event_callback(struct device *dev, void * _data) static int send_event(struct pcmcia_socket *s, event_t event, int priority) { - int ret = 0; struct send_event_data private; - struct pcmcia_bus_socket *skt = pcmcia_get_bus_socket(s->pcmcia); - - if (!skt) - return 0; private.skt = s; private.event = event; private.priority = priority; - ret = bus_for_each_dev(&pcmcia_bus_type, NULL, &private, send_event_callback); - - pcmcia_put_bus_socket(skt); - return ret; + return bus_for_each_dev(&pcmcia_bus_type, NULL, &private, send_event_callback); } /* send_event */ @@ -972,25 +946,25 @@ static int send_event(struct pcmcia_socket *s, event_t event, int priority) static int ds_event(struct pcmcia_socket *skt, event_t event, int priority) { - struct pcmcia_bus_socket *s = skt->pcmcia; + struct pcmcia_socket *s = pcmcia_get_socket(skt); int ret = 0; ds_dbg(1, "ds_event(0x%06x, %d, 0x%p)\n", - event, priority, s); + event, priority, skt); switch (event) { case CS_EVENT_CARD_REMOVAL: s->pcmcia_state.present = 0; send_event(skt, event, priority); - unbind_request(s); - handle_event(s, event); + unbind_request(skt); + handle_event(skt, event); break; case CS_EVENT_CARD_INSERTION: s->pcmcia_state.present = 1; pcmcia_card_add(skt); - handle_event(s, event); + handle_event(skt, event); break; case CS_EVENT_EJECTION_REQUEST: @@ -998,11 +972,13 @@ static int ds_event(struct pcmcia_socket *skt, event_t event, int priority) break; default: - handle_event(s, event); + handle_event(skt, event); send_event(skt, event, priority); break; } + pcmcia_put_socket(s); + return 0; } /* ds_event */ @@ -1011,8 +987,7 @@ static int ds_event(struct pcmcia_socket *skt, event_t event, int priority) int pcmcia_register_client(client_handle_t *handle, client_reg_t *req) { client_t *client = NULL; - struct pcmcia_socket *s; - struct pcmcia_bus_socket *skt = NULL; + struct pcmcia_socket *s = NULL; struct pcmcia_device *p_dev = NULL; /* Look for unbound client with matching dev_info */ @@ -1023,14 +998,11 @@ int pcmcia_register_client(client_handle_t *handle, client_reg_t *req) if (s->state & SOCKET_CARDBUS) continue; - skt = s->pcmcia; - if (!skt) - continue; - skt = pcmcia_get_bus_socket(skt); - if (!skt) + s = pcmcia_get_socket(s); + if (!s) continue; spin_lock_irqsave(&pcmcia_dev_list_lock, flags); - list_for_each_entry(p_dev, &skt->devices_list, socket_device_list) { + list_for_each_entry(p_dev, &s->devices_list, socket_device_list) { struct pcmcia_driver *p_drv; p_dev = pcmcia_get_dev(p_dev); if (!p_dev) @@ -1049,14 +1021,14 @@ int pcmcia_register_client(client_handle_t *handle, client_reg_t *req) pcmcia_put_dev(p_dev); } spin_unlock_irqrestore(&pcmcia_dev_list_lock, flags); - pcmcia_put_bus_socket(skt); + pcmcia_put_socket(s); } found: up_read(&pcmcia_socket_list_rwsem); if (!p_dev || !client) return -ENODEV; - pcmcia_put_bus_socket(skt); /* safe, as we already hold a reference from bind_device */ + pcmcia_put_socket(s); /* safe, as we already hold a reference from bind_device */ *handle = client; client->state &= ~CLIENT_UNBOUND; @@ -1106,12 +1078,12 @@ EXPORT_SYMBOL(pcmcia_register_client); /* unbind _all_ devices attached to a given pcmcia_bus_socket. The * drivers have been called with EVENT_CARD_REMOVAL before. */ -static int unbind_request(struct pcmcia_bus_socket *s) +static int unbind_request(struct pcmcia_socket *s) { struct pcmcia_device *p_dev; unsigned long flags; - ds_dbg(2, "unbind_request(%d)\n", s->parent->sock); + ds_dbg(2, "unbind_request(%d)\n", s->sock); s->device_count = 0; @@ -1176,24 +1148,14 @@ static struct pcmcia_callback pcmcia_bus_callback = { static int __devinit pcmcia_bus_add_socket(struct class_device *class_dev) { struct pcmcia_socket *socket = class_get_devdata(class_dev); - struct pcmcia_bus_socket *s; int ret; - s = kmalloc(sizeof(struct pcmcia_bus_socket), GFP_KERNEL); - if(!s) - return -ENOMEM; - memset(s, 0, sizeof(struct pcmcia_bus_socket)); - - /* get reference to parent socket */ - s->parent = pcmcia_get_socket(socket); - if (!s->parent) { + socket = pcmcia_get_socket(socket); + if (!socket) { printk(KERN_ERR "PCMCIA obtaining reference to socket %p failed\n", socket); - kfree (s); return -ENODEV; } - kref_init(&s->refcount); - /* * Ugly. But we want to wait for the socket threads to have started up. * We really should let the drivers themselves drive some of this.. @@ -1201,19 +1163,17 @@ static int __devinit pcmcia_bus_add_socket(struct class_device *class_dev) msleep(250); #ifdef CONFIG_PCMCIA_IOCTL - init_waitqueue_head(&s->queue); + init_waitqueue_head(&socket->queue); #endif - INIT_LIST_HEAD(&s->devices_list); - INIT_WORK(&s->device_add, pcmcia_delayed_add_pseudo_device, s); - - /* Set up hotline to Card Services */ - socket->pcmcia = s; + INIT_LIST_HEAD(&socket->devices_list); + INIT_WORK(&socket->device_add, pcmcia_delayed_add_pseudo_device, socket); + memset(&socket->pcmcia_state, 0, sizeof(u8)); + socket->device_count = 0; ret = pccard_register_pcmcia(socket, &pcmcia_bus_callback); if (ret) { printk(KERN_ERR "PCMCIA registration PCCard core failed for socket %p\n", socket); - pcmcia_put_bus_socket(s); - socket->pcmcia = NULL; + pcmcia_put_socket(socket); return (ret); } @@ -1224,14 +1184,13 @@ static void pcmcia_bus_remove_socket(struct class_device *class_dev) { struct pcmcia_socket *socket = class_get_devdata(class_dev); - if (!socket || !socket->pcmcia) + if (!socket) return; + socket->pcmcia_state.dead = 1; pccard_register_pcmcia(socket, NULL); - socket->pcmcia->pcmcia_state.dead = 1; - pcmcia_put_bus_socket(socket->pcmcia); - socket->pcmcia = NULL; + pcmcia_put_socket(socket); return; } diff --git a/drivers/pcmcia/ds_internal.h b/drivers/pcmcia/ds_internal.h index 2c3bb189dec..d359bd25a51 100644 --- a/drivers/pcmcia/ds_internal.h +++ b/drivers/pcmcia/ds_internal.h @@ -1,56 +1,21 @@ /* ds_internal.h - internal header for 16-bit PCMCIA devices management */ -struct user_info_t; - -/* Socket state information */ -struct pcmcia_bus_socket { - struct kref refcount; - struct pcmcia_socket *parent; - - /* the PCMCIA devices connected to this socket (normally one, more - * for multifunction devices: */ - struct list_head devices_list; - u8 device_count; /* the number of devices, used - * only internally and subject - * to incorrectness and change */ - - struct { - u8 present:1, - busy:1, - dead:1, - device_add_pending:1, - reserved:4; - } pcmcia_state; - - struct work_struct device_add; - - -#ifdef CONFIG_PCMCIA_IOCTL - struct user_info_t *user; - wait_queue_head_t queue; -#endif -}; extern spinlock_t pcmcia_dev_list_lock; - extern struct bus_type pcmcia_bus_type; - extern struct pcmcia_device * pcmcia_get_dev(struct pcmcia_device *p_dev); extern void pcmcia_put_dev(struct pcmcia_device *p_dev); -struct pcmcia_bus_socket *pcmcia_get_bus_socket(struct pcmcia_bus_socket *s); -void pcmcia_put_bus_socket(struct pcmcia_bus_socket *s); - -struct pcmcia_device * pcmcia_device_add(struct pcmcia_bus_socket *s, unsigned int function); +struct pcmcia_device * pcmcia_device_add(struct pcmcia_socket *s, unsigned int function); #ifdef CONFIG_PCMCIA_IOCTL extern void __init pcmcia_setup_ioctl(void); extern void __exit pcmcia_cleanup_ioctl(void); -extern void handle_event(struct pcmcia_bus_socket *s, event_t event); -extern int handle_request(struct pcmcia_bus_socket *s, event_t event); +extern void handle_event(struct pcmcia_socket *s, event_t event); +extern int handle_request(struct pcmcia_socket *s, event_t event); #else static inline void __init pcmcia_setup_ioctl(void) { return; } static inline void __init pcmcia_cleanup_ioctl(void) { return; } -static inline void handle_event(struct pcmcia_bus_socket *s, event_t event) { return; } -static inline int handle_request(struct pcmcia_bus_socket *s, event_t event) { return CS_SUCCESS; } +static inline void handle_event(struct pcmcia_socket *s, event_t event) { return; } +static inline int handle_request(struct pcmcia_socket *s, event_t event) { return CS_SUCCESS; } #endif diff --git a/drivers/pcmcia/pcmcia_ioctl.c b/drivers/pcmcia/pcmcia_ioctl.c index b223f5235de..0bebfdbdc27 100644 --- a/drivers/pcmcia/pcmcia_ioctl.c +++ b/drivers/pcmcia/pcmcia_ioctl.c @@ -70,7 +70,7 @@ typedef struct user_info_t { int event_head, event_tail; event_t event[MAX_EVENTS]; struct user_info_t *next; - struct pcmcia_bus_socket *socket; + struct pcmcia_socket *socket; } user_info_t; @@ -87,15 +87,6 @@ extern int ds_pc_debug; #endif -static struct pcmcia_bus_socket * get_socket_info_by_nr(unsigned int nr) -{ - struct pcmcia_socket * s = pcmcia_get_socket_by_nr(nr); - if (s && s->pcmcia) - return s->pcmcia; - else - return NULL; -} - /* backwards-compatible accessing of driver --- by name! */ static struct pcmcia_driver * get_pcmcia_driver (dev_info_t *dev_info) @@ -172,7 +163,7 @@ static void queue_event(user_info_t *user, event_t event) user->event[user->event_head] = event; } -void handle_event(struct pcmcia_bus_socket *s, event_t event) +void handle_event(struct pcmcia_socket *s, event_t event) { user_info_t *user; for (user = s->user; user; user = user->next) @@ -204,18 +195,18 @@ void handle_event(struct pcmcia_bus_socket *s, event_t event) ======================================================================*/ -static int bind_request(struct pcmcia_bus_socket *s, bind_info_t *bind_info) +static int bind_request(struct pcmcia_socket *s, bind_info_t *bind_info) { struct pcmcia_driver *p_drv; struct pcmcia_device *p_dev; int ret = 0; unsigned long flags; - s = pcmcia_get_bus_socket(s); + s = pcmcia_get_socket(s); if (!s) return -EINVAL; - ds_dbg(2, "bind_request(%d, '%s')\n", s->parent->sock, + ds_dbg(2, "bind_request(%d, '%s')\n", s->sock, (char *)bind_info->dev_info); p_drv = get_pcmcia_driver(&bind_info->dev_info); @@ -278,9 +269,9 @@ rescan: /* * Prevent this racing with a card insertion. */ - down(&s->parent->skt_sem); + down(&s->skt_sem); bus_rescan_devices(&pcmcia_bus_type); - up(&s->parent->skt_sem); + up(&s->skt_sem); /* check whether the driver indeed matched. I don't care if this * is racy or not, because it can only happen on cardmgr access @@ -294,7 +285,7 @@ rescan: err_put_driver: put_driver(&p_drv->drv); err_put: - pcmcia_put_bus_socket(s); + pcmcia_put_socket(s); return (ret); } /* bind_request */ @@ -302,7 +293,7 @@ rescan: extern struct pci_bus *pcmcia_lookup_bus(struct pcmcia_socket *s); -static int get_device_info(struct pcmcia_bus_socket *s, bind_info_t *bind_info, int first) +static int get_device_info(struct pcmcia_socket *s, bind_info_t *bind_info, int first) { dev_node_t *node; struct pcmcia_device *p_dev; @@ -317,7 +308,7 @@ static int get_device_info(struct pcmcia_bus_socket *s, bind_info_t *bind_info, { struct pci_bus *bus; - bus = pcmcia_lookup_bus(s->parent); + bus = pcmcia_lookup_bus(s); if (bus) { struct list_head *list; struct pci_dev *dev = NULL; @@ -391,21 +382,21 @@ static int get_device_info(struct pcmcia_bus_socket *s, bind_info_t *bind_info, static int ds_open(struct inode *inode, struct file *file) { socket_t i = iminor(inode); - struct pcmcia_bus_socket *s; + struct pcmcia_socket *s; user_info_t *user; ds_dbg(0, "ds_open(socket %d)\n", i); - s = get_socket_info_by_nr(i); + s = pcmcia_get_socket_by_nr(i); if (!s) return -ENODEV; - s = pcmcia_get_bus_socket(s); + s = pcmcia_get_socket(s); if (!s) return -ENODEV; if ((file->f_flags & O_ACCMODE) != O_RDONLY) { if (s->pcmcia_state.busy) { - pcmcia_put_bus_socket(s); + pcmcia_put_socket(s); return -EBUSY; } else @@ -414,7 +405,7 @@ static int ds_open(struct inode *inode, struct file *file) user = kmalloc(sizeof(user_info_t), GFP_KERNEL); if (!user) { - pcmcia_put_bus_socket(s); + pcmcia_put_socket(s); return -ENOMEM; } user->event_tail = user->event_head = 0; @@ -433,7 +424,7 @@ static int ds_open(struct inode *inode, struct file *file) static int ds_release(struct inode *inode, struct file *file) { - struct pcmcia_bus_socket *s; + struct pcmcia_socket *s; user_info_t *user, **link; ds_dbg(0, "ds_release(socket %d)\n", iminor(inode)); @@ -456,7 +447,7 @@ static int ds_release(struct inode *inode, struct file *file) *link = user->next; user->user_magic = 0; kfree(user); - pcmcia_put_bus_socket(s); + pcmcia_put_socket(s); out: return 0; } /* ds_release */ @@ -466,7 +457,7 @@ out: static ssize_t ds_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) { - struct pcmcia_bus_socket *s; + struct pcmcia_socket *s; user_info_t *user; int ret; @@ -510,7 +501,7 @@ static ssize_t ds_write(struct file *file, const char __user *buf, /* No kernel lock - fine */ static u_int ds_poll(struct file *file, poll_table *wait) { - struct pcmcia_bus_socket *s; + struct pcmcia_socket *s; user_info_t *user; ds_dbg(2, "ds_poll(socket %d)\n", iminor(file->f_dentry->d_inode)); @@ -536,7 +527,7 @@ extern int pcmcia_adjust_resource_info(adjust_t *adj); static int ds_ioctl(struct inode * inode, struct file * file, u_int cmd, u_long arg) { - struct pcmcia_bus_socket *s; + struct pcmcia_socket *s; void __user *uarg = (char __user *)arg; u_int size; int ret, err; @@ -589,57 +580,57 @@ static int ds_ioctl(struct inode * inode, struct file * file, break; case DS_GET_CONFIGURATION_INFO: if (buf->config.Function && - (buf->config.Function >= s->parent->functions)) + (buf->config.Function >= s->functions)) ret = CS_BAD_ARGS; else - ret = pccard_get_configuration_info(s->parent, + ret = pccard_get_configuration_info(s, buf->config.Function, &buf->config); break; case DS_GET_FIRST_TUPLE: - down(&s->parent->skt_sem); - pcmcia_validate_mem(s->parent); - up(&s->parent->skt_sem); - ret = pccard_get_first_tuple(s->parent, BIND_FN_ALL, &buf->tuple); + down(&s->skt_sem); + pcmcia_validate_mem(s); + up(&s->skt_sem); + ret = pccard_get_first_tuple(s, BIND_FN_ALL, &buf->tuple); break; case DS_GET_NEXT_TUPLE: - ret = pccard_get_next_tuple(s->parent, BIND_FN_ALL, &buf->tuple); + ret = pccard_get_next_tuple(s, BIND_FN_ALL, &buf->tuple); break; case DS_GET_TUPLE_DATA: buf->tuple.TupleData = buf->tuple_parse.data; buf->tuple.TupleDataMax = sizeof(buf->tuple_parse.data); - ret = pccard_get_tuple_data(s->parent, &buf->tuple); + ret = pccard_get_tuple_data(s, &buf->tuple); break; case DS_PARSE_TUPLE: buf->tuple.TupleData = buf->tuple_parse.data; ret = pccard_parse_tuple(&buf->tuple, &buf->tuple_parse.parse); break; case DS_RESET_CARD: - ret = pccard_reset_card(s->parent); + ret = pccard_reset_card(s); break; case DS_GET_STATUS: if (buf->status.Function && - (buf->status.Function >= s->parent->functions)) + (buf->status.Function >= s->functions)) ret = CS_BAD_ARGS; else - ret = pccard_get_status(s->parent, buf->status.Function, &buf->status); + ret = pccard_get_status(s, buf->status.Function, &buf->status); break; case DS_VALIDATE_CIS: - down(&s->parent->skt_sem); - pcmcia_validate_mem(s->parent); - up(&s->parent->skt_sem); - ret = pccard_validate_cis(s->parent, BIND_FN_ALL, &buf->cisinfo); + down(&s->skt_sem); + pcmcia_validate_mem(s); + up(&s->skt_sem); + ret = pccard_validate_cis(s, BIND_FN_ALL, &buf->cisinfo); break; case DS_SUSPEND_CARD: - ret = pcmcia_suspend_card(s->parent); + ret = pcmcia_suspend_card(s); break; case DS_RESUME_CARD: - ret = pcmcia_resume_card(s->parent); + ret = pcmcia_resume_card(s); break; case DS_EJECT_CARD: - err = pcmcia_eject_card(s->parent); + err = pcmcia_eject_card(s); break; case DS_INSERT_CARD: - err = pcmcia_insert_card(s->parent); + err = pcmcia_insert_card(s); break; case DS_ACCESS_CONFIGURATION_REGISTER: if ((buf->conf_reg.Action == CS_WRITE) && !capable(CAP_SYS_ADMIN)) { @@ -647,10 +638,10 @@ static int ds_ioctl(struct inode * inode, struct file * file, goto free_out; } if (buf->conf_reg.Function && - (buf->conf_reg.Function >= s->parent->functions)) + (buf->conf_reg.Function >= s->functions)) ret = CS_BAD_ARGS; else - ret = pccard_access_configuration_register(s->parent, + ret = pccard_access_configuration_register(s, buf->conf_reg.Function, &buf->conf_reg); break; case DS_GET_FIRST_REGION: @@ -671,11 +662,11 @@ static int ds_ioctl(struct inode * inode, struct file * file, goto free_out; break; case DS_GET_FIRST_WINDOW: - ret = pcmcia_get_window(s->parent, &buf->win_info.handle, 0, + ret = pcmcia_get_window(s, &buf->win_info.handle, 0, &buf->win_info.window); break; case DS_GET_NEXT_WINDOW: - ret = pcmcia_get_window(s->parent, &buf->win_info.handle, + ret = pcmcia_get_window(s, &buf->win_info.handle, buf->win_info.handle->index + 1, &buf->win_info.window); break; case DS_GET_MEM_PAGE: @@ -683,7 +674,7 @@ static int ds_ioctl(struct inode * inode, struct file * file, &buf->win_info.map); break; case DS_REPLACE_CIS: - ret = pcmcia_replace_cis(s->parent, &buf->cisdump); + ret = pcmcia_replace_cis(s, &buf->cisdump); break; case DS_BIND_REQUEST: if (!capable(CAP_SYS_ADMIN)) { diff --git a/include/pcmcia/ss.h b/include/pcmcia/ss.h index f8797767051..0f7aacc33fe 100644 --- a/include/pcmcia/ss.h +++ b/include/pcmcia/ss.h @@ -15,10 +15,12 @@ #ifndef _LINUX_SS_H #define _LINUX_SS_H +#include +#include + #include #include #include -#include /* Definitions for card status flags for GetStatus */ #define SS_WRPROT 0x0001 @@ -171,7 +173,7 @@ typedef struct window_t { struct config_t; struct pcmcia_callback; - +struct user_info_t; struct pcmcia_socket { struct module *owner; @@ -242,9 +244,32 @@ struct pcmcia_socket { unsigned int thread_events; /* pcmcia (16-bit) */ - struct pcmcia_bus_socket *pcmcia; struct pcmcia_callback *callback; +#if defined(CONFIG_PCMCIA) || defined(CONFIG_PCMCIA_MODULE) + struct list_head devices_list; /* PCMCIA devices */ + u8 device_count; /* the number of devices, used + * only internally and subject + * to incorrectness and change */ + + struct { + u8 present:1, /* PCMCIA card is present in socket */ + busy:1, /* "master" ioctl is used */ + dead:1, /* pcmcia module is being unloaded */ + device_add_pending:1, /* a pseudo-multifunction-device + * add event is pending */ + reserved:4; + } pcmcia_state; + + struct work_struct device_add; /* for adding further pseudo-multifunction + * devices */ + +#ifdef CONFIG_PCMCIA_IOCTL + struct user_info_t *user; + wait_queue_head_t queue; +#endif +#endif + /* cardbus (32-bit) */ #ifdef CONFIG_CARDBUS struct resource * cb_cis_res; -- cgit v1.2.3-70-g09d2 From 3448139b41b9e3b8799eed7d427cd50789dadc3e Mon Sep 17 00:00:00 2001 From: Dominik Brodowski Date: Mon, 27 Jun 2005 16:28:54 -0700 Subject: [PATCH] pcmcia: ds.c cleanup Clean up ds.c Signed-off-by: Dominik Brodowski Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/pcmcia/pcmcia_compat.c | 34 ---------------------- drivers/pcmcia/pcmcia_ioctl.c | 23 +++++++++++++++ drivers/pcmcia/pcmcia_resource.c | 62 +++++++++++++++++++++++----------------- include/pcmcia/cs.h | 1 - 4 files changed, 58 insertions(+), 62 deletions(-) (limited to 'include') diff --git a/drivers/pcmcia/pcmcia_compat.c b/drivers/pcmcia/pcmcia_compat.c index 68b80084f83..1cc83317e7e 100644 --- a/drivers/pcmcia/pcmcia_compat.c +++ b/drivers/pcmcia/pcmcia_compat.c @@ -74,19 +74,6 @@ int pcmcia_validate_cis(client_handle_t handle, cisinfo_t *info) } EXPORT_SYMBOL(pcmcia_validate_cis); -int pcmcia_get_configuration_info(client_handle_t handle, - config_info_t *config) -{ - struct pcmcia_socket *s; - - if ((CHECK_HANDLE(handle)) || !config) - return CS_BAD_HANDLE; - s = SOCKET(handle); - if (!s) - return CS_BAD_HANDLE; - return pccard_get_configuration_info(s, handle->Function, config); -} -EXPORT_SYMBOL(pcmcia_get_configuration_info); int pcmcia_reset_card(client_handle_t handle, client_req_t *req) { @@ -102,24 +89,3 @@ int pcmcia_reset_card(client_handle_t handle, client_req_t *req) } EXPORT_SYMBOL(pcmcia_reset_card); -int pcmcia_get_status(client_handle_t handle, cs_status_t *status) -{ - struct pcmcia_socket *s; - if (CHECK_HANDLE(handle)) - return CS_BAD_HANDLE; - s = SOCKET(handle); - return pccard_get_status(s, handle->Function, status); -} -EXPORT_SYMBOL(pcmcia_get_status); - -int pcmcia_access_configuration_register(client_handle_t handle, - conf_reg_t *reg) -{ - struct pcmcia_socket *s; - if (CHECK_HANDLE(handle)) - return CS_BAD_HANDLE; - s = SOCKET(handle); - return pccard_access_configuration_register(s, handle->Function, reg); -} -EXPORT_SYMBOL(pcmcia_access_configuration_register); - diff --git a/drivers/pcmcia/pcmcia_ioctl.c b/drivers/pcmcia/pcmcia_ioctl.c index fa2a79d29e1..3084d8a3ba4 100644 --- a/drivers/pcmcia/pcmcia_ioctl.c +++ b/drivers/pcmcia/pcmcia_ioctl.c @@ -71,6 +71,29 @@ extern int ds_pc_debug; #define ds_dbg(lvl, fmt, arg...) do { } while (0) #endif +static const char *release = "Linux Kernel Card Services"; + +/** pcmcia_get_card_services_info + * + * Return information about this version of Card Services + */ +static int pcmcia_get_card_services_info(servinfo_t *info) +{ + unsigned int socket_count = 0; + struct list_head *tmp; + info->Signature[0] = 'C'; + info->Signature[1] = 'S'; + down_read(&pcmcia_socket_list_rwsem); + list_for_each(tmp, &pcmcia_socket_list) + socket_count++; + up_read(&pcmcia_socket_list_rwsem); + info->Count = socket_count; + info->Revision = CS_RELEASE_CODE; + info->CSLevel = 0x0210; + info->VendorString = (char *)release; + return CS_SUCCESS; +} /* get_card_services_info */ + /* backwards-compatible accessing of driver --- by name! */ diff --git a/drivers/pcmcia/pcmcia_resource.c b/drivers/pcmcia/pcmcia_resource.c index cf66b997894..9ed3d4a9b99 100644 --- a/drivers/pcmcia/pcmcia_resource.c +++ b/drivers/pcmcia/pcmcia_resource.c @@ -36,8 +36,6 @@ #include "ds_internal.h" -static const char *release = "Linux Kernel Card Services"; - /* Access speed for IO windows */ static int io_speed = 0; module_param(io_speed, int, 0444); @@ -202,7 +200,18 @@ int pccard_access_configuration_register(struct pcmcia_socket *s, } return CS_SUCCESS; } /* pccard_access_configuration_register */ -EXPORT_SYMBOL(pccard_access_configuration_register); + +int pcmcia_access_configuration_register(client_handle_t handle, + conf_reg_t *reg) +{ + struct pcmcia_socket *s; + if (CHECK_HANDLE(handle)) + return CS_BAD_HANDLE; + s = SOCKET(handle); + return pccard_access_configuration_register(s, handle->Function, reg); +} +EXPORT_SYMBOL(pcmcia_access_configuration_register); + int pccard_get_configuration_info(struct pcmcia_socket *s, @@ -260,31 +269,20 @@ int pccard_get_configuration_info(struct pcmcia_socket *s, return CS_SUCCESS; } /* pccard_get_configuration_info */ -EXPORT_SYMBOL(pccard_get_configuration_info); - -/** pcmcia_get_card_services_info - * - * Return information about this version of Card Services - */ - -int pcmcia_get_card_services_info(servinfo_t *info) +int pcmcia_get_configuration_info(client_handle_t handle, + config_info_t *config) { - unsigned int socket_count = 0; - struct list_head *tmp; - info->Signature[0] = 'C'; - info->Signature[1] = 'S'; - down_read(&pcmcia_socket_list_rwsem); - list_for_each(tmp, &pcmcia_socket_list) - socket_count++; - up_read(&pcmcia_socket_list_rwsem); - info->Count = socket_count; - info->Revision = CS_RELEASE_CODE; - info->CSLevel = 0x0210; - info->VendorString = (char *)release; - return CS_SUCCESS; -} /* get_card_services_info */ -EXPORT_SYMBOL(pcmcia_get_card_services_info); + struct pcmcia_socket *s; + + if ((CHECK_HANDLE(handle)) || !config) + return CS_BAD_HANDLE; + s = SOCKET(handle); + if (!s) + return CS_BAD_HANDLE; + return pccard_get_configuration_info(s, handle->Function, config); +} +EXPORT_SYMBOL(pcmcia_get_configuration_info); /** pcmcia_get_window @@ -379,7 +377,17 @@ int pccard_get_status(struct pcmcia_socket *s, unsigned int function, (val & SS_READY) ? CS_EVENT_READY_CHANGE : 0; return CS_SUCCESS; } /* pccard_get_status */ -EXPORT_SYMBOL(pccard_get_status); + +int pcmcia_get_status(client_handle_t handle, cs_status_t *status) +{ + struct pcmcia_socket *s; + if (CHECK_HANDLE(handle)) + return CS_BAD_HANDLE; + s = SOCKET(handle); + return pccard_get_status(s, handle->Function, status); +} +EXPORT_SYMBOL(pcmcia_get_status); + /** pcmcia_get_mem_page diff --git a/include/pcmcia/cs.h b/include/pcmcia/cs.h index 60a3decea82..b42ddc0c114 100644 --- a/include/pcmcia/cs.h +++ b/include/pcmcia/cs.h @@ -396,7 +396,6 @@ struct pcmcia_socket; int pcmcia_access_configuration_register(client_handle_t handle, conf_reg_t *reg); int pcmcia_deregister_client(client_handle_t handle); int pcmcia_get_configuration_info(client_handle_t handle, config_info_t *config); -int pcmcia_get_card_services_info(servinfo_t *info); int pcmcia_get_first_window(window_handle_t *win, win_req_t *req); int pcmcia_get_next_window(window_handle_t *win, win_req_t *req); int pcmcia_get_status(client_handle_t handle, cs_status_t *status); -- cgit v1.2.3-70-g09d2 From aecab27aeabaa897d69fc082686df314329830de Mon Sep 17 00:00:00 2001 From: Dominik Brodowski Date: Mon, 27 Jun 2005 16:28:56 -0700 Subject: [PATCH] pcmcia: mod_devicetable.h fix for different sizes in kernel- and userspace The size of pointers may differ between (userspace) modpost and (kernelspace) modules -- so fix mod_devicetable.h to reflect this possibility. Signed-off-by: Dominik Brodowski Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/mod_devicetable.h | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/linux/mod_devicetable.h b/include/linux/mod_devicetable.h index 8a8dc82a941..9b6d05172ed 100644 --- a/include/linux/mod_devicetable.h +++ b/include/linux/mod_devicetable.h @@ -191,12 +191,22 @@ struct pcmcia_device_id { /* for pseude multi-function devices */ __u8 device_no; - const char * prod_id[4]; __u32 prod_id_hash[4]; + /* not matched against in kernelspace*/ +#ifdef __KERNEL__ + const char * prod_id[4]; +#else + kernel_ulong_t prod_id[4]; +#endif + /* not matched against */ kernel_ulong_t driver_info; +#ifdef __KERNEL__ char * cisfile; +#else + kernel_ulong_t cisfile; +#endif }; #define PCMCIA_DEV_ID_MATCH_MANF_ID 0x0001 -- cgit v1.2.3-70-g09d2