diff --git a/arch/arm/mach-s3c2410/clock.c b/arch/arm/mach-s3c2410/clock.c index e23f534d4e1d..8d986b8401c2 100644 --- a/arch/arm/mach-s3c2410/clock.c +++ b/arch/arm/mach-s3c2410/clock.c @@ -478,7 +478,7 @@ static int s3c2440_clk_add(struct sys_device *sysdev) { unsigned long upllcon = __raw_readl(S3C2410_UPLLCON); - s3c2440_clk_upll.rate = s3c2410_get_pll(upllcon, clk_xtal.rate) * 2; + s3c2440_clk_upll.rate = s3c2410_get_pll(upllcon, clk_xtal.rate); printk("S3C2440: Clock Support, UPLL %ld.%03ld MHz\n", print_mhz(s3c2440_clk_upll.rate)); diff --git a/arch/arm/mach-s3c2410/s3c2440.c b/arch/arm/mach-s3c2410/s3c2440.c index 9a8cc5ae2255..d4c8281b55f6 100644 --- a/arch/arm/mach-s3c2410/s3c2440.c +++ b/arch/arm/mach-s3c2410/s3c2440.c @@ -192,9 +192,11 @@ void __init s3c2440_map_io(struct map_desc *mach_desc, int size) iotable_init(s3c2440_iodesc, ARRAY_SIZE(s3c2440_iodesc)); iotable_init(mach_desc, size); + /* rename any peripherals used differing from the s3c2410 */ - s3c_device_i2c.name = "s3c2440-i2c"; + s3c_device_i2c.name = "s3c2440-i2c"; + s3c_device_nand.name = "s3c2440-nand"; /* change irq for watchdog */ @@ -225,7 +227,7 @@ void __init s3c2440_init_clocks(int xtal) break; case S3C2440_CLKDIVN_HDIVN_2: - hdiv = 1; + hdiv = 2; break; case S3C2440_CLKDIVN_HDIVN_4_8: diff --git a/arch/arm/mm/Kconfig b/arch/arm/mm/Kconfig index c4fc6be629de..48bac7da8c70 100644 --- a/arch/arm/mm/Kconfig +++ b/arch/arm/mm/Kconfig @@ -412,21 +412,20 @@ config CPU_BPREDICT_DISABLE config TLS_REG_EMUL bool - default y if (SMP || CPU_32v6) && (CPU_32v5 || CPU_32v4 || CPU_32v3) + default y if SMP && (CPU_32v5 || CPU_32v4 || CPU_32v3) help - We might be running on an ARMv6+ processor which should have the TLS - register but for some reason we can't use it, or maybe an SMP system - using a pre-ARMv6 processor (there are apparently a few prototypes - like that in existence) and therefore access to that register must - be emulated. + An SMP system using a pre-ARMv6 processor (there are apparently + a few prototypes like that in existence) and therefore access to + that required register must be emulated. config HAS_TLS_REG bool - depends on CPU_32v6 - default y if !TLS_REG_EMUL + depends on !TLS_REG_EMUL + default y if SMP || CPU_32v7 help This selects support for the CP15 thread register. - It is defined to be available on ARMv6 or later. If a particular - ARMv6 or later CPU doesn't support it then it must omc;ide "select - TLS_REG_EMUL" along with its other caracteristics. + It is defined to be available on some ARMv6 processors (including + all SMP capable ARMv6's) or later processors. User space may + assume directly accessing that register and always obtain the + expected value only on ARMv7 and above. diff --git a/arch/arm/mm/copypage-v4mc.S b/arch/arm/mm/copypage-v4mc.S deleted file mode 100644 index 305af3dab3d8..000000000000 --- a/arch/arm/mm/copypage-v4mc.S +++ /dev/null @@ -1,80 +0,0 @@ -/* - * linux/arch/arm/lib/copy_page-armv4mc.S - * - * Copyright (C) 1995-2001 Russell King - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * ASM optimised string functions - */ -#include -#include -#include - - .text - .align 5 -/* - * ARMv4 mini-dcache optimised copy_user_page - * - * We flush the destination cache lines just before we write the data into the - * corresponding address. Since the Dcache is read-allocate, this removes the - * Dcache aliasing issue. The writes will be forwarded to the write buffer, - * and merged as appropriate. - * - * Note: We rely on all ARMv4 processors implementing the "invalidate D line" - * instruction. If your processor does not supply this, you have to write your - * own copy_user_page that does the right thing. - */ -ENTRY(v4_mc_copy_user_page) - stmfd sp!, {r4, lr} @ 2 - mov r4, r0 - mov r0, r1 - bl map_page_minicache - mov r1, #PAGE_SZ/64 @ 1 - ldmia r0!, {r2, r3, ip, lr} @ 4 -1: mcr p15, 0, r4, c7, c6, 1 @ 1 invalidate D line - stmia r4!, {r2, r3, ip, lr} @ 4 - ldmia r0!, {r2, r3, ip, lr} @ 4+1 - stmia r4!, {r2, r3, ip, lr} @ 4 - ldmia r0!, {r2, r3, ip, lr} @ 4 - mcr p15, 0, r4, c7, c6, 1 @ 1 invalidate D line - stmia r4!, {r2, r3, ip, lr} @ 4 - ldmia r0!, {r2, r3, ip, lr} @ 4 - subs r1, r1, #1 @ 1 - stmia r4!, {r2, r3, ip, lr} @ 4 - ldmneia r0!, {r2, r3, ip, lr} @ 4 - bne 1b @ 1 - ldmfd sp!, {r4, pc} @ 3 - - .align 5 -/* - * ARMv4 optimised clear_user_page - * - * Same story as above. - */ -ENTRY(v4_mc_clear_user_page) - str lr, [sp, #-4]! - mov r1, #PAGE_SZ/64 @ 1 - mov r2, #0 @ 1 - mov r3, #0 @ 1 - mov ip, #0 @ 1 - mov lr, #0 @ 1 -1: mcr p15, 0, r0, c7, c6, 1 @ 1 invalidate D line - stmia r0!, {r2, r3, ip, lr} @ 4 - stmia r0!, {r2, r3, ip, lr} @ 4 - mcr p15, 0, r0, c7, c6, 1 @ 1 invalidate D line - stmia r0!, {r2, r3, ip, lr} @ 4 - stmia r0!, {r2, r3, ip, lr} @ 4 - subs r1, r1, #1 @ 1 - bne 1b @ 1 - ldr pc, [sp], #4 - - __INITDATA - - .type v4_mc_user_fns, #object -ENTRY(v4_mc_user_fns) - .long v4_mc_clear_user_page - .long v4_mc_copy_user_page - .size v4_mc_user_fns, . - v4_mc_user_fns diff --git a/arch/arm/mm/copypage-v4mc.c b/arch/arm/mm/copypage-v4mc.c new file mode 100644 index 000000000000..fc69dccdace1 --- /dev/null +++ b/arch/arm/mm/copypage-v4mc.c @@ -0,0 +1,111 @@ +/* + * linux/arch/arm/lib/copypage-armv4mc.S + * + * Copyright (C) 1995-2005 Russell King + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This handles the mini data cache, as found on SA11x0 and XScale + * processors. When we copy a user page page, we map it in such a way + * that accesses to this page will not touch the main data cache, but + * will be cached in the mini data cache. This prevents us thrashing + * the main data cache on page faults. + */ +#include +#include + +#include +#include +#include + +/* + * 0xffff8000 to 0xffffffff is reserved for any ARM architecture + * specific hacks for copying pages efficiently. + */ +#define minicache_pgprot __pgprot(L_PTE_PRESENT | L_PTE_YOUNG | \ + L_PTE_CACHEABLE) + +#define TOP_PTE(x) pte_offset_kernel(top_pmd, x) + +static DEFINE_SPINLOCK(minicache_lock); + +/* + * ARMv4 mini-dcache optimised copy_user_page + * + * We flush the destination cache lines just before we write the data into the + * corresponding address. Since the Dcache is read-allocate, this removes the + * Dcache aliasing issue. The writes will be forwarded to the write buffer, + * and merged as appropriate. + * + * Note: We rely on all ARMv4 processors implementing the "invalidate D line" + * instruction. If your processor does not supply this, you have to write your + * own copy_user_page that does the right thing. + */ +static void __attribute__((naked)) +mc_copy_user_page(void *from, void *to) +{ + asm volatile( + "stmfd sp!, {r4, lr} @ 2\n\ + mov r4, %2 @ 1\n\ + ldmia %0!, {r2, r3, ip, lr} @ 4\n\ +1: mcr p15, 0, %1, c7, c6, 1 @ 1 invalidate D line\n\ + stmia %1!, {r2, r3, ip, lr} @ 4\n\ + ldmia %0!, {r2, r3, ip, lr} @ 4+1\n\ + stmia %1!, {r2, r3, ip, lr} @ 4\n\ + ldmia %0!, {r2, r3, ip, lr} @ 4\n\ + mcr p15, 0, %1, c7, c6, 1 @ 1 invalidate D line\n\ + stmia %1!, {r2, r3, ip, lr} @ 4\n\ + ldmia %0!, {r2, r3, ip, lr} @ 4\n\ + subs r4, r4, #1 @ 1\n\ + stmia %1!, {r2, r3, ip, lr} @ 4\n\ + ldmneia %0!, {r2, r3, ip, lr} @ 4\n\ + bne 1b @ 1\n\ + ldmfd sp!, {r4, pc} @ 3" + : + : "r" (from), "r" (to), "I" (PAGE_SIZE / 64)); +} + +void v4_mc_copy_user_page(void *kto, const void *kfrom, unsigned long vaddr) +{ + spin_lock(&minicache_lock); + + set_pte(TOP_PTE(0xffff8000), pfn_pte(__pa(kfrom) >> PAGE_SHIFT, minicache_pgprot)); + flush_tlb_kernel_page(0xffff8000); + + mc_copy_user_page((void *)0xffff8000, kto); + + spin_unlock(&minicache_lock); +} + +/* + * ARMv4 optimised clear_user_page + */ +void __attribute__((naked)) +v4_mc_clear_user_page(void *kaddr, unsigned long vaddr) +{ + asm volatile( + "str lr, [sp, #-4]!\n\ + mov r1, %0 @ 1\n\ + mov r2, #0 @ 1\n\ + mov r3, #0 @ 1\n\ + mov ip, #0 @ 1\n\ + mov lr, #0 @ 1\n\ +1: mcr p15, 0, r0, c7, c6, 1 @ 1 invalidate D line\n\ + stmia r0!, {r2, r3, ip, lr} @ 4\n\ + stmia r0!, {r2, r3, ip, lr} @ 4\n\ + mcr p15, 0, r0, c7, c6, 1 @ 1 invalidate D line\n\ + stmia r0!, {r2, r3, ip, lr} @ 4\n\ + stmia r0!, {r2, r3, ip, lr} @ 4\n\ + subs r1, r1, #1 @ 1\n\ + bne 1b @ 1\n\ + ldr pc, [sp], #4" + : + : "I" (PAGE_SIZE / 64)); +} + +struct cpu_user_fns v4_mc_user_fns __initdata = { + .cpu_clear_user_page = v4_mc_clear_user_page, + .cpu_copy_user_page = v4_mc_copy_user_page, +}; diff --git a/arch/arm/mm/copypage-v6.c b/arch/arm/mm/copypage-v6.c index 694ac8208858..a8c00236bd3d 100644 --- a/arch/arm/mm/copypage-v6.c +++ b/arch/arm/mm/copypage-v6.c @@ -26,8 +26,8 @@ #define to_address (0xffffc000) #define to_pgprot PAGE_KERNEL -static pte_t *from_pte; -static pte_t *to_pte; +#define TOP_PTE(x) pte_offset_kernel(top_pmd, x) + static DEFINE_SPINLOCK(v6_lock); #define DCACHE_COLOUR(vaddr) ((vaddr & (SHMLBA - 1)) >> PAGE_SHIFT) @@ -74,8 +74,8 @@ void v6_copy_user_page_aliasing(void *kto, const void *kfrom, unsigned long vadd */ spin_lock(&v6_lock); - set_pte(from_pte + offset, pfn_pte(__pa(kfrom) >> PAGE_SHIFT, from_pgprot)); - set_pte(to_pte + offset, pfn_pte(__pa(kto) >> PAGE_SHIFT, to_pgprot)); + set_pte(TOP_PTE(from_address) + offset, pfn_pte(__pa(kfrom) >> PAGE_SHIFT, from_pgprot)); + set_pte(TOP_PTE(to_address) + offset, pfn_pte(__pa(kto) >> PAGE_SHIFT, to_pgprot)); from = from_address + (offset << PAGE_SHIFT); to = to_address + (offset << PAGE_SHIFT); @@ -114,7 +114,7 @@ void v6_clear_user_page_aliasing(void *kaddr, unsigned long vaddr) */ spin_lock(&v6_lock); - set_pte(to_pte + offset, pfn_pte(__pa(kaddr) >> PAGE_SHIFT, to_pgprot)); + set_pte(TOP_PTE(to_address) + offset, pfn_pte(__pa(kaddr) >> PAGE_SHIFT, to_pgprot)); flush_tlb_kernel_page(to); clear_page((void *)to); @@ -129,21 +129,6 @@ struct cpu_user_fns v6_user_fns __initdata = { static int __init v6_userpage_init(void) { if (cache_is_vipt_aliasing()) { - pgd_t *pgd; - pmd_t *pmd; - - pgd = pgd_offset_k(from_address); - pmd = pmd_alloc(&init_mm, pgd, from_address); - if (!pmd) - BUG(); - from_pte = pte_alloc_kernel(&init_mm, pmd, from_address); - if (!from_pte) - BUG(); - - to_pte = pte_alloc_kernel(&init_mm, pmd, to_address); - if (!to_pte) - BUG(); - cpu_user.cpu_clear_user_page = v6_clear_user_page_aliasing; cpu_user.cpu_copy_user_page = v6_copy_user_page_aliasing; } @@ -151,5 +136,4 @@ static int __init v6_userpage_init(void) return 0; } -__initcall(v6_userpage_init); - +core_initcall(v6_userpage_init); diff --git a/arch/arm/mm/flush.c b/arch/arm/mm/flush.c index c6de48d89503..4085ed983e46 100644 --- a/arch/arm/mm/flush.c +++ b/arch/arm/mm/flush.c @@ -13,6 +13,29 @@ #include #include +#include + +#ifdef CONFIG_CPU_CACHE_VIPT +#define ALIAS_FLUSH_START 0xffff4000 + +#define TOP_PTE(x) pte_offset_kernel(top_pmd, x) + +static void flush_pfn_alias(unsigned long pfn, unsigned long vaddr) +{ + unsigned long to = ALIAS_FLUSH_START + (CACHE_COLOUR(vaddr) << PAGE_SHIFT); + + set_pte(TOP_PTE(to), pfn_pte(pfn, PAGE_KERNEL)); + flush_tlb_kernel_page(to); + + asm( "mcrr p15, 0, %1, %0, c14\n" + " mcrr p15, 0, %1, %0, c5\n" + : + : "r" (to), "r" (to + PAGE_SIZE - L1_CACHE_BYTES) + : "cc"); +} +#else +#define flush_pfn_alias(pfn,vaddr) do { } while (0) +#endif static void __flush_dcache_page(struct address_space *mapping, struct page *page) { @@ -36,6 +59,18 @@ static void __flush_dcache_page(struct address_space *mapping, struct page *page if (!mapping) return; + /* + * This is a page cache page. If we have a VIPT cache, we + * only need to do one flush - which would be at the relevant + * userspace colour, which is congruent with page->index. + */ + if (cache_is_vipt()) { + if (cache_is_vipt_aliasing()) + flush_pfn_alias(page_to_pfn(page), + page->index << PAGE_CACHE_SHIFT); + return; + } + /* * There are possible user space mappings of this page: * - VIVT cache: we need to also write back and invalidate all user @@ -57,8 +92,6 @@ static void __flush_dcache_page(struct address_space *mapping, struct page *page continue; offset = (pgoff - mpnt->vm_pgoff) << PAGE_SHIFT; flush_cache_page(mpnt, mpnt->vm_start + offset, page_to_pfn(page)); - if (cache_is_vipt()) - break; } flush_dcache_mmap_unlock(mapping); } diff --git a/arch/arm/mm/mm-armv.c b/arch/arm/mm/mm-armv.c index 585dfb8e20b9..2c2b93d77d43 100644 --- a/arch/arm/mm/mm-armv.c +++ b/arch/arm/mm/mm-armv.c @@ -37,6 +37,8 @@ pgprot_t pgprot_kernel; EXPORT_SYMBOL(pgprot_kernel); +pmd_t *top_pmd; + struct cachepolicy { const char policy[16]; unsigned int cr_mask; @@ -142,6 +144,16 @@ __setup("noalign", noalign_setup); #define FIRST_KERNEL_PGD_NR (FIRST_USER_PGD_NR + USER_PTRS_PER_PGD) +static inline pmd_t *pmd_off(pgd_t *pgd, unsigned long virt) +{ + return pmd_offset(pgd, virt); +} + +static inline pmd_t *pmd_off_k(unsigned long virt) +{ + return pmd_off(pgd_offset_k(virt), virt); +} + /* * need to get a 16k page for level 1 */ @@ -220,7 +232,7 @@ void free_pgd_slow(pgd_t *pgd) return; /* pgd is always present and good */ - pmd = (pmd_t *)pgd; + pmd = pmd_off(pgd, 0); if (pmd_none(*pmd)) goto free; if (pmd_bad(*pmd)) { @@ -246,9 +258,8 @@ free: static inline void alloc_init_section(unsigned long virt, unsigned long phys, int prot) { - pmd_t *pmdp; + pmd_t *pmdp = pmd_off_k(virt); - pmdp = pmd_offset(pgd_offset_k(virt), virt); if (virt & (1 << 20)) pmdp++; @@ -283,11 +294,9 @@ alloc_init_supersection(unsigned long virt, unsigned long phys, int prot) static inline void alloc_init_page(unsigned long virt, unsigned long phys, unsigned int prot_l1, pgprot_t prot) { - pmd_t *pmdp; + pmd_t *pmdp = pmd_off_k(virt); pte_t *ptep; - pmdp = pmd_offset(pgd_offset_k(virt), virt); - if (pmd_none(*pmdp)) { unsigned long pmdval; ptep = alloc_bootmem_low_pages(2 * PTRS_PER_PTE * @@ -310,7 +319,7 @@ alloc_init_page(unsigned long virt, unsigned long phys, unsigned int prot_l1, pg */ static inline void clear_mapping(unsigned long virt) { - pmd_clear(pmd_offset(pgd_offset_k(virt), virt)); + pmd_clear(pmd_off_k(virt)); } struct mem_types { @@ -578,7 +587,7 @@ void setup_mm_for_reboot(char mode) PMD_TYPE_SECT; if (cpu_arch <= CPU_ARCH_ARMv5) pmdval |= PMD_BIT4; - pmd = pmd_offset(pgd + i, i << PGDIR_SHIFT); + pmd = pmd_off(pgd, i << PGDIR_SHIFT); pmd[0] = __pmd(pmdval); pmd[1] = __pmd(pmdval + (1 << (PGDIR_SHIFT - 1))); flush_pmd_entry(pmd); @@ -675,6 +684,8 @@ void __init memtable_init(struct meminfo *mi) flush_cache_all(); flush_tlb_all(); + + top_pmd = pmd_off_k(0xffff0000); } /* diff --git a/drivers/block/ioctl.c b/drivers/block/ioctl.c index 5e03f5157ef9..6d7bcc9da9e7 100644 --- a/drivers/block/ioctl.c +++ b/drivers/block/ioctl.c @@ -237,3 +237,5 @@ long compat_blkdev_ioctl(struct file *file, unsigned cmd, unsigned long arg) } return ret; } + +EXPORT_SYMBOL_GPL(blkdev_ioctl); diff --git a/drivers/block/pktcdvd.c b/drivers/block/pktcdvd.c index 1a1fa3ccb913..82ccad0a7f1a 100644 --- a/drivers/block/pktcdvd.c +++ b/drivers/block/pktcdvd.c @@ -2406,7 +2406,7 @@ static int pkt_ioctl(struct inode *inode, struct file *file, unsigned int cmd, u case CDROM_LAST_WRITTEN: case CDROM_SEND_PACKET: case SCSI_IOCTL_SEND_COMMAND: - return ioctl_by_bdev(pd->bdev, cmd, arg); + return blkdev_ioctl(pd->bdev->bd_inode, file, cmd, arg); case CDROMEJECT: /* @@ -2414,7 +2414,7 @@ static int pkt_ioctl(struct inode *inode, struct file *file, unsigned int cmd, u * have to unlock it or else the eject command fails. */ pkt_lock_door(pd, 0); - return ioctl_by_bdev(pd->bdev, cmd, arg); + return blkdev_ioctl(pd->bdev->bd_inode, file, cmd, arg); default: printk("pktcdvd: Unknown ioctl for %s (%x)\n", pd->name, cmd); diff --git a/drivers/char/raw.c b/drivers/char/raw.c index a2e33ec79615..131465e8de5a 100644 --- a/drivers/char/raw.c +++ b/drivers/char/raw.c @@ -122,7 +122,7 @@ raw_ioctl(struct inode *inode, struct file *filp, { struct block_device *bdev = filp->private_data; - return ioctl_by_bdev(bdev, command, arg); + return blkdev_ioctl(bdev->bd_inode, filp, command, arg); } static void bind_device(struct raw_config_request *rq) diff --git a/drivers/mmc/Kconfig b/drivers/mmc/Kconfig index 2e70d74fbdee..4991bbd054f3 100644 --- a/drivers/mmc/Kconfig +++ b/drivers/mmc/Kconfig @@ -51,7 +51,7 @@ config MMC_PXA config MMC_WBSD tristate "Winbond W83L51xD SD/MMC Card Interface support" - depends on MMC && ISA && ISA_DMA_API + depends on MMC && ISA_DMA_API help This selects the Winbond(R) W83L51xD Secure digital and Multimedia card Interface. diff --git a/drivers/mmc/wbsd.c b/drivers/mmc/wbsd.c index 39747526c719..b7fbd30b49a0 100644 --- a/drivers/mmc/wbsd.c +++ b/drivers/mmc/wbsd.c @@ -28,7 +28,9 @@ #include #include #include +#include #include +#include #include #include #include @@ -40,7 +42,7 @@ #include "wbsd.h" #define DRIVER_NAME "wbsd" -#define DRIVER_VERSION "1.1" +#define DRIVER_VERSION "1.2" #ifdef CONFIG_MMC_DEBUG #define DBG(x...) \ @@ -52,10 +54,6 @@ #define DBGF(x...) do { } while (0) #endif -static unsigned int io = 0x248; -static unsigned int irq = 6; -static int dma = 2; - #ifdef CONFIG_MMC_DEBUG void DBG_REG(int reg, u8 value) { @@ -78,29 +76,62 @@ void DBG_REG(int reg, u8 value) #define DBG_REG(r, v) do {} while (0) #endif +/* + * Device resources + */ + +#ifdef CONFIG_PNP + +static const struct pnp_device_id pnp_dev_table[] = { + { "WEC0517", 0 }, + { "WEC0518", 0 }, + { "", 0 }, +}; + +MODULE_DEVICE_TABLE(pnp, pnp_dev_table); + +#endif /* CONFIG_PNP */ + +#ifdef CONFIG_PNP +static unsigned int nopnp = 0; +#else +static const unsigned int nopnp = 1; +#endif +static unsigned int io = 0x248; +static unsigned int irq = 6; +static int dma = 2; + /* * Basic functions */ static inline void wbsd_unlock_config(struct wbsd_host* host) { + BUG_ON(host->config == 0); + outb(host->unlock_code, host->config); outb(host->unlock_code, host->config); } static inline void wbsd_lock_config(struct wbsd_host* host) { + BUG_ON(host->config == 0); + outb(LOCK_CODE, host->config); } static inline void wbsd_write_config(struct wbsd_host* host, u8 reg, u8 value) { + BUG_ON(host->config == 0); + outb(reg, host->config); outb(value, host->config + 1); } static inline u8 wbsd_read_config(struct wbsd_host* host, u8 reg) { + BUG_ON(host->config == 0); + outb(reg, host->config); return inb(host->config + 1); } @@ -132,6 +163,13 @@ static void wbsd_init_device(struct wbsd_host* host) setup |= WBSD_FIFO_RESET | WBSD_SOFT_RESET; wbsd_write_index(host, WBSD_IDX_SETUP, setup); + /* + * Set DAT3 to input + */ + setup &= ~WBSD_DAT3_H; + wbsd_write_index(host, WBSD_IDX_SETUP, setup); + host->flags &= ~WBSD_FIGNORE_DETECT; + /* * Read back default clock. */ @@ -147,6 +185,14 @@ static void wbsd_init_device(struct wbsd_host* host) */ wbsd_write_index(host, WBSD_IDX_TAAC, 0x7F); + /* + * Test for card presence + */ + if (inb(host->base + WBSD_CSR) & WBSD_CARDPRESENT) + host->flags |= WBSD_FCARD_PRESENT; + else + host->flags &= ~WBSD_FCARD_PRESENT; + /* * Enable interesting interrupts. */ @@ -407,8 +453,6 @@ static inline void wbsd_get_long_reply(struct wbsd_host* host, } } -static irqreturn_t wbsd_irq(int irq, void *dev_id, struct pt_regs *regs); - static void wbsd_send_command(struct wbsd_host* host, struct mmc_command* cmd) { int i; @@ -646,6 +690,13 @@ static void wbsd_fill_fifo(struct wbsd_host* host) } wbsd_kunmap_sg(host); + + /* + * The controller stops sending interrupts for + * 'FIFO empty' under certain conditions. So we + * need to be a bit more pro-active. + */ + tasklet_schedule(&host->fifo_tasklet); } static void wbsd_prepare_data(struct wbsd_host* host, struct mmc_data* data) @@ -850,9 +901,11 @@ static void wbsd_finish_data(struct wbsd_host* host, struct mmc_data* data) wbsd_request_end(host, host->mrq); } -/* - * MMC Callbacks - */ +/*****************************************************************************\ + * * + * MMC layer callbacks * + * * +\*****************************************************************************/ static void wbsd_request(struct mmc_host* mmc, struct mmc_request* mrq) { @@ -874,7 +927,7 @@ static void wbsd_request(struct mmc_host* mmc, struct mmc_request* mrq) * If there is no card in the slot then * timeout immediatly. */ - if (!(inb(host->base + WBSD_CSR) & WBSD_CARDPRESENT)) + if (!(host->flags & WBSD_FCARD_PRESENT)) { cmd->error = MMC_ERR_TIMEOUT; goto done; @@ -953,33 +1006,50 @@ static void wbsd_set_ios(struct mmc_host* mmc, struct mmc_ios* ios) host->clk = clk; } + /* + * Power up card. + */ if (ios->power_mode != MMC_POWER_OFF) { - /* - * Power up card. - */ pwr = inb(host->base + WBSD_CSR); pwr &= ~WBSD_POWER_N; outb(pwr, host->base + WBSD_CSR); - - /* - * This behaviour is stolen from the - * Windows driver. Don't know why, but - * it is needed. - */ - setup = wbsd_read_index(host, WBSD_IDX_SETUP); - if (ios->bus_mode == MMC_BUSMODE_OPENDRAIN) - setup |= WBSD_DAT3_H; - else - setup &= ~WBSD_DAT3_H; - wbsd_write_index(host, WBSD_IDX_SETUP, setup); - - mdelay(1); } + /* + * MMC cards need to have pin 1 high during init. + * Init time corresponds rather nicely with the bus mode. + * It wreaks havoc with the card detection though so + * that needs to be disabed. + */ + setup = wbsd_read_index(host, WBSD_IDX_SETUP); + if ((ios->power_mode == MMC_POWER_ON) && + (ios->bus_mode == MMC_BUSMODE_OPENDRAIN)) + { + setup |= WBSD_DAT3_H; + host->flags |= WBSD_FIGNORE_DETECT; + } + else + { + setup &= ~WBSD_DAT3_H; + host->flags &= ~WBSD_FIGNORE_DETECT; + } + wbsd_write_index(host, WBSD_IDX_SETUP, setup); + spin_unlock_bh(&host->lock); } +static struct mmc_host_ops wbsd_ops = { + .request = wbsd_request, + .set_ios = wbsd_set_ios, +}; + +/*****************************************************************************\ + * * + * Interrupt handling * + * * +\*****************************************************************************/ + /* * Tasklets */ @@ -1005,17 +1075,33 @@ static void wbsd_tasklet_card(unsigned long param) { struct wbsd_host* host = (struct wbsd_host*)param; u8 csr; + int change = 0; spin_lock(&host->lock); + if (host->flags & WBSD_FIGNORE_DETECT) + { + spin_unlock(&host->lock); + return; + } + csr = inb(host->base + WBSD_CSR); WARN_ON(csr == 0xff); if (csr & WBSD_CARDPRESENT) - DBG("Card inserted\n"); - else + { + if (!(host->flags & WBSD_FCARD_PRESENT)) + { + DBG("Card inserted\n"); + host->flags |= WBSD_FCARD_PRESENT; + change = 1; + } + } + else if (host->flags & WBSD_FCARD_PRESENT) { DBG("Card removed\n"); + host->flags &= ~WBSD_FCARD_PRESENT; + change = 1; if (host->mrq) { @@ -1033,7 +1119,8 @@ static void wbsd_tasklet_card(unsigned long param) */ spin_unlock(&host->lock); - mmc_detect_change(host->mmc); + if (change) + mmc_detect_change(host->mmc); } static void wbsd_tasklet_fifo(unsigned long param) @@ -1200,11 +1287,85 @@ static irqreturn_t wbsd_irq(int irq, void *dev_id, struct pt_regs *regs) return IRQ_HANDLED; } +/*****************************************************************************\ + * * + * Device initialisation and shutdown * + * * +\*****************************************************************************/ + /* - * Support functions for probe + * Allocate/free MMC structure. */ -static int wbsd_scan(struct wbsd_host* host) +static int __devinit wbsd_alloc_mmc(struct device* dev) +{ + struct mmc_host* mmc; + struct wbsd_host* host; + + /* + * Allocate MMC structure. + */ + mmc = mmc_alloc_host(sizeof(struct wbsd_host), dev); + if (!mmc) + return -ENOMEM; + + host = mmc_priv(mmc); + host->mmc = mmc; + + host->dma = -1; + + /* + * Set host parameters. + */ + mmc->ops = &wbsd_ops; + mmc->f_min = 375000; + mmc->f_max = 24000000; + mmc->ocr_avail = MMC_VDD_32_33|MMC_VDD_33_34; + + spin_lock_init(&host->lock); + + /* + * Maximum number of segments. Worst case is one sector per segment + * so this will be 64kB/512. + */ + mmc->max_hw_segs = 128; + mmc->max_phys_segs = 128; + + /* + * Maximum number of sectors in one transfer. Also limited by 64kB + * buffer. + */ + mmc->max_sectors = 128; + + /* + * Maximum segment size. Could be one segment with the maximum number + * of segments. + */ + mmc->max_seg_size = mmc->max_sectors * 512; + + dev_set_drvdata(dev, mmc); + + return 0; +} + +static void __devexit wbsd_free_mmc(struct device* dev) +{ + struct mmc_host* mmc; + + mmc = dev_get_drvdata(dev); + if (!mmc) + return; + + mmc_free_host(mmc); + + dev_set_drvdata(dev, NULL); +} + +/* + * Scan for known chip id:s + */ + +static int __devinit wbsd_scan(struct wbsd_host* host) { int i, j, k; int id; @@ -1258,12 +1419,16 @@ static int wbsd_scan(struct wbsd_host* host) return -ENODEV; } -static int wbsd_request_regions(struct wbsd_host* host) +/* + * Allocate/free io port ranges + */ + +static int __devinit wbsd_request_region(struct wbsd_host* host, int base) { if (io & 0x7) return -EINVAL; - if (!request_region(io, 8, DRIVER_NAME)) + if (!request_region(base, 8, DRIVER_NAME)) return -EIO; host->base = io; @@ -1271,19 +1436,25 @@ static int wbsd_request_regions(struct wbsd_host* host) return 0; } -static void wbsd_release_regions(struct wbsd_host* host) +static void __devexit wbsd_release_regions(struct wbsd_host* host) { if (host->base) release_region(host->base, 8); + + host->base = 0; if (host->config) release_region(host->config, 2); + + host->config = 0; } -static void wbsd_init_dma(struct wbsd_host* host) +/* + * Allocate/free DMA port and buffer + */ + +static void __devinit wbsd_request_dma(struct wbsd_host* host, int dma) { - host->dma = -1; - if (dma < 0) return; @@ -1294,7 +1465,7 @@ static void wbsd_init_dma(struct wbsd_host* host) * We need to allocate a special buffer in * order for ISA to be able to DMA to it. */ - host->dma_buffer = kmalloc(65536, + host->dma_buffer = kmalloc(WBSD_DMA_SIZE, GFP_NOIO | GFP_DMA | __GFP_REPEAT | __GFP_NOWARN); if (!host->dma_buffer) goto free; @@ -1302,7 +1473,8 @@ static void wbsd_init_dma(struct wbsd_host* host) /* * Translate the address to a physical address. */ - host->dma_addr = isa_virt_to_bus(host->dma_buffer); + host->dma_addr = dma_map_single(host->mmc->dev, host->dma_buffer, + WBSD_DMA_SIZE, DMA_BIDIRECTIONAL); /* * ISA DMA must be aligned on a 64k basis. @@ -1325,6 +1497,10 @@ kfree: */ BUG_ON(1); + dma_unmap_single(host->mmc->dev, host->dma_addr, WBSD_DMA_SIZE, + DMA_BIDIRECTIONAL); + host->dma_addr = (dma_addr_t)NULL; + kfree(host->dma_buffer); host->dma_buffer = NULL; @@ -1336,86 +1512,39 @@ err: "Falling back on FIFO.\n", dma); } -static struct mmc_host_ops wbsd_ops = { - .request = wbsd_request, - .set_ios = wbsd_set_ios, -}; +static void __devexit wbsd_release_dma(struct wbsd_host* host) +{ + if (host->dma_addr) + dma_unmap_single(host->mmc->dev, host->dma_addr, WBSD_DMA_SIZE, + DMA_BIDIRECTIONAL); + if (host->dma_buffer) + kfree(host->dma_buffer); + if (host->dma >= 0) + free_dma(host->dma); + + host->dma = -1; + host->dma_buffer = NULL; + host->dma_addr = (dma_addr_t)NULL; +} /* - * Device probe + * Allocate/free IRQ. */ -static int wbsd_probe(struct device* dev) +static int __devinit wbsd_request_irq(struct wbsd_host* host, int irq) { - struct wbsd_host* host = NULL; - struct mmc_host* mmc = NULL; int ret; - /* - * Allocate MMC structure. - */ - mmc = mmc_alloc_host(sizeof(struct wbsd_host), dev); - if (!mmc) - return -ENOMEM; - - host = mmc_priv(mmc); - host->mmc = mmc; - - /* - * Scan for hardware. - */ - ret = wbsd_scan(host); - if (ret) - goto freemmc; - - /* - * Reset the chip. - */ - wbsd_write_config(host, WBSD_CONF_SWRST, 1); - wbsd_write_config(host, WBSD_CONF_SWRST, 0); - - /* - * Allocate I/O ports. - */ - ret = wbsd_request_regions(host); - if (ret) - goto release; - - /* - * Set host parameters. - */ - mmc->ops = &wbsd_ops; - mmc->f_min = 375000; - mmc->f_max = 24000000; - mmc->ocr_avail = MMC_VDD_32_33|MMC_VDD_33_34; - - spin_lock_init(&host->lock); - - /* - * Select SD/MMC function. - */ - wbsd_write_config(host, WBSD_CONF_DEVICE, DEVICE_SD); - - /* - * Set up card detection. - */ - wbsd_write_config(host, WBSD_CONF_PINS, 0x02); - - /* - * Configure I/O port. - */ - wbsd_write_config(host, WBSD_CONF_PORT_HI, host->base >> 8); - wbsd_write_config(host, WBSD_CONF_PORT_LO, host->base & 0xff); - /* * Allocate interrupt. */ + ret = request_irq(irq, wbsd_irq, SA_SHIRQ, DRIVER_NAME, host); if (ret) - goto release; + return ret; host->irq = irq; - + /* * Set up tasklets. */ @@ -1426,138 +1555,323 @@ static int wbsd_probe(struct device* dev) tasklet_init(&host->finish_tasklet, wbsd_tasklet_finish, (unsigned long)host); tasklet_init(&host->block_tasklet, wbsd_tasklet_block, (unsigned long)host); - /* - * Configure interrupt. - */ - wbsd_write_config(host, WBSD_CONF_IRQ, host->irq); - - /* - * Allocate DMA. - */ - wbsd_init_dma(host); - - /* - * If all went well, then configure DMA. - */ - if (host->dma >= 0) - wbsd_write_config(host, WBSD_CONF_DRQ, host->dma); - - /* - * Maximum number of segments. Worst case is one sector per segment - * so this will be 64kB/512. - */ - mmc->max_hw_segs = 128; - mmc->max_phys_segs = 128; - - /* - * Maximum number of sectors in one transfer. Also limited by 64kB - * buffer. - */ - mmc->max_sectors = 128; - - /* - * Maximum segment size. Could be one segment with the maximum number - * of segments. - */ - mmc->max_seg_size = mmc->max_sectors * 512; - - /* - * Enable chip. - */ - wbsd_write_config(host, WBSD_CONF_ENABLE, 1); - - /* - * Power up chip. - */ - wbsd_write_config(host, WBSD_CONF_POWER, 0x20); - - /* - * Power Management stuff. No idea how this works. - * Not tested. - */ -#ifdef CONFIG_PM - wbsd_write_config(host, WBSD_CONF_PME, 0xA0); -#endif - - /* - * Reset the chip into a known state. - */ - wbsd_init_device(host); - - dev_set_drvdata(dev, mmc); - - /* - * Add host to MMC layer. - */ - mmc_add_host(mmc); - - printk(KERN_INFO "%s: W83L51xD id %x at 0x%x irq %d dma %d\n", - mmc->host_name, (int)host->chip_id, (int)host->base, - (int)host->irq, (int)host->dma); - return 0; - -release: - wbsd_release_regions(host); - -freemmc: - mmc_free_host(mmc); - - return ret; } -/* - * Device remove - */ - -static int wbsd_remove(struct device* dev) +static void __devexit wbsd_release_irq(struct wbsd_host* host) { - struct mmc_host* mmc = dev_get_drvdata(dev); - struct wbsd_host* host; - - if (!mmc) - return 0; - - host = mmc_priv(mmc); - - /* - * Unregister host with MMC layer. - */ - mmc_remove_host(mmc); - - /* - * Power down the SD/MMC function. - */ - wbsd_unlock_config(host); - wbsd_write_config(host, WBSD_CONF_DEVICE, DEVICE_SD); - wbsd_write_config(host, WBSD_CONF_ENABLE, 0); - wbsd_lock_config(host); - - /* - * Free resources. - */ - if (host->dma_buffer) - kfree(host->dma_buffer); - - if (host->dma >= 0) - free_dma(host->dma); + if (!host->irq) + return; free_irq(host->irq, host); + host->irq = 0; + tasklet_kill(&host->card_tasklet); tasklet_kill(&host->fifo_tasklet); tasklet_kill(&host->crc_tasklet); tasklet_kill(&host->timeout_tasklet); tasklet_kill(&host->finish_tasklet); tasklet_kill(&host->block_tasklet); +} + +/* + * Allocate all resources for the host. + */ + +static int __devinit wbsd_request_resources(struct wbsd_host* host, + int base, int irq, int dma) +{ + int ret; + /* + * Allocate I/O ports. + */ + ret = wbsd_request_region(host, base); + if (ret) + return ret; + + /* + * Allocate interrupt. + */ + ret = wbsd_request_irq(host, irq); + if (ret) + return ret; + + /* + * Allocate DMA. + */ + wbsd_request_dma(host, dma); + + return 0; +} + +/* + * Release all resources for the host. + */ + +static void __devexit wbsd_release_resources(struct wbsd_host* host) +{ + wbsd_release_dma(host); + wbsd_release_irq(host); wbsd_release_regions(host); +} + +/* + * Configure the resources the chip should use. + */ + +static void __devinit wbsd_chip_config(struct wbsd_host* host) +{ + /* + * Reset the chip. + */ + wbsd_write_config(host, WBSD_CONF_SWRST, 1); + wbsd_write_config(host, WBSD_CONF_SWRST, 0); + + /* + * Select SD/MMC function. + */ + wbsd_write_config(host, WBSD_CONF_DEVICE, DEVICE_SD); - mmc_free_host(mmc); + /* + * Set up card detection. + */ + wbsd_write_config(host, WBSD_CONF_PINS, WBSD_PINS_DETECT_GP11); + + /* + * Configure chip + */ + wbsd_write_config(host, WBSD_CONF_PORT_HI, host->base >> 8); + wbsd_write_config(host, WBSD_CONF_PORT_LO, host->base & 0xff); + + wbsd_write_config(host, WBSD_CONF_IRQ, host->irq); + + if (host->dma >= 0) + wbsd_write_config(host, WBSD_CONF_DRQ, host->dma); + + /* + * Enable and power up chip. + */ + wbsd_write_config(host, WBSD_CONF_ENABLE, 1); + wbsd_write_config(host, WBSD_CONF_POWER, 0x20); +} + +/* + * Check that configured resources are correct. + */ + +static int __devinit wbsd_chip_validate(struct wbsd_host* host) +{ + int base, irq, dma; + + /* + * Select SD/MMC function. + */ + wbsd_write_config(host, WBSD_CONF_DEVICE, DEVICE_SD); + + /* + * Read configuration. + */ + base = wbsd_read_config(host, WBSD_CONF_PORT_HI) << 8; + base |= wbsd_read_config(host, WBSD_CONF_PORT_LO); + + irq = wbsd_read_config(host, WBSD_CONF_IRQ); + + dma = wbsd_read_config(host, WBSD_CONF_DRQ); + + /* + * Validate against given configuration. + */ + if (base != host->base) + return 0; + if (irq != host->irq) + return 0; + if ((dma != host->dma) && (host->dma != -1)) + return 0; + + return 1; +} + +/*****************************************************************************\ + * * + * Devices setup and shutdown * + * * +\*****************************************************************************/ + +static int __devinit wbsd_init(struct device* dev, int base, int irq, int dma, + int pnp) +{ + struct wbsd_host* host = NULL; + struct mmc_host* mmc = NULL; + int ret; + + ret = wbsd_alloc_mmc(dev); + if (ret) + return ret; + + mmc = dev_get_drvdata(dev); + host = mmc_priv(mmc); + + /* + * Scan for hardware. + */ + ret = wbsd_scan(host); + if (ret) + { + if (pnp && (ret == -ENODEV)) + { + printk(KERN_WARNING DRIVER_NAME + ": Unable to confirm device presence. You may " + "experience lock-ups.\n"); + } + else + { + wbsd_free_mmc(dev); + return ret; + } + } + + /* + * Request resources. + */ + ret = wbsd_request_resources(host, io, irq, dma); + if (ret) + { + wbsd_release_resources(host); + wbsd_free_mmc(dev); + return ret; + } + + /* + * See if chip needs to be configured. + */ + if (pnp && (host->config != 0)) + { + if (!wbsd_chip_validate(host)) + { + printk(KERN_WARNING DRIVER_NAME + ": PnP active but chip not configured! " + "You probably have a buggy BIOS. " + "Configuring chip manually.\n"); + wbsd_chip_config(host); + } + } + else + wbsd_chip_config(host); + + /* + * Power Management stuff. No idea how this works. + * Not tested. + */ +#ifdef CONFIG_PM + if (host->config) + wbsd_write_config(host, WBSD_CONF_PME, 0xA0); +#endif + /* + * Allow device to initialise itself properly. + */ + mdelay(5); + + /* + * Reset the chip into a known state. + */ + wbsd_init_device(host); + + mmc_add_host(mmc); + + printk(KERN_INFO "%s: W83L51xD", mmc->host_name); + if (host->chip_id != 0) + printk(" id %x", (int)host->chip_id); + printk(" at 0x%x irq %d", (int)host->base, (int)host->irq); + if (host->dma >= 0) + printk(" dma %d", (int)host->dma); + else + printk(" FIFO"); + if (pnp) + printk(" PnP"); + printk("\n"); return 0; } +static void __devexit wbsd_shutdown(struct device* dev, int pnp) +{ + struct mmc_host* mmc = dev_get_drvdata(dev); + struct wbsd_host* host; + + if (!mmc) + return; + + host = mmc_priv(mmc); + + mmc_remove_host(mmc); + + if (!pnp) + { + /* + * Power down the SD/MMC function. + */ + wbsd_unlock_config(host); + wbsd_write_config(host, WBSD_CONF_DEVICE, DEVICE_SD); + wbsd_write_config(host, WBSD_CONF_ENABLE, 0); + wbsd_lock_config(host); + } + + wbsd_release_resources(host); + + wbsd_free_mmc(dev); +} + +/* + * Non-PnP + */ + +static int __devinit wbsd_probe(struct device* dev) +{ + return wbsd_init(dev, io, irq, dma, 0); +} + +static int __devexit wbsd_remove(struct device* dev) +{ + wbsd_shutdown(dev, 0); + + return 0; +} + +/* + * PnP + */ + +#ifdef CONFIG_PNP + +static int __devinit +wbsd_pnp_probe(struct pnp_dev * pnpdev, const struct pnp_device_id *dev_id) +{ + int io, irq, dma; + + /* + * Get resources from PnP layer. + */ + io = pnp_port_start(pnpdev, 0); + irq = pnp_irq(pnpdev, 0); + if (pnp_dma_valid(pnpdev, 0)) + dma = pnp_dma(pnpdev, 0); + else + dma = -1; + + DBGF("PnP resources: port %3x irq %d dma %d\n", io, irq, dma); + + return wbsd_init(&pnpdev->dev, io, irq, dma, 1); +} + +static void __devexit wbsd_pnp_remove(struct pnp_dev * dev) +{ + wbsd_shutdown(&dev->dev, 1); +} + +#endif /* CONFIG_PNP */ + /* * Power management */ @@ -1581,17 +1895,7 @@ static int wbsd_resume(struct device *dev, u32 level) #define wbsd_resume NULL #endif -static void wbsd_release(struct device *dev) -{ -} - -static struct platform_device wbsd_device = { - .name = DRIVER_NAME, - .id = -1, - .dev = { - .release = wbsd_release, - }, -}; +static struct platform_device *wbsd_device; static struct device_driver wbsd_driver = { .name = DRIVER_NAME, @@ -1603,6 +1907,17 @@ static struct device_driver wbsd_driver = { .resume = wbsd_resume, }; +#ifdef CONFIG_PNP + +static struct pnp_driver wbsd_pnp_driver = { + .name = DRIVER_NAME, + .id_table = pnp_dev_table, + .probe = wbsd_pnp_probe, + .remove = wbsd_pnp_remove, +}; + +#endif /* CONFIG_PNP */ + /* * Module loading/unloading */ @@ -1615,29 +1930,57 @@ static int __init wbsd_drv_init(void) ": Winbond W83L51xD SD/MMC card interface driver, " DRIVER_VERSION "\n"); printk(KERN_INFO DRIVER_NAME ": Copyright(c) Pierre Ossman\n"); - - result = driver_register(&wbsd_driver); - if (result < 0) - return result; - result = platform_device_register(&wbsd_device); - if (result < 0) - return result; +#ifdef CONFIG_PNP + + if (!nopnp) + { + result = pnp_register_driver(&wbsd_pnp_driver); + if (result < 0) + return result; + } + +#endif /* CONFIG_PNP */ + + if (nopnp) + { + result = driver_register(&wbsd_driver); + if (result < 0) + return result; + + wbsd_device = platform_device_register_simple(DRIVER_NAME, -1, + NULL, 0); + if (IS_ERR(wbsd_device)) + return PTR_ERR(wbsd_device); + } return 0; } static void __exit wbsd_drv_exit(void) { - platform_device_unregister(&wbsd_device); +#ifdef CONFIG_PNP + + if (!nopnp) + pnp_unregister_driver(&wbsd_pnp_driver); - driver_unregister(&wbsd_driver); +#endif /* CONFIG_PNP */ + + if (nopnp) + { + platform_device_unregister(wbsd_device); + + driver_unregister(&wbsd_driver); + } DBG("unloaded\n"); } module_init(wbsd_drv_init); module_exit(wbsd_drv_exit); +#ifdef CONFIG_PNP +module_param(nopnp, uint, 0444); +#endif module_param(io, uint, 0444); module_param(irq, uint, 0444); module_param(dma, int, 0444); @@ -1646,6 +1989,9 @@ MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("Winbond W83L51xD SD/MMC card interface driver"); MODULE_VERSION(DRIVER_VERSION); +#ifdef CONFIG_PNP +MODULE_PARM_DESC(nopnp, "Scan for device instead of relying on PNP. (default 0)"); +#endif MODULE_PARM_DESC(io, "I/O base to allocate. Must be 8 byte aligned. (default 0x248)"); MODULE_PARM_DESC(irq, "IRQ to allocate. (default 6)"); MODULE_PARM_DESC(dma, "DMA channel to allocate. -1 for no DMA. (default 2)"); diff --git a/drivers/mmc/wbsd.h b/drivers/mmc/wbsd.h index fdc03b56a81f..864f30828d01 100644 --- a/drivers/mmc/wbsd.h +++ b/drivers/mmc/wbsd.h @@ -35,6 +35,12 @@ const int valid_ids[] = { #define DEVICE_SD 0x03 +#define WBSD_PINS_DAT3_HI 0x20 +#define WBSD_PINS_DAT3_OUT 0x10 +#define WBSD_PINS_GP11_HI 0x04 +#define WBSD_PINS_DETECT_GP11 0x02 +#define WBSD_PINS_DETECT_DAT3 0x01 + #define WBSD_CMDR 0x00 #define WBSD_DFR 0x01 #define WBSD_EIR 0x02 @@ -133,6 +139,7 @@ const int valid_ids[] = { #define WBSD_CRC_OK 0x05 /* S010E (00101) */ #define WBSD_CRC_FAIL 0x0B /* S101E (01011) */ +#define WBSD_DMA_SIZE 65536 struct wbsd_host { @@ -140,6 +147,11 @@ struct wbsd_host spinlock_t lock; /* Mutex */ + int flags; /* Driver states */ + +#define WBSD_FCARD_PRESENT (1<<0) /* Card is present */ +#define WBSD_FIGNORE_DETECT (1<<1) /* Ignore card detection */ + struct mmc_request* mrq; /* Current request */ u8 isr; /* Accumulated ISR */ diff --git a/drivers/net/Makefile b/drivers/net/Makefile index 6202b10dbb4d..e038d55e4f6f 100644 --- a/drivers/net/Makefile +++ b/drivers/net/Makefile @@ -187,7 +187,7 @@ obj-$(CONFIG_TR) += tokenring/ obj-$(CONFIG_WAN) += wan/ obj-$(CONFIG_ARCNET) += arcnet/ obj-$(CONFIG_NET_PCMCIA) += pcmcia/ -obj-$(CONFIG_NET_WIRELESS) += wireless/ +obj-$(CONFIG_NET_RADIO) += wireless/ obj-$(CONFIG_NET_TULIP) += tulip/ obj-$(CONFIG_HAMRADIO) += hamradio/ obj-$(CONFIG_IRDA) += irda/ diff --git a/drivers/net/ibm_emac/ibm_emac_core.c b/drivers/net/ibm_emac/ibm_emac_core.c index ab44358ddbfc..6482d994d489 100644 --- a/drivers/net/ibm_emac/ibm_emac_core.c +++ b/drivers/net/ibm_emac/ibm_emac_core.c @@ -1595,7 +1595,7 @@ static struct ethtool_ops emac_ethtool_ops = { static int emac_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) { struct ocp_enet_private *fep = dev->priv; - uint *data = (uint *) & rq->ifr_ifru; + uint16_t *data = (uint16_t *) & rq->ifr_ifru; switch (cmd) { case SIOCGMIIPHY: diff --git a/drivers/net/tulip/tulip_core.c b/drivers/net/tulip/tulip_core.c index d098b3ba3538..e0ae3ed6e578 100644 --- a/drivers/net/tulip/tulip_core.c +++ b/drivers/net/tulip/tulip_core.c @@ -1104,7 +1104,7 @@ static void set_rx_mode(struct net_device *dev) if (entry != 0) { /* Avoid a chip errata by prefixing a dummy entry. Don't do this on the ULI526X as it triggers a different problem */ - if (!(tp->chip_id == ULI526X && (tp->revision = 0x40 || tp->revision == 0x50))) { + if (!(tp->chip_id == ULI526X && (tp->revision == 0x40 || tp->revision == 0x50))) { tp->tx_buffers[entry].skb = NULL; tp->tx_buffers[entry].mapping = 0; tp->tx_ring[entry].length = diff --git a/drivers/net/wireless/Kconfig b/drivers/net/wireless/Kconfig index 0aaa12c0c098..1d3231cc471a 100644 --- a/drivers/net/wireless/Kconfig +++ b/drivers/net/wireless/Kconfig @@ -323,7 +323,7 @@ config PRISM54 For a complete list of supported cards visit . Here is the latest confirmed list of supported cards: - 3com OfficeConnect 11g Cardbus Card aka 3CRWE154G72 + 3com OfficeConnect 11g Cardbus Card aka 3CRWE154G72 (version 1) Allnet ALL0271 PCI Card Compex WL54G Cardbus Card Corega CG-WLCB54GT Cardbus Card diff --git a/drivers/serial/21285.c b/drivers/serial/21285.c index 33fbda79f350..0b10169961eb 100644 --- a/drivers/serial/21285.c +++ b/drivers/serial/21285.c @@ -126,18 +126,8 @@ static irqreturn_t serial21285_rx_chars(int irq, void *dev_id, struct pt_regs *r flag = TTY_FRAME; } - if ((rxs & port->ignore_status_mask) == 0) { - tty_insert_flip_char(tty, ch, flag); - } - if ((rxs & RXSTAT_OVERRUN) && - tty->flip.count < TTY_FLIPBUF_SIZE) { - /* - * Overrun is special, since it's reported - * immediately, and doesn't affect the current - * character. - */ - tty_insert_flip_char(tty, 0, TTY_OVERRUN); - } + uart_insert_char(port, rxs, RXSTAT_OVERRUN, ch, flag); + status = *CSR_UARTFLG; } tty_flip_buffer_push(tty); diff --git a/drivers/serial/8250.c b/drivers/serial/8250.c index 0d9358608fdf..3bbf0cc6e53f 100644 --- a/drivers/serial/8250.c +++ b/drivers/serial/8250.c @@ -1122,18 +1122,9 @@ receive_chars(struct uart_8250_port *up, int *status, struct pt_regs *regs) } if (uart_handle_sysrq_char(&up->port, ch, regs)) goto ignore_char; - if ((lsr & up->port.ignore_status_mask) == 0) { - tty_insert_flip_char(tty, ch, flag); - } - if ((lsr & UART_LSR_OE) && - tty->flip.count < TTY_FLIPBUF_SIZE) { - /* - * Overrun is special, since it's reported - * immediately, and doesn't affect the current - * character. - */ - tty_insert_flip_char(tty, 0, TTY_OVERRUN); - } + + uart_insert_char(&up->port, lsr, UART_LSR_OE, ch, flag); + ignore_char: lsr = serial_inp(up, UART_LSR); } while ((lsr & UART_LSR_DR) && (max_count-- > 0)); diff --git a/drivers/serial/amba-pl010.c b/drivers/serial/amba-pl010.c index f2a5e2933c47..2884b310e54d 100644 --- a/drivers/serial/amba-pl010.c +++ b/drivers/serial/amba-pl010.c @@ -198,18 +198,8 @@ pl010_rx_chars(struct uart_port *port) if (uart_handle_sysrq_char(port, ch, regs)) goto ignore_char; - if ((rsr & port->ignore_status_mask) == 0) { - tty_insert_flip_char(tty, ch, flag); - } - if ((rsr & UART01x_RSR_OE) && - tty->flip.count < TTY_FLIPBUF_SIZE) { - /* - * Overrun is special, since it's reported - * immediately, and doesn't affect the current - * character - */ - tty_insert_flip_char(tty, 0, TTY_OVERRUN); - } + uart_insert_char(port, rsr, UART01x_RSR_OE, ch, flag); + ignore_char: status = UART_GET_FR(port); } diff --git a/drivers/serial/amba-pl011.c b/drivers/serial/amba-pl011.c index d5cbef3fe8b6..7db88ee18f75 100644 --- a/drivers/serial/amba-pl011.c +++ b/drivers/serial/amba-pl011.c @@ -163,18 +163,8 @@ pl011_rx_chars(struct uart_amba_port *uap) if (uart_handle_sysrq_char(&uap->port, ch, regs)) goto ignore_char; - if ((rsr & uap->port.ignore_status_mask) == 0) { - tty_insert_flip_char(tty, ch, flag); - } - if ((rsr & UART01x_RSR_OE) && - tty->flip.count < TTY_FLIPBUF_SIZE) { - /* - * Overrun is special, since it's reported - * immediately, and doesn't affect the current - * character - */ - tty_insert_flip_char(tty, 0, TTY_OVERRUN); - } + uart_insert_char(&uap->port, rsr, UART01x_RSR_OE, ch, flag); + ignore_char: status = readw(uap->port.membase + UART01x_FR); } diff --git a/drivers/serial/clps711x.c b/drivers/serial/clps711x.c index 6242f3090a96..e92522b33c48 100644 --- a/drivers/serial/clps711x.c +++ b/drivers/serial/clps711x.c @@ -143,10 +143,7 @@ static irqreturn_t clps711xuart_int_rx(int irq, void *dev_id, struct pt_regs *re * CHECK: does overrun affect the current character? * ASSUMPTION: it does not. */ - if ((ch & port->ignore_status_mask & ~RXSTAT_OVERRUN) == 0) - tty_insert_flip_char(tty, ch, flg); - if ((ch & ~port->ignore_status_mask & RXSTAT_OVERRUN) == 0) - tty_insert_flip_char(tty, 0, TTY_OVERRUN); + uart_insert_char(port, ch, UARTDR_OVERR, ch, flg); ignore_char: status = clps_readl(SYSFLG(port)); diff --git a/drivers/serial/pxa.c b/drivers/serial/pxa.c index 51d8a49f4477..9dc151d8fa61 100644 --- a/drivers/serial/pxa.c +++ b/drivers/serial/pxa.c @@ -161,20 +161,12 @@ receive_chars(struct uart_pxa_port *up, int *status, struct pt_regs *regs) else if (*status & UART_LSR_FE) flag = TTY_FRAME; } + if (uart_handle_sysrq_char(&up->port, ch, regs)) goto ignore_char; - if ((*status & up->port.ignore_status_mask) == 0) { - tty_insert_flip_char(tty, ch, flag); - } - if ((*status & UART_LSR_OE) && - tty->flip.count < TTY_FLIPBUF_SIZE) { - /* - * Overrun is special, since it's reported - * immediately, and doesn't affect the current - * character. - */ - tty_insert_flip_char(tty, 0, TTY_OVERRUN); - } + + uart_insert_char(&up->port, *status, UART_LSR_OE, ch, flag); + ignore_char: *status = serial_in(up, UART_LSR); } while ((*status & UART_LSR_DR) && (max_count-- > 0)); diff --git a/drivers/serial/s3c2410.c b/drivers/serial/s3c2410.c index 435750d40a47..2a9f7ade2c9d 100644 --- a/drivers/serial/s3c2410.c +++ b/drivers/serial/s3c2410.c @@ -394,20 +394,7 @@ s3c24xx_serial_rx_chars(int irq, void *dev_id, struct pt_regs *regs) if (uart_handle_sysrq_char(port, ch, regs)) goto ignore_char; - if ((uerstat & port->ignore_status_mask) == 0) { - tty_insert_flip_char(tty, ch, flag); - } - - if ((uerstat & S3C2410_UERSTAT_OVERRUN) && - tty->flip.count < TTY_FLIPBUF_SIZE) { - /* - * Overrun is special, since it's reported - * immediately, and doesn't affect the current - * character. - */ - - tty_insert_flip_char(tty, 0, TTY_OVERRUN); - } + uart_insert_char(port, uerstat, S3C2410_UERSTAT_OVERRUN, ch, flag); ignore_char: continue; diff --git a/drivers/serial/sa1100.c b/drivers/serial/sa1100.c index 157218bc6c6f..22565a67a57c 100644 --- a/drivers/serial/sa1100.c +++ b/drivers/serial/sa1100.c @@ -237,10 +237,7 @@ sa1100_rx_chars(struct sa1100_port *sport, struct pt_regs *regs) if (uart_handle_sysrq_char(&sport->port, ch, regs)) goto ignore_char; - if ((status & port->ignore_status_mask & ~UTSR1_TO_SM(UTSR1_ROR)) == 0) - tty_insert_flip_char(tty, ch, flg); - if (status & ~port->ignore_status_mask & UTSR1_TO_SM(UTSR1_ROR)) - tty_insert_flip_char(tty, 0, TTY_OVERRUN); + uart_insert_char(&sport->port, status, UTSR1_TO_SM(UTSR1_ROR), ch, flg); ignore_char: status = UTSR1_TO_SM(UART_GET_UTSR1(sport)) | diff --git a/drivers/serial/serial_lh7a40x.c b/drivers/serial/serial_lh7a40x.c index 85cfa08d3bad..56f269b6bfb1 100644 --- a/drivers/serial/serial_lh7a40x.c +++ b/drivers/serial/serial_lh7a40x.c @@ -190,18 +190,7 @@ lh7a40xuart_rx_chars (struct uart_port* port) if (uart_handle_sysrq_char (port, (unsigned char) data, regs)) continue; - if ((data & port->ignore_status_mask) == 0) { - tty_insert_flip_char(tty, data, flag); - } - if ((data & RxOverrunError) - && tty->flip.count < TTY_FLIPBUF_SIZE) { - /* - * Overrun is special, since it's reported - * immediately, and doesn't affect the current - * character - */ - tty_insert_flip_char(tty, 0, TTY_OVERRUN); - } + uart_insert_char(port, data, RxOverrunError, data, flag); } tty_flip_buffer_push (tty); return; diff --git a/drivers/serial/serial_txx9.c b/drivers/serial/serial_txx9.c index 37b2ef297cbe..3f1051a4a13f 100644 --- a/drivers/serial/serial_txx9.c +++ b/drivers/serial/serial_txx9.c @@ -350,18 +350,9 @@ receive_chars(struct uart_txx9_port *up, unsigned int *status, struct pt_regs *r } if (uart_handle_sysrq_char(&up->port, ch, regs)) goto ignore_char; - if ((disr & up->port.ignore_status_mask) == 0) { - tty_insert_flip_char(tty, ch, flag); - } - if ((disr & TXX9_SIDISR_UOER) && - tty->flip.count < TTY_FLIPBUF_SIZE) { - /* - * Overrun is special, since it's reported - * immediately, and doesn't affect the current - * character. - */ - tty_insert_flip_char(tty, 0, TTY_OVERRUN); - } + + uart_insert_char(&up->port, disr, TXX9_SIDISR_UOER, ch, flag); + ignore_char: disr = sio_in(up, TXX9_SIDISR); } while (!(disr & TXX9_SIDISR_UVALID) && (max_count-- > 0)); diff --git a/drivers/serial/vr41xx_siu.c b/drivers/serial/vr41xx_siu.c index 307886199f2f..5d2ceb623e6f 100644 --- a/drivers/serial/vr41xx_siu.c +++ b/drivers/serial/vr41xx_siu.c @@ -412,10 +412,8 @@ static inline void receive_chars(struct uart_port *port, uint8_t *status, if (uart_handle_sysrq_char(port, ch, regs)) goto ignore_char; - if ((lsr & port->ignore_status_mask) == 0) - tty_insert_flip_char(tty, ch, flag); - if ((lsr & UART_LSR_OE) && (tty->flip.count < TTY_FLIPBUF_SIZE)) - tty_insert_flip_char(tty, 0, TTY_OVERRUN); + + uart_insert_char(port, lsr, UART_LSR_OE, ch, flag); ignore_char: lsr = siu_read(port, UART_LSR); diff --git a/fs/binfmt_elf.c b/fs/binfmt_elf.c index ce9423bb2de3..c374be51b041 100644 --- a/fs/binfmt_elf.c +++ b/fs/binfmt_elf.c @@ -251,7 +251,7 @@ create_elf_tables(struct linux_binprm *bprm, struct elfhdr * exec, } /* Populate argv and envp */ - p = current->mm->arg_start; + p = current->mm->arg_end = current->mm->arg_start; while (argc-- > 0) { size_t len; __put_user((elf_addr_t)p, argv++); @@ -1301,7 +1301,7 @@ static void fill_prstatus(struct elf_prstatus *prstatus, static int fill_psinfo(struct elf_prpsinfo *psinfo, struct task_struct *p, struct mm_struct *mm) { - int i, len; + unsigned int i, len; /* first copy the parameters from user space */ memset(psinfo, 0, sizeof(struct elf_prpsinfo)); diff --git a/include/asm-arm/arch-imx/imx-regs.h b/include/asm-arm/arch-imx/imx-regs.h index f32c203952cf..93b840e8fa60 100644 --- a/include/asm-arm/arch-imx/imx-regs.h +++ b/include/asm-arm/arch-imx/imx-regs.h @@ -227,6 +227,30 @@ #define PD31_PF_TMR2OUT ( GPIO_PORTD | GPIO_PF | 31 ) #define PD31_BIN_SPI2_TXD ( GPIO_PORTD | GPIO_BIN | 31 ) +/* + * PWM controller + */ +#define PWMC __REG(IMX_PWM_BASE + 0x00) /* PWM Control Register */ +#define PWMS __REG(IMX_PWM_BASE + 0x04) /* PWM Sample Register */ +#define PWMP __REG(IMX_PWM_BASE + 0x08) /* PWM Period Register */ +#define PWMCNT __REG(IMX_PWM_BASE + 0x0C) /* PWM Counter Register */ + +#define PWMC_HCTR (0x01<<18) /* Halfword FIFO Data Swapping */ +#define PWMC_BCTR (0x01<<17) /* Byte FIFO Data Swapping */ +#define PWMC_SWR (0x01<<16) /* Software Reset */ +#define PWMC_CLKSRC (0x01<<15) /* Clock Source */ +#define PWMC_PRESCALER(x) (((x-1) & 0x7F) << 8) /* PRESCALER */ +#define PWMC_IRQ (0x01<< 7) /* Interrupt Request */ +#define PWMC_IRQEN (0x01<< 6) /* Interrupt Request Enable */ +#define PWMC_FIFOAV (0x01<< 5) /* FIFO Available */ +#define PWMC_EN (0x01<< 4) /* Enables/Disables the PWM */ +#define PWMC_REPEAT(x) (((x) & 0x03) << 2) /* Sample Repeats */ +#define PWMC_CLKSEL(x) (((x) & 0x03) << 0) /* Clock Selection */ + +#define PWMS_SAMPLE(x) ((x) & 0xFFFF) /* Contains a two-sample word */ +#define PWMP_PERIOD(x) ((x) & 0xFFFF) /* Represents the PWM's period */ +#define PWMC_COUNTER(x) ((x) & 0xFFFF) /* Represents the current count value */ + /* * DMA Controller */ diff --git a/include/asm-arm/arch-s3c2410/regs-nand.h b/include/asm-arm/arch-s3c2410/regs-nand.h index c443ac834698..7cff235e667a 100644 --- a/include/asm-arm/arch-s3c2410/regs-nand.h +++ b/include/asm-arm/arch-s3c2410/regs-nand.h @@ -1,16 +1,17 @@ /* linux/include/asm-arm/arch-s3c2410/regs-nand.h * - * Copyright (c) 2004 Simtec Electronics + * Copyright (c) 2004,2005 Simtec Electronics * http://www.simtec.co.uk/products/SWLINUX/ * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * - * S3C2410 clock register definitions + * S3C2410 NAND register definitions * * Changelog: * 18-Aug-2004 BJD Copied file from 2.4 and updated + * 01-May-2005 BJD Added definitions for s3c2440 controller */ #ifndef __ASM_ARM_REGS_NAND @@ -26,6 +27,22 @@ #define S3C2410_NFSTAT S3C2410_NFREG(0x10) #define S3C2410_NFECC S3C2410_NFREG(0x14) +#define S3C2440_NFCONT S3C2410_NFREG(0x04) +#define S3C2440_NFCMD S3C2410_NFREG(0x08) +#define S3C2440_NFADDR S3C2410_NFREG(0x0C) +#define S3C2440_NFDATA S3C2410_NFREG(0x10) +#define S3C2440_NFECCD0 S3C2410_NFREG(0x14) +#define S3C2440_NFECCD1 S3C2410_NFREG(0x18) +#define S3C2440_NFECCD S3C2410_NFREG(0x1C) +#define S3C2440_NFSTAT S3C2410_NFREG(0x20) +#define S3C2440_NFESTAT0 S3C2410_NFREG(0x24) +#define S3C2440_NFESTAT1 S3C2410_NFREG(0x28) +#define S3C2440_NFMECC0 S3C2410_NFREG(0x2C) +#define S3C2440_NFMECC1 S3C2410_NFREG(0x30) +#define S3C2440_NFSECC S3C2410_NFREG(0x34) +#define S3C2440_NFSBLK S3C2410_NFREG(0x38) +#define S3C2440_NFEBLK S3C2410_NFREG(0x3C) + #define S3C2410_NFCONF_EN (1<<15) #define S3C2410_NFCONF_512BYTE (1<<14) #define S3C2410_NFCONF_4STEP (1<<13) @@ -37,7 +54,28 @@ #define S3C2410_NFSTAT_BUSY (1<<0) -/* think ECC can only be 8bit read? */ +#define S3C2440_NFCONF_BUSWIDTH_8 (0<<0) +#define S3C2440_NFCONF_BUSWIDTH_16 (1<<0) +#define S3C2440_NFCONF_ADVFLASH (1<<3) +#define S3C2440_NFCONF_TACLS(x) ((x)<<12) +#define S3C2440_NFCONF_TWRPH0(x) ((x)<<8) +#define S3C2440_NFCONF_TWRPH1(x) ((x)<<4) + +#define S3C2440_NFCONT_LOCKTIGHT (1<<13) +#define S3C2440_NFCONT_SOFTLOCK (1<<12) +#define S3C2440_NFCONT_ILLEGALACC_EN (1<<10) +#define S3C2440_NFCONT_RNBINT_EN (1<<9) +#define S3C2440_NFCONT_RN_FALLING (1<<8) +#define S3C2440_NFCONT_SPARE_ECCLOCK (1<<6) +#define S3C2440_NFCONT_MAIN_ECCLOCK (1<<5) +#define S3C2440_NFCONT_INITECC (1<<4) +#define S3C2440_NFCONT_nFCE (1<<1) +#define S3C2440_NFCONT_ENABLE (1<<0) + +#define S3C2440_NFSTAT_READY (1<<0) +#define S3C2440_NFSTAT_nCE (1<<1) +#define S3C2440_NFSTAT_RnB_CHANGE (1<<2) +#define S3C2440_NFSTAT_ILLEGAL_ACCESS (1<<3) #endif /* __ASM_ARM_REGS_NAND */ diff --git a/include/asm-arm/page.h b/include/asm-arm/page.h index 4ca3a8e9348f..019c45d75730 100644 --- a/include/asm-arm/page.h +++ b/include/asm-arm/page.h @@ -114,19 +114,8 @@ extern void __cpu_copy_user_page(void *to, const void *from, unsigned long user); #endif -#define clear_user_page(addr,vaddr,pg) \ - do { \ - preempt_disable(); \ - __cpu_clear_user_page(addr, vaddr); \ - preempt_enable(); \ - } while (0) - -#define copy_user_page(to,from,vaddr,pg) \ - do { \ - preempt_disable(); \ - __cpu_copy_user_page(to, from, vaddr); \ - preempt_enable(); \ - } while (0) +#define clear_user_page(addr,vaddr,pg) __cpu_clear_user_page(addr, vaddr) +#define copy_user_page(to,from,vaddr,pg) __cpu_copy_user_page(to, from, vaddr) #define clear_page(page) memzero((void *)(page), PAGE_SIZE) extern void copy_page(void *to, const void *from); @@ -171,6 +160,9 @@ typedef unsigned long pgprot_t; #endif /* STRICT_MM_TYPECHECKS */ +/* the upper-most page table pointer */ +extern pmd_t *top_pmd; + /* Pure 2^n version of get_order */ static inline int get_order(unsigned long size) { diff --git a/include/linux/serial_core.h b/include/linux/serial_core.h index c3fb5984f250..d6025af7efac 100644 --- a/include/linux/serial_core.h +++ b/include/linux/serial_core.h @@ -479,6 +479,25 @@ uart_handle_cts_change(struct uart_port *port, unsigned int status) } } +#include + +static inline void +uart_insert_char(struct uart_port *port, unsigned int status, + unsigned int overrun, unsigned int ch, unsigned int flag) +{ + struct tty_struct *tty = port->info->tty; + + if ((status & port->ignore_status_mask & ~overrun) == 0) + tty_insert_flip_char(tty, ch, flag); + + /* + * Overrun is special. Since it's reported immediately, + * it doesn't affect the current character. + */ + if (status & ~port->ignore_status_mask & overrun) + tty_insert_flip_char(tty, 0, TTY_OVERRUN); +} + /* * UART_ENABLE_MS - determine if port should enable modem status irqs */