From ec448a0a361ce3fa97a32a5c267a680163ffe8c5 Mon Sep 17 00:00:00 2001 From: Roland Dreier Date: Tue, 18 Apr 2006 09:05:39 -0700 Subject: [SCSI] srp.h: avoid padding of structs Several structs in get padded to a multiple of 8 bytes on 64-bit architectures and end up with a size that does not match the definition in the SRP spec: SRP spec 64-bit sizeof (struct indirect_buf) 20 24 sizeof (struct srp_login_rsp) 52 56 sizeof (struct srp_rsp) 36 40 Fix this by adding __attribute__((packed)) to the offending structs. Problem pointed out by Arne Redlich . Signed-off-by: Roland Dreier Signed-off-by: James Bottomley --- include/scsi/srp.h | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/include/scsi/srp.h b/include/scsi/srp.h index 6c2681dc5b46..637f77eccf0c 100644 --- a/include/scsi/srp.h +++ b/include/scsi/srp.h @@ -95,14 +95,15 @@ struct srp_direct_buf { /* * We need the packed attribute because the SRP spec puts the list of - * descriptors at an offset of 20, which is not aligned to the size - * of struct srp_direct_buf. + * descriptors at an offset of 20, which is not aligned to the size of + * struct srp_direct_buf. The whole structure must be packed to avoid + * having the 20-byte structure padded to 24 bytes on 64-bit architectures. */ struct srp_indirect_buf { struct srp_direct_buf table_desc; __be32 len; - struct srp_direct_buf desc_list[0] __attribute__((packed)); -}; + struct srp_direct_buf desc_list[0]; +} __attribute__((packed)); enum { SRP_MULTICHAN_SINGLE = 0, @@ -122,6 +123,11 @@ struct srp_login_req { u8 target_port_id[16]; }; +/* + * The SRP spec defines the size of the LOGIN_RSP structure to be 52 + * bytes, so it needs to be packed to avoid having it padded to 56 + * bytes on 64-bit architectures. + */ struct srp_login_rsp { u8 opcode; u8 reserved1[3]; @@ -132,7 +138,7 @@ struct srp_login_rsp { __be16 buf_fmt; u8 rsp_flags; u8 reserved2[25]; -}; +} __attribute__((packed)); struct srp_login_rej { u8 opcode; @@ -207,6 +213,11 @@ enum { SRP_RSP_FLAG_DIUNDER = 1 << 5 }; +/* + * The SRP spec defines the size of the RSP structure to be 36 bytes, + * so it needs to be packed to avoid having it padded to 40 bytes on + * 64-bit architectures. + */ struct srp_rsp { u8 opcode; u8 sol_not; @@ -221,6 +232,6 @@ struct srp_rsp { __be32 sense_data_len; __be32 resp_data_len; u8 data[0]; -}; +} __attribute__((packed)); #endif /* SCSI_SRP_H */ -- cgit v1.2.3 From 68ac64cd3fd89fdaa091701f6ab98a9065e9b1b5 Mon Sep 17 00:00:00 2001 From: Russell King Date: Sun, 30 Apr 2006 11:13:50 +0100 Subject: [SERIAL] Clean up serial locking when obtaining a reference to a port The locking for the uart_port is over complicated, and can be simplified if we introduce a flag to indicate that a port is "dead" and will be removed. This also helps the validator because it removes a case of non-nested unlock ordering. Signed-off-by: Russell King Signed-off-by: Ingo Molnar Signed-off-by: Andrew Morton --- drivers/serial/serial_core.c | 114 +++++++++++++++++++++++-------------------- include/linux/serial_core.h | 1 + 2 files changed, 61 insertions(+), 54 deletions(-) (limited to 'include') diff --git a/drivers/serial/serial_core.c b/drivers/serial/serial_core.c index fcd7744c4253..aeb8153ccf24 100644 --- a/drivers/serial/serial_core.c +++ b/drivers/serial/serial_core.c @@ -1500,20 +1500,18 @@ uart_block_til_ready(struct file *filp, struct uart_state *state) static struct uart_state *uart_get(struct uart_driver *drv, int line) { struct uart_state *state; + int ret = 0; - mutex_lock(&port_mutex); state = drv->state + line; if (mutex_lock_interruptible(&state->mutex)) { - state = ERR_PTR(-ERESTARTSYS); - goto out; + ret = -ERESTARTSYS; + goto err; } state->count++; - if (!state->port) { - state->count--; - mutex_unlock(&state->mutex); - state = ERR_PTR(-ENXIO); - goto out; + if (!state->port || state->port->flags & UPF_DEAD) { + ret = -ENXIO; + goto err_unlock; } if (!state->info) { @@ -1531,15 +1529,17 @@ static struct uart_state *uart_get(struct uart_driver *drv, int line) tasklet_init(&state->info->tlet, uart_tasklet_action, (unsigned long)state); } else { - state->count--; - mutex_unlock(&state->mutex); - state = ERR_PTR(-ENOMEM); + ret = -ENOMEM; + goto err_unlock; } } - - out: - mutex_unlock(&port_mutex); return state; + + err_unlock: + state->count--; + mutex_unlock(&state->mutex); + err: + return ERR_PTR(ret); } /* @@ -2085,45 +2085,6 @@ uart_configure_port(struct uart_driver *drv, struct uart_state *state, } } -/* - * This reverses the effects of uart_configure_port, hanging up the - * port before removal. - */ -static void -uart_unconfigure_port(struct uart_driver *drv, struct uart_state *state) -{ - struct uart_port *port = state->port; - struct uart_info *info = state->info; - - if (info && info->tty) - tty_vhangup(info->tty); - - mutex_lock(&state->mutex); - - state->info = NULL; - - /* - * Free the port IO and memory resources, if any. - */ - if (port->type != PORT_UNKNOWN) - port->ops->release_port(port); - - /* - * Indicate that there isn't a port here anymore. - */ - port->type = PORT_UNKNOWN; - - /* - * Kill the tasklet, and free resources. - */ - if (info) { - tasklet_kill(&info->tlet); - kfree(info); - } - - mutex_unlock(&state->mutex); -} - static struct tty_operations uart_ops = { .open = uart_open, .close = uart_close, @@ -2270,6 +2231,7 @@ int uart_add_one_port(struct uart_driver *drv, struct uart_port *port) state = drv->state + port->line; mutex_lock(&port_mutex); + mutex_lock(&state->mutex); if (state->port) { ret = -EINVAL; goto out; @@ -2304,7 +2266,13 @@ int uart_add_one_port(struct uart_driver *drv, struct uart_port *port) port->cons && !(port->cons->flags & CON_ENABLED)) register_console(port->cons); + /* + * Ensure UPF_DEAD is not set. + */ + port->flags &= ~UPF_DEAD; + out: + mutex_unlock(&state->mutex); mutex_unlock(&port_mutex); return ret; @@ -2322,6 +2290,7 @@ int uart_add_one_port(struct uart_driver *drv, struct uart_port *port) int uart_remove_one_port(struct uart_driver *drv, struct uart_port *port) { struct uart_state *state = drv->state + port->line; + struct uart_info *info; BUG_ON(in_interrupt()); @@ -2331,12 +2300,49 @@ int uart_remove_one_port(struct uart_driver *drv, struct uart_port *port) mutex_lock(&port_mutex); + /* + * Mark the port "dead" - this prevents any opens from + * succeeding while we shut down the port. + */ + mutex_lock(&state->mutex); + port->flags |= UPF_DEAD; + mutex_unlock(&state->mutex); + /* * Remove the devices from devfs */ tty_unregister_device(drv->tty_driver, port->line); - uart_unconfigure_port(drv, state); + info = state->info; + if (info && info->tty) + tty_vhangup(info->tty); + + /* + * All users of this port should now be disconnected from + * this driver, and the port shut down. We should be the + * only thread fiddling with this port from now on. + */ + state->info = NULL; + + /* + * Free the port IO and memory resources, if any. + */ + if (port->type != PORT_UNKNOWN) + port->ops->release_port(port); + + /* + * Indicate that there isn't a port here anymore. + */ + port->type = PORT_UNKNOWN; + + /* + * Kill the tasklet, and free resources. + */ + if (info) { + tasklet_kill(&info->tlet); + kfree(info); + } + state->port = NULL; mutex_unlock(&port_mutex); diff --git a/include/linux/serial_core.h b/include/linux/serial_core.h index c32e60e79dea..bd14858121ea 100644 --- a/include/linux/serial_core.h +++ b/include/linux/serial_core.h @@ -254,6 +254,7 @@ struct uart_port { #define UPF_CONS_FLOW ((__force upf_t) (1 << 23)) #define UPF_SHARE_IRQ ((__force upf_t) (1 << 24)) #define UPF_BOOT_AUTOCONF ((__force upf_t) (1 << 28)) +#define UPF_DEAD ((__force upf_t) (1 << 30)) #define UPF_IOREMAP ((__force upf_t) (1 << 31)) #define UPF_CHANGE_MASK ((__force upf_t) (0x17fff)) -- cgit v1.2.3 From cd95842ca0ffb0e3df3b459832a60f9f4544ed9e Mon Sep 17 00:00:00 2001 From: Markus Gutschke Date: Sun, 30 Apr 2006 15:34:29 +0100 Subject: [ARM] 3486/1: Mark memory as clobbered by the ARM _syscallX() macros Patch from Markus Gutschke In order to prevent gcc from making incorrect optimizations, all asm() statements that define system calls should report memory as clobbered. Recent versions of the headers for i386 have been changed accordingly, but the ARM headers are still defective. This patch fixes the bug tracked at http://bugzilla.kernel.org/show_bug.cgi?id=6205 Signed-off-by: Markus Gutschke Signed-off-by: Russell King --- drivers/input/touchscreen/corgi_ts.c | 2 +- include/asm-arm/unistd.h | 21 ++++++++++++++------- 2 files changed, 15 insertions(+), 8 deletions(-) (limited to 'include') diff --git a/drivers/input/touchscreen/corgi_ts.c b/drivers/input/touchscreen/corgi_ts.c index 1042987856f7..5013703db0e6 100644 --- a/drivers/input/touchscreen/corgi_ts.c +++ b/drivers/input/touchscreen/corgi_ts.c @@ -17,7 +17,7 @@ #include #include #include -#include +//#include #include #include diff --git a/include/asm-arm/unistd.h b/include/asm-arm/unistd.h index ee8dfea549bc..26f2f4828e03 100644 --- a/include/asm-arm/unistd.h +++ b/include/asm-arm/unistd.h @@ -410,7 +410,8 @@ type name(void) { \ __asm__ __volatile__ ( \ __syscall(name) \ : "=r" (__res_r0) \ - : __SYS_REG_LIST() ); \ + : __SYS_REG_LIST() \ + : "memory" ); \ __res = __res_r0; \ __syscall_return(type,__res); \ } @@ -424,7 +425,8 @@ type name(type1 arg1) { \ __asm__ __volatile__ ( \ __syscall(name) \ : "=r" (__res_r0) \ - : __SYS_REG_LIST( "0" (__r0) ) ); \ + : __SYS_REG_LIST( "0" (__r0) ) \ + : "memory" ); \ __res = __res_r0; \ __syscall_return(type,__res); \ } @@ -439,7 +441,8 @@ type name(type1 arg1,type2 arg2) { \ __asm__ __volatile__ ( \ __syscall(name) \ : "=r" (__res_r0) \ - : __SYS_REG_LIST( "0" (__r0), "r" (__r1) ) ); \ + : __SYS_REG_LIST( "0" (__r0), "r" (__r1) ) \ + : "memory" ); \ __res = __res_r0; \ __syscall_return(type,__res); \ } @@ -456,7 +459,8 @@ type name(type1 arg1,type2 arg2,type3 arg3) { \ __asm__ __volatile__ ( \ __syscall(name) \ : "=r" (__res_r0) \ - : __SYS_REG_LIST( "0" (__r0), "r" (__r1), "r" (__r2) ) ); \ + : __SYS_REG_LIST( "0" (__r0), "r" (__r1), "r" (__r2) ) \ + : "memory" ); \ __res = __res_r0; \ __syscall_return(type,__res); \ } @@ -474,7 +478,8 @@ type name(type1 arg1, type2 arg2, type3 arg3, type4 arg4) { \ __asm__ __volatile__ ( \ __syscall(name) \ : "=r" (__res_r0) \ - : __SYS_REG_LIST( "0" (__r0), "r" (__r1), "r" (__r2), "r" (__r3) ) ); \ + : __SYS_REG_LIST( "0" (__r0), "r" (__r1), "r" (__r2), "r" (__r3) ) \ + : "memory" ); \ __res = __res_r0; \ __syscall_return(type,__res); \ } @@ -494,7 +499,8 @@ type name(type1 arg1, type2 arg2, type3 arg3, type4 arg4, type5 arg5) { \ __syscall(name) \ : "=r" (__res_r0) \ : __SYS_REG_LIST( "0" (__r0), "r" (__r1), "r" (__r2), \ - "r" (__r3), "r" (__r4) ) ); \ + "r" (__r3), "r" (__r4) ) \ + : "memory" ); \ __res = __res_r0; \ __syscall_return(type,__res); \ } @@ -514,7 +520,8 @@ type name(type1 arg1, type2 arg2, type3 arg3, type4 arg4, type5 arg5, type6 arg6 __syscall(name) \ : "=r" (__res_r0) \ : __SYS_REG_LIST( "0" (__r0), "r" (__r1), "r" (__r2), \ - "r" (__r3), "r" (__r4), "r" (__r5) ) ); \ + "r" (__r3), "r" (__r4), "r" (__r5) ) \ + : "memory" ); \ __res = __res_r0; \ __syscall_return(type,__res); \ } -- cgit v1.2.3 From 76bbb00288e569e7bd9ec18f45e4f814352260dd Mon Sep 17 00:00:00 2001 From: Deepak Saxena Date: Sun, 30 Apr 2006 15:34:29 +0100 Subject: [ARM] 3487/1: IXP4xx: Support non-PCI systems Patch from Deepak Saxena This patch allows for the addition of IXP4xx systems that do not make use of the PCI interface by moving the CONFIG_PCI symbol selection to be platform-specific instead of for all of IXP4xx. If at least one machine with PCI support is built, the PCI code will be compiled in, but when building !PCI, this will drastically shrink the kernel size. Signed-off-by: Deepak Saxena Signed-off-by: Russell King --- arch/arm/Kconfig | 2 -- arch/arm/mach-ixp4xx/Kconfig | 15 ++++++++++++++- arch/arm/mach-ixp4xx/Makefile | 3 ++- include/asm-arm/arch-ixp4xx/io.h | 7 +++++++ include/asm-arm/arch-ixp4xx/memory.h | 2 +- 5 files changed, 24 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index 1dbf6ddb300d..08b7cc900cae 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -150,8 +150,6 @@ config ARCH_IOP3XX config ARCH_IXP4XX bool "IXP4xx-based" - select DMABOUNCE - select PCI help Support for Intel's IXP4XX (XScale) family of processors. diff --git a/arch/arm/mach-ixp4xx/Kconfig b/arch/arm/mach-ixp4xx/Kconfig index 5bf50a2a737d..2a39f9e481ad 100644 --- a/arch/arm/mach-ixp4xx/Kconfig +++ b/arch/arm/mach-ixp4xx/Kconfig @@ -11,6 +11,7 @@ comment "IXP4xx Platforms" config MACH_NSLU2 bool prompt "Linksys NSLU2" + select PCI help Say 'Y' here if you want your kernel to support Linksys's NSLU2 NAS device. For more information on this platform, @@ -18,6 +19,7 @@ config MACH_NSLU2 config ARCH_AVILA bool "Avila" + select PCI help Say 'Y' here if you want your kernel to support the Gateworks Avila Network Platform. For more information on this platform, @@ -25,6 +27,7 @@ config ARCH_AVILA config ARCH_ADI_COYOTE bool "Coyote" + select PCI help Say 'Y' here if you want your kernel to support the ADI Engineering Coyote Gateway Reference Platform. For more @@ -32,6 +35,7 @@ config ARCH_ADI_COYOTE config ARCH_IXDP425 bool "IXDP425" + select PCI help Say 'Y' here if you want your kernel to support Intel's IXDP425 Development Platform (Also known as Richfield). @@ -39,6 +43,7 @@ config ARCH_IXDP425 config MACH_IXDPG425 bool "IXDPG425" + select PCI help Say 'Y' here if you want your kernel to support Intel's IXDPG425 Development Platform (Also known as Montajade). @@ -46,6 +51,7 @@ config MACH_IXDPG425 config MACH_IXDP465 bool "IXDP465" + select PCI help Say 'Y' here if you want your kernel to support Intel's IXDP465 Development Platform (Also known as BMP). @@ -72,6 +78,7 @@ config ARCH_PRPMC1100 config MACH_NAS100D bool prompt "NAS100D" + select PCI help Say 'Y' here if you want your kernel to support Iomega's NAS 100d device. For more information on this platform, @@ -96,6 +103,7 @@ config CPU_IXP46X config MACH_GTWX5715 bool "Gemtek WX5715 (Linksys WRV54G)" depends on ARCH_IXP4XX + select PCI help This board is currently inside the Linksys WRV54G Gateways. @@ -110,11 +118,16 @@ config MACH_GTWX5715 "High Speed" UART is n/c (as far as I can tell) 20 Pin ARM/Xscale JTAG interface on J2 - comment "IXP4xx Options" +config DMABOUNCE + bool + default y + depends on PCI + config IXP4XX_INDIRECT_PCI bool "Use indirect PCI memory access" + depends on PCI help IXP4xx provides two methods of accessing PCI memory space: diff --git a/arch/arm/mach-ixp4xx/Makefile b/arch/arm/mach-ixp4xx/Makefile index 0471044fa179..5a4aaa0e0a09 100644 --- a/arch/arm/mach-ixp4xx/Makefile +++ b/arch/arm/mach-ixp4xx/Makefile @@ -2,8 +2,9 @@ # Makefile for the linux kernel. # -obj-y += common.o common-pci.o +obj-y += common.o +obj-$(CONFIG_PCI) += common-pci.o obj-$(CONFIG_ARCH_IXDP4XX) += ixdp425-pci.o ixdp425-setup.o obj-$(CONFIG_MACH_IXDPG425) += ixdpg425-pci.o coyote-setup.o obj-$(CONFIG_ARCH_ADI_COYOTE) += coyote-pci.o coyote-setup.o diff --git a/include/asm-arm/arch-ixp4xx/io.h b/include/asm-arm/arch-ixp4xx/io.h index 942b622455bc..b59520e56fc7 100644 --- a/include/asm-arm/arch-ixp4xx/io.h +++ b/include/asm-arm/arch-ixp4xx/io.h @@ -260,6 +260,12 @@ out: #endif +#ifndef CONFIG_PCI + +#define __io(v) v + +#else + /* * IXP4xx does not have a transparent cpu -> PCI I/O translation * window. Instead, it has a set of registers that must be tweaked @@ -578,6 +584,7 @@ __ixp4xx_iowrite32_rep(void __iomem *addr, const void *vaddr, u32 count) #define ioport_map(port, nr) ((void __iomem*)(port + PIO_OFFSET)) #define ioport_unmap(addr) +#endif // !CONFIG_PCI #endif // __ASM_ARM_ARCH_IO_H diff --git a/include/asm-arm/arch-ixp4xx/memory.h b/include/asm-arm/arch-ixp4xx/memory.h index ee211d28a3ef..af9667b57ab3 100644 --- a/include/asm-arm/arch-ixp4xx/memory.h +++ b/include/asm-arm/arch-ixp4xx/memory.h @@ -14,7 +14,7 @@ */ #define PHYS_OFFSET UL(0x00000000) -#ifndef __ASSEMBLY__ +#if !defined(__ASSEMBLY__) && defined(CONFIG_PCI) void ixp4xx_adjust_zones(int node, unsigned long *size, unsigned long *holes); -- cgit v1.2.3 From 37be4e7809e0581db85387e126ae4da68c3d6286 Mon Sep 17 00:00:00 2001 From: Russell King Date: Tue, 2 May 2006 17:24:59 +0100 Subject: [MMC] extend data timeout for writes The CSD contains a "read2write factor" which determines the multiplier to be applied to the read timeout to obtain the write timeout. We were ignoring this parameter, resulting in the possibility for writes being timed out too early. Signed-off-by: Russell King --- drivers/mmc/mmc.c | 2 ++ drivers/mmc/mmc_block.c | 6 ++++++ include/linux/mmc/card.h | 1 + 3 files changed, 9 insertions(+) (limited to 'include') diff --git a/drivers/mmc/mmc.c b/drivers/mmc/mmc.c index da6ddd910fc5..05aa4d6b4f24 100644 --- a/drivers/mmc/mmc.c +++ b/drivers/mmc/mmc.c @@ -549,6 +549,7 @@ static void mmc_decode_csd(struct mmc_card *card) csd->read_partial = UNSTUFF_BITS(resp, 79, 1); csd->write_misalign = UNSTUFF_BITS(resp, 78, 1); csd->read_misalign = UNSTUFF_BITS(resp, 77, 1); + csd->r2w_factor = UNSTUFF_BITS(resp, 26, 3); csd->write_blkbits = UNSTUFF_BITS(resp, 22, 4); csd->write_partial = UNSTUFF_BITS(resp, 21, 1); } else { @@ -583,6 +584,7 @@ static void mmc_decode_csd(struct mmc_card *card) csd->read_partial = UNSTUFF_BITS(resp, 79, 1); csd->write_misalign = UNSTUFF_BITS(resp, 78, 1); csd->read_misalign = UNSTUFF_BITS(resp, 77, 1); + csd->r2w_factor = UNSTUFF_BITS(resp, 26, 3); csd->write_blkbits = UNSTUFF_BITS(resp, 22, 4); csd->write_partial = UNSTUFF_BITS(resp, 21, 1); } diff --git a/drivers/mmc/mmc_block.c b/drivers/mmc/mmc_block.c index 8eb2a2ede64b..06bd1f4cb9b1 100644 --- a/drivers/mmc/mmc_block.c +++ b/drivers/mmc/mmc_block.c @@ -187,6 +187,12 @@ static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req) brq.cmd.opcode = MMC_WRITE_BLOCK; brq.data.flags |= MMC_DATA_WRITE; brq.data.blocks = 1; + + /* + * Scale up the timeout by the r2w factor + */ + brq.data.timeout_ns <<= card->csd.r2w_factor; + brq.data.timeout_clks <<= card->csd.r2w_factor; } if (brq.data.blocks > 1) { diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h index 30dd978c1ec8..991a37382a22 100644 --- a/include/linux/mmc/card.h +++ b/include/linux/mmc/card.h @@ -28,6 +28,7 @@ struct mmc_csd { unsigned short cmdclass; unsigned short tacc_clks; unsigned int tacc_ns; + unsigned int r2w_factor; unsigned int max_dtr; unsigned int read_blkbits; unsigned int write_blkbits; -- cgit v1.2.3 From 61f5657c50341198ff05e375e6f1fc0476556562 Mon Sep 17 00:00:00 2001 From: Vitaly Bordug Date: Sat, 29 Apr 2006 22:32:44 +0400 Subject: [PATCH] ppc32 CPM_UART: Fixed break send on SCC SCC uart sends a break sequence each time it is stopped with the CPM_CR_STOP_TX command. That means that each time an application closes the serial device, a break is transmitted. To fix this, graceful tx stop is issued for SCC. Signed-off-by: David Jander Signed-off-by: Vitaly Bordug Signed-off-by: Paul Mackerras --- drivers/serial/cpm_uart/cpm_uart_core.c | 6 +++++- include/asm-ppc/commproc.h | 1 + include/asm-ppc/cpm2.h | 2 +- 3 files changed, 7 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/drivers/serial/cpm_uart/cpm_uart_core.c b/drivers/serial/cpm_uart/cpm_uart_core.c index ced193bf9e1e..b9d1185df20c 100644 --- a/drivers/serial/cpm_uart/cpm_uart_core.c +++ b/drivers/serial/cpm_uart/cpm_uart_core.c @@ -457,7 +457,11 @@ static void cpm_uart_shutdown(struct uart_port *port) } /* Shut them really down and reinit buffer descriptors */ - cpm_line_cr_cmd(line, CPM_CR_STOP_TX); + if (IS_SMC(pinfo)) + cpm_line_cr_cmd(line, CPM_CR_STOP_TX); + else + cpm_line_cr_cmd(line, CPM_CR_GRA_STOP_TX); + cpm_uart_initbd(pinfo); } } diff --git a/include/asm-ppc/commproc.h b/include/asm-ppc/commproc.h index 973e60908234..31f362966a58 100644 --- a/include/asm-ppc/commproc.h +++ b/include/asm-ppc/commproc.h @@ -35,6 +35,7 @@ #define CPM_CR_INIT_TX ((ushort)0x0002) #define CPM_CR_HUNT_MODE ((ushort)0x0003) #define CPM_CR_STOP_TX ((ushort)0x0004) +#define CPM_CR_GRA_STOP_TX ((ushort)0x0005) #define CPM_CR_RESTART_TX ((ushort)0x0006) #define CPM_CR_CLOSE_RX_BD ((ushort)0x0007) #define CPM_CR_SET_GADDR ((ushort)0x0008) diff --git a/include/asm-ppc/cpm2.h b/include/asm-ppc/cpm2.h index b638b87cebe3..c70344b91049 100644 --- a/include/asm-ppc/cpm2.h +++ b/include/asm-ppc/cpm2.h @@ -69,7 +69,7 @@ #define CPM_CR_INIT_TX ((ushort)0x0002) #define CPM_CR_HUNT_MODE ((ushort)0x0003) #define CPM_CR_STOP_TX ((ushort)0x0004) -#define CPM_CR_GRA_STOP_TX ((ushort)0x0005) +#define CPM_CR_GRA_STOP_TX ((ushort)0x0005) #define CPM_CR_RESTART_TX ((ushort)0x0006) #define CPM_CR_SET_GADDR ((ushort)0x0008) #define CPM_CR_START_IDMA ((ushort)0x0009) -- cgit v1.2.3 From 6e1976961c9bd9a3dc368139fab1883961efc879 Mon Sep 17 00:00:00 2001 From: Vitaly Bordug Date: Sat, 29 Apr 2006 23:06:00 +0400 Subject: [PATCH] ppc32 CPM_UART: fixes and improvements A number of small issues are fixed, and added the header file, missed from the original series. With this, driver should be pretty stable as tested among both platform-device-driven and "old way" boards. Also added missing GPL statement , and updated year field on existing ones to reflect code update. Signed-off-by: Vitaly Bordug Signed-off-by: Paul Mackerras --- arch/ppc/platforms/mpc866ads_setup.c | 2 +- drivers/serial/cpm_uart/cpm_uart.h | 19 ++++++++--- drivers/serial/cpm_uart/cpm_uart_core.c | 31 +++++++++++++---- drivers/serial/cpm_uart/cpm_uart_cpm1.c | 2 ++ drivers/serial/cpm_uart/cpm_uart_cpm2.c | 2 ++ include/linux/fs_uart_pd.h | 60 +++++++++++++++++++++++++++++++++ 6 files changed, 104 insertions(+), 12 deletions(-) create mode 100644 include/linux/fs_uart_pd.h (limited to 'include') diff --git a/arch/ppc/platforms/mpc866ads_setup.c b/arch/ppc/platforms/mpc866ads_setup.c index 6ce3b842defe..d919dab61347 100644 --- a/arch/ppc/platforms/mpc866ads_setup.c +++ b/arch/ppc/platforms/mpc866ads_setup.c @@ -378,7 +378,7 @@ int __init mpc866ads_init(void) ppc_sys_device_setfunc(MPC8xx_CPM_SMC1, PPC_SYS_FUNC_UART); #endif -#ifdef CONFIG_SERIAL_CPM_SMCer +#ifdef CONFIG_SERIAL_CPM_SMC ppc_sys_device_enable(MPC8xx_CPM_SMC2); ppc_sys_device_setfunc(MPC8xx_CPM_SMC2, PPC_SYS_FUNC_UART); #endif diff --git a/drivers/serial/cpm_uart/cpm_uart.h b/drivers/serial/cpm_uart/cpm_uart.h index aa5eb7ddeda9..3b35cb779539 100644 --- a/drivers/serial/cpm_uart/cpm_uart.h +++ b/drivers/serial/cpm_uart/cpm_uart.h @@ -5,6 +5,13 @@ * * Copyright (C) 2004 Freescale Semiconductor, Inc. * + * 2006 (c) MontaVista Software, Inc. + * Vitaly Bordug + * + * This file is licensed under the terms of the GNU General Public License + * version 2. This program is licensed "as is" without any warranty of any + * kind, whether express or implied. + * */ #ifndef CPM_UART_H #define CPM_UART_H @@ -101,12 +108,13 @@ static inline unsigned long cpu2cpm_addr(void* addr, struct uart_cpm_port *pinfo int offset; u32 val = (u32)addr; /* sane check */ - if ((val >= (u32)pinfo->mem_addr) && + if (likely((val >= (u32)pinfo->mem_addr)) && (val<((u32)pinfo->mem_addr + pinfo->mem_size))) { offset = val - (u32)pinfo->mem_addr; return pinfo->dma_addr+offset; } - printk("%s(): address %x to translate out of range!\n", __FUNCTION__, val); + /* something nasty happened */ + BUG(); return 0; } @@ -115,12 +123,13 @@ static inline void *cpm2cpu_addr(unsigned long addr, struct uart_cpm_port *pinfo int offset; u32 val = addr; /* sane check */ - if ((val >= pinfo->dma_addr) && - (val<(pinfo->dma_addr + pinfo->mem_size))) { + if (likely((val >= pinfo->dma_addr) && + (val<(pinfo->dma_addr + pinfo->mem_size)))) { offset = val - (u32)pinfo->dma_addr; return (void*)(pinfo->mem_addr+offset); } - printk("%s(): address %x to translate out of range!\n", __FUNCTION__, val); + /* something nasty happened */ + BUG(); return 0; } diff --git a/drivers/serial/cpm_uart/cpm_uart_core.c b/drivers/serial/cpm_uart/cpm_uart_core.c index b9d1185df20c..969f94900431 100644 --- a/drivers/serial/cpm_uart/cpm_uart_core.c +++ b/drivers/serial/cpm_uart/cpm_uart_core.c @@ -12,7 +12,8 @@ * * Copyright (C) 2004 Freescale Semiconductor, Inc. * (C) 2004 Intracom, S.A. - * (C) 2005 MontaVista Software, Inc. by Vitaly Bordug + * (C) 2005-2006 MontaVista Software, Inc. + * Vitaly Bordug * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -81,7 +82,7 @@ early_uart_get_pdev(int index) } -void cpm_uart_count(void) +static void cpm_uart_count(void) { cpm_uart_nr = 0; #ifdef CONFIG_SERIAL_CPM_SMC1 @@ -104,6 +105,21 @@ void cpm_uart_count(void) #endif } +/* Get UART number by its id */ +static int cpm_uart_id2nr(int id) +{ + int i; + if (id < UART_NR) { + for (i=0; ifs_no; line++); + line = cpm_uart_id2nr(idx); + if(line < 0) { + printk(KERN_ERR"%s(): port %d is not registered", __FUNCTION__, idx); + return -1; + } pinfo = (struct uart_cpm_port *) &cpm_uart_ports[idx]; @@ -1245,8 +1265,7 @@ static int cpm_uart_drv_probe(struct device *dev) } pdata = pdev->dev.platform_data; - pr_debug("cpm_uart_drv_probe: Adding CPM UART %d\n", - cpm_uart_port_map[pdata->fs_no]); + pr_debug("cpm_uart_drv_probe: Adding CPM UART %d\n", cpm_uart_id2nr(pdata->fs_no)); if ((ret = cpm_uart_drv_get_platform_data(pdev, 0))) return ret; @@ -1265,7 +1284,7 @@ static int cpm_uart_drv_remove(struct device *dev) struct fs_uart_platform_info *pdata = pdev->dev.platform_data; pr_debug("cpm_uart_drv_remove: Removing CPM UART %d\n", - cpm_uart_port_map[pdata->fs_no]); + cpm_uart_id2nr(pdata->fs_no)); uart_remove_one_port(&cpm_reg, &cpm_uart_ports[pdata->fs_no].port); return 0; diff --git a/drivers/serial/cpm_uart/cpm_uart_cpm1.c b/drivers/serial/cpm_uart/cpm_uart_cpm1.c index a5a30622637a..17406a05ce1f 100644 --- a/drivers/serial/cpm_uart/cpm_uart_cpm1.c +++ b/drivers/serial/cpm_uart/cpm_uart_cpm1.c @@ -8,6 +8,8 @@ * * Copyright (C) 2004 Freescale Semiconductor, Inc. * (C) 2004 Intracom, S.A. + * (C) 2006 MontaVista Software, Inc. + * Vitaly Bordug * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/drivers/serial/cpm_uart/cpm_uart_cpm2.c b/drivers/serial/cpm_uart/cpm_uart_cpm2.c index 7c6b07aeea92..4b2de08f46d0 100644 --- a/drivers/serial/cpm_uart/cpm_uart_cpm2.c +++ b/drivers/serial/cpm_uart/cpm_uart_cpm2.c @@ -8,6 +8,8 @@ * * Copyright (C) 2004 Freescale Semiconductor, Inc. * (C) 2004 Intracom, S.A. + * (C) 2006 MontaVista Software, Inc. + * Vitaly Bordug * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/include/linux/fs_uart_pd.h b/include/linux/fs_uart_pd.h new file mode 100644 index 000000000000..f5975126b712 --- /dev/null +++ b/include/linux/fs_uart_pd.h @@ -0,0 +1,60 @@ +/* + * Platform information definitions for the CPM Uart driver. + * + * 2006 (c) MontaVista Software, Inc. + * Vitaly Bordug + * + * This file is licensed under the terms of the GNU General Public License + * version 2. This program is licensed "as is" without any warranty of any + * kind, whether express or implied. + */ + +#ifndef FS_UART_PD_H +#define FS_UART_PD_H + +#include +#include + +enum fs_uart_id { + fsid_smc1_uart, + fsid_smc2_uart, + fsid_scc1_uart, + fsid_scc2_uart, + fsid_scc3_uart, + fsid_scc4_uart, + fs_uart_nr, +}; + +static inline int fs_uart_id_scc2fsid(int id) +{ + return fsid_scc1_uart + id - 1; +} + +static inline int fs_uart_id_fsid2scc(int id) +{ + return id - fsid_scc1_uart + 1; +} + +static inline int fs_uart_id_smc2fsid(int id) +{ + return fsid_smc1_uart + id - 1; +} + +static inline int fs_uart_id_fsid2smc(int id) +{ + return id - fsid_smc1_uart + 1; +} + +struct fs_uart_platform_info { + void(*init_ioports)(void); + /* device specific information */ + int fs_no; /* controller index */ + u32 uart_clk; + u8 tx_num_fifo; + u8 tx_buf_size; + u8 rx_num_fifo; + u8 rx_buf_size; + u8 brg; +}; + +#endif -- cgit v1.2.3 From 6bfd93c32a5065d0e858780b3beb0b667081601c Mon Sep 17 00:00:00 2001 From: Paul Mackerras Date: Wed, 3 May 2006 23:02:04 +1000 Subject: powerpc: Fix incorrect might_sleep in __get_user/__put_user on kernel addresses We have a case where __get_user and __put_user can validly be used on kernel addresses in interrupt context - namely, the alignment exception handler, as our get/put_unaligned just do a single access and rely on the alignment exception handler to fix things up in the rare cases where the cpu can't handle it in hardware. Thus we can get alignment exceptions in the network stack at interrupt level. The alignment exception handler does a __get_user to read the instruction and blows up in might_sleep(). Since a __get_user on a kernel address won't actually ever sleep, this makes the might_sleep conditional on the address being less than PAGE_OFFSET. Signed-off-by: Paul Mackerras --- include/asm-powerpc/uaccess.h | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/include/asm-powerpc/uaccess.h b/include/asm-powerpc/uaccess.h index 3872e924cdd6..d83fc29c2bbf 100644 --- a/include/asm-powerpc/uaccess.h +++ b/include/asm-powerpc/uaccess.h @@ -7,6 +7,7 @@ #include #include #include +#include #define VERIFY_READ 0 #define VERIFY_WRITE 1 @@ -179,9 +180,11 @@ do { \ #define __put_user_nocheck(x, ptr, size) \ ({ \ long __pu_err; \ - might_sleep(); \ + __typeof__(*(ptr)) __user *__pu_addr = (ptr); \ + if (!is_kernel_addr((unsigned long)__pu_addr)) \ + might_sleep(); \ __chk_user_ptr(ptr); \ - __put_user_size((x), (ptr), (size), __pu_err); \ + __put_user_size((x), __pu_addr, (size), __pu_err); \ __pu_err; \ }) @@ -258,9 +261,11 @@ do { \ ({ \ long __gu_err; \ unsigned long __gu_val; \ + const __typeof__(*(ptr)) __user *__gu_addr = (ptr); \ __chk_user_ptr(ptr); \ - might_sleep(); \ - __get_user_size(__gu_val, (ptr), (size), __gu_err); \ + if (!is_kernel_addr((unsigned long)__gu_addr)) \ + might_sleep(); \ + __get_user_size(__gu_val, __gu_addr, (size), __gu_err); \ (x) = (__typeof__(*(ptr)))__gu_val; \ __gu_err; \ }) @@ -270,9 +275,11 @@ do { \ ({ \ long __gu_err; \ long long __gu_val; \ + const __typeof__(*(ptr)) __user *__gu_addr = (ptr); \ __chk_user_ptr(ptr); \ - might_sleep(); \ - __get_user_size(__gu_val, (ptr), (size), __gu_err); \ + if (!is_kernel_addr((unsigned long)__gu_addr)) \ + might_sleep(); \ + __get_user_size(__gu_val, __gu_addr, (size), __gu_err); \ (x) = (__typeof__(*(ptr)))__gu_val; \ __gu_err; \ }) -- cgit v1.2.3 From 8c45112b823972e9ff7bbca77dc592ca2224951b Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Wed, 3 May 2006 13:55:46 -0700 Subject: [SPARC]: Hook up vmsplice into syscall tables. Signed-off-by: David S. Miller --- arch/sparc/kernel/systbls.S | 2 +- arch/sparc64/kernel/sys32.S | 1 + arch/sparc64/kernel/systbls.S | 4 ++-- include/asm-sparc/unistd.h | 2 +- include/asm-sparc64/unistd.h | 2 +- 5 files changed, 6 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/arch/sparc/kernel/systbls.S b/arch/sparc/kernel/systbls.S index db8faa75f94d..6e1135cc03b0 100644 --- a/arch/sparc/kernel/systbls.S +++ b/arch/sparc/kernel/systbls.S @@ -23,7 +23,7 @@ sys_call_table: /*10*/ .long sys_unlink, sunos_execv, sys_chdir, sys_chown16, sys_mknod /*15*/ .long sys_chmod, sys_lchown16, sparc_brk, sys_nis_syscall, sys_lseek /*20*/ .long sys_getpid, sys_capget, sys_capset, sys_setuid16, sys_getuid16 -/*25*/ .long sys_time, sys_ptrace, sys_alarm, sys_sigaltstack, sys_pause +/*25*/ .long sys_vmsplice, sys_ptrace, sys_alarm, sys_sigaltstack, sys_pause /*30*/ .long sys_utime, sys_lchown, sys_fchown, sys_access, sys_nice /*35*/ .long sys_chown, sys_sync, sys_kill, sys_newstat, sys_sendfile /*40*/ .long sys_newlstat, sys_dup, sys_pipe, sys_times, sys_getuid diff --git a/arch/sparc64/kernel/sys32.S b/arch/sparc64/kernel/sys32.S index f9b75760163c..bdf1f4d02e3f 100644 --- a/arch/sparc64/kernel/sys32.S +++ b/arch/sparc64/kernel/sys32.S @@ -139,6 +139,7 @@ SIGN3(sys32_ioprio_set, sys_ioprio_set, %o0, %o1, %o2) SIGN2(sys32_splice, sys_splice, %o0, %o1) SIGN2(sys32_sync_file_range, compat_sync_file_range, %o0, %o5) SIGN2(sys32_tee, sys_tee, %o0, %o1) +SIGN1(sys32_vmsplice, compat_sys_vmsplice, %o0) .globl sys32_mmap2 sys32_mmap2: diff --git a/arch/sparc64/kernel/systbls.S b/arch/sparc64/kernel/systbls.S index 62672cd92eca..d4b39cd30310 100644 --- a/arch/sparc64/kernel/systbls.S +++ b/arch/sparc64/kernel/systbls.S @@ -25,7 +25,7 @@ sys_call_table32: /*10*/ .word sys_unlink, sunos_execv, sys_chdir, sys32_chown16, sys32_mknod /*15*/ .word sys_chmod, sys32_lchown16, sparc_brk, sys32_perfctr, sys32_lseek /*20*/ .word sys_getpid, sys_capget, sys_capset, sys32_setuid16, sys32_getuid16 -/*25*/ .word compat_sys_time, sys_ptrace, sys_alarm, sys32_sigaltstack, sys32_pause +/*25*/ .word sys32_vmsplice, sys_ptrace, sys_alarm, sys32_sigaltstack, sys32_pause /*30*/ .word compat_sys_utime, sys_lchown, sys_fchown, sys32_access, sys32_nice .word sys_chown, sys_sync, sys32_kill, compat_sys_newstat, sys32_sendfile /*40*/ .word compat_sys_newlstat, sys_dup, sys_pipe, compat_sys_times, sys_getuid @@ -94,7 +94,7 @@ sys_call_table: /*10*/ .word sys_unlink, sys_nis_syscall, sys_chdir, sys_chown, sys_mknod /*15*/ .word sys_chmod, sys_lchown, sparc_brk, sys_perfctr, sys_lseek /*20*/ .word sys_getpid, sys_capget, sys_capset, sys_setuid, sys_getuid -/*25*/ .word sys_nis_syscall, sys_ptrace, sys_alarm, sys_sigaltstack, sys_nis_syscall +/*25*/ .word sys_vmsplice, sys_ptrace, sys_alarm, sys_sigaltstack, sys_nis_syscall /*30*/ .word sys_utime, sys_nis_syscall, sys_nis_syscall, sys_access, sys_nice .word sys_nis_syscall, sys_sync, sys_kill, sys_newstat, sys_sendfile64 /*40*/ .word sys_newlstat, sys_dup, sys_pipe, sys_times, sys_nis_syscall diff --git a/include/asm-sparc/unistd.h b/include/asm-sparc/unistd.h index 32a48f623e2b..f5611a721fbd 100644 --- a/include/asm-sparc/unistd.h +++ b/include/asm-sparc/unistd.h @@ -41,7 +41,7 @@ #define __NR_capset 22 /* Linux Specific */ #define __NR_setuid 23 /* Implemented via setreuid in SunOS */ #define __NR_getuid 24 /* Common */ -/* #define __NR_time alias 25 ENOSYS under SunOS */ +#define __NR_vmsplice 25 /* ENOSYS under SunOS */ #define __NR_ptrace 26 /* Common */ #define __NR_alarm 27 /* Implemented via setitimer in SunOS */ #define __NR_sigaltstack 28 /* Common */ diff --git a/include/asm-sparc64/unistd.h b/include/asm-sparc64/unistd.h index ca80e8aca128..68705748bec0 100644 --- a/include/asm-sparc64/unistd.h +++ b/include/asm-sparc64/unistd.h @@ -41,7 +41,7 @@ #define __NR_capset 22 /* Linux Specific */ #define __NR_setuid 23 /* Implemented via setreuid in SunOS */ #define __NR_getuid 24 /* Common */ -/* #define __NR_time alias 25 ENOSYS under SunOS */ +#define __NR_vmsplice 25 /* ENOSYS under SunOS */ #define __NR_ptrace 26 /* Common */ #define __NR_alarm 27 /* Implemented via setitimer in SunOS */ #define __NR_sigaltstack 28 /* Common */ -- cgit v1.2.3 From 1432873af7ae29d4bb3c56114c05b539d078ca62 Mon Sep 17 00:00:00 2001 From: Jens Axboe Date: Wed, 3 May 2006 10:35:26 +0200 Subject: [PATCH] splice: LRU fixups Nick says that the current construct isn't safe. This goes back to the original, but sets PIPE_BUF_FLAG_LRU on user pages as well as they all seem to be on the LRU in the first place. Signed-off-by: Jens Axboe --- fs/splice.c | 33 +++++++++++---------------------- include/linux/pipe_fs_i.h | 5 +++-- 2 files changed, 14 insertions(+), 24 deletions(-) (limited to 'include') diff --git a/fs/splice.c b/fs/splice.c index 27f5e3738a7b..0b202425b0b5 100644 --- a/fs/splice.c +++ b/fs/splice.c @@ -78,6 +78,7 @@ static int page_cache_pipe_buf_steal(struct pipe_inode_info *info, return 1; } + buf->flags |= PIPE_BUF_FLAG_LRU; return 0; } @@ -85,6 +86,7 @@ static void page_cache_pipe_buf_release(struct pipe_inode_info *info, struct pipe_buffer *buf) { page_cache_release(buf->page); + buf->flags &= ~PIPE_BUF_FLAG_LRU; } static int page_cache_pipe_buf_pin(struct pipe_inode_info *info, @@ -141,6 +143,7 @@ static int user_page_pipe_buf_steal(struct pipe_inode_info *pipe, if (!(buf->flags & PIPE_BUF_FLAG_GIFT)) return 1; + buf->flags |= PIPE_BUF_FLAG_LRU; return generic_pipe_buf_steal(pipe, buf); } @@ -566,37 +569,23 @@ static int pipe_to_file(struct pipe_inode_info *info, struct pipe_buffer *buf, */ if ((sd->flags & SPLICE_F_MOVE) && this_len == PAGE_CACHE_SIZE) { /* - * If steal succeeds, buf->page is now pruned from the vm - * side (page cache) and we can reuse it. The page will also - * be locked on successful return. + * If steal succeeds, buf->page is now pruned from the + * pagecache and we can reuse it. The page will also be + * locked on successful return. */ if (buf->ops->steal(info, buf)) goto find_page; page = buf->page; - page_cache_get(page); - - /* - * page must be on the LRU for adding to the pagecache. - * Check this without grabbing the zone lock, if it isn't - * the do grab the zone lock, recheck, and add if necessary. - */ - if (!PageLRU(page)) { - struct zone *zone = page_zone(page); - - spin_lock_irq(&zone->lru_lock); - if (!PageLRU(page)) { - SetPageLRU(page); - add_page_to_inactive_list(zone, page); - } - spin_unlock_irq(&zone->lru_lock); - } - if (add_to_page_cache(page, mapping, index, gfp_mask)) { - page_cache_release(page); unlock_page(page); goto find_page; } + + page_cache_get(page); + + if (!(buf->flags & PIPE_BUF_FLAG_LRU)) + lru_cache_add(page); } else { find_page: page = find_lock_page(mapping, index); diff --git a/include/linux/pipe_fs_i.h b/include/linux/pipe_fs_i.h index ba73108cbf8b..ea4f7cd7bfd8 100644 --- a/include/linux/pipe_fs_i.h +++ b/include/linux/pipe_fs_i.h @@ -5,8 +5,9 @@ #define PIPE_BUFFERS (16) -#define PIPE_BUF_FLAG_ATOMIC 0x01 /* was atomically mapped */ -#define PIPE_BUF_FLAG_GIFT 0x02 /* page is a gift */ +#define PIPE_BUF_FLAG_LRU 0x01 /* page is on the LRU */ +#define PIPE_BUF_FLAG_ATOMIC 0x02 /* was atomically mapped */ +#define PIPE_BUF_FLAG_GIFT 0x04 /* page is a gift */ struct pipe_buffer { struct page *page; -- cgit v1.2.3 From 7582e9d17edbabab6cbe59467c5d1b5e8c04fca8 Mon Sep 17 00:00:00 2001 From: Jing Min Zhao Date: Wed, 3 May 2006 23:19:59 -0700 Subject: [NETFILTER]: H.323 helper: Change author's email address Signed-off-by: Jing Min Zhao Signed-off-by: Patrick McHardy Signed-off-by: David S. Miller --- include/linux/netfilter_ipv4/ip_conntrack_helper_h323_asn1.h | 2 +- net/ipv4/netfilter/ip_conntrack_helper_h323_asn1.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/linux/netfilter_ipv4/ip_conntrack_helper_h323_asn1.h b/include/linux/netfilter_ipv4/ip_conntrack_helper_h323_asn1.h index 0bd828081c0c..c6e9a0b6d30b 100644 --- a/include/linux/netfilter_ipv4/ip_conntrack_helper_h323_asn1.h +++ b/include/linux/netfilter_ipv4/ip_conntrack_helper_h323_asn1.h @@ -2,7 +2,7 @@ * ip_conntrack_helper_h323_asn1.h - BER and PER decoding library for H.323 * conntrack/NAT module. * - * Copyright (c) 2006 by Jing Min Zhao + * Copyright (c) 2006 by Jing Min Zhao * * This source code is licensed under General Public License version 2. * diff --git a/net/ipv4/netfilter/ip_conntrack_helper_h323_asn1.c b/net/ipv4/netfilter/ip_conntrack_helper_h323_asn1.c index f52d2c45a6ae..355a53a5b6cd 100644 --- a/net/ipv4/netfilter/ip_conntrack_helper_h323_asn1.c +++ b/net/ipv4/netfilter/ip_conntrack_helper_h323_asn1.c @@ -2,7 +2,7 @@ * ip_conntrack_helper_h323_asn1.c - BER and PER decoding library for H.323 * conntrack/NAT module. * - * Copyright (c) 2006 by Jing Min Zhao + * Copyright (c) 2006 by Jing Min Zhao * * This source code is licensed under General Public License version 2. * -- cgit v1.2.3 From e1fdb5b39656ea2be8cadde565e543649a988af9 Mon Sep 17 00:00:00 2001 From: Ralf Baechle Date: Wed, 3 May 2006 23:27:16 -0700 Subject: [AX.25]: Eleminate HZ from AX.25 kernel interfaces Convert all AX.25 sysctl time values from jiffies to ms as units. Signed-off-by: Ralf Baechle Signed-off-by: David S. Miller --- include/net/ax25.h | 10 +++---- net/ax25/af_ax25.c | 73 ++++++++++++++++++++++++++-------------------- net/ax25/ax25_ds_timer.c | 3 +- net/ax25/sysctl_net_ax25.c | 10 +++---- 4 files changed, 53 insertions(+), 43 deletions(-) (limited to 'include') diff --git a/include/net/ax25.h b/include/net/ax25.h index d052b221dbcd..5bd997487054 100644 --- a/include/net/ax25.h +++ b/include/net/ax25.h @@ -145,14 +145,14 @@ enum { #define AX25_DEF_CONMODE 2 /* Connected mode allowed */ #define AX25_DEF_WINDOW 2 /* Window=2 */ #define AX25_DEF_EWINDOW 32 /* Module-128 Window=32 */ -#define AX25_DEF_T1 (10 * HZ) /* T1=10s */ -#define AX25_DEF_T2 (3 * HZ) /* T2=3s */ -#define AX25_DEF_T3 (300 * HZ) /* T3=300s */ +#define AX25_DEF_T1 10000 /* T1=10s */ +#define AX25_DEF_T2 3000 /* T2=3s */ +#define AX25_DEF_T3 300000 /* T3=300s */ #define AX25_DEF_N2 10 /* N2=10 */ -#define AX25_DEF_IDLE (0 * 60 * HZ) /* Idle=None */ +#define AX25_DEF_IDLE 0 /* Idle=None */ #define AX25_DEF_PACLEN 256 /* Paclen=256 */ #define AX25_DEF_PROTOCOL AX25_PROTO_STD_SIMPLEX /* Standard AX.25 */ -#define AX25_DEF_DS_TIMEOUT (3 * 60 * HZ) /* DAMA timeout 3 minutes */ +#define AX25_DEF_DS_TIMEOUT 180000 /* DAMA timeout 3 minutes */ typedef struct ax25_uid_assoc { struct hlist_node uid_node; diff --git a/net/ax25/af_ax25.c b/net/ax25/af_ax25.c index a9f13dfde07e..a2e0dd047e9f 100644 --- a/net/ax25/af_ax25.c +++ b/net/ax25/af_ax25.c @@ -426,6 +426,26 @@ static int ax25_ctl_ioctl(const unsigned int cmd, void __user *arg) return 0; } +static void ax25_fillin_cb_from_dev(ax25_cb *ax25, ax25_dev *ax25_dev) +{ + ax25->rtt = msecs_to_jiffies(ax25_dev->values[AX25_VALUES_T1]) / 2; + ax25->t1 = msecs_to_jiffies(ax25_dev->values[AX25_VALUES_T1]); + ax25->t2 = msecs_to_jiffies(ax25_dev->values[AX25_VALUES_T2]); + ax25->t3 = msecs_to_jiffies(ax25_dev->values[AX25_VALUES_T3]); + ax25->n2 = ax25_dev->values[AX25_VALUES_N2]; + ax25->paclen = ax25_dev->values[AX25_VALUES_PACLEN]; + ax25->idle = msecs_to_jiffies(ax25_dev->values[AX25_VALUES_IDLE]); + ax25->backoff = ax25_dev->values[AX25_VALUES_BACKOFF]; + + if (ax25_dev->values[AX25_VALUES_AXDEFMODE]) { + ax25->modulus = AX25_EMODULUS; + ax25->window = ax25_dev->values[AX25_VALUES_EWINDOW]; + } else { + ax25->modulus = AX25_MODULUS; + ax25->window = ax25_dev->values[AX25_VALUES_WINDOW]; + } +} + /* * Fill in a created AX.25 created control block with the default * values for a particular device. @@ -435,39 +455,28 @@ void ax25_fillin_cb(ax25_cb *ax25, ax25_dev *ax25_dev) ax25->ax25_dev = ax25_dev; if (ax25->ax25_dev != NULL) { - ax25->rtt = ax25_dev->values[AX25_VALUES_T1] / 2; - ax25->t1 = ax25_dev->values[AX25_VALUES_T1]; - ax25->t2 = ax25_dev->values[AX25_VALUES_T2]; - ax25->t3 = ax25_dev->values[AX25_VALUES_T3]; - ax25->n2 = ax25_dev->values[AX25_VALUES_N2]; - ax25->paclen = ax25_dev->values[AX25_VALUES_PACLEN]; - ax25->idle = ax25_dev->values[AX25_VALUES_IDLE]; - ax25->backoff = ax25_dev->values[AX25_VALUES_BACKOFF]; - - if (ax25_dev->values[AX25_VALUES_AXDEFMODE]) { - ax25->modulus = AX25_EMODULUS; - ax25->window = ax25_dev->values[AX25_VALUES_EWINDOW]; - } else { - ax25->modulus = AX25_MODULUS; - ax25->window = ax25_dev->values[AX25_VALUES_WINDOW]; - } + ax25_fillin_cb_from_dev(ax25, ax25_dev); + return; + } + + /* + * No device, use kernel / AX.25 spec default values + */ + ax25->rtt = msecs_to_jiffies(AX25_DEF_T1) / 2; + ax25->t1 = msecs_to_jiffies(AX25_DEF_T1); + ax25->t2 = msecs_to_jiffies(AX25_DEF_T2); + ax25->t3 = msecs_to_jiffies(AX25_DEF_T3); + ax25->n2 = AX25_DEF_N2; + ax25->paclen = AX25_DEF_PACLEN; + ax25->idle = msecs_to_jiffies(AX25_DEF_IDLE); + ax25->backoff = AX25_DEF_BACKOFF; + + if (AX25_DEF_AXDEFMODE) { + ax25->modulus = AX25_EMODULUS; + ax25->window = AX25_DEF_EWINDOW; } else { - ax25->rtt = AX25_DEF_T1 / 2; - ax25->t1 = AX25_DEF_T1; - ax25->t2 = AX25_DEF_T2; - ax25->t3 = AX25_DEF_T3; - ax25->n2 = AX25_DEF_N2; - ax25->paclen = AX25_DEF_PACLEN; - ax25->idle = AX25_DEF_IDLE; - ax25->backoff = AX25_DEF_BACKOFF; - - if (AX25_DEF_AXDEFMODE) { - ax25->modulus = AX25_EMODULUS; - ax25->window = AX25_DEF_EWINDOW; - } else { - ax25->modulus = AX25_MODULUS; - ax25->window = AX25_DEF_WINDOW; - } + ax25->modulus = AX25_MODULUS; + ax25->window = AX25_DEF_WINDOW; } } diff --git a/net/ax25/ax25_ds_timer.c b/net/ax25/ax25_ds_timer.c index 061083efc1dc..5961459935eb 100644 --- a/net/ax25/ax25_ds_timer.c +++ b/net/ax25/ax25_ds_timer.c @@ -61,7 +61,8 @@ void ax25_ds_set_timer(ax25_dev *ax25_dev) return; del_timer(&ax25_dev->dama.slave_timer); - ax25_dev->dama.slave_timeout = ax25_dev->values[AX25_VALUES_DS_TIMEOUT] / 10; + ax25_dev->dama.slave_timeout = + msecs_to_jiffies(ax25_dev->values[AX25_VALUES_DS_TIMEOUT]) / 10; ax25_ds_add_timer(ax25_dev); } diff --git a/net/ax25/sysctl_net_ax25.c b/net/ax25/sysctl_net_ax25.c index 894a22558d9d..bdb64c36df12 100644 --- a/net/ax25/sysctl_net_ax25.c +++ b/net/ax25/sysctl_net_ax25.c @@ -18,14 +18,14 @@ static int min_backoff[1], max_backoff[] = {2}; static int min_conmode[1], max_conmode[] = {2}; static int min_window[] = {1}, max_window[] = {7}; static int min_ewindow[] = {1}, max_ewindow[] = {63}; -static int min_t1[] = {1}, max_t1[] = {30 * HZ}; -static int min_t2[] = {1}, max_t2[] = {20 * HZ}; -static int min_t3[1], max_t3[] = {3600 * HZ}; -static int min_idle[1], max_idle[] = {65535 * HZ}; +static int min_t1[] = {1}, max_t1[] = {30000}; +static int min_t2[] = {1}, max_t2[] = {20000}; +static int min_t3[1], max_t3[] = {3600000}; +static int min_idle[1], max_idle[] = {65535000}; static int min_n2[] = {1}, max_n2[] = {31}; static int min_paclen[] = {1}, max_paclen[] = {512}; static int min_proto[1], max_proto[] = { AX25_PROTO_MAX }; -static int min_ds_timeout[1], max_ds_timeout[] = {65535 * HZ}; +static int min_ds_timeout[1], max_ds_timeout[] = {65535000}; static struct ctl_table_header *ax25_table_header; -- cgit v1.2.3 From 4d8937d0b113e8ec39f7d18cf13804f3b5fb8fd4 Mon Sep 17 00:00:00 2001 From: Ralf Baechle Date: Wed, 3 May 2006 23:27:47 -0700 Subject: [NETROM]: Eleminate HZ from NET/ROM kernel interfaces Convert all NET/ROM sysctl time values from jiffies to ms as units. Signed-off-by: Ralf Baechle Signed-off-by: David S. Miller --- include/net/netrom.h | 8 ++++---- net/netrom/af_netrom.c | 15 ++++++++++----- 2 files changed, 14 insertions(+), 9 deletions(-) (limited to 'include') diff --git a/include/net/netrom.h b/include/net/netrom.h index a5ee53bce62f..e0ca112024a3 100644 --- a/include/net/netrom.h +++ b/include/net/netrom.h @@ -42,11 +42,11 @@ enum { #define NR_COND_PEER_RX_BUSY 0x04 #define NR_COND_OWN_RX_BUSY 0x08 -#define NR_DEFAULT_T1 (120 * HZ) /* Outstanding frames - 120 seconds */ -#define NR_DEFAULT_T2 (5 * HZ) /* Response delay - 5 seconds */ +#define NR_DEFAULT_T1 120000 /* Outstanding frames - 120 seconds */ +#define NR_DEFAULT_T2 5000 /* Response delay - 5 seconds */ #define NR_DEFAULT_N2 3 /* Number of Retries - 3 */ -#define NR_DEFAULT_T4 (180 * HZ) /* Busy Delay - 180 seconds */ -#define NR_DEFAULT_IDLE (0 * 60 * HZ) /* No Activity Timeout - none */ +#define NR_DEFAULT_T4 180000 /* Busy Delay - 180 seconds */ +#define NR_DEFAULT_IDLE 0 /* No Activity Timeout - none */ #define NR_DEFAULT_WINDOW 4 /* Default Window Size - 4 */ #define NR_DEFAULT_OBS 6 /* Default Obsolescence Count - 6 */ #define NR_DEFAULT_QUAL 10 /* Default Neighbour Quality - 10 */ diff --git a/net/netrom/af_netrom.c b/net/netrom/af_netrom.c index d44981f5a619..ecd288beca7c 100644 --- a/net/netrom/af_netrom.c +++ b/net/netrom/af_netrom.c @@ -425,11 +425,16 @@ static int nr_create(struct socket *sock, int protocol) nr_init_timers(sk); - nr->t1 = sysctl_netrom_transport_timeout; - nr->t2 = sysctl_netrom_transport_acknowledge_delay; - nr->n2 = sysctl_netrom_transport_maximum_tries; - nr->t4 = sysctl_netrom_transport_busy_delay; - nr->idle = sysctl_netrom_transport_no_activity_timeout; + nr->t1 = + msecs_to_jiffies(sysctl_netrom_transport_timeout); + nr->t2 = + msecs_to_jiffies(sysctl_netrom_transport_acknowledge_delay); + nr->n2 = + msecs_to_jiffies(sysctl_netrom_transport_maximum_tries); + nr->t4 = + msecs_to_jiffies(sysctl_netrom_transport_busy_delay); + nr->idle = + msecs_to_jiffies(sysctl_netrom_transport_no_activity_timeout); nr->window = sysctl_netrom_transport_requested_window_size; nr->bpqext = 1; -- cgit v1.2.3 From 82e84249f0ee098e004c8bd6d90a1640bd56cfbb Mon Sep 17 00:00:00 2001 From: Ralf Baechle Date: Wed, 3 May 2006 23:28:20 -0700 Subject: [ROSE]: Eleminate HZ from ROSE kernel interfaces Convert all ROSE sysctl time values from jiffies to ms as units. Signed-off-by: Ralf Baechle Signed-off-by: David S. Miller --- include/net/rose.h | 14 +++++++------- net/rose/af_rose.c | 10 +++++----- net/rose/rose_link.c | 6 ++++-- 3 files changed, 16 insertions(+), 14 deletions(-) (limited to 'include') diff --git a/include/net/rose.h b/include/net/rose.h index 3249b979605a..012b09ed2401 100644 --- a/include/net/rose.h +++ b/include/net/rose.h @@ -49,14 +49,14 @@ enum { ROSE_STATE_5 /* Deferred Call Acceptance */ }; -#define ROSE_DEFAULT_T0 (180 * HZ) /* Default T10 T20 value */ -#define ROSE_DEFAULT_T1 (200 * HZ) /* Default T11 T21 value */ -#define ROSE_DEFAULT_T2 (180 * HZ) /* Default T12 T22 value */ -#define ROSE_DEFAULT_T3 (180 * HZ) /* Default T13 T23 value */ -#define ROSE_DEFAULT_HB (5 * HZ) /* Default Holdback value */ -#define ROSE_DEFAULT_IDLE (0 * 60 * HZ) /* No Activity Timeout - none */ +#define ROSE_DEFAULT_T0 180000 /* Default T10 T20 value */ +#define ROSE_DEFAULT_T1 200000 /* Default T11 T21 value */ +#define ROSE_DEFAULT_T2 180000 /* Default T12 T22 value */ +#define ROSE_DEFAULT_T3 180000 /* Default T13 T23 value */ +#define ROSE_DEFAULT_HB 5000 /* Default Holdback value */ +#define ROSE_DEFAULT_IDLE 0 /* No Activity Timeout - none */ #define ROSE_DEFAULT_ROUTING 1 /* Default routing flag */ -#define ROSE_DEFAULT_FAIL_TIMEOUT (120 * HZ) /* Time until link considered usable */ +#define ROSE_DEFAULT_FAIL_TIMEOUT 120000 /* Time until link considered usable */ #define ROSE_DEFAULT_MAXVC 50 /* Maximum number of VCs per neighbour */ #define ROSE_DEFAULT_WINDOW_SIZE 7 /* Default window size */ diff --git a/net/rose/af_rose.c b/net/rose/af_rose.c index ea65396d1619..ef4538ac84f0 100644 --- a/net/rose/af_rose.c +++ b/net/rose/af_rose.c @@ -518,11 +518,11 @@ static int rose_create(struct socket *sock, int protocol) init_timer(&rose->timer); init_timer(&rose->idletimer); - rose->t1 = sysctl_rose_call_request_timeout; - rose->t2 = sysctl_rose_reset_request_timeout; - rose->t3 = sysctl_rose_clear_request_timeout; - rose->hb = sysctl_rose_ack_hold_back_timeout; - rose->idle = sysctl_rose_no_activity_timeout; + rose->t1 = msecs_to_jiffies(sysctl_rose_call_request_timeout); + rose->t2 = msecs_to_jiffies(sysctl_rose_reset_request_timeout); + rose->t3 = msecs_to_jiffies(sysctl_rose_clear_request_timeout); + rose->hb = msecs_to_jiffies(sysctl_rose_ack_hold_back_timeout); + rose->idle = msecs_to_jiffies(sysctl_rose_no_activity_timeout); rose->state = ROSE_STATE_0; diff --git a/net/rose/rose_link.c b/net/rose/rose_link.c index 09e9e9d04d92..bd86a63960ce 100644 --- a/net/rose/rose_link.c +++ b/net/rose/rose_link.c @@ -40,7 +40,8 @@ void rose_start_ftimer(struct rose_neigh *neigh) neigh->ftimer.data = (unsigned long)neigh; neigh->ftimer.function = &rose_ftimer_expiry; - neigh->ftimer.expires = jiffies + sysctl_rose_link_fail_timeout; + neigh->ftimer.expires = + jiffies + msecs_to_jiffies(sysctl_rose_link_fail_timeout); add_timer(&neigh->ftimer); } @@ -51,7 +52,8 @@ static void rose_start_t0timer(struct rose_neigh *neigh) neigh->t0timer.data = (unsigned long)neigh; neigh->t0timer.function = &rose_t0timer_expiry; - neigh->t0timer.expires = jiffies + sysctl_rose_restart_request_timeout; + neigh->t0timer.expires = + jiffies + msecs_to_jiffies(sysctl_rose_restart_request_timeout); add_timer(&neigh->t0timer); } -- cgit v1.2.3 From 5b802344357338a4d645beac8ca97470bcbe3542 Mon Sep 17 00:00:00 2001 From: Sascha Hauer Date: Thu, 4 May 2006 14:07:42 +0100 Subject: [ARM] 3490/1: i.MX: move uart resources to board files Patch from Sascha Hauer This patch moves the i.MX uart resources and the gpio pin setup to the board files. This allows the boards to decide how many internal uarts are connected to the outside world and whether they use rts/cts or not. Signed-off-by: Sascha Hauer Signed-off-by: Russell King --- arch/arm/mach-imx/generic.c | 52 -------------------------- arch/arm/mach-imx/mx1ads.c | 74 +++++++++++++++++++++++++++++++++++++ drivers/serial/imx.c | 40 ++++++++------------ include/asm-arm/arch-imx/imx-uart.h | 10 +++++ 4 files changed, 100 insertions(+), 76 deletions(-) create mode 100644 include/asm-arm/arch-imx/imx-uart.h (limited to 'include') diff --git a/arch/arm/mach-imx/generic.c b/arch/arm/mach-imx/generic.c index 9d8331be2b58..12ea58a3b84f 100644 --- a/arch/arm/mach-imx/generic.c +++ b/arch/arm/mach-imx/generic.c @@ -195,56 +195,6 @@ void __init imx_set_mmc_info(struct imxmmc_platform_data *info) } EXPORT_SYMBOL(imx_set_mmc_info); -static struct resource imx_uart1_resources[] = { - [0] = { - .start = 0x00206000, - .end = 0x002060FF, - .flags = IORESOURCE_MEM, - }, - [1] = { - .start = (UART1_MINT_RX), - .end = (UART1_MINT_RX), - .flags = IORESOURCE_IRQ, - }, - [2] = { - .start = (UART1_MINT_TX), - .end = (UART1_MINT_TX), - .flags = IORESOURCE_IRQ, - }, -}; - -static struct platform_device imx_uart1_device = { - .name = "imx-uart", - .id = 0, - .num_resources = ARRAY_SIZE(imx_uart1_resources), - .resource = imx_uart1_resources, -}; - -static struct resource imx_uart2_resources[] = { - [0] = { - .start = 0x00207000, - .end = 0x002070FF, - .flags = IORESOURCE_MEM, - }, - [1] = { - .start = (UART2_MINT_RX), - .end = (UART2_MINT_RX), - .flags = IORESOURCE_IRQ, - }, - [2] = { - .start = (UART2_MINT_TX), - .end = (UART2_MINT_TX), - .flags = IORESOURCE_IRQ, - }, -}; - -static struct platform_device imx_uart2_device = { - .name = "imx-uart", - .id = 1, - .num_resources = ARRAY_SIZE(imx_uart2_resources), - .resource = imx_uart2_resources, -}; - static struct imxfb_mach_info imx_fb_info; void __init set_imx_fb_info(struct imxfb_mach_info *hard_imx_fb_info) @@ -283,8 +233,6 @@ static struct platform_device imxfb_device = { static struct platform_device *devices[] __initdata = { &imx_mmc_device, &imxfb_device, - &imx_uart1_device, - &imx_uart2_device, }; static struct map_desc imx_io_desc[] __initdata = { diff --git a/arch/arm/mach-imx/mx1ads.c b/arch/arm/mach-imx/mx1ads.c index e34d0df90aed..e1f6c0bbe1e7 100644 --- a/arch/arm/mach-imx/mx1ads.c +++ b/arch/arm/mach-imx/mx1ads.c @@ -26,6 +26,7 @@ #include #include +#include #include #include "generic.h" @@ -48,8 +49,70 @@ static struct platform_device cs89x0_device = { .resource = cs89x0_resources, }; +static struct imxuart_platform_data uart_pdata = { + .flags = IMXUART_HAVE_RTSCTS, +}; + +static struct resource imx_uart1_resources[] = { + [0] = { + .start = 0x00206000, + .end = 0x002060FF, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = (UART1_MINT_RX), + .end = (UART1_MINT_RX), + .flags = IORESOURCE_IRQ, + }, + [2] = { + .start = (UART1_MINT_TX), + .end = (UART1_MINT_TX), + .flags = IORESOURCE_IRQ, + }, +}; + +static struct platform_device imx_uart1_device = { + .name = "imx-uart", + .id = 0, + .num_resources = ARRAY_SIZE(imx_uart1_resources), + .resource = imx_uart1_resources, + .dev = { + .platform_data = &uart_pdata, + } +}; + +static struct resource imx_uart2_resources[] = { + [0] = { + .start = 0x00207000, + .end = 0x002070FF, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = (UART2_MINT_RX), + .end = (UART2_MINT_RX), + .flags = IORESOURCE_IRQ, + }, + [2] = { + .start = (UART2_MINT_TX), + .end = (UART2_MINT_TX), + .flags = IORESOURCE_IRQ, + }, +}; + +static struct platform_device imx_uart2_device = { + .name = "imx-uart", + .id = 1, + .num_resources = ARRAY_SIZE(imx_uart2_resources), + .resource = imx_uart2_resources, + .dev = { + .platform_data = &uart_pdata, + } +}; + static struct platform_device *devices[] __initdata = { &cs89x0_device, + &imx_uart1_device, + &imx_uart2_device, }; #ifdef CONFIG_MMC_IMX @@ -75,6 +138,17 @@ mx1ads_init(void) imx_gpio_mode(GPIO_PORTB | GPIO_GIUS | GPIO_IN | 20); imx_set_mmc_info(&mx1ads_mmc_info); #endif + + imx_gpio_mode(PC9_PF_UART1_CTS); + imx_gpio_mode(PC10_PF_UART1_RTS); + imx_gpio_mode(PC11_PF_UART1_TXD); + imx_gpio_mode(PC12_PF_UART1_RXD); + + imx_gpio_mode(PB28_PF_UART2_CTS); + imx_gpio_mode(PB29_PF_UART2_RTS); + imx_gpio_mode(PB30_PF_UART2_TXD); + imx_gpio_mode(PB31_PF_UART2_RXD); + platform_add_devices(devices, ARRAY_SIZE(devices)); } diff --git a/drivers/serial/imx.c b/drivers/serial/imx.c index c3b7a6673e9c..d202eb4f3848 100644 --- a/drivers/serial/imx.c +++ b/drivers/serial/imx.c @@ -45,6 +45,7 @@ #include #include #include +#include /* We've been assigned a range on the "Low-density serial ports" major */ #define SERIAL_IMX_MAJOR 204 @@ -73,7 +74,8 @@ struct imx_port { struct uart_port port; struct timer_list timer; unsigned int old_status; - int txirq,rxirq,rtsirq; + int txirq,rxirq,rtsirq; + int have_rtscts:1; }; /* @@ -491,8 +493,12 @@ imx_set_termios(struct uart_port *port, struct termios *termios, ucr2 = UCR2_SRST | UCR2_IRTS; if (termios->c_cflag & CRTSCTS) { - ucr2 &= ~UCR2_IRTS; - ucr2 |= UCR2_CTSC; + if( sport->have_rtscts ) { + ucr2 &= ~UCR2_IRTS; + ucr2 |= UCR2_CTSC; + } else { + termios->c_cflag &= ~CRTSCTS; + } } if (termios->c_cflag & CSTOPB) @@ -719,27 +725,6 @@ static void __init imx_init_ports(void) imx_ports[i].timer.function = imx_timeout; imx_ports[i].timer.data = (unsigned long)&imx_ports[i]; } - - imx_gpio_mode(PC9_PF_UART1_CTS); - imx_gpio_mode(PC10_PF_UART1_RTS); - imx_gpio_mode(PC11_PF_UART1_TXD); - imx_gpio_mode(PC12_PF_UART1_RXD); - imx_gpio_mode(PB28_PF_UART2_CTS); - imx_gpio_mode(PB29_PF_UART2_RTS); - - imx_gpio_mode(PB30_PF_UART2_TXD); - imx_gpio_mode(PB31_PF_UART2_RXD); - -#if 0 /* We don't need these, on the mx1 the _modem_ side of the uart - * is implemented. - */ - imx_gpio_mode(PD7_AF_UART2_DTR); - imx_gpio_mode(PD8_AF_UART2_DCD); - imx_gpio_mode(PD9_AF_UART2_RI); - imx_gpio_mode(PD10_AF_UART2_DSR); -#endif - - } #ifdef CONFIG_SERIAL_IMX_CONSOLE @@ -932,7 +917,14 @@ static int serial_imx_resume(struct platform_device *dev) static int serial_imx_probe(struct platform_device *dev) { + struct imxuart_platform_data *pdata; + imx_ports[dev->id].port.dev = &dev->dev; + + pdata = (struct imxuart_platform_data *)dev->dev.platform_data; + if(pdata && (pdata->flags & IMXUART_HAVE_RTSCTS)) + imx_ports[dev->id].have_rtscts = 1; + uart_add_one_port(&imx_reg, &imx_ports[dev->id].port); platform_set_drvdata(dev, &imx_ports[dev->id]); return 0; diff --git a/include/asm-arm/arch-imx/imx-uart.h b/include/asm-arm/arch-imx/imx-uart.h new file mode 100644 index 000000000000..3a685e1780ea --- /dev/null +++ b/include/asm-arm/arch-imx/imx-uart.h @@ -0,0 +1,10 @@ +#ifndef ASMARM_ARCH_UART_H +#define ASMARM_ARCH_UART_H + +#define IMXUART_HAVE_RTSCTS (1<<0) + +struct imxuart_platform_data { + unsigned int flags; +}; + +#endif -- cgit v1.2.3 From ff10952a547dad934d9ed9afc5cf579ed1ccb53a Mon Sep 17 00:00:00 2001 From: Nicolas Pitre Date: Fri, 5 May 2006 15:11:14 +0100 Subject: [ARM] 3494/1: asm-arm/bug.h needs linux/stddef.h Patch from Nicolas Pitre ... for the definition of NULL. Signed-off-by: Nicolas Pitre Signed-off-by: Russell King --- include/asm-arm/bug.h | 1 + 1 file changed, 1 insertion(+) (limited to 'include') diff --git a/include/asm-arm/bug.h b/include/asm-arm/bug.h index 7fb02138f585..5ab8216f5204 100644 --- a/include/asm-arm/bug.h +++ b/include/asm-arm/bug.h @@ -2,6 +2,7 @@ #define _ASMARM_BUG_H #include +#include #ifdef CONFIG_BUG #ifdef CONFIG_DEBUG_BUGVERBOSE -- cgit v1.2.3 From 913ed41eb5c948d2f8b5deffd29c2638eceef3d7 Mon Sep 17 00:00:00 2001 From: Jon Mason Date: Wed, 3 May 2006 17:26:58 -0500 Subject: [IA64] remove asm-ia64/bitops.h self-inclusion asm-ia64/bitops.h includes itself. The #ifndef _ASM_IA64_BITOPS_H prevents this from being an issue, but it should still be removed. Signed-off-by: Jon Mason Signed-off-by: Tony Luck --- include/asm-ia64/bitops.h | 1 - 1 file changed, 1 deletion(-) (limited to 'include') diff --git a/include/asm-ia64/bitops.h b/include/asm-ia64/bitops.h index 90921e162793..6cc517e212a9 100644 --- a/include/asm-ia64/bitops.h +++ b/include/asm-ia64/bitops.h @@ -11,7 +11,6 @@ #include #include -#include #include /** -- cgit v1.2.3 From d57336e3f2dd7c2d1fbe4a8323029869fb6e1f00 Mon Sep 17 00:00:00 2001 From: Daniel Drake Date: Sun, 30 Apr 2006 22:09:07 +0100 Subject: [PATCH] softmac: make non-operational after being stopped zd1211 with softmac and wpa_supplicant revealed an issue with softmac and the use of workqueues. Some of the work functions actually reschedule themselves, so this meant that there could still be pending work after flush_scheduled_work() had been called during ieee80211softmac_stop(). This patch introduces a "running" flag which is used to ensure that rescheduling does not happen in this situation. I also used this flag to ensure that softmac's hooks into ieee80211 are non-operational once the stop operation has been started. This simply makes softmac a little more robust, because I could crash it easily by receiving frames in the short timeframe after shutting down softmac and before turning off the ZD1211 radio. (ZD1211 is now fixed as well!) Signed-off-by: Daniel Drake Acked-by: Johannes Berg Signed-off-by: John W. Linville --- include/net/ieee80211softmac.h | 3 ++- net/ieee80211/softmac/ieee80211softmac_assoc.c | 17 +++++++++++++++-- net/ieee80211/softmac/ieee80211softmac_auth.c | 11 +++++++++++ net/ieee80211/softmac/ieee80211softmac_module.c | 4 ++++ net/ieee80211/softmac/ieee80211softmac_scan.c | 8 ++++++++ 5 files changed, 40 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/include/net/ieee80211softmac.h b/include/net/ieee80211softmac.h index b1ebfbae397f..052ed596a4e4 100644 --- a/include/net/ieee80211softmac.h +++ b/include/net/ieee80211softmac.h @@ -204,7 +204,8 @@ struct ieee80211softmac_device { /* couple of flags */ u8 scanning:1, /* protects scanning from being done multiple times at once */ - associated:1; + associated:1, + running:1; struct ieee80211softmac_scaninfo *scaninfo; struct ieee80211softmac_assoc_info associnfo; diff --git a/net/ieee80211/softmac/ieee80211softmac_assoc.c b/net/ieee80211/softmac/ieee80211softmac_assoc.c index fb79ce7d6439..57ea9f6f465c 100644 --- a/net/ieee80211/softmac/ieee80211softmac_assoc.c +++ b/net/ieee80211/softmac/ieee80211softmac_assoc.c @@ -51,11 +51,12 @@ ieee80211softmac_assoc(struct ieee80211softmac_device *mac, struct ieee80211soft spin_lock_irqsave(&mac->lock, flags); mac->associnfo.associating = 1; mac->associated = 0; /* just to make sure */ - spin_unlock_irqrestore(&mac->lock, flags); /* Set a timer for timeout */ /* FIXME: make timeout configurable */ - schedule_delayed_work(&mac->associnfo.timeout, 5 * HZ); + if (likely(mac->running)) + schedule_delayed_work(&mac->associnfo.timeout, 5 * HZ); + spin_unlock_irqrestore(&mac->lock, flags); } void @@ -319,6 +320,9 @@ ieee80211softmac_handle_assoc_response(struct net_device * dev, u16 status = le16_to_cpup(&resp->status); struct ieee80211softmac_network *network = NULL; unsigned long flags; + + if (unlikely(!mac->running)) + return -ENODEV; spin_lock_irqsave(&mac->lock, flags); @@ -377,10 +381,16 @@ ieee80211softmac_handle_disassoc(struct net_device * dev, { struct ieee80211softmac_device *mac = ieee80211_priv(dev); unsigned long flags; + + if (unlikely(!mac->running)) + return -ENODEV; + if (memcmp(disassoc->header.addr2, mac->associnfo.bssid, ETH_ALEN)) return 0; + if (memcmp(disassoc->header.addr1, mac->dev->dev_addr, ETH_ALEN)) return 0; + dprintk(KERN_INFO PFX "got disassoc frame\n"); netif_carrier_off(dev); spin_lock_irqsave(&mac->lock, flags); @@ -400,6 +410,9 @@ ieee80211softmac_handle_reassoc_req(struct net_device * dev, struct ieee80211softmac_device *mac = ieee80211_priv(dev); struct ieee80211softmac_network *network; + if (unlikely(!mac->running)) + return -ENODEV; + network = ieee80211softmac_get_network_by_bssid(mac, resp->header.addr3); if (!network) { dprintkl(KERN_INFO PFX "reassoc request from unknown network\n"); diff --git a/net/ieee80211/softmac/ieee80211softmac_auth.c b/net/ieee80211/softmac/ieee80211softmac_auth.c index d6a04f3ab86c..06e332624665 100644 --- a/net/ieee80211/softmac/ieee80211softmac_auth.c +++ b/net/ieee80211/softmac/ieee80211softmac_auth.c @@ -86,6 +86,11 @@ ieee80211softmac_auth_queue(void *data) /* Lock and set flags */ spin_lock_irqsave(&mac->lock, flags); + if (unlikely(!mac->running)) { + /* Prevent reschedule on workqueue flush */ + spin_unlock_irqrestore(&mac->lock, flags); + return; + } net->authenticated = 0; net->authenticating = 1; /* add a timeout call so we eventually give up waiting for an auth reply */ @@ -124,6 +129,9 @@ ieee80211softmac_auth_resp(struct net_device *dev, struct ieee80211_auth *auth) unsigned long flags; u8 * data; + if (unlikely(!mac->running)) + return -ENODEV; + /* Find correct auth queue item */ spin_lock_irqsave(&mac->lock, flags); list_for_each(list_ptr, &mac->auth_queue) { @@ -336,6 +344,9 @@ ieee80211softmac_deauth_resp(struct net_device *dev, struct ieee80211_deauth *de struct ieee80211softmac_network *net = NULL; struct ieee80211softmac_device *mac = ieee80211_priv(dev); + if (unlikely(!mac->running)) + return -ENODEV; + if (!deauth) { dprintk("deauth without deauth packet. eek!\n"); return 0; diff --git a/net/ieee80211/softmac/ieee80211softmac_module.c b/net/ieee80211/softmac/ieee80211softmac_module.c index be83bdc1644a..6252be2c0db9 100644 --- a/net/ieee80211/softmac/ieee80211softmac_module.c +++ b/net/ieee80211/softmac/ieee80211softmac_module.c @@ -89,6 +89,8 @@ ieee80211softmac_clear_pending_work(struct ieee80211softmac_device *sm) ieee80211softmac_wait_for_scan(sm); spin_lock_irqsave(&sm->lock, flags); + sm->running = 0; + /* Free all pending assoc work items */ cancel_delayed_work(&sm->associnfo.work); @@ -204,6 +206,8 @@ void ieee80211softmac_start(struct net_device *dev) assert(0); if (mac->txrates_change) mac->txrates_change(dev, change, &oldrates); + + mac->running = 1; } EXPORT_SYMBOL_GPL(ieee80211softmac_start); diff --git a/net/ieee80211/softmac/ieee80211softmac_scan.c b/net/ieee80211/softmac/ieee80211softmac_scan.c index 2b9e7edfa3ce..d31cf77498c4 100644 --- a/net/ieee80211/softmac/ieee80211softmac_scan.c +++ b/net/ieee80211/softmac/ieee80211softmac_scan.c @@ -115,7 +115,15 @@ void ieee80211softmac_scan(void *d) // TODO: is this if correct, or should we do this only if scanning from assoc request? if (sm->associnfo.req_essid.len) ieee80211softmac_send_mgt_frame(sm, &sm->associnfo.req_essid, IEEE80211_STYPE_PROBE_REQ, 0); + + spin_lock_irqsave(&sm->lock, flags); + if (unlikely(!sm->running)) { + /* Prevent reschedule on workqueue flush */ + spin_unlock_irqrestore(&sm->lock, flags); + break; + } schedule_delayed_work(&si->softmac_scan, IEEE80211SOFTMAC_PROBE_DELAY); + spin_unlock_irqrestore(&sm->lock, flags); return; } else { dprintk(PFX "Not probing Channel %d (not allowed here)\n", si->channels[current_channel_idx].channel); -- cgit v1.2.3 From f21709d70ad6d7ad50288f7056c3a368138b017c Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Thu, 4 May 2006 19:47:19 +0200 Subject: [PATCH] ieee80211: Fix A band channel count (resent) The channel count for 802.11a is still not right. We better compute it from the min and max channel numbers, rather than hardcoding it. Signed-off-by: Jean Delvare Signed-off-by: John W. Linville --- include/net/ieee80211.h | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/net/ieee80211.h b/include/net/ieee80211.h index 4725ff861c57..d5926bfb1fc9 100644 --- a/include/net/ieee80211.h +++ b/include/net/ieee80211.h @@ -955,11 +955,13 @@ enum ieee80211_state { #define IEEE80211_24GHZ_MIN_CHANNEL 1 #define IEEE80211_24GHZ_MAX_CHANNEL 14 -#define IEEE80211_24GHZ_CHANNELS 14 +#define IEEE80211_24GHZ_CHANNELS (IEEE80211_24GHZ_MAX_CHANNEL - \ + IEEE80211_24GHZ_MIN_CHANNEL + 1) #define IEEE80211_52GHZ_MIN_CHANNEL 34 #define IEEE80211_52GHZ_MAX_CHANNEL 165 -#define IEEE80211_52GHZ_CHANNELS 131 +#define IEEE80211_52GHZ_CHANNELS (IEEE80211_52GHZ_MAX_CHANNEL - \ + IEEE80211_52GHZ_MIN_CHANNEL + 1) enum { IEEE80211_CH_PASSIVE_ONLY = (1 << 0), -- cgit v1.2.3 From 99532559dc7a8e686b2cef14c780a7ad5dbd4a31 Mon Sep 17 00:00:00 2001 From: Nicolas Pitre Date: Fri, 5 May 2006 22:32:24 +0100 Subject: [ARM] 3500/1: fix PXA27x DMA allocation priority Patch from Nicolas Pitre Intel PXA27x developers manual section 5.4.1.1 lists a priority distribution for the DMA channels differently than what the code currently assumes. This patch fixes that. Noticed by Simon Vogl Signed-off-by: Nicolas Pitre Signed-off-by: Russell King --- arch/arm/mach-pxa/dma.c | 17 +++++------------ include/asm-arm/arch-pxa/dma.h | 26 ++++++++++++++------------ 2 files changed, 19 insertions(+), 24 deletions(-) (limited to 'include') diff --git a/arch/arm/mach-pxa/dma.c b/arch/arm/mach-pxa/dma.c index 458112b21e25..7d8c85486c66 100644 --- a/arch/arm/mach-pxa/dma.c +++ b/arch/arm/mach-pxa/dma.c @@ -45,23 +45,16 @@ int pxa_request_dma (char *name, pxa_dma_prio prio, local_irq_save(flags); - /* try grabbing a DMA channel with the requested priority */ - for (i = prio; i < prio + PXA_DMA_NBCH(prio); i++) { - if (!dma_channels[i].name) { - found = 1; - break; - } - } - - if (!found) { - /* requested prio group is full, try hier priorities */ - for (i = prio-1; i >= 0; i--) { + do { + /* try grabbing a DMA channel with the requested priority */ + pxa_for_each_dma_prio (i, prio) { if (!dma_channels[i].name) { found = 1; break; } } - } + /* if requested prio group is full, try a hier priority */ + } while (!found && prio--); if (found) { DCSR(i) = DCSR_STARTINTR|DCSR_ENDINTR|DCSR_BUSERR; diff --git a/include/asm-arm/arch-pxa/dma.h b/include/asm-arm/arch-pxa/dma.h index 3e88a2a02a0f..a008150abc59 100644 --- a/include/asm-arm/arch-pxa/dma.h +++ b/include/asm-arm/arch-pxa/dma.h @@ -24,27 +24,29 @@ typedef struct pxa_dma_desc { volatile u32 dcmd; /* DCMD value for the current transfer */ } pxa_dma_desc; +typedef enum { + DMA_PRIO_HIGH = 0, + DMA_PRIO_MEDIUM = 1, + DMA_PRIO_LOW = 2 +} pxa_dma_prio; + #if defined(CONFIG_PXA27x) #define PXA_DMA_CHANNELS 32 -#define PXA_DMA_NBCH(prio) ((prio == DMA_PRIO_LOW) ? 16 : 8) -typedef enum { - DMA_PRIO_HIGH = 0, - DMA_PRIO_MEDIUM = 8, - DMA_PRIO_LOW = 16 -} pxa_dma_prio; +#define pxa_for_each_dma_prio(ch, prio) \ +for ( \ + ch = prio * 4; \ + ch != (4 << prio) + 16; \ + ch = (ch + 1 == (4 << prio)) ? (prio * 4 + 16) : (ch + 1) \ +) #elif defined(CONFIG_PXA25x) #define PXA_DMA_CHANNELS 16 -#define PXA_DMA_NBCH(prio) ((prio == DMA_PRIO_LOW) ? 8 : 4) -typedef enum { - DMA_PRIO_HIGH = 0, - DMA_PRIO_MEDIUM = 4, - DMA_PRIO_LOW = 8 -} pxa_dma_prio; +#define pxa_for_each_dma_prio(ch, prio) \ + for (ch = prio * 4; ch != (4 << prio); ch++) #endif -- cgit v1.2.3 From 568cb09b9d889b6f2852ede19772b8e9eed36c1e Mon Sep 17 00:00:00 2001 From: Nicolas Pitre Date: Fri, 5 May 2006 22:35:05 +0100 Subject: [ARM] 3495/1: EABI: undefine removed syscalls, but... Patch from Nicolas Pitre ... but only for user space. Signed-off-by: Nicolas Pitre Signed-off-by: Russell King --- include/asm-arm/unistd.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/asm-arm/unistd.h b/include/asm-arm/unistd.h index 26f2f4828e03..cbf39a56dbe7 100644 --- a/include/asm-arm/unistd.h +++ b/include/asm-arm/unistd.h @@ -363,7 +363,7 @@ /* * The following syscalls are obsolete and no longer available for EABI. */ -#if defined(__ARM_EABI__) +#if defined(__ARM_EABI__) && !defined(__KERNEL__) #undef __NR_time #undef __NR_umount #undef __NR_stime -- cgit v1.2.3 From 7c3ceb4fb9667f34f1599a062efecf4cdc4a4ce5 Mon Sep 17 00:00:00 2001 From: Neil Horman Date: Fri, 5 May 2006 17:02:09 -0700 Subject: [SCTP]: Allow spillover of receive buffer to avoid deadlock. This patch fixes a deadlock situation in the receive path by allowing temporary spillover of the receive buffer. - If the chunk we receive has a tsn that immediately follows the ctsn, accept it even if we run out of receive buffer space and renege data with higher TSNs. - Once we accept one chunk in a packet, accept all the remaining chunks even if we run out of receive buffer space. Signed-off-by: Neil Horman Acked-by: Mark Butler Acked-by: Vlad Yasevich Signed-off-by: Sridhar Samudrala Signed-off-by: David S. Miller --- include/net/sctp/structs.h | 1 + net/sctp/inqueue.c | 1 + net/sctp/sm_statefuns.c | 46 ++++++++++++++++++++++++++++++++++++---------- 3 files changed, 38 insertions(+), 10 deletions(-) (limited to 'include') diff --git a/include/net/sctp/structs.h b/include/net/sctp/structs.h index eba99f375517..7f4fea173fb1 100644 --- a/include/net/sctp/structs.h +++ b/include/net/sctp/structs.h @@ -712,6 +712,7 @@ struct sctp_chunk { __u8 tsn_gap_acked; /* Is this chunk acked by a GAP ACK? */ __s8 fast_retransmit; /* Is this chunk fast retransmitted? */ __u8 tsn_missing_report; /* Data chunk missing counter. */ + __u8 data_accepted; /* At least 1 chunk in this packet accepted */ }; void sctp_chunk_hold(struct sctp_chunk *); diff --git a/net/sctp/inqueue.c b/net/sctp/inqueue.c index 297b8951463e..cf0c767d43ae 100644 --- a/net/sctp/inqueue.c +++ b/net/sctp/inqueue.c @@ -149,6 +149,7 @@ struct sctp_chunk *sctp_inq_pop(struct sctp_inq *queue) /* This is the first chunk in the packet. */ chunk->singleton = 1; ch = (sctp_chunkhdr_t *) chunk->skb->data; + chunk->data_accepted = 0; } chunk->chunk_hdr = ch; diff --git a/net/sctp/sm_statefuns.c b/net/sctp/sm_statefuns.c index 2b9a832b29a7..f5d131f52a70 100644 --- a/net/sctp/sm_statefuns.c +++ b/net/sctp/sm_statefuns.c @@ -5151,7 +5151,9 @@ static int sctp_eat_data(const struct sctp_association *asoc, int tmp; __u32 tsn; int account_value; + struct sctp_tsnmap *map = (struct sctp_tsnmap *)&asoc->peer.tsn_map; struct sock *sk = asoc->base.sk; + int rcvbuf_over = 0; data_hdr = chunk->subh.data_hdr = (sctp_datahdr_t *)chunk->skb->data; skb_pull(chunk->skb, sizeof(sctp_datahdr_t)); @@ -5162,10 +5164,16 @@ static int sctp_eat_data(const struct sctp_association *asoc, /* ASSERT: Now skb->data is really the user data. */ /* - * if we are established, and we have used up our receive - * buffer memory, drop the frame - */ - if (asoc->state == SCTP_STATE_ESTABLISHED) { + * If we are established, and we have used up our receive buffer + * memory, think about droping the frame. + * Note that we have an opportunity to improve performance here. + * If we accept one chunk from an skbuff, we have to keep all the + * memory of that skbuff around until the chunk is read into user + * space. Therefore, once we accept 1 chunk we may as well accept all + * remaining chunks in the skbuff. The data_accepted flag helps us do + * that. + */ + if ((asoc->state == SCTP_STATE_ESTABLISHED) && (!chunk->data_accepted)) { /* * If the receive buffer policy is 1, then each * association can allocate up to sk_rcvbuf bytes @@ -5176,9 +5184,25 @@ static int sctp_eat_data(const struct sctp_association *asoc, account_value = atomic_read(&asoc->rmem_alloc); else account_value = atomic_read(&sk->sk_rmem_alloc); - - if (account_value > sk->sk_rcvbuf) - return SCTP_IERROR_IGNORE_TSN; + if (account_value > sk->sk_rcvbuf) { + /* + * We need to make forward progress, even when we are + * under memory pressure, so we always allow the + * next tsn after the ctsn ack point to be accepted. + * This lets us avoid deadlocks in which we have to + * drop frames that would otherwise let us drain the + * receive queue. + */ + if ((sctp_tsnmap_get_ctsn(map) + 1) != tsn) + return SCTP_IERROR_IGNORE_TSN; + + /* + * We're going to accept the frame but we should renege + * to make space for it. This will send us down that + * path later in this function. + */ + rcvbuf_over = 1; + } } /* Process ECN based congestion. @@ -5226,6 +5250,7 @@ static int sctp_eat_data(const struct sctp_association *asoc, datalen -= sizeof(sctp_data_chunk_t); deliver = SCTP_CMD_CHUNK_ULP; + chunk->data_accepted = 1; /* Think about partial delivery. */ if ((datalen >= asoc->rwnd) && (!asoc->ulpq.pd_mode)) { @@ -5242,7 +5267,8 @@ static int sctp_eat_data(const struct sctp_association *asoc, * large spill over. */ if (!asoc->rwnd || asoc->rwnd_over || - (datalen > asoc->rwnd + asoc->frag_point)) { + (datalen > asoc->rwnd + asoc->frag_point) || + rcvbuf_over) { /* If this is the next TSN, consider reneging to make * room. Note: Playing nice with a confused sender. A @@ -5250,8 +5276,8 @@ static int sctp_eat_data(const struct sctp_association *asoc, * space and in the future we may want to detect and * do more drastic reneging. */ - if (sctp_tsnmap_has_gap(&asoc->peer.tsn_map) && - (sctp_tsnmap_get_ctsn(&asoc->peer.tsn_map) + 1) == tsn) { + if (sctp_tsnmap_has_gap(map) && + (sctp_tsnmap_get_ctsn(map) + 1) == tsn) { SCTP_DEBUG_PRINTK("Reneging for tsn:%u\n", tsn); deliver = SCTP_CMD_RENEGE; } else { -- cgit v1.2.3 From 1498221d51a43d5fa1a580618591497d90f957d9 Mon Sep 17 00:00:00 2001 From: Stephen Hemminger Date: Sat, 6 May 2006 17:55:11 -0700 Subject: [CLASS DEVICE]: add attribute_group creation Extend the support of attribute groups in class_device's to allow groups to be created as part of the registration process. This allows network device's to avoid race between registration and creating groups. Note that unlike attributes that are a property of the class object, the groups are a property of the class_device object. This is done because there are different types of network devices (wireless for example). Signed-off-by: Stephen Hemminger Signed-off-by: Greg Kroah-Hartman Signed-off-by: David S. Miller --- drivers/base/class.c | 32 ++++++++++++++++++++++++++++++++ include/linux/device.h | 2 ++ 2 files changed, 34 insertions(+) (limited to 'include') diff --git a/drivers/base/class.c b/drivers/base/class.c index 0e71dff327cd..b1ea4df85c7d 100644 --- a/drivers/base/class.c +++ b/drivers/base/class.c @@ -456,6 +456,35 @@ static void class_device_remove_attrs(struct class_device * cd) } } +static int class_device_add_groups(struct class_device * cd) +{ + int i; + int error = 0; + + if (cd->groups) { + for (i = 0; cd->groups[i]; i++) { + error = sysfs_create_group(&cd->kobj, cd->groups[i]); + if (error) { + while (--i >= 0) + sysfs_remove_group(&cd->kobj, cd->groups[i]); + goto out; + } + } + } +out: + return error; +} + +static void class_device_remove_groups(struct class_device * cd) +{ + int i; + if (cd->groups) { + for (i = 0; cd->groups[i]; i++) { + sysfs_remove_group(&cd->kobj, cd->groups[i]); + } + } +} + static ssize_t show_dev(struct class_device *class_dev, char *buf) { return print_dev_t(buf, class_dev->devt); @@ -559,6 +588,8 @@ int class_device_add(struct class_device *class_dev) class_name); } + class_device_add_groups(class_dev); + kobject_uevent(&class_dev->kobj, KOBJ_ADD); /* notify any interfaces this device is now here */ @@ -672,6 +703,7 @@ void class_device_del(struct class_device *class_dev) if (class_dev->devt_attr) class_device_remove_file(class_dev, class_dev->devt_attr); class_device_remove_attrs(class_dev); + class_device_remove_groups(class_dev); kobject_uevent(&class_dev->kobj, KOBJ_REMOVE); kobject_del(&class_dev->kobj); diff --git a/include/linux/device.h b/include/linux/device.h index f6e72a65a3f2..e8e53b9accc6 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -200,6 +200,7 @@ extern int class_device_create_file(struct class_device *, * @node: for internal use by the driver core only. * @kobj: for internal use by the driver core only. * @devt_attr: for internal use by the driver core only. + * @groups: optional additional groups to be created * @dev: if set, a symlink to the struct device is created in the sysfs * directory for this struct class device. * @class_data: pointer to whatever you want to store here for this struct @@ -228,6 +229,7 @@ struct class_device { struct device * dev; /* not necessary, but nice to have */ void * class_data; /* class-specific data */ struct class_device *parent; /* parent of this child device, if there is one */ + struct attribute_group ** groups; /* optional groups */ void (*release)(struct class_device *dev); int (*uevent)(struct class_device *dev, char **envp, -- cgit v1.2.3 From fe9925b551a95fae6ec61470c79f8b701a2fe928 Mon Sep 17 00:00:00 2001 From: Stephen Hemminger Date: Sat, 6 May 2006 17:56:03 -0700 Subject: [NET]: Create netdev attribute_groups with class_device_add Atomically create attributes when class device is added. This avoids the race between registering class_device (which generates hotplug event), and the creation of attribute groups. Signed-off-by: Stephen Hemminger Signed-off-by: Greg Kroah-Hartman Signed-off-by: David S. Miller --- include/linux/netdevice.h | 2 ++ net/core/dev.c | 2 +- net/core/net-sysfs.c | 49 ++++++++++++----------------------------------- 3 files changed, 15 insertions(+), 38 deletions(-) (limited to 'include') diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 01db7b88a2b1..309f9190a922 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -506,6 +506,8 @@ struct net_device /* class/net/name entry */ struct class_device class_dev; + /* space for optional statistics and wireless sysfs groups */ + struct attribute_group *sysfs_groups[3]; }; #define NETDEV_ALIGN 32 diff --git a/net/core/dev.c b/net/core/dev.c index 3bad1afc89fa..9ab3cfa58466 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -3043,11 +3043,11 @@ void netdev_run_todo(void) switch(dev->reg_state) { case NETREG_REGISTERING: - dev->reg_state = NETREG_REGISTERED; err = netdev_register_sysfs(dev); if (err) printk(KERN_ERR "%s: failed sysfs registration (%d)\n", dev->name, err); + dev->reg_state = NETREG_REGISTERED; break; case NETREG_UNREGISTERING: diff --git a/net/core/net-sysfs.c b/net/core/net-sysfs.c index c12990c9c603..47a6fceb6771 100644 --- a/net/core/net-sysfs.c +++ b/net/core/net-sysfs.c @@ -29,7 +29,7 @@ static const char fmt_ulong[] = "%lu\n"; static inline int dev_isalive(const struct net_device *dev) { - return dev->reg_state == NETREG_REGISTERED; + return dev->reg_state <= NETREG_REGISTERED; } /* use same locking rules as GIF* ioctl's */ @@ -445,58 +445,33 @@ static struct class net_class = { void netdev_unregister_sysfs(struct net_device * net) { - struct class_device * class_dev = &(net->class_dev); - - if (net->get_stats) - sysfs_remove_group(&class_dev->kobj, &netstat_group); - -#ifdef WIRELESS_EXT - if (net->get_wireless_stats || (net->wireless_handlers && - net->wireless_handlers->get_wireless_stats)) - sysfs_remove_group(&class_dev->kobj, &wireless_group); -#endif - class_device_del(class_dev); - + class_device_del(&(net->class_dev)); } /* Create sysfs entries for network device. */ int netdev_register_sysfs(struct net_device *net) { struct class_device *class_dev = &(net->class_dev); - int ret; + struct attribute_group **groups = net->sysfs_groups; + class_device_initialize(class_dev); class_dev->class = &net_class; class_dev->class_data = net; + class_dev->groups = groups; + BUILD_BUG_ON(BUS_ID_SIZE < IFNAMSIZ); strlcpy(class_dev->class_id, net->name, BUS_ID_SIZE); - if ((ret = class_device_register(class_dev))) - goto out; - if (net->get_stats && - (ret = sysfs_create_group(&class_dev->kobj, &netstat_group))) - goto out_unreg; + if (net->get_stats) + *groups++ = &netstat_group; #ifdef WIRELESS_EXT - if (net->get_wireless_stats || (net->wireless_handlers && - net->wireless_handlers->get_wireless_stats)) { - ret = sysfs_create_group(&class_dev->kobj, &wireless_group); - if (ret) - goto out_cleanup; - } - return 0; -out_cleanup: - if (net->get_stats) - sysfs_remove_group(&class_dev->kobj, &netstat_group); -#else - return 0; + if (net->get_wireless_stats + || (net->wireless_handlers && net->wireless_handlers->get_wireless_stats)) + *groups++ = &wireless_group; #endif -out_unreg: - printk(KERN_WARNING "%s: sysfs attribute registration failed %d\n", - net->name, ret); - class_device_unregister(class_dev); -out: - return ret; + return class_device_add(class_dev); } int netdev_sysfs_init(void) -- cgit v1.2.3 From 216251cff98838f2b79c53fc8a9e76884944be7d Mon Sep 17 00:00:00 2001 From: Sascha Hauer Date: Sun, 7 May 2006 18:56:27 +0100 Subject: [ARM] 3501/1: i.MX: fix lowlevel debug macros Patch from Sascha Hauer This patch fixes the addruart macro to work with both mmu enabled and disabled. Signed-off-by: Sascha Hauer Signed-off-by: Russell King --- arch/arm/mach-imx/mx1ads.c | 2 +- include/asm-arm/arch-imx/debug-macro.S | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/arch/arm/mach-imx/mx1ads.c b/arch/arm/mach-imx/mx1ads.c index e1f6c0bbe1e7..da893c80d471 100644 --- a/arch/arm/mach-imx/mx1ads.c +++ b/arch/arm/mach-imx/mx1ads.c @@ -161,7 +161,7 @@ mx1ads_map_io(void) MACHINE_START(MX1ADS, "Motorola MX1ADS") /* Maintainer: Sascha Hauer, Pengutronix */ .phys_io = 0x00200000, - .io_pg_offst = ((0xe0200000) >> 18) & 0xfffc, + .io_pg_offst = ((0xe0000000) >> 18) & 0xfffc, .boot_params = 0x08000100, .map_io = mx1ads_map_io, .init_irq = imx_init_irq, diff --git a/include/asm-arm/arch-imx/debug-macro.S b/include/asm-arm/arch-imx/debug-macro.S index 83f552f7bcc1..c611871643a2 100644 --- a/include/asm-arm/arch-imx/debug-macro.S +++ b/include/asm-arm/arch-imx/debug-macro.S @@ -16,7 +16,7 @@ tst \rx, #1 @ MMU enabled? moveq \rx, #0x00000000 @ physical movne \rx, #0xe0000000 @ virtual - orr \rx, \rx, #0x00200000 + orreq \rx, \rx, #0x00200000 @ physical orr \rx, \rx, #0x00006000 @ UART1 offset .endm -- cgit v1.2.3 From 201be92a4243e58bcc6c0878489bcc2aaaf51c80 Mon Sep 17 00:00:00 2001 From: Bellido Nicolas Date: Sun, 7 May 2006 22:49:22 +0100 Subject: [ARM] 3505/1: aaec2000: entry-macro.S needs asm/arch/irqs.h Patch from Bellido Nicolas Since git commit 2b78838842346da390e8547cd37035184376d506, entry-macro.S needs to include asm/arch/irqs.h Signed-off-by: Nicolas Bellido Signed-off-by: Russell King --- include/asm-arm/arch-aaec2000/entry-macro.S | 1 + 1 file changed, 1 insertion(+) (limited to 'include') diff --git a/include/asm-arm/arch-aaec2000/entry-macro.S b/include/asm-arm/arch-aaec2000/entry-macro.S index df31313ab07e..1eb3503bd16e 100644 --- a/include/asm-arm/arch-aaec2000/entry-macro.S +++ b/include/asm-arm/arch-aaec2000/entry-macro.S @@ -10,6 +10,7 @@ * published by the Free Software Foundation. * */ +#include .macro disable_fiq .endm -- cgit v1.2.3 From 9a708becafe99fa32211e8c53dbacefdb4b11718 Mon Sep 17 00:00:00 2001 From: Bellido Nicolas Date: Sun, 7 May 2006 22:49:23 +0100 Subject: [ARM] 3506/1: aaec2000: debug-macro.S needs hardware.h Patch from Bellido Nicolas Include hardware.h in debug-macro.S, otherwise io_p2v is undefined. Signed-off-by: Nicolas Bellido Signed-off-by: Russell King --- include/asm-arm/arch-aaec2000/debug-macro.S | 1 + 1 file changed, 1 insertion(+) (limited to 'include') diff --git a/include/asm-arm/arch-aaec2000/debug-macro.S b/include/asm-arm/arch-aaec2000/debug-macro.S index e4f1fa539a74..7b1fce021d8a 100644 --- a/include/asm-arm/arch-aaec2000/debug-macro.S +++ b/include/asm-arm/arch-aaec2000/debug-macro.S @@ -9,6 +9,7 @@ * published by the Free Software Foundation. */ +#include "hardware.h" .macro addruart,rx mrc p15, 0, \rx, c1, c0 tst \rx, #1 @ MMU enabled? -- cgit v1.2.3 From e0c1e9bf81badc7ba59e120d6218101903d5d103 Mon Sep 17 00:00:00 2001 From: Kimball Murray Date: Mon, 8 May 2006 15:17:16 +0200 Subject: [PATCH] x86_64: avoid IRQ0 ioapic pin collision The patch addresses a problem with ACPI SCI interrupt entry, which gets re-used, and the IRQ is assigned to another unrelated device. The patch corrects the code such that SCI IRQ is skipped and duplicate entry is avoided. Second issue came up with VIA chipset, the problem was caused by original patch assigning IRQs starting 16 and up. The VIA chipset uses 4-bit IRQ register for internal interrupt routing, and therefore cannot handle IRQ numbers assigned to its devices. The patch corrects this problem by allowing PCI IRQs below 16. Cc: len.brown@intel.com Signed-off by: Natalie Protasevich Signed-off-by: Andi Kleen Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- arch/i386/kernel/io_apic.c | 5 +++++ arch/i386/kernel/mpparse.c | 12 +++++++++++- arch/x86_64/kernel/io_apic.c | 5 +++++ arch/x86_64/kernel/mpparse.c | 12 +++++++++++- include/asm-i386/io_apic.h | 1 + include/asm-x86_64/io_apic.h | 1 + 6 files changed, 34 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/arch/i386/kernel/io_apic.c b/arch/i386/kernel/io_apic.c index f8f132aa5472..d70f2ade5cde 100644 --- a/arch/i386/kernel/io_apic.c +++ b/arch/i386/kernel/io_apic.c @@ -2238,6 +2238,8 @@ static inline void unlock_ExtINT_logic(void) spin_unlock_irqrestore(&ioapic_lock, flags); } +int timer_uses_ioapic_pin_0; + /* * This code may look a bit paranoid, but it's supposed to cooperate with * a wide range of boards and BIOS bugs. Fortunately only the timer IRQ @@ -2274,6 +2276,9 @@ static inline void check_timer(void) pin2 = ioapic_i8259.pin; apic2 = ioapic_i8259.apic; + if (pin1 == 0) + timer_uses_ioapic_pin_0 = 1; + printk(KERN_INFO "..TIMER: vector=0x%02X apic1=%d pin1=%d apic2=%d pin2=%d\n", vector, apic1, pin1, apic2, pin2); diff --git a/arch/i386/kernel/mpparse.c b/arch/i386/kernel/mpparse.c index 34d21e21e012..6b1392d33ed5 100644 --- a/arch/i386/kernel/mpparse.c +++ b/arch/i386/kernel/mpparse.c @@ -1130,7 +1130,17 @@ int mp_register_gsi (u32 gsi, int triggering, int polarity) */ int irq = gsi; if (gsi < MAX_GSI_NUM) { - if (gsi > 15) + /* + * Retain the VIA chipset work-around (gsi > 15), but + * avoid a problem where the 8254 timer (IRQ0) is setup + * via an override (so it's not on pin 0 of the ioapic), + * and at the same time, the pin 0 interrupt is a PCI + * type. The gsi > 15 test could cause these two pins + * to be shared as IRQ0, and they are not shareable. + * So test for this condition, and if necessary, avoid + * the pin collision. + */ + if (gsi > 15 || (gsi == 0 && !timer_uses_ioapic_pin_0)) gsi = pci_irq++; /* * Don't assign IRQ used by ACPI SCI diff --git a/arch/x86_64/kernel/io_apic.c b/arch/x86_64/kernel/io_apic.c index 77b4c608cca0..0de3ea938830 100644 --- a/arch/x86_64/kernel/io_apic.c +++ b/arch/x86_64/kernel/io_apic.c @@ -1777,6 +1777,8 @@ static inline void unlock_ExtINT_logic(void) spin_unlock_irqrestore(&ioapic_lock, flags); } +int timer_uses_ioapic_pin_0; + /* * This code may look a bit paranoid, but it's supposed to cooperate with * a wide range of boards and BIOS bugs. Fortunately only the timer IRQ @@ -1814,6 +1816,9 @@ static inline void check_timer(void) pin2 = ioapic_i8259.pin; apic2 = ioapic_i8259.apic; + if (pin1 == 0) + timer_uses_ioapic_pin_0 = 1; + apic_printk(APIC_VERBOSE,KERN_INFO "..TIMER: vector=0x%02X apic1=%d pin1=%d apic2=%d pin2=%d\n", vector, apic1, pin1, apic2, pin2); diff --git a/arch/x86_64/kernel/mpparse.c b/arch/x86_64/kernel/mpparse.c index b17cf3eba359..083da7e606b1 100644 --- a/arch/x86_64/kernel/mpparse.c +++ b/arch/x86_64/kernel/mpparse.c @@ -968,7 +968,17 @@ int mp_register_gsi(u32 gsi, int triggering, int polarity) */ int irq = gsi; if (gsi < MAX_GSI_NUM) { - if (gsi > 15) + /* + * Retain the VIA chipset work-around (gsi > 15), but + * avoid a problem where the 8254 timer (IRQ0) is setup + * via an override (so it's not on pin 0 of the ioapic), + * and at the same time, the pin 0 interrupt is a PCI + * type. The gsi > 15 test could cause these two pins + * to be shared as IRQ0, and they are not shareable. + * So test for this condition, and if necessary, avoid + * the pin collision. + */ + if (gsi > 15 || (gsi == 0 && !timer_uses_ioapic_pin_0)) gsi = pci_irq++; /* * Don't assign IRQ used by ACPI SCI diff --git a/include/asm-i386/io_apic.h b/include/asm-i386/io_apic.h index 51c4e5fe6062..d92e253f7f6f 100644 --- a/include/asm-i386/io_apic.h +++ b/include/asm-i386/io_apic.h @@ -200,6 +200,7 @@ extern int io_apic_get_unique_id (int ioapic, int apic_id); extern int io_apic_get_version (int ioapic); extern int io_apic_get_redir_entries (int ioapic); extern int io_apic_set_pci_routing (int ioapic, int pin, int irq, int edge_level, int active_high_low); +extern int timer_uses_ioapic_pin_0; #endif /* CONFIG_ACPI */ extern int (*ioapic_renumber_irq)(int ioapic, int irq); diff --git a/include/asm-x86_64/io_apic.h b/include/asm-x86_64/io_apic.h index ee1bc69aec9c..52484e82c641 100644 --- a/include/asm-x86_64/io_apic.h +++ b/include/asm-x86_64/io_apic.h @@ -205,6 +205,7 @@ extern int skip_ioapic_setup; extern int io_apic_get_version (int ioapic); extern int io_apic_get_redir_entries (int ioapic); extern int io_apic_set_pci_routing (int ioapic, int pin, int irq, int, int); +extern int timer_uses_ioapic_pin_0; #endif extern int sis_apic_bug; /* dummy */ -- cgit v1.2.3 From ac71d12c990526b01ef6cfe50907ef8530a30331 Mon Sep 17 00:00:00 2001 From: Andi Kleen Date: Mon, 8 May 2006 15:17:28 +0200 Subject: [PATCH] x86_64: Avoid EBDA area in early boot allocator Based on analysis&patch from Robert Hentosch Observed on a Dell PE6850 with 16GB The problem occurs very early on, when the kernel allocates space for the temporary memory map called bootmap. The bootmap overlaps the EBDA region. EBDA region is not historically reserved in the e820 mapping. When the bootmap is freed it marks the EBDA region as usable. If you notice in setup.c there is already code to work around the EBDA in reserve_ebda_region(), this check however occurs after the bootmap is allocated and doesn't prevent the bootmap from using this range. AK: I redid the original patch. Thanks also to Jan Beulich for spotting some mistakes. Cc: Robert_Hentosch@dell.com Cc: jbeulich@novell.com Signed-off-by: Andi Kleen Signed-off-by: Linus Torvalds --- arch/x86_64/kernel/e820.c | 6 ++++++ arch/x86_64/kernel/setup.c | 30 ++++++++++++++++++++++-------- include/asm-x86_64/e820.h | 2 ++ 3 files changed, 30 insertions(+), 8 deletions(-) (limited to 'include') diff --git a/arch/x86_64/kernel/e820.c b/arch/x86_64/kernel/e820.c index 62776c07cff1..222b5b46d2b2 100644 --- a/arch/x86_64/kernel/e820.c +++ b/arch/x86_64/kernel/e820.c @@ -76,6 +76,12 @@ static inline int bad_addr(unsigned long *addrp, unsigned long size) *addrp = __pa_symbol(&_end); return 1; } + + if (last >= ebda_addr && addr < ebda_addr + ebda_size) { + *addrp = ebda_addr + ebda_size; + return 1; + } + /* XXX ramdisk image here? */ return 0; } diff --git a/arch/x86_64/kernel/setup.c b/arch/x86_64/kernel/setup.c index ebc3c33b1c6c..f0870bef24d1 100644 --- a/arch/x86_64/kernel/setup.c +++ b/arch/x86_64/kernel/setup.c @@ -571,17 +571,28 @@ static inline void copy_edd(void) #endif #define EBDA_ADDR_POINTER 0x40E -static void __init reserve_ebda_region(void) + +unsigned __initdata ebda_addr; +unsigned __initdata ebda_size; + +static void discover_ebda(void) { - unsigned int addr; - /** + /* * there is a real-mode segmented pointer pointing to the * 4K EBDA area at 0x40E */ - addr = *(unsigned short *)phys_to_virt(EBDA_ADDR_POINTER); - addr <<= 4; - if (addr) - reserve_bootmem_generic(addr, PAGE_SIZE); + ebda_addr = *(unsigned short *)EBDA_ADDR_POINTER; + ebda_addr <<= 4; + + ebda_size = *(unsigned short *)(unsigned long)ebda_addr; + + /* Round EBDA up to pages */ + if (ebda_size == 0) + ebda_size = 1; + ebda_size <<= 10; + ebda_size = round_up(ebda_size + (ebda_addr & ~PAGE_MASK), PAGE_SIZE); + if (ebda_size > 64*1024) + ebda_size = 64*1024; } void __init setup_arch(char **cmdline_p) @@ -627,6 +638,8 @@ void __init setup_arch(char **cmdline_p) check_efer(); + discover_ebda(); + init_memory_mapping(0, (end_pfn_map << PAGE_SHIFT)); dmi_scan_machine(); @@ -669,7 +682,8 @@ void __init setup_arch(char **cmdline_p) reserve_bootmem_generic(0, PAGE_SIZE); /* reserve ebda region */ - reserve_ebda_region(); + if (ebda_addr) + reserve_bootmem_generic(ebda_addr, ebda_size); #ifdef CONFIG_SMP /* diff --git a/include/asm-x86_64/e820.h b/include/asm-x86_64/e820.h index 93b51df51687..670a3388e70a 100644 --- a/include/asm-x86_64/e820.h +++ b/include/asm-x86_64/e820.h @@ -59,6 +59,8 @@ extern void __init parse_memopt(char *p, char **end); extern void __init parse_memmapopt(char *p, char **end); extern struct e820map e820; + +extern unsigned ebda_addr, ebda_size; #endif/*!__ASSEMBLY__*/ #endif/*__E820_HEADER*/ -- cgit v1.2.3 From d324031245abbb54e4e0321004430826052b6c37 Mon Sep 17 00:00:00 2001 From: Stephen Hemminger Date: Mon, 8 May 2006 15:11:26 -0700 Subject: sky2: backout NAPI reschedule This is a backout of earlier patch. The whole rescheduling hack was a bad idea. It doesn't really solve the problem and it makes the code more complicated for no good reason. Signed-off-by: Stephen Hemminger --- drivers/net/sky2.c | 20 ++++---------------- include/linux/netdevice.h | 18 ++++++++---------- 2 files changed, 12 insertions(+), 26 deletions(-) (limited to 'include') diff --git a/drivers/net/sky2.c b/drivers/net/sky2.c index 227df9876a2c..76da74fbe859 100644 --- a/drivers/net/sky2.c +++ b/drivers/net/sky2.c @@ -2105,7 +2105,6 @@ static int sky2_poll(struct net_device *dev0, int *budget) int work_done = 0; u32 status = sky2_read32(hw, B0_Y2_SP_EISR); - restart_poll: if (unlikely(status & ~Y2_IS_STAT_BMU)) { if (status & Y2_IS_HW_ERR) sky2_hw_intr(hw); @@ -2136,7 +2135,7 @@ static int sky2_poll(struct net_device *dev0, int *budget) } if (status & Y2_IS_STAT_BMU) { - work_done += sky2_status_intr(hw, work_limit - work_done); + work_done = sky2_status_intr(hw, work_limit); *budget -= work_done; dev0->quota -= work_done; @@ -2148,22 +2147,9 @@ static int sky2_poll(struct net_device *dev0, int *budget) mod_timer(&hw->idle_timer, jiffies + HZ); - local_irq_disable(); - __netif_rx_complete(dev0); + netif_rx_complete(dev0); status = sky2_read32(hw, B0_Y2_SP_LISR); - - if (unlikely(status)) { - /* More work pending, try and keep going */ - if (__netif_rx_schedule_prep(dev0)) { - __netif_rx_reschedule(dev0, work_done); - status = sky2_read32(hw, B0_Y2_SP_EISR); - local_irq_enable(); - goto restart_poll; - } - } - - local_irq_enable(); return 0; } @@ -2181,6 +2167,8 @@ static irqreturn_t sky2_intr(int irq, void *dev_id, struct pt_regs *regs) prefetch(&hw->st_le[hw->st_idx]); if (likely(__netif_rx_schedule_prep(dev0))) __netif_rx_schedule(dev0); + else + printk(KERN_DEBUG PFX "irq race detected\n"); return IRQ_HANDLED; } diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 309f9190a922..a461b51d6076 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -831,21 +831,19 @@ static inline void netif_rx_schedule(struct net_device *dev) __netif_rx_schedule(dev); } - -static inline void __netif_rx_reschedule(struct net_device *dev, int undo) -{ - dev->quota += undo; - list_add_tail(&dev->poll_list, &__get_cpu_var(softnet_data).poll_list); - __raise_softirq_irqoff(NET_RX_SOFTIRQ); -} - -/* Try to reschedule poll. Called by dev->poll() after netif_rx_complete(). */ +/* Try to reschedule poll. Called by dev->poll() after netif_rx_complete(). + * Do not inline this? + */ static inline int netif_rx_reschedule(struct net_device *dev, int undo) { if (netif_rx_schedule_prep(dev)) { unsigned long flags; + + dev->quota += undo; + local_irq_save(flags); - __netif_rx_reschedule(dev, undo); + list_add_tail(&dev->poll_list, &__get_cpu_var(softnet_data).poll_list); + __raise_softirq_irqoff(NET_RX_SOFTIRQ); local_irq_restore(flags); return 1; } -- cgit v1.2.3 From e4de00215c3af02116db3d486bf53700dfe10619 Mon Sep 17 00:00:00 2001 From: Paul Mackerras Date: Tue, 9 May 2006 16:00:59 +1000 Subject: powerpc/32: Define an is_kernel_addr() to fix ARCH=ppc compilation My commit 6bfd93c32a5065d0e858780b3beb0b667081601c broke the ARCH=ppc compilation by using the is_kernel_addr() macro in asm/uaccess.h. This fixes it by defining a suitable is_kernel_addr() for ARCH=ppc. Signed-off-by: Paul Mackerras --- include/asm-ppc/page.h | 1 + 1 file changed, 1 insertion(+) (limited to 'include') diff --git a/include/asm-ppc/page.h b/include/asm-ppc/page.h index a70ba2ee552d..0fb68a0b0181 100644 --- a/include/asm-ppc/page.h +++ b/include/asm-ppc/page.h @@ -20,6 +20,7 @@ /* This must match what is in arch/ppc/Makefile */ #define PAGE_OFFSET CONFIG_KERNEL_START #define KERNELBASE PAGE_OFFSET +#define is_kernel_addr(x) ((x) >= PAGE_OFFSET) #ifndef __ASSEMBLY__ -- cgit v1.2.3 From b17a7c179dd3ce7d04373fddf660eda21efc9db9 Mon Sep 17 00:00:00 2001 From: Stephen Hemminger Date: Wed, 10 May 2006 13:21:17 -0700 Subject: [NET]: Do sysfs registration as part of register_netdevice. The last step of netdevice registration was being done by a delayed call, but because it was delayed, it was impossible to return any error code if the class_device registration failed. Side effects: * one state in registration process is unnecessary. * register_netdevice can sleep inside class_device registration/hotplug * code in netdev_run_todo only does unregistration so it is simpler. Signed-off-by: Stephen Hemminger Signed-off-by: David S. Miller --- include/linux/netdevice.h | 3 +-- net/core/dev.c | 63 ++++++++++++++++++++--------------------------- 2 files changed, 28 insertions(+), 38 deletions(-) (limited to 'include') diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index a461b51d6076..f4169bbb60eb 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -433,8 +433,7 @@ struct net_device /* register/unregister state machine */ enum { NETREG_UNINITIALIZED=0, - NETREG_REGISTERING, /* called register_netdevice */ - NETREG_REGISTERED, /* completed register todo */ + NETREG_REGISTERED, /* completed register_netdevice */ NETREG_UNREGISTERING, /* called unregister_netdevice */ NETREG_UNREGISTERED, /* completed unregister todo */ NETREG_RELEASED, /* called free_netdev */ diff --git a/net/core/dev.c b/net/core/dev.c index ced57430f6d8..2dce673a039b 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -2777,6 +2777,8 @@ int register_netdevice(struct net_device *dev) BUG_ON(dev_boot_phase); ASSERT_RTNL(); + might_sleep(); + /* When net_device's are persistent, this will be fatal. */ BUG_ON(dev->reg_state != NETREG_UNINITIALIZED); @@ -2863,6 +2865,11 @@ int register_netdevice(struct net_device *dev) if (!dev->rebuild_header) dev->rebuild_header = default_rebuild_header; + ret = netdev_register_sysfs(dev); + if (ret) + goto out_err; + dev->reg_state = NETREG_REGISTERED; + /* * Default initial state at registry is that the * device is present. @@ -2878,14 +2885,11 @@ int register_netdevice(struct net_device *dev) hlist_add_head(&dev->name_hlist, head); hlist_add_head(&dev->index_hlist, dev_index_hash(dev->ifindex)); dev_hold(dev); - dev->reg_state = NETREG_REGISTERING; write_unlock_bh(&dev_base_lock); /* Notify protocols, that a new device appeared. */ raw_notifier_call_chain(&netdev_chain, NETDEV_REGISTER, dev); - /* Finish registration after unlock */ - net_set_todo(dev); ret = 0; out: @@ -3008,7 +3012,7 @@ static void netdev_wait_allrefs(struct net_device *dev) * * We are invoked by rtnl_unlock() after it drops the semaphore. * This allows us to deal with problems: - * 1) We can create/delete sysfs objects which invoke hotplug + * 1) We can delete sysfs objects which invoke hotplug * without deadlocking with linkwatch via keventd. * 2) Since we run with the RTNL semaphore not held, we can sleep * safely in order to wait for the netdev refcnt to drop to zero. @@ -3017,8 +3021,6 @@ static DEFINE_MUTEX(net_todo_run_mutex); void netdev_run_todo(void) { struct list_head list = LIST_HEAD_INIT(list); - int err; - /* Need to guard against multiple cpu's getting out of order. */ mutex_lock(&net_todo_run_mutex); @@ -3041,40 +3043,29 @@ void netdev_run_todo(void) = list_entry(list.next, struct net_device, todo_list); list_del(&dev->todo_list); - switch(dev->reg_state) { - case NETREG_REGISTERING: - err = netdev_register_sysfs(dev); - if (err) - printk(KERN_ERR "%s: failed sysfs registration (%d)\n", - dev->name, err); - dev->reg_state = NETREG_REGISTERED; - break; - - case NETREG_UNREGISTERING: - netdev_unregister_sysfs(dev); - dev->reg_state = NETREG_UNREGISTERED; - - netdev_wait_allrefs(dev); + if (unlikely(dev->reg_state != NETREG_UNREGISTERING)) { + printk(KERN_ERR "network todo '%s' but state %d\n", + dev->name, dev->reg_state); + dump_stack(); + continue; + } - /* paranoia */ - BUG_ON(atomic_read(&dev->refcnt)); - BUG_TRAP(!dev->ip_ptr); - BUG_TRAP(!dev->ip6_ptr); - BUG_TRAP(!dev->dn_ptr); + netdev_unregister_sysfs(dev); + dev->reg_state = NETREG_UNREGISTERED; + netdev_wait_allrefs(dev); - /* It must be the very last action, - * after this 'dev' may point to freed up memory. - */ - if (dev->destructor) - dev->destructor(dev); - break; + /* paranoia */ + BUG_ON(atomic_read(&dev->refcnt)); + BUG_TRAP(!dev->ip_ptr); + BUG_TRAP(!dev->ip6_ptr); + BUG_TRAP(!dev->dn_ptr); - default: - printk(KERN_ERR "network todo '%s' but state %d\n", - dev->name, dev->reg_state); - break; - } + /* It must be the very last action, + * after this 'dev' may point to freed up memory. + */ + if (dev->destructor) + dev->destructor(dev); } out: -- cgit v1.2.3 From 4c1b46226ce4424a93b8ac544e37afb26c8a72c6 Mon Sep 17 00:00:00 2001 From: Francois Romieu Date: Wed, 10 May 2006 12:48:57 -0700 Subject: dl2k: use DMA_48BIT_MASK constant Typo will be harder with this one. Signed-off-by: Francois Romieu Signed-off-by: Stephen Hemminger --- drivers/net/dl2k.c | 12 ++++++------ include/linux/dma-mapping.h | 1 + 2 files changed, 7 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/drivers/net/dl2k.c b/drivers/net/dl2k.c index 1f3627470c95..1ddefd281213 100644 --- a/drivers/net/dl2k.c +++ b/drivers/net/dl2k.c @@ -765,7 +765,7 @@ rio_free_tx (struct net_device *dev, int irq) break; skb = np->tx_skbuff[entry]; pci_unmap_single (np->pdev, - np->tx_ring[entry].fraginfo & 0xffffffffffff, + np->tx_ring[entry].fraginfo & DMA_48BIT_MASK, skb->len, PCI_DMA_TODEVICE); if (irq) dev_kfree_skb_irq (skb); @@ -893,7 +893,7 @@ receive_packet (struct net_device *dev) /* Small skbuffs for short packets */ if (pkt_len > copy_thresh) { pci_unmap_single (np->pdev, - desc->fraginfo & 0xffffffffffff, + desc->fraginfo & DMA_48BIT_MASK, np->rx_buf_sz, PCI_DMA_FROMDEVICE); skb_put (skb = np->rx_skbuff[entry], pkt_len); @@ -901,7 +901,7 @@ receive_packet (struct net_device *dev) } else if ((skb = dev_alloc_skb (pkt_len + 2)) != NULL) { pci_dma_sync_single_for_cpu(np->pdev, desc->fraginfo & - 0xffffffffffff, + DMA_48BIT_MASK, np->rx_buf_sz, PCI_DMA_FROMDEVICE); skb->dev = dev; @@ -913,7 +913,7 @@ receive_packet (struct net_device *dev) skb_put (skb, pkt_len); pci_dma_sync_single_for_device(np->pdev, desc->fraginfo & - 0xffffffffffff, + DMA_48BIT_MASK, np->rx_buf_sz, PCI_DMA_FROMDEVICE); } @@ -1800,7 +1800,7 @@ rio_close (struct net_device *dev) skb = np->rx_skbuff[i]; if (skb) { pci_unmap_single(np->pdev, - np->rx_ring[i].fraginfo & 0xffffffffffff, + np->rx_ring[i].fraginfo & DMA_48BIT_MASK, skb->len, PCI_DMA_FROMDEVICE); dev_kfree_skb (skb); np->rx_skbuff[i] = NULL; @@ -1810,7 +1810,7 @@ rio_close (struct net_device *dev) skb = np->tx_skbuff[i]; if (skb) { pci_unmap_single(np->pdev, - np->tx_ring[i].fraginfo & 0xffffffffffff, + np->tx_ring[i].fraginfo & DMA_48BIT_MASK, skb->len, PCI_DMA_TODEVICE); dev_kfree_skb (skb); np->tx_skbuff[i] = NULL; diff --git a/include/linux/dma-mapping.h b/include/linux/dma-mapping.h index ff61817082fa..635690cf3e3d 100644 --- a/include/linux/dma-mapping.h +++ b/include/linux/dma-mapping.h @@ -14,6 +14,7 @@ enum dma_data_direction { }; #define DMA_64BIT_MASK 0xffffffffffffffffULL +#define DMA_48BIT_MASK 0x0000ffffffffffffULL #define DMA_40BIT_MASK 0x000000ffffffffffULL #define DMA_39BIT_MASK 0x0000007fffffffffULL #define DMA_32BIT_MASK 0x00000000ffffffffULL -- cgit v1.2.3 From bd89efc532fe41f867f848144cc8b42054ddf6f9 Mon Sep 17 00:00:00 2001 From: Simon Kelley Date: Fri, 12 May 2006 14:56:08 -0700 Subject: [NEIGH]: Fix IP-over-ATM and ARP interaction. The classical IP over ATM code maintains its own IPv4 <-> ARP table, using the standard neighbour-table code. The neigh_table_init function adds this neighbour table to a linked list of all neighbor tables which is used by the functions neigh_delete() neigh_add() and neightbl_set(), all called by the netlink code. Once the ATM neighbour table is added to the list, there are two tables with family == AF_INET there, and ARP entries sent via netlink go into the first table with matching family. This is indeterminate and often wrong. To see the bug, on a kernel with CLIP enabled, create a standard IPv4 ARP entry by pinging an unused address on a local subnet. Then attempt to complete that entry by doing ip neigh replace lladdr nud reachable Looking at the ARP tables by using ip neigh show will reveal two ARP entries for the same address. One of these can be found in /proc/net/arp, and the other in /proc/net/atm/arp. This patch adds a new function, neigh_table_init_no_netlink() which does everything the neigh_table_init() does, except add the table to the netlink all-arp-tables chain. In addition neigh_table_init() has a check that all tables on the chain have a distinct address family. The init call in clip.c is changed to call neigh_table_init_no_netlink(). Since ATM ARP tables are rather more complicated than can currently be handled by the available rtattrs in the netlink protocol, no functionality is lost by this patch, and non-ATM ARP manipulation via netlink is rescued. A more complete solution would involve a rtattr for ATM ARP entries and some way for the netlink code to give neigh_add and friends more information than just address family with which to find the correct ARP table. [ I've changed the assertion checking in neigh_table_init() to not use BUG_ON() while holding neigh_tbl_lock. Instead we remember that we found an existing tbl with the same family, and after dropping the lock we'll give a diagnostic kernel log message and a stack dump. -DaveM ] Signed-off-by: Simon Kelley Signed-off-by: David S. Miller --- include/net/neighbour.h | 1 + net/atm/clip.c | 2 +- net/core/neighbour.c | 21 +++++++++++++++++++-- 3 files changed, 21 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/include/net/neighbour.h b/include/net/neighbour.h index b0666d66293f..4901ee446879 100644 --- a/include/net/neighbour.h +++ b/include/net/neighbour.h @@ -211,6 +211,7 @@ struct neigh_table #define NEIGH_UPDATE_F_ADMIN 0x80000000 extern void neigh_table_init(struct neigh_table *tbl); +extern void neigh_table_init_no_netlink(struct neigh_table *tbl); extern int neigh_table_clear(struct neigh_table *tbl); extern struct neighbour * neigh_lookup(struct neigh_table *tbl, const void *pkey, diff --git a/net/atm/clip.c b/net/atm/clip.c index 1a786bfaa416..72d852982664 100644 --- a/net/atm/clip.c +++ b/net/atm/clip.c @@ -963,7 +963,7 @@ static struct file_operations arp_seq_fops = { static int __init atm_clip_init(void) { struct proc_dir_entry *p; - neigh_table_init(&clip_tbl); + neigh_table_init_no_netlink(&clip_tbl); clip_tbl_hook = &clip_tbl; register_atm_ioctl(&clip_ioctl_ops); diff --git a/net/core/neighbour.c b/net/core/neighbour.c index 4cf878efdb49..50a8c73caf97 100644 --- a/net/core/neighbour.c +++ b/net/core/neighbour.c @@ -1326,8 +1326,7 @@ void neigh_parms_destroy(struct neigh_parms *parms) kfree(parms); } - -void neigh_table_init(struct neigh_table *tbl) +void neigh_table_init_no_netlink(struct neigh_table *tbl) { unsigned long now = jiffies; unsigned long phsize; @@ -1383,10 +1382,27 @@ void neigh_table_init(struct neigh_table *tbl) tbl->last_flush = now; tbl->last_rand = now + tbl->parms.reachable_time * 20; +} + +void neigh_table_init(struct neigh_table *tbl) +{ + struct neigh_table *tmp; + + neigh_table_init_no_netlink(tbl); write_lock(&neigh_tbl_lock); + for (tmp = neigh_tables; tmp; tmp = tmp->next) { + if (tmp->family == tbl->family) + break; + } tbl->next = neigh_tables; neigh_tables = tbl; write_unlock(&neigh_tbl_lock); + + if (unlikely(tmp)) { + printk(KERN_ERR "NEIGH: Registering multiple tables for " + "family %d\n", tbl->family); + dump_stack(); + } } int neigh_table_clear(struct neigh_table *tbl) @@ -2657,6 +2673,7 @@ EXPORT_SYMBOL(neigh_rand_reach_time); EXPORT_SYMBOL(neigh_resolve_output); EXPORT_SYMBOL(neigh_table_clear); EXPORT_SYMBOL(neigh_table_init); +EXPORT_SYMBOL(neigh_table_init_no_netlink); EXPORT_SYMBOL(neigh_update); EXPORT_SYMBOL(neigh_update_hhs); EXPORT_SYMBOL(pneigh_enqueue); -- cgit v1.2.3 From 986733e01d258c26107f1da9d8d47c718349ad2f Mon Sep 17 00:00:00 2001 From: Heiko Carstens Date: Mon, 15 May 2006 09:43:58 -0700 Subject: [PATCH] RCU: introduce rcu_needs_cpu() interface With "Paul E. McKenney" Introduce rcu_needs_cpu() interface. This can be used to tell if there will be a new rcu batch on a cpu soon by looking at the curlist pointer. This can be used to avoid to enter a tickless idle state where the cpu would miss that a new batch is ready when rcu_start_batch would be called on a different cpu. Signed-off-by: Heiko Carstens Cc: "Paul E. McKenney" Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/rcupdate.h | 1 + kernel/rcupdate.c | 19 +++++++++++++++++++ 2 files changed, 20 insertions(+) (limited to 'include') diff --git a/include/linux/rcupdate.h b/include/linux/rcupdate.h index 5673008b61e1..970284f571a6 100644 --- a/include/linux/rcupdate.h +++ b/include/linux/rcupdate.h @@ -132,6 +132,7 @@ static inline void rcu_bh_qsctr_inc(int cpu) } extern int rcu_pending(int cpu); +extern int rcu_needs_cpu(int cpu); /** * rcu_read_lock - mark the beginning of an RCU read-side critical section. diff --git a/kernel/rcupdate.c b/kernel/rcupdate.c index 6d32ff26f948..2058f88c7bbb 100644 --- a/kernel/rcupdate.c +++ b/kernel/rcupdate.c @@ -479,12 +479,31 @@ static int __rcu_pending(struct rcu_ctrlblk *rcp, struct rcu_data *rdp) return 0; } +/* + * Check to see if there is any immediate RCU-related work to be done + * by the current CPU, returning 1 if so. This function is part of the + * RCU implementation; it is -not- an exported member of the RCU API. + */ int rcu_pending(int cpu) { return __rcu_pending(&rcu_ctrlblk, &per_cpu(rcu_data, cpu)) || __rcu_pending(&rcu_bh_ctrlblk, &per_cpu(rcu_bh_data, cpu)); } +/* + * Check to see if any future RCU-related work will need to be done + * by the current CPU, even if none need be done immediately, returning + * 1 if so. This function is part of the RCU implementation; it is -not- + * an exported member of the RCU API. + */ +int rcu_needs_cpu(int cpu) +{ + struct rcu_data *rdp = &per_cpu(rcu_data, cpu); + struct rcu_data *rdp_bh = &per_cpu(rcu_bh_data, cpu); + + return (!!rdp->curlist || !!rdp_bh->curlist || rcu_pending(cpu)); +} + void rcu_check_callbacks(int cpu, int user) { if (user || -- cgit v1.2.3 From 0159677857c5ada0a0a2c03a4dd59312382b73d0 Mon Sep 17 00:00:00 2001 From: Martin Schwidefsky Date: Mon, 15 May 2006 09:44:05 -0700 Subject: [PATCH] s390: add vmsplice system call Add new vmsplice system call and add missing __NR_xxx defines for sys_set_robust_list, sys_get_robust_list, sys_splice, sys_sync_file_range and sys_tee. Signed-off-by: Martin Schwidefsky Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- arch/s390/kernel/compat_wrapper.S | 8 ++++++++ arch/s390/kernel/syscalls.S | 1 + include/asm-s390/unistd.h | 8 +++++++- 3 files changed, 16 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/arch/s390/kernel/compat_wrapper.S b/arch/s390/kernel/compat_wrapper.S index ef5b9c44b86b..4d53b2739357 100644 --- a/arch/s390/kernel/compat_wrapper.S +++ b/arch/s390/kernel/compat_wrapper.S @@ -1650,3 +1650,11 @@ sys_tee_wrapper: llgfr %r4,%r4 # size_t llgfr %r5,%r5 # unsigned int jg sys_tee + + .globl compat_sys_vmsplice_wrapper +compat_sys_vmsplice_wrapper: + lgfr %r2,%r2 # int + llgtr %r3,%r3 # compat_iovec * + llgfr %r4,%r4 # unsigned int + llgfr %r5,%r5 # unsigned int + jg compat_sys_vmsplice diff --git a/arch/s390/kernel/syscalls.S b/arch/s390/kernel/syscalls.S index fc2c0767202b..93be1d56c036 100644 --- a/arch/s390/kernel/syscalls.S +++ b/arch/s390/kernel/syscalls.S @@ -317,3 +317,4 @@ SYSCALL(sys_get_robust_list,sys_get_robust_list,compat_sys_get_robust_list_wrapp SYSCALL(sys_splice,sys_splice,sys_splice_wrapper) SYSCALL(sys_sync_file_range,sys_sync_file_range,sys_sync_file_range_wrapper) SYSCALL(sys_tee,sys_tee,sys_tee_wrapper) +SYSCALL(sys_vmsplice,sys_vmsplice,compat_sys_vmsplice_wrapper) diff --git a/include/asm-s390/unistd.h b/include/asm-s390/unistd.h index 657d582e8149..41c2792ff6b0 100644 --- a/include/asm-s390/unistd.h +++ b/include/asm-s390/unistd.h @@ -296,8 +296,14 @@ #define __NR_pselect6 301 #define __NR_ppoll 302 #define __NR_unshare 303 +#define __NR_set_robust_list 304 +#define __NR_get_robust_list 305 +#define __NR_splice 306 +#define __NR_sync_file_range 307 +#define __NR_tee 308 +#define __NR_vmsplice 309 -#define NR_syscalls 304 +#define NR_syscalls 310 /* * There are some system calls that are not present on 64 bit, some -- cgit v1.2.3 From 5e376613899076396d0c97de67ad072587267370 Mon Sep 17 00:00:00 2001 From: Trent Piepho Date: Mon, 15 May 2006 09:44:06 -0700 Subject: [PATCH] symbol_put_addr() locks kernel Even since a previous patch: Fix race between CONFIG_DEBUG_SLABALLOC and modules Sun, 27 Jun 2004 17:55:19 +0000 (17:55 +0000) http://www.kernel.org/git/?p=linux/kernel/git/torvalds/old-2.6-bkcvs.git;a=commit;h=92b3db26d31cf21b70e3c1eadc56c179506d8fbe The function symbol_put_addr() will deadlock the kernel. symbol_put_addr() would acquire modlist_lock, then while holding the lock call two functions kernel_text_address() and module_text_address() which also try to acquire the same lock. This deadlocks the kernel of course. This patch changes symbol_put_addr() to not acquire the modlist_lock, it doesn't need it since it never looks at the module list directly. Also, it now uses core_kernel_text() instead of kernel_text_address(). The latter has an additional check for addr inside a module, but we don't need to do that since we call module_text_address() (the same function kernel_text_address uses) ourselves. Signed-off-by: Trent Piepho Cc: Zwane Mwaikambo Acked-by: Rusty Russell Cc: Johannes Stezenbach Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/kernel.h | 1 + kernel/extable.c | 2 +- kernel/module.c | 12 ++++++------ 3 files changed, 8 insertions(+), 7 deletions(-) (limited to 'include') diff --git a/include/linux/kernel.h b/include/linux/kernel.h index e1bd0842f6a1..f4fc576ed4c4 100644 --- a/include/linux/kernel.h +++ b/include/linux/kernel.h @@ -124,6 +124,7 @@ extern int get_option(char **str, int *pint); extern char *get_options(const char *str, int nints, int *ints); extern unsigned long long memparse(char *ptr, char **retptr); +extern int core_kernel_text(unsigned long addr); extern int __kernel_text_address(unsigned long addr); extern int kernel_text_address(unsigned long addr); extern int session_of_pgrp(int pgrp); diff --git a/kernel/extable.c b/kernel/extable.c index 7501b531ceed..7fe262855317 100644 --- a/kernel/extable.c +++ b/kernel/extable.c @@ -40,7 +40,7 @@ const struct exception_table_entry *search_exception_tables(unsigned long addr) return e; } -static int core_kernel_text(unsigned long addr) +int core_kernel_text(unsigned long addr) { if (addr >= (unsigned long)_stext && addr <= (unsigned long)_etext) diff --git a/kernel/module.c b/kernel/module.c index d24deb0dbbc9..bbe04862e1b0 100644 --- a/kernel/module.c +++ b/kernel/module.c @@ -705,14 +705,14 @@ EXPORT_SYMBOL(__symbol_put); void symbol_put_addr(void *addr) { - unsigned long flags; + struct module *modaddr; - spin_lock_irqsave(&modlist_lock, flags); - if (!kernel_text_address((unsigned long)addr)) - BUG(); + if (core_kernel_text((unsigned long)addr)) + return; - module_put(module_text_address((unsigned long)addr)); - spin_unlock_irqrestore(&modlist_lock, flags); + if (!(modaddr = module_text_address((unsigned long)addr))) + BUG(); + module_put(modaddr); } EXPORT_SYMBOL_GPL(symbol_put_addr); -- cgit v1.2.3 From 39d24e64263cd3211705d3b61ea4171c65030921 Mon Sep 17 00:00:00 2001 From: Mike Kravetz Date: Mon, 15 May 2006 09:44:13 -0700 Subject: [PATCH] add slab_is_available() routine for boot code slab_is_available() indicates slab based allocators are available for use. SPARSEMEM code needs to know this as it can be called at various times during the boot process. Signed-off-by: Mike Kravetz Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/slab.h | 1 + mm/slab.c | 8 ++++++++ mm/sparse.c | 2 +- 3 files changed, 10 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/linux/slab.h b/include/linux/slab.h index 3af03b19c983..2d985d59c7b8 100644 --- a/include/linux/slab.h +++ b/include/linux/slab.h @@ -150,6 +150,7 @@ static inline void *kcalloc(size_t n, size_t size, gfp_t flags) extern void kfree(const void *); extern unsigned int ksize(const void *); +extern int slab_is_available(void); #ifdef CONFIG_NUMA extern void *kmem_cache_alloc_node(kmem_cache_t *, gfp_t flags, int node); diff --git a/mm/slab.c b/mm/slab.c index c32af7e7581e..b1d643b5238d 100644 --- a/mm/slab.c +++ b/mm/slab.c @@ -700,6 +700,14 @@ static enum { FULL } g_cpucache_up; +/* + * used by boot code to determine if it can use slab based allocator + */ +int slab_is_available(void) +{ + return g_cpucache_up == FULL; +} + static DEFINE_PER_CPU(struct work_struct, reap_work); static void free_block(struct kmem_cache *cachep, void **objpp, int len, diff --git a/mm/sparse.c b/mm/sparse.c index d7c32de99ee8..c5e89eb9ac8f 100644 --- a/mm/sparse.c +++ b/mm/sparse.c @@ -32,7 +32,7 @@ static struct mem_section *sparse_index_alloc(int nid) unsigned long array_size = SECTIONS_PER_ROOT * sizeof(struct mem_section); - if (system_state == SYSTEM_RUNNING) + if (slab_is_available()) section = kmalloc_node(array_size, GFP_KERNEL, nid); else section = alloc_bootmem_node(NODE_DATA(nid), array_size); -- cgit v1.2.3 From e6333fd4ddf7a583480017f535b9ea53c116ab81 Mon Sep 17 00:00:00 2001 From: Hua Zhong Date: Mon, 15 May 2006 09:44:22 -0700 Subject: [PATCH] fix can_share_swap_page() when !CONFIG_SWAP can_share_swap_page() is used to check if the page has the last reference. This avoids allocating a new page for COW if it's the last page. However, if CONFIG_SWAP is not set, can_share_swap_page() is defined as 0, thus always causes a copy for the last COW page. The below simple patch fixes it. Signed-off-by: Hua Zhong Cc: David Howells Cc: Hugh Dickins Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/swap.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/linux/swap.h b/include/linux/swap.h index 5b1fdf1cff4f..f03c24719302 100644 --- a/include/linux/swap.h +++ b/include/linux/swap.h @@ -296,7 +296,7 @@ static inline void disable_swap_token(void) #define read_swap_cache_async(swp,vma,addr) NULL #define lookup_swap_cache(swp) NULL #define valid_swaphandles(swp, off) 0 -#define can_share_swap_page(p) 0 +#define can_share_swap_page(p) (page_mapcount(p) == 1) #define move_to_swap_cache(p, swp) 1 #define move_from_swap_cache(p, i, m) 1 #define __delete_from_swap_cache(p) /*NOTHING*/ -- cgit v1.2.3 From 2ceec0c8c6e2780d58dece91b4b787729405d9e7 Mon Sep 17 00:00:00 2001 From: Uwe Zeisberger Date: Wed, 10 May 2006 18:11:05 +0100 Subject: [ARM] 3517/1: move definition of PROC_INFO_SZ from procinfo.h to asm-offsets.h Patch from Uwe Zeisberger The symbol is only used in arch/arm/kernel/head-common.S. This in turn is included from arch/arm/kernel/head.S and arch/arm/kernel/head-nommu.S which include asm-offsets.h . Signed-off-by: Uwe Zeisberger Signed-off-by: Russell King --- arch/arm/kernel/asm-offsets.c | 2 ++ include/asm-arm/procinfo.h | 2 -- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/arch/arm/kernel/asm-offsets.c b/arch/arm/kernel/asm-offsets.c index 45fdf4a51a2a..396efba9bacd 100644 --- a/arch/arm/kernel/asm-offsets.c +++ b/arch/arm/kernel/asm-offsets.c @@ -99,6 +99,8 @@ int main(void) DEFINE(MACHINFO_NAME, offsetof(struct machine_desc, name)); DEFINE(MACHINFO_PHYSIO, offsetof(struct machine_desc, phys_io)); DEFINE(MACHINFO_PGOFFIO, offsetof(struct machine_desc, io_pg_offst)); + BLANK(); + DEFINE(PROC_INFO_SZ, sizeof(struct proc_info_list)); DEFINE(PROCINFO_INITFUNC, offsetof(struct proc_info_list, __cpu_flush)); DEFINE(PROCINFO_MMUFLAGS, offsetof(struct proc_info_list, __cpu_mmu_flags)); return 0; diff --git a/include/asm-arm/procinfo.h b/include/asm-arm/procinfo.h index a9c75b2c314f..842526055225 100644 --- a/include/asm-arm/procinfo.h +++ b/include/asm-arm/procinfo.h @@ -45,8 +45,6 @@ extern unsigned int elf_hwcap; #endif /* __ASSEMBLY__ */ -#define PROC_INFO_SZ 48 - #define HWCAP_SWP 1 #define HWCAP_HALF 2 #define HWCAP_THUMB 4 -- cgit v1.2.3 From 4cff33f94fefcce1b3c01a9d1da6bb85fe3cbdfa Mon Sep 17 00:00:00 2001 From: Imre Deak Date: Fri, 17 Feb 2006 10:02:18 -0800 Subject: [PATCH] SPI: per-transfer overrides for wordsize and clocking Some protocols (like one for some bitmap displays) require different clock speed or word size settings for each transfer in an SPI message. This adds those parameters to struct spi_transfer. They are to be used when they are nonzero; otherwise the defaults from spi_device are to be used. The patch also adds a setup_transfer callback to spi_bitbang, uses it for messages that use those overrides, and implements it so that the pure bitbanging code can help resolve any questions about how it should work. Signed-off-by: Imre Deak Signed-off-by: David Brownell Signed-off-by: Greg Kroah-Hartman --- drivers/spi/spi_bitbang.c | 77 +++++++++++++++++++++++++++++++++-------- include/linux/spi/spi.h | 8 +++++ include/linux/spi/spi_bitbang.h | 6 ++++ 3 files changed, 77 insertions(+), 14 deletions(-) (limited to 'include') diff --git a/drivers/spi/spi_bitbang.c b/drivers/spi/spi_bitbang.c index f037e5593269..0525c99118c3 100644 --- a/drivers/spi/spi_bitbang.c +++ b/drivers/spi/spi_bitbang.c @@ -138,6 +138,43 @@ static unsigned bitbang_txrx_32( return t->len - count; } +static int +bitbang_transfer_setup(struct spi_device *spi, struct spi_transfer *t) +{ + struct spi_bitbang_cs *cs = spi->controller_state; + u8 bits_per_word; + u32 hz; + + if (t) { + bits_per_word = t->bits_per_word; + hz = t->speed_hz; + } else { + bits_per_word = 0; + hz = 0; + } + + /* spi_transfer level calls that work per-word */ + if (!bits_per_word) + bits_per_word = spi->bits_per_word; + if (bits_per_word <= 8) + cs->txrx_bufs = bitbang_txrx_8; + else if (bits_per_word <= 16) + cs->txrx_bufs = bitbang_txrx_16; + else if (bits_per_word <= 32) + cs->txrx_bufs = bitbang_txrx_32; + else + return -EINVAL; + + /* nsecs = (clock period)/2 */ + if (!hz) + hz = spi->max_speed_hz; + cs->nsecs = (1000000000/2) / hz; + if (cs->nsecs > MAX_UDELAY_MS * 1000) + return -EINVAL; + + return 0; +} + /** * spi_bitbang_setup - default setup for per-word I/O loops */ @@ -145,6 +182,7 @@ int spi_bitbang_setup(struct spi_device *spi) { struct spi_bitbang_cs *cs = spi->controller_state; struct spi_bitbang *bitbang; + int retval; if (!spi->max_speed_hz) return -EINVAL; @@ -160,25 +198,14 @@ int spi_bitbang_setup(struct spi_device *spi) if (!spi->bits_per_word) spi->bits_per_word = 8; - /* spi_transfer level calls that work per-word */ - if (spi->bits_per_word <= 8) - cs->txrx_bufs = bitbang_txrx_8; - else if (spi->bits_per_word <= 16) - cs->txrx_bufs = bitbang_txrx_16; - else if (spi->bits_per_word <= 32) - cs->txrx_bufs = bitbang_txrx_32; - else - return -EINVAL; - /* per-word shift register access, in hardware or bitbanging */ cs->txrx_word = bitbang->txrx_word[spi->mode & (SPI_CPOL|SPI_CPHA)]; if (!cs->txrx_word) return -EINVAL; - /* nsecs = (clock period)/2 */ - cs->nsecs = (1000000000/2) / (spi->max_speed_hz); - if (cs->nsecs > MAX_UDELAY_MS * 1000) - return -EINVAL; + retval = bitbang_transfer_setup(spi, NULL); + if (retval < 0) + return retval; dev_dbg(&spi->dev, "%s, mode %d, %u bits/w, %u nsec\n", __FUNCTION__, spi->mode & (SPI_CPOL | SPI_CPHA), @@ -246,6 +273,8 @@ static void bitbang_work(void *_bitbang) unsigned tmp; unsigned cs_change; int status; + int (*setup_transfer)(struct spi_device *, + struct spi_transfer *); m = container_of(bitbang->queue.next, struct spi_message, queue); @@ -262,6 +291,7 @@ static void bitbang_work(void *_bitbang) tmp = 0; cs_change = 1; status = 0; + setup_transfer = NULL; list_for_each_entry (t, &m->transfers, transfer_list) { if (bitbang->shutdown) { @@ -269,6 +299,20 @@ static void bitbang_work(void *_bitbang) break; } + /* override or restore speed and wordsize */ + if (t->speed_hz || t->bits_per_word) { + setup_transfer = bitbang->setup_transfer; + if (!setup_transfer) { + status = -ENOPROTOOPT; + break; + } + } + if (setup_transfer) { + status = setup_transfer(spi, t); + if (status < 0) + break; + } + /* set up default clock polarity, and activate chip; * this implicitly updates clock and spi modes as * previously recorded for this device via setup(). @@ -325,6 +369,10 @@ static void bitbang_work(void *_bitbang) m->status = status; m->complete(m->context); + /* restore speed and wordsize */ + if (setup_transfer) + setup_transfer(spi, NULL); + /* normally deactivate chipselect ... unless no error and * cs_change has hinted that the next message will probably * be for this chip too. @@ -406,6 +454,7 @@ int spi_bitbang_start(struct spi_bitbang *bitbang) bitbang->use_dma = 0; bitbang->txrx_bufs = spi_bitbang_bufs; if (!bitbang->master->setup) { + bitbang->setup_transfer = bitbang_transfer_setup; bitbang->master->setup = spi_bitbang_setup; bitbang->master->cleanup = spi_bitbang_cleanup; } diff --git a/include/linux/spi/spi.h b/include/linux/spi/spi.h index b05f1463a267..caa4665e3fa2 100644 --- a/include/linux/spi/spi.h +++ b/include/linux/spi/spi.h @@ -31,6 +31,7 @@ extern struct bus_type spi_bus_type; * @master: SPI controller used with the device. * @max_speed_hz: Maximum clock rate to be used with this chip * (on this board); may be changed by the device's driver. + * The spi_transfer.speed_hz can override this for each transfer. * @chip-select: Chipselect, distinguishing chips handled by "master". * @mode: The spi mode defines how data is clocked out and in. * This may be changed by the device's driver. @@ -38,6 +39,7 @@ extern struct bus_type spi_bus_type; * like eight or 12 bits are common. In-memory wordsizes are * powers of two bytes (e.g. 20 bit samples use 32 bits). * This may be changed by the device's driver. + * The spi_transfer.bits_per_word can override this for each transfer. * @irq: Negative, or the number passed to request_irq() to receive * interrupts from this device. * @controller_state: Controller's runtime state @@ -268,6 +270,10 @@ extern struct spi_master *spi_busnum_to_master(u16 busnum); * @tx_dma: DMA address of tx_buf, if spi_message.is_dma_mapped * @rx_dma: DMA address of rx_buf, if spi_message.is_dma_mapped * @len: size of rx and tx buffers (in bytes) + * @speed_hz: Select a speed other then the device default for this + * transfer. If 0 the default (from spi_device) is used. + * @bits_per_word: select a bits_per_word other then the device default + * for this transfer. If 0 the default (from spi_device) is used. * @cs_change: affects chipselect after this transfer completes * @delay_usecs: microseconds to delay after this transfer before * (optionally) changing the chipselect status, then starting @@ -322,7 +328,9 @@ struct spi_transfer { dma_addr_t rx_dma; unsigned cs_change:1; + u8 bits_per_word; u16 delay_usecs; + u32 speed_hz; struct list_head transfer_list; }; diff --git a/include/linux/spi/spi_bitbang.h b/include/linux/spi/spi_bitbang.h index c961fe9bf3eb..c954557b757c 100644 --- a/include/linux/spi/spi_bitbang.h +++ b/include/linux/spi/spi_bitbang.h @@ -30,6 +30,12 @@ struct spi_bitbang { struct spi_master *master; + /* setup_transfer() changes clock and/or wordsize to match settings + * for this transfer; zeroes restore defaults from spi_device. + */ + int (*setup_transfer)(struct spi_device *spi, + struct spi_transfer *t); + void (*chipselect)(struct spi_device *spi, int is_on); #define BITBANG_CS_ACTIVE 1 /* normally nCS, active low */ #define BITBANG_CS_INACTIVE 0 -- cgit v1.2.3 From e0c9905e87ac1bc56c9ea8f5b2934aeee53dce26 Mon Sep 17 00:00:00 2001 From: Stephen Street Date: Tue, 7 Mar 2006 23:53:24 -0800 Subject: [PATCH] SPI: add PXA2xx SSP SPI Driver This driver turns a PXA2xx synchronous serial port (SSP) into a SPI master controller (see Documentation/spi/spi_summary). The driver has the following features: - Support for any PXA2xx SSP - SSP PIO and SSP DMA data transfers. - External and Internal (SSPFRM) chip selects. - Per slave device (chip) configuration. - Full suspend, freeze, resume support. Signed-off-by: Stephen Street Signed-off-by: Andrew Morton Cc: David Brownell Signed-off-by: Greg Kroah-Hartman --- Documentation/spi/pxa2xx | 234 ++++++ drivers/spi/Kconfig | 8 + drivers/spi/Makefile | 1 + drivers/spi/pxa2xx_spi.c | 1399 +++++++++++++++++++++++++++++++++ include/asm-arm/arch-pxa/pxa2xx_spi.h | 68 ++ 5 files changed, 1710 insertions(+) create mode 100644 Documentation/spi/pxa2xx create mode 100644 drivers/spi/pxa2xx_spi.c create mode 100644 include/asm-arm/arch-pxa/pxa2xx_spi.h (limited to 'include') diff --git a/Documentation/spi/pxa2xx b/Documentation/spi/pxa2xx new file mode 100644 index 000000000000..9c45f3df2e18 --- /dev/null +++ b/Documentation/spi/pxa2xx @@ -0,0 +1,234 @@ +PXA2xx SPI on SSP driver HOWTO +=================================================== +This a mini howto on the pxa2xx_spi driver. The driver turns a PXA2xx +synchronous serial port into a SPI master controller +(see Documentation/spi/spi_summary). The driver has the following features + +- Support for any PXA2xx SSP +- SSP PIO and SSP DMA data transfers. +- External and Internal (SSPFRM) chip selects. +- Per slave device (chip) configuration. +- Full suspend, freeze, resume support. + +The driver is built around a "spi_message" fifo serviced by workqueue and a +tasklet. The workqueue, "pump_messages", drives message fifo and the tasklet +(pump_transfer) is responsible for queuing SPI transactions and setting up and +launching the dma/interrupt driven transfers. + +Declaring PXA2xx Master Controllers +----------------------------------- +Typically a SPI master is defined in the arch/.../mach-*/board-*.c as a +"platform device". The master configuration is passed to the driver via a table +found in include/asm-arm/arch-pxa/pxa2xx_spi.h: + +struct pxa2xx_spi_master { + enum pxa_ssp_type ssp_type; + u32 clock_enable; + u16 num_chipselect; + u8 enable_dma; +}; + +The "pxa2xx_spi_master.ssp_type" field must have a value between 1 and 3 and +informs the driver which features a particular SSP supports. + +The "pxa2xx_spi_master.clock_enable" field is used to enable/disable the +corresponding SSP peripheral block in the "Clock Enable Register (CKEN"). See +the "PXA2xx Developer Manual" section "Clocks and Power Management". + +The "pxa2xx_spi_master.num_chipselect" field is used to determine the number of +slave device (chips) attached to this SPI master. + +The "pxa2xx_spi_master.enable_dma" field informs the driver that SSP DMA should +be used. This caused the driver to acquire two DMA channels: rx_channel and +tx_channel. The rx_channel has a higher DMA service priority the tx_channel. +See the "PXA2xx Developer Manual" section "DMA Controller". + +NSSP MASTER SAMPLE +------------------ +Below is a sample configuration using the PXA255 NSSP. + +static struct resource pxa_spi_nssp_resources[] = { + [0] = { + .start = __PREG(SSCR0_P(2)), /* Start address of NSSP */ + .end = __PREG(SSCR0_P(2)) + 0x2c, /* Range of registers */ + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = IRQ_NSSP, /* NSSP IRQ */ + .end = IRQ_NSSP, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct pxa2xx_spi_master pxa_nssp_master_info = { + .ssp_type = PXA25x_NSSP, /* Type of SSP */ + .clock_enable = CKEN9_NSSP, /* NSSP Peripheral clock */ + .num_chipselect = 1, /* Matches the number of chips attached to NSSP */ + .enable_dma = 1, /* Enables NSSP DMA */ +}; + +static struct platform_device pxa_spi_nssp = { + .name = "pxa2xx-spi", /* MUST BE THIS VALUE, so device match driver */ + .id = 2, /* Bus number, MUST MATCH SSP number 1..n */ + .resource = pxa_spi_nssp_resources, + .num_resources = ARRAY_SIZE(pxa_spi_nssp_resources), + .dev = { + .platform_data = &pxa_nssp_master_info, /* Passed to driver */ + }, +}; + +static struct platform_device *devices[] __initdata = { + &pxa_spi_nssp, +}; + +static void __init board_init(void) +{ + (void)platform_add_device(devices, ARRAY_SIZE(devices)); +} + +Declaring Slave Devices +----------------------- +Typically each SPI slave (chip) is defined in the arch/.../mach-*/board-*.c +using the "spi_board_info" structure found in "linux/spi/spi.h". See +"Documentation/spi/spi_summary" for additional information. + +Each slave device attached to the PXA must provide slave specific configuration +information via the structure "pxa2xx_spi_chip" found in +"include/asm-arm/arch-pxa/pxa2xx_spi.h". The pxa2xx_spi master controller driver +will uses the configuration whenever the driver communicates with the slave +device. + +struct pxa2xx_spi_chip { + u8 tx_threshold; + u8 rx_threshold; + u8 dma_burst_size; + u32 timeout_microsecs; + u8 enable_loopback; + void (*cs_control)(u32 command); +}; + +The "pxa2xx_spi_chip.tx_threshold" and "pxa2xx_spi_chip.rx_threshold" fields are +used to configure the SSP hardware fifo. These fields are critical to the +performance of pxa2xx_spi driver and misconfiguration will result in rx +fifo overruns (especially in PIO mode transfers). Good default values are + + .tx_threshold = 12, + .rx_threshold = 4, + +The "pxa2xx_spi_chip.dma_burst_size" field is used to configure PXA2xx DMA +engine and is related the "spi_device.bits_per_word" field. Read and understand +the PXA2xx "Developer Manual" sections on the DMA controller and SSP Controllers +to determine the correct value. An SSP configured for byte-wide transfers would +use a value of 8. + +The "pxa2xx_spi_chip.timeout_microsecs" fields is used to efficiently handle +trailing bytes in the SSP receiver fifo. The correct value for this field is +dependent on the SPI bus speed ("spi_board_info.max_speed_hz") and the specific +slave device. Please note the the PXA2xx SSP 1 does not support trailing byte +timeouts and must busy-wait any trailing bytes. + +The "pxa2xx_spi_chip.enable_loopback" field is used to place the SSP porting +into internal loopback mode. In this mode the SSP controller internally +connects the SSPTX pin the the SSPRX pin. This is useful for initial setup +testing. + +The "pxa2xx_spi_chip.cs_control" field is used to point to a board specific +function for asserting/deasserting a slave device chip select. If the field is +NULL, the pxa2xx_spi master controller driver assumes that the SSP port is +configured to use SSPFRM instead. + +NSSP SALVE SAMPLE +----------------- +The pxa2xx_spi_chip structure is passed to the pxa2xx_spi driver in the +"spi_board_info.controller_data" field. Below is a sample configuration using +the PXA255 NSSP. + +/* Chip Select control for the CS8415A SPI slave device */ +static void cs8415a_cs_control(u32 command) +{ + if (command & PXA2XX_CS_ASSERT) + GPCR(2) = GPIO_bit(2); + else + GPSR(2) = GPIO_bit(2); +} + +/* Chip Select control for the CS8405A SPI slave device */ +static void cs8405a_cs_control(u32 command) +{ + if (command & PXA2XX_CS_ASSERT) + GPCR(3) = GPIO_bit(3); + else + GPSR(3) = GPIO_bit(3); +} + +static struct pxa2xx_spi_chip cs8415a_chip_info = { + .tx_threshold = 12, /* SSP hardward FIFO threshold */ + .rx_threshold = 4, /* SSP hardward FIFO threshold */ + .dma_burst_size = 8, /* Byte wide transfers used so 8 byte bursts */ + .timeout_microsecs = 64, /* Wait at least 64usec to handle trailing */ + .cs_control = cs8415a_cs_control, /* Use external chip select */ +}; + +static struct pxa2xx_spi_chip cs8405a_chip_info = { + .tx_threshold = 12, /* SSP hardward FIFO threshold */ + .rx_threshold = 4, /* SSP hardward FIFO threshold */ + .dma_burst_size = 8, /* Byte wide transfers used so 8 byte bursts */ + .timeout_microsecs = 64, /* Wait at least 64usec to handle trailing */ + .cs_control = cs8405a_cs_control, /* Use external chip select */ +}; + +static struct spi_board_info streetracer_spi_board_info[] __initdata = { + { + .modalias = "cs8415a", /* Name of spi_driver for this device */ + .max_speed_hz = 3686400, /* Run SSP as fast a possbile */ + .bus_num = 2, /* Framework bus number */ + .chip_select = 0, /* Framework chip select */ + .platform_data = NULL; /* No spi_driver specific config */ + .controller_data = &cs8415a_chip_info, /* Master chip config */ + .irq = STREETRACER_APCI_IRQ, /* Slave device interrupt */ + }, + { + .modalias = "cs8405a", /* Name of spi_driver for this device */ + .max_speed_hz = 3686400, /* Run SSP as fast a possbile */ + .bus_num = 2, /* Framework bus number */ + .chip_select = 1, /* Framework chip select */ + .controller_data = &cs8405a_chip_info, /* Master chip config */ + .irq = STREETRACER_APCI_IRQ, /* Slave device interrupt */ + }, +}; + +static void __init streetracer_init(void) +{ + spi_register_board_info(streetracer_spi_board_info, + ARRAY_SIZE(streetracer_spi_board_info)); +} + + +DMA and PIO I/O Support +----------------------- +The pxa2xx_spi driver support both DMA and interrupt driven PIO message +transfers. The driver defaults to PIO mode and DMA transfers must enabled by +setting the "enable_dma" flag in the "pxa2xx_spi_master" structure and and +ensuring that the "pxa2xx_spi_chip.dma_burst_size" field is non-zero. The DMA +mode support both coherent and stream based DMA mappings. + +The following logic is used to determine the type of I/O to be used on +a per "spi_transfer" basis: + +if !enable_dma or dma_burst_size == 0 then + always use PIO transfers + +if spi_message.is_dma_mapped and rx_dma_buf != 0 and tx_dma_buf != 0 then + use coherent DMA mode + +if rx_buf and tx_buf are aligned on 8 byte boundary then + use streaming DMA mode + +otherwise + use PIO transfer + +THANKS TO +--------- + +David Brownell and others for mentoring the development of this driver. + diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index 7a75faeb0526..9ce1d01469b1 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -75,6 +75,14 @@ config SPI_BUTTERFLY inexpensive battery powered microcontroller evaluation board. This same cable can be used to flash new firmware. +config SPI_PXA2XX + tristate "PXA2xx SSP SPI master" + depends on SPI_MASTER && ARCH_PXA && EXPERIMENTAL + help + This enables using a PXA2xx SSP port as a SPI master controller. + The driver can be configured to use any SSP port and additional + documentation can be found a Documentation/spi/pxa2xx. + # # Add new SPI master controllers in alphabetical order above this line # diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index c2c87e845abf..1bca5f95de25 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -13,6 +13,7 @@ obj-$(CONFIG_SPI_MASTER) += spi.o # SPI master controller drivers (bus) obj-$(CONFIG_SPI_BITBANG) += spi_bitbang.o obj-$(CONFIG_SPI_BUTTERFLY) += spi_butterfly.o +obj-$(CONFIG_SPI_PXA2XX) += pxa2xx_spi.o # ... add above this line ... # SPI protocol drivers (device/link on bus) diff --git a/drivers/spi/pxa2xx_spi.c b/drivers/spi/pxa2xx_spi.c new file mode 100644 index 000000000000..913e1aff0235 --- /dev/null +++ b/drivers/spi/pxa2xx_spi.c @@ -0,0 +1,1399 @@ +/* + * Copyright (C) 2005 Stephen Street / StreetFire Sound Labs + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include + +MODULE_AUTHOR("Stephen Street"); +MODULE_DESCRIPTION("PXA2xx SSP SPI Contoller"); +MODULE_LICENSE("GPL"); + +#define MAX_BUSES 3 + +#define DMA_INT_MASK (DCSR_ENDINTR | DCSR_STARTINTR | DCSR_BUSERR) +#define RESET_DMA_CHANNEL (DCSR_NODESC | DMA_INT_MASK) +#define IS_DMA_ALIGNED(x) (((u32)(x)&0x07)==0) + +#define DEFINE_SSP_REG(reg, off) \ +static inline u32 read_##reg(void *p) { return __raw_readl(p + (off)); } \ +static inline void write_##reg(u32 v, void *p) { __raw_writel(v, p + (off)); } + +DEFINE_SSP_REG(SSCR0, 0x00) +DEFINE_SSP_REG(SSCR1, 0x04) +DEFINE_SSP_REG(SSSR, 0x08) +DEFINE_SSP_REG(SSITR, 0x0c) +DEFINE_SSP_REG(SSDR, 0x10) +DEFINE_SSP_REG(SSTO, 0x28) +DEFINE_SSP_REG(SSPSP, 0x2c) + +#define START_STATE ((void*)0) +#define RUNNING_STATE ((void*)1) +#define DONE_STATE ((void*)2) +#define ERROR_STATE ((void*)-1) + +#define QUEUE_RUNNING 0 +#define QUEUE_STOPPED 1 + +struct driver_data { + /* Driver model hookup */ + struct platform_device *pdev; + + /* SPI framework hookup */ + enum pxa_ssp_type ssp_type; + struct spi_master *master; + + /* PXA hookup */ + struct pxa2xx_spi_master *master_info; + + /* DMA setup stuff */ + int rx_channel; + int tx_channel; + u32 *null_dma_buf; + + /* SSP register addresses */ + void *ioaddr; + u32 ssdr_physical; + + /* SSP masks*/ + u32 dma_cr1; + u32 int_cr1; + u32 clear_sr; + u32 mask_sr; + + /* Driver message queue */ + struct workqueue_struct *workqueue; + struct work_struct pump_messages; + spinlock_t lock; + struct list_head queue; + int busy; + int run; + + /* Message Transfer pump */ + struct tasklet_struct pump_transfers; + + /* Current message transfer state info */ + struct spi_message* cur_msg; + struct spi_transfer* cur_transfer; + struct chip_data *cur_chip; + size_t len; + void *tx; + void *tx_end; + void *rx; + void *rx_end; + int dma_mapped; + dma_addr_t rx_dma; + dma_addr_t tx_dma; + size_t rx_map_len; + size_t tx_map_len; + int cs_change; + void (*write)(struct driver_data *drv_data); + void (*read)(struct driver_data *drv_data); + irqreturn_t (*transfer_handler)(struct driver_data *drv_data); + void (*cs_control)(u32 command); +}; + +struct chip_data { + u32 cr0; + u32 cr1; + u32 to; + u32 psp; + u32 timeout; + u8 n_bytes; + u32 dma_width; + u32 dma_burst_size; + u32 threshold; + u32 dma_threshold; + u8 enable_dma; + void (*write)(struct driver_data *drv_data); + void (*read)(struct driver_data *drv_data); + void (*cs_control)(u32 command); +}; + +static void pump_messages(void *data); + +static int flush(struct driver_data *drv_data) +{ + unsigned long limit = loops_per_jiffy << 1; + + void *reg = drv_data->ioaddr; + + do { + while (read_SSSR(reg) & SSSR_RNE) { + read_SSDR(reg); + } + } while ((read_SSSR(reg) & SSSR_BSY) && limit--); + write_SSSR(SSSR_ROR, reg); + + return limit; +} + +static void restore_state(struct driver_data *drv_data) +{ + void *reg = drv_data->ioaddr; + + /* Clear status and disable clock */ + write_SSSR(drv_data->clear_sr, reg); + write_SSCR0(drv_data->cur_chip->cr0 & ~SSCR0_SSE, reg); + + /* Load the registers */ + write_SSCR1(drv_data->cur_chip->cr1, reg); + write_SSCR0(drv_data->cur_chip->cr0, reg); + if (drv_data->ssp_type != PXA25x_SSP) { + write_SSTO(0, reg); + write_SSPSP(drv_data->cur_chip->psp, reg); + } +} + +static void null_cs_control(u32 command) +{ +} + +static void null_writer(struct driver_data *drv_data) +{ + void *reg = drv_data->ioaddr; + u8 n_bytes = drv_data->cur_chip->n_bytes; + + while ((read_SSSR(reg) & SSSR_TNF) + && (drv_data->tx < drv_data->tx_end)) { + write_SSDR(0, reg); + drv_data->tx += n_bytes; + } +} + +static void null_reader(struct driver_data *drv_data) +{ + void *reg = drv_data->ioaddr; + u8 n_bytes = drv_data->cur_chip->n_bytes; + + while ((read_SSSR(reg) & SSSR_RNE) + && (drv_data->rx < drv_data->rx_end)) { + read_SSDR(reg); + drv_data->rx += n_bytes; + } +} + +static void u8_writer(struct driver_data *drv_data) +{ + void *reg = drv_data->ioaddr; + + while ((read_SSSR(reg) & SSSR_TNF) + && (drv_data->tx < drv_data->tx_end)) { + write_SSDR(*(u8 *)(drv_data->tx), reg); + ++drv_data->tx; + } +} + +static void u8_reader(struct driver_data *drv_data) +{ + void *reg = drv_data->ioaddr; + + while ((read_SSSR(reg) & SSSR_RNE) + && (drv_data->rx < drv_data->rx_end)) { + *(u8 *)(drv_data->rx) = read_SSDR(reg); + ++drv_data->rx; + } +} + +static void u16_writer(struct driver_data *drv_data) +{ + void *reg = drv_data->ioaddr; + + while ((read_SSSR(reg) & SSSR_TNF) + && (drv_data->tx < drv_data->tx_end)) { + write_SSDR(*(u16 *)(drv_data->tx), reg); + drv_data->tx += 2; + } +} + +static void u16_reader(struct driver_data *drv_data) +{ + void *reg = drv_data->ioaddr; + + while ((read_SSSR(reg) & SSSR_RNE) + && (drv_data->rx < drv_data->rx_end)) { + *(u16 *)(drv_data->rx) = read_SSDR(reg); + drv_data->rx += 2; + } +} +static void u32_writer(struct driver_data *drv_data) +{ + void *reg = drv_data->ioaddr; + + while ((read_SSSR(reg) & SSSR_TNF) + && (drv_data->tx < drv_data->tx_end)) { + write_SSDR(*(u16 *)(drv_data->tx), reg); + drv_data->tx += 4; + } +} + +static void u32_reader(struct driver_data *drv_data) +{ + void *reg = drv_data->ioaddr; + + while ((read_SSSR(reg) & SSSR_RNE) + && (drv_data->rx < drv_data->rx_end)) { + *(u32 *)(drv_data->rx) = read_SSDR(reg); + drv_data->rx += 4; + } +} + +static void *next_transfer(struct driver_data *drv_data) +{ + struct spi_message *msg = drv_data->cur_msg; + struct spi_transfer *trans = drv_data->cur_transfer; + + /* Move to next transfer */ + if (trans->transfer_list.next != &msg->transfers) { + drv_data->cur_transfer = + list_entry(trans->transfer_list.next, + struct spi_transfer, + transfer_list); + return RUNNING_STATE; + } else + return DONE_STATE; +} + +static int map_dma_buffers(struct driver_data *drv_data) +{ + struct spi_message *msg = drv_data->cur_msg; + struct device *dev = &msg->spi->dev; + + if (!drv_data->cur_chip->enable_dma) + return 0; + + if (msg->is_dma_mapped) + return drv_data->rx_dma && drv_data->tx_dma; + + if (!IS_DMA_ALIGNED(drv_data->rx) || !IS_DMA_ALIGNED(drv_data->tx)) + return 0; + + /* Modify setup if rx buffer is null */ + if (drv_data->rx == NULL) { + *drv_data->null_dma_buf = 0; + drv_data->rx = drv_data->null_dma_buf; + drv_data->rx_map_len = 4; + } else + drv_data->rx_map_len = drv_data->len; + + + /* Modify setup if tx buffer is null */ + if (drv_data->tx == NULL) { + *drv_data->null_dma_buf = 0; + drv_data->tx = drv_data->null_dma_buf; + drv_data->tx_map_len = 4; + } else + drv_data->tx_map_len = drv_data->len; + + /* Stream map the rx buffer */ + drv_data->rx_dma = dma_map_single(dev, drv_data->rx, + drv_data->rx_map_len, + DMA_FROM_DEVICE); + if (dma_mapping_error(drv_data->rx_dma)) + return 0; + + /* Stream map the tx buffer */ + drv_data->tx_dma = dma_map_single(dev, drv_data->tx, + drv_data->tx_map_len, + DMA_TO_DEVICE); + + if (dma_mapping_error(drv_data->tx_dma)) { + dma_unmap_single(dev, drv_data->rx_dma, + drv_data->rx_map_len, DMA_FROM_DEVICE); + return 0; + } + + return 1; +} + +static void unmap_dma_buffers(struct driver_data *drv_data) +{ + struct device *dev; + + if (!drv_data->dma_mapped) + return; + + if (!drv_data->cur_msg->is_dma_mapped) { + dev = &drv_data->cur_msg->spi->dev; + dma_unmap_single(dev, drv_data->rx_dma, + drv_data->rx_map_len, DMA_FROM_DEVICE); + dma_unmap_single(dev, drv_data->tx_dma, + drv_data->tx_map_len, DMA_TO_DEVICE); + } + + drv_data->dma_mapped = 0; +} + +/* caller already set message->status; dma and pio irqs are blocked */ +static void giveback(struct spi_message *message, struct driver_data *drv_data) +{ + struct spi_transfer* last_transfer; + + last_transfer = list_entry(message->transfers.prev, + struct spi_transfer, + transfer_list); + + if (!last_transfer->cs_change) + drv_data->cs_control(PXA2XX_CS_DEASSERT); + + message->state = NULL; + if (message->complete) + message->complete(message->context); + + drv_data->cur_msg = NULL; + drv_data->cur_transfer = NULL; + drv_data->cur_chip = NULL; + queue_work(drv_data->workqueue, &drv_data->pump_messages); +} + +static int wait_ssp_rx_stall(void *ioaddr) +{ + unsigned long limit = loops_per_jiffy << 1; + + while ((read_SSSR(ioaddr) & SSSR_BSY) && limit--) + cpu_relax(); + + return limit; +} + +static int wait_dma_channel_stop(int channel) +{ + unsigned long limit = loops_per_jiffy << 1; + + while (!(DCSR(channel) & DCSR_STOPSTATE) && limit--) + cpu_relax(); + + return limit; +} + +static void dma_handler(int channel, void *data, struct pt_regs *regs) +{ + struct driver_data *drv_data = data; + struct spi_message *msg = drv_data->cur_msg; + void *reg = drv_data->ioaddr; + u32 irq_status = DCSR(channel) & DMA_INT_MASK; + u32 trailing_sssr = 0; + + if (irq_status & DCSR_BUSERR) { + + /* Disable interrupts, clear status and reset DMA */ + if (drv_data->ssp_type != PXA25x_SSP) + write_SSTO(0, reg); + write_SSSR(drv_data->clear_sr, reg); + write_SSCR1(read_SSCR1(reg) & ~drv_data->dma_cr1, reg); + DCSR(drv_data->rx_channel) = RESET_DMA_CHANNEL; + DCSR(drv_data->tx_channel) = RESET_DMA_CHANNEL; + + if (flush(drv_data) == 0) + dev_err(&drv_data->pdev->dev, + "dma_handler: flush fail\n"); + + unmap_dma_buffers(drv_data); + + if (channel == drv_data->tx_channel) + dev_err(&drv_data->pdev->dev, + "dma_handler: bad bus address on " + "tx channel %d, source %x target = %x\n", + channel, DSADR(channel), DTADR(channel)); + else + dev_err(&drv_data->pdev->dev, + "dma_handler: bad bus address on " + "rx channel %d, source %x target = %x\n", + channel, DSADR(channel), DTADR(channel)); + + msg->state = ERROR_STATE; + tasklet_schedule(&drv_data->pump_transfers); + } + + /* PXA255x_SSP has no timeout interrupt, wait for tailing bytes */ + if ((drv_data->ssp_type == PXA25x_SSP) + && (channel == drv_data->tx_channel) + && (irq_status & DCSR_ENDINTR)) { + + /* Wait for rx to stall */ + if (wait_ssp_rx_stall(drv_data->ioaddr) == 0) + dev_err(&drv_data->pdev->dev, + "dma_handler: ssp rx stall failed\n"); + + /* Clear and disable interrupts on SSP and DMA channels*/ + write_SSSR(drv_data->clear_sr, reg); + write_SSCR1(read_SSCR1(reg) & ~drv_data->dma_cr1, reg); + DCSR(drv_data->tx_channel) = RESET_DMA_CHANNEL; + DCSR(drv_data->rx_channel) = RESET_DMA_CHANNEL; + if (wait_dma_channel_stop(drv_data->rx_channel) == 0) + dev_err(&drv_data->pdev->dev, + "dma_handler: dma rx channel stop failed\n"); + + unmap_dma_buffers(drv_data); + + /* Read trailing bytes */ + /* Calculate number of trailing bytes, read them */ + trailing_sssr = read_SSSR(reg); + if ((trailing_sssr & 0xf008) != 0xf000) { + drv_data->rx = drv_data->rx_end - + (((trailing_sssr >> 12) & 0x0f) + 1); + drv_data->read(drv_data); + } + msg->actual_length += drv_data->len; + + /* Release chip select if requested, transfer delays are + * handled in pump_transfers */ + if (drv_data->cs_change) + drv_data->cs_control(PXA2XX_CS_DEASSERT); + + /* Move to next transfer */ + msg->state = next_transfer(drv_data); + + /* Schedule transfer tasklet */ + tasklet_schedule(&drv_data->pump_transfers); + } +} + +static irqreturn_t dma_transfer(struct driver_data *drv_data) +{ + u32 irq_status; + u32 trailing_sssr = 0; + struct spi_message *msg = drv_data->cur_msg; + void *reg = drv_data->ioaddr; + + irq_status = read_SSSR(reg) & drv_data->mask_sr; + if (irq_status & SSSR_ROR) { + /* Clear and disable interrupts on SSP and DMA channels*/ + if (drv_data->ssp_type != PXA25x_SSP) + write_SSTO(0, reg); + write_SSSR(drv_data->clear_sr, reg); + write_SSCR1(read_SSCR1(reg) & ~drv_data->dma_cr1, reg); + DCSR(drv_data->tx_channel) = RESET_DMA_CHANNEL; + DCSR(drv_data->rx_channel) = RESET_DMA_CHANNEL; + unmap_dma_buffers(drv_data); + + if (flush(drv_data) == 0) + dev_err(&drv_data->pdev->dev, + "dma_transfer: flush fail\n"); + + dev_warn(&drv_data->pdev->dev, "dma_transfer: fifo overun\n"); + + drv_data->cur_msg->state = ERROR_STATE; + tasklet_schedule(&drv_data->pump_transfers); + + return IRQ_HANDLED; + } + + /* Check for false positive timeout */ + if ((irq_status & SSSR_TINT) && DCSR(drv_data->tx_channel) & DCSR_RUN) { + write_SSSR(SSSR_TINT, reg); + return IRQ_HANDLED; + } + + if (irq_status & SSSR_TINT || drv_data->rx == drv_data->rx_end) { + + /* Clear and disable interrupts on SSP and DMA channels*/ + if (drv_data->ssp_type != PXA25x_SSP) + write_SSTO(0, reg); + write_SSSR(drv_data->clear_sr, reg); + write_SSCR1(read_SSCR1(reg) & ~drv_data->dma_cr1, reg); + DCSR(drv_data->tx_channel) = RESET_DMA_CHANNEL; + DCSR(drv_data->rx_channel) = RESET_DMA_CHANNEL; + + if (wait_dma_channel_stop(drv_data->rx_channel) == 0) + dev_err(&drv_data->pdev->dev, + "dma_transfer: dma rx channel stop failed\n"); + + if (wait_ssp_rx_stall(drv_data->ioaddr) == 0) + dev_err(&drv_data->pdev->dev, + "dma_transfer: ssp rx stall failed\n"); + + unmap_dma_buffers(drv_data); + + /* Calculate number of trailing bytes, read them */ + trailing_sssr = read_SSSR(reg); + if ((trailing_sssr & 0xf008) != 0xf000) { + drv_data->rx = drv_data->rx_end - + (((trailing_sssr >> 12) & 0x0f) + 1); + drv_data->read(drv_data); + } + msg->actual_length += drv_data->len; + + /* Release chip select if requested, transfer delays are + * handled in pump_transfers */ + if (drv_data->cs_change) + drv_data->cs_control(PXA2XX_CS_DEASSERT); + + /* Move to next transfer */ + msg->state = next_transfer(drv_data); + + /* Schedule transfer tasklet */ + tasklet_schedule(&drv_data->pump_transfers); + + return IRQ_HANDLED; + } + + /* Opps problem detected */ + return IRQ_NONE; +} + +static irqreturn_t interrupt_transfer(struct driver_data *drv_data) +{ + u32 irq_status; + struct spi_message *msg = drv_data->cur_msg; + void *reg = drv_data->ioaddr; + irqreturn_t handled = IRQ_NONE; + unsigned long limit = loops_per_jiffy << 1; + + while ((irq_status = (read_SSSR(reg) & drv_data->mask_sr))) { + + if (irq_status & SSSR_ROR) { + + /* Clear and disable interrupts */ + if (drv_data->ssp_type != PXA25x_SSP) + write_SSTO(0, reg); + write_SSSR(drv_data->clear_sr, reg); + write_SSCR1(read_SSCR1(reg) & ~drv_data->int_cr1, reg); + + if (flush(drv_data) == 0) + dev_err(&drv_data->pdev->dev, + "interrupt_transfer: flush fail\n"); + + dev_warn(&drv_data->pdev->dev, + "interrupt_transfer: fifo overun\n"); + + msg->state = ERROR_STATE; + tasklet_schedule(&drv_data->pump_transfers); + + return IRQ_HANDLED; + } + + /* Look for false positive timeout */ + if ((irq_status & SSSR_TINT) + && (drv_data->rx < drv_data->rx_end)) + write_SSSR(SSSR_TINT, reg); + + /* Pump data */ + drv_data->read(drv_data); + drv_data->write(drv_data); + + if (drv_data->tx == drv_data->tx_end) { + /* Disable tx interrupt */ + write_SSCR1(read_SSCR1(reg) & ~SSCR1_TIE, reg); + + /* PXA25x_SSP has no timeout, read trailing bytes */ + if (drv_data->ssp_type == PXA25x_SSP) { + while ((read_SSSR(reg) & SSSR_BSY) && limit--) + drv_data->read(drv_data); + + if (limit == 0) + dev_err(&drv_data->pdev->dev, + "interrupt_transfer: " + "trailing byte read failed\n"); + } + } + + if ((irq_status & SSSR_TINT) + || (drv_data->rx == drv_data->rx_end)) { + + /* Clear timeout */ + if (drv_data->ssp_type != PXA25x_SSP) + write_SSTO(0, reg); + write_SSSR(drv_data->clear_sr, reg); + write_SSCR1(read_SSCR1(reg) & ~drv_data->int_cr1, reg); + + /* Update total byte transfered */ + msg->actual_length += drv_data->len; + + /* Release chip select if requested, transfer delays are + * handled in pump_transfers */ + if (drv_data->cs_change) + drv_data->cs_control(PXA2XX_CS_DEASSERT); + + /* Move to next transfer */ + msg->state = next_transfer(drv_data); + + /* Schedule transfer tasklet */ + tasklet_schedule(&drv_data->pump_transfers); + + return IRQ_HANDLED; + } + + /* We did something */ + handled = IRQ_HANDLED; + } + + return handled; +} + +static irqreturn_t ssp_int(int irq, void *dev_id, struct pt_regs *regs) +{ + struct driver_data *drv_data = (struct driver_data *)dev_id; + + if (!drv_data->cur_msg) { + dev_err(&drv_data->pdev->dev, "bad message state " + "in interrupt handler\n"); + /* Never fail */ + return IRQ_HANDLED; + } + + return drv_data->transfer_handler(drv_data); +} + +static void pump_transfers(unsigned long data) +{ + struct driver_data *drv_data = (struct driver_data *)data; + struct spi_message *message = NULL; + struct spi_transfer *transfer = NULL; + struct spi_transfer *previous = NULL; + struct chip_data *chip = NULL; + void *reg = drv_data->ioaddr; + + /* Get current state information */ + message = drv_data->cur_msg; + transfer = drv_data->cur_transfer; + chip = drv_data->cur_chip; + + /* Handle for abort */ + if (message->state == ERROR_STATE) { + message->status = -EIO; + giveback(message, drv_data); + return; + } + + /* Handle end of message */ + if (message->state == DONE_STATE) { + message->status = 0; + giveback(message, drv_data); + return; + } + + /* Delay if requested at end of transfer*/ + if (message->state == RUNNING_STATE) { + previous = list_entry(transfer->transfer_list.prev, + struct spi_transfer, + transfer_list); + if (previous->delay_usecs) + udelay(previous->delay_usecs); + } + + /* Setup the transfer state based on the type of transfer */ + if (flush(drv_data) == 0) { + dev_err(&drv_data->pdev->dev, "pump_transfers: flush failed\n"); + message->status = -EIO; + giveback(message, drv_data); + return; + } + drv_data->cs_control = chip->cs_control; + drv_data->tx = (void *)transfer->tx_buf; + drv_data->tx_end = drv_data->tx + transfer->len; + drv_data->rx = transfer->rx_buf; + drv_data->rx_end = drv_data->rx + transfer->len; + drv_data->rx_dma = transfer->rx_dma; + drv_data->tx_dma = transfer->tx_dma; + drv_data->len = transfer->len; + drv_data->write = drv_data->tx ? chip->write : null_writer; + drv_data->read = drv_data->rx ? chip->read : null_reader; + drv_data->cs_change = transfer->cs_change; + message->state = RUNNING_STATE; + + /* Try to map dma buffer and do a dma transfer if successful */ + if ((drv_data->dma_mapped = map_dma_buffers(drv_data))) { + + /* Ensure we have the correct interrupt handler */ + drv_data->transfer_handler = dma_transfer; + + /* Setup rx DMA Channel */ + DCSR(drv_data->rx_channel) = RESET_DMA_CHANNEL; + DSADR(drv_data->rx_channel) = drv_data->ssdr_physical; + DTADR(drv_data->rx_channel) = drv_data->rx_dma; + if (drv_data->rx == drv_data->null_dma_buf) + /* No target address increment */ + DCMD(drv_data->rx_channel) = DCMD_FLOWSRC + | chip->dma_width + | chip->dma_burst_size + | drv_data->len; + else + DCMD(drv_data->rx_channel) = DCMD_INCTRGADDR + | DCMD_FLOWSRC + | chip->dma_width + | chip->dma_burst_size + | drv_data->len; + + /* Setup tx DMA Channel */ + DCSR(drv_data->tx_channel) = RESET_DMA_CHANNEL; + DSADR(drv_data->tx_channel) = drv_data->tx_dma; + DTADR(drv_data->tx_channel) = drv_data->ssdr_physical; + if (drv_data->tx == drv_data->null_dma_buf) + /* No source address increment */ + DCMD(drv_data->tx_channel) = DCMD_FLOWTRG + | chip->dma_width + | chip->dma_burst_size + | drv_data->len; + else + DCMD(drv_data->tx_channel) = DCMD_INCSRCADDR + | DCMD_FLOWTRG + | chip->dma_width + | chip->dma_burst_size + | drv_data->len; + + /* Enable dma end irqs on SSP to detect end of transfer */ + if (drv_data->ssp_type == PXA25x_SSP) + DCMD(drv_data->tx_channel) |= DCMD_ENDIRQEN; + + /* Fix me, need to handle cs polarity */ + drv_data->cs_control(PXA2XX_CS_ASSERT); + + /* Go baby, go */ + write_SSSR(drv_data->clear_sr, reg); + DCSR(drv_data->rx_channel) |= DCSR_RUN; + DCSR(drv_data->tx_channel) |= DCSR_RUN; + if (drv_data->ssp_type != PXA25x_SSP) + write_SSTO(chip->timeout, reg); + write_SSCR1(chip->cr1 + | chip->dma_threshold + | drv_data->dma_cr1, + reg); + } else { + /* Ensure we have the correct interrupt handler */ + drv_data->transfer_handler = interrupt_transfer; + + /* Fix me, need to handle cs polarity */ + drv_data->cs_control(PXA2XX_CS_ASSERT); + + /* Go baby, go */ + write_SSSR(drv_data->clear_sr, reg); + if (drv_data->ssp_type != PXA25x_SSP) + write_SSTO(chip->timeout, reg); + write_SSCR1(chip->cr1 + | chip->threshold + | drv_data->int_cr1, + reg); + } +} + +static void pump_messages(void *data) +{ + struct driver_data *drv_data = data; + unsigned long flags; + + /* Lock queue and check for queue work */ + spin_lock_irqsave(&drv_data->lock, flags); + if (list_empty(&drv_data->queue) || drv_data->run == QUEUE_STOPPED) { + drv_data->busy = 0; + spin_unlock_irqrestore(&drv_data->lock, flags); + return; + } + + /* Make sure we are not already running a message */ + if (drv_data->cur_msg) { + spin_unlock_irqrestore(&drv_data->lock, flags); + return; + } + + /* Extract head of queue */ + drv_data->cur_msg = list_entry(drv_data->queue.next, + struct spi_message, queue); + list_del_init(&drv_data->cur_msg->queue); + drv_data->busy = 1; + spin_unlock_irqrestore(&drv_data->lock, flags); + + /* Initial message state*/ + drv_data->cur_msg->state = START_STATE; + drv_data->cur_transfer = list_entry(drv_data->cur_msg->transfers.next, + struct spi_transfer, + transfer_list); + + /* Setup the SSP using the per chip configuration */ + drv_data->cur_chip = spi_get_ctldata(drv_data->cur_msg->spi); + restore_state(drv_data); + + /* Mark as busy and launch transfers */ + tasklet_schedule(&drv_data->pump_transfers); +} + +static int transfer(struct spi_device *spi, struct spi_message *msg) +{ + struct driver_data *drv_data = spi_master_get_devdata(spi->master); + unsigned long flags; + + spin_lock_irqsave(&drv_data->lock, flags); + + if (drv_data->run == QUEUE_STOPPED) { + spin_unlock_irqrestore(&drv_data->lock, flags); + return -ESHUTDOWN; + } + + msg->actual_length = 0; + msg->status = -EINPROGRESS; + msg->state = START_STATE; + + list_add_tail(&msg->queue, &drv_data->queue); + + if (drv_data->run == QUEUE_RUNNING && !drv_data->busy) + queue_work(drv_data->workqueue, &drv_data->pump_messages); + + spin_unlock_irqrestore(&drv_data->lock, flags); + + return 0; +} + +static int setup(struct spi_device *spi) +{ + struct pxa2xx_spi_chip *chip_info = NULL; + struct chip_data *chip; + struct driver_data *drv_data = spi_master_get_devdata(spi->master); + unsigned int clk_div; + + if (!spi->bits_per_word) + spi->bits_per_word = 8; + + if (drv_data->ssp_type != PXA25x_SSP + && (spi->bits_per_word < 4 || spi->bits_per_word > 32)) + return -EINVAL; + else if (spi->bits_per_word < 4 || spi->bits_per_word > 16) + return -EINVAL; + + /* Only alloc (or use chip_info) on first setup */ + chip = spi_get_ctldata(spi); + if (chip == NULL) { + chip = kzalloc(sizeof(struct chip_data), GFP_KERNEL); + if (!chip) + return -ENOMEM; + + chip->cs_control = null_cs_control; + chip->enable_dma = 0; + chip->timeout = 5; + chip->threshold = SSCR1_RxTresh(1) | SSCR1_TxTresh(1); + chip->dma_burst_size = drv_data->master_info->enable_dma ? + DCMD_BURST8 : 0; + + chip_info = spi->controller_data; + } + + /* chip_info isn't always needed */ + if (chip_info) { + if (chip_info->cs_control) + chip->cs_control = chip_info->cs_control; + + chip->timeout = (chip_info->timeout_microsecs * 10000) / 2712; + + chip->threshold = SSCR1_RxTresh(chip_info->rx_threshold) + | SSCR1_TxTresh(chip_info->tx_threshold); + + chip->enable_dma = chip_info->dma_burst_size != 0 + && drv_data->master_info->enable_dma; + chip->dma_threshold = 0; + + if (chip->enable_dma) { + if (chip_info->dma_burst_size <= 8) { + chip->dma_threshold = SSCR1_RxTresh(8) + | SSCR1_TxTresh(8); + chip->dma_burst_size = DCMD_BURST8; + } else if (chip_info->dma_burst_size <= 16) { + chip->dma_threshold = SSCR1_RxTresh(16) + | SSCR1_TxTresh(16); + chip->dma_burst_size = DCMD_BURST16; + } else { + chip->dma_threshold = SSCR1_RxTresh(32) + | SSCR1_TxTresh(32); + chip->dma_burst_size = DCMD_BURST32; + } + } + + + if (chip_info->enable_loopback) + chip->cr1 = SSCR1_LBM; + } + + if (drv_data->ioaddr == SSP1_VIRT) + clk_div = SSP1_SerClkDiv(spi->max_speed_hz); + else if (drv_data->ioaddr == SSP2_VIRT) + clk_div = SSP2_SerClkDiv(spi->max_speed_hz); + else if (drv_data->ioaddr == SSP3_VIRT) + clk_div = SSP3_SerClkDiv(spi->max_speed_hz); + else + return -ENODEV; + + chip->cr0 = clk_div + | SSCR0_Motorola + | SSCR0_DataSize(spi->bits_per_word & 0x0f) + | SSCR0_SSE + | (spi->bits_per_word > 16 ? SSCR0_EDSS : 0); + chip->cr1 |= (((spi->mode & SPI_CPHA) != 0) << 4) + | (((spi->mode & SPI_CPOL) != 0) << 3); + + /* NOTE: PXA25x_SSP _could_ use external clocking ... */ + if (drv_data->ssp_type != PXA25x_SSP) + dev_dbg(&spi->dev, "%d bits/word, %d Hz, mode %d\n", + spi->bits_per_word, + (CLOCK_SPEED_HZ) + / (1 + ((chip->cr0 & SSCR0_SCR) >> 8)), + spi->mode & 0x3); + else + dev_dbg(&spi->dev, "%d bits/word, %d Hz, mode %d\n", + spi->bits_per_word, + (CLOCK_SPEED_HZ/2) + / (1 + ((chip->cr0 & SSCR0_SCR) >> 8)), + spi->mode & 0x3); + + if (spi->bits_per_word <= 8) { + chip->n_bytes = 1; + chip->dma_width = DCMD_WIDTH1; + chip->read = u8_reader; + chip->write = u8_writer; + } else if (spi->bits_per_word <= 16) { + chip->n_bytes = 2; + chip->dma_width = DCMD_WIDTH2; + chip->read = u16_reader; + chip->write = u16_writer; + } else if (spi->bits_per_word <= 32) { + chip->cr0 |= SSCR0_EDSS; + chip->n_bytes = 4; + chip->dma_width = DCMD_WIDTH4; + chip->read = u32_reader; + chip->write = u32_writer; + } else { + dev_err(&spi->dev, "invalid wordsize\n"); + kfree(chip); + return -ENODEV; + } + + spi_set_ctldata(spi, chip); + + return 0; +} + +static void cleanup(const struct spi_device *spi) +{ + struct chip_data *chip = spi_get_ctldata((struct spi_device *)spi); + + kfree(chip); +} + +static int init_queue(struct driver_data *drv_data) +{ + INIT_LIST_HEAD(&drv_data->queue); + spin_lock_init(&drv_data->lock); + + drv_data->run = QUEUE_STOPPED; + drv_data->busy = 0; + + tasklet_init(&drv_data->pump_transfers, + pump_transfers, (unsigned long)drv_data); + + INIT_WORK(&drv_data->pump_messages, pump_messages, drv_data); + drv_data->workqueue = create_singlethread_workqueue( + drv_data->master->cdev.dev->bus_id); + if (drv_data->workqueue == NULL) + return -EBUSY; + + return 0; +} + +static int start_queue(struct driver_data *drv_data) +{ + unsigned long flags; + + spin_lock_irqsave(&drv_data->lock, flags); + + if (drv_data->run == QUEUE_RUNNING || drv_data->busy) { + spin_unlock_irqrestore(&drv_data->lock, flags); + return -EBUSY; + } + + drv_data->run = QUEUE_RUNNING; + drv_data->cur_msg = NULL; + drv_data->cur_transfer = NULL; + drv_data->cur_chip = NULL; + spin_unlock_irqrestore(&drv_data->lock, flags); + + queue_work(drv_data->workqueue, &drv_data->pump_messages); + + return 0; +} + +static int stop_queue(struct driver_data *drv_data) +{ + unsigned long flags; + unsigned limit = 500; + int status = 0; + + spin_lock_irqsave(&drv_data->lock, flags); + + /* This is a bit lame, but is optimized for the common execution path. + * A wait_queue on the drv_data->busy could be used, but then the common + * execution path (pump_messages) would be required to call wake_up or + * friends on every SPI message. Do this instead */ + drv_data->run = QUEUE_STOPPED; + while (!list_empty(&drv_data->queue) && drv_data->busy && limit--) { + spin_unlock_irqrestore(&drv_data->lock, flags); + msleep(10); + spin_lock_irqsave(&drv_data->lock, flags); + } + + if (!list_empty(&drv_data->queue) || drv_data->busy) + status = -EBUSY; + + spin_unlock_irqrestore(&drv_data->lock, flags); + + return status; +} + +static int destroy_queue(struct driver_data *drv_data) +{ + int status; + + status = stop_queue(drv_data); + if (status != 0) + return status; + + destroy_workqueue(drv_data->workqueue); + + return 0; +} + +static int pxa2xx_spi_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct pxa2xx_spi_master *platform_info; + struct spi_master *master; + struct driver_data *drv_data = 0; + struct resource *memory_resource; + int irq; + int status = 0; + + platform_info = dev->platform_data; + + if (platform_info->ssp_type == SSP_UNDEFINED) { + dev_err(&pdev->dev, "undefined SSP\n"); + return -ENODEV; + } + + /* Allocate master with space for drv_data and null dma buffer */ + master = spi_alloc_master(dev, sizeof(struct driver_data) + 16); + if (!master) { + dev_err(&pdev->dev, "can not alloc spi_master\n"); + return -ENOMEM; + } + drv_data = spi_master_get_devdata(master); + drv_data->master = master; + drv_data->master_info = platform_info; + drv_data->pdev = pdev; + + master->bus_num = pdev->id; + master->num_chipselect = platform_info->num_chipselect; + master->cleanup = cleanup; + master->setup = setup; + master->transfer = transfer; + + drv_data->ssp_type = platform_info->ssp_type; + drv_data->null_dma_buf = (u32 *)ALIGN((u32)(drv_data + + sizeof(struct driver_data)), 8); + + /* Setup register addresses */ + memory_resource = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!memory_resource) { + dev_err(&pdev->dev, "memory resources not defined\n"); + status = -ENODEV; + goto out_error_master_alloc; + } + + drv_data->ioaddr = (void *)io_p2v(memory_resource->start); + drv_data->ssdr_physical = memory_resource->start + 0x00000010; + if (platform_info->ssp_type == PXA25x_SSP) { + drv_data->int_cr1 = SSCR1_TIE | SSCR1_RIE; + drv_data->dma_cr1 = 0; + drv_data->clear_sr = SSSR_ROR; + drv_data->mask_sr = SSSR_RFS | SSSR_TFS | SSSR_ROR; + } else { + drv_data->int_cr1 = SSCR1_TIE | SSCR1_RIE | SSCR1_TINTE; + drv_data->dma_cr1 = SSCR1_TSRE | SSCR1_RSRE | SSCR1_TINTE; + drv_data->clear_sr = SSSR_ROR | SSSR_TINT; + drv_data->mask_sr = SSSR_TINT | SSSR_RFS | SSSR_TFS | SSSR_ROR; + } + + /* Attach to IRQ */ + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + dev_err(&pdev->dev, "irq resource not defined\n"); + status = -ENODEV; + goto out_error_master_alloc; + } + + status = request_irq(irq, ssp_int, SA_INTERRUPT, dev->bus_id, drv_data); + if (status < 0) { + dev_err(&pdev->dev, "can not get IRQ\n"); + goto out_error_master_alloc; + } + + /* Setup DMA if requested */ + drv_data->tx_channel = -1; + drv_data->rx_channel = -1; + if (platform_info->enable_dma) { + + /* Get two DMA channels (rx and tx) */ + drv_data->rx_channel = pxa_request_dma("pxa2xx_spi_ssp_rx", + DMA_PRIO_HIGH, + dma_handler, + drv_data); + if (drv_data->rx_channel < 0) { + dev_err(dev, "problem (%d) requesting rx channel\n", + drv_data->rx_channel); + status = -ENODEV; + goto out_error_irq_alloc; + } + drv_data->tx_channel = pxa_request_dma("pxa2xx_spi_ssp_tx", + DMA_PRIO_MEDIUM, + dma_handler, + drv_data); + if (drv_data->tx_channel < 0) { + dev_err(dev, "problem (%d) requesting tx channel\n", + drv_data->tx_channel); + status = -ENODEV; + goto out_error_dma_alloc; + } + + if (drv_data->ioaddr == SSP1_VIRT) { + DRCMRRXSSDR = DRCMR_MAPVLD + | drv_data->rx_channel; + DRCMRTXSSDR = DRCMR_MAPVLD + | drv_data->tx_channel; + } else if (drv_data->ioaddr == SSP2_VIRT) { + DRCMRRXSS2DR = DRCMR_MAPVLD + | drv_data->rx_channel; + DRCMRTXSS2DR = DRCMR_MAPVLD + | drv_data->tx_channel; + } else if (drv_data->ioaddr == SSP3_VIRT) { + DRCMRRXSS3DR = DRCMR_MAPVLD + | drv_data->rx_channel; + DRCMRTXSS3DR = DRCMR_MAPVLD + | drv_data->tx_channel; + } else { + dev_err(dev, "bad SSP type\n"); + goto out_error_dma_alloc; + } + } + + /* Enable SOC clock */ + pxa_set_cken(platform_info->clock_enable, 1); + + /* Load default SSP configuration */ + write_SSCR0(0, drv_data->ioaddr); + write_SSCR1(SSCR1_RxTresh(4) | SSCR1_TxTresh(12), drv_data->ioaddr); + write_SSCR0(SSCR0_SerClkDiv(2) + | SSCR0_Motorola + | SSCR0_DataSize(8), + drv_data->ioaddr); + if (drv_data->ssp_type != PXA25x_SSP) + write_SSTO(0, drv_data->ioaddr); + write_SSPSP(0, drv_data->ioaddr); + + /* Initial and start queue */ + status = init_queue(drv_data); + if (status != 0) { + dev_err(&pdev->dev, "problem initializing queue\n"); + goto out_error_clock_enabled; + } + status = start_queue(drv_data); + if (status != 0) { + dev_err(&pdev->dev, "problem starting queue\n"); + goto out_error_clock_enabled; + } + + /* Register with the SPI framework */ + platform_set_drvdata(pdev, drv_data); + status = spi_register_master(master); + if (status != 0) { + dev_err(&pdev->dev, "problem registering spi master\n"); + goto out_error_queue_alloc; + } + + return status; + +out_error_queue_alloc: + destroy_queue(drv_data); + +out_error_clock_enabled: + pxa_set_cken(platform_info->clock_enable, 0); + +out_error_dma_alloc: + if (drv_data->tx_channel != -1) + pxa_free_dma(drv_data->tx_channel); + if (drv_data->rx_channel != -1) + pxa_free_dma(drv_data->rx_channel); + +out_error_irq_alloc: + free_irq(irq, drv_data); + +out_error_master_alloc: + spi_master_put(master); + return status; +} + +static int pxa2xx_spi_remove(struct platform_device *pdev) +{ + struct driver_data *drv_data = platform_get_drvdata(pdev); + int irq; + int status = 0; + + if (!drv_data) + return 0; + + /* Remove the queue */ + status = destroy_queue(drv_data); + if (status != 0) + return status; + + /* Disable the SSP at the peripheral and SOC level */ + write_SSCR0(0, drv_data->ioaddr); + pxa_set_cken(drv_data->master_info->clock_enable, 0); + + /* Release DMA */ + if (drv_data->master_info->enable_dma) { + if (drv_data->ioaddr == SSP1_VIRT) { + DRCMRRXSSDR = 0; + DRCMRTXSSDR = 0; + } else if (drv_data->ioaddr == SSP2_VIRT) { + DRCMRRXSS2DR = 0; + DRCMRTXSS2DR = 0; + } else if (drv_data->ioaddr == SSP3_VIRT) { + DRCMRRXSS3DR = 0; + DRCMRTXSS3DR = 0; + } + pxa_free_dma(drv_data->tx_channel); + pxa_free_dma(drv_data->rx_channel); + } + + /* Release IRQ */ + irq = platform_get_irq(pdev, 0); + if (irq >= 0) + free_irq(irq, drv_data); + + /* Disconnect from the SPI framework */ + spi_unregister_master(drv_data->master); + + /* Prevent double remove */ + platform_set_drvdata(pdev, NULL); + + return 0; +} + +static void pxa2xx_spi_shutdown(struct platform_device *pdev) +{ + int status = 0; + + if ((status = pxa2xx_spi_remove(pdev)) != 0) + dev_err(&pdev->dev, "shutdown failed with %d\n", status); +} + +#ifdef CONFIG_PM +static int suspend_devices(struct device *dev, void *pm_message) +{ + pm_message_t *state = pm_message; + + if (dev->power.power_state.event != state->event) { + dev_warn(dev, "pm state does not match request\n"); + return -1; + } + + return 0; +} + +static int pxa2xx_spi_suspend(struct platform_device *pdev, pm_message_t state) +{ + struct driver_data *drv_data = platform_get_drvdata(pdev); + int status = 0; + + /* Check all childern for current power state */ + if (device_for_each_child(&pdev->dev, &state, suspend_devices) != 0) { + dev_warn(&pdev->dev, "suspend aborted\n"); + return -1; + } + + status = stop_queue(drv_data); + if (status != 0) + return status; + write_SSCR0(0, drv_data->ioaddr); + pxa_set_cken(drv_data->master_info->clock_enable, 0); + + return 0; +} + +static int pxa2xx_spi_resume(struct platform_device *pdev) +{ + struct driver_data *drv_data = platform_get_drvdata(pdev); + int status = 0; + + /* Enable the SSP clock */ + pxa_set_cken(drv_data->master_info->clock_enable, 1); + + /* Start the queue running */ + status = start_queue(drv_data); + if (status != 0) { + dev_err(&pdev->dev, "problem starting queue (%d)\n", status); + return status; + } + + return 0; +} +#else +#define pxa2xx_spi_suspend NULL +#define pxa2xx_spi_resume NULL +#endif /* CONFIG_PM */ + +static struct platform_driver driver = { + .driver = { + .name = "pxa2xx-spi", + .bus = &platform_bus_type, + .owner = THIS_MODULE, + }, + .probe = pxa2xx_spi_probe, + .remove = __devexit_p(pxa2xx_spi_remove), + .shutdown = pxa2xx_spi_shutdown, + .suspend = pxa2xx_spi_suspend, + .resume = pxa2xx_spi_resume, +}; + +static int __init pxa2xx_spi_init(void) +{ + platform_driver_register(&driver); + + return 0; +} +module_init(pxa2xx_spi_init); + +static void __exit pxa2xx_spi_exit(void) +{ + platform_driver_unregister(&driver); +} +module_exit(pxa2xx_spi_exit); diff --git a/include/asm-arm/arch-pxa/pxa2xx_spi.h b/include/asm-arm/arch-pxa/pxa2xx_spi.h new file mode 100644 index 000000000000..1e70908b816f --- /dev/null +++ b/include/asm-arm/arch-pxa/pxa2xx_spi.h @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2005 Stephen Street / StreetFire Sound Labs + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef PXA2XX_SPI_H_ +#define PXA2XX_SPI_H_ + +#define PXA2XX_CS_ASSERT (0x01) +#define PXA2XX_CS_DEASSERT (0x02) + +#if defined(CONFIG_PXA25x) +#define CLOCK_SPEED_HZ 3686400 +#define SSP1_SerClkDiv(x) (((CLOCK_SPEED_HZ/2/(x+1))<<8)&0x0000ff00) +#define SSP2_SerClkDiv(x) (((CLOCK_SPEED_HZ/(x+1))<<8)&0x000fff00) +#define SSP3_SerClkDiv(x) (((CLOCK_SPEED_HZ/(x+1))<<8)&0x000fff00) +#elif defined(CONFIG_PXA27x) +#define CLOCK_SPEED_HZ 13000000 +#define SSP1_SerClkDiv(x) (((CLOCK_SPEED_HZ/(x+1))<<8)&0x000fff00) +#define SSP2_SerClkDiv(x) (((CLOCK_SPEED_HZ/(x+1))<<8)&0x000fff00) +#define SSP3_SerClkDiv(x) (((CLOCK_SPEED_HZ/(x+1))<<8)&0x000fff00) +#endif + +#define SSP1_VIRT ((void *)(io_p2v(__PREG(SSCR0_P(1))))) +#define SSP2_VIRT ((void *)(io_p2v(__PREG(SSCR0_P(2))))) +#define SSP3_VIRT ((void *)(io_p2v(__PREG(SSCR0_P(3))))) + +enum pxa_ssp_type { + SSP_UNDEFINED = 0, + PXA25x_SSP, /* pxa 210, 250, 255, 26x */ + PXA25x_NSSP, /* pxa 255, 26x (including ASSP) */ + PXA27x_SSP, +}; + +/* device.platform_data for SSP controller devices */ +struct pxa2xx_spi_master { + enum pxa_ssp_type ssp_type; + u32 clock_enable; + u16 num_chipselect; + u8 enable_dma; +}; + +/* spi_board_info.controller_data for SPI slave devices, + * copied to spi_device.platform_data ... mostly for dma tuning + */ +struct pxa2xx_spi_chip { + u8 tx_threshold; + u8 rx_threshold; + u8 dma_burst_size; + u32 timeout_microsecs; + u8 enable_loopback; + void (*cs_control)(u32 command); +}; + +#endif /*PXA2XX_SPI_H_*/ -- cgit v1.2.3 From 747d844ee9a183ff3067bb1181f2a25c50649538 Mon Sep 17 00:00:00 2001 From: David Brownell Date: Sun, 2 Apr 2006 10:33:37 -0800 Subject: [PATCH] SPI: spi whitespace fixes This removes superfluous whitespace in the header. Signed-off-by: David Brownell Signed-off-by: Greg Kroah-Hartman --- include/linux/spi/spi.h | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) (limited to 'include') diff --git a/include/linux/spi/spi.h b/include/linux/spi/spi.h index caa4665e3fa2..082006714b85 100644 --- a/include/linux/spi/spi.h +++ b/include/linux/spi/spi.h @@ -36,15 +36,15 @@ extern struct bus_type spi_bus_type; * @mode: The spi mode defines how data is clocked out and in. * This may be changed by the device's driver. * @bits_per_word: Data transfers involve one or more words; word sizes - * like eight or 12 bits are common. In-memory wordsizes are + * like eight or 12 bits are common. In-memory wordsizes are * powers of two bytes (e.g. 20 bit samples use 32 bits). * This may be changed by the device's driver. * The spi_transfer.bits_per_word can override this for each transfer. * @irq: Negative, or the number passed to request_irq() to receive - * interrupts from this device. + * interrupts from this device. * @controller_state: Controller's runtime state * @controller_data: Board-specific definitions for controller, such as - * FIFO initialization parameters; from board_info.controller_data + * FIFO initialization parameters; from board_info.controller_data * * An spi_device is used to interchange data between an SPI slave * (usually a discrete chip) and CPU memory. @@ -145,13 +145,13 @@ static inline void spi_unregister_driver(struct spi_driver *sdrv) * struct spi_master - interface to SPI master controller * @cdev: class interface to this driver * @bus_num: board-specific (and often SOC-specific) identifier for a - * given SPI controller. + * given SPI controller. * @num_chipselect: chipselects are used to distinguish individual - * SPI slaves, and are numbered from zero to num_chipselects. - * each slave has a chipselect signal, but it's common that not - * every chipselect is connected to a slave. + * SPI slaves, and are numbered from zero to num_chipselects. + * each slave has a chipselect signal, but it's common that not + * every chipselect is connected to a slave. * @setup: updates the device mode and clocking records used by a - * device's SPI controller; protocol code may call this. + * device's SPI controller; protocol code may call this. * @transfer: adds a message to the controller's transfer queue. * @cleanup: frees controller-specific state * @@ -276,8 +276,8 @@ extern struct spi_master *spi_busnum_to_master(u16 busnum); * for this transfer. If 0 the default (from spi_device) is used. * @cs_change: affects chipselect after this transfer completes * @delay_usecs: microseconds to delay after this transfer before - * (optionally) changing the chipselect status, then starting - * the next transfer or completing this spi_message. + * (optionally) changing the chipselect status, then starting + * the next transfer or completing this spi_message. * @transfer_list: transfers are sequenced through spi_message.transfers * * SPI transfers always write the same number of bytes as they read. @@ -364,7 +364,7 @@ struct spi_transfer { * and its transfers, ignore them until its completion callback. */ struct spi_message { - struct list_head transfers; + struct list_head transfers; struct spi_device *spi; @@ -382,7 +382,7 @@ struct spi_message { */ /* completion is reported through a callback */ - void (*complete)(void *context); + void (*complete)(void *context); void *context; unsigned actual_length; int status; -- cgit v1.2.3 From ff9f4771b5f017ee0f57629488b6cd7a6ef3d19b Mon Sep 17 00:00:00 2001 From: Kumar Gala Date: Sun, 2 Apr 2006 16:06:35 -0500 Subject: [PATCH] SPI: Renamed bitbang_transfer_setup to spi_bitbang_setup_transfer and export it Renamed bitbang_transfer_setup to follow convention of other exported symbols from spi-bitbang. Exported spi_bitbang_setup_transfer to allow users of spi-bitbang to use the function in their own setup_transfer. Signed-off-by: Kumar Gala Cc: David Brownell Signed-off-by: Greg Kroah-Hartman --- drivers/spi/spi_bitbang.c | 10 ++++++---- include/linux/spi/spi_bitbang.h | 2 ++ 2 files changed, 8 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/drivers/spi/spi_bitbang.c b/drivers/spi/spi_bitbang.c index 0525c99118c3..6c3da64d609a 100644 --- a/drivers/spi/spi_bitbang.c +++ b/drivers/spi/spi_bitbang.c @@ -138,8 +138,7 @@ static unsigned bitbang_txrx_32( return t->len - count; } -static int -bitbang_transfer_setup(struct spi_device *spi, struct spi_transfer *t) +int spi_bitbang_setup_transfer(struct spi_device *spi, struct spi_transfer *t) { struct spi_bitbang_cs *cs = spi->controller_state; u8 bits_per_word; @@ -174,6 +173,7 @@ bitbang_transfer_setup(struct spi_device *spi, struct spi_transfer *t) return 0; } +EXPORT_SYMBOL_GPL(spi_bitbang_setup_transfer); /** * spi_bitbang_setup - default setup for per-word I/O loops @@ -203,7 +203,7 @@ int spi_bitbang_setup(struct spi_device *spi) if (!cs->txrx_word) return -EINVAL; - retval = bitbang_transfer_setup(spi, NULL); + retval = spi_bitbang_setup_transfer(spi, NULL); if (retval < 0) return retval; @@ -454,7 +454,9 @@ int spi_bitbang_start(struct spi_bitbang *bitbang) bitbang->use_dma = 0; bitbang->txrx_bufs = spi_bitbang_bufs; if (!bitbang->master->setup) { - bitbang->setup_transfer = bitbang_transfer_setup; + if (!bitbang->setup_transfer) + bitbang->setup_transfer = + spi_bitbang_setup_transfer; bitbang->master->setup = spi_bitbang_setup; bitbang->master->cleanup = spi_bitbang_cleanup; } diff --git a/include/linux/spi/spi_bitbang.h b/include/linux/spi/spi_bitbang.h index c954557b757c..16ce178f54d7 100644 --- a/include/linux/spi/spi_bitbang.h +++ b/include/linux/spi/spi_bitbang.h @@ -57,6 +57,8 @@ struct spi_bitbang { extern int spi_bitbang_setup(struct spi_device *spi); extern void spi_bitbang_cleanup(const struct spi_device *spi); extern int spi_bitbang_transfer(struct spi_device *spi, struct spi_message *m); +extern int spi_bitbang_setup_transfer(struct spi_device *spi, + struct spi_transfer *t); /* start or stop queue processing */ extern int spi_bitbang_start(struct spi_bitbang *spi); -- cgit v1.2.3 From ccf77cc4af5b048e20cfd9327fcc286cb69c34cc Mon Sep 17 00:00:00 2001 From: David Brownell Date: Mon, 3 Apr 2006 15:46:22 -0700 Subject: [PATCH] SPI: devices can require LSB-first encodings Add spi_device hook for LSB-first word encoding, and update all the (in-tree) controller drivers to reject such devices. Eventually, some controller drivers will be updated to support lsb-first encodings on the wire; no current drivers need this. Signed-off-by: David Brownell Signed-off-by: Greg Kroah-Hartman --- drivers/spi/spi_bitbang.c | 11 ++++++++++- include/linux/spi/spi.h | 7 +++++-- 2 files changed, 15 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/drivers/spi/spi_bitbang.c b/drivers/spi/spi_bitbang.c index 6c3da64d609a..0f7f5c64391c 100644 --- a/drivers/spi/spi_bitbang.c +++ b/drivers/spi/spi_bitbang.c @@ -187,13 +187,22 @@ int spi_bitbang_setup(struct spi_device *spi) if (!spi->max_speed_hz) return -EINVAL; + bitbang = spi_master_get_devdata(spi->master); + + /* REVISIT: some systems will want to support devices using lsb-first + * bit encodings on the wire. In pure software that would be trivial, + * just bitbang_txrx_le_cphaX() routines shifting the other way, and + * some hardware controllers also have this support. + */ + if ((spi->mode & SPI_LSB_FIRST) != 0) + return -EINVAL; + if (!cs) { cs = kzalloc(sizeof *cs, SLAB_KERNEL); if (!cs) return -ENOMEM; spi->controller_state = cs; } - bitbang = spi_master_get_devdata(spi->master); if (!spi->bits_per_word) spi->bits_per_word = 8; diff --git a/include/linux/spi/spi.h b/include/linux/spi/spi.h index 082006714b85..77add901691d 100644 --- a/include/linux/spi/spi.h +++ b/include/linux/spi/spi.h @@ -35,10 +35,13 @@ extern struct bus_type spi_bus_type; * @chip-select: Chipselect, distinguishing chips handled by "master". * @mode: The spi mode defines how data is clocked out and in. * This may be changed by the device's driver. + * The "active low" default for chipselect mode can be overridden, + * as can the "MSB first" default for each word in a transfer. * @bits_per_word: Data transfers involve one or more words; word sizes * like eight or 12 bits are common. In-memory wordsizes are * powers of two bytes (e.g. 20 bit samples use 32 bits). - * This may be changed by the device's driver. + * This may be changed by the device's driver, or left at the + * default (0) indicating protocol words are eight bit bytes. * The spi_transfer.bits_per_word can override this for each transfer. * @irq: Negative, or the number passed to request_irq() to receive * interrupts from this device. @@ -67,6 +70,7 @@ struct spi_device { #define SPI_MODE_2 (SPI_CPOL|0) #define SPI_MODE_3 (SPI_CPOL|SPI_CPHA) #define SPI_CS_HIGH 0x04 /* chipselect active high? */ +#define SPI_LSB_FIRST 0x08 /* per-word bits-on-wire */ u8 bits_per_word; int irq; void *controller_state; @@ -75,7 +79,6 @@ struct spi_device { // likely need more hooks for more protocol options affecting how // the controller talks to each chip, like: - // - bit order (default is wordwise msb-first) // - memory packing (12 bit samples into low bits, others zeroed) // - priority // - drop chipselect after each word -- cgit v1.2.3 From a020ed7521a9737bcf3e34eb880867c60c3c68d0 Mon Sep 17 00:00:00 2001 From: David Brownell Date: Mon, 3 Apr 2006 15:49:04 -0700 Subject: [PATCH] SPI: busnum == 0 needs to work We need to be able to have a "SPI bus 0" matching chip numbering; but that number was wrongly used to flag dynamic allocation of a bus number. This patch resolves that issue; now negative numbers trigger dynamic alloc. It also updates the how-to-write-a-controller-driver overview to mention this stuff. Signed-off-by: David Brownell Signed-off-by: Greg Kroah-Hartman --- Documentation/spi/spi-summary | 34 +++++++++++++++++++++++++++++++++- drivers/spi/spi.c | 4 ++-- include/linux/spi/spi.h | 6 +++--- 3 files changed, 38 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/Documentation/spi/spi-summary b/Documentation/spi/spi-summary index a5ffba33a351..068732d32276 100644 --- a/Documentation/spi/spi-summary +++ b/Documentation/spi/spi-summary @@ -414,7 +414,33 @@ to get the driver-private data allocated for that device. The driver will initialize the fields of that spi_master, including the bus number (maybe the same as the platform device ID) and three methods used to interact with the SPI core and SPI protocol drivers. It will -also initialize its own internal state. +also initialize its own internal state. (See below about bus numbering +and those methods.) + +After you initialize the spi_master, then use spi_register_master() to +publish it to the rest of the system. At that time, device nodes for +the controller and any predeclared spi devices will be made available, +and the driver model core will take care of binding them to drivers. + +If you need to remove your SPI controller driver, spi_unregister_master() +will reverse the effect of spi_register_master(). + + +BUS NUMBERING + +Bus numbering is important, since that's how Linux identifies a given +SPI bus (shared SCK, MOSI, MISO). Valid bus numbers start at zero. On +SOC systems, the bus numbers should match the numbers defined by the chip +manufacturer. For example, hardware controller SPI2 would be bus number 2, +and spi_board_info for devices connected to it would use that number. + +If you don't have such hardware-assigned bus number, and for some reason +you can't just assign them, then provide a negative bus number. That will +then be replaced by a dynamically assigned number. You'd then need to treat +this as a non-static configuration (see above). + + +SPI MASTER METHODS master->setup(struct spi_device *spi) This sets up the device clock rate, SPI mode, and word sizes. @@ -431,6 +457,9 @@ also initialize its own internal state. state it dynamically associates with that device. If you do that, be sure to provide the cleanup() method to free that state. + +SPI MESSAGE QUEUE + The bulk of the driver will be managing the I/O queue fed by transfer(). That queue could be purely conceptual. For example, a driver used only @@ -440,6 +469,9 @@ But the queue will probably be very real, using message->queue, PIO, often DMA (especially if the root filesystem is in SPI flash), and execution contexts like IRQ handlers, tasklets, or workqueues (such as keventd). Your driver can be as fancy, or as simple, as you need. +Such a transfer() method would normally just add the message to a +queue, and then start some asynchronous transfer engine (unless it's +already running). THANKS TO diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c index 1168ef015887..7a3f733051e9 100644 --- a/drivers/spi/spi.c +++ b/drivers/spi/spi.c @@ -395,7 +395,7 @@ EXPORT_SYMBOL_GPL(spi_alloc_master); int __init_or_module spi_register_master(struct spi_master *master) { - static atomic_t dyn_bus_id = ATOMIC_INIT(0); + static atomic_t dyn_bus_id = ATOMIC_INIT((1<<16) - 1); struct device *dev = master->cdev.dev; int status = -ENODEV; int dynamic = 0; @@ -404,7 +404,7 @@ spi_register_master(struct spi_master *master) return -ENODEV; /* convention: dynamically assigned bus IDs count down from the max */ - if (master->bus_num == 0) { + if (master->bus_num < 0) { master->bus_num = atomic_dec_return(&dyn_bus_id); dynamic = 1; } diff --git a/include/linux/spi/spi.h b/include/linux/spi/spi.h index 77add901691d..e928c0dcc297 100644 --- a/include/linux/spi/spi.h +++ b/include/linux/spi/spi.h @@ -172,13 +172,13 @@ static inline void spi_unregister_driver(struct spi_driver *sdrv) struct spi_master { struct class_device cdev; - /* other than zero (== assign one dynamically), bus_num is fully + /* other than negative (== assign one dynamically), bus_num is fully * board-specific. usually that simplifies to being SOC-specific. - * example: one SOC has three SPI controllers, numbered 1..3, + * example: one SOC has three SPI controllers, numbered 0..2, * and one board's schematics might show it using SPI-2. software * would normally use bus_num=2 for that controller. */ - u16 bus_num; + s16 bus_num; /* chipselects will be integral to many controllers; some others * might use board-specific GPIOs. -- cgit v1.2.3 From 8de8c8738086501bbe3057ed6f4b70dded657488 Mon Sep 17 00:00:00 2001 From: Sridhar Samudrala Date: Fri, 19 May 2006 10:58:12 -0700 Subject: [SCTP]: Set sk_err so that poll wakes up after a non-blocking connect failure. Also fix some other cases where sk_err is not set for 1-1 style sockets. Signed-off-by: Sridhar Samudrala --- include/net/sctp/command.h | 1 + net/sctp/input.c | 4 +-- net/sctp/sm_sideeffect.c | 16 +++++++--- net/sctp/sm_statefuns.c | 75 +++++++++++++++++++++++++++++----------------- net/sctp/socket.c | 1 + 5 files changed, 64 insertions(+), 33 deletions(-) (limited to 'include') diff --git a/include/net/sctp/command.h b/include/net/sctp/command.h index 34a1a09e5aef..807d6f1ef4b5 100644 --- a/include/net/sctp/command.h +++ b/include/net/sctp/command.h @@ -99,6 +99,7 @@ typedef enum { SCTP_CMD_DEL_NON_PRIMARY, /* Removes non-primary peer transports. */ SCTP_CMD_T3_RTX_TIMERS_STOP, /* Stops T3-rtx pending timers */ SCTP_CMD_FORCE_PRIM_RETRAN, /* Forces retrans. over primary path. */ + SCTP_CMD_SET_SK_ERR, /* Set sk_err */ SCTP_CMD_LAST } sctp_verb_t; diff --git a/net/sctp/input.c b/net/sctp/input.c index d117ebc75cf8..7523f4df2da6 100644 --- a/net/sctp/input.c +++ b/net/sctp/input.c @@ -412,7 +412,7 @@ struct sock *sctp_err_lookup(int family, struct sk_buff *skb, union sctp_addr daddr; struct sctp_af *af; struct sock *sk = NULL; - struct sctp_association *asoc = NULL; + struct sctp_association *asoc; struct sctp_transport *transport = NULL; *app = NULL; *tpp = NULL; @@ -490,7 +490,7 @@ void sctp_v4_err(struct sk_buff *skb, __u32 info) int type = skb->h.icmph->type; int code = skb->h.icmph->code; struct sock *sk; - struct sctp_association *asoc; + struct sctp_association *asoc = NULL; struct sctp_transport *transport; struct inet_sock *inet; char *saveip, *savesctp; diff --git a/net/sctp/sm_sideeffect.c b/net/sctp/sm_sideeffect.c index 8d1dc24bab4c..c5beb2ad7ef7 100644 --- a/net/sctp/sm_sideeffect.c +++ b/net/sctp/sm_sideeffect.c @@ -498,10 +498,6 @@ static void sctp_cmd_assoc_failed(sctp_cmd_seq_t *commands, sctp_add_cmd_sf(commands, SCTP_CMD_NEW_STATE, SCTP_STATE(SCTP_STATE_CLOSED)); - /* Set sk_err to ECONNRESET on a 1-1 style socket. */ - if (!sctp_style(asoc->base.sk, UDP)) - asoc->base.sk->sk_err = ECONNRESET; - /* SEND_FAILED sent later when cleaning up the association. */ asoc->outqueue.error = error; sctp_add_cmd_sf(commands, SCTP_CMD_DELETE_TCB, SCTP_NULL()); @@ -838,6 +834,15 @@ static void sctp_cmd_del_non_primary(struct sctp_association *asoc) return; } +/* Helper function to set sk_err on a 1-1 style socket. */ +static void sctp_cmd_set_sk_err(struct sctp_association *asoc, int error) +{ + struct sock *sk = asoc->base.sk; + + if (!sctp_style(sk, UDP)) + sk->sk_err = error; +} + /* These three macros allow us to pull the debugging code out of the * main flow of sctp_do_sm() to keep attention focused on the real * functionality there. @@ -1458,6 +1463,9 @@ static int sctp_cmd_interpreter(sctp_event_t event_type, local_cork = 0; asoc->peer.retran_path = t; break; + case SCTP_CMD_SET_SK_ERR: + sctp_cmd_set_sk_err(asoc, cmd->obj.error); + break; default: printk(KERN_WARNING "Impossible command: %u, %p\n", cmd->verb, cmd->obj.ptr); diff --git a/net/sctp/sm_statefuns.c b/net/sctp/sm_statefuns.c index 8cdba51ec076..174f7a7c6cd1 100644 --- a/net/sctp/sm_statefuns.c +++ b/net/sctp/sm_statefuns.c @@ -93,7 +93,7 @@ static sctp_disposition_t sctp_sf_shut_8_4_5(const struct sctp_endpoint *ep, static struct sctp_sackhdr *sctp_sm_pull_sack(struct sctp_chunk *chunk); static sctp_disposition_t sctp_stop_t1_and_abort(sctp_cmd_seq_t *commands, - __u16 error, + __u16 error, int sk_err, const struct sctp_association *asoc, struct sctp_transport *transport); @@ -448,7 +448,7 @@ sctp_disposition_t sctp_sf_do_5_1C_ack(const struct sctp_endpoint *ep, __u32 init_tag; struct sctp_chunk *err_chunk; struct sctp_packet *packet; - sctp_disposition_t ret; + __u16 error; if (!sctp_vtag_verify(chunk, asoc)) return sctp_sf_pdiscard(ep, asoc, type, arg, commands); @@ -480,11 +480,9 @@ sctp_disposition_t sctp_sf_do_5_1C_ack(const struct sctp_endpoint *ep, goto nomem; sctp_add_cmd_sf(commands, SCTP_CMD_REPLY, SCTP_CHUNK(reply)); - sctp_add_cmd_sf(commands, SCTP_CMD_NEW_STATE, - SCTP_STATE(SCTP_STATE_CLOSED)); - SCTP_INC_STATS(SCTP_MIB_ABORTEDS); - sctp_add_cmd_sf(commands, SCTP_CMD_DELETE_TCB, SCTP_NULL()); - return SCTP_DISPOSITION_DELETE_TCB; + return sctp_stop_t1_and_abort(commands, SCTP_ERROR_INV_PARAM, + ECONNREFUSED, asoc, + chunk->transport); } /* Verify the INIT chunk before processing it. */ @@ -511,27 +509,16 @@ sctp_disposition_t sctp_sf_do_5_1C_ack(const struct sctp_endpoint *ep, sctp_add_cmd_sf(commands, SCTP_CMD_SEND_PKT, SCTP_PACKET(packet)); SCTP_INC_STATS(SCTP_MIB_OUTCTRLCHUNKS); - sctp_add_cmd_sf(commands, SCTP_CMD_NEW_STATE, - SCTP_STATE(SCTP_STATE_CLOSED)); - sctp_add_cmd_sf(commands, SCTP_CMD_DELETE_TCB, - SCTP_NULL()); - return SCTP_DISPOSITION_CONSUME; + error = SCTP_ERROR_INV_PARAM; } else { - sctp_add_cmd_sf(commands, SCTP_CMD_NEW_STATE, - SCTP_STATE(SCTP_STATE_CLOSED)); - sctp_add_cmd_sf(commands, SCTP_CMD_DELETE_TCB, - SCTP_NULL()); - return SCTP_DISPOSITION_NOMEM; + error = SCTP_ERROR_NO_RESOURCE; } } else { - ret = sctp_sf_tabort_8_4_8(ep, asoc, type, arg, - commands); - sctp_add_cmd_sf(commands, SCTP_CMD_NEW_STATE, - SCTP_STATE(SCTP_STATE_CLOSED)); - sctp_add_cmd_sf(commands, SCTP_CMD_DELETE_TCB, - SCTP_NULL()); - return ret; + sctp_sf_tabort_8_4_8(ep, asoc, type, arg, commands); + error = SCTP_ERROR_INV_PARAM; } + return sctp_stop_t1_and_abort(commands, error, ECONNREFUSED, + asoc, chunk->transport); } /* Tag the variable length parameters. Note that we never @@ -886,6 +873,8 @@ sctp_disposition_t sctp_sf_sendbeat_8_3(const struct sctp_endpoint *ep, struct sctp_transport *transport = (struct sctp_transport *) arg; if (asoc->overall_error_count >= asoc->max_retrans) { + sctp_add_cmd_sf(commands, SCTP_CMD_SET_SK_ERR, + SCTP_ERROR(ETIMEDOUT)); /* CMD_ASSOC_FAILED calls CMD_DELETE_TCB. */ sctp_add_cmd_sf(commands, SCTP_CMD_ASSOC_FAILED, SCTP_U32(SCTP_ERROR_NO_ERROR)); @@ -2126,6 +2115,8 @@ static sctp_disposition_t sctp_sf_do_5_2_6_stale(const struct sctp_endpoint *ep, int attempts = asoc->init_err_counter + 1; if (attempts > asoc->max_init_attempts) { + sctp_add_cmd_sf(commands, SCTP_CMD_SET_SK_ERR, + SCTP_ERROR(ETIMEDOUT)); sctp_add_cmd_sf(commands, SCTP_CMD_INIT_FAILED, SCTP_U32(SCTP_ERROR_STALE_COOKIE)); return SCTP_DISPOSITION_DELETE_TCB; @@ -2262,6 +2253,7 @@ sctp_disposition_t sctp_sf_do_9_1_abort(const struct sctp_endpoint *ep, if (len >= sizeof(struct sctp_chunkhdr) + sizeof(struct sctp_errhdr)) error = ((sctp_errhdr_t *)chunk->skb->data)->cause; + sctp_add_cmd_sf(commands, SCTP_CMD_SET_SK_ERR, SCTP_ERROR(ECONNRESET)); /* ASSOC_FAILED will DELETE_TCB. */ sctp_add_cmd_sf(commands, SCTP_CMD_ASSOC_FAILED, SCTP_U32(error)); SCTP_INC_STATS(SCTP_MIB_ABORTEDS); @@ -2306,7 +2298,8 @@ sctp_disposition_t sctp_sf_cookie_wait_abort(const struct sctp_endpoint *ep, if (len >= sizeof(struct sctp_chunkhdr) + sizeof(struct sctp_errhdr)) error = ((sctp_errhdr_t *)chunk->skb->data)->cause; - return sctp_stop_t1_and_abort(commands, error, asoc, chunk->transport); + return sctp_stop_t1_and_abort(commands, error, ECONNREFUSED, asoc, + chunk->transport); } /* @@ -2318,7 +2311,8 @@ sctp_disposition_t sctp_sf_cookie_wait_icmp_abort(const struct sctp_endpoint *ep void *arg, sctp_cmd_seq_t *commands) { - return sctp_stop_t1_and_abort(commands, SCTP_ERROR_NO_ERROR, asoc, + return sctp_stop_t1_and_abort(commands, SCTP_ERROR_NO_ERROR, + ENOPROTOOPT, asoc, (struct sctp_transport *)arg); } @@ -2343,7 +2337,7 @@ sctp_disposition_t sctp_sf_cookie_echoed_abort(const struct sctp_endpoint *ep, * This is common code called by several sctp_sf_*_abort() functions above. */ static sctp_disposition_t sctp_stop_t1_and_abort(sctp_cmd_seq_t *commands, - __u16 error, + __u16 error, int sk_err, const struct sctp_association *asoc, struct sctp_transport *transport) { @@ -2353,6 +2347,7 @@ static sctp_disposition_t sctp_stop_t1_and_abort(sctp_cmd_seq_t *commands, SCTP_INC_STATS(SCTP_MIB_ABORTEDS); sctp_add_cmd_sf(commands, SCTP_CMD_TIMER_STOP, SCTP_TO(SCTP_EVENT_TIMEOUT_T1_INIT)); + sctp_add_cmd_sf(commands, SCTP_CMD_SET_SK_ERR, SCTP_ERROR(sk_err)); /* CMD_INIT_FAILED will DELETE_TCB. */ sctp_add_cmd_sf(commands, SCTP_CMD_INIT_FAILED, SCTP_U32(error)); @@ -3336,6 +3331,8 @@ sctp_disposition_t sctp_sf_do_asconf_ack(const struct sctp_endpoint *ep, sctp_add_cmd_sf(commands, SCTP_CMD_TIMER_STOP, SCTP_TO(SCTP_EVENT_TIMEOUT_T4_RTO)); sctp_add_cmd_sf(commands, SCTP_CMD_DISCARD_PACKET,SCTP_NULL()); + sctp_add_cmd_sf(commands, SCTP_CMD_SET_SK_ERR, + SCTP_ERROR(ECONNABORTED)); sctp_add_cmd_sf(commands, SCTP_CMD_ASSOC_FAILED, SCTP_U32(SCTP_ERROR_ASCONF_ACK)); SCTP_INC_STATS(SCTP_MIB_ABORTEDS); @@ -3362,6 +3359,8 @@ sctp_disposition_t sctp_sf_do_asconf_ack(const struct sctp_endpoint *ep, * processing the rest of the chunks in the packet. */ sctp_add_cmd_sf(commands, SCTP_CMD_DISCARD_PACKET,SCTP_NULL()); + sctp_add_cmd_sf(commands, SCTP_CMD_SET_SK_ERR, + SCTP_ERROR(ECONNABORTED)); sctp_add_cmd_sf(commands, SCTP_CMD_ASSOC_FAILED, SCTP_U32(SCTP_ERROR_ASCONF_ACK)); SCTP_INC_STATS(SCTP_MIB_ABORTEDS); @@ -3714,9 +3713,13 @@ static sctp_disposition_t sctp_sf_violation_chunklen( if (asoc->state <= SCTP_STATE_COOKIE_ECHOED) { sctp_add_cmd_sf(commands, SCTP_CMD_TIMER_STOP, SCTP_TO(SCTP_EVENT_TIMEOUT_T1_INIT)); + sctp_add_cmd_sf(commands, SCTP_CMD_SET_SK_ERR, + SCTP_ERROR(ECONNREFUSED)); sctp_add_cmd_sf(commands, SCTP_CMD_INIT_FAILED, SCTP_U32(SCTP_ERROR_PROTO_VIOLATION)); } else { + sctp_add_cmd_sf(commands, SCTP_CMD_SET_SK_ERR, + SCTP_ERROR(ECONNABORTED)); sctp_add_cmd_sf(commands, SCTP_CMD_ASSOC_FAILED, SCTP_U32(SCTP_ERROR_PROTO_VIOLATION)); SCTP_DEC_STATS(SCTP_MIB_CURRESTAB); @@ -4034,6 +4037,8 @@ sctp_disposition_t sctp_sf_do_9_1_prm_abort( * TCB. This is a departure from our typical NOMEM handling. */ + sctp_add_cmd_sf(commands, SCTP_CMD_SET_SK_ERR, + SCTP_ERROR(ECONNABORTED)); /* Delete the established association. */ sctp_add_cmd_sf(commands, SCTP_CMD_ASSOC_FAILED, SCTP_U32(SCTP_ERROR_USER_ABORT)); @@ -4175,6 +4180,8 @@ sctp_disposition_t sctp_sf_cookie_wait_prm_abort( * TCB. This is a departure from our typical NOMEM handling. */ + sctp_add_cmd_sf(commands, SCTP_CMD_SET_SK_ERR, + SCTP_ERROR(ECONNREFUSED)); /* Delete the established association. */ sctp_add_cmd_sf(commands, SCTP_CMD_INIT_FAILED, SCTP_U32(SCTP_ERROR_USER_ABORT)); @@ -4543,6 +4550,8 @@ sctp_disposition_t sctp_sf_do_6_3_3_rtx(const struct sctp_endpoint *ep, struct sctp_transport *transport = arg; if (asoc->overall_error_count >= asoc->max_retrans) { + sctp_add_cmd_sf(commands, SCTP_CMD_SET_SK_ERR, + SCTP_ERROR(ETIMEDOUT)); /* CMD_ASSOC_FAILED calls CMD_DELETE_TCB. */ sctp_add_cmd_sf(commands, SCTP_CMD_ASSOC_FAILED, SCTP_U32(SCTP_ERROR_NO_ERROR)); @@ -4662,6 +4671,8 @@ sctp_disposition_t sctp_sf_t1_init_timer_expire(const struct sctp_endpoint *ep, SCTP_DEBUG_PRINTK("Giving up on INIT, attempts: %d" " max_init_attempts: %d\n", attempts, asoc->max_init_attempts); + sctp_add_cmd_sf(commands, SCTP_CMD_SET_SK_ERR, + SCTP_ERROR(ETIMEDOUT)); sctp_add_cmd_sf(commands, SCTP_CMD_INIT_FAILED, SCTP_U32(SCTP_ERROR_NO_ERROR)); return SCTP_DISPOSITION_DELETE_TCB; @@ -4711,6 +4722,8 @@ sctp_disposition_t sctp_sf_t1_cookie_timer_expire(const struct sctp_endpoint *ep sctp_add_cmd_sf(commands, SCTP_CMD_REPLY, SCTP_CHUNK(repl)); } else { + sctp_add_cmd_sf(commands, SCTP_CMD_SET_SK_ERR, + SCTP_ERROR(ETIMEDOUT)); sctp_add_cmd_sf(commands, SCTP_CMD_INIT_FAILED, SCTP_U32(SCTP_ERROR_NO_ERROR)); return SCTP_DISPOSITION_DELETE_TCB; @@ -4742,6 +4755,8 @@ sctp_disposition_t sctp_sf_t2_timer_expire(const struct sctp_endpoint *ep, SCTP_DEBUG_PRINTK("Timer T2 expired.\n"); if (asoc->overall_error_count >= asoc->max_retrans) { + sctp_add_cmd_sf(commands, SCTP_CMD_SET_SK_ERR, + SCTP_ERROR(ETIMEDOUT)); /* Note: CMD_ASSOC_FAILED calls CMD_DELETE_TCB. */ sctp_add_cmd_sf(commands, SCTP_CMD_ASSOC_FAILED, SCTP_U32(SCTP_ERROR_NO_ERROR)); @@ -4817,6 +4832,8 @@ sctp_disposition_t sctp_sf_t4_timer_expire( if (asoc->overall_error_count >= asoc->max_retrans) { sctp_add_cmd_sf(commands, SCTP_CMD_TIMER_STOP, SCTP_TO(SCTP_EVENT_TIMEOUT_T4_RTO)); + sctp_add_cmd_sf(commands, SCTP_CMD_SET_SK_ERR, + SCTP_ERROR(ETIMEDOUT)); sctp_add_cmd_sf(commands, SCTP_CMD_ASSOC_FAILED, SCTP_U32(SCTP_ERROR_NO_ERROR)); SCTP_INC_STATS(SCTP_MIB_ABORTEDS); @@ -4870,6 +4887,8 @@ sctp_disposition_t sctp_sf_t5_timer_expire(const struct sctp_endpoint *ep, goto nomem; sctp_add_cmd_sf(commands, SCTP_CMD_REPLY, SCTP_CHUNK(reply)); + sctp_add_cmd_sf(commands, SCTP_CMD_SET_SK_ERR, + SCTP_ERROR(ETIMEDOUT)); sctp_add_cmd_sf(commands, SCTP_CMD_ASSOC_FAILED, SCTP_U32(SCTP_ERROR_NO_ERROR)); @@ -5309,6 +5328,8 @@ static int sctp_eat_data(const struct sctp_association *asoc, * processing the rest of the chunks in the packet. */ sctp_add_cmd_sf(commands, SCTP_CMD_DISCARD_PACKET,SCTP_NULL()); + sctp_add_cmd_sf(commands, SCTP_CMD_SET_SK_ERR, + SCTP_ERROR(ECONNABORTED)); sctp_add_cmd_sf(commands, SCTP_CMD_ASSOC_FAILED, SCTP_U32(SCTP_ERROR_NO_DATA)); SCTP_INC_STATS(SCTP_MIB_ABORTEDS); diff --git a/net/sctp/socket.c b/net/sctp/socket.c index b6e4b89539b3..90863307bcd9 100644 --- a/net/sctp/socket.c +++ b/net/sctp/socket.c @@ -1057,6 +1057,7 @@ static int __sctp_connect(struct sock* sk, inet_sk(sk)->dport = htons(asoc->peer.port); af = sctp_get_af_specific(to.sa.sa_family); af->to_sk_daddr(&to, sk); + sk->sk_err = 0; timeo = sock_sndtimeo(sk, sk->sk_socket->file->f_flags & O_NONBLOCK); err = sctp_wait_for_connect(asoc, &timeo); -- cgit v1.2.3 From dd2d1c6f2958d027e4591ca5d2a04dfe36ca6512 Mon Sep 17 00:00:00 2001 From: Vladislav Yasevich Date: Fri, 19 May 2006 11:52:20 -0700 Subject: [SCTP]: Respect the real chunk length when walking parameters. When performing bound checks during the parameter processing, we want to use the real chunk and paramter lengths for bounds instead of the rounded ones. This prevents us from potentially walking of the end if the chunk length was miscalculated. We still use rounded lengths when advancing the pointer. This was found during a conformance test that changed the chunk length without modifying parameters. Signed-off-by: Vlad Yasevich Signed-off-by: Sridhar Samudrala --- include/net/sctp/sctp.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/include/net/sctp/sctp.h b/include/net/sctp/sctp.h index e673b2c984e9..aa6033ca7cd8 100644 --- a/include/net/sctp/sctp.h +++ b/include/net/sctp/sctp.h @@ -461,12 +461,12 @@ static inline int sctp_frag_point(const struct sctp_sock *sp, int pmtu) * there is room for a param header too. */ #define sctp_walk_params(pos, chunk, member)\ -_sctp_walk_params((pos), (chunk), WORD_ROUND(ntohs((chunk)->chunk_hdr.length)), member) +_sctp_walk_params((pos), (chunk), ntohs((chunk)->chunk_hdr.length), member) #define _sctp_walk_params(pos, chunk, end, member)\ for (pos.v = chunk->member;\ pos.v <= (void *)chunk + end - sizeof(sctp_paramhdr_t) &&\ - pos.v <= (void *)chunk + end - WORD_ROUND(ntohs(pos.p->length)) &&\ + pos.v <= (void *)chunk + end - ntohs(pos.p->length) &&\ ntohs(pos.p->length) >= sizeof(sctp_paramhdr_t);\ pos.v += WORD_ROUND(ntohs(pos.p->length))) @@ -477,7 +477,7 @@ _sctp_walk_errors((err), (chunk_hdr), ntohs((chunk_hdr)->length)) for (err = (sctp_errhdr_t *)((void *)chunk_hdr + \ sizeof(sctp_chunkhdr_t));\ (void *)err <= (void *)chunk_hdr + end - sizeof(sctp_errhdr_t) &&\ - (void *)err <= (void *)chunk_hdr + end - WORD_ROUND(ntohs(err->length)) &&\ + (void *)err <= (void *)chunk_hdr + end - ntohs(err->length) &&\ ntohs(err->length) >= sizeof(sctp_errhdr_t); \ err = (sctp_errhdr_t *)((void *)err + WORD_ROUND(ntohs(err->length)))) -- cgit v1.2.3 From 2c171bf13423dc5293188cea7f6c2da1720926e2 Mon Sep 17 00:00:00 2001 From: Pavel Pisa Date: Fri, 19 May 2006 21:48:03 +0100 Subject: [ARM] 3531/1: i.MX/MX1 SD/MMC ensure, that clock are stopped before new command and cleanups Patch from Pavel Pisa There has been problems that for some paths that clock are not stopped during new command programming and initiation. Result is issuing of incorrect command to the card. Some other problems are cleaned too. Noisy report of known ERRATUM #4 has been suppressed. Signed-off-by: Pavel Pisa Signed-off-by: Russell King --- drivers/mmc/au1xmmc.c | 6 +++--- drivers/mmc/imxmmc.c | 24 ++++++++++++++---------- drivers/mmc/mmc.c | 1 + drivers/mmc/mmc_block.c | 1 + drivers/mmc/pxamci.c | 4 ++-- drivers/mmc/wbsd.c | 8 ++++---- include/linux/mmc/mmc.h | 1 + 7 files changed, 26 insertions(+), 19 deletions(-) (limited to 'include') diff --git a/drivers/mmc/au1xmmc.c b/drivers/mmc/au1xmmc.c index 914d62b24064..5dc4bee7abeb 100644 --- a/drivers/mmc/au1xmmc.c +++ b/drivers/mmc/au1xmmc.c @@ -310,7 +310,7 @@ static void au1xmmc_data_complete(struct au1xmmc_host *host, u32 status) } else data->bytes_xfered = - (data->blocks * (1 << data->blksz_bits)) - + (data->blocks * data->blksz) - host->pio.len; } @@ -575,7 +575,7 @@ static int au1xmmc_prepare_data(struct au1xmmc_host *host, struct mmc_data *data) { - int datalen = data->blocks * (1 << data->blksz_bits); + int datalen = data->blocks * data->blksz; if (dma != 0) host->flags |= HOST_F_DMA; @@ -596,7 +596,7 @@ au1xmmc_prepare_data(struct au1xmmc_host *host, struct mmc_data *data) if (host->dma.len == 0) return MMC_ERR_TIMEOUT; - au_writel((1 << data->blksz_bits) - 1, HOST_BLKSIZE(host)); + au_writel(data->blksz - 1, HOST_BLKSIZE(host)); if (host->flags & HOST_F_DMA) { int i; diff --git a/drivers/mmc/imxmmc.c b/drivers/mmc/imxmmc.c index 79358e223f57..a4eb1d0e7a71 100644 --- a/drivers/mmc/imxmmc.c +++ b/drivers/mmc/imxmmc.c @@ -218,8 +218,10 @@ static int imxmci_busy_wait_for_status(struct imxmci_host *host, if(!loops) return 0; - dev_info(mmc_dev(host->mmc), "busy wait for %d usec in %s, STATUS = 0x%x (0x%x)\n", - loops, where, *pstat, stat_mask); + /* The busy-wait is expected there for clock <8MHz due to SDHC hardware flaws */ + if(!(stat_mask & STATUS_END_CMD_RESP) || (host->mmc->ios.clock>=8000000)) + dev_info(mmc_dev(host->mmc), "busy wait for %d usec in %s, STATUS = 0x%x (0x%x)\n", + loops, where, *pstat, stat_mask); return loops; } @@ -333,6 +335,9 @@ static void imxmci_start_cmd(struct imxmci_host *host, struct mmc_command *cmd, WARN_ON(host->cmd != NULL); host->cmd = cmd; + /* Ensure, that clock are stopped else command programming and start fails */ + imxmci_stop_clock(host); + if (cmd->flags & MMC_RSP_BUSY) cmdat |= CMD_DAT_CONT_BUSY; @@ -553,7 +558,7 @@ static int imxmci_cpu_driven_data(struct imxmci_host *host, unsigned int *pstat) int trans_done = 0; unsigned int stat = *pstat; - if(host->actual_bus_width == MMC_BUS_WIDTH_4) + if(host->actual_bus_width != MMC_BUS_WIDTH_4) burst_len = 16; else burst_len = 64; @@ -591,8 +596,7 @@ static int imxmci_cpu_driven_data(struct imxmci_host *host, unsigned int *pstat) stat = MMC_STATUS; /* Flush extra bytes from FIFO */ - while(flush_len >= 2){ - flush_len -= 2; + while(flush_len && !(stat & STATUS_DATA_TRANS_DONE)){ i = MMC_BUFFER_ACCESS; stat = MMC_STATUS; stat &= ~STATUS_CRC_READ_ERR; /* Stupid but required there */ @@ -746,10 +750,6 @@ static void imxmci_tasklet_fnc(unsigned long data) data_dir_mask = STATUS_DATA_TRANS_DONE; } - imxmci_busy_wait_for_status(host, &stat, - data_dir_mask, - 50, "imxmci_tasklet_fnc data"); - if(stat & data_dir_mask) { clear_bit(IMXMCI_PEND_DMA_END_b, &host->pending_events); imxmci_data_done(host, stat); @@ -865,7 +865,11 @@ static void imxmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) imxmci_stop_clock(host); MMC_CLK_RATE = (prescaler<<3) | clk; - imxmci_start_clock(host); + /* + * Under my understanding, clock should not be started there, because it would + * initiate SDHC sequencer and send last or random command into card + */ + /*imxmci_start_clock(host);*/ dev_dbg(mmc_dev(host->mmc), "MMC_CLK_RATE: 0x%08x\n", MMC_CLK_RATE); } else { diff --git a/drivers/mmc/mmc.c b/drivers/mmc/mmc.c index 1ca2c8b9c9b5..6201f3086a02 100644 --- a/drivers/mmc/mmc.c +++ b/drivers/mmc/mmc.c @@ -951,6 +951,7 @@ static void mmc_read_scrs(struct mmc_host *host) data.timeout_ns = card->csd.tacc_ns * 10; data.timeout_clks = card->csd.tacc_clks * 10; data.blksz_bits = 3; + data.blksz = 1 << 3; data.blocks = 1; data.flags = MMC_DATA_READ; data.sg = &sg; diff --git a/drivers/mmc/mmc_block.c b/drivers/mmc/mmc_block.c index 06bd1f4cb9b1..e39cc05c64c2 100644 --- a/drivers/mmc/mmc_block.c +++ b/drivers/mmc/mmc_block.c @@ -175,6 +175,7 @@ static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req) brq.data.timeout_ns = card->csd.tacc_ns * 10; brq.data.timeout_clks = card->csd.tacc_clks * 10; brq.data.blksz_bits = md->block_bits; + brq.data.blksz = 1 << md->block_bits; brq.data.blocks = req->nr_sectors >> (md->block_bits - 9); brq.stop.opcode = MMC_STOP_TRANSMISSION; brq.stop.arg = 0; diff --git a/drivers/mmc/pxamci.c b/drivers/mmc/pxamci.c index f97b472085cb..b49368fd96b8 100644 --- a/drivers/mmc/pxamci.c +++ b/drivers/mmc/pxamci.c @@ -119,7 +119,7 @@ static void pxamci_setup_data(struct pxamci_host *host, struct mmc_data *data) nob = 0xffff; writel(nob, host->base + MMC_NOB); - writel(1 << data->blksz_bits, host->base + MMC_BLKLEN); + writel(data->blksz, host->base + MMC_BLKLEN); clks = (unsigned long long)data->timeout_ns * CLOCKRATE; do_div(clks, 1000000000UL); @@ -283,7 +283,7 @@ static int pxamci_data_done(struct pxamci_host *host, unsigned int stat) * data blocks as being in error. */ if (data->error == MMC_ERR_NONE) - data->bytes_xfered = data->blocks << data->blksz_bits; + data->bytes_xfered = data->blocks * data->blksz; else data->bytes_xfered = 0; diff --git a/drivers/mmc/wbsd.c b/drivers/mmc/wbsd.c index 39b3d97f891e..8167332d4013 100644 --- a/drivers/mmc/wbsd.c +++ b/drivers/mmc/wbsd.c @@ -662,14 +662,14 @@ static void wbsd_prepare_data(struct wbsd_host *host, struct mmc_data *data) unsigned long dmaflags; DBGF("blksz %04x blks %04x flags %08x\n", - 1 << data->blksz_bits, data->blocks, data->flags); + data->blksz, data->blocks, data->flags); DBGF("tsac %d ms nsac %d clk\n", data->timeout_ns / 1000000, data->timeout_clks); /* * Calculate size. */ - host->size = data->blocks << data->blksz_bits; + host->size = data->blocks * data->blksz; /* * Check timeout values for overflow. @@ -696,12 +696,12 @@ static void wbsd_prepare_data(struct wbsd_host *host, struct mmc_data *data) * Two bytes are needed for each data line. */ if (host->bus_width == MMC_BUS_WIDTH_1) { - blksize = (1 << data->blksz_bits) + 2; + blksize = data->blksz + 2; wbsd_write_index(host, WBSD_IDX_PBSMSB, (blksize >> 4) & 0xF0); wbsd_write_index(host, WBSD_IDX_PBSLSB, blksize & 0xFF); } else if (host->bus_width == MMC_BUS_WIDTH_4) { - blksize = (1 << data->blksz_bits) + 2 * 4; + blksize = data->blksz + 2 * 4; wbsd_write_index(host, WBSD_IDX_PBSMSB, ((blksize >> 4) & 0xF0) | WBSD_DATA_WIDTH); diff --git a/include/linux/mmc/mmc.h b/include/linux/mmc/mmc.h index bdc556d88498..03a14a30c46a 100644 --- a/include/linux/mmc/mmc.h +++ b/include/linux/mmc/mmc.h @@ -69,6 +69,7 @@ struct mmc_data { unsigned int timeout_ns; /* data timeout (in ns, max 80ms) */ unsigned int timeout_clks; /* data timeout (in clocks) */ unsigned int blksz_bits; /* data block size */ + unsigned int blksz; /* data block size */ unsigned int blocks; /* number of blocks */ unsigned int error; /* data error */ unsigned int flags; -- cgit v1.2.3 From c2a4c40651e08e465d3a6130bd9f6dcc1ce21d83 Mon Sep 17 00:00:00 2001 From: Catalin Marinas Date: Fri, 19 May 2006 21:55:35 +0100 Subject: [ARM] 3533/1: Implement the __raw_(read|write)_can_lock functions on ARM Patch from Catalin Marinas Recent patches introduced the write_can_lock() call in the kernel/ptrace.c file. Implement the __raw_* variants on ARM (SMP) as well. Signed-off-by: Catalin Marinas Signed-off-by: Russell King --- include/asm-arm/spinlock.h | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'include') diff --git a/include/asm-arm/spinlock.h b/include/asm-arm/spinlock.h index 43ad4e55878c..406ca97a8ab2 100644 --- a/include/asm-arm/spinlock.h +++ b/include/asm-arm/spinlock.h @@ -142,6 +142,9 @@ static inline void __raw_write_unlock(raw_rwlock_t *rw) : "cc"); } +/* write_can_lock - would write_trylock() succeed? */ +#define __raw_write_can_lock(x) ((x)->lock == 0x80000000) + /* * Read locks are a bit more hairy: * - Exclusively load the lock value. @@ -198,4 +201,7 @@ static inline void __raw_read_unlock(raw_rwlock_t *rw) #define __raw_read_trylock(lock) generic__raw_read_trylock(lock) +/* read_can_lock - would read_trylock() succeed? */ +#define __raw_read_can_lock(x) ((x)->lock < 0x80000000) + #endif /* __ASM_SPINLOCK_H */ -- cgit v1.2.3