summaryrefslogtreecommitdiff
path: root/drivers/misc
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/misc')
-rw-r--r--drivers/misc/Kconfig1
-rw-r--r--drivers/misc/Makefile1
-rw-r--r--drivers/misc/c2port/core.c2
-rw-r--r--drivers/misc/cb710/Kconfig25
-rw-r--r--drivers/misc/cb710/Makefile8
-rw-r--r--drivers/misc/cb710/core.c357
-rw-r--r--drivers/misc/cb710/debug.c119
-rw-r--r--drivers/misc/cb710/sgbuf2.c150
-rw-r--r--drivers/misc/eeprom/Kconfig14
-rw-r--r--drivers/misc/eeprom/Makefile1
-rw-r--r--drivers/misc/eeprom/max6875.c246
-rw-r--r--drivers/misc/sgi-gru/grufile.c2
-rw-r--r--drivers/misc/sgi-xp/xpc_uv.c2
-rw-r--r--drivers/misc/sgi-xp/xpnet.c3
14 files changed, 928 insertions, 3 deletions
diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
index 6d1ac180f6ee..68ab39d7cb35 100644
--- a/drivers/misc/Kconfig
+++ b/drivers/misc/Kconfig
@@ -235,5 +235,6 @@ config ISL29003
source "drivers/misc/c2port/Kconfig"
source "drivers/misc/eeprom/Kconfig"
+source "drivers/misc/cb710/Kconfig"
endif # MISC_DEVICES
diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
index 7871f05dcb9b..36f733cd60e6 100644
--- a/drivers/misc/Makefile
+++ b/drivers/misc/Makefile
@@ -21,3 +21,4 @@ obj-$(CONFIG_HP_ILO) += hpilo.o
obj-$(CONFIG_ISL29003) += isl29003.o
obj-$(CONFIG_C2PORT) += c2port/
obj-y += eeprom/
+obj-y += cb710/
diff --git a/drivers/misc/c2port/core.c b/drivers/misc/c2port/core.c
index 0207dd59090d..b5346b4db91a 100644
--- a/drivers/misc/c2port/core.c
+++ b/drivers/misc/c2port/core.c
@@ -15,6 +15,7 @@
#include <linux/errno.h>
#include <linux/err.h>
#include <linux/kernel.h>
+#include <linux/kmemcheck.h>
#include <linux/ctype.h>
#include <linux/delay.h>
#include <linux/idr.h>
@@ -891,6 +892,7 @@ struct c2port_device *c2port_device_register(char *name,
return ERR_PTR(-EINVAL);
c2dev = kmalloc(sizeof(struct c2port_device), GFP_KERNEL);
+ kmemcheck_annotate_bitfield(c2dev, flags);
if (unlikely(!c2dev))
return ERR_PTR(-ENOMEM);
diff --git a/drivers/misc/cb710/Kconfig b/drivers/misc/cb710/Kconfig
new file mode 100644
index 000000000000..22429b8b1068
--- /dev/null
+++ b/drivers/misc/cb710/Kconfig
@@ -0,0 +1,25 @@
+config CB710_CORE
+ tristate "ENE CB710/720 Flash memory card reader support"
+ depends on PCI
+ help
+ This option enables support for PCI ENE CB710/720 Flash memory card
+ reader found in some laptops (ie. some versions of HP Compaq nx9500).
+
+ You will also have to select some flash card format drivers (MMC/SD,
+ MemoryStick).
+
+ This driver can also be built as a module. If so, the module
+ will be called cb710.
+
+config CB710_DEBUG
+ bool "Enable driver debugging"
+ depends on CB710_CORE != n
+ default n
+ help
+ This is an option for use by developers; most people should
+ say N here. This adds a lot of debugging output to dmesg.
+
+config CB710_DEBUG_ASSUMPTIONS
+ bool
+ depends on CB710_CORE != n
+ default y
diff --git a/drivers/misc/cb710/Makefile b/drivers/misc/cb710/Makefile
new file mode 100644
index 000000000000..7b80cbf1a609
--- /dev/null
+++ b/drivers/misc/cb710/Makefile
@@ -0,0 +1,8 @@
+ifeq ($(CONFIG_CB710_DEBUG),y)
+ EXTRA_CFLAGS += -DDEBUG
+endif
+
+obj-$(CONFIG_CB710_CORE) += cb710.o
+
+cb710-y := core.o sgbuf2.o
+cb710-$(CONFIG_CB710_DEBUG) += debug.o
diff --git a/drivers/misc/cb710/core.c b/drivers/misc/cb710/core.c
new file mode 100644
index 000000000000..b14eab0f2ba5
--- /dev/null
+++ b/drivers/misc/cb710/core.c
@@ -0,0 +1,357 @@
+/*
+ * cb710/core.c
+ *
+ * Copyright by Michał Mirosław, 2008-2009
+ *
+ * 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/module.h>
+#include <linux/slab.h>
+#include <linux/pci.h>
+#include <linux/spinlock.h>
+#include <linux/idr.h>
+#include <linux/cb710.h>
+
+static DEFINE_IDA(cb710_ida);
+static DEFINE_SPINLOCK(cb710_ida_lock);
+
+void cb710_pci_update_config_reg(struct pci_dev *pdev,
+ int reg, uint32_t mask, uint32_t xor)
+{
+ u32 rval;
+
+ pci_read_config_dword(pdev, reg, &rval);
+ rval = (rval & mask) ^ xor;
+ pci_write_config_dword(pdev, reg, rval);
+}
+EXPORT_SYMBOL_GPL(cb710_pci_update_config_reg);
+
+/* Some magic writes based on Windows driver init code */
+static int __devinit cb710_pci_configure(struct pci_dev *pdev)
+{
+ unsigned int devfn = PCI_DEVFN(PCI_SLOT(pdev->devfn), 0);
+ struct pci_dev *pdev0 = pci_get_slot(pdev->bus, devfn);
+ u32 val;
+
+ cb710_pci_update_config_reg(pdev, 0x48,
+ ~0x000000FF, 0x0000003F);
+
+ pci_read_config_dword(pdev, 0x48, &val);
+ if (val & 0x80000000)
+ return 0;
+
+ if (!pdev0)
+ return -ENODEV;
+
+ if (pdev0->vendor == PCI_VENDOR_ID_ENE
+ && pdev0->device == PCI_DEVICE_ID_ENE_720) {
+ cb710_pci_update_config_reg(pdev0, 0x8C,
+ ~0x00F00000, 0x00100000);
+ cb710_pci_update_config_reg(pdev0, 0xB0,
+ ~0x08000000, 0x08000000);
+ }
+
+ cb710_pci_update_config_reg(pdev0, 0x8C,
+ ~0x00000F00, 0x00000200);
+ cb710_pci_update_config_reg(pdev0, 0x90,
+ ~0x00060000, 0x00040000);
+
+ pci_dev_put(pdev0);
+
+ return 0;
+}
+
+static irqreturn_t cb710_irq_handler(int irq, void *data)
+{
+ struct cb710_chip *chip = data;
+ struct cb710_slot *slot = &chip->slot[0];
+ irqreturn_t handled = IRQ_NONE;
+ unsigned nr;
+
+ spin_lock(&chip->irq_lock); /* incl. smp_rmb() */
+
+ for (nr = chip->slots; nr; ++slot, --nr) {
+ cb710_irq_handler_t handler_func = slot->irq_handler;
+ if (handler_func && handler_func(slot))
+ handled = IRQ_HANDLED;
+ }
+
+ spin_unlock(&chip->irq_lock);
+
+ return handled;
+}
+
+static void cb710_release_slot(struct device *dev)
+{
+#ifdef CONFIG_CB710_DEBUG_ASSUMPTIONS
+ struct cb710_slot *slot = cb710_pdev_to_slot(to_platform_device(dev));
+ struct cb710_chip *chip = cb710_slot_to_chip(slot);
+
+ /* slot struct can be freed now */
+ atomic_dec(&chip->slot_refs_count);
+#endif
+}
+
+static int __devinit cb710_register_slot(struct cb710_chip *chip,
+ unsigned slot_mask, unsigned io_offset, const char *name)
+{
+ int nr = chip->slots;
+ struct cb710_slot *slot = &chip->slot[nr];
+ int err;
+
+ dev_dbg(cb710_chip_dev(chip),
+ "register: %s.%d; slot %d; mask %d; IO offset: 0x%02X\n",
+ name, chip->platform_id, nr, slot_mask, io_offset);
+
+ /* slot->irq_handler == NULL here; this needs to be
+ * seen before platform_device_register() */
+ ++chip->slots;
+ smp_wmb();
+
+ slot->iobase = chip->iobase + io_offset;
+ slot->pdev.name = name;
+ slot->pdev.id = chip->platform_id;
+ slot->pdev.dev.parent = &chip->pdev->dev;
+ slot->pdev.dev.release = cb710_release_slot;
+
+ err = platform_device_register(&slot->pdev);
+
+#ifdef CONFIG_CB710_DEBUG_ASSUMPTIONS
+ atomic_inc(&chip->slot_refs_count);
+#endif
+
+ if (err) {
+ /* device_initialize() called from platform_device_register()
+ * wants this on error path */
+ platform_device_put(&slot->pdev);
+
+ /* slot->irq_handler == NULL here anyway, so no lock needed */
+ --chip->slots;
+ return err;
+ }
+
+ chip->slot_mask |= slot_mask;
+
+ return 0;
+}
+
+static void cb710_unregister_slot(struct cb710_chip *chip,
+ unsigned slot_mask)
+{
+ int nr = chip->slots - 1;
+
+ if (!(chip->slot_mask & slot_mask))
+ return;
+
+ platform_device_unregister(&chip->slot[nr].pdev);
+
+ /* complementary to spin_unlock() in cb710_set_irq_handler() */
+ smp_rmb();
+ BUG_ON(chip->slot[nr].irq_handler != NULL);
+
+ /* slot->irq_handler == NULL here, so no lock needed */
+ --chip->slots;
+ chip->slot_mask &= ~slot_mask;
+}
+
+void cb710_set_irq_handler(struct cb710_slot *slot,
+ cb710_irq_handler_t handler)
+{
+ struct cb710_chip *chip = cb710_slot_to_chip(slot);
+ unsigned long flags;
+
+ spin_lock_irqsave(&chip->irq_lock, flags);
+ slot->irq_handler = handler;
+ spin_unlock_irqrestore(&chip->irq_lock, flags);
+}
+EXPORT_SYMBOL_GPL(cb710_set_irq_handler);
+
+#ifdef CONFIG_PM
+
+static int cb710_suspend(struct pci_dev *pdev, pm_message_t state)
+{
+ struct cb710_chip *chip = pci_get_drvdata(pdev);
+
+ free_irq(pdev->irq, chip);
+ pci_save_state(pdev);
+ pci_disable_device(pdev);
+ if (state.event & PM_EVENT_SLEEP)
+ pci_set_power_state(pdev, PCI_D3cold);
+ return 0;
+}
+
+static int cb710_resume(struct pci_dev *pdev)
+{
+ struct cb710_chip *chip = pci_get_drvdata(pdev);
+ int err;
+
+ pci_set_power_state(pdev, PCI_D0);
+ pci_restore_state(pdev);
+ err = pcim_enable_device(pdev);
+ if (err)
+ return err;
+
+ return devm_request_irq(&pdev->dev, pdev->irq,
+ cb710_irq_handler, IRQF_SHARED, KBUILD_MODNAME, chip);
+}
+
+#endif /* CONFIG_PM */
+
+static int __devinit cb710_probe(struct pci_dev *pdev,
+ const struct pci_device_id *ent)
+{
+ struct cb710_chip *chip;
+ unsigned long flags;
+ u32 val;
+ int err;
+ int n = 0;
+
+ err = cb710_pci_configure(pdev);
+ if (err)
+ return err;
+
+ /* this is actually magic... */
+ pci_read_config_dword(pdev, 0x48, &val);
+ if (!(val & 0x80000000)) {
+ pci_write_config_dword(pdev, 0x48, val|0x71000000);
+ pci_read_config_dword(pdev, 0x48, &val);
+ }
+
+ dev_dbg(&pdev->dev, "PCI config[0x48] = 0x%08X\n", val);
+ if (!(val & 0x70000000))
+ return -ENODEV;
+ val = (val >> 28) & 7;
+ if (val & CB710_SLOT_MMC)
+ ++n;
+ if (val & CB710_SLOT_MS)
+ ++n;
+ if (val & CB710_SLOT_SM)
+ ++n;
+
+ chip = devm_kzalloc(&pdev->dev,
+ sizeof(*chip) + n * sizeof(*chip->slot), GFP_KERNEL);
+ if (!chip)
+ return -ENOMEM;
+
+ err = pcim_enable_device(pdev);
+ if (err)
+ return err;
+
+ err = pcim_iomap_regions(pdev, 0x0001, KBUILD_MODNAME);
+ if (err)
+ return err;
+
+ chip->pdev = pdev;
+ chip->iobase = pcim_iomap_table(pdev)[0];
+
+ pci_set_drvdata(pdev, chip);
+
+ err = devm_request_irq(&pdev->dev, pdev->irq,
+ cb710_irq_handler, IRQF_SHARED, KBUILD_MODNAME, chip);
+ if (err)
+ return err;
+
+ do {
+ if (!ida_pre_get(&cb710_ida, GFP_KERNEL))
+ return -ENOMEM;
+
+ spin_lock_irqsave(&cb710_ida_lock, flags);
+ err = ida_get_new(&cb710_ida, &chip->platform_id);
+ spin_unlock_irqrestore(&cb710_ida_lock, flags);
+
+ if (err && err != -EAGAIN)
+ return err;
+ } while (err);
+
+
+ dev_info(&pdev->dev, "id %d, IO 0x%p, IRQ %d\n",
+ chip->platform_id, chip->iobase, pdev->irq);
+
+ if (val & CB710_SLOT_MMC) { /* MMC/SD slot */
+ err = cb710_register_slot(chip,
+ CB710_SLOT_MMC, 0x00, "cb710-mmc");
+ if (err)
+ return err;
+ }
+
+ if (val & CB710_SLOT_MS) { /* MemoryStick slot */
+ err = cb710_register_slot(chip,
+ CB710_SLOT_MS, 0x40, "cb710-ms");
+ if (err)
+ goto unreg_mmc;
+ }
+
+ if (val & CB710_SLOT_SM) { /* SmartMedia slot */
+ err = cb710_register_slot(chip,
+ CB710_SLOT_SM, 0x60, "cb710-sm");
+ if (err)
+ goto unreg_ms;
+ }
+
+ return 0;
+unreg_ms:
+ cb710_unregister_slot(chip, CB710_SLOT_MS);
+unreg_mmc:
+ cb710_unregister_slot(chip, CB710_SLOT_MMC);
+
+#ifdef CONFIG_CB710_DEBUG_ASSUMPTIONS
+ BUG_ON(atomic_read(&chip->slot_refs_count) != 0);
+#endif
+ return err;
+}
+
+static void __devexit cb710_remove_one(struct pci_dev *pdev)
+{
+ struct cb710_chip *chip = pci_get_drvdata(pdev);
+ unsigned long flags;
+
+ cb710_unregister_slot(chip, CB710_SLOT_SM);
+ cb710_unregister_slot(chip, CB710_SLOT_MS);
+ cb710_unregister_slot(chip, CB710_SLOT_MMC);
+#ifdef CONFIG_CB710_DEBUG_ASSUMPTIONS
+ BUG_ON(atomic_read(&chip->slot_refs_count) != 0);
+#endif
+
+ spin_lock_irqsave(&cb710_ida_lock, flags);
+ ida_remove(&cb710_ida, chip->platform_id);
+ spin_unlock_irqrestore(&cb710_ida_lock, flags);
+}
+
+static const struct pci_device_id cb710_pci_tbl[] = {
+ { PCI_VENDOR_ID_ENE, PCI_DEVICE_ID_ENE_CB710_FLASH,
+ PCI_ANY_ID, PCI_ANY_ID, },
+ { 0, }
+};
+
+static struct pci_driver cb710_driver = {
+ .name = KBUILD_MODNAME,
+ .id_table = cb710_pci_tbl,
+ .probe = cb710_probe,
+ .remove = __devexit_p(cb710_remove_one),
+#ifdef CONFIG_PM
+ .suspend = cb710_suspend,
+ .resume = cb710_resume,
+#endif
+};
+
+static int __init cb710_init_module(void)
+{
+ return pci_register_driver(&cb710_driver);
+}
+
+static void __exit cb710_cleanup_module(void)
+{
+ pci_unregister_driver(&cb710_driver);
+ ida_destroy(&cb710_ida);
+}
+
+module_init(cb710_init_module);
+module_exit(cb710_cleanup_module);
+
+MODULE_AUTHOR("Michał Mirosław <mirq-linux@rere.qmqm.pl>");
+MODULE_DESCRIPTION("ENE CB710 memory card reader driver");
+MODULE_LICENSE("GPL");
+MODULE_DEVICE_TABLE(pci, cb710_pci_tbl);
diff --git a/drivers/misc/cb710/debug.c b/drivers/misc/cb710/debug.c
new file mode 100644
index 000000000000..02358d086e03
--- /dev/null
+++ b/drivers/misc/cb710/debug.c
@@ -0,0 +1,119 @@
+/*
+ * cb710/debug.c
+ *
+ * Copyright by Michał Mirosław, 2008-2009
+ *
+ * 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/cb710.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+
+#define CB710_REG_COUNT 0x80
+
+static const u16 allow[CB710_REG_COUNT/16] = {
+ 0xFFF0, 0xFFFF, 0xFFFF, 0xFFFF,
+ 0xFFF0, 0xFFFF, 0xFFFF, 0xFFFF,
+};
+static const char *const prefix[ARRAY_SIZE(allow)] = {
+ "MMC", "MMC", "MMC", "MMC",
+ "MS?", "MS?", "SM?", "SM?"
+};
+
+static inline int allow_reg_read(unsigned block, unsigned offset, unsigned bits)
+{
+ unsigned mask = (1 << bits/8) - 1;
+ offset *= bits/8;
+ return ((allow[block] >> offset) & mask) == mask;
+}
+
+#define CB710_READ_REGS_TEMPLATE(t) \
+static void cb710_read_regs_##t(void __iomem *iobase, \
+ u##t *reg, unsigned select) \
+{ \
+ unsigned i, j; \
+ \
+ for (i = 0; i < ARRAY_SIZE(allow); ++i, reg += 16/(t/8)) { \
+ if (!(select & (1 << i))) \
+ continue; \
+ \
+ for (j = 0; j < 0x10/(t/8); ++j) { \
+ if (!allow_reg_read(i, j, t)) \
+ continue; \
+ reg[j] = ioread##t(iobase \
+ + (i << 4) + (j * (t/8))); \
+ } \
+ } \
+}
+
+static const char cb710_regf_8[] = "%02X";
+static const char cb710_regf_16[] = "%04X";
+static const char cb710_regf_32[] = "%08X";
+static const char cb710_xes[] = "xxxxxxxx";
+
+#define CB710_DUMP_REGS_TEMPLATE(t) \
+static void cb710_dump_regs_##t(struct device *dev, \
+ const u##t *reg, unsigned select) \
+{ \
+ const char *const xp = &cb710_xes[8 - t/4]; \
+ const char *const format = cb710_regf_##t; \
+ \
+ char msg[100], *p; \
+ unsigned i, j; \
+ \
+ for (i = 0; i < ARRAY_SIZE(allow); ++i, reg += 16/(t/8)) { \
+ if (!(select & (1 << i))) \
+ continue; \
+ p = msg; \
+ for (j = 0; j < 0x10/(t/8); ++j) { \
+ *p++ = ' '; \
+ if (j == 8/(t/8)) \
+ *p++ = ' '; \
+ if (allow_reg_read(i, j, t)) \
+ p += sprintf(p, format, reg[j]); \
+ else \
+ p += sprintf(p, "%s", xp); \
+ } \
+ dev_dbg(dev, "%s 0x%02X %s\n", prefix[i], i << 4, msg); \
+ } \
+}
+
+#define CB710_READ_AND_DUMP_REGS_TEMPLATE(t) \
+static void cb710_read_and_dump_regs_##t(struct cb710_chip *chip, \
+ unsigned select) \
+{ \
+ u##t regs[CB710_REG_COUNT/sizeof(u##t)]; \
+ \
+ memset(&regs, 0, sizeof(regs)); \
+ cb710_read_regs_##t(chip->iobase, regs, select); \
+ cb710_dump_regs_##t(cb710_chip_dev(chip), regs, select); \
+}
+
+#define CB710_REG_ACCESS_TEMPLATES(t) \
+ CB710_READ_REGS_TEMPLATE(t) \
+ CB710_DUMP_REGS_TEMPLATE(t) \
+ CB710_READ_AND_DUMP_REGS_TEMPLATE(t)
+
+CB710_REG_ACCESS_TEMPLATES(8)
+CB710_REG_ACCESS_TEMPLATES(16)
+CB710_REG_ACCESS_TEMPLATES(32)
+
+void cb710_dump_regs(struct cb710_chip *chip, unsigned select)
+{
+ if (!(select & CB710_DUMP_REGS_MASK))
+ select = CB710_DUMP_REGS_ALL;
+ if (!(select & CB710_DUMP_ACCESS_MASK))
+ select |= CB710_DUMP_ACCESS_8;
+
+ if (select & CB710_DUMP_ACCESS_32)
+ cb710_read_and_dump_regs_32(chip, select);
+ if (select & CB710_DUMP_ACCESS_16)
+ cb710_read_and_dump_regs_16(chip, select);
+ if (select & CB710_DUMP_ACCESS_8)
+ cb710_read_and_dump_regs_8(chip, select);
+}
+EXPORT_SYMBOL_GPL(cb710_dump_regs);
+
diff --git a/drivers/misc/cb710/sgbuf2.c b/drivers/misc/cb710/sgbuf2.c
new file mode 100644
index 000000000000..d38a7acdb6ec
--- /dev/null
+++ b/drivers/misc/cb710/sgbuf2.c
@@ -0,0 +1,150 @@
+/*
+ * cb710/sgbuf2.c
+ *
+ * Copyright by Michał Mirosław, 2008-2009
+ *
+ * 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/module.h>
+#include <linux/cb710.h>
+
+static bool sg_dwiter_next(struct sg_mapping_iter *miter)
+{
+ if (sg_miter_next(miter)) {
+ miter->consumed = 0;
+ return true;
+ } else
+ return false;
+}
+
+static bool sg_dwiter_is_at_end(struct sg_mapping_iter *miter)
+{
+ return miter->length == miter->consumed && !sg_dwiter_next(miter);
+}
+
+static uint32_t sg_dwiter_read_buffer(struct sg_mapping_iter *miter)
+{
+ size_t len, left = 4;
+ uint32_t data;
+ void *addr = &data;
+
+ do {
+ len = min(miter->length - miter->consumed, left);
+ memcpy(addr, miter->addr + miter->consumed, len);
+ miter->consumed += len;
+ left -= len;
+ if (!left)
+ return data;
+ addr += len;
+ } while (sg_dwiter_next(miter));
+
+ memset(addr, 0, left);
+ return data;
+}
+
+static inline bool needs_unaligned_copy(const void *ptr)
+{
+#ifdef HAVE_EFFICIENT_UNALIGNED_ACCESS
+ return false;
+#else
+ return ((ptr - NULL) & 3) != 0;
+#endif
+}
+
+static bool sg_dwiter_get_next_block(struct sg_mapping_iter *miter, uint32_t **ptr)
+{
+ size_t len;
+
+ if (sg_dwiter_is_at_end(miter))
+ return true;
+
+ len = miter->length - miter->consumed;
+
+ if (likely(len >= 4 && !needs_unaligned_copy(
+ miter->addr + miter->consumed))) {
+ *ptr = miter->addr + miter->consumed;
+ miter->consumed += 4;
+ return true;
+ }
+
+ return false;
+}
+
+/**
+ * cb710_sg_dwiter_read_next_block() - get next 32-bit word from sg buffer
+ * @miter: sg mapping iterator used for reading
+ *
+ * Description:
+ * Returns 32-bit word starting at byte pointed to by @miter@
+ * handling any alignment issues. Bytes past the buffer's end
+ * are not accessed (read) but are returned as zeroes. @miter@
+ * is advanced by 4 bytes or to the end of buffer whichever is
+ * closer.
+ *
+ * Context:
+ * Same requirements as in sg_miter_next().
+ *
+ * Returns:
+ * 32-bit word just read.
+ */
+uint32_t cb710_sg_dwiter_read_next_block(struct sg_mapping_iter *miter)
+{
+ uint32_t *ptr = NULL;
+
+ if (likely(sg_dwiter_get_next_block(miter, &ptr)))
+ return ptr ? *ptr : 0;
+
+ return sg_dwiter_read_buffer(miter);
+}
+EXPORT_SYMBOL_GPL(cb710_sg_dwiter_read_next_block);
+
+static void sg_dwiter_write_slow(struct sg_mapping_iter *miter, uint32_t data)
+{
+ size_t len, left = 4;
+ void *addr = &data;
+
+ do {
+ len = min(miter->length - miter->consumed, left);
+ memcpy(miter->addr, addr, len);
+ miter->consumed += len;
+ left -= len;
+ if (!left)
+ return;
+ addr += len;
+ flush_kernel_dcache_page(miter->page);
+ } while (sg_dwiter_next(miter));
+}
+
+/**
+ * cb710_sg_dwiter_write_next_block() - write next 32-bit word to sg buffer
+ * @miter: sg mapping iterator used for writing
+ *
+ * Description:
+ * Writes 32-bit word starting at byte pointed to by @miter@
+ * handling any alignment issues. Bytes which would be written
+ * past the buffer's end are silently discarded. @miter@ is
+ * advanced by 4 bytes or to the end of buffer whichever is closer.
+ *
+ * Context:
+ * Same requirements as in sg_miter_next().
+ */
+void cb710_sg_dwiter_write_next_block(struct sg_mapping_iter *miter, uint32_t data)
+{
+ uint32_t *ptr = NULL;
+
+ if (likely(sg_dwiter_get_next_block(miter, &ptr))) {
+ if (ptr)
+ *ptr = data;
+ else
+ return;
+ } else
+ sg_dwiter_write_slow(miter, data);
+
+ if (miter->length == miter->consumed)
+ flush_kernel_dcache_page(miter->page);
+}
+EXPORT_SYMBOL_GPL(cb710_sg_dwiter_write_next_block);
+
diff --git a/drivers/misc/eeprom/Kconfig b/drivers/misc/eeprom/Kconfig
index 89fec052f3b4..9118613af321 100644
--- a/drivers/misc/eeprom/Kconfig
+++ b/drivers/misc/eeprom/Kconfig
@@ -48,6 +48,20 @@ config EEPROM_LEGACY
This driver can also be built as a module. If so, the module
will be called eeprom.
+config EEPROM_MAX6875
+ tristate "Maxim MAX6874/5 power supply supervisor"
+ depends on I2C && EXPERIMENTAL
+ help
+ If you say yes here you get read-only support for the user EEPROM of
+ the Maxim MAX6874/5 EEPROM-programmable, quad power-supply
+ sequencer/supervisor.
+
+ All other features of this chip should be accessed via i2c-dev.
+
+ This driver can also be built as a module. If so, the module
+ will be called max6875.
+
+
config EEPROM_93CX6
tristate "EEPROM 93CX6 support"
help
diff --git a/drivers/misc/eeprom/Makefile b/drivers/misc/eeprom/Makefile
index 539dd8f88128..df3d68ffa9d1 100644
--- a/drivers/misc/eeprom/Makefile
+++ b/drivers/misc/eeprom/Makefile
@@ -1,4 +1,5 @@
obj-$(CONFIG_EEPROM_AT24) += at24.o
obj-$(CONFIG_EEPROM_AT25) += at25.o
obj-$(CONFIG_EEPROM_LEGACY) += eeprom.o
+obj-$(CONFIG_EEPROM_MAX6875) += max6875.o
obj-$(CONFIG_EEPROM_93CX6) += eeprom_93cx6.o
diff --git a/drivers/misc/eeprom/max6875.c b/drivers/misc/eeprom/max6875.c
new file mode 100644
index 000000000000..3c0c58eed347
--- /dev/null
+++ b/drivers/misc/eeprom/max6875.c
@@ -0,0 +1,246 @@
+/*
+ max6875.c - driver for MAX6874/MAX6875
+
+ Copyright (C) 2005 Ben Gardner <bgardner@wabtec.com>
+
+ Based on eeprom.c
+
+ The MAX6875 has a bank of registers and two banks of EEPROM.
+ Address ranges are defined as follows:
+ * 0x0000 - 0x0046 = configuration registers
+ * 0x8000 - 0x8046 = configuration EEPROM
+ * 0x8100 - 0x82FF = user EEPROM
+
+ This driver makes the user EEPROM available for read.
+
+ The registers & config EEPROM should be accessed via i2c-dev.
+
+ The MAX6875 ignores the lowest address bit, so each chip responds to
+ two addresses - 0x50/0x51 and 0x52/0x53.
+
+ Note that the MAX6875 uses i2c_smbus_write_byte_data() to set the read
+ address, so this driver is destructive if loaded for the wrong EEPROM chip.
+
+ 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; version 2 of the License.
+*/
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/mutex.h>
+
+/* Do not scan - the MAX6875 access method will write to some EEPROM chips */
+static const unsigned short normal_i2c[] = { I2C_CLIENT_END };
+
+/* Insmod parameters */
+I2C_CLIENT_INSMOD_1(max6875);
+
+/* The MAX6875 can only read/write 16 bytes at a time */
+#define SLICE_SIZE 16
+#define SLICE_BITS 4
+
+/* USER EEPROM is at addresses 0x8100 - 0x82FF */
+#define USER_EEPROM_BASE 0x8100
+#define USER_EEPROM_SIZE 0x0200
+#define USER_EEPROM_SLICES 32
+
+/* MAX6875 commands */
+#define MAX6875_CMD_BLK_READ 0x84
+
+/* Each client has this additional data */
+struct max6875_data {
+ struct i2c_client *fake_client;
+ struct mutex update_lock;
+
+ u32 valid;
+ u8 data[USER_EEPROM_SIZE];
+ unsigned long last_updated[USER_EEPROM_SLICES];
+};
+
+static void max6875_update_slice(struct i2c_client *client, int slice)
+{
+ struct max6875_data *data = i2c_get_clientdata(client);
+ int i, j, addr;
+ u8 *buf;
+
+ if (slice >= USER_EEPROM_SLICES)
+ return;
+
+ mutex_lock(&data->update_lock);
+
+ buf = &data->data[slice << SLICE_BITS];
+
+ if (!(data->valid & (1 << slice)) ||
+ time_after(jiffies, data->last_updated[slice])) {
+
+ dev_dbg(&client->dev, "Starting update of slice %u\n", slice);
+
+ data->valid &= ~(1 << slice);
+
+ addr = USER_EEPROM_BASE + (slice << SLICE_BITS);
+
+ /* select the eeprom address */
+ if (i2c_smbus_write_byte_data(client, addr >> 8, addr & 0xFF)) {
+ dev_err(&client->dev, "address set failed\n");
+ goto exit_up;
+ }
+
+ if (i2c_check_functionality(client->adapter,
+ I2C_FUNC_SMBUS_READ_I2C_BLOCK)) {
+ if (i2c_smbus_read_i2c_block_data(client,
+ MAX6875_CMD_BLK_READ,
+ SLICE_SIZE,
+ buf) != SLICE_SIZE) {
+ goto exit_up;
+ }
+ } else {
+ for (i = 0; i < SLICE_SIZE; i++) {
+ j = i2c_smbus_read_byte(client);
+ if (j < 0) {
+ goto exit_up;
+ }
+ buf[i] = j;
+ }
+ }
+ data->last_updated[slice] = jiffies;
+ data->valid |= (1 << slice);
+ }
+exit_up:
+ mutex_unlock(&data->update_lock);
+}
+
+static ssize_t max6875_read(struct kobject *kobj,
+ struct bin_attribute *bin_attr,
+ char *buf, loff_t off, size_t count)
+{
+ struct i2c_client *client = kobj_to_i2c_client(kobj);
+ struct max6875_data *data = i2c_get_clientdata(client);
+ int slice, max_slice;
+
+ if (off > USER_EEPROM_SIZE)
+ return 0;
+
+ if (off + count > USER_EEPROM_SIZE)
+ count = USER_EEPROM_SIZE - off;
+
+ /* refresh slices which contain requested bytes */
+ max_slice = (off + count - 1) >> SLICE_BITS;
+ for (slice = (off >> SLICE_BITS); slice <= max_slice; slice++)
+ max6875_update_slice(client, slice);
+
+ memcpy(buf, &data->data[off], count);
+
+ return count;
+}
+
+static struct bin_attribute user_eeprom_attr = {
+ .attr = {
+ .name = "eeprom",
+ .mode = S_IRUGO,
+ },
+ .size = USER_EEPROM_SIZE,
+ .read = max6875_read,
+};
+
+/* Return 0 if detection is successful, -ENODEV otherwise */
+static int max6875_detect(struct i2c_client *client, int kind,
+ struct i2c_board_info *info)
+{
+ struct i2c_adapter *adapter = client->adapter;
+
+ if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_WRITE_BYTE_DATA
+ | I2C_FUNC_SMBUS_READ_BYTE))
+ return -ENODEV;
+
+ /* Only check even addresses */
+ if (client->addr & 1)
+ return -ENODEV;
+
+ strlcpy(info->type, "max6875", I2C_NAME_SIZE);
+
+ return 0;
+}
+
+static int max6875_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct max6875_data *data;
+ int err;
+
+ if (!(data = kzalloc(sizeof(struct max6875_data), GFP_KERNEL)))
+ return -ENOMEM;
+
+ /* A fake client is created on the odd address */
+ data->fake_client = i2c_new_dummy(client->adapter, client->addr + 1);
+ if (!data->fake_client) {
+ err = -ENOMEM;
+ goto exit_kfree;
+ }
+
+ /* Init real i2c_client */
+ i2c_set_clientdata(client, data);
+ mutex_init(&data->update_lock);
+
+ err = sysfs_create_bin_file(&client->dev.kobj, &user_eeprom_attr);
+ if (err)
+ goto exit_remove_fake;
+
+ return 0;
+
+exit_remove_fake:
+ i2c_unregister_device(data->fake_client);
+exit_kfree:
+ kfree(data);
+ return err;
+}
+
+static int max6875_remove(struct i2c_client *client)
+{
+ struct max6875_data *data = i2c_get_clientdata(client);
+
+ i2c_unregister_device(data->fake_client);
+
+ sysfs_remove_bin_file(&client->dev.kobj, &user_eeprom_attr);
+ kfree(data);
+
+ return 0;
+}
+
+static const struct i2c_device_id max6875_id[] = {
+ { "max6875", 0 },
+ { }
+};
+
+static struct i2c_driver max6875_driver = {
+ .driver = {
+ .name = "max6875",
+ },
+ .probe = max6875_probe,
+ .remove = max6875_remove,
+ .id_table = max6875_id,
+
+ .detect = max6875_detect,
+ .address_data = &addr_data,
+};
+
+static int __init max6875_init(void)
+{
+ return i2c_add_driver(&max6875_driver);
+}
+
+static void __exit max6875_exit(void)
+{
+ i2c_del_driver(&max6875_driver);
+}
+
+
+MODULE_AUTHOR("Ben Gardner <bgardner@wabtec.com>");
+MODULE_DESCRIPTION("MAX6875 driver");
+MODULE_LICENSE("GPL");
+
+module_init(max6875_init);
+module_exit(max6875_exit);
diff --git a/drivers/misc/sgi-gru/grufile.c b/drivers/misc/sgi-gru/grufile.c
index bbefe77c67a9..3ce2920e2bf3 100644
--- a/drivers/misc/sgi-gru/grufile.c
+++ b/drivers/misc/sgi-gru/grufile.c
@@ -302,7 +302,7 @@ static int gru_init_tables(unsigned long gru_base_paddr, void *gru_base_vaddr)
pnode = uv_node_to_pnode(nid);
if (bid < 0 || gru_base[bid])
continue;
- page = alloc_pages_node(nid, GFP_KERNEL, order);
+ page = alloc_pages_exact_node(nid, GFP_KERNEL, order);
if (!page)
goto fail;
gru_base[bid] = page_address(page);
diff --git a/drivers/misc/sgi-xp/xpc_uv.c b/drivers/misc/sgi-xp/xpc_uv.c
index 9172fcdee4e2..c76677afda1b 100644
--- a/drivers/misc/sgi-xp/xpc_uv.c
+++ b/drivers/misc/sgi-xp/xpc_uv.c
@@ -232,7 +232,7 @@ xpc_create_gru_mq_uv(unsigned int mq_size, int cpu, char *irq_name,
mq->mmr_blade = uv_cpu_to_blade_id(cpu);
nid = cpu_to_node(cpu);
- page = alloc_pages_node(nid, GFP_KERNEL | __GFP_ZERO | GFP_THISNODE,
+ page = alloc_pages_exact_node(nid, GFP_KERNEL | __GFP_ZERO | GFP_THISNODE,
pg_order);
if (page == NULL) {
dev_err(xpc_part, "xpc_create_gru_mq_uv() failed to alloc %d "
diff --git a/drivers/misc/sgi-xp/xpnet.c b/drivers/misc/sgi-xp/xpnet.c
index 6faefcffcb53..8d1c60a3f0df 100644
--- a/drivers/misc/sgi-xp/xpnet.c
+++ b/drivers/misc/sgi-xp/xpnet.c
@@ -450,7 +450,8 @@ xpnet_dev_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
"packet\n", sizeof(struct xpnet_pending_msg));
dev->stats.tx_errors++;
- return -ENOMEM;
+ dev_kfree_skb(skb);
+ return NETDEV_TX_OK;
}
/* get the beginning of the first cacheline and end of last */