Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/mason/btrfs...
authorLinus Torvalds <torvalds@linux-foundation.org>
Fri, 18 Dec 2009 00:01:03 +0000 (16:01 -0800)
committerLinus Torvalds <torvalds@linux-foundation.org>
Fri, 18 Dec 2009 00:01:03 +0000 (16:01 -0800)
* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/mason/btrfs-unstable:
  Btrfs: make sure fallocate properly starts a transaction
  Btrfs: make metadata chunks smaller
  Btrfs: Show discard option in /proc/mounts
  Btrfs: deny sys_link across subvolumes.
  Btrfs: fail mount on bad mount options
  Btrfs: don't add extent 0 to the free space cache v2
  Btrfs: Fix per root used space accounting
  Btrfs: Fix btrfs_drop_extent_cache for skip pinned case
  Btrfs: Add delayed iput
  Btrfs: Pass transaction handle to security and ACL initialization functions
  Btrfs: Make truncate(2) more ENOSPC friendly
  Btrfs: Make fallocate(2) more ENOSPC friendly
  Btrfs: Avoid orphan inodes cleanup during committing transaction
  Btrfs: Avoid orphan inodes cleanup while replaying log
  Btrfs: Fix disk_i_size update corner case
  Btrfs: Rewrite btrfs_drop_extents
  Btrfs: Add btrfs_duplicate_item
  Btrfs: Avoid superfluous tree-log writeout

138 files changed:
arch/arm/common/dmabounce.c
arch/arm/include/asm/cacheflush.h
arch/arm/mach-kirkwood/Kconfig
arch/arm/mach-kirkwood/Makefile
arch/arm/mach-kirkwood/netspace_v2-setup.c [new file with mode: 0644]
arch/arm/mach-pxa/Kconfig
arch/arm/mach-pxa/devices.c
arch/arm/mach-s3c2410/include/mach/spi.h
arch/arm/mm/cache-fa.S
arch/arm/mm/cache-l2x0.c
arch/arm/mm/cache-v3.S
arch/arm/mm/cache-v4.S
arch/arm/mm/cache-v4wb.S
arch/arm/mm/cache-v4wt.S
arch/arm/mm/cache-v6.S
arch/arm/mm/cache-v7.S
arch/arm/mm/flush.c
arch/arm/mm/highmem.c
arch/arm/mm/nommu.c
arch/arm/mm/proc-arm1020.S
arch/arm/mm/proc-arm1020e.S
arch/arm/mm/proc-arm1022.S
arch/arm/mm/proc-arm1026.S
arch/arm/mm/proc-arm920.S
arch/arm/mm/proc-arm922.S
arch/arm/mm/proc-arm925.S
arch/arm/mm/proc-arm926.S
arch/arm/mm/proc-arm940.S
arch/arm/mm/proc-arm946.S
arch/arm/mm/proc-feroceon.S
arch/arm/mm/proc-mohawk.S
arch/arm/mm/proc-syms.c
arch/arm/mm/proc-v6.S
arch/arm/mm/proc-xsc3.S
arch/arm/mm/proc-xscale.S
arch/arm/tools/mach-types
drivers/hwmon/Kconfig
drivers/hwmon/Makefile
drivers/hwmon/lis3lv02d_i2c.c [new file with mode: 0644]
drivers/leds/Kconfig
drivers/leds/Makefile
drivers/leds/leds-adp5520.c [new file with mode: 0644]
drivers/leds/leds-alix2.c
drivers/leds/leds-cobalt-qube.c
drivers/leds/leds-cobalt-raq.c
drivers/leds/leds-lt3593.c [new file with mode: 0644]
drivers/leds/leds-pwm.c
drivers/leds/leds-regulator.c [new file with mode: 0644]
drivers/leds/leds-ss4200.c [new file with mode: 0644]
drivers/misc/Kconfig
drivers/mmc/core/sdio.c
drivers/mmc/core/sdio_bus.c
drivers/mmc/host/Kconfig
drivers/mmc/host/Makefile
drivers/mmc/host/sdhci-of-core.c [new file with mode: 0644]
drivers/mmc/host/sdhci-of-esdhc.c [new file with mode: 0644]
drivers/mmc/host/sdhci-of-hlwd.c [new file with mode: 0644]
drivers/mmc/host/sdhci-of.c [deleted file]
drivers/mmc/host/sdhci-of.h [new file with mode: 0644]
drivers/mmc/host/sdhci.h
drivers/mtd/maps/pxa2xx-flash.c
drivers/pcmcia/pxa2xx_base.c
drivers/rtc/rtc-ds1305.c
drivers/rtc/rtc-ds1307.c
drivers/rtc/rtc-ds1374.c
drivers/spi/Kconfig
drivers/spi/Makefile
drivers/spi/atmel_spi.c
drivers/spi/dw_spi.c [new file with mode: 0644]
drivers/spi/dw_spi_pci.c [new file with mode: 0644]
drivers/spi/spi_bfin5xx.c
drivers/spi/spi_mpc8xxx.c
drivers/spi/spi_s3c24xx.c
drivers/spi/spi_s3c24xx_fiq.S [new file with mode: 0644]
drivers/spi/spi_s3c24xx_fiq.h [new file with mode: 0644]
drivers/spi/spi_s3c64xx.c [new file with mode: 0644]
drivers/spi/spi_sh_sci.c
drivers/spi/spi_txx9.c
drivers/spi/spidev.c
drivers/video/backlight/adp5520_bl.c
drivers/video/backlight/adx_bl.c
drivers/video/backlight/atmel-pwm-bl.c
drivers/video/backlight/backlight.c
drivers/video/backlight/corgi_lcd.c
drivers/video/backlight/cr_bllcd.c
drivers/video/backlight/da903x_bl.c
drivers/video/backlight/generic_bl.c
drivers/video/backlight/hp680_bl.c
drivers/video/backlight/jornada720_bl.c
drivers/video/backlight/kb3886_bl.c
drivers/video/backlight/locomolcd.c
drivers/video/backlight/mbp_nvidia_bl.c
drivers/video/backlight/omap1_bl.c
drivers/video/backlight/progear_bl.c
drivers/video/backlight/pwm_bl.c
drivers/video/backlight/tosa_bl.c
drivers/video/backlight/wm831x_bl.c
drivers/video/via/viafbdev.c
fs/Kconfig
fs/binfmt_aout.c
fs/binfmt_elf.c
fs/binfmt_elf_fdpic.c
fs/binfmt_flat.c
fs/binfmt_som.c
fs/btrfs/Kconfig
fs/exec.c
fs/ext4/Kconfig
fs/gfs2/Kconfig
fs/jbd/Kconfig
fs/jbd2/Kconfig
fs/namespace.c
fs/nfs/super.c
fs/nilfs2/Kconfig
fs/ramfs/file-nommu.c
fs/reiserfs/Kconfig
fs/reiserfs/inode.c
include/linux/backlight.h
include/linux/binfmts.h
include/linux/init_task.h
include/linux/kmemleak.h
include/linux/leds-lp3944.h
include/linux/leds-pca9532.h
include/linux/leds-regulator.h [new file with mode: 0644]
include/linux/mnt_namespace.h
include/linux/pwm_backlight.h
include/linux/sched.h
include/linux/spi/dw_spi.h [new file with mode: 0644]
include/linux/vt.h
kernel/exit.c
kernel/fork.c
kernel/module.c
kernel/printk.c
kernel/sysctl.c
lib/Kconfig.debug
lib/vsprintf.c
mm/kmemleak.c
mm/readahead.c
mm/slab.c

index 5a375e5fef21766c3a5120c16c7cd2f8929b42ea..bc90364a96c7bf364fdd10aa198a0d93ab2af2b3 100644 (file)
@@ -308,15 +308,11 @@ static inline void unmap_single(struct device *dev, dma_addr_t dma_addr,
                        memcpy(ptr, buf->safe, size);
 
                        /*
-                        * DMA buffers must have the same cache properties
-                        * as if they were really used for DMA - which means
-                        * data must be written back to RAM.  Note that
-                        * we don't use dmac_flush_range() here for the
-                        * bidirectional case because we know the cache
-                        * lines will be coherent with the data written.
+                        * Since we may have written to a page cache page,
+                        * we need to ensure that the data will be coherent
+                        * with user mappings.
                         */
-                       dmac_clean_range(ptr, ptr + size);
-                       outer_clean_range(__pa(ptr), __pa(ptr) + size);
+                       __cpuc_flush_kernel_dcache_area(ptr, size);
                }
                free_safe_buffer(dev->archdata.dmabounce, buf);
        }
index 73eceb87e58869ffa978bf35cc71b015a2d40d03..730aefcfbee3eb8e0c46ea0a6dc0b9ba0fd1e27f 100644 (file)
@@ -211,7 +211,7 @@ struct cpu_cache_fns {
 
        void (*coherent_kern_range)(unsigned long, unsigned long);
        void (*coherent_user_range)(unsigned long, unsigned long);
-       void (*flush_kern_dcache_page)(void *);
+       void (*flush_kern_dcache_area)(void *, size_t);
 
        void (*dma_inv_range)(const void *, const void *);
        void (*dma_clean_range)(const void *, const void *);
@@ -236,7 +236,7 @@ extern struct cpu_cache_fns cpu_cache;
 #define __cpuc_flush_user_range                cpu_cache.flush_user_range
 #define __cpuc_coherent_kern_range     cpu_cache.coherent_kern_range
 #define __cpuc_coherent_user_range     cpu_cache.coherent_user_range
-#define __cpuc_flush_dcache_page       cpu_cache.flush_kern_dcache_page
+#define __cpuc_flush_dcache_area       cpu_cache.flush_kern_dcache_area
 
 /*
  * These are private to the dma-mapping API.  Do not use directly.
@@ -255,14 +255,14 @@ extern struct cpu_cache_fns cpu_cache;
 #define __cpuc_flush_user_range                __glue(_CACHE,_flush_user_cache_range)
 #define __cpuc_coherent_kern_range     __glue(_CACHE,_coherent_kern_range)
 #define __cpuc_coherent_user_range     __glue(_CACHE,_coherent_user_range)
-#define __cpuc_flush_dcache_page       __glue(_CACHE,_flush_kern_dcache_page)
+#define __cpuc_flush_dcache_area       __glue(_CACHE,_flush_kern_dcache_area)
 
 extern void __cpuc_flush_kern_all(void);
 extern void __cpuc_flush_user_all(void);
 extern void __cpuc_flush_user_range(unsigned long, unsigned long, unsigned int);
 extern void __cpuc_coherent_kern_range(unsigned long, unsigned long);
 extern void __cpuc_coherent_user_range(unsigned long, unsigned long);
-extern void __cpuc_flush_dcache_page(void *);
+extern void __cpuc_flush_dcache_area(void *, size_t);
 
 /*
  * These are private to the dma-mapping API.  Do not use directly.
@@ -448,7 +448,7 @@ static inline void flush_kernel_dcache_page(struct page *page)
 {
        /* highmem pages are always flushed upon kunmap already */
        if ((cache_is_vivt() || cache_is_vipt_aliasing()) && !PageHighMem(page))
-               __cpuc_flush_dcache_page(page_address(page));
+               __cpuc_flush_dcache_area(page_address(page), PAGE_SIZE);
 }
 
 #define flush_dcache_mmap_lock(mapping) \
@@ -465,13 +465,6 @@ static inline void flush_kernel_dcache_page(struct page *page)
  */
 #define flush_icache_page(vma,page)    do { } while (0)
 
-static inline void flush_ioremap_region(unsigned long phys, void __iomem *virt,
-       unsigned offset, size_t size)
-{
-       const void *start = (void __force *)virt + offset;
-       dmac_inv_range(start, start + size);
-}
-
 /*
  * flush_cache_vmap() is used when creating mappings (eg, via vmap,
  * vmalloc, ioremap etc) in kernel space for pages.  On non-VIPT
index 8bf09ae5b347dd19351a491d0c55d4c27ea0f614..f6c6196a51fa489cb1338a9ced5a06e0e6f52cb9 100644 (file)
@@ -52,6 +52,12 @@ config MACH_OPENRD_BASE
          Say 'Y' here if you want your kernel to support the
          Marvell OpenRD Base Board.
 
+config MACH_NETSPACE_V2
+       bool "LaCie Network Space v2 NAS Board"
+       help
+         Say 'Y' here if you want your kernel to support the
+         LaCie Network Space v2 NAS.
+
 endmenu
 
 endif
index 9f2f67b2b63d92aafc166a73ff86906ec837a12f..d4d7f53b0fb9d51c6b4cba7174c6749944e4bdc9 100644 (file)
@@ -8,5 +8,6 @@ obj-$(CONFIG_MACH_SHEEVAPLUG)           += sheevaplug-setup.o
 obj-$(CONFIG_MACH_TS219)               += ts219-setup.o tsx1x-common.o
 obj-$(CONFIG_MACH_TS41X)               += ts41x-setup.o tsx1x-common.o
 obj-$(CONFIG_MACH_OPENRD_BASE)         += openrd_base-setup.o
+obj-$(CONFIG_MACH_NETSPACE_V2)         += netspace_v2-setup.o
 
 obj-$(CONFIG_CPU_IDLE)                 += cpuidle.o
diff --git a/arch/arm/mach-kirkwood/netspace_v2-setup.c b/arch/arm/mach-kirkwood/netspace_v2-setup.c
new file mode 100644 (file)
index 0000000..9a06406
--- /dev/null
@@ -0,0 +1,325 @@
+/*
+ * arch/arm/mach-kirkwood/netspace_v2-setup.c
+ *
+ * LaCie Network Space v2 board setup
+ *
+ * Copyright (C) 2009 Simon Guinot <sguinot@lacie.com>
+ * Copyright (C) 2009 Benoît Canet <benoit.canet@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/mtd/physmap.h>
+#include <linux/spi/flash.h>
+#include <linux/spi/spi.h>
+#include <linux/ata_platform.h>
+#include <linux/mv643xx_eth.h>
+#include <linux/i2c.h>
+#include <linux/i2c/at24.h>
+#include <linux/input.h>
+#include <linux/gpio.h>
+#include <linux/gpio_keys.h>
+#include <linux/leds.h>
+#include <asm/mach-types.h>
+#include <asm/mach/arch.h>
+#include <asm/mach/time.h>
+#include <mach/kirkwood.h>
+#include <plat/time.h>
+#include "common.h"
+#include "mpp.h"
+
+/*****************************************************************************
+ * 512KB SPI Flash on Boot Device (MACRONIX MX25L4005)
+ ****************************************************************************/
+
+static struct mtd_partition netspace_v2_flash_parts[] = {
+       {
+               .name = "u-boot",
+               .size = MTDPART_SIZ_FULL,
+               .offset = 0,
+               .mask_flags = MTD_WRITEABLE, /* force read-only */
+       },
+};
+
+static const struct flash_platform_data netspace_v2_flash = {
+       .type           = "mx25l4005a",
+       .name           = "spi_flash",
+       .parts          = netspace_v2_flash_parts,
+       .nr_parts       = ARRAY_SIZE(netspace_v2_flash_parts),
+};
+
+static struct spi_board_info __initdata netspace_v2_spi_slave_info[] = {
+       {
+               .modalias       = "m25p80",
+               .platform_data  = &netspace_v2_flash,
+               .irq            = -1,
+               .max_speed_hz   = 20000000,
+               .bus_num        = 0,
+               .chip_select    = 0,
+       },
+};
+
+/*****************************************************************************
+ * Ethernet
+ ****************************************************************************/
+
+static struct mv643xx_eth_platform_data netspace_v2_ge00_data = {
+       .phy_addr       = MV643XX_ETH_PHY_ADDR(8),
+};
+
+/*****************************************************************************
+ * I2C devices
+ ****************************************************************************/
+
+static struct at24_platform_data at24c04 = {
+       .byte_len       = SZ_4K / 8,
+       .page_size      = 16,
+};
+
+/*
+ * i2c addr | chip         | description
+ * 0x50     | HT24LC04     | eeprom (512B)
+ */
+
+static struct i2c_board_info __initdata netspace_v2_i2c_info[] = {
+       {
+               I2C_BOARD_INFO("24c04", 0x50),
+               .platform_data  = &at24c04,
+       }
+};
+
+/*****************************************************************************
+ * SATA
+ ****************************************************************************/
+
+static struct mv_sata_platform_data netspace_v2_sata_data = {
+       .n_ports        = 2,
+};
+
+#define NETSPACE_V2_GPIO_SATA0_POWER   16
+#define NETSPACE_V2_GPIO_SATA1_POWER   17
+
+static void __init netspace_v2_sata_power_init(void)
+{
+       int err;
+
+       err = gpio_request(NETSPACE_V2_GPIO_SATA0_POWER, "SATA0 power");
+       if (err == 0) {
+               err = gpio_direction_output(NETSPACE_V2_GPIO_SATA0_POWER, 1);
+               if (err)
+                       gpio_free(NETSPACE_V2_GPIO_SATA0_POWER);
+       }
+       if (err)
+               pr_err("netspace_v2: failed to setup SATA0 power\n");
+}
+
+/*****************************************************************************
+ * GPIO keys
+ ****************************************************************************/
+
+#define NETSPACE_V2_PUSH_BUTTON                32
+
+static struct gpio_keys_button netspace_v2_buttons[] = {
+       [0] = {
+               .code           = KEY_POWER,
+               .gpio           = NETSPACE_V2_PUSH_BUTTON,
+               .desc           = "Power push button",
+               .active_low     = 0,
+       },
+};
+
+static struct gpio_keys_platform_data netspace_v2_button_data = {
+       .buttons        = netspace_v2_buttons,
+       .nbuttons       = ARRAY_SIZE(netspace_v2_buttons),
+};
+
+static struct platform_device netspace_v2_gpio_buttons = {
+       .name           = "gpio-keys",
+       .id             = -1,
+       .dev            = {
+               .platform_data  = &netspace_v2_button_data,
+       },
+};
+
+/*****************************************************************************
+ * GPIO LEDs
+ ****************************************************************************/
+
+/*
+ * The blue front LED is wired to a CPLD and can blink in relation with the
+ * SATA activity.
+ *
+ * The following array detail the different LED registers and the combination
+ * of their possible values:
+ *
+ *  cmd_led   |  slow_led  | /SATA active | LED state
+ *            |            |              |
+ *     1      |     0      |      x       |  off
+ *     -      |     1      |      x       |  on
+ *     0      |     0      |      1       |  on
+ *     0      |     0      |      0       |  blink (rate 300ms)
+ */
+
+#define NETSPACE_V2_GPIO_RED_LED       12
+#define NETSPACE_V2_GPIO_BLUE_LED_SLOW 29
+#define NETSPACE_V2_GPIO_BLUE_LED_CMD  30
+
+
+static struct gpio_led netspace_v2_gpio_led_pins[] = {
+       {
+               .name   = "ns_v2:red:fail",
+               .gpio   = NETSPACE_V2_GPIO_RED_LED,
+       },
+};
+
+static struct gpio_led_platform_data netspace_v2_gpio_leds_data = {
+       .num_leds       = ARRAY_SIZE(netspace_v2_gpio_led_pins),
+       .leds           = netspace_v2_gpio_led_pins,
+};
+
+static struct platform_device netspace_v2_gpio_leds = {
+       .name           = "leds-gpio",
+       .id             = -1,
+       .dev            = {
+               .platform_data  = &netspace_v2_gpio_leds_data,
+       },
+};
+
+static void __init netspace_v2_gpio_leds_init(void)
+{
+       platform_device_register(&netspace_v2_gpio_leds);
+
+       /*
+        * Configure the front blue LED to blink in relation with the SATA
+        * activity.
+        */
+       if (gpio_request(NETSPACE_V2_GPIO_BLUE_LED_SLOW,
+                        "SATA blue LED slow") != 0)
+               return;
+       if (gpio_direction_output(NETSPACE_V2_GPIO_BLUE_LED_SLOW, 0) != 0)
+               goto err_free_1;
+       if (gpio_request(NETSPACE_V2_GPIO_BLUE_LED_CMD,
+                        "SATA blue LED command") != 0)
+               goto err_free_1;
+       if (gpio_direction_output(NETSPACE_V2_GPIO_BLUE_LED_CMD, 0) != 0)
+               goto err_free_2;
+
+       return;
+
+err_free_2:
+       gpio_free(NETSPACE_V2_GPIO_BLUE_LED_CMD);
+err_free_1:
+       gpio_free(NETSPACE_V2_GPIO_BLUE_LED_SLOW);
+       pr_err("netspace_v2: failed to configure SATA blue LED\n");
+}
+
+/*****************************************************************************
+ * Timer
+ ****************************************************************************/
+
+static void netspace_v2_timer_init(void)
+{
+       kirkwood_tclk = 166666667;
+       orion_time_init(IRQ_KIRKWOOD_BRIDGE, kirkwood_tclk);
+}
+
+struct sys_timer netspace_v2_timer = {
+       .init = netspace_v2_timer_init,
+};
+
+/*****************************************************************************
+ * General Setup
+ ****************************************************************************/
+
+static unsigned int netspace_v2_mpp_config[] __initdata = {
+       MPP0_SPI_SCn,
+       MPP1_SPI_MOSI,
+       MPP2_SPI_SCK,
+       MPP3_SPI_MISO,
+       MPP4_NF_IO6,
+       MPP5_NF_IO7,
+       MPP6_SYSRST_OUTn,
+       MPP8_TW_SDA,
+       MPP9_TW_SCK,
+       MPP10_UART0_TXD,
+       MPP11_UART0_RXD,
+       MPP12_GPO,              /* Red led */
+       MPP14_GPIO,             /* USB fuse */
+       MPP16_GPIO,             /* SATA 0 power */
+       MPP18_NF_IO0,
+       MPP19_NF_IO1,
+       MPP20_SATA1_ACTn,
+       MPP21_SATA0_ACTn,
+       MPP24_GPIO,             /* USB mode select */
+       MPP25_GPIO,             /* Fan rotation fail */
+       MPP26_GPIO,             /* USB device vbus */
+       MPP28_GPIO,             /* USB enable host vbus */
+       MPP29_GPIO,             /* Blue led (slow register) */
+       MPP30_GPIO,             /* Blue led (command register) */
+       MPP31_GPIO,             /* Board power off */
+       MPP32_GPIO,             /* Power button (0 = Released, 1 = Pushed) */
+       0
+};
+
+#define NETSPACE_V2_GPIO_POWER_OFF     31
+
+static void netspace_v2_power_off(void)
+{
+       gpio_set_value(NETSPACE_V2_GPIO_POWER_OFF, 1);
+}
+
+static void __init netspace_v2_init(void)
+{
+       /*
+        * Basic setup. Needs to be called early.
+        */
+       kirkwood_init();
+       kirkwood_mpp_conf(netspace_v2_mpp_config);
+
+       netspace_v2_sata_power_init();
+
+       kirkwood_ehci_init();
+       kirkwood_ge00_init(&netspace_v2_ge00_data);
+       kirkwood_sata_init(&netspace_v2_sata_data);
+       kirkwood_uart0_init();
+       spi_register_board_info(netspace_v2_spi_slave_info,
+                               ARRAY_SIZE(netspace_v2_spi_slave_info));
+       kirkwood_spi_init();
+       kirkwood_i2c_init();
+       i2c_register_board_info(0, netspace_v2_i2c_info,
+                               ARRAY_SIZE(netspace_v2_i2c_info));
+
+       netspace_v2_gpio_leds_init();
+       platform_device_register(&netspace_v2_gpio_buttons);
+
+       if (gpio_request(NETSPACE_V2_GPIO_POWER_OFF, "power-off") == 0 &&
+           gpio_direction_output(NETSPACE_V2_GPIO_POWER_OFF, 0) == 0)
+               pm_power_off = netspace_v2_power_off;
+       else
+               pr_err("netspace_v2: failed to configure power-off GPIO\n");
+}
+
+MACHINE_START(NETSPACE_V2, "LaCie Network Space v2")
+       .phys_io        = KIRKWOOD_REGS_PHYS_BASE,
+       .io_pg_offst    = ((KIRKWOOD_REGS_VIRT_BASE) >> 18) & 0xfffc,
+       .boot_params    = 0x00000100,
+       .init_machine   = netspace_v2_init,
+       .map_io         = kirkwood_map_io,
+       .init_irq       = kirkwood_init_irq,
+       .timer          = &netspace_v2_timer,
+MACHINE_END
index e6d8e10ae5d1fe74c8e8b17414a325c69a763f6c..8a0837ea0294218a6f9ed85d43ba4862d4afc3fd 100644 (file)
@@ -110,6 +110,8 @@ config MACH_CM_X300
        bool "CompuLab CM-X300 modules"
        select PXA3xx
        select CPU_PXA300
+       select CPU_PXA310
+       select HAVE_PWM
 
 config ARCH_GUMSTIX
        bool "Gumstix XScale 255 boards"
@@ -240,7 +242,6 @@ config MACH_COLIBRI300
        select PXA3xx
        select CPU_PXA300
        select CPU_PXA310
-       select HAVE_PWM
 
 config MACH_COLIBRI320
        bool "Toradex Colibri PXA320"
index 3395463bb5a654e6327a51028c17a4ce0dfd6301..8e10db148f1b2cccdc70b07ba0785b280a80dcfc 100644 (file)
@@ -4,7 +4,6 @@
 #include <linux/platform_device.h>
 #include <linux/dma-mapping.h>
 
-#include <mach/hardware.h>
 #include <mach/udc.h>
 #include <mach/pxafb.h>
 #include <mach/mmc.h>
@@ -14,6 +13,7 @@
 #include <mach/pxa2xx_spi.h>
 #include <mach/camera.h>
 #include <mach/audio.h>
+#include <mach/hardware.h>
 #include <plat/i2c.h>
 #include <plat/pxa3xx_nand.h>
 
index 193b39d654edc41b4ef9569b5d2927c29751f87d..4d9588373aa55a624de6ac4e8c451fe7dca0759e 100644 (file)
@@ -18,6 +18,8 @@ struct s3c2410_spi_info {
        unsigned int             num_cs;        /* total chipselects */
        int                      bus_num;       /* bus number to use. */
 
+       unsigned int             use_fiq:1;     /* use fiq */
+
        void (*gpio_setup)(struct s3c2410_spi_info *spi, int enable);
        void (*set_cs)(struct s3c2410_spi_info *spi, int cs, int pol);
 };
index b63a8f7b95cf5a2575f012b91324cc2e273563cf..a89444a3c016f0c2ecee35d38655b109664bc91b 100644 (file)
@@ -127,15 +127,16 @@ ENTRY(fa_coherent_user_range)
        mov     pc, lr
 
 /*
- *     flush_kern_dcache_page(kaddr)
+ *     flush_kern_dcache_area(void *addr, size_t size)
  *
  *     Ensure that the data held in the page kaddr is written back
  *     to the page in question.
  *
- *     - kaddr   - kernel address (guaranteed to be page aligned)
+ *     - addr  - kernel address
+ *     - size  - size of region
  */
-ENTRY(fa_flush_kern_dcache_page)
-       add     r1, r0, #PAGE_SZ
+ENTRY(fa_flush_kern_dcache_area)
+       add     r1, r0, r1
 1:     mcr     p15, 0, r0, c7, c14, 1          @ clean & invalidate D line
        add     r0, r0, #CACHE_DLINESIZE
        cmp     r0, r1
@@ -213,7 +214,7 @@ ENTRY(fa_cache_fns)
        .long   fa_flush_user_cache_range
        .long   fa_coherent_kern_range
        .long   fa_coherent_user_range
-       .long   fa_flush_kern_dcache_page
+       .long   fa_flush_kern_dcache_area
        .long   fa_dma_inv_range
        .long   fa_dma_clean_range
        .long   fa_dma_flush_range
index 747f9a9021bb9d97de9e22d13b04bb5c38ecc15b..cb8fc6573b1b2c9dedbeeec0f9a87c491a78ba85 100644 (file)
 static void __iomem *l2x0_base;
 static DEFINE_SPINLOCK(l2x0_lock);
 
-static inline void sync_writel(unsigned long val, unsigned long reg,
-                              unsigned long complete_mask)
+static inline void cache_wait(void __iomem *reg, unsigned long mask)
 {
-       unsigned long flags;
-
-       spin_lock_irqsave(&l2x0_lock, flags);
-       writel(val, l2x0_base + reg);
        /* wait for the operation to complete */
-       while (readl(l2x0_base + reg) & complete_mask)
+       while (readl(reg) & mask)
                ;
-       spin_unlock_irqrestore(&l2x0_lock, flags);
 }
 
 static inline void cache_sync(void)
 {
-       sync_writel(0, L2X0_CACHE_SYNC, 1);
+       void __iomem *base = l2x0_base;
+       writel(0, base + L2X0_CACHE_SYNC);
+       cache_wait(base + L2X0_CACHE_SYNC, 1);
 }
 
 static inline void l2x0_inv_all(void)
 {
+       unsigned long flags;
+
        /* invalidate all ways */
-       sync_writel(0xff, L2X0_INV_WAY, 0xff);
+       spin_lock_irqsave(&l2x0_lock, flags);
+       writel(0xff, l2x0_base + L2X0_INV_WAY);
+       cache_wait(l2x0_base + L2X0_INV_WAY, 0xff);
        cache_sync();
+       spin_unlock_irqrestore(&l2x0_lock, flags);
 }
 
 static void l2x0_inv_range(unsigned long start, unsigned long end)
 {
-       unsigned long addr;
+       void __iomem *base = l2x0_base;
+       unsigned long flags;
 
+       spin_lock_irqsave(&l2x0_lock, flags);
        if (start & (CACHE_LINE_SIZE - 1)) {
                start &= ~(CACHE_LINE_SIZE - 1);
-               sync_writel(start, L2X0_CLEAN_INV_LINE_PA, 1);
+               cache_wait(base + L2X0_CLEAN_INV_LINE_PA, 1);
+               writel(start, base + L2X0_CLEAN_INV_LINE_PA);
                start += CACHE_LINE_SIZE;
        }
 
        if (end & (CACHE_LINE_SIZE - 1)) {
                end &= ~(CACHE_LINE_SIZE - 1);
-               sync_writel(end, L2X0_CLEAN_INV_LINE_PA, 1);
+               cache_wait(base + L2X0_CLEAN_INV_LINE_PA, 1);
+               writel(end, base + L2X0_CLEAN_INV_LINE_PA);
        }
 
-       for (addr = start; addr < end; addr += CACHE_LINE_SIZE)
-               sync_writel(addr, L2X0_INV_LINE_PA, 1);
+       while (start < end) {
+               unsigned long blk_end = start + min(end - start, 4096UL);
+
+               while (start < blk_end) {
+                       cache_wait(base + L2X0_INV_LINE_PA, 1);
+                       writel(start, base + L2X0_INV_LINE_PA);
+                       start += CACHE_LINE_SIZE;
+               }
+
+               if (blk_end < end) {
+                       spin_unlock_irqrestore(&l2x0_lock, flags);
+                       spin_lock_irqsave(&l2x0_lock, flags);
+               }
+       }
+       cache_wait(base + L2X0_INV_LINE_PA, 1);
        cache_sync();
+       spin_unlock_irqrestore(&l2x0_lock, flags);
 }
 
 static void l2x0_clean_range(unsigned long start, unsigned long end)
 {
-       unsigned long addr;
+       void __iomem *base = l2x0_base;
+       unsigned long flags;
 
+       spin_lock_irqsave(&l2x0_lock, flags);
        start &= ~(CACHE_LINE_SIZE - 1);
-       for (addr = start; addr < end; addr += CACHE_LINE_SIZE)
-               sync_writel(addr, L2X0_CLEAN_LINE_PA, 1);
+       while (start < end) {
+               unsigned long blk_end = start + min(end - start, 4096UL);
+
+               while (start < blk_end) {
+                       cache_wait(base + L2X0_CLEAN_LINE_PA, 1);
+                       writel(start, base + L2X0_CLEAN_LINE_PA);
+                       start += CACHE_LINE_SIZE;
+               }
+
+               if (blk_end < end) {
+                       spin_unlock_irqrestore(&l2x0_lock, flags);
+                       spin_lock_irqsave(&l2x0_lock, flags);
+               }
+       }
+       cache_wait(base + L2X0_CLEAN_LINE_PA, 1);
        cache_sync();
+       spin_unlock_irqrestore(&l2x0_lock, flags);
 }
 
 static void l2x0_flush_range(unsigned long start, unsigned long end)
 {
-       unsigned long addr;
+       void __iomem *base = l2x0_base;
+       unsigned long flags;
 
+       spin_lock_irqsave(&l2x0_lock, flags);
        start &= ~(CACHE_LINE_SIZE - 1);
-       for (addr = start; addr < end; addr += CACHE_LINE_SIZE)
-               sync_writel(addr, L2X0_CLEAN_INV_LINE_PA, 1);
+       while (start < end) {
+               unsigned long blk_end = start + min(end - start, 4096UL);
+
+               while (start < blk_end) {
+                       cache_wait(base + L2X0_CLEAN_INV_LINE_PA, 1);
+                       writel(start, base + L2X0_CLEAN_INV_LINE_PA);
+                       start += CACHE_LINE_SIZE;
+               }
+
+               if (blk_end < end) {
+                       spin_unlock_irqrestore(&l2x0_lock, flags);
+                       spin_lock_irqsave(&l2x0_lock, flags);
+               }
+       }
+       cache_wait(base + L2X0_CLEAN_INV_LINE_PA, 1);
        cache_sync();
+       spin_unlock_irqrestore(&l2x0_lock, flags);
 }
 
 void __init l2x0_init(void __iomem *base, __u32 aux_val, __u32 aux_mask)
index 8a4abebc478a29617a55efeabd871480ca4ab9c2..2a482731ea36914f2f6ca2b6bb4299e7dee84742 100644 (file)
@@ -72,14 +72,15 @@ ENTRY(v3_coherent_user_range)
        mov     pc, lr
 
 /*
- *     flush_kern_dcache_page(void *page)
+ *     flush_kern_dcache_area(void *page, size_t size)
  *
  *     Ensure no D cache aliasing occurs, either with itself or
  *     the I cache
  *
- *     - addr  - page aligned address
+ *     - addr  - kernel address
+ *     - size  - region size
  */
-ENTRY(v3_flush_kern_dcache_page)
+ENTRY(v3_flush_kern_dcache_area)
        /* FALLTHROUGH */
 
 /*
@@ -129,7 +130,7 @@ ENTRY(v3_cache_fns)
        .long   v3_flush_user_cache_range
        .long   v3_coherent_kern_range
        .long   v3_coherent_user_range
-       .long   v3_flush_kern_dcache_page
+       .long   v3_flush_kern_dcache_area
        .long   v3_dma_inv_range
        .long   v3_dma_clean_range
        .long   v3_dma_flush_range
index 3668611cb400325d9368e2707f41a25d0da93c50..5c7da3e372e94faa6f85a07b2c154730140f44b6 100644 (file)
@@ -82,14 +82,15 @@ ENTRY(v4_coherent_user_range)
        mov     pc, lr
 
 /*
- *     flush_kern_dcache_page(void *page)
+ *     flush_kern_dcache_area(void *addr, size_t size)
  *
  *     Ensure no D cache aliasing occurs, either with itself or
  *     the I cache
  *
- *     - addr  - page aligned address
+ *     - addr  - kernel address
+ *     - size  - region size
  */
-ENTRY(v4_flush_kern_dcache_page)
+ENTRY(v4_flush_kern_dcache_area)
        /* FALLTHROUGH */
 
 /*
@@ -141,7 +142,7 @@ ENTRY(v4_cache_fns)
        .long   v4_flush_user_cache_range
        .long   v4_coherent_kern_range
        .long   v4_coherent_user_range
-       .long   v4_flush_kern_dcache_page
+       .long   v4_flush_kern_dcache_area
        .long   v4_dma_inv_range
        .long   v4_dma_clean_range
        .long   v4_dma_flush_range
index 2ebc1b3bf856ff454f5a0c114ef0dea9e9fbcaed..3dbedf1ec0e7790612385b34def041378af5f0d9 100644 (file)
@@ -114,15 +114,16 @@ ENTRY(v4wb_flush_user_cache_range)
        mov     pc, lr
 
 /*
- *     flush_kern_dcache_page(void *page)
+ *     flush_kern_dcache_area(void *addr, size_t size)
  *
  *     Ensure no D cache aliasing occurs, either with itself or
  *     the I cache
  *
- *     - addr  - page aligned address
+ *     - addr  - kernel address
+ *     - size  - region size
  */
-ENTRY(v4wb_flush_kern_dcache_page)
-       add     r1, r0, #PAGE_SZ
+ENTRY(v4wb_flush_kern_dcache_area)
+       add     r1, r0, r1
        /* fall through */
 
 /*
@@ -224,7 +225,7 @@ ENTRY(v4wb_cache_fns)
        .long   v4wb_flush_user_cache_range
        .long   v4wb_coherent_kern_range
        .long   v4wb_coherent_user_range
-       .long   v4wb_flush_kern_dcache_page
+       .long   v4wb_flush_kern_dcache_area
        .long   v4wb_dma_inv_range
        .long   v4wb_dma_clean_range
        .long   v4wb_dma_flush_range
index c54fa2cc40e6e2f8ebfa2c60c7849ea94682bd70..b3b7410270b48e6bf3414de930e2eff0010cf6b3 100644 (file)
@@ -117,17 +117,18 @@ ENTRY(v4wt_coherent_user_range)
        mov     pc, lr
 
 /*
- *     flush_kern_dcache_page(void *page)
+ *     flush_kern_dcache_area(void *addr, size_t size)
  *
  *     Ensure no D cache aliasing occurs, either with itself or
  *     the I cache
  *
- *     - addr  - page aligned address
+ *     - addr  - kernel address
+ *     - size  - region size
  */
-ENTRY(v4wt_flush_kern_dcache_page)
+ENTRY(v4wt_flush_kern_dcache_area)
        mov     r2, #0
        mcr     p15, 0, r2, c7, c5, 0           @ invalidate I cache
-       add     r1, r0, #PAGE_SZ
+       add     r1, r0, r1
        /* fallthrough */
 
 /*
@@ -180,7 +181,7 @@ ENTRY(v4wt_cache_fns)
        .long   v4wt_flush_user_cache_range
        .long   v4wt_coherent_kern_range
        .long   v4wt_coherent_user_range
-       .long   v4wt_flush_kern_dcache_page
+       .long   v4wt_flush_kern_dcache_area
        .long   v4wt_dma_inv_range
        .long   v4wt_dma_clean_range
        .long   v4wt_dma_flush_range
index 295e25dd6381f69da0eaf59c8e9da1854434ddd8..4ba0a24ce6f58341bcf0a78aef56531709e7b774 100644 (file)
@@ -159,15 +159,16 @@ ENDPROC(v6_coherent_user_range)
 ENDPROC(v6_coherent_kern_range)
 
 /*
- *     v6_flush_kern_dcache_page(kaddr)
+ *     v6_flush_kern_dcache_area(void *addr, size_t size)
  *
  *     Ensure that the data held in the page kaddr is written back
  *     to the page in question.
  *
- *     - kaddr   - kernel address (guaranteed to be page aligned)
+ *     - addr  - kernel address
+ *     - size  - region size
  */
-ENTRY(v6_flush_kern_dcache_page)
-       add     r1, r0, #PAGE_SZ
+ENTRY(v6_flush_kern_dcache_area)
+       add     r1, r0, r1
 1:
 #ifdef HARVARD_CACHE
        mcr     p15, 0, r0, c7, c14, 1          @ clean & invalidate D line
@@ -271,7 +272,7 @@ ENTRY(v6_cache_fns)
        .long   v6_flush_user_cache_range
        .long   v6_coherent_kern_range
        .long   v6_coherent_user_range
-       .long   v6_flush_kern_dcache_page
+       .long   v6_flush_kern_dcache_area
        .long   v6_dma_inv_range
        .long   v6_dma_clean_range
        .long   v6_dma_flush_range
index e1bd9759617f16cce4d8b59c738e340afa22b2e2..9073db849fb46a75f08c18c1ae9790614696e377 100644 (file)
@@ -186,16 +186,17 @@ ENDPROC(v7_coherent_kern_range)
 ENDPROC(v7_coherent_user_range)
 
 /*
- *     v7_flush_kern_dcache_page(kaddr)
+ *     v7_flush_kern_dcache_area(void *addr, size_t size)
  *
  *     Ensure that the data held in the page kaddr is written back
  *     to the page in question.
  *
- *     - kaddr   - kernel address (guaranteed to be page aligned)
+ *     - addr  - kernel address
+ *     - size  - region size
  */
-ENTRY(v7_flush_kern_dcache_page)
+ENTRY(v7_flush_kern_dcache_area)
        dcache_line_size r2, r3
-       add     r1, r0, #PAGE_SZ
+       add     r1, r0, r1
 1:
        mcr     p15, 0, r0, c7, c14, 1          @ clean & invalidate D line / unified line
        add     r0, r0, r2
@@ -203,7 +204,7 @@ ENTRY(v7_flush_kern_dcache_page)
        blo     1b
        dsb
        mov     pc, lr
-ENDPROC(v7_flush_kern_dcache_page)
+ENDPROC(v7_flush_kern_dcache_area)
 
 /*
  *     v7_dma_inv_range(start,end)
@@ -279,7 +280,7 @@ ENTRY(v7_cache_fns)
        .long   v7_flush_user_cache_range
        .long   v7_coherent_kern_range
        .long   v7_coherent_user_range
-       .long   v7_flush_kern_dcache_page
+       .long   v7_flush_kern_dcache_area
        .long   v7_dma_inv_range
        .long   v7_dma_clean_range
        .long   v7_dma_flush_range
index 329594e760cdb09e868b1e1ad42a30dfda78f429..6f3a4b7a3b8276e5c442e4bbfc8273c8e9745f54 100644 (file)
@@ -131,7 +131,7 @@ void __flush_dcache_page(struct address_space *mapping, struct page *page)
         */
        if (addr)
 #endif
-               __cpuc_flush_dcache_page(addr);
+               __cpuc_flush_dcache_area(addr, PAGE_SIZE);
 
        /*
         * If this is a page cache page, and we have an aliasing VIPT cache,
@@ -258,5 +258,5 @@ void __flush_anon_page(struct vm_area_struct *vma, struct page *page, unsigned l
         * in this mapping of the page.  FIXME: this is overkill
         * since we actually ask for a write-back and invalidate.
         */
-       __cpuc_flush_dcache_page(page_address(page));
+       __cpuc_flush_dcache_area(page_address(page), PAGE_SIZE);
 }
index 30f82fb5918c9e2a8b3cce49849ff4fd7d97c2dc..2be1ec7c1b41acea66987a3ef532e96020b71c5b 100644 (file)
@@ -79,7 +79,7 @@ void kunmap_atomic(void *kvaddr, enum km_type type)
        unsigned int idx = type + KM_TYPE_NR * smp_processor_id();
 
        if (kvaddr >= (void *)FIXADDR_START) {
-               __cpuc_flush_dcache_page((void *)vaddr);
+               __cpuc_flush_dcache_area((void *)vaddr, PAGE_SIZE);
 #ifdef CONFIG_DEBUG_HIGHMEM
                BUG_ON(vaddr != __fix_to_virt(FIX_KMAP_BEGIN + idx));
                set_pte_ext(TOP_PTE(vaddr), __pte(0), 0);
index 900811cc9130669b0377f7a155b95cd57c64ca34..374a8311bc84b0eeadaa37a14004ee53f499ab67 100644 (file)
@@ -61,7 +61,7 @@ void setup_mm_for_reboot(char mode)
 
 void flush_dcache_page(struct page *page)
 {
-       __cpuc_flush_dcache_page(page_address(page));
+       __cpuc_flush_dcache_area(page_address(page), PAGE_SIZE);
 }
 EXPORT_SYMBOL(flush_dcache_page);
 
index d9fb4b98c49ff8866a1d36288d431ce364ad1180..8012e24282b2d0ffbbab5a38cd963c6cb9acf6e5 100644 (file)
@@ -231,17 +231,18 @@ ENTRY(arm1020_coherent_user_range)
        mov     pc, lr
 
 /*
- *     flush_kern_dcache_page(void *page)
+ *     flush_kern_dcache_area(void *addr, size_t size)
  *
  *     Ensure no D cache aliasing occurs, either with itself or
  *     the I cache
  *
- *     - page  - page aligned address
+ *     - addr  - kernel address
+ *     - size  - region size
  */
-ENTRY(arm1020_flush_kern_dcache_page)
+ENTRY(arm1020_flush_kern_dcache_area)
        mov     ip, #0
 #ifndef CONFIG_CPU_DCACHE_DISABLE
-       add     r1, r0, #PAGE_SZ
+       add     r1, r0, r1
 1:     mcr     p15, 0, r0, c7, c14, 1          @ clean+invalidate D entry
        mcr     p15, 0, ip, c7, c10, 4          @ drain WB
        add     r0, r0, #CACHE_DLINESIZE
@@ -335,7 +336,7 @@ ENTRY(arm1020_cache_fns)
        .long   arm1020_flush_user_cache_range
        .long   arm1020_coherent_kern_range
        .long   arm1020_coherent_user_range
-       .long   arm1020_flush_kern_dcache_page
+       .long   arm1020_flush_kern_dcache_area
        .long   arm1020_dma_inv_range
        .long   arm1020_dma_clean_range
        .long   arm1020_dma_flush_range
index 7453b75dcea5f1527790e940263f113a70aec417..41fe25d234f50b76b563c6b5516a258d45c014fe 100644 (file)
@@ -225,17 +225,18 @@ ENTRY(arm1020e_coherent_user_range)
        mov     pc, lr
 
 /*
- *     flush_kern_dcache_page(void *page)
+ *     flush_kern_dcache_area(void *addr, size_t size)
  *
  *     Ensure no D cache aliasing occurs, either with itself or
  *     the I cache
  *
- *     - page  - page aligned address
+ *     - addr  - kernel address
+ *     - size  - region size
  */
-ENTRY(arm1020e_flush_kern_dcache_page)
+ENTRY(arm1020e_flush_kern_dcache_area)
        mov     ip, #0
 #ifndef CONFIG_CPU_DCACHE_DISABLE
-       add     r1, r0, #PAGE_SZ
+       add     r1, r0, r1
 1:     mcr     p15, 0, r0, c7, c14, 1          @ clean+invalidate D entry
        add     r0, r0, #CACHE_DLINESIZE
        cmp     r0, r1
@@ -321,7 +322,7 @@ ENTRY(arm1020e_cache_fns)
        .long   arm1020e_flush_user_cache_range
        .long   arm1020e_coherent_kern_range
        .long   arm1020e_coherent_user_range
-       .long   arm1020e_flush_kern_dcache_page
+       .long   arm1020e_flush_kern_dcache_area
        .long   arm1020e_dma_inv_range
        .long   arm1020e_dma_clean_range
        .long   arm1020e_dma_flush_range
index 8eb72d75a8b6fe2757e0c6c8bcfa7325519c2a11..20a5b1b31a706051ac2c1b6cb58b293b3b2794a2 100644 (file)
@@ -214,17 +214,18 @@ ENTRY(arm1022_coherent_user_range)
        mov     pc, lr
 
 /*
- *     flush_kern_dcache_page(void *page)
+ *     flush_kern_dcache_area(void *addr, size_t size)
  *
  *     Ensure no D cache aliasing occurs, either with itself or
  *     the I cache
  *
- *     - page  - page aligned address
+ *     - addr  - kernel address
+ *     - size  - region size
  */
-ENTRY(arm1022_flush_kern_dcache_page)
+ENTRY(arm1022_flush_kern_dcache_area)
        mov     ip, #0
 #ifndef CONFIG_CPU_DCACHE_DISABLE
-       add     r1, r0, #PAGE_SZ
+       add     r1, r0, r1
 1:     mcr     p15, 0, r0, c7, c14, 1          @ clean+invalidate D entry
        add     r0, r0, #CACHE_DLINESIZE
        cmp     r0, r1
@@ -310,7 +311,7 @@ ENTRY(arm1022_cache_fns)
        .long   arm1022_flush_user_cache_range
        .long   arm1022_coherent_kern_range
        .long   arm1022_coherent_user_range
-       .long   arm1022_flush_kern_dcache_page
+       .long   arm1022_flush_kern_dcache_area
        .long   arm1022_dma_inv_range
        .long   arm1022_dma_clean_range
        .long   arm1022_dma_flush_range
index 3b59f0d6713962d3e83222d1d05dc2c10606157c..96aedb10fcc418c528f536fb9a06f77185a3fa9c 100644 (file)
@@ -208,17 +208,18 @@ ENTRY(arm1026_coherent_user_range)
        mov     pc, lr
 
 /*
- *     flush_kern_dcache_page(void *page)
+ *     flush_kern_dcache_area(void *addr, size_t size)
  *
  *     Ensure no D cache aliasing occurs, either with itself or
  *     the I cache
  *
- *     - page  - page aligned address
+ *     - addr  - kernel address
+ *     - size  - region size
  */
-ENTRY(arm1026_flush_kern_dcache_page)
+ENTRY(arm1026_flush_kern_dcache_area)
        mov     ip, #0
 #ifndef CONFIG_CPU_DCACHE_DISABLE
-       add     r1, r0, #PAGE_SZ
+       add     r1, r0, r1
 1:     mcr     p15, 0, r0, c7, c14, 1          @ clean+invalidate D entry
        add     r0, r0, #CACHE_DLINESIZE
        cmp     r0, r1
@@ -304,7 +305,7 @@ ENTRY(arm1026_cache_fns)
        .long   arm1026_flush_user_cache_range
        .long   arm1026_coherent_kern_range
        .long   arm1026_coherent_user_range
-       .long   arm1026_flush_kern_dcache_page
+       .long   arm1026_flush_kern_dcache_area
        .long   arm1026_dma_inv_range
        .long   arm1026_dma_clean_range
        .long   arm1026_dma_flush_range
index 2b7c197cc58d2ab4babcf39920220b96a973570e..471669e2d7cb458567d9233a805dd8ce772bf719 100644 (file)
@@ -207,15 +207,16 @@ ENTRY(arm920_coherent_user_range)
        mov     pc, lr
 
 /*
- *     flush_kern_dcache_page(void *page)
+ *     flush_kern_dcache_area(void *addr, size_t size)
  *
  *     Ensure no D cache aliasing occurs, either with itself or
  *     the I cache
  *
- *     - addr  - page aligned address
+ *     - addr  - kernel address
+ *     - size  - region size
  */
-ENTRY(arm920_flush_kern_dcache_page)
-       add     r1, r0, #PAGE_SZ
+ENTRY(arm920_flush_kern_dcache_area)
+       add     r1, r0, r1
 1:     mcr     p15, 0, r0, c7, c14, 1          @ clean+invalidate D entry
        add     r0, r0, #CACHE_DLINESIZE
        cmp     r0, r1
@@ -293,7 +294,7 @@ ENTRY(arm920_cache_fns)
        .long   arm920_flush_user_cache_range
        .long   arm920_coherent_kern_range
        .long   arm920_coherent_user_range
-       .long   arm920_flush_kern_dcache_page
+       .long   arm920_flush_kern_dcache_area
        .long   arm920_dma_inv_range
        .long   arm920_dma_clean_range
        .long   arm920_dma_flush_range
index 06a1aa4e33989976465155586fe0390e19109bb9..ee111b00fa41951619593c758fe22eb06a4e4ee4 100644 (file)
@@ -209,15 +209,16 @@ ENTRY(arm922_coherent_user_range)
        mov     pc, lr
 
 /*
- *     flush_kern_dcache_page(void *page)
+ *     flush_kern_dcache_area(void *addr, size_t size)
  *
  *     Ensure no D cache aliasing occurs, either with itself or
  *     the I cache
  *
- *     - addr  - page aligned address
+ *     - addr  - kernel address
+ *     - size  - region size
  */
-ENTRY(arm922_flush_kern_dcache_page)
-       add     r1, r0, #PAGE_SZ
+ENTRY(arm922_flush_kern_dcache_area)
+       add     r1, r0, r1
 1:     mcr     p15, 0, r0, c7, c14, 1          @ clean+invalidate D entry
        add     r0, r0, #CACHE_DLINESIZE
        cmp     r0, r1
@@ -295,7 +296,7 @@ ENTRY(arm922_cache_fns)
        .long   arm922_flush_user_cache_range
        .long   arm922_coherent_kern_range
        .long   arm922_coherent_user_range
-       .long   arm922_flush_kern_dcache_page
+       .long   arm922_flush_kern_dcache_area
        .long   arm922_dma_inv_range
        .long   arm922_dma_clean_range
        .long   arm922_dma_flush_range
index cb53435a85aee2120f2e97f6b01c24561f32e860..8deb5bde58e4883765e1d7bb6b81b10e0388911b 100644 (file)
@@ -251,15 +251,16 @@ ENTRY(arm925_coherent_user_range)
        mov     pc, lr
 
 /*
- *     flush_kern_dcache_page(void *page)
+ *     flush_kern_dcache_area(void *addr, size_t size)
  *
  *     Ensure no D cache aliasing occurs, either with itself or
  *     the I cache
  *
- *     - addr  - page aligned address
+ *     - addr  - kernel address
+ *     - size  - region size
  */
-ENTRY(arm925_flush_kern_dcache_page)
-       add     r1, r0, #PAGE_SZ
+ENTRY(arm925_flush_kern_dcache_area)
+       add     r1, r0, r1
 1:     mcr     p15, 0, r0, c7, c14, 1          @ clean+invalidate D entry
        add     r0, r0, #CACHE_DLINESIZE
        cmp     r0, r1
@@ -346,7 +347,7 @@ ENTRY(arm925_cache_fns)
        .long   arm925_flush_user_cache_range
        .long   arm925_coherent_kern_range
        .long   arm925_coherent_user_range
-       .long   arm925_flush_kern_dcache_page
+       .long   arm925_flush_kern_dcache_area
        .long   arm925_dma_inv_range
        .long   arm925_dma_clean_range
        .long   arm925_dma_flush_range
index 1c4848704bb358c98a0e4c87141f7326e802d3fb..64db6e275a442f610239fe9b692753c4d88d14f9 100644 (file)
@@ -214,15 +214,16 @@ ENTRY(arm926_coherent_user_range)
        mov     pc, lr
 
 /*
- *     flush_kern_dcache_page(void *page)
+ *     flush_kern_dcache_area(void *addr, size_t size)
  *
  *     Ensure no D cache aliasing occurs, either with itself or
  *     the I cache
  *
- *     - addr  - page aligned address
+ *     - addr  - kernel address
+ *     - size  - region size
  */
-ENTRY(arm926_flush_kern_dcache_page)
-       add     r1, r0, #PAGE_SZ
+ENTRY(arm926_flush_kern_dcache_area)
+       add     r1, r0, r1
 1:     mcr     p15, 0, r0, c7, c14, 1          @ clean+invalidate D entry
        add     r0, r0, #CACHE_DLINESIZE
        cmp     r0, r1
@@ -309,7 +310,7 @@ ENTRY(arm926_cache_fns)
        .long   arm926_flush_user_cache_range
        .long   arm926_coherent_kern_range
        .long   arm926_coherent_user_range
-       .long   arm926_flush_kern_dcache_page
+       .long   arm926_flush_kern_dcache_area
        .long   arm926_dma_inv_range
        .long   arm926_dma_clean_range
        .long   arm926_dma_flush_range
index 5b0f8464c8f29f9cc908cd2bbff5a820652f7751..8196b9f401fb53f17cd0a215c384a872330e054d 100644 (file)
@@ -141,14 +141,15 @@ ENTRY(arm940_coherent_user_range)
        /* FALLTHROUGH */
 
 /*
- *     flush_kern_dcache_page(void *page)
+ *     flush_kern_dcache_area(void *addr, size_t size)
  *
  *     Ensure no D cache aliasing occurs, either with itself or
  *     the I cache
  *
- *     - addr  - page aligned address
+ *     - addr  - kernel address
+ *     - size  - region size
  */
-ENTRY(arm940_flush_kern_dcache_page)
+ENTRY(arm940_flush_kern_dcache_area)
        mov     ip, #0
        mov     r1, #(CACHE_DSEGMENTS - 1) << 4 @ 4 segments
 1:     orr     r3, r1, #(CACHE_DENTRIES - 1) << 26 @ 64 entries
@@ -238,7 +239,7 @@ ENTRY(arm940_cache_fns)
        .long   arm940_flush_user_cache_range
        .long   arm940_coherent_kern_range
        .long   arm940_coherent_user_range
-       .long   arm940_flush_kern_dcache_page
+       .long   arm940_flush_kern_dcache_area
        .long   arm940_dma_inv_range
        .long   arm940_dma_clean_range
        .long   arm940_dma_flush_range
index 40c0449a139b829a709525984442b36561494e13..9a951239c86c0a1b93dcefdf6687312b6c3c51c7 100644 (file)
@@ -183,16 +183,17 @@ ENTRY(arm946_coherent_user_range)
        mov     pc, lr
 
 /*
- *     flush_kern_dcache_page(void *page)
+ *     flush_kern_dcache_area(void *addr, size_t size)
  *
  *     Ensure no D cache aliasing occurs, either with itself or
  *     the I cache
  *
- *     - addr  - page aligned address
+ *     - addr  - kernel address
+ *     - size  - region size
  * (same as arm926)
  */
-ENTRY(arm946_flush_kern_dcache_page)
-       add     r1, r0, #PAGE_SZ
+ENTRY(arm946_flush_kern_dcache_area)
+       add     r1, r0, r1
 1:     mcr     p15, 0, r0, c7, c14, 1          @ clean+invalidate D entry
        add     r0, r0, #CACHE_DLINESIZE
        cmp     r0, r1
@@ -280,7 +281,7 @@ ENTRY(arm946_cache_fns)
        .long   arm946_flush_user_cache_range
        .long   arm946_coherent_kern_range
        .long   arm946_coherent_user_range
-       .long   arm946_flush_kern_dcache_page
+       .long   arm946_flush_kern_dcache_area
        .long   arm946_dma_inv_range
        .long   arm946_dma_clean_range
        .long   arm946_dma_flush_range
index d0d7795200fc143a0362312c960f334deb5f1957..dbc39383e66aaf0d2ec0e5865f15e21dafb02ec7 100644 (file)
@@ -226,16 +226,17 @@ ENTRY(feroceon_coherent_user_range)
        mov     pc, lr
 
 /*
- *     flush_kern_dcache_page(void *page)
+ *     flush_kern_dcache_area(void *addr, size_t size)
  *
  *     Ensure no D cache aliasing occurs, either with itself or
  *     the I cache
  *
- *     - addr  - page aligned address
+ *     - addr  - kernel address
+ *     - size  - region size
  */
        .align  5
-ENTRY(feroceon_flush_kern_dcache_page)
-       add     r1, r0, #PAGE_SZ
+ENTRY(feroceon_flush_kern_dcache_area)
+       add     r1, r0, r1
 1:     mcr     p15, 0, r0, c7, c14, 1          @ clean+invalidate D entry
        add     r0, r0, #CACHE_DLINESIZE
        cmp     r0, r1
@@ -246,7 +247,7 @@ ENTRY(feroceon_flush_kern_dcache_page)
        mov     pc, lr
 
        .align  5
-ENTRY(feroceon_range_flush_kern_dcache_page)
+ENTRY(feroceon_range_flush_kern_dcache_area)
        mrs     r2, cpsr
        add     r1, r0, #PAGE_SZ - CACHE_DLINESIZE      @ top addr is inclusive
        orr     r3, r2, #PSR_I_BIT
@@ -372,7 +373,7 @@ ENTRY(feroceon_cache_fns)
        .long   feroceon_flush_user_cache_range
        .long   feroceon_coherent_kern_range
        .long   feroceon_coherent_user_range
-       .long   feroceon_flush_kern_dcache_page
+       .long   feroceon_flush_kern_dcache_area
        .long   feroceon_dma_inv_range
        .long   feroceon_dma_clean_range
        .long   feroceon_dma_flush_range
@@ -383,7 +384,7 @@ ENTRY(feroceon_range_cache_fns)
        .long   feroceon_flush_user_cache_range
        .long   feroceon_coherent_kern_range
        .long   feroceon_coherent_user_range
-       .long   feroceon_range_flush_kern_dcache_page
+       .long   feroceon_range_flush_kern_dcache_area
        .long   feroceon_range_dma_inv_range
        .long   feroceon_range_dma_clean_range
        .long   feroceon_range_dma_flush_range
index 52b5fd74fbb3fcbfe3295d570a2e121001318562..9674d36cc97d4c1a6489599d2047b3c29ef58110 100644 (file)
@@ -186,15 +186,16 @@ ENTRY(mohawk_coherent_user_range)
        mov     pc, lr
 
 /*
- *     flush_kern_dcache_page(void *page)
+ *     flush_kern_dcache_area(void *addr, size_t size)
  *
  *     Ensure no D cache aliasing occurs, either with itself or
  *     the I cache
  *
- *     - addr  - page aligned address
+ *     - addr  - kernel address
+ *     - size  - region size
  */
-ENTRY(mohawk_flush_kern_dcache_page)
-       add     r1, r0, #PAGE_SZ
+ENTRY(mohawk_flush_kern_dcache_area)
+       add     r1, r0, r1
 1:     mcr     p15, 0, r0, c7, c14, 1          @ clean+invalidate D entry
        add     r0, r0, #CACHE_DLINESIZE
        cmp     r0, r1
@@ -273,7 +274,7 @@ ENTRY(mohawk_cache_fns)
        .long   mohawk_flush_user_cache_range
        .long   mohawk_coherent_kern_range
        .long   mohawk_coherent_user_range
-       .long   mohawk_flush_kern_dcache_page
+       .long   mohawk_flush_kern_dcache_area
        .long   mohawk_dma_inv_range
        .long   mohawk_dma_clean_range
        .long   mohawk_dma_flush_range
index ac5c80062b704b791d0d9af9be34e7484f8d4311..3e6210b4d6d4cc713ba524ab3966760ad1d6f6f8 100644 (file)
@@ -27,8 +27,7 @@ EXPORT_SYMBOL(__cpuc_flush_kern_all);
 EXPORT_SYMBOL(__cpuc_flush_user_all);
 EXPORT_SYMBOL(__cpuc_flush_user_range);
 EXPORT_SYMBOL(__cpuc_coherent_kern_range);
-EXPORT_SYMBOL(__cpuc_flush_dcache_page);
-EXPORT_SYMBOL(dmac_inv_range);  /* because of flush_ioremap_region() */
+EXPORT_SYMBOL(__cpuc_flush_dcache_area);
 #else
 EXPORT_SYMBOL(cpu_cache);
 #endif
index 5485c821101ca1a9d242f28eedef1b02e1be47ff..395cc90c6613d616f56c502102747464baa2b159 100644 (file)
@@ -254,10 +254,9 @@ __pj4_v6_proc_info:
        .long   0x560f5810
        .long   0xff0ffff0
        .long   PMD_TYPE_SECT | \
-               PMD_SECT_BUFFERABLE | \
-               PMD_SECT_CACHEABLE | \
                PMD_SECT_AP_WRITE | \
-               PMD_SECT_AP_READ
+               PMD_SECT_AP_READ | \
+               PMD_FLAGS
        .long   PMD_TYPE_SECT | \
                PMD_SECT_XN | \
                PMD_SECT_AP_WRITE | \
index fab134e29826d626c3ec7b5ca0da5ac37d203f40..96456f5487986f349513b154be2ed22de1bd9eab 100644 (file)
@@ -226,15 +226,16 @@ ENTRY(xsc3_coherent_user_range)
        mov     pc, lr
 
 /*
- *     flush_kern_dcache_page(void *page)
+ *     flush_kern_dcache_area(void *addr, size_t size)
  *
  *     Ensure no D cache aliasing occurs, either with itself or
  *     the I cache.
  *
- *     - addr  - page aligned address
+ *     - addr  - kernel address
+ *     - size  - region size
  */
-ENTRY(xsc3_flush_kern_dcache_page)
-       add     r1, r0, #PAGE_SZ
+ENTRY(xsc3_flush_kern_dcache_area)
+       add     r1, r0, r1
 1:     mcr     p15, 0, r0, c7, c14, 1          @ clean/invalidate L1 D line
        add     r0, r0, #CACHELINESIZE
        cmp     r0, r1
@@ -309,7 +310,7 @@ ENTRY(xsc3_cache_fns)
        .long   xsc3_flush_user_cache_range
        .long   xsc3_coherent_kern_range
        .long   xsc3_coherent_user_range
-       .long   xsc3_flush_kern_dcache_page
+       .long   xsc3_flush_kern_dcache_area
        .long   xsc3_dma_inv_range
        .long   xsc3_dma_clean_range
        .long   xsc3_dma_flush_range
index f056c283682db09b37b3b282a1cded25d9683367..93df47265f2dfda487fcb7c11425d14b0a9ea02f 100644 (file)
@@ -284,15 +284,16 @@ ENTRY(xscale_coherent_user_range)
        mov     pc, lr
 
 /*
- *     flush_kern_dcache_page(void *page)
+ *     flush_kern_dcache_area(void *addr, size_t size)
  *
  *     Ensure no D cache aliasing occurs, either with itself or
  *     the I cache
  *
- *     - addr  - page aligned address
+ *     - addr  - kernel address
+ *     - size  - region size
  */
-ENTRY(xscale_flush_kern_dcache_page)
-       add     r1, r0, #PAGE_SZ
+ENTRY(xscale_flush_kern_dcache_area)
+       add     r1, r0, r1
 1:     mcr     p15, 0, r0, c7, c10, 1          @ clean D entry
        mcr     p15, 0, r0, c7, c6, 1           @ invalidate D entry
        add     r0, r0, #CACHELINESIZE
@@ -368,7 +369,7 @@ ENTRY(xscale_cache_fns)
        .long   xscale_flush_user_cache_range
        .long   xscale_coherent_kern_range
        .long   xscale_coherent_user_range
-       .long   xscale_flush_kern_dcache_page
+       .long   xscale_flush_kern_dcache_area
        .long   xscale_dma_inv_range
        .long   xscale_dma_clean_range
        .long   xscale_dma_flush_range
@@ -392,7 +393,7 @@ ENTRY(xscale_80200_A0_A1_cache_fns)
        .long   xscale_flush_user_cache_range
        .long   xscale_coherent_kern_range
        .long   xscale_coherent_user_range
-       .long   xscale_flush_kern_dcache_page
+       .long   xscale_flush_kern_dcache_area
        .long   xscale_dma_flush_range
        .long   xscale_dma_clean_range
        .long   xscale_dma_flush_range
index 07b976da617418d32b6c0a7d2d84a4ffeb7a5ace..c3a74ce24ef6c16f101bb5898a0c23056e0ee9e8 100644 (file)
@@ -12,7 +12,7 @@
 #
 #   http://www.arm.linux.org.uk/developer/machines/?action=new
 #
-# Last update: Wed Nov 25 22:14:58 2009
+# Last update: Wed Dec 16 20:06:34 2009
 #
 # machine_is_xxx       CONFIG_xxxx             MACH_TYPE_xxx           number
 #
@@ -1776,6 +1776,7 @@ cybook3                   MACH_CYBOOK3            CYBOOK3                 1784
 wdg002                 MACH_WDG002             WDG002                  1785
 sg560adsl              MACH_SG560ADSL          SG560ADSL               1786
 nextio_n2800_ica       MACH_NEXTIO_N2800_ICA   NEXTIO_N2800_ICA        1787
+dove_db                        MACH_DOVE_DB            DOVE_DB                 1788
 marvell_newdb          MACH_MARVELL_NEWDB      MARVELL_NEWDB           1789
 vandihud               MACH_VANDIHUD           VANDIHUD                1790
 magx_e8                        MACH_MAGX_E8            MAGX_E8                 1791
@@ -2536,3 +2537,44 @@ c3ax03                   MACH_C3AX03             C3AX03                  2549
 mxt_td60               MACH_MXT_TD60           MXT_TD60                2550
 esyx                   MACH_ESYX               ESYX                    2551
 bulldog                        MACH_BULLDOG            BULLDOG                 2553
+derell_me2000          MACH_DERELL_ME2000      DERELL_ME2000           2554
+bcmring_base           MACH_BCMRING_BASE       BCMRING_BASE            2555
+bcmring_evm            MACH_BCMRING_EVM        BCMRING_EVM             2556
+bcmring_evm_jazz       MACH_BCMRING_EVM_JAZZ   BCMRING_EVM_JAZZ        2557
+bcmring_sp             MACH_BCMRING_SP         BCMRING_SP              2558
+bcmring_sv             MACH_BCMRING_SV         BCMRING_SV              2559
+bcmring_sv_jazz                MACH_BCMRING_SV_JAZZ    BCMRING_SV_JAZZ         2560
+bcmring_tablet         MACH_BCMRING_TABLET     BCMRING_TABLET          2561
+bcmring_vp             MACH_BCMRING_VP         BCMRING_VP              2562
+bcmring_evm_seikor     MACH_BCMRING_EVM_SEIKOR BCMRING_EVM_SEIKOR      2563
+bcmring_sp_wqvga       MACH_BCMRING_SP_WQVGA   BCMRING_SP_WQVGA        2564
+bcmring_custom         MACH_BCMRING_CUSTOM     BCMRING_CUSTOM          2565
+acer_s200              MACH_ACER_S200          ACER_S200               2566
+bt270                  MACH_BT270              BT270                   2567
+iseo                   MACH_ISEO               ISEO                    2568
+cezanne                        MACH_CEZANNE            CEZANNE                 2569
+lucca                  MACH_LUCCA              LUCCA                   2570
+supersmart             MACH_SUPERSMART         SUPERSMART              2571
+magnolia2              MACH_MAGNOLIA2          MAGNOLIA2               2573
+emxx                   MACH_EMXX               EMXX                    2574
+outlaw                 MACH_OUTLAW             OUTLAW                  2575
+riot_bei2              MACH_RIOT_BEI2          RIOT_BEI2               2576
+riot_vox               MACH_RIOT_VOX           RIOT_VOX                2577
+riot_x37               MACH_RIOT_X37           RIOT_X37                2578
+mega25mx               MACH_MEGA25MX           MEGA25MX                2579
+benzina2               MACH_BENZINA2           BENZINA2                2580
+ignite                 MACH_IGNITE             IGNITE                  2581
+foggia                 MACH_FOGGIA             FOGGIA                  2582
+arezzo                 MACH_AREZZO             AREZZO                  2583
+leica_skywalker                MACH_LEICA_SKYWALKER    LEICA_SKYWALKER         2584
+jacinto2_jamr          MACH_JACINTO2_JAMR      JACINTO2_JAMR           2585
+gts_nova               MACH_GTS_NOVA           GTS_NOVA                2586
+p3600                  MACH_P3600              P3600                   2587
+dlt2                   MACH_DLT2               DLT2                    2588
+df3120                 MACH_DF3120             DF3120                  2589
+ecucore_9g20           MACH_ECUCORE_9G20       ECUCORE_9G20            2590
+nautel_lpc3240         MACH_NAUTEL_LPC3240     NAUTEL_LPC3240          2591
+glacier                        MACH_GLACIER            GLACIER                 2592
+phrazer_bulldog                MACH_PHRAZER_BULLDOG    PHRAZER_BULLDOG         2593
+omap3_bulldog          MACH_OMAP3_BULLDOG      OMAP3_BULLDOG           2594
+pca101                 MACH_PCA101             PCA101                  2595
index 95ccbe377f9c91398dd0f20300d9dd430eb4deeb..bf28945c610d14cb933f2cb7fd50cdbc4946d27c 100644 (file)
@@ -998,6 +998,23 @@ config SENSORS_LIS3_SPI
          will be called lis3lv02d and a specific module for the SPI transport
          is called lis3lv02d_spi.
 
+config SENSORS_LIS3_I2C
+       tristate "STMicroeletronics LIS3LV02Dx three-axis digital accelerometer (I2C)"
+       depends on I2C && INPUT
+       select INPUT_POLLDEV
+       default n
+       help
+         This driver provides support for the LIS3LV02Dx accelerometer connected
+         via I2C. The accelerometer data is readable via
+         /sys/devices/platform/lis3lv02d.
+
+         This driver also provides an absolute input class device, allowing
+         the device to act as a pinball machine-esque joystick.
+
+         This driver can also be built as modules.  If so, the core module
+         will be called lis3lv02d and a specific module for the I2C transport
+         is called lis3lv02d_i2c.
+
 config SENSORS_APPLESMC
        tristate "Apple SMC (Motion sensor, light sensor, keyboard backlight)"
        depends on INPUT && X86
index 33c2ee105284124ee1247823fc391c4611577771..4131e253f96a17b551c6d5fccfb5323d73029296 100644 (file)
@@ -55,6 +55,7 @@ obj-$(CONFIG_SENSORS_IT87)    += it87.o
 obj-$(CONFIG_SENSORS_K8TEMP)   += k8temp.o
 obj-$(CONFIG_SENSORS_LIS3LV02D) += lis3lv02d.o hp_accel.o
 obj-$(CONFIG_SENSORS_LIS3_SPI) += lis3lv02d.o lis3lv02d_spi.o
+obj-$(CONFIG_SENSORS_LIS3_I2C) += lis3lv02d.o lis3lv02d_i2c.o
 obj-$(CONFIG_SENSORS_LM63)     += lm63.o
 obj-$(CONFIG_SENSORS_LM70)     += lm70.o
 obj-$(CONFIG_SENSORS_LM73)     += lm73.o
diff --git a/drivers/hwmon/lis3lv02d_i2c.c b/drivers/hwmon/lis3lv02d_i2c.c
new file mode 100644 (file)
index 0000000..dc1f540
--- /dev/null
@@ -0,0 +1,183 @@
+/*
+ * drivers/hwmon/lis3lv02d_i2c.c
+ *
+ * Implements I2C interface for lis3lv02d (STMicroelectronics) accelerometer.
+ * Driver is based on corresponding SPI driver written by Daniel Mack
+ * (lis3lv02d_spi.c (C) 2009 Daniel Mack <daniel@caiaq.de> ).
+ *
+ * Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+ *
+ * Contact: Samu Onkalo <samu.p.onkalo@nokia.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This 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., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/i2c.h>
+#include "lis3lv02d.h"
+
+#define DRV_NAME       "lis3lv02d_i2c"
+
+static inline s32 lis3_i2c_write(struct lis3lv02d *lis3, int reg, u8 value)
+{
+       struct i2c_client *c = lis3->bus_priv;
+       return i2c_smbus_write_byte_data(c, reg, value);
+}
+
+static inline s32 lis3_i2c_read(struct lis3lv02d *lis3, int reg, u8 *v)
+{
+       struct i2c_client *c = lis3->bus_priv;
+       *v = i2c_smbus_read_byte_data(c, reg);
+       return 0;
+}
+
+static int lis3_i2c_init(struct lis3lv02d *lis3)
+{
+       u8 reg;
+       int ret;
+
+       /* power up the device */
+       ret = lis3->read(lis3, CTRL_REG1, &reg);
+       if (ret < 0)
+               return ret;
+
+       reg |= CTRL1_PD0;
+       return lis3->write(lis3, CTRL_REG1, reg);
+}
+
+/* Default axis mapping but it can be overwritten by platform data */
+static struct axis_conversion lis3lv02d_axis_map = { LIS3_DEV_X,
+                                                    LIS3_DEV_Y,
+                                                    LIS3_DEV_Z };
+
+static int __devinit lis3lv02d_i2c_probe(struct i2c_client *client,
+                                       const struct i2c_device_id *id)
+{
+       int ret = 0;
+       struct lis3lv02d_platform_data *pdata = client->dev.platform_data;
+
+       if (pdata) {
+               if (pdata->axis_x)
+                       lis3lv02d_axis_map.x = pdata->axis_x;
+
+               if (pdata->axis_y)
+                       lis3lv02d_axis_map.y = pdata->axis_y;
+
+               if (pdata->axis_z)
+                       lis3lv02d_axis_map.z = pdata->axis_z;
+
+               if (pdata->setup_resources)
+                       ret = pdata->setup_resources();
+
+               if (ret)
+                       goto fail;
+       }
+
+       lis3_dev.pdata    = pdata;
+       lis3_dev.bus_priv = client;
+       lis3_dev.init     = lis3_i2c_init;
+       lis3_dev.read     = lis3_i2c_read;
+       lis3_dev.write    = lis3_i2c_write;
+       lis3_dev.irq      = client->irq;
+       lis3_dev.ac       = lis3lv02d_axis_map;
+
+       i2c_set_clientdata(client, &lis3_dev);
+       ret = lis3lv02d_init_device(&lis3_dev);
+fail:
+       return ret;
+}
+
+static int __devexit lis3lv02d_i2c_remove(struct i2c_client *client)
+{
+       struct lis3lv02d *lis3 = i2c_get_clientdata(client);
+       struct lis3lv02d_platform_data *pdata = client->dev.platform_data;
+
+       if (pdata && pdata->release_resources)
+               pdata->release_resources();
+
+       lis3lv02d_joystick_disable();
+       lis3lv02d_poweroff(lis3);
+
+       return lis3lv02d_remove_fs(&lis3_dev);
+}
+
+#ifdef CONFIG_PM
+static int lis3lv02d_i2c_suspend(struct i2c_client *client, pm_message_t mesg)
+{
+       struct lis3lv02d *lis3 = i2c_get_clientdata(client);
+
+       if (!lis3->pdata->wakeup_flags)
+               lis3lv02d_poweroff(lis3);
+       return 0;
+}
+
+static int lis3lv02d_i2c_resume(struct i2c_client *client)
+{
+       struct lis3lv02d *lis3 = i2c_get_clientdata(client);
+
+       if (!lis3->pdata->wakeup_flags)
+               lis3lv02d_poweron(lis3);
+       return 0;
+}
+
+static void lis3lv02d_i2c_shutdown(struct i2c_client *client)
+{
+       lis3lv02d_i2c_suspend(client, PMSG_SUSPEND);
+}
+#else
+#define lis3lv02d_i2c_suspend  NULL
+#define lis3lv02d_i2c_resume   NULL
+#define lis3lv02d_i2c_shutdown NULL
+#endif
+
+static const struct i2c_device_id lis3lv02d_id[] = {
+       {"lis3lv02d", 0 },
+       {}
+};
+
+MODULE_DEVICE_TABLE(i2c, lis3lv02d_id);
+
+static struct i2c_driver lis3lv02d_i2c_driver = {
+       .driver  = {
+               .name   = DRV_NAME,
+               .owner  = THIS_MODULE,
+       },
+       .suspend = lis3lv02d_i2c_suspend,
+       .shutdown = lis3lv02d_i2c_shutdown,
+       .resume = lis3lv02d_i2c_resume,
+       .probe  = lis3lv02d_i2c_probe,
+       .remove = __devexit_p(lis3lv02d_i2c_remove),
+       .id_table = lis3lv02d_id,
+};
+
+static int __init lis3lv02d_init(void)
+{
+       return i2c_add_driver(&lis3lv02d_i2c_driver);
+}
+
+static void __exit lis3lv02d_exit(void)
+{
+       i2c_del_driver(&lis3lv02d_i2c_driver);
+}
+
+MODULE_AUTHOR("Nokia Corporation");
+MODULE_DESCRIPTION("lis3lv02d I2C interface");
+MODULE_LICENSE("GPL");
+
+module_init(lis3lv02d_init);
+module_exit(lis3lv02d_exit);
index e4f599f20e38f5804da67013cc5936834de3e2e1..8a0e1ec95e4aaf03bf3d8a91fd6a6ef32db07ec7 100644 (file)
@@ -229,6 +229,12 @@ config LEDS_PWM
        help
          This option enables support for pwm driven LEDs
 
+config LEDS_REGULATOR
+       tristate "REGULATOR driven LED support"
+       depends on LEDS_CLASS && REGULATOR
+       help
+         This option enables support for regulator driven LEDs.
+
 config LEDS_BD2802
        tristate "LED driver for BD2802 RGB LED"
        depends on LEDS_CLASS && I2C
@@ -236,6 +242,33 @@ config LEDS_BD2802
          This option enables support for BD2802GU RGB LED driver chips
          accessed via the I2C bus.
 
+config LEDS_INTEL_SS4200
+       tristate "LED driver for Intel NAS SS4200 series"
+       depends on LEDS_CLASS && PCI && DMI
+       help
+         This option enables support for the Intel SS4200 series of
+         Network Attached Storage servers.  You may control the hard
+         drive or power LEDs on the front panel.  Using this driver
+         can stop the front LED from blinking after startup.
+
+config LEDS_LT3593
+       tristate "LED driver for LT3593 controllers"
+       depends on LEDS_CLASS && GENERIC_GPIO
+       help
+         This option enables support for LEDs driven by a Linear Technology
+         LT3593 controller. This controller uses a special one-wire pulse
+         coding protocol to set the brightness.
+
+config LEDS_ADP5520
+       tristate "LED Support for ADP5520/ADP5501 PMIC"
+       depends on LEDS_CLASS && PMIC_ADP5520
+       help
+         This option enables support for on-chip LED drivers found
+         on Analog Devices ADP5520/ADP5501 PMICs.
+
+         To compile this driver as a module, choose M here: the module will
+         be called leds-adp5520.
+
 comment "LED Triggers"
 
 config LEDS_TRIGGERS
index 46d72704d60651c43031a220f7dc416043b63774..9e63869d7c0d1bfd2e211d890f5b57371c4e1f65 100644 (file)
@@ -29,6 +29,10 @@ obj-$(CONFIG_LEDS_DA903X)            += leds-da903x.o
 obj-$(CONFIG_LEDS_WM831X_STATUS)       += leds-wm831x-status.o
 obj-$(CONFIG_LEDS_WM8350)              += leds-wm8350.o
 obj-$(CONFIG_LEDS_PWM)                 += leds-pwm.o
+obj-$(CONFIG_LEDS_REGULATOR)           += leds-regulator.o
+obj-$(CONFIG_LEDS_INTEL_SS4200)                += leds-ss4200.o
+obj-$(CONFIG_LEDS_LT3593)              += leds-lt3593.o
+obj-$(CONFIG_LEDS_ADP5520)             += leds-adp5520.o
 
 # LED SPI Drivers
 obj-$(CONFIG_LEDS_DAC124S085)          += leds-dac124s085.o
diff --git a/drivers/leds/leds-adp5520.c b/drivers/leds/leds-adp5520.c
new file mode 100644 (file)
index 0000000..a8f3159
--- /dev/null
@@ -0,0 +1,230 @@
+/*
+ * LEDs driver for Analog Devices ADP5520/ADP5501 MFD PMICs
+ *
+ * Copyright 2009 Analog Devices Inc.
+ *
+ * Loosely derived from leds-da903x:
+ * Copyright (C) 2008 Compulab, Ltd.
+ *     Mike Rapoport <mike@compulab.co.il>
+ *
+ * Copyright (C) 2006-2008 Marvell International Ltd.
+ *     Eric Miao <eric.miao@marvell.com>
+ *
+ * Licensed under the GPL-2 or later.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/leds.h>
+#include <linux/workqueue.h>
+#include <linux/mfd/adp5520.h>
+
+struct adp5520_led {
+       struct led_classdev     cdev;
+       struct work_struct      work;
+       struct device           *master;
+       enum led_brightness     new_brightness;
+       int                     id;
+       int                     flags;
+};
+
+static void adp5520_led_work(struct work_struct *work)
+{
+       struct adp5520_led *led = container_of(work, struct adp5520_led, work);
+       adp5520_write(led->master, ADP5520_LED1_CURRENT + led->id - 1,
+                        led->new_brightness >> 2);
+}
+
+static void adp5520_led_set(struct led_classdev *led_cdev,
+                          enum led_brightness value)
+{
+       struct adp5520_led *led;
+
+       led = container_of(led_cdev, struct adp5520_led, cdev);
+       led->new_brightness = value;
+       schedule_work(&led->work);
+}
+
+static int adp5520_led_setup(struct adp5520_led *led)
+{
+       struct device *dev = led->master;
+       int flags = led->flags;
+       int ret = 0;
+
+       switch (led->id) {
+       case FLAG_ID_ADP5520_LED1_ADP5501_LED0:
+               ret |= adp5520_set_bits(dev, ADP5520_LED_TIME,
+                                       (flags >> ADP5520_FLAG_OFFT_SHIFT) &
+                                       ADP5520_FLAG_OFFT_MASK);
+               ret |= adp5520_set_bits(dev, ADP5520_LED_CONTROL,
+                                       ADP5520_LED1_EN);
+               break;
+       case FLAG_ID_ADP5520_LED2_ADP5501_LED1:
+               ret |= adp5520_set_bits(dev,  ADP5520_LED_TIME,
+                                       ((flags >> ADP5520_FLAG_OFFT_SHIFT) &
+                                       ADP5520_FLAG_OFFT_MASK) << 2);
+               ret |= adp5520_clr_bits(dev, ADP5520_LED_CONTROL,
+                                        ADP5520_R3_MODE);
+               ret |= adp5520_set_bits(dev, ADP5520_LED_CONTROL,
+                                       ADP5520_LED2_EN);
+               break;
+       case FLAG_ID_ADP5520_LED3_ADP5501_LED2:
+               ret |= adp5520_set_bits(dev,  ADP5520_LED_TIME,
+                                       ((flags >> ADP5520_FLAG_OFFT_SHIFT) &
+                                       ADP5520_FLAG_OFFT_MASK) << 4);
+               ret |= adp5520_clr_bits(dev, ADP5520_LED_CONTROL,
+                                       ADP5520_C3_MODE);
+               ret |= adp5520_set_bits(dev, ADP5520_LED_CONTROL,
+                                       ADP5520_LED3_EN);
+               break;
+       }
+
+       return ret;
+}
+
+static int __devinit adp5520_led_prepare(struct platform_device *pdev)
+{
+       struct adp5520_leds_platform_data *pdata = pdev->dev.platform_data;
+       struct device *dev = pdev->dev.parent;
+       int ret = 0;
+
+       ret |= adp5520_write(dev, ADP5520_LED1_CURRENT, 0);
+       ret |= adp5520_write(dev, ADP5520_LED2_CURRENT, 0);
+       ret |= adp5520_write(dev, ADP5520_LED3_CURRENT, 0);
+       ret |= adp5520_write(dev, ADP5520_LED_TIME, pdata->led_on_time << 6);
+       ret |= adp5520_write(dev, ADP5520_LED_FADE, FADE_VAL(pdata->fade_in,
+                pdata->fade_out));
+
+       return ret;
+}
+
+static int __devinit adp5520_led_probe(struct platform_device *pdev)
+{
+       struct adp5520_leds_platform_data *pdata = pdev->dev.platform_data;
+       struct adp5520_led *led, *led_dat;
+       struct led_info *cur_led;
+       int ret, i;
+
+       if (pdata == NULL) {
+               dev_err(&pdev->dev, "missing platform data\n");
+               return -ENODEV;
+       }
+
+       if (pdata->num_leds > ADP5520_01_MAXLEDS) {
+               dev_err(&pdev->dev, "can't handle more than %d LEDS\n",
+                                ADP5520_01_MAXLEDS);
+               return -EFAULT;
+       }
+
+       led = kzalloc(sizeof(*led) * pdata->num_leds, GFP_KERNEL);
+       if (led == NULL) {
+               dev_err(&pdev->dev, "failed to alloc memory\n");
+               return -ENOMEM;
+       }
+
+       ret = adp5520_led_prepare(pdev);
+
+       if (ret) {
+               dev_err(&pdev->dev, "failed to write\n");
+               goto err_free;
+       }
+
+       for (i = 0; i < pdata->num_leds; ++i) {
+               cur_led = &pdata->leds[i];
+               led_dat = &led[i];
+
+               led_dat->cdev.name = cur_led->name;
+               led_dat->cdev.default_trigger = cur_led->default_trigger;
+               led_dat->cdev.brightness_set = adp5520_led_set;
+               led_dat->cdev.brightness = LED_OFF;
+
+               if (cur_led->flags & ADP5520_FLAG_LED_MASK)
+                       led_dat->flags = cur_led->flags;
+               else
+                       led_dat->flags = i + 1;
+
+               led_dat->id = led_dat->flags & ADP5520_FLAG_LED_MASK;
+
+               led_dat->master = pdev->dev.parent;
+               led_dat->new_brightness = LED_OFF;
+
+               INIT_WORK(&led_dat->work, adp5520_led_work);
+
+               ret = led_classdev_register(led_dat->master, &led_dat->cdev);
+               if (ret) {
+                       dev_err(&pdev->dev, "failed to register LED %d\n",
+                               led_dat->id);
+                       goto err;
+               }
+
+               ret = adp5520_led_setup(led_dat);
+               if (ret) {
+                       dev_err(&pdev->dev, "failed to write\n");
+                       i++;
+                       goto err;
+               }
+       }
+
+       platform_set_drvdata(pdev, led);
+       return 0;
+
+err:
+       if (i > 0) {
+               for (i = i - 1; i >= 0; i--) {
+                       led_classdev_unregister(&led[i].cdev);
+                       cancel_work_sync(&led[i].work);
+               }
+       }
+
+err_free:
+       kfree(led);
+       return ret;
+}
+
+static int __devexit adp5520_led_remove(struct platform_device *pdev)
+{
+       struct adp5520_leds_platform_data *pdata = pdev->dev.platform_data;
+       struct adp5520_led *led;
+       int i;
+
+       led = platform_get_drvdata(pdev);
+
+       adp5520_clr_bits(led->master, ADP5520_LED_CONTROL,
+                ADP5520_LED1_EN | ADP5520_LED2_EN | ADP5520_LED3_EN);
+
+       for (i = 0; i < pdata->num_leds; i++) {
+               led_classdev_unregister(&led[i].cdev);
+               cancel_work_sync(&led[i].work);
+       }
+
+       kfree(led);
+       return 0;
+}
+
+static struct platform_driver adp5520_led_driver = {
+       .driver = {
+               .name   = "adp5520-led",
+               .owner  = THIS_MODULE,
+       },
+       .probe          = adp5520_led_probe,
+       .remove         = __devexit_p(adp5520_led_remove),
+};
+
+static int __init adp5520_led_init(void)
+{
+       return platform_driver_register(&adp5520_led_driver);
+}
+module_init(adp5520_led_init);
+
+static void __exit adp5520_led_exit(void)
+{
+       platform_driver_unregister(&adp5520_led_driver);
+}
+module_exit(adp5520_led_exit);
+
+MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>");
+MODULE_DESCRIPTION("LEDS ADP5520(01) Driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:adp5520-led");
index 731d4eef342590dca6566f229d60d7f8add6b872..f59ffadf51253991451adbbc7b51eb25142ad937 100644 (file)
 #include <linux/module.h>
 #include <linux/platform_device.h>
 #include <linux/string.h>
+#include <linux/pci.h>
 
 static int force = 0;
 module_param(force, bool, 0444);
 MODULE_PARM_DESC(force, "Assume system has ALIX.2/ALIX.3 style LEDs");
 
+#define MSR_LBAR_GPIO          0x5140000C
+#define CS5535_GPIO_SIZE       256
+
+static u32 gpio_base;
+
+static struct pci_device_id divil_pci[] = {
+       { PCI_DEVICE(PCI_VENDOR_ID_NS,  PCI_DEVICE_ID_NS_CS5535_ISA) },
+       { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_CS5536_ISA) },
+       { } /* NULL entry */
+};
+MODULE_DEVICE_TABLE(pci, divil_pci);
+
 struct alix_led {
        struct led_classdev cdev;
        unsigned short port;
@@ -30,9 +43,9 @@ static void alix_led_set(struct led_classdev *led_cdev,
                container_of(led_cdev, struct alix_led, cdev);
 
        if (brightness)
-               outl(led_dev->on_value, led_dev->port);
+               outl(led_dev->on_value, gpio_base + led_dev->port);
        else
-               outl(led_dev->off_value, led_dev->port);
+               outl(led_dev->off_value, gpio_base + led_dev->port);
 }
 
 static struct alix_led alix_leds[] = {
@@ -41,7 +54,7 @@ static struct alix_led alix_leds[] = {
                        .name = "alix:1",
                        .brightness_set = alix_led_set,
                },
-               .port = 0x6100,
+               .port = 0x00,
                .on_value = 1 << 22,
                .off_value = 1 << 6,
        },
@@ -50,7 +63,7 @@ static struct alix_led alix_leds[] = {
                        .name = "alix:2",
                        .brightness_set = alix_led_set,
                },
-               .port = 0x6180,
+               .port = 0x80,
                .on_value = 1 << 25,
                .off_value = 1 << 9,
        },
@@ -59,7 +72,7 @@ static struct alix_led alix_leds[] = {
                        .name = "alix:3",
                        .brightness_set = alix_led_set,
                },
-               .port = 0x6180,
+               .port = 0x80,
                .on_value = 1 << 27,
                .off_value = 1 << 11,
        },
@@ -101,64 +114,104 @@ static struct platform_driver alix_led_driver = {
        },
 };
 
-static int __init alix_present(void)
+static int __init alix_present(unsigned long bios_phys,
+                               const char *alix_sig,
+                               size_t alix_sig_len)
 {
-       const unsigned long bios_phys = 0x000f0000;
        const size_t bios_len = 0x00010000;
-       const char alix_sig[] = "PC Engines ALIX.";
-       const size_t alix_sig_len = sizeof(alix_sig) - 1;
-
        const char *bios_virt;
        const char *scan_end;
        const char *p;
-       int ret = 0;
+       char name[64];
 
        if (force) {
                printk(KERN_NOTICE "%s: forced to skip BIOS test, "
                       "assume system has ALIX.2 style LEDs\n",
                       KBUILD_MODNAME);
-               ret = 1;
-               goto out;
+               return 1;
        }
 
        bios_virt = phys_to_virt(bios_phys);
        scan_end = bios_virt + bios_len - (alix_sig_len + 2);
        for (p = bios_virt; p < scan_end; p++) {
                const char *tail;
+               char *a;
 
-               if (memcmp(p, alix_sig, alix_sig_len) != 0) {
+               if (memcmp(p, alix_sig, alix_sig_len) != 0)
                        continue;
-               }
+
+               memcpy(name, p, sizeof(name));
+
+               /* remove the first \0 character from string */
+               a = strchr(name, '\0');
+               if (a)
+                       *a = ' ';
+
+               /* cut the string at a newline */
+               a = strchr(name, '\r');
+               if (a)
+                       *a = '\0';
 
                tail = p + alix_sig_len;
-               if ((tail[0] == '2' || tail[0] == '3') && tail[1] == '\0') {
+               if ((tail[0] == '2' || tail[0] == '3')) {
                        printk(KERN_INFO
                               "%s: system is recognized as \"%s\"\n",
-                              KBUILD_MODNAME, p);
-                       ret = 1;
-                       break;
+                              KBUILD_MODNAME, name);
+                       return 1;
                }
        }
 
-out:
-       return ret;
+       return 0;
 }
 
 static struct platform_device *pdev;
 
-static int __init alix_led_init(void)
+static int __init alix_pci_led_init(void)
 {
-       int ret;
+       u32 low, hi;
 
-       if (!alix_present()) {
-               ret = -ENODEV;
-               goto out;
+       if (pci_dev_present(divil_pci) == 0) {
+               printk(KERN_WARNING KBUILD_MODNAME": DIVIL not found\n");
+               return -ENODEV;
        }
 
-       /* enable output on GPIO for LED 1,2,3 */
-       outl(1 << 6, 0x6104);
-       outl(1 << 9, 0x6184);
-       outl(1 << 11, 0x6184);
+       /* Grab the GPIO I/O range */
+       rdmsr(MSR_LBAR_GPIO, low, hi);
+
+       /* Check the mask and whether GPIO is enabled (sanity check) */
+       if (hi != 0x0000f001) {
+               printk(KERN_WARNING KBUILD_MODNAME": GPIO not enabled\n");
+               return -ENODEV;
+       }
+
+       /* Mask off the IO base address */
+       gpio_base = low & 0x0000ff00;
+
+       if (!request_region(gpio_base, CS5535_GPIO_SIZE, KBUILD_MODNAME)) {
+               printk(KERN_ERR KBUILD_MODNAME": can't allocate I/O for GPIO\n");
+               return -ENODEV;
+       }
+
+       /* Set GPIO function to output */
+       outl(1 << 6, gpio_base + 0x04);
+       outl(1 << 9, gpio_base + 0x84);
+       outl(1 << 11, gpio_base + 0x84);
+
+       return 0;
+}
+
+static int __init alix_led_init(void)
+{
+       int ret = -ENODEV;
+       const char tinybios_sig[] = "PC Engines ALIX.";
+       const char coreboot_sig[] = "PC Engines\0ALIX.";
+
+       if (alix_present(0xf0000, tinybios_sig, sizeof(tinybios_sig) - 1) ||
+           alix_present(0x500, coreboot_sig, sizeof(coreboot_sig) - 1))
+               ret = alix_pci_led_init();
+
+       if (ret < 0)
+               return ret;
 
        pdev = platform_device_register_simple(KBUILD_MODNAME, -1, NULL, 0);
        if (!IS_ERR(pdev)) {
@@ -168,7 +221,6 @@ static int __init alix_led_init(void)
        } else
                ret = PTR_ERR(pdev);
 
-out:
        return ret;
 }
 
@@ -176,6 +228,7 @@ static void __exit alix_led_exit(void)
 {
        platform_device_unregister(pdev);
        platform_driver_unregister(&alix_led_driver);
+       release_region(gpio_base, CS5535_GPIO_SIZE);
 }
 
 module_init(alix_led_init);
index 8816806accd24a380c3877785f3bd71e5ddbbbf7..da5fb016b1a550fabfee5114bb11727a22c01749 100644 (file)
@@ -31,7 +31,7 @@ static struct led_classdev qube_front_led = {
        .name                   = "qube::front",
        .brightness             = LED_FULL,
        .brightness_set         = qube_front_led_set,
-       .default_trigger        = "ide-disk",
+       .default_trigger        = "default-on",
 };
 
 static int __devinit cobalt_qube_led_probe(struct platform_device *pdev)
@@ -43,7 +43,7 @@ static int __devinit cobalt_qube_led_probe(struct platform_device *pdev)
        if (!res)
                return -EBUSY;
 
-       led_port = ioremap(res->start, res->end - res->start + 1);
+       led_port = ioremap(res->start, resource_size(res));
        if (!led_port)
                return -ENOMEM;
 
index defc212105f3e4b12c216fadebbd82d1b9ab31ec..438d48384636f2d188e06a1ee648c41c190ce00c 100644 (file)
@@ -84,7 +84,7 @@ static int __devinit cobalt_raq_led_probe(struct platform_device *pdev)
        if (!res)
                return -EBUSY;
 
-       led_port = ioremap(res->start, res->end - res->start + 1);
+       led_port = ioremap(res->start, resource_size(res));
        if (!led_port)
                return -ENOMEM;
 
diff --git a/drivers/leds/leds-lt3593.c b/drivers/leds/leds-lt3593.c
new file mode 100644 (file)
index 0000000..fee40a8
--- /dev/null
@@ -0,0 +1,217 @@
+/*
+ * LEDs driver for LT3593 controllers
+ *
+ * See the datasheet at http://cds.linear.com/docs/Datasheet/3593f.pdf
+ *
+ * Copyright (c) 2009 Daniel Mack <daniel@caiaq.de>
+ *
+ * Based on leds-gpio.c,
+ *
+ *   Copyright (C) 2007 8D Technologies inc.
+ *   Raphael Assenat <raph@8d.com>
+ *   Copyright (C) 2008 Freescale Semiconductor, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/leds.h>
+#include <linux/workqueue.h>
+#include <linux/delay.h>
+#include <linux/gpio.h>
+
+struct lt3593_led_data {
+       struct led_classdev cdev;
+       unsigned gpio;
+       struct work_struct work;
+       u8 new_level;
+};
+
+static void lt3593_led_work(struct work_struct *work)
+{
+       int pulses;
+       struct lt3593_led_data *led_dat =
+               container_of(work, struct lt3593_led_data, work);
+
+       /*
+        * The LT3593 resets its internal current level register to the maximum
+        * level on the first falling edge on the control pin. Each following
+        * falling edge decreases the current level by 625uA. Up to 32 pulses
+        * can be sent, so the maximum power reduction is 20mA.
+        * After a timeout of 128us, the value is taken from the register and
+        * applied is to the output driver.
+        */
+
+       if (led_dat->new_level == 0) {
+               gpio_set_value_cansleep(led_dat->gpio, 0);
+               return;
+       }
+
+       pulses = 32 - (led_dat->new_level * 32) / 255;
+
+       if (pulses == 0) {
+               gpio_set_value_cansleep(led_dat->gpio, 0);
+               mdelay(1);
+               gpio_set_value_cansleep(led_dat->gpio, 1);
+               return;
+       }
+
+       gpio_set_value_cansleep(led_dat->gpio, 1);
+
+       while (pulses--) {
+               gpio_set_value_cansleep(led_dat->gpio, 0);
+               udelay(1);
+               gpio_set_value_cansleep(led_dat->gpio, 1);
+               udelay(1);
+       }
+}
+
+static void lt3593_led_set(struct led_classdev *led_cdev,
+       enum led_brightness value)
+{
+       struct lt3593_led_data *led_dat =
+               container_of(led_cdev, struct lt3593_led_data, cdev);
+
+       led_dat->new_level = value;
+       schedule_work(&led_dat->work);
+}
+
+static int __devinit create_lt3593_led(const struct gpio_led *template,
+       struct lt3593_led_data *led_dat, struct device *parent)
+{
+       int ret, state;
+
+       /* skip leds on GPIOs that aren't available */
+       if (!gpio_is_valid(template->gpio)) {
+               printk(KERN_INFO "%s: skipping unavailable LT3593 LED at gpio %d (%s)\n",
+                               KBUILD_MODNAME, template->gpio, template->name);
+               return 0;
+       }
+
+       ret = gpio_request(template->gpio, template->name);
+       if (ret < 0)
+               return ret;
+
+       led_dat->cdev.name = template->name;
+       led_dat->cdev.default_trigger = template->default_trigger;
+       led_dat->gpio = template->gpio;
+
+       led_dat->cdev.brightness_set = lt3593_led_set;
+
+       state = (template->default_state == LEDS_GPIO_DEFSTATE_ON);
+       led_dat->cdev.brightness = state ? LED_FULL : LED_OFF;
+
+       if (!template->retain_state_suspended)
+               led_dat->cdev.flags |= LED_CORE_SUSPENDRESUME;
+
+       ret = gpio_direction_output(led_dat->gpio, state);
+       if (ret < 0)
+               goto err;
+
+       INIT_WORK(&led_dat->work, lt3593_led_work);
+
+       ret = led_classdev_register(parent, &led_dat->cdev);
+       if (ret < 0)
+               goto err;
+
+       printk(KERN_INFO "%s: registered LT3593 LED '%s' at GPIO %d\n",
+               KBUILD_MODNAME, template->name, template->gpio);
+
+       return 0;
+
+err:
+       gpio_free(led_dat->gpio);
+       return ret;
+}
+
+static void delete_lt3593_led(struct lt3593_led_data *led)
+{
+       if (!gpio_is_valid(led->gpio))
+               return;
+
+       led_classdev_unregister(&led->cdev);
+       cancel_work_sync(&led->work);
+       gpio_free(led->gpio);
+}
+
+static int __devinit lt3593_led_probe(struct platform_device *pdev)
+{
+       struct gpio_led_platform_data *pdata = pdev->dev.platform_data;
+       struct lt3593_led_data *leds_data;
+       int i, ret = 0;
+
+       if (!pdata)
+               return -EBUSY;
+
+       leds_data = kzalloc(sizeof(struct lt3593_led_data) * pdata->num_leds,
+                               GFP_KERNEL);
+       if (!leds_data)
+               return -ENOMEM;
+
+       for (i = 0; i < pdata->num_leds; i++) {
+               ret = create_lt3593_led(&pdata->leds[i], &leds_data[i],
+                                     &pdev->dev);
+               if (ret < 0)
+                       goto err;
+       }
+
+       platform_set_drvdata(pdev, leds_data);
+
+       return 0;
+
+err:
+       for (i = i - 1; i >= 0; i--)
+               delete_lt3593_led(&leds_data[i]);
+
+       kfree(leds_data);
+
+       return ret;
+}
+
+static int __devexit lt3593_led_remove(struct platform_device *pdev)
+{
+       int i;
+       struct gpio_led_platform_data *pdata = pdev->dev.platform_data;
+       struct lt3593_led_data *leds_data;
+
+       leds_data = platform_get_drvdata(pdev);
+
+       for (i = 0; i < pdata->num_leds; i++)
+               delete_lt3593_led(&leds_data[i]);
+
+       kfree(leds_data);
+
+       return 0;
+}
+
+static struct platform_driver lt3593_led_driver = {
+       .probe          = lt3593_led_probe,
+       .remove         = __devexit_p(lt3593_led_remove),
+       .driver         = {
+               .name   = "leds-lt3593",
+               .owner  = THIS_MODULE,
+       },
+};
+
+MODULE_ALIAS("platform:leds-lt3593");
+
+static int __init lt3593_led_init(void)
+{
+       return platform_driver_register(&lt3593_led_driver);
+}
+
+static void __exit lt3593_led_exit(void)
+{
+       platform_driver_unregister(&lt3593_led_driver);
+}
+
+module_init(lt3593_led_init);
+module_exit(lt3593_led_exit);
+
+MODULE_AUTHOR("Daniel Mack <daniel@caiaq.de>");
+MODULE_DESCRIPTION("LED driver for LT3593 controllers");
+MODULE_LICENSE("GPL");
index cdfdc8714e1056962506519ee56d63d043410f3f..88b1dd091cfb9f3887ec052f75a3f4e0620d69ad 100644 (file)
@@ -27,7 +27,6 @@ struct led_pwm_data {
        struct pwm_device       *pwm;
        unsigned int            active_low;
        unsigned int            period;
-       unsigned int            max_brightness;
 };
 
 static void led_pwm_set(struct led_classdev *led_cdev,
@@ -35,7 +34,7 @@ static void led_pwm_set(struct led_classdev *led_cdev,
 {
        struct led_pwm_data *led_dat =
                container_of(led_cdev, struct led_pwm_data, cdev);
-       unsigned int max = led_dat->max_brightness;
+       unsigned int max = led_dat->cdev.max_brightness;
        unsigned int period =  led_dat->period;
 
        if (brightness == 0) {
@@ -77,10 +76,10 @@ static int led_pwm_probe(struct platform_device *pdev)
                led_dat->cdev.name = cur_led->name;
                led_dat->cdev.default_trigger = cur_led->default_trigger;
                led_dat->active_low = cur_led->active_low;
-               led_dat->max_brightness = cur_led->max_brightness;
                led_dat->period = cur_led->pwm_period_ns;
                led_dat->cdev.brightness_set = led_pwm_set;
                led_dat->cdev.brightness = LED_OFF;
+               led_dat->cdev.max_brightness = cur_led->max_brightness;
                led_dat->cdev.flags |= LED_CORE_SUSPENDRESUME;
 
                ret = led_classdev_register(&pdev->dev, &led_dat->cdev);
diff --git a/drivers/leds/leds-regulator.c b/drivers/leds/leds-regulator.c
new file mode 100644 (file)
index 0000000..7f00de3
--- /dev/null
@@ -0,0 +1,242 @@
+/*
+ * leds-regulator.c - LED class driver for regulator driven LEDs.
+ *
+ * Copyright (C) 2009 Antonio Ospite <ospite@studenti.unina.it>
+ *
+ * Inspired by leds-wm8350 driver.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/err.h>
+#include <linux/workqueue.h>
+#include <linux/leds.h>
+#include <linux/leds-regulator.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/consumer.h>
+
+#define to_regulator_led(led_cdev) \
+       container_of(led_cdev, struct regulator_led, cdev)
+
+struct regulator_led {
+       struct led_classdev cdev;
+       enum led_brightness value;
+       int enabled;
+       struct mutex mutex;
+       struct work_struct work;
+
+       struct regulator *vcc;
+};
+
+static inline int led_regulator_get_max_brightness(struct regulator *supply)
+{
+       int ret;
+       int voltage = regulator_list_voltage(supply, 0);
+
+       if (voltage <= 0)
+               return 1;
+
+       /* even if regulator can't change voltages,
+        * we still assume it can change status
+        * and the LED can be turned on and off.
+        */
+       ret = regulator_set_voltage(supply, voltage, voltage);
+       if (ret < 0)
+               return 1;
+
+       return regulator_count_voltages(supply);
+}
+
+static int led_regulator_get_voltage(struct regulator *supply,
+               enum led_brightness brightness)
+{
+       if (brightness == 0)
+               return -EINVAL;
+
+       return regulator_list_voltage(supply, brightness - 1);
+}
+
+
+static void regulator_led_enable(struct regulator_led *led)
+{
+       int ret;
+
+       if (led->enabled)
+               return;
+
+       ret = regulator_enable(led->vcc);
+       if (ret != 0) {
+               dev_err(led->cdev.dev, "Failed to enable vcc: %d\n", ret);
+               return;
+       }
+
+       led->enabled = 1;
+}
+
+static void regulator_led_disable(struct regulator_led *led)
+{
+       int ret;
+
+       if (!led->enabled)
+               return;
+
+       ret = regulator_disable(led->vcc);
+       if (ret != 0) {
+               dev_err(led->cdev.dev, "Failed to disable vcc: %d\n", ret);
+               return;
+       }
+
+       led->enabled = 0;
+}
+
+static void regulator_led_set_value(struct regulator_led *led)
+{
+       int voltage;
+       int ret;
+
+       mutex_lock(&led->mutex);
+
+       if (led->value == LED_OFF) {
+               regulator_led_disable(led);
+               goto out;
+       }
+
+       if (led->cdev.max_brightness > 1) {
+               voltage = led_regulator_get_voltage(led->vcc, led->value);
+               dev_dbg(led->cdev.dev, "brightness: %d voltage: %d\n",
+                               led->value, voltage);
+
+               ret = regulator_set_voltage(led->vcc, voltage, voltage);
+               if (ret != 0)
+                       dev_err(led->cdev.dev, "Failed to set voltage %d: %d\n",
+                               voltage, ret);
+       }
+
+       regulator_led_enable(led);
+
+out:
+       mutex_unlock(&led->mutex);
+}
+
+static void led_work(struct work_struct *work)
+{
+       struct regulator_led *led;
+
+       led = container_of(work, struct regulator_led, work);
+       regulator_led_set_value(led);
+}
+
+static void regulator_led_brightness_set(struct led_classdev *led_cdev,
+                          enum led_brightness value)
+{
+       struct regulator_led *led = to_regulator_led(led_cdev);
+
+       led->value = value;
+       schedule_work(&led->work);
+}
+
+static int __devinit regulator_led_probe(struct platform_device *pdev)
+{
+       struct led_regulator_platform_data *pdata = pdev->dev.platform_data;
+       struct regulator_led *led;
+       struct regulator *vcc;
+       int ret = 0;
+
+       if (pdata == NULL) {
+               dev_err(&pdev->dev, "no platform data\n");
+               return -ENODEV;
+       }
+
+       vcc = regulator_get_exclusive(&pdev->dev, "vled");
+       if (IS_ERR(vcc)) {
+               dev_err(&pdev->dev, "Cannot get vcc for %s\n", pdata->name);
+               return PTR_ERR(vcc);
+       }
+
+       led = kzalloc(sizeof(*led), GFP_KERNEL);
+       if (led == NULL) {
+               ret = -ENOMEM;
+               goto err_vcc;
+       }
+
+       led->cdev.max_brightness = led_regulator_get_max_brightness(vcc);
+       if (pdata->brightness > led->cdev.max_brightness) {
+               dev_err(&pdev->dev, "Invalid default brightness %d\n",
+                               pdata->brightness);
+               ret = -EINVAL;
+               goto err_led;
+       }
+       led->value = pdata->brightness;
+
+       led->cdev.brightness_set = regulator_led_brightness_set;
+       led->cdev.name = pdata->name;
+       led->cdev.flags |= LED_CORE_SUSPENDRESUME;
+       led->vcc = vcc;
+
+       mutex_init(&led->mutex);
+       INIT_WORK(&led->work, led_work);
+
+       platform_set_drvdata(pdev, led);
+
+       ret = led_classdev_register(&pdev->dev, &led->cdev);
+       if (ret < 0) {
+               cancel_work_sync(&led->work);
+               goto err_led;
+       }
+
+       /* to expose the default value to userspace */
+       led->cdev.brightness = led->value;
+
+       /* Set the default led status */
+       regulator_led_set_value(led);
+
+       return 0;
+
+err_led:
+       kfree(led);
+err_vcc:
+       regulator_put(vcc);
+       return ret;
+}
+
+static int __devexit regulator_led_remove(struct platform_device *pdev)
+{
+       struct regulator_led *led = platform_get_drvdata(pdev);
+
+       led_classdev_unregister(&led->cdev);
+       cancel_work_sync(&led->work);
+       regulator_led_disable(led);
+       regulator_put(led->vcc);
+       kfree(led);
+       return 0;
+}
+
+static struct platform_driver regulator_led_driver = {
+       .driver = {
+                  .name  = "leds-regulator",
+                  .owner = THIS_MODULE,
+                  },
+       .probe  = regulator_led_probe,
+       .remove = __devexit_p(regulator_led_remove),
+};
+
+static int __init regulator_led_init(void)
+{
+       return platform_driver_register(&regulator_led_driver);
+}
+module_init(regulator_led_init);
+
+static void __exit regulator_led_exit(void)
+{
+       platform_driver_unregister(&regulator_led_driver);
+}
+module_exit(regulator_led_exit);
+
+MODULE_AUTHOR("Antonio Ospite <ospite@studenti.unina.it>");
+MODULE_DESCRIPTION("Regulator driven LED driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:leds-regulator");
diff --git a/drivers/leds/leds-ss4200.c b/drivers/leds/leds-ss4200.c
new file mode 100644 (file)
index 0000000..97f0498
--- /dev/null
@@ -0,0 +1,556 @@
+/*
+ * SS4200-E Hardware API
+ * Copyright (c) 2009, Intel Corporation.
+ * Copyright IBM Corporation, 2009
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Author: Dave Hansen <dave@sr71.net>
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/dmi.h>
+#include <linux/init.h>
+#include <linux/ioport.h>
+#include <linux/kernel.h>
+#include <linux/leds.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/types.h>
+#include <linux/uaccess.h>
+
+MODULE_AUTHOR("Rodney Girod <rgirod@confocus.com>, Dave Hansen <dave@sr71.net>");
+MODULE_DESCRIPTION("Intel NAS/Home Server ICH7 GPIO Driver");
+MODULE_LICENSE("GPL");
+
+/*
+ * ICH7 LPC/GPIO PCI Config register offsets
+ */
+#define PMBASE         0x040
+#define GPIO_BASE      0x048
+#define GPIO_CTRL      0x04c
+#define GPIO_EN                0x010
+
+/*
+ * The ICH7 GPIO register block is 64 bytes in size.
+ */
+#define ICH7_GPIO_SIZE 64
+
+/*
+ * Define register offsets within the ICH7 register block.
+ */
+#define GPIO_USE_SEL   0x000
+#define GP_IO_SEL      0x004
+#define GP_LVL         0x00c
+#define GPO_BLINK      0x018
+#define GPI_INV                0x030
+#define GPIO_USE_SEL2  0x034
+#define GP_IO_SEL2     0x038
+#define GP_LVL2                0x03c
+
+/*
+ * PCI ID of the Intel ICH7 LPC Device within which the GPIO block lives.
+ */
+static struct pci_device_id ich7_lpc_pci_id[] =
+{
+       { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH7_0) },
+       { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH7_1) },
+       { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH7_30) },
+       { } /* NULL entry */
+};
+
+MODULE_DEVICE_TABLE(pci, ich7_lpc_pci_id);
+
+static int __init ss4200_led_dmi_callback(const struct dmi_system_id *id)
+{
+       pr_info("detected '%s'\n", id->ident);
+       return 1;
+}
+
+static unsigned int __initdata nodetect;
+module_param_named(nodetect, nodetect, bool, 0);
+MODULE_PARM_DESC(nodetect, "Skip DMI-based hardware detection");
+
+/*
+ * struct nas_led_whitelist - List of known good models
+ *
+ * Contains the known good models this driver is compatible with.
+ * When adding a new model try to be as strict as possible. This
+ * makes it possible to keep the false positives (the model is
+ * detected as working, but in reality it is not) as low as
+ * possible.
+ */
+static struct dmi_system_id __initdata nas_led_whitelist[] = {
+       {
+               .callback = ss4200_led_dmi_callback,
+               .ident = "Intel SS4200-E",
+               .matches = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "Intel"),
+                       DMI_MATCH(DMI_PRODUCT_NAME, "SS4200-E"),
+                       DMI_MATCH(DMI_PRODUCT_VERSION, "1.00.00")
+               }
+       },
+};
+
+/*
+ * Base I/O address assigned to the Power Management register block
+ */
+static u32 g_pm_io_base;
+
+/*
+ * Base I/O address assigned to the ICH7 GPIO register block
+ */
+static u32 nas_gpio_io_base;
+
+/*
+ * When we successfully register a region, we are returned a resource.
+ * We use these to identify which regions we need to release on our way
+ * back out.
+ */
+static struct resource *gp_gpio_resource;
+
+struct nasgpio_led {
+       char *name;
+       u32 gpio_bit;
+       struct led_classdev led_cdev;
+};
+
+/*
+ * gpio_bit(s) are the ICH7 GPIO bit assignments
+ */
+static struct nasgpio_led nasgpio_leds[] = {
+       { .name = "hdd1:blue:sata",     .gpio_bit = 0 },
+       { .name = "hdd1:amber:sata",    .gpio_bit = 1 },
+       { .name = "hdd2:blue:sata",     .gpio_bit = 2 },
+       { .name = "hdd2:amber:sata",    .gpio_bit = 3 },
+       { .name = "hdd3:blue:sata",     .gpio_bit = 4 },
+       { .name = "hdd3:amber:sata",    .gpio_bit = 5 },
+       { .name = "hdd4:blue:sata",     .gpio_bit = 6 },
+       { .name = "hdd4:amber:sata",    .gpio_bit = 7 },
+       { .name = "power:blue:power",   .gpio_bit = 27},
+       { .name = "power:amber:power",  .gpio_bit = 28},
+};
+
+#define NAS_RECOVERY   0x00000400      /* GPIO10 */
+
+static struct nasgpio_led *
+led_classdev_to_nasgpio_led(struct led_classdev *led_cdev)
+{
+       return container_of(led_cdev, struct nasgpio_led, led_cdev);
+}
+
+static struct nasgpio_led *get_led_named(char *name)
+{
+       int i;
+       for (i = 0; i < ARRAY_SIZE(nasgpio_leds); i++) {
+               if (strcmp(nasgpio_leds[i].name, name))
+                       continue;
+               return &nasgpio_leds[i];
+       }
+       return NULL;
+}
+
+/*
+ * This protects access to the gpio ports.
+ */
+static DEFINE_SPINLOCK(nasgpio_gpio_lock);
+
+/*
+ * There are two gpio ports, one for blinking and the other
+ * for power.  @port tells us if we're doing blinking or
+ * power control.
+ *
+ * Caller must hold nasgpio_gpio_lock
+ */
+static void __nasgpio_led_set_attr(struct led_classdev *led_cdev,
+                                  u32 port, u32 value)
+{
+       struct nasgpio_led *led = led_classdev_to_nasgpio_led(led_cdev);
+       u32 gpio_out;
+
+       gpio_out = inl(nas_gpio_io_base + port);
+       if (value)
+               gpio_out |= (1<<led->gpio_bit);
+       else
+               gpio_out &= ~(1<<led->gpio_bit);
+
+       outl(gpio_out, nas_gpio_io_base + port);
+}
+
+static void nasgpio_led_set_attr(struct led_classdev *led_cdev,
+                                u32 port, u32 value)
+{
+       spin_lock(&nasgpio_gpio_lock);
+       __nasgpio_led_set_attr(led_cdev, port, value);
+       spin_unlock(&nasgpio_gpio_lock);
+}
+
+u32 nasgpio_led_get_attr(struct led_classdev *led_cdev, u32 port)
+{
+       struct nasgpio_led *led = led_classdev_to_nasgpio_led(led_cdev);
+       u32 gpio_in;
+
+       spin_lock(&nasgpio_gpio_lock);
+       gpio_in = inl(nas_gpio_io_base + port);
+       spin_unlock(&nasgpio_gpio_lock);
+       if (gpio_in & (1<<led->gpio_bit))
+               return 1;
+       return 0;
+}
+
+/*
+ * There is actual brightness control in the hardware,
+ * but it is via smbus commands and not implemented
+ * in this driver.
+ */
+static void nasgpio_led_set_brightness(struct led_classdev *led_cdev,
+                                      enum led_brightness brightness)
+{
+       u32 setting = 0;
+       if (brightness >= LED_HALF)
+               setting = 1;
+       /*
+        * Hold the lock across both operations.  This ensures
+        * consistency so that both the "turn off blinking"
+        * and "turn light off" operations complete as a set.
+        */
+       spin_lock(&nasgpio_gpio_lock);
+       /*
+        * LED class documentation asks that past blink state
+        * be disabled when brightness is turned to zero.
+        */
+       if (brightness == 0)
+               __nasgpio_led_set_attr(led_cdev, GPO_BLINK, 0);
+       __nasgpio_led_set_attr(led_cdev, GP_LVL, setting);
+       spin_unlock(&nasgpio_gpio_lock);
+}
+
+static int nasgpio_led_set_blink(struct led_classdev *led_cdev,
+                                unsigned long *delay_on,
+                                unsigned long *delay_off)
+{
+       u32 setting = 1;
+       if (!(*delay_on == 0 && *delay_off == 0) &&
+           !(*delay_on == 500 && *delay_off == 500))
+               return -EINVAL;
+       /*
+        * These are very approximate.
+        */
+       *delay_on = 500;
+       *delay_off = 500;
+
+       nasgpio_led_set_attr(led_cdev, GPO_BLINK, setting);
+
+       return 0;
+}
+
+
+/*
+ * Initialize the ICH7 GPIO registers for NAS usage.  The BIOS should have
+ * already taken care of this, but we will do so in a non destructive manner
+ * so that we have what we need whether the BIOS did it or not.
+ */
+static int __devinit ich7_gpio_init(struct device *dev)
+{
+       int i;
+       u32 config_data = 0;
+       u32 all_nas_led = 0;
+
+       for (i = 0; i < ARRAY_SIZE(nasgpio_leds); i++)
+               all_nas_led |= (1<<nasgpio_leds[i].gpio_bit);
+
+       spin_lock(&nasgpio_gpio_lock);
+       /*
+        * We need to enable all of the GPIO lines used by the NAS box,
+        * so we will read the current Use Selection and add our usage
+        * to it.  This should be benign with regard to the original
+        * BIOS configuration.
+        */
+       config_data = inl(nas_gpio_io_base + GPIO_USE_SEL);
+       dev_dbg(dev, ": Data read from GPIO_USE_SEL = 0x%08x\n", config_data);
+       config_data |= all_nas_led + NAS_RECOVERY;
+       outl(config_data, nas_gpio_io_base + GPIO_USE_SEL);
+       config_data = inl(nas_gpio_io_base + GPIO_USE_SEL);
+       dev_dbg(dev, ": GPIO_USE_SEL = 0x%08x\n\n", config_data);
+
+       /*
+        * The LED GPIO outputs need to be configured for output, so we
+        * will ensure that all LED lines are cleared for output and the
+        * RECOVERY line ready for input.  This too should be benign with
+        * regard to BIOS configuration.
+        */
+       config_data = inl(nas_gpio_io_base + GP_IO_SEL);
+       dev_dbg(dev, ": Data read from GP_IO_SEL = 0x%08x\n",
+                                       config_data);
+       config_data &= ~all_nas_led;
+       config_data |= NAS_RECOVERY;
+       outl(config_data, nas_gpio_io_base + GP_IO_SEL);
+       config_data = inl(nas_gpio_io_base + GP_IO_SEL);
+       dev_dbg(dev, ": GP_IO_SEL = 0x%08x\n", config_data);
+
+       /*
+        * In our final system, the BIOS will initialize the state of all
+        * of the LEDs.  For now, we turn them all off (or Low).
+        */
+       config_data = inl(nas_gpio_io_base + GP_LVL);
+       dev_dbg(dev, ": Data read from GP_LVL = 0x%08x\n", config_data);
+       /*
+        * In our final system, the BIOS will initialize the blink state of all
+        * of the LEDs.  For now, we turn blink off for all of them.
+        */
+       config_data = inl(nas_gpio_io_base + GPO_BLINK);
+       dev_dbg(dev, ": Data read from GPO_BLINK = 0x%08x\n", config_data);
+
+       /*
+        * At this moment, I am unsure if anything needs to happen with GPI_INV
+        */
+       config_data = inl(nas_gpio_io_base + GPI_INV);
+       dev_dbg(dev, ": Data read from GPI_INV = 0x%08x\n", config_data);
+
+       spin_unlock(&nasgpio_gpio_lock);
+       return 0;
+}
+
+static void ich7_lpc_cleanup(struct device *dev)
+{
+       /*
+        * If we were given exclusive use of the GPIO
+        * I/O Address range, we must return it.
+        */
+       if (gp_gpio_resource) {
+               dev_dbg(dev, ": Releasing GPIO I/O addresses\n");
+               release_region(nas_gpio_io_base, ICH7_GPIO_SIZE);
+               gp_gpio_resource = NULL;
+       }
+}
+
+/*
+ * The OS has determined that the LPC of the Intel ICH7 Southbridge is present
+ * so we can retrive the required operational information and prepare the GPIO.
+ */
+static struct pci_dev *nas_gpio_pci_dev;
+static int __devinit ich7_lpc_probe(struct pci_dev *dev,
+                                   const struct pci_device_id *id)
+{
+       int status;
+       u32 gc = 0;
+
+       status = pci_enable_device(dev);
+       if (status) {
+               dev_err(&dev->dev, "pci_enable_device failed\n");
+               return -EIO;
+       }
+
+       nas_gpio_pci_dev = dev;
+       status = pci_read_config_dword(dev, PMBASE, &g_pm_io_base);
+       if (status)
+               goto out;
+       g_pm_io_base &= 0x00000ff80;
+
+       status = pci_read_config_dword(dev, GPIO_CTRL, &gc);
+       if (!(GPIO_EN & gc)) {
+               status = -EEXIST;
+               dev_info(&dev->dev,
+                          "ERROR: The LPC GPIO Block has not been enabled.\n");
+               goto out;
+       }
+
+       status = pci_read_config_dword(dev, GPIO_BASE, &nas_gpio_io_base);
+       if (0 > status) {
+               dev_info(&dev->dev, "Unable to read GPIOBASE.\n");
+               goto out;
+       }
+       dev_dbg(&dev->dev, ": GPIOBASE = 0x%08x\n", nas_gpio_io_base);
+       nas_gpio_io_base &= 0x00000ffc0;
+
+       /*
+        * Insure that we have exclusive access to the GPIO I/O address range.
+        */
+       gp_gpio_resource = request_region(nas_gpio_io_base, ICH7_GPIO_SIZE,
+                                         KBUILD_MODNAME);
+       if (NULL == gp_gpio_resource) {
+               dev_info(&dev->dev,
+                        "ERROR Unable to register GPIO I/O addresses.\n");
+               status = -1;
+               goto out;
+       }
+
+       /*
+        * Initialize the GPIO for NAS/Home Server Use
+        */
+       ich7_gpio_init(&dev->dev);
+
+out:
+       if (status) {
+               ich7_lpc_cleanup(&dev->dev);
+               pci_disable_device(dev);
+       }
+       return status;
+}
+
+static void ich7_lpc_remove(struct pci_dev *dev)
+{
+       ich7_lpc_cleanup(&dev->dev);
+       pci_disable_device(dev);
+}
+
+/*
+ * pci_driver structure passed to the PCI modules
+ */
+static struct pci_driver nas_gpio_pci_driver = {
+       .name = KBUILD_MODNAME,
+       .id_table = ich7_lpc_pci_id,
+       .probe = ich7_lpc_probe,
+       .remove = ich7_lpc_remove,
+};
+
+static struct led_classdev *get_classdev_for_led_nr(int nr)
+{
+       struct nasgpio_led *nas_led = &nasgpio_leds[nr];
+       struct led_classdev *led = &nas_led->led_cdev;
+       return led;
+}
+
+
+static void set_power_light_amber_noblink(void)
+{
+       struct nasgpio_led *amber = get_led_named("power:amber:power");
+       struct nasgpio_led *blue = get_led_named("power:blue:power");
+
+       if (!amber || !blue)
+               return;
+       /*
+        * LED_OFF implies disabling future blinking
+        */
+       pr_debug("setting blue off and amber on\n");
+
+       nasgpio_led_set_brightness(&blue->led_cdev, LED_OFF);
+       nasgpio_led_set_brightness(&amber->led_cdev, LED_FULL);
+}
+
+static ssize_t nas_led_blink_show(struct device *dev,
+                                 struct device_attribute *attr, char *buf)
+{
+       struct led_classdev *led = dev_get_drvdata(dev);
+       int blinking = 0;
+       if (nasgpio_led_get_attr(led, GPO_BLINK))
+               blinking = 1;
+       return sprintf(buf, "%u\n", blinking);
+}
+
+static ssize_t nas_led_blink_store(struct device *dev,
+                                  struct device_attribute *attr,
+                                  const char *buf, size_t size)
+{
+       int ret;
+       struct led_classdev *led = dev_get_drvdata(dev);
+       unsigned long blink_state;
+
+       ret = strict_strtoul(buf, 10, &blink_state);
+       if (ret)
+               return ret;
+
+       nasgpio_led_set_attr(led, GPO_BLINK, blink_state);
+
+       return size;
+}
+
+static DEVICE_ATTR(blink, 0644, nas_led_blink_show, nas_led_blink_store);
+
+static int register_nasgpio_led(int led_nr)
+{
+       int ret;
+       struct nasgpio_led *nas_led = &nasgpio_leds[led_nr];
+       struct led_classdev *led = get_classdev_for_led_nr(led_nr);
+
+       led->name = nas_led->name;
+       led->brightness = LED_OFF;
+       if (nasgpio_led_get_attr(led, GP_LVL))
+               led->brightness = LED_FULL;
+       led->brightness_set = nasgpio_led_set_brightness;
+       led->blink_set = nasgpio_led_set_blink;
+       ret = led_classdev_register(&nas_gpio_pci_dev->dev, led);
+       if (ret)
+               return ret;
+       ret = device_create_file(led->dev, &dev_attr_blink);
+       if (ret)
+               led_classdev_unregister(led);
+       return ret;
+}
+
+static void unregister_nasgpio_led(int led_nr)
+{
+       struct led_classdev *led = get_classdev_for_led_nr(led_nr);
+       led_classdev_unregister(led);
+       device_remove_file(led->dev, &dev_attr_blink);
+}
+/*
+ * module load/initialization
+ */
+static int __init nas_gpio_init(void)
+{
+       int i;
+       int ret = 0;
+       int nr_devices = 0;
+
+       nr_devices = dmi_check_system(nas_led_whitelist);
+       if (nodetect) {
+               pr_info("skipping hardware autodetection\n");
+               pr_info("Please send 'dmidecode' output to dave@sr71.net\n");
+               nr_devices++;
+       }
+
+       if (nr_devices <= 0) {
+               pr_info("no LED devices found\n");
+               return -ENODEV;
+       }
+
+       pr_info("registering PCI driver\n");
+       ret = pci_register_driver(&nas_gpio_pci_driver);
+       if (ret)
+               return ret;
+       for (i = 0; i < ARRAY_SIZE(nasgpio_leds); i++) {
+               ret = register_nasgpio_led(i);
+               if (ret)
+                       goto out_err;
+       }
+       /*
+        * When the system powers on, the BIOS leaves the power
+        * light blue and blinking.  This will turn it solid
+        * amber once the driver is loaded.
+        */
+       set_power_light_amber_noblink();
+       return 0;
+out_err:
+       for (; i >= 0; i--)
+               unregister_nasgpio_led(i);
+       pci_unregister_driver(&nas_gpio_pci_driver);
+       return ret;
+}
+
+/*
+ * module unload
+ */
+static void __exit nas_gpio_exit(void)
+{
+       int i;
+       pr_info("Unregistering driver\n");
+       for (i = 0; i < ARRAY_SIZE(nasgpio_leds); i++)
+               unregister_nasgpio_led(i);
+       pci_unregister_driver(&nas_gpio_pci_driver);
+}
+
+module_init(nas_gpio_init);
+module_exit(nas_gpio_exit);
index 1a7a9fc50ea1019fc74965daf6de40fff65b0f47..e3551d20464fe8f56c9edecfd9babe8c496e1aec 100644 (file)
@@ -203,6 +203,7 @@ config CS5535_MFGPT
 
 config CS5535_MFGPT_DEFAULT_IRQ
        int
+       depends on CS5535_MFGPT
        default 7
        help
          MFGPTs on the CS5535 require an interrupt.  The selected IRQ
index cdb845b68ab5e37222048b47769014fa32186b8a..06b64085a355b3aed5198548b406ddcc79174a48 100644 (file)
@@ -516,7 +516,8 @@ int mmc_attach_sdio(struct mmc_host *host, u32 ocr)
         * The number of functions on the card is encoded inside
         * the ocr.
         */
-       card->sdio_funcs = funcs = (ocr & 0x70000000) >> 28;
+       funcs = (ocr & 0x70000000) >> 28;
+       card->sdio_funcs = 0;
 
        /*
         * If needed, disconnect card detection pull-up resistor.
@@ -528,7 +529,7 @@ int mmc_attach_sdio(struct mmc_host *host, u32 ocr)
        /*
         * Initialize (but don't add) all present functions.
         */
-       for (i = 0;i < funcs;i++) {
+       for (i = 0; i < funcs; i++, card->sdio_funcs++) {
                err = sdio_init_func(host->card, i + 1);
                if (err)
                        goto remove;
index d37464e296a507deb743182c3764714f83cdda9c..9e060c87e64db6775fc5aa7b8aa07033dae7e365 100644 (file)
@@ -248,12 +248,15 @@ int sdio_add_func(struct sdio_func *func)
 /*
  * Unregister a SDIO function with the driver model, and
  * (eventually) free it.
+ * This function can be called through error paths where sdio_add_func() was
+ * never executed (because a failure occurred at an earlier point).
  */
 void sdio_remove_func(struct sdio_func *func)
 {
-       if (sdio_func_present(func))
-               device_del(&func->dev);
+       if (!sdio_func_present(func))
+               return;
 
+       device_del(&func->dev);
        put_device(&func->dev);
 }
 
index 9d405b1817812407469f1cf2b778639237acd940..ce1d28884e2987a059f9cba3ead143d041fc9a57 100644 (file)
@@ -44,6 +44,19 @@ config MMC_SDHCI_IO_ACCESSORS
          This is silent Kconfig symbol that is selected by the drivers that
          need to overwrite SDHCI IO memory accessors.
 
+config MMC_SDHCI_BIG_ENDIAN_32BIT_BYTE_SWAPPER
+       bool
+       select MMC_SDHCI_IO_ACCESSORS
+       help
+         This option is selected by drivers running on big endian hosts
+         and performing I/O to a SDHCI controller through a bus that
+         implements a hardware byte swapper using a 32-bit datum.
+         This endian mapping mode is called "data invariance" and
+         has the effect of scrambling the addresses and formats of data
+         accessed in sizes other than the datum size.
+
+         This is the case for the Freescale eSDHC and Nintendo Wii SDHCI.
+
 config MMC_SDHCI_PCI
        tristate "SDHCI support on PCI bus"
        depends on MMC_SDHCI && PCI
@@ -75,11 +88,29 @@ config MMC_RICOH_MMC
 config MMC_SDHCI_OF
        tristate "SDHCI support on OpenFirmware platforms"
        depends on MMC_SDHCI && PPC_OF
-       select MMC_SDHCI_IO_ACCESSORS
        help
          This selects the OF support for Secure Digital Host Controller
-         Interfaces. So far, only the Freescale eSDHC controller is known
-         to exist on OF platforms.
+         Interfaces.
+
+         If unsure, say N.
+
+config MMC_SDHCI_OF_ESDHC
+       bool "SDHCI OF support for the Freescale eSDHC controller"
+       depends on MMC_SDHCI_OF
+       select MMC_SDHCI_BIG_ENDIAN_32BIT_BYTE_SWAPPER
+       help
+         This selects the Freescale eSDHC controller support.
+
+         If unsure, say N.
+
+config MMC_SDHCI_OF_HLWD
+       bool "SDHCI OF support for the Nintendo Wii SDHCI controllers"
+       depends on MMC_SDHCI_OF
+       select MMC_SDHCI_BIG_ENDIAN_32BIT_BYTE_SWAPPER
+       help
+         This selects the Secure Digital Host Controller Interface (SDHCI)
+         found in the "Hollywood" chipset of the Nintendo Wii video game
+         console.
 
          If unsure, say N.
 
index ded4d8cdd9d7d371baf3ce225abbe197cedbe4b0..3d253dd4240fee2c7d8fbb42f631563af370fb36 100644 (file)
@@ -13,7 +13,6 @@ obj-$(CONFIG_MMC_MXC)         += mxcmmc.o
 obj-$(CONFIG_MMC_SDHCI)                += sdhci.o
 obj-$(CONFIG_MMC_SDHCI_PCI)    += sdhci-pci.o
 obj-$(CONFIG_MMC_RICOH_MMC)    += ricoh_mmc.o
-obj-$(CONFIG_MMC_SDHCI_OF)     += sdhci-of.o
 obj-$(CONFIG_MMC_SDHCI_PLTFM)  += sdhci-pltfm.o
 obj-$(CONFIG_MMC_SDHCI_S3C)    += sdhci-s3c.o
 obj-$(CONFIG_MMC_WBSD)         += wbsd.o
@@ -37,6 +36,11 @@ obj-$(CONFIG_MMC_CB710)      += cb710-mmc.o
 obj-$(CONFIG_MMC_VIA_SDMMC)    += via-sdmmc.o
 obj-$(CONFIG_SDH_BFIN)         += bfin_sdh.o
 
+obj-$(CONFIG_MMC_SDHCI_OF)     += sdhci-of.o
+sdhci-of-y                             := sdhci-of-core.o
+sdhci-of-$(CONFIG_MMC_SDHCI_OF_ESDHC)  += sdhci-of-esdhc.o
+sdhci-of-$(CONFIG_MMC_SDHCI_OF_HLWD)   += sdhci-of-hlwd.o
+
 ifeq ($(CONFIG_CB710_DEBUG),y)
        CFLAGS-cb710-mmc        += -DDEBUG
 endif
diff --git a/drivers/mmc/host/sdhci-of-core.c b/drivers/mmc/host/sdhci-of-core.c
new file mode 100644 (file)
index 0000000..55e3313
--- /dev/null
@@ -0,0 +1,231 @@
+/*
+ * OpenFirmware bindings for Secure Digital Host Controller Interface.
+ *
+ * Copyright (c) 2007 Freescale Semiconductor, Inc.
+ * Copyright (c) 2009 MontaVista Software, Inc.
+ *
+ * Authors: Xiaobo Xie <X.Xie@freescale.com>
+ *         Anton Vorontsov <avorontsov@ru.mvista.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or (at
+ * your option) any later version.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/mmc/host.h>
+#include <asm/machdep.h>
+#include "sdhci-of.h"
+#include "sdhci.h"
+
+#ifdef CONFIG_MMC_SDHCI_BIG_ENDIAN_32BIT_BYTE_SWAPPER
+
+/*
+ * These accessors are designed for big endian hosts doing I/O to
+ * little endian controllers incorporating a 32-bit hardware byte swapper.
+ */
+
+u32 sdhci_be32bs_readl(struct sdhci_host *host, int reg)
+{
+       return in_be32(host->ioaddr + reg);
+}
+
+u16 sdhci_be32bs_readw(struct sdhci_host *host, int reg)
+{
+       return in_be16(host->ioaddr + (reg ^ 0x2));
+}
+
+u8 sdhci_be32bs_readb(struct sdhci_host *host, int reg)
+{
+       return in_8(host->ioaddr + (reg ^ 0x3));
+}
+
+void sdhci_be32bs_writel(struct sdhci_host *host, u32 val, int reg)
+{
+       out_be32(host->ioaddr + reg, val);
+}
+
+void sdhci_be32bs_writew(struct sdhci_host *host, u16 val, int reg)
+{
+       struct sdhci_of_host *of_host = sdhci_priv(host);
+       int base = reg & ~0x3;
+       int shift = (reg & 0x2) * 8;
+
+       switch (reg) {
+       case SDHCI_TRANSFER_MODE:
+               /*
+                * Postpone this write, we must do it together with a
+                * command write that is down below.
+                */
+               of_host->xfer_mode_shadow = val;
+               return;
+       case SDHCI_COMMAND:
+               sdhci_be32bs_writel(host, val << 16 | of_host->xfer_mode_shadow,
+                                   SDHCI_TRANSFER_MODE);
+               return;
+       }
+       clrsetbits_be32(host->ioaddr + base, 0xffff << shift, val << shift);
+}
+
+void sdhci_be32bs_writeb(struct sdhci_host *host, u8 val, int reg)
+{
+       int base = reg & ~0x3;
+       int shift = (reg & 0x3) * 8;
+
+       clrsetbits_be32(host->ioaddr + base , 0xff << shift, val << shift);
+}
+#endif /* CONFIG_MMC_SDHCI_BIG_ENDIAN_32BIT_BYTE_SWAPPER */
+
+#ifdef CONFIG_PM
+
+static int sdhci_of_suspend(struct of_device *ofdev, pm_message_t state)
+{
+       struct sdhci_host *host = dev_get_drvdata(&ofdev->dev);
+
+       return mmc_suspend_host(host->mmc, state);
+}
+
+static int sdhci_of_resume(struct of_device *ofdev)
+{
+       struct sdhci_host *host = dev_get_drvdata(&ofdev->dev);
+
+       return mmc_resume_host(host->mmc);
+}
+
+#else
+
+#define sdhci_of_suspend NULL
+#define sdhci_of_resume NULL
+
+#endif
+
+static bool __devinit sdhci_of_wp_inverted(struct device_node *np)
+{
+       if (of_get_property(np, "sdhci,wp-inverted", NULL))
+               return true;
+
+       /* Old device trees don't have the wp-inverted property. */
+       return machine_is(mpc837x_rdb) || machine_is(mpc837x_mds);
+}
+
+static int __devinit sdhci_of_probe(struct of_device *ofdev,
+                                const struct of_device_id *match)
+{
+       struct device_node *np = ofdev->node;
+       struct sdhci_of_data *sdhci_of_data = match->data;
+       struct sdhci_host *host;
+       struct sdhci_of_host *of_host;
+       const u32 *clk;
+       int size;
+       int ret;
+
+       if (!of_device_is_available(np))
+               return -ENODEV;
+
+       host = sdhci_alloc_host(&ofdev->dev, sizeof(*of_host));
+       if (IS_ERR(host))
+               return -ENOMEM;
+
+       of_host = sdhci_priv(host);
+       dev_set_drvdata(&ofdev->dev, host);
+
+       host->ioaddr = of_iomap(np, 0);
+       if (!host->ioaddr) {
+               ret = -ENOMEM;
+               goto err_addr_map;
+       }
+
+       host->irq = irq_of_parse_and_map(np, 0);
+       if (!host->irq) {
+               ret = -EINVAL;
+               goto err_no_irq;
+       }
+
+       host->hw_name = dev_name(&ofdev->dev);
+       if (sdhci_of_data) {
+               host->quirks = sdhci_of_data->quirks;
+               host->ops = &sdhci_of_data->ops;
+       }
+
+       if (of_get_property(np, "sdhci,1-bit-only", NULL))
+               host->quirks |= SDHCI_QUIRK_FORCE_1_BIT_DATA;
+
+       if (sdhci_of_wp_inverted(np))
+               host->quirks |= SDHCI_QUIRK_INVERTED_WRITE_PROTECT;
+
+       clk = of_get_property(np, "clock-frequency", &size);
+       if (clk && size == sizeof(*clk) && *clk)
+               of_host->clock = *clk;
+
+       ret = sdhci_add_host(host);
+       if (ret)
+               goto err_add_host;
+
+       return 0;
+
+err_add_host:
+       irq_dispose_mapping(host->irq);
+err_no_irq:
+       iounmap(host->ioaddr);
+err_addr_map:
+       sdhci_free_host(host);
+       return ret;
+}
+
+static int __devexit sdhci_of_remove(struct of_device *ofdev)
+{
+       struct sdhci_host *host = dev_get_drvdata(&ofdev->dev);
+
+       sdhci_remove_host(host, 0);
+       sdhci_free_host(host);
+       irq_dispose_mapping(host->irq);
+       iounmap(host->ioaddr);
+       return 0;
+}
+
+static const struct of_device_id sdhci_of_match[] = {
+#ifdef CONFIG_MMC_SDHCI_OF_ESDHC
+       { .compatible = "fsl,mpc8379-esdhc", .data = &sdhci_esdhc, },
+       { .compatible = "fsl,mpc8536-esdhc", .data = &sdhci_esdhc, },
+       { .compatible = "fsl,esdhc", .data = &sdhci_esdhc, },
+#endif
+#ifdef CONFIG_MMC_SDHCI_OF_HLWD
+       { .compatible = "nintendo,hollywood-sdhci", .data = &sdhci_hlwd, },
+#endif
+       { .compatible = "generic-sdhci", },
+       {},
+};
+MODULE_DEVICE_TABLE(of, sdhci_of_match);
+
+static struct of_platform_driver sdhci_of_driver = {
+       .driver.name = "sdhci-of",
+       .match_table = sdhci_of_match,
+       .probe = sdhci_of_probe,
+       .remove = __devexit_p(sdhci_of_remove),
+       .suspend = sdhci_of_suspend,
+       .resume = sdhci_of_resume,
+};
+
+static int __init sdhci_of_init(void)
+{
+       return of_register_platform_driver(&sdhci_of_driver);
+}
+module_init(sdhci_of_init);
+
+static void __exit sdhci_of_exit(void)
+{
+       of_unregister_platform_driver(&sdhci_of_driver);
+}
+module_exit(sdhci_of_exit);
+
+MODULE_DESCRIPTION("Secure Digital Host Controller Interface OF driver");
+MODULE_AUTHOR("Xiaobo Xie <X.Xie@freescale.com>, "
+             "Anton Vorontsov <avorontsov@ru.mvista.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/mmc/host/sdhci-of-esdhc.c b/drivers/mmc/host/sdhci-of-esdhc.c
new file mode 100644 (file)
index 0000000..d5b11a1
--- /dev/null
@@ -0,0 +1,143 @@
+/*
+ * Freescale eSDHC controller driver.
+ *
+ * Copyright (c) 2007 Freescale Semiconductor, Inc.
+ * Copyright (c) 2009 MontaVista Software, Inc.
+ *
+ * Authors: Xiaobo Xie <X.Xie@freescale.com>
+ *         Anton Vorontsov <avorontsov@ru.mvista.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or (at
+ * your option) any later version.
+ */
+
+#include <linux/io.h>
+#include <linux/delay.h>
+#include <linux/mmc/host.h>
+#include "sdhci-of.h"
+#include "sdhci.h"
+
+/*
+ * Ops and quirks for the Freescale eSDHC controller.
+ */
+
+#define ESDHC_DMA_SYSCTL       0x40c
+#define ESDHC_DMA_SNOOP                0x00000040
+
+#define ESDHC_SYSTEM_CONTROL   0x2c
+#define ESDHC_CLOCK_MASK       0x0000fff0
+#define ESDHC_PREDIV_SHIFT     8
+#define ESDHC_DIVIDER_SHIFT    4
+#define ESDHC_CLOCK_PEREN      0x00000004
+#define ESDHC_CLOCK_HCKEN      0x00000002
+#define ESDHC_CLOCK_IPGEN      0x00000001
+
+#define ESDHC_HOST_CONTROL_RES 0x05
+
+static u16 esdhc_readw(struct sdhci_host *host, int reg)
+{
+       u16 ret;
+
+       if (unlikely(reg == SDHCI_HOST_VERSION))
+               ret = in_be16(host->ioaddr + reg);
+       else
+               ret = sdhci_be32bs_readw(host, reg);
+       return ret;
+}
+
+static void esdhc_writew(struct sdhci_host *host, u16 val, int reg)
+{
+       if (reg == SDHCI_BLOCK_SIZE) {
+               /*
+                * Two last DMA bits are reserved, and first one is used for
+                * non-standard blksz of 4096 bytes that we don't support
+                * yet. So clear the DMA boundary bits.
+                */
+               val &= ~SDHCI_MAKE_BLKSZ(0x7, 0);
+       }
+       sdhci_be32bs_writew(host, val, reg);
+}
+
+static void esdhc_writeb(struct sdhci_host *host, u8 val, int reg)
+{
+       /* Prevent SDHCI core from writing reserved bits (e.g. HISPD). */
+       if (reg == SDHCI_HOST_CONTROL)
+               val &= ~ESDHC_HOST_CONTROL_RES;
+       sdhci_be32bs_writeb(host, val, reg);
+}
+
+static void esdhc_set_clock(struct sdhci_host *host, unsigned int clock)
+{
+       int pre_div = 2;
+       int div = 1;
+
+       clrbits32(host->ioaddr + ESDHC_SYSTEM_CONTROL, ESDHC_CLOCK_IPGEN |
+                 ESDHC_CLOCK_HCKEN | ESDHC_CLOCK_PEREN | ESDHC_CLOCK_MASK);
+
+       if (clock == 0)
+               goto out;
+
+       while (host->max_clk / pre_div / 16 > clock && pre_div < 256)
+               pre_div *= 2;
+
+       while (host->max_clk / pre_div / div > clock && div < 16)
+               div++;
+
+       dev_dbg(mmc_dev(host->mmc), "desired SD clock: %d, actual: %d\n",
+               clock, host->max_clk / pre_div / div);
+
+       pre_div >>= 1;
+       div--;
+
+       setbits32(host->ioaddr + ESDHC_SYSTEM_CONTROL, ESDHC_CLOCK_IPGEN |
+                 ESDHC_CLOCK_HCKEN | ESDHC_CLOCK_PEREN |
+                 div << ESDHC_DIVIDER_SHIFT | pre_div << ESDHC_PREDIV_SHIFT);
+       mdelay(100);
+out:
+       host->clock = clock;
+}
+
+static int esdhc_enable_dma(struct sdhci_host *host)
+{
+       setbits32(host->ioaddr + ESDHC_DMA_SYSCTL, ESDHC_DMA_SNOOP);
+       return 0;
+}
+
+static unsigned int esdhc_get_max_clock(struct sdhci_host *host)
+{
+       struct sdhci_of_host *of_host = sdhci_priv(host);
+
+       return of_host->clock;
+}
+
+static unsigned int esdhc_get_min_clock(struct sdhci_host *host)
+{
+       struct sdhci_of_host *of_host = sdhci_priv(host);
+
+       return of_host->clock / 256 / 16;
+}
+
+struct sdhci_of_data sdhci_esdhc = {
+       .quirks = SDHCI_QUIRK_FORCE_BLK_SZ_2048 |
+                 SDHCI_QUIRK_BROKEN_CARD_DETECTION |
+                 SDHCI_QUIRK_NO_BUSY_IRQ |
+                 SDHCI_QUIRK_NONSTANDARD_CLOCK |
+                 SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK |
+                 SDHCI_QUIRK_PIO_NEEDS_DELAY |
+                 SDHCI_QUIRK_RESTORE_IRQS_AFTER_RESET |
+                 SDHCI_QUIRK_NO_CARD_NO_RESET,
+       .ops = {
+               .readl = sdhci_be32bs_readl,
+               .readw = esdhc_readw,
+               .readb = sdhci_be32bs_readb,
+               .writel = sdhci_be32bs_writel,
+               .writew = esdhc_writew,
+               .writeb = esdhc_writeb,
+               .set_clock = esdhc_set_clock,
+               .enable_dma = esdhc_enable_dma,
+               .get_max_clock = esdhc_get_max_clock,
+               .get_min_clock = esdhc_get_min_clock,
+       },
+};
diff --git a/drivers/mmc/host/sdhci-of-hlwd.c b/drivers/mmc/host/sdhci-of-hlwd.c
new file mode 100644 (file)
index 0000000..35117f3
--- /dev/null
@@ -0,0 +1,65 @@
+/*
+ * drivers/mmc/host/sdhci-of-hlwd.c
+ *
+ * Nintendo Wii Secure Digital Host Controller Interface.
+ * Copyright (C) 2009 The GameCube Linux Team
+ * Copyright (C) 2009 Albert Herranz
+ *
+ * Based on sdhci-of-esdhc.c
+ *
+ * Copyright (c) 2007 Freescale Semiconductor, Inc.
+ * Copyright (c) 2009 MontaVista Software, Inc.
+ *
+ * Authors: Xiaobo Xie <X.Xie@freescale.com>
+ *         Anton Vorontsov <avorontsov@ru.mvista.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or (at
+ * your option) any later version.
+ */
+
+#include <linux/delay.h>
+#include <linux/mmc/host.h>
+#include "sdhci-of.h"
+#include "sdhci.h"
+
+/*
+ * Ops and quirks for the Nintendo Wii SDHCI controllers.
+ */
+
+/*
+ * We need a small delay after each write, or things go horribly wrong.
+ */
+#define SDHCI_HLWD_WRITE_DELAY 5 /* usecs */
+
+static void sdhci_hlwd_writel(struct sdhci_host *host, u32 val, int reg)
+{
+       sdhci_be32bs_writel(host, val, reg);
+       udelay(SDHCI_HLWD_WRITE_DELAY);
+}
+
+static void sdhci_hlwd_writew(struct sdhci_host *host, u16 val, int reg)
+{
+       sdhci_be32bs_writew(host, val, reg);
+       udelay(SDHCI_HLWD_WRITE_DELAY);
+}
+
+static void sdhci_hlwd_writeb(struct sdhci_host *host, u8 val, int reg)
+{
+       sdhci_be32bs_writeb(host, val, reg);
+       udelay(SDHCI_HLWD_WRITE_DELAY);
+}
+
+struct sdhci_of_data sdhci_hlwd = {
+       .quirks = SDHCI_QUIRK_32BIT_DMA_ADDR |
+                 SDHCI_QUIRK_32BIT_DMA_SIZE,
+       .ops = {
+               .readl = sdhci_be32bs_readl,
+               .readw = sdhci_be32bs_readw,
+               .readb = sdhci_be32bs_readb,
+               .writel = sdhci_hlwd_writel,
+               .writew = sdhci_hlwd_writew,
+               .writeb = sdhci_hlwd_writeb,
+       },
+};
diff --git a/drivers/mmc/host/sdhci-of.c b/drivers/mmc/host/sdhci-of.c
deleted file mode 100644 (file)
index 01ab916..0000000
+++ /dev/null
@@ -1,336 +0,0 @@
-/*
- * OpenFirmware bindings for Secure Digital Host Controller Interface.
- *
- * Copyright (c) 2007 Freescale Semiconductor, Inc.
- * Copyright (c) 2009 MontaVista Software, Inc.
- *
- * Authors: Xiaobo Xie <X.Xie@freescale.com>
- *         Anton Vorontsov <avorontsov@ru.mvista.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or (at
- * your option) any later version.
- */
-
-#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/io.h>
-#include <linux/interrupt.h>
-#include <linux/delay.h>
-#include <linux/of.h>
-#include <linux/of_platform.h>
-#include <linux/mmc/host.h>
-#include <asm/machdep.h>
-#include "sdhci.h"
-
-struct sdhci_of_data {
-       unsigned int quirks;
-       struct sdhci_ops ops;
-};
-
-struct sdhci_of_host {
-       unsigned int clock;
-       u16 xfer_mode_shadow;
-};
-
-/*
- * Ops and quirks for the Freescale eSDHC controller.
- */
-
-#define ESDHC_DMA_SYSCTL       0x40c
-#define ESDHC_DMA_SNOOP                0x00000040
-
-#define ESDHC_SYSTEM_CONTROL   0x2c
-#define ESDHC_CLOCK_MASK       0x0000fff0
-#define ESDHC_PREDIV_SHIFT     8
-#define ESDHC_DIVIDER_SHIFT    4
-#define ESDHC_CLOCK_PEREN      0x00000004
-#define ESDHC_CLOCK_HCKEN      0x00000002
-#define ESDHC_CLOCK_IPGEN      0x00000001
-
-#define ESDHC_HOST_CONTROL_RES 0x05
-
-static u32 esdhc_readl(struct sdhci_host *host, int reg)
-{
-       return in_be32(host->ioaddr + reg);
-}
-
-static u16 esdhc_readw(struct sdhci_host *host, int reg)
-{
-       u16 ret;
-
-       if (unlikely(reg == SDHCI_HOST_VERSION))
-               ret = in_be16(host->ioaddr + reg);
-       else
-               ret = in_be16(host->ioaddr + (reg ^ 0x2));
-       return ret;
-}
-
-static u8 esdhc_readb(struct sdhci_host *host, int reg)
-{
-       return in_8(host->ioaddr + (reg ^ 0x3));
-}
-
-static void esdhc_writel(struct sdhci_host *host, u32 val, int reg)
-{
-       out_be32(host->ioaddr + reg, val);
-}
-
-static void esdhc_writew(struct sdhci_host *host, u16 val, int reg)
-{
-       struct sdhci_of_host *of_host = sdhci_priv(host);
-       int base = reg & ~0x3;
-       int shift = (reg & 0x2) * 8;
-
-       switch (reg) {
-       case SDHCI_TRANSFER_MODE:
-               /*
-                * Postpone this write, we must do it together with a
-                * command write that is down below.
-                */
-               of_host->xfer_mode_shadow = val;
-               return;
-       case SDHCI_COMMAND:
-               esdhc_writel(host, val << 16 | of_host->xfer_mode_shadow,
-                            SDHCI_TRANSFER_MODE);
-               return;
-       case SDHCI_BLOCK_SIZE:
-               /*
-                * Two last DMA bits are reserved, and first one is used for
-                * non-standard blksz of 4096 bytes that we don't support
-                * yet. So clear the DMA boundary bits.
-                */
-               val &= ~SDHCI_MAKE_BLKSZ(0x7, 0);
-               /* fall through */
-       }
-       clrsetbits_be32(host->ioaddr + base, 0xffff << shift, val << shift);
-}
-
-static void esdhc_writeb(struct sdhci_host *host, u8 val, int reg)
-{
-       int base = reg & ~0x3;
-       int shift = (reg & 0x3) * 8;
-
-       /* Prevent SDHCI core from writing reserved bits (e.g. HISPD). */
-       if (reg == SDHCI_HOST_CONTROL)
-               val &= ~ESDHC_HOST_CONTROL_RES;
-
-       clrsetbits_be32(host->ioaddr + base , 0xff << shift, val << shift);
-}
-
-static void esdhc_set_clock(struct sdhci_host *host, unsigned int clock)
-{
-       int pre_div = 2;
-       int div = 1;
-
-       clrbits32(host->ioaddr + ESDHC_SYSTEM_CONTROL, ESDHC_CLOCK_IPGEN |
-                 ESDHC_CLOCK_HCKEN | ESDHC_CLOCK_PEREN | ESDHC_CLOCK_MASK);
-
-       if (clock == 0)
-               goto out;
-
-       while (host->max_clk / pre_div / 16 > clock && pre_div < 256)
-               pre_div *= 2;
-
-       while (host->max_clk / pre_div / div > clock && div < 16)
-               div++;
-
-       dev_dbg(mmc_dev(host->mmc), "desired SD clock: %d, actual: %d\n",
-               clock, host->max_clk / pre_div / div);
-
-       pre_div >>= 1;
-       div--;
-
-       setbits32(host->ioaddr + ESDHC_SYSTEM_CONTROL, ESDHC_CLOCK_IPGEN |
-                 ESDHC_CLOCK_HCKEN | ESDHC_CLOCK_PEREN |
-                 div << ESDHC_DIVIDER_SHIFT | pre_div << ESDHC_PREDIV_SHIFT);
-       mdelay(100);
-out:
-       host->clock = clock;
-}
-
-static int esdhc_enable_dma(struct sdhci_host *host)
-{
-       setbits32(host->ioaddr + ESDHC_DMA_SYSCTL, ESDHC_DMA_SNOOP);
-       return 0;
-}
-
-static unsigned int esdhc_get_max_clock(struct sdhci_host *host)
-{
-       struct sdhci_of_host *of_host = sdhci_priv(host);
-
-       return of_host->clock;
-}
-
-static unsigned int esdhc_get_min_clock(struct sdhci_host *host)
-{
-       struct sdhci_of_host *of_host = sdhci_priv(host);
-
-       return of_host->clock / 256 / 16;
-}
-
-static struct sdhci_of_data sdhci_esdhc = {
-       .quirks = SDHCI_QUIRK_FORCE_BLK_SZ_2048 |
-                 SDHCI_QUIRK_BROKEN_CARD_DETECTION |
-                 SDHCI_QUIRK_NO_BUSY_IRQ |
-                 SDHCI_QUIRK_NONSTANDARD_CLOCK |
-                 SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK |
-                 SDHCI_QUIRK_PIO_NEEDS_DELAY |
-                 SDHCI_QUIRK_RESTORE_IRQS_AFTER_RESET |
-                 SDHCI_QUIRK_NO_CARD_NO_RESET,
-       .ops = {
-               .readl = esdhc_readl,
-               .readw = esdhc_readw,
-               .readb = esdhc_readb,
-               .writel = esdhc_writel,
-               .writew = esdhc_writew,
-               .writeb = esdhc_writeb,
-               .set_clock = esdhc_set_clock,
-               .enable_dma = esdhc_enable_dma,
-               .get_max_clock = esdhc_get_max_clock,
-               .get_min_clock = esdhc_get_min_clock,
-       },
-};
-
-#ifdef CONFIG_PM
-
-static int sdhci_of_suspend(struct of_device *ofdev, pm_message_t state)
-{
-       struct sdhci_host *host = dev_get_drvdata(&ofdev->dev);
-
-       return mmc_suspend_host(host->mmc, state);
-}
-
-static int sdhci_of_resume(struct of_device *ofdev)
-{
-       struct sdhci_host *host = dev_get_drvdata(&ofdev->dev);
-
-       return mmc_resume_host(host->mmc);
-}
-
-#else
-
-#define sdhci_of_suspend NULL
-#define sdhci_of_resume NULL
-
-#endif
-
-static bool __devinit sdhci_of_wp_inverted(struct device_node *np)
-{
-       if (of_get_property(np, "sdhci,wp-inverted", NULL))
-               return true;
-
-       /* Old device trees don't have the wp-inverted property. */
-       return machine_is(mpc837x_rdb) || machine_is(mpc837x_mds);
-}
-
-static int __devinit sdhci_of_probe(struct of_device *ofdev,
-                                const struct of_device_id *match)
-{
-       struct device_node *np = ofdev->node;
-       struct sdhci_of_data *sdhci_of_data = match->data;
-       struct sdhci_host *host;
-       struct sdhci_of_host *of_host;
-       const u32 *clk;
-       int size;
-       int ret;
-
-       if (!of_device_is_available(np))
-               return -ENODEV;
-
-       host = sdhci_alloc_host(&ofdev->dev, sizeof(*of_host));
-       if (IS_ERR(host))
-               return -ENOMEM;
-
-       of_host = sdhci_priv(host);
-       dev_set_drvdata(&ofdev->dev, host);
-
-       host->ioaddr = of_iomap(np, 0);
-       if (!host->ioaddr) {
-               ret = -ENOMEM;
-               goto err_addr_map;
-       }
-
-       host->irq = irq_of_parse_and_map(np, 0);
-       if (!host->irq) {
-               ret = -EINVAL;
-               goto err_no_irq;
-       }
-
-       host->hw_name = dev_name(&ofdev->dev);
-       if (sdhci_of_data) {
-               host->quirks = sdhci_of_data->quirks;
-               host->ops = &sdhci_of_data->ops;
-       }
-
-       if (of_get_property(np, "sdhci,1-bit-only", NULL))
-               host->quirks |= SDHCI_QUIRK_FORCE_1_BIT_DATA;
-
-       if (sdhci_of_wp_inverted(np))
-               host->quirks |= SDHCI_QUIRK_INVERTED_WRITE_PROTECT;
-
-       clk = of_get_property(np, "clock-frequency", &size);
-       if (clk && size == sizeof(*clk) && *clk)
-               of_host->clock = *clk;
-
-       ret = sdhci_add_host(host);
-       if (ret)
-               goto err_add_host;
-
-       return 0;
-
-err_add_host:
-       irq_dispose_mapping(host->irq);
-err_no_irq:
-       iounmap(host->ioaddr);
-err_addr_map:
-       sdhci_free_host(host);
-       return ret;
-}
-
-static int __devexit sdhci_of_remove(struct of_device *ofdev)
-{
-       struct sdhci_host *host = dev_get_drvdata(&ofdev->dev);
-
-       sdhci_remove_host(host, 0);
-       sdhci_free_host(host);
-       irq_dispose_mapping(host->irq);
-       iounmap(host->ioaddr);
-       return 0;
-}
-
-static const struct of_device_id sdhci_of_match[] = {
-       { .compatible = "fsl,mpc8379-esdhc", .data = &sdhci_esdhc, },
-       { .compatible = "fsl,mpc8536-esdhc", .data = &sdhci_esdhc, },
-       { .compatible = "fsl,esdhc", .data = &sdhci_esdhc, },
-       { .compatible = "generic-sdhci", },
-       {},
-};
-MODULE_DEVICE_TABLE(of, sdhci_of_match);
-
-static struct of_platform_driver sdhci_of_driver = {
-       .driver.name = "sdhci-of",
-       .match_table = sdhci_of_match,
-       .probe = sdhci_of_probe,
-       .remove = __devexit_p(sdhci_of_remove),
-       .suspend = sdhci_of_suspend,
-       .resume = sdhci_of_resume,
-};
-
-static int __init sdhci_of_init(void)
-{
-       return of_register_platform_driver(&sdhci_of_driver);
-}
-module_init(sdhci_of_init);
-
-static void __exit sdhci_of_exit(void)
-{
-       of_unregister_platform_driver(&sdhci_of_driver);
-}
-module_exit(sdhci_of_exit);
-
-MODULE_DESCRIPTION("Secure Digital Host Controller Interface OF driver");
-MODULE_AUTHOR("Xiaobo Xie <X.Xie@freescale.com>, "
-             "Anton Vorontsov <avorontsov@ru.mvista.com>");
-MODULE_LICENSE("GPL");
diff --git a/drivers/mmc/host/sdhci-of.h b/drivers/mmc/host/sdhci-of.h
new file mode 100644 (file)
index 0000000..ad09ad9
--- /dev/null
@@ -0,0 +1,42 @@
+/*
+ * OpenFirmware bindings for Secure Digital Host Controller Interface.
+ *
+ * Copyright (c) 2007 Freescale Semiconductor, Inc.
+ * Copyright (c) 2009 MontaVista Software, Inc.
+ *
+ * Authors: Xiaobo Xie <X.Xie@freescale.com>
+ *         Anton Vorontsov <avorontsov@ru.mvista.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or (at
+ * your option) any later version.
+ */
+
+#ifndef __SDHCI_OF_H
+#define __SDHCI_OF_H
+
+#include <linux/types.h>
+#include "sdhci.h"
+
+struct sdhci_of_data {
+       unsigned int quirks;
+       struct sdhci_ops ops;
+};
+
+struct sdhci_of_host {
+       unsigned int clock;
+       u16 xfer_mode_shadow;
+};
+
+extern u32 sdhci_be32bs_readl(struct sdhci_host *host, int reg);
+extern u16 sdhci_be32bs_readw(struct sdhci_host *host, int reg);
+extern u8 sdhci_be32bs_readb(struct sdhci_host *host, int reg);
+extern void sdhci_be32bs_writel(struct sdhci_host *host, u32 val, int reg);
+extern void sdhci_be32bs_writew(struct sdhci_host *host, u16 val, int reg);
+extern void sdhci_be32bs_writeb(struct sdhci_host *host, u8 val, int reg);
+
+extern struct sdhci_of_data sdhci_esdhc;
+extern struct sdhci_of_data sdhci_hlwd;
+
+#endif /* __SDHCI_OF_H */
index ce5f1d73dc04667841c6da99973500e9e3c96e7f..842f46f9428469657679e45c1b5b1dc8097c694f 100644 (file)
@@ -8,6 +8,8 @@
  * the Free Software Foundation; either version 2 of the License, or (at
  * your option) any later version.
  */
+#ifndef __SDHCI_H
+#define __SDHCI_H
 
 #include <linux/scatterlist.h>
 #include <linux/compiler.h>
@@ -408,3 +410,5 @@ extern void sdhci_remove_host(struct sdhci_host *host, int dead);
 extern int sdhci_suspend_host(struct sdhci_host *host, pm_message_t state);
 extern int sdhci_resume_host(struct sdhci_host *host);
 #endif
+
+#endif /* __SDHCI_H */
index 74fa075c838a52209c2c6db29d49df1a7e00f7bc..b13f6417b5b262a81b149713184fe4de9364bea9 100644 (file)
 
 #include <asm/io.h>
 #include <mach/hardware.h>
-#include <asm/cacheflush.h>
 
 #include <asm/mach/flash.h>
 
+#define CACHELINESIZE  32
+
 static void pxa2xx_map_inval_cache(struct map_info *map, unsigned long from,
                                      ssize_t len)
 {
-       flush_ioremap_region(map->phys, map->cached, from, len);
+       unsigned long start = (unsigned long)map->cached + from;
+       unsigned long end = start + len;
+
+       start &= ~(CACHELINESIZE - 1);
+       while (start < end) {
+               /* invalidate D cache line */
+               asm volatile ("mcr p15, 0, %0, c7, c6, 1" : : "r" (start));
+               start += CACHELINESIZE;
+       }
 }
 
 struct pxa2xx_flash_info {
index 3aabf1e379888e7bf4c025d5d779862132efa007..76e640bccde848c258799b2d4b245e73a17e37bb 100644 (file)
@@ -291,7 +291,7 @@ static int pxa2xx_drv_pcmcia_probe(struct platform_device *dev)
                skt->nr = ops->first + i;
                skt->ops = ops;
                skt->socket.owner = ops->owner;
-               skt->socket.dev.parent = dev;
+               skt->socket.dev.parent = &dev->dev;
                skt->socket.pci_irq = NO_IRQ;
 
                ret = pxa2xx_drv_pcmcia_add_one(skt);
@@ -304,8 +304,8 @@ static int pxa2xx_drv_pcmcia_probe(struct platform_device *dev)
                        soc_pcmcia_remove_one(&sinfo->skt[i]);
                kfree(sinfo);
        } else {
-               pxa2xx_configure_sockets(dev);
-               dev_set_drvdata(dev, sinfo);
+               pxa2xx_configure_sockets(&dev->dev);
+               dev_set_drvdata(&dev->dev, sinfo);
        }
 
        return ret;
index 259db7f3535b4754b3fe2d95e9a92d722e75a4ec..9630e7d3314e59d19dd9e94be9f917cab7307f16 100644 (file)
@@ -778,6 +778,8 @@ static int __devinit ds1305_probe(struct spi_device *spi)
                                        spi->irq, status);
                        goto fail1;
                }
+
+               device_set_wakeup_capable(&spi->dev, 1);
        }
 
        /* export NVRAM */
index 8a99da6f2f2439fba20b9e6bd3772dafc639eedd..c4ec5c158aa14f81ca355c9c0b0366ffa383e11c 100644 (file)
@@ -881,6 +881,8 @@ read_rtc:
                                "unable to request IRQ!\n");
                        goto exit_irq;
                }
+
+               device_set_wakeup_capable(&client->dev, 1);
                set_bit(HAS_ALARM, &ds1307->flags);
                dev_dbg(&client->dev, "got IRQ %d\n", client->irq);
        }
index 713f7bf5afb330c508b5d2482804f97ed0083470..5317bbcbc7a0c7cb3f60e16e6bafac483327a666 100644 (file)
@@ -383,6 +383,8 @@ static int ds1374_probe(struct i2c_client *client,
                        dev_err(&client->dev, "unable to request IRQ\n");
                        goto out_free;
                }
+
+               device_set_wakeup_capable(&client->dev, 1);
        }
 
        ds1374->rtc = rtc_device_register(client->name, &client->dev,
index 2d9d70359360719ea3851ee8ad33fba70f884635..f55eb0107336b0a66ca8f68dd41b9a3752d3bb3a 100644 (file)
@@ -216,6 +216,17 @@ config SPI_S3C24XX
        help
          SPI driver for Samsung S3C24XX series ARM SoCs
 
+config SPI_S3C24XX_FIQ
+       bool "S3C24XX driver with FIQ pseudo-DMA"
+       depends on SPI_S3C24XX
+       select FIQ
+       help
+         Enable FIQ support for the S3C24XX SPI driver to provide pseudo
+         DMA by using the fast-interrupt request framework, This allows
+         the driver to get DMA-like performance when there are either
+         no free DMA channels, or when doing transfers that required both
+         TX and RX data paths.
+
 config SPI_S3C24XX_GPIO
        tristate "Samsung S3C24XX series SPI by GPIO"
        depends on ARCH_S3C2410 && EXPERIMENTAL
@@ -226,6 +237,13 @@ config SPI_S3C24XX_GPIO
          the inbuilt hardware cannot provide the transfer mode, or
          where the board is using non hardware connected pins.
 
+config SPI_S3C64XX
+       tristate "Samsung S3C64XX series type SPI"
+       depends on ARCH_S3C64XX && EXPERIMENTAL
+       select S3C64XX_DMA
+       help
+         SPI driver for Samsung S3C64XX and newer SoCs.
+
 config SPI_SH_MSIOF
        tristate "SuperH MSIOF SPI controller"
        depends on SUPERH && HAVE_CLK
@@ -289,6 +307,16 @@ config SPI_NUC900
 # Add new SPI master controllers in alphabetical order above this line
 #
 
+config SPI_DESIGNWARE
+       bool "DesignWare SPI controller core support"
+       depends on SPI_MASTER
+       help
+         general driver for SPI controller core from DesignWare
+
+config SPI_DW_PCI
+       tristate "PCI interface driver for DW SPI core"
+       depends on SPI_DESIGNWARE && PCI
+
 #
 # There are lots of SPI device types, with sensors and memory
 # being probably the most widely used ones.
index ed8c1675b52f8c1af6918eb016fe871b85ccf973..f3d2810ba11c9f3fd11cbdaddf9b50431e70b70b 100644 (file)
@@ -16,6 +16,8 @@ obj-$(CONFIG_SPI_BFIN)                        += spi_bfin5xx.o
 obj-$(CONFIG_SPI_BITBANG)              += spi_bitbang.o
 obj-$(CONFIG_SPI_AU1550)               += au1550_spi.o
 obj-$(CONFIG_SPI_BUTTERFLY)            += spi_butterfly.o
+obj-$(CONFIG_SPI_DESIGNWARE)           += dw_spi.o
+obj-$(CONFIG_SPI_DW_PCI)               += dw_spi_pci.o
 obj-$(CONFIG_SPI_GPIO)                 += spi_gpio.o
 obj-$(CONFIG_SPI_IMX)                  += spi_imx.o
 obj-$(CONFIG_SPI_LM70_LLP)             += spi_lm70llp.o
@@ -30,7 +32,8 @@ obj-$(CONFIG_SPI_MPC52xx)             += mpc52xx_spi.o
 obj-$(CONFIG_SPI_MPC8xxx)              += spi_mpc8xxx.o
 obj-$(CONFIG_SPI_PPC4xx)               += spi_ppc4xx.o
 obj-$(CONFIG_SPI_S3C24XX_GPIO)         += spi_s3c24xx_gpio.o
-obj-$(CONFIG_SPI_S3C24XX)              += spi_s3c24xx.o
+obj-$(CONFIG_SPI_S3C24XX)              += spi_s3c24xx_hw.o
+obj-$(CONFIG_SPI_S3C64XX)              += spi_s3c64xx.o
 obj-$(CONFIG_SPI_TXX9)                 += spi_txx9.o
 obj-$(CONFIG_SPI_XILINX)               += xilinx_spi.o
 obj-$(CONFIG_SPI_XILINX_OF)            += xilinx_spi_of.o
@@ -39,6 +42,11 @@ obj-$(CONFIG_SPI_SH_SCI)             += spi_sh_sci.o
 obj-$(CONFIG_SPI_SH_MSIOF)             += spi_sh_msiof.o
 obj-$(CONFIG_SPI_STMP3XXX)             += spi_stmp.o
 obj-$(CONFIG_SPI_NUC900)               += spi_nuc900.o
+
+# special build for s3c24xx spi driver with fiq support
+spi_s3c24xx_hw-y                       := spi_s3c24xx.o
+spi_s3c24xx_hw-$(CONFIG_SPI_S3C24XX_FIQ) += spi_s3c24xx_fiq.o
+
 #      ... add above this line ...
 
 # SPI protocol drivers (device/link on bus)
index f5b3fdbb1e27c22044df0b8c8759129a98622712..d21c24eaf0a95537baae7f22e783bff8658aa816 100644 (file)
@@ -189,14 +189,14 @@ static void atmel_spi_next_xfer_data(struct spi_master *master,
 
        /* use scratch buffer only when rx or tx data is unspecified */
        if (xfer->rx_buf)
-               *rx_dma = xfer->rx_dma + xfer->len - len;
+               *rx_dma = xfer->rx_dma + xfer->len - *plen;
        else {
                *rx_dma = as->buffer_dma;
                if (len > BUFFER_SIZE)
                        len = BUFFER_SIZE;
        }
        if (xfer->tx_buf)
-               *tx_dma = xfer->tx_dma + xfer->len - len;
+               *tx_dma = xfer->tx_dma + xfer->len - *plen;
        else {
                *tx_dma = as->buffer_dma;
                if (len > BUFFER_SIZE)
@@ -788,7 +788,7 @@ static int __init atmel_spi_probe(struct platform_device *pdev)
        spin_lock_init(&as->lock);
        INIT_LIST_HEAD(&as->queue);
        as->pdev = pdev;
-       as->regs = ioremap(regs->start, (regs->end - regs->start) + 1);
+       as->regs = ioremap(regs->start, resource_size(regs));
        if (!as->regs)
                goto out_free_buffer;
        as->irq = irq;
diff --git a/drivers/spi/dw_spi.c b/drivers/spi/dw_spi.c
new file mode 100644 (file)
index 0000000..31620fa
--- /dev/null
@@ -0,0 +1,944 @@
+/*
+ * dw_spi.c - Designware SPI core controller driver (refer pxa2xx_spi.c)
+ *
+ * Copyright (c) 2009, Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <linux/dma-mapping.h>
+#include <linux/interrupt.h>
+#include <linux/highmem.h>
+#include <linux/delay.h>
+
+#include <linux/spi/dw_spi.h>
+#include <linux/spi/spi.h>
+
+#ifdef CONFIG_DEBUG_FS
+#include <linux/debugfs.h>
+#endif
+
+#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
+
+#define MRST_SPI_DEASSERT      0
+#define MRST_SPI_ASSERT                1
+
+/* Slave spi_dev related */
+struct chip_data {
+       u16 cr0;
+       u8 cs;                  /* chip select pin */
+       u8 n_bytes;             /* current is a 1/2/4 byte op */
+       u8 tmode;               /* TR/TO/RO/EEPROM */
+       u8 type;                /* SPI/SSP/MicroWire */
+
+       u8 poll_mode;           /* 1 means use poll mode */
+
+       u32 dma_width;
+       u32 rx_threshold;
+       u32 tx_threshold;
+       u8 enable_dma;
+       u8 bits_per_word;
+       u16 clk_div;            /* baud rate divider */
+       u32 speed_hz;           /* baud rate */
+       int (*write)(struct dw_spi *dws);
+       int (*read)(struct dw_spi *dws);
+       void (*cs_control)(u32 command);
+};
+
+#ifdef CONFIG_DEBUG_FS
+static int spi_show_regs_open(struct inode *inode, struct file *file)
+{
+       file->private_data = inode->i_private;
+       return 0;
+}
+
+#define SPI_REGS_BUFSIZE       1024
+static ssize_t  spi_show_regs(struct file *file, char __user *user_buf,
+                               size_t count, loff_t *ppos)
+{
+       struct dw_spi *dws;
+       char *buf;
+       u32 len = 0;
+       ssize_t ret;
+
+       dws = file->private_data;
+
+       buf = kzalloc(SPI_REGS_BUFSIZE, GFP_KERNEL);
+       if (!buf)
+               return 0;
+
+       len += snprintf(buf + len, SPI_REGS_BUFSIZE - len,
+                       "MRST SPI0 registers:\n");
+       len += snprintf(buf + len, SPI_REGS_BUFSIZE - len,
+                       "=================================\n");
+       len += snprintf(buf + len, SPI_REGS_BUFSIZE - len,
+                       "CTRL0: \t\t0x%08x\n", dw_readl(dws, ctrl0));
+       len += snprintf(buf + len, SPI_REGS_BUFSIZE - len,
+                       "CTRL1: \t\t0x%08x\n", dw_readl(dws, ctrl1));
+       len += snprintf(buf + len, SPI_REGS_BUFSIZE - len,
+                       "SSIENR: \t0x%08x\n", dw_readl(dws, ssienr));
+       len += snprintf(buf + len, SPI_REGS_BUFSIZE - len,
+                       "SER: \t\t0x%08x\n", dw_readl(dws, ser));
+       len += snprintf(buf + len, SPI_REGS_BUFSIZE - len,
+                       "BAUDR: \t\t0x%08x\n", dw_readl(dws, baudr));
+       len += snprintf(buf + len, SPI_REGS_BUFSIZE - len,
+                       "TXFTLR: \t0x%08x\n", dw_readl(dws, txfltr));
+       len += snprintf(buf + len, SPI_REGS_BUFSIZE - len,
+                       "RXFTLR: \t0x%08x\n", dw_readl(dws, rxfltr));
+       len += snprintf(buf + len, SPI_REGS_BUFSIZE - len,
+                       "TXFLR: \t\t0x%08x\n", dw_readl(dws, txflr));
+       len += snprintf(buf + len, SPI_REGS_BUFSIZE - len,
+                       "RXFLR: \t\t0x%08x\n", dw_readl(dws, rxflr));
+       len += snprintf(buf + len, SPI_REGS_BUFSIZE - len,
+                       "SR: \t\t0x%08x\n", dw_readl(dws, sr));
+       len += snprintf(buf + len, SPI_REGS_BUFSIZE - len,
+                       "IMR: \t\t0x%08x\n", dw_readl(dws, imr));
+       len += snprintf(buf + len, SPI_REGS_BUFSIZE - len,
+                       "ISR: \t\t0x%08x\n", dw_readl(dws, isr));
+       len += snprintf(buf + len, SPI_REGS_BUFSIZE - len,
+                       "DMACR: \t\t0x%08x\n", dw_readl(dws, dmacr));
+       len += snprintf(buf + len, SPI_REGS_BUFSIZE - len,
+                       "DMATDLR: \t0x%08x\n", dw_readl(dws, dmatdlr));
+       len += snprintf(buf + len, SPI_REGS_BUFSIZE - len,
+                       "DMARDLR: \t0x%08x\n", dw_readl(dws, dmardlr));
+       len += snprintf(buf + len, SPI_REGS_BUFSIZE - len,
+                       "=================================\n");
+
+       ret =  simple_read_from_buffer(user_buf, count, ppos, buf, len);
+       kfree(buf);
+       return ret;
+}
+
+static const struct file_operations mrst_spi_regs_ops = {
+       .owner          = THIS_MODULE,
+       .open           = spi_show_regs_open,
+       .read           = spi_show_regs,
+};
+
+static int mrst_spi_debugfs_init(struct dw_spi *dws)
+{
+       dws->debugfs = debugfs_create_dir("mrst_spi", NULL);
+       if (!dws->debugfs)
+               return -ENOMEM;
+
+       debugfs_create_file("registers", S_IFREG | S_IRUGO,
+               dws->debugfs, (void *)dws, &mrst_spi_regs_ops);
+       return 0;
+}
+
+static void mrst_spi_debugfs_remove(struct dw_spi *dws)
+{
+       if (dws->debugfs)
+               debugfs_remove_recursive(dws->debugfs);
+}
+
+#else
+static inline int mrst_spi_debugfs_init(struct dw_spi *dws)
+{
+}
+
+static inline void mrst_spi_debugfs_remove(struct dw_spi *dws)
+{
+}
+#endif /* CONFIG_DEBUG_FS */
+
+static void wait_till_not_busy(struct dw_spi *dws)
+{
+       unsigned long end = jiffies + usecs_to_jiffies(1000);
+
+       while (time_before(jiffies, end)) {
+               if (!(dw_readw(dws, sr) & SR_BUSY))
+                       return;
+       }
+       dev_err(&dws->master->dev,
+               "DW SPI: Stutus keeps busy for 1000us after a read/write!\n");
+}
+
+static void flush(struct dw_spi *dws)
+{
+       while (dw_readw(dws, sr) & SR_RF_NOT_EMPT)
+               dw_readw(dws, dr);
+
+       wait_till_not_busy(dws);
+}
+
+static void null_cs_control(u32 command)
+{
+}
+
+static int null_writer(struct dw_spi *dws)
+{
+       u8 n_bytes = dws->n_bytes;
+
+       if (!(dw_readw(dws, sr) & SR_TF_NOT_FULL)
+               || (dws->tx == dws->tx_end))
+               return 0;
+       dw_writew(dws, dr, 0);
+       dws->tx += n_bytes;
+
+       wait_till_not_busy(dws);
+       return 1;
+}
+
+static int null_reader(struct dw_spi *dws)
+{
+       u8 n_bytes = dws->n_bytes;
+
+       while ((dw_readw(dws, sr) & SR_RF_NOT_EMPT)
+               && (dws->rx < dws->rx_end)) {
+               dw_readw(dws, dr);
+               dws->rx += n_bytes;
+       }
+       wait_till_not_busy(dws);
+       return dws->rx == dws->rx_end;
+}
+
+static int u8_writer(struct dw_spi *dws)
+{
+       if (!(dw_readw(dws, sr) & SR_TF_NOT_FULL)
+               || (dws->tx == dws->tx_end))
+               return 0;
+
+       dw_writew(dws, dr, *(u8 *)(dws->tx));
+       ++dws->tx;
+
+       wait_till_not_busy(dws);
+       return 1;
+}
+
+static int u8_reader(struct dw_spi *dws)
+{
+       while ((dw_readw(dws, sr) & SR_RF_NOT_EMPT)
+               && (dws->rx < dws->rx_end)) {
+               *(u8 *)(dws->rx) = dw_readw(dws, dr);
+               ++dws->rx;
+       }
+
+       wait_till_not_busy(dws);
+       return dws->rx == dws->rx_end;
+}
+
+static int u16_writer(struct dw_spi *dws)
+{
+       if (!(dw_readw(dws, sr) & SR_TF_NOT_FULL)
+               || (dws->tx == dws->tx_end))
+               return 0;
+
+       dw_writew(dws, dr, *(u16 *)(dws->tx));
+       dws->tx += 2;
+
+       wait_till_not_busy(dws);
+       return 1;
+}
+
+static int u16_reader(struct dw_spi *dws)
+{
+       u16 temp;
+
+       while ((dw_readw(dws, sr) & SR_RF_NOT_EMPT)
+               && (dws->rx < dws->rx_end)) {
+               temp = dw_readw(dws, dr);
+               *(u16 *)(dws->rx) = temp;
+               dws->rx += 2;
+       }
+
+       wait_till_not_busy(dws);
+       return dws->rx == dws->rx_end;
+}
+
+static void *next_transfer(struct dw_spi *dws)
+{
+       struct spi_message *msg = dws->cur_msg;
+       struct spi_transfer *trans = dws->cur_transfer;
+
+       /* Move to next transfer */
+       if (trans->transfer_list.next != &msg->transfers) {
+               dws->cur_transfer =
+                       list_entry(trans->transfer_list.next,
+                                       struct spi_transfer,
+                                       transfer_list);
+               return RUNNING_STATE;
+       } else
+               return DONE_STATE;
+}
+
+/*
+ * Note: first step is the protocol driver prepares
+ * a dma-capable memory, and this func just need translate
+ * the virt addr to physical
+ */
+static int map_dma_buffers(struct dw_spi *dws)
+{
+       if (!dws->cur_msg->is_dma_mapped || !dws->dma_inited
+               || !dws->cur_chip->enable_dma)
+               return 0;
+
+       if (dws->cur_transfer->tx_dma)
+               dws->tx_dma = dws->cur_transfer->tx_dma;
+
+       if (dws->cur_transfer->rx_dma)
+               dws->rx_dma = dws->cur_transfer->rx_dma;
+
+       return 1;
+}
+
+/* Caller already set message->status; dma and pio irqs are blocked */
+static void giveback(struct dw_spi *dws)
+{
+       struct spi_transfer *last_transfer;
+       unsigned long flags;
+       struct spi_message *msg;
+
+       spin_lock_irqsave(&dws->lock, flags);
+       msg = dws->cur_msg;
+       dws->cur_msg = NULL;
+       dws->cur_transfer = NULL;
+       dws->prev_chip = dws->cur_chip;
+       dws->cur_chip = NULL;
+       dws->dma_mapped = 0;
+       queue_work(dws->workqueue, &dws->pump_messages);
+       spin_unlock_irqrestore(&dws->lock, flags);
+
+       last_transfer = list_entry(msg->transfers.prev,
+                                       struct spi_transfer,
+                                       transfer_list);
+
+       if (!last_transfer->cs_change)
+               dws->cs_control(MRST_SPI_DEASSERT);
+
+       msg->state = NULL;
+       if (msg->complete)
+               msg->complete(msg->context);
+}
+
+static void int_error_stop(struct dw_spi *dws, const char *msg)
+{
+       /* Stop and reset hw */
+       flush(dws);
+       spi_enable_chip(dws, 0);
+
+       dev_err(&dws->master->dev, "%s\n", msg);
+       dws->cur_msg->state = ERROR_STATE;
+       tasklet_schedule(&dws->pump_transfers);
+}
+
+static void transfer_complete(struct dw_spi *dws)
+{
+       /* Update total byte transfered return count actual bytes read */
+       dws->cur_msg->actual_length += dws->len;
+
+       /* Move to next transfer */
+       dws->cur_msg->state = next_transfer(dws);
+
+       /* Handle end of message */
+       if (dws->cur_msg->state == DONE_STATE) {
+               dws->cur_msg->status = 0;
+               giveback(dws);
+       } else
+               tasklet_schedule(&dws->pump_transfers);
+}
+
+static irqreturn_t interrupt_transfer(struct dw_spi *dws)
+{
+       u16 irq_status, irq_mask = 0x3f;
+
+       irq_status = dw_readw(dws, isr) & irq_mask;
+       /* Error handling */
+       if (irq_status & (SPI_INT_TXOI | SPI_INT_RXOI | SPI_INT_RXUI)) {
+               dw_readw(dws, txoicr);
+               dw_readw(dws, rxoicr);
+               dw_readw(dws, rxuicr);
+               int_error_stop(dws, "interrupt_transfer: fifo overrun");
+               return IRQ_HANDLED;
+       }
+
+       /* INT comes from tx */
+       if (dws->tx && (irq_status & SPI_INT_TXEI)) {
+               while (dws->tx < dws->tx_end)
+                       dws->write(dws);
+
+               if (dws->tx == dws->tx_end) {
+                       spi_mask_intr(dws, SPI_INT_TXEI);
+                       transfer_complete(dws);
+               }
+       }
+
+       /* INT comes from rx */
+       if (dws->rx && (irq_status & SPI_INT_RXFI)) {
+               if (dws->read(dws))
+                       transfer_complete(dws);
+       }
+       return IRQ_HANDLED;
+}
+
+static irqreturn_t dw_spi_irq(int irq, void *dev_id)
+{
+       struct dw_spi *dws = dev_id;
+
+       if (!dws->cur_msg) {
+               spi_mask_intr(dws, SPI_INT_TXEI);
+               /* Never fail */
+               return IRQ_HANDLED;
+       }
+
+       return dws->transfer_handler(dws);
+}
+
+/* Must be called inside pump_transfers() */
+static void poll_transfer(struct dw_spi *dws)
+{
+       if (dws->tx) {
+               while (dws->write(dws))
+                       dws->read(dws);
+       }
+
+       dws->read(dws);
+       transfer_complete(dws);
+}
+
+static void dma_transfer(struct dw_spi *dws, int cs_change)
+{
+}
+
+static void pump_transfers(unsigned long data)
+{
+       struct dw_spi *dws = (struct dw_spi *)data;
+       struct spi_message *message = NULL;
+       struct spi_transfer *transfer = NULL;
+       struct spi_transfer *previous = NULL;
+       struct spi_device *spi = NULL;
+       struct chip_data *chip = NULL;
+       u8 bits = 0;
+       u8 imask = 0;
+       u8 cs_change = 0;
+       u16 clk_div = 0;
+       u32 speed = 0;
+       u32 cr0 = 0;
+
+       /* Get current state information */
+       message = dws->cur_msg;
+       transfer = dws->cur_transfer;
+       chip = dws->cur_chip;
+       spi = message->spi;
+
+       if (message->state == ERROR_STATE) {
+               message->status = -EIO;
+               goto early_exit;
+       }
+
+       /* Handle end of message */
+       if (message->state == DONE_STATE) {
+               message->status = 0;
+               goto early_exit;
+       }
+
+       /* 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);
+       }
+
+       dws->n_bytes = chip->n_bytes;
+       dws->dma_width = chip->dma_width;
+       dws->cs_control = chip->cs_control;
+
+       dws->rx_dma = transfer->rx_dma;
+       dws->tx_dma = transfer->tx_dma;
+       dws->tx = (void *)transfer->tx_buf;
+       dws->tx_end = dws->tx + transfer->len;
+       dws->rx = transfer->rx_buf;
+       dws->rx_end = dws->rx + transfer->len;
+       dws->write = dws->tx ? chip->write : null_writer;
+       dws->read = dws->rx ? chip->read : null_reader;
+       dws->cs_change = transfer->cs_change;
+       dws->len = dws->cur_transfer->len;
+       if (chip != dws->prev_chip)
+               cs_change = 1;
+
+       cr0 = chip->cr0;
+
+       /* Handle per transfer options for bpw and speed */
+       if (transfer->speed_hz) {
+               speed = chip->speed_hz;
+
+               if (transfer->speed_hz != speed) {
+                       speed = transfer->speed_hz;
+                       if (speed > dws->max_freq) {
+                               printk(KERN_ERR "MRST SPI0: unsupported"
+                                       "freq: %dHz\n", speed);
+                               message->status = -EIO;
+                               goto early_exit;
+                       }
+
+                       /* clk_div doesn't support odd number */
+                       clk_div = dws->max_freq / speed;
+                       clk_div = (clk_div >> 1) << 1;
+
+                       chip->speed_hz = speed;
+                       chip->clk_div = clk_div;
+               }
+       }
+       if (transfer->bits_per_word) {
+               bits = transfer->bits_per_word;
+
+               switch (bits) {
+               case 8:
+                       dws->n_bytes = 1;
+                       dws->dma_width = 1;
+                       dws->read = (dws->read != null_reader) ?
+                                       u8_reader : null_reader;
+                       dws->write = (dws->write != null_writer) ?
+                                       u8_writer : null_writer;
+                       break;
+               case 16:
+                       dws->n_bytes = 2;
+                       dws->dma_width = 2;
+                       dws->read = (dws->read != null_reader) ?
+                                       u16_reader : null_reader;
+                       dws->write = (dws->write != null_writer) ?
+                                       u16_writer : null_writer;
+                       break;
+               default:
+                       printk(KERN_ERR "MRST SPI0: unsupported bits:"
+                               "%db\n", bits);
+                       message->status = -EIO;
+                       goto early_exit;
+               }
+
+               cr0 = (bits - 1)
+                       | (chip->type << SPI_FRF_OFFSET)
+                       | (spi->mode << SPI_MODE_OFFSET)
+                       | (chip->tmode << SPI_TMOD_OFFSET);
+       }
+       message->state = RUNNING_STATE;
+
+       /* Check if current transfer is a DMA transaction */
+       dws->dma_mapped = map_dma_buffers(dws);
+
+       if (!dws->dma_mapped && !chip->poll_mode) {
+               if (dws->rx)
+                       imask |= SPI_INT_RXFI;
+               if (dws->tx)
+                       imask |= SPI_INT_TXEI;
+               dws->transfer_handler = interrupt_transfer;
+       }
+
+       /*
+        * Reprogram registers only if
+        *      1. chip select changes
+        *      2. clk_div is changed
+        *      3. control value changes
+        */
+       if (dw_readw(dws, ctrl0) != cr0 || cs_change || clk_div) {
+               spi_enable_chip(dws, 0);
+
+               if (dw_readw(dws, ctrl0) != cr0)
+                       dw_writew(dws, ctrl0, cr0);
+
+               /* Set the interrupt mask, for poll mode just diable all int */
+               spi_mask_intr(dws, 0xff);
+               if (!chip->poll_mode)
+                       spi_umask_intr(dws, imask);
+
+               spi_set_clk(dws, clk_div ? clk_div : chip->clk_div);
+               spi_chip_sel(dws, spi->chip_select);
+               spi_enable_chip(dws, 1);
+
+               if (cs_change)
+                       dws->prev_chip = chip;
+       }
+
+       if (dws->dma_mapped)
+               dma_transfer(dws, cs_change);
+
+       if (chip->poll_mode)
+               poll_transfer(dws);
+
+       return;
+
+early_exit:
+       giveback(dws);
+       return;
+}
+
+static void pump_messages(struct work_struct *work)
+{
+       struct dw_spi *dws =
+               container_of(work, struct dw_spi, pump_messages);
+       unsigned long flags;
+
+       /* Lock queue and check for queue work */
+       spin_lock_irqsave(&dws->lock, flags);
+       if (list_empty(&dws->queue) || dws->run == QUEUE_STOPPED) {
+               dws->busy = 0;
+               spin_unlock_irqrestore(&dws->lock, flags);
+               return;
+       }
+
+       /* Make sure we are not already running a message */
+       if (dws->cur_msg) {
+               spin_unlock_irqrestore(&dws->lock, flags);
+               return;
+       }
+
+       /* Extract head of queue */
+       dws->cur_msg = list_entry(dws->queue.next, struct spi_message, queue);
+       list_del_init(&dws->cur_msg->queue);
+
+       /* Initial message state*/
+       dws->cur_msg->state = START_STATE;
+       dws->cur_transfer = list_entry(dws->cur_msg->transfers.next,
+                                               struct spi_transfer,
+                                               transfer_list);
+       dws->cur_chip = spi_get_ctldata(dws->cur_msg->spi);
+
+       /* Mark as busy and launch transfers */
+       tasklet_schedule(&dws->pump_transfers);
+
+       dws->busy = 1;
+       spin_unlock_irqrestore(&dws->lock, flags);
+}
+
+/* spi_device use this to queue in their spi_msg */
+static int dw_spi_transfer(struct spi_device *spi, struct spi_message *msg)
+{
+       struct dw_spi *dws = spi_master_get_devdata(spi->master);
+       unsigned long flags;
+
+       spin_lock_irqsave(&dws->lock, flags);
+
+       if (dws->run == QUEUE_STOPPED) {
+               spin_unlock_irqrestore(&dws->lock, flags);
+               return -ESHUTDOWN;
+       }
+
+       msg->actual_length = 0;
+       msg->status = -EINPROGRESS;
+       msg->state = START_STATE;
+
+       list_add_tail(&msg->queue, &dws->queue);
+
+       if (dws->run == QUEUE_RUNNING && !dws->busy) {
+
+               if (dws->cur_transfer || dws->cur_msg)
+                       queue_work(dws->workqueue,
+                                       &dws->pump_messages);
+               else {
+                       /* If no other data transaction in air, just go */
+                       spin_unlock_irqrestore(&dws->lock, flags);
+                       pump_messages(&dws->pump_messages);
+                       return 0;
+               }
+       }
+
+       spin_unlock_irqrestore(&dws->lock, flags);
+       return 0;
+}
+
+/* This may be called twice for each spi dev */
+static int dw_spi_setup(struct spi_device *spi)
+{
+       struct dw_spi_chip *chip_info = NULL;
+       struct chip_data *chip;
+
+       if (spi->bits_per_word != 8 && spi->bits_per_word != 16)
+               return -EINVAL;
+
+       /* Only alloc on first setup */
+       chip = spi_get_ctldata(spi);
+       if (!chip) {
+               chip = kzalloc(sizeof(struct chip_data), GFP_KERNEL);
+               if (!chip)
+                       return -ENOMEM;
+
+               chip->cs_control = null_cs_control;
+               chip->enable_dma = 0;
+       }
+
+       /*
+        * Protocol drivers may change the chip settings, so...
+        * if chip_info exists, use it
+        */
+       chip_info = spi->controller_data;
+
+       /* chip_info doesn't always exist */
+       if (chip_info) {
+               if (chip_info->cs_control)
+                       chip->cs_control = chip_info->cs_control;
+
+               chip->poll_mode = chip_info->poll_mode;
+               chip->type = chip_info->type;
+
+               chip->rx_threshold = 0;
+               chip->tx_threshold = 0;
+
+               chip->enable_dma = chip_info->enable_dma;
+       }
+
+       if (spi->bits_per_word <= 8) {
+               chip->n_bytes = 1;
+               chip->dma_width = 1;
+               chip->read = u8_reader;
+               chip->write = u8_writer;
+       } else if (spi->bits_per_word <= 16) {
+               chip->n_bytes = 2;
+               chip->dma_width = 2;
+               chip->read = u16_reader;
+               chip->write = u16_writer;
+       } else {
+               /* Never take >16b case for MRST SPIC */
+               dev_err(&spi->dev, "invalid wordsize\n");
+               return -EINVAL;
+       }
+       chip->bits_per_word = spi->bits_per_word;
+
+       chip->speed_hz = spi->max_speed_hz;
+       if (chip->speed_hz)
+               chip->clk_div = 25000000 / chip->speed_hz;
+       else
+               chip->clk_div = 8;      /* default value */
+
+       chip->tmode = 0; /* Tx & Rx */
+       /* Default SPI mode is SCPOL = 0, SCPH = 0 */
+       chip->cr0 = (chip->bits_per_word - 1)
+                       | (chip->type << SPI_FRF_OFFSET)
+                       | (spi->mode  << SPI_MODE_OFFSET)
+                       | (chip->tmode << SPI_TMOD_OFFSET);
+
+       spi_set_ctldata(spi, chip);
+       return 0;
+}
+
+static void dw_spi_cleanup(struct spi_device *spi)
+{
+       struct chip_data *chip = spi_get_ctldata(spi);
+       kfree(chip);
+}
+
+static int __init init_queue(struct dw_spi *dws)
+{
+       INIT_LIST_HEAD(&dws->queue);
+       spin_lock_init(&dws->lock);
+
+       dws->run = QUEUE_STOPPED;
+       dws->busy = 0;
+
+       tasklet_init(&dws->pump_transfers,
+                       pump_transfers, (unsigned long)dws);
+
+       INIT_WORK(&dws->pump_messages, pump_messages);
+       dws->workqueue = create_singlethread_workqueue(
+                                       dev_name(dws->master->dev.parent));
+       if (dws->workqueue == NULL)
+               return -EBUSY;
+
+       return 0;
+}
+
+static int start_queue(struct dw_spi *dws)
+{
+       unsigned long flags;
+
+       spin_lock_irqsave(&dws->lock, flags);
+
+       if (dws->run == QUEUE_RUNNING || dws->busy) {
+               spin_unlock_irqrestore(&dws->lock, flags);
+               return -EBUSY;
+       }
+
+       dws->run = QUEUE_RUNNING;
+       dws->cur_msg = NULL;
+       dws->cur_transfer = NULL;
+       dws->cur_chip = NULL;
+       dws->prev_chip = NULL;
+       spin_unlock_irqrestore(&dws->lock, flags);
+
+       queue_work(dws->workqueue, &dws->pump_messages);
+
+       return 0;
+}
+
+static int stop_queue(struct dw_spi *dws)
+{
+       unsigned long flags;
+       unsigned limit = 50;
+       int status = 0;
+
+       spin_lock_irqsave(&dws->lock, flags);
+       dws->run = QUEUE_STOPPED;
+       while (!list_empty(&dws->queue) && dws->busy && limit--) {
+               spin_unlock_irqrestore(&dws->lock, flags);
+               msleep(10);
+               spin_lock_irqsave(&dws->lock, flags);
+       }
+
+       if (!list_empty(&dws->queue) || dws->busy)
+               status = -EBUSY;
+       spin_unlock_irqrestore(&dws->lock, flags);
+
+       return status;
+}
+
+static int destroy_queue(struct dw_spi *dws)
+{
+       int status;
+
+       status = stop_queue(dws);
+       if (status != 0)
+               return status;
+       destroy_workqueue(dws->workqueue);
+       return 0;
+}
+
+/* Restart the controller, disable all interrupts, clean rx fifo */
+static void spi_hw_init(struct dw_spi *dws)
+{
+       spi_enable_chip(dws, 0);
+       spi_mask_intr(dws, 0xff);
+       spi_enable_chip(dws, 1);
+       flush(dws);
+}
+
+int __devinit dw_spi_add_host(struct dw_spi *dws)
+{
+       struct spi_master *master;
+       int ret;
+
+       BUG_ON(dws == NULL);
+
+       master = spi_alloc_master(dws->parent_dev, 0);
+       if (!master) {
+               ret = -ENOMEM;
+               goto exit;
+       }
+
+       dws->master = master;
+       dws->type = SSI_MOTO_SPI;
+       dws->prev_chip = NULL;
+       dws->dma_inited = 0;
+       dws->dma_addr = (dma_addr_t)(dws->paddr + 0x60);
+
+       ret = request_irq(dws->irq, dw_spi_irq, 0,
+                       "dw_spi", dws);
+       if (ret < 0) {
+               dev_err(&master->dev, "can not get IRQ\n");
+               goto err_free_master;
+       }
+
+       master->mode_bits = SPI_CPOL | SPI_CPHA;
+       master->bus_num = dws->bus_num;
+       master->num_chipselect = dws->num_cs;
+       master->cleanup = dw_spi_cleanup;
+       master->setup = dw_spi_setup;
+       master->transfer = dw_spi_transfer;
+
+       dws->dma_inited = 0;
+
+       /* Basic HW init */
+       spi_hw_init(dws);
+
+       /* Initial and start queue */
+       ret = init_queue(dws);
+       if (ret) {
+               dev_err(&master->dev, "problem initializing queue\n");
+               goto err_diable_hw;
+       }
+       ret = start_queue(dws);
+       if (ret) {
+               dev_err(&master->dev, "problem starting queue\n");
+               goto err_diable_hw;
+       }
+
+       spi_master_set_devdata(master, dws);
+       ret = spi_register_master(master);
+       if (ret) {
+               dev_err(&master->dev, "problem registering spi master\n");
+               goto err_queue_alloc;
+       }
+
+       mrst_spi_debugfs_init(dws);
+       return 0;
+
+err_queue_alloc:
+       destroy_queue(dws);
+err_diable_hw:
+       spi_enable_chip(dws, 0);
+       free_irq(dws->irq, dws);
+err_free_master:
+       spi_master_put(master);
+exit:
+       return ret;
+}
+EXPORT_SYMBOL(dw_spi_add_host);
+
+void __devexit dw_spi_remove_host(struct dw_spi *dws)
+{
+       int status = 0;
+
+       if (!dws)
+               return;
+       mrst_spi_debugfs_remove(dws);
+
+       /* Remove the queue */
+       status = destroy_queue(dws);
+       if (status != 0)
+               dev_err(&dws->master->dev, "dw_spi_remove: workqueue will not "
+                       "complete, message memory not freed\n");
+
+       spi_enable_chip(dws, 0);
+       /* Disable clk */
+       spi_set_clk(dws, 0);
+       free_irq(dws->irq, dws);
+
+       /* Disconnect from the SPI framework */
+       spi_unregister_master(dws->master);
+}
+
+int dw_spi_suspend_host(struct dw_spi *dws)
+{
+       int ret = 0;
+
+       ret = stop_queue(dws);
+       if (ret)
+               return ret;
+       spi_enable_chip(dws, 0);
+       spi_set_clk(dws, 0);
+       return ret;
+}
+EXPORT_SYMBOL(dw_spi_suspend_host);
+
+int dw_spi_resume_host(struct dw_spi *dws)
+{
+       int ret;
+
+       spi_hw_init(dws);
+       ret = start_queue(dws);
+       if (ret)
+               dev_err(&dws->master->dev, "fail to start queue (%d)\n", ret);
+       return ret;
+}
+EXPORT_SYMBOL(dw_spi_resume_host);
+
+MODULE_AUTHOR("Feng Tang <feng.tang@intel.com>");
+MODULE_DESCRIPTION("Driver for DesignWare SPI controller core");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/spi/dw_spi_pci.c b/drivers/spi/dw_spi_pci.c
new file mode 100644 (file)
index 0000000..34ba691
--- /dev/null
@@ -0,0 +1,169 @@
+/*
+ * mrst_spi_pci.c - PCI interface driver for DW SPI Core
+ *
+ * Copyright (c) 2009, Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <linux/interrupt.h>
+#include <linux/pci.h>
+#include <linux/spi/dw_spi.h>
+#include <linux/spi/spi.h>
+
+#define DRIVER_NAME "dw_spi_pci"
+
+struct dw_spi_pci {
+       struct pci_dev          *pdev;
+       struct dw_spi           dws;
+};
+
+static int __devinit spi_pci_probe(struct pci_dev *pdev,
+       const struct pci_device_id *ent)
+{
+       struct dw_spi_pci *dwpci;
+       struct dw_spi *dws;
+       int pci_bar = 0;
+       int ret;
+
+       printk(KERN_INFO "DW: found PCI SPI controller(ID: %04x:%04x)\n",
+               pdev->vendor, pdev->device);
+
+       ret = pci_enable_device(pdev);
+       if (ret)
+               return ret;
+
+       dwpci = kzalloc(sizeof(struct dw_spi_pci), GFP_KERNEL);
+       if (!dwpci) {
+               ret = -ENOMEM;
+               goto err_disable;
+       }
+
+       dwpci->pdev = pdev;
+       dws = &dwpci->dws;
+
+       /* Get basic io resource and map it */
+       dws->paddr = pci_resource_start(pdev, pci_bar);
+       dws->iolen = pci_resource_len(pdev, pci_bar);
+
+       ret = pci_request_region(pdev, pci_bar, dev_name(&pdev->dev));
+       if (ret)
+               goto err_kfree;
+
+       dws->regs = ioremap_nocache((unsigned long)dws->paddr,
+                               pci_resource_len(pdev, pci_bar));
+       if (!dws->regs) {
+               ret = -ENOMEM;
+               goto err_release_reg;
+       }
+
+       dws->parent_dev = &pdev->dev;
+       dws->bus_num = 0;
+       dws->num_cs = 4;
+       dws->max_freq = 25000000;       /* for Moorestwon */
+       dws->irq = pdev->irq;
+
+       ret = dw_spi_add_host(dws);
+       if (ret)
+               goto err_unmap;
+
+       /* PCI hook and SPI hook use the same drv data */
+       pci_set_drvdata(pdev, dwpci);
+       return 0;
+
+err_unmap:
+       iounmap(dws->regs);
+err_release_reg:
+       pci_release_region(pdev, pci_bar);
+err_kfree:
+       kfree(dwpci);
+err_disable:
+       pci_disable_device(pdev);
+       return ret;
+}
+
+static void __devexit spi_pci_remove(struct pci_dev *pdev)
+{
+       struct dw_spi_pci *dwpci = pci_get_drvdata(pdev);
+
+       pci_set_drvdata(pdev, NULL);
+       iounmap(dwpci->dws.regs);
+       pci_release_region(pdev, 0);
+       kfree(dwpci);
+       pci_disable_device(pdev);
+}
+
+#ifdef CONFIG_PM
+static int spi_suspend(struct pci_dev *pdev, pm_message_t state)
+{
+       struct dw_spi_pci *dwpci = pci_get_drvdata(pdev);
+       int ret;
+
+       ret = dw_spi_suspend_host(&dwpci->dws);
+       if (ret)
+               return ret;
+       pci_save_state(pdev);
+       pci_disable_device(pdev);
+       pci_set_power_state(pdev, pci_choose_state(pdev, state));
+       return ret;
+}
+
+static int spi_resume(struct pci_dev *pdev)
+{
+       struct dw_spi_pci *dwpci = pci_get_drvdata(pdev);
+       int ret;
+
+       pci_set_power_state(pdev, PCI_D0);
+       pci_restore_state(pdev);
+       ret = pci_enable_device(pdev);
+       if (ret)
+               return ret;
+       return dw_spi_resume_host(&dwpci->dws);
+}
+#else
+#define spi_suspend    NULL
+#define spi_resume     NULL
+#endif
+
+static const struct pci_device_id pci_ids[] __devinitdata = {
+       /* Intel Moorestown platform SPI controller 0 */
+       { PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x0800) },
+       {},
+};
+
+static struct pci_driver dw_spi_driver = {
+       .name =         DRIVER_NAME,
+       .id_table =     pci_ids,
+       .probe =        spi_pci_probe,
+       .remove =       __devexit_p(spi_pci_remove),
+       .suspend =      spi_suspend,
+       .resume =       spi_resume,
+};
+
+static int __init mrst_spi_init(void)
+{
+       return pci_register_driver(&dw_spi_driver);
+}
+
+static void __exit mrst_spi_exit(void)
+{
+       pci_unregister_driver(&dw_spi_driver);
+}
+
+module_init(mrst_spi_init);
+module_exit(mrst_spi_exit);
+
+MODULE_AUTHOR("Feng Tang <feng.tang@intel.com>");
+MODULE_DESCRIPTION("PCI interface driver for DW SPI Core");
+MODULE_LICENSE("GPL v2");
index 73e24ef5a2f961fa41c0d3ee81dc03143e768cd5..1d41058bbab2a4227dc663f388e3e026e1ce84b3 100644 (file)
@@ -1294,7 +1294,7 @@ static int __init bfin_spi_probe(struct platform_device *pdev)
                goto out_error_get_res;
        }
 
-       drv_data->regs_base = ioremap(res->start, (res->end - res->start + 1));
+       drv_data->regs_base = ioremap(res->start, resource_size(res));
        if (drv_data->regs_base == NULL) {
                dev_err(dev, "Cannot map IO\n");
                status = -ENXIO;
index e9390d747bfcf35e018747fcfc6a1b2e529ef486..1fb2a6ea328cfcff9ce8ec3a03700f3bec729947 100644 (file)
@@ -1013,7 +1013,7 @@ mpc8xxx_spi_probe(struct device *dev, struct resource *mem, unsigned int irq)
 
        init_completion(&mpc8xxx_spi->done);
 
-       mpc8xxx_spi->base = ioremap(mem->start, mem->end - mem->start + 1);
+       mpc8xxx_spi->base = ioremap(mem->start, resource_size(mem));
        if (mpc8xxx_spi->base == NULL) {
                ret = -ENOMEM;
                goto err_ioremap;
index 276591569c8bce421f1c2c9c4b0ed94385d22f44..c010733877ae73f8dc57c211c97163d0301a34d6 100644 (file)
@@ -1,7 +1,7 @@
 /* linux/drivers/spi/spi_s3c24xx.c
  *
  * Copyright (c) 2006 Ben Dooks
- * Copyright (c) 2006 Simtec Electronics
+ * Copyright 2006-2009 Simtec Electronics
  *     Ben Dooks <ben@simtec.co.uk>
  *
  * This program is free software; you can redistribute it and/or modify
 #include <plat/regs-spi.h>
 #include <mach/spi.h>
 
+#include <plat/fiq.h>
+#include <asm/fiq.h>
+
+#include "spi_s3c24xx_fiq.h"
+
 /**
  * s3c24xx_spi_devstate - per device data
  * @hz: Last frequency calculated for @sppre field.
@@ -42,6 +47,13 @@ struct s3c24xx_spi_devstate {
        u8              sppre;
 };
 
+enum spi_fiq_mode {
+       FIQ_MODE_NONE   = 0,
+       FIQ_MODE_TX     = 1,
+       FIQ_MODE_RX     = 2,
+       FIQ_MODE_TXRX   = 3,
+};
+
 struct s3c24xx_spi {
        /* bitbang has to be first */
        struct spi_bitbang       bitbang;
@@ -52,6 +64,11 @@ struct s3c24xx_spi {
        int                      len;
        int                      count;
 
+       struct fiq_handler       fiq_handler;
+       enum spi_fiq_mode        fiq_mode;
+       unsigned char            fiq_inuse;
+       unsigned char            fiq_claimed;
+
        void                    (*set_cs)(struct s3c2410_spi_info *spi,
                                          int cs, int pol);
 
@@ -67,6 +84,7 @@ struct s3c24xx_spi {
        struct s3c2410_spi_info *pdata;
 };
 
+
 #define SPCON_DEFAULT (S3C2410_SPCON_MSTR | S3C2410_SPCON_SMOD_INT)
 #define SPPIN_DEFAULT (S3C2410_SPPIN_KEEP)
 
@@ -127,7 +145,7 @@ static int s3c24xx_spi_update_state(struct spi_device *spi,
        }
 
        if (spi->mode != cs->mode) {
-               u8 spcon = SPCON_DEFAULT;
+               u8 spcon = SPCON_DEFAULT | S3C2410_SPCON_ENSCK;
 
                if (spi->mode & SPI_CPHA)
                        spcon |= S3C2410_SPCON_CPHA_FMTB;
@@ -214,13 +232,196 @@ static inline unsigned int hw_txbyte(struct s3c24xx_spi *hw, int count)
        return hw->tx ? hw->tx[count] : 0;
 }
 
+#ifdef CONFIG_SPI_S3C24XX_FIQ
+/* Support for FIQ based pseudo-DMA to improve the transfer speed.
+ *
+ * This code uses the assembly helper in spi_s3c24xx_spi.S which is
+ * used by the FIQ core to move data between main memory and the peripheral
+ * block. Since this is code running on the processor, there is no problem
+ * with cache coherency of the buffers, so we can use any buffer we like.
+ */
+
+/**
+ * struct spi_fiq_code - FIQ code and header
+ * @length: The length of the code fragment, excluding this header.
+ * @ack_offset: The offset from @data to the word to place the IRQ ACK bit at.
+ * @data: The code itself to install as a FIQ handler.
+ */
+struct spi_fiq_code {
+       u32     length;
+       u32     ack_offset;
+       u8      data[0];
+};
+
+extern struct spi_fiq_code s3c24xx_spi_fiq_txrx;
+extern struct spi_fiq_code s3c24xx_spi_fiq_tx;
+extern struct spi_fiq_code s3c24xx_spi_fiq_rx;
+
+/**
+ * ack_bit - turn IRQ into IRQ acknowledgement bit
+ * @irq: The interrupt number
+ *
+ * Returns the bit to write to the interrupt acknowledge register.
+ */
+static inline u32 ack_bit(unsigned int irq)
+{
+       return 1 << (irq - IRQ_EINT0);
+}
+
+/**
+ * s3c24xx_spi_tryfiq - attempt to claim and setup FIQ for transfer
+ * @hw: The hardware state.
+ *
+ * Claim the FIQ handler (only one can be active at any one time) and
+ * then setup the correct transfer code for this transfer.
+ *
+ * This call updates all the necessary state information if sucessful,
+ * so the caller does not need to do anything more than start the transfer
+ * as normal, since the IRQ will have been re-routed to the FIQ handler.
+*/
+void s3c24xx_spi_tryfiq(struct s3c24xx_spi *hw)
+{
+       struct pt_regs regs;
+       enum spi_fiq_mode mode;
+       struct spi_fiq_code *code;
+       int ret;
+
+       if (!hw->fiq_claimed) {
+               /* try and claim fiq if we haven't got it, and if not
+                * then return and simply use another transfer method */
+
+               ret = claim_fiq(&hw->fiq_handler);
+               if (ret)
+                       return;
+       }
+
+       if (hw->tx && !hw->rx)
+               mode = FIQ_MODE_TX;
+       else if (hw->rx && !hw->tx)
+               mode = FIQ_MODE_RX;
+       else
+               mode = FIQ_MODE_TXRX;
+
+       regs.uregs[fiq_rspi] = (long)hw->regs;
+       regs.uregs[fiq_rrx]  = (long)hw->rx;
+       regs.uregs[fiq_rtx]  = (long)hw->tx + 1;
+       regs.uregs[fiq_rcount] = hw->len - 1;
+       regs.uregs[fiq_rirq] = (long)S3C24XX_VA_IRQ;
+
+       set_fiq_regs(&regs);
+
+       if (hw->fiq_mode != mode) {
+               u32 *ack_ptr;
+
+               hw->fiq_mode = mode;
+
+               switch (mode) {
+               case FIQ_MODE_TX:
+                       code = &s3c24xx_spi_fiq_tx;
+                       break;
+               case FIQ_MODE_RX:
+                       code = &s3c24xx_spi_fiq_rx;
+                       break;
+               case FIQ_MODE_TXRX:
+                       code = &s3c24xx_spi_fiq_txrx;
+                       break;
+               default:
+                       code = NULL;
+               }
+
+               BUG_ON(!code);
+
+               ack_ptr = (u32 *)&code->data[code->ack_offset];
+               *ack_ptr = ack_bit(hw->irq);
+
+               set_fiq_handler(&code->data, code->length);
+       }
+
+       s3c24xx_set_fiq(hw->irq, true);
+
+       hw->fiq_mode = mode;
+       hw->fiq_inuse = 1;
+}
+
+/**
+ * s3c24xx_spi_fiqop - FIQ core code callback
+ * @pw: Data registered with the handler
+ * @release: Whether this is a release or a return.
+ *
+ * Called by the FIQ code when another module wants to use the FIQ, so
+ * return whether we are currently using this or not and then update our
+ * internal state.
+ */
+static int s3c24xx_spi_fiqop(void *pw, int release)
+{
+       struct s3c24xx_spi *hw = pw;
+       int ret = 0;
+
+       if (release) {
+               if (hw->fiq_inuse)
+                       ret = -EBUSY;
+
+               /* note, we do not need to unroute the FIQ, as the FIQ
+                * vector code de-routes it to signal the end of transfer */
+
+               hw->fiq_mode = FIQ_MODE_NONE;
+               hw->fiq_claimed = 0;
+       } else {
+               hw->fiq_claimed = 1;
+       }
+
+       return ret;
+}
+
+/**
+ * s3c24xx_spi_initfiq - setup the information for the FIQ core
+ * @hw: The hardware state.
+ *
+ * Setup the fiq_handler block to pass to the FIQ core.
+ */
+static inline void s3c24xx_spi_initfiq(struct s3c24xx_spi *hw)
+{
+       hw->fiq_handler.dev_id = hw;
+       hw->fiq_handler.name = dev_name(hw->dev);
+       hw->fiq_handler.fiq_op = s3c24xx_spi_fiqop;
+}
+
+/**
+ * s3c24xx_spi_usefiq - return if we should be using FIQ.
+ * @hw: The hardware state.
+ *
+ * Return true if the platform data specifies whether this channel is
+ * allowed to use the FIQ.
+ */
+static inline bool s3c24xx_spi_usefiq(struct s3c24xx_spi *hw)
+{
+       return hw->pdata->use_fiq;
+}
+
+/**
+ * s3c24xx_spi_usingfiq - return if channel is using FIQ
+ * @spi: The hardware state.
+ *
+ * Return whether the channel is currently using the FIQ (separate from
+ * whether the FIQ is claimed).
+ */
+static inline bool s3c24xx_spi_usingfiq(struct s3c24xx_spi *spi)
+{
+       return spi->fiq_inuse;
+}
+#else
+
+static inline void s3c24xx_spi_initfiq(struct s3c24xx_spi *s) { }
+static inline void s3c24xx_spi_tryfiq(struct s3c24xx_spi *s) { }
+static inline bool s3c24xx_spi_usefiq(struct s3c24xx_spi *s) { return false; }
+static inline bool s3c24xx_spi_usingfiq(struct s3c24xx_spi *s) { return false; }
+
+#endif /* CONFIG_SPI_S3C24XX_FIQ */
+
 static int s3c24xx_spi_txrx(struct spi_device *spi, struct spi_transfer *t)
 {
        struct s3c24xx_spi *hw = to_hw(spi);
 
-       dev_dbg(&spi->dev, "txrx: tx %p, rx %p, len %d\n",
-               t->tx_buf, t->rx_buf, t->len);
-
        hw->tx = t->tx_buf;
        hw->rx = t->rx_buf;
        hw->len = t->len;
@@ -228,11 +429,14 @@ static int s3c24xx_spi_txrx(struct spi_device *spi, struct spi_transfer *t)
 
        init_completion(&hw->done);
 
+       hw->fiq_inuse = 0;
+       if (s3c24xx_spi_usefiq(hw) && t->len >= 3)
+               s3c24xx_spi_tryfiq(hw);
+
        /* send the first byte */
        writeb(hw_txbyte(hw, 0), hw->regs + S3C2410_SPTDAT);
 
        wait_for_completion(&hw->done);
-
        return hw->count;
 }
 
@@ -254,17 +458,27 @@ static irqreturn_t s3c24xx_spi_irq(int irq, void *dev)
                goto irq_done;
        }
 
-       hw->count++;
+       if (!s3c24xx_spi_usingfiq(hw)) {
+               hw->count++;
 
-       if (hw->rx)
-               hw->rx[count] = readb(hw->regs + S3C2410_SPRDAT);
+               if (hw->rx)
+                       hw->rx[count] = readb(hw->regs + S3C2410_SPRDAT);
 
-       count++;
+               count++;
+
+               if (count < hw->len)
+                       writeb(hw_txbyte(hw, count), hw->regs + S3C2410_SPTDAT);
+               else
+                       complete(&hw->done);
+       } else {
+               hw->count = hw->len;
+               hw->fiq_inuse = 0;
+
+               if (hw->rx)
+                       hw->rx[hw->len-1] = readb(hw->regs + S3C2410_SPRDAT);
 
-       if (count < hw->len)
-               writeb(hw_txbyte(hw, count), hw->regs + S3C2410_SPTDAT);
-       else
                complete(&hw->done);
+       }
 
  irq_done:
        return IRQ_HANDLED;
@@ -322,6 +536,10 @@ static int __init s3c24xx_spi_probe(struct platform_device *pdev)
        platform_set_drvdata(pdev, hw);
        init_completion(&hw->done);
 
+       /* initialise fiq handler */
+
+       s3c24xx_spi_initfiq(hw);
+
        /* setup the master state. */
 
        /* the spi->mode bits understood by this driver: */
diff --git a/drivers/spi/spi_s3c24xx_fiq.S b/drivers/spi/spi_s3c24xx_fiq.S
new file mode 100644 (file)
index 0000000..3793cae
--- /dev/null
@@ -0,0 +1,116 @@
+/* linux/drivers/spi/spi_s3c24xx_fiq.S
+ *
+ * Copyright 2009 Simtec Electronics
+ *     Ben Dooks <ben@simtec.co.uk>
+ *
+ * S3C24XX SPI - FIQ pseudo-DMA transfer code
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+*/
+
+#include <linux/linkage.h>
+#include <asm/assembler.h>
+
+#include <mach/map.h>
+#include <mach/regs-irq.h>
+#include <plat/regs-spi.h>
+
+#include "spi_s3c24xx_fiq.h"
+
+       .text
+
+       @ entry to these routines is as follows, with the register names
+       @ defined in fiq.h so that they can be shared with the C files which
+       @ setup the calling registers.
+       @
+       @ fiq_rirq      The base of the IRQ registers to find S3C2410_SRCPND
+       @ fiq_rtmp      Temporary register to hold tx/rx data
+       @ fiq_rspi      The base of the SPI register block
+       @ fiq_rtx       The tx buffer pointer
+       @ fiq_rrx       The rx buffer pointer
+       @ fiq_rcount    The number of bytes to move
+
+       @ each entry starts with a word entry of how long it is
+       @ and an offset to the irq acknowledgment word
+
+ENTRY(s3c24xx_spi_fiq_rx)
+s3c24xx_spi_fix_rx:
+       .word   fiq_rx_end - fiq_rx_start
+       .word   fiq_rx_irq_ack - fiq_rx_start
+fiq_rx_start:
+       ldr     fiq_rtmp, fiq_rx_irq_ack
+       str     fiq_rtmp, [ fiq_rirq, # S3C2410_SRCPND - S3C24XX_VA_IRQ ]
+
+       ldrb    fiq_rtmp, [ fiq_rspi, #  S3C2410_SPRDAT ]
+       strb    fiq_rtmp, [ fiq_rrx ], #1
+
+       mov     fiq_rtmp, #0xff
+       strb    fiq_rtmp, [ fiq_rspi, # S3C2410_SPTDAT ]
+
+       subs    fiq_rcount, fiq_rcount, #1
+       subnes  pc, lr, #4              @@ return, still have work to do
+
+       @@ set IRQ controller so that next op will trigger IRQ
+       mov     fiq_rtmp, #0
+       str     fiq_rtmp, [ fiq_rirq, # S3C2410_INTMOD  - S3C24XX_VA_IRQ ]
+       subs    pc, lr, #4
+
+fiq_rx_irq_ack:
+       .word   0
+fiq_rx_end:
+
+ENTRY(s3c24xx_spi_fiq_txrx)
+s3c24xx_spi_fiq_txrx:
+       .word   fiq_txrx_end - fiq_txrx_start
+       .word   fiq_txrx_irq_ack - fiq_txrx_start
+fiq_txrx_start:
+
+       ldrb    fiq_rtmp, [ fiq_rspi, #  S3C2410_SPRDAT ]
+       strb    fiq_rtmp, [ fiq_rrx ], #1
+
+       ldr     fiq_rtmp, fiq_txrx_irq_ack
+       str     fiq_rtmp, [ fiq_rirq, # S3C2410_SRCPND - S3C24XX_VA_IRQ ]
+
+       ldrb    fiq_rtmp, [ fiq_rtx ], #1
+       strb    fiq_rtmp, [ fiq_rspi, # S3C2410_SPTDAT ]
+
+       subs    fiq_rcount, fiq_rcount, #1
+       subnes  pc, lr, #4              @@ return, still have work to do
+
+       mov     fiq_rtmp, #0
+       str     fiq_rtmp, [ fiq_rirq, # S3C2410_INTMOD  - S3C24XX_VA_IRQ ]
+       subs    pc, lr, #4
+
+fiq_txrx_irq_ack:
+       .word   0
+
+fiq_txrx_end:
+
+ENTRY(s3c24xx_spi_fiq_tx)
+s3c24xx_spi_fix_tx:
+       .word   fiq_tx_end - fiq_tx_start
+       .word   fiq_tx_irq_ack - fiq_tx_start
+fiq_tx_start:
+       ldrb    fiq_rtmp, [ fiq_rspi, #  S3C2410_SPRDAT ]
+
+       ldr     fiq_rtmp, fiq_tx_irq_ack
+       str     fiq_rtmp, [ fiq_rirq, # S3C2410_SRCPND - S3C24XX_VA_IRQ ]
+
+       ldrb    fiq_rtmp, [ fiq_rtx ], #1
+       strb    fiq_rtmp, [ fiq_rspi, # S3C2410_SPTDAT ]
+
+       subs    fiq_rcount, fiq_rcount, #1
+       subnes  pc, lr, #4              @@ return, still have work to do
+
+       mov     fiq_rtmp, #0
+       str     fiq_rtmp, [ fiq_rirq, # S3C2410_INTMOD  - S3C24XX_VA_IRQ ]
+       subs    pc, lr, #4
+
+fiq_tx_irq_ack:
+       .word   0
+
+fiq_tx_end:
+
+       .end
diff --git a/drivers/spi/spi_s3c24xx_fiq.h b/drivers/spi/spi_s3c24xx_fiq.h
new file mode 100644 (file)
index 0000000..a5950bb
--- /dev/null
@@ -0,0 +1,26 @@
+/* linux/drivers/spi/spi_s3c24xx_fiq.h
+ *
+ * Copyright 2009 Simtec Electronics
+ *     Ben Dooks <ben@simtec.co.uk>
+ *
+ * S3C24XX SPI - FIQ pseudo-DMA transfer support
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+*/
+
+/* We have R8 through R13 to play with */
+
+#ifdef __ASSEMBLY__
+#define __REG_NR(x)     r##x
+#else
+#define __REG_NR(x)     (x)
+#endif
+
+#define fiq_rspi       __REG_NR(8)
+#define fiq_rtmp       __REG_NR(9)
+#define fiq_rrx                __REG_NR(10)
+#define fiq_rtx                __REG_NR(11)
+#define fiq_rcount     __REG_NR(12)
+#define fiq_rirq       __REG_NR(13)
diff --git a/drivers/spi/spi_s3c64xx.c b/drivers/spi/spi_s3c64xx.c
new file mode 100644 (file)
index 0000000..88a456d
--- /dev/null
@@ -0,0 +1,1196 @@
+/* linux/drivers/spi/spi_s3c64xx.c
+ *
+ * Copyright (C) 2009 Samsung Electronics Ltd.
+ *     Jaswinder Singh <jassi.brar@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/workqueue.h>
+#include <linux/delay.h>
+#include <linux/clk.h>
+#include <linux/dma-mapping.h>
+#include <linux/platform_device.h>
+#include <linux/spi/spi.h>
+
+#include <mach/dma.h>
+#include <plat/spi.h>
+
+/* Registers and bit-fields */
+
+#define S3C64XX_SPI_CH_CFG             0x00
+#define S3C64XX_SPI_CLK_CFG            0x04
+#define S3C64XX_SPI_MODE_CFG   0x08
+#define S3C64XX_SPI_SLAVE_SEL  0x0C
+#define S3C64XX_SPI_INT_EN             0x10
+#define S3C64XX_SPI_STATUS             0x14
+#define S3C64XX_SPI_TX_DATA            0x18
+#define S3C64XX_SPI_RX_DATA            0x1C
+#define S3C64XX_SPI_PACKET_CNT 0x20
+#define S3C64XX_SPI_PENDING_CLR        0x24
+#define S3C64XX_SPI_SWAP_CFG   0x28
+#define S3C64XX_SPI_FB_CLK             0x2C
+
+#define S3C64XX_SPI_CH_HS_EN           (1<<6)  /* High Speed Enable */
+#define S3C64XX_SPI_CH_SW_RST          (1<<5)
+#define S3C64XX_SPI_CH_SLAVE           (1<<4)
+#define S3C64XX_SPI_CPOL_L             (1<<3)
+#define S3C64XX_SPI_CPHA_B             (1<<2)
+#define S3C64XX_SPI_CH_RXCH_ON         (1<<1)
+#define S3C64XX_SPI_CH_TXCH_ON         (1<<0)
+
+#define S3C64XX_SPI_CLKSEL_SRCMSK      (3<<9)
+#define S3C64XX_SPI_CLKSEL_SRCSHFT     9
+#define S3C64XX_SPI_ENCLK_ENABLE       (1<<8)
+#define S3C64XX_SPI_PSR_MASK           0xff
+
+#define S3C64XX_SPI_MODE_CH_TSZ_BYTE           (0<<29)
+#define S3C64XX_SPI_MODE_CH_TSZ_HALFWORD       (1<<29)
+#define S3C64XX_SPI_MODE_CH_TSZ_WORD           (2<<29)
+#define S3C64XX_SPI_MODE_CH_TSZ_MASK           (3<<29)
+#define S3C64XX_SPI_MODE_BUS_TSZ_BYTE          (0<<17)
+#define S3C64XX_SPI_MODE_BUS_TSZ_HALFWORD      (1<<17)
+#define S3C64XX_SPI_MODE_BUS_TSZ_WORD          (2<<17)
+#define S3C64XX_SPI_MODE_BUS_TSZ_MASK          (3<<17)
+#define S3C64XX_SPI_MODE_RXDMA_ON              (1<<2)
+#define S3C64XX_SPI_MODE_TXDMA_ON              (1<<1)
+#define S3C64XX_SPI_MODE_4BURST                        (1<<0)
+
+#define S3C64XX_SPI_SLAVE_AUTO                 (1<<1)
+#define S3C64XX_SPI_SLAVE_SIG_INACT            (1<<0)
+
+#define S3C64XX_SPI_ACT(c) writel(0, (c)->regs + S3C64XX_SPI_SLAVE_SEL)
+
+#define S3C64XX_SPI_DEACT(c) writel(S3C64XX_SPI_SLAVE_SIG_INACT, \
+                                       (c)->regs + S3C64XX_SPI_SLAVE_SEL)
+
+#define S3C64XX_SPI_INT_TRAILING_EN            (1<<6)
+#define S3C64XX_SPI_INT_RX_OVERRUN_EN          (1<<5)
+#define S3C64XX_SPI_INT_RX_UNDERRUN_EN         (1<<4)
+#define S3C64XX_SPI_INT_TX_OVERRUN_EN          (1<<3)
+#define S3C64XX_SPI_INT_TX_UNDERRUN_EN         (1<<2)
+#define S3C64XX_SPI_INT_RX_FIFORDY_EN          (1<<1)
+#define S3C64XX_SPI_INT_TX_FIFORDY_EN          (1<<0)
+
+#define S3C64XX_SPI_ST_RX_OVERRUN_ERR          (1<<5)
+#define S3C64XX_SPI_ST_RX_UNDERRUN_ERR (1<<4)
+#define S3C64XX_SPI_ST_TX_OVERRUN_ERR          (1<<3)
+#define S3C64XX_SPI_ST_TX_UNDERRUN_ERR (1<<2)
+#define S3C64XX_SPI_ST_RX_FIFORDY              (1<<1)
+#define S3C64XX_SPI_ST_TX_FIFORDY              (1<<0)
+
+#define S3C64XX_SPI_PACKET_CNT_EN              (1<<16)
+
+#define S3C64XX_SPI_PND_TX_UNDERRUN_CLR                (1<<4)
+#define S3C64XX_SPI_PND_TX_OVERRUN_CLR         (1<<3)
+#define S3C64XX_SPI_PND_RX_UNDERRUN_CLR                (1<<2)
+#define S3C64XX_SPI_PND_RX_OVERRUN_CLR         (1<<1)
+#define S3C64XX_SPI_PND_TRAILING_CLR           (1<<0)
+
+#define S3C64XX_SPI_SWAP_RX_HALF_WORD          (1<<7)
+#define S3C64XX_SPI_SWAP_RX_BYTE               (1<<6)
+#define S3C64XX_SPI_SWAP_RX_BIT                        (1<<5)
+#define S3C64XX_SPI_SWAP_RX_EN                 (1<<4)
+#define S3C64XX_SPI_SWAP_TX_HALF_WORD          (1<<3)
+#define S3C64XX_SPI_SWAP_TX_BYTE               (1<<2)
+#define S3C64XX_SPI_SWAP_TX_BIT                        (1<<1)
+#define S3C64XX_SPI_SWAP_TX_EN                 (1<<0)
+
+#define S3C64XX_SPI_FBCLK_MSK          (3<<0)
+
+#define S3C64XX_SPI_ST_TRLCNTZ(v, i) ((((v) >> (i)->rx_lvl_offset) & \
+                                       (((i)->fifo_lvl_mask + 1))) \
+                                       ? 1 : 0)
+
+#define S3C64XX_SPI_ST_TX_DONE(v, i) ((((v) >> (i)->rx_lvl_offset) & \
+                                       (((i)->fifo_lvl_mask + 1) << 1)) \
+                                       ? 1 : 0)
+#define TX_FIFO_LVL(v, i) (((v) >> 6) & (i)->fifo_lvl_mask)
+#define RX_FIFO_LVL(v, i) (((v) >> (i)->rx_lvl_offset) & (i)->fifo_lvl_mask)
+
+#define S3C64XX_SPI_MAX_TRAILCNT       0x3ff
+#define S3C64XX_SPI_TRAILCNT_OFF       19
+
+#define S3C64XX_SPI_TRAILCNT           S3C64XX_SPI_MAX_TRAILCNT
+
+#define msecs_to_loops(t) (loops_per_jiffy / 1000 * HZ * t)
+
+#define SUSPND    (1<<0)
+#define SPIBUSY   (1<<1)
+#define RXBUSY    (1<<2)
+#define TXBUSY    (1<<3)
+
+/**
+ * struct s3c64xx_spi_driver_data - Runtime info holder for SPI driver.
+ * @clk: Pointer to the spi clock.
+ * @master: Pointer to the SPI Protocol master.
+ * @workqueue: Work queue for the SPI xfer requests.
+ * @cntrlr_info: Platform specific data for the controller this driver manages.
+ * @tgl_spi: Pointer to the last CS left untoggled by the cs_change hint.
+ * @work: Work
+ * @queue: To log SPI xfer requests.
+ * @lock: Controller specific lock.
+ * @state: Set of FLAGS to indicate status.
+ * @rx_dmach: Controller's DMA channel for Rx.
+ * @tx_dmach: Controller's DMA channel for Tx.
+ * @sfr_start: BUS address of SPI controller regs.
+ * @regs: Pointer to ioremap'ed controller registers.
+ * @xfer_completion: To indicate completion of xfer task.
+ * @cur_mode: Stores the active configuration of the controller.
+ * @cur_bpw: Stores the active bits per word settings.
+ * @cur_speed: Stores the active xfer clock speed.
+ */
+struct s3c64xx_spi_driver_data {
+       void __iomem                    *regs;
+       struct clk                      *clk;
+       struct platform_device          *pdev;
+       struct spi_master               *master;
+       struct workqueue_struct         *workqueue;
+       struct s3c64xx_spi_cntrlr_info  *cntrlr_info;
+       struct spi_device               *tgl_spi;
+       struct work_struct              work;
+       struct list_head                queue;
+       spinlock_t                      lock;
+       enum dma_ch                     rx_dmach;
+       enum dma_ch                     tx_dmach;
+       unsigned long                   sfr_start;
+       struct completion               xfer_completion;
+       unsigned                        state;
+       unsigned                        cur_mode, cur_bpw;
+       unsigned                        cur_speed;
+};
+
+static struct s3c2410_dma_client s3c64xx_spi_dma_client = {
+       .name = "samsung-spi-dma",
+};
+
+static void flush_fifo(struct s3c64xx_spi_driver_data *sdd)
+{
+       struct s3c64xx_spi_cntrlr_info *sci = sdd->cntrlr_info;
+       void __iomem *regs = sdd->regs;
+       unsigned long loops;
+       u32 val;
+
+       writel(0, regs + S3C64XX_SPI_PACKET_CNT);
+
+       val = readl(regs + S3C64XX_SPI_CH_CFG);
+       val |= S3C64XX_SPI_CH_SW_RST;
+       val &= ~S3C64XX_SPI_CH_HS_EN;
+       writel(val, regs + S3C64XX_SPI_CH_CFG);
+
+       /* Flush TxFIFO*/
+       loops = msecs_to_loops(1);
+       do {
+               val = readl(regs + S3C64XX_SPI_STATUS);
+       } while (TX_FIFO_LVL(val, sci) && loops--);
+
+       /* Flush RxFIFO*/
+       loops = msecs_to_loops(1);
+       do {
+               val = readl(regs + S3C64XX_SPI_STATUS);
+               if (RX_FIFO_LVL(val, sci))
+                       readl(regs + S3C64XX_SPI_RX_DATA);
+               else
+                       break;
+       } while (loops--);
+
+       val = readl(regs + S3C64XX_SPI_CH_CFG);
+       val &= ~S3C64XX_SPI_CH_SW_RST;
+       writel(val, regs + S3C64XX_SPI_CH_CFG);
+
+       val = readl(regs + S3C64XX_SPI_MODE_CFG);
+       val &= ~(S3C64XX_SPI_MODE_TXDMA_ON | S3C64XX_SPI_MODE_RXDMA_ON);
+       writel(val, regs + S3C64XX_SPI_MODE_CFG);
+
+       val = readl(regs + S3C64XX_SPI_CH_CFG);
+       val &= ~(S3C64XX_SPI_CH_RXCH_ON | S3C64XX_SPI_CH_TXCH_ON);
+       writel(val, regs + S3C64XX_SPI_CH_CFG);
+}
+
+static void enable_datapath(struct s3c64xx_spi_driver_data *sdd,
+                               struct spi_device *spi,
+                               struct spi_transfer *xfer, int dma_mode)
+{
+       struct s3c64xx_spi_cntrlr_info *sci = sdd->cntrlr_info;
+       void __iomem *regs = sdd->regs;
+       u32 modecfg, chcfg;
+
+       modecfg = readl(regs + S3C64XX_SPI_MODE_CFG);
+       modecfg &= ~(S3C64XX_SPI_MODE_TXDMA_ON | S3C64XX_SPI_MODE_RXDMA_ON);
+
+       chcfg = readl(regs + S3C64XX_SPI_CH_CFG);
+       chcfg &= ~S3C64XX_SPI_CH_TXCH_ON;
+
+       if (dma_mode) {
+               chcfg &= ~S3C64XX_SPI_CH_RXCH_ON;
+       } else {
+               /* Always shift in data in FIFO, even if xfer is Tx only,
+                * this helps setting PCKT_CNT value for generating clocks
+                * as exactly needed.
+                */
+               chcfg |= S3C64XX_SPI_CH_RXCH_ON;
+               writel(((xfer->len * 8 / sdd->cur_bpw) & 0xffff)
+                                       | S3C64XX_SPI_PACKET_CNT_EN,
+                                       regs + S3C64XX_SPI_PACKET_CNT);
+       }
+
+       if (xfer->tx_buf != NULL) {
+               sdd->state |= TXBUSY;
+               chcfg |= S3C64XX_SPI_CH_TXCH_ON;
+               if (dma_mode) {
+                       modecfg |= S3C64XX_SPI_MODE_TXDMA_ON;
+                       s3c2410_dma_config(sdd->tx_dmach, 1);
+                       s3c2410_dma_enqueue(sdd->tx_dmach, (void *)sdd,
+                                               xfer->tx_dma, xfer->len);
+                       s3c2410_dma_ctrl(sdd->tx_dmach, S3C2410_DMAOP_START);
+               } else {
+                       unsigned char *buf = (unsigned char *) xfer->tx_buf;
+                       int i = 0;
+                       while (i < xfer->len)
+                               writeb(buf[i++], regs + S3C64XX_SPI_TX_DATA);
+               }
+       }
+
+       if (xfer->rx_buf != NULL) {
+               sdd->state |= RXBUSY;
+
+               if (sci->high_speed && sdd->cur_speed >= 30000000UL
+                                       && !(sdd->cur_mode & SPI_CPHA))
+                       chcfg |= S3C64XX_SPI_CH_HS_EN;
+
+               if (dma_mode) {
+                       modecfg |= S3C64XX_SPI_MODE_RXDMA_ON;
+                       chcfg |= S3C64XX_SPI_CH_RXCH_ON;
+                       writel(((xfer->len * 8 / sdd->cur_bpw) & 0xffff)
+                                       | S3C64XX_SPI_PACKET_CNT_EN,
+                                       regs + S3C64XX_SPI_PACKET_CNT);
+                       s3c2410_dma_config(sdd->rx_dmach, 1);
+                       s3c2410_dma_enqueue(sdd->rx_dmach, (void *)sdd,
+                                               xfer->rx_dma, xfer->len);
+                       s3c2410_dma_ctrl(sdd->rx_dmach, S3C2410_DMAOP_START);
+               }
+       }
+
+       writel(modecfg, regs + S3C64XX_SPI_MODE_CFG);
+       writel(chcfg, regs + S3C64XX_SPI_CH_CFG);
+}
+
+static inline void enable_cs(struct s3c64xx_spi_driver_data *sdd,
+                                               struct spi_device *spi)
+{
+       struct s3c64xx_spi_csinfo *cs;
+
+       if (sdd->tgl_spi != NULL) { /* If last device toggled after mssg */
+               if (sdd->tgl_spi != spi) { /* if last mssg on diff device */
+                       /* Deselect the last toggled device */
+                       cs = sdd->tgl_spi->controller_data;
+                       cs->set_level(spi->mode & SPI_CS_HIGH ? 0 : 1);
+               }
+               sdd->tgl_spi = NULL;
+       }
+
+       cs = spi->controller_data;
+       cs->set_level(spi->mode & SPI_CS_HIGH ? 1 : 0);
+}
+
+static int wait_for_xfer(struct s3c64xx_spi_driver_data *sdd,
+                               struct spi_transfer *xfer, int dma_mode)
+{
+       struct s3c64xx_spi_cntrlr_info *sci = sdd->cntrlr_info;
+       void __iomem *regs = sdd->regs;
+       unsigned long val;
+       int ms;
+
+       /* millisecs to xfer 'len' bytes @ 'cur_speed' */
+       ms = xfer->len * 8 * 1000 / sdd->cur_speed;
+       ms += 5; /* some tolerance */
+
+       if (dma_mode) {
+               val = msecs_to_jiffies(ms) + 10;
+               val = wait_for_completion_timeout(&sdd->xfer_completion, val);
+       } else {
+               val = msecs_to_loops(ms);
+               do {
+                       val = readl(regs + S3C64XX_SPI_STATUS);
+               } while (RX_FIFO_LVL(val, sci) < xfer->len && --val);
+       }
+
+       if (!val)
+               return -EIO;
+
+       if (dma_mode) {
+               u32 status;
+
+               /*
+                * DmaTx returns after simply writing data in the FIFO,
+                * w/o waiting for real transmission on the bus to finish.
+                * DmaRx returns only after Dma read data from FIFO which
+                * needs bus transmission to finish, so we don't worry if
+                * Xfer involved Rx(with or without Tx).
+                */
+               if (xfer->rx_buf == NULL) {
+                       val = msecs_to_loops(10);
+                       status = readl(regs + S3C64XX_SPI_STATUS);
+                       while ((TX_FIFO_LVL(status, sci)
+                               || !S3C64XX_SPI_ST_TX_DONE(status, sci))
+                                       && --val) {
+                               cpu_relax();
+                               status = readl(regs + S3C64XX_SPI_STATUS);
+                       }
+
+                       if (!val)
+                               return -EIO;
+               }
+       } else {
+               unsigned char *buf;
+               int i;
+
+               /* If it was only Tx */
+               if (xfer->rx_buf == NULL) {
+                       sdd->state &= ~TXBUSY;
+                       return 0;
+               }
+
+               i = 0;
+               buf = xfer->rx_buf;
+               while (i < xfer->len)
+                       buf[i++] = readb(regs + S3C64XX_SPI_RX_DATA);
+
+               sdd->state &= ~RXBUSY;
+       }
+
+       return 0;
+}
+
+static inline void disable_cs(struct s3c64xx_spi_driver_data *sdd,
+                                               struct spi_device *spi)
+{
+       struct s3c64xx_spi_csinfo *cs = spi->controller_data;
+
+       if (sdd->tgl_spi == spi)
+               sdd->tgl_spi = NULL;
+
+       cs->set_level(spi->mode & SPI_CS_HIGH ? 0 : 1);
+}
+
+static void s3c64xx_spi_config(struct s3c64xx_spi_driver_data *sdd)
+{
+       struct s3c64xx_spi_cntrlr_info *sci = sdd->cntrlr_info;
+       void __iomem *regs = sdd->regs;
+       u32 val;
+
+       /* Disable Clock */
+       val = readl(regs + S3C64XX_SPI_CLK_CFG);
+       val &= ~S3C64XX_SPI_ENCLK_ENABLE;
+       writel(val, regs + S3C64XX_SPI_CLK_CFG);
+
+       /* Set Polarity and Phase */
+       val = readl(regs + S3C64XX_SPI_CH_CFG);
+       val &= ~(S3C64XX_SPI_CH_SLAVE |
+                       S3C64XX_SPI_CPOL_L |
+                       S3C64XX_SPI_CPHA_B);
+
+       if (sdd->cur_mode & SPI_CPOL)
+               val |= S3C64XX_SPI_CPOL_L;
+
+       if (sdd->cur_mode & SPI_CPHA)
+               val |= S3C64XX_SPI_CPHA_B;
+
+       writel(val, regs + S3C64XX_SPI_CH_CFG);
+
+       /* Set Channel & DMA Mode */
+       val = readl(regs + S3C64XX_SPI_MODE_CFG);
+       val &= ~(S3C64XX_SPI_MODE_BUS_TSZ_MASK
+                       | S3C64XX_SPI_MODE_CH_TSZ_MASK);
+
+       switch (sdd->cur_bpw) {
+       case 32:
+               val |= S3C64XX_SPI_MODE_BUS_TSZ_WORD;
+               break;
+       case 16:
+               val |= S3C64XX_SPI_MODE_BUS_TSZ_HALFWORD;
+               break;
+       default:
+               val |= S3C64XX_SPI_MODE_BUS_TSZ_BYTE;
+               break;
+       }
+       val |= S3C64XX_SPI_MODE_CH_TSZ_BYTE; /* Always 8bits wide */
+
+       writel(val, regs + S3C64XX_SPI_MODE_CFG);
+
+       /* Configure Clock */
+       val = readl(regs + S3C64XX_SPI_CLK_CFG);
+       val &= ~S3C64XX_SPI_PSR_MASK;
+       val |= ((clk_get_rate(sci->src_clk) / sdd->cur_speed / 2 - 1)
+                       & S3C64XX_SPI_PSR_MASK);
+       writel(val, regs + S3C64XX_SPI_CLK_CFG);
+
+       /* Enable Clock */
+       val = readl(regs + S3C64XX_SPI_CLK_CFG);
+       val |= S3C64XX_SPI_ENCLK_ENABLE;
+       writel(val, regs + S3C64XX_SPI_CLK_CFG);
+}
+
+void s3c64xx_spi_dma_rxcb(struct s3c2410_dma_chan *chan, void *buf_id,
+                               int size, enum s3c2410_dma_buffresult res)
+{
+       struct s3c64xx_spi_driver_data *sdd = buf_id;
+       unsigned long flags;
+
+       spin_lock_irqsave(&sdd->lock, flags);
+
+       if (res == S3C2410_RES_OK)
+               sdd->state &= ~RXBUSY;
+       else
+               dev_err(&sdd->pdev->dev, "DmaAbrtRx-%d\n", size);
+
+       /* If the other done */
+       if (!(sdd->state & TXBUSY))
+               complete(&sdd->xfer_completion);
+
+       spin_unlock_irqrestore(&sdd->lock, flags);
+}
+
+void s3c64xx_spi_dma_txcb(struct s3c2410_dma_chan *chan, void *buf_id,
+                               int size, enum s3c2410_dma_buffresult res)
+{
+       struct s3c64xx_spi_driver_data *sdd = buf_id;
+       unsigned long flags;
+
+       spin_lock_irqsave(&sdd->lock, flags);
+
+       if (res == S3C2410_RES_OK)
+               sdd->state &= ~TXBUSY;
+       else
+               dev_err(&sdd->pdev->dev, "DmaAbrtTx-%d \n", size);
+
+       /* If the other done */
+       if (!(sdd->state & RXBUSY))
+               complete(&sdd->xfer_completion);
+
+       spin_unlock_irqrestore(&sdd->lock, flags);
+}
+
+#define XFER_DMAADDR_INVALID DMA_BIT_MASK(32)
+
+static int s3c64xx_spi_map_mssg(struct s3c64xx_spi_driver_data *sdd,
+                                               struct spi_message *msg)
+{
+       struct device *dev = &sdd->pdev->dev;
+       struct spi_transfer *xfer;
+
+       if (msg->is_dma_mapped)
+               return 0;
+
+       /* First mark all xfer unmapped */
+       list_for_each_entry(xfer, &msg->transfers, transfer_list) {
+               xfer->rx_dma = XFER_DMAADDR_INVALID;
+               xfer->tx_dma = XFER_DMAADDR_INVALID;
+       }
+
+       /* Map until end or first fail */
+       list_for_each_entry(xfer, &msg->transfers, transfer_list) {
+
+               if (xfer->tx_buf != NULL) {
+                       xfer->tx_dma = dma_map_single(dev, xfer->tx_buf,
+                                               xfer->len, DMA_TO_DEVICE);
+                       if (dma_mapping_error(dev, xfer->tx_dma)) {
+                               dev_err(dev, "dma_map_single Tx failed\n");
+                               xfer->tx_dma = XFER_DMAADDR_INVALID;
+                               return -ENOMEM;
+                       }
+               }
+
+               if (xfer->rx_buf != NULL) {
+                       xfer->rx_dma = dma_map_single(dev, xfer->rx_buf,
+                                               xfer->len, DMA_FROM_DEVICE);
+                       if (dma_mapping_error(dev, xfer->rx_dma)) {
+                               dev_err(dev, "dma_map_single Rx failed\n");
+                               dma_unmap_single(dev, xfer->tx_dma,
+                                               xfer->len, DMA_TO_DEVICE);
+                               xfer->tx_dma = XFER_DMAADDR_INVALID;
+                               xfer->rx_dma = XFER_DMAADDR_INVALID;
+                               return -ENOMEM;
+                       }
+               }
+       }
+
+       return 0;
+}
+
+static void s3c64xx_spi_unmap_mssg(struct s3c64xx_spi_driver_data *sdd,
+                                               struct spi_message *msg)
+{
+       struct device *dev = &sdd->pdev->dev;
+       struct spi_transfer *xfer;
+
+       if (msg->is_dma_mapped)
+               return;
+
+       list_for_each_entry(xfer, &msg->transfers, transfer_list) {
+
+               if (xfer->rx_buf != NULL
+                               && xfer->rx_dma != XFER_DMAADDR_INVALID)
+                       dma_unmap_single(dev, xfer->rx_dma,
+                                               xfer->len, DMA_FROM_DEVICE);
+
+               if (xfer->tx_buf != NULL
+                               && xfer->tx_dma != XFER_DMAADDR_INVALID)
+                       dma_unmap_single(dev, xfer->tx_dma,
+                                               xfer->len, DMA_TO_DEVICE);
+       }
+}
+
+static void handle_msg(struct s3c64xx_spi_driver_data *sdd,
+                                       struct spi_message *msg)
+{
+       struct s3c64xx_spi_cntrlr_info *sci = sdd->cntrlr_info;
+       struct spi_device *spi = msg->spi;
+       struct s3c64xx_spi_csinfo *cs = spi->controller_data;
+       struct spi_transfer *xfer;
+       int status = 0, cs_toggle = 0;
+       u32 speed;
+       u8 bpw;
+
+       /* If Master's(controller) state differs from that needed by Slave */
+       if (sdd->cur_speed != spi->max_speed_hz
+                       || sdd->cur_mode != spi->mode
+                       || sdd->cur_bpw != spi->bits_per_word) {
+               sdd->cur_bpw = spi->bits_per_word;
+               sdd->cur_speed = spi->max_speed_hz;
+               sdd->cur_mode = spi->mode;
+               s3c64xx_spi_config(sdd);
+       }
+
+       /* Map all the transfers if needed */
+       if (s3c64xx_spi_map_mssg(sdd, msg)) {
+               dev_err(&spi->dev,
+                       "Xfer: Unable to map message buffers!\n");
+               status = -ENOMEM;
+               goto out;
+       }
+
+       /* Configure feedback delay */
+       writel(cs->fb_delay & 0x3, sdd->regs + S3C64XX_SPI_FB_CLK);
+
+       list_for_each_entry(xfer, &msg->transfers, transfer_list) {
+
+               unsigned long flags;
+               int use_dma;
+
+               INIT_COMPLETION(sdd->xfer_completion);
+
+               /* Only BPW and Speed may change across transfers */
+               bpw = xfer->bits_per_word ? : spi->bits_per_word;
+               speed = xfer->speed_hz ? : spi->max_speed_hz;
+
+               if (bpw != sdd->cur_bpw || speed != sdd->cur_speed) {
+                       sdd->cur_bpw = bpw;
+                       sdd->cur_speed = speed;
+                       s3c64xx_spi_config(sdd);
+               }
+
+               /* Polling method for xfers not bigger than FIFO capacity */
+               if (xfer->len <= ((sci->fifo_lvl_mask >> 1) + 1))
+                       use_dma = 0;
+               else
+                       use_dma = 1;
+
+               spin_lock_irqsave(&sdd->lock, flags);
+
+               /* Pending only which is to be done */
+               sdd->state &= ~RXBUSY;
+               sdd->state &= ~TXBUSY;
+
+               enable_datapath(sdd, spi, xfer, use_dma);
+
+               /* Slave Select */
+               enable_cs(sdd, spi);
+
+               /* Start the signals */
+               S3C64XX_SPI_ACT(sdd);
+
+               spin_unlock_irqrestore(&sdd->lock, flags);
+
+               status = wait_for_xfer(sdd, xfer, use_dma);
+
+               /* Quiese the signals */
+               S3C64XX_SPI_DEACT(sdd);
+
+               if (status) {
+                       dev_err(&spi->dev, "I/O Error: \
+                               rx-%d tx-%d res:rx-%c tx-%c len-%d\n",
+                               xfer->rx_buf ? 1 : 0, xfer->tx_buf ? 1 : 0,
+                               (sdd->state & RXBUSY) ? 'f' : 'p',
+                               (sdd->state & TXBUSY) ? 'f' : 'p',
+                               xfer->len);
+
+                       if (use_dma) {
+                               if (xfer->tx_buf != NULL
+                                               && (sdd->state & TXBUSY))
+                                       s3c2410_dma_ctrl(sdd->tx_dmach,
+                                                       S3C2410_DMAOP_FLUSH);
+                               if (xfer->rx_buf != NULL
+                                               && (sdd->state & RXBUSY))
+                                       s3c2410_dma_ctrl(sdd->rx_dmach,
+                                                       S3C2410_DMAOP_FLUSH);
+                       }
+
+                       goto out;
+               }
+
+               if (xfer->delay_usecs)
+                       udelay(xfer->delay_usecs);
+
+               if (xfer->cs_change) {
+                       /* Hint that the next mssg is gonna be
+                          for the same device */
+                       if (list_is_last(&xfer->transfer_list,
+                                               &msg->transfers))
+                               cs_toggle = 1;
+                       else
+                               disable_cs(sdd, spi);
+               }
+
+               msg->actual_length += xfer->len;
+
+               flush_fifo(sdd);
+       }
+
+out:
+       if (!cs_toggle || status)
+               disable_cs(sdd, spi);
+       else
+               sdd->tgl_spi = spi;
+
+       s3c64xx_spi_unmap_mssg(sdd, msg);
+
+       msg->status = status;
+
+       if (msg->complete)
+               msg->complete(msg->context);
+}
+
+static int acquire_dma(struct s3c64xx_spi_driver_data *sdd)
+{
+       if (s3c2410_dma_request(sdd->rx_dmach,
+                                       &s3c64xx_spi_dma_client, NULL) < 0) {
+               dev_err(&sdd->pdev->dev, "cannot get RxDMA\n");
+               return 0;
+       }
+       s3c2410_dma_set_buffdone_fn(sdd->rx_dmach, s3c64xx_spi_dma_rxcb);
+       s3c2410_dma_devconfig(sdd->rx_dmach, S3C2410_DMASRC_HW,
+                                       sdd->sfr_start + S3C64XX_SPI_RX_DATA);
+
+       if (s3c2410_dma_request(sdd->tx_dmach,
+                                       &s3c64xx_spi_dma_client, NULL) < 0) {
+               dev_err(&sdd->pdev->dev, "cannot get TxDMA\n");
+               s3c2410_dma_free(sdd->rx_dmach, &s3c64xx_spi_dma_client);
+               return 0;
+       }
+       s3c2410_dma_set_buffdone_fn(sdd->tx_dmach, s3c64xx_spi_dma_txcb);
+       s3c2410_dma_devconfig(sdd->tx_dmach, S3C2410_DMASRC_MEM,
+                                       sdd->sfr_start + S3C64XX_SPI_TX_DATA);
+
+       return 1;
+}
+
+static void s3c64xx_spi_work(struct work_struct *work)
+{
+       struct s3c64xx_spi_driver_data *sdd = container_of(work,
+                                       struct s3c64xx_spi_driver_data, work);
+       unsigned long flags;
+
+       /* Acquire DMA channels */
+       while (!acquire_dma(sdd))
+               msleep(10);
+
+       spin_lock_irqsave(&sdd->lock, flags);
+
+       while (!list_empty(&sdd->queue)
+                               && !(sdd->state & SUSPND)) {
+
+               struct spi_message *msg;
+
+               msg = container_of(sdd->queue.next, struct spi_message, queue);
+
+               list_del_init(&msg->queue);
+
+               /* Set Xfer busy flag */
+               sdd->state |= SPIBUSY;
+
+               spin_unlock_irqrestore(&sdd->lock, flags);
+
+               handle_msg(sdd, msg);
+
+               spin_lock_irqsave(&sdd->lock, flags);
+
+               sdd->state &= ~SPIBUSY;
+       }
+
+       spin_unlock_irqrestore(&sdd->lock, flags);
+
+       /* Free DMA channels */
+       s3c2410_dma_free(sdd->tx_dmach, &s3c64xx_spi_dma_client);
+       s3c2410_dma_free(sdd->rx_dmach, &s3c64xx_spi_dma_client);
+}
+
+static int s3c64xx_spi_transfer(struct spi_device *spi,
+                                               struct spi_message *msg)
+{
+       struct s3c64xx_spi_driver_data *sdd;
+       unsigned long flags;
+
+       sdd = spi_master_get_devdata(spi->master);
+
+       spin_lock_irqsave(&sdd->lock, flags);
+
+       if (sdd->state & SUSPND) {
+               spin_unlock_irqrestore(&sdd->lock, flags);
+               return -ESHUTDOWN;
+       }
+
+       msg->status = -EINPROGRESS;
+       msg->actual_length = 0;
+
+       list_add_tail(&msg->queue, &sdd->queue);
+
+       queue_work(sdd->workqueue, &sdd->work);
+
+       spin_unlock_irqrestore(&sdd->lock, flags);
+
+       return 0;
+}
+
+/*
+ * Here we only check the validity of requested configuration
+ * and save the configuration in a local data-structure.
+ * The controller is actually configured only just before we
+ * get a message to transfer.
+ */
+static int s3c64xx_spi_setup(struct spi_device *spi)
+{
+       struct s3c64xx_spi_csinfo *cs = spi->controller_data;
+       struct s3c64xx_spi_driver_data *sdd;
+       struct s3c64xx_spi_cntrlr_info *sci;
+       struct spi_message *msg;
+       u32 psr, speed;
+       unsigned long flags;
+       int err = 0;
+
+       if (cs == NULL || cs->set_level == NULL) {
+               dev_err(&spi->dev, "No CS for SPI(%d)\n", spi->chip_select);
+               return -ENODEV;
+       }
+
+       sdd = spi_master_get_devdata(spi->master);
+       sci = sdd->cntrlr_info;
+
+       spin_lock_irqsave(&sdd->lock, flags);
+
+       list_for_each_entry(msg, &sdd->queue, queue) {
+               /* Is some mssg is already queued for this device */
+               if (msg->spi == spi) {
+                       dev_err(&spi->dev,
+                               "setup: attempt while mssg in queue!\n");
+                       spin_unlock_irqrestore(&sdd->lock, flags);
+                       return -EBUSY;
+               }
+       }
+
+       if (sdd->state & SUSPND) {
+               spin_unlock_irqrestore(&sdd->lock, flags);
+               dev_err(&spi->dev,
+                       "setup: SPI-%d not active!\n", spi->master->bus_num);
+               return -ESHUTDOWN;
+       }
+
+       spin_unlock_irqrestore(&sdd->lock, flags);
+
+       if (spi->bits_per_word != 8
+                       && spi->bits_per_word != 16
+                       && spi->bits_per_word != 32) {
+               dev_err(&spi->dev, "setup: %dbits/wrd not supported!\n",
+                                                       spi->bits_per_word);
+               err = -EINVAL;
+               goto setup_exit;
+       }
+
+       /* Check if we can provide the requested rate */
+       speed = clk_get_rate(sci->src_clk) / 2 / (0 + 1); /* Max possible */
+
+       if (spi->max_speed_hz > speed)
+               spi->max_speed_hz = speed;
+
+       psr = clk_get_rate(sci->src_clk) / 2 / spi->max_speed_hz - 1;
+       psr &= S3C64XX_SPI_PSR_MASK;
+       if (psr == S3C64XX_SPI_PSR_MASK)
+               psr--;
+
+       speed = clk_get_rate(sci->src_clk) / 2 / (psr + 1);
+       if (spi->max_speed_hz < speed) {
+               if (psr+1 < S3C64XX_SPI_PSR_MASK) {
+                       psr++;
+               } else {
+                       err = -EINVAL;
+                       goto setup_exit;
+               }
+       }
+
+       speed = clk_get_rate(sci->src_clk) / 2 / (psr + 1);
+       if (spi->max_speed_hz >= speed)
+               spi->max_speed_hz = speed;
+       else
+               err = -EINVAL;
+
+setup_exit:
+
+       /* setup() returns with device de-selected */
+       disable_cs(sdd, spi);
+
+       return err;
+}
+
+static void s3c64xx_spi_hwinit(struct s3c64xx_spi_driver_data *sdd, int channel)
+{
+       struct s3c64xx_spi_cntrlr_info *sci = sdd->cntrlr_info;
+       void __iomem *regs = sdd->regs;
+       unsigned int val;
+
+       sdd->cur_speed = 0;
+
+       S3C64XX_SPI_DEACT(sdd);
+
+       /* Disable Interrupts - we use Polling if not DMA mode */
+       writel(0, regs + S3C64XX_SPI_INT_EN);
+
+       writel(sci->src_clk_nr << S3C64XX_SPI_CLKSEL_SRCSHFT,
+                               regs + S3C64XX_SPI_CLK_CFG);
+       writel(0, regs + S3C64XX_SPI_MODE_CFG);
+       writel(0, regs + S3C64XX_SPI_PACKET_CNT);
+
+       /* Clear any irq pending bits */
+       writel(readl(regs + S3C64XX_SPI_PENDING_CLR),
+                               regs + S3C64XX_SPI_PENDING_CLR);
+
+       writel(0, regs + S3C64XX_SPI_SWAP_CFG);
+
+       val = readl(regs + S3C64XX_SPI_MODE_CFG);
+       val &= ~S3C64XX_SPI_MODE_4BURST;
+       val &= ~(S3C64XX_SPI_MAX_TRAILCNT << S3C64XX_SPI_TRAILCNT_OFF);
+       val |= (S3C64XX_SPI_TRAILCNT << S3C64XX_SPI_TRAILCNT_OFF);
+       writel(val, regs + S3C64XX_SPI_MODE_CFG);
+
+       flush_fifo(sdd);
+}
+
+static int __init s3c64xx_spi_probe(struct platform_device *pdev)
+{
+       struct resource *mem_res, *dmatx_res, *dmarx_res;
+       struct s3c64xx_spi_driver_data *sdd;
+       struct s3c64xx_spi_cntrlr_info *sci;
+       struct spi_master *master;
+       int ret;
+
+       if (pdev->id < 0) {
+               dev_err(&pdev->dev,
+                               "Invalid platform device id-%d\n", pdev->id);
+               return -ENODEV;
+       }
+
+       if (pdev->dev.platform_data == NULL) {
+               dev_err(&pdev->dev, "platform_data missing!\n");
+               return -ENODEV;
+       }
+
+       /* Check for availability of necessary resource */
+
+       dmatx_res = platform_get_resource(pdev, IORESOURCE_DMA, 0);
+       if (dmatx_res == NULL) {
+               dev_err(&pdev->dev, "Unable to get SPI-Tx dma resource\n");
+               return -ENXIO;
+       }
+
+       dmarx_res = platform_get_resource(pdev, IORESOURCE_DMA, 1);
+       if (dmarx_res == NULL) {
+               dev_err(&pdev->dev, "Unable to get SPI-Rx dma resource\n");
+               return -ENXIO;
+       }
+
+       mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       if (mem_res == NULL) {
+               dev_err(&pdev->dev, "Unable to get SPI MEM resource\n");
+               return -ENXIO;
+       }
+
+       master = spi_alloc_master(&pdev->dev,
+                               sizeof(struct s3c64xx_spi_driver_data));
+       if (master == NULL) {
+               dev_err(&pdev->dev, "Unable to allocate SPI Master\n");
+               return -ENOMEM;
+       }
+
+       sci = pdev->dev.platform_data;
+
+       platform_set_drvdata(pdev, master);
+
+       sdd = spi_master_get_devdata(master);
+       sdd->master = master;
+       sdd->cntrlr_info = sci;
+       sdd->pdev = pdev;
+       sdd->sfr_start = mem_res->start;
+       sdd->tx_dmach = dmatx_res->start;
+       sdd->rx_dmach = dmarx_res->start;
+
+       sdd->cur_bpw = 8;
+
+       master->bus_num = pdev->id;
+       master->setup = s3c64xx_spi_setup;
+       master->transfer = s3c64xx_spi_transfer;
+       master->num_chipselect = sci->num_cs;
+       master->dma_alignment = 8;
+       /* the spi->mode bits understood by this driver: */
+       master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH;
+
+       if (request_mem_region(mem_res->start,
+                       resource_size(mem_res), pdev->name) == NULL) {
+               dev_err(&pdev->dev, "Req mem region failed\n");
+               ret = -ENXIO;
+               goto err0;
+       }
+
+       sdd->regs = ioremap(mem_res->start, resource_size(mem_res));
+       if (sdd->regs == NULL) {
+               dev_err(&pdev->dev, "Unable to remap IO\n");
+               ret = -ENXIO;
+               goto err1;
+       }
+
+       if (sci->cfg_gpio == NULL || sci->cfg_gpio(pdev)) {
+               dev_err(&pdev->dev, "Unable to config gpio\n");
+               ret = -EBUSY;
+               goto err2;
+       }
+
+       /* Setup clocks */
+       sdd->clk = clk_get(&pdev->dev, "spi");
+       if (IS_ERR(sdd->clk)) {
+               dev_err(&pdev->dev, "Unable to acquire clock 'spi'\n");
+               ret = PTR_ERR(sdd->clk);
+               goto err3;
+       }
+
+       if (clk_enable(sdd->clk)) {
+               dev_err(&pdev->dev, "Couldn't enable clock 'spi'\n");
+               ret = -EBUSY;
+               goto err4;
+       }
+
+       if (sci->src_clk_nr == S3C64XX_SPI_SRCCLK_PCLK)
+               sci->src_clk = sdd->clk;
+       else
+               sci->src_clk = clk_get(&pdev->dev, sci->src_clk_name);
+       if (IS_ERR(sci->src_clk)) {
+               dev_err(&pdev->dev,
+                       "Unable to acquire clock '%s'\n", sci->src_clk_name);
+               ret = PTR_ERR(sci->src_clk);
+               goto err5;
+       }
+
+       if (sci->src_clk != sdd->clk && clk_enable(sci->src_clk)) {
+               dev_err(&pdev->dev, "Couldn't enable clock '%s'\n",
+                                                       sci->src_clk_name);
+               ret = -EBUSY;
+               goto err6;
+       }
+
+       sdd->workqueue = create_singlethread_workqueue(
+                                               dev_name(master->dev.parent));
+       if (sdd->workqueue == NULL) {
+               dev_err(&pdev->dev, "Unable to create workqueue\n");
+               ret = -ENOMEM;
+               goto err7;
+       }
+
+       /* Setup Deufult Mode */
+       s3c64xx_spi_hwinit(sdd, pdev->id);
+
+       spin_lock_init(&sdd->lock);
+       init_completion(&sdd->xfer_completion);
+       INIT_WORK(&sdd->work, s3c64xx_spi_work);
+       INIT_LIST_HEAD(&sdd->queue);
+
+       if (spi_register_master(master)) {
+               dev_err(&pdev->dev, "cannot register SPI master\n");
+               ret = -EBUSY;
+               goto err8;
+       }
+
+       dev_dbg(&pdev->dev, "Samsung SoC SPI Driver loaded for Bus SPI-%d \
+                                       with %d Slaves attached\n",
+                                       pdev->id, master->num_chipselect);
+       dev_dbg(&pdev->dev, "\tIOmem=[0x%x-0x%x]\
+                                       \tDMA=[Rx-%d, Tx-%d]\n",
+                                       mem_res->end, mem_res->start,
+                                       sdd->rx_dmach, sdd->tx_dmach);
+
+       return 0;
+
+err8:
+       destroy_workqueue(sdd->workqueue);
+err7:
+       if (sci->src_clk != sdd->clk)
+               clk_disable(sci->src_clk);
+err6:
+       if (sci->src_clk != sdd->clk)
+               clk_put(sci->src_clk);
+err5:
+       clk_disable(sdd->clk);
+err4:
+       clk_put(sdd->clk);
+err3:
+err2:
+       iounmap((void *) sdd->regs);
+err1:
+       release_mem_region(mem_res->start, resource_size(mem_res));
+err0:
+       platform_set_drvdata(pdev, NULL);
+       spi_master_put(master);
+
+       return ret;
+}
+
+static int s3c64xx_spi_remove(struct platform_device *pdev)
+{
+       struct spi_master *master = spi_master_get(platform_get_drvdata(pdev));
+       struct s3c64xx_spi_driver_data *sdd = spi_master_get_devdata(master);
+       struct s3c64xx_spi_cntrlr_info *sci = sdd->cntrlr_info;
+       struct resource *mem_res;
+       unsigned long flags;
+
+       spin_lock_irqsave(&sdd->lock, flags);
+       sdd->state |= SUSPND;
+       spin_unlock_irqrestore(&sdd->lock, flags);
+
+       while (sdd->state & SPIBUSY)
+               msleep(10);
+
+       spi_unregister_master(master);
+
+       destroy_workqueue(sdd->workqueue);
+
+       if (sci->src_clk != sdd->clk)
+               clk_disable(sci->src_clk);
+
+       if (sci->src_clk != sdd->clk)
+               clk_put(sci->src_clk);
+
+       clk_disable(sdd->clk);
+       clk_put(sdd->clk);
+
+       iounmap((void *) sdd->regs);
+
+       mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       release_mem_region(mem_res->start, resource_size(mem_res));
+
+       platform_set_drvdata(pdev, NULL);
+       spi_master_put(master);
+
+       return 0;
+}
+
+#ifdef CONFIG_PM
+static int s3c64xx_spi_suspend(struct platform_device *pdev, pm_message_t state)
+{
+       struct spi_master *master = spi_master_get(platform_get_drvdata(pdev));
+       struct s3c64xx_spi_driver_data *sdd = spi_master_get_devdata(master);
+       struct s3c64xx_spi_cntrlr_info *sci = sdd->cntrlr_info;
+       struct s3c64xx_spi_csinfo *cs;
+       unsigned long flags;
+
+       spin_lock_irqsave(&sdd->lock, flags);
+       sdd->state |= SUSPND;
+       spin_unlock_irqrestore(&sdd->lock, flags);
+
+       while (sdd->state & SPIBUSY)
+               msleep(10);
+
+       /* Disable the clock */
+       if (sci->src_clk != sdd->clk)
+               clk_disable(sci->src_clk);
+
+       clk_disable(sdd->clk);
+
+       sdd->cur_speed = 0; /* Output Clock is stopped */
+
+       return 0;
+}
+
+static int s3c64xx_spi_resume(struct platform_device *pdev)
+{
+       struct spi_master *master = spi_master_get(platform_get_drvdata(pdev));
+       struct s3c64xx_spi_driver_data *sdd = spi_master_get_devdata(master);
+       struct s3c64xx_spi_cntrlr_info *sci = sdd->cntrlr_info;
+       unsigned long flags;
+
+       sci->cfg_gpio(pdev);
+
+       /* Enable the clock */
+       if (sci->src_clk != sdd->clk)
+               clk_enable(sci->src_clk);
+
+       clk_enable(sdd->clk);
+
+       s3c64xx_spi_hwinit(sdd, pdev->id);
+
+       spin_lock_irqsave(&sdd->lock, flags);
+       sdd->state &= ~SUSPND;
+       spin_unlock_irqrestore(&sdd->lock, flags);
+
+       return 0;
+}
+#else
+#define s3c64xx_spi_suspend    NULL
+#define s3c64xx_spi_resume     NULL
+#endif /* CONFIG_PM */
+
+static struct platform_driver s3c64xx_spi_driver = {
+       .driver = {
+               .name   = "s3c64xx-spi",
+               .owner = THIS_MODULE,
+       },
+       .remove = s3c64xx_spi_remove,
+       .suspend = s3c64xx_spi_suspend,
+       .resume = s3c64xx_spi_resume,
+};
+MODULE_ALIAS("platform:s3c64xx-spi");
+
+static int __init s3c64xx_spi_init(void)
+{
+       return platform_driver_probe(&s3c64xx_spi_driver, s3c64xx_spi_probe);
+}
+module_init(s3c64xx_spi_init);
+
+static void __exit s3c64xx_spi_exit(void)
+{
+       platform_driver_unregister(&s3c64xx_spi_driver);
+}
+module_exit(s3c64xx_spi_exit);
+
+MODULE_AUTHOR("Jaswinder Singh <jassi.brar@samsung.com>");
+MODULE_DESCRIPTION("S3C64XX SPI Controller Driver");
+MODULE_LICENSE("GPL");
index 7d36720eb98299858878c777ffe09b74d6641a96..a65c12ffa73352a79a160dcc5ae665fecb862dc1 100644 (file)
@@ -148,7 +148,7 @@ static int sh_sci_spi_probe(struct platform_device *dev)
                ret = -ENOENT;
                goto err1;
        }
-       sp->membase = ioremap(r->start, r->end - r->start + 1);
+       sp->membase = ioremap(r->start, resource_size(r));
        if (!sp->membase) {
                ret = -ENXIO;
                goto err1;
index 19f75627c3deaf0dd0d31a38689a550abf66f488..dfa024b633e173787dd7c467d7581b27f2d0fd79 100644 (file)
@@ -375,12 +375,10 @@ static int __init txx9spi_probe(struct platform_device *dev)
        res = platform_get_resource(dev, IORESOURCE_MEM, 0);
        if (!res)
                goto exit_busy;
-       if (!devm_request_mem_region(&dev->dev,
-                                    res->start, res->end - res->start + 1,
+       if (!devm_request_mem_region(&dev->dev, res->start, resource_size(res),
                                     "spi_txx9"))
                goto exit_busy;
-       c->membase = devm_ioremap(&dev->dev,
-                                 res->start, res->end - res->start + 1);
+       c->membase = devm_ioremap(&dev->dev, res->start, resource_size(res));
        if (!c->membase)
                goto exit_busy;
 
index 9c446e6003d5c16bae4b9b7c5cb589411bc79770..ea1bec3c9a13359a4058a762e9f2f8de7755cc36 100644 (file)
@@ -53,7 +53,7 @@
 #define SPIDEV_MAJOR                   153     /* assigned */
 #define N_SPI_MINORS                   32      /* ... up to 256 */
 
-static unsigned long   minors[N_SPI_MINORS / BITS_PER_LONG];
+static DECLARE_BITMAP(minors, N_SPI_MINORS);
 
 
 /* Bit masks for spi_device.mode management.  Note that incorrect
@@ -558,7 +558,7 @@ static struct class *spidev_class;
 
 /*-------------------------------------------------------------------------*/
 
-static int spidev_probe(struct spi_device *spi)
+static int __devinit spidev_probe(struct spi_device *spi)
 {
        struct spidev_data      *spidev;
        int                     status;
@@ -607,7 +607,7 @@ static int spidev_probe(struct spi_device *spi)
        return status;
 }
 
-static int spidev_remove(struct spi_device *spi)
+static int __devexit spidev_remove(struct spi_device *spi)
 {
        struct spidev_data      *spidev = spi_get_drvdata(spi);
 
@@ -629,7 +629,7 @@ static int spidev_remove(struct spi_device *spi)
        return 0;
 }
 
-static struct spi_driver spidev_spi = {
+static struct spi_driver spidev_spi_driver = {
        .driver = {
                .name =         "spidev",
                .owner =        THIS_MODULE,
@@ -661,14 +661,14 @@ static int __init spidev_init(void)
 
        spidev_class = class_create(THIS_MODULE, "spidev");
        if (IS_ERR(spidev_class)) {
-               unregister_chrdev(SPIDEV_MAJOR, spidev_spi.driver.name);
+               unregister_chrdev(SPIDEV_MAJOR, spidev_spi_driver.driver.name);
                return PTR_ERR(spidev_class);
        }
 
-       status = spi_register_driver(&spidev_spi);
+       status = spi_register_driver(&spidev_spi_driver);
        if (status < 0) {
                class_destroy(spidev_class);
-               unregister_chrdev(SPIDEV_MAJOR, spidev_spi.driver.name);
+               unregister_chrdev(SPIDEV_MAJOR, spidev_spi_driver.driver.name);
        }
        return status;
 }
@@ -676,9 +676,9 @@ module_init(spidev_init);
 
 static void __exit spidev_exit(void)
 {
-       spi_unregister_driver(&spidev_spi);
+       spi_unregister_driver(&spidev_spi_driver);
        class_destroy(spidev_class);
-       unregister_chrdev(SPIDEV_MAJOR, spidev_spi.driver.name);
+       unregister_chrdev(SPIDEV_MAJOR, spidev_spi_driver.driver.name);
 }
 module_exit(spidev_exit);
 
index 4c10edecfb6616070ce46610a5694f2fbf051080..86d95c228adb7289ec294f454b44e9575516d1f7 100644 (file)
@@ -85,7 +85,7 @@ static int adp5520_bl_get_brightness(struct backlight_device *bl)
        return error ? data->current_brightness : reg_val;
 }
 
-static struct backlight_ops adp5520_bl_ops = {
+static const struct backlight_ops adp5520_bl_ops = {
        .update_status  = adp5520_bl_update_status,
        .get_brightness = adp5520_bl_get_brightness,
 };
index 2c3bdfc620b74a0d92cdc0a7bfd5b932b347c682..d769b0bab21abfc5b41105f430210532a7bd948e 100644 (file)
@@ -61,7 +61,7 @@ static int adx_backlight_check_fb(struct fb_info *fb)
        return 1;
 }
 
-static struct backlight_ops adx_backlight_ops = {
+static const struct backlight_ops adx_backlight_ops = {
        .options = 0,
        .update_status = adx_backlight_update_status,
        .get_brightness = adx_backlight_get_brightness,
index 2cf7ba52f67c1f62ffd0f0ee61dc65650f67768e..f625ffc69ad3139cef6ba88688ac1e76878ff1e4 100644 (file)
@@ -113,7 +113,7 @@ static int atmel_pwm_bl_init_pwm(struct atmel_pwm_bl *pwmbl)
        return pwm_channel_enable(&pwmbl->pwmc);
 }
 
-static struct backlight_ops atmel_pwm_bl_ops = {
+static const struct backlight_ops atmel_pwm_bl_ops = {
        .get_brightness = atmel_pwm_bl_get_intensity,
        .update_status  = atmel_pwm_bl_set_intensity,
 };
index 6615ac7fa60a6124e31257a2ce1b09cf871ed719..18829cf68b1b97ca111af2d390820a3576ad68c1 100644 (file)
@@ -269,7 +269,7 @@ EXPORT_SYMBOL(backlight_force_update);
  * ERR_PTR() or a pointer to the newly allocated device.
  */
 struct backlight_device *backlight_device_register(const char *name,
-               struct device *parent, void *devdata, struct backlight_ops *ops)
+               struct device *parent, void *devdata, const struct backlight_ops *ops)
 {
        struct backlight_device *new_bd;
        int rc;
index 96774949cd30ba3964ea1340e47a3524f841c08b..b4bcf8043797d18ced2ab1618ee9ef57853e6399 100644 (file)
@@ -451,7 +451,7 @@ void corgi_lcd_limit_intensity(int limit)
 }
 EXPORT_SYMBOL(corgi_lcd_limit_intensity);
 
-static struct backlight_ops corgi_bl_ops = {
+static const struct backlight_ops corgi_bl_ops = {
        .get_brightness = corgi_bl_get_intensity,
        .update_status  = corgi_bl_update_status,
 };
index b9fe62b475c63a3fa953ea182958496cd4037d52..da86db4374a05a350084b2f06834d489e773f4ba 100644 (file)
@@ -108,7 +108,7 @@ static int cr_backlight_get_intensity(struct backlight_device *bd)
        return intensity;
 }
 
-static struct backlight_ops cr_backlight_ops = {
+static const struct backlight_ops cr_backlight_ops = {
        .get_brightness = cr_backlight_get_intensity,
        .update_status = cr_backlight_set_intensity,
 };
@@ -201,7 +201,7 @@ static int cr_backlight_probe(struct platform_device *pdev)
        if (IS_ERR(ldp)) {
                backlight_device_unregister(bdp);
                pci_dev_put(lpc_dev);
-               return PTR_ERR(bdp);
+               return PTR_ERR(ldp);
        }
 
        pci_read_config_dword(lpc_dev, CRVML_REG_GPIOBAR,
index f2d76dae1eb370813b7c31b338aa951d45e2267b..74cdc640173de5879af656d46cfaafa4ec8ad6bf 100644 (file)
@@ -95,7 +95,7 @@ static int da903x_backlight_get_brightness(struct backlight_device *bl)
        return data->current_brightness;
 }
 
-static struct backlight_ops da903x_backlight_ops = {
+static const struct backlight_ops da903x_backlight_ops = {
        .update_status  = da903x_backlight_update_status,
        .get_brightness = da903x_backlight_get_brightness,
 };
index 6d27f62fdcd09055db7bba7207a72905fc8aa36d..e6d348e63596455f06d4a3443e82ea4f982a91e6 100644 (file)
@@ -70,7 +70,7 @@ void corgibl_limit_intensity(int limit)
 }
 EXPORT_SYMBOL(corgibl_limit_intensity);
 
-static struct backlight_ops genericbl_ops = {
+static const struct backlight_ops genericbl_ops = {
        .options = BL_CORE_SUSPENDRESUME,
        .get_brightness = genericbl_get_intensity,
        .update_status  = genericbl_send_intensity,
index 7fb4eefff80daa497fdc4440508b5b3ca8739886..f7cc528d5be79a066347e43b3271c0d376e5275c 100644 (file)
@@ -98,7 +98,7 @@ static int hp680bl_get_intensity(struct backlight_device *bd)
        return current_intensity;
 }
 
-static struct backlight_ops hp680bl_ops = {
+static const struct backlight_ops hp680bl_ops = {
        .get_brightness = hp680bl_get_intensity,
        .update_status  = hp680bl_set_intensity,
 };
index 7aed2565c1bd1b666ab7b46f63e02879e6097a54..db9071fc56654add5b279b31845c75e34e7726cc 100644 (file)
@@ -93,7 +93,7 @@ out:
        return ret;
 }
 
-static struct backlight_ops jornada_bl_ops = {
+static const struct backlight_ops jornada_bl_ops = {
        .get_brightness = jornada_bl_get_brightness,
        .update_status = jornada_bl_update_status,
        .options = BL_CORE_SUSPENDRESUME,
index a38fda1742ddb58e374ad825218fd4bc06f6a332..939e7b830cf3f5afabaf104e694d465e63759bd5 100644 (file)
@@ -134,7 +134,7 @@ static int kb3886bl_get_intensity(struct backlight_device *bd)
        return kb3886bl_intensity;
 }
 
-static struct backlight_ops kb3886bl_ops = {
+static const struct backlight_ops kb3886bl_ops = {
        .get_brightness = kb3886bl_get_intensity,
        .update_status  = kb3886bl_send_intensity,
 };
index 6b488b8a7eee6cb6b0e5a9631239d57aac7e66e9..00a9591b00030f8a3bf744ec124cc163ff061ad9 100644 (file)
@@ -141,7 +141,7 @@ static int locomolcd_get_intensity(struct backlight_device *bd)
        return current_intensity;
 }
 
-static struct backlight_ops locomobl_data = {
+static const struct backlight_ops locomobl_data = {
        .get_brightness = locomolcd_get_intensity,
        .update_status  = locomolcd_set_intensity,
 };
index 9edb8d7c295f38ed02a31642b3eba3be1c5681c9..2e78b0784bdc8b1dae7083fbee8f052302a30227 100644 (file)
@@ -33,7 +33,7 @@ struct dmi_match_data {
        unsigned long iostart;
        unsigned long iolen;
        /* Backlight operations structure. */
-       struct backlight_ops backlight_ops;
+       const struct backlight_ops backlight_ops;
 };
 
 /* Module parameters. */
@@ -218,6 +218,24 @@ static const struct dmi_system_id __initdata mbp_device_table[] = {
                },
                .driver_data    = (void *)&nvidia_chipset_data,
        },
+       {
+               .callback       = mbp_dmi_match,
+               .ident          = "MacBookPro 5,3",
+               .matches        = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "Apple Inc."),
+                       DMI_MATCH(DMI_PRODUCT_NAME, "MacBookPro5,3"),
+               },
+               .driver_data    = (void *)&nvidia_chipset_data,
+       },
+       {
+               .callback       = mbp_dmi_match,
+               .ident          = "MacBookPro 5,4",
+               .matches        = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "Apple Inc."),
+                       DMI_MATCH(DMI_PRODUCT_NAME, "MacBookPro5,4"),
+               },
+               .driver_data    = (void *)&nvidia_chipset_data,
+       },
        {
                .callback       = mbp_dmi_match,
                .ident          = "MacBookPro 5,5",
index 8693e5fcd2eb83dea83083a5ff3126900c87abc9..409ca9643528b6d52a0906afdd97b3cbcf158a9b 100644 (file)
@@ -125,7 +125,7 @@ static int omapbl_get_intensity(struct backlight_device *dev)
        return bl->current_intensity;
 }
 
-static struct backlight_ops omapbl_ops = {
+static const struct backlight_ops omapbl_ops = {
        .get_brightness = omapbl_get_intensity,
        .update_status  = omapbl_update_status,
 };
index 9edaf24fd82d4e49f0bf439162fa156088cbc25e..075786e05034cc5defba6606a32e13b2e18f8de6 100644 (file)
@@ -54,7 +54,7 @@ static int progearbl_get_intensity(struct backlight_device *bd)
        return intensity - HW_LEVEL_MIN;
 }
 
-static struct backlight_ops progearbl_ops = {
+static const struct backlight_ops progearbl_ops = {
        .get_brightness = progearbl_get_intensity,
        .update_status = progearbl_set_intensity,
 };
index 88716626744391a89cae97a5658fb3cc0ffbd545..9d2ec2a1cce8fff721d40680277d45ac5bc1bb55 100644 (file)
 
 struct pwm_bl_data {
        struct pwm_device       *pwm;
+       struct device           *dev;
        unsigned int            period;
-       int                     (*notify)(int brightness);
+       int                     (*notify)(struct device *,
+                                         int brightness);
 };
 
 static int pwm_backlight_update_status(struct backlight_device *bl)
@@ -39,7 +41,7 @@ static int pwm_backlight_update_status(struct backlight_device *bl)
                brightness = 0;
 
        if (pb->notify)
-               brightness = pb->notify(brightness);
+               brightness = pb->notify(pb->dev, brightness);
 
        if (brightness == 0) {
                pwm_config(pb->pwm, 0, pb->period);
@@ -56,7 +58,7 @@ static int pwm_backlight_get_brightness(struct backlight_device *bl)
        return bl->props.brightness;
 }
 
-static struct backlight_ops pwm_backlight_ops = {
+static const struct backlight_ops pwm_backlight_ops = {
        .update_status  = pwm_backlight_update_status,
        .get_brightness = pwm_backlight_get_brightness,
 };
@@ -88,6 +90,7 @@ static int pwm_backlight_probe(struct platform_device *pdev)
 
        pb->period = data->pwm_period_ns;
        pb->notify = data->notify;
+       pb->dev = &pdev->dev;
 
        pb->pwm = pwm_request(data->pwm_id, "backlight");
        if (IS_ERR(pb->pwm)) {
@@ -146,7 +149,7 @@ static int pwm_backlight_suspend(struct platform_device *pdev,
        struct pwm_bl_data *pb = dev_get_drvdata(&bl->dev);
 
        if (pb->notify)
-               pb->notify(0);
+               pb->notify(pb->dev, 0);
        pwm_config(pb->pwm, 0, pb->period);
        pwm_disable(pb->pwm);
        return 0;
index 43edbada12d19156970d5b84f68e56de73333c25..e14ce4d469f57f1ed6525ba53113bcbd6bca6913 100644 (file)
@@ -72,7 +72,7 @@ static int tosa_bl_get_brightness(struct backlight_device *dev)
        return props->brightness;
 }
 
-static struct backlight_ops bl_ops = {
+static const struct backlight_ops bl_ops = {
        .get_brightness         = tosa_bl_get_brightness,
        .update_status          = tosa_bl_update_status,
 };
index 467bdb7efb231057160aa5333960057439fac502..e32add37a203453558c80a6c195276cb402094c6 100644 (file)
@@ -112,7 +112,7 @@ static int wm831x_backlight_get_brightness(struct backlight_device *bl)
        return data->current_brightness;
 }
 
-static struct backlight_ops wm831x_backlight_ops = {
+static const struct backlight_ops wm831x_backlight_ops = {
        .options = BL_CORE_SUSPENDRESUME,
        .update_status  = wm831x_backlight_update_status,
        .get_brightness = wm831x_backlight_get_brightness,
index 10d8c4b4baeb855f2ec54853e8988947e0231a12..d8df17a7d5fce50a0f2566f5ee06713eb0ae96bd 100644 (file)
@@ -680,7 +680,7 @@ static int viafb_ioctl(struct fb_info *info, u_int cmd, u_long arg)
                if (!viafb_gamma_table)
                        return -ENOMEM;
                if (copy_from_user(viafb_gamma_table, argp,
-                               sizeof(viafb_gamma_table))) {
+                               256 * sizeof(u32))) {
                        kfree(viafb_gamma_table);
                        return -EFAULT;
                }
@@ -694,7 +694,7 @@ static int viafb_ioctl(struct fb_info *info, u_int cmd, u_long arg)
                        return -ENOMEM;
                viafb_get_gamma_table(viafb_gamma_table);
                if (copy_to_user(argp, viafb_gamma_table,
-                       sizeof(viafb_gamma_table))) {
+                       256 * sizeof(u32))) {
                        kfree(viafb_gamma_table);
                        return -EFAULT;
                }
index f8fccaaad6282b097f75fae49f2aa4d6c2e6c390..64d44efad7a5d6752ad5c867ff7d10dd0b94b1e3 100644 (file)
@@ -6,10 +6,6 @@ menu "File systems"
 
 if BLOCK
 
-config FS_JOURNAL_INFO
-       bool
-       default n
-
 source "fs/ext2/Kconfig"
 source "fs/ext3/Kconfig"
 source "fs/ext4/Kconfig"
index b639dcf7c778cfc6f81eca964c8c6748a5840949..346b69405363b7419d7d4b591b240074d72bff6b 100644 (file)
@@ -32,7 +32,7 @@
 
 static int load_aout_binary(struct linux_binprm *, struct pt_regs * regs);
 static int load_aout_library(struct file*);
-static int aout_core_dump(long signr, struct pt_regs *regs, struct file *file, unsigned long limit);
+static int aout_core_dump(struct coredump_params *cprm);
 
 static struct linux_binfmt aout_format = {
        .module         = THIS_MODULE,
@@ -89,8 +89,9 @@ if (file->f_op->llseek) { \
  * dumping of the process results in another error..
  */
 
-static int aout_core_dump(long signr, struct pt_regs *regs, struct file *file, unsigned long limit)
+static int aout_core_dump(struct coredump_params *cprm)
 {
+       struct file *file = cprm->file;
        mm_segment_t fs;
        int has_dumped = 0;
        unsigned long dump_start, dump_size;
@@ -108,16 +109,16 @@ static int aout_core_dump(long signr, struct pt_regs *regs, struct file *file, u
        current->flags |= PF_DUMPCORE;
                strncpy(dump.u_comm, current->comm, sizeof(dump.u_comm));
        dump.u_ar0 = offsetof(struct user, regs);
-       dump.signal = signr;
-       aout_dump_thread(regs, &dump);
+       dump.signal = cprm->signr;
+       aout_dump_thread(cprm->regs, &dump);
 
 /* If the size of the dump file exceeds the rlimit, then see what would happen
    if we wrote the stack, but not the data area.  */
-       if ((dump.u_dsize + dump.u_ssize+1) * PAGE_SIZE > limit)
+       if ((dump.u_dsize + dump.u_ssize+1) * PAGE_SIZE > cprm->limit)
                dump.u_dsize = 0;
 
 /* Make sure we have enough room to write the stack and data areas. */
-       if ((dump.u_ssize + 1) * PAGE_SIZE > limit)
+       if ((dump.u_ssize + 1) * PAGE_SIZE > cprm->limit)
                dump.u_ssize = 0;
 
 /* make sure we actually have a data and stack area to dump */
index 97b6e9efeb7f356296ed03d5f1a9f4762067ce6b..edd90c49003cff3b2fdf62ecb0e406007369eedb 100644 (file)
@@ -45,7 +45,7 @@ static unsigned long elf_map(struct file *, unsigned long, struct elf_phdr *,
  * don't even try.
  */
 #ifdef CONFIG_ELF_CORE
-static int elf_core_dump(long signr, struct pt_regs *regs, struct file *file, unsigned long limit);
+static int elf_core_dump(struct coredump_params *cprm);
 #else
 #define elf_core_dump  NULL
 #endif
@@ -1272,8 +1272,9 @@ static int writenote(struct memelfnote *men, struct file *file,
 }
 #undef DUMP_WRITE
 
-#define DUMP_WRITE(addr, nr)   \
-       if ((size += (nr)) > limit || !dump_write(file, (addr), (nr))) \
+#define DUMP_WRITE(addr, nr)                           \
+       if ((size += (nr)) > cprm->limit ||             \
+           !dump_write(cprm->file, (addr), (nr)))      \
                goto end_coredump;
 
 static void fill_elf_header(struct elfhdr *elf, int segs,
@@ -1901,7 +1902,7 @@ static struct vm_area_struct *next_vma(struct vm_area_struct *this_vma,
  * and then they are actually written out.  If we run out of core limit
  * we just truncate.
  */
-static int elf_core_dump(long signr, struct pt_regs *regs, struct file *file, unsigned long limit)
+static int elf_core_dump(struct coredump_params *cprm)
 {
        int has_dumped = 0;
        mm_segment_t fs;
@@ -1947,7 +1948,7 @@ static int elf_core_dump(long signr, struct pt_regs *regs, struct file *file, un
         * notes.  This also sets up the file header.
         */
        if (!fill_note_info(elf, segs + 1, /* including notes section */
-                           &info, signr, regs))
+                           &info, cprm->signr, cprm->regs))
                goto cleanup;
 
        has_dumped = 1;
@@ -2009,14 +2010,14 @@ static int elf_core_dump(long signr, struct pt_regs *regs, struct file *file, un
 #endif
 
        /* write out the notes section */
-       if (!write_note_info(&info, file, &foffset))
+       if (!write_note_info(&info, cprm->file, &foffset))
                goto end_coredump;
 
-       if (elf_coredump_extra_notes_write(file, &foffset))
+       if (elf_coredump_extra_notes_write(cprm->file, &foffset))
                goto end_coredump;
 
        /* Align to page */
-       if (!dump_seek(file, dataoff - foffset))
+       if (!dump_seek(cprm->file, dataoff - foffset))
                goto end_coredump;
 
        for (vma = first_vma(current, gate_vma); vma != NULL;
@@ -2033,12 +2034,13 @@ static int elf_core_dump(long signr, struct pt_regs *regs, struct file *file, un
                        page = get_dump_page(addr);
                        if (page) {
                                void *kaddr = kmap(page);
-                               stop = ((size += PAGE_SIZE) > limit) ||
-                                       !dump_write(file, kaddr, PAGE_SIZE);
+                               stop = ((size += PAGE_SIZE) > cprm->limit) ||
+                                       !dump_write(cprm->file, kaddr,
+                                                   PAGE_SIZE);
                                kunmap(page);
                                page_cache_release(page);
                        } else
-                               stop = !dump_seek(file, PAGE_SIZE);
+                               stop = !dump_seek(cprm->file, PAGE_SIZE);
                        if (stop)
                                goto end_coredump;
                }
index 7b055385db8ee8a341bb1af9ed63859523324f2f..c25256a5c5b0e432f63fe3fd07d8e3a2c61a909b 100644 (file)
@@ -76,7 +76,7 @@ static int elf_fdpic_map_file_by_direct_mmap(struct elf_fdpic_params *,
                                             struct file *, struct mm_struct *);
 
 #ifdef CONFIG_ELF_CORE
-static int elf_fdpic_core_dump(long, struct pt_regs *, struct file *, unsigned long limit);
+static int elf_fdpic_core_dump(struct coredump_params *cprm);
 #endif
 
 static struct linux_binfmt elf_fdpic_format = {
@@ -1326,8 +1326,9 @@ static int writenote(struct memelfnote *men, struct file *file)
 #undef DUMP_WRITE
 #undef DUMP_SEEK
 
-#define DUMP_WRITE(addr, nr)   \
-       if ((size += (nr)) > limit || !dump_write(file, (addr), (nr))) \
+#define DUMP_WRITE(addr, nr)                           \
+       if ((size += (nr)) > cprm->limit ||             \
+           !dump_write(cprm->file, (addr), (nr)))      \
                goto end_coredump;
 
 static inline void fill_elf_fdpic_header(struct elfhdr *elf, int segs)
@@ -1582,8 +1583,7 @@ static int elf_fdpic_dump_segments(struct file *file, size_t *size,
  * and then they are actually written out.  If we run out of core limit
  * we just truncate.
  */
-static int elf_fdpic_core_dump(long signr, struct pt_regs *regs,
-                              struct file *file, unsigned long limit)
+static int elf_fdpic_core_dump(struct coredump_params *cprm)
 {
 #define        NUM_NOTES       6
        int has_dumped = 0;
@@ -1642,7 +1642,7 @@ static int elf_fdpic_core_dump(long signr, struct pt_regs *regs,
                goto cleanup;
 #endif
 
-       if (signr) {
+       if (cprm->signr) {
                struct core_thread *ct;
                struct elf_thread_status *tmp;
 
@@ -1661,14 +1661,14 @@ static int elf_fdpic_core_dump(long signr, struct pt_regs *regs,
                        int sz;
 
                        tmp = list_entry(t, struct elf_thread_status, list);
-                       sz = elf_dump_thread_status(signr, tmp);
+                       sz = elf_dump_thread_status(cprm->signr, tmp);
                        thread_status_size += sz;
                }
        }
 
        /* now collect the dump for the current */
-       fill_prstatus(prstatus, current, signr);
-       elf_core_copy_regs(&prstatus->pr_reg, regs);
+       fill_prstatus(prstatus, current, cprm->signr);
+       elf_core_copy_regs(&prstatus->pr_reg, cprm->regs);
 
        segs = current->mm->map_count;
 #ifdef ELF_CORE_EXTRA_PHDRS
@@ -1703,7 +1703,7 @@ static int elf_fdpic_core_dump(long signr, struct pt_regs *regs,
 
        /* Try to dump the FPU. */
        if ((prstatus->pr_fpvalid =
-            elf_core_copy_task_fpregs(current, regs, fpu)))
+            elf_core_copy_task_fpregs(current, cprm->regs, fpu)))
                fill_note(notes + numnote++,
                          "CORE", NT_PRFPREG, sizeof(*fpu), fpu);
 #ifdef ELF_CORE_COPY_XFPREGS
@@ -1774,7 +1774,7 @@ static int elf_fdpic_core_dump(long signr, struct pt_regs *regs,
 
        /* write out the notes section */
        for (i = 0; i < numnote; i++)
-               if (!writenote(notes + i, file))
+               if (!writenote(notes + i, cprm->file))
                        goto end_coredump;
 
        /* write out the thread status notes section */
@@ -1783,14 +1783,15 @@ static int elf_fdpic_core_dump(long signr, struct pt_regs *regs,
                                list_entry(t, struct elf_thread_status, list);
 
                for (i = 0; i < tmp->num_notes; i++)
-                       if (!writenote(&tmp->notes[i], file))
+                       if (!writenote(&tmp->notes[i], cprm->file))
                                goto end_coredump;
        }
 
-       if (!dump_seek(file, dataoff))
+       if (!dump_seek(cprm->file, dataoff))
                goto end_coredump;
 
-       if (elf_fdpic_dump_segments(file, &size, &limit, mm_flags) < 0)
+       if (elf_fdpic_dump_segments(cprm->file, &size, &cprm->limit,
+                                   mm_flags) < 0)
                goto end_coredump;
 
 #ifdef ELF_CORE_WRITE_EXTRA_DATA
index a2796651e75690eb8e8a875146be852bfb1d637b..d4a00ea1054c63f0401bb0b1ef0e78a245df8851 100644 (file)
@@ -87,7 +87,7 @@ static int load_flat_shared_library(int id, struct lib_info *p);
 #endif
 
 static int load_flat_binary(struct linux_binprm *, struct pt_regs * regs);
-static int flat_core_dump(long signr, struct pt_regs *regs, struct file *file, unsigned long limit);
+static int flat_core_dump(struct coredump_params *cprm);
 
 static struct linux_binfmt flat_format = {
        .module         = THIS_MODULE,
@@ -102,10 +102,10 @@ static struct linux_binfmt flat_format = {
  * Currently only a stub-function.
  */
 
-static int flat_core_dump(long signr, struct pt_regs *regs, struct file *file, unsigned long limit)
+static int flat_core_dump(struct coredump_params *cprm)
 {
        printk("Process %s:%d received signr %d and should have core dumped\n",
-                       current->comm, current->pid, (int) signr);
+                       current->comm, current->pid, (int) cprm->signr);
        return(1);
 }
 
index eff74b9c9e77cf8f0b933adb2addcc9aac899a46..2a9b5330cc5e9ada9c23f41c6007996f58b6b938 100644 (file)
@@ -43,7 +43,7 @@ static int load_som_library(struct file *);
  * don't even try.
  */
 #if 0
-static int som_core_dump(long signr, struct pt_regs *regs, unsigned long limit);
+static int som_core_dump(struct coredump_params *cprm);
 #else
 #define som_core_dump  NULL
 #endif
index 402afe0a0bfbbab01e307800716c8b2553dc6479..7bb3c020e570d9a1e0d2d640f6c9127df8e57a01 100644 (file)
@@ -4,7 +4,6 @@ config BTRFS_FS
        select LIBCRC32C
        select ZLIB_INFLATE
        select ZLIB_DEFLATE
-       select FS_JOURNAL_INFO
        help
          Btrfs is a new filesystem with extents, writable snapshotting,
          support for multiple devices and many more features.
index 623a5cc3076a114af8808ca030f04bdd82638829..632b02e34ec72b17564602f9c944996e4889a9cf 100644 (file)
--- a/fs/exec.c
+++ b/fs/exec.c
@@ -826,7 +826,9 @@ static int de_thread(struct task_struct *tsk)
                attach_pid(tsk, PIDTYPE_PID,  task_pid(leader));
                transfer_pid(leader, tsk, PIDTYPE_PGID);
                transfer_pid(leader, tsk, PIDTYPE_SID);
+
                list_replace_rcu(&leader->tasks, &tsk->tasks);
+               list_replace_init(&leader->sibling, &tsk->sibling);
 
                tsk->group_leader = tsk;
                leader->group_leader = tsk;
@@ -1761,17 +1763,20 @@ void do_coredump(long signr, int exit_code, struct pt_regs *regs)
        struct mm_struct *mm = current->mm;
        struct linux_binfmt * binfmt;
        struct inode * inode;
-       struct file * file;
        const struct cred *old_cred;
        struct cred *cred;
        int retval = 0;
        int flag = 0;
        int ispipe = 0;
-       unsigned long core_limit = current->signal->rlim[RLIMIT_CORE].rlim_cur;
        char **helper_argv = NULL;
        int helper_argc = 0;
        int dump_count = 0;
        static atomic_t core_dump_count = ATOMIC_INIT(0);
+       struct coredump_params cprm = {
+               .signr = signr,
+               .regs = regs,
+               .limit = current->signal->rlim[RLIMIT_CORE].rlim_cur,
+       };
 
        audit_core_dumps(signr);
 
@@ -1827,15 +1832,15 @@ void do_coredump(long signr, int exit_code, struct pt_regs *regs)
        ispipe = format_corename(corename, signr);
        unlock_kernel();
 
-       if ((!ispipe) && (core_limit < binfmt->min_coredump))
+       if ((!ispipe) && (cprm.limit < binfmt->min_coredump))
                goto fail_unlock;
 
        if (ispipe) {
-               if (core_limit == 0) {
+               if (cprm.limit == 0) {
                        /*
                         * Normally core limits are irrelevant to pipes, since
                         * we're not writing to the file system, but we use
-                        * core_limit of 0 here as a speacial value. Any
+                        * cprm.limit of 0 here as a speacial value. Any
                         * non-zero limit gets set to RLIM_INFINITY below, but
                         * a limit of 0 skips the dump.  This is a consistent
                         * way to catch recursive crashes.  We can still crash
@@ -1868,25 +1873,25 @@ void do_coredump(long signr, int exit_code, struct pt_regs *regs)
                        goto fail_dropcount;
                }
 
-               core_limit = RLIM_INFINITY;
+               cprm.limit = RLIM_INFINITY;
 
                /* SIGPIPE can happen, but it's just never processed */
                if (call_usermodehelper_pipe(helper_argv[0], helper_argv, NULL,
-                               &file)) {
+                               &cprm.file)) {
                        printk(KERN_INFO "Core dump to %s pipe failed\n",
                               corename);
                        goto fail_dropcount;
                }
        } else
-               file = filp_open(corename,
+               cprm.file = filp_open(corename,
                                 O_CREAT | 2 | O_NOFOLLOW | O_LARGEFILE | flag,
                                 0600);
-       if (IS_ERR(file))
+       if (IS_ERR(cprm.file))
                goto fail_dropcount;
-       inode = file->f_path.dentry->d_inode;
+       inode = cprm.file->f_path.dentry->d_inode;
        if (inode->i_nlink > 1)
                goto close_fail;        /* multiple links - don't dump */
-       if (!ispipe && d_unhashed(file->f_path.dentry))
+       if (!ispipe && d_unhashed(cprm.file->f_path.dentry))
                goto close_fail;
 
        /* AK: actually i see no reason to not allow this for named pipes etc.,
@@ -1899,21 +1904,22 @@ void do_coredump(long signr, int exit_code, struct pt_regs *regs)
         */
        if (inode->i_uid != current_fsuid())
                goto close_fail;
-       if (!file->f_op)
+       if (!cprm.file->f_op)
                goto close_fail;
-       if (!file->f_op->write)
+       if (!cprm.file->f_op->write)
                goto close_fail;
-       if (!ispipe && do_truncate(file->f_path.dentry, 0, 0, file) != 0)
+       if (!ispipe &&
+           do_truncate(cprm.file->f_path.dentry, 0, 0, cprm.file) != 0)
                goto close_fail;
 
-       retval = binfmt->core_dump(signr, regs, file, core_limit);
+       retval = binfmt->core_dump(&cprm);
 
        if (retval)
                current->signal->group_exit_code |= 0x80;
 close_fail:
        if (ispipe && core_pipe_limit)
-               wait_for_dump_helpers(file);
-       filp_close(file, NULL);
+               wait_for_dump_helpers(cprm.file);
+       filp_close(cprm.file, NULL);
 fail_dropcount:
        if (dump_count)
                atomic_dec(&core_dump_count);
index e5f6774846e46337d9e2e39790c239815f20caf3..9acf7e808139d3df688da7d66ffb33b5cf081d8e 100644 (file)
@@ -2,7 +2,6 @@ config EXT4_FS
        tristate "The Extended 4 (ext4) filesystem"
        select JBD2
        select CRC16
-       select FS_JOURNAL_INFO
        help
          This is the next generation of the ext3 filesystem.
 
index b192c661caa69a3c04b8d499c85721297a3cd9f2..4dcddf83326f45f7dd874587ef915b7844d60b0b 100644 (file)
@@ -10,7 +10,6 @@ config GFS2_FS
        select SLOW_WORK
        select QUOTA
        select QUOTACTL
-       select FS_JOURNAL_INFO
        help
          A cluster filesystem.
 
index a8408983abd4abbf32c356f4234aa63f63116b15..4e28beeed157236c7a73a9270972f46dddd3d69b 100644 (file)
@@ -1,6 +1,5 @@
 config JBD
        tristate
-       select FS_JOURNAL_INFO
        help
          This is a generic journalling layer for block devices.  It is
          currently used by the ext3 file system, but it could also be
index 0f7d1ceafdfd39a0a5c8a8cd7ddb0b8785cd8d4c..f32f346f4b0a521a5b6bbaedc7b0a5a7750c4b1e 100644 (file)
@@ -1,7 +1,6 @@
 config JBD2
        tristate
        select CRC32
-       select FS_JOURNAL_INFO
        help
          This is a generic journaling layer for block devices that support
          both 32-bit and 64-bit block numbers.  It is currently used by
index faab1273281e6da035a36529a401701ffb67fc43..7d70d63ceb2948c6b8e25ef7628314a4c0eab3b8 100644 (file)
@@ -2068,7 +2068,7 @@ struct mnt_namespace *copy_mnt_ns(unsigned long flags, struct mnt_namespace *ns,
  * create_mnt_ns - creates a private namespace and adds a root filesystem
  * @mnt: pointer to the new root filesystem mountpoint
  */
-static struct mnt_namespace *create_mnt_ns(struct vfsmount *mnt)
+struct mnt_namespace *create_mnt_ns(struct vfsmount *mnt)
 {
        struct mnt_namespace *new_ns;
 
@@ -2080,6 +2080,7 @@ static struct mnt_namespace *create_mnt_ns(struct vfsmount *mnt)
        }
        return new_ns;
 }
+EXPORT_SYMBOL(create_mnt_ns);
 
 SYSCALL_DEFINE5(mount, char __user *, dev_name, char __user *, dir_name,
                char __user *, type, unsigned long, flags, void __user *, data)
index d5b112bcf3ded1c423f1ef0585a82c55efcf26b5..ce907efc5508f049a9ff000a28f8d082159704ce 100644 (file)
@@ -2648,13 +2648,21 @@ out_freepage:
 static int nfs_follow_remote_path(struct vfsmount *root_mnt,
                const char *export_path, struct vfsmount *mnt_target)
 {
+       struct mnt_namespace *ns_private;
        struct nameidata nd;
        struct super_block *s;
        int ret;
 
+       ns_private = create_mnt_ns(root_mnt);
+       ret = PTR_ERR(ns_private);
+       if (IS_ERR(ns_private))
+               goto out_mntput;
+
        ret = vfs_path_lookup(root_mnt->mnt_root, root_mnt,
                        export_path, LOOKUP_FOLLOW, &nd);
 
+       put_mnt_ns(ns_private);
+
        if (ret != 0)
                goto out_err;
 
index 1225af7b21669a02368cea76868f0faae7c05b86..251da07b2a1ddab95bbfb1579ceb20845ddf3e6e 100644 (file)
@@ -2,7 +2,6 @@ config NILFS2_FS
        tristate "NILFS2 file system support (EXPERIMENTAL)"
        depends on EXPERIMENTAL
        select CRC32
-       select FS_JOURNAL_INFO
        help
          NILFS2 is a log-structured file system (LFS) supporting continuous
          snapshotting.  In addition to versioning capability of the entire
index 32fae4040ebf46a94bc84cf1260c2edebce0c330..2efc57173fd703b74bfc1adaad5289df1ff1f179 100644 (file)
@@ -60,7 +60,7 @@ const struct inode_operations ramfs_file_inode_operations = {
  */
 int ramfs_nommu_expand_for_mapping(struct inode *inode, size_t newsize)
 {
-       unsigned long npages, xpages, loop, limit;
+       unsigned long npages, xpages, loop;
        struct page *pages;
        unsigned order;
        void *data;
index ac7cd75c86f8d351e4240bac5d1b12b1c317a4bb..513f431038f9a749960b6c9b328ece5799684330 100644 (file)
@@ -1,7 +1,6 @@
 config REISERFS_FS
        tristate "Reiserfs support"
        select CRC32
-       select FS_JOURNAL_INFO
        help
          Stores not just filenames but the files themselves in a balanced
          tree.  Uses journalling.
index 3a28e7751b3c714da6e3b2c0b7fe9ad89e82a87b..290ae38fca8ae915c749c287ce3e4a19307d3d89 100644 (file)
@@ -2538,6 +2538,12 @@ static int reiserfs_writepage(struct page *page, struct writeback_control *wbc)
        return reiserfs_write_full_page(page, wbc);
 }
 
+static void reiserfs_truncate_failed_write(struct inode *inode)
+{
+       truncate_inode_pages(inode->i_mapping, inode->i_size);
+       reiserfs_truncate_file(inode, 0);
+}
+
 static int reiserfs_write_begin(struct file *file,
                                struct address_space *mapping,
                                loff_t pos, unsigned len, unsigned flags,
@@ -2604,6 +2610,8 @@ static int reiserfs_write_begin(struct file *file,
        if (ret) {
                unlock_page(page);
                page_cache_release(page);
+               /* Truncate allocated blocks */
+               reiserfs_truncate_failed_write(inode);
        }
        return ret;
 }
@@ -2701,9 +2709,7 @@ static int reiserfs_write_end(struct file *file, struct address_space *mapping,
         ** transaction tracking stuff when the size changes.  So, we have
         ** to do the i_size updates here.
         */
-       pos += copied;
-
-       if (pos > inode->i_size) {
+       if (pos + copied > inode->i_size) {
                struct reiserfs_transaction_handle myth;
                lock_depth = reiserfs_write_lock_once(inode->i_sb);
                locked = true;
@@ -2721,7 +2727,7 @@ static int reiserfs_write_end(struct file *file, struct address_space *mapping,
                        goto journal_error;
 
                reiserfs_update_inode_transaction(inode);
-               inode->i_size = pos;
+               inode->i_size = pos + copied;
                /*
                 * this will just nest into our transaction.  It's important
                 * to use mark_inode_dirty so the inode gets pushed around on the
@@ -2751,6 +2757,10 @@ static int reiserfs_write_end(struct file *file, struct address_space *mapping,
                reiserfs_write_unlock_once(inode->i_sb, lock_depth);
        unlock_page(page);
        page_cache_release(page);
+
+       if (pos + len > inode->i_size)
+               reiserfs_truncate_failed_write(inode);
+
        return ret == 0 ? copied : ret;
 
       journal_error:
index 0f5f57858a237bb16335b90bafbe9ece0cf9d8f8..8c4f884db6b40044a5934985656551b1dee22d8d 100644 (file)
@@ -36,18 +36,18 @@ struct backlight_device;
 struct fb_info;
 
 struct backlight_ops {
-       unsigned int options;
+       const unsigned int options;
 
 #define BL_CORE_SUSPENDRESUME  (1 << 0)
 
        /* Notify the backlight driver some property has changed */
-       int (*update_status)(struct backlight_device *);
+       int (* const update_status)(struct backlight_device *);
        /* Return the current backlight brightness (accounting for power,
           fb_blank etc.) */
-       int (*get_brightness)(struct backlight_device *);
+       int (* const get_brightness)(struct backlight_device *);
        /* Check if given framebuffer device is the one bound to this backlight;
           return 0 if not, !=0 if it is. If NULL, backlight always matches the fb. */
-       int (*check_fb)(struct fb_info *);
+       int (* const check_fb)(struct fb_info *);
 };
 
 /* This structure defines all the properties of a backlight */
@@ -86,7 +86,7 @@ struct backlight_device {
           registered this device has been unloaded, and if class_get_devdata()
           points to something in the body of that driver, it is also invalid. */
        struct mutex ops_lock;
-       struct backlight_ops *ops;
+       const struct backlight_ops *ops;
 
        /* The framebuffer notifier block */
        struct notifier_block fb_notif;
@@ -103,7 +103,7 @@ static inline void backlight_update_status(struct backlight_device *bd)
 }
 
 extern struct backlight_device *backlight_device_register(const char *name,
-       struct device *dev, void *devdata, struct backlight_ops *ops);
+       struct device *dev, void *devdata, const struct backlight_ops *ops);
 extern void backlight_device_unregister(struct backlight_device *bd);
 extern void backlight_force_update(struct backlight_device *bd,
                                   enum backlight_update_reason reason);
index aece486ac7349b8463ace95585b2fa9379a04ab3..cd4349bdc34efd2b5044eb8f83e0e4f98e211817 100644 (file)
@@ -68,6 +68,14 @@ struct linux_binprm{
 
 #define BINPRM_MAX_RECURSION 4
 
+/* Function parameter for binfmt->coredump */
+struct coredump_params {
+       long signr;
+       struct pt_regs *regs;
+       struct file *file;
+       unsigned long limit;
+};
+
 /*
  * This structure defines the functions that are used to load the binary formats that
  * linux accepts.
@@ -77,7 +85,7 @@ struct linux_binfmt {
        struct module *module;
        int (*load_binary)(struct linux_binprm *, struct  pt_regs * regs);
        int (*load_shlib)(struct file *);
-       int (*core_dump)(long signr, struct pt_regs *regs, struct file *file, unsigned long limit);
+       int (*core_dump)(struct coredump_params *cprm);
        unsigned long min_coredump;     /* minimal dump size */
        int hasvdso;
 };
index 5ed8b9c50355185439736b3cb4d850d3b07cb646..abec69b63d7e3a5d65f2bc430a0cd389138367b0 100644 (file)
@@ -111,12 +111,6 @@ extern struct cred init_cred;
 # define INIT_PERF_EVENTS(tsk)
 #endif
 
-#ifdef CONFIG_FS_JOURNAL_INFO
-#define INIT_JOURNAL_INFO      .journal_info = NULL,
-#else
-#define INIT_JOURNAL_INFO
-#endif
-
 /*
  *  INIT_TASK is used to set up the first task table, touch at
  * your own risk!. Base=0, limit=0x1fffff (=2MB)
@@ -168,6 +162,7 @@ extern struct cred init_cred;
                .signal = {{0}}},                                       \
        .blocked        = {{0}},                                        \
        .alloc_lock     = __SPIN_LOCK_UNLOCKED(tsk.alloc_lock),         \
+       .journal_info   = NULL,                                         \
        .cpu_timers     = INIT_CPU_TIMERS(tsk.cpu_timers),              \
        .fs_excl        = ATOMIC_INIT(0),                               \
        .pi_lock        = __RAW_SPIN_LOCK_UNLOCKED(tsk.pi_lock),        \
@@ -178,7 +173,6 @@ extern struct cred init_cred;
                [PIDTYPE_SID]  = INIT_PID_LINK(PIDTYPE_SID),            \
        },                                                              \
        .dirties = INIT_PROP_LOCAL_SINGLE(dirties),                     \
-       INIT_JOURNAL_INFO                                               \
        INIT_IDS                                                        \
        INIT_PERF_EVENTS(tsk)                                           \
        INIT_TRACE_IRQFLAGS                                             \
index 3c7497d46ee9655fa86f7bb42f1637a023d6fd25..99d9a6766f7ec055feca4a25d7d5e4c89fd9b058 100644 (file)
@@ -32,8 +32,7 @@ extern void kmemleak_padding(const void *ptr, unsigned long offset,
                             size_t size) __ref;
 extern void kmemleak_not_leak(const void *ptr) __ref;
 extern void kmemleak_ignore(const void *ptr) __ref;
-extern void kmemleak_scan_area(const void *ptr, unsigned long offset,
-                              size_t length, gfp_t gfp) __ref;
+extern void kmemleak_scan_area(const void *ptr, size_t size, gfp_t gfp) __ref;
 extern void kmemleak_no_scan(const void *ptr) __ref;
 
 static inline void kmemleak_alloc_recursive(const void *ptr, size_t size,
@@ -84,8 +83,7 @@ static inline void kmemleak_not_leak(const void *ptr)
 static inline void kmemleak_ignore(const void *ptr)
 {
 }
-static inline void kmemleak_scan_area(const void *ptr, unsigned long offset,
-                                     size_t length, gfp_t gfp)
+static inline void kmemleak_scan_area(const void *ptr, size_t size, gfp_t gfp)
 {
 }
 static inline void kmemleak_erase(void **ptr)
index afc9f9fd70f516a47bfceb4a49cc1e5d3b9bb492..2618aa9063bcc6f1a0fdf83727f234ad83448920 100644 (file)
@@ -12,9 +12,6 @@
 #ifndef __LINUX_LEDS_LP3944_H
 #define __LINUX_LEDS_LP3944_H
 
-#include <linux/leds.h>
-#include <linux/workqueue.h>
-
 #define LP3944_LED0 0
 #define LP3944_LED1 1
 #define LP3944_LED2 2
index 96eea90f01a87711440b6fd3162e2d40d38269b4..f158eb1149aa8b6cd0029ca92ac2645a672a3928 100644 (file)
@@ -32,7 +32,7 @@ struct pca9532_led {
        struct i2c_client *client;
        char *name;
        struct led_classdev ldev;
-       struct work_struct work;
+       struct work_struct work;
        enum pca9532_type type;
        enum pca9532_state state;
 };
diff --git a/include/linux/leds-regulator.h b/include/linux/leds-regulator.h
new file mode 100644 (file)
index 0000000..5a8eb38
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ * leds-regulator.h - platform data structure for regulator driven LEDs.
+ *
+ * Copyright (C) 2009 Antonio Ospite <ospite@studenti.unina.it>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#ifndef __LINUX_LEDS_REGULATOR_H
+#define __LINUX_LEDS_REGULATOR_H
+
+/*
+ * Use "vled" as supply id when declaring the regulator consumer:
+ *
+ * static struct regulator_consumer_supply pcap_regulator_VVIB_consumers [] = {
+ *     { .dev_name = "leds-regulator.0", supply = "vled" },
+ * };
+ *
+ * If you have several regulator driven LEDs, you can append a numerical id to
+ * .dev_name as done above, and use the same id when declaring the platform
+ * device:
+ *
+ * static struct led_regulator_platform_data a780_vibrator_data = {
+ *     .name   = "a780::vibrator",
+ * };
+ *
+ * static struct platform_device a780_vibrator = {
+ *     .name = "leds-regulator",
+ *     .id   = 0,
+ *     .dev  = {
+ *             .platform_data = &a780_vibrator_data,
+ *     },
+ * };
+ */
+
+#include <linux/leds.h>
+
+struct led_regulator_platform_data {
+       char *name;                     /* LED name as expected by LED class */
+       enum led_brightness brightness; /* initial brightness value */
+};
+
+#endif /* __LINUX_LEDS_REGULATOR_H */
index d9ebf1037dfa01699f67722b361f003ed5697abe..d74785c2393ad03976925e8ea6b63e3716718b79 100644 (file)
@@ -23,6 +23,7 @@ struct proc_mounts {
 
 struct fs_struct;
 
+extern struct mnt_namespace *create_mnt_ns(struct vfsmount *mnt);
 extern struct mnt_namespace *copy_mnt_ns(unsigned long, struct mnt_namespace *,
                struct fs_struct *);
 extern void put_mnt_ns(struct mnt_namespace *ns);
index 7a9754c96775091d1ac9ef9c002682e29c38ca88..01b3d759f1fccec7f88e1b276973aa6f70e5bae5 100644 (file)
@@ -10,7 +10,7 @@ struct platform_pwm_backlight_data {
        unsigned int dft_brightness;
        unsigned int pwm_period_ns;
        int (*init)(struct device *dev);
-       int (*notify)(int brightness);
+       int (*notify)(struct device *dev, int brightness);
        void (*exit)(struct device *dev);
 };
 
index 244c287a5ac12f4b0c10068322b3daac493ba29e..211ed32befbd8cfec7065a236924987210877d42 100644 (file)
@@ -1446,10 +1446,8 @@ struct task_struct {
        gfp_t lockdep_reclaim_gfp;
 #endif
 
-#ifdef CONFIG_FS_JOURNAL_INFO
 /* journalling filesystem info */
        void *journal_info;
-#endif
 
 /* stacked block device info */
        struct bio *bio_list, **bio_tail;
diff --git a/include/linux/spi/dw_spi.h b/include/linux/spi/dw_spi.h
new file mode 100644 (file)
index 0000000..51b3e77
--- /dev/null
@@ -0,0 +1,212 @@
+#ifndef DW_SPI_HEADER_H
+#define DW_SPI_HEADER_H
+#include <linux/io.h>
+
+/* Bit fields in CTRLR0 */
+#define SPI_DFS_OFFSET                 0
+
+#define SPI_FRF_OFFSET                 4
+#define SPI_FRF_SPI                    0x0
+#define SPI_FRF_SSP                    0x1
+#define SPI_FRF_MICROWIRE              0x2
+#define SPI_FRF_RESV                   0x3
+
+#define SPI_MODE_OFFSET                        6
+#define SPI_SCPH_OFFSET                        6
+#define SPI_SCOL_OFFSET                        7
+#define SPI_TMOD_OFFSET                        8
+#define        SPI_TMOD_TR                     0x0             /* xmit & recv */
+#define SPI_TMOD_TO                    0x1             /* xmit only */
+#define SPI_TMOD_RO                    0x2             /* recv only */
+#define SPI_TMOD_EPROMREAD             0x3             /* eeprom read mode */
+
+#define SPI_SLVOE_OFFSET               10
+#define SPI_SRL_OFFSET                 11
+#define SPI_CFS_OFFSET                 12
+
+/* Bit fields in SR, 7 bits */
+#define SR_MASK                                0x7f            /* cover 7 bits */
+#define SR_BUSY                                (1 << 0)
+#define SR_TF_NOT_FULL                 (1 << 1)
+#define SR_TF_EMPT                     (1 << 2)
+#define SR_RF_NOT_EMPT                 (1 << 3)
+#define SR_RF_FULL                     (1 << 4)
+#define SR_TX_ERR                      (1 << 5)
+#define SR_DCOL                                (1 << 6)
+
+/* Bit fields in ISR, IMR, RISR, 7 bits */
+#define SPI_INT_TXEI                   (1 << 0)
+#define SPI_INT_TXOI                   (1 << 1)
+#define SPI_INT_RXUI                   (1 << 2)
+#define SPI_INT_RXOI                   (1 << 3)
+#define SPI_INT_RXFI                   (1 << 4)
+#define SPI_INT_MSTI                   (1 << 5)
+
+/* TX RX interrupt level threshhold, max can be 256 */
+#define SPI_INT_THRESHOLD              32
+
+enum dw_ssi_type {
+       SSI_MOTO_SPI = 0,
+       SSI_TI_SSP,
+       SSI_NS_MICROWIRE,
+};
+
+struct dw_spi_reg {
+       u32     ctrl0;
+       u32     ctrl1;
+       u32     ssienr;
+       u32     mwcr;
+       u32     ser;
+       u32     baudr;
+       u32     txfltr;
+       u32     rxfltr;
+       u32     txflr;
+       u32     rxflr;
+       u32     sr;
+       u32     imr;
+       u32     isr;
+       u32     risr;
+       u32     txoicr;
+       u32     rxoicr;
+       u32     rxuicr;
+       u32     msticr;
+       u32     icr;
+       u32     dmacr;
+       u32     dmatdlr;
+       u32     dmardlr;
+       u32     idr;
+       u32     version;
+       u32     dr;             /* Currently oper as 32 bits,
+                               though only low 16 bits matters */
+} __packed;
+
+struct dw_spi {
+       struct spi_master       *master;
+       struct spi_device       *cur_dev;
+       struct device           *parent_dev;
+       enum dw_ssi_type        type;
+
+       void __iomem            *regs;
+       unsigned long           paddr;
+       u32                     iolen;
+       int                     irq;
+       u32                     max_freq;       /* max bus freq supported */
+
+       u16                     bus_num;
+       u16                     num_cs;         /* supported slave numbers */
+
+       /* 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;
+       struct chip_data        *prev_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;
+       u8                      n_bytes;        /* current is a 1/2 bytes op */
+       u8                      max_bits_per_word;      /* maxim is 16b */
+       u32                     dma_width;
+       int                     cs_change;
+       int                     (*write)(struct dw_spi *dws);
+       int                     (*read)(struct dw_spi *dws);
+       irqreturn_t             (*transfer_handler)(struct dw_spi *dws);
+       void                    (*cs_control)(u32 command);
+
+       /* Dma info */
+       int                     dma_inited;
+       struct dma_chan         *txchan;
+       struct dma_chan         *rxchan;
+       int                     txdma_done;
+       int                     rxdma_done;
+       u64                     tx_param;
+       u64                     rx_param;
+       struct device           *dma_dev;
+       dma_addr_t              dma_addr;
+
+       /* Bus interface info */
+       void                    *priv;
+#ifdef CONFIG_DEBUG_FS
+       struct dentry *debugfs;
+#endif
+};
+
+#define dw_readl(dw, name) \
+       __raw_readl(&(((struct dw_spi_reg *)dw->regs)->name))
+#define dw_writel(dw, name, val) \
+       __raw_writel((val), &(((struct dw_spi_reg *)dw->regs)->name))
+#define dw_readw(dw, name) \
+       __raw_readw(&(((struct dw_spi_reg *)dw->regs)->name))
+#define dw_writew(dw, name, val) \
+       __raw_writew((val), &(((struct dw_spi_reg *)dw->regs)->name))
+
+static inline void spi_enable_chip(struct dw_spi *dws, int enable)
+{
+       dw_writel(dws, ssienr, (enable ? 1 : 0));
+}
+
+static inline void spi_set_clk(struct dw_spi *dws, u16 div)
+{
+       dw_writel(dws, baudr, div);
+}
+
+static inline void spi_chip_sel(struct dw_spi *dws, u16 cs)
+{
+       if (cs > dws->num_cs)
+               return;
+       dw_writel(dws, ser, 1 << cs);
+}
+
+/* Disable IRQ bits */
+static inline void spi_mask_intr(struct dw_spi *dws, u32 mask)
+{
+       u32 new_mask;
+
+       new_mask = dw_readl(dws, imr) & ~mask;
+       dw_writel(dws, imr, new_mask);
+}
+
+/* Enable IRQ bits */
+static inline void spi_umask_intr(struct dw_spi *dws, u32 mask)
+{
+       u32 new_mask;
+
+       new_mask = dw_readl(dws, imr) | mask;
+       dw_writel(dws, imr, new_mask);
+}
+
+/*
+ * Each SPI slave device to work with dw_api controller should
+ * has such a structure claiming its working mode (PIO/DMA etc),
+ * which can be save in the "controller_data" member of the
+ * struct spi_device
+ */
+struct dw_spi_chip {
+       u8 poll_mode;   /* 0 for contoller polling mode */
+       u8 type;        /* SPI/SSP/Micrwire */
+       u8 enable_dma;
+       void (*cs_control)(u32 command);
+};
+
+extern int dw_spi_add_host(struct dw_spi *dws);
+extern void dw_spi_remove_host(struct dw_spi *dws);
+extern int dw_spi_suspend_host(struct dw_spi *dws);
+extern int dw_spi_resume_host(struct dw_spi *dws);
+#endif /* DW_SPI_HEADER_H */
index 3fb9944e50a650f35075fd37ecb02cdf04d8e8aa..d5dd0bc408fd4e7b1e79ea8326841a8a187f2738 100644 (file)
@@ -84,6 +84,8 @@ struct vt_setactivate {
 
 #define VT_SETACTIVATE 0x560F  /* Activate and set the mode of a console */
 
+#ifdef __KERNEL__
+
 #ifdef CONFIG_VT_CONSOLE
 
 extern int vt_kmsg_redirect(int new);
@@ -97,6 +99,8 @@ static inline int vt_kmsg_redirect(int new)
 
 #endif
 
+#endif /* __KERNEL__ */
+
 #define vt_get_kmsg_redirect() vt_kmsg_redirect(-1)
 
 #endif /* _LINUX_VT_H */
index 5962d7ccf24371ed7aedf4832cac0cbd5e9c3820..546774a31a66752a698faa02c4e54f7c39fbef9e 100644 (file)
@@ -68,10 +68,10 @@ static void __unhash_process(struct task_struct *p)
                detach_pid(p, PIDTYPE_SID);
 
                list_del_rcu(&p->tasks);
+               list_del_init(&p->sibling);
                __get_cpu_var(process_counts)--;
        }
        list_del_rcu(&p->thread_group);
-       list_del_init(&p->sibling);
 }
 
 /*
@@ -736,12 +736,9 @@ static struct task_struct *find_new_reaper(struct task_struct *father)
 /*
 * Any that need to be release_task'd are put on the @dead list.
  */
-static void reparent_thread(struct task_struct *father, struct task_struct *p,
+static void reparent_leader(struct task_struct *father, struct task_struct *p,
                                struct list_head *dead)
 {
-       if (p->pdeath_signal)
-               group_send_sig_info(p->pdeath_signal, SEND_SIG_NOINFO, p);
-
        list_move_tail(&p->sibling, &p->real_parent->children);
 
        if (task_detached(p))
@@ -780,12 +777,18 @@ static void forget_original_parent(struct task_struct *father)
        reaper = find_new_reaper(father);
 
        list_for_each_entry_safe(p, n, &father->children, sibling) {
-               p->real_parent = reaper;
-               if (p->parent == father) {
-                       BUG_ON(task_ptrace(p));
-                       p->parent = p->real_parent;
-               }
-               reparent_thread(father, p, &dead_children);
+               struct task_struct *t = p;
+               do {
+                       t->real_parent = reaper;
+                       if (t->parent == father) {
+                               BUG_ON(task_ptrace(t));
+                               t->parent = t->real_parent;
+                       }
+                       if (t->pdeath_signal)
+                               group_send_sig_info(t->pdeath_signal,
+                                                   SEND_SIG_NOINFO, t);
+               } while_each_thread(p, t);
+               reparent_leader(father, p, &dead_children);
        }
        write_unlock_irq(&tasklist_lock);
 
@@ -1551,14 +1554,9 @@ static int do_wait_thread(struct wait_opts *wo, struct task_struct *tsk)
        struct task_struct *p;
 
        list_for_each_entry(p, &tsk->children, sibling) {
-               /*
-                * Do not consider detached threads.
-                */
-               if (!task_detached(p)) {
-                       int ret = wait_consider_task(wo, 0, p);
-                       if (ret)
-                               return ret;
-               }
+               int ret = wait_consider_task(wo, 0, p);
+               if (ret)
+                       return ret;
        }
 
        return 0;
index 202a0ba63d3c198a2c891536fc93b749abb955b2..5b2959b3ffc2c239244c25548f3d5c5506baf674 100644 (file)
@@ -1291,7 +1291,6 @@ static struct task_struct *copy_process(unsigned long clone_flags,
        }
 
        if (likely(p->pid)) {
-               list_add_tail(&p->sibling, &p->real_parent->children);
                tracehook_finish_clone(p, clone_flags, trace);
 
                if (thread_group_leader(p)) {
@@ -1303,6 +1302,7 @@ static struct task_struct *copy_process(unsigned long clone_flags,
                        p->signal->tty = tty_kref_get(current->signal->tty);
                        attach_pid(p, PIDTYPE_PGID, task_pgrp(current));
                        attach_pid(p, PIDTYPE_SID, task_session(current));
+                       list_add_tail(&p->sibling, &p->real_parent->children);
                        list_add_tail_rcu(&p->tasks, &init_task.tasks);
                        __get_cpu_var(process_counts)++;
                }
index a65dc787a27bf6f28e4df980523820dacd1d14b3..e96b8ed1cb6aff09033114dd219157b84e8b23a3 100644 (file)
@@ -1910,9 +1910,7 @@ static void kmemleak_load_module(struct module *mod, Elf_Ehdr *hdr,
        unsigned int i;
 
        /* only scan the sections containing data */
-       kmemleak_scan_area(mod->module_core, (unsigned long)mod -
-                          (unsigned long)mod->module_core,
-                          sizeof(struct module), GFP_KERNEL);
+       kmemleak_scan_area(mod, sizeof(struct module), GFP_KERNEL);
 
        for (i = 1; i < hdr->e_shnum; i++) {
                if (!(sechdrs[i].sh_flags & SHF_ALLOC))
@@ -1921,8 +1919,7 @@ static void kmemleak_load_module(struct module *mod, Elf_Ehdr *hdr,
                    && strncmp(secstrings + sechdrs[i].sh_name, ".bss", 4) != 0)
                        continue;
 
-               kmemleak_scan_area(mod->module_core, sechdrs[i].sh_addr -
-                                  (unsigned long)mod->module_core,
+               kmemleak_scan_area((void *)sechdrs[i].sh_addr,
                                   sechdrs[i].sh_size, GFP_KERNEL);
        }
 }
@@ -2250,6 +2247,12 @@ static noinline struct module *load_module(void __user *umod,
                                         "_ftrace_events",
                                         sizeof(*mod->trace_events),
                                         &mod->num_trace_events);
+       /*
+        * This section contains pointers to allocated objects in the trace
+        * code and not scanning it leads to false positives.
+        */
+       kmemleak_scan_area(mod->trace_events, sizeof(*mod->trace_events) *
+                          mod->num_trace_events, GFP_KERNEL);
 #endif
 #ifdef CONFIG_FTRACE_MCOUNT_RECORD
        /* sechdrs[0].sh_size is always zero */
index 1ded8e7dd19b5fde61542b950ba3bb2d41f0690b..17463ca2e2292038027318222177979f83ee6bdb 100644 (file)
@@ -1412,7 +1412,7 @@ static LIST_HEAD(dump_list);
 
 /**
  * kmsg_dump_register - register a kernel log dumper.
- * @dump: pointer to the kmsg_dumper structure
+ * @dumper: pointer to the kmsg_dumper structure
  *
  * Adds a kernel log dumper to the system. The dump callback in the
  * structure will be called when the kernel oopses or panics and must be
@@ -1442,7 +1442,7 @@ EXPORT_SYMBOL_GPL(kmsg_dump_register);
 
 /**
  * kmsg_dump_unregister - unregister a kmsg dumper.
- * @dump: pointer to the kmsg_dumper structure
+ * @dumper: pointer to the kmsg_dumper structure
  *
  * Removes a dump device from the system. Returns zero on success and
  * %-EINVAL otherwise.
index 45e4bef0012a6747d6608cbb78ba0f700a29d7cb..6665761c006d0a67a5600cebb38d6856906ef9c5 100644 (file)
@@ -1131,7 +1131,7 @@ static struct ctl_table vm_table[] = {
                .data           = &sysctl_max_map_count,
                .maxlen         = sizeof(sysctl_max_map_count),
                .mode           = 0644,
-               .proc_handler   = proc_dointvec,
+               .proc_handler   = proc_dointvec_minmax,
                .extra1         = &zero,
        },
 #else
index 8cf9938dd147840d1dbc67166fa30fc22208bf78..25c3ed594c547966c1c291750d8adeb3b9e5674b 100644 (file)
@@ -360,6 +360,7 @@ config DEBUG_KMEMLEAK
        select DEBUG_FS if SYSFS
        select STACKTRACE if STACKTRACE_SUPPORT
        select KALLSYMS
+       select CRC32
        help
          Say Y here if you want to enable the memory leak
          detector. The memory allocation/freeing is traced in a way
index 735343fc857a7b3a6803a59e1ae3f33a7b5090e2..d4996cf46eb6ac0b2227a28ff26c734568b86f99 100644 (file)
@@ -1179,7 +1179,18 @@ qualifier:
  * %ps output the name of a text symbol without offset
  * %pF output the name of a function pointer with its offset
  * %pf output the name of a function pointer without its offset
- * %pR output the address range in a struct resource
+ * %pR output the address range in a struct resource with decoded flags
+ * %pr output the address range in a struct resource with raw flags
+ * %pM output a 6-byte MAC address with colons
+ * %pm output a 6-byte MAC address without colons
+ * %pI4 print an IPv4 address without leading zeros
+ * %pi4 print an IPv4 address with leading zeros
+ * %pI6 print an IPv6 address with colons
+ * %pi6 print an IPv6 address without colons
+ * %pI6c print an IPv6 address as specified by
+ *   http://www.ietf.org/id/draft-kawamura-ipv6-text-representation-03.txt
+ * %pU[bBlL] print a UUID/GUID in big or little endian using lower or upper
+ *   case.
  * %n is ignored
  *
  * The return value is the number of characters which would
index 13f33b3081ec2794afdae74d9071a88ec6aa9df3..5b069e4f5e485ac8f5906c4a0cad7dbdeef75f7e 100644 (file)
@@ -93,6 +93,7 @@
 #include <linux/nodemask.h>
 #include <linux/mm.h>
 #include <linux/workqueue.h>
+#include <linux/crc32.h>
 
 #include <asm/sections.h>
 #include <asm/processor.h>
 #define MSECS_MIN_AGE          5000    /* minimum object age for reporting */
 #define SECS_FIRST_SCAN                60      /* delay before the first scan */
 #define SECS_SCAN_WAIT         600     /* subsequent auto scanning delay */
-#define GRAY_LIST_PASSES       25      /* maximum number of gray list scans */
 #define MAX_SCAN_SIZE          4096    /* maximum size of a scanned block */
 
 #define BYTES_PER_POINTER      sizeof(void *)
 /* scanning area inside a memory block */
 struct kmemleak_scan_area {
        struct hlist_node node;
-       unsigned long offset;
-       size_t length;
+       unsigned long start;
+       size_t size;
 };
 
 #define KMEMLEAK_GREY  0
@@ -149,6 +149,8 @@ struct kmemleak_object {
        int min_count;
        /* the total number of pointers found pointing to this object */
        int count;
+       /* checksum for detecting modified objects */
+       u32 checksum;
        /* memory ranges to be scanned inside an object (empty for all) */
        struct hlist_head area_list;
        unsigned long trace[MAX_TRACE];
@@ -164,8 +166,6 @@ struct kmemleak_object {
 #define OBJECT_REPORTED                (1 << 1)
 /* flag set to not scan the object */
 #define OBJECT_NO_SCAN         (1 << 2)
-/* flag set on newly allocated objects */
-#define OBJECT_NEW             (1 << 3)
 
 /* number of bytes to print per line; must be 16 or 32 */
 #define HEX_ROW_SIZE           16
@@ -241,8 +241,6 @@ struct early_log {
        const void *ptr;                /* allocated/freed memory block */
        size_t size;                    /* memory block size */
        int min_count;                  /* minimum reference count */
-       unsigned long offset;           /* scan area offset */
-       size_t length;                  /* scan area length */
        unsigned long trace[MAX_TRACE]; /* stack trace */
        unsigned int trace_len;         /* stack trace length */
 };
@@ -323,11 +321,6 @@ static bool color_gray(const struct kmemleak_object *object)
                object->count >= object->min_count;
 }
 
-static bool color_black(const struct kmemleak_object *object)
-{
-       return object->min_count == KMEMLEAK_BLACK;
-}
-
 /*
  * Objects are considered unreferenced only if their color is white, they have
  * not be deleted and have a minimum age to avoid false positives caused by
@@ -335,7 +328,7 @@ static bool color_black(const struct kmemleak_object *object)
  */
 static bool unreferenced_object(struct kmemleak_object *object)
 {
-       return (object->flags & OBJECT_ALLOCATED) && color_white(object) &&
+       return (color_white(object) && object->flags & OBJECT_ALLOCATED) &&
                time_before_eq(object->jiffies + jiffies_min_age,
                               jiffies_last_scan);
 }
@@ -348,11 +341,13 @@ static void print_unreferenced(struct seq_file *seq,
                               struct kmemleak_object *object)
 {
        int i;
+       unsigned int msecs_age = jiffies_to_msecs(jiffies - object->jiffies);
 
        seq_printf(seq, "unreferenced object 0x%08lx (size %zu):\n",
                   object->pointer, object->size);
-       seq_printf(seq, "  comm \"%s\", pid %d, jiffies %lu\n",
-                  object->comm, object->pid, object->jiffies);
+       seq_printf(seq, "  comm \"%s\", pid %d, jiffies %lu (age %d.%03ds)\n",
+                  object->comm, object->pid, object->jiffies,
+                  msecs_age / 1000, msecs_age % 1000);
        hex_dump_object(seq, object);
        seq_printf(seq, "  backtrace:\n");
 
@@ -381,6 +376,7 @@ static void dump_object_info(struct kmemleak_object *object)
        pr_notice("  min_count = %d\n", object->min_count);
        pr_notice("  count = %d\n", object->count);
        pr_notice("  flags = 0x%lx\n", object->flags);
+       pr_notice("  checksum = %d\n", object->checksum);
        pr_notice("  backtrace:\n");
        print_stack_trace(&trace, 4);
 }
@@ -522,12 +518,13 @@ static struct kmemleak_object *create_object(unsigned long ptr, size_t size,
        INIT_HLIST_HEAD(&object->area_list);
        spin_lock_init(&object->lock);
        atomic_set(&object->use_count, 1);
-       object->flags = OBJECT_ALLOCATED | OBJECT_NEW;
+       object->flags = OBJECT_ALLOCATED;
        object->pointer = ptr;
        object->size = size;
        object->min_count = min_count;
-       object->count = -1;                     /* no color initially */
+       object->count = 0;                      /* white color initially */
        object->jiffies = jiffies;
+       object->checksum = 0;
 
        /* task information */
        if (in_irq()) {
@@ -720,14 +717,13 @@ static void make_black_object(unsigned long ptr)
  * Add a scanning area to the object. If at least one such area is added,
  * kmemleak will only scan these ranges rather than the whole memory block.
  */
-static void add_scan_area(unsigned long ptr, unsigned long offset,
-                         size_t length, gfp_t gfp)
+static void add_scan_area(unsigned long ptr, size_t size, gfp_t gfp)
 {
        unsigned long flags;
        struct kmemleak_object *object;
        struct kmemleak_scan_area *area;
 
-       object = find_and_get_object(ptr, 0);
+       object = find_and_get_object(ptr, 1);
        if (!object) {
                kmemleak_warn("Adding scan area to unknown object at 0x%08lx\n",
                              ptr);
@@ -741,7 +737,7 @@ static void add_scan_area(unsigned long ptr, unsigned long offset,
        }
 
        spin_lock_irqsave(&object->lock, flags);
-       if (offset + length > object->size) {
+       if (ptr + size > object->pointer + object->size) {
                kmemleak_warn("Scan area larger than object 0x%08lx\n", ptr);
                dump_object_info(object);
                kmem_cache_free(scan_area_cache, area);
@@ -749,8 +745,8 @@ static void add_scan_area(unsigned long ptr, unsigned long offset,
        }
 
        INIT_HLIST_NODE(&area->node);
-       area->offset = offset;
-       area->length = length;
+       area->start = ptr;
+       area->size = size;
 
        hlist_add_head(&area->node, &object->area_list);
 out_unlock:
@@ -786,7 +782,7 @@ static void object_no_scan(unsigned long ptr)
  * processed later once kmemleak is fully initialized.
  */
 static void __init log_early(int op_type, const void *ptr, size_t size,
-                            int min_count, unsigned long offset, size_t length)
+                            int min_count)
 {
        unsigned long flags;
        struct early_log *log;
@@ -808,8 +804,6 @@ static void __init log_early(int op_type, const void *ptr, size_t size,
        log->ptr = ptr;
        log->size = size;
        log->min_count = min_count;
-       log->offset = offset;
-       log->length = length;
        if (op_type == KMEMLEAK_ALLOC)
                log->trace_len = __save_stack_trace(log->trace);
        crt_early_log++;
@@ -858,7 +852,7 @@ void __ref kmemleak_alloc(const void *ptr, size_t size, int min_count,
        if (atomic_read(&kmemleak_enabled) && ptr && !IS_ERR(ptr))
                create_object((unsigned long)ptr, size, min_count, gfp);
        else if (atomic_read(&kmemleak_early_log))
-               log_early(KMEMLEAK_ALLOC, ptr, size, min_count, 0, 0);
+               log_early(KMEMLEAK_ALLOC, ptr, size, min_count);
 }
 EXPORT_SYMBOL_GPL(kmemleak_alloc);
 
@@ -873,7 +867,7 @@ void __ref kmemleak_free(const void *ptr)
        if (atomic_read(&kmemleak_enabled) && ptr && !IS_ERR(ptr))
                delete_object_full((unsigned long)ptr);
        else if (atomic_read(&kmemleak_early_log))
-               log_early(KMEMLEAK_FREE, ptr, 0, 0, 0, 0);
+               log_early(KMEMLEAK_FREE, ptr, 0, 0);
 }
 EXPORT_SYMBOL_GPL(kmemleak_free);
 
@@ -888,7 +882,7 @@ void __ref kmemleak_free_part(const void *ptr, size_t size)
        if (atomic_read(&kmemleak_enabled) && ptr && !IS_ERR(ptr))
                delete_object_part((unsigned long)ptr, size);
        else if (atomic_read(&kmemleak_early_log))
-               log_early(KMEMLEAK_FREE_PART, ptr, size, 0, 0, 0);
+               log_early(KMEMLEAK_FREE_PART, ptr, size, 0);
 }
 EXPORT_SYMBOL_GPL(kmemleak_free_part);
 
@@ -903,7 +897,7 @@ void __ref kmemleak_not_leak(const void *ptr)
        if (atomic_read(&kmemleak_enabled) && ptr && !IS_ERR(ptr))
                make_gray_object((unsigned long)ptr);
        else if (atomic_read(&kmemleak_early_log))
-               log_early(KMEMLEAK_NOT_LEAK, ptr, 0, 0, 0, 0);
+               log_early(KMEMLEAK_NOT_LEAK, ptr, 0, 0);
 }
 EXPORT_SYMBOL(kmemleak_not_leak);
 
@@ -919,22 +913,21 @@ void __ref kmemleak_ignore(const void *ptr)
        if (atomic_read(&kmemleak_enabled) && ptr && !IS_ERR(ptr))
                make_black_object((unsigned long)ptr);
        else if (atomic_read(&kmemleak_early_log))
-               log_early(KMEMLEAK_IGNORE, ptr, 0, 0, 0, 0);
+               log_early(KMEMLEAK_IGNORE, ptr, 0, 0);
 }
 EXPORT_SYMBOL(kmemleak_ignore);
 
 /*
  * Limit the range to be scanned in an allocated memory block.
  */
-void __ref kmemleak_scan_area(const void *ptr, unsigned long offset,
-                             size_t length, gfp_t gfp)
+void __ref kmemleak_scan_area(const void *ptr, size_t size, gfp_t gfp)
 {
        pr_debug("%s(0x%p)\n", __func__, ptr);
 
        if (atomic_read(&kmemleak_enabled) && ptr && !IS_ERR(ptr))
-               add_scan_area((unsigned long)ptr, offset, length, gfp);
+               add_scan_area((unsigned long)ptr, size, gfp);
        else if (atomic_read(&kmemleak_early_log))
-               log_early(KMEMLEAK_SCAN_AREA, ptr, 0, 0, offset, length);
+               log_early(KMEMLEAK_SCAN_AREA, ptr, size, 0);
 }
 EXPORT_SYMBOL(kmemleak_scan_area);
 
@@ -948,10 +941,24 @@ void __ref kmemleak_no_scan(const void *ptr)
        if (atomic_read(&kmemleak_enabled) && ptr && !IS_ERR(ptr))
                object_no_scan((unsigned long)ptr);
        else if (atomic_read(&kmemleak_early_log))
-               log_early(KMEMLEAK_NO_SCAN, ptr, 0, 0, 0, 0);
+               log_early(KMEMLEAK_NO_SCAN, ptr, 0, 0);
 }
 EXPORT_SYMBOL(kmemleak_no_scan);
 
+/*
+ * Update an object's checksum and return true if it was modified.
+ */
+static bool update_checksum(struct kmemleak_object *object)
+{
+       u32 old_csum = object->checksum;
+
+       if (!kmemcheck_is_obj_initialized(object->pointer, object->size))
+               return false;
+
+       object->checksum = crc32(0, (void *)object->pointer, object->size);
+       return object->checksum != old_csum;
+}
+
 /*
  * Memory scanning is a long process and it needs to be interruptable. This
  * function checks whether such interrupt condition occured.
@@ -1031,11 +1038,14 @@ static void scan_block(void *_start, void *_end,
                 * added to the gray_list.
                 */
                object->count++;
-               if (color_gray(object))
+               if (color_gray(object)) {
                        list_add_tail(&object->gray_list, &gray_list);
-               else
-                       put_object(object);
+                       spin_unlock_irqrestore(&object->lock, flags);
+                       continue;
+               }
+
                spin_unlock_irqrestore(&object->lock, flags);
+               put_object(object);
        }
 }
 
@@ -1075,13 +1085,46 @@ static void scan_object(struct kmemleak_object *object)
                }
        } else
                hlist_for_each_entry(area, elem, &object->area_list, node)
-                       scan_block((void *)(object->pointer + area->offset),
-                                  (void *)(object->pointer + area->offset
-                                           + area->length), object, 0);
+                       scan_block((void *)area->start,
+                                  (void *)(area->start + area->size),
+                                  object, 0);
 out:
        spin_unlock_irqrestore(&object->lock, flags);
 }
 
+/*
+ * Scan the objects already referenced (gray objects). More objects will be
+ * referenced and, if there are no memory leaks, all the objects are scanned.
+ */
+static void scan_gray_list(void)
+{
+       struct kmemleak_object *object, *tmp;
+
+       /*
+        * The list traversal is safe for both tail additions and removals
+        * from inside the loop. The kmemleak objects cannot be freed from
+        * outside the loop because their use_count was incremented.
+        */
+       object = list_entry(gray_list.next, typeof(*object), gray_list);
+       while (&object->gray_list != &gray_list) {
+               cond_resched();
+
+               /* may add new objects to the list */
+               if (!scan_should_stop())
+                       scan_object(object);
+
+               tmp = list_entry(object->gray_list.next, typeof(*object),
+                                gray_list);
+
+               /* remove the object from the list and release it */
+               list_del(&object->gray_list);
+               put_object(object);
+
+               object = tmp;
+       }
+       WARN_ON(!list_empty(&gray_list));
+}
+
 /*
  * Scan data sections and all the referenced memory blocks allocated via the
  * kernel's standard allocators. This function must be called with the
@@ -1090,10 +1133,9 @@ out:
 static void kmemleak_scan(void)
 {
        unsigned long flags;
-       struct kmemleak_object *object, *tmp;
+       struct kmemleak_object *object;
        int i;
        int new_leaks = 0;
-       int gray_list_pass = 0;
 
        jiffies_last_scan = jiffies;
 
@@ -1114,7 +1156,6 @@ static void kmemleak_scan(void)
 #endif
                /* reset the reference count (whiten the object) */
                object->count = 0;
-               object->flags &= ~OBJECT_NEW;
                if (color_gray(object) && get_object(object))
                        list_add_tail(&object->gray_list, &gray_list);
 
@@ -1172,62 +1213,36 @@ static void kmemleak_scan(void)
 
        /*
         * Scan the objects already referenced from the sections scanned
-        * above. More objects will be referenced and, if there are no memory
-        * leaks, all the objects will be scanned. The list traversal is safe
-        * for both tail additions and removals from inside the loop. The
-        * kmemleak objects cannot be freed from outside the loop because their
-        * use_count was increased.
+        * above.
         */
-repeat:
-       object = list_entry(gray_list.next, typeof(*object), gray_list);
-       while (&object->gray_list != &gray_list) {
-               cond_resched();
-
-               /* may add new objects to the list */
-               if (!scan_should_stop())
-                       scan_object(object);
-
-               tmp = list_entry(object->gray_list.next, typeof(*object),
-                                gray_list);
-
-               /* remove the object from the list and release it */
-               list_del(&object->gray_list);
-               put_object(object);
-
-               object = tmp;
-       }
-
-       if (scan_should_stop() || ++gray_list_pass >= GRAY_LIST_PASSES)
-               goto scan_end;
+       scan_gray_list();
 
        /*
-        * Check for new objects allocated during this scanning and add them
-        * to the gray list.
+        * Check for new or unreferenced objects modified since the previous
+        * scan and color them gray until the next scan.
         */
        rcu_read_lock();
        list_for_each_entry_rcu(object, &object_list, object_list) {
                spin_lock_irqsave(&object->lock, flags);
-               if ((object->flags & OBJECT_NEW) && !color_black(object) &&
-                   get_object(object)) {
-                       object->flags &= ~OBJECT_NEW;
+               if (color_white(object) && (object->flags & OBJECT_ALLOCATED)
+                   && update_checksum(object) && get_object(object)) {
+                       /* color it gray temporarily */
+                       object->count = object->min_count;
                        list_add_tail(&object->gray_list, &gray_list);
                }
                spin_unlock_irqrestore(&object->lock, flags);
        }
        rcu_read_unlock();
 
-       if (!list_empty(&gray_list))
-               goto repeat;
-
-scan_end:
-       WARN_ON(!list_empty(&gray_list));
+       /*
+        * Re-scan the gray list for modified unreferenced objects.
+        */
+       scan_gray_list();
 
        /*
-        * If scanning was stopped or new objects were being allocated at a
-        * higher rate than gray list scanning, do not report any new
-        * unreferenced objects.
+        * If scanning was stopped do not report any new unreferenced objects.
         */
-       if (scan_should_stop() || gray_list_pass >= GRAY_LIST_PASSES)
+       if (scan_should_stop())
                return;
 
        /*
@@ -1642,8 +1657,7 @@ void __init kmemleak_init(void)
                        kmemleak_ignore(log->ptr);
                        break;
                case KMEMLEAK_SCAN_AREA:
-                       kmemleak_scan_area(log->ptr, log->offset, log->length,
-                                          GFP_KERNEL);
+                       kmemleak_scan_area(log->ptr, log->size, GFP_KERNEL);
                        break;
                case KMEMLEAK_NO_SCAN:
                        kmemleak_no_scan(log->ptr);
index aa1aa23452355067af9d62179cd41d62c64e68fc..033bc135a41f3885cae863ca0c9f5185d6c5d665 100644 (file)
@@ -547,5 +547,17 @@ page_cache_async_readahead(struct address_space *mapping,
 
        /* do read-ahead */
        ondemand_readahead(mapping, ra, filp, true, offset, req_size);
+
+#ifdef CONFIG_BLOCK
+       /*
+        * Normally the current page is !uptodate and lock_page() will be
+        * immediately called to implicitly unplug the device. However this
+        * is not always true for RAID conifgurations, where data arrives
+        * not strictly in their submission order. In this case we need to
+        * explicitly kick off the IO.
+        */
+       if (PageUptodate(page))
+               blk_run_backing_dev(mapping->backing_dev_info, NULL);
+#endif
 }
 EXPORT_SYMBOL_GPL(page_cache_async_readahead);
index 3f4822938f4605f4e6e1cf55f996f812332cd4a6..e17cc2c337b8b6d2794a1a92e0117a8dec16bf0a 100644 (file)
--- a/mm/slab.c
+++ b/mm/slab.c
@@ -2275,9 +2275,11 @@ kmem_cache_create (const char *name, size_t size, size_t align,
        /*
         * Determine if the slab management is 'on' or 'off' slab.
         * (bootstrapping cannot cope with offslab caches so don't do
-        * it too early on.)
+        * it too early on. Always use on-slab management when
+        * SLAB_NOLEAKTRACE to avoid recursive calls into kmemleak)
         */
-       if ((size >= (PAGE_SIZE >> 3)) && !slab_early_init)
+       if ((size >= (PAGE_SIZE >> 3)) && !slab_early_init &&
+           !(flags & SLAB_NOLEAKTRACE))
                /*
                 * Size is large, assume best to place the slab management obj
                 * off-slab (should allow better packing of objs).
@@ -2596,8 +2598,8 @@ static struct slab *alloc_slabmgmt(struct kmem_cache *cachep, void *objp,
                 * kmemleak does not treat the ->s_mem pointer as a reference
                 * to the object. Otherwise we will not report the leak.
                 */
-               kmemleak_scan_area(slabp, offsetof(struct slab, list),
-                                  sizeof(struct list_head), local_flags);
+               kmemleak_scan_area(&slabp->list, sizeof(struct list_head),
+                                  local_flags);
                if (!slabp)
                        return NULL;
        } else {