diff options
Diffstat (limited to 'drivers')
387 files changed, 16925 insertions, 9267 deletions
diff --git a/drivers/Kconfig b/drivers/Kconfig index 202fa6d051b9..78a956e286e6 100644 --- a/drivers/Kconfig +++ b/drivers/Kconfig @@ -52,6 +52,8 @@ source "drivers/i2c/Kconfig" source "drivers/spi/Kconfig" +source "drivers/ssbi/Kconfig" + source "drivers/hsi/Kconfig" source "drivers/pps/Kconfig" diff --git a/drivers/Makefile b/drivers/Makefile index dce39a95fa71..4865ed24708a 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -114,6 +114,7 @@ obj-y += firmware/ obj-$(CONFIG_CRYPTO) += crypto/ obj-$(CONFIG_SUPERH) += sh/ obj-$(CONFIG_ARCH_SHMOBILE) += sh/ +obj-$(CONFIG_SSBI) += ssbi/ ifndef CONFIG_ARCH_USES_GETTIMEOFFSET obj-y += clocksource/ endif diff --git a/drivers/acpi/pci_root.c b/drivers/acpi/pci_root.c index 6ae5e440436e..ac8688b89705 100644 --- a/drivers/acpi/pci_root.c +++ b/drivers/acpi/pci_root.c @@ -65,44 +65,12 @@ static struct acpi_scan_handler pci_root_handler = { .detach = acpi_pci_root_remove, }; -/* Lock to protect both acpi_pci_roots and acpi_pci_drivers lists */ +/* Lock to protect both acpi_pci_roots lists */ static DEFINE_MUTEX(acpi_pci_root_lock); static LIST_HEAD(acpi_pci_roots); -static LIST_HEAD(acpi_pci_drivers); static DEFINE_MUTEX(osc_lock); -int acpi_pci_register_driver(struct acpi_pci_driver *driver) -{ - int n = 0; - struct acpi_pci_root *root; - - mutex_lock(&acpi_pci_root_lock); - list_add_tail(&driver->node, &acpi_pci_drivers); - if (driver->add) - list_for_each_entry(root, &acpi_pci_roots, node) { - driver->add(root); - n++; - } - mutex_unlock(&acpi_pci_root_lock); - - return n; -} -EXPORT_SYMBOL(acpi_pci_register_driver); - -void acpi_pci_unregister_driver(struct acpi_pci_driver *driver) -{ - struct acpi_pci_root *root; - - mutex_lock(&acpi_pci_root_lock); - list_del(&driver->node); - if (driver->remove) - list_for_each_entry(root, &acpi_pci_roots, node) - driver->remove(root); - mutex_unlock(&acpi_pci_root_lock); -} -EXPORT_SYMBOL(acpi_pci_unregister_driver); - /** * acpi_is_root_bridge - determine whether an ACPI CA node is a PCI root bridge * @handle - the ACPI CA node in question. @@ -413,7 +381,6 @@ static int acpi_pci_root_add(struct acpi_device *device, acpi_status status; int result; struct acpi_pci_root *root; - struct acpi_pci_driver *driver; u32 flags, base_flags; root = kzalloc(sizeof(struct acpi_pci_root), GFP_KERNEL); @@ -571,12 +538,6 @@ static int acpi_pci_root_add(struct acpi_device *device, pci_assign_unassigned_bus_resources(root->bus); } - mutex_lock(&acpi_pci_root_lock); - list_for_each_entry(driver, &acpi_pci_drivers, node) - if (driver->add) - driver->add(root); - mutex_unlock(&acpi_pci_root_lock); - /* need to after hot-added ioapic is registered */ if (system_state != SYSTEM_BOOTING) pci_enable_bridges(root->bus); @@ -597,16 +558,9 @@ end: static void acpi_pci_root_remove(struct acpi_device *device) { struct acpi_pci_root *root = acpi_driver_data(device); - struct acpi_pci_driver *driver; pci_stop_root_bus(root->bus); - mutex_lock(&acpi_pci_root_lock); - list_for_each_entry_reverse(driver, &acpi_pci_drivers, node) - if (driver->remove) - driver->remove(root); - mutex_unlock(&acpi_pci_root_lock); - device_set_run_wake(root->bus->bridge, false); pci_acpi_remove_bus_pm_notifier(device); diff --git a/drivers/acpi/pci_slot.c b/drivers/acpi/pci_slot.c index cd1434eb1de8..033d1179bdb5 100644 --- a/drivers/acpi/pci_slot.c +++ b/drivers/acpi/pci_slot.c @@ -9,6 +9,9 @@ * Copyright (C) 2007-2008 Hewlett-Packard Development Company, L.P. * Alex Chiang <achiang@hp.com> * + * Copyright (C) 2013 Huawei Tech. Co., Ltd. + * Jiang Liu <jiang.liu@huawei.com> + * * 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. @@ -28,10 +31,9 @@ #include <linux/init.h> #include <linux/slab.h> #include <linux/types.h> +#include <linux/list.h> #include <linux/pci.h> #include <linux/acpi.h> -#include <acpi/acpi_bus.h> -#include <acpi/acpi_drivers.h> #include <linux/dmi.h> static bool debug; @@ -61,20 +63,12 @@ ACPI_MODULE_NAME("pci_slot"); #define SLOT_NAME_SIZE 21 /* Inspired by #define in acpiphp.h */ struct acpi_pci_slot { - acpi_handle root_handle; /* handle of the root bridge */ struct pci_slot *pci_slot; /* corresponding pci_slot */ struct list_head list; /* node in the list of slots */ }; -static int acpi_pci_slot_add(struct acpi_pci_root *root); -static void acpi_pci_slot_remove(struct acpi_pci_root *root); - static LIST_HEAD(slot_list); static DEFINE_MUTEX(slot_list_lock); -static struct acpi_pci_driver acpi_pci_slot_driver = { - .add = acpi_pci_slot_add, - .remove = acpi_pci_slot_remove, -}; static int check_slot(acpi_handle handle, unsigned long long *sun) @@ -113,21 +107,8 @@ out: return device; } -struct callback_args { - acpi_walk_callback user_function; /* only for walk_p2p_bridge */ - struct pci_bus *pci_bus; - acpi_handle root_handle; -}; - /* - * register_slot - * - * Called once for each SxFy object in the namespace. Don't worry about - * calling pci_create_slot multiple times for the same pci_bus:device, - * since each subsequent call simply bumps the refcount on the pci_slot. - * - * The number of calls to pci_destroy_slot from unregister_slot is - * symmetrical. + * Check whether handle has an associated slot and create PCI slot if it has. */ static acpi_status register_slot(acpi_handle handle, u32 lvl, void *context, void **rv) @@ -137,13 +118,22 @@ register_slot(acpi_handle handle, u32 lvl, void *context, void **rv) char name[SLOT_NAME_SIZE]; struct acpi_pci_slot *slot; struct pci_slot *pci_slot; - struct callback_args *parent_context = context; - struct pci_bus *pci_bus = parent_context->pci_bus; + struct pci_bus *pci_bus = context; device = check_slot(handle, &sun); if (device < 0) return AE_OK; + /* + * There may be multiple PCI functions associated with the same slot. + * Check whether PCI slot has already been created for this PCI device. + */ + list_for_each_entry(slot, &slot_list, list) { + pci_slot = slot->pci_slot; + if (pci_slot->bus == pci_bus && pci_slot->number == device) + return AE_OK; + } + slot = kmalloc(sizeof(*slot), GFP_KERNEL); if (!slot) { err("%s: cannot allocate memory\n", __func__); @@ -158,12 +148,8 @@ register_slot(acpi_handle handle, u32 lvl, void *context, void **rv) return AE_OK; } - slot->root_handle = parent_context->root_handle; slot->pci_slot = pci_slot; - INIT_LIST_HEAD(&slot->list); - mutex_lock(&slot_list_lock); list_add(&slot->list, &slot_list); - mutex_unlock(&slot_list_lock); get_device(&pci_bus->dev); @@ -173,131 +159,24 @@ register_slot(acpi_handle handle, u32 lvl, void *context, void **rv) return AE_OK; } -/* - * walk_p2p_bridge - discover and walk p2p bridges - * @handle: points to an acpi_pci_root - * @context: p2p_bridge_context pointer - * - * Note that when we call ourselves recursively, we pass a different - * value of pci_bus in the child_context. - */ -static acpi_status -walk_p2p_bridge(acpi_handle handle, u32 lvl, void *context, void **rv) -{ - int device, function; - unsigned long long adr; - acpi_status status; - acpi_handle dummy_handle; - acpi_walk_callback user_function; - - struct pci_dev *dev; - struct pci_bus *pci_bus; - struct callback_args child_context; - struct callback_args *parent_context = context; - - pci_bus = parent_context->pci_bus; - user_function = parent_context->user_function; - - status = acpi_get_handle(handle, "_ADR", &dummy_handle); - if (ACPI_FAILURE(status)) - return AE_OK; - - status = acpi_evaluate_integer(handle, "_ADR", NULL, &adr); - if (ACPI_FAILURE(status)) - return AE_OK; - - device = (adr >> 16) & 0xffff; - function = adr & 0xffff; - - dev = pci_get_slot(pci_bus, PCI_DEVFN(device, function)); - if (!dev || !dev->subordinate) - goto out; - - child_context.pci_bus = dev->subordinate; - child_context.user_function = user_function; - child_context.root_handle = parent_context->root_handle; - - dbg("p2p bridge walk, pci_bus = %x\n", dev->subordinate->number); - status = acpi_walk_namespace(ACPI_TYPE_DEVICE, handle, (u32)1, - user_function, NULL, &child_context, NULL); - if (ACPI_FAILURE(status)) - goto out; - - status = acpi_walk_namespace(ACPI_TYPE_DEVICE, handle, (u32)1, - walk_p2p_bridge, NULL, &child_context, NULL); -out: - pci_dev_put(dev); - return AE_OK; -} - -/* - * walk_root_bridge - generic root bridge walker - * @root: poiner of an acpi_pci_root - * @user_function: user callback for slot objects - * - * Call user_function for all objects underneath this root bridge. - * Walk p2p bridges underneath us and call user_function on those too. - */ -static int -walk_root_bridge(struct acpi_pci_root *root, acpi_walk_callback user_function) -{ - acpi_status status; - acpi_handle handle = root->device->handle; - struct pci_bus *pci_bus = root->bus; - struct callback_args context; - - context.pci_bus = pci_bus; - context.user_function = user_function; - context.root_handle = handle; - - dbg("root bridge walk, pci_bus = %x\n", pci_bus->number); - status = acpi_walk_namespace(ACPI_TYPE_DEVICE, handle, (u32)1, - user_function, NULL, &context, NULL); - if (ACPI_FAILURE(status)) - return status; - - status = acpi_walk_namespace(ACPI_TYPE_DEVICE, handle, (u32)1, - walk_p2p_bridge, NULL, &context, NULL); - if (ACPI_FAILURE(status)) - err("%s: walk_p2p_bridge failure - %d\n", __func__, status); - - return status; -} - -/* - * acpi_pci_slot_add - * @handle: points to an acpi_pci_root - */ -static int -acpi_pci_slot_add(struct acpi_pci_root *root) +void acpi_pci_slot_enumerate(struct pci_bus *bus, acpi_handle handle) { - acpi_status status; - - status = walk_root_bridge(root, register_slot); - if (ACPI_FAILURE(status)) - err("%s: register_slot failure - %d\n", __func__, status); - - return status; + mutex_lock(&slot_list_lock); + acpi_walk_namespace(ACPI_TYPE_DEVICE, handle, 1, + register_slot, NULL, bus, NULL); + mutex_unlock(&slot_list_lock); } -/* - * acpi_pci_slot_remove - * @handle: points to an acpi_pci_root - */ -static void -acpi_pci_slot_remove(struct acpi_pci_root *root) +void acpi_pci_slot_remove(struct pci_bus *bus) { struct acpi_pci_slot *slot, *tmp; - struct pci_bus *pbus; - acpi_handle handle = root->device->handle; mutex_lock(&slot_list_lock); list_for_each_entry_safe(slot, tmp, &slot_list, list) { - if (slot->root_handle == handle) { + if (slot->pci_slot->bus == bus) { list_del(&slot->list); - pbus = slot->pci_slot->bus; pci_destroy_slot(slot->pci_slot); - put_device(&pbus->dev); + put_device(&bus->dev); kfree(slot); } } @@ -332,5 +211,4 @@ static struct dmi_system_id acpi_pci_slot_dmi_table[] __initdata = { void __init acpi_pci_slot_init(void) { dmi_check_system(acpi_pci_slot_dmi_table); - acpi_pci_register_driver(&acpi_pci_slot_driver); } diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index 5e7e991717d7..f54d1985e594 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -1790,7 +1790,6 @@ int __init acpi_scan_init(void) acpi_platform_init(); acpi_csrt_init(); acpi_container_init(); - acpi_pci_slot_init(); mutex_lock(&acpi_scan_lock); /* diff --git a/drivers/ata/ahci.c b/drivers/ata/ahci.c index 6a67b07de494..251e57d38942 100644 --- a/drivers/ata/ahci.c +++ b/drivers/ata/ahci.c @@ -415,17 +415,17 @@ static const struct pci_device_id ahci_pci_tbl[] = { /* Marvell */ { PCI_VDEVICE(MARVELL, 0x6145), board_ahci_mv }, /* 6145 */ { PCI_VDEVICE(MARVELL, 0x6121), board_ahci_mv }, /* 6121 */ - { PCI_DEVICE(0x1b4b, 0x9123), + { PCI_DEVICE(PCI_VENDOR_ID_MARVELL_EXT, 0x9123), .class = PCI_CLASS_STORAGE_SATA_AHCI, .class_mask = 0xffffff, .driver_data = board_ahci_yes_fbs }, /* 88se9128 */ - { PCI_DEVICE(0x1b4b, 0x9125), + { PCI_DEVICE(PCI_VENDOR_ID_MARVELL_EXT, 0x9125), .driver_data = board_ahci_yes_fbs }, /* 88se9125 */ - { PCI_DEVICE(0x1b4b, 0x917a), + { PCI_DEVICE(PCI_VENDOR_ID_MARVELL_EXT, 0x917a), .driver_data = board_ahci_yes_fbs }, /* 88se9172 */ - { PCI_DEVICE(0x1b4b, 0x9192), + { PCI_DEVICE(PCI_VENDOR_ID_MARVELL_EXT, 0x9192), .driver_data = board_ahci_yes_fbs }, /* 88se9172 on some Gigabyte */ - { PCI_DEVICE(0x1b4b, 0x91a3), + { PCI_DEVICE(PCI_VENDOR_ID_MARVELL_EXT, 0x91a3), .driver_data = board_ahci_yes_fbs }, /* Promise */ diff --git a/drivers/ata/pata_pcmcia.c b/drivers/ata/pata_pcmcia.c index 958238dda8fc..40254f4df584 100644 --- a/drivers/ata/pata_pcmcia.c +++ b/drivers/ata/pata_pcmcia.c @@ -387,21 +387,9 @@ static struct pcmcia_driver pcmcia_driver = { .probe = pcmcia_init_one, .remove = pcmcia_remove_one, }; - -static int __init pcmcia_init(void) -{ - return pcmcia_register_driver(&pcmcia_driver); -} - -static void __exit pcmcia_exit(void) -{ - pcmcia_unregister_driver(&pcmcia_driver); -} +module_pcmcia_driver(pcmcia_driver); MODULE_AUTHOR("Alan Cox"); MODULE_DESCRIPTION("low-level driver for PCMCIA ATA"); MODULE_LICENSE("GPL"); MODULE_VERSION(DRV_VERSION); - -module_init(pcmcia_init); -module_exit(pcmcia_exit); diff --git a/drivers/base/bus.c b/drivers/base/bus.c index 519865b53f76..8a00dec574d6 100644 --- a/drivers/base/bus.c +++ b/drivers/base/bus.c @@ -898,18 +898,18 @@ static ssize_t bus_uevent_store(struct bus_type *bus, static BUS_ATTR(uevent, S_IWUSR, NULL, bus_uevent_store); /** - * __bus_register - register a driver-core subsystem + * bus_register - register a driver-core subsystem * @bus: bus to register - * @key: lockdep class key * * Once we have that, we register the bus with the kobject * infrastructure, then register the children subsystems it has: * the devices and drivers that belong to the subsystem. */ -int __bus_register(struct bus_type *bus, struct lock_class_key *key) +int bus_register(struct bus_type *bus) { int retval; struct subsys_private *priv; + struct lock_class_key *key = &bus->lock_key; priv = kzalloc(sizeof(struct subsys_private), GFP_KERNEL); if (!priv) @@ -981,7 +981,7 @@ out: bus->p = NULL; return retval; } -EXPORT_SYMBOL_GPL(__bus_register); +EXPORT_SYMBOL_GPL(bus_register); /** * bus_unregister - remove a bus from the system diff --git a/drivers/base/core.c b/drivers/base/core.c index 56536f4b0f6b..f88d9e259a32 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -283,15 +283,21 @@ static int dev_uevent(struct kset *kset, struct kobject *kobj, const char *tmp; const char *name; umode_t mode = 0; + kuid_t uid = GLOBAL_ROOT_UID; + kgid_t gid = GLOBAL_ROOT_GID; add_uevent_var(env, "MAJOR=%u", MAJOR(dev->devt)); add_uevent_var(env, "MINOR=%u", MINOR(dev->devt)); - name = device_get_devnode(dev, &mode, &tmp); + name = device_get_devnode(dev, &mode, &uid, &gid, &tmp); if (name) { add_uevent_var(env, "DEVNAME=%s", name); - kfree(tmp); if (mode) add_uevent_var(env, "DEVMODE=%#o", mode & 0777); + if (!uid_eq(uid, GLOBAL_ROOT_UID)) + add_uevent_var(env, "DEVUID=%u", from_kuid(&init_user_ns, uid)); + if (!gid_eq(gid, GLOBAL_ROOT_GID)) + add_uevent_var(env, "DEVGID=%u", from_kgid(&init_user_ns, gid)); + kfree(tmp); } } @@ -563,8 +569,15 @@ int device_create_file(struct device *dev, const struct device_attribute *attr) { int error = 0; - if (dev) + + if (dev) { + WARN(((attr->attr.mode & S_IWUGO) && !attr->store), + "Write permission without 'store'\n"); + WARN(((attr->attr.mode & S_IRUGO) && !attr->show), + "Read permission without 'show'\n"); error = sysfs_create_file(&dev->kobj, &attr->attr); + } + return error; } @@ -1274,6 +1287,8 @@ static struct device *next_device(struct klist_iter *i) * device_get_devnode - path of device node file * @dev: device * @mode: returned file access mode + * @uid: returned file owner + * @gid: returned file group * @tmp: possibly allocated string * * Return the relative path of a possible device node. @@ -1282,7 +1297,8 @@ static struct device *next_device(struct klist_iter *i) * freed by the caller. */ const char *device_get_devnode(struct device *dev, - umode_t *mode, const char **tmp) + umode_t *mode, kuid_t *uid, kgid_t *gid, + const char **tmp) { char *s; @@ -1290,7 +1306,7 @@ const char *device_get_devnode(struct device *dev, /* the device type may provide a specific name */ if (dev->type && dev->type->devnode) - *tmp = dev->type->devnode(dev, mode); + *tmp = dev->type->devnode(dev, mode, uid, gid); if (*tmp) return *tmp; diff --git a/drivers/base/cpu.c b/drivers/base/cpu.c index fb10728f6372..d8c7f3ee6e19 100644 --- a/drivers/base/cpu.c +++ b/drivers/base/cpu.c @@ -132,6 +132,17 @@ static ssize_t show_crash_notes(struct device *dev, struct device_attribute *att return rc; } static DEVICE_ATTR(crash_notes, 0400, show_crash_notes, NULL); + +static ssize_t show_crash_notes_size(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + ssize_t rc; + + rc = sprintf(buf, "%zu\n", sizeof(note_buf_t)); + return rc; +} +static DEVICE_ATTR(crash_notes_size, 0400, show_crash_notes_size, NULL); #endif /* @@ -259,6 +270,9 @@ int __cpuinit register_cpu(struct cpu *cpu, int num) #ifdef CONFIG_KEXEC if (!error) error = device_create_file(&cpu->dev, &dev_attr_crash_notes); + if (!error) + error = device_create_file(&cpu->dev, + &dev_attr_crash_notes_size); #endif return error; } diff --git a/drivers/base/dd.c b/drivers/base/dd.c index bb5645ea0282..35fa36898916 100644 --- a/drivers/base/dd.c +++ b/drivers/base/dd.c @@ -380,7 +380,7 @@ int driver_probe_device(struct device_driver *drv, struct device *dev) pm_runtime_barrier(dev); ret = really_probe(dev, drv); - pm_runtime_idle(dev); + pm_request_idle(dev); return ret; } @@ -428,7 +428,7 @@ int device_attach(struct device *dev) } } else { ret = bus_for_each_drv(dev->bus, NULL, dev, __device_attach); - pm_runtime_idle(dev); + pm_request_idle(dev); } out_unlock: device_unlock(dev); @@ -499,7 +499,7 @@ static void __device_release_driver(struct device *dev) BUS_NOTIFY_UNBIND_DRIVER, dev); - pm_runtime_put_sync(dev); + pm_runtime_put(dev); if (dev->bus && dev->bus->remove) dev->bus->remove(dev); diff --git a/drivers/base/devtmpfs.c b/drivers/base/devtmpfs.c index 01fc5b07f951..7413d065906b 100644 --- a/drivers/base/devtmpfs.c +++ b/drivers/base/devtmpfs.c @@ -24,6 +24,7 @@ #include <linux/sched.h> #include <linux/slab.h> #include <linux/kthread.h> +#include "base.h" static struct task_struct *thread; @@ -41,6 +42,8 @@ static struct req { int err; const char *name; umode_t mode; /* 0 => delete */ + kuid_t uid; + kgid_t gid; struct device *dev; } *requests; @@ -85,7 +88,9 @@ int devtmpfs_create_node(struct device *dev) return 0; req.mode = 0; - req.name = device_get_devnode(dev, &req.mode, &tmp); + req.uid = GLOBAL_ROOT_UID; + req.gid = GLOBAL_ROOT_GID; + req.name = device_get_devnode(dev, &req.mode, &req.uid, &req.gid, &tmp); if (!req.name) return -ENOMEM; @@ -121,7 +126,7 @@ int devtmpfs_delete_node(struct device *dev) if (!thread) return 0; - req.name = device_get_devnode(dev, NULL, &tmp); + req.name = device_get_devnode(dev, NULL, NULL, NULL, &tmp); if (!req.name) return -ENOMEM; @@ -187,7 +192,8 @@ static int create_path(const char *nodepath) return err; } -static int handle_create(const char *nodename, umode_t mode, struct device *dev) +static int handle_create(const char *nodename, umode_t mode, kuid_t uid, + kgid_t gid, struct device *dev) { struct dentry *dentry; struct path path; @@ -201,14 +207,14 @@ static int handle_create(const char *nodename, umode_t mode, struct device *dev) if (IS_ERR(dentry)) return PTR_ERR(dentry); - err = vfs_mknod(path.dentry->d_inode, - dentry, mode, dev->devt); + err = vfs_mknod(path.dentry->d_inode, dentry, mode, dev->devt); if (!err) { struct iattr newattrs; - /* fixup possibly umasked mode */ newattrs.ia_mode = mode; - newattrs.ia_valid = ATTR_MODE; + newattrs.ia_uid = uid; + newattrs.ia_gid = gid; + newattrs.ia_valid = ATTR_MODE|ATTR_UID|ATTR_GID; mutex_lock(&dentry->d_inode->i_mutex); notify_change(dentry, &newattrs); mutex_unlock(&dentry->d_inode->i_mutex); @@ -358,10 +364,11 @@ int devtmpfs_mount(const char *mntdir) static DECLARE_COMPLETION(setup_done); -static int handle(const char *name, umode_t mode, struct device *dev) +static int handle(const char *name, umode_t mode, kuid_t uid, kgid_t gid, + struct device *dev) { if (mode) - return handle_create(name, mode, dev); + return handle_create(name, mode, uid, gid, dev); else return handle_remove(name, dev); } @@ -387,7 +394,8 @@ static int devtmpfsd(void *p) spin_unlock(&req_lock); while (req) { struct req *next = req->next; - req->err = handle(req->name, req->mode, req->dev); + req->err = handle(req->name, req->mode, + req->uid, req->gid, req->dev); complete(&req->done); req = next; } diff --git a/drivers/base/platform.c b/drivers/base/platform.c index c0b8df38402b..9eda84246ffd 100644 --- a/drivers/base/platform.c +++ b/drivers/base/platform.c @@ -46,8 +46,8 @@ EXPORT_SYMBOL_GPL(platform_bus); * manipulate any relevant information in the pdev_archdata they can do: * * platform_device_alloc() - * ... manipulate ... - * platform_device_add() + * ... manipulate ... + * platform_device_add() * * And if they don't care they can just call platform_device_register() and * everything will just work out. @@ -326,9 +326,7 @@ int platform_device_add(struct platform_device *pdev) } if (p && insert_resource(p, r)) { - printk(KERN_ERR - "%s: failed to claim resource %d\n", - dev_name(&pdev->dev), i); + dev_err(&pdev->dev, "failed to claim resource %d\n", i); ret = -EBUSY; goto failed; } @@ -555,7 +553,8 @@ EXPORT_SYMBOL_GPL(platform_driver_unregister); /** * platform_driver_probe - register driver for non-hotpluggable device * @drv: platform driver structure - * @probe: the driver probe routine, probably from an __init section + * @probe: the driver probe routine, probably from an __init section, + * must not return -EPROBE_DEFER. * * Use this instead of platform_driver_register() when you know the device * is not hotpluggable and has already been registered, and you want to @@ -566,6 +565,9 @@ EXPORT_SYMBOL_GPL(platform_driver_unregister); * into system-on-chip processors, where the controller devices have been * configured as part of board setup. * + * This is incompatible with deferred probing so probe() must not + * return -EPROBE_DEFER. + * * Returns zero if the driver registered and bound to a device, else returns * a negative error code and with the driver not registered. */ @@ -682,7 +684,7 @@ static int platform_uevent(struct device *dev, struct kobj_uevent_env *env) int rc; /* Some devices have extra OF data and an OF-style MODALIAS */ - rc = of_device_uevent_modalias(dev,env); + rc = of_device_uevent_modalias(dev, env); if (rc != -ENODEV) return rc; @@ -1126,8 +1128,8 @@ static int __init early_platform_driver_probe_id(char *class_str, switch (match_id) { case EARLY_PLATFORM_ID_ERROR: - pr_warning("%s: unable to parse %s parameter\n", - class_str, epdrv->pdrv->driver.name); + pr_warn("%s: unable to parse %s parameter\n", + class_str, epdrv->pdrv->driver.name); /* fall-through */ case EARLY_PLATFORM_ID_UNSET: match = NULL; @@ -1158,8 +1160,8 @@ static int __init early_platform_driver_probe_id(char *class_str, } if (epdrv->pdrv->probe(match)) - pr_warning("%s: unable to probe %s early.\n", - class_str, match->name); + pr_warn("%s: unable to probe %s early.\n", + class_str, match->name); else n++; } diff --git a/drivers/block/rbd.c b/drivers/block/rbd.c index f556f8a8b3f9..b7b7a88d9f68 100644 --- a/drivers/block/rbd.c +++ b/drivers/block/rbd.c @@ -1742,9 +1742,10 @@ static int rbd_img_request_submit(struct rbd_img_request *img_request) struct rbd_device *rbd_dev = img_request->rbd_dev; struct ceph_osd_client *osdc = &rbd_dev->rbd_client->client->osdc; struct rbd_obj_request *obj_request; + struct rbd_obj_request *next_obj_request; dout("%s: img %p\n", __func__, img_request); - for_each_obj_request(img_request, obj_request) { + for_each_obj_request_safe(img_request, obj_request, next_obj_request) { int ret; obj_request->callback = rbd_img_obj_callback; diff --git a/drivers/bluetooth/bluecard_cs.c b/drivers/bluetooth/bluecard_cs.c index 0d26851d6e49..6c3e3d43c718 100644 --- a/drivers/bluetooth/bluecard_cs.c +++ b/drivers/bluetooth/bluecard_cs.c @@ -934,17 +934,4 @@ static struct pcmcia_driver bluecard_driver = { .remove = bluecard_detach, .id_table = bluecard_ids, }; - -static int __init init_bluecard_cs(void) -{ - return pcmcia_register_driver(&bluecard_driver); -} - - -static void __exit exit_bluecard_cs(void) -{ - pcmcia_unregister_driver(&bluecard_driver); -} - -module_init(init_bluecard_cs); -module_exit(exit_bluecard_cs); +module_pcmcia_driver(bluecard_driver); diff --git a/drivers/bluetooth/bt3c_cs.c b/drivers/bluetooth/bt3c_cs.c index 7ffd3f407144..a1aaa3ba2a4b 100644 --- a/drivers/bluetooth/bt3c_cs.c +++ b/drivers/bluetooth/bt3c_cs.c @@ -760,17 +760,4 @@ static struct pcmcia_driver bt3c_driver = { .remove = bt3c_detach, .id_table = bt3c_ids, }; - -static int __init init_bt3c_cs(void) -{ - return pcmcia_register_driver(&bt3c_driver); -} - - -static void __exit exit_bt3c_cs(void) -{ - pcmcia_unregister_driver(&bt3c_driver); -} - -module_init(init_bt3c_cs); -module_exit(exit_bt3c_cs); +module_pcmcia_driver(bt3c_driver); diff --git a/drivers/bluetooth/btuart_cs.c b/drivers/bluetooth/btuart_cs.c index 35a553a90616..beb262f2dc4d 100644 --- a/drivers/bluetooth/btuart_cs.c +++ b/drivers/bluetooth/btuart_cs.c @@ -688,17 +688,4 @@ static struct pcmcia_driver btuart_driver = { .remove = btuart_detach, .id_table = btuart_ids, }; - -static int __init init_btuart_cs(void) -{ - return pcmcia_register_driver(&btuart_driver); -} - - -static void __exit exit_btuart_cs(void) -{ - pcmcia_unregister_driver(&btuart_driver); -} - -module_init(init_btuart_cs); -module_exit(exit_btuart_cs); +module_pcmcia_driver(btuart_driver); diff --git a/drivers/bluetooth/dtl1_cs.c b/drivers/bluetooth/dtl1_cs.c index 036cb366fe6e..33f3a6950c0e 100644 --- a/drivers/bluetooth/dtl1_cs.c +++ b/drivers/bluetooth/dtl1_cs.c @@ -628,17 +628,4 @@ static struct pcmcia_driver dtl1_driver = { .remove = dtl1_detach, .id_table = dtl1_ids, }; - -static int __init init_dtl1_cs(void) -{ - return pcmcia_register_driver(&dtl1_driver); -} - - -static void __exit exit_dtl1_cs(void) -{ - pcmcia_unregister_driver(&dtl1_driver); -} - -module_init(init_dtl1_cs); -module_exit(exit_dtl1_cs); +module_pcmcia_driver(dtl1_driver); diff --git a/drivers/char/applicom.c b/drivers/char/applicom.c index 25373df1dcf8..974321a2508d 100644 --- a/drivers/char/applicom.c +++ b/drivers/char/applicom.c @@ -804,8 +804,8 @@ static long ac_ioctl(struct file *file, unsigned int cmd, unsigned long arg) printk(KERN_INFO "Prom version board %d ....... V%d.%d %s", i+1, - (int)(readb(apbs[IndexCard].RamIO + VERS) >> 4), - (int)(readb(apbs[IndexCard].RamIO + VERS) & 0xF), + (int)(readb(apbs[i].RamIO + VERS) >> 4), + (int)(readb(apbs[i].RamIO + VERS) & 0xF), boardname); diff --git a/drivers/char/hpet.c b/drivers/char/hpet.c index e3f9a99b8522..d784650d14f0 100644 --- a/drivers/char/hpet.c +++ b/drivers/char/hpet.c @@ -373,26 +373,14 @@ static int hpet_mmap(struct file *file, struct vm_area_struct *vma) struct hpet_dev *devp; unsigned long addr; - if (((vma->vm_end - vma->vm_start) != PAGE_SIZE) || vma->vm_pgoff) - return -EINVAL; - devp = file->private_data; addr = devp->hd_hpets->hp_hpet_phys; if (addr & (PAGE_SIZE - 1)) return -ENOSYS; - vma->vm_flags |= VM_IO; vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); - - if (io_remap_pfn_range(vma, vma->vm_start, addr >> PAGE_SHIFT, - PAGE_SIZE, vma->vm_page_prot)) { - printk(KERN_ERR "%s: io_remap_pfn_range failed\n", - __func__); - return -EAGAIN; - } - - return 0; + return vm_iomap_memory(vma, addr, PAGE_SIZE); #else return -ENOSYS; #endif diff --git a/drivers/char/hw_random/mxc-rnga.c b/drivers/char/hw_random/mxc-rnga.c index f05d85713fd3..895d0b8fb9ab 100644 --- a/drivers/char/hw_random/mxc-rnga.c +++ b/drivers/char/hw_random/mxc-rnga.c @@ -228,18 +228,7 @@ static struct platform_driver mxc_rnga_driver = { .remove = __exit_p(mxc_rnga_remove), }; -static int __init mod_init(void) -{ - return platform_driver_probe(&mxc_rnga_driver, mxc_rnga_probe); -} - -static void __exit mod_exit(void) -{ - platform_driver_unregister(&mxc_rnga_driver); -} - -module_init(mod_init); -module_exit(mod_exit); +module_platform_driver_probe(mxc_rnga_driver, mxc_rnga_probe); MODULE_AUTHOR("Freescale Semiconductor, Inc."); MODULE_DESCRIPTION("H/W RNGA driver for i.MX"); diff --git a/drivers/char/hw_random/tx4939-rng.c b/drivers/char/hw_random/tx4939-rng.c index 30991989d65b..d34a24a0d484 100644 --- a/drivers/char/hw_random/tx4939-rng.c +++ b/drivers/char/hw_random/tx4939-rng.c @@ -166,18 +166,7 @@ static struct platform_driver tx4939_rng_driver = { .remove = tx4939_rng_remove, }; -static int __init tx4939rng_init(void) -{ - return platform_driver_probe(&tx4939_rng_driver, tx4939_rng_probe); -} - -static void __exit tx4939rng_exit(void) -{ - platform_driver_unregister(&tx4939_rng_driver); -} - -module_init(tx4939rng_init); -module_exit(tx4939rng_exit); +module_platform_driver_probe(tx4939_rng_driver, tx4939_rng_probe); MODULE_DESCRIPTION("H/W Random Number Generator (RNG) driver for TX4939"); MODULE_LICENSE("GPL"); diff --git a/drivers/char/tile-srom.c b/drivers/char/tile-srom.c index 3b22a606f79d..2e2036e940fc 100644 --- a/drivers/char/tile-srom.c +++ b/drivers/char/tile-srom.c @@ -371,7 +371,7 @@ static int srom_setup_minor(struct srom_dev *srom, int index) dev = device_create(srom_class, &platform_bus, MKDEV(srom_major, index), srom, "%d", index); - return IS_ERR(dev) ? PTR_ERR(dev) : 0; + return PTR_RET(dev); } /** srom_init() - Initialize the driver's module. */ diff --git a/drivers/dma/at_hdmac.c b/drivers/dma/at_hdmac.c index 6e13f262139a..88cfc61329d2 100644 --- a/drivers/dma/at_hdmac.c +++ b/drivers/dma/at_hdmac.c @@ -310,8 +310,6 @@ static void atc_complete_all(struct at_dma_chan *atchan) dev_vdbg(chan2dev(&atchan->chan_common), "complete all\n"); - BUG_ON(atc_chan_is_enabled(atchan)); - /* * Submit queued descriptors ASAP, i.e. before we go through * the completed ones. @@ -368,6 +366,9 @@ static void atc_advance_work(struct at_dma_chan *atchan) { dev_vdbg(chan2dev(&atchan->chan_common), "advance_work\n"); + if (atc_chan_is_enabled(atchan)) + return; + if (list_empty(&atchan->active_list) || list_is_singular(&atchan->active_list)) { atc_complete_all(atchan); @@ -1078,9 +1079,7 @@ static void atc_issue_pending(struct dma_chan *chan) return; spin_lock_irqsave(&atchan->lock, flags); - if (!atc_chan_is_enabled(atchan)) { - atc_advance_work(atchan); - } + atc_advance_work(atchan); spin_unlock_irqrestore(&atchan->lock, flags); } diff --git a/drivers/eisa/eisa-bus.c b/drivers/eisa/eisa-bus.c index 806c77bfd434..272a3ec35957 100644 --- a/drivers/eisa/eisa-bus.c +++ b/drivers/eisa/eisa-bus.c @@ -275,19 +275,18 @@ static int __init eisa_request_resources(struct eisa_root_device *root, } if (slot) { - edev->res[i].name = NULL; edev->res[i].start = SLOT_ADDRESS(root, slot) + (i * 0x400); edev->res[i].end = edev->res[i].start + 0xff; edev->res[i].flags = IORESOURCE_IO; } else { - edev->res[i].name = NULL; edev->res[i].start = SLOT_ADDRESS(root, slot) + EISA_VENDOR_ID_OFFSET; edev->res[i].end = edev->res[i].start + 3; - edev->res[i].flags = IORESOURCE_BUSY; + edev->res[i].flags = IORESOURCE_IO | IORESOURCE_BUSY; } + dev_printk(KERN_DEBUG, &edev->dev, "%pR\n", &edev->res[i]); if (request_resource(root->res, &edev->res[i])) goto failed; } @@ -314,41 +313,40 @@ static int __init eisa_probe(struct eisa_root_device *root) { int i, c; struct eisa_device *edev; + char *enabled_str; - printk(KERN_INFO "EISA: Probing bus %d at %s\n", - root->bus_nr, dev_name(root->dev)); + dev_info(root->dev, "Probing EISA bus %d\n", root->bus_nr); /* First try to get hold of slot 0. If there is no device * here, simply fail, unless root->force_probe is set. */ edev = kzalloc(sizeof(*edev), GFP_KERNEL); if (!edev) { - printk(KERN_ERR "EISA: Couldn't allocate mainboard slot\n"); + dev_err(root->dev, "EISA: Couldn't allocate mainboard slot\n"); return -ENOMEM; } - if (eisa_request_resources(root, edev, 0)) { - printk(KERN_WARNING \ - "EISA: Cannot allocate resource for mainboard\n"); + if (eisa_init_device(root, edev, 0)) { kfree(edev); if (!root->force_probe) - return -EBUSY; + return -ENODEV; goto force_probe; } - if (eisa_init_device(root, edev, 0)) { - eisa_release_resources(edev); + if (eisa_request_resources(root, edev, 0)) { + dev_warn(root->dev, + "EISA: Cannot allocate resource for mainboard\n"); kfree(edev); if (!root->force_probe) - return -ENODEV; + return -EBUSY; goto force_probe; } - printk(KERN_INFO "EISA: Mainboard %s detected.\n", edev->id.sig); + dev_info(&edev->dev, "EISA: Mainboard %s detected\n", edev->id.sig); if (eisa_register_device(edev)) { - printk(KERN_ERR "EISA: Failed to register %s\n", - edev->id.sig); + dev_err(&edev->dev, "EISA: Failed to register %s\n", + edev->id.sig); eisa_release_resources(edev); kfree(edev); } @@ -358,55 +356,47 @@ static int __init eisa_probe(struct eisa_root_device *root) for (c = 0, i = 1; i <= root->slots; i++) { edev = kzalloc(sizeof(*edev), GFP_KERNEL); if (!edev) { - printk(KERN_ERR "EISA: Out of memory for slot %d\n", i); + dev_err(root->dev, "EISA: Out of memory for slot %d\n", + i); continue; } - if (eisa_request_resources(root, edev, i)) { - printk(KERN_WARNING \ - "Cannot allocate resource for EISA slot %d\n", - i); + if (eisa_init_device(root, edev, i)) { kfree(edev); continue; } - if (eisa_init_device(root, edev, i)) { - eisa_release_resources(edev); + if (eisa_request_resources(root, edev, i)) { + dev_warn(root->dev, + "Cannot allocate resource for EISA slot %d\n", + i); kfree(edev); continue; } - - printk(KERN_INFO "EISA: slot %d : %s detected", - i, edev->id.sig); - - switch (edev->state) { - case EISA_CONFIG_ENABLED | EISA_CONFIG_FORCED: - printk(" (forced enabled)"); - break; - - case EISA_CONFIG_FORCED: - printk(" (forced disabled)"); - break; - - case 0: - printk(" (disabled)"); - break; - } - - printk (".\n"); + + if (edev->state == (EISA_CONFIG_ENABLED | EISA_CONFIG_FORCED)) + enabled_str = " (forced enabled)"; + else if (edev->state == EISA_CONFIG_FORCED) + enabled_str = " (forced disabled)"; + else if (edev->state == 0) + enabled_str = " (disabled)"; + else + enabled_str = ""; + + dev_info(&edev->dev, "EISA: slot %d: %s detected%s\n", i, + edev->id.sig, enabled_str); c++; if (eisa_register_device(edev)) { - printk(KERN_ERR "EISA: Failed to register %s\n", - edev->id.sig); + dev_err(&edev->dev, "EISA: Failed to register %s\n", + edev->id.sig); eisa_release_resources(edev); kfree(edev); } } - printk(KERN_INFO "EISA: Detected %d card%s.\n", c, c == 1 ? "" : "s"); - + dev_info(root->dev, "EISA: Detected %d card%s\n", c, c == 1 ? "" : "s"); return 0; } diff --git a/drivers/eisa/pci_eisa.c b/drivers/eisa/pci_eisa.c index 6c3fca97d346..a333bf3517de 100644 --- a/drivers/eisa/pci_eisa.c +++ b/drivers/eisa/pci_eisa.c @@ -25,8 +25,7 @@ static int __init pci_eisa_init(struct pci_dev *pdev) struct resource *res, *bus_res = NULL; if ((rc = pci_enable_device (pdev))) { - printk (KERN_ERR "pci_eisa : Could not enable device %s\n", - pci_name(pdev)); + dev_err(&pdev->dev, "Could not enable device\n"); return rc; } @@ -59,7 +58,7 @@ static int __init pci_eisa_init(struct pci_dev *pdev) dev_set_drvdata(pci_eisa_root.dev, &pci_eisa_root); if (eisa_root_register (&pci_eisa_root)) { - printk (KERN_ERR "pci_eisa : Could not register EISA root\n"); + dev_err(&pdev->dev, "Could not register EISA root\n"); return -1; } diff --git a/drivers/extcon/extcon-arizona.c b/drivers/extcon/extcon-arizona.c index dc357a4051f6..7a1b4a7791ba 100644 --- a/drivers/extcon/extcon-arizona.c +++ b/drivers/extcon/extcon-arizona.c @@ -33,12 +33,17 @@ #include <linux/mfd/arizona/pdata.h> #include <linux/mfd/arizona/registers.h> -#define ARIZONA_NUM_BUTTONS 6 +#define ARIZONA_MAX_MICD_RANGE 8 #define ARIZONA_ACCDET_MODE_MIC 0 #define ARIZONA_ACCDET_MODE_HPL 1 #define ARIZONA_ACCDET_MODE_HPR 2 +#define ARIZONA_HPDET_MAX 10000 + +#define HPDET_DEBOUNCE 500 +#define DEFAULT_MICD_TIMEOUT 2000 + struct arizona_extcon_info { struct device *dev; struct arizona *arizona; @@ -46,17 +51,27 @@ struct arizona_extcon_info { struct regulator *micvdd; struct input_dev *input; + u16 last_jackdet; + int micd_mode; const struct arizona_micd_config *micd_modes; int micd_num_modes; + const struct arizona_micd_range *micd_ranges; + int num_micd_ranges; + + int micd_timeout; + bool micd_reva; bool micd_clamp; struct delayed_work hpdet_work; + struct delayed_work micd_detect_work; + struct delayed_work micd_timeout_work; bool hpdet_active; bool hpdet_done; + bool hpdet_retried; int num_hpdet_res; unsigned int hpdet_res[3]; @@ -71,20 +86,25 @@ struct arizona_extcon_info { }; static const struct arizona_micd_config micd_default_modes[] = { - { 0, 2 << ARIZONA_MICD_BIAS_SRC_SHIFT, 1 }, { ARIZONA_ACCDET_SRC, 1 << ARIZONA_MICD_BIAS_SRC_SHIFT, 0 }, + { 0, 2 << ARIZONA_MICD_BIAS_SRC_SHIFT, 1 }, }; -static struct { - u16 status; - int report; -} arizona_lvl_to_key[ARIZONA_NUM_BUTTONS] = { - { 0x1, BTN_0 }, - { 0x2, BTN_1 }, - { 0x4, BTN_2 }, - { 0x8, BTN_3 }, - { 0x10, BTN_4 }, - { 0x20, BTN_5 }, +static const struct arizona_micd_range micd_default_ranges[] = { + { .max = 11, .key = BTN_0 }, + { .max = 28, .key = BTN_1 }, + { .max = 54, .key = BTN_2 }, + { .max = 100, .key = BTN_3 }, + { .max = 186, .key = BTN_4 }, + { .max = 430, .key = BTN_5 }, +}; + +static const int arizona_micd_levels[] = { + 3, 6, 8, 11, 13, 16, 18, 21, 23, 26, 28, 31, 34, 36, 39, 41, 44, 46, + 49, 52, 54, 57, 60, 62, 65, 67, 70, 73, 75, 78, 81, 83, 89, 94, 100, + 105, 111, 116, 122, 127, 139, 150, 161, 173, 186, 196, 209, 220, 245, + 270, 295, 321, 348, 375, 402, 430, 489, 550, 614, 681, 752, 903, 1071, + 1257, }; #define ARIZONA_CABLE_MECHANICAL 0 @@ -100,10 +120,63 @@ static const char *arizona_cable[] = { NULL, }; +static void arizona_start_hpdet_acc_id(struct arizona_extcon_info *info); + +static void arizona_extcon_do_magic(struct arizona_extcon_info *info, + unsigned int magic) +{ + struct arizona *arizona = info->arizona; + int ret; + + mutex_lock(&arizona->dapm->card->dapm_mutex); + + arizona->hpdet_magic = magic; + + /* Keep the HP output stages disabled while doing the magic */ + if (magic) { + ret = regmap_update_bits(arizona->regmap, + ARIZONA_OUTPUT_ENABLES_1, + ARIZONA_OUT1L_ENA | + ARIZONA_OUT1R_ENA, 0); + if (ret != 0) + dev_warn(arizona->dev, + "Failed to disable headphone outputs: %d\n", + ret); + } + + ret = regmap_update_bits(arizona->regmap, 0x225, 0x4000, + magic); + if (ret != 0) + dev_warn(arizona->dev, "Failed to do magic: %d\n", + ret); + + ret = regmap_update_bits(arizona->regmap, 0x226, 0x4000, + magic); + if (ret != 0) + dev_warn(arizona->dev, "Failed to do magic: %d\n", + ret); + + /* Restore the desired state while not doing the magic */ + if (!magic) { + ret = regmap_update_bits(arizona->regmap, + ARIZONA_OUTPUT_ENABLES_1, + ARIZONA_OUT1L_ENA | + ARIZONA_OUT1R_ENA, arizona->hp_ena); + if (ret != 0) + dev_warn(arizona->dev, + "Failed to restore headphone outputs: %d\n", + ret); + } + + mutex_unlock(&arizona->dapm->card->dapm_mutex); +} + static void arizona_extcon_set_mode(struct arizona_extcon_info *info, int mode) { struct arizona *arizona = info->arizona; + mode %= info->micd_num_modes; + if (arizona->pdata.micd_pol_gpio > 0) gpio_set_value_cansleep(arizona->pdata.micd_pol_gpio, info->micd_modes[mode].gpio); @@ -330,7 +403,7 @@ static int arizona_hpdet_read(struct arizona_extcon_info *info) /* If we go out of range report top of range */ if (val < 100 || val > 0x3fb) { dev_dbg(arizona->dev, "Measurement out of range\n"); - return 10000; + return ARIZONA_HPDET_MAX; } dev_dbg(arizona->dev, "HPDET read %d in range %d\n", @@ -391,7 +464,8 @@ static int arizona_hpdet_read(struct arizona_extcon_info *info) return val; } -static int arizona_hpdet_do_id(struct arizona_extcon_info *info, int *reading) +static int arizona_hpdet_do_id(struct arizona_extcon_info *info, int *reading, + bool *mic) { struct arizona *arizona = info->arizona; int id_gpio = arizona->pdata.hpdet_id_gpio; @@ -403,32 +477,8 @@ static int arizona_hpdet_do_id(struct arizona_extcon_info *info, int *reading) if (arizona->pdata.hpdet_acc_id) { info->hpdet_res[info->num_hpdet_res++] = *reading; - /* - * If the impedence is too high don't measure the - * second ground. - */ - if (info->num_hpdet_res == 1 && *reading >= 45) { - dev_dbg(arizona->dev, "Skipping ground flip\n"); - info->hpdet_res[info->num_hpdet_res++] = *reading; - } - - if (info->num_hpdet_res == 1) { - dev_dbg(arizona->dev, "Flipping ground\n"); - - regmap_update_bits(arizona->regmap, - ARIZONA_ACCESSORY_DETECT_MODE_1, - ARIZONA_ACCDET_SRC, - ~info->micd_modes[0].src); - - regmap_update_bits(arizona->regmap, - ARIZONA_HEADPHONE_DETECT_1, - ARIZONA_HP_POLL, ARIZONA_HP_POLL); - return -EAGAIN; - } - /* Only check the mic directly if we didn't already ID it */ - if (id_gpio && info->num_hpdet_res == 2 && - !((info->hpdet_res[0] > info->hpdet_res[1] * 2))) { + if (id_gpio && info->num_hpdet_res == 1) { dev_dbg(arizona->dev, "Measuring mic\n"); regmap_update_bits(arizona->regmap, @@ -447,22 +497,28 @@ static int arizona_hpdet_do_id(struct arizona_extcon_info *info, int *reading) } /* OK, got both. Now, compare... */ - dev_dbg(arizona->dev, "HPDET measured %d %d %d\n", - info->hpdet_res[0], info->hpdet_res[1], - info->hpdet_res[2]); - + dev_dbg(arizona->dev, "HPDET measured %d %d\n", + info->hpdet_res[0], info->hpdet_res[1]); /* Take the headphone impedance for the main report */ *reading = info->hpdet_res[0]; + /* Sometimes we get false readings due to slow insert */ + if (*reading >= ARIZONA_HPDET_MAX && !info->hpdet_retried) { + dev_dbg(arizona->dev, "Retrying high impedance\n"); + info->num_hpdet_res = 0; + info->hpdet_retried = true; + arizona_start_hpdet_acc_id(info); + pm_runtime_put(info->dev); + return -EAGAIN; + } + /* - * Either the two grounds measure differently or we - * measure the mic as high impedance. + * If we measure the mic as */ - if ((info->hpdet_res[0] > info->hpdet_res[1] * 2) || - (id_gpio && info->hpdet_res[2] > 10)) { + if (!id_gpio || info->hpdet_res[1] > 50) { dev_dbg(arizona->dev, "Detected mic\n"); - info->mic = true; + *mic = true; info->detecting = true; } else { dev_dbg(arizona->dev, "Detected headphone\n"); @@ -484,8 +540,8 @@ static irqreturn_t arizona_hpdet_irq(int irq, void *data) struct arizona *arizona = info->arizona; int id_gpio = arizona->pdata.hpdet_id_gpio; int report = ARIZONA_CABLE_HEADPHONE; - unsigned int val; int ret, reading; + bool mic = false; mutex_lock(&info->lock); @@ -521,7 +577,7 @@ static irqreturn_t arizona_hpdet_irq(int irq, void *data) ARIZONA_HP_IMPEDANCE_RANGE_MASK | ARIZONA_HP_POLL, 0); - ret = arizona_hpdet_do_id(info, &reading); + ret = arizona_hpdet_do_id(info, &reading, &mic); if (ret == -EAGAIN) { goto out; } else if (ret < 0) { @@ -539,28 +595,7 @@ static irqreturn_t arizona_hpdet_irq(int irq, void *data) dev_err(arizona->dev, "Failed to report HP/line: %d\n", ret); - mutex_lock(&arizona->dapm->card->dapm_mutex); - - ret = regmap_read(arizona->regmap, ARIZONA_OUTPUT_ENABLES_1, &val); - if (ret != 0) { - dev_err(arizona->dev, "Failed to read output enables: %d\n", - ret); - val = 0; - } - - if (!(val & (ARIZONA_OUT1L_ENA | ARIZONA_OUT1R_ENA))) { - ret = regmap_update_bits(arizona->regmap, 0x225, 0x4000, 0); - if (ret != 0) - dev_warn(arizona->dev, "Failed to undo magic: %d\n", - ret); - - ret = regmap_update_bits(arizona->regmap, 0x226, 0x4000, 0); - if (ret != 0) - dev_warn(arizona->dev, "Failed to undo magic: %d\n", - ret); - } - - mutex_unlock(&arizona->dapm->card->dapm_mutex); + arizona_extcon_do_magic(info, 0); done: if (id_gpio) @@ -572,7 +607,7 @@ done: ARIZONA_ACCDET_MODE_MASK, ARIZONA_ACCDET_MODE_MIC); /* If we have a mic then reenable MICDET */ - if (info->mic) + if (mic || info->mic) arizona_start_mic(info); if (info->hpdet_active) { @@ -606,13 +641,7 @@ static void arizona_identify_headphone(struct arizona_extcon_info *info) if (info->mic) arizona_stop_mic(info); - ret = regmap_update_bits(arizona->regmap, 0x225, 0x4000, 0x4000); - if (ret != 0) - dev_warn(arizona->dev, "Failed to do magic: %d\n", ret); - - ret = regmap_update_bits(arizona->regmap, 0x226, 0x4000, 0x4000); - if (ret != 0) - dev_warn(arizona->dev, "Failed to do magic: %d\n", ret); + arizona_extcon_do_magic(info, 0x4000); ret = regmap_update_bits(arizona->regmap, ARIZONA_ACCESSORY_DETECT_MODE_1, @@ -653,7 +682,8 @@ err: static void arizona_start_hpdet_acc_id(struct arizona_extcon_info *info) { struct arizona *arizona = info->arizona; - unsigned int val; + int hp_reading = 32; + bool mic; int ret; dev_dbg(arizona->dev, "Starting identification via HPDET\n"); @@ -663,32 +693,7 @@ static void arizona_start_hpdet_acc_id(struct arizona_extcon_info *info) info->hpdet_active = true; - arizona_extcon_pulse_micbias(info); - - mutex_lock(&arizona->dapm->card->dapm_mutex); - - ret = regmap_read(arizona->regmap, ARIZONA_OUTPUT_ENABLES_1, &val); - if (ret != 0) { - dev_err(arizona->dev, "Failed to read output enables: %d\n", - ret); - val = 0; - } - - if (!(val & (ARIZONA_OUT1L_ENA | ARIZONA_OUT1R_ENA))) { - ret = regmap_update_bits(arizona->regmap, 0x225, 0x4000, - 0x4000); - if (ret != 0) - dev_warn(arizona->dev, "Failed to do magic: %d\n", - ret); - - ret = regmap_update_bits(arizona->regmap, 0x226, 0x4000, - 0x4000); - if (ret != 0) - dev_warn(arizona->dev, "Failed to do magic: %d\n", - ret); - } - - mutex_unlock(&arizona->dapm->card->dapm_mutex); + arizona_extcon_do_magic(info, 0x4000); ret = regmap_update_bits(arizona->regmap, ARIZONA_ACCESSORY_DETECT_MODE_1, @@ -700,12 +705,18 @@ static void arizona_start_hpdet_acc_id(struct arizona_extcon_info *info) goto err; } - ret = regmap_update_bits(arizona->regmap, ARIZONA_HEADPHONE_DETECT_1, - ARIZONA_HP_POLL, ARIZONA_HP_POLL); - if (ret != 0) { - dev_err(arizona->dev, "Can't start HPDETL measurement: %d\n", - ret); - goto err; + if (arizona->pdata.hpdet_acc_id_line) { + ret = regmap_update_bits(arizona->regmap, + ARIZONA_HEADPHONE_DETECT_1, + ARIZONA_HP_POLL, ARIZONA_HP_POLL); + if (ret != 0) { + dev_err(arizona->dev, + "Can't start HPDETL measurement: %d\n", + ret); + goto err; + } + } else { + arizona_hpdet_do_id(info, &hp_reading, &mic); } return; @@ -724,28 +735,58 @@ err: info->hpdet_active = false; } -static irqreturn_t arizona_micdet(int irq, void *data) +static void arizona_micd_timeout_work(struct work_struct *work) { - struct arizona_extcon_info *info = data; + struct arizona_extcon_info *info = container_of(work, + struct arizona_extcon_info, + micd_timeout_work.work); + + mutex_lock(&info->lock); + + dev_dbg(info->arizona->dev, "MICD timed out, reporting HP\n"); + arizona_identify_headphone(info); + + info->detecting = false; + + arizona_stop_mic(info); + + mutex_unlock(&info->lock); +} + +static void arizona_micd_detect(struct work_struct *work) +{ + struct arizona_extcon_info *info = container_of(work, + struct arizona_extcon_info, + micd_detect_work.work); struct arizona *arizona = info->arizona; - unsigned int val, lvl; - int ret, i; + unsigned int val = 0, lvl; + int ret, i, key; + + cancel_delayed_work_sync(&info->micd_timeout_work); mutex_lock(&info->lock); - ret = regmap_read(arizona->regmap, ARIZONA_MIC_DETECT_3, &val); - if (ret != 0) { - dev_err(arizona->dev, "Failed to read MICDET: %d\n", ret); - mutex_unlock(&info->lock); - return IRQ_NONE; - } + for (i = 0; i < 10 && !(val & 0x7fc); i++) { + ret = regmap_read(arizona->regmap, ARIZONA_MIC_DETECT_3, &val); + if (ret != 0) { + dev_err(arizona->dev, "Failed to read MICDET: %d\n", ret); + mutex_unlock(&info->lock); + return; + } - dev_dbg(arizona->dev, "MICDET: %x\n", val); + dev_dbg(arizona->dev, "MICDET: %x\n", val); + + if (!(val & ARIZONA_MICD_VALID)) { + dev_warn(arizona->dev, "Microphone detection state invalid\n"); + mutex_unlock(&info->lock); + return; + } + } - if (!(val & ARIZONA_MICD_VALID)) { - dev_warn(arizona->dev, "Microphone detection state invalid\n"); + if (i == 10 && !(val & 0x7fc)) { + dev_err(arizona->dev, "Failed to get valid MICDET value\n"); mutex_unlock(&info->lock); - return IRQ_NONE; + return; } /* Due to jack detect this should never happen */ @@ -786,7 +827,7 @@ static irqreturn_t arizona_micdet(int irq, void *data) * impedence then give up and report headphones. */ if (info->detecting && (val & 0x3f8)) { - if (info->jack_flips >= info->micd_num_modes) { + if (info->jack_flips >= info->micd_num_modes * 10) { dev_dbg(arizona->dev, "Detected HP/line\n"); arizona_identify_headphone(info); @@ -816,12 +857,17 @@ static irqreturn_t arizona_micdet(int irq, void *data) lvl = val & ARIZONA_MICD_LVL_MASK; lvl >>= ARIZONA_MICD_LVL_SHIFT; - for (i = 0; i < ARIZONA_NUM_BUTTONS; i++) - if (lvl & arizona_lvl_to_key[i].status) - input_report_key(info->input, - arizona_lvl_to_key[i].report, - 1); - input_sync(info->input); + for (i = 0; i < info->num_micd_ranges; i++) + input_report_key(info->input, + info->micd_ranges[i].key, 0); + + WARN_ON(!lvl); + WARN_ON(ffs(lvl) - 1 >= info->num_micd_ranges); + if (lvl && ffs(lvl) - 1 < info->num_micd_ranges) { + key = info->micd_ranges[ffs(lvl) - 1].key; + input_report_key(info->input, key, 1); + input_sync(info->input); + } } else if (info->detecting) { dev_dbg(arizona->dev, "Headphone detected\n"); @@ -835,16 +881,41 @@ static irqreturn_t arizona_micdet(int irq, void *data) } } else { dev_dbg(arizona->dev, "Mic button released\n"); - for (i = 0; i < ARIZONA_NUM_BUTTONS; i++) + for (i = 0; i < info->num_micd_ranges; i++) input_report_key(info->input, - arizona_lvl_to_key[i].report, 0); + info->micd_ranges[i].key, 0); input_sync(info->input); arizona_extcon_pulse_micbias(info); } handled: + if (info->detecting) + schedule_delayed_work(&info->micd_timeout_work, + msecs_to_jiffies(info->micd_timeout)); + pm_runtime_mark_last_busy(info->dev); mutex_unlock(&info->lock); +} + +static irqreturn_t arizona_micdet(int irq, void *data) +{ + struct arizona_extcon_info *info = data; + struct arizona *arizona = info->arizona; + int debounce = arizona->pdata.micd_detect_debounce; + + cancel_delayed_work_sync(&info->micd_detect_work); + cancel_delayed_work_sync(&info->micd_timeout_work); + + mutex_lock(&info->lock); + if (!info->detecting) + debounce = 0; + mutex_unlock(&info->lock); + + if (debounce) + schedule_delayed_work(&info->micd_detect_work, + msecs_to_jiffies(debounce)); + else + arizona_micd_detect(&info->micd_detect_work.work); return IRQ_HANDLED; } @@ -865,11 +936,13 @@ static irqreturn_t arizona_jackdet(int irq, void *data) struct arizona_extcon_info *info = data; struct arizona *arizona = info->arizona; unsigned int val, present, mask; + bool cancelled_hp, cancelled_mic; int ret, i; - pm_runtime_get_sync(info->dev); + cancelled_hp = cancel_delayed_work_sync(&info->hpdet_work); + cancelled_mic = cancel_delayed_work_sync(&info->micd_timeout_work); - cancel_delayed_work_sync(&info->hpdet_work); + pm_runtime_get_sync(info->dev); mutex_lock(&info->lock); @@ -890,7 +963,22 @@ static irqreturn_t arizona_jackdet(int irq, void *data) return IRQ_NONE; } - if ((val & mask) == present) { + val &= mask; + if (val == info->last_jackdet) { + dev_dbg(arizona->dev, "Suppressing duplicate JACKDET\n"); + if (cancelled_hp) + schedule_delayed_work(&info->hpdet_work, + msecs_to_jiffies(HPDET_DEBOUNCE)); + + if (cancelled_mic) + schedule_delayed_work(&info->micd_timeout_work, + msecs_to_jiffies(info->micd_timeout)); + + goto out; + } + info->last_jackdet = val; + + if (info->last_jackdet == present) { dev_dbg(arizona->dev, "Detected jack\n"); ret = extcon_set_cable_state_(&info->edev, ARIZONA_CABLE_MECHANICAL, true); @@ -907,7 +995,7 @@ static irqreturn_t arizona_jackdet(int irq, void *data) arizona_start_mic(info); } else { schedule_delayed_work(&info->hpdet_work, - msecs_to_jiffies(250)); + msecs_to_jiffies(HPDET_DEBOUNCE)); } regmap_update_bits(arizona->regmap, @@ -923,10 +1011,11 @@ static irqreturn_t arizona_jackdet(int irq, void *data) info->hpdet_res[i] = 0; info->mic = false; info->hpdet_done = false; + info->hpdet_retried = false; - for (i = 0; i < ARIZONA_NUM_BUTTONS; i++) + for (i = 0; i < info->num_micd_ranges; i++) input_report_key(info->input, - arizona_lvl_to_key[i].report, 0); + info->micd_ranges[i].key, 0); input_sync(info->input); ret = extcon_update_state(&info->edev, 0xffffffff, 0); @@ -940,6 +1029,11 @@ static irqreturn_t arizona_jackdet(int irq, void *data) ARIZONA_MICD_CLAMP_DB | ARIZONA_JD1_DB); } + if (arizona->pdata.micd_timeout) + info->micd_timeout = arizona->pdata.micd_timeout; + else + info->micd_timeout = DEFAULT_MICD_TIMEOUT; + /* Clear trig_sts to make sure DCVDD is not forced up */ regmap_write(arizona->regmap, ARIZONA_AOD_WKUP_AND_TRIG, ARIZONA_MICD_CLAMP_FALL_TRIG_STS | @@ -947,6 +1041,7 @@ static irqreturn_t arizona_jackdet(int irq, void *data) ARIZONA_JD1_FALL_TRIG_STS | ARIZONA_JD1_RISE_TRIG_STS); +out: mutex_unlock(&info->lock); pm_runtime_mark_last_busy(info->dev); @@ -955,13 +1050,34 @@ static irqreturn_t arizona_jackdet(int irq, void *data) return IRQ_HANDLED; } +/* Map a level onto a slot in the register bank */ +static void arizona_micd_set_level(struct arizona *arizona, int index, + unsigned int level) +{ + int reg; + unsigned int mask; + + reg = ARIZONA_MIC_DETECT_LEVEL_4 - (index / 2); + + if (!(index % 2)) { + mask = 0x3f00; + level <<= 8; + } else { + mask = 0x3f; + } + + /* Program the level itself */ + regmap_update_bits(arizona->regmap, reg, mask, level); +} + static int arizona_extcon_probe(struct platform_device *pdev) { struct arizona *arizona = dev_get_drvdata(pdev->dev.parent); struct arizona_pdata *pdata; struct arizona_extcon_info *info; + unsigned int val; int jack_irq_fall, jack_irq_rise; - int ret, mode, i; + int ret, mode, i, j; if (!arizona->dapm || !arizona->dapm->card) return -EPROBE_DEFER; @@ -985,7 +1101,10 @@ static int arizona_extcon_probe(struct platform_device *pdev) mutex_init(&info->lock); info->arizona = arizona; info->dev = &pdev->dev; + info->last_jackdet = ~(ARIZONA_MICD_CLAMP_STS | ARIZONA_JD1_STS); INIT_DELAYED_WORK(&info->hpdet_work, arizona_hpdet_work); + INIT_DELAYED_WORK(&info->micd_detect_work, arizona_micd_detect); + INIT_DELAYED_WORK(&info->micd_timeout_work, arizona_micd_timeout_work); platform_set_drvdata(pdev, info); switch (arizona->type) { @@ -1014,6 +1133,17 @@ static int arizona_extcon_probe(struct platform_device *pdev) goto err; } + info->input = devm_input_allocate_device(&pdev->dev); + if (!info->input) { + dev_err(arizona->dev, "Can't allocate input dev\n"); + ret = -ENOMEM; + goto err_register; + } + + info->input->name = "Headset"; + info->input->phys = "arizona/extcon"; + info->input->dev.parent = &pdev->dev; + if (pdata->num_micd_configs) { info->micd_modes = pdata->micd_configs; info->micd_num_modes = pdata->num_micd_configs; @@ -1069,15 +1199,79 @@ static int arizona_extcon_probe(struct platform_device *pdev) arizona->pdata.micd_dbtime << ARIZONA_MICD_DBTIME_SHIFT); + BUILD_BUG_ON(ARRAY_SIZE(arizona_micd_levels) != 0x40); + + if (arizona->pdata.num_micd_ranges) { + info->micd_ranges = pdata->micd_ranges; + info->num_micd_ranges = pdata->num_micd_ranges; + } else { + info->micd_ranges = micd_default_ranges; + info->num_micd_ranges = ARRAY_SIZE(micd_default_ranges); + } + + if (arizona->pdata.num_micd_ranges > ARIZONA_MAX_MICD_RANGE) { + dev_err(arizona->dev, "Too many MICD ranges: %d\n", + arizona->pdata.num_micd_ranges); + } + + if (info->num_micd_ranges > 1) { + for (i = 1; i < info->num_micd_ranges; i++) { + if (info->micd_ranges[i - 1].max > + info->micd_ranges[i].max) { + dev_err(arizona->dev, + "MICD ranges must be sorted\n"); + ret = -EINVAL; + goto err_input; + } + } + } + + /* Disable all buttons by default */ + regmap_update_bits(arizona->regmap, ARIZONA_MIC_DETECT_2, + ARIZONA_MICD_LVL_SEL_MASK, 0x81); + + /* Set up all the buttons the user specified */ + for (i = 0; i < info->num_micd_ranges; i++) { + for (j = 0; j < ARRAY_SIZE(arizona_micd_levels); j++) + if (arizona_micd_levels[j] >= info->micd_ranges[i].max) + break; + + if (j == ARRAY_SIZE(arizona_micd_levels)) { + dev_err(arizona->dev, "Unsupported MICD level %d\n", + info->micd_ranges[i].max); + ret = -EINVAL; + goto err_input; + } + + dev_dbg(arizona->dev, "%d ohms for MICD threshold %d\n", + arizona_micd_levels[j], i); + + arizona_micd_set_level(arizona, i, j); + input_set_capability(info->input, EV_KEY, + info->micd_ranges[i].key); + + /* Enable reporting of that range */ + regmap_update_bits(arizona->regmap, ARIZONA_MIC_DETECT_2, + 1 << i, 1 << i); + } + + /* Set all the remaining keys to a maximum */ + for (; i < ARIZONA_MAX_MICD_RANGE; i++) + arizona_micd_set_level(arizona, i, 0x3f); + /* * If we have a clamp use it, activating in conjunction with * GPIO5 if that is connected for jack detect operation. */ if (info->micd_clamp) { if (arizona->pdata.jd_gpio5) { - /* Put the GPIO into input mode */ + /* Put the GPIO into input mode with optional pull */ + val = 0xc101; + if (arizona->pdata.jd_gpio5_nopull) + val &= ~ARIZONA_GPN_PU; + regmap_write(arizona->regmap, ARIZONA_GPIO5_CTRL, - 0xc101); + val); regmap_update_bits(arizona->regmap, ARIZONA_MICD_CLAMP_CONTROL, @@ -1096,20 +1290,6 @@ static int arizona_extcon_probe(struct platform_device *pdev) arizona_extcon_set_mode(info, 0); - info->input = devm_input_allocate_device(&pdev->dev); - if (!info->input) { - dev_err(arizona->dev, "Can't allocate input dev\n"); - ret = -ENOMEM; - goto err_register; - } - - for (i = 0; i < ARIZONA_NUM_BUTTONS; i++) - input_set_capability(info->input, EV_KEY, - arizona_lvl_to_key[i].report); - info->input->name = "Headset"; - info->input->phys = "arizona/extcon"; - info->input->dev.parent = &pdev->dev; - pm_runtime_enable(&pdev->dev); pm_runtime_idle(&pdev->dev); pm_runtime_get_sync(&pdev->dev); diff --git a/drivers/extcon/extcon-max77693.c b/drivers/extcon/extcon-max77693.c index 8f3c947b0029..b56bdaa27d4b 100644 --- a/drivers/extcon/extcon-max77693.c +++ b/drivers/extcon/extcon-max77693.c @@ -38,7 +38,7 @@ * extcon-max77693 driver use 'default_init_data' to bring up base operation * of MAX77693 MUIC device. */ -struct max77693_reg_data default_init_data[] = { +static struct max77693_reg_data default_init_data[] = { { /* STATUS2 - [3]ChgDetRun */ .addr = MAX77693_MUIC_REG_STATUS2, @@ -258,7 +258,7 @@ static int max77693_muic_set_debounce_time(struct max77693_muic_info *info, CONTROL3_ADCDBSET_MASK); if (ret) { dev_err(info->dev, "failed to set ADC debounce time\n"); - return -EAGAIN; + return ret; } break; default: @@ -294,7 +294,7 @@ static int max77693_muic_set_path(struct max77693_muic_info *info, MAX77693_MUIC_REG_CTRL1, ctrl1, COMP_SW_MASK); if (ret < 0) { dev_err(info->dev, "failed to update MUIC register\n"); - return -EAGAIN; + return ret; } if (attached) @@ -307,7 +307,7 @@ static int max77693_muic_set_path(struct max77693_muic_info *info, CONTROL2_LOWPWR_MASK | CONTROL2_CPEN_MASK); if (ret < 0) { dev_err(info->dev, "failed to update MUIC register\n"); - return -EAGAIN; + return ret; } dev_info(info->dev, @@ -1035,7 +1035,7 @@ static int max77693_muic_detect_accessory(struct max77693_muic_info *info) if (ret) { dev_err(info->dev, "failed to read MUIC register\n"); mutex_unlock(&info->mutex); - return -EINVAL; + return ret; } adc = max77693_muic_get_cable_type(info, MAX77693_CABLE_GROUP_ADC, diff --git a/drivers/extcon/extcon-max8997.c b/drivers/extcon/extcon-max8997.c index 69641bcae325..67d6738d85a0 100644 --- a/drivers/extcon/extcon-max8997.c +++ b/drivers/extcon/extcon-max8997.c @@ -196,7 +196,7 @@ static int max8997_muic_set_debounce_time(struct max8997_muic_info *info, CONTROL3_ADCDBSET_MASK); if (ret) { dev_err(info->dev, "failed to set ADC debounce time\n"); - return -EAGAIN; + return ret; } break; default: @@ -232,7 +232,7 @@ static int max8997_muic_set_path(struct max8997_muic_info *info, MAX8997_MUIC_REG_CONTROL1, ctrl1, COMP_SW_MASK); if (ret < 0) { dev_err(info->dev, "failed to update MUIC register\n"); - return -EAGAIN; + return ret; } if (attached) @@ -245,7 +245,7 @@ static int max8997_muic_set_path(struct max8997_muic_info *info, CONTROL2_LOWPWR_MASK | CONTROL2_CPEN_MASK); if (ret < 0) { dev_err(info->dev, "failed to update MUIC register\n"); - return -EAGAIN; + return ret; } dev_info(info->dev, @@ -397,7 +397,7 @@ static int max8997_muic_handle_jig_uart(struct max8997_muic_info *info, ret = max8997_muic_set_path(info, info->path_uart, attached); if (ret) { dev_err(info->dev, "failed to update muic register\n"); - return -EINVAL; + return ret; } extcon_set_cable_state(info->edev, "JIG", attached); @@ -608,7 +608,7 @@ static int max8997_muic_detect_dev(struct max8997_muic_info *info) if (ret) { dev_err(info->dev, "failed to read MUIC register\n"); mutex_unlock(&info->mutex); - return -EINVAL; + return ret; } adc = max8997_muic_get_cable_type(info, MAX8997_CABLE_GROUP_ADC, @@ -646,7 +646,7 @@ static void max8997_muic_detect_cable_wq(struct work_struct *work) ret = max8997_muic_detect_dev(info); if (ret < 0) - pr_err("failed to detect cable type\n"); + dev_err(info->dev, "failed to detect cable type\n"); } static int max8997_muic_probe(struct platform_device *pdev) diff --git a/drivers/firmware/Kconfig b/drivers/firmware/Kconfig index 42c759a4d047..3e532002e4d1 100644 --- a/drivers/firmware/Kconfig +++ b/drivers/firmware/Kconfig @@ -39,6 +39,7 @@ config FIRMWARE_MEMMAP config EFI_VARS tristate "EFI Variable Support via sysfs" depends on EFI + select UCS2_STRING default n help If you say Y here, you are able to get EFI (Extensible Firmware diff --git a/drivers/firmware/efivars.c b/drivers/firmware/efivars.c index 7acafb80fd4c..f4baa11d3644 100644 --- a/drivers/firmware/efivars.c +++ b/drivers/firmware/efivars.c @@ -80,6 +80,7 @@ #include <linux/slab.h> #include <linux/pstore.h> #include <linux/ctype.h> +#include <linux/ucs2_string.h> #include <linux/fs.h> #include <linux/ramfs.h> @@ -172,51 +173,6 @@ static void efivar_update_sysfs_entries(struct work_struct *); static DECLARE_WORK(efivar_work, efivar_update_sysfs_entries); static bool efivar_wq_enabled = true; -/* Return the number of unicode characters in data */ -static unsigned long -utf16_strnlen(efi_char16_t *s, size_t maxlength) -{ - unsigned long length = 0; - - while (*s++ != 0 && length < maxlength) - length++; - return length; -} - -static inline unsigned long -utf16_strlen(efi_char16_t *s) -{ - return utf16_strnlen(s, ~0UL); -} - -/* - * Return the number of bytes is the length of this string - * Note: this is NOT the same as the number of unicode characters - */ -static inline unsigned long -utf16_strsize(efi_char16_t *data, unsigned long maxlength) -{ - return utf16_strnlen(data, maxlength/sizeof(efi_char16_t)) * sizeof(efi_char16_t); -} - -static inline int -utf16_strncmp(const efi_char16_t *a, const efi_char16_t *b, size_t len) -{ - while (1) { - if (len == 0) - return 0; - if (*a < *b) - return -1; - if (*a > *b) - return 1; - if (*a == 0) /* implies *b == 0 */ - return 0; - a++; - b++; - len--; - } -} - static bool validate_device_path(struct efi_variable *var, int match, u8 *buffer, unsigned long len) @@ -268,7 +224,7 @@ validate_load_option(struct efi_variable *var, int match, u8 *buffer, u16 filepathlength; int i, desclength = 0, namelen; - namelen = utf16_strnlen(var->VariableName, sizeof(var->VariableName)); + namelen = ucs2_strnlen(var->VariableName, sizeof(var->VariableName)); /* Either "Boot" or "Driver" followed by four digits of hex */ for (i = match; i < match+4; i++) { @@ -291,7 +247,7 @@ validate_load_option(struct efi_variable *var, int match, u8 *buffer, * There's no stored length for the description, so it has to be * found by hand */ - desclength = utf16_strsize((efi_char16_t *)(buffer + 6), len - 6) + 2; + desclength = ucs2_strsize((efi_char16_t *)(buffer + 6), len - 6) + 2; /* Each boot entry must have a descriptor */ if (!desclength) @@ -436,24 +392,12 @@ static efi_status_t check_var_size_locked(struct efivars *efivars, u32 attributes, unsigned long size) { - u64 storage_size, remaining_size, max_size; - efi_status_t status; const struct efivar_operations *fops = efivars->ops; - if (!efivars->ops->query_variable_info) + if (!efivars->ops->query_variable_store) return EFI_UNSUPPORTED; - status = fops->query_variable_info(attributes, &storage_size, - &remaining_size, &max_size); - - if (status != EFI_SUCCESS) - return status; - - if (!storage_size || size > remaining_size || size > max_size || - (remaining_size - size) < (storage_size / 2)) - return EFI_OUT_OF_RESOURCES; - - return status; + return fops->query_variable_store(attributes, size); } @@ -593,7 +537,7 @@ efivar_store_raw(struct efivar_entry *entry, const char *buf, size_t count) spin_lock_irq(&efivars->lock); status = check_var_size_locked(efivars, new_var->Attributes, - new_var->DataSize + utf16_strsize(new_var->VariableName, 1024)); + new_var->DataSize + ucs2_strsize(new_var->VariableName, 1024)); if (status == EFI_SUCCESS || status == EFI_UNSUPPORTED) status = efivars->ops->set_variable(new_var->VariableName, @@ -771,7 +715,7 @@ static ssize_t efivarfs_file_write(struct file *file, * QueryVariableInfo() isn't supported by the firmware. */ - varsize = datasize + utf16_strsize(var->var.VariableName, 1024); + varsize = datasize + ucs2_strsize(var->var.VariableName, 1024); status = check_var_size(efivars, attributes, varsize); if (status != EFI_SUCCESS) { @@ -1223,7 +1167,7 @@ static int efivarfs_fill_super(struct super_block *sb, void *data, int silent) inode = NULL; - len = utf16_strlen(entry->var.VariableName); + len = ucs2_strlen(entry->var.VariableName); /* name, plus '-', plus GUID, plus NUL*/ name = kmalloc(len + 1 + GUID_LEN + 1, GFP_ATOMIC); @@ -1481,8 +1425,8 @@ static int efi_pstore_erase(enum pstore_type_id type, u64 id, int count, if (efi_guidcmp(entry->var.VendorGuid, vendor)) continue; - if (utf16_strncmp(entry->var.VariableName, efi_name, - utf16_strlen(efi_name))) { + if (ucs2_strncmp(entry->var.VariableName, efi_name, + ucs2_strlen(efi_name))) { /* * Check if an old format, * which doesn't support holding @@ -1494,8 +1438,8 @@ static int efi_pstore_erase(enum pstore_type_id type, u64 id, int count, for (i = 0; i < DUMP_NAME_LEN; i++) efi_name_old[i] = name_old[i]; - if (utf16_strncmp(entry->var.VariableName, efi_name_old, - utf16_strlen(efi_name_old))) + if (ucs2_strncmp(entry->var.VariableName, efi_name_old, + ucs2_strlen(efi_name_old))) continue; } @@ -1573,8 +1517,8 @@ static ssize_t efivar_create(struct file *filp, struct kobject *kobj, * Does this variable already exist? */ list_for_each_entry_safe(search_efivar, n, &efivars->list, list) { - strsize1 = utf16_strsize(search_efivar->var.VariableName, 1024); - strsize2 = utf16_strsize(new_var->VariableName, 1024); + strsize1 = ucs2_strsize(search_efivar->var.VariableName, 1024); + strsize2 = ucs2_strsize(new_var->VariableName, 1024); if (strsize1 == strsize2 && !memcmp(&(search_efivar->var.VariableName), new_var->VariableName, strsize1) && @@ -1590,7 +1534,7 @@ static ssize_t efivar_create(struct file *filp, struct kobject *kobj, } status = check_var_size_locked(efivars, new_var->Attributes, - new_var->DataSize + utf16_strsize(new_var->VariableName, 1024)); + new_var->DataSize + ucs2_strsize(new_var->VariableName, 1024)); if (status && status != EFI_UNSUPPORTED) { spin_unlock_irq(&efivars->lock); @@ -1614,7 +1558,7 @@ static ssize_t efivar_create(struct file *filp, struct kobject *kobj, /* Create the entry in sysfs. Locking is not required here */ status = efivar_create_sysfs_entry(efivars, - utf16_strsize(new_var->VariableName, + ucs2_strsize(new_var->VariableName, 1024), new_var->VariableName, &new_var->VendorGuid); @@ -1644,8 +1588,8 @@ static ssize_t efivar_delete(struct file *filp, struct kobject *kobj, * Does this variable already exist? */ list_for_each_entry_safe(search_efivar, n, &efivars->list, list) { - strsize1 = utf16_strsize(search_efivar->var.VariableName, 1024); - strsize2 = utf16_strsize(del_var->VariableName, 1024); + strsize1 = ucs2_strsize(search_efivar->var.VariableName, 1024); + strsize2 = ucs2_strsize(del_var->VariableName, 1024); if (strsize1 == strsize2 && !memcmp(&(search_efivar->var.VariableName), del_var->VariableName, strsize1) && @@ -1684,16 +1628,17 @@ static ssize_t efivar_delete(struct file *filp, struct kobject *kobj, return count; } -static bool variable_is_present(efi_char16_t *variable_name, efi_guid_t *vendor) +static bool variable_is_present(struct efivars *efivars, + efi_char16_t *variable_name, + efi_guid_t *vendor) { struct efivar_entry *entry, *n; - struct efivars *efivars = &__efivars; unsigned long strsize1, strsize2; bool found = false; - strsize1 = utf16_strsize(variable_name, 1024); + strsize1 = ucs2_strsize(variable_name, 1024); list_for_each_entry_safe(entry, n, &efivars->list, list) { - strsize2 = utf16_strsize(entry->var.VariableName, 1024); + strsize2 = ucs2_strsize(entry->var.VariableName, 1024); if (strsize1 == strsize2 && !memcmp(variable_name, &(entry->var.VariableName), strsize2) && @@ -1759,8 +1704,8 @@ static void efivar_update_sysfs_entries(struct work_struct *work) if (status != EFI_SUCCESS) { break; } else { - if (!variable_is_present(variable_name, - &vendor)) { + if (!variable_is_present(efivars, + variable_name, &vendor)) { found = true; break; } @@ -2064,7 +2009,8 @@ int register_efivars(struct efivars *efivars, * we'll ever see a different variable name, * and may end up looping here forever. */ - if (variable_is_present(variable_name, &vendor_guid)) { + if (variable_is_present(efivars, variable_name, + &vendor_guid)) { dup_variable_bug(variable_name, &vendor_guid, variable_name_size); status = EFI_NOT_FOUND; @@ -2131,7 +2077,7 @@ efivars_init(void) ops.get_variable = efi.get_variable; ops.set_variable = efi.set_variable; ops.get_next_variable = efi.get_next_variable; - ops.query_variable_info = efi.query_variable_info; + ops.query_variable_store = efi_query_variable_store; error = register_efivars(&__efivars, &ops, efi_kobj); if (error) diff --git a/drivers/gpio/gpio-pl061.c b/drivers/gpio/gpio-pl061.c index b820869ca93c..d7008dfdd6f0 100644 --- a/drivers/gpio/gpio-pl061.c +++ b/drivers/gpio/gpio-pl061.c @@ -15,6 +15,7 @@ #include <linux/io.h> #include <linux/ioport.h> #include <linux/irq.h> +#include <linux/irqdomain.h> #include <linux/bitops.h> #include <linux/workqueue.h> #include <linux/gpio.h> @@ -22,6 +23,7 @@ #include <linux/amba/bus.h> #include <linux/amba/pl061.h> #include <linux/slab.h> +#include <linux/pinctrl/consumer.h> #include <linux/pm.h> #include <asm/mach/irq.h> @@ -51,8 +53,7 @@ struct pl061_gpio { spinlock_t lock; void __iomem *base; - int irq_base; - struct irq_chip_generic *irq_gc; + struct irq_domain *domain; struct gpio_chip gc; #ifdef CONFIG_PM @@ -60,6 +61,24 @@ struct pl061_gpio { #endif }; +static int pl061_gpio_request(struct gpio_chip *chip, unsigned offset) +{ + /* + * Map back to global GPIO space and request muxing, the direction + * parameter does not matter for this controller. + */ + int gpio = chip->base + offset; + + return pinctrl_request_gpio(gpio); +} + +static void pl061_gpio_free(struct gpio_chip *chip, unsigned offset) +{ + int gpio = chip->base + offset; + + pinctrl_free_gpio(gpio); +} + static int pl061_direction_input(struct gpio_chip *gc, unsigned offset) { struct pl061_gpio *chip = container_of(gc, struct pl061_gpio, gc); @@ -122,24 +141,20 @@ static int pl061_to_irq(struct gpio_chip *gc, unsigned offset) { struct pl061_gpio *chip = container_of(gc, struct pl061_gpio, gc); - if (chip->irq_base <= 0) - return -EINVAL; - - return chip->irq_base + offset; + return irq_create_mapping(chip->domain, offset); } static int pl061_irq_type(struct irq_data *d, unsigned trigger) { - struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d); - struct pl061_gpio *chip = gc->private; - int offset = d->irq - chip->irq_base; + struct pl061_gpio *chip = irq_data_get_irq_chip_data(d); + int offset = irqd_to_hwirq(d); unsigned long flags; u8 gpiois, gpioibe, gpioiev; if (offset < 0 || offset >= PL061_GPIO_NR) return -EINVAL; - raw_spin_lock_irqsave(&gc->lock, flags); + spin_lock_irqsave(&chip->lock, flags); gpioiev = readb(chip->base + GPIOIEV); @@ -168,7 +183,7 @@ static int pl061_irq_type(struct irq_data *d, unsigned trigger) writeb(gpioiev, chip->base + GPIOIEV); - raw_spin_unlock_irqrestore(&gc->lock, flags); + spin_unlock_irqrestore(&chip->lock, flags); return 0; } @@ -192,31 +207,61 @@ static void pl061_irq_handler(unsigned irq, struct irq_desc *desc) chained_irq_exit(irqchip, desc); } -static void __init pl061_init_gc(struct pl061_gpio *chip, int irq_base) +static void pl061_irq_mask(struct irq_data *d) +{ + struct pl061_gpio *chip = irq_data_get_irq_chip_data(d); + u8 mask = 1 << (irqd_to_hwirq(d) % PL061_GPIO_NR); + u8 gpioie; + + spin_lock(&chip->lock); + gpioie = readb(chip->base + GPIOIE) & ~mask; + writeb(gpioie, chip->base + GPIOIE); + spin_unlock(&chip->lock); +} + +static void pl061_irq_unmask(struct irq_data *d) { - struct irq_chip_type *ct; + struct pl061_gpio *chip = irq_data_get_irq_chip_data(d); + u8 mask = 1 << (irqd_to_hwirq(d) % PL061_GPIO_NR); + u8 gpioie; + + spin_lock(&chip->lock); + gpioie = readb(chip->base + GPIOIE) | mask; + writeb(gpioie, chip->base + GPIOIE); + spin_unlock(&chip->lock); +} + +static struct irq_chip pl061_irqchip = { + .name = "pl061 gpio", + .irq_mask = pl061_irq_mask, + .irq_unmask = pl061_irq_unmask, + .irq_set_type = pl061_irq_type, +}; - chip->irq_gc = irq_alloc_generic_chip("gpio-pl061", 1, irq_base, - chip->base, handle_simple_irq); - chip->irq_gc->private = chip; +static int pl061_irq_map(struct irq_domain *d, unsigned int virq, + irq_hw_number_t hw) +{ + struct pl061_gpio *chip = d->host_data; - ct = chip->irq_gc->chip_types; - ct->chip.irq_mask = irq_gc_mask_clr_bit; - ct->chip.irq_unmask = irq_gc_mask_set_bit; - ct->chip.irq_set_type = pl061_irq_type; - ct->chip.irq_set_wake = irq_gc_set_wake; - ct->regs.mask = GPIOIE; + irq_set_chip_and_handler_name(virq, &pl061_irqchip, handle_simple_irq, + "pl061"); + irq_set_chip_data(virq, chip); + irq_set_irq_type(virq, IRQ_TYPE_NONE); - irq_setup_generic_chip(chip->irq_gc, IRQ_MSK(PL061_GPIO_NR), - IRQ_GC_INIT_NESTED_LOCK, IRQ_NOREQUEST, 0); + return 0; } +static const struct irq_domain_ops pl061_domain_ops = { + .map = pl061_irq_map, + .xlate = irq_domain_xlate_twocell, +}; + static int pl061_probe(struct amba_device *adev, const struct amba_id *id) { struct device *dev = &adev->dev; struct pl061_platform_data *pdata = dev->platform_data; struct pl061_gpio *chip; - int ret, irq, i; + int ret, irq, i, irq_base; chip = devm_kzalloc(dev, sizeof(*chip), GFP_KERNEL); if (chip == NULL) @@ -224,24 +269,32 @@ static int pl061_probe(struct amba_device *adev, const struct amba_id *id) if (pdata) { chip->gc.base = pdata->gpio_base; - chip->irq_base = pdata->irq_base; - } else if (adev->dev.of_node) { + irq_base = pdata->irq_base; + if (irq_base <= 0) + return -ENODEV; + } else { chip->gc.base = -1; - chip->irq_base = 0; - } else - return -ENODEV; + irq_base = 0; + } if (!devm_request_mem_region(dev, adev->res.start, - resource_size(&adev->res), "pl061")) + resource_size(&adev->res), "pl061")) return -EBUSY; chip->base = devm_ioremap(dev, adev->res.start, - resource_size(&adev->res)); - if (chip->base == NULL) + resource_size(&adev->res)); + if (!chip->base) return -ENOMEM; + chip->domain = irq_domain_add_simple(adev->dev.of_node, PL061_GPIO_NR, + irq_base, &pl061_domain_ops, chip); + if (!chip->domain) + return -ENODEV; + spin_lock_init(&chip->lock); + chip->gc.request = pl061_gpio_request; + chip->gc.free = pl061_gpio_free; chip->gc.direction_input = pl061_direction_input; chip->gc.direction_output = pl061_direction_output; chip->gc.get = pl061_get_value; @@ -259,12 +312,6 @@ static int pl061_probe(struct amba_device *adev, const struct amba_id *id) /* * irq_chip support */ - - if (chip->irq_base <= 0) - return 0; - - pl061_init_gc(chip, chip->irq_base); - writeb(0, chip->base + GPIOIE); /* disable irqs */ irq = adev->irq[0]; if (irq < 0) diff --git a/drivers/gpio/gpio-pxa.c b/drivers/gpio/gpio-pxa.c index 9cc108d2b770..8325f580c0f1 100644 --- a/drivers/gpio/gpio-pxa.c +++ b/drivers/gpio/gpio-pxa.c @@ -642,7 +642,12 @@ static struct platform_driver pxa_gpio_driver = { .of_match_table = of_match_ptr(pxa_gpio_dt_ids), }, }; -module_platform_driver(pxa_gpio_driver); + +static int __init pxa_gpio_init(void) +{ + return platform_driver_register(&pxa_gpio_driver); +} +postcore_initcall(pxa_gpio_init); #ifdef CONFIG_PM static int pxa_gpio_suspend(void) diff --git a/drivers/gpio/gpiolib-of.c b/drivers/gpio/gpiolib-of.c index 5150df6cba08..465f4ca57e80 100644 --- a/drivers/gpio/gpiolib-of.c +++ b/drivers/gpio/gpiolib-of.c @@ -203,22 +203,11 @@ static void of_gpiochip_add_pin_range(struct gpio_chip *chip) if (!pctldev) break; - /* - * This assumes that the n GPIO pins are consecutive in the - * GPIO number space, and that the pins are also consecutive - * in their local number space. Currently it is not possible - * to add different ranges for one and the same GPIO chip, - * as the code assumes that we have one consecutive range - * on both, mapping 1-to-1. - * - * TODO: make the OF bindings handle multiple sparse ranges - * on the same GPIO chip. - */ ret = gpiochip_add_pin_range(chip, pinctrl_dev_get_devname(pctldev), - 0, /* offset in gpiochip */ pinspec.args[0], - pinspec.args[1]); + pinspec.args[1], + pinspec.args[2]); if (ret) break; diff --git a/drivers/gpu/drm/drm_modes.c b/drivers/gpu/drm/drm_modes.c index 04fa6f1808d1..f83f0719922e 100644 --- a/drivers/gpu/drm/drm_modes.c +++ b/drivers/gpu/drm/drm_modes.c @@ -506,7 +506,7 @@ drm_gtf_mode(struct drm_device *dev, int hdisplay, int vdisplay, int vrefresh, } EXPORT_SYMBOL(drm_gtf_mode); -#if IS_ENABLED(CONFIG_VIDEOMODE) +#ifdef CONFIG_VIDEOMODE_HELPERS int drm_display_mode_from_videomode(const struct videomode *vm, struct drm_display_mode *dmode) { @@ -523,26 +523,25 @@ int drm_display_mode_from_videomode(const struct videomode *vm, dmode->clock = vm->pixelclock / 1000; dmode->flags = 0; - if (vm->dmt_flags & VESA_DMT_HSYNC_HIGH) + if (vm->flags & DISPLAY_FLAGS_HSYNC_HIGH) dmode->flags |= DRM_MODE_FLAG_PHSYNC; - else if (vm->dmt_flags & VESA_DMT_HSYNC_LOW) + else if (vm->flags & DISPLAY_FLAGS_HSYNC_LOW) dmode->flags |= DRM_MODE_FLAG_NHSYNC; - if (vm->dmt_flags & VESA_DMT_VSYNC_HIGH) + if (vm->flags & DISPLAY_FLAGS_VSYNC_HIGH) dmode->flags |= DRM_MODE_FLAG_PVSYNC; - else if (vm->dmt_flags & VESA_DMT_VSYNC_LOW) + else if (vm->flags & DISPLAY_FLAGS_VSYNC_LOW) dmode->flags |= DRM_MODE_FLAG_NVSYNC; - if (vm->data_flags & DISPLAY_FLAGS_INTERLACED) + if (vm->flags & DISPLAY_FLAGS_INTERLACED) dmode->flags |= DRM_MODE_FLAG_INTERLACE; - if (vm->data_flags & DISPLAY_FLAGS_DOUBLESCAN) + if (vm->flags & DISPLAY_FLAGS_DOUBLESCAN) dmode->flags |= DRM_MODE_FLAG_DBLSCAN; drm_mode_set_name(dmode); return 0; } EXPORT_SYMBOL_GPL(drm_display_mode_from_videomode); -#endif -#if IS_ENABLED(CONFIG_OF_VIDEOMODE) +#ifdef CONFIG_OF /** * of_get_drm_display_mode - get a drm_display_mode from devicetree * @np: device_node with the timing specification @@ -572,7 +571,8 @@ int of_get_drm_display_mode(struct device_node *np, return 0; } EXPORT_SYMBOL_GPL(of_get_drm_display_mode); -#endif +#endif /* CONFIG_OF */ +#endif /* CONFIG_VIDEOMODE_HELPERS */ /** * drm_mode_set_name - set the name on a mode diff --git a/drivers/gpu/drm/tilcdc/Kconfig b/drivers/gpu/drm/tilcdc/Kconfig index d24d04013476..e461e9972455 100644 --- a/drivers/gpu/drm/tilcdc/Kconfig +++ b/drivers/gpu/drm/tilcdc/Kconfig @@ -4,8 +4,7 @@ config DRM_TILCDC select DRM_KMS_HELPER select DRM_KMS_CMA_HELPER select DRM_GEM_CMA_HELPER - select OF_VIDEOMODE - select OF_DISPLAY_TIMING + select VIDEOMODE_HELPERS select BACKLIGHT_CLASS_DEVICE help Choose this option if you have an TI SoC with LCDC display diff --git a/drivers/gpu/drm/tilcdc/tilcdc_panel.c b/drivers/gpu/drm/tilcdc/tilcdc_panel.c index 580b74e2022b..90ee49786372 100644 --- a/drivers/gpu/drm/tilcdc/tilcdc_panel.c +++ b/drivers/gpu/drm/tilcdc/tilcdc_panel.c @@ -173,7 +173,7 @@ static int panel_connector_get_modes(struct drm_connector *connector) struct drm_display_mode *mode = drm_mode_create(dev); struct videomode vm; - if (videomode_from_timing(timings, &vm, i)) + if (videomode_from_timings(timings, &vm, i)) break; drm_display_mode_from_videomode(&vm, mode); diff --git a/drivers/hv/Makefile b/drivers/hv/Makefile index e6abfa02d8b7..0a74b5661186 100644 --- a/drivers/hv/Makefile +++ b/drivers/hv/Makefile @@ -5,4 +5,4 @@ obj-$(CONFIG_HYPERV_BALLOON) += hv_balloon.o hv_vmbus-y := vmbus_drv.o \ hv.o connection.o channel.o \ channel_mgmt.o ring_buffer.o -hv_utils-y := hv_util.o hv_kvp.o +hv_utils-y := hv_util.o hv_kvp.o hv_snapshot.o diff --git a/drivers/hv/channel_mgmt.c b/drivers/hv/channel_mgmt.c index ff1be167eb04..bad8128b283a 100644 --- a/drivers/hv/channel_mgmt.c +++ b/drivers/hv/channel_mgmt.c @@ -165,8 +165,19 @@ static void vmbus_process_rescind_offer(struct work_struct *work) struct vmbus_channel *channel = container_of(work, struct vmbus_channel, work); + unsigned long flags; + struct vmbus_channel_relid_released msg; vmbus_device_unregister(channel->device_obj); + memset(&msg, 0, sizeof(struct vmbus_channel_relid_released)); + msg.child_relid = channel->offermsg.child_relid; + msg.header.msgtype = CHANNELMSG_RELID_RELEASED; + vmbus_post_msg(&msg, sizeof(struct vmbus_channel_relid_released)); + + spin_lock_irqsave(&vmbus_connection.channel_lock, flags); + list_del(&channel->listentry); + spin_unlock_irqrestore(&vmbus_connection.channel_lock, flags); + free_channel(channel); } void vmbus_free_channels(void) diff --git a/drivers/hv/hv.c b/drivers/hv/hv.c index 731158910c1e..ae4923756d98 100644 --- a/drivers/hv/hv.c +++ b/drivers/hv/hv.c @@ -289,9 +289,8 @@ void hv_synic_init(void *arg) /* Check the version */ rdmsrl(HV_X64_MSR_SVERSION, version); - hv_context.event_dpc[cpu] = (struct tasklet_struct *) - kmalloc(sizeof(struct tasklet_struct), - GFP_ATOMIC); + hv_context.event_dpc[cpu] = kmalloc(sizeof(struct tasklet_struct), + GFP_ATOMIC); if (hv_context.event_dpc[cpu] == NULL) { pr_err("Unable to allocate event dpc\n"); goto cleanup; diff --git a/drivers/hv/hv_balloon.c b/drivers/hv/hv_balloon.c index 37873213e24f..4c605c70ebf9 100644 --- a/drivers/hv/hv_balloon.c +++ b/drivers/hv/hv_balloon.c @@ -117,7 +117,14 @@ union dm_caps { struct { __u64 balloon:1; __u64 hot_add:1; - __u64 reservedz:62; + /* + * To support guests that may have alignment + * limitations on hot-add, the guest can specify + * its alignment requirements; a value of n + * represents an alignment of 2^n in mega bytes. + */ + __u64 hot_add_alignment:4; + __u64 reservedz:58; } cap_bits; __u64 caps; } __packed; @@ -412,13 +419,45 @@ struct dm_info_msg { * End protocol definitions. */ -static bool hot_add; +/* + * State to manage hot adding memory into the guest. + * The range start_pfn : end_pfn specifies the range + * that the host has asked us to hot add. The range + * start_pfn : ha_end_pfn specifies the range that we have + * currently hot added. We hot add in multiples of 128M + * chunks; it is possible that we may not be able to bring + * online all the pages in the region. The range + * covered_start_pfn : covered_end_pfn defines the pages that can + * be brough online. + */ + +struct hv_hotadd_state { + struct list_head list; + unsigned long start_pfn; + unsigned long covered_start_pfn; + unsigned long covered_end_pfn; + unsigned long ha_end_pfn; + unsigned long end_pfn; +}; + +struct balloon_state { + __u32 num_pages; + struct work_struct wrk; +}; + +struct hot_add_wrk { + union dm_mem_page_range ha_page_range; + union dm_mem_page_range ha_region_range; + struct work_struct wrk; +}; + +static bool hot_add = true; static bool do_hot_add; /* * Delay reporting memory pressure by * the specified number of seconds. */ -static uint pressure_report_delay = 30; +static uint pressure_report_delay = 45; module_param(hot_add, bool, (S_IRUGO | S_IWUSR)); MODULE_PARM_DESC(hot_add, "If set attempt memory hot_add"); @@ -446,6 +485,7 @@ enum hv_dm_state { static __u8 recv_buffer[PAGE_SIZE]; static __u8 *send_buffer; #define PAGES_IN_2M 512 +#define HA_CHUNK (32 * 1024) struct hv_dynmem_device { struct hv_device *dev; @@ -459,7 +499,28 @@ struct hv_dynmem_device { unsigned int num_pages_ballooned; /* - * This thread handles both balloon/hot-add + * State to manage the ballooning (up) operation. + */ + struct balloon_state balloon_wrk; + + /* + * State to execute the "hot-add" operation. + */ + struct hot_add_wrk ha_wrk; + + /* + * This state tracks if the host has specified a hot-add + * region. + */ + bool host_specified_ha_region; + + /* + * State to synchronize hot-add. + */ + struct completion ol_waitevent; + bool ha_waiting; + /* + * This thread handles hot-add * requests from the host as well as notifying * the host with regards to memory pressure in * the guest. @@ -467,6 +528,11 @@ struct hv_dynmem_device { struct task_struct *thread; /* + * A list of hot-add regions. + */ + struct list_head ha_region_list; + + /* * We start with the highest version we can support * and downgrade based on the host; we save here the * next version to try. @@ -476,35 +542,358 @@ struct hv_dynmem_device { static struct hv_dynmem_device dm_device; -static void hot_add_req(struct hv_dynmem_device *dm, struct dm_hot_add *msg) +#ifdef CONFIG_MEMORY_HOTPLUG + +static void hv_bring_pgs_online(unsigned long start_pfn, unsigned long size) { + int i; - struct dm_hot_add_response resp; + for (i = 0; i < size; i++) { + struct page *pg; + pg = pfn_to_page(start_pfn + i); + __online_page_set_limits(pg); + __online_page_increment_counters(pg); + __online_page_free(pg); + } +} - if (do_hot_add) { +static void hv_mem_hot_add(unsigned long start, unsigned long size, + unsigned long pfn_count, + struct hv_hotadd_state *has) +{ + int ret = 0; + int i, nid, t; + unsigned long start_pfn; + unsigned long processed_pfn; + unsigned long total_pfn = pfn_count; + + for (i = 0; i < (size/HA_CHUNK); i++) { + start_pfn = start + (i * HA_CHUNK); + has->ha_end_pfn += HA_CHUNK; + + if (total_pfn > HA_CHUNK) { + processed_pfn = HA_CHUNK; + total_pfn -= HA_CHUNK; + } else { + processed_pfn = total_pfn; + total_pfn = 0; + } + + has->covered_end_pfn += processed_pfn; + + init_completion(&dm_device.ol_waitevent); + dm_device.ha_waiting = true; + + nid = memory_add_physaddr_to_nid(PFN_PHYS(start_pfn)); + ret = add_memory(nid, PFN_PHYS((start_pfn)), + (HA_CHUNK << PAGE_SHIFT)); - pr_info("Memory hot add not supported\n"); + if (ret) { + pr_info("hot_add memory failed error is %d\n", ret); + if (ret == -EEXIST) { + /* + * This error indicates that the error + * is not a transient failure. This is the + * case where the guest's physical address map + * precludes hot adding memory. Stop all further + * memory hot-add. + */ + do_hot_add = false; + } + has->ha_end_pfn -= HA_CHUNK; + has->covered_end_pfn -= processed_pfn; + break; + } /* - * Currently we do not support hot add. - * Just fail the request. + * Wait for the memory block to be onlined. */ + t = wait_for_completion_timeout(&dm_device.ol_waitevent, 5*HZ); + if (t == 0) { + pr_info("hot_add memory timedout\n"); + has->ha_end_pfn -= HA_CHUNK; + has->covered_end_pfn -= processed_pfn; + break; + } + + } + + return; +} + +static void hv_online_page(struct page *pg) +{ + struct list_head *cur; + struct hv_hotadd_state *has; + unsigned long cur_start_pgp; + unsigned long cur_end_pgp; + + if (dm_device.ha_waiting) { + dm_device.ha_waiting = false; + complete(&dm_device.ol_waitevent); + } + + list_for_each(cur, &dm_device.ha_region_list) { + has = list_entry(cur, struct hv_hotadd_state, list); + cur_start_pgp = (unsigned long) + pfn_to_page(has->covered_start_pfn); + cur_end_pgp = (unsigned long)pfn_to_page(has->covered_end_pfn); + + if (((unsigned long)pg >= cur_start_pgp) && + ((unsigned long)pg < cur_end_pgp)) { + /* + * This frame is currently backed; online the + * page. + */ + __online_page_set_limits(pg); + __online_page_increment_counters(pg); + __online_page_free(pg); + has->covered_start_pfn++; + } + } +} + +static bool pfn_covered(unsigned long start_pfn, unsigned long pfn_cnt) +{ + struct list_head *cur; + struct hv_hotadd_state *has; + unsigned long residual, new_inc; + + if (list_empty(&dm_device.ha_region_list)) + return false; + + list_for_each(cur, &dm_device.ha_region_list) { + has = list_entry(cur, struct hv_hotadd_state, list); + + /* + * If the pfn range we are dealing with is not in the current + * "hot add block", move on. + */ + if ((start_pfn >= has->end_pfn)) + continue; + /* + * If the current hot add-request extends beyond + * our current limit; extend it. + */ + if ((start_pfn + pfn_cnt) > has->end_pfn) { + residual = (start_pfn + pfn_cnt - has->end_pfn); + /* + * Extend the region by multiples of HA_CHUNK. + */ + new_inc = (residual / HA_CHUNK) * HA_CHUNK; + if (residual % HA_CHUNK) + new_inc += HA_CHUNK; + + has->end_pfn += new_inc; + } + + /* + * If the current start pfn is not where the covered_end + * is, update it. + */ + + if (has->covered_end_pfn != start_pfn) { + has->covered_end_pfn = start_pfn; + has->covered_start_pfn = start_pfn; + } + return true; + } + return false; +} + +static unsigned long handle_pg_range(unsigned long pg_start, + unsigned long pg_count) +{ + unsigned long start_pfn = pg_start; + unsigned long pfn_cnt = pg_count; + unsigned long size; + struct list_head *cur; + struct hv_hotadd_state *has; + unsigned long pgs_ol = 0; + unsigned long old_covered_state; + + if (list_empty(&dm_device.ha_region_list)) + return 0; + + list_for_each(cur, &dm_device.ha_region_list) { + has = list_entry(cur, struct hv_hotadd_state, list); + + /* + * If the pfn range we are dealing with is not in the current + * "hot add block", move on. + */ + if ((start_pfn >= has->end_pfn)) + continue; + + old_covered_state = has->covered_end_pfn; + + if (start_pfn < has->ha_end_pfn) { + /* + * This is the case where we are backing pages + * in an already hot added region. Bring + * these pages online first. + */ + pgs_ol = has->ha_end_pfn - start_pfn; + if (pgs_ol > pfn_cnt) + pgs_ol = pfn_cnt; + hv_bring_pgs_online(start_pfn, pgs_ol); + has->covered_end_pfn += pgs_ol; + has->covered_start_pfn += pgs_ol; + pfn_cnt -= pgs_ol; + } + + if ((has->ha_end_pfn < has->end_pfn) && (pfn_cnt > 0)) { + /* + * We have some residual hot add range + * that needs to be hot added; hot add + * it now. Hot add a multiple of + * of HA_CHUNK that fully covers the pages + * we have. + */ + size = (has->end_pfn - has->ha_end_pfn); + if (pfn_cnt <= size) { + size = ((pfn_cnt / HA_CHUNK) * HA_CHUNK); + if (pfn_cnt % HA_CHUNK) + size += HA_CHUNK; + } else { + pfn_cnt = size; + } + hv_mem_hot_add(has->ha_end_pfn, size, pfn_cnt, has); + } + /* + * If we managed to online any pages that were given to us, + * we declare success. + */ + return has->covered_end_pfn - old_covered_state; + + } + + return 0; +} + +static unsigned long process_hot_add(unsigned long pg_start, + unsigned long pfn_cnt, + unsigned long rg_start, + unsigned long rg_size) +{ + struct hv_hotadd_state *ha_region = NULL; + + if (pfn_cnt == 0) + return 0; + + if (!dm_device.host_specified_ha_region) + if (pfn_covered(pg_start, pfn_cnt)) + goto do_pg_range; + + /* + * If the host has specified a hot-add range; deal with it first. + */ + + if (rg_size != 0) { + ha_region = kzalloc(sizeof(struct hv_hotadd_state), GFP_KERNEL); + if (!ha_region) + return 0; + + INIT_LIST_HEAD(&ha_region->list); + + list_add_tail(&ha_region->list, &dm_device.ha_region_list); + ha_region->start_pfn = rg_start; + ha_region->ha_end_pfn = rg_start; + ha_region->covered_start_pfn = pg_start; + ha_region->covered_end_pfn = pg_start; + ha_region->end_pfn = rg_start + rg_size; + } + +do_pg_range: + /* + * Process the page range specified; bringing them + * online if possible. + */ + return handle_pg_range(pg_start, pfn_cnt); +} + +#endif + +static void hot_add_req(struct work_struct *dummy) +{ + struct dm_hot_add_response resp; +#ifdef CONFIG_MEMORY_HOTPLUG + unsigned long pg_start, pfn_cnt; + unsigned long rg_start, rg_sz; +#endif + struct hv_dynmem_device *dm = &dm_device; + memset(&resp, 0, sizeof(struct dm_hot_add_response)); resp.hdr.type = DM_MEM_HOT_ADD_RESPONSE; resp.hdr.size = sizeof(struct dm_hot_add_response); resp.hdr.trans_id = atomic_inc_return(&trans_id); - resp.page_count = 0; - resp.result = 0; +#ifdef CONFIG_MEMORY_HOTPLUG + pg_start = dm->ha_wrk.ha_page_range.finfo.start_page; + pfn_cnt = dm->ha_wrk.ha_page_range.finfo.page_cnt; + + rg_start = dm->ha_wrk.ha_region_range.finfo.start_page; + rg_sz = dm->ha_wrk.ha_region_range.finfo.page_cnt; + + if ((rg_start == 0) && (!dm->host_specified_ha_region)) { + unsigned long region_size; + unsigned long region_start; + + /* + * The host has not specified the hot-add region. + * Based on the hot-add page range being specified, + * compute a hot-add region that can cover the pages + * that need to be hot-added while ensuring the alignment + * and size requirements of Linux as it relates to hot-add. + */ + region_start = pg_start; + region_size = (pfn_cnt / HA_CHUNK) * HA_CHUNK; + if (pfn_cnt % HA_CHUNK) + region_size += HA_CHUNK; + + region_start = (pg_start / HA_CHUNK) * HA_CHUNK; + + rg_start = region_start; + rg_sz = region_size; + } + + if (do_hot_add) + resp.page_count = process_hot_add(pg_start, pfn_cnt, + rg_start, rg_sz); +#endif + /* + * The result field of the response structure has the + * following semantics: + * + * 1. If all or some pages hot-added: Guest should return success. + * + * 2. If no pages could be hot-added: + * + * If the guest returns success, then the host + * will not attempt any further hot-add operations. This + * signifies a permanent failure. + * + * If the guest returns failure, then this failure will be + * treated as a transient failure and the host may retry the + * hot-add operation after some delay. + */ + if (resp.page_count > 0) + resp.result = 1; + else if (!do_hot_add) + resp.result = 1; + else + resp.result = 0; + + if (!do_hot_add || (resp.page_count == 0)) + pr_info("Memory hot add failed\n"); dm->state = DM_INITIALIZED; vmbus_sendpacket(dm->dev->channel, &resp, sizeof(struct dm_hot_add_response), (unsigned long)NULL, VM_PKT_DATA_INBAND, 0); - } static void process_info(struct hv_dynmem_device *dm, struct dm_info_msg *msg) @@ -523,7 +912,7 @@ static void process_info(struct hv_dynmem_device *dm, struct dm_info_msg *msg) } } -unsigned long compute_balloon_floor(void) +static unsigned long compute_balloon_floor(void) { unsigned long min_pages; #define MB2PAGES(mb) ((mb) << (20 - PAGE_SHIFT)) @@ -644,6 +1033,14 @@ static int alloc_balloon_pages(struct hv_dynmem_device *dm, int num_pages, dm->num_pages_ballooned += alloc_unit; + /* + * If we allocatted 2M pages; split them so we + * can free them in any order we get. + */ + + if (alloc_unit != 1) + split_page(pg, get_order(alloc_unit << PAGE_SHIFT)); + bl_resp->range_count++; bl_resp->range_array[i].finfo.start_page = page_to_pfn(pg); @@ -657,9 +1054,9 @@ static int alloc_balloon_pages(struct hv_dynmem_device *dm, int num_pages, -static void balloon_up(struct hv_dynmem_device *dm, struct dm_balloon *req) +static void balloon_up(struct work_struct *dummy) { - int num_pages = req->num_pages; + int num_pages = dm_device.balloon_wrk.num_pages; int num_ballooned = 0; struct dm_balloon_response *bl_resp; int alloc_unit; @@ -670,9 +1067,10 @@ static void balloon_up(struct hv_dynmem_device *dm, struct dm_balloon *req) /* - * Currently, we only support 4k allocations. + * We will attempt 2M allocations. However, if we fail to + * allocate 2M chunks, we will go back to 4k allocations. */ - alloc_unit = 1; + alloc_unit = 512; while (!done) { bl_resp = (struct dm_balloon_response *)send_buffer; @@ -684,14 +1082,19 @@ static void balloon_up(struct hv_dynmem_device *dm, struct dm_balloon *req) num_pages -= num_ballooned; - num_ballooned = alloc_balloon_pages(dm, num_pages, + num_ballooned = alloc_balloon_pages(&dm_device, num_pages, bl_resp, alloc_unit, &alloc_error); + if ((alloc_error) && (alloc_unit != 1)) { + alloc_unit = 1; + continue; + } + if ((alloc_error) || (num_ballooned == num_pages)) { bl_resp->more_pages = 0; done = true; - dm->state = DM_INITIALIZED; + dm_device.state = DM_INITIALIZED; } /* @@ -719,7 +1122,7 @@ static void balloon_up(struct hv_dynmem_device *dm, struct dm_balloon *req) pr_info("Balloon response failed\n"); for (i = 0; i < bl_resp->range_count; i++) - free_balloon_pages(dm, + free_balloon_pages(&dm_device, &bl_resp->range_array[i]); done = true; @@ -761,7 +1164,6 @@ static int dm_thread_func(void *dm_dev) { struct hv_dynmem_device *dm = dm_dev; int t; - unsigned long scan_start; while (!kthread_should_stop()) { t = wait_for_completion_timeout(&dm_device.config_event, 1*HZ); @@ -773,22 +1175,6 @@ static int dm_thread_func(void *dm_dev) if (t == 0) post_status(dm); - scan_start = jiffies; - switch (dm->state) { - case DM_BALLOON_UP: - balloon_up(dm, (struct dm_balloon *)recv_buffer); - break; - - case DM_HOT_ADD: - hot_add_req(dm, (struct dm_hot_add *)recv_buffer); - break; - default: - break; - } - - if (!time_in_range(jiffies, scan_start, scan_start + HZ)) - post_status(dm); - } return 0; @@ -861,6 +1247,10 @@ static void balloon_onchannelcallback(void *context) struct dm_message *dm_msg; struct dm_header *dm_hdr; struct hv_dynmem_device *dm = hv_get_drvdata(dev); + struct dm_balloon *bal_msg; + struct dm_hot_add *ha_msg; + union dm_mem_page_range *ha_pg_range; + union dm_mem_page_range *ha_region; memset(recv_buffer, 0, sizeof(recv_buffer)); vmbus_recvpacket(dev->channel, recv_buffer, @@ -882,8 +1272,12 @@ static void balloon_onchannelcallback(void *context) break; case DM_BALLOON_REQUEST: + if (dm->state == DM_BALLOON_UP) + pr_warn("Currently ballooning\n"); + bal_msg = (struct dm_balloon *)recv_buffer; dm->state = DM_BALLOON_UP; - complete(&dm->config_event); + dm_device.balloon_wrk.num_pages = bal_msg->num_pages; + schedule_work(&dm_device.balloon_wrk.wrk); break; case DM_UNBALLOON_REQUEST: @@ -893,8 +1287,31 @@ static void balloon_onchannelcallback(void *context) break; case DM_MEM_HOT_ADD_REQUEST: + if (dm->state == DM_HOT_ADD) + pr_warn("Currently hot-adding\n"); dm->state = DM_HOT_ADD; - complete(&dm->config_event); + ha_msg = (struct dm_hot_add *)recv_buffer; + if (ha_msg->hdr.size == sizeof(struct dm_hot_add)) { + /* + * This is a normal hot-add request specifying + * hot-add memory. + */ + ha_pg_range = &ha_msg->range; + dm->ha_wrk.ha_page_range = *ha_pg_range; + dm->ha_wrk.ha_region_range.page_range = 0; + } else { + /* + * Host is specifying that we first hot-add + * a region and then partially populate this + * region. + */ + dm->host_specified_ha_region = true; + ha_pg_range = &ha_msg->range; + ha_region = &ha_pg_range[1]; + dm->ha_wrk.ha_page_range = *ha_pg_range; + dm->ha_wrk.ha_region_range = *ha_region; + } + schedule_work(&dm_device.ha_wrk.wrk); break; case DM_INFO_MESSAGE: @@ -937,6 +1354,10 @@ static int balloon_probe(struct hv_device *dev, dm_device.next_version = DYNMEM_PROTOCOL_VERSION_WIN7; init_completion(&dm_device.host_event); init_completion(&dm_device.config_event); + INIT_LIST_HEAD(&dm_device.ha_region_list); + INIT_WORK(&dm_device.balloon_wrk.wrk, balloon_up); + INIT_WORK(&dm_device.ha_wrk.wrk, hot_add_req); + dm_device.host_specified_ha_region = false; dm_device.thread = kthread_run(dm_thread_func, &dm_device, "hv_balloon"); @@ -945,6 +1366,10 @@ static int balloon_probe(struct hv_device *dev, goto probe_error1; } +#ifdef CONFIG_MEMORY_HOTPLUG + set_online_page_callback(&hv_online_page); +#endif + hv_set_drvdata(dev, &dm_device); /* * Initiate the hand shake with the host and negotiate @@ -962,8 +1387,7 @@ static int balloon_probe(struct hv_device *dev, ret = vmbus_sendpacket(dev->channel, &version_req, sizeof(struct dm_version_request), (unsigned long)NULL, - VM_PKT_DATA_INBAND, - VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED); + VM_PKT_DATA_INBAND, 0); if (ret) goto probe_error2; @@ -990,13 +1414,13 @@ static int balloon_probe(struct hv_device *dev, cap_msg.hdr.trans_id = atomic_inc_return(&trans_id); cap_msg.caps.cap_bits.balloon = 1; + cap_msg.caps.cap_bits.hot_add = 1; + /* - * While we currently don't support hot-add, - * we still advertise this capability since the - * host requires that guests partcipating in the - * dynamic memory protocol support hot add. + * Specify our alignment requirements as it relates + * memory hot-add. Specify 128MB alignment. */ - cap_msg.caps.cap_bits.hot_add = 1; + cap_msg.caps.cap_bits.hot_add_alignment = 7; /* * Currently the host does not use these @@ -1009,8 +1433,7 @@ static int balloon_probe(struct hv_device *dev, ret = vmbus_sendpacket(dev->channel, &cap_msg, sizeof(struct dm_capabilities), (unsigned long)NULL, - VM_PKT_DATA_INBAND, - VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED); + VM_PKT_DATA_INBAND, 0); if (ret) goto probe_error2; @@ -1034,6 +1457,9 @@ static int balloon_probe(struct hv_device *dev, return 0; probe_error2: +#ifdef CONFIG_MEMORY_HOTPLUG + restore_online_page_callback(&hv_online_page); +#endif kthread_stop(dm_device.thread); probe_error1: @@ -1046,13 +1472,26 @@ probe_error0: static int balloon_remove(struct hv_device *dev) { struct hv_dynmem_device *dm = hv_get_drvdata(dev); + struct list_head *cur, *tmp; + struct hv_hotadd_state *has; if (dm->num_pages_ballooned != 0) pr_warn("Ballooned pages: %d\n", dm->num_pages_ballooned); + cancel_work_sync(&dm->balloon_wrk.wrk); + cancel_work_sync(&dm->ha_wrk.wrk); + vmbus_close(dev->channel); kthread_stop(dm->thread); kfree(send_buffer); +#ifdef CONFIG_MEMORY_HOTPLUG + restore_online_page_callback(&hv_online_page); +#endif + list_for_each_safe(cur, tmp, &dm->ha_region_list) { + has = list_entry(cur, struct hv_hotadd_state, list); + list_del(&has->list); + kfree(has); + } return 0; } @@ -1079,14 +1518,7 @@ static int __init init_balloon_drv(void) return vmbus_driver_register(&balloon_drv); } -static void exit_balloon_drv(void) -{ - - vmbus_driver_unregister(&balloon_drv); -} - module_init(init_balloon_drv); -module_exit(exit_balloon_drv); MODULE_DESCRIPTION("Hyper-V Balloon"); MODULE_VERSION(HV_DRV_VERSION); diff --git a/drivers/hv/hv_snapshot.c b/drivers/hv/hv_snapshot.c new file mode 100644 index 000000000000..8ad5653ce447 --- /dev/null +++ b/drivers/hv/hv_snapshot.c @@ -0,0 +1,287 @@ +/* + * An implementation of host initiated guest snapshot. + * + * + * Copyright (C) 2013, Microsoft, Inc. + * Author : K. Y. Srinivasan <kys@microsoft.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, GOOD TITLE or + * NON INFRINGEMENT. See the GNU General Public License for more + * details. + * + */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include <linux/net.h> +#include <linux/nls.h> +#include <linux/connector.h> +#include <linux/workqueue.h> +#include <linux/hyperv.h> + + + +/* + * Global state maintained for transaction that is being processed. + * Note that only one transaction can be active at any point in time. + * + * This state is set when we receive a request from the host; we + * cleanup this state when the transaction is completed - when we respond + * to the host with the key value. + */ + +static struct { + bool active; /* transaction status - active or not */ + int recv_len; /* number of bytes received. */ + struct vmbus_channel *recv_channel; /* chn we got the request */ + u64 recv_req_id; /* request ID. */ + struct hv_vss_msg *msg; /* current message */ +} vss_transaction; + + +static void vss_respond_to_host(int error); + +static struct cb_id vss_id = { CN_VSS_IDX, CN_VSS_VAL }; +static const char vss_name[] = "vss_kernel_module"; +static __u8 *recv_buffer; + +static void vss_send_op(struct work_struct *dummy); +static DECLARE_WORK(vss_send_op_work, vss_send_op); + +/* + * Callback when data is received from user mode. + */ + +static void +vss_cn_callback(struct cn_msg *msg, struct netlink_skb_parms *nsp) +{ + struct hv_vss_msg *vss_msg; + + vss_msg = (struct hv_vss_msg *)msg->data; + + if (vss_msg->vss_hdr.operation == VSS_OP_REGISTER) { + pr_info("VSS daemon registered\n"); + vss_transaction.active = false; + if (vss_transaction.recv_channel != NULL) + hv_vss_onchannelcallback(vss_transaction.recv_channel); + return; + + } + vss_respond_to_host(vss_msg->error); +} + + +static void vss_send_op(struct work_struct *dummy) +{ + int op = vss_transaction.msg->vss_hdr.operation; + struct cn_msg *msg; + struct hv_vss_msg *vss_msg; + + msg = kzalloc(sizeof(*msg) + sizeof(*vss_msg), GFP_ATOMIC); + if (!msg) + return; + + vss_msg = (struct hv_vss_msg *)msg->data; + + msg->id.idx = CN_VSS_IDX; + msg->id.val = CN_VSS_VAL; + + vss_msg->vss_hdr.operation = op; + msg->len = sizeof(struct hv_vss_msg); + + cn_netlink_send(msg, 0, GFP_ATOMIC); + kfree(msg); + + return; +} + +/* + * Send a response back to the host. + */ + +static void +vss_respond_to_host(int error) +{ + struct icmsg_hdr *icmsghdrp; + u32 buf_len; + struct vmbus_channel *channel; + u64 req_id; + + /* + * If a transaction is not active; log and return. + */ + + if (!vss_transaction.active) { + /* + * This is a spurious call! + */ + pr_warn("VSS: Transaction not active\n"); + return; + } + /* + * Copy the global state for completing the transaction. Note that + * only one transaction can be active at a time. + */ + + buf_len = vss_transaction.recv_len; + channel = vss_transaction.recv_channel; + req_id = vss_transaction.recv_req_id; + vss_transaction.active = false; + + icmsghdrp = (struct icmsg_hdr *) + &recv_buffer[sizeof(struct vmbuspipe_hdr)]; + + if (channel->onchannel_callback == NULL) + /* + * We have raced with util driver being unloaded; + * silently return. + */ + return; + + icmsghdrp->status = error; + + icmsghdrp->icflags = ICMSGHDRFLAG_TRANSACTION | ICMSGHDRFLAG_RESPONSE; + + vmbus_sendpacket(channel, recv_buffer, buf_len, req_id, + VM_PKT_DATA_INBAND, 0); + +} + +/* + * This callback is invoked when we get a VSS message from the host. + * The host ensures that only one VSS transaction can be active at a time. + */ + +void hv_vss_onchannelcallback(void *context) +{ + struct vmbus_channel *channel = context; + u32 recvlen; + u64 requestid; + struct hv_vss_msg *vss_msg; + + + struct icmsg_hdr *icmsghdrp; + struct icmsg_negotiate *negop = NULL; + + if (vss_transaction.active) { + /* + * We will defer processing this callback once + * the current transaction is complete. + */ + vss_transaction.recv_channel = channel; + return; + } + + vmbus_recvpacket(channel, recv_buffer, PAGE_SIZE * 2, &recvlen, + &requestid); + + if (recvlen > 0) { + icmsghdrp = (struct icmsg_hdr *)&recv_buffer[ + sizeof(struct vmbuspipe_hdr)]; + + if (icmsghdrp->icmsgtype == ICMSGTYPE_NEGOTIATE) { + vmbus_prep_negotiate_resp(icmsghdrp, negop, + recv_buffer, MAX_SRV_VER, MAX_SRV_VER); + /* + * We currently negotiate the highest number the + * host has presented. If this version is not + * atleast 5.0, reject. + */ + negop = (struct icmsg_negotiate *)&recv_buffer[ + sizeof(struct vmbuspipe_hdr) + + sizeof(struct icmsg_hdr)]; + + if (negop->icversion_data[1].major < 5) + negop->icframe_vercnt = 0; + } else { + vss_msg = (struct hv_vss_msg *)&recv_buffer[ + sizeof(struct vmbuspipe_hdr) + + sizeof(struct icmsg_hdr)]; + + /* + * Stash away this global state for completing the + * transaction; note transactions are serialized. + */ + + vss_transaction.recv_len = recvlen; + vss_transaction.recv_channel = channel; + vss_transaction.recv_req_id = requestid; + vss_transaction.active = true; + vss_transaction.msg = (struct hv_vss_msg *)vss_msg; + + switch (vss_msg->vss_hdr.operation) { + /* + * Initiate a "freeze/thaw" + * operation in the guest. + * We respond to the host once + * the operation is complete. + * + * We send the message to the + * user space daemon and the + * operation is performed in + * the daemon. + */ + case VSS_OP_FREEZE: + case VSS_OP_THAW: + schedule_work(&vss_send_op_work); + return; + + case VSS_OP_HOT_BACKUP: + vss_msg->vss_cf.flags = + VSS_HBU_NO_AUTO_RECOVERY; + vss_respond_to_host(0); + return; + + case VSS_OP_GET_DM_INFO: + vss_msg->dm_info.flags = 0; + vss_respond_to_host(0); + return; + + default: + vss_respond_to_host(0); + return; + + } + + } + + icmsghdrp->icflags = ICMSGHDRFLAG_TRANSACTION + | ICMSGHDRFLAG_RESPONSE; + + vmbus_sendpacket(channel, recv_buffer, + recvlen, requestid, + VM_PKT_DATA_INBAND, 0); + } + +} + +int +hv_vss_init(struct hv_util_service *srv) +{ + int err; + + err = cn_add_callback(&vss_id, vss_name, vss_cn_callback); + if (err) + return err; + recv_buffer = srv->recv_buffer; + + /* + * When this driver loads, the user level daemon that + * processes the host requests may not yet be running. + * Defer processing channel callbacks until the daemon + * has registered. + */ + vss_transaction.active = true; + return 0; +} + +void hv_vss_deinit(void) +{ + cn_del_callback(&vss_id); + cancel_work_sync(&vss_send_op_work); +} diff --git a/drivers/hv/hv_util.c b/drivers/hv/hv_util.c index 1d4cbd8e8261..2f561c5dfe24 100644 --- a/drivers/hv/hv_util.c +++ b/drivers/hv/hv_util.c @@ -49,6 +49,12 @@ static struct hv_util_service util_kvp = { .util_deinit = hv_kvp_deinit, }; +static struct hv_util_service util_vss = { + .util_cb = hv_vss_onchannelcallback, + .util_init = hv_vss_init, + .util_deinit = hv_vss_deinit, +}; + static void perform_shutdown(struct work_struct *dummy) { orderly_poweroff(true); @@ -339,6 +345,10 @@ static const struct hv_vmbus_device_id id_table[] = { { HV_KVP_GUID, .driver_data = (unsigned long)&util_kvp }, + /* VSS GUID */ + { HV_VSS_GUID, + .driver_data = (unsigned long)&util_vss + }, { }, }; diff --git a/drivers/hv/ring_buffer.c b/drivers/hv/ring_buffer.c index cafa72ffdc30..d6fbb5772b8d 100644 --- a/drivers/hv/ring_buffer.c +++ b/drivers/hv/ring_buffer.c @@ -71,6 +71,7 @@ u32 hv_end_read(struct hv_ring_buffer_info *rbi) static bool hv_need_to_signal(u32 old_write, struct hv_ring_buffer_info *rbi) { + smp_mb(); if (rbi->ring_buffer->interrupt_mask) return false; diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index effbd11ff5ec..f61d98a62967 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -179,9 +179,29 @@ config SENSORS_ADM9240 This driver can also be built as a module. If so, the module will be called adm9240. +config SENSORS_ADT7X10 + tristate + help + This module contains common code shared by the ADT7310/ADT7320 and + ADT7410/ADT7420 temperature monitoring chip drivers. + + If build as a module, the module will be called adt7x10. + +config SENSORS_ADT7310 + tristate "Analog Devices ADT7310/ADT7320" + depends on SPI_MASTER + select SENSORS_ADT7X10 + help + If you say yes here you get support for the Analog Devices + ADT7310 and ADT7320 temperature monitoring chips. + + This driver can also be built as a module. If so, the module + will be called adt7310. + config SENSORS_ADT7410 tristate "Analog Devices ADT7410/ADT7420" depends on I2C + select SENSORS_ADT7X10 help If you say yes here you get support for the Analog Devices ADT7410 and ADT7420 temperature monitoring chips. @@ -760,6 +780,16 @@ config SENSORS_LTC4261 This driver can also be built as a module. If so, the module will be called ltc4261. +config SENSORS_LM95234 + tristate "National Semiconductor LM95234" + depends on I2C + help + If you say yes here you get support for the LM95234 temperature + sensor. + + This driver can also be built as a module. If so, the module + will be called lm95234. + config SENSORS_LM95241 tristate "National Semiconductor LM95241 and compatibles" depends on I2C @@ -886,8 +916,22 @@ config SENSORS_MCP3021 This driver can also be built as a module. If so, the module will be called mcp3021. +config SENSORS_NCT6775 + tristate "Nuvoton NCT6775F and compatibles" + depends on !PPC + select HWMON_VID + help + If you say yes here you get support for the hardware monitoring + functionality of the Nuvoton NCT6775F, NCT6776F, NCT6779D + and compatible Super-I/O chips. This driver replaces the + w83627ehf driver for NCT6775F and NCT6776F. + + This driver can also be built as a module. If so, the module + will be called nct6775. + config SENSORS_NTC_THERMISTOR tristate "NTC thermistor support" + depends on (!OF && !IIO) || (OF && IIO) help This driver supports NTC thermistors sensor reading and its interpretation. The driver can also monitor the temperature and @@ -1213,8 +1257,8 @@ config SENSORS_TMP401 tristate "Texas Instruments TMP401 and compatibles" depends on I2C help - If you say yes here you get support for Texas Instruments TMP401 and - TMP411 temperature sensor chips. + If you say yes here you get support for Texas Instruments TMP401, + TMP411, TMP431, and TMP432 temperature sensor chips. This driver can also be built as a module. If so, the module will be called tmp401. diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile index a87fdabddfff..c51b0dc35dc8 100644 --- a/drivers/hwmon/Makefile +++ b/drivers/hwmon/Makefile @@ -34,6 +34,8 @@ obj-$(CONFIG_SENSORS_ADM9240) += adm9240.o obj-$(CONFIG_SENSORS_ADS1015) += ads1015.o obj-$(CONFIG_SENSORS_ADS7828) += ads7828.o obj-$(CONFIG_SENSORS_ADS7871) += ads7871.o +obj-$(CONFIG_SENSORS_ADT7X10) += adt7x10.o +obj-$(CONFIG_SENSORS_ADT7310) += adt7310.o obj-$(CONFIG_SENSORS_ADT7410) += adt7410.o obj-$(CONFIG_SENSORS_ADT7411) += adt7411.o obj-$(CONFIG_SENSORS_ADT7462) += adt7462.o @@ -87,6 +89,7 @@ obj-$(CONFIG_SENSORS_LM87) += lm87.o obj-$(CONFIG_SENSORS_LM90) += lm90.o obj-$(CONFIG_SENSORS_LM92) += lm92.o obj-$(CONFIG_SENSORS_LM93) += lm93.o +obj-$(CONFIG_SENSORS_LM95234) += lm95234.o obj-$(CONFIG_SENSORS_LM95241) += lm95241.o obj-$(CONFIG_SENSORS_LM95245) += lm95245.o obj-$(CONFIG_SENSORS_LTC4151) += ltc4151.o @@ -104,6 +107,7 @@ obj-$(CONFIG_SENSORS_MAX6650) += max6650.o obj-$(CONFIG_SENSORS_MAX6697) += max6697.o obj-$(CONFIG_SENSORS_MC13783_ADC)+= mc13783-adc.o obj-$(CONFIG_SENSORS_MCP3021) += mcp3021.o +obj-$(CONFIG_SENSORS_NCT6775) += nct6775.o obj-$(CONFIG_SENSORS_NTC_THERMISTOR) += ntc_thermistor.o obj-$(CONFIG_SENSORS_PC87360) += pc87360.o obj-$(CONFIG_SENSORS_PC87427) += pc87427.o diff --git a/drivers/hwmon/abituguru.c b/drivers/hwmon/abituguru.c index 6119ff8e8c87..df0b69987914 100644 --- a/drivers/hwmon/abituguru.c +++ b/drivers/hwmon/abituguru.c @@ -96,9 +96,12 @@ #define ABIT_UGURU_MAX_TIMEOUTS 2 /* utility macros */ #define ABIT_UGURU_NAME "abituguru" -#define ABIT_UGURU_DEBUG(level, format, arg...) \ - if (level <= verbose) \ - printk(KERN_DEBUG ABIT_UGURU_NAME ": " format , ## arg) +#define ABIT_UGURU_DEBUG(level, format, arg...) \ + do { \ + if (level <= verbose) \ + pr_debug(format , ## arg); \ + } while (0) + /* Macros to help calculate the sysfs_names array length */ /* * sum of strlen of: in??_input\0, in??_{min,max}\0, in??_{min,max}_alarm\0, @@ -1533,7 +1536,7 @@ static int abituguru_resume(struct device *dev) } static SIMPLE_DEV_PM_OPS(abituguru_pm, abituguru_suspend, abituguru_resume); -#define ABIT_UGURU_PM &abituguru_pm +#define ABIT_UGURU_PM (&abituguru_pm) #else #define ABIT_UGURU_PM NULL #endif /* CONFIG_PM */ diff --git a/drivers/hwmon/abituguru3.c b/drivers/hwmon/abituguru3.c index 205327d33c4d..1d2da31c27c6 100644 --- a/drivers/hwmon/abituguru3.c +++ b/drivers/hwmon/abituguru3.c @@ -76,9 +76,11 @@ #define ABIT_UGURU3_SYNCHRONIZE_TIMEOUT 5 /* utility macros */ #define ABIT_UGURU3_NAME "abituguru3" -#define ABIT_UGURU3_DEBUG(format, arg...) \ - if (verbose) \ - printk(KERN_DEBUG ABIT_UGURU3_NAME ": " format , ## arg) +#define ABIT_UGURU3_DEBUG(format, arg...) \ + do { \ + if (verbose) \ + pr_debug(format , ## arg); \ + } while (0) /* Macros to help calculate the sysfs_names array length */ #define ABIT_UGURU3_MAX_NO_SENSORS 26 @@ -1159,7 +1161,7 @@ static int abituguru3_resume(struct device *dev) } static SIMPLE_DEV_PM_OPS(abituguru3_pm, abituguru3_suspend, abituguru3_resume); -#define ABIT_UGURU3_PM &abituguru3_pm +#define ABIT_UGURU3_PM (&abituguru3_pm) #else #define ABIT_UGURU3_PM NULL #endif /* CONFIG_PM */ diff --git a/drivers/hwmon/ad7314.c b/drivers/hwmon/ad7314.c index a57584d28a40..f4f9b219bf16 100644 --- a/drivers/hwmon/ad7314.c +++ b/drivers/hwmon/ad7314.c @@ -116,7 +116,7 @@ static int ad7314_probe(struct spi_device *spi_dev) if (chip == NULL) return -ENOMEM; - dev_set_drvdata(&spi_dev->dev, chip); + spi_set_drvdata(spi_dev, chip); ret = sysfs_create_group(&spi_dev->dev.kobj, &ad7314_group); if (ret < 0) @@ -137,7 +137,7 @@ error_remove_group: static int ad7314_remove(struct spi_device *spi_dev) { - struct ad7314_data *chip = dev_get_drvdata(&spi_dev->dev); + struct ad7314_data *chip = spi_get_drvdata(spi_dev); hwmon_device_unregister(chip->hwmon_dev); sysfs_remove_group(&spi_dev->dev.kobj, &ad7314_group); @@ -166,6 +166,5 @@ static struct spi_driver ad7314_driver = { module_spi_driver(ad7314_driver); MODULE_AUTHOR("Sonic Zhang <sonic.zhang@analog.com>"); -MODULE_DESCRIPTION("Analog Devices AD7314, ADT7301 and ADT7302 digital" - " temperature sensor driver"); +MODULE_DESCRIPTION("Analog Devices AD7314, ADT7301 and ADT7302 digital temperature sensor driver"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/hwmon/adm1021.c b/drivers/hwmon/adm1021.c index 71bcba8abfc0..7e76922a4ba9 100644 --- a/drivers/hwmon/adm1021.c +++ b/drivers/hwmon/adm1021.c @@ -312,8 +312,7 @@ static int adm1021_detect(struct i2c_client *client, int conv_rate, status, config, man_id, dev_id; if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) { - pr_debug("adm1021: detect failed, " - "smbus byte data not supported!\n"); + pr_debug("detect failed, smbus byte data not supported!\n"); return -ENODEV; } @@ -324,7 +323,7 @@ static int adm1021_detect(struct i2c_client *client, /* Check unused bits */ if ((status & 0x03) || (config & 0x3F) || (conv_rate & 0xF8)) { - pr_debug("adm1021: detect failed, chip not detected!\n"); + pr_debug("detect failed, chip not detected!\n"); return -ENODEV; } @@ -353,7 +352,7 @@ static int adm1021_detect(struct i2c_client *client, else type_name = "max1617"; - pr_debug("adm1021: Detected chip %s at adapter %d, address 0x%02x.\n", + pr_debug("Detected chip %s at adapter %d, address 0x%02x.\n", type_name, i2c_adapter_id(adapter), client->addr); strlcpy(info->type, type_name, I2C_NAME_SIZE); @@ -368,10 +367,8 @@ static int adm1021_probe(struct i2c_client *client, data = devm_kzalloc(&client->dev, sizeof(struct adm1021_data), GFP_KERNEL); - if (!data) { - pr_debug("adm1021: detect failed, devm_kzalloc failed!\n"); + if (!data) return -ENOMEM; - } i2c_set_clientdata(client, data); data->type = id->driver_data; diff --git a/drivers/hwmon/adm1026.c b/drivers/hwmon/adm1026.c index ea09046e651d..3a6d9ef1c16c 100644 --- a/drivers/hwmon/adm1026.c +++ b/drivers/hwmon/adm1026.c @@ -49,14 +49,14 @@ static int gpio_fan[8] = { -1, -1, -1, -1, -1, -1, -1, -1 }; module_param_array(gpio_input, int, NULL, 0); MODULE_PARM_DESC(gpio_input, "List of GPIO pins (0-16) to program as inputs"); module_param_array(gpio_output, int, NULL, 0); -MODULE_PARM_DESC(gpio_output, "List of GPIO pins (0-16) to program as " - "outputs"); +MODULE_PARM_DESC(gpio_output, + "List of GPIO pins (0-16) to program as outputs"); module_param_array(gpio_inverted, int, NULL, 0); -MODULE_PARM_DESC(gpio_inverted, "List of GPIO pins (0-16) to program as " - "inverted"); +MODULE_PARM_DESC(gpio_inverted, + "List of GPIO pins (0-16) to program as inverted"); module_param_array(gpio_normal, int, NULL, 0); -MODULE_PARM_DESC(gpio_normal, "List of GPIO pins (0-16) to program as " - "normal/non-inverted"); +MODULE_PARM_DESC(gpio_normal, + "List of GPIO pins (0-16) to program as normal/non-inverted"); module_param_array(gpio_fan, int, NULL, 0); MODULE_PARM_DESC(gpio_fan, "List of GPIO pins (0-7) to program as fan tachs"); @@ -372,31 +372,31 @@ static void adm1026_init_client(struct i2c_client *client) dev_dbg(&client->dev, "ADM1026_REG_CONFIG1 is: 0x%02x\n", data->config1); if ((data->config1 & CFG1_MONITOR) == 0) { - dev_dbg(&client->dev, "Monitoring not currently " - "enabled.\n"); + dev_dbg(&client->dev, + "Monitoring not currently enabled.\n"); } if (data->config1 & CFG1_INT_ENABLE) { - dev_dbg(&client->dev, "SMBALERT interrupts are " - "enabled.\n"); + dev_dbg(&client->dev, + "SMBALERT interrupts are enabled.\n"); } if (data->config1 & CFG1_AIN8_9) { - dev_dbg(&client->dev, "in8 and in9 enabled. " - "temp3 disabled.\n"); + dev_dbg(&client->dev, + "in8 and in9 enabled. temp3 disabled.\n"); } else { - dev_dbg(&client->dev, "temp3 enabled. in8 and " - "in9 disabled.\n"); + dev_dbg(&client->dev, + "temp3 enabled. in8 and in9 disabled.\n"); } if (data->config1 & CFG1_THERM_HOT) { - dev_dbg(&client->dev, "Automatic THERM, PWM, " - "and temp limits enabled.\n"); + dev_dbg(&client->dev, + "Automatic THERM, PWM, and temp limits enabled.\n"); } if (data->config3 & CFG3_GPIO16_ENABLE) { - dev_dbg(&client->dev, "GPIO16 enabled. THERM " - "pin disabled.\n"); + dev_dbg(&client->dev, + "GPIO16 enabled. THERM pin disabled.\n"); } else { - dev_dbg(&client->dev, "THERM pin enabled. " - "GPIO16 disabled.\n"); + dev_dbg(&client->dev, + "THERM pin enabled. GPIO16 disabled.\n"); } if (data->config3 & CFG3_VREF_250) dev_dbg(&client->dev, "Vref is 2.50 Volts.\n"); @@ -1798,8 +1798,8 @@ static int adm1026_detect(struct i2c_client *client, company = adm1026_read_value(client, ADM1026_REG_COMPANY); verstep = adm1026_read_value(client, ADM1026_REG_VERSTEP); - dev_dbg(&adapter->dev, "Detecting device at %d,0x%02x with" - " COMPANY: 0x%02x and VERSTEP: 0x%02x\n", + dev_dbg(&adapter->dev, + "Detecting device at %d,0x%02x with COMPANY: 0x%02x and VERSTEP: 0x%02x\n", i2c_adapter_id(client->adapter), client->addr, company, verstep); @@ -1811,11 +1811,12 @@ static int adm1026_detect(struct i2c_client *client, /* Analog Devices ADM1026 */ } else if (company == ADM1026_COMPANY_ANALOG_DEV && (verstep & 0xf0) == ADM1026_VERSTEP_GENERIC) { - dev_err(&adapter->dev, "Unrecognized stepping " - "0x%02x. Defaulting to ADM1026.\n", verstep); + dev_err(&adapter->dev, + "Unrecognized stepping 0x%02x. Defaulting to ADM1026.\n", + verstep); } else if ((verstep & 0xf0) == ADM1026_VERSTEP_GENERIC) { - dev_err(&adapter->dev, "Found version/stepping " - "0x%02x. Assuming generic ADM1026.\n", + dev_err(&adapter->dev, + "Found version/stepping 0x%02x. Assuming generic ADM1026.\n", verstep); } else { dev_dbg(&adapter->dev, "Autodetection failed\n"); diff --git a/drivers/hwmon/adm1029.c b/drivers/hwmon/adm1029.c index 97f4718382f6..9ee5e066423b 100644 --- a/drivers/hwmon/adm1029.c +++ b/drivers/hwmon/adm1029.c @@ -224,8 +224,9 @@ static ssize_t set_fan_div(struct device *dev, break; default: mutex_unlock(&data->update_lock); - dev_err(&client->dev, "fan_div value %ld not " - "supported. Choose one of 1, 2 or 4!\n", val); + dev_err(&client->dev, + "fan_div value %ld not supported. Choose one of 1, 2 or 4!\n", + val); return -EINVAL; } /* Update the value */ @@ -326,8 +327,8 @@ static int adm1029_detect(struct i2c_client *client, * There are no "official" CHIP ID, so actually * we use Major/Minor revision for that */ - pr_info("adm1029: Unknown major revision %x, " - "please let us know\n", chip_id); + pr_info("Unknown major revision %x, please let us know\n", + chip_id); return -ENODEV; } diff --git a/drivers/hwmon/adm9240.c b/drivers/hwmon/adm9240.c index 2416628e0ab1..086d02a9ecdc 100644 --- a/drivers/hwmon/adm9240.c +++ b/drivers/hwmon/adm9240.c @@ -351,8 +351,9 @@ static void adm9240_write_fan_div(struct i2c_client *client, int nr, reg &= ~(3 << shift); reg |= (fan_div << shift); i2c_smbus_write_byte_data(client, ADM9240_REG_VID_FAN_DIV, reg); - dev_dbg(&client->dev, "fan%d clock divider changed from %u " - "to %u\n", nr + 1, 1 << old, 1 << fan_div); + dev_dbg(&client->dev, + "fan%d clock divider changed from %u to %u\n", + nr + 1, 1 << old, 1 << fan_div); } /* @@ -699,8 +700,8 @@ static void adm9240_init_client(struct i2c_client *client) /* start measurement cycle */ i2c_smbus_write_byte_data(client, ADM9240_REG_CONFIG, 1); - dev_info(&client->dev, "cold start: config was 0x%02x " - "mode %u\n", conf, mode); + dev_info(&client->dev, + "cold start: config was 0x%02x mode %u\n", conf, mode); } } diff --git a/drivers/hwmon/ads7871.c b/drivers/hwmon/ads7871.c index a79875986f91..3eff73b6220d 100644 --- a/drivers/hwmon/ads7871.c +++ b/drivers/hwmon/ads7871.c @@ -40,25 +40,25 @@ * the instruction byte */ /*Instruction Bit masks*/ -#define INST_MODE_bm (1<<7) -#define INST_READ_bm (1<<6) -#define INST_16BIT_bm (1<<5) +#define INST_MODE_BM (1 << 7) +#define INST_READ_BM (1 << 6) +#define INST_16BIT_BM (1 << 5) /*From figure 18 in the datasheet*/ /*bit masks for Rev/Oscillator Control Register*/ -#define MUX_CNV_bv 7 -#define MUX_CNV_bm (1<<MUX_CNV_bv) -#define MUX_M3_bm (1<<3) /*M3 selects single ended*/ -#define MUX_G_bv 4 /*allows for reg = (gain << MUX_G_bv) | ...*/ +#define MUX_CNV_BV 7 +#define MUX_CNV_BM (1 << MUX_CNV_BV) +#define MUX_M3_BM (1 << 3) /*M3 selects single ended*/ +#define MUX_G_BV 4 /*allows for reg = (gain << MUX_G_BV) | ...*/ /*From figure 18 in the datasheet*/ /*bit masks for Rev/Oscillator Control Register*/ -#define OSC_OSCR_bm (1<<5) -#define OSC_OSCE_bm (1<<4) -#define OSC_REFE_bm (1<<3) -#define OSC_BUFE_bm (1<<2) -#define OSC_R2V_bm (1<<1) -#define OSC_RBG_bm (1<<0) +#define OSC_OSCR_BM (1 << 5) +#define OSC_OSCE_BM (1 << 4) +#define OSC_REFE_BM (1 << 3) +#define OSC_BUFE_BM (1 << 2) +#define OSC_R2V_BM (1 << 1) +#define OSC_RBG_BM (1 << 0) #include <linux/module.h> #include <linux/init.h> @@ -79,7 +79,7 @@ struct ads7871_data { static int ads7871_read_reg8(struct spi_device *spi, int reg) { int ret; - reg = reg | INST_READ_bm; + reg = reg | INST_READ_BM; ret = spi_w8r8(spi, reg); return ret; } @@ -87,7 +87,7 @@ static int ads7871_read_reg8(struct spi_device *spi, int reg) static int ads7871_read_reg16(struct spi_device *spi, int reg) { int ret; - reg = reg | INST_READ_bm | INST_16BIT_bm; + reg = reg | INST_READ_BM | INST_16BIT_BM; ret = spi_w8r16(spi, reg); return ret; } @@ -111,13 +111,13 @@ static ssize_t show_voltage(struct device *dev, * TODO: add support for conversions * other than single ended with a gain of 1 */ - /*MUX_M3_bm forces single ended*/ + /*MUX_M3_BM forces single ended*/ /*This is also where the gain of the PGA would be set*/ ads7871_write_reg8(spi, REG_GAIN_MUX, - (MUX_CNV_bm | MUX_M3_bm | channel)); + (MUX_CNV_BM | MUX_M3_BM | channel)); ret = ads7871_read_reg8(spi, REG_GAIN_MUX); - mux_cnv = ((ret & MUX_CNV_bm)>>MUX_CNV_bv); + mux_cnv = ((ret & MUX_CNV_BM) >> MUX_CNV_BV); /* * on 400MHz arm9 platform the conversion * is already done when we do this test @@ -125,14 +125,14 @@ static ssize_t show_voltage(struct device *dev, while ((i < 2) && mux_cnv) { i++; ret = ads7871_read_reg8(spi, REG_GAIN_MUX); - mux_cnv = ((ret & MUX_CNV_bm)>>MUX_CNV_bv); + mux_cnv = ((ret & MUX_CNV_BM) >> MUX_CNV_BV); msleep_interruptible(1); } if (mux_cnv == 0) { val = ads7871_read_reg16(spi, REG_LS_BYTE); /*result in volts*10000 = (val/8192)*2.5*10000*/ - val = ((val>>2) * 25000) / 8192; + val = ((val >> 2) * 25000) / 8192; return sprintf(buf, "%d\n", val); } else { return -1; @@ -189,7 +189,7 @@ static int ads7871_probe(struct spi_device *spi) ads7871_write_reg8(spi, REG_SER_CONTROL, 0); ads7871_write_reg8(spi, REG_AD_CONTROL, 0); - val = (OSC_OSCR_bm | OSC_OSCE_bm | OSC_REFE_bm | OSC_BUFE_bm); + val = (OSC_OSCR_BM | OSC_OSCE_BM | OSC_REFE_BM | OSC_BUFE_BM); ads7871_write_reg8(spi, REG_OSC_CONTROL, val); ret = ads7871_read_reg8(spi, REG_OSC_CONTROL); diff --git a/drivers/hwmon/adt7310.c b/drivers/hwmon/adt7310.c new file mode 100644 index 000000000000..da5f0789fb97 --- /dev/null +++ b/drivers/hwmon/adt7310.c @@ -0,0 +1,123 @@ +/* + * ADT7310/ADT7310 digital temperature sensor driver + * + * Copyright 2012-2013 Analog Devices Inc. + * Author: Lars-Peter Clausen <lars@metafoo.de> + * + * Licensed under the GPL-2 or later. + */ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/spi/spi.h> +#include <asm/unaligned.h> + +#include "adt7x10.h" + +#define ADT7310_STATUS 0 +#define ADT7310_CONFIG 1 +#define ADT7310_TEMPERATURE 2 +#define ADT7310_ID 3 +#define ADT7310_T_CRIT 4 +#define ADT7310_T_HYST 5 +#define ADT7310_T_ALARM_HIGH 6 +#define ADT7310_T_ALARM_LOW 7 + +static const u8 adt7310_reg_table[] = { + [ADT7X10_TEMPERATURE] = ADT7310_TEMPERATURE, + [ADT7X10_STATUS] = ADT7310_STATUS, + [ADT7X10_CONFIG] = ADT7310_CONFIG, + [ADT7X10_T_ALARM_HIGH] = ADT7310_T_ALARM_HIGH, + [ADT7X10_T_ALARM_LOW] = ADT7310_T_ALARM_LOW, + [ADT7X10_T_CRIT] = ADT7310_T_CRIT, + [ADT7X10_T_HYST] = ADT7310_T_HYST, + [ADT7X10_ID] = ADT7310_ID, +}; + +#define ADT7310_CMD_REG_OFFSET 3 +#define ADT7310_CMD_READ 0x40 + +#define AD7310_COMMAND(reg) (adt7310_reg_table[(reg)] << ADT7310_CMD_REG_OFFSET) + +static int adt7310_spi_read_word(struct device *dev, u8 reg) +{ + struct spi_device *spi = to_spi_device(dev); + int ret; + + ret = spi_w8r16(spi, AD7310_COMMAND(reg) | ADT7310_CMD_READ); + if (ret < 0) + return ret; + + return be16_to_cpu((__force __be16)ret); +} + +static int adt7310_spi_write_word(struct device *dev, u8 reg, u16 data) +{ + struct spi_device *spi = to_spi_device(dev); + u8 buf[3]; + + buf[0] = AD7310_COMMAND(reg); + put_unaligned_be16(data, &buf[1]); + + return spi_write(spi, buf, sizeof(buf)); +} + +static int adt7310_spi_read_byte(struct device *dev, u8 reg) +{ + struct spi_device *spi = to_spi_device(dev); + + return spi_w8r8(spi, AD7310_COMMAND(reg) | ADT7310_CMD_READ); +} + +static int adt7310_spi_write_byte(struct device *dev, u8 reg, + u8 data) +{ + struct spi_device *spi = to_spi_device(dev); + u8 buf[2]; + + buf[0] = AD7310_COMMAND(reg); + buf[1] = data; + + return spi_write(spi, buf, sizeof(buf)); +} + +static const struct adt7x10_ops adt7310_spi_ops = { + .read_word = adt7310_spi_read_word, + .write_word = adt7310_spi_write_word, + .read_byte = adt7310_spi_read_byte, + .write_byte = adt7310_spi_write_byte, +}; + +static int adt7310_spi_probe(struct spi_device *spi) +{ + return adt7x10_probe(&spi->dev, spi_get_device_id(spi)->name, spi->irq, + &adt7310_spi_ops); +} + +static int adt7310_spi_remove(struct spi_device *spi) +{ + return adt7x10_remove(&spi->dev, spi->irq); +} + +static const struct spi_device_id adt7310_id[] = { + { "adt7310", 0 }, + { "adt7320", 0 }, + {} +}; +MODULE_DEVICE_TABLE(spi, adt7310_id); + +static struct spi_driver adt7310_driver = { + .driver = { + .name = "adt7310", + .owner = THIS_MODULE, + .pm = ADT7X10_DEV_PM_OPS, + }, + .probe = adt7310_spi_probe, + .remove = adt7310_spi_remove, + .id_table = adt7310_id, +}; +module_spi_driver(adt7310_driver); + +MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>"); +MODULE_DESCRIPTION("ADT7310/ADT7320 driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/hwmon/adt7410.c b/drivers/hwmon/adt7410.c index 99a7290da0a3..0dc066a939b4 100644 --- a/drivers/hwmon/adt7410.c +++ b/drivers/hwmon/adt7410.c @@ -1,460 +1,80 @@ /* - * adt7410.c - Part of lm_sensors, Linux kernel modules for hardware - * monitoring - * This driver handles the ADT7410 and compatible digital temperature sensors. - * Hartmut Knaack <knaack.h@gmx.de> 2012-07-22 - * based on lm75.c by Frodo Looijaard <frodol@dds.nl> - * and adt7410.c from iio-staging by Sonic Zhang <sonic.zhang@analog.com> + * ADT7410/ADT7420 digital temperature sensor driver * - * 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. + * Copyright 2012-2013 Analog Devices Inc. + * Author: Lars-Peter Clausen <lars@metafoo.de> * - * 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. + * Licensed under the GPL-2 or later. */ #include <linux/module.h> #include <linux/init.h> -#include <linux/slab.h> -#include <linux/jiffies.h> #include <linux/i2c.h> -#include <linux/hwmon.h> -#include <linux/hwmon-sysfs.h> -#include <linux/err.h> -#include <linux/mutex.h> -#include <linux/delay.h> - -/* - * ADT7410 registers definition - */ - -#define ADT7410_TEMPERATURE 0 -#define ADT7410_STATUS 2 -#define ADT7410_CONFIG 3 -#define ADT7410_T_ALARM_HIGH 4 -#define ADT7410_T_ALARM_LOW 6 -#define ADT7410_T_CRIT 8 -#define ADT7410_T_HYST 0xA - -/* - * ADT7410 status - */ -#define ADT7410_STAT_T_LOW (1 << 4) -#define ADT7410_STAT_T_HIGH (1 << 5) -#define ADT7410_STAT_T_CRIT (1 << 6) -#define ADT7410_STAT_NOT_RDY (1 << 7) - -/* - * ADT7410 config - */ -#define ADT7410_FAULT_QUEUE_MASK (1 << 0 | 1 << 1) -#define ADT7410_CT_POLARITY (1 << 2) -#define ADT7410_INT_POLARITY (1 << 3) -#define ADT7410_EVENT_MODE (1 << 4) -#define ADT7410_MODE_MASK (1 << 5 | 1 << 6) -#define ADT7410_FULL (0 << 5 | 0 << 6) -#define ADT7410_PD (1 << 5 | 1 << 6) -#define ADT7410_RESOLUTION (1 << 7) - -/* - * ADT7410 masks - */ -#define ADT7410_T13_VALUE_MASK 0xFFF8 -#define ADT7410_T_HYST_MASK 0xF - -/* straight from the datasheet */ -#define ADT7410_TEMP_MIN (-55000) -#define ADT7410_TEMP_MAX 150000 - -enum adt7410_type { /* keep sorted in alphabetical order */ - adt7410, -}; - -static const u8 ADT7410_REG_TEMP[4] = { - ADT7410_TEMPERATURE, /* input */ - ADT7410_T_ALARM_HIGH, /* high */ - ADT7410_T_ALARM_LOW, /* low */ - ADT7410_T_CRIT, /* critical */ -}; - -/* Each client has this additional data */ -struct adt7410_data { - struct device *hwmon_dev; - struct mutex update_lock; - u8 config; - u8 oldconfig; - bool valid; /* true if registers valid */ - unsigned long last_updated; /* In jiffies */ - s16 temp[4]; /* Register values, - 0 = input - 1 = high - 2 = low - 3 = critical */ - u8 hyst; /* hysteresis offset */ -}; - -/* - * adt7410 register access by I2C - */ -static int adt7410_temp_ready(struct i2c_client *client) -{ - int i, status; - - for (i = 0; i < 6; i++) { - status = i2c_smbus_read_byte_data(client, ADT7410_STATUS); - if (status < 0) - return status; - if (!(status & ADT7410_STAT_NOT_RDY)) - return 0; - msleep(60); - } - return -ETIMEDOUT; -} - -static struct adt7410_data *adt7410_update_device(struct device *dev) -{ - struct i2c_client *client = to_i2c_client(dev); - struct adt7410_data *data = i2c_get_clientdata(client); - struct adt7410_data *ret = data; - mutex_lock(&data->update_lock); - - if (time_after(jiffies, data->last_updated + HZ + HZ / 2) - || !data->valid) { - int i, status; - dev_dbg(&client->dev, "Starting update\n"); +#include "adt7x10.h" - status = adt7410_temp_ready(client); /* check for new value */ - if (unlikely(status)) { - ret = ERR_PTR(status); - goto abort; - } - for (i = 0; i < ARRAY_SIZE(data->temp); i++) { - status = i2c_smbus_read_word_swapped(client, - ADT7410_REG_TEMP[i]); - if (unlikely(status < 0)) { - dev_dbg(dev, - "Failed to read value: reg %d, error %d\n", - ADT7410_REG_TEMP[i], status); - ret = ERR_PTR(status); - goto abort; - } - data->temp[i] = status; - } - status = i2c_smbus_read_byte_data(client, ADT7410_T_HYST); - if (unlikely(status < 0)) { - dev_dbg(dev, - "Failed to read value: reg %d, error %d\n", - ADT7410_T_HYST, status); - ret = ERR_PTR(status); - goto abort; - } - data->hyst = status; - data->last_updated = jiffies; - data->valid = true; - } - -abort: - mutex_unlock(&data->update_lock); - return ret; -} - -static s16 ADT7410_TEMP_TO_REG(long temp) +static int adt7410_i2c_read_word(struct device *dev, u8 reg) { - return DIV_ROUND_CLOSEST(clamp_val(temp, ADT7410_TEMP_MIN, - ADT7410_TEMP_MAX) * 128, 1000); + return i2c_smbus_read_word_swapped(to_i2c_client(dev), reg); } -static int ADT7410_REG_TO_TEMP(struct adt7410_data *data, s16 reg) +static int adt7410_i2c_write_word(struct device *dev, u8 reg, u16 data) { - /* in 13 bit mode, bits 0-2 are status flags - mask them out */ - if (!(data->config & ADT7410_RESOLUTION)) - reg &= ADT7410_T13_VALUE_MASK; - /* - * temperature is stored in twos complement format, in steps of - * 1/128°C - */ - return DIV_ROUND_CLOSEST(reg * 1000, 128); + return i2c_smbus_write_word_swapped(to_i2c_client(dev), reg, data); } -/*-----------------------------------------------------------------------*/ - -/* sysfs attributes for hwmon */ - -static ssize_t adt7410_show_temp(struct device *dev, - struct device_attribute *da, char *buf) +static int adt7410_i2c_read_byte(struct device *dev, u8 reg) { - struct sensor_device_attribute *attr = to_sensor_dev_attr(da); - struct adt7410_data *data = adt7410_update_device(dev); - - if (IS_ERR(data)) - return PTR_ERR(data); - - return sprintf(buf, "%d\n", ADT7410_REG_TO_TEMP(data, - data->temp[attr->index])); + return i2c_smbus_read_byte_data(to_i2c_client(dev), reg); } -static ssize_t adt7410_set_temp(struct device *dev, - struct device_attribute *da, - const char *buf, size_t count) +static int adt7410_i2c_write_byte(struct device *dev, u8 reg, u8 data) { - struct sensor_device_attribute *attr = to_sensor_dev_attr(da); - struct i2c_client *client = to_i2c_client(dev); - struct adt7410_data *data = i2c_get_clientdata(client); - int nr = attr->index; - long temp; - int ret; - - ret = kstrtol(buf, 10, &temp); - if (ret) - return ret; - - mutex_lock(&data->update_lock); - data->temp[nr] = ADT7410_TEMP_TO_REG(temp); - ret = i2c_smbus_write_word_swapped(client, ADT7410_REG_TEMP[nr], - data->temp[nr]); - if (ret) - count = ret; - mutex_unlock(&data->update_lock); - return count; + return i2c_smbus_write_byte_data(to_i2c_client(dev), reg, data); } -static ssize_t adt7410_show_t_hyst(struct device *dev, - struct device_attribute *da, - char *buf) -{ - struct sensor_device_attribute *attr = to_sensor_dev_attr(da); - struct adt7410_data *data; - int nr = attr->index; - int hyst; - - data = adt7410_update_device(dev); - if (IS_ERR(data)) - return PTR_ERR(data); - hyst = (data->hyst & ADT7410_T_HYST_MASK) * 1000; - - /* - * hysteresis is stored as a 4 bit offset in the device, convert it - * to an absolute value - */ - if (nr == 2) /* min has positive offset, others have negative */ - hyst = -hyst; - return sprintf(buf, "%d\n", - ADT7410_REG_TO_TEMP(data, data->temp[nr]) - hyst); -} - -static ssize_t adt7410_set_t_hyst(struct device *dev, - struct device_attribute *da, - const char *buf, size_t count) -{ - struct i2c_client *client = to_i2c_client(dev); - struct adt7410_data *data = i2c_get_clientdata(client); - int limit, ret; - long hyst; - - ret = kstrtol(buf, 10, &hyst); - if (ret) - return ret; - /* convert absolute hysteresis value to a 4 bit delta value */ - limit = ADT7410_REG_TO_TEMP(data, data->temp[1]); - hyst = clamp_val(hyst, ADT7410_TEMP_MIN, ADT7410_TEMP_MAX); - data->hyst = clamp_val(DIV_ROUND_CLOSEST(limit - hyst, 1000), 0, - ADT7410_T_HYST_MASK); - ret = i2c_smbus_write_byte_data(client, ADT7410_T_HYST, data->hyst); - if (ret) - return ret; - - return count; -} - -static ssize_t adt7410_show_alarm(struct device *dev, - struct device_attribute *da, - char *buf) -{ - struct i2c_client *client = to_i2c_client(dev); - struct sensor_device_attribute *attr = to_sensor_dev_attr(da); - int ret; - - ret = i2c_smbus_read_byte_data(client, ADT7410_STATUS); - if (ret < 0) - return ret; - - return sprintf(buf, "%d\n", !!(ret & attr->index)); -} - -static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, adt7410_show_temp, NULL, 0); -static SENSOR_DEVICE_ATTR(temp1_max, S_IWUSR | S_IRUGO, - adt7410_show_temp, adt7410_set_temp, 1); -static SENSOR_DEVICE_ATTR(temp1_min, S_IWUSR | S_IRUGO, - adt7410_show_temp, adt7410_set_temp, 2); -static SENSOR_DEVICE_ATTR(temp1_crit, S_IWUSR | S_IRUGO, - adt7410_show_temp, adt7410_set_temp, 3); -static SENSOR_DEVICE_ATTR(temp1_max_hyst, S_IWUSR | S_IRUGO, - adt7410_show_t_hyst, adt7410_set_t_hyst, 1); -static SENSOR_DEVICE_ATTR(temp1_min_hyst, S_IRUGO, - adt7410_show_t_hyst, NULL, 2); -static SENSOR_DEVICE_ATTR(temp1_crit_hyst, S_IRUGO, - adt7410_show_t_hyst, NULL, 3); -static SENSOR_DEVICE_ATTR(temp1_min_alarm, S_IRUGO, adt7410_show_alarm, - NULL, ADT7410_STAT_T_LOW); -static SENSOR_DEVICE_ATTR(temp1_max_alarm, S_IRUGO, adt7410_show_alarm, - NULL, ADT7410_STAT_T_HIGH); -static SENSOR_DEVICE_ATTR(temp1_crit_alarm, S_IRUGO, adt7410_show_alarm, - NULL, ADT7410_STAT_T_CRIT); - -static struct attribute *adt7410_attributes[] = { - &sensor_dev_attr_temp1_input.dev_attr.attr, - &sensor_dev_attr_temp1_max.dev_attr.attr, - &sensor_dev_attr_temp1_min.dev_attr.attr, - &sensor_dev_attr_temp1_crit.dev_attr.attr, - &sensor_dev_attr_temp1_max_hyst.dev_attr.attr, - &sensor_dev_attr_temp1_min_hyst.dev_attr.attr, - &sensor_dev_attr_temp1_crit_hyst.dev_attr.attr, - &sensor_dev_attr_temp1_min_alarm.dev_attr.attr, - &sensor_dev_attr_temp1_max_alarm.dev_attr.attr, - &sensor_dev_attr_temp1_crit_alarm.dev_attr.attr, - NULL +static const struct adt7x10_ops adt7410_i2c_ops = { + .read_word = adt7410_i2c_read_word, + .write_word = adt7410_i2c_write_word, + .read_byte = adt7410_i2c_read_byte, + .write_byte = adt7410_i2c_write_byte, }; -static const struct attribute_group adt7410_group = { - .attrs = adt7410_attributes, -}; - -/*-----------------------------------------------------------------------*/ - -/* device probe and removal */ - -static int adt7410_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int adt7410_i2c_probe(struct i2c_client *client, + const struct i2c_device_id *id) { - struct adt7410_data *data; - int ret; - if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA)) return -ENODEV; - data = devm_kzalloc(&client->dev, sizeof(struct adt7410_data), - GFP_KERNEL); - if (!data) - return -ENOMEM; - - i2c_set_clientdata(client, data); - mutex_init(&data->update_lock); - - /* configure as specified */ - ret = i2c_smbus_read_byte_data(client, ADT7410_CONFIG); - if (ret < 0) { - dev_dbg(&client->dev, "Can't read config? %d\n", ret); - return ret; - } - data->oldconfig = ret; - /* - * Set to 16 bit resolution, continous conversion and comparator mode. - */ - ret &= ~ADT7410_MODE_MASK; - data->config = ret | ADT7410_FULL | ADT7410_RESOLUTION | - ADT7410_EVENT_MODE; - if (data->config != data->oldconfig) { - ret = i2c_smbus_write_byte_data(client, ADT7410_CONFIG, - data->config); - if (ret) - return ret; - } - dev_dbg(&client->dev, "Config %02x\n", data->config); - - /* Register sysfs hooks */ - ret = sysfs_create_group(&client->dev.kobj, &adt7410_group); - if (ret) - goto exit_restore; - - data->hwmon_dev = hwmon_device_register(&client->dev); - if (IS_ERR(data->hwmon_dev)) { - ret = PTR_ERR(data->hwmon_dev); - goto exit_remove; - } - - dev_info(&client->dev, "sensor '%s'\n", client->name); - - return 0; - -exit_remove: - sysfs_remove_group(&client->dev.kobj, &adt7410_group); -exit_restore: - i2c_smbus_write_byte_data(client, ADT7410_CONFIG, data->oldconfig); - return ret; + return adt7x10_probe(&client->dev, NULL, client->irq, &adt7410_i2c_ops); } -static int adt7410_remove(struct i2c_client *client) +static int adt7410_i2c_remove(struct i2c_client *client) { - struct adt7410_data *data = i2c_get_clientdata(client); - - hwmon_device_unregister(data->hwmon_dev); - sysfs_remove_group(&client->dev.kobj, &adt7410_group); - if (data->oldconfig != data->config) - i2c_smbus_write_byte_data(client, ADT7410_CONFIG, - data->oldconfig); - return 0; + return adt7x10_remove(&client->dev, client->irq); } static const struct i2c_device_id adt7410_ids[] = { - { "adt7410", adt7410, }, - { "adt7420", adt7410, }, - { /* LIST END */ } + { "adt7410", 0 }, + { "adt7420", 0 }, + {} }; MODULE_DEVICE_TABLE(i2c, adt7410_ids); -#ifdef CONFIG_PM_SLEEP -static int adt7410_suspend(struct device *dev) -{ - int ret; - struct i2c_client *client = to_i2c_client(dev); - struct adt7410_data *data = i2c_get_clientdata(client); - - ret = i2c_smbus_write_byte_data(client, ADT7410_CONFIG, - data->config | ADT7410_PD); - return ret; -} - -static int adt7410_resume(struct device *dev) -{ - int ret; - struct i2c_client *client = to_i2c_client(dev); - struct adt7410_data *data = i2c_get_clientdata(client); - - ret = i2c_smbus_write_byte_data(client, ADT7410_CONFIG, data->config); - return ret; -} - -static SIMPLE_DEV_PM_OPS(adt7410_dev_pm_ops, adt7410_suspend, adt7410_resume); - -#define ADT7410_DEV_PM_OPS (&adt7410_dev_pm_ops) -#else -#define ADT7410_DEV_PM_OPS NULL -#endif /* CONFIG_PM */ - static struct i2c_driver adt7410_driver = { .class = I2C_CLASS_HWMON, .driver = { .name = "adt7410", - .pm = ADT7410_DEV_PM_OPS, + .pm = ADT7X10_DEV_PM_OPS, }, - .probe = adt7410_probe, - .remove = adt7410_remove, + .probe = adt7410_i2c_probe, + .remove = adt7410_i2c_remove, .id_table = adt7410_ids, .address_list = I2C_ADDRS(0x48, 0x49, 0x4a, 0x4b), }; - module_i2c_driver(adt7410_driver); -MODULE_AUTHOR("Hartmut Knaack"); -MODULE_DESCRIPTION("ADT7410/ADT7420 driver"); +MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>"); +MODULE_DESCRIPTION("ADT7410/AD7420 driver"); MODULE_LICENSE("GPL"); diff --git a/drivers/hwmon/adt7411.c b/drivers/hwmon/adt7411.c index 34ff03abb50b..d9299dee37d1 100644 --- a/drivers/hwmon/adt7411.c +++ b/drivers/hwmon/adt7411.c @@ -259,15 +259,17 @@ static int adt7411_detect(struct i2c_client *client, val = i2c_smbus_read_byte_data(client, ADT7411_REG_MANUFACTURER_ID); if (val < 0 || val != ADT7411_MANUFACTURER_ID) { - dev_dbg(&client->dev, "Wrong manufacturer ID. Got %d, " - "expected %d\n", val, ADT7411_MANUFACTURER_ID); + dev_dbg(&client->dev, + "Wrong manufacturer ID. Got %d, expected %d\n", + val, ADT7411_MANUFACTURER_ID); return -ENODEV; } val = i2c_smbus_read_byte_data(client, ADT7411_REG_DEVICE_ID); if (val < 0 || val != ADT7411_DEVICE_ID) { - dev_dbg(&client->dev, "Wrong device ID. Got %d, " - "expected %d\n", val, ADT7411_DEVICE_ID); + dev_dbg(&client->dev, + "Wrong device ID. Got %d, expected %d\n", + val, ADT7411_DEVICE_ID); return -ENODEV; } diff --git a/drivers/hwmon/adt7x10.c b/drivers/hwmon/adt7x10.c new file mode 100644 index 000000000000..98141f483165 --- /dev/null +++ b/drivers/hwmon/adt7x10.c @@ -0,0 +1,511 @@ +/* + * adt7x10.c - Part of lm_sensors, Linux kernel modules for hardware + * monitoring + * This driver handles the ADT7410 and compatible digital temperature sensors. + * Hartmut Knaack <knaack.h@gmx.de> 2012-07-22 + * based on lm75.c by Frodo Looijaard <frodol@dds.nl> + * and adt7410.c from iio-staging by Sonic Zhang <sonic.zhang@analog.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/module.h> +#include <linux/init.h> +#include <linux/slab.h> +#include <linux/jiffies.h> +#include <linux/hwmon.h> +#include <linux/hwmon-sysfs.h> +#include <linux/err.h> +#include <linux/mutex.h> +#include <linux/delay.h> +#include <linux/interrupt.h> + +#include "adt7x10.h" + +/* + * ADT7X10 status + */ +#define ADT7X10_STAT_T_LOW (1 << 4) +#define ADT7X10_STAT_T_HIGH (1 << 5) +#define ADT7X10_STAT_T_CRIT (1 << 6) +#define ADT7X10_STAT_NOT_RDY (1 << 7) + +/* + * ADT7X10 config + */ +#define ADT7X10_FAULT_QUEUE_MASK (1 << 0 | 1 << 1) +#define ADT7X10_CT_POLARITY (1 << 2) +#define ADT7X10_INT_POLARITY (1 << 3) +#define ADT7X10_EVENT_MODE (1 << 4) +#define ADT7X10_MODE_MASK (1 << 5 | 1 << 6) +#define ADT7X10_FULL (0 << 5 | 0 << 6) +#define ADT7X10_PD (1 << 5 | 1 << 6) +#define ADT7X10_RESOLUTION (1 << 7) + +/* + * ADT7X10 masks + */ +#define ADT7X10_T13_VALUE_MASK 0xFFF8 +#define ADT7X10_T_HYST_MASK 0xF + +/* straight from the datasheet */ +#define ADT7X10_TEMP_MIN (-55000) +#define ADT7X10_TEMP_MAX 150000 + +/* Each client has this additional data */ +struct adt7x10_data { + const struct adt7x10_ops *ops; + const char *name; + struct device *hwmon_dev; + struct mutex update_lock; + u8 config; + u8 oldconfig; + bool valid; /* true if registers valid */ + unsigned long last_updated; /* In jiffies */ + s16 temp[4]; /* Register values, + 0 = input + 1 = high + 2 = low + 3 = critical */ + u8 hyst; /* hysteresis offset */ +}; + +static int adt7x10_read_byte(struct device *dev, u8 reg) +{ + struct adt7x10_data *d = dev_get_drvdata(dev); + return d->ops->read_byte(dev, reg); +} + +static int adt7x10_write_byte(struct device *dev, u8 reg, u8 data) +{ + struct adt7x10_data *d = dev_get_drvdata(dev); + return d->ops->write_byte(dev, reg, data); +} + +static int adt7x10_read_word(struct device *dev, u8 reg) +{ + struct adt7x10_data *d = dev_get_drvdata(dev); + return d->ops->read_word(dev, reg); +} + +static int adt7x10_write_word(struct device *dev, u8 reg, u16 data) +{ + struct adt7x10_data *d = dev_get_drvdata(dev); + return d->ops->write_word(dev, reg, data); +} + +static const u8 ADT7X10_REG_TEMP[4] = { + ADT7X10_TEMPERATURE, /* input */ + ADT7X10_T_ALARM_HIGH, /* high */ + ADT7X10_T_ALARM_LOW, /* low */ + ADT7X10_T_CRIT, /* critical */ +}; + +static irqreturn_t adt7x10_irq_handler(int irq, void *private) +{ + struct device *dev = private; + int status; + + status = adt7x10_read_byte(dev, ADT7X10_STATUS); + if (status < 0) + return IRQ_HANDLED; + + if (status & ADT7X10_STAT_T_HIGH) + sysfs_notify(&dev->kobj, NULL, "temp1_max_alarm"); + if (status & ADT7X10_STAT_T_LOW) + sysfs_notify(&dev->kobj, NULL, "temp1_min_alarm"); + if (status & ADT7X10_STAT_T_CRIT) + sysfs_notify(&dev->kobj, NULL, "temp1_crit_alarm"); + + return IRQ_HANDLED; +} + +static int adt7x10_temp_ready(struct device *dev) +{ + int i, status; + + for (i = 0; i < 6; i++) { + status = adt7x10_read_byte(dev, ADT7X10_STATUS); + if (status < 0) + return status; + if (!(status & ADT7X10_STAT_NOT_RDY)) + return 0; + msleep(60); + } + return -ETIMEDOUT; +} + +static int adt7x10_update_temp(struct device *dev) +{ + struct adt7x10_data *data = dev_get_drvdata(dev); + int ret = 0; + + mutex_lock(&data->update_lock); + + if (time_after(jiffies, data->last_updated + HZ + HZ / 2) + || !data->valid) { + int temp; + + dev_dbg(dev, "Starting update\n"); + + ret = adt7x10_temp_ready(dev); /* check for new value */ + if (ret) + goto abort; + + temp = adt7x10_read_word(dev, ADT7X10_REG_TEMP[0]); + if (temp < 0) { + ret = temp; + dev_dbg(dev, "Failed to read value: reg %d, error %d\n", + ADT7X10_REG_TEMP[0], ret); + goto abort; + } + data->temp[0] = temp; + data->last_updated = jiffies; + data->valid = true; + } + +abort: + mutex_unlock(&data->update_lock); + return ret; +} + +static int adt7x10_fill_cache(struct device *dev) +{ + struct adt7x10_data *data = dev_get_drvdata(dev); + int ret; + int i; + + for (i = 1; i < ARRAY_SIZE(data->temp); i++) { + ret = adt7x10_read_word(dev, ADT7X10_REG_TEMP[i]); + if (ret < 0) { + dev_dbg(dev, "Failed to read value: reg %d, error %d\n", + ADT7X10_REG_TEMP[i], ret); + return ret; + } + data->temp[i] = ret; + } + + ret = adt7x10_read_byte(dev, ADT7X10_T_HYST); + if (ret < 0) { + dev_dbg(dev, "Failed to read value: reg %d, error %d\n", + ADT7X10_T_HYST, ret); + return ret; + } + data->hyst = ret; + + return 0; +} + +static s16 ADT7X10_TEMP_TO_REG(long temp) +{ + return DIV_ROUND_CLOSEST(clamp_val(temp, ADT7X10_TEMP_MIN, + ADT7X10_TEMP_MAX) * 128, 1000); +} + +static int ADT7X10_REG_TO_TEMP(struct adt7x10_data *data, s16 reg) +{ + /* in 13 bit mode, bits 0-2 are status flags - mask them out */ + if (!(data->config & ADT7X10_RESOLUTION)) + reg &= ADT7X10_T13_VALUE_MASK; + /* + * temperature is stored in twos complement format, in steps of + * 1/128°C + */ + return DIV_ROUND_CLOSEST(reg * 1000, 128); +} + +/*-----------------------------------------------------------------------*/ + +/* sysfs attributes for hwmon */ + +static ssize_t adt7x10_show_temp(struct device *dev, + struct device_attribute *da, + char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct adt7x10_data *data = dev_get_drvdata(dev); + + + if (attr->index == 0) { + int ret; + + ret = adt7x10_update_temp(dev); + if (ret) + return ret; + } + + return sprintf(buf, "%d\n", ADT7X10_REG_TO_TEMP(data, + data->temp[attr->index])); +} + +static ssize_t adt7x10_set_temp(struct device *dev, + struct device_attribute *da, + const char *buf, size_t count) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct adt7x10_data *data = dev_get_drvdata(dev); + int nr = attr->index; + long temp; + int ret; + + ret = kstrtol(buf, 10, &temp); + if (ret) + return ret; + + mutex_lock(&data->update_lock); + data->temp[nr] = ADT7X10_TEMP_TO_REG(temp); + ret = adt7x10_write_word(dev, ADT7X10_REG_TEMP[nr], data->temp[nr]); + if (ret) + count = ret; + mutex_unlock(&data->update_lock); + return count; +} + +static ssize_t adt7x10_show_t_hyst(struct device *dev, + struct device_attribute *da, + char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct adt7x10_data *data = dev_get_drvdata(dev); + int nr = attr->index; + int hyst; + + hyst = (data->hyst & ADT7X10_T_HYST_MASK) * 1000; + + /* + * hysteresis is stored as a 4 bit offset in the device, convert it + * to an absolute value + */ + if (nr == 2) /* min has positive offset, others have negative */ + hyst = -hyst; + return sprintf(buf, "%d\n", + ADT7X10_REG_TO_TEMP(data, data->temp[nr]) - hyst); +} + +static ssize_t adt7x10_set_t_hyst(struct device *dev, + struct device_attribute *da, + const char *buf, size_t count) +{ + struct adt7x10_data *data = dev_get_drvdata(dev); + int limit, ret; + long hyst; + + ret = kstrtol(buf, 10, &hyst); + if (ret) + return ret; + /* convert absolute hysteresis value to a 4 bit delta value */ + limit = ADT7X10_REG_TO_TEMP(data, data->temp[1]); + hyst = clamp_val(hyst, ADT7X10_TEMP_MIN, ADT7X10_TEMP_MAX); + data->hyst = clamp_val(DIV_ROUND_CLOSEST(limit - hyst, 1000), + 0, ADT7X10_T_HYST_MASK); + ret = adt7x10_write_byte(dev, ADT7X10_T_HYST, data->hyst); + if (ret) + return ret; + + return count; +} + +static ssize_t adt7x10_show_alarm(struct device *dev, + struct device_attribute *da, + char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + int ret; + + ret = adt7x10_read_byte(dev, ADT7X10_STATUS); + if (ret < 0) + return ret; + + return sprintf(buf, "%d\n", !!(ret & attr->index)); +} + +static ssize_t adt7x10_show_name(struct device *dev, + struct device_attribute *da, + char *buf) +{ + struct adt7x10_data *data = dev_get_drvdata(dev); + + return sprintf(buf, "%s\n", data->name); +} + +static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, adt7x10_show_temp, NULL, 0); +static SENSOR_DEVICE_ATTR(temp1_max, S_IWUSR | S_IRUGO, + adt7x10_show_temp, adt7x10_set_temp, 1); +static SENSOR_DEVICE_ATTR(temp1_min, S_IWUSR | S_IRUGO, + adt7x10_show_temp, adt7x10_set_temp, 2); +static SENSOR_DEVICE_ATTR(temp1_crit, S_IWUSR | S_IRUGO, + adt7x10_show_temp, adt7x10_set_temp, 3); +static SENSOR_DEVICE_ATTR(temp1_max_hyst, S_IWUSR | S_IRUGO, + adt7x10_show_t_hyst, adt7x10_set_t_hyst, 1); +static SENSOR_DEVICE_ATTR(temp1_min_hyst, S_IRUGO, + adt7x10_show_t_hyst, NULL, 2); +static SENSOR_DEVICE_ATTR(temp1_crit_hyst, S_IRUGO, + adt7x10_show_t_hyst, NULL, 3); +static SENSOR_DEVICE_ATTR(temp1_min_alarm, S_IRUGO, adt7x10_show_alarm, + NULL, ADT7X10_STAT_T_LOW); +static SENSOR_DEVICE_ATTR(temp1_max_alarm, S_IRUGO, adt7x10_show_alarm, + NULL, ADT7X10_STAT_T_HIGH); +static SENSOR_DEVICE_ATTR(temp1_crit_alarm, S_IRUGO, adt7x10_show_alarm, + NULL, ADT7X10_STAT_T_CRIT); +static DEVICE_ATTR(name, S_IRUGO, adt7x10_show_name, NULL); + +static struct attribute *adt7x10_attributes[] = { + &sensor_dev_attr_temp1_input.dev_attr.attr, + &sensor_dev_attr_temp1_max.dev_attr.attr, + &sensor_dev_attr_temp1_min.dev_attr.attr, + &sensor_dev_attr_temp1_crit.dev_attr.attr, + &sensor_dev_attr_temp1_max_hyst.dev_attr.attr, + &sensor_dev_attr_temp1_min_hyst.dev_attr.attr, + &sensor_dev_attr_temp1_crit_hyst.dev_attr.attr, + &sensor_dev_attr_temp1_min_alarm.dev_attr.attr, + &sensor_dev_attr_temp1_max_alarm.dev_attr.attr, + &sensor_dev_attr_temp1_crit_alarm.dev_attr.attr, + NULL +}; + +static const struct attribute_group adt7x10_group = { + .attrs = adt7x10_attributes, +}; + +int adt7x10_probe(struct device *dev, const char *name, int irq, + const struct adt7x10_ops *ops) +{ + struct adt7x10_data *data; + int ret; + + data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + data->ops = ops; + data->name = name; + + dev_set_drvdata(dev, data); + mutex_init(&data->update_lock); + + /* configure as specified */ + ret = adt7x10_read_byte(dev, ADT7X10_CONFIG); + if (ret < 0) { + dev_dbg(dev, "Can't read config? %d\n", ret); + return ret; + } + data->oldconfig = ret; + + /* + * Set to 16 bit resolution, continous conversion and comparator mode. + */ + data->config = data->oldconfig; + data->config &= ~(ADT7X10_MODE_MASK | ADT7X10_CT_POLARITY | + ADT7X10_INT_POLARITY); + data->config |= ADT7X10_FULL | ADT7X10_RESOLUTION | ADT7X10_EVENT_MODE; + + if (data->config != data->oldconfig) { + ret = adt7x10_write_byte(dev, ADT7X10_CONFIG, data->config); + if (ret) + return ret; + } + dev_dbg(dev, "Config %02x\n", data->config); + + ret = adt7x10_fill_cache(dev); + if (ret) + goto exit_restore; + + /* Register sysfs hooks */ + ret = sysfs_create_group(&dev->kobj, &adt7x10_group); + if (ret) + goto exit_restore; + + /* + * The I2C device will already have it's own 'name' attribute, but for + * the SPI device we need to register it. name will only be non NULL if + * the device doesn't register the 'name' attribute on its own. + */ + if (name) { + ret = device_create_file(dev, &dev_attr_name); + if (ret) + goto exit_remove; + } + + data->hwmon_dev = hwmon_device_register(dev); + if (IS_ERR(data->hwmon_dev)) { + ret = PTR_ERR(data->hwmon_dev); + goto exit_remove_name; + } + + if (irq > 0) { + ret = request_threaded_irq(irq, NULL, adt7x10_irq_handler, + IRQF_TRIGGER_FALLING | IRQF_ONESHOT, + dev_name(dev), dev); + if (ret) + goto exit_hwmon_device_unregister; + } + + return 0; + +exit_hwmon_device_unregister: + hwmon_device_unregister(data->hwmon_dev); +exit_remove_name: + if (name) + device_remove_file(dev, &dev_attr_name); +exit_remove: + sysfs_remove_group(&dev->kobj, &adt7x10_group); +exit_restore: + adt7x10_write_byte(dev, ADT7X10_CONFIG, data->oldconfig); + return ret; +} +EXPORT_SYMBOL_GPL(adt7x10_probe); + +int adt7x10_remove(struct device *dev, int irq) +{ + struct adt7x10_data *data = dev_get_drvdata(dev); + + if (irq > 0) + free_irq(irq, dev); + + hwmon_device_unregister(data->hwmon_dev); + if (data->name) + device_remove_file(dev, &dev_attr_name); + sysfs_remove_group(&dev->kobj, &adt7x10_group); + if (data->oldconfig != data->config) + adt7x10_write_byte(dev, ADT7X10_CONFIG, data->oldconfig); + return 0; +} +EXPORT_SYMBOL_GPL(adt7x10_remove); + +#ifdef CONFIG_PM_SLEEP + +static int adt7x10_suspend(struct device *dev) +{ + struct adt7x10_data *data = dev_get_drvdata(dev); + + return adt7x10_write_byte(dev, ADT7X10_CONFIG, + data->config | ADT7X10_PD); +} + +static int adt7x10_resume(struct device *dev) +{ + struct adt7x10_data *data = dev_get_drvdata(dev); + + return adt7x10_write_byte(dev, ADT7X10_CONFIG, data->config); +} + +SIMPLE_DEV_PM_OPS(adt7x10_dev_pm_ops, adt7x10_suspend, adt7x10_resume); +EXPORT_SYMBOL_GPL(adt7x10_dev_pm_ops); + +#endif /* CONFIG_PM_SLEEP */ + +MODULE_AUTHOR("Hartmut Knaack"); +MODULE_DESCRIPTION("ADT7410/ADT7420, ADT7310/ADT7320 common code"); +MODULE_LICENSE("GPL"); diff --git a/drivers/hwmon/adt7x10.h b/drivers/hwmon/adt7x10.h new file mode 100644 index 000000000000..d491c698529e --- /dev/null +++ b/drivers/hwmon/adt7x10.h @@ -0,0 +1,37 @@ +#ifndef __HWMON_ADT7X10_H__ +#define __HWMON_ADT7X10_H__ + +#include <linux/types.h> +#include <linux/pm.h> + +/* ADT7410 registers definition */ +#define ADT7X10_TEMPERATURE 0 +#define ADT7X10_STATUS 2 +#define ADT7X10_CONFIG 3 +#define ADT7X10_T_ALARM_HIGH 4 +#define ADT7X10_T_ALARM_LOW 6 +#define ADT7X10_T_CRIT 8 +#define ADT7X10_T_HYST 0xA +#define ADT7X10_ID 0xB + +struct device; + +struct adt7x10_ops { + int (*read_byte)(struct device *, u8 reg); + int (*write_byte)(struct device *, u8 reg, u8 data); + int (*read_word)(struct device *, u8 reg); + int (*write_word)(struct device *, u8 reg, u16 data); +}; + +int adt7x10_probe(struct device *dev, const char *name, int irq, + const struct adt7x10_ops *ops); +int adt7x10_remove(struct device *dev, int irq); + +#ifdef CONFIG_PM_SLEEP +extern const struct dev_pm_ops adt7x10_dev_pm_ops; +#define ADT7X10_DEV_PM_OPS (&adt7x10_dev_pm_ops) +#else +#define ADT7X10_DEV_PM_OPS NULL +#endif + +#endif diff --git a/drivers/hwmon/applesmc.c b/drivers/hwmon/applesmc.c index b41baffa20f0..62c2e32e25ef 100644 --- a/drivers/hwmon/applesmc.c +++ b/drivers/hwmon/applesmc.c @@ -922,7 +922,7 @@ static void applesmc_brightness_set(struct led_classdev *led_cdev, ret = queue_work(applesmc_led_wq, &backlight_work); if (debug && (!ret)) - printk(KERN_DEBUG "applesmc: work was already on the queue.\n"); + dev_dbg(led_cdev->dev, "work was already on the queue.\n"); } static ssize_t applesmc_key_count_show(struct device *dev, diff --git a/drivers/hwmon/asb100.c b/drivers/hwmon/asb100.c index 6ac612cabda1..f96063680e58 100644 --- a/drivers/hwmon/asb100.c +++ b/drivers/hwmon/asb100.c @@ -55,8 +55,8 @@ static const unsigned short normal_i2c[] = { 0x2d, I2C_CLIENT_END }; static unsigned short force_subclients[4]; module_param_array(force_subclients, short, NULL, 0); -MODULE_PARM_DESC(force_subclients, "List of subclient addresses: " - "{bus, clientaddr, subclientaddr1, subclientaddr2}"); +MODULE_PARM_DESC(force_subclients, + "List of subclient addresses: {bus, clientaddr, subclientaddr1, subclientaddr2}"); /* Voltage IN registers 0-6 */ #define ASB100_REG_IN(nr) (0x20 + (nr)) @@ -689,8 +689,8 @@ static int asb100_detect_subclients(struct i2c_client *client) for (i = 2; i <= 3; i++) { if (force_subclients[i] < 0x48 || force_subclients[i] > 0x4f) { - dev_err(&client->dev, "invalid subclient " - "address %d; must be 0x48-0x4f\n", + dev_err(&client->dev, + "invalid subclient address %d; must be 0x48-0x4f\n", force_subclients[i]); err = -ENODEV; goto ERROR_SC_2; @@ -708,24 +708,27 @@ static int asb100_detect_subclients(struct i2c_client *client) } if (sc_addr[0] == sc_addr[1]) { - dev_err(&client->dev, "duplicate addresses 0x%x " - "for subclients\n", sc_addr[0]); + dev_err(&client->dev, + "duplicate addresses 0x%x for subclients\n", + sc_addr[0]); err = -ENODEV; goto ERROR_SC_2; } data->lm75[0] = i2c_new_dummy(adapter, sc_addr[0]); if (!data->lm75[0]) { - dev_err(&client->dev, "subclient %d registration " - "at address 0x%x failed.\n", 1, sc_addr[0]); + dev_err(&client->dev, + "subclient %d registration at address 0x%x failed.\n", + 1, sc_addr[0]); err = -ENOMEM; goto ERROR_SC_2; } data->lm75[1] = i2c_new_dummy(adapter, sc_addr[1]); if (!data->lm75[1]) { - dev_err(&client->dev, "subclient %d registration " - "at address 0x%x failed.\n", 2, sc_addr[1]); + dev_err(&client->dev, + "subclient %d registration at address 0x%x failed.\n", + 2, sc_addr[1]); err = -ENOMEM; goto ERROR_SC_3; } diff --git a/drivers/hwmon/asc7621.c b/drivers/hwmon/asc7621.c index da7f5b5d5db5..3ad9d849add2 100644 --- a/drivers/hwmon/asc7621.c +++ b/drivers/hwmon/asc7621.c @@ -159,12 +159,12 @@ static inline int write_byte(struct i2c_client *client, u8 reg, u8 data) * and retrieval of like parameters. */ -#define SETUP_SHOW_data_param(d, a) \ +#define SETUP_SHOW_DATA_PARAM(d, a) \ struct sensor_device_attribute *sda = to_sensor_dev_attr(a); \ struct asc7621_data *data = asc7621_update_device(d); \ struct asc7621_param *param = to_asc7621_param(sda) -#define SETUP_STORE_data_param(d, a) \ +#define SETUP_STORE_DATA_PARAM(d, a) \ struct sensor_device_attribute *sda = to_sensor_dev_attr(a); \ struct i2c_client *client = to_i2c_client(d); \ struct asc7621_data *data = i2c_get_clientdata(client); \ @@ -177,7 +177,7 @@ static inline int write_byte(struct i2c_client *client, u8 reg, u8 data) static ssize_t show_u8(struct device *dev, struct device_attribute *attr, char *buf) { - SETUP_SHOW_data_param(dev, attr); + SETUP_SHOW_DATA_PARAM(dev, attr); return sprintf(buf, "%u\n", data->reg[param->msb[0]]); } @@ -185,7 +185,7 @@ static ssize_t show_u8(struct device *dev, struct device_attribute *attr, static ssize_t store_u8(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { - SETUP_STORE_data_param(dev, attr); + SETUP_STORE_DATA_PARAM(dev, attr); long reqval; if (kstrtol(buf, 10, &reqval)) @@ -206,7 +206,7 @@ static ssize_t store_u8(struct device *dev, struct device_attribute *attr, static ssize_t show_bitmask(struct device *dev, struct device_attribute *attr, char *buf) { - SETUP_SHOW_data_param(dev, attr); + SETUP_SHOW_DATA_PARAM(dev, attr); return sprintf(buf, "%u\n", (data->reg[param->msb[0]] >> param-> @@ -217,7 +217,7 @@ static ssize_t store_bitmask(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { - SETUP_STORE_data_param(dev, attr); + SETUP_STORE_DATA_PARAM(dev, attr); long reqval; u8 currval; @@ -246,7 +246,7 @@ static ssize_t store_bitmask(struct device *dev, static ssize_t show_fan16(struct device *dev, struct device_attribute *attr, char *buf) { - SETUP_SHOW_data_param(dev, attr); + SETUP_SHOW_DATA_PARAM(dev, attr); u16 regval; mutex_lock(&data->update_lock); @@ -262,7 +262,7 @@ static ssize_t store_fan16(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { - SETUP_STORE_data_param(dev, attr); + SETUP_STORE_DATA_PARAM(dev, attr); long reqval; if (kstrtol(buf, 10, &reqval)) @@ -307,7 +307,7 @@ static int asc7621_in_scaling[] = { static ssize_t show_in10(struct device *dev, struct device_attribute *attr, char *buf) { - SETUP_SHOW_data_param(dev, attr); + SETUP_SHOW_DATA_PARAM(dev, attr); u16 regval; u8 nr = sda->index; @@ -325,7 +325,7 @@ static ssize_t show_in10(struct device *dev, struct device_attribute *attr, static ssize_t show_in8(struct device *dev, struct device_attribute *attr, char *buf) { - SETUP_SHOW_data_param(dev, attr); + SETUP_SHOW_DATA_PARAM(dev, attr); u8 nr = sda->index; return sprintf(buf, "%u\n", @@ -336,7 +336,7 @@ static ssize_t show_in8(struct device *dev, struct device_attribute *attr, static ssize_t store_in8(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { - SETUP_STORE_data_param(dev, attr); + SETUP_STORE_DATA_PARAM(dev, attr); long reqval; u8 nr = sda->index; @@ -360,7 +360,7 @@ static ssize_t store_in8(struct device *dev, struct device_attribute *attr, static ssize_t show_temp8(struct device *dev, struct device_attribute *attr, char *buf) { - SETUP_SHOW_data_param(dev, attr); + SETUP_SHOW_DATA_PARAM(dev, attr); return sprintf(buf, "%d\n", ((s8) data->reg[param->msb[0]]) * 1000); } @@ -369,7 +369,7 @@ static ssize_t store_temp8(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { - SETUP_STORE_data_param(dev, attr); + SETUP_STORE_DATA_PARAM(dev, attr); long reqval; s8 temp; @@ -397,7 +397,7 @@ static ssize_t store_temp8(struct device *dev, static ssize_t show_temp10(struct device *dev, struct device_attribute *attr, char *buf) { - SETUP_SHOW_data_param(dev, attr); + SETUP_SHOW_DATA_PARAM(dev, attr); u8 msb, lsb; int temp; @@ -414,7 +414,7 @@ static ssize_t show_temp10(struct device *dev, static ssize_t show_temp62(struct device *dev, struct device_attribute *attr, char *buf) { - SETUP_SHOW_data_param(dev, attr); + SETUP_SHOW_DATA_PARAM(dev, attr); u8 regval = data->reg[param->msb[0]]; int temp = ((s8) (regval & 0xfc) * 1000) + ((regval & 0x03) * 250); @@ -425,7 +425,7 @@ static ssize_t store_temp62(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { - SETUP_STORE_data_param(dev, attr); + SETUP_STORE_DATA_PARAM(dev, attr); long reqval, i, f; s8 temp; @@ -459,7 +459,7 @@ static u32 asc7621_range_map[] = { static ssize_t show_ap2_temp(struct device *dev, struct device_attribute *attr, char *buf) { - SETUP_SHOW_data_param(dev, attr); + SETUP_SHOW_DATA_PARAM(dev, attr); long auto_point1; u8 regval; int temp; @@ -479,7 +479,7 @@ static ssize_t store_ap2_temp(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { - SETUP_STORE_data_param(dev, attr); + SETUP_STORE_DATA_PARAM(dev, attr); long reqval, auto_point1; int i; u8 currval, newval = 0; @@ -510,7 +510,7 @@ static ssize_t store_ap2_temp(struct device *dev, static ssize_t show_pwm_ac(struct device *dev, struct device_attribute *attr, char *buf) { - SETUP_SHOW_data_param(dev, attr); + SETUP_SHOW_DATA_PARAM(dev, attr); u8 config, altbit, regval; u8 map[] = { 0x01, 0x02, 0x04, 0x1f, 0x00, 0x06, 0x07, 0x10, @@ -530,7 +530,7 @@ static ssize_t store_pwm_ac(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { - SETUP_STORE_data_param(dev, attr); + SETUP_STORE_DATA_PARAM(dev, attr); unsigned long reqval; u8 currval, config, altbit, newval; u16 map[] = { @@ -569,7 +569,7 @@ static ssize_t store_pwm_ac(struct device *dev, static ssize_t show_pwm_enable(struct device *dev, struct device_attribute *attr, char *buf) { - SETUP_SHOW_data_param(dev, attr); + SETUP_SHOW_DATA_PARAM(dev, attr); u8 config, altbit, minoff, val, newval; mutex_lock(&data->update_lock); @@ -599,7 +599,7 @@ static ssize_t store_pwm_enable(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { - SETUP_STORE_data_param(dev, attr); + SETUP_STORE_DATA_PARAM(dev, attr); long reqval; u8 currval, config, altbit, newval, minoff = 255; @@ -659,7 +659,7 @@ static u32 asc7621_pwm_freq_map[] = { static ssize_t show_pwm_freq(struct device *dev, struct device_attribute *attr, char *buf) { - SETUP_SHOW_data_param(dev, attr); + SETUP_SHOW_DATA_PARAM(dev, attr); u8 regval = (data->reg[param->msb[0]] >> param->shift[0]) & param->mask[0]; @@ -672,7 +672,7 @@ static ssize_t store_pwm_freq(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { - SETUP_STORE_data_param(dev, attr); + SETUP_STORE_DATA_PARAM(dev, attr); unsigned long reqval; u8 currval, newval = 255; int i; @@ -707,7 +707,7 @@ static u32 asc7621_pwm_auto_spinup_map[] = { static ssize_t show_pwm_ast(struct device *dev, struct device_attribute *attr, char *buf) { - SETUP_SHOW_data_param(dev, attr); + SETUP_SHOW_DATA_PARAM(dev, attr); u8 regval = (data->reg[param->msb[0]] >> param->shift[0]) & param->mask[0]; @@ -721,7 +721,7 @@ static ssize_t store_pwm_ast(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { - SETUP_STORE_data_param(dev, attr); + SETUP_STORE_DATA_PARAM(dev, attr); long reqval; u8 currval, newval = 255; u32 i; @@ -756,7 +756,7 @@ static u32 asc7621_temp_smoothing_time_map[] = { static ssize_t show_temp_st(struct device *dev, struct device_attribute *attr, char *buf) { - SETUP_SHOW_data_param(dev, attr); + SETUP_SHOW_DATA_PARAM(dev, attr); u8 regval = (data->reg[param->msb[0]] >> param->shift[0]) & param->mask[0]; regval = clamp_val(regval, 0, 7); @@ -768,7 +768,7 @@ static ssize_t store_temp_st(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { - SETUP_STORE_data_param(dev, attr); + SETUP_STORE_DATA_PARAM(dev, attr); long reqval; u8 currval, newval = 255; u32 i; diff --git a/drivers/hwmon/coretemp.c b/drivers/hwmon/coretemp.c index 3f1e297663ad..658ce3a8717f 100644 --- a/drivers/hwmon/coretemp.c +++ b/drivers/hwmon/coretemp.c @@ -411,8 +411,7 @@ static int __cpuinit chk_ucode_version(unsigned int cpu) * fixed for stepping D0 (6EC). */ if (c->x86_model == 0xe && c->x86_mask < 0xc && c->microcode < 0x39) { - pr_err("Errata AE18 not fixed, update BIOS or " - "microcode of the CPU!\n"); + pr_err("Errata AE18 not fixed, update BIOS or microcode of the CPU!\n"); return -ENODEV; } return 0; diff --git a/drivers/hwmon/da9052-hwmon.c b/drivers/hwmon/da9052-hwmon.c index ab4452c5a98c..960fac3fb166 100644 --- a/drivers/hwmon/da9052-hwmon.c +++ b/drivers/hwmon/da9052-hwmon.c @@ -43,19 +43,19 @@ static const char * const input_names[] = { }; /* Conversion function for VDDOUT and VBAT */ -static inline int volt_reg_to_mV(int value) +static inline int volt_reg_to_mv(int value) { return DIV_ROUND_CLOSEST(value * 1000, 512) + 2500; } /* Conversion function for ADC channels 4, 5 and 6 */ -static inline int input_reg_to_mV(int value) +static inline int input_reg_to_mv(int value) { return DIV_ROUND_CLOSEST(value * 2500, 1023); } /* Conversion function for VBBAT */ -static inline int vbbat_reg_to_mV(int value) +static inline int vbbat_reg_to_mv(int value) { return DIV_ROUND_CLOSEST(value * 2500, 512); } @@ -96,7 +96,7 @@ static ssize_t da9052_read_vddout(struct device *dev, goto hwmon_err; mutex_unlock(&hwmon->hwmon_lock); - return sprintf(buf, "%d\n", volt_reg_to_mV(vdd)); + return sprintf(buf, "%d\n", volt_reg_to_mv(vdd)); hwmon_err_release: da9052_disable_vddout_channel(hwmon->da9052); @@ -137,7 +137,7 @@ static ssize_t da9052_read_vbat(struct device *dev, if (ret < 0) return ret; - return sprintf(buf, "%d\n", volt_reg_to_mV(ret)); + return sprintf(buf, "%d\n", volt_reg_to_mv(ret)); } static ssize_t da9052_read_misc_channel(struct device *dev, @@ -152,7 +152,7 @@ static ssize_t da9052_read_misc_channel(struct device *dev, if (ret < 0) return ret; - return sprintf(buf, "%d\n", input_reg_to_mV(ret)); + return sprintf(buf, "%d\n", input_reg_to_mv(ret)); } static ssize_t da9052_read_tjunc(struct device *dev, @@ -187,7 +187,7 @@ static ssize_t da9052_read_vbbat(struct device *dev, if (ret < 0) return ret; - return sprintf(buf, "%d\n", vbbat_reg_to_mV(ret)); + return sprintf(buf, "%d\n", vbbat_reg_to_mv(ret)); } static ssize_t da9052_hwmon_show_name(struct device *dev, diff --git a/drivers/hwmon/da9055-hwmon.c b/drivers/hwmon/da9055-hwmon.c index 9465c050c326..029ecabc4380 100644 --- a/drivers/hwmon/da9055-hwmon.c +++ b/drivers/hwmon/da9055-hwmon.c @@ -119,7 +119,7 @@ static irqreturn_t da9055_auxadc_irq(int irq, void *irq_data) } /* Conversion function for VSYS and ADCINx */ -static inline int volt_reg_to_mV(int value, int channel) +static inline int volt_reg_to_mv(int value, int channel) { if (channel == DA9055_ADC_VSYS) return DIV_ROUND_CLOSEST(value * 1000, DA9055_VSYS_DIV) + 2500; @@ -168,7 +168,7 @@ static ssize_t da9055_read_auto_ch(struct device *dev, mutex_unlock(&hwmon->hwmon_lock); - return sprintf(buf, "%d\n", volt_reg_to_mV(adc, channel)); + return sprintf(buf, "%d\n", volt_reg_to_mv(adc, channel)); hwmon_err_release: da9055_disable_auto_mode(hwmon->da9055, channel); diff --git a/drivers/hwmon/dme1737.c b/drivers/hwmon/dme1737.c index c347c94f2f73..4ae3fff13f44 100644 --- a/drivers/hwmon/dme1737.c +++ b/drivers/hwmon/dme1737.c @@ -55,14 +55,16 @@ MODULE_PARM_DESC(force_id, "Override the detected device ID"); static bool probe_all_addr; module_param(probe_all_addr, bool, 0); -MODULE_PARM_DESC(probe_all_addr, "Include probing of non-standard LPC " - "addresses"); +MODULE_PARM_DESC(probe_all_addr, + "Include probing of non-standard LPC addresses"); /* Addresses to scan */ static const unsigned short normal_i2c[] = {0x2c, 0x2d, 0x2e, I2C_CLIENT_END}; enum chips { dme1737, sch5027, sch311x, sch5127 }; +#define DO_REPORT "Please report to the driver maintainer." + /* --------------------------------------------------------------------- * Registers * @@ -566,9 +568,9 @@ static u8 dme1737_read(const struct dme1737_data *data, u8 reg) val = i2c_smbus_read_byte_data(client, reg); if (val < 0) { - dev_warn(&client->dev, "Read from register " - "0x%02x failed! Please report to the driver " - "maintainer.\n", reg); + dev_warn(&client->dev, + "Read from register 0x%02x failed! %s\n", + reg, DO_REPORT); } } else { /* ISA device */ outb(reg, data->addr); @@ -587,9 +589,9 @@ static s32 dme1737_write(const struct dme1737_data *data, u8 reg, u8 val) res = i2c_smbus_write_byte_data(client, reg, val); if (res < 0) { - dev_warn(&client->dev, "Write to register " - "0x%02x failed! Please report to the driver " - "maintainer.\n", reg); + dev_warn(&client->dev, + "Write to register 0x%02x failed! %s\n", + reg, DO_REPORT); } } else { /* ISA device */ outb(reg, data->addr); @@ -1167,8 +1169,8 @@ static ssize_t set_fan(struct device *dev, struct device_attribute *attr, /* Only valid for fan[1-4] */ if (!(val == 1 || val == 2 || val == 4)) { count = -EINVAL; - dev_warn(dev, "Fan type value %ld not " - "supported. Choose one of 1, 2, or 4.\n", + dev_warn(dev, + "Fan type value %ld not supported. Choose one of 1, 2, or 4.\n", val); goto exit; } @@ -1294,8 +1296,8 @@ static ssize_t set_pwm(struct device *dev, struct device_attribute *attr, /* Only valid for pwm[1-3] */ if (val < 0 || val > 2) { count = -EINVAL; - dev_warn(dev, "PWM enable %ld not " - "supported. Choose one of 0, 1, or 2.\n", + dev_warn(dev, + "PWM enable %ld not supported. Choose one of 0, 1, or 2.\n", val); goto exit; } @@ -1399,8 +1401,8 @@ static ssize_t set_pwm(struct device *dev, struct device_attribute *attr, if (!(val == 1 || val == 2 || val == 4 || val == 6 || val == 7)) { count = -EINVAL; - dev_warn(dev, "PWM auto channels zone %ld " - "not supported. Choose one of 1, 2, 4, 6, " + dev_warn(dev, + "PWM auto channels zone %ld not supported. Choose one of 1, 2, 4, 6, " "or 7.\n", val); goto exit; } @@ -2178,8 +2180,8 @@ static int dme1737_create_files(struct device *dev) * selected attributes from read-only to read-writeable. */ if (data->config & 0x02) { - dev_info(dev, "Device is locked. Some attributes " - "will be read-only.\n"); + dev_info(dev, + "Device is locked. Some attributes will be read-only.\n"); } else { /* Change permissions of zone sysfs attributes */ dme1737_chmod_group(dev, &dme1737_zone_chmod_group, @@ -2247,9 +2249,8 @@ static int dme1737_init_device(struct device *dev) /* Inform if part is not monitoring/started */ if (!(data->config & 0x01)) { if (!force_start) { - dev_err(dev, "Device is not monitoring. " - "Use the force_start load parameter to " - "override.\n"); + dev_err(dev, + "Device is not monitoring. Use the force_start load parameter to override.\n"); return -EFAULT; } @@ -2289,8 +2290,8 @@ static int dme1737_init_device(struct device *dev) */ if (dme1737_i2c_get_features(0x2e, data) && dme1737_i2c_get_features(0x4e, data)) { - dev_warn(dev, "Failed to query Super-IO for optional " - "features.\n"); + dev_warn(dev, + "Failed to query Super-IO for optional features.\n"); } } @@ -2317,8 +2318,8 @@ static int dme1737_init_device(struct device *dev) break; } - dev_info(dev, "Optional features: pwm3=%s, pwm5=%s, pwm6=%s, " - "fan3=%s, fan4=%s, fan5=%s, fan6=%s.\n", + dev_info(dev, + "Optional features: pwm3=%s, pwm5=%s, pwm6=%s, fan3=%s, fan4=%s, fan5=%s, fan6=%s.\n", (data->has_features & HAS_PWM(2)) ? "yes" : "no", (data->has_features & HAS_PWM(4)) ? "yes" : "no", (data->has_features & HAS_PWM(5)) ? "yes" : "no", @@ -2330,18 +2331,16 @@ static int dme1737_init_device(struct device *dev) reg = dme1737_read(data, DME1737_REG_TACH_PWM); /* Inform if fan-to-pwm mapping differs from the default */ if (client && reg != 0xa4) { /* I2C chip */ - dev_warn(dev, "Non-standard fan to pwm mapping: " - "fan1->pwm%d, fan2->pwm%d, fan3->pwm%d, " - "fan4->pwm%d. Please report to the driver " - "maintainer.\n", + dev_warn(dev, + "Non-standard fan to pwm mapping: fan1->pwm%d, fan2->pwm%d, fan3->pwm%d, fan4->pwm%d. %s\n", (reg & 0x03) + 1, ((reg >> 2) & 0x03) + 1, - ((reg >> 4) & 0x03) + 1, ((reg >> 6) & 0x03) + 1); + ((reg >> 4) & 0x03) + 1, ((reg >> 6) & 0x03) + 1, + DO_REPORT); } else if (!client && reg != 0x24) { /* ISA chip */ - dev_warn(dev, "Non-standard fan to pwm mapping: " - "fan1->pwm%d, fan2->pwm%d, fan3->pwm%d. " - "Please report to the driver maintainer.\n", + dev_warn(dev, + "Non-standard fan to pwm mapping: fan1->pwm%d, fan2->pwm%d, fan3->pwm%d. %s\n", (reg & 0x03) + 1, ((reg >> 2) & 0x03) + 1, - ((reg >> 4) & 0x03) + 1); + ((reg >> 4) & 0x03) + 1, DO_REPORT); } /* @@ -2355,8 +2354,9 @@ static int dme1737_init_device(struct device *dev) DME1737_REG_PWM_CONFIG(ix)); if ((data->has_features & HAS_PWM(ix)) && (PWM_EN_FROM_REG(data->pwm_config[ix]) == -1)) { - dev_info(dev, "Switching pwm%d to " - "manual mode.\n", ix + 1); + dev_info(dev, + "Switching pwm%d to manual mode.\n", + ix + 1); data->pwm_config[ix] = PWM_EN_TO_REG(1, data->pwm_config[ix]); dme1737_write(data, DME1737_REG_PWM(ix), 0); diff --git a/drivers/hwmon/f71805f.c b/drivers/hwmon/f71805f.c index a9816979c5de..0c9f3da242bf 100644 --- a/drivers/hwmon/f71805f.c +++ b/drivers/hwmon/f71805f.c @@ -1350,8 +1350,7 @@ static void f71805f_init_device(struct f71805f_data *data) reg = f71805f_read8(data, F71805F_REG_START); if ((reg & 0x41) != 0x01) { - printk(KERN_DEBUG DRVNAME ": Starting monitoring " - "operations\n"); + pr_debug("Starting monitoring operations\n"); f71805f_write8(data, F71805F_REG_START, (reg | 0x01) & ~0x40); } diff --git a/drivers/hwmon/fam15h_power.c b/drivers/hwmon/fam15h_power.c index b757088aeddb..dff841085baf 100644 --- a/drivers/hwmon/fam15h_power.c +++ b/drivers/hwmon/fam15h_power.c @@ -189,8 +189,8 @@ static void fam15h_power_init_data(struct pci_dev *f4, /* result not allowed to be >= 256W */ if ((tmp >> 16) >= 256) - dev_warn(&f4->dev, "Bogus value for ProcessorPwrWatts " - "(processor_pwr_watts>=%u)\n", + dev_warn(&f4->dev, + "Bogus value for ProcessorPwrWatts (processor_pwr_watts>=%u)\n", (unsigned int) (tmp >> 16)); /* convert to microWatt */ diff --git a/drivers/hwmon/fschmd.c b/drivers/hwmon/fschmd.c index 8af2755cdb87..d58abdc5a4cf 100644 --- a/drivers/hwmon/fschmd.c +++ b/drivers/hwmon/fschmd.c @@ -463,8 +463,9 @@ static ssize_t store_fan_div(struct device *dev, struct device_attribute v = 3; break; default: - dev_err(dev, "fan_div value %lu not supported. " - "Choose one of 2, 4 or 8!\n", v); + dev_err(dev, + "fan_div value %lu not supported. Choose one of 2, 4 or 8!\n", + v); return -EINVAL; } @@ -1249,8 +1250,8 @@ static int fschmd_probe(struct i2c_client *client, } if (i == ARRAY_SIZE(watchdog_minors)) { data->watchdog_miscdev.minor = 0; - dev_warn(&client->dev, "Couldn't register watchdog chardev " - "(due to no free minor)\n"); + dev_warn(&client->dev, + "Couldn't register watchdog chardev (due to no free minor)\n"); } mutex_unlock(&watchdog_data_mutex); diff --git a/drivers/hwmon/gl518sm.c b/drivers/hwmon/gl518sm.c index e2e5909a34df..95257a5621d8 100644 --- a/drivers/hwmon/gl518sm.c +++ b/drivers/hwmon/gl518sm.c @@ -344,8 +344,9 @@ static ssize_t set_fan_div(struct device *dev, struct device_attribute *attr, val = 3; break; default: - dev_err(dev, "Invalid fan clock divider %lu, choose one " - "of 1, 2, 4 or 8\n", val); + dev_err(dev, + "Invalid fan clock divider %lu, choose one of 1, 2, 4 or 8\n", + val); return -EINVAL; } diff --git a/drivers/hwmon/gpio-fan.c b/drivers/hwmon/gpio-fan.c index 39781945a5d2..3104149795c5 100644 --- a/drivers/hwmon/gpio-fan.c +++ b/drivers/hwmon/gpio-fan.c @@ -105,10 +105,6 @@ static int fan_alarm_init(struct gpio_fan_data *fan_data, if (err) return err; - err = device_create_file(&pdev->dev, &dev_attr_fan1_alarm); - if (err) - return err; - /* * If the alarm GPIO don't support interrupts, just leave * without initializing the fail notification support. @@ -121,23 +117,9 @@ static int fan_alarm_init(struct gpio_fan_data *fan_data, irq_set_irq_type(alarm_irq, IRQ_TYPE_EDGE_BOTH); err = devm_request_irq(&pdev->dev, alarm_irq, fan_alarm_irq_handler, IRQF_SHARED, "GPIO fan alarm", fan_data); - if (err) - goto err_free_sysfs; - - return 0; - -err_free_sysfs: - device_remove_file(&pdev->dev, &dev_attr_fan1_alarm); return err; } -static void fan_alarm_free(struct gpio_fan_data *fan_data) -{ - struct platform_device *pdev = fan_data->pdev; - - device_remove_file(&pdev->dev, &dev_attr_fan1_alarm); -} - /* * Control GPIOs. */ @@ -327,6 +309,12 @@ exit_unlock: return ret; } +static ssize_t show_name(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "gpio-fan\n"); +} + static DEVICE_ATTR(pwm1, S_IRUGO | S_IWUSR, show_pwm, set_pwm); static DEVICE_ATTR(pwm1_enable, S_IRUGO | S_IWUSR, show_pwm_enable, set_pwm_enable); @@ -336,8 +324,26 @@ static DEVICE_ATTR(fan1_max, S_IRUGO, show_rpm_max, NULL); static DEVICE_ATTR(fan1_input, S_IRUGO, show_rpm, NULL); static DEVICE_ATTR(fan1_target, S_IRUGO | S_IWUSR, show_rpm, set_rpm); -static struct attribute *gpio_fan_ctrl_attributes[] = { - &dev_attr_pwm1.attr, +static DEVICE_ATTR(name, S_IRUGO, show_name, NULL); + +static umode_t gpio_fan_is_visible(struct kobject *kobj, + struct attribute *attr, int index) +{ + struct device *dev = container_of(kobj, struct device, kobj); + struct gpio_fan_data *data = dev_get_drvdata(dev); + + if (index == 1 && !data->alarm) + return 0; + if (index > 1 && !data->ctrl) + return 0; + + return attr->mode; +} + +static struct attribute *gpio_fan_attributes[] = { + &dev_attr_name.attr, + &dev_attr_fan1_alarm.attr, /* 1 */ + &dev_attr_pwm1.attr, /* 2 */ &dev_attr_pwm1_enable.attr, &dev_attr_pwm1_mode.attr, &dev_attr_fan1_input.attr, @@ -347,8 +353,9 @@ static struct attribute *gpio_fan_ctrl_attributes[] = { NULL }; -static const struct attribute_group gpio_fan_ctrl_group = { - .attrs = gpio_fan_ctrl_attributes, +static const struct attribute_group gpio_fan_group = { + .attrs = gpio_fan_attributes, + .is_visible = gpio_fan_is_visible, }; static int fan_ctrl_init(struct gpio_fan_data *fan_data, @@ -379,30 +386,9 @@ static int fan_ctrl_init(struct gpio_fan_data *fan_data, if (fan_data->speed_index < 0) return -ENODEV; - err = sysfs_create_group(&pdev->dev.kobj, &gpio_fan_ctrl_group); - return err; -} - -static void fan_ctrl_free(struct gpio_fan_data *fan_data) -{ - struct platform_device *pdev = fan_data->pdev; - - sysfs_remove_group(&pdev->dev.kobj, &gpio_fan_ctrl_group); -} - -/* - * Platform driver. - */ - -static ssize_t show_name(struct device *dev, - struct device_attribute *attr, char *buf) -{ - return sprintf(buf, "gpio-fan\n"); + return 0; } -static DEVICE_ATTR(name, S_IRUGO, show_name, NULL); - - #ifdef CONFIG_OF_GPIO /* * Translate OpenFirmware node properties into platform_data @@ -546,38 +532,30 @@ static int gpio_fan_probe(struct platform_device *pdev) /* Configure control GPIOs if available. */ if (pdata->ctrl && pdata->num_ctrl > 0) { - if (!pdata->speed || pdata->num_speed <= 1) { - err = -EINVAL; - goto err_free_alarm; - } + if (!pdata->speed || pdata->num_speed <= 1) + return -EINVAL; err = fan_ctrl_init(fan_data, pdata); if (err) - goto err_free_alarm; + return err; } - err = device_create_file(&pdev->dev, &dev_attr_name); + err = sysfs_create_group(&pdev->dev.kobj, &gpio_fan_group); if (err) - goto err_free_ctrl; + return err; /* Make this driver part of hwmon class. */ fan_data->hwmon_dev = hwmon_device_register(&pdev->dev); if (IS_ERR(fan_data->hwmon_dev)) { err = PTR_ERR(fan_data->hwmon_dev); - goto err_remove_name; + goto err_remove; } dev_info(&pdev->dev, "GPIO fan initialized\n"); return 0; -err_remove_name: - device_remove_file(&pdev->dev, &dev_attr_name); -err_free_ctrl: - if (fan_data->ctrl) - fan_ctrl_free(fan_data); -err_free_alarm: - if (fan_data->alarm) - fan_alarm_free(fan_data); +err_remove: + sysfs_remove_group(&pdev->dev.kobj, &gpio_fan_group); return err; } @@ -586,11 +564,7 @@ static int gpio_fan_remove(struct platform_device *pdev) struct gpio_fan_data *fan_data = platform_get_drvdata(pdev); hwmon_device_unregister(fan_data->hwmon_dev); - device_remove_file(&pdev->dev, &dev_attr_name); - if (fan_data->alarm) - fan_alarm_free(fan_data); - if (fan_data->ctrl) - fan_ctrl_free(fan_data); + sysfs_remove_group(&pdev->dev.kobj, &gpio_fan_group); return 0; } @@ -619,7 +593,7 @@ static int gpio_fan_resume(struct device *dev) } static SIMPLE_DEV_PM_OPS(gpio_fan_pm, gpio_fan_suspend, gpio_fan_resume); -#define GPIO_FAN_PM &gpio_fan_pm +#define GPIO_FAN_PM (&gpio_fan_pm) #else #define GPIO_FAN_PM NULL #endif diff --git a/drivers/hwmon/ibmaem.c b/drivers/hwmon/ibmaem.c index a14f634248e7..1429f6e177f4 100644 --- a/drivers/hwmon/ibmaem.c +++ b/drivers/hwmon/ibmaem.c @@ -289,8 +289,9 @@ static int aem_init_ipmi_data(struct aem_ipmi_data *data, int iface, err = ipmi_create_user(data->interface, &driver_data.ipmi_hndlrs, data, &data->user); if (err < 0) { - dev_err(bmc, "Unable to register user with IPMI " - "interface %d\n", data->interface); + dev_err(bmc, + "Unable to register user with IPMI interface %d\n", + data->interface); return -EACCES; } @@ -328,8 +329,8 @@ static void aem_msg_handler(struct ipmi_recv_msg *msg, void *user_msg_data) struct aem_ipmi_data *data = user_msg_data; if (msg->msgid != data->tx_msgid) { - dev_err(data->bmc_device, "Mismatch between received msgid " - "(%02x) and transmitted msgid (%02x)!\n", + dev_err(data->bmc_device, + "Mismatch between received msgid (%02x) and transmitted msgid (%02x)!\n", (int)msg->msgid, (int)data->tx_msgid); ipmi_free_recv_msg(msg); @@ -575,8 +576,8 @@ static int aem_init_aem1_inst(struct aem_ipmi_data *probe, u8 module_handle) /* Register with hwmon */ data->hwmon_dev = hwmon_device_register(&data->pdev->dev); if (IS_ERR(data->hwmon_dev)) { - dev_err(&data->pdev->dev, "Unable to register hwmon " - "device for IPMI interface %d\n", + dev_err(&data->pdev->dev, + "Unable to register hwmon device for IPMI interface %d\n", probe->interface); res = PTR_ERR(data->hwmon_dev); goto hwmon_reg_err; @@ -715,8 +716,8 @@ static int aem_init_aem2_inst(struct aem_ipmi_data *probe, /* Register with hwmon */ data->hwmon_dev = hwmon_device_register(&data->pdev->dev); if (IS_ERR(data->hwmon_dev)) { - dev_err(&data->pdev->dev, "Unable to register hwmon " - "device for IPMI interface %d\n", + dev_err(&data->pdev->dev, + "Unable to register hwmon device for IPMI interface %d\n", probe->interface); res = PTR_ERR(data->hwmon_dev); goto hwmon_reg_err; @@ -768,8 +769,8 @@ static void aem_init_aem2(struct aem_ipmi_data *probe) while (!aem_find_aem2(probe, &fi_resp, i)) { if (fi_resp.major != 2) { - dev_err(probe->bmc_device, "Unknown AEM v%d; please " - "report this to the maintainer.\n", + dev_err(probe->bmc_device, + "Unknown AEM v%d; please report this to the maintainer.\n", fi_resp.major); i++; continue; diff --git a/drivers/hwmon/ibmpex.c b/drivers/hwmon/ibmpex.c index b622a93ec32c..74b365ea01c7 100644 --- a/drivers/hwmon/ibmpex.c +++ b/drivers/hwmon/ibmpex.c @@ -163,8 +163,8 @@ static int ibmpex_ver_check(struct ibmpex_bmc_data *data) data->sensor_major = data->rx_msg_data[0]; data->sensor_minor = data->rx_msg_data[1]; - dev_info(data->bmc_device, "Found BMC with sensor interface " - "v%d.%d %d-%02d-%02d on interface %d\n", + dev_info(data->bmc_device, + "Found BMC with sensor interface v%d.%d %d-%02d-%02d on interface %d\n", data->sensor_major, data->sensor_minor, extract_value(data->rx_msg_data, 2), @@ -478,8 +478,9 @@ static void ibmpex_register_bmc(int iface, struct device *dev) err = ipmi_create_user(data->interface, &driver_data.ipmi_hndlrs, data, &data->user); if (err < 0) { - dev_err(dev, "Unable to register user with IPMI " - "interface %d\n", data->interface); + dev_err(dev, + "Unable to register user with IPMI interface %d\n", + data->interface); goto out; } @@ -501,8 +502,8 @@ static void ibmpex_register_bmc(int iface, struct device *dev) data->hwmon_dev = hwmon_device_register(data->bmc_device); if (IS_ERR(data->hwmon_dev)) { - dev_err(data->bmc_device, "Unable to register hwmon " - "device for IPMI interface %d\n", + dev_err(data->bmc_device, + "Unable to register hwmon device for IPMI interface %d\n", data->interface); goto out_user; } @@ -567,8 +568,8 @@ static void ibmpex_msg_handler(struct ipmi_recv_msg *msg, void *user_msg_data) struct ibmpex_bmc_data *data = (struct ibmpex_bmc_data *)user_msg_data; if (msg->msgid != data->tx_msgid) { - dev_err(data->bmc_device, "Mismatch between received msgid " - "(%02x) and transmitted msgid (%02x)!\n", + dev_err(data->bmc_device, + "Mismatch between received msgid (%02x) and transmitted msgid (%02x)!\n", (int)msg->msgid, (int)data->tx_msgid); ipmi_free_recv_msg(msg); diff --git a/drivers/hwmon/ina2xx.c b/drivers/hwmon/ina2xx.c index 8e7158c3ad2f..4958b2f89dce 100644 --- a/drivers/hwmon/ina2xx.c +++ b/drivers/hwmon/ina2xx.c @@ -186,20 +186,20 @@ static ssize_t ina2xx_show_value(struct device *dev, } /* shunt voltage */ -static SENSOR_DEVICE_ATTR(in0_input, S_IRUGO, \ - ina2xx_show_value, NULL, INA2XX_SHUNT_VOLTAGE); +static SENSOR_DEVICE_ATTR(in0_input, S_IRUGO, ina2xx_show_value, NULL, + INA2XX_SHUNT_VOLTAGE); /* bus voltage */ -static SENSOR_DEVICE_ATTR(in1_input, S_IRUGO, \ - ina2xx_show_value, NULL, INA2XX_BUS_VOLTAGE); +static SENSOR_DEVICE_ATTR(in1_input, S_IRUGO, ina2xx_show_value, NULL, + INA2XX_BUS_VOLTAGE); /* calculated current */ -static SENSOR_DEVICE_ATTR(curr1_input, S_IRUGO, \ - ina2xx_show_value, NULL, INA2XX_CURRENT); +static SENSOR_DEVICE_ATTR(curr1_input, S_IRUGO, ina2xx_show_value, NULL, + INA2XX_CURRENT); /* calculated power */ -static SENSOR_DEVICE_ATTR(power1_input, S_IRUGO, \ - ina2xx_show_value, NULL, INA2XX_POWER); +static SENSOR_DEVICE_ATTR(power1_input, S_IRUGO, ina2xx_show_value, NULL, + INA2XX_POWER); /* pointers to created device attributes */ static struct attribute *ina2xx_attributes[] = { diff --git a/drivers/hwmon/it87.c b/drivers/hwmon/it87.c index 37fc980fde24..72b21d5b1c62 100644 --- a/drivers/hwmon/it87.c +++ b/drivers/hwmon/it87.c @@ -1778,7 +1778,7 @@ static int __init it87_find(unsigned short *address, superio_select(5); sio_data->beep_pin = superio_inb(IT87_SIO_BEEP_PIN_REG) & 0x3f; } else if (sio_data->type == it8783) { - int reg25, reg27, reg2A, reg2C, regEF; + int reg25, reg27, reg2a, reg2c, regef; sio_data->skip_vid = 1; /* No VID */ @@ -1786,15 +1786,15 @@ static int __init it87_find(unsigned short *address, reg25 = superio_inb(IT87_SIO_GPIO1_REG); reg27 = superio_inb(IT87_SIO_GPIO3_REG); - reg2A = superio_inb(IT87_SIO_PINX1_REG); - reg2C = superio_inb(IT87_SIO_PINX2_REG); - regEF = superio_inb(IT87_SIO_SPI_REG); + reg2a = superio_inb(IT87_SIO_PINX1_REG); + reg2c = superio_inb(IT87_SIO_PINX2_REG); + regef = superio_inb(IT87_SIO_SPI_REG); /* Check if fan3 is there or not */ - if ((reg27 & (1 << 0)) || !(reg2C & (1 << 2))) + if ((reg27 & (1 << 0)) || !(reg2c & (1 << 2))) sio_data->skip_fan |= (1 << 2); if ((reg25 & (1 << 4)) - || (!(reg2A & (1 << 1)) && (regEF & (1 << 0)))) + || (!(reg2a & (1 << 1)) && (regef & (1 << 0)))) sio_data->skip_pwm |= (1 << 2); /* Check if fan2 is there or not */ @@ -1804,7 +1804,7 @@ static int __init it87_find(unsigned short *address, sio_data->skip_pwm |= (1 << 1); /* VIN5 */ - if ((reg27 & (1 << 0)) || (reg2C & (1 << 2))) + if ((reg27 & (1 << 0)) || (reg2c & (1 << 2))) sio_data->skip_in |= (1 << 5); /* No VIN5 */ /* VIN6 */ @@ -1829,18 +1829,18 @@ static int __init it87_find(unsigned short *address, * not the case, and ask the user to report if the * resulting voltage is sane. */ - if (!(reg2C & (1 << 1))) { - reg2C |= (1 << 1); - superio_outb(IT87_SIO_PINX2_REG, reg2C); + if (!(reg2c & (1 << 1))) { + reg2c |= (1 << 1); + superio_outb(IT87_SIO_PINX2_REG, reg2c); pr_notice("Routing internal VCCH5V to in7.\n"); } pr_notice("in7 routed to internal voltage divider, with external pin disabled.\n"); pr_notice("Please report if it displays a reasonable voltage.\n"); } - if (reg2C & (1 << 0)) + if (reg2c & (1 << 0)) sio_data->internal |= (1 << 0); - if (reg2C & (1 << 1)) + if (reg2c & (1 << 1)) sio_data->internal |= (1 << 1); sio_data->beep_pin = superio_inb(IT87_SIO_BEEP_PIN_REG) & 0x3f; diff --git a/drivers/hwmon/k8temp.c b/drivers/hwmon/k8temp.c index 9f3c0aeacdb9..5b50e9e4f96b 100644 --- a/drivers/hwmon/k8temp.c +++ b/drivers/hwmon/k8temp.c @@ -200,8 +200,8 @@ static int k8temp_probe(struct pci_dev *pdev, */ if (model >= 0x40) { data->swap_core_select = 1; - dev_warn(&pdev->dev, "Temperature readouts might be wrong - " - "check erratum #141\n"); + dev_warn(&pdev->dev, + "Temperature readouts might be wrong - check erratum #141\n"); } /* diff --git a/drivers/hwmon/lm78.c b/drivers/hwmon/lm78.c index 483538fa1bd5..6cf6bff79003 100644 --- a/drivers/hwmon/lm78.c +++ b/drivers/hwmon/lm78.c @@ -386,8 +386,9 @@ static ssize_t set_fan_div(struct device *dev, struct device_attribute *da, data->fan_div[nr] = 3; break; default: - dev_err(dev, "fan_div value %ld not " - "supported. Choose one of 1, 2, 4 or 8!\n", val); + dev_err(dev, + "fan_div value %ld not supported. Choose one of 1, 2, 4 or 8!\n", + val); mutex_unlock(&data->update_lock); return -EINVAL; } @@ -636,8 +637,9 @@ static int lm78_i2c_detect(struct i2c_client *client, goto err_nodev; if (lm78_alias_detect(client, i)) { - dev_dbg(&adapter->dev, "Device at 0x%02x appears to " - "be the same as ISA device\n", address); + dev_dbg(&adapter->dev, + "Device at 0x%02x appears to be the same as ISA device\n", + address); goto err_nodev; } diff --git a/drivers/hwmon/lm80.c b/drivers/hwmon/lm80.c index 357fbb998728..eba89aac3ece 100644 --- a/drivers/hwmon/lm80.c +++ b/drivers/hwmon/lm80.c @@ -286,8 +286,9 @@ static ssize_t set_fan_div(struct device *dev, struct device_attribute *attr, data->fan_div[nr] = 3; break; default: - dev_err(&client->dev, "fan_div value %ld not " - "supported. Choose one of 1, 2, 4 or 8!\n", val); + dev_err(&client->dev, + "fan_div value %ld not supported. Choose one of 1, 2, 4 or 8!\n", + val); mutex_unlock(&data->update_lock); return -EINVAL; } diff --git a/drivers/hwmon/lm85.c b/drivers/hwmon/lm85.c index 47ade8ba152d..3894c408fda3 100644 --- a/drivers/hwmon/lm85.c +++ b/drivers/hwmon/lm85.c @@ -1293,8 +1293,8 @@ static int lm85_detect(struct i2c_client *client, struct i2c_board_info *info) company = lm85_read_value(client, LM85_REG_COMPANY); verstep = lm85_read_value(client, LM85_REG_VERSTEP); - dev_dbg(&adapter->dev, "Detecting device at 0x%02x with " - "COMPANY: 0x%02x and VERSTEP: 0x%02x\n", + dev_dbg(&adapter->dev, + "Detecting device at 0x%02x with COMPANY: 0x%02x and VERSTEP: 0x%02x\n", address, company, verstep); /* All supported chips have the version in common */ diff --git a/drivers/hwmon/lm93.c b/drivers/hwmon/lm93.c index b40f34cdb3ca..a6f46058b1be 100644 --- a/drivers/hwmon/lm93.c +++ b/drivers/hwmon/lm93.c @@ -354,12 +354,12 @@ static const unsigned long lm93_vin_val_max[16] = { static unsigned LM93_IN_FROM_REG(int nr, u8 reg) { - const long uV_max = lm93_vin_val_max[nr] * 1000; - const long uV_min = lm93_vin_val_min[nr] * 1000; + const long uv_max = lm93_vin_val_max[nr] * 1000; + const long uv_min = lm93_vin_val_min[nr] * 1000; - const long slope = (uV_max - uV_min) / + const long slope = (uv_max - uv_min) / (lm93_vin_reg_max[nr] - lm93_vin_reg_min[nr]); - const long intercept = uV_min - slope * lm93_vin_reg_min[nr]; + const long intercept = uv_min - slope * lm93_vin_reg_min[nr]; return (slope * reg + intercept + 500) / 1000; } @@ -371,20 +371,20 @@ static unsigned LM93_IN_FROM_REG(int nr, u8 reg) static u8 LM93_IN_TO_REG(int nr, unsigned val) { /* range limit */ - const long mV = clamp_val(val, + const long mv = clamp_val(val, lm93_vin_val_min[nr], lm93_vin_val_max[nr]); /* try not to lose too much precision here */ - const long uV = mV * 1000; - const long uV_max = lm93_vin_val_max[nr] * 1000; - const long uV_min = lm93_vin_val_min[nr] * 1000; + const long uv = mv * 1000; + const long uv_max = lm93_vin_val_max[nr] * 1000; + const long uv_min = lm93_vin_val_min[nr] * 1000; /* convert */ - const long slope = (uV_max - uV_min) / + const long slope = (uv_max - uv_min) / (lm93_vin_reg_max[nr] - lm93_vin_reg_min[nr]); - const long intercept = uV_min - slope * lm93_vin_reg_min[nr]; + const long intercept = uv_min - slope * lm93_vin_reg_min[nr]; - u8 result = ((uV - intercept + (slope/2)) / slope); + u8 result = ((uv - intercept + (slope/2)) / slope); result = clamp_val(result, lm93_vin_reg_min[nr], lm93_vin_reg_max[nr]); return result; @@ -393,10 +393,10 @@ static u8 LM93_IN_TO_REG(int nr, unsigned val) /* vid in mV, upper == 0 indicates low limit, otherwise upper limit */ static unsigned LM93_IN_REL_FROM_REG(u8 reg, int upper, int vid) { - const long uV_offset = upper ? (((reg >> 4 & 0x0f) + 1) * 12500) : + const long uv_offset = upper ? (((reg >> 4 & 0x0f) + 1) * 12500) : (((reg >> 0 & 0x0f) + 1) * -25000); - const long uV_vid = vid * 1000; - return (uV_vid + uV_offset + 5000) / 10000; + const long uv_vid = vid * 1000; + return (uv_vid + uv_offset + 5000) / 10000; } #define LM93_IN_MIN_FROM_REG(reg, vid) LM93_IN_REL_FROM_REG((reg), 0, (vid)) @@ -409,13 +409,13 @@ static unsigned LM93_IN_REL_FROM_REG(u8 reg, int upper, int vid) */ static u8 LM93_IN_REL_TO_REG(unsigned val, int upper, int vid) { - long uV_offset = vid * 1000 - val * 10000; + long uv_offset = vid * 1000 - val * 10000; if (upper) { - uV_offset = clamp_val(uV_offset, 12500, 200000); - return (u8)((uV_offset / 12500 - 1) << 4); + uv_offset = clamp_val(uv_offset, 12500, 200000); + return (u8)((uv_offset / 12500 - 1) << 4); } else { - uV_offset = clamp_val(uV_offset, -400000, -25000); - return (u8)((uV_offset / -25000 - 1) << 0); + uv_offset = clamp_val(uv_offset, -400000, -25000); + return (u8)((uv_offset / -25000 - 1) << 0); } } @@ -818,8 +818,9 @@ static u8 lm93_read_byte(struct i2c_client *client, u8 reg) if (value >= 0) { return value; } else { - dev_warn(&client->dev, "lm93: read byte data failed, " - "address 0x%02x.\n", reg); + dev_warn(&client->dev, + "lm93: read byte data failed, address 0x%02x.\n", + reg); mdelay(i + 3); } @@ -838,8 +839,9 @@ static int lm93_write_byte(struct i2c_client *client, u8 reg, u8 value) result = i2c_smbus_write_byte_data(client, reg, value); if (result < 0) - dev_warn(&client->dev, "lm93: write byte data failed, " - "0x%02x at address 0x%02x.\n", value, reg); + dev_warn(&client->dev, + "lm93: write byte data failed, 0x%02x at address 0x%02x.\n", + value, reg); return result; } @@ -854,8 +856,9 @@ static u16 lm93_read_word(struct i2c_client *client, u8 reg) if (value >= 0) { return value; } else { - dev_warn(&client->dev, "lm93: read word data failed, " - "address 0x%02x.\n", reg); + dev_warn(&client->dev, + "lm93: read word data failed, address 0x%02x.\n", + reg); mdelay(i + 3); } @@ -874,8 +877,9 @@ static int lm93_write_word(struct i2c_client *client, u8 reg, u16 value) result = i2c_smbus_write_word_data(client, reg, value); if (result < 0) - dev_warn(&client->dev, "lm93: write word data failed, " - "0x%04x at address 0x%02x.\n", value, reg); + dev_warn(&client->dev, + "lm93: write word data failed, 0x%04x at address 0x%02x.\n", + value, reg); return result; } @@ -898,8 +902,8 @@ static void lm93_read_block(struct i2c_client *client, u8 fbn, u8 *values) if (result == lm93_block_read_cmds[fbn].len) { break; } else { - dev_warn(&client->dev, "lm93: block read data failed, " - "command 0x%02x.\n", + dev_warn(&client->dev, + "lm93: block read data failed, command 0x%02x.\n", lm93_block_read_cmds[fbn].cmd); mdelay(i + 3); } @@ -2672,8 +2676,8 @@ static void lm93_init_client(struct i2c_client *client) return; } - dev_warn(&client->dev, "timed out waiting for sensor " - "chip to signal ready!\n"); + dev_warn(&client->dev, + "timed out waiting for sensor chip to signal ready!\n"); } /* Return 0 if detection is successful, -ENODEV otherwise */ @@ -2733,12 +2737,12 @@ static int lm93_probe(struct i2c_client *client, dev_dbg(&client->dev, "using SMBus block data transactions\n"); update = lm93_update_client_full; } else if ((LM93_SMBUS_FUNC_MIN & func) == LM93_SMBUS_FUNC_MIN) { - dev_dbg(&client->dev, "disabled SMBus block data " - "transactions\n"); + dev_dbg(&client->dev, + "disabled SMBus block data transactions\n"); update = lm93_update_client_min; } else { - dev_dbg(&client->dev, "detect failed, " - "smbus byte and/or word data not supported!\n"); + dev_dbg(&client->dev, + "detect failed, smbus byte and/or word data not supported!\n"); return -ENODEV; } diff --git a/drivers/hwmon/lm95234.c b/drivers/hwmon/lm95234.c new file mode 100644 index 000000000000..307c9eaeeb9f --- /dev/null +++ b/drivers/hwmon/lm95234.c @@ -0,0 +1,769 @@ +/* + * Driver for Texas Instruments / National Semiconductor LM95234 + * + * Copyright (c) 2013 Guenter Roeck <linux@roeck-us.net> + * + * Derived from lm95241.c + * Copyright (C) 2008, 2010 Davide Rizzo <elpa.rizzo@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. + */ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/slab.h> +#include <linux/jiffies.h> +#include <linux/i2c.h> +#include <linux/hwmon.h> +#include <linux/hwmon-sysfs.h> +#include <linux/err.h> +#include <linux/mutex.h> +#include <linux/sysfs.h> + +#define DRVNAME "lm95234" + +static const unsigned short normal_i2c[] = { 0x18, 0x4d, 0x4e, I2C_CLIENT_END }; + +/* LM95234 registers */ +#define LM95234_REG_MAN_ID 0xFE +#define LM95234_REG_CHIP_ID 0xFF +#define LM95234_REG_STATUS 0x02 +#define LM95234_REG_CONFIG 0x03 +#define LM95234_REG_CONVRATE 0x04 +#define LM95234_REG_STS_FAULT 0x07 +#define LM95234_REG_STS_TCRIT1 0x08 +#define LM95234_REG_STS_TCRIT2 0x09 +#define LM95234_REG_TEMPH(x) ((x) + 0x10) +#define LM95234_REG_TEMPL(x) ((x) + 0x20) +#define LM95234_REG_UTEMPH(x) ((x) + 0x19) /* Remote only */ +#define LM95234_REG_UTEMPL(x) ((x) + 0x29) +#define LM95234_REG_REM_MODEL 0x30 +#define LM95234_REG_REM_MODEL_STS 0x38 +#define LM95234_REG_OFFSET(x) ((x) + 0x31) /* Remote only */ +#define LM95234_REG_TCRIT1(x) ((x) + 0x40) +#define LM95234_REG_TCRIT2(x) ((x) + 0x49) /* Remote channel 1,2 */ +#define LM95234_REG_TCRIT_HYST 0x5a + +#define NATSEMI_MAN_ID 0x01 +#define LM95234_CHIP_ID 0x79 + +/* Client data (each client gets its own) */ +struct lm95234_data { + struct device *hwmon_dev; + struct mutex update_lock; + unsigned long last_updated, interval; /* in jiffies */ + bool valid; /* false until following fields are valid */ + /* registers values */ + int temp[5]; /* temperature (signed) */ + u32 status; /* fault/alarm status */ + u8 tcrit1[5]; /* critical temperature limit */ + u8 tcrit2[2]; /* high temperature limit */ + s8 toffset[4]; /* remote temperature offset */ + u8 thyst; /* common hysteresis */ + + u8 sensor_type; /* temperature sensor type */ +}; + +static int lm95234_read_temp(struct i2c_client *client, int index, int *t) +{ + int val; + u16 temp = 0; + + if (index) { + val = i2c_smbus_read_byte_data(client, + LM95234_REG_UTEMPH(index - 1)); + if (val < 0) + return val; + temp = val << 8; + val = i2c_smbus_read_byte_data(client, + LM95234_REG_UTEMPL(index - 1)); + if (val < 0) + return val; + temp |= val; + *t = temp; + } + /* + * Read signed temperature if unsigned temperature is 0, + * or if this is the local sensor. + */ + if (!temp) { + val = i2c_smbus_read_byte_data(client, + LM95234_REG_TEMPH(index)); + if (val < 0) + return val; + temp = val << 8; + val = i2c_smbus_read_byte_data(client, + LM95234_REG_TEMPL(index)); + if (val < 0) + return val; + temp |= val; + *t = (s16)temp; + } + return 0; +} + +static u16 update_intervals[] = { 143, 364, 1000, 2500 }; + +/* Fill value cache. Must be called with update lock held. */ + +static int lm95234_fill_cache(struct i2c_client *client) +{ + struct lm95234_data *data = i2c_get_clientdata(client); + int i, ret; + + ret = i2c_smbus_read_byte_data(client, LM95234_REG_CONVRATE); + if (ret < 0) + return ret; + + data->interval = msecs_to_jiffies(update_intervals[ret & 0x03]); + + for (i = 0; i < ARRAY_SIZE(data->tcrit1); i++) { + ret = i2c_smbus_read_byte_data(client, LM95234_REG_TCRIT1(i)); + if (ret < 0) + return ret; + data->tcrit1[i] = ret; + } + for (i = 0; i < ARRAY_SIZE(data->tcrit2); i++) { + ret = i2c_smbus_read_byte_data(client, LM95234_REG_TCRIT2(i)); + if (ret < 0) + return ret; + data->tcrit2[i] = ret; + } + for (i = 0; i < ARRAY_SIZE(data->toffset); i++) { + ret = i2c_smbus_read_byte_data(client, LM95234_REG_OFFSET(i)); + if (ret < 0) + return ret; + data->toffset[i] = ret; + } + + ret = i2c_smbus_read_byte_data(client, LM95234_REG_TCRIT_HYST); + if (ret < 0) + return ret; + data->thyst = ret; + + ret = i2c_smbus_read_byte_data(client, LM95234_REG_REM_MODEL); + if (ret < 0) + return ret; + data->sensor_type = ret; + + return 0; +} + +static int lm95234_update_device(struct i2c_client *client, + struct lm95234_data *data) +{ + int ret; + + mutex_lock(&data->update_lock); + + if (time_after(jiffies, data->last_updated + data->interval) || + !data->valid) { + int i; + + if (!data->valid) { + ret = lm95234_fill_cache(client); + if (ret < 0) + goto abort; + } + + data->valid = false; + for (i = 0; i < ARRAY_SIZE(data->temp); i++) { + ret = lm95234_read_temp(client, i, &data->temp[i]); + if (ret < 0) + goto abort; + } + + ret = i2c_smbus_read_byte_data(client, LM95234_REG_STS_FAULT); + if (ret < 0) + goto abort; + data->status = ret; + + ret = i2c_smbus_read_byte_data(client, LM95234_REG_STS_TCRIT1); + if (ret < 0) + goto abort; + data->status |= ret << 8; + + ret = i2c_smbus_read_byte_data(client, LM95234_REG_STS_TCRIT2); + if (ret < 0) + goto abort; + data->status |= ret << 16; + + data->last_updated = jiffies; + data->valid = true; + } + ret = 0; +abort: + mutex_unlock(&data->update_lock); + + return ret; +} + +static ssize_t show_temp(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + struct lm95234_data *data = i2c_get_clientdata(client); + int index = to_sensor_dev_attr(attr)->index; + int ret = lm95234_update_device(client, data); + + if (ret) + return ret; + + return sprintf(buf, "%d\n", + DIV_ROUND_CLOSEST(data->temp[index] * 125, 32)); +} + +static ssize_t show_alarm(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + struct lm95234_data *data = i2c_get_clientdata(client); + u32 mask = to_sensor_dev_attr(attr)->index; + int ret = lm95234_update_device(client, data); + + if (ret) + return ret; + + return sprintf(buf, "%u", !!(data->status & mask)); +} + +static ssize_t show_type(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + struct lm95234_data *data = i2c_get_clientdata(client); + u8 mask = to_sensor_dev_attr(attr)->index; + int ret = lm95234_update_device(client, data); + + if (ret) + return ret; + + return sprintf(buf, data->sensor_type & mask ? "1\n" : "2\n"); +} + +static ssize_t set_type(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct i2c_client *client = to_i2c_client(dev); + struct lm95234_data *data = i2c_get_clientdata(client); + unsigned long val; + u8 mask = to_sensor_dev_attr(attr)->index; + int ret = lm95234_update_device(client, data); + + if (ret) + return ret; + + ret = kstrtoul(buf, 10, &val); + if (ret < 0) + return ret; + + if (val != 1 && val != 2) + return -EINVAL; + + mutex_lock(&data->update_lock); + if (val == 1) + data->sensor_type |= mask; + else + data->sensor_type &= ~mask; + data->valid = false; + i2c_smbus_write_byte_data(client, LM95234_REG_REM_MODEL, + data->sensor_type); + mutex_unlock(&data->update_lock); + + return count; +} + +static ssize_t show_tcrit2(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + struct lm95234_data *data = i2c_get_clientdata(client); + int index = to_sensor_dev_attr(attr)->index; + int ret = lm95234_update_device(client, data); + + if (ret) + return ret; + + return sprintf(buf, "%u", data->tcrit2[index] * 1000); +} + +static ssize_t set_tcrit2(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct i2c_client *client = to_i2c_client(dev); + struct lm95234_data *data = i2c_get_clientdata(client); + int index = to_sensor_dev_attr(attr)->index; + long val; + int ret = lm95234_update_device(client, data); + + if (ret) + return ret; + + ret = kstrtol(buf, 10, &val); + if (ret < 0) + return ret; + + val = clamp_val(DIV_ROUND_CLOSEST(val, 1000), 0, index ? 255 : 127); + + mutex_lock(&data->update_lock); + data->tcrit2[index] = val; + i2c_smbus_write_byte_data(client, LM95234_REG_TCRIT2(index), val); + mutex_unlock(&data->update_lock); + + return count; +} + +static ssize_t show_tcrit2_hyst(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + struct lm95234_data *data = i2c_get_clientdata(client); + int index = to_sensor_dev_attr(attr)->index; + int ret = lm95234_update_device(client, data); + + if (ret) + return ret; + + /* Result can be negative, so be careful with unsigned operands */ + return sprintf(buf, "%d", + ((int)data->tcrit2[index] - (int)data->thyst) * 1000); +} + +static ssize_t show_tcrit1(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + struct lm95234_data *data = i2c_get_clientdata(client); + int index = to_sensor_dev_attr(attr)->index; + + return sprintf(buf, "%u", data->tcrit1[index] * 1000); +} + +static ssize_t set_tcrit1(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct i2c_client *client = to_i2c_client(dev); + struct lm95234_data *data = i2c_get_clientdata(client); + int index = to_sensor_dev_attr(attr)->index; + long val; + int ret = lm95234_update_device(client, data); + + if (ret) + return ret; + + ret = kstrtol(buf, 10, &val); + if (ret < 0) + return ret; + + val = clamp_val(DIV_ROUND_CLOSEST(val, 1000), 0, 255); + + mutex_lock(&data->update_lock); + data->tcrit1[index] = val; + i2c_smbus_write_byte_data(client, LM95234_REG_TCRIT1(index), val); + mutex_unlock(&data->update_lock); + + return count; +} + +static ssize_t show_tcrit1_hyst(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + struct lm95234_data *data = i2c_get_clientdata(client); + int index = to_sensor_dev_attr(attr)->index; + int ret = lm95234_update_device(client, data); + + if (ret) + return ret; + + /* Result can be negative, so be careful with unsigned operands */ + return sprintf(buf, "%d", + ((int)data->tcrit1[index] - (int)data->thyst) * 1000); +} + +static ssize_t set_tcrit1_hyst(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct i2c_client *client = to_i2c_client(dev); + struct lm95234_data *data = i2c_get_clientdata(client); + int index = to_sensor_dev_attr(attr)->index; + long val; + int ret = lm95234_update_device(client, data); + + if (ret) + return ret; + + ret = kstrtol(buf, 10, &val); + if (ret < 0) + return ret; + + val = DIV_ROUND_CLOSEST(val, 1000); + val = clamp_val((int)data->tcrit1[index] - val, 0, 31); + + mutex_lock(&data->update_lock); + data->thyst = val; + i2c_smbus_write_byte_data(client, LM95234_REG_TCRIT_HYST, val); + mutex_unlock(&data->update_lock); + + return count; +} + +static ssize_t show_offset(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + struct lm95234_data *data = i2c_get_clientdata(client); + int index = to_sensor_dev_attr(attr)->index; + int ret = lm95234_update_device(client, data); + + if (ret) + return ret; + + return sprintf(buf, "%d", data->toffset[index] * 500); +} + +static ssize_t set_offset(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct i2c_client *client = to_i2c_client(dev); + struct lm95234_data *data = i2c_get_clientdata(client); + int index = to_sensor_dev_attr(attr)->index; + long val; + int ret = lm95234_update_device(client, data); + + if (ret) + return ret; + + ret = kstrtol(buf, 10, &val); + if (ret < 0) + return ret; + + /* Accuracy is 1/2 degrees C */ + val = clamp_val(DIV_ROUND_CLOSEST(val, 500), -128, 127); + + mutex_lock(&data->update_lock); + data->toffset[index] = val; + i2c_smbus_write_byte_data(client, LM95234_REG_OFFSET(index), val); + mutex_unlock(&data->update_lock); + + return count; +} + +static ssize_t show_interval(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + struct lm95234_data *data = i2c_get_clientdata(client); + int ret = lm95234_update_device(client, data); + + if (ret) + return ret; + + return sprintf(buf, "%lu\n", + DIV_ROUND_CLOSEST(data->interval * 1000, HZ)); +} + +static ssize_t set_interval(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct i2c_client *client = to_i2c_client(dev); + struct lm95234_data *data = i2c_get_clientdata(client); + unsigned long val; + u8 regval; + int ret = lm95234_update_device(client, data); + + if (ret) + return ret; + + ret = kstrtoul(buf, 10, &val); + if (ret < 0) + return ret; + + for (regval = 0; regval < 3; regval++) { + if (val <= update_intervals[regval]) + break; + } + + mutex_lock(&data->update_lock); + data->interval = msecs_to_jiffies(update_intervals[regval]); + i2c_smbus_write_byte_data(client, LM95234_REG_CONVRATE, regval); + mutex_unlock(&data->update_lock); + + return count; +} + +static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, show_temp, NULL, 0); +static SENSOR_DEVICE_ATTR(temp2_input, S_IRUGO, show_temp, NULL, 1); +static SENSOR_DEVICE_ATTR(temp3_input, S_IRUGO, show_temp, NULL, 2); +static SENSOR_DEVICE_ATTR(temp4_input, S_IRUGO, show_temp, NULL, 3); +static SENSOR_DEVICE_ATTR(temp5_input, S_IRUGO, show_temp, NULL, 4); + +static SENSOR_DEVICE_ATTR(temp2_fault, S_IRUGO, show_alarm, NULL, + BIT(0) | BIT(1)); +static SENSOR_DEVICE_ATTR(temp3_fault, S_IRUGO, show_alarm, NULL, + BIT(2) | BIT(3)); +static SENSOR_DEVICE_ATTR(temp4_fault, S_IRUGO, show_alarm, NULL, + BIT(4) | BIT(5)); +static SENSOR_DEVICE_ATTR(temp5_fault, S_IRUGO, show_alarm, NULL, + BIT(6) | BIT(7)); + +static SENSOR_DEVICE_ATTR(temp2_type, S_IWUSR | S_IRUGO, show_type, set_type, + BIT(1)); +static SENSOR_DEVICE_ATTR(temp3_type, S_IWUSR | S_IRUGO, show_type, set_type, + BIT(2)); +static SENSOR_DEVICE_ATTR(temp4_type, S_IWUSR | S_IRUGO, show_type, set_type, + BIT(3)); +static SENSOR_DEVICE_ATTR(temp5_type, S_IWUSR | S_IRUGO, show_type, set_type, + BIT(4)); + +static SENSOR_DEVICE_ATTR(temp1_max, S_IWUSR | S_IRUGO, show_tcrit1, + set_tcrit1, 0); +static SENSOR_DEVICE_ATTR(temp2_max, S_IWUSR | S_IRUGO, show_tcrit2, + set_tcrit2, 0); +static SENSOR_DEVICE_ATTR(temp3_max, S_IWUSR | S_IRUGO, show_tcrit2, + set_tcrit2, 1); +static SENSOR_DEVICE_ATTR(temp4_max, S_IWUSR | S_IRUGO, show_tcrit1, + set_tcrit1, 3); +static SENSOR_DEVICE_ATTR(temp5_max, S_IWUSR | S_IRUGO, show_tcrit1, + set_tcrit1, 4); + +static SENSOR_DEVICE_ATTR(temp1_max_hyst, S_IWUSR | S_IRUGO, show_tcrit1_hyst, + set_tcrit1_hyst, 0); +static SENSOR_DEVICE_ATTR(temp2_max_hyst, S_IRUGO, show_tcrit2_hyst, NULL, 0); +static SENSOR_DEVICE_ATTR(temp3_max_hyst, S_IRUGO, show_tcrit2_hyst, NULL, 1); +static SENSOR_DEVICE_ATTR(temp4_max_hyst, S_IRUGO, show_tcrit1_hyst, NULL, 3); +static SENSOR_DEVICE_ATTR(temp5_max_hyst, S_IRUGO, show_tcrit1_hyst, NULL, 4); + +static SENSOR_DEVICE_ATTR(temp1_max_alarm, S_IRUGO, show_alarm, NULL, + BIT(0 + 8)); +static SENSOR_DEVICE_ATTR(temp2_max_alarm, S_IRUGO, show_alarm, NULL, + BIT(1 + 16)); +static SENSOR_DEVICE_ATTR(temp3_max_alarm, S_IRUGO, show_alarm, NULL, + BIT(2 + 16)); +static SENSOR_DEVICE_ATTR(temp4_max_alarm, S_IRUGO, show_alarm, NULL, + BIT(3 + 8)); +static SENSOR_DEVICE_ATTR(temp5_max_alarm, S_IRUGO, show_alarm, NULL, + BIT(4 + 8)); + +static SENSOR_DEVICE_ATTR(temp2_crit, S_IWUSR | S_IRUGO, show_tcrit1, + set_tcrit1, 1); +static SENSOR_DEVICE_ATTR(temp3_crit, S_IWUSR | S_IRUGO, show_tcrit1, + set_tcrit1, 2); + +static SENSOR_DEVICE_ATTR(temp2_crit_hyst, S_IRUGO, show_tcrit1_hyst, NULL, 1); +static SENSOR_DEVICE_ATTR(temp3_crit_hyst, S_IRUGO, show_tcrit1_hyst, NULL, 2); + +static SENSOR_DEVICE_ATTR(temp2_crit_alarm, S_IRUGO, show_alarm, NULL, + BIT(1 + 8)); +static SENSOR_DEVICE_ATTR(temp3_crit_alarm, S_IRUGO, show_alarm, NULL, + BIT(2 + 8)); + +static SENSOR_DEVICE_ATTR(temp2_offset, S_IWUSR | S_IRUGO, show_offset, + set_offset, 0); +static SENSOR_DEVICE_ATTR(temp3_offset, S_IWUSR | S_IRUGO, show_offset, + set_offset, 1); +static SENSOR_DEVICE_ATTR(temp4_offset, S_IWUSR | S_IRUGO, show_offset, + set_offset, 2); +static SENSOR_DEVICE_ATTR(temp5_offset, S_IWUSR | S_IRUGO, show_offset, + set_offset, 3); + +static DEVICE_ATTR(update_interval, S_IWUSR | S_IRUGO, show_interval, + set_interval); + +static struct attribute *lm95234_attributes[] = { + &sensor_dev_attr_temp1_input.dev_attr.attr, + &sensor_dev_attr_temp2_input.dev_attr.attr, + &sensor_dev_attr_temp3_input.dev_attr.attr, + &sensor_dev_attr_temp4_input.dev_attr.attr, + &sensor_dev_attr_temp5_input.dev_attr.attr, + &sensor_dev_attr_temp2_fault.dev_attr.attr, + &sensor_dev_attr_temp3_fault.dev_attr.attr, + &sensor_dev_attr_temp4_fault.dev_attr.attr, + &sensor_dev_attr_temp5_fault.dev_attr.attr, + &sensor_dev_attr_temp2_type.dev_attr.attr, + &sensor_dev_attr_temp3_type.dev_attr.attr, + &sensor_dev_attr_temp4_type.dev_attr.attr, + &sensor_dev_attr_temp5_type.dev_attr.attr, + &sensor_dev_attr_temp1_max.dev_attr.attr, + &sensor_dev_attr_temp2_max.dev_attr.attr, + &sensor_dev_attr_temp3_max.dev_attr.attr, + &sensor_dev_attr_temp4_max.dev_attr.attr, + &sensor_dev_attr_temp5_max.dev_attr.attr, + &sensor_dev_attr_temp1_max_hyst.dev_attr.attr, + &sensor_dev_attr_temp2_max_hyst.dev_attr.attr, + &sensor_dev_attr_temp3_max_hyst.dev_attr.attr, + &sensor_dev_attr_temp4_max_hyst.dev_attr.attr, + &sensor_dev_attr_temp5_max_hyst.dev_attr.attr, + &sensor_dev_attr_temp1_max_alarm.dev_attr.attr, + &sensor_dev_attr_temp2_max_alarm.dev_attr.attr, + &sensor_dev_attr_temp3_max_alarm.dev_attr.attr, + &sensor_dev_attr_temp4_max_alarm.dev_attr.attr, + &sensor_dev_attr_temp5_max_alarm.dev_attr.attr, + &sensor_dev_attr_temp2_crit.dev_attr.attr, + &sensor_dev_attr_temp3_crit.dev_attr.attr, + &sensor_dev_attr_temp2_crit_hyst.dev_attr.attr, + &sensor_dev_attr_temp3_crit_hyst.dev_attr.attr, + &sensor_dev_attr_temp2_crit_alarm.dev_attr.attr, + &sensor_dev_attr_temp3_crit_alarm.dev_attr.attr, + &sensor_dev_attr_temp2_offset.dev_attr.attr, + &sensor_dev_attr_temp3_offset.dev_attr.attr, + &sensor_dev_attr_temp4_offset.dev_attr.attr, + &sensor_dev_attr_temp5_offset.dev_attr.attr, + &dev_attr_update_interval.attr, + NULL +}; + +static const struct attribute_group lm95234_group = { + .attrs = lm95234_attributes, +}; + +static int lm95234_detect(struct i2c_client *client, + struct i2c_board_info *info) +{ + struct i2c_adapter *adapter = client->adapter; + int mfg_id, chip_id, val; + + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) + return -ENODEV; + + mfg_id = i2c_smbus_read_byte_data(client, LM95234_REG_MAN_ID); + if (mfg_id != NATSEMI_MAN_ID) + return -ENODEV; + + chip_id = i2c_smbus_read_byte_data(client, LM95234_REG_CHIP_ID); + if (chip_id != LM95234_CHIP_ID) + return -ENODEV; + + val = i2c_smbus_read_byte_data(client, LM95234_REG_STATUS); + if (val & 0x30) + return -ENODEV; + + val = i2c_smbus_read_byte_data(client, LM95234_REG_CONFIG); + if (val & 0xbc) + return -ENODEV; + + val = i2c_smbus_read_byte_data(client, LM95234_REG_CONVRATE); + if (val & 0xfc) + return -ENODEV; + + val = i2c_smbus_read_byte_data(client, LM95234_REG_REM_MODEL); + if (val & 0xe1) + return -ENODEV; + + val = i2c_smbus_read_byte_data(client, LM95234_REG_REM_MODEL_STS); + if (val & 0xe1) + return -ENODEV; + + strlcpy(info->type, "lm95234", I2C_NAME_SIZE); + return 0; +} + +static int lm95234_init_client(struct i2c_client *client) +{ + int val, model; + + /* start conversion if necessary */ + val = i2c_smbus_read_byte_data(client, LM95234_REG_CONFIG); + if (val < 0) + return val; + if (val & 0x40) + i2c_smbus_write_byte_data(client, LM95234_REG_CONFIG, + val & ~0x40); + + /* If diode type status reports an error, try to fix it */ + val = i2c_smbus_read_byte_data(client, LM95234_REG_REM_MODEL_STS); + if (val < 0) + return val; + model = i2c_smbus_read_byte_data(client, LM95234_REG_REM_MODEL); + if (model < 0) + return model; + if (model & val) { + dev_notice(&client->dev, + "Fixing remote diode type misconfiguration (0x%x)\n", + val); + i2c_smbus_write_byte_data(client, LM95234_REG_REM_MODEL, + model & ~val); + } + return 0; +} + +static int lm95234_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct device *dev = &client->dev; + struct lm95234_data *data; + int err; + + data = devm_kzalloc(dev, sizeof(struct lm95234_data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + i2c_set_clientdata(client, data); + mutex_init(&data->update_lock); + + /* Initialize the LM95234 chip */ + err = lm95234_init_client(client); + if (err < 0) + return err; + + /* Register sysfs hooks */ + err = sysfs_create_group(&dev->kobj, &lm95234_group); + if (err) + return err; + + data->hwmon_dev = hwmon_device_register(dev); + if (IS_ERR(data->hwmon_dev)) { + err = PTR_ERR(data->hwmon_dev); + goto exit_remove_files; + } + + return 0; + +exit_remove_files: + sysfs_remove_group(&dev->kobj, &lm95234_group); + return err; +} + +static int lm95234_remove(struct i2c_client *client) +{ + struct lm95234_data *data = i2c_get_clientdata(client); + + hwmon_device_unregister(data->hwmon_dev); + sysfs_remove_group(&client->dev.kobj, &lm95234_group); + + return 0; +} + +/* Driver data (common to all clients) */ +static const struct i2c_device_id lm95234_id[] = { + { "lm95234", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, lm95234_id); + +static struct i2c_driver lm95234_driver = { + .class = I2C_CLASS_HWMON, + .driver = { + .name = DRVNAME, + }, + .probe = lm95234_probe, + .remove = lm95234_remove, + .id_table = lm95234_id, + .detect = lm95234_detect, + .address_list = normal_i2c, +}; + +module_i2c_driver(lm95234_driver); + +MODULE_AUTHOR("Guenter Roeck <linux@roeck-us.net>"); +MODULE_DESCRIPTION("LM95234 sensor driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/hwmon/ltc4151.c b/drivers/hwmon/ltc4151.c index 4319a94f549d..af81be1237c9 100644 --- a/drivers/hwmon/ltc4151.c +++ b/drivers/hwmon/ltc4151.c @@ -146,14 +146,14 @@ static ssize_t ltc4151_show_value(struct device *dev, /* * Input voltages. */ -static SENSOR_DEVICE_ATTR(in1_input, S_IRUGO, \ - ltc4151_show_value, NULL, LTC4151_VIN_H); -static SENSOR_DEVICE_ATTR(in2_input, S_IRUGO, \ - ltc4151_show_value, NULL, LTC4151_ADIN_H); +static SENSOR_DEVICE_ATTR(in1_input, S_IRUGO, ltc4151_show_value, NULL, + LTC4151_VIN_H); +static SENSOR_DEVICE_ATTR(in2_input, S_IRUGO, ltc4151_show_value, NULL, + LTC4151_ADIN_H); /* Currents (via sense resistor) */ -static SENSOR_DEVICE_ATTR(curr1_input, S_IRUGO, \ - ltc4151_show_value, NULL, LTC4151_SENSE_H); +static SENSOR_DEVICE_ATTR(curr1_input, S_IRUGO, ltc4151_show_value, NULL, + LTC4151_SENSE_H); /* * Finally, construct an array of pointers to members of the above objects, diff --git a/drivers/hwmon/ltc4215.c b/drivers/hwmon/ltc4215.c index e8876108a6b3..8a142960d69e 100644 --- a/drivers/hwmon/ltc4215.c +++ b/drivers/hwmon/ltc4215.c @@ -172,12 +172,12 @@ static ssize_t ltc4215_show_alarm(struct device *dev, struct device_attribute *da, char *buf) { - struct sensor_device_attribute_2 *attr = to_sensor_dev_attr_2(da); + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); struct ltc4215_data *data = ltc4215_update_device(dev); - const u8 reg = data->regs[attr->index]; - const u32 mask = attr->nr; + const u8 reg = data->regs[LTC4215_STATUS]; + const u32 mask = attr->index; - return snprintf(buf, PAGE_SIZE, "%u\n", (reg & mask) ? 1 : 0); + return snprintf(buf, PAGE_SIZE, "%u\n", !!(reg & mask)); } /* @@ -186,39 +186,29 @@ static ssize_t ltc4215_show_alarm(struct device *dev, * for each register. */ -#define LTC4215_VOLTAGE(name, ltc4215_cmd_idx) \ - static SENSOR_DEVICE_ATTR(name, S_IRUGO, \ - ltc4215_show_voltage, NULL, ltc4215_cmd_idx) - -#define LTC4215_CURRENT(name) \ - static SENSOR_DEVICE_ATTR(name, S_IRUGO, \ - ltc4215_show_current, NULL, 0); - -#define LTC4215_POWER(name) \ - static SENSOR_DEVICE_ATTR(name, S_IRUGO, \ - ltc4215_show_power, NULL, 0); - -#define LTC4215_ALARM(name, mask, reg) \ - static SENSOR_DEVICE_ATTR_2(name, S_IRUGO, \ - ltc4215_show_alarm, NULL, (mask), reg) - /* Construct a sensor_device_attribute structure for each register */ /* Current */ -LTC4215_CURRENT(curr1_input); -LTC4215_ALARM(curr1_max_alarm, (1 << 2), LTC4215_STATUS); +static SENSOR_DEVICE_ATTR(curr1_input, S_IRUGO, ltc4215_show_current, NULL, 0); +static SENSOR_DEVICE_ATTR(curr1_max_alarm, S_IRUGO, ltc4215_show_alarm, NULL, + 1 << 2); /* Power (virtual) */ -LTC4215_POWER(power1_input); +static SENSOR_DEVICE_ATTR(power1_input, S_IRUGO, ltc4215_show_power, NULL, 0); /* Input Voltage */ -LTC4215_VOLTAGE(in1_input, LTC4215_ADIN); -LTC4215_ALARM(in1_max_alarm, (1 << 0), LTC4215_STATUS); -LTC4215_ALARM(in1_min_alarm, (1 << 1), LTC4215_STATUS); +static SENSOR_DEVICE_ATTR(in1_input, S_IRUGO, ltc4215_show_voltage, NULL, + LTC4215_ADIN); +static SENSOR_DEVICE_ATTR(in1_max_alarm, S_IRUGO, ltc4215_show_alarm, NULL, + 1 << 0); +static SENSOR_DEVICE_ATTR(in1_min_alarm, S_IRUGO, ltc4215_show_alarm, NULL, + 1 << 1); /* Output Voltage */ -LTC4215_VOLTAGE(in2_input, LTC4215_SOURCE); -LTC4215_ALARM(in2_min_alarm, (1 << 3), LTC4215_STATUS); +static SENSOR_DEVICE_ATTR(in2_input, S_IRUGO, ltc4215_show_voltage, NULL, + LTC4215_SOURCE); +static SENSOR_DEVICE_ATTR(in2_min_alarm, S_IRUGO, ltc4215_show_alarm, NULL, + 1 << 3); /* * Finally, construct an array of pointers to members of the above objects, diff --git a/drivers/hwmon/ltc4245.c b/drivers/hwmon/ltc4245.c index 3653f79dc2de..cdc1ecc6734d 100644 --- a/drivers/hwmon/ltc4245.c +++ b/drivers/hwmon/ltc4245.c @@ -319,80 +319,82 @@ static ssize_t ltc4245_show_gpio(struct device *dev, return snprintf(buf, PAGE_SIZE, "%u\n", val * 10); } -/* - * These macros are used below in constructing device attribute objects - * for use with sysfs_create_group() to make a sysfs device file - * for each register. - */ - -#define LTC4245_VOLTAGE(name, ltc4245_cmd_idx) \ - static SENSOR_DEVICE_ATTR(name, S_IRUGO, \ - ltc4245_show_voltage, NULL, ltc4245_cmd_idx) - -#define LTC4245_CURRENT(name, ltc4245_cmd_idx) \ - static SENSOR_DEVICE_ATTR(name, S_IRUGO, \ - ltc4245_show_current, NULL, ltc4245_cmd_idx) - -#define LTC4245_POWER(name, ltc4245_cmd_idx) \ - static SENSOR_DEVICE_ATTR(name, S_IRUGO, \ - ltc4245_show_power, NULL, ltc4245_cmd_idx) - -#define LTC4245_ALARM(name, mask, reg) \ - static SENSOR_DEVICE_ATTR_2(name, S_IRUGO, \ - ltc4245_show_alarm, NULL, (mask), reg) - -#define LTC4245_GPIO_VOLTAGE(name, gpio_num) \ - static SENSOR_DEVICE_ATTR(name, S_IRUGO, \ - ltc4245_show_gpio, NULL, gpio_num) - /* Construct a sensor_device_attribute structure for each register */ /* Input voltages */ -LTC4245_VOLTAGE(in1_input, LTC4245_12VIN); -LTC4245_VOLTAGE(in2_input, LTC4245_5VIN); -LTC4245_VOLTAGE(in3_input, LTC4245_3VIN); -LTC4245_VOLTAGE(in4_input, LTC4245_VEEIN); +static SENSOR_DEVICE_ATTR(in1_input, S_IRUGO, ltc4245_show_voltage, NULL, + LTC4245_12VIN); +static SENSOR_DEVICE_ATTR(in2_input, S_IRUGO, ltc4245_show_voltage, NULL, + LTC4245_5VIN); +static SENSOR_DEVICE_ATTR(in3_input, S_IRUGO, ltc4245_show_voltage, NULL, + LTC4245_3VIN); +static SENSOR_DEVICE_ATTR(in4_input, S_IRUGO, ltc4245_show_voltage, NULL, + LTC4245_VEEIN); /* Input undervoltage alarms */ -LTC4245_ALARM(in1_min_alarm, (1 << 0), LTC4245_FAULT1); -LTC4245_ALARM(in2_min_alarm, (1 << 1), LTC4245_FAULT1); -LTC4245_ALARM(in3_min_alarm, (1 << 2), LTC4245_FAULT1); -LTC4245_ALARM(in4_min_alarm, (1 << 3), LTC4245_FAULT1); +static SENSOR_DEVICE_ATTR_2(in1_min_alarm, S_IRUGO, ltc4245_show_alarm, NULL, + 1 << 0, LTC4245_FAULT1); +static SENSOR_DEVICE_ATTR_2(in2_min_alarm, S_IRUGO, ltc4245_show_alarm, NULL, + 1 << 1, LTC4245_FAULT1); +static SENSOR_DEVICE_ATTR_2(in3_min_alarm, S_IRUGO, ltc4245_show_alarm, NULL, + 1 << 2, LTC4245_FAULT1); +static SENSOR_DEVICE_ATTR_2(in4_min_alarm, S_IRUGO, ltc4245_show_alarm, NULL, + 1 << 3, LTC4245_FAULT1); /* Currents (via sense resistor) */ -LTC4245_CURRENT(curr1_input, LTC4245_12VSENSE); -LTC4245_CURRENT(curr2_input, LTC4245_5VSENSE); -LTC4245_CURRENT(curr3_input, LTC4245_3VSENSE); -LTC4245_CURRENT(curr4_input, LTC4245_VEESENSE); +static SENSOR_DEVICE_ATTR(curr1_input, S_IRUGO, ltc4245_show_current, NULL, + LTC4245_12VSENSE); +static SENSOR_DEVICE_ATTR(curr2_input, S_IRUGO, ltc4245_show_current, NULL, + LTC4245_5VSENSE); +static SENSOR_DEVICE_ATTR(curr3_input, S_IRUGO, ltc4245_show_current, NULL, + LTC4245_3VSENSE); +static SENSOR_DEVICE_ATTR(curr4_input, S_IRUGO, ltc4245_show_current, NULL, + LTC4245_VEESENSE); /* Overcurrent alarms */ -LTC4245_ALARM(curr1_max_alarm, (1 << 4), LTC4245_FAULT1); -LTC4245_ALARM(curr2_max_alarm, (1 << 5), LTC4245_FAULT1); -LTC4245_ALARM(curr3_max_alarm, (1 << 6), LTC4245_FAULT1); -LTC4245_ALARM(curr4_max_alarm, (1 << 7), LTC4245_FAULT1); +static SENSOR_DEVICE_ATTR_2(curr1_max_alarm, S_IRUGO, ltc4245_show_alarm, NULL, + 1 << 4, LTC4245_FAULT1); +static SENSOR_DEVICE_ATTR_2(curr2_max_alarm, S_IRUGO, ltc4245_show_alarm, NULL, + 1 << 5, LTC4245_FAULT1); +static SENSOR_DEVICE_ATTR_2(curr3_max_alarm, S_IRUGO, ltc4245_show_alarm, NULL, + 1 << 6, LTC4245_FAULT1); +static SENSOR_DEVICE_ATTR_2(curr4_max_alarm, S_IRUGO, ltc4245_show_alarm, NULL, + 1 << 7, LTC4245_FAULT1); /* Output voltages */ -LTC4245_VOLTAGE(in5_input, LTC4245_12VOUT); -LTC4245_VOLTAGE(in6_input, LTC4245_5VOUT); -LTC4245_VOLTAGE(in7_input, LTC4245_3VOUT); -LTC4245_VOLTAGE(in8_input, LTC4245_VEEOUT); +static SENSOR_DEVICE_ATTR(in5_input, S_IRUGO, ltc4245_show_voltage, NULL, + LTC4245_12VOUT); +static SENSOR_DEVICE_ATTR(in6_input, S_IRUGO, ltc4245_show_voltage, NULL, + LTC4245_5VOUT); +static SENSOR_DEVICE_ATTR(in7_input, S_IRUGO, ltc4245_show_voltage, NULL, + LTC4245_3VOUT); +static SENSOR_DEVICE_ATTR(in8_input, S_IRUGO, ltc4245_show_voltage, NULL, + LTC4245_VEEOUT); /* Power Bad alarms */ -LTC4245_ALARM(in5_min_alarm, (1 << 0), LTC4245_FAULT2); -LTC4245_ALARM(in6_min_alarm, (1 << 1), LTC4245_FAULT2); -LTC4245_ALARM(in7_min_alarm, (1 << 2), LTC4245_FAULT2); -LTC4245_ALARM(in8_min_alarm, (1 << 3), LTC4245_FAULT2); +static SENSOR_DEVICE_ATTR_2(in5_min_alarm, S_IRUGO, ltc4245_show_alarm, NULL, + 1 << 0, LTC4245_FAULT2); +static SENSOR_DEVICE_ATTR_2(in6_min_alarm, S_IRUGO, ltc4245_show_alarm, NULL, + 1 << 1, LTC4245_FAULT2); +static SENSOR_DEVICE_ATTR_2(in7_min_alarm, S_IRUGO, ltc4245_show_alarm, NULL, + 1 << 2, LTC4245_FAULT2); +static SENSOR_DEVICE_ATTR_2(in8_min_alarm, S_IRUGO, ltc4245_show_alarm, NULL, + 1 << 3, LTC4245_FAULT2); /* GPIO voltages */ -LTC4245_GPIO_VOLTAGE(in9_input, 0); -LTC4245_GPIO_VOLTAGE(in10_input, 1); -LTC4245_GPIO_VOLTAGE(in11_input, 2); +static SENSOR_DEVICE_ATTR(in9_input, S_IRUGO, ltc4245_show_gpio, NULL, 0); +static SENSOR_DEVICE_ATTR(in10_input, S_IRUGO, ltc4245_show_gpio, NULL, 1); +static SENSOR_DEVICE_ATTR(in11_input, S_IRUGO, ltc4245_show_gpio, NULL, 2); /* Power Consumption (virtual) */ -LTC4245_POWER(power1_input, LTC4245_12VSENSE); -LTC4245_POWER(power2_input, LTC4245_5VSENSE); -LTC4245_POWER(power3_input, LTC4245_3VSENSE); -LTC4245_POWER(power4_input, LTC4245_VEESENSE); +static SENSOR_DEVICE_ATTR(power1_input, S_IRUGO, ltc4245_show_power, NULL, + LTC4245_12VSENSE); +static SENSOR_DEVICE_ATTR(power2_input, S_IRUGO, ltc4245_show_power, NULL, + LTC4245_5VSENSE); +static SENSOR_DEVICE_ATTR(power3_input, S_IRUGO, ltc4245_show_power, NULL, + LTC4245_3VSENSE); +static SENSOR_DEVICE_ATTR(power4_input, S_IRUGO, ltc4245_show_power, NULL, + LTC4245_VEESENSE); /* * Finally, construct an array of pointers to members of the above objects, diff --git a/drivers/hwmon/ltc4261.c b/drivers/hwmon/ltc4261.c index 84a2d2872b20..487da58ec86c 100644 --- a/drivers/hwmon/ltc4261.c +++ b/drivers/hwmon/ltc4261.c @@ -165,24 +165,12 @@ static ssize_t ltc4261_show_bool(struct device *dev, } /* - * These macros are used below in constructing device attribute objects - * for use with sysfs_create_group() to make a sysfs device file - * for each register. - */ - -#define LTC4261_VALUE(name, ltc4261_cmd_idx) \ - static SENSOR_DEVICE_ATTR(name, S_IRUGO, \ - ltc4261_show_value, NULL, ltc4261_cmd_idx) - -#define LTC4261_BOOL(name, mask) \ - static SENSOR_DEVICE_ATTR(name, S_IRUGO, \ - ltc4261_show_bool, NULL, (mask)) - -/* * Input voltages. */ -LTC4261_VALUE(in1_input, LTC4261_ADIN_H); -LTC4261_VALUE(in2_input, LTC4261_ADIN2_H); +static SENSOR_DEVICE_ATTR(in1_input, S_IRUGO, ltc4261_show_value, NULL, + LTC4261_ADIN_H); +static SENSOR_DEVICE_ATTR(in2_input, S_IRUGO, ltc4261_show_value, NULL, + LTC4261_ADIN2_H); /* * Voltage alarms. The chip has only one set of voltage alarm status bits, @@ -192,16 +180,22 @@ LTC4261_VALUE(in2_input, LTC4261_ADIN2_H); * To ensure that the alarm condition is reported to the user, report it * with both voltage sensors. */ -LTC4261_BOOL(in1_min_alarm, FAULT_UV); -LTC4261_BOOL(in1_max_alarm, FAULT_OV); -LTC4261_BOOL(in2_min_alarm, FAULT_UV); -LTC4261_BOOL(in2_max_alarm, FAULT_OV); +static SENSOR_DEVICE_ATTR(in1_min_alarm, S_IRUGO, ltc4261_show_bool, NULL, + FAULT_UV); +static SENSOR_DEVICE_ATTR(in1_max_alarm, S_IRUGO, ltc4261_show_bool, NULL, + FAULT_OV); +static SENSOR_DEVICE_ATTR(in2_min_alarm, S_IRUGO, ltc4261_show_bool, NULL, + FAULT_UV); +static SENSOR_DEVICE_ATTR(in2_max_alarm, S_IRUGO, ltc4261_show_bool, NULL, + FAULT_OV); /* Currents (via sense resistor) */ -LTC4261_VALUE(curr1_input, LTC4261_SENSE_H); +static SENSOR_DEVICE_ATTR(curr1_input, S_IRUGO, ltc4261_show_value, NULL, + LTC4261_SENSE_H); /* Overcurrent alarm */ -LTC4261_BOOL(curr1_max_alarm, FAULT_OC); +static SENSOR_DEVICE_ATTR(curr1_max_alarm, S_IRUGO, ltc4261_show_bool, NULL, + FAULT_OC); static struct attribute *ltc4261_attributes[] = { &sensor_dev_attr_in1_input.dev_attr.attr, diff --git a/drivers/hwmon/max6697.c b/drivers/hwmon/max6697.c index bf4aa3777fc1..328fb0353c17 100644 --- a/drivers/hwmon/max6697.c +++ b/drivers/hwmon/max6697.c @@ -399,82 +399,95 @@ static SENSOR_DEVICE_ATTR(temp6_fault, S_IRUGO, show_alarm, NULL, 5); static SENSOR_DEVICE_ATTR(temp7_fault, S_IRUGO, show_alarm, NULL, 6); static SENSOR_DEVICE_ATTR(temp8_fault, S_IRUGO, show_alarm, NULL, 7); -static struct attribute *max6697_attributes[8][7] = { - { - &sensor_dev_attr_temp1_input.dev_attr.attr, - &sensor_dev_attr_temp1_max.dev_attr.attr, - &sensor_dev_attr_temp1_max_alarm.dev_attr.attr, - &sensor_dev_attr_temp1_crit.dev_attr.attr, - &sensor_dev_attr_temp1_crit_alarm.dev_attr.attr, - NULL - }, { - &sensor_dev_attr_temp2_input.dev_attr.attr, - &sensor_dev_attr_temp2_max.dev_attr.attr, - &sensor_dev_attr_temp2_max_alarm.dev_attr.attr, - &sensor_dev_attr_temp2_crit.dev_attr.attr, - &sensor_dev_attr_temp2_crit_alarm.dev_attr.attr, - &sensor_dev_attr_temp2_fault.dev_attr.attr, - NULL - }, { - &sensor_dev_attr_temp3_input.dev_attr.attr, - &sensor_dev_attr_temp3_max.dev_attr.attr, - &sensor_dev_attr_temp3_max_alarm.dev_attr.attr, - &sensor_dev_attr_temp3_crit.dev_attr.attr, - &sensor_dev_attr_temp3_crit_alarm.dev_attr.attr, - &sensor_dev_attr_temp3_fault.dev_attr.attr, - NULL - }, { - &sensor_dev_attr_temp4_input.dev_attr.attr, - &sensor_dev_attr_temp4_max.dev_attr.attr, - &sensor_dev_attr_temp4_max_alarm.dev_attr.attr, - &sensor_dev_attr_temp4_crit.dev_attr.attr, - &sensor_dev_attr_temp4_crit_alarm.dev_attr.attr, - &sensor_dev_attr_temp4_fault.dev_attr.attr, - NULL - }, { - &sensor_dev_attr_temp5_input.dev_attr.attr, - &sensor_dev_attr_temp5_max.dev_attr.attr, - &sensor_dev_attr_temp5_max_alarm.dev_attr.attr, - &sensor_dev_attr_temp5_crit.dev_attr.attr, - &sensor_dev_attr_temp5_crit_alarm.dev_attr.attr, - &sensor_dev_attr_temp5_fault.dev_attr.attr, - NULL - }, { - &sensor_dev_attr_temp6_input.dev_attr.attr, - &sensor_dev_attr_temp6_max.dev_attr.attr, - &sensor_dev_attr_temp6_max_alarm.dev_attr.attr, - &sensor_dev_attr_temp6_crit.dev_attr.attr, - &sensor_dev_attr_temp6_crit_alarm.dev_attr.attr, - &sensor_dev_attr_temp6_fault.dev_attr.attr, - NULL - }, { - &sensor_dev_attr_temp7_input.dev_attr.attr, - &sensor_dev_attr_temp7_max.dev_attr.attr, - &sensor_dev_attr_temp7_max_alarm.dev_attr.attr, - &sensor_dev_attr_temp7_crit.dev_attr.attr, - &sensor_dev_attr_temp7_crit_alarm.dev_attr.attr, - &sensor_dev_attr_temp7_fault.dev_attr.attr, - NULL - }, { - &sensor_dev_attr_temp8_input.dev_attr.attr, - &sensor_dev_attr_temp8_max.dev_attr.attr, - &sensor_dev_attr_temp8_max_alarm.dev_attr.attr, - &sensor_dev_attr_temp8_crit.dev_attr.attr, - &sensor_dev_attr_temp8_crit_alarm.dev_attr.attr, - &sensor_dev_attr_temp8_fault.dev_attr.attr, - NULL - } +static DEVICE_ATTR(dummy, 0, NULL, NULL); + +static umode_t max6697_is_visible(struct kobject *kobj, struct attribute *attr, + int index) +{ + struct device *dev = container_of(kobj, struct device, kobj); + struct i2c_client *client = to_i2c_client(dev); + struct max6697_data *data = i2c_get_clientdata(client); + const struct max6697_chip_data *chip = data->chip; + int channel = index / 6; /* channel number */ + int nr = index % 6; /* attribute index within channel */ + + if (channel >= chip->channels) + return 0; + + if ((nr == 3 || nr == 4) && !(chip->have_crit & (1 << channel))) + return 0; + if (nr == 5 && !(chip->have_fault & (1 << channel))) + return 0; + + return attr->mode; +} + +/* + * max6697_is_visible uses the index into the following array to determine + * if attributes should be created or not. Any change in order or content + * must be matched in max6697_is_visible. + */ +static struct attribute *max6697_attributes[] = { + &sensor_dev_attr_temp1_input.dev_attr.attr, + &sensor_dev_attr_temp1_max.dev_attr.attr, + &sensor_dev_attr_temp1_max_alarm.dev_attr.attr, + &sensor_dev_attr_temp1_crit.dev_attr.attr, + &sensor_dev_attr_temp1_crit_alarm.dev_attr.attr, + &dev_attr_dummy.attr, + + &sensor_dev_attr_temp2_input.dev_attr.attr, + &sensor_dev_attr_temp2_max.dev_attr.attr, + &sensor_dev_attr_temp2_max_alarm.dev_attr.attr, + &sensor_dev_attr_temp2_crit.dev_attr.attr, + &sensor_dev_attr_temp2_crit_alarm.dev_attr.attr, + &sensor_dev_attr_temp2_fault.dev_attr.attr, + + &sensor_dev_attr_temp3_input.dev_attr.attr, + &sensor_dev_attr_temp3_max.dev_attr.attr, + &sensor_dev_attr_temp3_max_alarm.dev_attr.attr, + &sensor_dev_attr_temp3_crit.dev_attr.attr, + &sensor_dev_attr_temp3_crit_alarm.dev_attr.attr, + &sensor_dev_attr_temp3_fault.dev_attr.attr, + + &sensor_dev_attr_temp4_input.dev_attr.attr, + &sensor_dev_attr_temp4_max.dev_attr.attr, + &sensor_dev_attr_temp4_max_alarm.dev_attr.attr, + &sensor_dev_attr_temp4_crit.dev_attr.attr, + &sensor_dev_attr_temp4_crit_alarm.dev_attr.attr, + &sensor_dev_attr_temp4_fault.dev_attr.attr, + + &sensor_dev_attr_temp5_input.dev_attr.attr, + &sensor_dev_attr_temp5_max.dev_attr.attr, + &sensor_dev_attr_temp5_max_alarm.dev_attr.attr, + &sensor_dev_attr_temp5_crit.dev_attr.attr, + &sensor_dev_attr_temp5_crit_alarm.dev_attr.attr, + &sensor_dev_attr_temp5_fault.dev_attr.attr, + + &sensor_dev_attr_temp6_input.dev_attr.attr, + &sensor_dev_attr_temp6_max.dev_attr.attr, + &sensor_dev_attr_temp6_max_alarm.dev_attr.attr, + &sensor_dev_attr_temp6_crit.dev_attr.attr, + &sensor_dev_attr_temp6_crit_alarm.dev_attr.attr, + &sensor_dev_attr_temp6_fault.dev_attr.attr, + + &sensor_dev_attr_temp7_input.dev_attr.attr, + &sensor_dev_attr_temp7_max.dev_attr.attr, + &sensor_dev_attr_temp7_max_alarm.dev_attr.attr, + &sensor_dev_attr_temp7_crit.dev_attr.attr, + &sensor_dev_attr_temp7_crit_alarm.dev_attr.attr, + &sensor_dev_attr_temp7_fault.dev_attr.attr, + + &sensor_dev_attr_temp8_input.dev_attr.attr, + &sensor_dev_attr_temp8_max.dev_attr.attr, + &sensor_dev_attr_temp8_max_alarm.dev_attr.attr, + &sensor_dev_attr_temp8_crit.dev_attr.attr, + &sensor_dev_attr_temp8_crit_alarm.dev_attr.attr, + &sensor_dev_attr_temp8_fault.dev_attr.attr, + NULL }; -static const struct attribute_group max6697_group[8] = { - { .attrs = max6697_attributes[0] }, - { .attrs = max6697_attributes[1] }, - { .attrs = max6697_attributes[2] }, - { .attrs = max6697_attributes[3] }, - { .attrs = max6697_attributes[4] }, - { .attrs = max6697_attributes[5] }, - { .attrs = max6697_attributes[6] }, - { .attrs = max6697_attributes[7] }, +static const struct attribute_group max6697_group = { + .attrs = max6697_attributes, .is_visible = max6697_is_visible, }; static void max6697_get_config_of(struct device_node *node, @@ -606,21 +619,13 @@ done: return 0; } -static void max6697_remove_files(struct i2c_client *client) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(max6697_group); i++) - sysfs_remove_group(&client->dev.kobj, &max6697_group[i]); -} - static int max6697_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct i2c_adapter *adapter = client->adapter; struct device *dev = &client->dev; struct max6697_data *data; - int i, err; + int err; if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) return -ENODEV; @@ -639,37 +644,9 @@ static int max6697_probe(struct i2c_client *client, if (err) return err; - for (i = 0; i < data->chip->channels; i++) { - err = sysfs_create_file(&dev->kobj, - max6697_attributes[i][0]); - if (err) - goto error; - err = sysfs_create_file(&dev->kobj, - max6697_attributes[i][1]); - if (err) - goto error; - err = sysfs_create_file(&dev->kobj, - max6697_attributes[i][2]); - if (err) - goto error; - - if (data->chip->have_crit & (1 << i)) { - err = sysfs_create_file(&dev->kobj, - max6697_attributes[i][3]); - if (err) - goto error; - err = sysfs_create_file(&dev->kobj, - max6697_attributes[i][4]); - if (err) - goto error; - } - if (data->chip->have_fault & (1 << i)) { - err = sysfs_create_file(&dev->kobj, - max6697_attributes[i][5]); - if (err) - goto error; - } - } + err = sysfs_create_group(&client->dev.kobj, &max6697_group); + if (err) + return err; data->hwmon_dev = hwmon_device_register(dev); if (IS_ERR(data->hwmon_dev)) { @@ -680,7 +657,7 @@ static int max6697_probe(struct i2c_client *client, return 0; error: - max6697_remove_files(client); + sysfs_remove_group(&client->dev.kobj, &max6697_group); return err; } @@ -689,7 +666,7 @@ static int max6697_remove(struct i2c_client *client) struct max6697_data *data = i2c_get_clientdata(client); hwmon_device_unregister(data->hwmon_dev); - max6697_remove_files(client); + sysfs_remove_group(&client->dev.kobj, &max6697_group); return 0; } diff --git a/drivers/hwmon/mc13783-adc.c b/drivers/hwmon/mc13783-adc.c index 2a7f331cd3c0..982d8622c09b 100644 --- a/drivers/hwmon/mc13783-adc.c +++ b/drivers/hwmon/mc13783-adc.c @@ -273,18 +273,7 @@ static struct platform_driver mc13783_adc_driver = { .id_table = mc13783_adc_idtable, }; -static int __init mc13783_adc_init(void) -{ - return platform_driver_probe(&mc13783_adc_driver, mc13783_adc_probe); -} - -static void __exit mc13783_adc_exit(void) -{ - platform_driver_unregister(&mc13783_adc_driver); -} - -module_init(mc13783_adc_init); -module_exit(mc13783_adc_exit); +module_platform_driver_probe(mc13783_adc_driver, mc13783_adc_probe); MODULE_DESCRIPTION("MC13783 ADC driver"); MODULE_AUTHOR("Luotao Fu <l.fu@pengutronix.de>"); diff --git a/drivers/hwmon/nct6775.c b/drivers/hwmon/nct6775.c new file mode 100644 index 000000000000..f43f5e571db9 --- /dev/null +++ b/drivers/hwmon/nct6775.c @@ -0,0 +1,4191 @@ +/* + * nct6775 - Driver for the hardware monitoring functionality of + * Nuvoton NCT677x Super-I/O chips + * + * Copyright (C) 2012 Guenter Roeck <linux@roeck-us.net> + * + * Derived from w83627ehf driver + * Copyright (C) 2005-2012 Jean Delvare <khali@linux-fr.org> + * Copyright (C) 2006 Yuan Mu (Winbond), + * Rudolf Marek <r.marek@assembler.cz> + * David Hubbard <david.c.hubbard@gmail.com> + * Daniel J Blueman <daniel.blueman@gmail.com> + * Copyright (C) 2010 Sheng-Yuan Huang (Nuvoton) (PS00) + * + * Shamelessly ripped from the w83627hf driver + * Copyright (C) 2003 Mark Studebaker + * + * 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. + * + * + * Supports the following chips: + * + * Chip #vin #fan #pwm #temp chip IDs man ID + * nct6775f 9 4 3 6+3 0xb470 0xc1 0x5ca3 + * nct6776f 9 5 3 6+3 0xc330 0xc1 0x5ca3 + * nct6779d 15 5 5 2+6 0xc560 0xc1 0x5ca3 + * + * #temp lists the number of monitored temperature sources (first value) plus + * the number of directly connectable temperature sensors (second value). + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/slab.h> +#include <linux/jiffies.h> +#include <linux/platform_device.h> +#include <linux/hwmon.h> +#include <linux/hwmon-sysfs.h> +#include <linux/hwmon-vid.h> +#include <linux/err.h> +#include <linux/mutex.h> +#include <linux/acpi.h> +#include <linux/io.h> +#include "lm75.h" + +#define USE_ALTERNATE + +enum kinds { nct6775, nct6776, nct6779 }; + +/* used to set data->name = nct6775_device_names[data->sio_kind] */ +static const char * const nct6775_device_names[] = { + "nct6775", + "nct6776", + "nct6779", +}; + +static unsigned short force_id; +module_param(force_id, ushort, 0); +MODULE_PARM_DESC(force_id, "Override the detected device ID"); + +static unsigned short fan_debounce; +module_param(fan_debounce, ushort, 0); +MODULE_PARM_DESC(fan_debounce, "Enable debouncing for fan RPM signal"); + +#define DRVNAME "nct6775" + +/* + * Super-I/O constants and functions + */ + +#define NCT6775_LD_ACPI 0x0a +#define NCT6775_LD_HWM 0x0b +#define NCT6775_LD_VID 0x0d + +#define SIO_REG_LDSEL 0x07 /* Logical device select */ +#define SIO_REG_DEVID 0x20 /* Device ID (2 bytes) */ +#define SIO_REG_ENABLE 0x30 /* Logical device enable */ +#define SIO_REG_ADDR 0x60 /* Logical device address (2 bytes) */ + +#define SIO_NCT6775_ID 0xb470 +#define SIO_NCT6776_ID 0xc330 +#define SIO_NCT6779_ID 0xc560 +#define SIO_ID_MASK 0xFFF0 + +enum pwm_enable { off, manual, thermal_cruise, speed_cruise, sf3, sf4 }; + +static inline void +superio_outb(int ioreg, int reg, int val) +{ + outb(reg, ioreg); + outb(val, ioreg + 1); +} + +static inline int +superio_inb(int ioreg, int reg) +{ + outb(reg, ioreg); + return inb(ioreg + 1); +} + +static inline void +superio_select(int ioreg, int ld) +{ + outb(SIO_REG_LDSEL, ioreg); + outb(ld, ioreg + 1); +} + +static inline int +superio_enter(int ioreg) +{ + /* + * Try to reserve <ioreg> and <ioreg + 1> for exclusive access. + */ + if (!request_muxed_region(ioreg, 2, DRVNAME)) + return -EBUSY; + + outb(0x87, ioreg); + outb(0x87, ioreg); + + return 0; +} + +static inline void +superio_exit(int ioreg) +{ + outb(0xaa, ioreg); + outb(0x02, ioreg); + outb(0x02, ioreg + 1); + release_region(ioreg, 2); +} + +/* + * ISA constants + */ + +#define IOREGION_ALIGNMENT (~7) +#define IOREGION_OFFSET 5 +#define IOREGION_LENGTH 2 +#define ADDR_REG_OFFSET 0 +#define DATA_REG_OFFSET 1 + +#define NCT6775_REG_BANK 0x4E +#define NCT6775_REG_CONFIG 0x40 + +/* + * Not currently used: + * REG_MAN_ID has the value 0x5ca3 for all supported chips. + * REG_CHIP_ID == 0x88/0xa1/0xc1 depending on chip model. + * REG_MAN_ID is at port 0x4f + * REG_CHIP_ID is at port 0x58 + */ + +#define NUM_TEMP 10 /* Max number of temp attribute sets w/ limits*/ +#define NUM_TEMP_FIXED 6 /* Max number of fixed temp attribute sets */ + +#define NUM_REG_ALARM 4 /* Max number of alarm registers */ + +/* Common and NCT6775 specific data */ + +/* Voltage min/max registers for nr=7..14 are in bank 5 */ + +static const u16 NCT6775_REG_IN_MAX[] = { + 0x2b, 0x2d, 0x2f, 0x31, 0x33, 0x35, 0x37, 0x554, 0x556, 0x558, 0x55a, + 0x55c, 0x55e, 0x560, 0x562 }; +static const u16 NCT6775_REG_IN_MIN[] = { + 0x2c, 0x2e, 0x30, 0x32, 0x34, 0x36, 0x38, 0x555, 0x557, 0x559, 0x55b, + 0x55d, 0x55f, 0x561, 0x563 }; +static const u16 NCT6775_REG_IN[] = { + 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x550, 0x551, 0x552 +}; + +#define NCT6775_REG_VBAT 0x5D +#define NCT6775_REG_DIODE 0x5E + +#define NCT6775_REG_FANDIV1 0x506 +#define NCT6775_REG_FANDIV2 0x507 + +#define NCT6775_REG_CR_FAN_DEBOUNCE 0xf0 + +static const u16 NCT6775_REG_ALARM[NUM_REG_ALARM] = { 0x459, 0x45A, 0x45B }; + +/* 0..15 voltages, 16..23 fans, 24..31 temperatures */ + +static const s8 NCT6775_ALARM_BITS[] = { + 0, 1, 2, 3, 8, 21, 20, 16, /* in0.. in7 */ + 17, -1, -1, -1, -1, -1, -1, /* in8..in14 */ + -1, /* unused */ + 6, 7, 11, 10, 23, /* fan1..fan5 */ + -1, -1, -1, /* unused */ + 4, 5, 13, -1, -1, -1, /* temp1..temp6 */ + 12, -1 }; /* intrusion0, intrusion1 */ + +#define FAN_ALARM_BASE 16 +#define TEMP_ALARM_BASE 24 +#define INTRUSION_ALARM_BASE 30 + +static const u8 NCT6775_REG_CR_CASEOPEN_CLR[] = { 0xe6, 0xee }; +static const u8 NCT6775_CR_CASEOPEN_CLR_MASK[] = { 0x20, 0x01 }; + +/* DC or PWM output fan configuration */ +static const u8 NCT6775_REG_PWM_MODE[] = { 0x04, 0x04, 0x12 }; +static const u8 NCT6775_PWM_MODE_MASK[] = { 0x01, 0x02, 0x01 }; + +/* Advanced Fan control, some values are common for all fans */ + +static const u16 NCT6775_REG_TARGET[] = { 0x101, 0x201, 0x301, 0x801, 0x901 }; +static const u16 NCT6775_REG_FAN_MODE[] = { 0x102, 0x202, 0x302, 0x802, 0x902 }; +static const u16 NCT6775_REG_FAN_STEP_DOWN_TIME[] = { + 0x103, 0x203, 0x303, 0x803, 0x903 }; +static const u16 NCT6775_REG_FAN_STEP_UP_TIME[] = { + 0x104, 0x204, 0x304, 0x804, 0x904 }; +static const u16 NCT6775_REG_FAN_STOP_OUTPUT[] = { + 0x105, 0x205, 0x305, 0x805, 0x905 }; +static const u16 NCT6775_REG_FAN_START_OUTPUT[] + = { 0x106, 0x206, 0x306, 0x806, 0x906 }; +static const u16 NCT6775_REG_FAN_MAX_OUTPUT[] = { 0x10a, 0x20a, 0x30a }; +static const u16 NCT6775_REG_FAN_STEP_OUTPUT[] = { 0x10b, 0x20b, 0x30b }; + +static const u16 NCT6775_REG_FAN_STOP_TIME[] = { + 0x107, 0x207, 0x307, 0x807, 0x907 }; +static const u16 NCT6775_REG_PWM[] = { 0x109, 0x209, 0x309, 0x809, 0x909 }; +static const u16 NCT6775_REG_PWM_READ[] = { 0x01, 0x03, 0x11, 0x13, 0x15 }; + +static const u16 NCT6775_REG_FAN[] = { 0x630, 0x632, 0x634, 0x636, 0x638 }; +static const u16 NCT6775_REG_FAN_MIN[] = { 0x3b, 0x3c, 0x3d }; +static const u16 NCT6775_REG_FAN_PULSES[] = { 0x641, 0x642, 0x643, 0x644, 0 }; + +static const u16 NCT6775_REG_TEMP[] = { + 0x27, 0x150, 0x250, 0x62b, 0x62c, 0x62d }; + +static const u16 NCT6775_REG_TEMP_CONFIG[ARRAY_SIZE(NCT6775_REG_TEMP)] = { + 0, 0x152, 0x252, 0x628, 0x629, 0x62A }; +static const u16 NCT6775_REG_TEMP_HYST[ARRAY_SIZE(NCT6775_REG_TEMP)] = { + 0x3a, 0x153, 0x253, 0x673, 0x678, 0x67D }; +static const u16 NCT6775_REG_TEMP_OVER[ARRAY_SIZE(NCT6775_REG_TEMP)] = { + 0x39, 0x155, 0x255, 0x672, 0x677, 0x67C }; + +static const u16 NCT6775_REG_TEMP_SOURCE[ARRAY_SIZE(NCT6775_REG_TEMP)] = { + 0x621, 0x622, 0x623, 0x624, 0x625, 0x626 }; + +static const u16 NCT6775_REG_TEMP_SEL[] = { + 0x100, 0x200, 0x300, 0x800, 0x900 }; + +static const u16 NCT6775_REG_WEIGHT_TEMP_SEL[] = { + 0x139, 0x239, 0x339, 0x839, 0x939 }; +static const u16 NCT6775_REG_WEIGHT_TEMP_STEP[] = { + 0x13a, 0x23a, 0x33a, 0x83a, 0x93a }; +static const u16 NCT6775_REG_WEIGHT_TEMP_STEP_TOL[] = { + 0x13b, 0x23b, 0x33b, 0x83b, 0x93b }; +static const u16 NCT6775_REG_WEIGHT_DUTY_STEP[] = { + 0x13c, 0x23c, 0x33c, 0x83c, 0x93c }; +static const u16 NCT6775_REG_WEIGHT_TEMP_BASE[] = { + 0x13d, 0x23d, 0x33d, 0x83d, 0x93d }; + +static const u16 NCT6775_REG_TEMP_OFFSET[] = { 0x454, 0x455, 0x456 }; + +static const u16 NCT6775_REG_AUTO_TEMP[] = { + 0x121, 0x221, 0x321, 0x821, 0x921 }; +static const u16 NCT6775_REG_AUTO_PWM[] = { + 0x127, 0x227, 0x327, 0x827, 0x927 }; + +#define NCT6775_AUTO_TEMP(data, nr, p) ((data)->REG_AUTO_TEMP[nr] + (p)) +#define NCT6775_AUTO_PWM(data, nr, p) ((data)->REG_AUTO_PWM[nr] + (p)) + +static const u16 NCT6775_REG_CRITICAL_ENAB[] = { 0x134, 0x234, 0x334 }; + +static const u16 NCT6775_REG_CRITICAL_TEMP[] = { + 0x135, 0x235, 0x335, 0x835, 0x935 }; +static const u16 NCT6775_REG_CRITICAL_TEMP_TOLERANCE[] = { + 0x138, 0x238, 0x338, 0x838, 0x938 }; + +static const char *const nct6775_temp_label[] = { + "", + "SYSTIN", + "CPUTIN", + "AUXTIN", + "AMD SB-TSI", + "PECI Agent 0", + "PECI Agent 1", + "PECI Agent 2", + "PECI Agent 3", + "PECI Agent 4", + "PECI Agent 5", + "PECI Agent 6", + "PECI Agent 7", + "PCH_CHIP_CPU_MAX_TEMP", + "PCH_CHIP_TEMP", + "PCH_CPU_TEMP", + "PCH_MCH_TEMP", + "PCH_DIM0_TEMP", + "PCH_DIM1_TEMP", + "PCH_DIM2_TEMP", + "PCH_DIM3_TEMP" +}; + +static const u16 NCT6775_REG_TEMP_ALTERNATE[ARRAY_SIZE(nct6775_temp_label) - 1] + = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x661, 0x662, 0x664 }; + +static const u16 NCT6775_REG_TEMP_CRIT[ARRAY_SIZE(nct6775_temp_label) - 1] + = { 0, 0, 0, 0, 0xa00, 0xa01, 0xa02, 0xa03, 0xa04, 0xa05, 0xa06, + 0xa07 }; + +/* NCT6776 specific data */ + +static const s8 NCT6776_ALARM_BITS[] = { + 0, 1, 2, 3, 8, 21, 20, 16, /* in0.. in7 */ + 17, -1, -1, -1, -1, -1, -1, /* in8..in14 */ + -1, /* unused */ + 6, 7, 11, 10, 23, /* fan1..fan5 */ + -1, -1, -1, /* unused */ + 4, 5, 13, -1, -1, -1, /* temp1..temp6 */ + 12, 9 }; /* intrusion0, intrusion1 */ + +static const u16 NCT6776_REG_TOLERANCE_H[] = { + 0x10c, 0x20c, 0x30c, 0x80c, 0x90c }; + +static const u8 NCT6776_REG_PWM_MODE[] = { 0x04, 0, 0 }; +static const u8 NCT6776_PWM_MODE_MASK[] = { 0x01, 0, 0 }; + +static const u16 NCT6776_REG_FAN_MIN[] = { 0x63a, 0x63c, 0x63e, 0x640, 0x642 }; +static const u16 NCT6776_REG_FAN_PULSES[] = { 0x644, 0x645, 0x646, 0, 0 }; + +static const u16 NCT6776_REG_WEIGHT_DUTY_BASE[] = { + 0x13e, 0x23e, 0x33e, 0x83e, 0x93e }; + +static const u16 NCT6776_REG_TEMP_CONFIG[ARRAY_SIZE(NCT6775_REG_TEMP)] = { + 0x18, 0x152, 0x252, 0x628, 0x629, 0x62A }; + +static const char *const nct6776_temp_label[] = { + "", + "SYSTIN", + "CPUTIN", + "AUXTIN", + "SMBUSMASTER 0", + "SMBUSMASTER 1", + "SMBUSMASTER 2", + "SMBUSMASTER 3", + "SMBUSMASTER 4", + "SMBUSMASTER 5", + "SMBUSMASTER 6", + "SMBUSMASTER 7", + "PECI Agent 0", + "PECI Agent 1", + "PCH_CHIP_CPU_MAX_TEMP", + "PCH_CHIP_TEMP", + "PCH_CPU_TEMP", + "PCH_MCH_TEMP", + "PCH_DIM0_TEMP", + "PCH_DIM1_TEMP", + "PCH_DIM2_TEMP", + "PCH_DIM3_TEMP", + "BYTE_TEMP" +}; + +static const u16 NCT6776_REG_TEMP_ALTERNATE[ARRAY_SIZE(nct6776_temp_label) - 1] + = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x401, 0x402, 0x404 }; + +static const u16 NCT6776_REG_TEMP_CRIT[ARRAY_SIZE(nct6776_temp_label) - 1] + = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x709, 0x70a }; + +/* NCT6779 specific data */ + +static const u16 NCT6779_REG_IN[] = { + 0x480, 0x481, 0x482, 0x483, 0x484, 0x485, 0x486, 0x487, + 0x488, 0x489, 0x48a, 0x48b, 0x48c, 0x48d, 0x48e }; + +static const u16 NCT6779_REG_ALARM[NUM_REG_ALARM] = { + 0x459, 0x45A, 0x45B, 0x568 }; + +static const s8 NCT6779_ALARM_BITS[] = { + 0, 1, 2, 3, 8, 21, 20, 16, /* in0.. in7 */ + 17, 24, 25, 26, 27, 28, 29, /* in8..in14 */ + -1, /* unused */ + 6, 7, 11, 10, 23, /* fan1..fan5 */ + -1, -1, -1, /* unused */ + 4, 5, 13, -1, -1, -1, /* temp1..temp6 */ + 12, 9 }; /* intrusion0, intrusion1 */ + +static const u16 NCT6779_REG_FAN[] = { 0x4b0, 0x4b2, 0x4b4, 0x4b6, 0x4b8 }; +static const u16 NCT6779_REG_FAN_PULSES[] = { + 0x644, 0x645, 0x646, 0x647, 0x648 }; + +static const u16 NCT6779_REG_CRITICAL_PWM_ENABLE[] = { + 0x136, 0x236, 0x336, 0x836, 0x936 }; +static const u16 NCT6779_REG_CRITICAL_PWM[] = { + 0x137, 0x237, 0x337, 0x837, 0x937 }; + +static const u16 NCT6779_REG_TEMP[] = { 0x27, 0x150 }; +static const u16 NCT6779_REG_TEMP_CONFIG[ARRAY_SIZE(NCT6779_REG_TEMP)] = { + 0x18, 0x152 }; +static const u16 NCT6779_REG_TEMP_HYST[ARRAY_SIZE(NCT6779_REG_TEMP)] = { + 0x3a, 0x153 }; +static const u16 NCT6779_REG_TEMP_OVER[ARRAY_SIZE(NCT6779_REG_TEMP)] = { + 0x39, 0x155 }; + +static const u16 NCT6779_REG_TEMP_OFFSET[] = { + 0x454, 0x455, 0x456, 0x44a, 0x44b, 0x44c }; + +static const char *const nct6779_temp_label[] = { + "", + "SYSTIN", + "CPUTIN", + "AUXTIN0", + "AUXTIN1", + "AUXTIN2", + "AUXTIN3", + "", + "SMBUSMASTER 0", + "SMBUSMASTER 1", + "SMBUSMASTER 2", + "SMBUSMASTER 3", + "SMBUSMASTER 4", + "SMBUSMASTER 5", + "SMBUSMASTER 6", + "SMBUSMASTER 7", + "PECI Agent 0", + "PECI Agent 1", + "PCH_CHIP_CPU_MAX_TEMP", + "PCH_CHIP_TEMP", + "PCH_CPU_TEMP", + "PCH_MCH_TEMP", + "PCH_DIM0_TEMP", + "PCH_DIM1_TEMP", + "PCH_DIM2_TEMP", + "PCH_DIM3_TEMP", + "BYTE_TEMP" +}; + +static const u16 NCT6779_REG_TEMP_ALTERNATE[ARRAY_SIZE(nct6779_temp_label) - 1] + = { 0x490, 0x491, 0x492, 0x493, 0x494, 0x495, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0x400, 0x401, 0x402, 0x404, 0x405, 0x406, 0x407, + 0x408, 0 }; + +static const u16 NCT6779_REG_TEMP_CRIT[ARRAY_SIZE(nct6779_temp_label) - 1] + = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x709, 0x70a }; + +static enum pwm_enable reg_to_pwm_enable(int pwm, int mode) +{ + if (mode == 0 && pwm == 255) + return off; + return mode + 1; +} + +static int pwm_enable_to_reg(enum pwm_enable mode) +{ + if (mode == off) + return 0; + return mode - 1; +} + +/* + * Conversions + */ + +/* 1 is DC mode, output in ms */ +static unsigned int step_time_from_reg(u8 reg, u8 mode) +{ + return mode ? 400 * reg : 100 * reg; +} + +static u8 step_time_to_reg(unsigned int msec, u8 mode) +{ + return clamp_val((mode ? (msec + 200) / 400 : + (msec + 50) / 100), 1, 255); +} + +static unsigned int fan_from_reg8(u16 reg, unsigned int divreg) +{ + if (reg == 0 || reg == 255) + return 0; + return 1350000U / (reg << divreg); +} + +static unsigned int fan_from_reg13(u16 reg, unsigned int divreg) +{ + if ((reg & 0xff1f) == 0xff1f) + return 0; + + reg = (reg & 0x1f) | ((reg & 0xff00) >> 3); + + if (reg == 0) + return 0; + + return 1350000U / reg; +} + +static unsigned int fan_from_reg16(u16 reg, unsigned int divreg) +{ + if (reg == 0 || reg == 0xffff) + return 0; + + /* + * Even though the registers are 16 bit wide, the fan divisor + * still applies. + */ + return 1350000U / (reg << divreg); +} + +static u16 fan_to_reg(u32 fan, unsigned int divreg) +{ + if (!fan) + return 0; + + return (1350000U / fan) >> divreg; +} + +static inline unsigned int +div_from_reg(u8 reg) +{ + return 1 << reg; +} + +/* + * Some of the voltage inputs have internal scaling, the tables below + * contain 8 (the ADC LSB in mV) * scaling factor * 100 + */ +static const u16 scale_in[15] = { + 800, 800, 1600, 1600, 800, 800, 800, 1600, 1600, 800, 800, 800, 800, + 800, 800 +}; + +static inline long in_from_reg(u8 reg, u8 nr) +{ + return DIV_ROUND_CLOSEST(reg * scale_in[nr], 100); +} + +static inline u8 in_to_reg(u32 val, u8 nr) +{ + return clamp_val(DIV_ROUND_CLOSEST(val * 100, scale_in[nr]), 0, 255); +} + +/* + * Data structures and manipulation thereof + */ + +struct nct6775_data { + int addr; /* IO base of hw monitor block */ + enum kinds kind; + const char *name; + + struct device *hwmon_dev; + + u16 reg_temp[4][NUM_TEMP]; /* 0=temp, 1=temp_over, 2=temp_hyst, + * 3=temp_crit + */ + u8 temp_src[NUM_TEMP]; + u16 reg_temp_config[NUM_TEMP]; + const char * const *temp_label; + int temp_label_num; + + u16 REG_CONFIG; + u16 REG_VBAT; + u16 REG_DIODE; + + const s8 *ALARM_BITS; + + const u16 *REG_VIN; + const u16 *REG_IN_MINMAX[2]; + + const u16 *REG_TARGET; + const u16 *REG_FAN; + const u16 *REG_FAN_MODE; + const u16 *REG_FAN_MIN; + const u16 *REG_FAN_PULSES; + const u16 *REG_FAN_TIME[3]; + + const u16 *REG_TOLERANCE_H; + + const u8 *REG_PWM_MODE; + const u8 *PWM_MODE_MASK; + + const u16 *REG_PWM[7]; /* [0]=pwm, [1]=pwm_start, [2]=pwm_floor, + * [3]=pwm_max, [4]=pwm_step, + * [5]=weight_duty_step, [6]=weight_duty_base + */ + const u16 *REG_PWM_READ; + + const u16 *REG_AUTO_TEMP; + const u16 *REG_AUTO_PWM; + + const u16 *REG_CRITICAL_TEMP; + const u16 *REG_CRITICAL_TEMP_TOLERANCE; + + const u16 *REG_TEMP_SOURCE; /* temp register sources */ + const u16 *REG_TEMP_SEL; + const u16 *REG_WEIGHT_TEMP_SEL; + const u16 *REG_WEIGHT_TEMP[3]; /* 0=base, 1=tolerance, 2=step */ + + const u16 *REG_TEMP_OFFSET; + + const u16 *REG_ALARM; + + unsigned int (*fan_from_reg)(u16 reg, unsigned int divreg); + unsigned int (*fan_from_reg_min)(u16 reg, unsigned int divreg); + + struct mutex update_lock; + bool valid; /* true if following fields are valid */ + unsigned long last_updated; /* In jiffies */ + + /* Register values */ + u8 bank; /* current register bank */ + u8 in_num; /* number of in inputs we have */ + u8 in[15][3]; /* [0]=in, [1]=in_max, [2]=in_min */ + unsigned int rpm[5]; + u16 fan_min[5]; + u8 fan_pulses[5]; + u8 fan_div[5]; + u8 has_pwm; + u8 has_fan; /* some fan inputs can be disabled */ + u8 has_fan_min; /* some fans don't have min register */ + bool has_fan_div; + + u8 temp_fixed_num; /* 3 or 6 */ + u8 temp_type[NUM_TEMP_FIXED]; + s8 temp_offset[NUM_TEMP_FIXED]; + s16 temp[4][NUM_TEMP]; /* 0=temp, 1=temp_over, 2=temp_hyst, + * 3=temp_crit */ + u64 alarms; + + u8 pwm_num; /* number of pwm */ + u8 pwm_mode[5]; /* 1->DC variable voltage, 0->PWM variable duty cycle */ + enum pwm_enable pwm_enable[5]; + /* 0->off + * 1->manual + * 2->thermal cruise mode (also called SmartFan I) + * 3->fan speed cruise mode + * 4->SmartFan III + * 5->enhanced variable thermal cruise (SmartFan IV) + */ + u8 pwm[7][5]; /* [0]=pwm, [1]=pwm_start, [2]=pwm_floor, + * [3]=pwm_max, [4]=pwm_step, + * [5]=weight_duty_step, [6]=weight_duty_base + */ + + u8 target_temp[5]; + u8 target_temp_mask; + u32 target_speed[5]; + u32 target_speed_tolerance[5]; + u8 speed_tolerance_limit; + + u8 temp_tolerance[2][5]; + u8 tolerance_mask; + + u8 fan_time[3][5]; /* 0 = stop_time, 1 = step_up, 2 = step_down */ + + /* Automatic fan speed control registers */ + int auto_pwm_num; + u8 auto_pwm[5][7]; + u8 auto_temp[5][7]; + u8 pwm_temp_sel[5]; + u8 pwm_weight_temp_sel[5]; + u8 weight_temp[3][5]; /* 0->temp_step, 1->temp_step_tol, + * 2->temp_base + */ + + u8 vid; + u8 vrm; + + u16 have_temp; + u16 have_temp_fixed; + u16 have_in; +#ifdef CONFIG_PM + /* Remember extra register values over suspend/resume */ + u8 vbat; + u8 fandiv1; + u8 fandiv2; +#endif +}; + +struct nct6775_sio_data { + int sioreg; + enum kinds kind; +}; + +static bool is_word_sized(struct nct6775_data *data, u16 reg) +{ + switch (data->kind) { + case nct6775: + return (((reg & 0xff00) == 0x100 || + (reg & 0xff00) == 0x200) && + ((reg & 0x00ff) == 0x50 || + (reg & 0x00ff) == 0x53 || + (reg & 0x00ff) == 0x55)) || + (reg & 0xfff0) == 0x630 || + reg == 0x640 || reg == 0x642 || + reg == 0x662 || + ((reg & 0xfff0) == 0x650 && (reg & 0x000f) >= 0x06) || + reg == 0x73 || reg == 0x75 || reg == 0x77; + case nct6776: + return (((reg & 0xff00) == 0x100 || + (reg & 0xff00) == 0x200) && + ((reg & 0x00ff) == 0x50 || + (reg & 0x00ff) == 0x53 || + (reg & 0x00ff) == 0x55)) || + (reg & 0xfff0) == 0x630 || + reg == 0x402 || + reg == 0x640 || reg == 0x642 || + ((reg & 0xfff0) == 0x650 && (reg & 0x000f) >= 0x06) || + reg == 0x73 || reg == 0x75 || reg == 0x77; + case nct6779: + return reg == 0x150 || reg == 0x153 || reg == 0x155 || + ((reg & 0xfff0) == 0x4b0 && (reg & 0x000f) < 0x09) || + reg == 0x402 || + reg == 0x63a || reg == 0x63c || reg == 0x63e || + reg == 0x640 || reg == 0x642 || + reg == 0x73 || reg == 0x75 || reg == 0x77 || reg == 0x79 || + reg == 0x7b; + } + return false; +} + +/* + * On older chips, only registers 0x50-0x5f are banked. + * On more recent chips, all registers are banked. + * Assume that is the case and set the bank number for each access. + * Cache the bank number so it only needs to be set if it changes. + */ +static inline void nct6775_set_bank(struct nct6775_data *data, u16 reg) +{ + u8 bank = reg >> 8; + if (data->bank != bank) { + outb_p(NCT6775_REG_BANK, data->addr + ADDR_REG_OFFSET); + outb_p(bank, data->addr + DATA_REG_OFFSET); + data->bank = bank; + } +} + +static u16 nct6775_read_value(struct nct6775_data *data, u16 reg) +{ + int res, word_sized = is_word_sized(data, reg); + + nct6775_set_bank(data, reg); + outb_p(reg & 0xff, data->addr + ADDR_REG_OFFSET); + res = inb_p(data->addr + DATA_REG_OFFSET); + if (word_sized) { + outb_p((reg & 0xff) + 1, + data->addr + ADDR_REG_OFFSET); + res = (res << 8) + inb_p(data->addr + DATA_REG_OFFSET); + } + return res; +} + +static int nct6775_write_value(struct nct6775_data *data, u16 reg, u16 value) +{ + int word_sized = is_word_sized(data, reg); + + nct6775_set_bank(data, reg); + outb_p(reg & 0xff, data->addr + ADDR_REG_OFFSET); + if (word_sized) { + outb_p(value >> 8, data->addr + DATA_REG_OFFSET); + outb_p((reg & 0xff) + 1, + data->addr + ADDR_REG_OFFSET); + } + outb_p(value & 0xff, data->addr + DATA_REG_OFFSET); + return 0; +} + +/* We left-align 8-bit temperature values to make the code simpler */ +static u16 nct6775_read_temp(struct nct6775_data *data, u16 reg) +{ + u16 res; + + res = nct6775_read_value(data, reg); + if (!is_word_sized(data, reg)) + res <<= 8; + + return res; +} + +static int nct6775_write_temp(struct nct6775_data *data, u16 reg, u16 value) +{ + if (!is_word_sized(data, reg)) + value >>= 8; + return nct6775_write_value(data, reg, value); +} + +/* This function assumes that the caller holds data->update_lock */ +static void nct6775_write_fan_div(struct nct6775_data *data, int nr) +{ + u8 reg; + + switch (nr) { + case 0: + reg = (nct6775_read_value(data, NCT6775_REG_FANDIV1) & 0x70) + | (data->fan_div[0] & 0x7); + nct6775_write_value(data, NCT6775_REG_FANDIV1, reg); + break; + case 1: + reg = (nct6775_read_value(data, NCT6775_REG_FANDIV1) & 0x7) + | ((data->fan_div[1] << 4) & 0x70); + nct6775_write_value(data, NCT6775_REG_FANDIV1, reg); + break; + case 2: + reg = (nct6775_read_value(data, NCT6775_REG_FANDIV2) & 0x70) + | (data->fan_div[2] & 0x7); + nct6775_write_value(data, NCT6775_REG_FANDIV2, reg); + break; + case 3: + reg = (nct6775_read_value(data, NCT6775_REG_FANDIV2) & 0x7) + | ((data->fan_div[3] << 4) & 0x70); + nct6775_write_value(data, NCT6775_REG_FANDIV2, reg); + break; + } +} + +static void nct6775_write_fan_div_common(struct nct6775_data *data, int nr) +{ + if (data->kind == nct6775) + nct6775_write_fan_div(data, nr); +} + +static void nct6775_update_fan_div(struct nct6775_data *data) +{ + u8 i; + + i = nct6775_read_value(data, NCT6775_REG_FANDIV1); + data->fan_div[0] = i & 0x7; + data->fan_div[1] = (i & 0x70) >> 4; + i = nct6775_read_value(data, NCT6775_REG_FANDIV2); + data->fan_div[2] = i & 0x7; + if (data->has_fan & (1 << 3)) + data->fan_div[3] = (i & 0x70) >> 4; +} + +static void nct6775_update_fan_div_common(struct nct6775_data *data) +{ + if (data->kind == nct6775) + nct6775_update_fan_div(data); +} + +static void nct6775_init_fan_div(struct nct6775_data *data) +{ + int i; + + nct6775_update_fan_div_common(data); + /* + * For all fans, start with highest divider value if the divider + * register is not initialized. This ensures that we get a + * reading from the fan count register, even if it is not optimal. + * We'll compute a better divider later on. + */ + for (i = 0; i < ARRAY_SIZE(data->fan_div); i++) { + if (!(data->has_fan & (1 << i))) + continue; + if (data->fan_div[i] == 0) { + data->fan_div[i] = 7; + nct6775_write_fan_div_common(data, i); + } + } +} + +static void nct6775_init_fan_common(struct device *dev, + struct nct6775_data *data) +{ + int i; + u8 reg; + + if (data->has_fan_div) + nct6775_init_fan_div(data); + + /* + * If fan_min is not set (0), set it to 0xff to disable it. This + * prevents the unnecessary warning when fanX_min is reported as 0. + */ + for (i = 0; i < ARRAY_SIZE(data->fan_min); i++) { + if (data->has_fan_min & (1 << i)) { + reg = nct6775_read_value(data, data->REG_FAN_MIN[i]); + if (!reg) + nct6775_write_value(data, data->REG_FAN_MIN[i], + data->has_fan_div ? 0xff + : 0xff1f); + } + } +} + +static void nct6775_select_fan_div(struct device *dev, + struct nct6775_data *data, int nr, u16 reg) +{ + u8 fan_div = data->fan_div[nr]; + u16 fan_min; + + if (!data->has_fan_div) + return; + + /* + * If we failed to measure the fan speed, or the reported value is not + * in the optimal range, and the clock divider can be modified, + * let's try that for next time. + */ + if (reg == 0x00 && fan_div < 0x07) + fan_div++; + else if (reg != 0x00 && reg < 0x30 && fan_div > 0) + fan_div--; + + if (fan_div != data->fan_div[nr]) { + dev_dbg(dev, "Modifying fan%d clock divider from %u to %u\n", + nr + 1, div_from_reg(data->fan_div[nr]), + div_from_reg(fan_div)); + + /* Preserve min limit if possible */ + if (data->has_fan_min & (1 << nr)) { + fan_min = data->fan_min[nr]; + if (fan_div > data->fan_div[nr]) { + if (fan_min != 255 && fan_min > 1) + fan_min >>= 1; + } else { + if (fan_min != 255) { + fan_min <<= 1; + if (fan_min > 254) + fan_min = 254; + } + } + if (fan_min != data->fan_min[nr]) { + data->fan_min[nr] = fan_min; + nct6775_write_value(data, data->REG_FAN_MIN[nr], + fan_min); + } + } + data->fan_div[nr] = fan_div; + nct6775_write_fan_div_common(data, nr); + } +} + +static void nct6775_update_pwm(struct device *dev) +{ + struct nct6775_data *data = dev_get_drvdata(dev); + int i, j; + int fanmodecfg, reg; + bool duty_is_dc; + + for (i = 0; i < data->pwm_num; i++) { + if (!(data->has_pwm & (1 << i))) + continue; + + duty_is_dc = data->REG_PWM_MODE[i] && + (nct6775_read_value(data, data->REG_PWM_MODE[i]) + & data->PWM_MODE_MASK[i]); + data->pwm_mode[i] = duty_is_dc; + + fanmodecfg = nct6775_read_value(data, data->REG_FAN_MODE[i]); + for (j = 0; j < ARRAY_SIZE(data->REG_PWM); j++) { + if (data->REG_PWM[j] && data->REG_PWM[j][i]) { + data->pwm[j][i] + = nct6775_read_value(data, + data->REG_PWM[j][i]); + } + } + + data->pwm_enable[i] = reg_to_pwm_enable(data->pwm[0][i], + (fanmodecfg >> 4) & 7); + + if (!data->temp_tolerance[0][i] || + data->pwm_enable[i] != speed_cruise) + data->temp_tolerance[0][i] = fanmodecfg & 0x0f; + if (!data->target_speed_tolerance[i] || + data->pwm_enable[i] == speed_cruise) { + u8 t = fanmodecfg & 0x0f; + if (data->REG_TOLERANCE_H) { + t |= (nct6775_read_value(data, + data->REG_TOLERANCE_H[i]) & 0x70) >> 1; + } + data->target_speed_tolerance[i] = t; + } + + data->temp_tolerance[1][i] = + nct6775_read_value(data, + data->REG_CRITICAL_TEMP_TOLERANCE[i]); + + reg = nct6775_read_value(data, data->REG_TEMP_SEL[i]); + data->pwm_temp_sel[i] = reg & 0x1f; + /* If fan can stop, report floor as 0 */ + if (reg & 0x80) + data->pwm[2][i] = 0; + + reg = nct6775_read_value(data, data->REG_WEIGHT_TEMP_SEL[i]); + data->pwm_weight_temp_sel[i] = reg & 0x1f; + /* If weight is disabled, report weight source as 0 */ + if (j == 1 && !(reg & 0x80)) + data->pwm_weight_temp_sel[i] = 0; + + /* Weight temp data */ + for (j = 0; j < ARRAY_SIZE(data->weight_temp); j++) { + data->weight_temp[j][i] + = nct6775_read_value(data, + data->REG_WEIGHT_TEMP[j][i]); + } + } +} + +static void nct6775_update_pwm_limits(struct device *dev) +{ + struct nct6775_data *data = dev_get_drvdata(dev); + int i, j; + u8 reg; + u16 reg_t; + + for (i = 0; i < data->pwm_num; i++) { + if (!(data->has_pwm & (1 << i))) + continue; + + for (j = 0; j < ARRAY_SIZE(data->fan_time); j++) { + data->fan_time[j][i] = + nct6775_read_value(data, data->REG_FAN_TIME[j][i]); + } + + reg_t = nct6775_read_value(data, data->REG_TARGET[i]); + /* Update only in matching mode or if never updated */ + if (!data->target_temp[i] || + data->pwm_enable[i] == thermal_cruise) + data->target_temp[i] = reg_t & data->target_temp_mask; + if (!data->target_speed[i] || + data->pwm_enable[i] == speed_cruise) { + if (data->REG_TOLERANCE_H) { + reg_t |= (nct6775_read_value(data, + data->REG_TOLERANCE_H[i]) & 0x0f) << 8; + } + data->target_speed[i] = reg_t; + } + + for (j = 0; j < data->auto_pwm_num; j++) { + data->auto_pwm[i][j] = + nct6775_read_value(data, + NCT6775_AUTO_PWM(data, i, j)); + data->auto_temp[i][j] = + nct6775_read_value(data, + NCT6775_AUTO_TEMP(data, i, j)); + } + + /* critical auto_pwm temperature data */ + data->auto_temp[i][data->auto_pwm_num] = + nct6775_read_value(data, data->REG_CRITICAL_TEMP[i]); + + switch (data->kind) { + case nct6775: + reg = nct6775_read_value(data, + NCT6775_REG_CRITICAL_ENAB[i]); + data->auto_pwm[i][data->auto_pwm_num] = + (reg & 0x02) ? 0xff : 0x00; + break; + case nct6776: + data->auto_pwm[i][data->auto_pwm_num] = 0xff; + break; + case nct6779: + reg = nct6775_read_value(data, + NCT6779_REG_CRITICAL_PWM_ENABLE[i]); + if (reg & 1) + data->auto_pwm[i][data->auto_pwm_num] = + nct6775_read_value(data, + NCT6779_REG_CRITICAL_PWM[i]); + else + data->auto_pwm[i][data->auto_pwm_num] = 0xff; + break; + } + } +} + +static struct nct6775_data *nct6775_update_device(struct device *dev) +{ + struct nct6775_data *data = dev_get_drvdata(dev); + int i, j; + + mutex_lock(&data->update_lock); + + if (time_after(jiffies, data->last_updated + HZ + HZ / 2) + || !data->valid) { + /* Fan clock dividers */ + nct6775_update_fan_div_common(data); + + /* Measured voltages and limits */ + for (i = 0; i < data->in_num; i++) { + if (!(data->have_in & (1 << i))) + continue; + + data->in[i][0] = nct6775_read_value(data, + data->REG_VIN[i]); + data->in[i][1] = nct6775_read_value(data, + data->REG_IN_MINMAX[0][i]); + data->in[i][2] = nct6775_read_value(data, + data->REG_IN_MINMAX[1][i]); + } + + /* Measured fan speeds and limits */ + for (i = 0; i < ARRAY_SIZE(data->rpm); i++) { + u16 reg; + + if (!(data->has_fan & (1 << i))) + continue; + + reg = nct6775_read_value(data, data->REG_FAN[i]); + data->rpm[i] = data->fan_from_reg(reg, + data->fan_div[i]); + + if (data->has_fan_min & (1 << i)) + data->fan_min[i] = nct6775_read_value(data, + data->REG_FAN_MIN[i]); + data->fan_pulses[i] = + nct6775_read_value(data, data->REG_FAN_PULSES[i]); + + nct6775_select_fan_div(dev, data, i, reg); + } + + nct6775_update_pwm(dev); + nct6775_update_pwm_limits(dev); + + /* Measured temperatures and limits */ + for (i = 0; i < NUM_TEMP; i++) { + if (!(data->have_temp & (1 << i))) + continue; + for (j = 0; j < ARRAY_SIZE(data->reg_temp); j++) { + if (data->reg_temp[j][i]) + data->temp[j][i] + = nct6775_read_temp(data, + data->reg_temp[j][i]); + } + if (!(data->have_temp_fixed & (1 << i))) + continue; + data->temp_offset[i] + = nct6775_read_value(data, data->REG_TEMP_OFFSET[i]); + } + + data->alarms = 0; + for (i = 0; i < NUM_REG_ALARM; i++) { + u8 alarm; + if (!data->REG_ALARM[i]) + continue; + alarm = nct6775_read_value(data, data->REG_ALARM[i]); + data->alarms |= ((u64)alarm) << (i << 3); + } + + data->last_updated = jiffies; + data->valid = true; + } + + mutex_unlock(&data->update_lock); + return data; +} + +/* + * Sysfs callback functions + */ +static ssize_t +show_in_reg(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct nct6775_data *data = nct6775_update_device(dev); + struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr); + int nr = sattr->nr; + int index = sattr->index; + return sprintf(buf, "%ld\n", in_from_reg(data->in[nr][index], nr)); +} + +static ssize_t +store_in_reg(struct device *dev, struct device_attribute *attr, const char *buf, + size_t count) +{ + struct nct6775_data *data = dev_get_drvdata(dev); + struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr); + int nr = sattr->nr; + int index = sattr->index; + unsigned long val; + int err = kstrtoul(buf, 10, &val); + if (err < 0) + return err; + mutex_lock(&data->update_lock); + data->in[nr][index] = in_to_reg(val, nr); + nct6775_write_value(data, data->REG_IN_MINMAX[index - 1][nr], + data->in[nr][index]); + mutex_unlock(&data->update_lock); + return count; +} + +static ssize_t +show_alarm(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct nct6775_data *data = nct6775_update_device(dev); + struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr); + int nr = data->ALARM_BITS[sattr->index]; + return sprintf(buf, "%u\n", + (unsigned int)((data->alarms >> nr) & 0x01)); +} + +static SENSOR_DEVICE_ATTR_2(in0_input, S_IRUGO, show_in_reg, NULL, 0, 0); +static SENSOR_DEVICE_ATTR_2(in1_input, S_IRUGO, show_in_reg, NULL, 1, 0); +static SENSOR_DEVICE_ATTR_2(in2_input, S_IRUGO, show_in_reg, NULL, 2, 0); +static SENSOR_DEVICE_ATTR_2(in3_input, S_IRUGO, show_in_reg, NULL, 3, 0); +static SENSOR_DEVICE_ATTR_2(in4_input, S_IRUGO, show_in_reg, NULL, 4, 0); +static SENSOR_DEVICE_ATTR_2(in5_input, S_IRUGO, show_in_reg, NULL, 5, 0); +static SENSOR_DEVICE_ATTR_2(in6_input, S_IRUGO, show_in_reg, NULL, 6, 0); +static SENSOR_DEVICE_ATTR_2(in7_input, S_IRUGO, show_in_reg, NULL, 7, 0); +static SENSOR_DEVICE_ATTR_2(in8_input, S_IRUGO, show_in_reg, NULL, 8, 0); +static SENSOR_DEVICE_ATTR_2(in9_input, S_IRUGO, show_in_reg, NULL, 9, 0); +static SENSOR_DEVICE_ATTR_2(in10_input, S_IRUGO, show_in_reg, NULL, 10, 0); +static SENSOR_DEVICE_ATTR_2(in11_input, S_IRUGO, show_in_reg, NULL, 11, 0); +static SENSOR_DEVICE_ATTR_2(in12_input, S_IRUGO, show_in_reg, NULL, 12, 0); +static SENSOR_DEVICE_ATTR_2(in13_input, S_IRUGO, show_in_reg, NULL, 13, 0); +static SENSOR_DEVICE_ATTR_2(in14_input, S_IRUGO, show_in_reg, NULL, 14, 0); + +static SENSOR_DEVICE_ATTR(in0_alarm, S_IRUGO, show_alarm, NULL, 0); +static SENSOR_DEVICE_ATTR(in1_alarm, S_IRUGO, show_alarm, NULL, 1); +static SENSOR_DEVICE_ATTR(in2_alarm, S_IRUGO, show_alarm, NULL, 2); +static SENSOR_DEVICE_ATTR(in3_alarm, S_IRUGO, show_alarm, NULL, 3); +static SENSOR_DEVICE_ATTR(in4_alarm, S_IRUGO, show_alarm, NULL, 4); +static SENSOR_DEVICE_ATTR(in5_alarm, S_IRUGO, show_alarm, NULL, 5); +static SENSOR_DEVICE_ATTR(in6_alarm, S_IRUGO, show_alarm, NULL, 6); +static SENSOR_DEVICE_ATTR(in7_alarm, S_IRUGO, show_alarm, NULL, 7); +static SENSOR_DEVICE_ATTR(in8_alarm, S_IRUGO, show_alarm, NULL, 8); +static SENSOR_DEVICE_ATTR(in9_alarm, S_IRUGO, show_alarm, NULL, 9); +static SENSOR_DEVICE_ATTR(in10_alarm, S_IRUGO, show_alarm, NULL, 10); +static SENSOR_DEVICE_ATTR(in11_alarm, S_IRUGO, show_alarm, NULL, 11); +static SENSOR_DEVICE_ATTR(in12_alarm, S_IRUGO, show_alarm, NULL, 12); +static SENSOR_DEVICE_ATTR(in13_alarm, S_IRUGO, show_alarm, NULL, 13); +static SENSOR_DEVICE_ATTR(in14_alarm, S_IRUGO, show_alarm, NULL, 14); + +static SENSOR_DEVICE_ATTR_2(in0_min, S_IWUSR | S_IRUGO, show_in_reg, + store_in_reg, 0, 1); +static SENSOR_DEVICE_ATTR_2(in1_min, S_IWUSR | S_IRUGO, show_in_reg, + store_in_reg, 1, 1); +static SENSOR_DEVICE_ATTR_2(in2_min, S_IWUSR | S_IRUGO, show_in_reg, + store_in_reg, 2, 1); +static SENSOR_DEVICE_ATTR_2(in3_min, S_IWUSR | S_IRUGO, show_in_reg, + store_in_reg, 3, 1); +static SENSOR_DEVICE_ATTR_2(in4_min, S_IWUSR | S_IRUGO, show_in_reg, + store_in_reg, 4, 1); +static SENSOR_DEVICE_ATTR_2(in5_min, S_IWUSR | S_IRUGO, show_in_reg, + store_in_reg, 5, 1); +static SENSOR_DEVICE_ATTR_2(in6_min, S_IWUSR | S_IRUGO, show_in_reg, + store_in_reg, 6, 1); +static SENSOR_DEVICE_ATTR_2(in7_min, S_IWUSR | S_IRUGO, show_in_reg, + store_in_reg, 7, 1); +static SENSOR_DEVICE_ATTR_2(in8_min, S_IWUSR | S_IRUGO, show_in_reg, + store_in_reg, 8, 1); +static SENSOR_DEVICE_ATTR_2(in9_min, S_IWUSR | S_IRUGO, show_in_reg, + store_in_reg, 9, 1); +static SENSOR_DEVICE_ATTR_2(in10_min, S_IWUSR | S_IRUGO, show_in_reg, + store_in_reg, 10, 1); +static SENSOR_DEVICE_ATTR_2(in11_min, S_IWUSR | S_IRUGO, show_in_reg, + store_in_reg, 11, 1); +static SENSOR_DEVICE_ATTR_2(in12_min, S_IWUSR | S_IRUGO, show_in_reg, + store_in_reg, 12, 1); +static SENSOR_DEVICE_ATTR_2(in13_min, S_IWUSR | S_IRUGO, show_in_reg, + store_in_reg, 13, 1); +static SENSOR_DEVICE_ATTR_2(in14_min, S_IWUSR | S_IRUGO, show_in_reg, + store_in_reg, 14, 1); + +static SENSOR_DEVICE_ATTR_2(in0_max, S_IWUSR | S_IRUGO, show_in_reg, + store_in_reg, 0, 2); +static SENSOR_DEVICE_ATTR_2(in1_max, S_IWUSR | S_IRUGO, show_in_reg, + store_in_reg, 1, 2); +static SENSOR_DEVICE_ATTR_2(in2_max, S_IWUSR | S_IRUGO, show_in_reg, + store_in_reg, 2, 2); +static SENSOR_DEVICE_ATTR_2(in3_max, S_IWUSR | S_IRUGO, show_in_reg, + store_in_reg, 3, 2); +static SENSOR_DEVICE_ATTR_2(in4_max, S_IWUSR | S_IRUGO, show_in_reg, + store_in_reg, 4, 2); +static SENSOR_DEVICE_ATTR_2(in5_max, S_IWUSR | S_IRUGO, show_in_reg, + store_in_reg, 5, 2); +static SENSOR_DEVICE_ATTR_2(in6_max, S_IWUSR | S_IRUGO, show_in_reg, + store_in_reg, 6, 2); +static SENSOR_DEVICE_ATTR_2(in7_max, S_IWUSR | S_IRUGO, show_in_reg, + store_in_reg, 7, 2); +static SENSOR_DEVICE_ATTR_2(in8_max, S_IWUSR | S_IRUGO, show_in_reg, + store_in_reg, 8, 2); +static SENSOR_DEVICE_ATTR_2(in9_max, S_IWUSR | S_IRUGO, show_in_reg, + store_in_reg, 9, 2); +static SENSOR_DEVICE_ATTR_2(in10_max, S_IWUSR | S_IRUGO, show_in_reg, + store_in_reg, 10, 2); +static SENSOR_DEVICE_ATTR_2(in11_max, S_IWUSR | S_IRUGO, show_in_reg, + store_in_reg, 11, 2); +static SENSOR_DEVICE_ATTR_2(in12_max, S_IWUSR | S_IRUGO, show_in_reg, + store_in_reg, 12, 2); +static SENSOR_DEVICE_ATTR_2(in13_max, S_IWUSR | S_IRUGO, show_in_reg, + store_in_reg, 13, 2); +static SENSOR_DEVICE_ATTR_2(in14_max, S_IWUSR | S_IRUGO, show_in_reg, + store_in_reg, 14, 2); + +static struct attribute *nct6775_attributes_in[15][5] = { + { + &sensor_dev_attr_in0_input.dev_attr.attr, + &sensor_dev_attr_in0_min.dev_attr.attr, + &sensor_dev_attr_in0_max.dev_attr.attr, + &sensor_dev_attr_in0_alarm.dev_attr.attr, + NULL + }, + { + &sensor_dev_attr_in1_input.dev_attr.attr, + &sensor_dev_attr_in1_min.dev_attr.attr, + &sensor_dev_attr_in1_max.dev_attr.attr, + &sensor_dev_attr_in1_alarm.dev_attr.attr, + NULL + }, + { + &sensor_dev_attr_in2_input.dev_attr.attr, + &sensor_dev_attr_in2_min.dev_attr.attr, + &sensor_dev_attr_in2_max.dev_attr.attr, + &sensor_dev_attr_in2_alarm.dev_attr.attr, + NULL + }, + { + &sensor_dev_attr_in3_input.dev_attr.attr, + &sensor_dev_attr_in3_min.dev_attr.attr, + &sensor_dev_attr_in3_max.dev_attr.attr, + &sensor_dev_attr_in3_alarm.dev_attr.attr, + NULL + }, + { + &sensor_dev_attr_in4_input.dev_attr.attr, + &sensor_dev_attr_in4_min.dev_attr.attr, + &sensor_dev_attr_in4_max.dev_attr.attr, + &sensor_dev_attr_in4_alarm.dev_attr.attr, + NULL + }, + { + &sensor_dev_attr_in5_input.dev_attr.attr, + &sensor_dev_attr_in5_min.dev_attr.attr, + &sensor_dev_attr_in5_max.dev_attr.attr, + &sensor_dev_attr_in5_alarm.dev_attr.attr, + NULL + }, + { + &sensor_dev_attr_in6_input.dev_attr.attr, + &sensor_dev_attr_in6_min.dev_attr.attr, + &sensor_dev_attr_in6_max.dev_attr.attr, + &sensor_dev_attr_in6_alarm.dev_attr.attr, + NULL + }, + { + &sensor_dev_attr_in7_input.dev_attr.attr, + &sensor_dev_attr_in7_min.dev_attr.attr, + &sensor_dev_attr_in7_max.dev_attr.attr, + &sensor_dev_attr_in7_alarm.dev_attr.attr, + NULL + }, + { + &sensor_dev_attr_in8_input.dev_attr.attr, + &sensor_dev_attr_in8_min.dev_attr.attr, + &sensor_dev_attr_in8_max.dev_attr.attr, + &sensor_dev_attr_in8_alarm.dev_attr.attr, + NULL + }, + { + &sensor_dev_attr_in9_input.dev_attr.attr, + &sensor_dev_attr_in9_min.dev_attr.attr, + &sensor_dev_attr_in9_max.dev_attr.attr, + &sensor_dev_attr_in9_alarm.dev_attr.attr, + NULL + }, + { + &sensor_dev_attr_in10_input.dev_attr.attr, + &sensor_dev_attr_in10_min.dev_attr.attr, + &sensor_dev_attr_in10_max.dev_attr.attr, + &sensor_dev_attr_in10_alarm.dev_attr.attr, + NULL + }, + { + &sensor_dev_attr_in11_input.dev_attr.attr, + &sensor_dev_attr_in11_min.dev_attr.attr, + &sensor_dev_attr_in11_max.dev_attr.attr, + &sensor_dev_attr_in11_alarm.dev_attr.attr, + NULL + }, + { + &sensor_dev_attr_in12_input.dev_attr.attr, + &sensor_dev_attr_in12_min.dev_attr.attr, + &sensor_dev_attr_in12_max.dev_attr.attr, + &sensor_dev_attr_in12_alarm.dev_attr.attr, + NULL + }, + { + &sensor_dev_attr_in13_input.dev_attr.attr, + &sensor_dev_attr_in13_min.dev_attr.attr, + &sensor_dev_attr_in13_max.dev_attr.attr, + &sensor_dev_attr_in13_alarm.dev_attr.attr, + NULL + }, + { + &sensor_dev_attr_in14_input.dev_attr.attr, + &sensor_dev_attr_in14_min.dev_attr.attr, + &sensor_dev_attr_in14_max.dev_attr.attr, + &sensor_dev_attr_in14_alarm.dev_attr.attr, + NULL + }, +}; + +static const struct attribute_group nct6775_group_in[15] = { + { .attrs = nct6775_attributes_in[0] }, + { .attrs = nct6775_attributes_in[1] }, + { .attrs = nct6775_attributes_in[2] }, + { .attrs = nct6775_attributes_in[3] }, + { .attrs = nct6775_attributes_in[4] }, + { .attrs = nct6775_attributes_in[5] }, + { .attrs = nct6775_attributes_in[6] }, + { .attrs = nct6775_attributes_in[7] }, + { .attrs = nct6775_attributes_in[8] }, + { .attrs = nct6775_attributes_in[9] }, + { .attrs = nct6775_attributes_in[10] }, + { .attrs = nct6775_attributes_in[11] }, + { .attrs = nct6775_attributes_in[12] }, + { .attrs = nct6775_attributes_in[13] }, + { .attrs = nct6775_attributes_in[14] }, +}; + +static ssize_t +show_fan(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct nct6775_data *data = nct6775_update_device(dev); + struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr); + int nr = sattr->index; + return sprintf(buf, "%d\n", data->rpm[nr]); +} + +static ssize_t +show_fan_min(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct nct6775_data *data = nct6775_update_device(dev); + struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr); + int nr = sattr->index; + return sprintf(buf, "%d\n", + data->fan_from_reg_min(data->fan_min[nr], + data->fan_div[nr])); +} + +static ssize_t +show_fan_div(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct nct6775_data *data = nct6775_update_device(dev); + struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr); + int nr = sattr->index; + return sprintf(buf, "%u\n", div_from_reg(data->fan_div[nr])); +} + +static ssize_t +store_fan_min(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct nct6775_data *data = dev_get_drvdata(dev); + struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr); + int nr = sattr->index; + unsigned long val; + int err; + unsigned int reg; + u8 new_div; + + err = kstrtoul(buf, 10, &val); + if (err < 0) + return err; + + mutex_lock(&data->update_lock); + if (!data->has_fan_div) { + /* NCT6776F or NCT6779D; we know this is a 13 bit register */ + if (!val) { + val = 0xff1f; + } else { + if (val > 1350000U) + val = 135000U; + val = 1350000U / val; + val = (val & 0x1f) | ((val << 3) & 0xff00); + } + data->fan_min[nr] = val; + goto write_min; /* Leave fan divider alone */ + } + if (!val) { + /* No min limit, alarm disabled */ + data->fan_min[nr] = 255; + new_div = data->fan_div[nr]; /* No change */ + dev_info(dev, "fan%u low limit and alarm disabled\n", nr + 1); + goto write_div; + } + reg = 1350000U / val; + if (reg >= 128 * 255) { + /* + * Speed below this value cannot possibly be represented, + * even with the highest divider (128) + */ + data->fan_min[nr] = 254; + new_div = 7; /* 128 == (1 << 7) */ + dev_warn(dev, + "fan%u low limit %lu below minimum %u, set to minimum\n", + nr + 1, val, data->fan_from_reg_min(254, 7)); + } else if (!reg) { + /* + * Speed above this value cannot possibly be represented, + * even with the lowest divider (1) + */ + data->fan_min[nr] = 1; + new_div = 0; /* 1 == (1 << 0) */ + dev_warn(dev, + "fan%u low limit %lu above maximum %u, set to maximum\n", + nr + 1, val, data->fan_from_reg_min(1, 0)); + } else { + /* + * Automatically pick the best divider, i.e. the one such + * that the min limit will correspond to a register value + * in the 96..192 range + */ + new_div = 0; + while (reg > 192 && new_div < 7) { + reg >>= 1; + new_div++; + } + data->fan_min[nr] = reg; + } + +write_div: + /* + * Write both the fan clock divider (if it changed) and the new + * fan min (unconditionally) + */ + if (new_div != data->fan_div[nr]) { + dev_dbg(dev, "fan%u clock divider changed from %u to %u\n", + nr + 1, div_from_reg(data->fan_div[nr]), + div_from_reg(new_div)); + data->fan_div[nr] = new_div; + nct6775_write_fan_div_common(data, nr); + /* Give the chip time to sample a new speed value */ + data->last_updated = jiffies; + } + +write_min: + nct6775_write_value(data, data->REG_FAN_MIN[nr], data->fan_min[nr]); + mutex_unlock(&data->update_lock); + + return count; +} + +static ssize_t +show_fan_pulses(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct nct6775_data *data = nct6775_update_device(dev); + struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr); + int p = data->fan_pulses[sattr->index]; + + return sprintf(buf, "%d\n", p ? : 4); +} + +static ssize_t +store_fan_pulses(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct nct6775_data *data = dev_get_drvdata(dev); + struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr); + int nr = sattr->index; + unsigned long val; + int err; + + err = kstrtoul(buf, 10, &val); + if (err < 0) + return err; + + if (val > 4) + return -EINVAL; + + mutex_lock(&data->update_lock); + data->fan_pulses[nr] = val & 3; + nct6775_write_value(data, data->REG_FAN_PULSES[nr], val & 3); + mutex_unlock(&data->update_lock); + + return count; +} + +static struct sensor_device_attribute sda_fan_input[] = { + SENSOR_ATTR(fan1_input, S_IRUGO, show_fan, NULL, 0), + SENSOR_ATTR(fan2_input, S_IRUGO, show_fan, NULL, 1), + SENSOR_ATTR(fan3_input, S_IRUGO, show_fan, NULL, 2), + SENSOR_ATTR(fan4_input, S_IRUGO, show_fan, NULL, 3), + SENSOR_ATTR(fan5_input, S_IRUGO, show_fan, NULL, 4), +}; + +static struct sensor_device_attribute sda_fan_alarm[] = { + SENSOR_ATTR(fan1_alarm, S_IRUGO, show_alarm, NULL, FAN_ALARM_BASE), + SENSOR_ATTR(fan2_alarm, S_IRUGO, show_alarm, NULL, FAN_ALARM_BASE + 1), + SENSOR_ATTR(fan3_alarm, S_IRUGO, show_alarm, NULL, FAN_ALARM_BASE + 2), + SENSOR_ATTR(fan4_alarm, S_IRUGO, show_alarm, NULL, FAN_ALARM_BASE + 3), + SENSOR_ATTR(fan5_alarm, S_IRUGO, show_alarm, NULL, FAN_ALARM_BASE + 4), +}; + +static struct sensor_device_attribute sda_fan_min[] = { + SENSOR_ATTR(fan1_min, S_IWUSR | S_IRUGO, show_fan_min, + store_fan_min, 0), + SENSOR_ATTR(fan2_min, S_IWUSR | S_IRUGO, show_fan_min, + store_fan_min, 1), + SENSOR_ATTR(fan3_min, S_IWUSR | S_IRUGO, show_fan_min, + store_fan_min, 2), + SENSOR_ATTR(fan4_min, S_IWUSR | S_IRUGO, show_fan_min, + store_fan_min, 3), + SENSOR_ATTR(fan5_min, S_IWUSR | S_IRUGO, show_fan_min, + store_fan_min, 4), +}; + +static struct sensor_device_attribute sda_fan_pulses[] = { + SENSOR_ATTR(fan1_pulses, S_IWUSR | S_IRUGO, show_fan_pulses, + store_fan_pulses, 0), + SENSOR_ATTR(fan2_pulses, S_IWUSR | S_IRUGO, show_fan_pulses, + store_fan_pulses, 1), + SENSOR_ATTR(fan3_pulses, S_IWUSR | S_IRUGO, show_fan_pulses, + store_fan_pulses, 2), + SENSOR_ATTR(fan4_pulses, S_IWUSR | S_IRUGO, show_fan_pulses, + store_fan_pulses, 3), + SENSOR_ATTR(fan5_pulses, S_IWUSR | S_IRUGO, show_fan_pulses, + store_fan_pulses, 4), +}; + +static struct sensor_device_attribute sda_fan_div[] = { + SENSOR_ATTR(fan1_div, S_IRUGO, show_fan_div, NULL, 0), + SENSOR_ATTR(fan2_div, S_IRUGO, show_fan_div, NULL, 1), + SENSOR_ATTR(fan3_div, S_IRUGO, show_fan_div, NULL, 2), + SENSOR_ATTR(fan4_div, S_IRUGO, show_fan_div, NULL, 3), + SENSOR_ATTR(fan5_div, S_IRUGO, show_fan_div, NULL, 4), +}; + +static ssize_t +show_temp_label(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct nct6775_data *data = nct6775_update_device(dev); + struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr); + int nr = sattr->index; + return sprintf(buf, "%s\n", data->temp_label[data->temp_src[nr]]); +} + +static ssize_t +show_temp(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct nct6775_data *data = nct6775_update_device(dev); + struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr); + int nr = sattr->nr; + int index = sattr->index; + + return sprintf(buf, "%d\n", LM75_TEMP_FROM_REG(data->temp[index][nr])); +} + +static ssize_t +store_temp(struct device *dev, struct device_attribute *attr, const char *buf, + size_t count) +{ + struct nct6775_data *data = dev_get_drvdata(dev); + struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr); + int nr = sattr->nr; + int index = sattr->index; + int err; + long val; + + err = kstrtol(buf, 10, &val); + if (err < 0) + return err; + + mutex_lock(&data->update_lock); + data->temp[index][nr] = LM75_TEMP_TO_REG(val); + nct6775_write_temp(data, data->reg_temp[index][nr], + data->temp[index][nr]); + mutex_unlock(&data->update_lock); + return count; +} + +static ssize_t +show_temp_offset(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct nct6775_data *data = nct6775_update_device(dev); + struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr); + + return sprintf(buf, "%d\n", data->temp_offset[sattr->index] * 1000); +} + +static ssize_t +store_temp_offset(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct nct6775_data *data = dev_get_drvdata(dev); + struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr); + int nr = sattr->index; + long val; + int err; + + err = kstrtol(buf, 10, &val); + if (err < 0) + return err; + + val = clamp_val(DIV_ROUND_CLOSEST(val, 1000), -128, 127); + + mutex_lock(&data->update_lock); + data->temp_offset[nr] = val; + nct6775_write_value(data, data->REG_TEMP_OFFSET[nr], val); + mutex_unlock(&data->update_lock); + + return count; +} + +static ssize_t +show_temp_type(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct nct6775_data *data = nct6775_update_device(dev); + struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr); + int nr = sattr->index; + return sprintf(buf, "%d\n", (int)data->temp_type[nr]); +} + +static ssize_t +store_temp_type(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct nct6775_data *data = nct6775_update_device(dev); + struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr); + int nr = sattr->index; + unsigned long val; + int err; + u8 vbat, diode, bit; + + err = kstrtoul(buf, 10, &val); + if (err < 0) + return err; + + if (val != 1 && val != 3 && val != 4) + return -EINVAL; + + mutex_lock(&data->update_lock); + + data->temp_type[nr] = val; + vbat = nct6775_read_value(data, data->REG_VBAT) & ~(0x02 << nr); + diode = nct6775_read_value(data, data->REG_DIODE) & ~(0x02 << nr); + bit = 0x02 << nr; + switch (val) { + case 1: /* CPU diode (diode, current mode) */ + vbat |= bit; + diode |= bit; + break; + case 3: /* diode, voltage mode */ + vbat |= bit; + break; + case 4: /* thermistor */ + break; + } + nct6775_write_value(data, data->REG_VBAT, vbat); + nct6775_write_value(data, data->REG_DIODE, diode); + + mutex_unlock(&data->update_lock); + return count; +} + +static struct sensor_device_attribute_2 sda_temp_input[] = { + SENSOR_ATTR_2(temp1_input, S_IRUGO, show_temp, NULL, 0, 0), + SENSOR_ATTR_2(temp2_input, S_IRUGO, show_temp, NULL, 1, 0), + SENSOR_ATTR_2(temp3_input, S_IRUGO, show_temp, NULL, 2, 0), + SENSOR_ATTR_2(temp4_input, S_IRUGO, show_temp, NULL, 3, 0), + SENSOR_ATTR_2(temp5_input, S_IRUGO, show_temp, NULL, 4, 0), + SENSOR_ATTR_2(temp6_input, S_IRUGO, show_temp, NULL, 5, 0), + SENSOR_ATTR_2(temp7_input, S_IRUGO, show_temp, NULL, 6, 0), + SENSOR_ATTR_2(temp8_input, S_IRUGO, show_temp, NULL, 7, 0), + SENSOR_ATTR_2(temp9_input, S_IRUGO, show_temp, NULL, 8, 0), + SENSOR_ATTR_2(temp10_input, S_IRUGO, show_temp, NULL, 9, 0), +}; + +static struct sensor_device_attribute sda_temp_label[] = { + SENSOR_ATTR(temp1_label, S_IRUGO, show_temp_label, NULL, 0), + SENSOR_ATTR(temp2_label, S_IRUGO, show_temp_label, NULL, 1), + SENSOR_ATTR(temp3_label, S_IRUGO, show_temp_label, NULL, 2), + SENSOR_ATTR(temp4_label, S_IRUGO, show_temp_label, NULL, 3), + SENSOR_ATTR(temp5_label, S_IRUGO, show_temp_label, NULL, 4), + SENSOR_ATTR(temp6_label, S_IRUGO, show_temp_label, NULL, 5), + SENSOR_ATTR(temp7_label, S_IRUGO, show_temp_label, NULL, 6), + SENSOR_ATTR(temp8_label, S_IRUGO, show_temp_label, NULL, 7), + SENSOR_ATTR(temp9_label, S_IRUGO, show_temp_label, NULL, 8), + SENSOR_ATTR(temp10_label, S_IRUGO, show_temp_label, NULL, 9), +}; + +static struct sensor_device_attribute_2 sda_temp_max[] = { + SENSOR_ATTR_2(temp1_max, S_IRUGO | S_IWUSR, show_temp, store_temp, + 0, 1), + SENSOR_ATTR_2(temp2_max, S_IRUGO | S_IWUSR, show_temp, store_temp, + 1, 1), + SENSOR_ATTR_2(temp3_max, S_IRUGO | S_IWUSR, show_temp, store_temp, + 2, 1), + SENSOR_ATTR_2(temp4_max, S_IRUGO | S_IWUSR, show_temp, store_temp, + 3, 1), + SENSOR_ATTR_2(temp5_max, S_IRUGO | S_IWUSR, show_temp, store_temp, + 4, 1), + SENSOR_ATTR_2(temp6_max, S_IRUGO | S_IWUSR, show_temp, store_temp, + 5, 1), + SENSOR_ATTR_2(temp7_max, S_IRUGO | S_IWUSR, show_temp, store_temp, + 6, 1), + SENSOR_ATTR_2(temp8_max, S_IRUGO | S_IWUSR, show_temp, store_temp, + 7, 1), + SENSOR_ATTR_2(temp9_max, S_IRUGO | S_IWUSR, show_temp, store_temp, + 8, 1), + SENSOR_ATTR_2(temp10_max, S_IRUGO | S_IWUSR, show_temp, store_temp, + 9, 1), +}; + +static struct sensor_device_attribute_2 sda_temp_max_hyst[] = { + SENSOR_ATTR_2(temp1_max_hyst, S_IRUGO | S_IWUSR, show_temp, store_temp, + 0, 2), + SENSOR_ATTR_2(temp2_max_hyst, S_IRUGO | S_IWUSR, show_temp, store_temp, + 1, 2), + SENSOR_ATTR_2(temp3_max_hyst, S_IRUGO | S_IWUSR, show_temp, store_temp, + 2, 2), + SENSOR_ATTR_2(temp4_max_hyst, S_IRUGO | S_IWUSR, show_temp, store_temp, + 3, 2), + SENSOR_ATTR_2(temp5_max_hyst, S_IRUGO | S_IWUSR, show_temp, store_temp, + 4, 2), + SENSOR_ATTR_2(temp6_max_hyst, S_IRUGO | S_IWUSR, show_temp, store_temp, + 5, 2), + SENSOR_ATTR_2(temp7_max_hyst, S_IRUGO | S_IWUSR, show_temp, store_temp, + 6, 2), + SENSOR_ATTR_2(temp8_max_hyst, S_IRUGO | S_IWUSR, show_temp, store_temp, + 7, 2), + SENSOR_ATTR_2(temp9_max_hyst, S_IRUGO | S_IWUSR, show_temp, store_temp, + 8, 2), + SENSOR_ATTR_2(temp10_max_hyst, S_IRUGO | S_IWUSR, show_temp, store_temp, + 9, 2), +}; + +static struct sensor_device_attribute_2 sda_temp_crit[] = { + SENSOR_ATTR_2(temp1_crit, S_IRUGO | S_IWUSR, show_temp, store_temp, + 0, 3), + SENSOR_ATTR_2(temp2_crit, S_IRUGO | S_IWUSR, show_temp, store_temp, + 1, 3), + SENSOR_ATTR_2(temp3_crit, S_IRUGO | S_IWUSR, show_temp, store_temp, + 2, 3), + SENSOR_ATTR_2(temp4_crit, S_IRUGO | S_IWUSR, show_temp, store_temp, + 3, 3), + SENSOR_ATTR_2(temp5_crit, S_IRUGO | S_IWUSR, show_temp, store_temp, + 4, 3), + SENSOR_ATTR_2(temp6_crit, S_IRUGO | S_IWUSR, show_temp, store_temp, + 5, 3), + SENSOR_ATTR_2(temp7_crit, S_IRUGO | S_IWUSR, show_temp, store_temp, + 6, 3), + SENSOR_ATTR_2(temp8_crit, S_IRUGO | S_IWUSR, show_temp, store_temp, + 7, 3), + SENSOR_ATTR_2(temp9_crit, S_IRUGO | S_IWUSR, show_temp, store_temp, + 8, 3), + SENSOR_ATTR_2(temp10_crit, S_IRUGO | S_IWUSR, show_temp, store_temp, + 9, 3), +}; + +static struct sensor_device_attribute sda_temp_offset[] = { + SENSOR_ATTR(temp1_offset, S_IRUGO | S_IWUSR, show_temp_offset, + store_temp_offset, 0), + SENSOR_ATTR(temp2_offset, S_IRUGO | S_IWUSR, show_temp_offset, + store_temp_offset, 1), + SENSOR_ATTR(temp3_offset, S_IRUGO | S_IWUSR, show_temp_offset, + store_temp_offset, 2), + SENSOR_ATTR(temp4_offset, S_IRUGO | S_IWUSR, show_temp_offset, + store_temp_offset, 3), + SENSOR_ATTR(temp5_offset, S_IRUGO | S_IWUSR, show_temp_offset, + store_temp_offset, 4), + SENSOR_ATTR(temp6_offset, S_IRUGO | S_IWUSR, show_temp_offset, + store_temp_offset, 5), +}; + +static struct sensor_device_attribute sda_temp_type[] = { + SENSOR_ATTR(temp1_type, S_IRUGO | S_IWUSR, show_temp_type, + store_temp_type, 0), + SENSOR_ATTR(temp2_type, S_IRUGO | S_IWUSR, show_temp_type, + store_temp_type, 1), + SENSOR_ATTR(temp3_type, S_IRUGO | S_IWUSR, show_temp_type, + store_temp_type, 2), + SENSOR_ATTR(temp4_type, S_IRUGO | S_IWUSR, show_temp_type, + store_temp_type, 3), + SENSOR_ATTR(temp5_type, S_IRUGO | S_IWUSR, show_temp_type, + store_temp_type, 4), + SENSOR_ATTR(temp6_type, S_IRUGO | S_IWUSR, show_temp_type, + store_temp_type, 5), +}; + +static struct sensor_device_attribute sda_temp_alarm[] = { + SENSOR_ATTR(temp1_alarm, S_IRUGO, show_alarm, NULL, + TEMP_ALARM_BASE), + SENSOR_ATTR(temp2_alarm, S_IRUGO, show_alarm, NULL, + TEMP_ALARM_BASE + 1), + SENSOR_ATTR(temp3_alarm, S_IRUGO, show_alarm, NULL, + TEMP_ALARM_BASE + 2), + SENSOR_ATTR(temp4_alarm, S_IRUGO, show_alarm, NULL, + TEMP_ALARM_BASE + 3), + SENSOR_ATTR(temp5_alarm, S_IRUGO, show_alarm, NULL, + TEMP_ALARM_BASE + 4), + SENSOR_ATTR(temp6_alarm, S_IRUGO, show_alarm, NULL, + TEMP_ALARM_BASE + 5), +}; + +#define NUM_TEMP_ALARM ARRAY_SIZE(sda_temp_alarm) + +static ssize_t +show_pwm_mode(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct nct6775_data *data = nct6775_update_device(dev); + struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr); + + return sprintf(buf, "%d\n", !data->pwm_mode[sattr->index]); +} + +static ssize_t +store_pwm_mode(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct nct6775_data *data = dev_get_drvdata(dev); + struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr); + int nr = sattr->index; + unsigned long val; + int err; + u8 reg; + + err = kstrtoul(buf, 10, &val); + if (err < 0) + return err; + + if (val > 1) + return -EINVAL; + + /* Setting DC mode is not supported for all chips/channels */ + if (data->REG_PWM_MODE[nr] == 0) { + if (val) + return -EINVAL; + return count; + } + + mutex_lock(&data->update_lock); + data->pwm_mode[nr] = val; + reg = nct6775_read_value(data, data->REG_PWM_MODE[nr]); + reg &= ~data->PWM_MODE_MASK[nr]; + if (val) + reg |= data->PWM_MODE_MASK[nr]; + nct6775_write_value(data, data->REG_PWM_MODE[nr], reg); + mutex_unlock(&data->update_lock); + return count; +} + +static ssize_t +show_pwm(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct nct6775_data *data = nct6775_update_device(dev); + struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr); + int nr = sattr->nr; + int index = sattr->index; + int pwm; + + /* + * For automatic fan control modes, show current pwm readings. + * Otherwise, show the configured value. + */ + if (index == 0 && data->pwm_enable[nr] > manual) + pwm = nct6775_read_value(data, data->REG_PWM_READ[nr]); + else + pwm = data->pwm[index][nr]; + + return sprintf(buf, "%d\n", pwm); +} + +static ssize_t +store_pwm(struct device *dev, struct device_attribute *attr, const char *buf, + size_t count) +{ + struct nct6775_data *data = dev_get_drvdata(dev); + struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr); + int nr = sattr->nr; + int index = sattr->index; + unsigned long val; + int minval[7] = { 0, 1, 1, data->pwm[2][nr], 0, 0, 0 }; + int maxval[7] + = { 255, 255, data->pwm[3][nr] ? : 255, 255, 255, 255, 255 }; + int err; + u8 reg; + + err = kstrtoul(buf, 10, &val); + if (err < 0) + return err; + val = clamp_val(val, minval[index], maxval[index]); + + mutex_lock(&data->update_lock); + data->pwm[index][nr] = val; + nct6775_write_value(data, data->REG_PWM[index][nr], val); + if (index == 2) { /* floor: disable if val == 0 */ + reg = nct6775_read_value(data, data->REG_TEMP_SEL[nr]); + reg &= 0x7f; + if (val) + reg |= 0x80; + nct6775_write_value(data, data->REG_TEMP_SEL[nr], reg); + } + mutex_unlock(&data->update_lock); + return count; +} + +/* Returns 0 if OK, -EINVAL otherwise */ +static int check_trip_points(struct nct6775_data *data, int nr) +{ + int i; + + for (i = 0; i < data->auto_pwm_num - 1; i++) { + if (data->auto_temp[nr][i] > data->auto_temp[nr][i + 1]) + return -EINVAL; + } + for (i = 0; i < data->auto_pwm_num - 1; i++) { + if (data->auto_pwm[nr][i] > data->auto_pwm[nr][i + 1]) + return -EINVAL; + } + /* validate critical temperature and pwm if enabled (pwm > 0) */ + if (data->auto_pwm[nr][data->auto_pwm_num]) { + if (data->auto_temp[nr][data->auto_pwm_num - 1] > + data->auto_temp[nr][data->auto_pwm_num] || + data->auto_pwm[nr][data->auto_pwm_num - 1] > + data->auto_pwm[nr][data->auto_pwm_num]) + return -EINVAL; + } + return 0; +} + +static void pwm_update_registers(struct nct6775_data *data, int nr) +{ + u8 reg; + + switch (data->pwm_enable[nr]) { + case off: + case manual: + break; + case speed_cruise: + reg = nct6775_read_value(data, data->REG_FAN_MODE[nr]); + reg = (reg & ~data->tolerance_mask) | + (data->target_speed_tolerance[nr] & data->tolerance_mask); + nct6775_write_value(data, data->REG_FAN_MODE[nr], reg); + nct6775_write_value(data, data->REG_TARGET[nr], + data->target_speed[nr] & 0xff); + if (data->REG_TOLERANCE_H) { + reg = (data->target_speed[nr] >> 8) & 0x0f; + reg |= (data->target_speed_tolerance[nr] & 0x38) << 1; + nct6775_write_value(data, + data->REG_TOLERANCE_H[nr], + reg); + } + break; + case thermal_cruise: + nct6775_write_value(data, data->REG_TARGET[nr], + data->target_temp[nr]); + /* intentional */ + default: + reg = nct6775_read_value(data, data->REG_FAN_MODE[nr]); + reg = (reg & ~data->tolerance_mask) | + data->temp_tolerance[0][nr]; + nct6775_write_value(data, data->REG_FAN_MODE[nr], reg); + break; + } +} + +static ssize_t +show_pwm_enable(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct nct6775_data *data = nct6775_update_device(dev); + struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr); + + return sprintf(buf, "%d\n", data->pwm_enable[sattr->index]); +} + +static ssize_t +store_pwm_enable(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct nct6775_data *data = dev_get_drvdata(dev); + struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr); + int nr = sattr->index; + unsigned long val; + int err; + u16 reg; + + err = kstrtoul(buf, 10, &val); + if (err < 0) + return err; + + if (val > sf4) + return -EINVAL; + + if (val == sf3 && data->kind != nct6775) + return -EINVAL; + + if (val == sf4 && check_trip_points(data, nr)) { + dev_err(dev, "Inconsistent trip points, not switching to SmartFan IV mode\n"); + dev_err(dev, "Adjust trip points and try again\n"); + return -EINVAL; + } + + mutex_lock(&data->update_lock); + data->pwm_enable[nr] = val; + if (val == off) { + /* + * turn off pwm control: select manual mode, set pwm to maximum + */ + data->pwm[0][nr] = 255; + nct6775_write_value(data, data->REG_PWM[0][nr], 255); + } + pwm_update_registers(data, nr); + reg = nct6775_read_value(data, data->REG_FAN_MODE[nr]); + reg &= 0x0f; + reg |= pwm_enable_to_reg(val) << 4; + nct6775_write_value(data, data->REG_FAN_MODE[nr], reg); + mutex_unlock(&data->update_lock); + return count; +} + +static ssize_t +show_pwm_temp_sel_common(struct nct6775_data *data, char *buf, int src) +{ + int i, sel = 0; + + for (i = 0; i < NUM_TEMP; i++) { + if (!(data->have_temp & (1 << i))) + continue; + if (src == data->temp_src[i]) { + sel = i + 1; + break; + } + } + + return sprintf(buf, "%d\n", sel); +} + +static ssize_t +show_pwm_temp_sel(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct nct6775_data *data = nct6775_update_device(dev); + struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr); + int index = sattr->index; + + return show_pwm_temp_sel_common(data, buf, data->pwm_temp_sel[index]); +} + +static ssize_t +store_pwm_temp_sel(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct nct6775_data *data = nct6775_update_device(dev); + struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr); + int nr = sattr->index; + unsigned long val; + int err, reg, src; + + err = kstrtoul(buf, 10, &val); + if (err < 0) + return err; + if (val == 0 || val > NUM_TEMP) + return -EINVAL; + if (!(data->have_temp & (1 << (val - 1))) || !data->temp_src[val - 1]) + return -EINVAL; + + mutex_lock(&data->update_lock); + src = data->temp_src[val - 1]; + data->pwm_temp_sel[nr] = src; + reg = nct6775_read_value(data, data->REG_TEMP_SEL[nr]); + reg &= 0xe0; + reg |= src; + nct6775_write_value(data, data->REG_TEMP_SEL[nr], reg); + mutex_unlock(&data->update_lock); + + return count; +} + +static ssize_t +show_pwm_weight_temp_sel(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct nct6775_data *data = nct6775_update_device(dev); + struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr); + int index = sattr->index; + + return show_pwm_temp_sel_common(data, buf, + data->pwm_weight_temp_sel[index]); +} + +static ssize_t +store_pwm_weight_temp_sel(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct nct6775_data *data = nct6775_update_device(dev); + struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr); + int nr = sattr->index; + unsigned long val; + int err, reg, src; + + err = kstrtoul(buf, 10, &val); + if (err < 0) + return err; + if (val > NUM_TEMP) + return -EINVAL; + if (val && (!(data->have_temp & (1 << (val - 1))) || + !data->temp_src[val - 1])) + return -EINVAL; + + mutex_lock(&data->update_lock); + if (val) { + src = data->temp_src[val - 1]; + data->pwm_weight_temp_sel[nr] = src; + reg = nct6775_read_value(data, data->REG_WEIGHT_TEMP_SEL[nr]); + reg &= 0xe0; + reg |= (src | 0x80); + nct6775_write_value(data, data->REG_WEIGHT_TEMP_SEL[nr], reg); + } else { + data->pwm_weight_temp_sel[nr] = 0; + reg = nct6775_read_value(data, data->REG_WEIGHT_TEMP_SEL[nr]); + reg &= 0x7f; + nct6775_write_value(data, data->REG_WEIGHT_TEMP_SEL[nr], reg); + } + mutex_unlock(&data->update_lock); + + return count; +} + +static ssize_t +show_target_temp(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct nct6775_data *data = nct6775_update_device(dev); + struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr); + + return sprintf(buf, "%d\n", data->target_temp[sattr->index] * 1000); +} + +static ssize_t +store_target_temp(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct nct6775_data *data = dev_get_drvdata(dev); + struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr); + int nr = sattr->index; + unsigned long val; + int err; + + err = kstrtoul(buf, 10, &val); + if (err < 0) + return err; + + val = clamp_val(DIV_ROUND_CLOSEST(val, 1000), 0, + data->target_temp_mask); + + mutex_lock(&data->update_lock); + data->target_temp[nr] = val; + pwm_update_registers(data, nr); + mutex_unlock(&data->update_lock); + return count; +} + +static ssize_t +show_target_speed(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct nct6775_data *data = nct6775_update_device(dev); + struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr); + int nr = sattr->index; + + return sprintf(buf, "%d\n", + fan_from_reg16(data->target_speed[nr], + data->fan_div[nr])); +} + +static ssize_t +store_target_speed(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct nct6775_data *data = dev_get_drvdata(dev); + struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr); + int nr = sattr->index; + unsigned long val; + int err; + u16 speed; + + err = kstrtoul(buf, 10, &val); + if (err < 0) + return err; + + val = clamp_val(val, 0, 1350000U); + speed = fan_to_reg(val, data->fan_div[nr]); + + mutex_lock(&data->update_lock); + data->target_speed[nr] = speed; + pwm_update_registers(data, nr); + mutex_unlock(&data->update_lock); + return count; +} + +static ssize_t +show_temp_tolerance(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct nct6775_data *data = nct6775_update_device(dev); + struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr); + int nr = sattr->nr; + int index = sattr->index; + + return sprintf(buf, "%d\n", data->temp_tolerance[index][nr] * 1000); +} + +static ssize_t +store_temp_tolerance(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct nct6775_data *data = dev_get_drvdata(dev); + struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr); + int nr = sattr->nr; + int index = sattr->index; + unsigned long val; + int err; + + err = kstrtoul(buf, 10, &val); + if (err < 0) + return err; + + /* Limit tolerance as needed */ + val = clamp_val(DIV_ROUND_CLOSEST(val, 1000), 0, data->tolerance_mask); + + mutex_lock(&data->update_lock); + data->temp_tolerance[index][nr] = val; + if (index) + pwm_update_registers(data, nr); + else + nct6775_write_value(data, + data->REG_CRITICAL_TEMP_TOLERANCE[nr], + val); + mutex_unlock(&data->update_lock); + return count; +} + +/* + * Fan speed tolerance is a tricky beast, since the associated register is + * a tick counter, but the value is reported and configured as rpm. + * Compute resulting low and high rpm values and report the difference. + */ +static ssize_t +show_speed_tolerance(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct nct6775_data *data = nct6775_update_device(dev); + struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr); + int nr = sattr->index; + int low = data->target_speed[nr] - data->target_speed_tolerance[nr]; + int high = data->target_speed[nr] + data->target_speed_tolerance[nr]; + int tolerance; + + if (low <= 0) + low = 1; + if (high > 0xffff) + high = 0xffff; + if (high < low) + high = low; + + tolerance = (fan_from_reg16(low, data->fan_div[nr]) + - fan_from_reg16(high, data->fan_div[nr])) / 2; + + return sprintf(buf, "%d\n", tolerance); +} + +static ssize_t +store_speed_tolerance(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct nct6775_data *data = dev_get_drvdata(dev); + struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr); + int nr = sattr->index; + unsigned long val; + int err; + int low, high; + + err = kstrtoul(buf, 10, &val); + if (err < 0) + return err; + + high = fan_from_reg16(data->target_speed[nr], + data->fan_div[nr]) + val; + low = fan_from_reg16(data->target_speed[nr], + data->fan_div[nr]) - val; + if (low <= 0) + low = 1; + if (high < low) + high = low; + + val = (fan_to_reg(low, data->fan_div[nr]) - + fan_to_reg(high, data->fan_div[nr])) / 2; + + /* Limit tolerance as needed */ + val = clamp_val(val, 0, data->speed_tolerance_limit); + + mutex_lock(&data->update_lock); + data->target_speed_tolerance[nr] = val; + pwm_update_registers(data, nr); + mutex_unlock(&data->update_lock); + return count; +} + +static SENSOR_DEVICE_ATTR_2(pwm1, S_IWUSR | S_IRUGO, show_pwm, store_pwm, 0, 0); +static SENSOR_DEVICE_ATTR_2(pwm2, S_IWUSR | S_IRUGO, show_pwm, store_pwm, 1, 0); +static SENSOR_DEVICE_ATTR_2(pwm3, S_IWUSR | S_IRUGO, show_pwm, store_pwm, 2, 0); +static SENSOR_DEVICE_ATTR_2(pwm4, S_IWUSR | S_IRUGO, show_pwm, store_pwm, 3, 0); +static SENSOR_DEVICE_ATTR_2(pwm5, S_IWUSR | S_IRUGO, show_pwm, store_pwm, 4, 0); + +static SENSOR_DEVICE_ATTR(pwm1_mode, S_IWUSR | S_IRUGO, show_pwm_mode, + store_pwm_mode, 0); +static SENSOR_DEVICE_ATTR(pwm2_mode, S_IWUSR | S_IRUGO, show_pwm_mode, + store_pwm_mode, 1); +static SENSOR_DEVICE_ATTR(pwm3_mode, S_IWUSR | S_IRUGO, show_pwm_mode, + store_pwm_mode, 2); +static SENSOR_DEVICE_ATTR(pwm4_mode, S_IWUSR | S_IRUGO, show_pwm_mode, + store_pwm_mode, 3); +static SENSOR_DEVICE_ATTR(pwm5_mode, S_IWUSR | S_IRUGO, show_pwm_mode, + store_pwm_mode, 4); + +static SENSOR_DEVICE_ATTR(pwm1_enable, S_IWUSR | S_IRUGO, show_pwm_enable, + store_pwm_enable, 0); +static SENSOR_DEVICE_ATTR(pwm2_enable, S_IWUSR | S_IRUGO, show_pwm_enable, + store_pwm_enable, 1); +static SENSOR_DEVICE_ATTR(pwm3_enable, S_IWUSR | S_IRUGO, show_pwm_enable, + store_pwm_enable, 2); +static SENSOR_DEVICE_ATTR(pwm4_enable, S_IWUSR | S_IRUGO, show_pwm_enable, + store_pwm_enable, 3); +static SENSOR_DEVICE_ATTR(pwm5_enable, S_IWUSR | S_IRUGO, show_pwm_enable, + store_pwm_enable, 4); + +static SENSOR_DEVICE_ATTR(pwm1_temp_sel, S_IWUSR | S_IRUGO, + show_pwm_temp_sel, store_pwm_temp_sel, 0); +static SENSOR_DEVICE_ATTR(pwm2_temp_sel, S_IWUSR | S_IRUGO, + show_pwm_temp_sel, store_pwm_temp_sel, 1); +static SENSOR_DEVICE_ATTR(pwm3_temp_sel, S_IWUSR | S_IRUGO, + show_pwm_temp_sel, store_pwm_temp_sel, 2); +static SENSOR_DEVICE_ATTR(pwm4_temp_sel, S_IWUSR | S_IRUGO, + show_pwm_temp_sel, store_pwm_temp_sel, 3); +static SENSOR_DEVICE_ATTR(pwm5_temp_sel, S_IWUSR | S_IRUGO, + show_pwm_temp_sel, store_pwm_temp_sel, 4); + +static SENSOR_DEVICE_ATTR(pwm1_target_temp, S_IWUSR | S_IRUGO, show_target_temp, + store_target_temp, 0); +static SENSOR_DEVICE_ATTR(pwm2_target_temp, S_IWUSR | S_IRUGO, show_target_temp, + store_target_temp, 1); +static SENSOR_DEVICE_ATTR(pwm3_target_temp, S_IWUSR | S_IRUGO, show_target_temp, + store_target_temp, 2); +static SENSOR_DEVICE_ATTR(pwm4_target_temp, S_IWUSR | S_IRUGO, show_target_temp, + store_target_temp, 3); +static SENSOR_DEVICE_ATTR(pwm5_target_temp, S_IWUSR | S_IRUGO, show_target_temp, + store_target_temp, 4); + +static SENSOR_DEVICE_ATTR(fan1_target, S_IWUSR | S_IRUGO, show_target_speed, + store_target_speed, 0); +static SENSOR_DEVICE_ATTR(fan2_target, S_IWUSR | S_IRUGO, show_target_speed, + store_target_speed, 1); +static SENSOR_DEVICE_ATTR(fan3_target, S_IWUSR | S_IRUGO, show_target_speed, + store_target_speed, 2); +static SENSOR_DEVICE_ATTR(fan4_target, S_IWUSR | S_IRUGO, show_target_speed, + store_target_speed, 3); +static SENSOR_DEVICE_ATTR(fan5_target, S_IWUSR | S_IRUGO, show_target_speed, + store_target_speed, 4); + +static SENSOR_DEVICE_ATTR(fan1_tolerance, S_IWUSR | S_IRUGO, + show_speed_tolerance, store_speed_tolerance, 0); +static SENSOR_DEVICE_ATTR(fan2_tolerance, S_IWUSR | S_IRUGO, + show_speed_tolerance, store_speed_tolerance, 1); +static SENSOR_DEVICE_ATTR(fan3_tolerance, S_IWUSR | S_IRUGO, + show_speed_tolerance, store_speed_tolerance, 2); +static SENSOR_DEVICE_ATTR(fan4_tolerance, S_IWUSR | S_IRUGO, + show_speed_tolerance, store_speed_tolerance, 3); +static SENSOR_DEVICE_ATTR(fan5_tolerance, S_IWUSR | S_IRUGO, + show_speed_tolerance, store_speed_tolerance, 4); + +/* Smart Fan registers */ + +static ssize_t +show_weight_temp(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct nct6775_data *data = nct6775_update_device(dev); + struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr); + int nr = sattr->nr; + int index = sattr->index; + + return sprintf(buf, "%d\n", data->weight_temp[index][nr] * 1000); +} + +static ssize_t +store_weight_temp(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct nct6775_data *data = dev_get_drvdata(dev); + struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr); + int nr = sattr->nr; + int index = sattr->index; + unsigned long val; + int err; + + err = kstrtoul(buf, 10, &val); + if (err < 0) + return err; + + val = clamp_val(DIV_ROUND_CLOSEST(val, 1000), 0, 255); + + mutex_lock(&data->update_lock); + data->weight_temp[index][nr] = val; + nct6775_write_value(data, data->REG_WEIGHT_TEMP[index][nr], val); + mutex_unlock(&data->update_lock); + return count; +} + +static SENSOR_DEVICE_ATTR(pwm1_weight_temp_sel, S_IWUSR | S_IRUGO, + show_pwm_weight_temp_sel, store_pwm_weight_temp_sel, + 0); +static SENSOR_DEVICE_ATTR(pwm2_weight_temp_sel, S_IWUSR | S_IRUGO, + show_pwm_weight_temp_sel, store_pwm_weight_temp_sel, + 1); +static SENSOR_DEVICE_ATTR(pwm3_weight_temp_sel, S_IWUSR | S_IRUGO, + show_pwm_weight_temp_sel, store_pwm_weight_temp_sel, + 2); +static SENSOR_DEVICE_ATTR(pwm4_weight_temp_sel, S_IWUSR | S_IRUGO, + show_pwm_weight_temp_sel, store_pwm_weight_temp_sel, + 3); +static SENSOR_DEVICE_ATTR(pwm5_weight_temp_sel, S_IWUSR | S_IRUGO, + show_pwm_weight_temp_sel, store_pwm_weight_temp_sel, + 4); + +static SENSOR_DEVICE_ATTR_2(pwm1_weight_temp_step, S_IWUSR | S_IRUGO, + show_weight_temp, store_weight_temp, 0, 0); +static SENSOR_DEVICE_ATTR_2(pwm2_weight_temp_step, S_IWUSR | S_IRUGO, + show_weight_temp, store_weight_temp, 1, 0); +static SENSOR_DEVICE_ATTR_2(pwm3_weight_temp_step, S_IWUSR | S_IRUGO, + show_weight_temp, store_weight_temp, 2, 0); +static SENSOR_DEVICE_ATTR_2(pwm4_weight_temp_step, S_IWUSR | S_IRUGO, + show_weight_temp, store_weight_temp, 3, 0); +static SENSOR_DEVICE_ATTR_2(pwm5_weight_temp_step, S_IWUSR | S_IRUGO, + show_weight_temp, store_weight_temp, 4, 0); + +static SENSOR_DEVICE_ATTR_2(pwm1_weight_temp_step_tol, S_IWUSR | S_IRUGO, + show_weight_temp, store_weight_temp, 0, 1); +static SENSOR_DEVICE_ATTR_2(pwm2_weight_temp_step_tol, S_IWUSR | S_IRUGO, + show_weight_temp, store_weight_temp, 1, 1); +static SENSOR_DEVICE_ATTR_2(pwm3_weight_temp_step_tol, S_IWUSR | S_IRUGO, + show_weight_temp, store_weight_temp, 2, 1); +static SENSOR_DEVICE_ATTR_2(pwm4_weight_temp_step_tol, S_IWUSR | S_IRUGO, + show_weight_temp, store_weight_temp, 3, 1); +static SENSOR_DEVICE_ATTR_2(pwm5_weight_temp_step_tol, S_IWUSR | S_IRUGO, + show_weight_temp, store_weight_temp, 4, 1); + +static SENSOR_DEVICE_ATTR_2(pwm1_weight_temp_step_base, S_IWUSR | S_IRUGO, + show_weight_temp, store_weight_temp, 0, 2); +static SENSOR_DEVICE_ATTR_2(pwm2_weight_temp_step_base, S_IWUSR | S_IRUGO, + show_weight_temp, store_weight_temp, 1, 2); +static SENSOR_DEVICE_ATTR_2(pwm3_weight_temp_step_base, S_IWUSR | S_IRUGO, + show_weight_temp, store_weight_temp, 2, 2); +static SENSOR_DEVICE_ATTR_2(pwm4_weight_temp_step_base, S_IWUSR | S_IRUGO, + show_weight_temp, store_weight_temp, 3, 2); +static SENSOR_DEVICE_ATTR_2(pwm5_weight_temp_step_base, S_IWUSR | S_IRUGO, + show_weight_temp, store_weight_temp, 4, 2); + +static SENSOR_DEVICE_ATTR_2(pwm1_weight_duty_step, S_IWUSR | S_IRUGO, + show_pwm, store_pwm, 0, 5); +static SENSOR_DEVICE_ATTR_2(pwm2_weight_duty_step, S_IWUSR | S_IRUGO, + show_pwm, store_pwm, 1, 5); +static SENSOR_DEVICE_ATTR_2(pwm3_weight_duty_step, S_IWUSR | S_IRUGO, + show_pwm, store_pwm, 2, 5); +static SENSOR_DEVICE_ATTR_2(pwm4_weight_duty_step, S_IWUSR | S_IRUGO, + show_pwm, store_pwm, 3, 5); +static SENSOR_DEVICE_ATTR_2(pwm5_weight_duty_step, S_IWUSR | S_IRUGO, + show_pwm, store_pwm, 4, 5); + +/* duty_base is not supported on all chips */ +static struct sensor_device_attribute_2 sda_weight_duty_base[] = { + SENSOR_ATTR_2(pwm1_weight_duty_base, S_IWUSR | S_IRUGO, + show_pwm, store_pwm, 0, 6), + SENSOR_ATTR_2(pwm2_weight_duty_base, S_IWUSR | S_IRUGO, + show_pwm, store_pwm, 1, 6), + SENSOR_ATTR_2(pwm3_weight_duty_base, S_IWUSR | S_IRUGO, + show_pwm, store_pwm, 2, 6), + SENSOR_ATTR_2(pwm4_weight_duty_base, S_IWUSR | S_IRUGO, + show_pwm, store_pwm, 3, 6), + SENSOR_ATTR_2(pwm5_weight_duty_base, S_IWUSR | S_IRUGO, + show_pwm, store_pwm, 4, 6), +}; + +static ssize_t +show_fan_time(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct nct6775_data *data = nct6775_update_device(dev); + struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr); + int nr = sattr->nr; + int index = sattr->index; + + return sprintf(buf, "%d\n", + step_time_from_reg(data->fan_time[index][nr], + data->pwm_mode[nr])); +} + +static ssize_t +store_fan_time(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct nct6775_data *data = dev_get_drvdata(dev); + struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr); + int nr = sattr->nr; + int index = sattr->index; + unsigned long val; + int err; + + err = kstrtoul(buf, 10, &val); + if (err < 0) + return err; + + val = step_time_to_reg(val, data->pwm_mode[nr]); + mutex_lock(&data->update_lock); + data->fan_time[index][nr] = val; + nct6775_write_value(data, data->REG_FAN_TIME[index][nr], val); + mutex_unlock(&data->update_lock); + return count; +} + +static ssize_t +show_name(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct nct6775_data *data = dev_get_drvdata(dev); + + return sprintf(buf, "%s\n", data->name); +} + +static DEVICE_ATTR(name, S_IRUGO, show_name, NULL); + +static SENSOR_DEVICE_ATTR_2(pwm1_stop_time, S_IWUSR | S_IRUGO, show_fan_time, + store_fan_time, 0, 0); +static SENSOR_DEVICE_ATTR_2(pwm2_stop_time, S_IWUSR | S_IRUGO, show_fan_time, + store_fan_time, 1, 0); +static SENSOR_DEVICE_ATTR_2(pwm3_stop_time, S_IWUSR | S_IRUGO, show_fan_time, + store_fan_time, 2, 0); +static SENSOR_DEVICE_ATTR_2(pwm4_stop_time, S_IWUSR | S_IRUGO, show_fan_time, + store_fan_time, 3, 0); +static SENSOR_DEVICE_ATTR_2(pwm5_stop_time, S_IWUSR | S_IRUGO, show_fan_time, + store_fan_time, 4, 0); + +static SENSOR_DEVICE_ATTR_2(pwm1_step_up_time, S_IWUSR | S_IRUGO, show_fan_time, + store_fan_time, 0, 1); +static SENSOR_DEVICE_ATTR_2(pwm2_step_up_time, S_IWUSR | S_IRUGO, show_fan_time, + store_fan_time, 1, 1); +static SENSOR_DEVICE_ATTR_2(pwm3_step_up_time, S_IWUSR | S_IRUGO, show_fan_time, + store_fan_time, 2, 1); +static SENSOR_DEVICE_ATTR_2(pwm4_step_up_time, S_IWUSR | S_IRUGO, show_fan_time, + store_fan_time, 3, 1); +static SENSOR_DEVICE_ATTR_2(pwm5_step_up_time, S_IWUSR | S_IRUGO, show_fan_time, + store_fan_time, 4, 1); + +static SENSOR_DEVICE_ATTR_2(pwm1_step_down_time, S_IWUSR | S_IRUGO, + show_fan_time, store_fan_time, 0, 2); +static SENSOR_DEVICE_ATTR_2(pwm2_step_down_time, S_IWUSR | S_IRUGO, + show_fan_time, store_fan_time, 1, 2); +static SENSOR_DEVICE_ATTR_2(pwm3_step_down_time, S_IWUSR | S_IRUGO, + show_fan_time, store_fan_time, 2, 2); +static SENSOR_DEVICE_ATTR_2(pwm4_step_down_time, S_IWUSR | S_IRUGO, + show_fan_time, store_fan_time, 3, 2); +static SENSOR_DEVICE_ATTR_2(pwm5_step_down_time, S_IWUSR | S_IRUGO, + show_fan_time, store_fan_time, 4, 2); + +static SENSOR_DEVICE_ATTR_2(pwm1_start, S_IWUSR | S_IRUGO, show_pwm, + store_pwm, 0, 1); +static SENSOR_DEVICE_ATTR_2(pwm2_start, S_IWUSR | S_IRUGO, show_pwm, + store_pwm, 1, 1); +static SENSOR_DEVICE_ATTR_2(pwm3_start, S_IWUSR | S_IRUGO, show_pwm, + store_pwm, 2, 1); +static SENSOR_DEVICE_ATTR_2(pwm4_start, S_IWUSR | S_IRUGO, show_pwm, + store_pwm, 3, 1); +static SENSOR_DEVICE_ATTR_2(pwm5_start, S_IWUSR | S_IRUGO, show_pwm, + store_pwm, 4, 1); + +static SENSOR_DEVICE_ATTR_2(pwm1_floor, S_IWUSR | S_IRUGO, show_pwm, + store_pwm, 0, 2); +static SENSOR_DEVICE_ATTR_2(pwm2_floor, S_IWUSR | S_IRUGO, show_pwm, + store_pwm, 1, 2); +static SENSOR_DEVICE_ATTR_2(pwm3_floor, S_IWUSR | S_IRUGO, show_pwm, + store_pwm, 2, 2); +static SENSOR_DEVICE_ATTR_2(pwm4_floor, S_IWUSR | S_IRUGO, show_pwm, + store_pwm, 3, 2); +static SENSOR_DEVICE_ATTR_2(pwm5_floor, S_IWUSR | S_IRUGO, show_pwm, + store_pwm, 4, 2); + +static SENSOR_DEVICE_ATTR_2(pwm1_temp_tolerance, S_IWUSR | S_IRUGO, + show_temp_tolerance, store_temp_tolerance, 0, 0); +static SENSOR_DEVICE_ATTR_2(pwm2_temp_tolerance, S_IWUSR | S_IRUGO, + show_temp_tolerance, store_temp_tolerance, 1, 0); +static SENSOR_DEVICE_ATTR_2(pwm3_temp_tolerance, S_IWUSR | S_IRUGO, + show_temp_tolerance, store_temp_tolerance, 2, 0); +static SENSOR_DEVICE_ATTR_2(pwm4_temp_tolerance, S_IWUSR | S_IRUGO, + show_temp_tolerance, store_temp_tolerance, 3, 0); +static SENSOR_DEVICE_ATTR_2(pwm5_temp_tolerance, S_IWUSR | S_IRUGO, + show_temp_tolerance, store_temp_tolerance, 4, 0); + +static SENSOR_DEVICE_ATTR_2(pwm1_crit_temp_tolerance, S_IWUSR | S_IRUGO, + show_temp_tolerance, store_temp_tolerance, 0, 1); +static SENSOR_DEVICE_ATTR_2(pwm2_crit_temp_tolerance, S_IWUSR | S_IRUGO, + show_temp_tolerance, store_temp_tolerance, 1, 1); +static SENSOR_DEVICE_ATTR_2(pwm3_crit_temp_tolerance, S_IWUSR | S_IRUGO, + show_temp_tolerance, store_temp_tolerance, 2, 1); +static SENSOR_DEVICE_ATTR_2(pwm4_crit_temp_tolerance, S_IWUSR | S_IRUGO, + show_temp_tolerance, store_temp_tolerance, 3, 1); +static SENSOR_DEVICE_ATTR_2(pwm5_crit_temp_tolerance, S_IWUSR | S_IRUGO, + show_temp_tolerance, store_temp_tolerance, 4, 1); + +/* pwm_max is not supported on all chips */ +static struct sensor_device_attribute_2 sda_pwm_max[] = { + SENSOR_ATTR_2(pwm1_max, S_IWUSR | S_IRUGO, show_pwm, store_pwm, + 0, 3), + SENSOR_ATTR_2(pwm2_max, S_IWUSR | S_IRUGO, show_pwm, store_pwm, + 1, 3), + SENSOR_ATTR_2(pwm3_max, S_IWUSR | S_IRUGO, show_pwm, store_pwm, + 2, 3), + SENSOR_ATTR_2(pwm4_max, S_IWUSR | S_IRUGO, show_pwm, store_pwm, + 3, 3), + SENSOR_ATTR_2(pwm5_max, S_IWUSR | S_IRUGO, show_pwm, store_pwm, + 4, 3), +}; + +/* pwm_step is not supported on all chips */ +static struct sensor_device_attribute_2 sda_pwm_step[] = { + SENSOR_ATTR_2(pwm1_step, S_IWUSR | S_IRUGO, show_pwm, store_pwm, 0, 4), + SENSOR_ATTR_2(pwm2_step, S_IWUSR | S_IRUGO, show_pwm, store_pwm, 1, 4), + SENSOR_ATTR_2(pwm3_step, S_IWUSR | S_IRUGO, show_pwm, store_pwm, 2, 4), + SENSOR_ATTR_2(pwm4_step, S_IWUSR | S_IRUGO, show_pwm, store_pwm, 3, 4), + SENSOR_ATTR_2(pwm5_step, S_IWUSR | S_IRUGO, show_pwm, store_pwm, 4, 4), +}; + +static struct attribute *nct6775_attributes_pwm[5][20] = { + { + &sensor_dev_attr_pwm1.dev_attr.attr, + &sensor_dev_attr_pwm1_mode.dev_attr.attr, + &sensor_dev_attr_pwm1_enable.dev_attr.attr, + &sensor_dev_attr_pwm1_temp_sel.dev_attr.attr, + &sensor_dev_attr_pwm1_temp_tolerance.dev_attr.attr, + &sensor_dev_attr_pwm1_crit_temp_tolerance.dev_attr.attr, + &sensor_dev_attr_pwm1_target_temp.dev_attr.attr, + &sensor_dev_attr_fan1_target.dev_attr.attr, + &sensor_dev_attr_fan1_tolerance.dev_attr.attr, + &sensor_dev_attr_pwm1_stop_time.dev_attr.attr, + &sensor_dev_attr_pwm1_step_up_time.dev_attr.attr, + &sensor_dev_attr_pwm1_step_down_time.dev_attr.attr, + &sensor_dev_attr_pwm1_start.dev_attr.attr, + &sensor_dev_attr_pwm1_floor.dev_attr.attr, + &sensor_dev_attr_pwm1_weight_temp_sel.dev_attr.attr, + &sensor_dev_attr_pwm1_weight_temp_step.dev_attr.attr, + &sensor_dev_attr_pwm1_weight_temp_step_tol.dev_attr.attr, + &sensor_dev_attr_pwm1_weight_temp_step_base.dev_attr.attr, + &sensor_dev_attr_pwm1_weight_duty_step.dev_attr.attr, + NULL + }, + { + &sensor_dev_attr_pwm2.dev_attr.attr, + &sensor_dev_attr_pwm2_mode.dev_attr.attr, + &sensor_dev_attr_pwm2_enable.dev_attr.attr, + &sensor_dev_attr_pwm2_temp_sel.dev_attr.attr, + &sensor_dev_attr_pwm2_temp_tolerance.dev_attr.attr, + &sensor_dev_attr_pwm2_crit_temp_tolerance.dev_attr.attr, + &sensor_dev_attr_pwm2_target_temp.dev_attr.attr, + &sensor_dev_attr_fan2_target.dev_attr.attr, + &sensor_dev_attr_fan2_tolerance.dev_attr.attr, + &sensor_dev_attr_pwm2_stop_time.dev_attr.attr, + &sensor_dev_attr_pwm2_step_up_time.dev_attr.attr, + &sensor_dev_attr_pwm2_step_down_time.dev_attr.attr, + &sensor_dev_attr_pwm2_start.dev_attr.attr, + &sensor_dev_attr_pwm2_floor.dev_attr.attr, + &sensor_dev_attr_pwm2_weight_temp_sel.dev_attr.attr, + &sensor_dev_attr_pwm2_weight_temp_step.dev_attr.attr, + &sensor_dev_attr_pwm2_weight_temp_step_tol.dev_attr.attr, + &sensor_dev_attr_pwm2_weight_temp_step_base.dev_attr.attr, + &sensor_dev_attr_pwm2_weight_duty_step.dev_attr.attr, + NULL + }, + { + &sensor_dev_attr_pwm3.dev_attr.attr, + &sensor_dev_attr_pwm3_mode.dev_attr.attr, + &sensor_dev_attr_pwm3_enable.dev_attr.attr, + &sensor_dev_attr_pwm3_temp_sel.dev_attr.attr, + &sensor_dev_attr_pwm3_temp_tolerance.dev_attr.attr, + &sensor_dev_attr_pwm3_crit_temp_tolerance.dev_attr.attr, + &sensor_dev_attr_pwm3_target_temp.dev_attr.attr, + &sensor_dev_attr_fan3_target.dev_attr.attr, + &sensor_dev_attr_fan3_tolerance.dev_attr.attr, + &sensor_dev_attr_pwm3_stop_time.dev_attr.attr, + &sensor_dev_attr_pwm3_step_up_time.dev_attr.attr, + &sensor_dev_attr_pwm3_step_down_time.dev_attr.attr, + &sensor_dev_attr_pwm3_start.dev_attr.attr, + &sensor_dev_attr_pwm3_floor.dev_attr.attr, + &sensor_dev_attr_pwm3_weight_temp_sel.dev_attr.attr, + &sensor_dev_attr_pwm3_weight_temp_step.dev_attr.attr, + &sensor_dev_attr_pwm3_weight_temp_step_tol.dev_attr.attr, + &sensor_dev_attr_pwm3_weight_temp_step_base.dev_attr.attr, + &sensor_dev_attr_pwm3_weight_duty_step.dev_attr.attr, + NULL + }, + { + &sensor_dev_attr_pwm4.dev_attr.attr, + &sensor_dev_attr_pwm4_mode.dev_attr.attr, + &sensor_dev_attr_pwm4_enable.dev_attr.attr, + &sensor_dev_attr_pwm4_temp_sel.dev_attr.attr, + &sensor_dev_attr_pwm4_temp_tolerance.dev_attr.attr, + &sensor_dev_attr_pwm4_crit_temp_tolerance.dev_attr.attr, + &sensor_dev_attr_pwm4_target_temp.dev_attr.attr, + &sensor_dev_attr_fan4_target.dev_attr.attr, + &sensor_dev_attr_fan4_tolerance.dev_attr.attr, + &sensor_dev_attr_pwm4_stop_time.dev_attr.attr, + &sensor_dev_attr_pwm4_step_up_time.dev_attr.attr, + &sensor_dev_attr_pwm4_step_down_time.dev_attr.attr, + &sensor_dev_attr_pwm4_start.dev_attr.attr, + &sensor_dev_attr_pwm4_floor.dev_attr.attr, + &sensor_dev_attr_pwm4_weight_temp_sel.dev_attr.attr, + &sensor_dev_attr_pwm4_weight_temp_step.dev_attr.attr, + &sensor_dev_attr_pwm4_weight_temp_step_tol.dev_attr.attr, + &sensor_dev_attr_pwm4_weight_temp_step_base.dev_attr.attr, + &sensor_dev_attr_pwm4_weight_duty_step.dev_attr.attr, + NULL + }, + { + &sensor_dev_attr_pwm5.dev_attr.attr, + &sensor_dev_attr_pwm5_mode.dev_attr.attr, + &sensor_dev_attr_pwm5_enable.dev_attr.attr, + &sensor_dev_attr_pwm5_temp_sel.dev_attr.attr, + &sensor_dev_attr_pwm5_temp_tolerance.dev_attr.attr, + &sensor_dev_attr_pwm5_crit_temp_tolerance.dev_attr.attr, + &sensor_dev_attr_pwm5_target_temp.dev_attr.attr, + &sensor_dev_attr_fan5_target.dev_attr.attr, + &sensor_dev_attr_fan5_tolerance.dev_attr.attr, + &sensor_dev_attr_pwm5_stop_time.dev_attr.attr, + &sensor_dev_attr_pwm5_step_up_time.dev_attr.attr, + &sensor_dev_attr_pwm5_step_down_time.dev_attr.attr, + &sensor_dev_attr_pwm5_start.dev_attr.attr, + &sensor_dev_attr_pwm5_floor.dev_attr.attr, + &sensor_dev_attr_pwm5_weight_temp_sel.dev_attr.attr, + &sensor_dev_attr_pwm5_weight_temp_step.dev_attr.attr, + &sensor_dev_attr_pwm5_weight_temp_step_tol.dev_attr.attr, + &sensor_dev_attr_pwm5_weight_temp_step_base.dev_attr.attr, + &sensor_dev_attr_pwm5_weight_duty_step.dev_attr.attr, + NULL + }, +}; + +static const struct attribute_group nct6775_group_pwm[5] = { + { .attrs = nct6775_attributes_pwm[0] }, + { .attrs = nct6775_attributes_pwm[1] }, + { .attrs = nct6775_attributes_pwm[2] }, + { .attrs = nct6775_attributes_pwm[3] }, + { .attrs = nct6775_attributes_pwm[4] }, +}; + +static ssize_t +show_auto_pwm(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct nct6775_data *data = nct6775_update_device(dev); + struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr); + + return sprintf(buf, "%d\n", data->auto_pwm[sattr->nr][sattr->index]); +} + +static ssize_t +store_auto_pwm(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct nct6775_data *data = dev_get_drvdata(dev); + struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr); + int nr = sattr->nr; + int point = sattr->index; + unsigned long val; + int err; + u8 reg; + + err = kstrtoul(buf, 10, &val); + if (err < 0) + return err; + if (val > 255) + return -EINVAL; + + if (point == data->auto_pwm_num) { + if (data->kind != nct6775 && !val) + return -EINVAL; + if (data->kind != nct6779 && val) + val = 0xff; + } + + mutex_lock(&data->update_lock); + data->auto_pwm[nr][point] = val; + if (point < data->auto_pwm_num) { + nct6775_write_value(data, + NCT6775_AUTO_PWM(data, nr, point), + data->auto_pwm[nr][point]); + } else { + switch (data->kind) { + case nct6775: + /* disable if needed (pwm == 0) */ + reg = nct6775_read_value(data, + NCT6775_REG_CRITICAL_ENAB[nr]); + if (val) + reg |= 0x02; + else + reg &= ~0x02; + nct6775_write_value(data, NCT6775_REG_CRITICAL_ENAB[nr], + reg); + break; + case nct6776: + break; /* always enabled, nothing to do */ + case nct6779: + nct6775_write_value(data, NCT6779_REG_CRITICAL_PWM[nr], + val); + reg = nct6775_read_value(data, + NCT6779_REG_CRITICAL_PWM_ENABLE[nr]); + if (val == 255) + reg &= ~0x01; + else + reg |= 0x01; + nct6775_write_value(data, + NCT6779_REG_CRITICAL_PWM_ENABLE[nr], + reg); + break; + } + } + mutex_unlock(&data->update_lock); + return count; +} + +static ssize_t +show_auto_temp(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct nct6775_data *data = nct6775_update_device(dev); + struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr); + int nr = sattr->nr; + int point = sattr->index; + + /* + * We don't know for sure if the temperature is signed or unsigned. + * Assume it is unsigned. + */ + return sprintf(buf, "%d\n", data->auto_temp[nr][point] * 1000); +} + +static ssize_t +store_auto_temp(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct nct6775_data *data = dev_get_drvdata(dev); + struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr); + int nr = sattr->nr; + int point = sattr->index; + unsigned long val; + int err; + + err = kstrtoul(buf, 10, &val); + if (err) + return err; + if (val > 255000) + return -EINVAL; + + mutex_lock(&data->update_lock); + data->auto_temp[nr][point] = DIV_ROUND_CLOSEST(val, 1000); + if (point < data->auto_pwm_num) { + nct6775_write_value(data, + NCT6775_AUTO_TEMP(data, nr, point), + data->auto_temp[nr][point]); + } else { + nct6775_write_value(data, data->REG_CRITICAL_TEMP[nr], + data->auto_temp[nr][point]); + } + mutex_unlock(&data->update_lock); + return count; +} + +/* + * The number of auto-point trip points is chip dependent. + * Need to check support while generating/removing attribute files. + */ +static struct sensor_device_attribute_2 sda_auto_pwm_arrays[] = { + SENSOR_ATTR_2(pwm1_auto_point1_pwm, S_IWUSR | S_IRUGO, + show_auto_pwm, store_auto_pwm, 0, 0), + SENSOR_ATTR_2(pwm1_auto_point1_temp, S_IWUSR | S_IRUGO, + show_auto_temp, store_auto_temp, 0, 0), + SENSOR_ATTR_2(pwm1_auto_point2_pwm, S_IWUSR | S_IRUGO, + show_auto_pwm, store_auto_pwm, 0, 1), + SENSOR_ATTR_2(pwm1_auto_point2_temp, S_IWUSR | S_IRUGO, + show_auto_temp, store_auto_temp, 0, 1), + SENSOR_ATTR_2(pwm1_auto_point3_pwm, S_IWUSR | S_IRUGO, + show_auto_pwm, store_auto_pwm, 0, 2), + SENSOR_ATTR_2(pwm1_auto_point3_temp, S_IWUSR | S_IRUGO, + show_auto_temp, store_auto_temp, 0, 2), + SENSOR_ATTR_2(pwm1_auto_point4_pwm, S_IWUSR | S_IRUGO, + show_auto_pwm, store_auto_pwm, 0, 3), + SENSOR_ATTR_2(pwm1_auto_point4_temp, S_IWUSR | S_IRUGO, + show_auto_temp, store_auto_temp, 0, 3), + SENSOR_ATTR_2(pwm1_auto_point5_pwm, S_IWUSR | S_IRUGO, + show_auto_pwm, store_auto_pwm, 0, 4), + SENSOR_ATTR_2(pwm1_auto_point5_temp, S_IWUSR | S_IRUGO, + show_auto_temp, store_auto_temp, 0, 4), + SENSOR_ATTR_2(pwm1_auto_point6_pwm, S_IWUSR | S_IRUGO, + show_auto_pwm, store_auto_pwm, 0, 5), + SENSOR_ATTR_2(pwm1_auto_point6_temp, S_IWUSR | S_IRUGO, + show_auto_temp, store_auto_temp, 0, 5), + SENSOR_ATTR_2(pwm1_auto_point7_pwm, S_IWUSR | S_IRUGO, + show_auto_pwm, store_auto_pwm, 0, 6), + SENSOR_ATTR_2(pwm1_auto_point7_temp, S_IWUSR | S_IRUGO, + show_auto_temp, store_auto_temp, 0, 6), + + SENSOR_ATTR_2(pwm2_auto_point1_pwm, S_IWUSR | S_IRUGO, + show_auto_pwm, store_auto_pwm, 1, 0), + SENSOR_ATTR_2(pwm2_auto_point1_temp, S_IWUSR | S_IRUGO, + show_auto_temp, store_auto_temp, 1, 0), + SENSOR_ATTR_2(pwm2_auto_point2_pwm, S_IWUSR | S_IRUGO, + show_auto_pwm, store_auto_pwm, 1, 1), + SENSOR_ATTR_2(pwm2_auto_point2_temp, S_IWUSR | S_IRUGO, + show_auto_temp, store_auto_temp, 1, 1), + SENSOR_ATTR_2(pwm2_auto_point3_pwm, S_IWUSR | S_IRUGO, + show_auto_pwm, store_auto_pwm, 1, 2), + SENSOR_ATTR_2(pwm2_auto_point3_temp, S_IWUSR | S_IRUGO, + show_auto_temp, store_auto_temp, 1, 2), + SENSOR_ATTR_2(pwm2_auto_point4_pwm, S_IWUSR | S_IRUGO, + show_auto_pwm, store_auto_pwm, 1, 3), + SENSOR_ATTR_2(pwm2_auto_point4_temp, S_IWUSR | S_IRUGO, + show_auto_temp, store_auto_temp, 1, 3), + SENSOR_ATTR_2(pwm2_auto_point5_pwm, S_IWUSR | S_IRUGO, + show_auto_pwm, store_auto_pwm, 1, 4), + SENSOR_ATTR_2(pwm2_auto_point5_temp, S_IWUSR | S_IRUGO, + show_auto_temp, store_auto_temp, 1, 4), + SENSOR_ATTR_2(pwm2_auto_point6_pwm, S_IWUSR | S_IRUGO, + show_auto_pwm, store_auto_pwm, 1, 5), + SENSOR_ATTR_2(pwm2_auto_point6_temp, S_IWUSR | S_IRUGO, + show_auto_temp, store_auto_temp, 1, 5), + SENSOR_ATTR_2(pwm2_auto_point7_pwm, S_IWUSR | S_IRUGO, + show_auto_pwm, store_auto_pwm, 1, 6), + SENSOR_ATTR_2(pwm2_auto_point7_temp, S_IWUSR | S_IRUGO, + show_auto_temp, store_auto_temp, 1, 6), + + SENSOR_ATTR_2(pwm3_auto_point1_pwm, S_IWUSR | S_IRUGO, + show_auto_pwm, store_auto_pwm, 2, 0), + SENSOR_ATTR_2(pwm3_auto_point1_temp, S_IWUSR | S_IRUGO, + show_auto_temp, store_auto_temp, 2, 0), + SENSOR_ATTR_2(pwm3_auto_point2_pwm, S_IWUSR | S_IRUGO, + show_auto_pwm, store_auto_pwm, 2, 1), + SENSOR_ATTR_2(pwm3_auto_point2_temp, S_IWUSR | S_IRUGO, + show_auto_temp, store_auto_temp, 2, 1), + SENSOR_ATTR_2(pwm3_auto_point3_pwm, S_IWUSR | S_IRUGO, + show_auto_pwm, store_auto_pwm, 2, 2), + SENSOR_ATTR_2(pwm3_auto_point3_temp, S_IWUSR | S_IRUGO, + show_auto_temp, store_auto_temp, 2, 2), + SENSOR_ATTR_2(pwm3_auto_point4_pwm, S_IWUSR | S_IRUGO, + show_auto_pwm, store_auto_pwm, 2, 3), + SENSOR_ATTR_2(pwm3_auto_point4_temp, S_IWUSR | S_IRUGO, + show_auto_temp, store_auto_temp, 2, 3), + SENSOR_ATTR_2(pwm3_auto_point5_pwm, S_IWUSR | S_IRUGO, + show_auto_pwm, store_auto_pwm, 2, 4), + SENSOR_ATTR_2(pwm3_auto_point5_temp, S_IWUSR | S_IRUGO, + show_auto_temp, store_auto_temp, 2, 4), + SENSOR_ATTR_2(pwm3_auto_point6_pwm, S_IWUSR | S_IRUGO, + show_auto_pwm, store_auto_pwm, 2, 5), + SENSOR_ATTR_2(pwm3_auto_point6_temp, S_IWUSR | S_IRUGO, + show_auto_temp, store_auto_temp, 2, 5), + SENSOR_ATTR_2(pwm3_auto_point7_pwm, S_IWUSR | S_IRUGO, + show_auto_pwm, store_auto_pwm, 2, 6), + SENSOR_ATTR_2(pwm3_auto_point7_temp, S_IWUSR | S_IRUGO, + show_auto_temp, store_auto_temp, 2, 6), + + SENSOR_ATTR_2(pwm4_auto_point1_pwm, S_IWUSR | S_IRUGO, + show_auto_pwm, store_auto_pwm, 3, 0), + SENSOR_ATTR_2(pwm4_auto_point1_temp, S_IWUSR | S_IRUGO, + show_auto_temp, store_auto_temp, 3, 0), + SENSOR_ATTR_2(pwm4_auto_point2_pwm, S_IWUSR | S_IRUGO, + show_auto_pwm, store_auto_pwm, 3, 1), + SENSOR_ATTR_2(pwm4_auto_point2_temp, S_IWUSR | S_IRUGO, + show_auto_temp, store_auto_temp, 3, 1), + SENSOR_ATTR_2(pwm4_auto_point3_pwm, S_IWUSR | S_IRUGO, + show_auto_pwm, store_auto_pwm, 3, 2), + SENSOR_ATTR_2(pwm4_auto_point3_temp, S_IWUSR | S_IRUGO, + show_auto_temp, store_auto_temp, 3, 2), + SENSOR_ATTR_2(pwm4_auto_point4_pwm, S_IWUSR | S_IRUGO, + show_auto_pwm, store_auto_pwm, 3, 3), + SENSOR_ATTR_2(pwm4_auto_point4_temp, S_IWUSR | S_IRUGO, + show_auto_temp, store_auto_temp, 3, 3), + SENSOR_ATTR_2(pwm4_auto_point5_pwm, S_IWUSR | S_IRUGO, + show_auto_pwm, store_auto_pwm, 3, 4), + SENSOR_ATTR_2(pwm4_auto_point5_temp, S_IWUSR | S_IRUGO, + show_auto_temp, store_auto_temp, 3, 4), + SENSOR_ATTR_2(pwm4_auto_point6_pwm, S_IWUSR | S_IRUGO, + show_auto_pwm, store_auto_pwm, 3, 5), + SENSOR_ATTR_2(pwm4_auto_point6_temp, S_IWUSR | S_IRUGO, + show_auto_temp, store_auto_temp, 3, 5), + SENSOR_ATTR_2(pwm4_auto_point7_pwm, S_IWUSR | S_IRUGO, + show_auto_pwm, store_auto_pwm, 3, 6), + SENSOR_ATTR_2(pwm4_auto_point7_temp, S_IWUSR | S_IRUGO, + show_auto_temp, store_auto_temp, 3, 6), + + SENSOR_ATTR_2(pwm5_auto_point1_pwm, S_IWUSR | S_IRUGO, + show_auto_pwm, store_auto_pwm, 4, 0), + SENSOR_ATTR_2(pwm5_auto_point1_temp, S_IWUSR | S_IRUGO, + show_auto_temp, store_auto_temp, 4, 0), + SENSOR_ATTR_2(pwm5_auto_point2_pwm, S_IWUSR | S_IRUGO, + show_auto_pwm, store_auto_pwm, 4, 1), + SENSOR_ATTR_2(pwm5_auto_point2_temp, S_IWUSR | S_IRUGO, + show_auto_temp, store_auto_temp, 4, 1), + SENSOR_ATTR_2(pwm5_auto_point3_pwm, S_IWUSR | S_IRUGO, + show_auto_pwm, store_auto_pwm, 4, 2), + SENSOR_ATTR_2(pwm5_auto_point3_temp, S_IWUSR | S_IRUGO, + show_auto_temp, store_auto_temp, 4, 2), + SENSOR_ATTR_2(pwm5_auto_point4_pwm, S_IWUSR | S_IRUGO, + show_auto_pwm, store_auto_pwm, 4, 3), + SENSOR_ATTR_2(pwm5_auto_point4_temp, S_IWUSR | S_IRUGO, + show_auto_temp, store_auto_temp, 4, 3), + SENSOR_ATTR_2(pwm5_auto_point5_pwm, S_IWUSR | S_IRUGO, + show_auto_pwm, store_auto_pwm, 4, 4), + SENSOR_ATTR_2(pwm5_auto_point5_temp, S_IWUSR | S_IRUGO, + show_auto_temp, store_auto_temp, 4, 4), + SENSOR_ATTR_2(pwm5_auto_point6_pwm, S_IWUSR | S_IRUGO, + show_auto_pwm, store_auto_pwm, 4, 5), + SENSOR_ATTR_2(pwm5_auto_point6_temp, S_IWUSR | S_IRUGO, + show_auto_temp, store_auto_temp, 4, 5), + SENSOR_ATTR_2(pwm5_auto_point7_pwm, S_IWUSR | S_IRUGO, + show_auto_pwm, store_auto_pwm, 4, 6), + SENSOR_ATTR_2(pwm5_auto_point7_temp, S_IWUSR | S_IRUGO, + show_auto_temp, store_auto_temp, 4, 6), +}; + +static ssize_t +show_vid(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct nct6775_data *data = dev_get_drvdata(dev); + return sprintf(buf, "%d\n", vid_from_reg(data->vid, data->vrm)); +} + +static DEVICE_ATTR(cpu0_vid, S_IRUGO, show_vid, NULL); + +/* Case open detection */ + +static ssize_t +clear_caseopen(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct nct6775_data *data = dev_get_drvdata(dev); + struct nct6775_sio_data *sio_data = dev->platform_data; + int nr = to_sensor_dev_attr(attr)->index - INTRUSION_ALARM_BASE; + unsigned long val; + u8 reg; + int ret; + + if (kstrtoul(buf, 10, &val) || val != 0) + return -EINVAL; + + mutex_lock(&data->update_lock); + + /* + * Use CR registers to clear caseopen status. + * The CR registers are the same for all chips, and not all chips + * support clearing the caseopen status through "regular" registers. + */ + ret = superio_enter(sio_data->sioreg); + if (ret) { + count = ret; + goto error; + } + + superio_select(sio_data->sioreg, NCT6775_LD_ACPI); + reg = superio_inb(sio_data->sioreg, NCT6775_REG_CR_CASEOPEN_CLR[nr]); + reg |= NCT6775_CR_CASEOPEN_CLR_MASK[nr]; + superio_outb(sio_data->sioreg, NCT6775_REG_CR_CASEOPEN_CLR[nr], reg); + reg &= ~NCT6775_CR_CASEOPEN_CLR_MASK[nr]; + superio_outb(sio_data->sioreg, NCT6775_REG_CR_CASEOPEN_CLR[nr], reg); + superio_exit(sio_data->sioreg); + + data->valid = false; /* Force cache refresh */ +error: + mutex_unlock(&data->update_lock); + return count; +} + +static struct sensor_device_attribute sda_caseopen[] = { + SENSOR_ATTR(intrusion0_alarm, S_IWUSR | S_IRUGO, show_alarm, + clear_caseopen, INTRUSION_ALARM_BASE), + SENSOR_ATTR(intrusion1_alarm, S_IWUSR | S_IRUGO, show_alarm, + clear_caseopen, INTRUSION_ALARM_BASE + 1), +}; + +/* + * Driver and device management + */ + +static void nct6775_device_remove_files(struct device *dev) +{ + /* + * some entries in the following arrays may not have been used in + * device_create_file(), but device_remove_file() will ignore them + */ + int i; + struct nct6775_data *data = dev_get_drvdata(dev); + + for (i = 0; i < data->pwm_num; i++) + sysfs_remove_group(&dev->kobj, &nct6775_group_pwm[i]); + + for (i = 0; i < ARRAY_SIZE(sda_pwm_max); i++) + device_remove_file(dev, &sda_pwm_max[i].dev_attr); + + for (i = 0; i < ARRAY_SIZE(sda_pwm_step); i++) + device_remove_file(dev, &sda_pwm_step[i].dev_attr); + + for (i = 0; i < ARRAY_SIZE(sda_weight_duty_base); i++) + device_remove_file(dev, &sda_weight_duty_base[i].dev_attr); + + for (i = 0; i < ARRAY_SIZE(sda_auto_pwm_arrays); i++) + device_remove_file(dev, &sda_auto_pwm_arrays[i].dev_attr); + + for (i = 0; i < data->in_num; i++) + sysfs_remove_group(&dev->kobj, &nct6775_group_in[i]); + + for (i = 0; i < 5; i++) { + device_remove_file(dev, &sda_fan_input[i].dev_attr); + device_remove_file(dev, &sda_fan_alarm[i].dev_attr); + device_remove_file(dev, &sda_fan_div[i].dev_attr); + device_remove_file(dev, &sda_fan_min[i].dev_attr); + device_remove_file(dev, &sda_fan_pulses[i].dev_attr); + } + for (i = 0; i < NUM_TEMP; i++) { + if (!(data->have_temp & (1 << i))) + continue; + device_remove_file(dev, &sda_temp_input[i].dev_attr); + device_remove_file(dev, &sda_temp_label[i].dev_attr); + device_remove_file(dev, &sda_temp_max[i].dev_attr); + device_remove_file(dev, &sda_temp_max_hyst[i].dev_attr); + device_remove_file(dev, &sda_temp_crit[i].dev_attr); + if (!(data->have_temp_fixed & (1 << i))) + continue; + device_remove_file(dev, &sda_temp_type[i].dev_attr); + device_remove_file(dev, &sda_temp_offset[i].dev_attr); + if (i >= NUM_TEMP_ALARM) + continue; + device_remove_file(dev, &sda_temp_alarm[i].dev_attr); + } + + device_remove_file(dev, &sda_caseopen[0].dev_attr); + device_remove_file(dev, &sda_caseopen[1].dev_attr); + + device_remove_file(dev, &dev_attr_name); + device_remove_file(dev, &dev_attr_cpu0_vid); +} + +/* Get the monitoring functions started */ +static inline void nct6775_init_device(struct nct6775_data *data) +{ + int i; + u8 tmp, diode; + + /* Start monitoring if needed */ + if (data->REG_CONFIG) { + tmp = nct6775_read_value(data, data->REG_CONFIG); + if (!(tmp & 0x01)) + nct6775_write_value(data, data->REG_CONFIG, tmp | 0x01); + } + + /* Enable temperature sensors if needed */ + for (i = 0; i < NUM_TEMP; i++) { + if (!(data->have_temp & (1 << i))) + continue; + if (!data->reg_temp_config[i]) + continue; + tmp = nct6775_read_value(data, data->reg_temp_config[i]); + if (tmp & 0x01) + nct6775_write_value(data, data->reg_temp_config[i], + tmp & 0xfe); + } + + /* Enable VBAT monitoring if needed */ + tmp = nct6775_read_value(data, data->REG_VBAT); + if (!(tmp & 0x01)) + nct6775_write_value(data, data->REG_VBAT, tmp | 0x01); + + diode = nct6775_read_value(data, data->REG_DIODE); + + for (i = 0; i < data->temp_fixed_num; i++) { + if (!(data->have_temp_fixed & (1 << i))) + continue; + if ((tmp & (0x02 << i))) /* diode */ + data->temp_type[i] = 3 - ((diode >> i) & 0x02); + else /* thermistor */ + data->temp_type[i] = 4; + } +} + +static int +nct6775_check_fan_inputs(const struct nct6775_sio_data *sio_data, + struct nct6775_data *data) +{ + int regval; + bool fan3pin, fan3min, fan4pin, fan4min, fan5pin; + bool pwm3pin, pwm4pin, pwm5pin; + int ret; + + ret = superio_enter(sio_data->sioreg); + if (ret) + return ret; + + /* fan4 and fan5 share some pins with the GPIO and serial flash */ + if (data->kind == nct6775) { + regval = superio_inb(sio_data->sioreg, 0x2c); + + fan3pin = regval & (1 << 6); + fan3min = fan3pin; + pwm3pin = regval & (1 << 7); + + /* On NCT6775, fan4 shares pins with the fdc interface */ + fan4pin = !(superio_inb(sio_data->sioreg, 0x2A) & 0x80); + fan4min = 0; + fan5pin = 0; + pwm4pin = 0; + pwm5pin = 0; + } else if (data->kind == nct6776) { + bool gpok = superio_inb(sio_data->sioreg, 0x27) & 0x80; + + superio_select(sio_data->sioreg, NCT6775_LD_HWM); + regval = superio_inb(sio_data->sioreg, SIO_REG_ENABLE); + + if (regval & 0x80) + fan3pin = gpok; + else + fan3pin = !(superio_inb(sio_data->sioreg, 0x24) & 0x40); + + if (regval & 0x40) + fan4pin = gpok; + else + fan4pin = superio_inb(sio_data->sioreg, 0x1C) & 0x01; + + if (regval & 0x20) + fan5pin = gpok; + else + fan5pin = superio_inb(sio_data->sioreg, 0x1C) & 0x02; + + fan4min = fan4pin; + fan3min = fan3pin; + pwm3pin = fan3pin; + pwm4pin = 0; + pwm5pin = 0; + } else { /* NCT6779D */ + regval = superio_inb(sio_data->sioreg, 0x1c); + + fan3pin = !(regval & (1 << 5)); + fan4pin = !(regval & (1 << 6)); + fan5pin = !(regval & (1 << 7)); + + pwm3pin = !(regval & (1 << 0)); + pwm4pin = !(regval & (1 << 1)); + pwm5pin = !(regval & (1 << 2)); + + fan3min = fan3pin; + fan4min = fan4pin; + } + + superio_exit(sio_data->sioreg); + + data->has_fan = data->has_fan_min = 0x03; /* fan1 and fan2 */ + data->has_fan |= fan3pin << 2; + data->has_fan_min |= fan3min << 2; + + data->has_fan |= (fan4pin << 3) | (fan5pin << 4); + data->has_fan_min |= (fan4min << 3) | (fan5pin << 4); + + data->has_pwm = 0x03 | (pwm3pin << 2) | (pwm4pin << 3) | (pwm5pin << 4); + + return 0; +} + +static void add_temp_sensors(struct nct6775_data *data, const u16 *regp, + int *available, int *mask) +{ + int i; + u8 src; + + for (i = 0; i < data->pwm_num && *available; i++) { + int index; + + if (!regp[i]) + continue; + src = nct6775_read_value(data, regp[i]); + src &= 0x1f; + if (!src || (*mask & (1 << src))) + continue; + if (src >= data->temp_label_num || + !strlen(data->temp_label[src])) + continue; + + index = __ffs(*available); + nct6775_write_value(data, data->REG_TEMP_SOURCE[index], src); + *available &= ~(1 << index); + *mask |= 1 << src; + } +} + +static int nct6775_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct nct6775_sio_data *sio_data = dev->platform_data; + struct nct6775_data *data; + struct resource *res; + int i, s, err = 0; + int src, mask, available; + const u16 *reg_temp, *reg_temp_over, *reg_temp_hyst, *reg_temp_config; + const u16 *reg_temp_alternate, *reg_temp_crit; + int num_reg_temp; + bool have_vid = false; + u8 cr2a; + + res = platform_get_resource(pdev, IORESOURCE_IO, 0); + if (!devm_request_region(&pdev->dev, res->start, IOREGION_LENGTH, + DRVNAME)) + return -EBUSY; + + data = devm_kzalloc(&pdev->dev, sizeof(struct nct6775_data), + GFP_KERNEL); + if (!data) + return -ENOMEM; + + data->kind = sio_data->kind; + data->addr = res->start; + mutex_init(&data->update_lock); + data->name = nct6775_device_names[data->kind]; + data->bank = 0xff; /* Force initial bank selection */ + platform_set_drvdata(pdev, data); + + switch (data->kind) { + case nct6775: + data->in_num = 9; + data->pwm_num = 3; + data->auto_pwm_num = 6; + data->has_fan_div = true; + data->temp_fixed_num = 3; + + data->ALARM_BITS = NCT6775_ALARM_BITS; + + data->fan_from_reg = fan_from_reg16; + data->fan_from_reg_min = fan_from_reg8; + data->target_temp_mask = 0x7f; + data->tolerance_mask = 0x0f; + data->speed_tolerance_limit = 15; + + data->temp_label = nct6775_temp_label; + data->temp_label_num = ARRAY_SIZE(nct6775_temp_label); + + data->REG_CONFIG = NCT6775_REG_CONFIG; + data->REG_VBAT = NCT6775_REG_VBAT; + data->REG_DIODE = NCT6775_REG_DIODE; + data->REG_VIN = NCT6775_REG_IN; + data->REG_IN_MINMAX[0] = NCT6775_REG_IN_MIN; + data->REG_IN_MINMAX[1] = NCT6775_REG_IN_MAX; + data->REG_TARGET = NCT6775_REG_TARGET; + data->REG_FAN = NCT6775_REG_FAN; + data->REG_FAN_MODE = NCT6775_REG_FAN_MODE; + data->REG_FAN_MIN = NCT6775_REG_FAN_MIN; + data->REG_FAN_PULSES = NCT6775_REG_FAN_PULSES; + data->REG_FAN_TIME[0] = NCT6775_REG_FAN_STOP_TIME; + data->REG_FAN_TIME[1] = NCT6775_REG_FAN_STEP_UP_TIME; + data->REG_FAN_TIME[2] = NCT6775_REG_FAN_STEP_DOWN_TIME; + data->REG_PWM[0] = NCT6775_REG_PWM; + data->REG_PWM[1] = NCT6775_REG_FAN_START_OUTPUT; + data->REG_PWM[2] = NCT6775_REG_FAN_STOP_OUTPUT; + data->REG_PWM[3] = NCT6775_REG_FAN_MAX_OUTPUT; + data->REG_PWM[4] = NCT6775_REG_FAN_STEP_OUTPUT; + data->REG_PWM[5] = NCT6775_REG_WEIGHT_DUTY_STEP; + data->REG_PWM_READ = NCT6775_REG_PWM_READ; + data->REG_PWM_MODE = NCT6775_REG_PWM_MODE; + data->PWM_MODE_MASK = NCT6775_PWM_MODE_MASK; + data->REG_AUTO_TEMP = NCT6775_REG_AUTO_TEMP; + data->REG_AUTO_PWM = NCT6775_REG_AUTO_PWM; + data->REG_CRITICAL_TEMP = NCT6775_REG_CRITICAL_TEMP; + data->REG_CRITICAL_TEMP_TOLERANCE + = NCT6775_REG_CRITICAL_TEMP_TOLERANCE; + data->REG_TEMP_OFFSET = NCT6775_REG_TEMP_OFFSET; + data->REG_TEMP_SOURCE = NCT6775_REG_TEMP_SOURCE; + data->REG_TEMP_SEL = NCT6775_REG_TEMP_SEL; + data->REG_WEIGHT_TEMP_SEL = NCT6775_REG_WEIGHT_TEMP_SEL; + data->REG_WEIGHT_TEMP[0] = NCT6775_REG_WEIGHT_TEMP_STEP; + data->REG_WEIGHT_TEMP[1] = NCT6775_REG_WEIGHT_TEMP_STEP_TOL; + data->REG_WEIGHT_TEMP[2] = NCT6775_REG_WEIGHT_TEMP_BASE; + data->REG_ALARM = NCT6775_REG_ALARM; + + reg_temp = NCT6775_REG_TEMP; + num_reg_temp = ARRAY_SIZE(NCT6775_REG_TEMP); + reg_temp_over = NCT6775_REG_TEMP_OVER; + reg_temp_hyst = NCT6775_REG_TEMP_HYST; + reg_temp_config = NCT6775_REG_TEMP_CONFIG; + reg_temp_alternate = NCT6775_REG_TEMP_ALTERNATE; + reg_temp_crit = NCT6775_REG_TEMP_CRIT; + + break; + case nct6776: + data->in_num = 9; + data->pwm_num = 3; + data->auto_pwm_num = 4; + data->has_fan_div = false; + data->temp_fixed_num = 3; + + data->ALARM_BITS = NCT6776_ALARM_BITS; + + data->fan_from_reg = fan_from_reg13; + data->fan_from_reg_min = fan_from_reg13; + data->target_temp_mask = 0xff; + data->tolerance_mask = 0x07; + data->speed_tolerance_limit = 63; + + data->temp_label = nct6776_temp_label; + data->temp_label_num = ARRAY_SIZE(nct6776_temp_label); + + data->REG_CONFIG = NCT6775_REG_CONFIG; + data->REG_VBAT = NCT6775_REG_VBAT; + data->REG_DIODE = NCT6775_REG_DIODE; + data->REG_VIN = NCT6775_REG_IN; + data->REG_IN_MINMAX[0] = NCT6775_REG_IN_MIN; + data->REG_IN_MINMAX[1] = NCT6775_REG_IN_MAX; + data->REG_TARGET = NCT6775_REG_TARGET; + data->REG_FAN = NCT6775_REG_FAN; + data->REG_FAN_MODE = NCT6775_REG_FAN_MODE; + data->REG_FAN_MIN = NCT6776_REG_FAN_MIN; + data->REG_FAN_PULSES = NCT6776_REG_FAN_PULSES; + data->REG_FAN_TIME[0] = NCT6775_REG_FAN_STOP_TIME; + data->REG_FAN_TIME[1] = NCT6775_REG_FAN_STEP_UP_TIME; + data->REG_FAN_TIME[2] = NCT6775_REG_FAN_STEP_DOWN_TIME; + data->REG_TOLERANCE_H = NCT6776_REG_TOLERANCE_H; + data->REG_PWM[0] = NCT6775_REG_PWM; + data->REG_PWM[1] = NCT6775_REG_FAN_START_OUTPUT; + data->REG_PWM[2] = NCT6775_REG_FAN_STOP_OUTPUT; + data->REG_PWM[5] = NCT6775_REG_WEIGHT_DUTY_STEP; + data->REG_PWM[6] = NCT6776_REG_WEIGHT_DUTY_BASE; + data->REG_PWM_READ = NCT6775_REG_PWM_READ; + data->REG_PWM_MODE = NCT6776_REG_PWM_MODE; + data->PWM_MODE_MASK = NCT6776_PWM_MODE_MASK; + data->REG_AUTO_TEMP = NCT6775_REG_AUTO_TEMP; + data->REG_AUTO_PWM = NCT6775_REG_AUTO_PWM; + data->REG_CRITICAL_TEMP = NCT6775_REG_CRITICAL_TEMP; + data->REG_CRITICAL_TEMP_TOLERANCE + = NCT6775_REG_CRITICAL_TEMP_TOLERANCE; + data->REG_TEMP_OFFSET = NCT6775_REG_TEMP_OFFSET; + data->REG_TEMP_SOURCE = NCT6775_REG_TEMP_SOURCE; + data->REG_TEMP_SEL = NCT6775_REG_TEMP_SEL; + data->REG_WEIGHT_TEMP_SEL = NCT6775_REG_WEIGHT_TEMP_SEL; + data->REG_WEIGHT_TEMP[0] = NCT6775_REG_WEIGHT_TEMP_STEP; + data->REG_WEIGHT_TEMP[1] = NCT6775_REG_WEIGHT_TEMP_STEP_TOL; + data->REG_WEIGHT_TEMP[2] = NCT6775_REG_WEIGHT_TEMP_BASE; + data->REG_ALARM = NCT6775_REG_ALARM; + + reg_temp = NCT6775_REG_TEMP; + num_reg_temp = ARRAY_SIZE(NCT6775_REG_TEMP); + reg_temp_over = NCT6775_REG_TEMP_OVER; + reg_temp_hyst = NCT6775_REG_TEMP_HYST; + reg_temp_config = NCT6776_REG_TEMP_CONFIG; + reg_temp_alternate = NCT6776_REG_TEMP_ALTERNATE; + reg_temp_crit = NCT6776_REG_TEMP_CRIT; + + break; + case nct6779: + data->in_num = 15; + data->pwm_num = 5; + data->auto_pwm_num = 4; + data->has_fan_div = false; + data->temp_fixed_num = 6; + + data->ALARM_BITS = NCT6779_ALARM_BITS; + + data->fan_from_reg = fan_from_reg13; + data->fan_from_reg_min = fan_from_reg13; + data->target_temp_mask = 0xff; + data->tolerance_mask = 0x07; + data->speed_tolerance_limit = 63; + + data->temp_label = nct6779_temp_label; + data->temp_label_num = ARRAY_SIZE(nct6779_temp_label); + + data->REG_CONFIG = NCT6775_REG_CONFIG; + data->REG_VBAT = NCT6775_REG_VBAT; + data->REG_DIODE = NCT6775_REG_DIODE; + data->REG_VIN = NCT6779_REG_IN; + data->REG_IN_MINMAX[0] = NCT6775_REG_IN_MIN; + data->REG_IN_MINMAX[1] = NCT6775_REG_IN_MAX; + data->REG_TARGET = NCT6775_REG_TARGET; + data->REG_FAN = NCT6779_REG_FAN; + data->REG_FAN_MODE = NCT6775_REG_FAN_MODE; + data->REG_FAN_MIN = NCT6776_REG_FAN_MIN; + data->REG_FAN_PULSES = NCT6779_REG_FAN_PULSES; + data->REG_FAN_TIME[0] = NCT6775_REG_FAN_STOP_TIME; + data->REG_FAN_TIME[1] = NCT6775_REG_FAN_STEP_UP_TIME; + data->REG_FAN_TIME[2] = NCT6775_REG_FAN_STEP_DOWN_TIME; + data->REG_TOLERANCE_H = NCT6776_REG_TOLERANCE_H; + data->REG_PWM[0] = NCT6775_REG_PWM; + data->REG_PWM[1] = NCT6775_REG_FAN_START_OUTPUT; + data->REG_PWM[2] = NCT6775_REG_FAN_STOP_OUTPUT; + data->REG_PWM[5] = NCT6775_REG_WEIGHT_DUTY_STEP; + data->REG_PWM[6] = NCT6776_REG_WEIGHT_DUTY_BASE; + data->REG_PWM_READ = NCT6775_REG_PWM_READ; + data->REG_PWM_MODE = NCT6776_REG_PWM_MODE; + data->PWM_MODE_MASK = NCT6776_PWM_MODE_MASK; + data->REG_AUTO_TEMP = NCT6775_REG_AUTO_TEMP; + data->REG_AUTO_PWM = NCT6775_REG_AUTO_PWM; + data->REG_CRITICAL_TEMP = NCT6775_REG_CRITICAL_TEMP; + data->REG_CRITICAL_TEMP_TOLERANCE + = NCT6775_REG_CRITICAL_TEMP_TOLERANCE; + data->REG_TEMP_OFFSET = NCT6779_REG_TEMP_OFFSET; + data->REG_TEMP_SOURCE = NCT6775_REG_TEMP_SOURCE; + data->REG_TEMP_SEL = NCT6775_REG_TEMP_SEL; + data->REG_WEIGHT_TEMP_SEL = NCT6775_REG_WEIGHT_TEMP_SEL; + data->REG_WEIGHT_TEMP[0] = NCT6775_REG_WEIGHT_TEMP_STEP; + data->REG_WEIGHT_TEMP[1] = NCT6775_REG_WEIGHT_TEMP_STEP_TOL; + data->REG_WEIGHT_TEMP[2] = NCT6775_REG_WEIGHT_TEMP_BASE; + data->REG_ALARM = NCT6779_REG_ALARM; + + reg_temp = NCT6779_REG_TEMP; + num_reg_temp = ARRAY_SIZE(NCT6779_REG_TEMP); + reg_temp_over = NCT6779_REG_TEMP_OVER; + reg_temp_hyst = NCT6779_REG_TEMP_HYST; + reg_temp_config = NCT6779_REG_TEMP_CONFIG; + reg_temp_alternate = NCT6779_REG_TEMP_ALTERNATE; + reg_temp_crit = NCT6779_REG_TEMP_CRIT; + + break; + default: + return -ENODEV; + } + data->have_in = (1 << data->in_num) - 1; + data->have_temp = 0; + + /* + * On some boards, not all available temperature sources are monitored, + * even though some of the monitoring registers are unused. + * Get list of unused monitoring registers, then detect if any fan + * controls are configured to use unmonitored temperature sources. + * If so, assign the unmonitored temperature sources to available + * monitoring registers. + */ + mask = 0; + available = 0; + for (i = 0; i < num_reg_temp; i++) { + if (reg_temp[i] == 0) + continue; + + src = nct6775_read_value(data, data->REG_TEMP_SOURCE[i]) & 0x1f; + if (!src || (mask & (1 << src))) + available |= 1 << i; + + mask |= 1 << src; + } + + /* + * Now find unmonitored temperature registers and enable monitoring + * if additional monitoring registers are available. + */ + add_temp_sensors(data, data->REG_TEMP_SEL, &available, &mask); + add_temp_sensors(data, data->REG_WEIGHT_TEMP_SEL, &available, &mask); + + mask = 0; + s = NUM_TEMP_FIXED; /* First dynamic temperature attribute */ + for (i = 0; i < num_reg_temp; i++) { + if (reg_temp[i] == 0) + continue; + + src = nct6775_read_value(data, data->REG_TEMP_SOURCE[i]) & 0x1f; + if (!src || (mask & (1 << src))) + continue; + + if (src >= data->temp_label_num || + !strlen(data->temp_label[src])) { + dev_info(dev, + "Invalid temperature source %d at index %d, source register 0x%x, temp register 0x%x\n", + src, i, data->REG_TEMP_SOURCE[i], reg_temp[i]); + continue; + } + + mask |= 1 << src; + + /* Use fixed index for SYSTIN(1), CPUTIN(2), AUXTIN(3) */ + if (src <= data->temp_fixed_num) { + data->have_temp |= 1 << (src - 1); + data->have_temp_fixed |= 1 << (src - 1); + data->reg_temp[0][src - 1] = reg_temp[i]; + data->reg_temp[1][src - 1] = reg_temp_over[i]; + data->reg_temp[2][src - 1] = reg_temp_hyst[i]; + data->reg_temp_config[src - 1] = reg_temp_config[i]; + data->temp_src[src - 1] = src; + continue; + } + + if (s >= NUM_TEMP) + continue; + + /* Use dynamic index for other sources */ + data->have_temp |= 1 << s; + data->reg_temp[0][s] = reg_temp[i]; + data->reg_temp[1][s] = reg_temp_over[i]; + data->reg_temp[2][s] = reg_temp_hyst[i]; + data->reg_temp_config[s] = reg_temp_config[i]; + if (reg_temp_crit[src - 1]) + data->reg_temp[3][s] = reg_temp_crit[src - 1]; + + data->temp_src[s] = src; + s++; + } + +#ifdef USE_ALTERNATE + /* + * Go through the list of alternate temp registers and enable + * if possible. + * The temperature is already monitored if the respective bit in <mask> + * is set. + */ + for (i = 0; i < data->temp_label_num - 1; i++) { + if (!reg_temp_alternate[i]) + continue; + if (mask & (1 << (i + 1))) + continue; + if (i < data->temp_fixed_num) { + if (data->have_temp & (1 << i)) + continue; + data->have_temp |= 1 << i; + data->have_temp_fixed |= 1 << i; + data->reg_temp[0][i] = reg_temp_alternate[i]; + data->reg_temp[1][i] = reg_temp_over[i]; + data->reg_temp[2][i] = reg_temp_hyst[i]; + data->temp_src[i] = i + 1; + continue; + } + + if (s >= NUM_TEMP) /* Abort if no more space */ + break; + + data->have_temp |= 1 << s; + data->reg_temp[0][s] = reg_temp_alternate[i]; + data->temp_src[s] = i + 1; + s++; + } +#endif /* USE_ALTERNATE */ + + /* Initialize the chip */ + nct6775_init_device(data); + + err = superio_enter(sio_data->sioreg); + if (err) + return err; + + cr2a = superio_inb(sio_data->sioreg, 0x2a); + switch (data->kind) { + case nct6775: + have_vid = (cr2a & 0x40); + break; + case nct6776: + have_vid = (cr2a & 0x60) == 0x40; + break; + case nct6779: + break; + } + + /* + * Read VID value + * We can get the VID input values directly at logical device D 0xe3. + */ + if (have_vid) { + superio_select(sio_data->sioreg, NCT6775_LD_VID); + data->vid = superio_inb(sio_data->sioreg, 0xe3); + data->vrm = vid_which_vrm(); + } + + if (fan_debounce) { + u8 tmp; + + superio_select(sio_data->sioreg, NCT6775_LD_HWM); + tmp = superio_inb(sio_data->sioreg, + NCT6775_REG_CR_FAN_DEBOUNCE); + switch (data->kind) { + case nct6775: + tmp |= 0x1e; + break; + case nct6776: + case nct6779: + tmp |= 0x3e; + break; + } + superio_outb(sio_data->sioreg, NCT6775_REG_CR_FAN_DEBOUNCE, + tmp); + dev_info(&pdev->dev, "Enabled fan debounce for chip %s\n", + data->name); + } + + superio_exit(sio_data->sioreg); + + if (have_vid) { + err = device_create_file(dev, &dev_attr_cpu0_vid); + if (err) + return err; + } + + err = nct6775_check_fan_inputs(sio_data, data); + if (err) + goto exit_remove; + + /* Read fan clock dividers immediately */ + nct6775_init_fan_common(dev, data); + + /* Register sysfs hooks */ + for (i = 0; i < data->pwm_num; i++) { + if (!(data->has_pwm & (1 << i))) + continue; + + err = sysfs_create_group(&dev->kobj, &nct6775_group_pwm[i]); + if (err) + goto exit_remove; + + if (data->REG_PWM[3]) { + err = device_create_file(dev, + &sda_pwm_max[i].dev_attr); + if (err) + goto exit_remove; + } + if (data->REG_PWM[4]) { + err = device_create_file(dev, + &sda_pwm_step[i].dev_attr); + if (err) + goto exit_remove; + } + if (data->REG_PWM[6]) { + err = device_create_file(dev, + &sda_weight_duty_base[i].dev_attr); + if (err) + goto exit_remove; + } + } + for (i = 0; i < ARRAY_SIZE(sda_auto_pwm_arrays); i++) { + struct sensor_device_attribute_2 *attr = + &sda_auto_pwm_arrays[i]; + + if (!(data->has_pwm & (1 << attr->nr))) + continue; + if (attr->index > data->auto_pwm_num) + continue; + err = device_create_file(dev, &attr->dev_attr); + if (err) + goto exit_remove; + } + + for (i = 0; i < data->in_num; i++) { + if (!(data->have_in & (1 << i))) + continue; + err = sysfs_create_group(&dev->kobj, &nct6775_group_in[i]); + if (err) + goto exit_remove; + } + + for (i = 0; i < 5; i++) { + if (data->has_fan & (1 << i)) { + err = device_create_file(dev, + &sda_fan_input[i].dev_attr); + if (err) + goto exit_remove; + err = device_create_file(dev, + &sda_fan_alarm[i].dev_attr); + if (err) + goto exit_remove; + if (data->kind != nct6776 && + data->kind != nct6779) { + err = device_create_file(dev, + &sda_fan_div[i].dev_attr); + if (err) + goto exit_remove; + } + if (data->has_fan_min & (1 << i)) { + err = device_create_file(dev, + &sda_fan_min[i].dev_attr); + if (err) + goto exit_remove; + } + err = device_create_file(dev, + &sda_fan_pulses[i].dev_attr); + if (err) + goto exit_remove; + } + } + + for (i = 0; i < NUM_TEMP; i++) { + if (!(data->have_temp & (1 << i))) + continue; + err = device_create_file(dev, &sda_temp_input[i].dev_attr); + if (err) + goto exit_remove; + if (data->temp_label) { + err = device_create_file(dev, + &sda_temp_label[i].dev_attr); + if (err) + goto exit_remove; + } + if (data->reg_temp[1][i]) { + err = device_create_file(dev, + &sda_temp_max[i].dev_attr); + if (err) + goto exit_remove; + } + if (data->reg_temp[2][i]) { + err = device_create_file(dev, + &sda_temp_max_hyst[i].dev_attr); + if (err) + goto exit_remove; + } + if (data->reg_temp[3][i]) { + err = device_create_file(dev, + &sda_temp_crit[i].dev_attr); + if (err) + goto exit_remove; + } + if (!(data->have_temp_fixed & (1 << i))) + continue; + err = device_create_file(dev, &sda_temp_type[i].dev_attr); + if (err) + goto exit_remove; + err = device_create_file(dev, &sda_temp_offset[i].dev_attr); + if (err) + goto exit_remove; + if (i >= NUM_TEMP_ALARM || + data->ALARM_BITS[TEMP_ALARM_BASE + i] < 0) + continue; + err = device_create_file(dev, &sda_temp_alarm[i].dev_attr); + if (err) + goto exit_remove; + } + + for (i = 0; i < ARRAY_SIZE(sda_caseopen); i++) { + if (data->ALARM_BITS[INTRUSION_ALARM_BASE + i] < 0) + continue; + err = device_create_file(dev, &sda_caseopen[i].dev_attr); + if (err) + goto exit_remove; + } + + err = device_create_file(dev, &dev_attr_name); + if (err) + goto exit_remove; + + data->hwmon_dev = hwmon_device_register(dev); + if (IS_ERR(data->hwmon_dev)) { + err = PTR_ERR(data->hwmon_dev); + goto exit_remove; + } + + return 0; + +exit_remove: + nct6775_device_remove_files(dev); + return err; +} + +static int nct6775_remove(struct platform_device *pdev) +{ + struct nct6775_data *data = platform_get_drvdata(pdev); + + hwmon_device_unregister(data->hwmon_dev); + nct6775_device_remove_files(&pdev->dev); + + return 0; +} + +#ifdef CONFIG_PM +static int nct6775_suspend(struct device *dev) +{ + struct nct6775_data *data = nct6775_update_device(dev); + struct nct6775_sio_data *sio_data = dev->platform_data; + + mutex_lock(&data->update_lock); + data->vbat = nct6775_read_value(data, data->REG_VBAT); + if (sio_data->kind == nct6775) { + data->fandiv1 = nct6775_read_value(data, NCT6775_REG_FANDIV1); + data->fandiv2 = nct6775_read_value(data, NCT6775_REG_FANDIV2); + } + mutex_unlock(&data->update_lock); + + return 0; +} + +static int nct6775_resume(struct device *dev) +{ + struct nct6775_data *data = dev_get_drvdata(dev); + struct nct6775_sio_data *sio_data = dev->platform_data; + int i, j; + + mutex_lock(&data->update_lock); + data->bank = 0xff; /* Force initial bank selection */ + + /* Restore limits */ + for (i = 0; i < data->in_num; i++) { + if (!(data->have_in & (1 << i))) + continue; + + nct6775_write_value(data, data->REG_IN_MINMAX[0][i], + data->in[i][1]); + nct6775_write_value(data, data->REG_IN_MINMAX[1][i], + data->in[i][2]); + } + + for (i = 0; i < ARRAY_SIZE(data->fan_min); i++) { + if (!(data->has_fan_min & (1 << i))) + continue; + + nct6775_write_value(data, data->REG_FAN_MIN[i], + data->fan_min[i]); + } + + for (i = 0; i < NUM_TEMP; i++) { + if (!(data->have_temp & (1 << i))) + continue; + + for (j = 1; j < ARRAY_SIZE(data->reg_temp); j++) + if (data->reg_temp[j][i]) + nct6775_write_temp(data, data->reg_temp[j][i], + data->temp[j][i]); + } + + /* Restore other settings */ + nct6775_write_value(data, data->REG_VBAT, data->vbat); + if (sio_data->kind == nct6775) { + nct6775_write_value(data, NCT6775_REG_FANDIV1, data->fandiv1); + nct6775_write_value(data, NCT6775_REG_FANDIV2, data->fandiv2); + } + + /* Force re-reading all values */ + data->valid = false; + mutex_unlock(&data->update_lock); + + return 0; +} + +static const struct dev_pm_ops nct6775_dev_pm_ops = { + .suspend = nct6775_suspend, + .resume = nct6775_resume, +}; + +#define NCT6775_DEV_PM_OPS (&nct6775_dev_pm_ops) +#else +#define NCT6775_DEV_PM_OPS NULL +#endif /* CONFIG_PM */ + +static struct platform_driver nct6775_driver = { + .driver = { + .owner = THIS_MODULE, + .name = DRVNAME, + .pm = NCT6775_DEV_PM_OPS, + }, + .probe = nct6775_probe, + .remove = nct6775_remove, +}; + +static const char * const nct6775_sio_names[] __initconst = { + "NCT6775F", + "NCT6776D/F", + "NCT6779D", +}; + +/* nct6775_find() looks for a '627 in the Super-I/O config space */ +static int __init nct6775_find(int sioaddr, unsigned short *addr, + struct nct6775_sio_data *sio_data) +{ + u16 val; + int err; + + err = superio_enter(sioaddr); + if (err) + return err; + + if (force_id) + val = force_id; + else + val = (superio_inb(sioaddr, SIO_REG_DEVID) << 8) + | superio_inb(sioaddr, SIO_REG_DEVID + 1); + switch (val & SIO_ID_MASK) { + case SIO_NCT6775_ID: + sio_data->kind = nct6775; + break; + case SIO_NCT6776_ID: + sio_data->kind = nct6776; + break; + case SIO_NCT6779_ID: + sio_data->kind = nct6779; + break; + default: + if (val != 0xffff) + pr_debug("unsupported chip ID: 0x%04x\n", val); + superio_exit(sioaddr); + return -ENODEV; + } + + /* We have a known chip, find the HWM I/O address */ + superio_select(sioaddr, NCT6775_LD_HWM); + val = (superio_inb(sioaddr, SIO_REG_ADDR) << 8) + | superio_inb(sioaddr, SIO_REG_ADDR + 1); + *addr = val & IOREGION_ALIGNMENT; + if (*addr == 0) { + pr_err("Refusing to enable a Super-I/O device with a base I/O port 0\n"); + superio_exit(sioaddr); + return -ENODEV; + } + + /* Activate logical device if needed */ + val = superio_inb(sioaddr, SIO_REG_ENABLE); + if (!(val & 0x01)) { + pr_warn("Forcibly enabling Super-I/O. Sensor is probably unusable.\n"); + superio_outb(sioaddr, SIO_REG_ENABLE, val | 0x01); + } + + superio_exit(sioaddr); + pr_info("Found %s or compatible chip at %#x\n", + nct6775_sio_names[sio_data->kind], *addr); + sio_data->sioreg = sioaddr; + + return 0; +} + +/* + * when Super-I/O functions move to a separate file, the Super-I/O + * bus will manage the lifetime of the device and this module will only keep + * track of the nct6775 driver. But since we platform_device_alloc(), we + * must keep track of the device + */ +static struct platform_device *pdev; + +static int __init sensors_nct6775_init(void) +{ + int err; + unsigned short address; + struct resource res; + struct nct6775_sio_data sio_data; + + /* + * initialize sio_data->kind and sio_data->sioreg. + * + * when Super-I/O functions move to a separate file, the Super-I/O + * driver will probe 0x2e and 0x4e and auto-detect the presence of a + * nct6775 hardware monitor, and call probe() + */ + if (nct6775_find(0x2e, &address, &sio_data) && + nct6775_find(0x4e, &address, &sio_data)) + return -ENODEV; + + err = platform_driver_register(&nct6775_driver); + if (err) + goto exit; + + pdev = platform_device_alloc(DRVNAME, address); + if (!pdev) { + err = -ENOMEM; + pr_err("Device allocation failed\n"); + goto exit_unregister; + } + + err = platform_device_add_data(pdev, &sio_data, + sizeof(struct nct6775_sio_data)); + if (err) { + pr_err("Platform data allocation failed\n"); + goto exit_device_put; + } + + memset(&res, 0, sizeof(res)); + res.name = DRVNAME; + res.start = address + IOREGION_OFFSET; + res.end = address + IOREGION_OFFSET + IOREGION_LENGTH - 1; + res.flags = IORESOURCE_IO; + + err = acpi_check_resource_conflict(&res); + if (err) + goto exit_device_put; + + err = platform_device_add_resources(pdev, &res, 1); + if (err) { + pr_err("Device resource addition failed (%d)\n", err); + goto exit_device_put; + } + + /* platform_device_add calls probe() */ + err = platform_device_add(pdev); + if (err) { + pr_err("Device addition failed (%d)\n", err); + goto exit_device_put; + } + + return 0; + +exit_device_put: + platform_device_put(pdev); +exit_unregister: + platform_driver_unregister(&nct6775_driver); +exit: + return err; +} + +static void __exit sensors_nct6775_exit(void) +{ + platform_device_unregister(pdev); + platform_driver_unregister(&nct6775_driver); +} + +MODULE_AUTHOR("Guenter Roeck <linux@roeck-us.net>"); +MODULE_DESCRIPTION("NCT6775F/NCT6776F/NCT6779D driver"); +MODULE_LICENSE("GPL"); + +module_init(sensors_nct6775_init); +module_exit(sensors_nct6775_exit); diff --git a/drivers/hwmon/ntc_thermistor.c b/drivers/hwmon/ntc_thermistor.c index b5f63f9c0ce1..d6d640a733d5 100644 --- a/drivers/hwmon/ntc_thermistor.c +++ b/drivers/hwmon/ntc_thermistor.c @@ -26,17 +26,33 @@ #include <linux/math64.h> #include <linux/platform_device.h> #include <linux/err.h> +#include <linux/of.h> +#include <linux/of_device.h> #include <linux/platform_data/ntc_thermistor.h> +#include <linux/iio/iio.h> +#include <linux/iio/machine.h> +#include <linux/iio/driver.h> +#include <linux/iio/consumer.h> + #include <linux/hwmon.h> #include <linux/hwmon-sysfs.h> struct ntc_compensation { - int temp_C; + int temp_c; unsigned int ohm; }; +static const struct platform_device_id ntc_thermistor_id[] = { + { "ncp15wb473", TYPE_NCPXXWB473 }, + { "ncp18wb473", TYPE_NCPXXWB473 }, + { "ncp21wb473", TYPE_NCPXXWB473 }, + { "ncp03wb473", TYPE_NCPXXWB473 }, + { "ncp15wl333", TYPE_NCPXXWL333 }, + { }, +}; + /* * A compensation table should be sorted by the values of .ohm * in descending order. @@ -44,76 +60,76 @@ struct ntc_compensation { * Thermistors Datasheet */ static const struct ntc_compensation ncpXXwb473[] = { - { .temp_C = -40, .ohm = 1747920 }, - { .temp_C = -35, .ohm = 1245428 }, - { .temp_C = -30, .ohm = 898485 }, - { .temp_C = -25, .ohm = 655802 }, - { .temp_C = -20, .ohm = 483954 }, - { .temp_C = -15, .ohm = 360850 }, - { .temp_C = -10, .ohm = 271697 }, - { .temp_C = -5, .ohm = 206463 }, - { .temp_C = 0, .ohm = 158214 }, - { .temp_C = 5, .ohm = 122259 }, - { .temp_C = 10, .ohm = 95227 }, - { .temp_C = 15, .ohm = 74730 }, - { .temp_C = 20, .ohm = 59065 }, - { .temp_C = 25, .ohm = 47000 }, - { .temp_C = 30, .ohm = 37643 }, - { .temp_C = 35, .ohm = 30334 }, - { .temp_C = 40, .ohm = 24591 }, - { .temp_C = 45, .ohm = 20048 }, - { .temp_C = 50, .ohm = 16433 }, - { .temp_C = 55, .ohm = 13539 }, - { .temp_C = 60, .ohm = 11209 }, - { .temp_C = 65, .ohm = 9328 }, - { .temp_C = 70, .ohm = 7798 }, - { .temp_C = 75, .ohm = 6544 }, - { .temp_C = 80, .ohm = 5518 }, - { .temp_C = 85, .ohm = 4674 }, - { .temp_C = 90, .ohm = 3972 }, - { .temp_C = 95, .ohm = 3388 }, - { .temp_C = 100, .ohm = 2902 }, - { .temp_C = 105, .ohm = 2494 }, - { .temp_C = 110, .ohm = 2150 }, - { .temp_C = 115, .ohm = 1860 }, - { .temp_C = 120, .ohm = 1615 }, - { .temp_C = 125, .ohm = 1406 }, + { .temp_c = -40, .ohm = 1747920 }, + { .temp_c = -35, .ohm = 1245428 }, + { .temp_c = -30, .ohm = 898485 }, + { .temp_c = -25, .ohm = 655802 }, + { .temp_c = -20, .ohm = 483954 }, + { .temp_c = -15, .ohm = 360850 }, + { .temp_c = -10, .ohm = 271697 }, + { .temp_c = -5, .ohm = 206463 }, + { .temp_c = 0, .ohm = 158214 }, + { .temp_c = 5, .ohm = 122259 }, + { .temp_c = 10, .ohm = 95227 }, + { .temp_c = 15, .ohm = 74730 }, + { .temp_c = 20, .ohm = 59065 }, + { .temp_c = 25, .ohm = 47000 }, + { .temp_c = 30, .ohm = 37643 }, + { .temp_c = 35, .ohm = 30334 }, + { .temp_c = 40, .ohm = 24591 }, + { .temp_c = 45, .ohm = 20048 }, + { .temp_c = 50, .ohm = 16433 }, + { .temp_c = 55, .ohm = 13539 }, + { .temp_c = 60, .ohm = 11209 }, + { .temp_c = 65, .ohm = 9328 }, + { .temp_c = 70, .ohm = 7798 }, + { .temp_c = 75, .ohm = 6544 }, + { .temp_c = 80, .ohm = 5518 }, + { .temp_c = 85, .ohm = 4674 }, + { .temp_c = 90, .ohm = 3972 }, + { .temp_c = 95, .ohm = 3388 }, + { .temp_c = 100, .ohm = 2902 }, + { .temp_c = 105, .ohm = 2494 }, + { .temp_c = 110, .ohm = 2150 }, + { .temp_c = 115, .ohm = 1860 }, + { .temp_c = 120, .ohm = 1615 }, + { .temp_c = 125, .ohm = 1406 }, }; static const struct ntc_compensation ncpXXwl333[] = { - { .temp_C = -40, .ohm = 1610154 }, - { .temp_C = -35, .ohm = 1130850 }, - { .temp_C = -30, .ohm = 802609 }, - { .temp_C = -25, .ohm = 575385 }, - { .temp_C = -20, .ohm = 416464 }, - { .temp_C = -15, .ohm = 304219 }, - { .temp_C = -10, .ohm = 224193 }, - { .temp_C = -5, .ohm = 166623 }, - { .temp_C = 0, .ohm = 124850 }, - { .temp_C = 5, .ohm = 94287 }, - { .temp_C = 10, .ohm = 71747 }, - { .temp_C = 15, .ohm = 54996 }, - { .temp_C = 20, .ohm = 42455 }, - { .temp_C = 25, .ohm = 33000 }, - { .temp_C = 30, .ohm = 25822 }, - { .temp_C = 35, .ohm = 20335 }, - { .temp_C = 40, .ohm = 16115 }, - { .temp_C = 45, .ohm = 12849 }, - { .temp_C = 50, .ohm = 10306 }, - { .temp_C = 55, .ohm = 8314 }, - { .temp_C = 60, .ohm = 6746 }, - { .temp_C = 65, .ohm = 5503 }, - { .temp_C = 70, .ohm = 4513 }, - { .temp_C = 75, .ohm = 3721 }, - { .temp_C = 80, .ohm = 3084 }, - { .temp_C = 85, .ohm = 2569 }, - { .temp_C = 90, .ohm = 2151 }, - { .temp_C = 95, .ohm = 1809 }, - { .temp_C = 100, .ohm = 1529 }, - { .temp_C = 105, .ohm = 1299 }, - { .temp_C = 110, .ohm = 1108 }, - { .temp_C = 115, .ohm = 949 }, - { .temp_C = 120, .ohm = 817 }, - { .temp_C = 125, .ohm = 707 }, + { .temp_c = -40, .ohm = 1610154 }, + { .temp_c = -35, .ohm = 1130850 }, + { .temp_c = -30, .ohm = 802609 }, + { .temp_c = -25, .ohm = 575385 }, + { .temp_c = -20, .ohm = 416464 }, + { .temp_c = -15, .ohm = 304219 }, + { .temp_c = -10, .ohm = 224193 }, + { .temp_c = -5, .ohm = 166623 }, + { .temp_c = 0, .ohm = 124850 }, + { .temp_c = 5, .ohm = 94287 }, + { .temp_c = 10, .ohm = 71747 }, + { .temp_c = 15, .ohm = 54996 }, + { .temp_c = 20, .ohm = 42455 }, + { .temp_c = 25, .ohm = 33000 }, + { .temp_c = 30, .ohm = 25822 }, + { .temp_c = 35, .ohm = 20335 }, + { .temp_c = 40, .ohm = 16115 }, + { .temp_c = 45, .ohm = 12849 }, + { .temp_c = 50, .ohm = 10306 }, + { .temp_c = 55, .ohm = 8314 }, + { .temp_c = 60, .ohm = 6746 }, + { .temp_c = 65, .ohm = 5503 }, + { .temp_c = 70, .ohm = 4513 }, + { .temp_c = 75, .ohm = 3721 }, + { .temp_c = 80, .ohm = 3084 }, + { .temp_c = 85, .ohm = 2569 }, + { .temp_c = 90, .ohm = 2151 }, + { .temp_c = 95, .ohm = 1809 }, + { .temp_c = 100, .ohm = 1529 }, + { .temp_c = 105, .ohm = 1299 }, + { .temp_c = 110, .ohm = 1108 }, + { .temp_c = 115, .ohm = 949 }, + { .temp_c = 120, .ohm = 817 }, + { .temp_c = 125, .ohm = 707 }, }; struct ntc_data { @@ -125,6 +141,92 @@ struct ntc_data { char name[PLATFORM_NAME_SIZE]; }; +#ifdef CONFIG_OF +static int ntc_adc_iio_read(struct ntc_thermistor_platform_data *pdata) +{ + struct iio_channel *channel = pdata->chan; + unsigned int result; + int val, ret; + + ret = iio_read_channel_raw(channel, &val); + if (ret < 0) { + pr_err("read channel() error: %d\n", ret); + return ret; + } + + /* unit: mV */ + result = pdata->pullup_uv * val; + result >>= 12; + + return result; +} + +static const struct of_device_id ntc_match[] = { + { .compatible = "ntc,ncp15wb473", + .data = &ntc_thermistor_id[TYPE_NCPXXWB473] }, + { .compatible = "ntc,ncp18wb473", + .data = &ntc_thermistor_id[TYPE_NCPXXWB473] }, + { .compatible = "ntc,ncp21wb473", + .data = &ntc_thermistor_id[TYPE_NCPXXWB473] }, + { .compatible = "ntc,ncp03wb473", + .data = &ntc_thermistor_id[TYPE_NCPXXWB473] }, + { .compatible = "ntc,ncp15wl333", + .data = &ntc_thermistor_id[TYPE_NCPXXWL333] }, + { }, +}; +MODULE_DEVICE_TABLE(of, ntc_match); + +static struct ntc_thermistor_platform_data * +ntc_thermistor_parse_dt(struct platform_device *pdev) +{ + struct iio_channel *chan; + struct device_node *np = pdev->dev.of_node; + struct ntc_thermistor_platform_data *pdata; + + if (!np) + return NULL; + + pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL); + if (!pdata) + return ERR_PTR(-ENOMEM); + + chan = iio_channel_get(&pdev->dev, NULL); + if (IS_ERR(chan)) + return ERR_CAST(chan); + + if (of_property_read_u32(np, "pullup-uv", &pdata->pullup_uv)) + return ERR_PTR(-ENODEV); + if (of_property_read_u32(np, "pullup-ohm", &pdata->pullup_ohm)) + return ERR_PTR(-ENODEV); + if (of_property_read_u32(np, "pulldown-ohm", &pdata->pulldown_ohm)) + return ERR_PTR(-ENODEV); + + if (of_find_property(np, "connected-positive", NULL)) + pdata->connect = NTC_CONNECTED_POSITIVE; + else /* status change should be possible if not always on. */ + pdata->connect = NTC_CONNECTED_GROUND; + + pdata->chan = chan; + pdata->read_uv = ntc_adc_iio_read; + + return pdata; +} +static void ntc_iio_channel_release(struct ntc_thermistor_platform_data *pdata) +{ + if (pdata->chan) + iio_channel_release(pdata->chan); +} +#else +static struct ntc_thermistor_platform_data * +ntc_thermistor_parse_dt(struct platform_device *pdev) +{ + return NULL; +} + +static void ntc_iio_channel_release(struct ntc_thermistor_platform_data *pdata) +{ } +#endif + static inline u64 div64_u64_safe(u64 dividend, u64 divisor) { if (divisor == 0 && dividend == 0) @@ -134,37 +236,37 @@ static inline u64 div64_u64_safe(u64 dividend, u64 divisor) return div64_u64(dividend, divisor); } -static int get_ohm_of_thermistor(struct ntc_data *data, unsigned int uV) +static int get_ohm_of_thermistor(struct ntc_data *data, unsigned int uv) { struct ntc_thermistor_platform_data *pdata = data->pdata; - u64 mV = uV / 1000; - u64 pmV = pdata->pullup_uV / 1000; - u64 N, puO, pdO; - puO = pdata->pullup_ohm; - pdO = pdata->pulldown_ohm; + u64 mv = uv / 1000; + u64 pmv = pdata->pullup_uv / 1000; + u64 n, puo, pdo; + puo = pdata->pullup_ohm; + pdo = pdata->pulldown_ohm; - if (mV == 0) { + if (mv == 0) { if (pdata->connect == NTC_CONNECTED_POSITIVE) return INT_MAX; return 0; } - if (mV >= pmV) + if (mv >= pmv) return (pdata->connect == NTC_CONNECTED_POSITIVE) ? 0 : INT_MAX; - if (pdata->connect == NTC_CONNECTED_POSITIVE && puO == 0) - N = div64_u64_safe(pdO * (pmV - mV), mV); - else if (pdata->connect == NTC_CONNECTED_GROUND && pdO == 0) - N = div64_u64_safe(puO * mV, pmV - mV); + if (pdata->connect == NTC_CONNECTED_POSITIVE && puo == 0) + n = div64_u64_safe(pdo * (pmv - mv), mv); + else if (pdata->connect == NTC_CONNECTED_GROUND && pdo == 0) + n = div64_u64_safe(puo * mv, pmv - mv); else if (pdata->connect == NTC_CONNECTED_POSITIVE) - N = div64_u64_safe(pdO * puO * (pmV - mV), - puO * mV - pdO * (pmV - mV)); + n = div64_u64_safe(pdo * puo * (pmv - mv), + puo * mv - pdo * (pmv - mv)); else - N = div64_u64_safe(pdO * puO * mV, pdO * (pmV - mV) - puO * mV); + n = div64_u64_safe(pdo * puo * mv, pdo * (pmv - mv) - puo * mv); - if (N > INT_MAX) - N = INT_MAX; - return N; + if (n > INT_MAX) + n = INT_MAX; + return n; } static void lookup_comp(struct ntc_data *data, unsigned int ohm, @@ -233,7 +335,7 @@ static void lookup_comp(struct ntc_data *data, unsigned int ohm, *i_high = end - 1; } -static int get_temp_mC(struct ntc_data *data, unsigned int ohm) +static int get_temp_mc(struct ntc_data *data, unsigned int ohm) { int low, high; int temp; @@ -241,10 +343,10 @@ static int get_temp_mC(struct ntc_data *data, unsigned int ohm) lookup_comp(data, ohm, &low, &high); if (low == high) { /* Unable to use linear approximation */ - temp = data->comp[low].temp_C * 1000; + temp = data->comp[low].temp_c * 1000; } else { - temp = data->comp[low].temp_C * 1000 + - ((data->comp[high].temp_C - data->comp[low].temp_C) * + temp = data->comp[low].temp_c * 1000 + + ((data->comp[high].temp_c - data->comp[low].temp_c) * 1000 * ((int)ohm - (int)data->comp[low].ohm)) / ((int)data->comp[high].ohm - (int)data->comp[low].ohm); } @@ -253,16 +355,16 @@ static int get_temp_mC(struct ntc_data *data, unsigned int ohm) static int ntc_thermistor_get_ohm(struct ntc_data *data) { - int read_uV; + int read_uv; if (data->pdata->read_ohm) return data->pdata->read_ohm(); - if (data->pdata->read_uV) { - read_uV = data->pdata->read_uV(); - if (read_uV < 0) - return read_uV; - return get_ohm_of_thermistor(data, read_uV); + if (data->pdata->read_uv) { + read_uv = data->pdata->read_uv(data->pdata); + if (read_uv < 0) + return read_uv; + return get_ohm_of_thermistor(data, read_uv); } return -EINVAL; } @@ -291,7 +393,7 @@ static ssize_t ntc_show_temp(struct device *dev, if (ohm < 0) return ohm; - return sprintf(buf, "%d\n", get_temp_mC(data, ohm)); + return sprintf(buf, "%d\n", get_temp_mc(data, ohm)); } static SENSOR_DEVICE_ATTR(temp1_type, S_IRUGO, ntc_show_type, NULL, 0); @@ -311,9 +413,18 @@ static const struct attribute_group ntc_attr_group = { static int ntc_thermistor_probe(struct platform_device *pdev) { + const struct of_device_id *of_id = + of_match_device(of_match_ptr(ntc_match), &pdev->dev); + const struct platform_device_id *pdev_id; + struct ntc_thermistor_platform_data *pdata; struct ntc_data *data; - struct ntc_thermistor_platform_data *pdata = pdev->dev.platform_data; - int ret = 0; + int ret; + + pdata = ntc_thermistor_parse_dt(pdev); + if (IS_ERR(pdata)) + return PTR_ERR(pdata); + else if (pdata == NULL) + pdata = pdev->dev.platform_data; if (!pdata) { dev_err(&pdev->dev, "No platform init data supplied.\n"); @@ -321,19 +432,19 @@ static int ntc_thermistor_probe(struct platform_device *pdev) } /* Either one of the two is required. */ - if (!pdata->read_uV && !pdata->read_ohm) { + if (!pdata->read_uv && !pdata->read_ohm) { dev_err(&pdev->dev, - "Both read_uV and read_ohm missing. Need either one of the two.\n"); + "Both read_uv and read_ohm missing. Need either one of the two.\n"); return -EINVAL; } - if (pdata->read_uV && pdata->read_ohm) { + if (pdata->read_uv && pdata->read_ohm) { dev_warn(&pdev->dev, - "Only one of read_uV and read_ohm is needed; ignoring read_uV.\n"); - pdata->read_uV = NULL; + "Only one of read_uv and read_ohm is needed; ignoring read_uv.\n"); + pdata->read_uv = NULL; } - if (pdata->read_uV && (pdata->pullup_uV == 0 || + if (pdata->read_uv && (pdata->pullup_uv == 0 || (pdata->pullup_ohm == 0 && pdata->connect == NTC_CONNECTED_GROUND) || (pdata->pulldown_ohm == 0 && pdata->connect == @@ -341,7 +452,7 @@ static int ntc_thermistor_probe(struct platform_device *pdev) (pdata->connect != NTC_CONNECTED_POSITIVE && pdata->connect != NTC_CONNECTED_GROUND))) { dev_err(&pdev->dev, - "Required data to use read_uV not supplied.\n"); + "Required data to use read_uv not supplied.\n"); return -EINVAL; } @@ -349,11 +460,13 @@ static int ntc_thermistor_probe(struct platform_device *pdev) if (!data) return -ENOMEM; + pdev_id = of_id ? of_id->data : platform_get_device_id(pdev); + data->dev = &pdev->dev; data->pdata = pdata; - strlcpy(data->name, pdev->id_entry->name, sizeof(data->name)); + strlcpy(data->name, pdev_id->name, sizeof(data->name)); - switch (pdev->id_entry->driver_data) { + switch (pdev_id->driver_data) { case TYPE_NCPXXWB473: data->comp = ncpXXwb473; data->n_comp = ARRAY_SIZE(ncpXXwb473); @@ -364,8 +477,7 @@ static int ntc_thermistor_probe(struct platform_device *pdev) break; default: dev_err(&pdev->dev, "Unknown device type: %lu(%s)\n", - pdev->id_entry->driver_data, - pdev->id_entry->name); + pdev_id->driver_data, pdev_id->name); return -EINVAL; } @@ -384,39 +496,34 @@ static int ntc_thermistor_probe(struct platform_device *pdev) goto err_after_sysfs; } - dev_info(&pdev->dev, "Thermistor %s:%d (type: %s/%lu) successfully probed.\n", - pdev->name, pdev->id, pdev->id_entry->name, - pdev->id_entry->driver_data); + dev_info(&pdev->dev, "Thermistor type: %s successfully probed.\n", + pdev->name); + return 0; err_after_sysfs: sysfs_remove_group(&data->dev->kobj, &ntc_attr_group); + ntc_iio_channel_release(pdata); return ret; } static int ntc_thermistor_remove(struct platform_device *pdev) { struct ntc_data *data = platform_get_drvdata(pdev); + struct ntc_thermistor_platform_data *pdata = data->pdata; hwmon_device_unregister(data->hwmon_dev); sysfs_remove_group(&data->dev->kobj, &ntc_attr_group); + ntc_iio_channel_release(pdata); platform_set_drvdata(pdev, NULL); return 0; } -static const struct platform_device_id ntc_thermistor_id[] = { - { "ncp15wb473", TYPE_NCPXXWB473 }, - { "ncp18wb473", TYPE_NCPXXWB473 }, - { "ncp21wb473", TYPE_NCPXXWB473 }, - { "ncp03wb473", TYPE_NCPXXWB473 }, - { "ncp15wl333", TYPE_NCPXXWL333 }, - { }, -}; - static struct platform_driver ntc_thermistor_driver = { .driver = { .name = "ntc-thermistor", .owner = THIS_MODULE, + .of_match_table = of_match_ptr(ntc_match), }, .probe = ntc_thermistor_probe, .remove = ntc_thermistor_remove, diff --git a/drivers/hwmon/pc87360.c b/drivers/hwmon/pc87360.c index e35856bb79b4..aa615ba73d4b 100644 --- a/drivers/hwmon/pc87360.c +++ b/drivers/hwmon/pc87360.c @@ -1190,8 +1190,7 @@ static int __init pc87360_find(int sioaddr, u8 *devid, confreg[3] = superio_inb(sioaddr, 0x25); if (confreg[2] & 0x40) { - pr_info("Using thermistors for " - "temperature monitoring\n"); + pr_info("Using thermistors for temperature monitoring\n"); } if (confreg[3] & 0xE0) { pr_info("VID inputs routed (mode %u)\n", @@ -1271,9 +1270,9 @@ static int pc87360_probe(struct platform_device *pdev) if (data->address[i] && !devm_request_region(dev, extra_isa[i], PC87360_EXTENT, pc87360_driver.driver.name)) { - dev_err(dev, "Region 0x%x-0x%x already " - "in use!\n", extra_isa[i], - extra_isa[i]+PC87360_EXTENT-1); + dev_err(dev, + "Region 0x%x-0x%x already in use!\n", + extra_isa[i], extra_isa[i]+PC87360_EXTENT-1); return -EBUSY; } } @@ -1435,8 +1434,8 @@ static void pc87360_init_device(struct platform_device *pdev, if (init >= 2 && data->innr) { reg = pc87360_read_value(data, LD_IN, NO_BANK, PC87365_REG_IN_CONVRATE); - dev_info(&pdev->dev, "VLM conversion set to " - "1s period, 160us delay\n"); + dev_info(&pdev->dev, + "VLM conversion set to 1s period, 160us delay\n"); pc87360_write_value(data, LD_IN, NO_BANK, PC87365_REG_IN_CONVRATE, (reg & 0xC0) | 0x11); @@ -1450,8 +1449,8 @@ static void pc87360_init_device(struct platform_device *pdev, if (init >= init_in[i]) { /* Forcibly enable voltage channel */ if (!(reg & CHAN_ENA)) { - dev_dbg(&pdev->dev, "Forcibly " - "enabling in%d\n", i); + dev_dbg(&pdev->dev, "Forcibly enabling in%d\n", + i); pc87360_write_value(data, LD_IN, i, PC87365_REG_IN_STATUS, (reg & 0x68) | 0x87); @@ -1575,8 +1574,8 @@ static void pc87360_autodiv(struct device *dev, int nr) data->fan_status[nr] += 0x20; data->fan_min[nr] >>= 1; data->fan[nr] >>= 1; - dev_dbg(dev, "Increasing " - "clock divider to %d for fan %d\n", + dev_dbg(dev, + "Increasing clock divider to %d for fan %d\n", FAN_DIV_FROM_REG(data->fan_status[nr]), nr + 1); } } else { @@ -1587,8 +1586,8 @@ static void pc87360_autodiv(struct device *dev, int nr) data->fan_status[nr] -= 0x20; data->fan_min[nr] <<= 1; data->fan[nr] <<= 1; - dev_dbg(dev, "Decreasing " - "clock divider to %d for fan %d\n", + dev_dbg(dev, + "Decreasing clock divider to %d for fan %d\n", FAN_DIV_FROM_REG(data->fan_status[nr]), nr + 1); } diff --git a/drivers/hwmon/pc87427.c b/drivers/hwmon/pc87427.c index 6086ad039d7d..ea606860d2b2 100644 --- a/drivers/hwmon/pc87427.c +++ b/drivers/hwmon/pc87427.c @@ -627,8 +627,9 @@ static ssize_t set_pwm(struct device *dev, struct device_attribute pc87427_readall_pwm(data, nr); mode = data->pwm_enable[nr] & PWM_ENABLE_MODE_MASK; if (mode != PWM_MODE_MANUAL && mode != PWM_MODE_OFF) { - dev_notice(dev, "Can't set PWM%d duty cycle while not in " - "manual mode\n", nr + 1); + dev_notice(dev, + "Can't set PWM%d duty cycle while not in manual mode\n", + nr + 1); mutex_unlock(&data->lock); return -EPERM; } @@ -1245,16 +1246,16 @@ static int __init pc87427_find(int sioaddr, struct pc87427_sio_data *sio_data) val = superio_inb(sioaddr, SIOREG_MAP); if (val & 0x01) { - pr_warn("Logical device 0x%02x is memory-mapped, " - "can't use\n", logdev[i]); + pr_warn("Logical device 0x%02x is memory-mapped, can't use\n", + logdev[i]); continue; } val = (superio_inb(sioaddr, SIOREG_IOBASE) << 8) | superio_inb(sioaddr, SIOREG_IOBASE + 1); if (!val) { - pr_info("I/O base address not set for logical device " - "0x%02x\n", logdev[i]); + pr_info("I/O base address not set for logical device 0x%02x\n", + logdev[i]); continue; } sio_data->address[i] = val; diff --git a/drivers/hwmon/pmbus/Kconfig b/drivers/hwmon/pmbus/Kconfig index 4f9eb0af5229..39cc63edfbb0 100644 --- a/drivers/hwmon/pmbus/Kconfig +++ b/drivers/hwmon/pmbus/Kconfig @@ -42,17 +42,17 @@ config SENSORS_LM25066 default n help If you say yes here you get hardware monitoring support for National - Semiconductor LM25066, LM5064, and LM5066. + Semiconductor LM25056, LM25066, LM5064, and LM5066. This driver can also be built as a module. If so, the module will be called lm25066. config SENSORS_LTC2978 - tristate "Linear Technologies LTC2978 and LTC3880" + tristate "Linear Technologies LTC2974, LTC2978, LTC3880, and LTC3883" default n help If you say yes here you get hardware monitoring support for Linear - Technology LTC2978 and LTC3880. + Technology LTC2974, LTC2978, LTC3880, and LTC3883. This driver can also be built as a module. If so, the module will be called ltc2978. diff --git a/drivers/hwmon/pmbus/lm25066.c b/drivers/hwmon/pmbus/lm25066.c index c299392716af..6a9d6edaacb3 100644 --- a/drivers/hwmon/pmbus/lm25066.c +++ b/drivers/hwmon/pmbus/lm25066.c @@ -1,7 +1,8 @@ /* - * Hardware monitoring driver for LM25066 / LM5064 / LM5066 + * Hardware monitoring driver for LM25056 / LM25066 / LM5064 / LM5066 * * Copyright (c) 2011 Ericsson AB. + * Copyright (c) 2013 Guenter Roeck * * 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 @@ -26,7 +27,7 @@ #include <linux/i2c.h> #include "pmbus.h" -enum chips { lm25066, lm5064, lm5066 }; +enum chips { lm25056, lm25066, lm5064, lm5066 }; #define LM25066_READ_VAUX 0xd0 #define LM25066_MFR_READ_IIN 0xd1 @@ -43,6 +44,138 @@ enum chips { lm25066, lm5064, lm5066 }; #define LM25066_DEV_SETUP_CL (1 << 4) /* Current limit */ +/* LM25056 only */ + +#define LM25056_VAUX_OV_WARN_LIMIT 0xe3 +#define LM25056_VAUX_UV_WARN_LIMIT 0xe4 + +#define LM25056_MFR_STS_VAUX_OV_WARN (1 << 1) +#define LM25056_MFR_STS_VAUX_UV_WARN (1 << 0) + +struct __coeff { + short m, b, R; +}; + +#define PSC_CURRENT_IN_L (PSC_NUM_CLASSES) +#define PSC_POWER_L (PSC_NUM_CLASSES + 1) + +static struct __coeff lm25066_coeff[4][PSC_NUM_CLASSES + 2] = { + [lm25056] = { + [PSC_VOLTAGE_IN] = { + .m = 16296, + .R = -2, + }, + [PSC_CURRENT_IN] = { + .m = 13797, + .R = -2, + }, + [PSC_CURRENT_IN_L] = { + .m = 6726, + .R = -2, + }, + [PSC_POWER] = { + .m = 5501, + .R = -3, + }, + [PSC_POWER_L] = { + .m = 26882, + .R = -4, + }, + [PSC_TEMPERATURE] = { + .m = 1580, + .b = -14500, + .R = -2, + }, + }, + [lm25066] = { + [PSC_VOLTAGE_IN] = { + .m = 22070, + .R = -2, + }, + [PSC_VOLTAGE_OUT] = { + .m = 22070, + .R = -2, + }, + [PSC_CURRENT_IN] = { + .m = 13661, + .R = -2, + }, + [PSC_CURRENT_IN_L] = { + .m = 6852, + .R = -2, + }, + [PSC_POWER] = { + .m = 736, + .R = -2, + }, + [PSC_POWER_L] = { + .m = 369, + .R = -2, + }, + [PSC_TEMPERATURE] = { + .m = 16, + }, + }, + [lm5064] = { + [PSC_VOLTAGE_IN] = { + .m = 4611, + .R = -2, + }, + [PSC_VOLTAGE_OUT] = { + .m = 4621, + .R = -2, + }, + [PSC_CURRENT_IN] = { + .m = 10742, + .R = -2, + }, + [PSC_CURRENT_IN_L] = { + .m = 5456, + .R = -2, + }, + [PSC_POWER] = { + .m = 1204, + .R = -3, + }, + [PSC_POWER_L] = { + .m = 612, + .R = -3, + }, + [PSC_TEMPERATURE] = { + .m = 16, + }, + }, + [lm5066] = { + [PSC_VOLTAGE_IN] = { + .m = 4587, + .R = -2, + }, + [PSC_VOLTAGE_OUT] = { + .m = 4587, + .R = -2, + }, + [PSC_CURRENT_IN] = { + .m = 10753, + .R = -2, + }, + [PSC_CURRENT_IN_L] = { + .m = 5405, + .R = -2, + }, + [PSC_POWER] = { + .m = 1204, + .R = -3, + }, + [PSC_POWER_L] = { + .m = 605, + .R = -3, + }, + [PSC_TEMPERATURE] = { + .m = 16, + }, + }, +}; + struct lm25066_data { int id; struct pmbus_driver_info info; @@ -56,42 +189,31 @@ static int lm25066_read_word_data(struct i2c_client *client, int page, int reg) const struct lm25066_data *data = to_lm25066_data(info); int ret; - if (page > 1) - return -ENXIO; - - /* Map READ_VAUX into READ_VOUT register on page 1 */ - if (page == 1) { - switch (reg) { - case PMBUS_READ_VOUT: - ret = pmbus_read_word_data(client, 0, - LM25066_READ_VAUX); - if (ret < 0) - break; - /* Adjust returned value to match VOUT coefficients */ - switch (data->id) { - case lm25066: - /* VOUT: 4.54 mV VAUX: 283.2 uV LSB */ - ret = DIV_ROUND_CLOSEST(ret * 2832, 45400); - break; - case lm5064: - /* VOUT: 4.53 mV VAUX: 700 uV LSB */ - ret = DIV_ROUND_CLOSEST(ret * 70, 453); - break; - case lm5066: - /* VOUT: 2.18 mV VAUX: 725 uV LSB */ - ret = DIV_ROUND_CLOSEST(ret * 725, 2180); - break; - } + switch (reg) { + case PMBUS_VIRT_READ_VMON: + ret = pmbus_read_word_data(client, 0, LM25066_READ_VAUX); + if (ret < 0) + break; + /* Adjust returned value to match VIN coefficients */ + switch (data->id) { + case lm25056: + /* VIN: 6.14 mV VAUX: 293 uV LSB */ + ret = DIV_ROUND_CLOSEST(ret * 293, 6140); break; - default: - /* No other valid registers on page 1 */ - ret = -ENXIO; + case lm25066: + /* VIN: 4.54 mV VAUX: 283.2 uV LSB */ + ret = DIV_ROUND_CLOSEST(ret * 2832, 45400); + break; + case lm5064: + /* VIN: 4.53 mV VAUX: 700 uV LSB */ + ret = DIV_ROUND_CLOSEST(ret * 70, 453); + break; + case lm5066: + /* VIN: 2.18 mV VAUX: 725 uV LSB */ + ret = DIV_ROUND_CLOSEST(ret * 725, 2180); break; } - goto done; - } - - switch (reg) { + break; case PMBUS_READ_IIN: ret = pmbus_read_word_data(client, 0, LM25066_MFR_READ_IIN); break; @@ -128,7 +250,58 @@ static int lm25066_read_word_data(struct i2c_client *client, int page, int reg) ret = -ENODATA; break; } -done: + return ret; +} + +static int lm25056_read_word_data(struct i2c_client *client, int page, int reg) +{ + int ret; + + switch (reg) { + case PMBUS_VIRT_VMON_UV_WARN_LIMIT: + ret = pmbus_read_word_data(client, 0, + LM25056_VAUX_UV_WARN_LIMIT); + if (ret < 0) + break; + /* Adjust returned value to match VIN coefficients */ + ret = DIV_ROUND_CLOSEST(ret * 293, 6140); + break; + case PMBUS_VIRT_VMON_OV_WARN_LIMIT: + ret = pmbus_read_word_data(client, 0, + LM25056_VAUX_OV_WARN_LIMIT); + if (ret < 0) + break; + /* Adjust returned value to match VIN coefficients */ + ret = DIV_ROUND_CLOSEST(ret * 293, 6140); + break; + default: + ret = lm25066_read_word_data(client, page, reg); + break; + } + return ret; +} + +static int lm25056_read_byte_data(struct i2c_client *client, int page, int reg) +{ + int ret, s; + + switch (reg) { + case PMBUS_VIRT_STATUS_VMON: + ret = pmbus_read_byte_data(client, 0, + PMBUS_STATUS_MFR_SPECIFIC); + if (ret < 0) + break; + s = 0; + if (ret & LM25056_MFR_STS_VAUX_UV_WARN) + s |= PB_VOLTAGE_UV_WARNING; + if (ret & LM25056_MFR_STS_VAUX_OV_WARN) + s |= PB_VOLTAGE_OV_WARNING; + ret = s; + break; + default: + ret = -ENODATA; + break; + } return ret; } @@ -137,19 +310,45 @@ static int lm25066_write_word_data(struct i2c_client *client, int page, int reg, { int ret; - if (page > 1) - return -ENXIO; - switch (reg) { + case PMBUS_VOUT_UV_WARN_LIMIT: + case PMBUS_OT_FAULT_LIMIT: + case PMBUS_OT_WARN_LIMIT: + case PMBUS_VIN_UV_WARN_LIMIT: + case PMBUS_VIN_OV_WARN_LIMIT: + word = ((s16)word < 0) ? 0 : clamp_val(word, 0, 0x0fff); + ret = pmbus_write_word_data(client, 0, reg, word); + pmbus_clear_cache(client); + break; case PMBUS_IIN_OC_WARN_LIMIT: + word = ((s16)word < 0) ? 0 : clamp_val(word, 0, 0x0fff); ret = pmbus_write_word_data(client, 0, LM25066_MFR_IIN_OC_WARN_LIMIT, word); + pmbus_clear_cache(client); break; case PMBUS_PIN_OP_WARN_LIMIT: + word = ((s16)word < 0) ? 0 : clamp_val(word, 0, 0x0fff); ret = pmbus_write_word_data(client, 0, LM25066_MFR_PIN_OP_WARN_LIMIT, word); + pmbus_clear_cache(client); + break; + case PMBUS_VIRT_VMON_UV_WARN_LIMIT: + /* Adjust from VIN coefficients (for LM25056) */ + word = DIV_ROUND_CLOSEST((int)word * 6140, 293); + word = ((s16)word < 0) ? 0 : clamp_val(word, 0, 0x0fff); + ret = pmbus_write_word_data(client, 0, + LM25056_VAUX_UV_WARN_LIMIT, word); + pmbus_clear_cache(client); + break; + case PMBUS_VIRT_VMON_OV_WARN_LIMIT: + /* Adjust from VIN coefficients (for LM25056) */ + word = DIV_ROUND_CLOSEST((int)word * 6140, 293); + word = ((s16)word < 0) ? 0 : clamp_val(word, 0, 0x0fff); + ret = pmbus_write_word_data(client, 0, + LM25056_VAUX_OV_WARN_LIMIT, word); + pmbus_clear_cache(client); break; case PMBUS_VIRT_RESET_PIN_HISTORY: ret = pmbus_write_byte(client, 0, LM25066_CLEAR_PIN_PEAK); @@ -161,23 +360,13 @@ static int lm25066_write_word_data(struct i2c_client *client, int page, int reg, return ret; } -static int lm25066_write_byte(struct i2c_client *client, int page, u8 value) -{ - if (page > 1) - return -ENXIO; - - if (page <= 0) - return pmbus_write_byte(client, page, value); - - return 0; -} - static int lm25066_probe(struct i2c_client *client, const struct i2c_device_id *id) { int config; struct lm25066_data *data; struct pmbus_driver_info *info; + struct __coeff *coeff; if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_READ_BYTE_DATA)) @@ -195,107 +384,54 @@ static int lm25066_probe(struct i2c_client *client, data->id = id->driver_data; info = &data->info; - info->pages = 2; + info->pages = 1; info->format[PSC_VOLTAGE_IN] = direct; info->format[PSC_VOLTAGE_OUT] = direct; info->format[PSC_CURRENT_IN] = direct; info->format[PSC_TEMPERATURE] = direct; info->format[PSC_POWER] = direct; - info->m[PSC_TEMPERATURE] = 16; - info->b[PSC_TEMPERATURE] = 0; - info->R[PSC_TEMPERATURE] = 0; - - info->func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_VOUT - | PMBUS_HAVE_STATUS_VOUT | PMBUS_HAVE_PIN | PMBUS_HAVE_IIN - | PMBUS_HAVE_STATUS_INPUT | PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP; - info->func[1] = PMBUS_HAVE_VOUT; - - info->read_word_data = lm25066_read_word_data; + info->func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_VMON + | PMBUS_HAVE_PIN | PMBUS_HAVE_IIN | PMBUS_HAVE_STATUS_INPUT + | PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP; + + if (data->id == lm25056) { + info->func[0] |= PMBUS_HAVE_STATUS_VMON; + info->read_word_data = lm25056_read_word_data; + info->read_byte_data = lm25056_read_byte_data; + } else { + info->func[0] |= PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT; + info->read_word_data = lm25066_read_word_data; + } info->write_word_data = lm25066_write_word_data; - info->write_byte = lm25066_write_byte; - - switch (id->driver_data) { - case lm25066: - info->m[PSC_VOLTAGE_IN] = 22070; - info->b[PSC_VOLTAGE_IN] = 0; - info->R[PSC_VOLTAGE_IN] = -2; - info->m[PSC_VOLTAGE_OUT] = 22070; - info->b[PSC_VOLTAGE_OUT] = 0; - info->R[PSC_VOLTAGE_OUT] = -2; - - if (config & LM25066_DEV_SETUP_CL) { - info->m[PSC_CURRENT_IN] = 6852; - info->b[PSC_CURRENT_IN] = 0; - info->R[PSC_CURRENT_IN] = -2; - info->m[PSC_POWER] = 369; - info->b[PSC_POWER] = 0; - info->R[PSC_POWER] = -2; - } else { - info->m[PSC_CURRENT_IN] = 13661; - info->b[PSC_CURRENT_IN] = 0; - info->R[PSC_CURRENT_IN] = -2; - info->m[PSC_POWER] = 736; - info->b[PSC_POWER] = 0; - info->R[PSC_POWER] = -2; - } - break; - case lm5064: - info->m[PSC_VOLTAGE_IN] = 22075; - info->b[PSC_VOLTAGE_IN] = 0; - info->R[PSC_VOLTAGE_IN] = -2; - info->m[PSC_VOLTAGE_OUT] = 22075; - info->b[PSC_VOLTAGE_OUT] = 0; - info->R[PSC_VOLTAGE_OUT] = -2; - - if (config & LM25066_DEV_SETUP_CL) { - info->m[PSC_CURRENT_IN] = 6713; - info->b[PSC_CURRENT_IN] = 0; - info->R[PSC_CURRENT_IN] = -2; - info->m[PSC_POWER] = 3619; - info->b[PSC_POWER] = 0; - info->R[PSC_POWER] = -3; - } else { - info->m[PSC_CURRENT_IN] = 13426; - info->b[PSC_CURRENT_IN] = 0; - info->R[PSC_CURRENT_IN] = -2; - info->m[PSC_POWER] = 7238; - info->b[PSC_POWER] = 0; - info->R[PSC_POWER] = -3; - } - break; - case lm5066: - info->m[PSC_VOLTAGE_IN] = 4587; - info->b[PSC_VOLTAGE_IN] = 0; - info->R[PSC_VOLTAGE_IN] = -2; - info->m[PSC_VOLTAGE_OUT] = 4587; - info->b[PSC_VOLTAGE_OUT] = 0; - info->R[PSC_VOLTAGE_OUT] = -2; - - if (config & LM25066_DEV_SETUP_CL) { - info->m[PSC_CURRENT_IN] = 10753; - info->b[PSC_CURRENT_IN] = 0; - info->R[PSC_CURRENT_IN] = -2; - info->m[PSC_POWER] = 1204; - info->b[PSC_POWER] = 0; - info->R[PSC_POWER] = -3; - } else { - info->m[PSC_CURRENT_IN] = 5405; - info->b[PSC_CURRENT_IN] = 0; - info->R[PSC_CURRENT_IN] = -2; - info->m[PSC_POWER] = 605; - info->b[PSC_POWER] = 0; - info->R[PSC_POWER] = -3; - } - break; - default: - return -ENODEV; + + coeff = &lm25066_coeff[data->id][0]; + info->m[PSC_TEMPERATURE] = coeff[PSC_TEMPERATURE].m; + info->b[PSC_TEMPERATURE] = coeff[PSC_TEMPERATURE].b; + info->R[PSC_TEMPERATURE] = coeff[PSC_TEMPERATURE].R; + info->m[PSC_VOLTAGE_IN] = coeff[PSC_VOLTAGE_IN].m; + info->b[PSC_VOLTAGE_IN] = coeff[PSC_VOLTAGE_IN].b; + info->R[PSC_VOLTAGE_IN] = coeff[PSC_VOLTAGE_IN].R; + info->m[PSC_VOLTAGE_OUT] = coeff[PSC_VOLTAGE_OUT].m; + info->b[PSC_VOLTAGE_OUT] = coeff[PSC_VOLTAGE_OUT].b; + info->R[PSC_VOLTAGE_OUT] = coeff[PSC_VOLTAGE_OUT].R; + info->b[PSC_CURRENT_IN] = coeff[PSC_CURRENT_IN].b; + info->R[PSC_CURRENT_IN] = coeff[PSC_CURRENT_IN].R; + info->b[PSC_POWER] = coeff[PSC_POWER].b; + info->R[PSC_POWER] = coeff[PSC_POWER].R; + if (config & LM25066_DEV_SETUP_CL) { + info->m[PSC_CURRENT_IN] = coeff[PSC_CURRENT_IN_L].m; + info->m[PSC_POWER] = coeff[PSC_POWER_L].m; + } else { + info->m[PSC_CURRENT_IN] = coeff[PSC_CURRENT_IN].m; + info->m[PSC_POWER] = coeff[PSC_POWER].m; } return pmbus_do_probe(client, id, info); } static const struct i2c_device_id lm25066_id[] = { + {"lm25056", lm25056}, {"lm25066", lm25066}, {"lm5064", lm5064}, {"lm5066", lm5066}, @@ -317,5 +453,5 @@ static struct i2c_driver lm25066_driver = { module_i2c_driver(lm25066_driver); MODULE_AUTHOR("Guenter Roeck"); -MODULE_DESCRIPTION("PMBus driver for LM25066/LM5064/LM5066"); +MODULE_DESCRIPTION("PMBus driver for LM25056/LM25066/LM5064/LM5066"); MODULE_LICENSE("GPL"); diff --git a/drivers/hwmon/pmbus/ltc2978.c b/drivers/hwmon/pmbus/ltc2978.c index 6d6130752f94..586a89ef9e0f 100644 --- a/drivers/hwmon/pmbus/ltc2978.c +++ b/drivers/hwmon/pmbus/ltc2978.c @@ -1,7 +1,8 @@ /* - * Hardware monitoring driver for LTC2978 and LTC3880 + * Hardware monitoring driver for LTC2974, LTC2978, LTC3880, and LTC3883 * * Copyright (c) 2011 Ericsson AB. + * Copyright (c) 2013 Guenter Roeck * * 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 @@ -26,28 +27,43 @@ #include <linux/i2c.h> #include "pmbus.h" -enum chips { ltc2978, ltc3880 }; +enum chips { ltc2974, ltc2978, ltc3880, ltc3883 }; -/* LTC2978 and LTC3880 */ +/* Common for all chips */ #define LTC2978_MFR_VOUT_PEAK 0xdd #define LTC2978_MFR_VIN_PEAK 0xde #define LTC2978_MFR_TEMPERATURE_PEAK 0xdf #define LTC2978_MFR_SPECIAL_ID 0xe7 -/* LTC2978 only */ +/* LTC2974 and LTC2978 */ #define LTC2978_MFR_VOUT_MIN 0xfb #define LTC2978_MFR_VIN_MIN 0xfc #define LTC2978_MFR_TEMPERATURE_MIN 0xfd -/* LTC3880 only */ +/* LTC2974 only */ +#define LTC2974_MFR_IOUT_PEAK 0xd7 +#define LTC2974_MFR_IOUT_MIN 0xd8 + +/* LTC3880 and LTC3883 */ #define LTC3880_MFR_IOUT_PEAK 0xd7 #define LTC3880_MFR_CLEAR_PEAKS 0xe3 #define LTC3880_MFR_TEMPERATURE2_PEAK 0xf4 +/* LTC3883 only */ +#define LTC3883_MFR_IIN_PEAK 0xe1 + +#define LTC2974_ID 0x0212 #define LTC2978_ID_REV1 0x0121 #define LTC2978_ID_REV2 0x0122 #define LTC3880_ID 0x4000 #define LTC3880_ID_MASK 0xff00 +#define LTC3883_ID 0x4300 +#define LTC3883_ID_MASK 0xff00 + +#define LTC2974_NUM_PAGES 4 +#define LTC2978_NUM_PAGES 8 +#define LTC3880_NUM_PAGES 2 +#define LTC3883_NUM_PAGES 1 /* * LTC2978 clears peak data whenever the CLEAR_FAULTS command is executed, which @@ -56,13 +72,15 @@ enum chips { ltc2978, ltc3880 }; * internal cache of measured peak data, which is only cleared if an explicit * "clear peak" command is executed for the sensor in question. */ + struct ltc2978_data { enum chips id; - int vin_min, vin_max; - int temp_min, temp_max[2]; - int vout_min[8], vout_max[8]; - int iout_max[2]; - int temp2_max; + u16 vin_min, vin_max; + u16 temp_min[LTC2974_NUM_PAGES], temp_max[LTC2974_NUM_PAGES]; + u16 vout_min[LTC2978_NUM_PAGES], vout_max[LTC2978_NUM_PAGES]; + u16 iout_min[LTC2974_NUM_PAGES], iout_max[LTC2974_NUM_PAGES]; + u16 iin_max; + u16 temp2_max; struct pmbus_driver_info info; }; @@ -167,9 +185,9 @@ static int ltc2978_read_word_data(struct i2c_client *client, int page, int reg) LTC2978_MFR_TEMPERATURE_MIN); if (ret >= 0) { if (lin11_to_val(ret) - < lin11_to_val(data->temp_min)) - data->temp_min = ret; - ret = data->temp_min; + < lin11_to_val(data->temp_min[page])) + data->temp_min[page] = ret; + ret = data->temp_min[page]; } break; case PMBUS_VIRT_READ_IOUT_MAX: @@ -185,6 +203,41 @@ static int ltc2978_read_word_data(struct i2c_client *client, int page, int reg) return ret; } +static int ltc2974_read_word_data(struct i2c_client *client, int page, int reg) +{ + const struct pmbus_driver_info *info = pmbus_get_driver_info(client); + struct ltc2978_data *data = to_ltc2978_data(info); + int ret; + + switch (reg) { + case PMBUS_VIRT_READ_IOUT_MAX: + ret = pmbus_read_word_data(client, page, LTC2974_MFR_IOUT_PEAK); + if (ret >= 0) { + if (lin11_to_val(ret) + > lin11_to_val(data->iout_max[page])) + data->iout_max[page] = ret; + ret = data->iout_max[page]; + } + break; + case PMBUS_VIRT_READ_IOUT_MIN: + ret = pmbus_read_word_data(client, page, LTC2974_MFR_IOUT_MIN); + if (ret >= 0) { + if (lin11_to_val(ret) + < lin11_to_val(data->iout_min[page])) + data->iout_min[page] = ret; + ret = data->iout_min[page]; + } + break; + case PMBUS_VIRT_RESET_IOUT_HISTORY: + ret = 0; + break; + default: + ret = ltc2978_read_word_data(client, page, reg); + break; + } + return ret; +} + static int ltc3880_read_word_data(struct i2c_client *client, int page, int reg) { const struct pmbus_driver_info *info = pmbus_get_driver_info(client); @@ -226,15 +279,41 @@ static int ltc3880_read_word_data(struct i2c_client *client, int page, int reg) return ret; } +static int ltc3883_read_word_data(struct i2c_client *client, int page, int reg) +{ + const struct pmbus_driver_info *info = pmbus_get_driver_info(client); + struct ltc2978_data *data = to_ltc2978_data(info); + int ret; + + switch (reg) { + case PMBUS_VIRT_READ_IIN_MAX: + ret = pmbus_read_word_data(client, page, LTC3883_MFR_IIN_PEAK); + if (ret >= 0) { + if (lin11_to_val(ret) + > lin11_to_val(data->iin_max)) + data->iin_max = ret; + ret = data->iin_max; + } + break; + case PMBUS_VIRT_RESET_IIN_HISTORY: + ret = 0; + break; + default: + ret = ltc3880_read_word_data(client, page, reg); + break; + } + return ret; +} + static int ltc2978_clear_peaks(struct i2c_client *client, int page, enum chips id) { int ret; - if (id == ltc2978) - ret = pmbus_write_byte(client, page, PMBUS_CLEAR_FAULTS); - else + if (id == ltc3880 || id == ltc3883) ret = pmbus_write_byte(client, 0, LTC3880_MFR_CLEAR_PEAKS); + else + ret = pmbus_write_byte(client, page, PMBUS_CLEAR_FAULTS); return ret; } @@ -247,8 +326,13 @@ static int ltc2978_write_word_data(struct i2c_client *client, int page, int ret; switch (reg) { + case PMBUS_VIRT_RESET_IIN_HISTORY: + data->iin_max = 0x7c00; + ret = ltc2978_clear_peaks(client, page, data->id); + break; case PMBUS_VIRT_RESET_IOUT_HISTORY: data->iout_max[page] = 0x7c00; + data->iout_min[page] = 0xfbff; ret = ltc2978_clear_peaks(client, page, data->id); break; case PMBUS_VIRT_RESET_TEMP2_HISTORY: @@ -266,7 +350,7 @@ static int ltc2978_write_word_data(struct i2c_client *client, int page, ret = ltc2978_clear_peaks(client, page, data->id); break; case PMBUS_VIRT_RESET_TEMP_HISTORY: - data->temp_min = 0x7bff; + data->temp_min[page] = 0x7bff; data->temp_max[page] = 0x7c00; ret = ltc2978_clear_peaks(client, page, data->id); break; @@ -278,8 +362,10 @@ static int ltc2978_write_word_data(struct i2c_client *client, int page, } static const struct i2c_device_id ltc2978_id[] = { + {"ltc2974", ltc2974}, {"ltc2978", ltc2978}, {"ltc3880", ltc3880}, + {"ltc3883", ltc3883}, {} }; MODULE_DEVICE_TABLE(i2c, ltc2978_id); @@ -304,10 +390,14 @@ static int ltc2978_probe(struct i2c_client *client, if (chip_id < 0) return chip_id; - if (chip_id == LTC2978_ID_REV1 || chip_id == LTC2978_ID_REV2) { + if (chip_id == LTC2974_ID) { + data->id = ltc2974; + } else if (chip_id == LTC2978_ID_REV1 || chip_id == LTC2978_ID_REV2) { data->id = ltc2978; } else if ((chip_id & LTC3880_ID_MASK) == LTC3880_ID) { data->id = ltc3880; + } else if ((chip_id & LTC3883_ID_MASK) == LTC3883_ID) { + data->id = ltc3883; } else { dev_err(&client->dev, "Unsupported chip ID 0x%x\n", chip_id); return -ENODEV; @@ -323,26 +413,45 @@ static int ltc2978_probe(struct i2c_client *client, data->vin_min = 0x7bff; data->vin_max = 0x7c00; - data->temp_min = 0x7bff; + for (i = 0; i < ARRAY_SIZE(data->vout_min); i++) + data->vout_min[i] = 0xffff; + for (i = 0; i < ARRAY_SIZE(data->iout_min); i++) + data->iout_min[i] = 0xfbff; + for (i = 0; i < ARRAY_SIZE(data->iout_max); i++) + data->iout_max[i] = 0x7c00; + for (i = 0; i < ARRAY_SIZE(data->temp_min); i++) + data->temp_min[i] = 0x7bff; for (i = 0; i < ARRAY_SIZE(data->temp_max); i++) data->temp_max[i] = 0x7c00; data->temp2_max = 0x7c00; switch (data->id) { + case ltc2974: + info->read_word_data = ltc2974_read_word_data; + info->pages = LTC2974_NUM_PAGES; + info->func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_STATUS_INPUT + | PMBUS_HAVE_TEMP2; + for (i = 0; i < info->pages; i++) { + info->func[i] |= PMBUS_HAVE_VOUT + | PMBUS_HAVE_STATUS_VOUT | PMBUS_HAVE_POUT + | PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP + | PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT; + } + break; case ltc2978: info->read_word_data = ltc2978_read_word_data; - info->pages = 8; + info->pages = LTC2978_NUM_PAGES; info->func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_STATUS_INPUT | PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT | PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP; - for (i = 1; i < 8; i++) { + for (i = 1; i < LTC2978_NUM_PAGES; i++) { info->func[i] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT; } break; case ltc3880: info->read_word_data = ltc3880_read_word_data; - info->pages = 2; + info->pages = LTC3880_NUM_PAGES; info->func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_IIN | PMBUS_HAVE_STATUS_INPUT | PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT @@ -353,15 +462,20 @@ static int ltc2978_probe(struct i2c_client *client, | PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT | PMBUS_HAVE_POUT | PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP; - data->iout_max[0] = 0x7c00; - data->iout_max[1] = 0x7c00; + break; + case ltc3883: + info->read_word_data = ltc3883_read_word_data; + info->pages = LTC3883_NUM_PAGES; + info->func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_IIN + | PMBUS_HAVE_STATUS_INPUT + | PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT + | PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT + | PMBUS_HAVE_PIN | PMBUS_HAVE_POUT | PMBUS_HAVE_TEMP + | PMBUS_HAVE_TEMP2 | PMBUS_HAVE_STATUS_TEMP; break; default: return -ENODEV; } - for (i = 0; i < info->pages; i++) - data->vout_min[i] = 0xffff; - return pmbus_do_probe(client, id, info); } @@ -378,5 +492,5 @@ static struct i2c_driver ltc2978_driver = { module_i2c_driver(ltc2978_driver); MODULE_AUTHOR("Guenter Roeck"); -MODULE_DESCRIPTION("PMBus driver for LTC2978 and LTC3880"); +MODULE_DESCRIPTION("PMBus driver for LTC2974, LTC2978, LTC3880, and LTC3883"); MODULE_LICENSE("GPL"); diff --git a/drivers/hwmon/s3c-hwmon.c b/drivers/hwmon/s3c-hwmon.c index ff2ae0252a48..a9f7e804f1e4 100644 --- a/drivers/hwmon/s3c-hwmon.c +++ b/drivers/hwmon/s3c-hwmon.c @@ -107,17 +107,14 @@ static ssize_t s3c_hwmon_show_raw(struct device *dev, return (ret < 0) ? ret : snprintf(buf, PAGE_SIZE, "%d\n", ret); } -#define DEF_ADC_ATTR(x) \ - static SENSOR_DEVICE_ATTR(adc##x##_raw, S_IRUGO, s3c_hwmon_show_raw, NULL, x) - -DEF_ADC_ATTR(0); -DEF_ADC_ATTR(1); -DEF_ADC_ATTR(2); -DEF_ADC_ATTR(3); -DEF_ADC_ATTR(4); -DEF_ADC_ATTR(5); -DEF_ADC_ATTR(6); -DEF_ADC_ATTR(7); +static SENSOR_DEVICE_ATTR(adc0_raw, S_IRUGO, s3c_hwmon_show_raw, NULL, 0); +static SENSOR_DEVICE_ATTR(adc1_raw, S_IRUGO, s3c_hwmon_show_raw, NULL, 1); +static SENSOR_DEVICE_ATTR(adc2_raw, S_IRUGO, s3c_hwmon_show_raw, NULL, 2); +static SENSOR_DEVICE_ATTR(adc3_raw, S_IRUGO, s3c_hwmon_show_raw, NULL, 3); +static SENSOR_DEVICE_ATTR(adc4_raw, S_IRUGO, s3c_hwmon_show_raw, NULL, 4); +static SENSOR_DEVICE_ATTR(adc5_raw, S_IRUGO, s3c_hwmon_show_raw, NULL, 5); +static SENSOR_DEVICE_ATTR(adc6_raw, S_IRUGO, s3c_hwmon_show_raw, NULL, 6); +static SENSOR_DEVICE_ATTR(adc7_raw, S_IRUGO, s3c_hwmon_show_raw, NULL, 7); static struct attribute *s3c_hwmon_attrs[9] = { &sensor_dev_attr_adc0_raw.dev_attr.attr, diff --git a/drivers/hwmon/sch56xx-common.c b/drivers/hwmon/sch56xx-common.c index d00b30adc34b..738681983284 100644 --- a/drivers/hwmon/sch56xx-common.c +++ b/drivers/hwmon/sch56xx-common.c @@ -161,8 +161,8 @@ static int sch56xx_send_cmd(u16 addr, u8 cmd, u16 reg, u8 v) break; } if (i == max_busy_polls + max_lazy_polls) { - pr_err("Max retries exceeded reading virtual " - "register 0x%04hx (%d)\n", reg, 1); + pr_err("Max retries exceeded reading virtual register 0x%04hx (%d)\n", + reg, 1); return -EIO; } @@ -178,12 +178,12 @@ static int sch56xx_send_cmd(u16 addr, u8 cmd, u16 reg, u8 v) break; if (i == 0) - pr_warn("EC reports: 0x%02x reading virtual register " - "0x%04hx\n", (unsigned int)val, reg); + pr_warn("EC reports: 0x%02x reading virtual register 0x%04hx\n", + (unsigned int)val, reg); } if (i == max_busy_polls) { - pr_err("Max retries exceeded reading virtual " - "register 0x%04hx (%d)\n", reg, 2); + pr_err("Max retries exceeded reading virtual register 0x%04hx (%d)\n", + reg, 2); return -EIO; } diff --git a/drivers/hwmon/sis5595.c b/drivers/hwmon/sis5595.c index c35847a1a0a3..1404e6319deb 100644 --- a/drivers/hwmon/sis5595.c +++ b/drivers/hwmon/sis5595.c @@ -456,8 +456,9 @@ static ssize_t set_fan_div(struct device *dev, struct device_attribute *da, data->fan_div[nr] = 3; break; default: - dev_err(dev, "fan_div value %ld not " - "supported. Choose one of 1, 2, 4 or 8!\n", val); + dev_err(dev, + "fan_div value %ld not supported. Choose one of 1, 2, 4 or 8!\n", + val); mutex_unlock(&data->update_lock); return -EINVAL; } diff --git a/drivers/hwmon/thmc50.c b/drivers/hwmon/thmc50.c index 4b59eb53b18a..db288db7d3e9 100644 --- a/drivers/hwmon/thmc50.c +++ b/drivers/hwmon/thmc50.c @@ -41,8 +41,8 @@ enum chips { thmc50, adm1022 }; static unsigned short adm1022_temp3[16]; static unsigned int adm1022_temp3_num; module_param_array(adm1022_temp3, ushort, &adm1022_temp3_num, 0); -MODULE_PARM_DESC(adm1022_temp3, "List of adapter,address pairs " - "to enable 3rd temperature (ADM1022 only)"); +MODULE_PARM_DESC(adm1022_temp3, + "List of adapter,address pairs to enable 3rd temperature (ADM1022 only)"); /* Many THMC50 constants specified below */ @@ -312,8 +312,7 @@ static int thmc50_detect(struct i2c_client *client, const char *type_name; if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) { - pr_debug("thmc50: detect failed, " - "smbus byte data not supported!\n"); + pr_debug("thmc50: detect failed, smbus byte data not supported!\n"); return -ENODEV; } diff --git a/drivers/hwmon/tmp102.c b/drivers/hwmon/tmp102.c index 523dd89ba498..d7b47abf37fe 100644 --- a/drivers/hwmon/tmp102.c +++ b/drivers/hwmon/tmp102.c @@ -155,8 +155,8 @@ static int tmp102_probe(struct i2c_client *client, if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_WORD_DATA)) { - dev_err(&client->dev, "adapter doesn't support SMBus word " - "transactions\n"); + dev_err(&client->dev, + "adapter doesn't support SMBus word transactions\n"); return -ENODEV; } diff --git a/drivers/hwmon/tmp401.c b/drivers/hwmon/tmp401.c index c85f6967ccc3..a478454f690f 100644 --- a/drivers/hwmon/tmp401.c +++ b/drivers/hwmon/tmp401.c @@ -5,6 +5,9 @@ * Gabriel Konat, Sander Leget, Wouter Willems * Copyright (C) 2009 Andre Prendel <andre.prendel@gmx.de> * + * Cleanup and support for TMP431 and TMP432 by Guenter Roeck + * Copyright (c) 2013 Guenter Roeck <linux@roeck-us.net> + * * 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 @@ -30,6 +33,7 @@ #include <linux/module.h> #include <linux/init.h> +#include <linux/bitops.h> #include <linux/slab.h> #include <linux/jiffies.h> #include <linux/i2c.h> @@ -40,9 +44,9 @@ #include <linux/sysfs.h> /* Addresses to scan */ -static const unsigned short normal_i2c[] = { 0x4c, I2C_CLIENT_END }; +static const unsigned short normal_i2c[] = { 0x4c, 0x4d, 0x4e, I2C_CLIENT_END }; -enum chips { tmp401, tmp411 }; +enum chips { tmp401, tmp411, tmp431, tmp432 }; /* * The TMP401 registers, note some registers have different addresses for @@ -54,42 +58,84 @@ enum chips { tmp401, tmp411 }; #define TMP401_CONVERSION_RATE_READ 0x04 #define TMP401_CONVERSION_RATE_WRITE 0x0A #define TMP401_TEMP_CRIT_HYST 0x21 -#define TMP401_CONSECUTIVE_ALERT 0x22 #define TMP401_MANUFACTURER_ID_REG 0xFE #define TMP401_DEVICE_ID_REG 0xFF -#define TMP411_N_FACTOR_REG 0x18 - -static const u8 TMP401_TEMP_MSB[2] = { 0x00, 0x01 }; -static const u8 TMP401_TEMP_LSB[2] = { 0x15, 0x10 }; -static const u8 TMP401_TEMP_LOW_LIMIT_MSB_READ[2] = { 0x06, 0x08 }; -static const u8 TMP401_TEMP_LOW_LIMIT_MSB_WRITE[2] = { 0x0C, 0x0E }; -static const u8 TMP401_TEMP_LOW_LIMIT_LSB[2] = { 0x17, 0x14 }; -static const u8 TMP401_TEMP_HIGH_LIMIT_MSB_READ[2] = { 0x05, 0x07 }; -static const u8 TMP401_TEMP_HIGH_LIMIT_MSB_WRITE[2] = { 0x0B, 0x0D }; -static const u8 TMP401_TEMP_HIGH_LIMIT_LSB[2] = { 0x16, 0x13 }; -/* These are called the THERM limit / hysteresis / mask in the datasheet */ -static const u8 TMP401_TEMP_CRIT_LIMIT[2] = { 0x20, 0x19 }; - -static const u8 TMP411_TEMP_LOWEST_MSB[2] = { 0x30, 0x34 }; -static const u8 TMP411_TEMP_LOWEST_LSB[2] = { 0x31, 0x35 }; -static const u8 TMP411_TEMP_HIGHEST_MSB[2] = { 0x32, 0x36 }; -static const u8 TMP411_TEMP_HIGHEST_LSB[2] = { 0x33, 0x37 }; + +static const u8 TMP401_TEMP_MSB_READ[6][2] = { + { 0x00, 0x01 }, /* temp */ + { 0x06, 0x08 }, /* low limit */ + { 0x05, 0x07 }, /* high limit */ + { 0x20, 0x19 }, /* therm (crit) limit */ + { 0x30, 0x34 }, /* lowest */ + { 0x32, 0x36 }, /* highest */ +}; + +static const u8 TMP401_TEMP_MSB_WRITE[6][2] = { + { 0, 0 }, /* temp (unused) */ + { 0x0C, 0x0E }, /* low limit */ + { 0x0B, 0x0D }, /* high limit */ + { 0x20, 0x19 }, /* therm (crit) limit */ + { 0x30, 0x34 }, /* lowest */ + { 0x32, 0x36 }, /* highest */ +}; + +static const u8 TMP401_TEMP_LSB[6][2] = { + { 0x15, 0x10 }, /* temp */ + { 0x17, 0x14 }, /* low limit */ + { 0x16, 0x13 }, /* high limit */ + { 0, 0 }, /* therm (crit) limit (unused) */ + { 0x31, 0x35 }, /* lowest */ + { 0x33, 0x37 }, /* highest */ +}; + +static const u8 TMP432_TEMP_MSB_READ[4][3] = { + { 0x00, 0x01, 0x23 }, /* temp */ + { 0x06, 0x08, 0x16 }, /* low limit */ + { 0x05, 0x07, 0x15 }, /* high limit */ + { 0x20, 0x19, 0x1A }, /* therm (crit) limit */ +}; + +static const u8 TMP432_TEMP_MSB_WRITE[4][3] = { + { 0, 0, 0 }, /* temp - unused */ + { 0x0C, 0x0E, 0x16 }, /* low limit */ + { 0x0B, 0x0D, 0x15 }, /* high limit */ + { 0x20, 0x19, 0x1A }, /* therm (crit) limit */ +}; + +static const u8 TMP432_TEMP_LSB[3][3] = { + { 0x29, 0x10, 0x24 }, /* temp */ + { 0x3E, 0x14, 0x18 }, /* low limit */ + { 0x3D, 0x13, 0x17 }, /* high limit */ +}; + +/* [0] = fault, [1] = low, [2] = high, [3] = therm/crit */ +static const u8 TMP432_STATUS_REG[] = { + 0x1b, 0x36, 0x35, 0x37 }; /* Flags */ -#define TMP401_CONFIG_RANGE 0x04 -#define TMP401_CONFIG_SHUTDOWN 0x40 -#define TMP401_STATUS_LOCAL_CRIT 0x01 -#define TMP401_STATUS_REMOTE_CRIT 0x02 -#define TMP401_STATUS_REMOTE_OPEN 0x04 -#define TMP401_STATUS_REMOTE_LOW 0x08 -#define TMP401_STATUS_REMOTE_HIGH 0x10 -#define TMP401_STATUS_LOCAL_LOW 0x20 -#define TMP401_STATUS_LOCAL_HIGH 0x40 +#define TMP401_CONFIG_RANGE BIT(2) +#define TMP401_CONFIG_SHUTDOWN BIT(6) +#define TMP401_STATUS_LOCAL_CRIT BIT(0) +#define TMP401_STATUS_REMOTE_CRIT BIT(1) +#define TMP401_STATUS_REMOTE_OPEN BIT(2) +#define TMP401_STATUS_REMOTE_LOW BIT(3) +#define TMP401_STATUS_REMOTE_HIGH BIT(4) +#define TMP401_STATUS_LOCAL_LOW BIT(5) +#define TMP401_STATUS_LOCAL_HIGH BIT(6) + +/* On TMP432, each status has its own register */ +#define TMP432_STATUS_LOCAL BIT(0) +#define TMP432_STATUS_REMOTE1 BIT(1) +#define TMP432_STATUS_REMOTE2 BIT(2) /* Manufacturer / Device ID's */ #define TMP401_MANUFACTURER_ID 0x55 #define TMP401_DEVICE_ID 0x11 -#define TMP411_DEVICE_ID 0x12 +#define TMP411A_DEVICE_ID 0x12 +#define TMP411B_DEVICE_ID 0x13 +#define TMP411C_DEVICE_ID 0x10 +#define TMP431_DEVICE_ID 0x31 +#define TMP432_DEVICE_ID 0x32 /* * Driver data (common to all clients) @@ -98,6 +144,8 @@ static const u8 TMP411_TEMP_HIGHEST_LSB[2] = { 0x33, 0x37 }; static const struct i2c_device_id tmp401_id[] = { { "tmp401", tmp401 }, { "tmp411", tmp411 }, + { "tmp431", tmp431 }, + { "tmp432", tmp432 }, { } }; MODULE_DEVICE_TABLE(i2c, tmp401_id); @@ -113,16 +161,13 @@ struct tmp401_data { unsigned long last_updated; /* in jiffies */ enum chips kind; + unsigned int update_interval; /* in milliseconds */ + /* register values */ - u8 status; + u8 status[4]; u8 config; - u16 temp[2]; - u16 temp_low[2]; - u16 temp_high[2]; - u8 temp_crit[2]; + u16 temp[6][3]; u8 temp_crit_hyst; - u16 temp_lowest[2]; - u16 temp_highest[2]; }; /* @@ -136,31 +181,10 @@ static int tmp401_register_to_temp(u16 reg, u8 config) if (config & TMP401_CONFIG_RANGE) temp -= 64 * 256; - return (temp * 625 + 80) / 160; -} - -static u16 tmp401_temp_to_register(long temp, u8 config) -{ - if (config & TMP401_CONFIG_RANGE) { - temp = clamp_val(temp, -64000, 191000); - temp += 64000; - } else - temp = clamp_val(temp, 0, 127000); - - return (temp * 160 + 312) / 625; -} - -static int tmp401_crit_register_to_temp(u8 reg, u8 config) -{ - int temp = reg; - - if (config & TMP401_CONFIG_RANGE) - temp -= 64; - - return temp * 1000; + return DIV_ROUND_CLOSEST(temp * 125, 32); } -static u8 tmp401_crit_temp_to_register(long temp, u8 config) +static u16 tmp401_temp_to_register(long temp, u8 config, int zbits) { if (config & TMP401_CONFIG_RANGE) { temp = clamp_val(temp, -64000, 191000); @@ -168,113 +192,127 @@ static u8 tmp401_crit_temp_to_register(long temp, u8 config) } else temp = clamp_val(temp, 0, 127000); - return (temp + 500) / 1000; + return DIV_ROUND_CLOSEST(temp * (1 << (8 - zbits)), 1000) << zbits; } -static struct tmp401_data *tmp401_update_device_reg16( - struct i2c_client *client, struct tmp401_data *data) +static int tmp401_update_device_reg16(struct i2c_client *client, + struct tmp401_data *data) { - int i; - - for (i = 0; i < 2; i++) { - /* - * High byte must be read first immediately followed - * by the low byte - */ - data->temp[i] = i2c_smbus_read_byte_data(client, - TMP401_TEMP_MSB[i]) << 8; - data->temp[i] |= i2c_smbus_read_byte_data(client, - TMP401_TEMP_LSB[i]); - data->temp_low[i] = i2c_smbus_read_byte_data(client, - TMP401_TEMP_LOW_LIMIT_MSB_READ[i]) << 8; - data->temp_low[i] |= i2c_smbus_read_byte_data(client, - TMP401_TEMP_LOW_LIMIT_LSB[i]); - data->temp_high[i] = i2c_smbus_read_byte_data(client, - TMP401_TEMP_HIGH_LIMIT_MSB_READ[i]) << 8; - data->temp_high[i] |= i2c_smbus_read_byte_data(client, - TMP401_TEMP_HIGH_LIMIT_LSB[i]); - data->temp_crit[i] = i2c_smbus_read_byte_data(client, - TMP401_TEMP_CRIT_LIMIT[i]); - - if (data->kind == tmp411) { - data->temp_lowest[i] = i2c_smbus_read_byte_data(client, - TMP411_TEMP_LOWEST_MSB[i]) << 8; - data->temp_lowest[i] |= i2c_smbus_read_byte_data( - client, TMP411_TEMP_LOWEST_LSB[i]); - - data->temp_highest[i] = i2c_smbus_read_byte_data( - client, TMP411_TEMP_HIGHEST_MSB[i]) << 8; - data->temp_highest[i] |= i2c_smbus_read_byte_data( - client, TMP411_TEMP_HIGHEST_LSB[i]); + int i, j, val; + int num_regs = data->kind == tmp411 ? 6 : 4; + int num_sensors = data->kind == tmp432 ? 3 : 2; + + for (i = 0; i < num_sensors; i++) { /* local / r1 / r2 */ + for (j = 0; j < num_regs; j++) { /* temp / low / ... */ + u8 regaddr; + /* + * High byte must be read first immediately followed + * by the low byte + */ + regaddr = data->kind == tmp432 ? + TMP432_TEMP_MSB_READ[j][i] : + TMP401_TEMP_MSB_READ[j][i]; + val = i2c_smbus_read_byte_data(client, regaddr); + if (val < 0) + return val; + data->temp[j][i] = val << 8; + if (j == 3) /* crit is msb only */ + continue; + regaddr = data->kind == tmp432 ? TMP432_TEMP_LSB[j][i] + : TMP401_TEMP_LSB[j][i]; + val = i2c_smbus_read_byte_data(client, regaddr); + if (val < 0) + return val; + data->temp[j][i] |= val; } } - return data; + return 0; } static struct tmp401_data *tmp401_update_device(struct device *dev) { struct i2c_client *client = to_i2c_client(dev); struct tmp401_data *data = i2c_get_clientdata(client); + struct tmp401_data *ret = data; + int i, val; + unsigned long next_update; mutex_lock(&data->update_lock); - if (time_after(jiffies, data->last_updated + HZ) || !data->valid) { - data->status = i2c_smbus_read_byte_data(client, TMP401_STATUS); - data->config = i2c_smbus_read_byte_data(client, - TMP401_CONFIG_READ); - tmp401_update_device_reg16(client, data); + next_update = data->last_updated + + msecs_to_jiffies(data->update_interval) + 1; + if (time_after(jiffies, next_update) || !data->valid) { + if (data->kind != tmp432) { + /* + * The driver uses the TMP432 status format internally. + * Convert status to TMP432 format for other chips. + */ + val = i2c_smbus_read_byte_data(client, TMP401_STATUS); + if (val < 0) { + ret = ERR_PTR(val); + goto abort; + } + data->status[0] = + (val & TMP401_STATUS_REMOTE_OPEN) >> 1; + data->status[1] = + ((val & TMP401_STATUS_REMOTE_LOW) >> 2) | + ((val & TMP401_STATUS_LOCAL_LOW) >> 5); + data->status[2] = + ((val & TMP401_STATUS_REMOTE_HIGH) >> 3) | + ((val & TMP401_STATUS_LOCAL_HIGH) >> 6); + data->status[3] = val & (TMP401_STATUS_LOCAL_CRIT + | TMP401_STATUS_REMOTE_CRIT); + } else { + for (i = 0; i < ARRAY_SIZE(data->status); i++) { + val = i2c_smbus_read_byte_data(client, + TMP432_STATUS_REG[i]); + if (val < 0) { + ret = ERR_PTR(val); + goto abort; + } + data->status[i] = val; + } + } - data->temp_crit_hyst = i2c_smbus_read_byte_data(client, - TMP401_TEMP_CRIT_HYST); + val = i2c_smbus_read_byte_data(client, TMP401_CONFIG_READ); + if (val < 0) { + ret = ERR_PTR(val); + goto abort; + } + data->config = val; + val = tmp401_update_device_reg16(client, data); + if (val < 0) { + ret = ERR_PTR(val); + goto abort; + } + val = i2c_smbus_read_byte_data(client, TMP401_TEMP_CRIT_HYST); + if (val < 0) { + ret = ERR_PTR(val); + goto abort; + } + data->temp_crit_hyst = val; data->last_updated = jiffies; data->valid = 1; } +abort: mutex_unlock(&data->update_lock); - - return data; -} - -static ssize_t show_temp_value(struct device *dev, - struct device_attribute *devattr, char *buf) -{ - int index = to_sensor_dev_attr(devattr)->index; - struct tmp401_data *data = tmp401_update_device(dev); - - return sprintf(buf, "%d\n", - tmp401_register_to_temp(data->temp[index], data->config)); -} - -static ssize_t show_temp_min(struct device *dev, - struct device_attribute *devattr, char *buf) -{ - int index = to_sensor_dev_attr(devattr)->index; - struct tmp401_data *data = tmp401_update_device(dev); - - return sprintf(buf, "%d\n", - tmp401_register_to_temp(data->temp_low[index], data->config)); + return ret; } -static ssize_t show_temp_max(struct device *dev, - struct device_attribute *devattr, char *buf) +static ssize_t show_temp(struct device *dev, + struct device_attribute *devattr, char *buf) { - int index = to_sensor_dev_attr(devattr)->index; + int nr = to_sensor_dev_attr_2(devattr)->nr; + int index = to_sensor_dev_attr_2(devattr)->index; struct tmp401_data *data = tmp401_update_device(dev); - return sprintf(buf, "%d\n", - tmp401_register_to_temp(data->temp_high[index], data->config)); -} - -static ssize_t show_temp_crit(struct device *dev, - struct device_attribute *devattr, char *buf) -{ - int index = to_sensor_dev_attr(devattr)->index; - struct tmp401_data *data = tmp401_update_device(dev); + if (IS_ERR(data)) + return PTR_ERR(data); return sprintf(buf, "%d\n", - tmp401_crit_register_to_temp(data->temp_crit[index], - data->config)); + tmp401_register_to_temp(data->temp[nr][index], data->config)); } static ssize_t show_temp_crit_hyst(struct device *dev, @@ -283,122 +321,60 @@ static ssize_t show_temp_crit_hyst(struct device *dev, int temp, index = to_sensor_dev_attr(devattr)->index; struct tmp401_data *data = tmp401_update_device(dev); + if (IS_ERR(data)) + return PTR_ERR(data); + mutex_lock(&data->update_lock); - temp = tmp401_crit_register_to_temp(data->temp_crit[index], - data->config); + temp = tmp401_register_to_temp(data->temp[3][index], data->config); temp -= data->temp_crit_hyst * 1000; mutex_unlock(&data->update_lock); return sprintf(buf, "%d\n", temp); } -static ssize_t show_temp_lowest(struct device *dev, - struct device_attribute *devattr, char *buf) -{ - int index = to_sensor_dev_attr(devattr)->index; - struct tmp401_data *data = tmp401_update_device(dev); - - return sprintf(buf, "%d\n", - tmp401_register_to_temp(data->temp_lowest[index], - data->config)); -} - -static ssize_t show_temp_highest(struct device *dev, - struct device_attribute *devattr, char *buf) -{ - int index = to_sensor_dev_attr(devattr)->index; - struct tmp401_data *data = tmp401_update_device(dev); - - return sprintf(buf, "%d\n", - tmp401_register_to_temp(data->temp_highest[index], - data->config)); -} - static ssize_t show_status(struct device *dev, struct device_attribute *devattr, char *buf) { - int mask = to_sensor_dev_attr(devattr)->index; - struct tmp401_data *data = tmp401_update_device(dev); - - if (data->status & mask) - return sprintf(buf, "1\n"); - else - return sprintf(buf, "0\n"); -} - -static ssize_t store_temp_min(struct device *dev, struct device_attribute - *devattr, const char *buf, size_t count) -{ - int index = to_sensor_dev_attr(devattr)->index; + int nr = to_sensor_dev_attr_2(devattr)->nr; + int mask = to_sensor_dev_attr_2(devattr)->index; struct tmp401_data *data = tmp401_update_device(dev); - long val; - u16 reg; - - if (kstrtol(buf, 10, &val)) - return -EINVAL; - - reg = tmp401_temp_to_register(val, data->config); - - mutex_lock(&data->update_lock); - - i2c_smbus_write_byte_data(to_i2c_client(dev), - TMP401_TEMP_LOW_LIMIT_MSB_WRITE[index], reg >> 8); - i2c_smbus_write_byte_data(to_i2c_client(dev), - TMP401_TEMP_LOW_LIMIT_LSB[index], reg & 0xFF); - - data->temp_low[index] = reg; - mutex_unlock(&data->update_lock); + if (IS_ERR(data)) + return PTR_ERR(data); - return count; + return sprintf(buf, "%d\n", !!(data->status[nr] & mask)); } -static ssize_t store_temp_max(struct device *dev, struct device_attribute - *devattr, const char *buf, size_t count) +static ssize_t store_temp(struct device *dev, struct device_attribute *devattr, + const char *buf, size_t count) { - int index = to_sensor_dev_attr(devattr)->index; + int nr = to_sensor_dev_attr_2(devattr)->nr; + int index = to_sensor_dev_attr_2(devattr)->index; + struct i2c_client *client = to_i2c_client(dev); struct tmp401_data *data = tmp401_update_device(dev); long val; u16 reg; + u8 regaddr; - if (kstrtol(buf, 10, &val)) - return -EINVAL; - - reg = tmp401_temp_to_register(val, data->config); - - mutex_lock(&data->update_lock); - - i2c_smbus_write_byte_data(to_i2c_client(dev), - TMP401_TEMP_HIGH_LIMIT_MSB_WRITE[index], reg >> 8); - i2c_smbus_write_byte_data(to_i2c_client(dev), - TMP401_TEMP_HIGH_LIMIT_LSB[index], reg & 0xFF); - - data->temp_high[index] = reg; - - mutex_unlock(&data->update_lock); - - return count; -} - -static ssize_t store_temp_crit(struct device *dev, struct device_attribute - *devattr, const char *buf, size_t count) -{ - int index = to_sensor_dev_attr(devattr)->index; - struct tmp401_data *data = tmp401_update_device(dev); - long val; - u8 reg; + if (IS_ERR(data)) + return PTR_ERR(data); if (kstrtol(buf, 10, &val)) return -EINVAL; - reg = tmp401_crit_temp_to_register(val, data->config); + reg = tmp401_temp_to_register(val, data->config, nr == 3 ? 8 : 4); mutex_lock(&data->update_lock); - i2c_smbus_write_byte_data(to_i2c_client(dev), - TMP401_TEMP_CRIT_LIMIT[index], reg); - - data->temp_crit[index] = reg; + regaddr = data->kind == tmp432 ? TMP432_TEMP_MSB_WRITE[nr][index] + : TMP401_TEMP_MSB_WRITE[nr][index]; + i2c_smbus_write_byte_data(client, regaddr, reg >> 8); + if (nr != 3) { + regaddr = data->kind == tmp432 ? TMP432_TEMP_LSB[nr][index] + : TMP401_TEMP_LSB[nr][index]; + i2c_smbus_write_byte_data(client, regaddr, reg & 0xFF); + } + data->temp[nr][index] = reg; mutex_unlock(&data->update_lock); @@ -413,6 +389,9 @@ static ssize_t store_temp_crit_hyst(struct device *dev, struct device_attribute long val; u8 reg; + if (IS_ERR(data)) + return PTR_ERR(data); + if (kstrtol(buf, 10, &val)) return -EINVAL; @@ -422,13 +401,12 @@ static ssize_t store_temp_crit_hyst(struct device *dev, struct device_attribute val = clamp_val(val, 0, 127000); mutex_lock(&data->update_lock); - temp = tmp401_crit_register_to_temp(data->temp_crit[index], - data->config); + temp = tmp401_register_to_temp(data->temp[3][index], data->config); val = clamp_val(val, temp - 255000, temp); reg = ((temp - val) + 500) / 1000; - i2c_smbus_write_byte_data(to_i2c_client(dev), - TMP401_TEMP_CRIT_HYST, reg); + i2c_smbus_write_byte_data(to_i2c_client(dev), TMP401_TEMP_CRIT_HYST, + reg); data->temp_crit_hyst = reg; @@ -445,54 +423,130 @@ static ssize_t store_temp_crit_hyst(struct device *dev, struct device_attribute static ssize_t reset_temp_history(struct device *dev, struct device_attribute *devattr, const char *buf, size_t count) { + struct i2c_client *client = to_i2c_client(dev); + struct tmp401_data *data = i2c_get_clientdata(client); long val; if (kstrtol(buf, 10, &val)) return -EINVAL; if (val != 1) { - dev_err(dev, "temp_reset_history value %ld not" - " supported. Use 1 to reset the history!\n", val); + dev_err(dev, + "temp_reset_history value %ld not supported. Use 1 to reset the history!\n", + val); return -EINVAL; } - i2c_smbus_write_byte_data(to_i2c_client(dev), - TMP411_TEMP_LOWEST_MSB[0], val); + mutex_lock(&data->update_lock); + i2c_smbus_write_byte_data(client, TMP401_TEMP_MSB_WRITE[5][0], val); + data->valid = 0; + mutex_unlock(&data->update_lock); + + return count; +} + +static ssize_t show_update_interval(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + struct tmp401_data *data = i2c_get_clientdata(client); + + return sprintf(buf, "%u\n", data->update_interval); +} + +static ssize_t set_update_interval(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct i2c_client *client = to_i2c_client(dev); + struct tmp401_data *data = i2c_get_clientdata(client); + unsigned long val; + int err, rate; + + err = kstrtoul(buf, 10, &val); + if (err) + return err; + + /* + * For valid rates, interval can be calculated as + * interval = (1 << (7 - rate)) * 125; + * Rounded rate is therefore + * rate = 7 - __fls(interval * 4 / (125 * 3)); + * Use clamp_val() to avoid overflows, and to ensure valid input + * for __fls. + */ + val = clamp_val(val, 125, 16000); + rate = 7 - __fls(val * 4 / (125 * 3)); + mutex_lock(&data->update_lock); + i2c_smbus_write_byte_data(client, TMP401_CONVERSION_RATE_WRITE, rate); + data->update_interval = (1 << (7 - rate)) * 125; + mutex_unlock(&data->update_lock); return count; } -static struct sensor_device_attribute tmp401_attr[] = { - SENSOR_ATTR(temp1_input, S_IRUGO, show_temp_value, NULL, 0), - SENSOR_ATTR(temp1_min, S_IWUSR | S_IRUGO, show_temp_min, - store_temp_min, 0), - SENSOR_ATTR(temp1_max, S_IWUSR | S_IRUGO, show_temp_max, - store_temp_max, 0), - SENSOR_ATTR(temp1_crit, S_IWUSR | S_IRUGO, show_temp_crit, - store_temp_crit, 0), - SENSOR_ATTR(temp1_crit_hyst, S_IWUSR | S_IRUGO, show_temp_crit_hyst, - store_temp_crit_hyst, 0), - SENSOR_ATTR(temp1_min_alarm, S_IRUGO, show_status, NULL, - TMP401_STATUS_LOCAL_LOW), - SENSOR_ATTR(temp1_max_alarm, S_IRUGO, show_status, NULL, - TMP401_STATUS_LOCAL_HIGH), - SENSOR_ATTR(temp1_crit_alarm, S_IRUGO, show_status, NULL, - TMP401_STATUS_LOCAL_CRIT), - SENSOR_ATTR(temp2_input, S_IRUGO, show_temp_value, NULL, 1), - SENSOR_ATTR(temp2_min, S_IWUSR | S_IRUGO, show_temp_min, - store_temp_min, 1), - SENSOR_ATTR(temp2_max, S_IWUSR | S_IRUGO, show_temp_max, - store_temp_max, 1), - SENSOR_ATTR(temp2_crit, S_IWUSR | S_IRUGO, show_temp_crit, - store_temp_crit, 1), - SENSOR_ATTR(temp2_crit_hyst, S_IRUGO, show_temp_crit_hyst, NULL, 1), - SENSOR_ATTR(temp2_fault, S_IRUGO, show_status, NULL, - TMP401_STATUS_REMOTE_OPEN), - SENSOR_ATTR(temp2_min_alarm, S_IRUGO, show_status, NULL, - TMP401_STATUS_REMOTE_LOW), - SENSOR_ATTR(temp2_max_alarm, S_IRUGO, show_status, NULL, - TMP401_STATUS_REMOTE_HIGH), - SENSOR_ATTR(temp2_crit_alarm, S_IRUGO, show_status, NULL, - TMP401_STATUS_REMOTE_CRIT), +static SENSOR_DEVICE_ATTR_2(temp1_input, S_IRUGO, show_temp, NULL, 0, 0); +static SENSOR_DEVICE_ATTR_2(temp1_min, S_IWUSR | S_IRUGO, show_temp, + store_temp, 1, 0); +static SENSOR_DEVICE_ATTR_2(temp1_max, S_IWUSR | S_IRUGO, show_temp, + store_temp, 2, 0); +static SENSOR_DEVICE_ATTR_2(temp1_crit, S_IWUSR | S_IRUGO, show_temp, + store_temp, 3, 0); +static SENSOR_DEVICE_ATTR(temp1_crit_hyst, S_IWUSR | S_IRUGO, + show_temp_crit_hyst, store_temp_crit_hyst, 0); +static SENSOR_DEVICE_ATTR_2(temp1_min_alarm, S_IRUGO, show_status, NULL, + 1, TMP432_STATUS_LOCAL); +static SENSOR_DEVICE_ATTR_2(temp1_max_alarm, S_IRUGO, show_status, NULL, + 2, TMP432_STATUS_LOCAL); +static SENSOR_DEVICE_ATTR_2(temp1_crit_alarm, S_IRUGO, show_status, NULL, + 3, TMP432_STATUS_LOCAL); +static SENSOR_DEVICE_ATTR_2(temp2_input, S_IRUGO, show_temp, NULL, 0, 1); +static SENSOR_DEVICE_ATTR_2(temp2_min, S_IWUSR | S_IRUGO, show_temp, + store_temp, 1, 1); +static SENSOR_DEVICE_ATTR_2(temp2_max, S_IWUSR | S_IRUGO, show_temp, + store_temp, 2, 1); +static SENSOR_DEVICE_ATTR_2(temp2_crit, S_IWUSR | S_IRUGO, show_temp, + store_temp, 3, 1); +static SENSOR_DEVICE_ATTR(temp2_crit_hyst, S_IRUGO, show_temp_crit_hyst, + NULL, 1); +static SENSOR_DEVICE_ATTR_2(temp2_fault, S_IRUGO, show_status, NULL, + 0, TMP432_STATUS_REMOTE1); +static SENSOR_DEVICE_ATTR_2(temp2_min_alarm, S_IRUGO, show_status, NULL, + 1, TMP432_STATUS_REMOTE1); +static SENSOR_DEVICE_ATTR_2(temp2_max_alarm, S_IRUGO, show_status, NULL, + 2, TMP432_STATUS_REMOTE1); +static SENSOR_DEVICE_ATTR_2(temp2_crit_alarm, S_IRUGO, show_status, NULL, + 3, TMP432_STATUS_REMOTE1); + +static DEVICE_ATTR(update_interval, S_IRUGO | S_IWUSR, show_update_interval, + set_update_interval); + +static struct attribute *tmp401_attributes[] = { + &sensor_dev_attr_temp1_input.dev_attr.attr, + &sensor_dev_attr_temp1_min.dev_attr.attr, + &sensor_dev_attr_temp1_max.dev_attr.attr, + &sensor_dev_attr_temp1_crit.dev_attr.attr, + &sensor_dev_attr_temp1_crit_hyst.dev_attr.attr, + &sensor_dev_attr_temp1_max_alarm.dev_attr.attr, + &sensor_dev_attr_temp1_min_alarm.dev_attr.attr, + &sensor_dev_attr_temp1_crit_alarm.dev_attr.attr, + + &sensor_dev_attr_temp2_input.dev_attr.attr, + &sensor_dev_attr_temp2_min.dev_attr.attr, + &sensor_dev_attr_temp2_max.dev_attr.attr, + &sensor_dev_attr_temp2_crit.dev_attr.attr, + &sensor_dev_attr_temp2_crit_hyst.dev_attr.attr, + &sensor_dev_attr_temp2_fault.dev_attr.attr, + &sensor_dev_attr_temp2_max_alarm.dev_attr.attr, + &sensor_dev_attr_temp2_min_alarm.dev_attr.attr, + &sensor_dev_attr_temp2_crit_alarm.dev_attr.attr, + + &dev_attr_update_interval.attr, + + NULL +}; + +static const struct attribute_group tmp401_group = { + .attrs = tmp401_attributes, }; /* @@ -502,12 +556,60 @@ static struct sensor_device_attribute tmp401_attr[] = { * minimum and maximum register reset for both the local * and remote channels. */ -static struct sensor_device_attribute tmp411_attr[] = { - SENSOR_ATTR(temp1_highest, S_IRUGO, show_temp_highest, NULL, 0), - SENSOR_ATTR(temp1_lowest, S_IRUGO, show_temp_lowest, NULL, 0), - SENSOR_ATTR(temp2_highest, S_IRUGO, show_temp_highest, NULL, 1), - SENSOR_ATTR(temp2_lowest, S_IRUGO, show_temp_lowest, NULL, 1), - SENSOR_ATTR(temp_reset_history, S_IWUSR, NULL, reset_temp_history, 0), +static SENSOR_DEVICE_ATTR_2(temp1_lowest, S_IRUGO, show_temp, NULL, 4, 0); +static SENSOR_DEVICE_ATTR_2(temp1_highest, S_IRUGO, show_temp, NULL, 5, 0); +static SENSOR_DEVICE_ATTR_2(temp2_lowest, S_IRUGO, show_temp, NULL, 4, 1); +static SENSOR_DEVICE_ATTR_2(temp2_highest, S_IRUGO, show_temp, NULL, 5, 1); +static SENSOR_DEVICE_ATTR(temp_reset_history, S_IWUSR, NULL, reset_temp_history, + 0); + +static struct attribute *tmp411_attributes[] = { + &sensor_dev_attr_temp1_highest.dev_attr.attr, + &sensor_dev_attr_temp1_lowest.dev_attr.attr, + &sensor_dev_attr_temp2_highest.dev_attr.attr, + &sensor_dev_attr_temp2_lowest.dev_attr.attr, + &sensor_dev_attr_temp_reset_history.dev_attr.attr, + NULL +}; + +static const struct attribute_group tmp411_group = { + .attrs = tmp411_attributes, +}; + +static SENSOR_DEVICE_ATTR_2(temp3_input, S_IRUGO, show_temp, NULL, 0, 2); +static SENSOR_DEVICE_ATTR_2(temp3_min, S_IWUSR | S_IRUGO, show_temp, + store_temp, 1, 2); +static SENSOR_DEVICE_ATTR_2(temp3_max, S_IWUSR | S_IRUGO, show_temp, + store_temp, 2, 2); +static SENSOR_DEVICE_ATTR_2(temp3_crit, S_IWUSR | S_IRUGO, show_temp, + store_temp, 3, 2); +static SENSOR_DEVICE_ATTR(temp3_crit_hyst, S_IRUGO, show_temp_crit_hyst, + NULL, 2); +static SENSOR_DEVICE_ATTR_2(temp3_fault, S_IRUGO, show_status, NULL, + 0, TMP432_STATUS_REMOTE2); +static SENSOR_DEVICE_ATTR_2(temp3_min_alarm, S_IRUGO, show_status, NULL, + 1, TMP432_STATUS_REMOTE2); +static SENSOR_DEVICE_ATTR_2(temp3_max_alarm, S_IRUGO, show_status, NULL, + 2, TMP432_STATUS_REMOTE2); +static SENSOR_DEVICE_ATTR_2(temp3_crit_alarm, S_IRUGO, show_status, NULL, + 3, TMP432_STATUS_REMOTE2); + +static struct attribute *tmp432_attributes[] = { + &sensor_dev_attr_temp3_input.dev_attr.attr, + &sensor_dev_attr_temp3_min.dev_attr.attr, + &sensor_dev_attr_temp3_max.dev_attr.attr, + &sensor_dev_attr_temp3_crit.dev_attr.attr, + &sensor_dev_attr_temp3_crit_hyst.dev_attr.attr, + &sensor_dev_attr_temp3_fault.dev_attr.attr, + &sensor_dev_attr_temp3_max_alarm.dev_attr.attr, + &sensor_dev_attr_temp3_min_alarm.dev_attr.attr, + &sensor_dev_attr_temp3_crit_alarm.dev_attr.attr, + + NULL +}; + +static const struct attribute_group tmp432_group = { + .attrs = tmp432_attributes, }; /* @@ -517,9 +619,11 @@ static struct sensor_device_attribute tmp411_attr[] = { static void tmp401_init_client(struct i2c_client *client) { int config, config_orig; + struct tmp401_data *data = i2c_get_clientdata(client); /* Set the conversion rate to 2 Hz */ i2c_smbus_write_byte_data(client, TMP401_CONVERSION_RATE_WRITE, 5); + data->update_interval = 500; /* Start conversions (disable shutdown if necessary) */ config = i2c_smbus_read_byte_data(client, TMP401_CONFIG_READ); @@ -554,11 +658,35 @@ static int tmp401_detect(struct i2c_client *client, switch (reg) { case TMP401_DEVICE_ID: + if (client->addr != 0x4c) + return -ENODEV; kind = tmp401; break; - case TMP411_DEVICE_ID: + case TMP411A_DEVICE_ID: + if (client->addr != 0x4c) + return -ENODEV; + kind = tmp411; + break; + case TMP411B_DEVICE_ID: + if (client->addr != 0x4d) + return -ENODEV; kind = tmp411; break; + case TMP411C_DEVICE_ID: + if (client->addr != 0x4e) + return -ENODEV; + kind = tmp411; + break; + case TMP431_DEVICE_ID: + if (client->addr == 0x4e) + return -ENODEV; + kind = tmp431; + break; + case TMP432_DEVICE_ID: + if (client->addr == 0x4e) + return -ENODEV; + kind = tmp432; + break; default: return -ENODEV; } @@ -579,20 +707,19 @@ static int tmp401_detect(struct i2c_client *client, static int tmp401_remove(struct i2c_client *client) { + struct device *dev = &client->dev; struct tmp401_data *data = i2c_get_clientdata(client); - int i; if (data->hwmon_dev) hwmon_device_unregister(data->hwmon_dev); - for (i = 0; i < ARRAY_SIZE(tmp401_attr); i++) - device_remove_file(&client->dev, &tmp401_attr[i].dev_attr); + sysfs_remove_group(&dev->kobj, &tmp401_group); - if (data->kind == tmp411) { - for (i = 0; i < ARRAY_SIZE(tmp411_attr); i++) - device_remove_file(&client->dev, - &tmp411_attr[i].dev_attr); - } + if (data->kind == tmp411) + sysfs_remove_group(&dev->kobj, &tmp411_group); + + if (data->kind == tmp432) + sysfs_remove_group(&dev->kobj, &tmp432_group); return 0; } @@ -600,12 +727,12 @@ static int tmp401_remove(struct i2c_client *client) static int tmp401_probe(struct i2c_client *client, const struct i2c_device_id *id) { - int i, err = 0; + struct device *dev = &client->dev; + int err; struct tmp401_data *data; - const char *names[] = { "TMP401", "TMP411" }; + const char *names[] = { "TMP401", "TMP411", "TMP431", "TMP432" }; - data = devm_kzalloc(&client->dev, sizeof(struct tmp401_data), - GFP_KERNEL); + data = devm_kzalloc(dev, sizeof(struct tmp401_data), GFP_KERNEL); if (!data) return -ENOMEM; @@ -617,31 +744,32 @@ static int tmp401_probe(struct i2c_client *client, tmp401_init_client(client); /* Register sysfs hooks */ - for (i = 0; i < ARRAY_SIZE(tmp401_attr); i++) { - err = device_create_file(&client->dev, - &tmp401_attr[i].dev_attr); + err = sysfs_create_group(&dev->kobj, &tmp401_group); + if (err) + return err; + + /* Register additional tmp411 sysfs hooks */ + if (data->kind == tmp411) { + err = sysfs_create_group(&dev->kobj, &tmp411_group); if (err) goto exit_remove; } - /* Register additional tmp411 sysfs hooks */ - if (data->kind == tmp411) { - for (i = 0; i < ARRAY_SIZE(tmp411_attr); i++) { - err = device_create_file(&client->dev, - &tmp411_attr[i].dev_attr); - if (err) - goto exit_remove; - } + /* Register additional tmp432 sysfs hooks */ + if (data->kind == tmp432) { + err = sysfs_create_group(&dev->kobj, &tmp432_group); + if (err) + goto exit_remove; } - data->hwmon_dev = hwmon_device_register(&client->dev); + data->hwmon_dev = hwmon_device_register(dev); if (IS_ERR(data->hwmon_dev)) { err = PTR_ERR(data->hwmon_dev); data->hwmon_dev = NULL; goto exit_remove; } - dev_info(&client->dev, "Detected TI %s chip\n", names[data->kind]); + dev_info(dev, "Detected TI %s chip\n", names[data->kind]); return 0; diff --git a/drivers/hwmon/tmp421.c b/drivers/hwmon/tmp421.c index 6a8ded29f1ed..964c1d688274 100644 --- a/drivers/hwmon/tmp421.c +++ b/drivers/hwmon/tmp421.c @@ -208,8 +208,8 @@ static int tmp421_init_client(struct i2c_client *client) /* Start conversions (disable shutdown if necessary) */ config = i2c_smbus_read_byte_data(client, TMP421_CONFIG_REG_1); if (config < 0) { - dev_err(&client->dev, "Could not read configuration" - " register (%d)\n", config); + dev_err(&client->dev, + "Could not read configuration register (%d)\n", config); return -ENODEV; } @@ -322,6 +322,5 @@ static struct i2c_driver tmp421_driver = { module_i2c_driver(tmp421_driver); MODULE_AUTHOR("Andre Prendel <andre.prendel@gmx.de>"); -MODULE_DESCRIPTION("Texas Instruments TMP421/422/423 temperature sensor" - " driver"); +MODULE_DESCRIPTION("Texas Instruments TMP421/422/423 temperature sensor driver"); MODULE_LICENSE("GPL"); diff --git a/drivers/hwmon/via686a.c b/drivers/hwmon/via686a.c index 3123b30208c5..c9dcce8c3dc3 100644 --- a/drivers/hwmon/via686a.c +++ b/drivers/hwmon/via686a.c @@ -125,7 +125,7 @@ static const u8 VIA686A_REG_TEMP_HYST[] = { 0x3a, 0x3e, 0x1e }; * (These conversions were contributed by Jonathan Teh Soon Yew * <j.teh@iname.com>) */ -static inline u8 IN_TO_REG(long val, int inNum) +static inline u8 IN_TO_REG(long val, int in_num) { /* * To avoid floating point, we multiply constants by 10 (100 for +12V). @@ -134,29 +134,29 @@ static inline u8 IN_TO_REG(long val, int inNum) * by an additional 10000 (100000 for +12V): 1000 for val and 10 (100) * for the constants. */ - if (inNum <= 1) + if (in_num <= 1) return (u8) clamp_val((val * 21024 - 1205000) / 250000, 0, 255); - else if (inNum == 2) + else if (in_num == 2) return (u8) clamp_val((val * 15737 - 1205000) / 250000, 0, 255); - else if (inNum == 3) + else if (in_num == 3) return (u8) clamp_val((val * 10108 - 1205000) / 250000, 0, 255); else return (u8) clamp_val((val * 41714 - 12050000) / 2500000, 0, 255); } -static inline long IN_FROM_REG(u8 val, int inNum) +static inline long IN_FROM_REG(u8 val, int in_num) { /* * To avoid floating point, we multiply constants by 10 (100 for +12V). * We also multiply them by 1000 because we want 0.001V/bit for the * output value. Rounding is done. */ - if (inNum <= 1) + if (in_num <= 1) return (long) ((250000 * val + 1330000 + 21024 / 2) / 21024); - else if (inNum == 2) + else if (in_num == 2) return (long) ((250000 * val + 1330000 + 15737 / 2) / 15737); - else if (inNum == 3) + else if (in_num == 3) return (long) ((250000 * val + 1330000 + 10108 / 2) / 10108); else return (long) ((2500000 * val + 13300000 + 41714 / 2) / 41714); @@ -210,10 +210,10 @@ static inline u8 FAN_TO_REG(long rpm, int div) * VIA register values 0-255. I *10 before rounding, so we get tenth-degree * precision. (I could have done all 1024 values for our 10-bit readings, * but the function is very linear in the useful range (0-80 deg C), so - * we'll just use linear interpolation for 10-bit readings.) So, tempLUT + * we'll just use linear interpolation for 10-bit readings.) So, temp_lut * is the temp at via register values 0-255: */ -static const s16 tempLUT[] = { +static const s16 temp_lut[] = { -709, -688, -667, -646, -627, -607, -589, -570, -553, -536, -519, -503, -487, -471, -456, -442, -428, -414, -400, -387, -375, -362, -350, -339, -327, -316, -305, -295, -285, -275, -265, @@ -261,7 +261,7 @@ static const s16 tempLUT[] = { * - 2.525453e-04*val^3 + 1.424593e-02*val^2 + 2.148941e+00*val +7.275808e+01) * Note that n=161: */ -static const u8 viaLUT[] = { +static const u8 via_lut[] = { 12, 12, 13, 14, 14, 15, 16, 16, 17, 18, 18, 19, 20, 20, 21, 22, 23, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 35, 36, 37, 39, 40, 41, 43, 45, 46, 48, 49, 51, 53, 55, 57, 59, 60, 62, 64, 66, @@ -284,26 +284,26 @@ static const u8 viaLUT[] = { */ static inline u8 TEMP_TO_REG(long val) { - return viaLUT[val <= -50000 ? 0 : val >= 110000 ? 160 : + return via_lut[val <= -50000 ? 0 : val >= 110000 ? 160 : (val < 0 ? val - 500 : val + 500) / 1000 + 50]; } /* for 8-bit temperature hyst and over registers */ -#define TEMP_FROM_REG(val) ((long)tempLUT[val] * 100) +#define TEMP_FROM_REG(val) ((long)temp_lut[val] * 100) /* for 10-bit temperature readings */ static inline long TEMP_FROM_REG10(u16 val) { - u16 eightBits = val >> 2; - u16 twoBits = val & 3; + u16 eight_bits = val >> 2; + u16 two_bits = val & 3; /* no interpolation for these */ - if (twoBits == 0 || eightBits == 255) - return TEMP_FROM_REG(eightBits); + if (two_bits == 0 || eight_bits == 255) + return TEMP_FROM_REG(eight_bits); /* do some linear interpolation */ - return (tempLUT[eightBits] * (4 - twoBits) + - tempLUT[eightBits + 1] * twoBits) * 25; + return (temp_lut[eight_bits] * (4 - two_bits) + + temp_lut[eight_bits + 1] * two_bits) * 25; } #define DIV_FROM_REG(val) (1 << (val)) @@ -889,8 +889,8 @@ static int via686a_pci_probe(struct pci_dev *dev, address = val & ~(VIA686A_EXTENT - 1); if (address == 0) { - dev_err(&dev->dev, "base address not set - upgrade BIOS " - "or use force_addr=0xaddr\n"); + dev_err(&dev->dev, + "base address not set - upgrade BIOS or use force_addr=0xaddr\n"); return -ENODEV; } @@ -899,8 +899,9 @@ static int via686a_pci_probe(struct pci_dev *dev, return -ENODEV; if (!(val & 0x0001)) { if (!force_addr) { - dev_warn(&dev->dev, "Sensors disabled, enable " - "with force_addr=0x%x\n", address); + dev_warn(&dev->dev, + "Sensors disabled, enable with force_addr=0x%x\n", + address); return -ENODEV; } diff --git a/drivers/hwmon/vt1211.c b/drivers/hwmon/vt1211.c index dcc62f80f67b..6b2f1a42b3ff 100644 --- a/drivers/hwmon/vt1211.c +++ b/drivers/hwmon/vt1211.c @@ -571,8 +571,9 @@ static ssize_t set_fan(struct device *dev, struct device_attribute *attr, break; default: count = -EINVAL; - dev_warn(dev, "fan div value %ld not supported. " - "Choose one of 1, 2, 4, or 8.\n", val); + dev_warn(dev, + "fan div value %ld not supported. Choose one of 1, 2, 4, or 8.\n", + val); goto EXIT; } vt1211_write8(data, VT1211_REG_FAN_DIV, @@ -674,8 +675,9 @@ static ssize_t set_pwm(struct device *dev, struct device_attribute *attr, break; default: count = -EINVAL; - dev_warn(dev, "pwm mode %ld not supported. " - "Choose one of 0 or 2.\n", val); + dev_warn(dev, + "pwm mode %ld not supported. Choose one of 0 or 2.\n", + val); goto EXIT; } vt1211_write8(data, VT1211_REG_PWM_CTL, @@ -700,8 +702,9 @@ static ssize_t set_pwm(struct device *dev, struct device_attribute *attr, case SHOW_SET_PWM_AUTO_CHANNELS_TEMP: if (val < 1 || val > 7) { count = -EINVAL; - dev_warn(dev, "temp channel %ld not supported. " - "Choose a value between 1 and 7.\n", val); + dev_warn(dev, + "temp channel %ld not supported. Choose a value between 1 and 7.\n", + val); goto EXIT; } if (!ISTEMP(val - 1, data->uch_config)) { @@ -1325,15 +1328,15 @@ static int __init vt1211_init(void) if ((uch_config < -1) || (uch_config > 31)) { err = -EINVAL; - pr_warn("Invalid UCH configuration %d. " - "Choose a value between 0 and 31.\n", uch_config); + pr_warn("Invalid UCH configuration %d. Choose a value between 0 and 31.\n", + uch_config); goto EXIT; } if ((int_mode < -1) || (int_mode > 0)) { err = -EINVAL; - pr_warn("Invalid interrupt mode %d. " - "Only mode 0 is supported.\n", int_mode); + pr_warn("Invalid interrupt mode %d. Only mode 0 is supported.\n", + int_mode); goto EXIT; } diff --git a/drivers/hwmon/vt8231.c b/drivers/hwmon/vt8231.c index 988a2a796764..0e7017841f7d 100644 --- a/drivers/hwmon/vt8231.c +++ b/drivers/hwmon/vt8231.c @@ -573,8 +573,9 @@ static ssize_t set_fan_div(struct device *dev, struct device_attribute *attr, data->fan_div[nr] = 3; break; default: - dev_err(dev, "fan_div value %ld not supported. " - "Choose one of 1, 2, 4 or 8!\n", val); + dev_err(dev, + "fan_div value %ld not supported. Choose one of 1, 2, 4 or 8!\n", + val); mutex_unlock(&data->update_lock); return -EINVAL; } diff --git a/drivers/hwmon/w83627ehf.c b/drivers/hwmon/w83627ehf.c index 0a89211c25f6..016027229e81 100644 --- a/drivers/hwmon/w83627ehf.c +++ b/drivers/hwmon/w83627ehf.c @@ -840,8 +840,8 @@ static struct w83627ehf_data *w83627ehf_update_device(struct device *dev) && (reg >= 0xff || (sio_data->kind == nct6775 && reg == 0x00)) && data->fan_div[i] < 0x07) { - dev_dbg(dev, "Increasing fan%d " - "clock divider from %u to %u\n", + dev_dbg(dev, + "Increasing fan%d clock divider from %u to %u\n", i + 1, div_from_reg(data->fan_div[i]), div_from_reg(data->fan_div[i] + 1)); data->fan_div[i]++; @@ -1110,9 +1110,9 @@ store_fan_min(struct device *dev, struct device_attribute *attr, */ data->fan_min[nr] = 254; new_div = 7; /* 128 == (1 << 7) */ - dev_warn(dev, "fan%u low limit %lu below minimum %u, set to " - "minimum\n", nr + 1, val, - data->fan_from_reg_min(254, 7)); + dev_warn(dev, + "fan%u low limit %lu below minimum %u, set to minimum\n", + nr + 1, val, data->fan_from_reg_min(254, 7)); } else if (!reg) { /* * Speed above this value cannot possibly be represented, @@ -1120,9 +1120,9 @@ store_fan_min(struct device *dev, struct device_attribute *attr, */ data->fan_min[nr] = 1; new_div = 0; /* 1 == (1 << 0) */ - dev_warn(dev, "fan%u low limit %lu above maximum %u, set to " - "maximum\n", nr + 1, val, - data->fan_from_reg_min(1, 0)); + dev_warn(dev, + "fan%u low limit %lu above maximum %u, set to maximum\n", + nr + 1, val, data->fan_from_reg_min(1, 0)); } else { /* * Automatically pick the best divider, i.e. the one such @@ -2396,15 +2396,15 @@ static int w83627ehf_probe(struct platform_device *pdev) en_vrm10 = superio_inb(sio_data->sioreg, SIO_REG_EN_VRM10); if ((en_vrm10 & 0x08) && data->vrm == 90) { - dev_warn(dev, "Setting VID input " - "voltage to TTL\n"); + dev_warn(dev, + "Setting VID input voltage to TTL\n"); superio_outb(sio_data->sioreg, SIO_REG_EN_VRM10, en_vrm10 & ~0x08); } else if (!(en_vrm10 & 0x08) && data->vrm == 100) { - dev_warn(dev, "Setting VID input " - "voltage to VRM10\n"); + dev_warn(dev, + "Setting VID input voltage to VRM10\n"); superio_outb(sio_data->sioreg, SIO_REG_EN_VRM10, en_vrm10 | 0x08); @@ -2420,8 +2420,8 @@ static int w83627ehf_probe(struct platform_device *pdev) if (err) goto exit_release; } else { - dev_info(dev, "VID pins in output mode, CPU VID not " - "available\n"); + dev_info(dev, + "VID pins in output mode, CPU VID not available\n"); } } @@ -2795,8 +2795,7 @@ static int __init w83627ehf_find(int sioaddr, unsigned short *addr, /* Activate logical device if needed */ val = superio_inb(sioaddr, SIO_REG_ENABLE); if (!(val & 0x01)) { - pr_warn("Forcibly enabling Super-I/O. " - "Sensor is probably unusable.\n"); + pr_warn("Forcibly enabling Super-I/O. Sensor is probably unusable.\n"); superio_outb(sioaddr, SIO_REG_ENABLE, val | 0x01); } diff --git a/drivers/hwmon/w83781d.c b/drivers/hwmon/w83781d.c index aeec5b1d81c9..f9d513949a38 100644 --- a/drivers/hwmon/w83781d.c +++ b/drivers/hwmon/w83781d.c @@ -64,8 +64,8 @@ enum chips { w83781d, w83782d, w83783s, as99127f }; /* Insmod parameters */ static unsigned short force_subclients[4]; module_param_array(force_subclients, short, NULL, 0); -MODULE_PARM_DESC(force_subclients, "List of subclient addresses: " - "{bus, clientaddr, subclientaddr1, subclientaddr2}"); +MODULE_PARM_DESC(force_subclients, + "List of subclient addresses: {bus, clientaddr, subclientaddr1, subclientaddr2}"); static bool reset; module_param(reset, bool, 0); @@ -826,8 +826,9 @@ store_sensor(struct device *dev, struct device_attribute *da, data->sens[nr] = val; break; case W83781D_DEFAULT_BETA: - dev_warn(dev, "Sensor type %d is deprecated, please use 4 " - "instead\n", W83781D_DEFAULT_BETA); + dev_warn(dev, + "Sensor type %d is deprecated, please use 4 instead\n", + W83781D_DEFAULT_BETA); /* fall through */ case 4: /* thermistor */ tmp = w83781d_read_value(data, W83781D_REG_SCFG1); @@ -874,8 +875,8 @@ w83781d_detect_subclients(struct i2c_client *new_client) for (i = 2; i <= 3; i++) { if (force_subclients[i] < 0x48 || force_subclients[i] > 0x4f) { - dev_err(&new_client->dev, "Invalid subclient " - "address %d; must be 0x48-0x4f\n", + dev_err(&new_client->dev, + "Invalid subclient address %d; must be 0x48-0x4f\n", force_subclients[i]); err = -EINVAL; goto ERROR_SC_1; @@ -910,9 +911,9 @@ w83781d_detect_subclients(struct i2c_client *new_client) for (i = 0; i < num_sc; i++) { data->lm75[i] = i2c_new_dummy(adapter, sc_addr[i]); if (!data->lm75[i]) { - dev_err(&new_client->dev, "Subclient %d " - "registration at address 0x%x " - "failed.\n", i, sc_addr[i]); + dev_err(&new_client->dev, + "Subclient %d registration at address 0x%x failed.\n", + i, sc_addr[i]); err = -ENOMEM; if (i == 1) goto ERROR_SC_3; @@ -1176,8 +1177,9 @@ w83781d_detect(struct i2c_client *client, struct i2c_board_info *info) goto err_nodev; if (val1 <= 0x30 && w83781d_alias_detect(client, val1)) { - dev_dbg(&adapter->dev, "Device at 0x%02x appears to " - "be the same as ISA device\n", address); + dev_dbg(&adapter->dev, + "Device at 0x%02x appears to be the same as ISA device\n", + address); goto err_nodev; } @@ -1367,8 +1369,8 @@ w83781d_init_device(struct device *dev) * as I see very little reason why this would be needed at * all. */ - dev_info(dev, "If reset=1 solved a problem you were " - "having, please report!\n"); + dev_info(dev, + "If reset=1 solved a problem you were having, please report!\n"); /* save these registers */ i = w83781d_read_value(data, W83781D_REG_BEEP_CONFIG); @@ -1425,8 +1427,8 @@ w83781d_init_device(struct device *dev) /* Enable temp2 */ tmp = w83781d_read_value(data, W83781D_REG_TEMP2_CONFIG); if (tmp & 0x01) { - dev_warn(dev, "Enabling temp2, readings " - "might not make sense\n"); + dev_warn(dev, + "Enabling temp2, readings might not make sense\n"); w83781d_write_value(data, W83781D_REG_TEMP2_CONFIG, tmp & 0xfe); } @@ -1436,8 +1438,8 @@ w83781d_init_device(struct device *dev) tmp = w83781d_read_value(data, W83781D_REG_TEMP3_CONFIG); if (tmp & 0x01) { - dev_warn(dev, "Enabling temp3, " - "readings might not make sense\n"); + dev_warn(dev, + "Enabling temp3, readings might not make sense\n"); w83781d_write_value(data, W83781D_REG_TEMP3_CONFIG, tmp & 0xfe); } diff --git a/drivers/hwmon/w83791d.c b/drivers/hwmon/w83791d.c index 38dddddf8875..a3feee332e20 100644 --- a/drivers/hwmon/w83791d.c +++ b/drivers/hwmon/w83791d.c @@ -56,8 +56,8 @@ static const unsigned short normal_i2c[] = { 0x2c, 0x2d, 0x2e, 0x2f, static unsigned short force_subclients[4]; module_param_array(force_subclients, short, NULL, 0); -MODULE_PARM_DESC(force_subclients, "List of subclient addresses: " - "{bus, clientaddr, subclientaddr1, subclientaddr2}"); +MODULE_PARM_DESC(force_subclients, + "List of subclient addresses: {bus, clientaddr, subclientaddr1, subclientaddr2}"); static bool reset; module_param(reset, bool, 0); diff --git a/drivers/hwmon/w83792d.c b/drivers/hwmon/w83792d.c index 5cb83ddf2cc6..0b804895be43 100644 --- a/drivers/hwmon/w83792d.c +++ b/drivers/hwmon/w83792d.c @@ -54,8 +54,8 @@ static const unsigned short normal_i2c[] = { 0x2c, 0x2d, 0x2e, 0x2f, static unsigned short force_subclients[4]; module_param_array(force_subclients, short, NULL, 0); -MODULE_PARM_DESC(force_subclients, "List of subclient addresses: " - "{bus, clientaddr, subclientaddr1, subclientaddr2}"); +MODULE_PARM_DESC(force_subclients, + "List of subclient addresses: {bus, clientaddr, subclientaddr1, subclientaddr2}"); static bool init; module_param(init, bool, 0); @@ -951,8 +951,8 @@ w83792d_detect_subclients(struct i2c_client *new_client) for (i = 2; i <= 3; i++) { if (force_subclients[i] < 0x48 || force_subclients[i] > 0x4f) { - dev_err(&new_client->dev, "invalid subclient " - "address %d; must be 0x48-0x4f\n", + dev_err(&new_client->dev, + "invalid subclient address %d; must be 0x48-0x4f\n", force_subclients[i]); err = -ENODEV; goto ERROR_SC_0; @@ -969,8 +969,9 @@ w83792d_detect_subclients(struct i2c_client *new_client) if (!(val & 0x80)) { if ((data->lm75[0] != NULL) && ((val & 0x7) == ((val >> 4) & 0x7))) { - dev_err(&new_client->dev, "duplicate addresses 0x%x, " - "use force_subclient\n", data->lm75[0]->addr); + dev_err(&new_client->dev, + "duplicate addresses 0x%x, use force_subclient\n", + data->lm75[0]->addr); err = -ENODEV; goto ERROR_SC_1; } diff --git a/drivers/hwmon/w83793.c b/drivers/hwmon/w83793.c index 660427520c53..b0c30a546ff2 100644 --- a/drivers/hwmon/w83793.c +++ b/drivers/hwmon/w83793.c @@ -59,8 +59,8 @@ static const unsigned short normal_i2c[] = { 0x2c, 0x2d, 0x2e, 0x2f, static unsigned short force_subclients[4]; module_param_array(force_subclients, short, NULL, 0); -MODULE_PARM_DESC(force_subclients, "List of subclient addresses: " - "{bus, clientaddr, subclientaddr1, subclientaddr2}"); +MODULE_PARM_DESC(force_subclients, + "List of subclient addresses: {bus, clientaddr, subclientaddr1, subclientaddr2}"); static bool reset; module_param(reset, bool, 0); @@ -1921,8 +1921,8 @@ static int w83793_probe(struct i2c_client *client, } if (i == ARRAY_SIZE(watchdog_minors)) { data->watchdog_miscdev.minor = 0; - dev_warn(&client->dev, "Couldn't register watchdog chardev " - "(due to no free minor)\n"); + dev_warn(&client->dev, + "Couldn't register watchdog chardev (due to no free minor)\n"); } mutex_unlock(&watchdog_data_mutex); diff --git a/drivers/hwmon/w83795.c b/drivers/hwmon/w83795.c index e226096148eb..908209d24664 100644 --- a/drivers/hwmon/w83795.c +++ b/drivers/hwmon/w83795.c @@ -2120,11 +2120,12 @@ static void w83795_check_dynamic_in_limits(struct i2c_client *client) &w83795_in[i][3].dev_attr.attr, S_IRUGO); if (err_max || err_min) - dev_warn(&client->dev, "Failed to set in%d limits " - "read-only (%d, %d)\n", i, err_max, err_min); + dev_warn(&client->dev, + "Failed to set in%d limits read-only (%d, %d)\n", + i, err_max, err_min); else - dev_info(&client->dev, "in%d limits set dynamically " - "from VID\n", i); + dev_info(&client->dev, + "in%d limits set dynamically from VID\n", i); } } diff --git a/drivers/idle/intel_idle.c b/drivers/idle/intel_idle.c index 5d6675013864..1a38dd7dfe4e 100644 --- a/drivers/idle/intel_idle.c +++ b/drivers/idle/intel_idle.c @@ -465,6 +465,7 @@ static const struct x86_cpu_id intel_idle_ids[] = { ICPU(0x3c, idle_cpu_hsw), ICPU(0x3f, idle_cpu_hsw), ICPU(0x45, idle_cpu_hsw), + ICPU(0x46, idle_cpu_hsw), {} }; MODULE_DEVICE_TABLE(x86cpu, intel_idle_ids); diff --git a/drivers/input/tablet/wacom_wac.c b/drivers/input/tablet/wacom_wac.c index 1daa97913b7d..0bfd8cf25200 100644 --- a/drivers/input/tablet/wacom_wac.c +++ b/drivers/input/tablet/wacom_wac.c @@ -359,7 +359,7 @@ static int wacom_intuos_inout(struct wacom_wac *wacom) case 0x802: /* Intuos4 General Pen */ case 0x804: /* Intuos4 Marker Pen */ case 0x40802: /* Intuos4 Classic Pen */ - case 0x18803: /* DTH2242 Grip Pen */ + case 0x18802: /* DTH2242 Grip Pen */ case 0x022: wacom->tool[idx] = BTN_TOOL_PEN; break; @@ -1912,7 +1912,7 @@ static const struct wacom_features wacom_features_0xBB = { "Wacom Intuos4 12x19", WACOM_PKGLEN_INTUOS, 97536, 60960, 2047, 63, INTUOS4L, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES }; static const struct wacom_features wacom_features_0xBC = - { "Wacom Intuos4 WL", WACOM_PKGLEN_INTUOS, 40840, 25400, 2047, + { "Wacom Intuos4 WL", WACOM_PKGLEN_INTUOS, 40640, 25400, 2047, 63, INTUOS4, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES }; static const struct wacom_features wacom_features_0x26 = { "Wacom Intuos5 touch S", WACOM_PKGLEN_INTUOS, 31496, 19685, 2047, @@ -2144,7 +2144,7 @@ const struct usb_device_id wacom_ids[] = { { USB_DEVICE_WACOM(0x44) }, { USB_DEVICE_WACOM(0x45) }, { USB_DEVICE_WACOM(0x59) }, - { USB_DEVICE_WACOM(0x5D) }, + { USB_DEVICE_DETAILED(0x5D, USB_CLASS_HID, 0, 0) }, { USB_DEVICE_WACOM(0xB0) }, { USB_DEVICE_WACOM(0xB1) }, { USB_DEVICE_WACOM(0xB2) }, @@ -2209,7 +2209,7 @@ const struct usb_device_id wacom_ids[] = { { USB_DEVICE_WACOM(0x47) }, { USB_DEVICE_WACOM(0xF4) }, { USB_DEVICE_WACOM(0xF8) }, - { USB_DEVICE_WACOM(0xF6) }, + { USB_DEVICE_DETAILED(0xF6, USB_CLASS_HID, 0, 0) }, { USB_DEVICE_WACOM(0xFA) }, { USB_DEVICE_LENOVO(0x6004) }, { } diff --git a/drivers/iommu/amd_iommu.c b/drivers/iommu/amd_iommu.c index b287ca33833d..830183737b0f 100644 --- a/drivers/iommu/amd_iommu.c +++ b/drivers/iommu/amd_iommu.c @@ -173,7 +173,7 @@ static inline u16 get_device_id(struct device *dev) { struct pci_dev *pdev = to_pci_dev(dev); - return calc_devid(pdev->bus->number, pdev->devfn); + return PCI_DEVID(pdev->bus->number, pdev->devfn); } static struct iommu_dev_data *get_dev_data(struct device *dev) @@ -649,26 +649,26 @@ retry: case EVENT_TYPE_ILL_DEV: printk("ILLEGAL_DEV_TABLE_ENTRY device=%02x:%02x.%x " "address=0x%016llx flags=0x%04x]\n", - PCI_BUS(devid), PCI_SLOT(devid), PCI_FUNC(devid), + PCI_BUS_NUM(devid), PCI_SLOT(devid), PCI_FUNC(devid), address, flags); dump_dte_entry(devid); break; case EVENT_TYPE_IO_FAULT: printk("IO_PAGE_FAULT device=%02x:%02x.%x " "domain=0x%04x address=0x%016llx flags=0x%04x]\n", - PCI_BUS(devid), PCI_SLOT(devid), PCI_FUNC(devid), + PCI_BUS_NUM(devid), PCI_SLOT(devid), PCI_FUNC(devid), domid, address, flags); break; case EVENT_TYPE_DEV_TAB_ERR: printk("DEV_TAB_HARDWARE_ERROR device=%02x:%02x.%x " "address=0x%016llx flags=0x%04x]\n", - PCI_BUS(devid), PCI_SLOT(devid), PCI_FUNC(devid), + PCI_BUS_NUM(devid), PCI_SLOT(devid), PCI_FUNC(devid), address, flags); break; case EVENT_TYPE_PAGE_TAB_ERR: printk("PAGE_TAB_HARDWARE_ERROR device=%02x:%02x.%x " "domain=0x%04x address=0x%016llx flags=0x%04x]\n", - PCI_BUS(devid), PCI_SLOT(devid), PCI_FUNC(devid), + PCI_BUS_NUM(devid), PCI_SLOT(devid), PCI_FUNC(devid), domid, address, flags); break; case EVENT_TYPE_ILL_CMD: @@ -682,13 +682,13 @@ retry: case EVENT_TYPE_IOTLB_INV_TO: printk("IOTLB_INV_TIMEOUT device=%02x:%02x.%x " "address=0x%016llx]\n", - PCI_BUS(devid), PCI_SLOT(devid), PCI_FUNC(devid), + PCI_BUS_NUM(devid), PCI_SLOT(devid), PCI_FUNC(devid), address); break; case EVENT_TYPE_INV_DEV_REQ: printk("INVALID_DEVICE_REQUEST device=%02x:%02x.%x " "address=0x%016llx flags=0x%04x]\n", - PCI_BUS(devid), PCI_SLOT(devid), PCI_FUNC(devid), + PCI_BUS_NUM(devid), PCI_SLOT(devid), PCI_FUNC(devid), address, flags); break; default: diff --git a/drivers/iommu/amd_iommu_init.c b/drivers/iommu/amd_iommu_init.c index e3c2d74b7684..2f46881256a2 100644 --- a/drivers/iommu/amd_iommu_init.c +++ b/drivers/iommu/amd_iommu_init.c @@ -406,7 +406,7 @@ static int __init find_last_devid_on_pci(int bus, int dev, int fn, int cap_ptr) u32 cap; cap = read_pci_config(bus, dev, fn, cap_ptr+MMIO_RANGE_OFFSET); - update_last_devid(calc_devid(MMIO_GET_BUS(cap), MMIO_GET_LD(cap))); + update_last_devid(PCI_DEVID(MMIO_GET_BUS(cap), MMIO_GET_LD(cap))); return 0; } @@ -423,7 +423,7 @@ static int __init find_last_devid_from_ivhd(struct ivhd_header *h) p += sizeof(*h); end += h->length; - find_last_devid_on_pci(PCI_BUS(h->devid), + find_last_devid_on_pci(PCI_BUS_NUM(h->devid), PCI_SLOT(h->devid), PCI_FUNC(h->devid), h->cap_ptr); @@ -784,10 +784,10 @@ static int __init init_iommu_from_acpi(struct amd_iommu *iommu, DUMP_printk(" DEV_ALL\t\t\t first devid: %02x:%02x.%x" " last device %02x:%02x.%x flags: %02x\n", - PCI_BUS(iommu->first_device), + PCI_BUS_NUM(iommu->first_device), PCI_SLOT(iommu->first_device), PCI_FUNC(iommu->first_device), - PCI_BUS(iommu->last_device), + PCI_BUS_NUM(iommu->last_device), PCI_SLOT(iommu->last_device), PCI_FUNC(iommu->last_device), e->flags); @@ -801,7 +801,7 @@ static int __init init_iommu_from_acpi(struct amd_iommu *iommu, DUMP_printk(" DEV_SELECT\t\t\t devid: %02x:%02x.%x " "flags: %02x\n", - PCI_BUS(e->devid), + PCI_BUS_NUM(e->devid), PCI_SLOT(e->devid), PCI_FUNC(e->devid), e->flags); @@ -813,7 +813,7 @@ static int __init init_iommu_from_acpi(struct amd_iommu *iommu, DUMP_printk(" DEV_SELECT_RANGE_START\t " "devid: %02x:%02x.%x flags: %02x\n", - PCI_BUS(e->devid), + PCI_BUS_NUM(e->devid), PCI_SLOT(e->devid), PCI_FUNC(e->devid), e->flags); @@ -827,11 +827,11 @@ static int __init init_iommu_from_acpi(struct amd_iommu *iommu, DUMP_printk(" DEV_ALIAS\t\t\t devid: %02x:%02x.%x " "flags: %02x devid_to: %02x:%02x.%x\n", - PCI_BUS(e->devid), + PCI_BUS_NUM(e->devid), PCI_SLOT(e->devid), PCI_FUNC(e->devid), e->flags, - PCI_BUS(e->ext >> 8), + PCI_BUS_NUM(e->ext >> 8), PCI_SLOT(e->ext >> 8), PCI_FUNC(e->ext >> 8)); @@ -846,11 +846,11 @@ static int __init init_iommu_from_acpi(struct amd_iommu *iommu, DUMP_printk(" DEV_ALIAS_RANGE\t\t " "devid: %02x:%02x.%x flags: %02x " "devid_to: %02x:%02x.%x\n", - PCI_BUS(e->devid), + PCI_BUS_NUM(e->devid), PCI_SLOT(e->devid), PCI_FUNC(e->devid), e->flags, - PCI_BUS(e->ext >> 8), + PCI_BUS_NUM(e->ext >> 8), PCI_SLOT(e->ext >> 8), PCI_FUNC(e->ext >> 8)); @@ -864,7 +864,7 @@ static int __init init_iommu_from_acpi(struct amd_iommu *iommu, DUMP_printk(" DEV_EXT_SELECT\t\t devid: %02x:%02x.%x " "flags: %02x ext: %08x\n", - PCI_BUS(e->devid), + PCI_BUS_NUM(e->devid), PCI_SLOT(e->devid), PCI_FUNC(e->devid), e->flags, e->ext); @@ -877,7 +877,7 @@ static int __init init_iommu_from_acpi(struct amd_iommu *iommu, DUMP_printk(" DEV_EXT_SELECT_RANGE\t devid: " "%02x:%02x.%x flags: %02x ext: %08x\n", - PCI_BUS(e->devid), + PCI_BUS_NUM(e->devid), PCI_SLOT(e->devid), PCI_FUNC(e->devid), e->flags, e->ext); @@ -890,7 +890,7 @@ static int __init init_iommu_from_acpi(struct amd_iommu *iommu, case IVHD_DEV_RANGE_END: DUMP_printk(" DEV_RANGE_END\t\t devid: %02x:%02x.%x\n", - PCI_BUS(e->devid), + PCI_BUS_NUM(e->devid), PCI_SLOT(e->devid), PCI_FUNC(e->devid)); @@ -924,7 +924,7 @@ static int __init init_iommu_from_acpi(struct amd_iommu *iommu, DUMP_printk(" DEV_SPECIAL(%s[%d])\t\tdevid: %02x:%02x.%x\n", var, (int)handle, - PCI_BUS(devid), + PCI_BUS_NUM(devid), PCI_SLOT(devid), PCI_FUNC(devid)); @@ -1086,7 +1086,7 @@ static int __init init_iommu_all(struct acpi_table_header *table) DUMP_printk("device: %02x:%02x.%01x cap: %04x " "seg: %d flags: %01x info %04x\n", - PCI_BUS(h->devid), PCI_SLOT(h->devid), + PCI_BUS_NUM(h->devid), PCI_SLOT(h->devid), PCI_FUNC(h->devid), h->cap_ptr, h->pci_seg, h->flags, h->info); DUMP_printk(" mmio-addr: %016llx\n", @@ -1116,7 +1116,7 @@ static int iommu_init_pci(struct amd_iommu *iommu) int cap_ptr = iommu->cap_ptr; u32 range, misc, low, high; - iommu->dev = pci_get_bus_and_slot(PCI_BUS(iommu->devid), + iommu->dev = pci_get_bus_and_slot(PCI_BUS_NUM(iommu->devid), iommu->devid & 0xff); if (!iommu->dev) return -ENODEV; @@ -1128,9 +1128,9 @@ static int iommu_init_pci(struct amd_iommu *iommu) pci_read_config_dword(iommu->dev, cap_ptr + MMIO_MISC_OFFSET, &misc); - iommu->first_device = calc_devid(MMIO_GET_BUS(range), + iommu->first_device = PCI_DEVID(MMIO_GET_BUS(range), MMIO_GET_FD(range)); - iommu->last_device = calc_devid(MMIO_GET_BUS(range), + iommu->last_device = PCI_DEVID(MMIO_GET_BUS(range), MMIO_GET_LD(range)); if (!(iommu->cap & (1 << IOMMU_CAP_IOTLB))) @@ -1388,8 +1388,8 @@ static int __init init_unity_map_range(struct ivmd_header *m) DUMP_printk("%s devid_start: %02x:%02x.%x devid_end: %02x:%02x.%x" " range_start: %016llx range_end: %016llx flags: %x\n", s, - PCI_BUS(e->devid_start), PCI_SLOT(e->devid_start), - PCI_FUNC(e->devid_start), PCI_BUS(e->devid_end), + PCI_BUS_NUM(e->devid_start), PCI_SLOT(e->devid_start), + PCI_FUNC(e->devid_start), PCI_BUS_NUM(e->devid_end), PCI_SLOT(e->devid_end), PCI_FUNC(e->devid_end), e->address_start, e->address_end, m->flags); diff --git a/drivers/iommu/amd_iommu_types.h b/drivers/iommu/amd_iommu_types.h index e38ab438bb34..ec36cf63e0ca 100644 --- a/drivers/iommu/amd_iommu_types.h +++ b/drivers/iommu/amd_iommu_types.h @@ -24,6 +24,7 @@ #include <linux/mutex.h> #include <linux/list.h> #include <linux/spinlock.h> +#include <linux/pci.h> /* * Maximum number of IOMMUs supported @@ -315,9 +316,6 @@ #define MAX_DOMAIN_ID 65536 -/* FIXME: move this macro to <linux/pci.h> */ -#define PCI_BUS(x) (((x) >> 8) & 0xff) - /* Protection domain flags */ #define PD_DMA_OPS_MASK (1UL << 0) /* domain used for dma_ops */ #define PD_DEFAULT_MASK (1UL << 1) /* domain is a default dma_ops @@ -703,13 +701,6 @@ extern int amd_iommu_max_glx_val; */ extern void iommu_flush_all_caches(struct amd_iommu *iommu); -/* takes bus and device/function and returns the device id - * FIXME: should that be in generic PCI code? */ -static inline u16 calc_devid(u8 bus, u8 devfn) -{ - return (((u16)bus) << 8) | devfn; -} - static inline int get_ioapic_devid(int id) { struct devid_map *entry; diff --git a/drivers/ipack/carriers/tpci200.c b/drivers/ipack/carriers/tpci200.c index 0246b1fddffe..c276fde318e5 100644 --- a/drivers/ipack/carriers/tpci200.c +++ b/drivers/ipack/carriers/tpci200.c @@ -480,6 +480,7 @@ static void tpci200_release_device(struct ipack_device *dev) static int tpci200_create_device(struct tpci200_board *tpci200, int i) { + int ret; enum ipack_space space; struct ipack_device *dev = kzalloc(sizeof(struct ipack_device), GFP_KERNEL); @@ -495,7 +496,18 @@ static int tpci200_create_device(struct tpci200_board *tpci200, int i) + tpci200_space_interval[space] * i; dev->region[space].size = tpci200_space_size[space]; } - return ipack_device_register(dev); + + ret = ipack_device_init(dev); + if (ret < 0) { + ipack_put_device(dev); + return ret; + } + + ret = ipack_device_add(dev); + if (ret < 0) + ipack_put_device(dev); + + return ret; } static int tpci200_pci_probe(struct pci_dev *pdev, diff --git a/drivers/ipack/ipack.c b/drivers/ipack/ipack.c index 7ec6b208b1cb..6e066c53acce 100644 --- a/drivers/ipack/ipack.c +++ b/drivers/ipack/ipack.c @@ -227,7 +227,7 @@ static int ipack_unregister_bus_member(struct device *dev, void *data) struct ipack_bus_device *bus = data; if (idev->bus == bus) - ipack_device_unregister(idev); + ipack_device_del(idev); return 1; } @@ -419,7 +419,7 @@ out: return ret; } -int ipack_device_register(struct ipack_device *dev) +int ipack_device_init(struct ipack_device *dev) { int ret; @@ -428,6 +428,7 @@ int ipack_device_register(struct ipack_device *dev) dev->dev.parent = dev->bus->parent; dev_set_name(&dev->dev, "ipack-dev.%u.%u", dev->bus->bus_nr, dev->slot); + device_initialize(&dev->dev); if (dev->bus->ops->set_clockrate(dev, 8)) dev_warn(&dev->dev, "failed to switch to 8 MHz operation for reading of device ID.\n"); @@ -447,19 +448,34 @@ int ipack_device_register(struct ipack_device *dev) dev_err(&dev->dev, "failed to switch to 32 MHz operation.\n"); } - ret = device_register(&dev->dev); - if (ret < 0) - kfree(dev->id); + return 0; +} +EXPORT_SYMBOL_GPL(ipack_device_init); - return ret; +int ipack_device_add(struct ipack_device *dev) +{ + return device_add(&dev->dev); +} +EXPORT_SYMBOL_GPL(ipack_device_add); + +void ipack_device_del(struct ipack_device *dev) +{ + device_del(&dev->dev); + ipack_put_device(dev); +} +EXPORT_SYMBOL_GPL(ipack_device_del); + +void ipack_get_device(struct ipack_device *dev) +{ + get_device(&dev->dev); } -EXPORT_SYMBOL_GPL(ipack_device_register); +EXPORT_SYMBOL_GPL(ipack_get_device); -void ipack_device_unregister(struct ipack_device *dev) +void ipack_put_device(struct ipack_device *dev) { - device_unregister(&dev->dev); + put_device(&dev->dev); } -EXPORT_SYMBOL_GPL(ipack_device_unregister); +EXPORT_SYMBOL_GPL(ipack_put_device); static int __init ipack_init(void) { diff --git a/drivers/irqchip/irq-gic.c b/drivers/irqchip/irq-gic.c index a32e0d5aa45f..fc6aebf1e4b2 100644 --- a/drivers/irqchip/irq-gic.c +++ b/drivers/irqchip/irq-gic.c @@ -236,7 +236,8 @@ static int gic_retrigger(struct irq_data *d) if (gic_arch_extn.irq_retrigger) return gic_arch_extn.irq_retrigger(d); - return -ENXIO; + /* the genirq layer expects 0 if we can't retrigger in hardware */ + return 0; } #ifdef CONFIG_SMP diff --git a/drivers/isdn/hardware/avm/avm_cs.c b/drivers/isdn/hardware/avm/avm_cs.c index c21353d8e915..62b8030ee331 100644 --- a/drivers/isdn/hardware/avm/avm_cs.c +++ b/drivers/isdn/hardware/avm/avm_cs.c @@ -163,16 +163,4 @@ static struct pcmcia_driver avmcs_driver = { .remove = avmcs_detach, .id_table = avmcs_ids, }; - -static int __init avmcs_init(void) -{ - return pcmcia_register_driver(&avmcs_driver); -} - -static void __exit avmcs_exit(void) -{ - pcmcia_unregister_driver(&avmcs_driver); -} - -module_init(avmcs_init); -module_exit(avmcs_exit); +module_pcmcia_driver(avmcs_driver); diff --git a/drivers/isdn/hisax/avma1_cs.c b/drivers/isdn/hisax/avma1_cs.c index 4e676bcf8506..baad94ec1f4a 100644 --- a/drivers/isdn/hisax/avma1_cs.c +++ b/drivers/isdn/hisax/avma1_cs.c @@ -159,16 +159,4 @@ static struct pcmcia_driver avma1cs_driver = { .remove = avma1cs_detach, .id_table = avma1cs_ids, }; - -static int __init init_avma1_cs(void) -{ - return pcmcia_register_driver(&avma1cs_driver); -} - -static void __exit exit_avma1_cs(void) -{ - pcmcia_unregister_driver(&avma1cs_driver); -} - -module_init(init_avma1_cs); -module_exit(exit_avma1_cs); +module_pcmcia_driver(avma1cs_driver); diff --git a/drivers/isdn/hisax/elsa_cs.c b/drivers/isdn/hisax/elsa_cs.c index ebe56918f6fc..40f6fad79de3 100644 --- a/drivers/isdn/hisax/elsa_cs.c +++ b/drivers/isdn/hisax/elsa_cs.c @@ -215,16 +215,4 @@ static struct pcmcia_driver elsa_cs_driver = { .suspend = elsa_suspend, .resume = elsa_resume, }; - -static int __init init_elsa_cs(void) -{ - return pcmcia_register_driver(&elsa_cs_driver); -} - -static void __exit exit_elsa_cs(void) -{ - pcmcia_unregister_driver(&elsa_cs_driver); -} - -module_init(init_elsa_cs); -module_exit(exit_elsa_cs); +module_pcmcia_driver(elsa_cs_driver); diff --git a/drivers/isdn/hisax/sedlbauer_cs.c b/drivers/isdn/hisax/sedlbauer_cs.c index 90f81291641b..92ef62d4caf4 100644 --- a/drivers/isdn/hisax/sedlbauer_cs.c +++ b/drivers/isdn/hisax/sedlbauer_cs.c @@ -206,16 +206,4 @@ static struct pcmcia_driver sedlbauer_driver = { .suspend = sedlbauer_suspend, .resume = sedlbauer_resume, }; - -static int __init init_sedlbauer_cs(void) -{ - return pcmcia_register_driver(&sedlbauer_driver); -} - -static void __exit exit_sedlbauer_cs(void) -{ - pcmcia_unregister_driver(&sedlbauer_driver); -} - -module_init(init_sedlbauer_cs); -module_exit(exit_sedlbauer_cs); +module_pcmcia_driver(sedlbauer_driver); diff --git a/drivers/isdn/hisax/teles_cs.c b/drivers/isdn/hisax/teles_cs.c index f2476ffb04fd..b8dd14958757 100644 --- a/drivers/isdn/hisax/teles_cs.c +++ b/drivers/isdn/hisax/teles_cs.c @@ -197,16 +197,4 @@ static struct pcmcia_driver teles_cs_driver = { .suspend = teles_suspend, .resume = teles_resume, }; - -static int __init init_teles_cs(void) -{ - return pcmcia_register_driver(&teles_cs_driver); -} - -static void __exit exit_teles_cs(void) -{ - pcmcia_unregister_driver(&teles_cs_driver); -} - -module_init(init_teles_cs); -module_exit(exit_teles_cs); +module_pcmcia_driver(teles_cs_driver); diff --git a/drivers/md/dm.c b/drivers/md/dm.c index 7e469260fe5e..9a0bdad9ad8f 100644 --- a/drivers/md/dm.c +++ b/drivers/md/dm.c @@ -611,6 +611,7 @@ static void dec_pending(struct dm_io *io, int error) queue_io(md, bio); } else { /* done with normal IO or empty flush */ + trace_block_bio_complete(md->queue, bio, io_error); bio_endio(bio, io_error); } } diff --git a/drivers/md/raid5.c b/drivers/md/raid5.c index 24909eb13fec..f4e87bfc7567 100644 --- a/drivers/md/raid5.c +++ b/drivers/md/raid5.c @@ -184,6 +184,8 @@ static void return_io(struct bio *return_bi) return_bi = bi->bi_next; bi->bi_next = NULL; bi->bi_size = 0; + trace_block_bio_complete(bdev_get_queue(bi->bi_bdev), + bi, 0); bio_endio(bi, 0); bi = return_bi; } @@ -3914,6 +3916,8 @@ static void raid5_align_endio(struct bio *bi, int error) rdev_dec_pending(rdev, conf->mddev); if (!error && uptodate) { + trace_block_bio_complete(bdev_get_queue(raid_bi->bi_bdev), + raid_bi, 0); bio_endio(raid_bi, 0); if (atomic_dec_and_test(&conf->active_aligned_reads)) wake_up(&conf->wait_for_stripe); @@ -4382,6 +4386,8 @@ static void make_request(struct mddev *mddev, struct bio * bi) if ( rw == WRITE ) md_write_end(mddev); + trace_block_bio_complete(bdev_get_queue(bi->bi_bdev), + bi, 0); bio_endio(bi, 0); } } @@ -4758,8 +4764,11 @@ static int retry_aligned_read(struct r5conf *conf, struct bio *raid_bio) handled++; } remaining = raid5_dec_bi_active_stripes(raid_bio); - if (remaining == 0) + if (remaining == 0) { + trace_block_bio_complete(bdev_get_queue(raid_bio->bi_bdev), + raid_bio, 0); bio_endio(raid_bio, 0); + } if (atomic_dec_and_test(&conf->active_aligned_reads)) wake_up(&conf->wait_for_stripe); return handled; diff --git a/drivers/media/dvb-frontends/mb86a20s.c b/drivers/media/dvb-frontends/mb86a20s.c index f19cd7367040..4faaf8053f26 100644 --- a/drivers/media/dvb-frontends/mb86a20s.c +++ b/drivers/media/dvb-frontends/mb86a20s.c @@ -610,7 +610,7 @@ static void mb86a20s_layer_bitrate(struct dvb_frontend *fe, u32 layer, __func__, 'A' + layer, segment * isdbt_rate[m][f][i]/1000, rate, rate); - state->estimated_rate[i] = rate; + state->estimated_rate[layer] = rate; } diff --git a/drivers/media/pci/cx25821/cx25821-video.c b/drivers/media/pci/cx25821/cx25821-video.c index d4de021dc844..31ce7698acb9 100644 --- a/drivers/media/pci/cx25821/cx25821-video.c +++ b/drivers/media/pci/cx25821/cx25821-video.c @@ -461,7 +461,7 @@ int cx25821_video_register(struct cx25821_dev *dev) spin_lock_init(&dev->slock); - for (i = 0; i < MAX_VID_CHANNEL_NUM - 1; ++i) { + for (i = 0; i < VID_CHANNEL_NUM; ++i) { cx25821_init_controls(dev, i); cx25821_risc_stopper(dev->pci, &dev->channels[i].vidq.stopper, diff --git a/drivers/memory/emif.c b/drivers/memory/emif.c index df0873694858..cadf1cc19aaf 100644 --- a/drivers/memory/emif.c +++ b/drivers/memory/emif.c @@ -25,6 +25,7 @@ #include <linux/module.h> #include <linux/list.h> #include <linux/spinlock.h> +#include <linux/pm.h> #include <memory/jedec_ddr.h> #include "emif.h" #include "of_memory.h" @@ -256,6 +257,41 @@ static void set_lpmode(struct emif_data *emif, u8 lpmode) u32 temp; void __iomem *base = emif->base; + /* + * Workaround for errata i743 - LPDDR2 Power-Down State is Not + * Efficient + * + * i743 DESCRIPTION: + * The EMIF supports power-down state for low power. The EMIF + * automatically puts the SDRAM into power-down after the memory is + * not accessed for a defined number of cycles and the + * EMIF_PWR_MGMT_CTRL[10:8] REG_LP_MODE bit field is set to 0x4. + * As the EMIF supports automatic output impedance calibration, a ZQ + * calibration long command is issued every time it exits active + * power-down and precharge power-down modes. The EMIF waits and + * blocks any other command during this calibration. + * The EMIF does not allow selective disabling of ZQ calibration upon + * exit of power-down mode. Due to very short periods of power-down + * cycles, ZQ calibration overhead creates bandwidth issues and + * increases overall system power consumption. On the other hand, + * issuing ZQ calibration long commands when exiting self-refresh is + * still required. + * + * WORKAROUND + * Because there is no power consumption benefit of the power-down due + * to the calibration and there is a performance risk, the guideline + * is to not allow power-down state and, therefore, to not have set + * the EMIF_PWR_MGMT_CTRL[10:8] REG_LP_MODE bit field to 0x4. + */ + if ((emif->plat_data->ip_rev == EMIF_4D) && + (EMIF_LP_MODE_PWR_DN == lpmode)) { + WARN_ONCE(1, + "REG_LP_MODE = LP_MODE_PWR_DN(4) is prohibited by" + "erratum i743 switch to LP_MODE_SELF_REFRESH(2)\n"); + /* rollback LP_MODE to Self-refresh mode */ + lpmode = EMIF_LP_MODE_SELF_REFRESH; + } + temp = readl(base + EMIF_POWER_MANAGEMENT_CONTROL); temp &= ~LP_MODE_MASK; temp |= (lpmode << LP_MODE_SHIFT); @@ -715,6 +751,8 @@ static u32 get_pwr_mgmt_ctrl(u32 freq, struct emif_data *emif, u32 ip_rev) u32 timeout_perf = EMIF_LP_MODE_TIMEOUT_PERFORMANCE; u32 timeout_pwr = EMIF_LP_MODE_TIMEOUT_POWER; u32 freq_threshold = EMIF_LP_MODE_FREQ_THRESHOLD; + u32 mask; + u8 shift; struct emif_custom_configs *cust_cfgs = emif->plat_data->custom_configs; @@ -728,37 +766,59 @@ static u32 get_pwr_mgmt_ctrl(u32 freq, struct emif_data *emif, u32 ip_rev) /* Timeout based on DDR frequency */ timeout = freq >= freq_threshold ? timeout_perf : timeout_pwr; - /* The value to be set in register is "log2(timeout) - 3" */ + /* + * The value to be set in register is "log2(timeout) - 3" + * if timeout < 16 load 0 in register + * if timeout is not a power of 2, round to next highest power of 2 + */ if (timeout < 16) { timeout = 0; } else { - timeout = __fls(timeout) - 3; if (timeout & (timeout - 1)) - timeout++; + timeout <<= 1; + timeout = __fls(timeout) - 3; } switch (lpmode) { case EMIF_LP_MODE_CLOCK_STOP: - pwr_mgmt_ctrl = (timeout << CS_TIM_SHIFT) | - SR_TIM_MASK | PD_TIM_MASK; + shift = CS_TIM_SHIFT; + mask = CS_TIM_MASK; break; case EMIF_LP_MODE_SELF_REFRESH: /* Workaround for errata i735 */ if (timeout < 6) timeout = 6; - pwr_mgmt_ctrl = (timeout << SR_TIM_SHIFT) | - CS_TIM_MASK | PD_TIM_MASK; + shift = SR_TIM_SHIFT; + mask = SR_TIM_MASK; break; case EMIF_LP_MODE_PWR_DN: - pwr_mgmt_ctrl = (timeout << PD_TIM_SHIFT) | - CS_TIM_MASK | SR_TIM_MASK; + shift = PD_TIM_SHIFT; + mask = PD_TIM_MASK; break; case EMIF_LP_MODE_DISABLE: default: - pwr_mgmt_ctrl = CS_TIM_MASK | - PD_TIM_MASK | SR_TIM_MASK; + mask = 0; + shift = 0; + break; } + /* Round to maximum in case of overflow, BUT warn! */ + if (lpmode != EMIF_LP_MODE_DISABLE && timeout > mask >> shift) { + pr_err("TIMEOUT Overflow - lpmode=%d perf=%d pwr=%d freq=%d\n", + lpmode, + timeout_perf, + timeout_pwr, + freq_threshold); + WARN(1, "timeout=0x%02x greater than 0x%02x. Using max\n", + timeout, mask >> shift); + timeout = mask >> shift; + } + + /* Setup required timing */ + pwr_mgmt_ctrl = (timeout << shift) & mask; + /* setup a default mask for rest of the modes */ + pwr_mgmt_ctrl |= (SR_TIM_MASK | CS_TIM_MASK | PD_TIM_MASK) & + ~mask; /* No CS_TIM in EMIF_4D5 */ if (ip_rev == EMIF_4D5) @@ -815,6 +875,8 @@ static void setup_registers(struct emif_data *emif, struct emif_regs *regs) writel(regs->sdram_tim2_shdw, base + EMIF_SDRAM_TIMING_2_SHDW); writel(regs->phy_ctrl_1_shdw, base + EMIF_DDR_PHY_CTRL_1_SHDW); + writel(regs->pwr_mgmt_ctrl_shdw, + base + EMIF_POWER_MANAGEMENT_CTRL_SHDW); /* Settings specific for EMIF4D5 */ if (emif->plat_data->ip_rev != EMIF_4D5) @@ -892,6 +954,7 @@ static irqreturn_t handle_temp_alert(void __iomem *base, struct emif_data *emif) { u32 old_temp_level; irqreturn_t ret = IRQ_HANDLED; + struct emif_custom_configs *custom_configs; spin_lock_irqsave(&emif_lock, irq_state); old_temp_level = emif->temperature_level; @@ -904,6 +967,29 @@ static irqreturn_t handle_temp_alert(void __iomem *base, struct emif_data *emif) goto out; } + custom_configs = emif->plat_data->custom_configs; + + /* + * IF we detect higher than "nominal rating" from DDR sensor + * on an unsupported DDR part, shutdown system + */ + if (custom_configs && !(custom_configs->mask & + EMIF_CUSTOM_CONFIG_EXTENDED_TEMP_PART)) { + if (emif->temperature_level >= SDRAM_TEMP_HIGH_DERATE_REFRESH) { + dev_err(emif->dev, + "%s:NOT Extended temperature capable memory." + "Converting MR4=0x%02x as shutdown event\n", + __func__, emif->temperature_level); + /* + * Temperature far too high - do kernel_power_off() + * from thread context + */ + emif->temperature_level = SDRAM_TEMP_VERY_HIGH_SHUTDOWN; + ret = IRQ_WAKE_THREAD; + goto out; + } + } + if (emif->temperature_level < old_temp_level || emif->temperature_level == SDRAM_TEMP_VERY_HIGH_SHUTDOWN) { /* @@ -965,7 +1051,14 @@ static irqreturn_t emif_threaded_isr(int irq, void *dev_id) if (emif->temperature_level == SDRAM_TEMP_VERY_HIGH_SHUTDOWN) { dev_emerg(emif->dev, "SDRAM temperature exceeds operating limit.. Needs shut down!!!\n"); - kernel_power_off(); + + /* If we have Power OFF ability, use it, else try restarting */ + if (pm_power_off) { + kernel_power_off(); + } else { + WARN(1, "FIXME: NO pm_power_off!!! trying restart\n"); + kernel_restart("SDRAM Over-temp Emergency restart"); + } return IRQ_HANDLED; } @@ -1170,7 +1263,7 @@ static void __init_or_module of_get_custom_configs(struct device_node *np_emif, { struct emif_custom_configs *cust_cfgs = NULL; int len; - const int *lpmode, *poll_intvl; + const __be32 *lpmode, *poll_intvl; lpmode = of_get_property(np_emif, "low-power-mode", &len); poll_intvl = of_get_property(np_emif, "temp-alert-poll-interval", &len); @@ -1184,7 +1277,7 @@ static void __init_or_module of_get_custom_configs(struct device_node *np_emif, if (lpmode) { cust_cfgs->mask |= EMIF_CUSTOM_CONFIG_LPMODE; - cust_cfgs->lpmode = *lpmode; + cust_cfgs->lpmode = be32_to_cpup(lpmode); of_property_read_u32(np_emif, "low-power-mode-timeout-performance", &cust_cfgs->lpmode_timeout_performance); @@ -1199,9 +1292,13 @@ static void __init_or_module of_get_custom_configs(struct device_node *np_emif, if (poll_intvl) { cust_cfgs->mask |= EMIF_CUSTOM_CONFIG_TEMP_ALERT_POLL_INTERVAL; - cust_cfgs->temp_alert_poll_interval_ms = *poll_intvl; + cust_cfgs->temp_alert_poll_interval_ms = + be32_to_cpup(poll_intvl); } + if (of_find_property(np_emif, "extended-temp-part", &len)) + cust_cfgs->mask |= EMIF_CUSTOM_CONFIG_EXTENDED_TEMP_PART; + if (!is_custom_config_valid(cust_cfgs, emif->dev)) { devm_kfree(emif->dev, cust_cfgs); return; @@ -1407,7 +1504,7 @@ static struct emif_data *__init_or_module get_device_details( if (pd->timings) { temp = devm_kzalloc(dev, size, GFP_KERNEL); if (temp) { - memcpy(temp, pd->timings, sizeof(*pd->timings)); + memcpy(temp, pd->timings, size); pd->timings = temp; } else { dev_warn(dev, "%s:%d: allocation error\n", __func__, @@ -1841,18 +1938,8 @@ static struct platform_driver emif_driver = { }, }; -static int __init_or_module emif_register(void) -{ - return platform_driver_probe(&emif_driver, emif_probe); -} - -static void __exit emif_unregister(void) -{ - platform_driver_unregister(&emif_driver); -} +module_platform_driver_probe(emif_driver, emif_probe); -module_init(emif_register); -module_exit(emif_unregister); MODULE_DESCRIPTION("TI EMIF SDRAM Controller Driver"); MODULE_LICENSE("GPL"); MODULE_ALIAS("platform:emif"); diff --git a/drivers/memory/tegra30-mc.c b/drivers/memory/tegra30-mc.c index 0b975986777d..f4ae074badc3 100644 --- a/drivers/memory/tegra30-mc.c +++ b/drivers/memory/tegra30-mc.c @@ -268,6 +268,7 @@ static const u32 tegra30_mc_ctx[] = { MC_INTMASK, }; +#ifdef CONFIG_PM static int tegra30_mc_suspend(struct device *dev) { int i; @@ -291,6 +292,7 @@ static int tegra30_mc_resume(struct device *dev) mc_readl(mc, MC_TIMING_CONTROL); return 0; } +#endif static UNIVERSAL_DEV_PM_OPS(tegra30_mc_pm, tegra30_mc_suspend, diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index c346941a2515..ca86581d02ce 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -991,7 +991,7 @@ config MFD_PM8XXX config MFD_PM8921_CORE tristate "Qualcomm PM8921 PMIC chip" - depends on MSM_SSBI + depends on SSBI && BROKEN select MFD_CORE select MFD_PM8XXX help diff --git a/drivers/mfd/pm8921-core.c b/drivers/mfd/pm8921-core.c index d4b297cbd801..ecc137ffa8c3 100644 --- a/drivers/mfd/pm8921-core.c +++ b/drivers/mfd/pm8921-core.c @@ -17,7 +17,7 @@ #include <linux/platform_device.h> #include <linux/slab.h> #include <linux/err.h> -#include <linux/msm_ssbi.h> +#include <linux/ssbi.h> #include <linux/mfd/core.h> #include <linux/mfd/pm8xxx/pm8921.h> #include <linux/mfd/pm8xxx/core.h> @@ -35,7 +35,7 @@ static int pm8921_readb(const struct device *dev, u16 addr, u8 *val) const struct pm8xxx_drvdata *pm8921_drvdata = dev_get_drvdata(dev); const struct pm8921 *pmic = pm8921_drvdata->pm_chip_data; - return msm_ssbi_read(pmic->dev->parent, addr, val, 1); + return ssbi_read(pmic->dev->parent, addr, val, 1); } static int pm8921_writeb(const struct device *dev, u16 addr, u8 val) @@ -43,7 +43,7 @@ static int pm8921_writeb(const struct device *dev, u16 addr, u8 val) const struct pm8xxx_drvdata *pm8921_drvdata = dev_get_drvdata(dev); const struct pm8921 *pmic = pm8921_drvdata->pm_chip_data; - return msm_ssbi_write(pmic->dev->parent, addr, &val, 1); + return ssbi_write(pmic->dev->parent, addr, &val, 1); } static int pm8921_read_buf(const struct device *dev, u16 addr, u8 *buf, @@ -52,7 +52,7 @@ static int pm8921_read_buf(const struct device *dev, u16 addr, u8 *buf, const struct pm8xxx_drvdata *pm8921_drvdata = dev_get_drvdata(dev); const struct pm8921 *pmic = pm8921_drvdata->pm_chip_data; - return msm_ssbi_read(pmic->dev->parent, addr, buf, cnt); + return ssbi_read(pmic->dev->parent, addr, buf, cnt); } static int pm8921_write_buf(const struct device *dev, u16 addr, u8 *buf, @@ -61,7 +61,7 @@ static int pm8921_write_buf(const struct device *dev, u16 addr, u8 *buf, const struct pm8xxx_drvdata *pm8921_drvdata = dev_get_drvdata(dev); const struct pm8921 *pmic = pm8921_drvdata->pm_chip_data; - return msm_ssbi_write(pmic->dev->parent, addr, buf, cnt); + return ssbi_write(pmic->dev->parent, addr, buf, cnt); } static int pm8921_read_irq_stat(const struct device *dev, int irq) @@ -124,7 +124,7 @@ static int pm8921_probe(struct platform_device *pdev) } /* Read PMIC chip revision */ - rc = msm_ssbi_read(pdev->dev.parent, REG_HWREV, &val, sizeof(val)); + rc = ssbi_read(pdev->dev.parent, REG_HWREV, &val, sizeof(val)); if (rc) { pr_err("Failed to read hw rev reg %d:rc=%d\n", REG_HWREV, rc); goto err_read_rev; @@ -133,7 +133,7 @@ static int pm8921_probe(struct platform_device *pdev) rev = val; /* Read PMIC chip revision 2 */ - rc = msm_ssbi_read(pdev->dev.parent, REG_HWREV_2, &val, sizeof(val)); + rc = ssbi_read(pdev->dev.parent, REG_HWREV_2, &val, sizeof(val)); if (rc) { pr_err("Failed to read hw rev 2 reg %d:rc=%d\n", REG_HWREV_2, rc); diff --git a/drivers/mfd/wm5102-tables.c b/drivers/mfd/wm5102-tables.c index a433f580aa4c..ca2aed6bc830 100644 --- a/drivers/mfd/wm5102-tables.c +++ b/drivers/mfd/wm5102-tables.c @@ -331,6 +331,10 @@ static const struct reg_default wm5102_reg_default[] = { { 0x000002A3, 0x1102 }, /* R675 - Mic Detect 1 */ { 0x000002A4, 0x009F }, /* R676 - Mic Detect 2 */ { 0x000002A5, 0x0000 }, /* R677 - Mic Detect 3 */ + { 0x000002A6, 0x3737 }, /* R678 - Mic Detect Level 1 */ + { 0x000002A7, 0x372C }, /* R679 - Mic Detect Level 2 */ + { 0x000002A8, 0x1422 }, /* R680 - Mic Detect Level 3 */ + { 0x000002A9, 0x030A }, /* R681 - Mic Detect Level 4 */ { 0x000002C3, 0x0000 }, /* R707 - Mic noise mix control 1 */ { 0x000002CB, 0x0000 }, /* R715 - Isolation control */ { 0x000002D3, 0x0000 }, /* R723 - Jack detect analogue */ @@ -1090,6 +1094,10 @@ static bool wm5102_readable_register(struct device *dev, unsigned int reg) case ARIZONA_MIC_DETECT_1: case ARIZONA_MIC_DETECT_2: case ARIZONA_MIC_DETECT_3: + case ARIZONA_MIC_DETECT_LEVEL_1: + case ARIZONA_MIC_DETECT_LEVEL_2: + case ARIZONA_MIC_DETECT_LEVEL_3: + case ARIZONA_MIC_DETECT_LEVEL_4: case ARIZONA_MIC_NOISE_MIX_CONTROL_1: case ARIZONA_ISOLATION_CONTROL: case ARIZONA_JACK_DETECT_ANALOGUE: diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index e83fdfe0c8ca..e29e7980a359 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -93,6 +93,14 @@ config ATMEL_TCB_CLKSRC_BLOCK TC can be used for other purposes, such as PWM generation and interval timing. +config DUMMY_IRQ + tristate "Dummy IRQ handler" + default n + ---help--- + This module accepts a single 'irq' parameter, which it should register for. + The sole purpose of this module is to help with debugging of systems on + which spurious IRQs would happen on disabled IRQ vector. + config IBM_ASM tristate "Device driver for IBM RSA service processor" depends on X86 && PCI && INPUT @@ -398,7 +406,7 @@ config DS1682 config SPEAR13XX_PCIE_GADGET bool "PCIe gadget support for SPEAr13XX platform" - depends on ARCH_SPEAR13XX + depends on ARCH_SPEAR13XX && BROKEN default n help This option enables gadget support for PCIe controller. If diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile index 35a1463c72d9..865cbc6a7ae1 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile @@ -13,6 +13,7 @@ obj-$(CONFIG_ATMEL_TCLIB) += atmel_tclib.o obj-$(CONFIG_BMP085) += bmp085.o obj-$(CONFIG_BMP085_I2C) += bmp085-i2c.o obj-$(CONFIG_BMP085_SPI) += bmp085-spi.o +obj-$(CONFIG_DUMMY_IRQ) += dummy-irq.o obj-$(CONFIG_ICS932S401) += ics932s401.o obj-$(CONFIG_LKDTM) += lkdtm.o obj-$(CONFIG_TIFM_CORE) += tifm_core.o @@ -49,6 +50,5 @@ obj-y += carma/ obj-$(CONFIG_USB_SWITCH_FSA9480) += fsa9480.o obj-$(CONFIG_ALTERA_STAPL) +=altera-stapl/ obj-$(CONFIG_INTEL_MEI) += mei/ -obj-$(CONFIG_MAX8997_MUIC) += max8997-muic.o obj-$(CONFIG_VMWARE_VMCI) += vmw_vmci/ obj-$(CONFIG_LATTICE_ECP3_CONFIG) += lattice-ecp3-config.o diff --git a/drivers/misc/apds9802als.c b/drivers/misc/apds9802als.c index d648b0893027..5b5fd8416b3e 100644 --- a/drivers/misc/apds9802als.c +++ b/drivers/misc/apds9802als.c @@ -272,19 +272,8 @@ static int apds9802als_remove(struct i2c_client *client) } #ifdef CONFIG_PM -static int apds9802als_suspend(struct i2c_client *client, pm_message_t mesg) -{ - als_set_power_state(client, false); - return 0; -} - -static int apds9802als_resume(struct i2c_client *client) -{ - als_set_default_config(client); - return 0; -} -static int apds9802als_runtime_suspend(struct device *dev) +static int apds9802als_suspend(struct device *dev) { struct i2c_client *client = to_i2c_client(dev); @@ -292,7 +281,7 @@ static int apds9802als_runtime_suspend(struct device *dev) return 0; } -static int apds9802als_runtime_resume(struct device *dev) +static int apds9802als_resume(struct device *dev) { struct i2c_client *client = to_i2c_client(dev); @@ -300,16 +289,12 @@ static int apds9802als_runtime_resume(struct device *dev) return 0; } -static const struct dev_pm_ops apds9802als_pm_ops = { - .runtime_suspend = apds9802als_runtime_suspend, - .runtime_resume = apds9802als_runtime_resume, -}; +static UNIVERSAL_DEV_PM_OPS(apds9802als_pm_ops, apds9802als_suspend, + apds9802als_resume, NULL); #define APDS9802ALS_PM_OPS (&apds9802als_pm_ops) #else /* CONFIG_PM */ -#define apds9802als_suspend NULL -#define apds9802als_resume NULL #define APDS9802ALS_PM_OPS NULL #endif /* CONFIG_PM */ @@ -327,8 +312,6 @@ static struct i2c_driver apds9802als_driver = { }, .probe = apds9802als_probe, .remove = apds9802als_remove, - .suspend = apds9802als_suspend, - .resume = apds9802als_resume, .id_table = apds9802als_id, }; diff --git a/drivers/misc/apds990x.c b/drivers/misc/apds990x.c index 0e67f8263cd8..98f9bb26492a 100644 --- a/drivers/misc/apds990x.c +++ b/drivers/misc/apds990x.c @@ -700,9 +700,6 @@ static ssize_t apds990x_lux_calib_store(struct device *dev, if (strict_strtoul(buf, 0, &value)) return -EINVAL; - if (chip->lux_calib > APDS_RANGE) - return -EINVAL; - chip->lux_calib = value; return len; @@ -1204,7 +1201,7 @@ static int apds990x_remove(struct i2c_client *client) return 0; } -#ifdef CONFIG_PM +#ifdef CONFIG_PM_SLEEP static int apds990x_suspend(struct device *dev) { struct i2c_client *client = container_of(dev, struct i2c_client, dev); @@ -1227,10 +1224,6 @@ static int apds990x_resume(struct device *dev) return 0; } -#else -#define apds990x_suspend NULL -#define apds990x_resume NULL -#define apds990x_shutdown NULL #endif #ifdef CONFIG_PM_RUNTIME diff --git a/drivers/misc/arm-charlcd.c b/drivers/misc/arm-charlcd.c index fe8616a8d287..48651ef0028c 100644 --- a/drivers/misc/arm-charlcd.c +++ b/drivers/misc/arm-charlcd.c @@ -378,18 +378,7 @@ static struct platform_driver charlcd_driver = { .remove = __exit_p(charlcd_remove), }; -static int __init charlcd_init(void) -{ - return platform_driver_probe(&charlcd_driver, charlcd_probe); -} - -static void __exit charlcd_exit(void) -{ - platform_driver_unregister(&charlcd_driver); -} - -module_init(charlcd_init); -module_exit(charlcd_exit); +module_platform_driver_probe(charlcd_driver, charlcd_probe); MODULE_AUTHOR("Linus Walleij <triad@df.lth.se>"); MODULE_DESCRIPTION("ARM Character LCD Driver"); diff --git a/drivers/misc/atmel_pwm.c b/drivers/misc/atmel_pwm.c index 28f5aaa19d4a..494d0500bda6 100644 --- a/drivers/misc/atmel_pwm.c +++ b/drivers/misc/atmel_pwm.c @@ -393,17 +393,7 @@ static struct platform_driver atmel_pwm_driver = { */ }; -static int __init pwm_init(void) -{ - return platform_driver_probe(&atmel_pwm_driver, pwm_probe); -} -module_init(pwm_init); - -static void __exit pwm_exit(void) -{ - platform_driver_unregister(&atmel_pwm_driver); -} -module_exit(pwm_exit); +module_platform_driver_probe(atmel_pwm_driver, pwm_probe); MODULE_DESCRIPTION("Driver for AT32/AT91 PWM module"); MODULE_LICENSE("GPL"); diff --git a/drivers/misc/bh1770glc.c b/drivers/misc/bh1770glc.c index 2ed8fc3be7e6..f4975f7d0d5b 100644 --- a/drivers/misc/bh1770glc.c +++ b/drivers/misc/bh1770glc.c @@ -1310,7 +1310,7 @@ static int bh1770_remove(struct i2c_client *client) return 0; } -#ifdef CONFIG_PM +#ifdef CONFIG_PM_SLEEP static int bh1770_suspend(struct device *dev) { struct i2c_client *client = container_of(dev, struct i2c_client, dev); @@ -1346,11 +1346,6 @@ static int bh1770_resume(struct device *dev) } return ret; } - -#else -#define bh1770_suspend NULL -#define bh1770_shutdown NULL -#define bh1770_resume NULL #endif #ifdef CONFIG_PM_RUNTIME diff --git a/drivers/misc/bh1780gli.c b/drivers/misc/bh1780gli.c index cf03d0abf33e..818f3a0e62bf 100644 --- a/drivers/misc/bh1780gli.c +++ b/drivers/misc/bh1780gli.c @@ -196,7 +196,7 @@ static int bh1780_remove(struct i2c_client *client) return 0; } -#ifdef CONFIG_PM +#ifdef CONFIG_PM_SLEEP static int bh1780_suspend(struct device *dev) { struct bh1780_data *ddata; @@ -235,11 +235,9 @@ static int bh1780_resume(struct device *dev) return 0; } +#endif /* CONFIG_PM_SLEEP */ + static SIMPLE_DEV_PM_OPS(bh1780_pm, bh1780_suspend, bh1780_resume); -#define BH1780_PMOPS (&bh1780_pm) -#else -#define BH1780_PMOPS NULL -#endif /* CONFIG_PM */ static const struct i2c_device_id bh1780_id[] = { { "bh1780", 0 }, @@ -252,7 +250,7 @@ static struct i2c_driver bh1780_driver = { .id_table = bh1780_id, .driver = { .name = "bh1780", - .pm = BH1780_PMOPS, + .pm = &bh1780_pm, }, }; diff --git a/drivers/misc/cs5535-mfgpt.c b/drivers/misc/cs5535-mfgpt.c index 9858f36dad8b..effd8c6b2b94 100644 --- a/drivers/misc/cs5535-mfgpt.c +++ b/drivers/misc/cs5535-mfgpt.c @@ -24,8 +24,11 @@ static int mfgpt_reset_timers; module_param_named(mfgptfix, mfgpt_reset_timers, int, 0644); -MODULE_PARM_DESC(mfgptfix, "Reset the MFGPT timers during init; " - "required by some broken BIOSes (ie, TinyBIOS < 0.99)."); +MODULE_PARM_DESC(mfgptfix, "Try to reset the MFGPT timers during init; " + "required by some broken BIOSes (ie, TinyBIOS < 0.99) or kexec " + "(1 = reset the MFGPT using an undocumented bit, " + "2 = perform a soft reset by unconfiguring all timers); " + "use what works best for you."); struct cs5535_mfgpt_timer { struct cs5535_mfgpt_chip *chip; @@ -256,6 +259,28 @@ static void reset_all_timers(void) } /* + * This is another sledgehammer to reset all MFGPT timers. + * Instead of using the undocumented bit method it clears + * IRQ, NMI and RESET settings. + */ +static void soft_reset(void) +{ + int i; + struct cs5535_mfgpt_timer t; + + for (i = 0; i < MFGPT_MAX_TIMERS; i++) { + t.nr = i; + + cs5535_mfgpt_toggle_event(&t, MFGPT_CMP1, MFGPT_EVENT_RESET, 0); + cs5535_mfgpt_toggle_event(&t, MFGPT_CMP2, MFGPT_EVENT_RESET, 0); + cs5535_mfgpt_toggle_event(&t, MFGPT_CMP1, MFGPT_EVENT_NMI, 0); + cs5535_mfgpt_toggle_event(&t, MFGPT_CMP2, MFGPT_EVENT_NMI, 0); + cs5535_mfgpt_toggle_event(&t, MFGPT_CMP1, MFGPT_EVENT_IRQ, 0); + cs5535_mfgpt_toggle_event(&t, MFGPT_CMP2, MFGPT_EVENT_IRQ, 0); + } +} + +/* * Check whether any MFGPTs are available for the kernel to use. In most * cases, firmware that uses AMD's VSA code will claim all timers during * bootup; we certainly don't want to take them if they're already in use. @@ -271,15 +296,17 @@ static int scan_timers(struct cs5535_mfgpt_chip *mfgpt) int i; /* bios workaround */ - if (mfgpt_reset_timers) + if (mfgpt_reset_timers == 1) reset_all_timers(); + else if (mfgpt_reset_timers == 2) + soft_reset(); /* just to be safe, protect this section w/ lock */ spin_lock_irqsave(&mfgpt->lock, flags); for (i = 0; i < MFGPT_MAX_TIMERS; i++) { timer.nr = i; val = cs5535_mfgpt_read(&timer, MFGPT_REG_SETUP); - if (!(val & MFGPT_SETUP_SETUP)) { + if (!(val & MFGPT_SETUP_SETUP) || mfgpt_reset_timers == 2) { __set_bit(i, mfgpt->avail); timers++; } @@ -294,6 +321,12 @@ static int cs5535_mfgpt_probe(struct platform_device *pdev) struct resource *res; int err = -EIO, t; + if (mfgpt_reset_timers < 0 || mfgpt_reset_timers > 2) { + dev_err(&pdev->dev, "Bad mfgpt_reset_timers value: %i\n", + mfgpt_reset_timers); + goto done; + } + /* There are two ways to get the MFGPT base address; one is by * fetching it from MSR_LBAR_MFGPT, the other is by reading the * PCI BAR info. The latter method is easier (especially across diff --git a/drivers/misc/dummy-irq.c b/drivers/misc/dummy-irq.c new file mode 100644 index 000000000000..7014167e2c61 --- /dev/null +++ b/drivers/misc/dummy-irq.c @@ -0,0 +1,59 @@ +/* + * Dummy IRQ handler driver. + * + * This module only registers itself as a handler that is specified to it + * by the 'irq' parameter. + * + * The sole purpose of this module is to help with debugging of systems on + * which spurious IRQs would happen on disabled IRQ vector. + * + * Copyright (C) 2013 Jiri Kosina + */ + +/* + * 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/irq.h> +#include <linux/interrupt.h> + +static int irq; + +static irqreturn_t dummy_interrupt(int irq, void *dev_id) +{ + static int count = 0; + + if (count == 0) { + printk(KERN_INFO "dummy-irq: interrupt occured on IRQ %d\n", + irq); + count++; + } + + return IRQ_NONE; +} + +static int __init dummy_irq_init(void) +{ + if (request_irq(irq, &dummy_interrupt, IRQF_SHARED, "dummy_irq", &irq)) { + printk(KERN_ERR "dummy-irq: cannot register IRQ %d\n", irq); + return -EIO; + } + printk(KERN_INFO "dummy-irq: registered for IRQ %d\n", irq); + return 0; +} + +static void __exit dummy_irq_exit(void) +{ + printk(KERN_INFO "dummy-irq unloaded\n"); + free_irq(irq, &irq); +} + +module_init(dummy_irq_init); +module_exit(dummy_irq_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Jiri Kosina"); +module_param(irq, uint, 0444); +MODULE_PARM_DESC(irq, "The IRQ to register for"); diff --git a/drivers/misc/eeprom/at25.c b/drivers/misc/eeprom/at25.c index b08cf8a08789..ad8fd8e64937 100644 --- a/drivers/misc/eeprom/at25.c +++ b/drivers/misc/eeprom/at25.c @@ -412,7 +412,7 @@ static int at25_probe(struct spi_device *spi) mutex_init(&at25->lock); at25->chip = chip; at25->spi = spi_dev_get(spi); - dev_set_drvdata(&spi->dev, at25); + spi_set_drvdata(spi, at25); at25->addrlen = addrlen; /* Export the EEPROM bytes through sysfs, since that's convenient. @@ -463,7 +463,7 @@ static int at25_remove(struct spi_device *spi) { struct at25_data *at25; - at25 = dev_get_drvdata(&spi->dev); + at25 = spi_get_drvdata(spi); sysfs_remove_bin_file(&spi->dev.kobj, &at25->bin); kfree(at25); return 0; diff --git a/drivers/misc/eeprom/eeprom_93xx46.c b/drivers/misc/eeprom/eeprom_93xx46.c index a6b5d5e73485..94cfc1212577 100644 --- a/drivers/misc/eeprom/eeprom_93xx46.c +++ b/drivers/misc/eeprom/eeprom_93xx46.c @@ -363,7 +363,7 @@ static int eeprom_93xx46_probe(struct spi_device *spi) dev_err(&spi->dev, "can't create erase interface\n"); } - dev_set_drvdata(&spi->dev, edev); + spi_set_drvdata(spi, edev); return 0; fail: kfree(edev); @@ -372,13 +372,13 @@ fail: static int eeprom_93xx46_remove(struct spi_device *spi) { - struct eeprom_93xx46_dev *edev = dev_get_drvdata(&spi->dev); + struct eeprom_93xx46_dev *edev = spi_get_drvdata(spi); if (!(edev->pdata->flags & EE_READONLY)) device_remove_file(&spi->dev, &dev_attr_erase); sysfs_remove_bin_file(&spi->dev.kobj, &edev->bin); - dev_set_drvdata(&spi->dev, NULL); + spi_set_drvdata(spi, NULL); kfree(edev); return 0; } diff --git a/drivers/misc/ep93xx_pwm.c b/drivers/misc/ep93xx_pwm.c index 16d7179e2f9b..96787ec15cad 100644 --- a/drivers/misc/ep93xx_pwm.c +++ b/drivers/misc/ep93xx_pwm.c @@ -365,18 +365,7 @@ static struct platform_driver ep93xx_pwm_driver = { .remove = __exit_p(ep93xx_pwm_remove), }; -static int __init ep93xx_pwm_init(void) -{ - return platform_driver_probe(&ep93xx_pwm_driver, ep93xx_pwm_probe); -} - -static void __exit ep93xx_pwm_exit(void) -{ - platform_driver_unregister(&ep93xx_pwm_driver); -} - -module_init(ep93xx_pwm_init); -module_exit(ep93xx_pwm_exit); +module_platform_driver_probe(ep93xx_pwm_driver, ep93xx_pwm_probe); MODULE_AUTHOR("Matthieu Crapet <mcrapet@gmail.com>, " "H Hartley Sweeten <hsweeten@visionengravers.com>"); diff --git a/drivers/misc/fsa9480.c b/drivers/misc/fsa9480.c index e8cbb1c59f4c..a725c79c35f5 100644 --- a/drivers/misc/fsa9480.c +++ b/drivers/misc/fsa9480.c @@ -474,10 +474,11 @@ static int fsa9480_remove(struct i2c_client *client) return 0; } -#ifdef CONFIG_PM +#ifdef CONFIG_PM_SLEEP -static int fsa9480_suspend(struct i2c_client *client, pm_message_t state) +static int fsa9480_suspend(struct device *dev) { + struct i2c_client *client = to_i2c_client(dev); struct fsa9480_usbsw *usbsw = i2c_get_clientdata(client); struct fsa9480_platform_data *pdata = usbsw->pdata; @@ -490,8 +491,9 @@ static int fsa9480_suspend(struct i2c_client *client, pm_message_t state) return 0; } -static int fsa9480_resume(struct i2c_client *client) +static int fsa9480_resume(struct device *dev) { + struct i2c_client *client = to_i2c_client(dev); struct fsa9480_usbsw *usbsw = i2c_get_clientdata(client); int dev1, dev2; @@ -515,12 +517,14 @@ static int fsa9480_resume(struct i2c_client *client) return 0; } +static SIMPLE_DEV_PM_OPS(fsa9480_pm_ops, fsa9480_suspend, fsa9480_resume); +#define FSA9480_PM_OPS (&fsa9480_pm_ops) + #else -#define fsa9480_suspend NULL -#define fsa9480_resume NULL +#define FSA9480_PM_OPS NULL -#endif /* CONFIG_PM */ +#endif /* CONFIG_PM_SLEEP */ static const struct i2c_device_id fsa9480_id[] = { {"fsa9480", 0}, @@ -531,11 +535,10 @@ MODULE_DEVICE_TABLE(i2c, fsa9480_id); static struct i2c_driver fsa9480_i2c_driver = { .driver = { .name = "fsa9480", + .pm = FSA9480_PM_OPS, }, .probe = fsa9480_probe, .remove = fsa9480_remove, - .resume = fsa9480_resume, - .suspend = fsa9480_suspend, .id_table = fsa9480_id, }; diff --git a/drivers/misc/isl29003.c b/drivers/misc/isl29003.c index 29b306c6bdb3..c5145b3fcce8 100644 --- a/drivers/misc/isl29003.c +++ b/drivers/misc/isl29003.c @@ -409,18 +409,20 @@ static int isl29003_remove(struct i2c_client *client) return 0; } -#ifdef CONFIG_PM -static int isl29003_suspend(struct i2c_client *client, pm_message_t mesg) +#ifdef CONFIG_PM_SLEEP +static int isl29003_suspend(struct device *dev) { + struct i2c_client *client = to_i2c_client(dev); struct isl29003_data *data = i2c_get_clientdata(client); data->power_state_before_suspend = isl29003_get_power_state(client); return isl29003_set_power_state(client, 0); } -static int isl29003_resume(struct i2c_client *client) +static int isl29003_resume(struct device *dev) { int i; + struct i2c_client *client = to_i2c_client(dev); struct isl29003_data *data = i2c_get_clientdata(client); /* restore registers from cache */ @@ -432,10 +434,12 @@ static int isl29003_resume(struct i2c_client *client) data->power_state_before_suspend); } +static SIMPLE_DEV_PM_OPS(isl29003_pm_ops, isl29003_suspend, isl29003_resume); +#define ISL29003_PM_OPS (&isl29003_pm_ops) + #else -#define isl29003_suspend NULL -#define isl29003_resume NULL -#endif /* CONFIG_PM */ +#define ISL29003_PM_OPS NULL +#endif /* CONFIG_PM_SLEEP */ static const struct i2c_device_id isl29003_id[] = { { "isl29003", 0 }, @@ -447,9 +451,8 @@ static struct i2c_driver isl29003_driver = { .driver = { .name = ISL29003_DRV_NAME, .owner = THIS_MODULE, + .pm = ISL29003_PM_OPS, }, - .suspend = isl29003_suspend, - .resume = isl29003_resume, .probe = isl29003_probe, .remove = isl29003_remove, .id_table = isl29003_id, diff --git a/drivers/misc/lattice-ecp3-config.c b/drivers/misc/lattice-ecp3-config.c index 155700bfd2b6..bb26f086bd8b 100644 --- a/drivers/misc/lattice-ecp3-config.c +++ b/drivers/misc/lattice-ecp3-config.c @@ -69,7 +69,7 @@ static const struct ecp3_dev ecp3_dev[] = { static void firmware_load(const struct firmware *fw, void *context) { struct spi_device *spi = (struct spi_device *)context; - struct fpga_data *data = dev_get_drvdata(&spi->dev); + struct fpga_data *data = spi_get_drvdata(spi); u8 *buffer; int ret; u8 txbuf[8]; diff --git a/drivers/misc/mei/Kconfig b/drivers/misc/mei/Kconfig index d21b4d006a55..c76fa31e9bf6 100644 --- a/drivers/misc/mei/Kconfig +++ b/drivers/misc/mei/Kconfig @@ -10,10 +10,9 @@ config INTEL_MEI <http://software.intel.com/en-us/manageability/> config INTEL_MEI_ME - bool "ME Enabled Intel Chipsets" - depends on INTEL_MEI + tristate "ME Enabled Intel Chipsets" + select INTEL_MEI depends on X86 && PCI && WATCHDOG_CORE - default y help MEI support for ME Enabled Intel chipsets. diff --git a/drivers/misc/mei/Makefile b/drivers/misc/mei/Makefile index 040af6c7b147..08698a466268 100644 --- a/drivers/misc/mei/Makefile +++ b/drivers/misc/mei/Makefile @@ -10,5 +10,10 @@ mei-objs += client.o mei-objs += main.o mei-objs += amthif.o mei-objs += wd.o -mei-$(CONFIG_INTEL_MEI_ME) += pci-me.o -mei-$(CONFIG_INTEL_MEI_ME) += hw-me.o +mei-objs += bus.o +mei-objs += nfc.o +mei-$(CONFIG_DEBUG_FS) += debugfs.o + +obj-$(CONFIG_INTEL_MEI_ME) += mei-me.o +mei-me-objs := pci-me.o +mei-me-objs += hw-me.o diff --git a/drivers/misc/mei/amthif.c b/drivers/misc/mei/amthif.c index c86d7e3839a4..31a6212cb8b1 100644 --- a/drivers/misc/mei/amthif.c +++ b/drivers/misc/mei/amthif.c @@ -449,7 +449,7 @@ int mei_amthif_irq_write_complete(struct mei_device *dev, s32 *slots, struct mei_msg_hdr mei_hdr; struct mei_cl *cl = cb->cl; size_t len = dev->iamthif_msg_buf_size - dev->iamthif_msg_buf_index; - size_t msg_slots = mei_data2slots(len); + u32 msg_slots = mei_data2slots(len); mei_hdr.host_addr = cl->host_client_id; mei_hdr.me_addr = cl->me_client_id; @@ -505,14 +505,15 @@ int mei_amthif_irq_write_complete(struct mei_device *dev, s32 *slots, * mei_amthif_irq_read_message - read routine after ISR to * handle the read amthif message * - * @complete_list: An instance of our list structure * @dev: the device structure * @mei_hdr: header of amthif message + * @complete_list: An instance of our list structure * * returns 0 on success, <0 on failure. */ -int mei_amthif_irq_read_message(struct mei_cl_cb *complete_list, - struct mei_device *dev, struct mei_msg_hdr *mei_hdr) +int mei_amthif_irq_read_msg(struct mei_device *dev, + struct mei_msg_hdr *mei_hdr, + struct mei_cl_cb *complete_list) { struct mei_cl_cb *cb; unsigned char *buffer; @@ -530,8 +531,7 @@ int mei_amthif_irq_read_message(struct mei_cl_cb *complete_list, if (!mei_hdr->msg_complete) return 0; - dev_dbg(&dev->pdev->dev, - "amthif_message_buffer_index =%d\n", + dev_dbg(&dev->pdev->dev, "amthif_message_buffer_index =%d\n", mei_hdr->length); dev_dbg(&dev->pdev->dev, "completed amthif read.\n "); @@ -566,12 +566,13 @@ int mei_amthif_irq_read_message(struct mei_cl_cb *complete_list, */ int mei_amthif_irq_read(struct mei_device *dev, s32 *slots) { + u32 msg_slots = mei_data2slots(sizeof(struct hbm_flow_control)); - if (((*slots) * sizeof(u32)) < (sizeof(struct mei_msg_hdr) - + sizeof(struct hbm_flow_control))) { + if (*slots < msg_slots) return -EMSGSIZE; - } - *slots -= mei_data2slots(sizeof(struct hbm_flow_control)); + + *slots -= msg_slots; + if (mei_hbm_cl_flow_control_req(dev, &dev->iamthif_cl)) { dev_dbg(&dev->pdev->dev, "iamthif flow control failed\n"); return -EIO; diff --git a/drivers/misc/mei/bus.c b/drivers/misc/mei/bus.c new file mode 100644 index 000000000000..1e935eacaa7f --- /dev/null +++ b/drivers/misc/mei/bus.c @@ -0,0 +1,528 @@ +/* + * Intel Management Engine Interface (Intel MEI) Linux driver + * Copyright (c) 2012-2013, 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. + * + */ + +#include <linux/module.h> +#include <linux/device.h> +#include <linux/kernel.h> +#include <linux/sched.h> +#include <linux/init.h> +#include <linux/errno.h> +#include <linux/slab.h> +#include <linux/mutex.h> +#include <linux/interrupt.h> +#include <linux/pci.h> +#include <linux/mei_cl_bus.h> + +#include "mei_dev.h" +#include "hw-me.h" +#include "client.h" + +#define to_mei_cl_driver(d) container_of(d, struct mei_cl_driver, driver) +#define to_mei_cl_device(d) container_of(d, struct mei_cl_device, dev) + +static int mei_cl_device_match(struct device *dev, struct device_driver *drv) +{ + struct mei_cl_device *device = to_mei_cl_device(dev); + struct mei_cl_driver *driver = to_mei_cl_driver(drv); + const struct mei_cl_device_id *id; + + if (!device) + return 0; + + if (!driver || !driver->id_table) + return 0; + + id = driver->id_table; + + while (id->name[0]) { + if (!strcmp(dev_name(dev), id->name)) + return 1; + + id++; + } + + return 0; +} + +static int mei_cl_device_probe(struct device *dev) +{ + struct mei_cl_device *device = to_mei_cl_device(dev); + struct mei_cl_driver *driver; + struct mei_cl_device_id id; + + if (!device) + return 0; + + driver = to_mei_cl_driver(dev->driver); + if (!driver || !driver->probe) + return -ENODEV; + + dev_dbg(dev, "Device probe\n"); + + strncpy(id.name, dev_name(dev), MEI_CL_NAME_SIZE); + + return driver->probe(device, &id); +} + +static int mei_cl_device_remove(struct device *dev) +{ + struct mei_cl_device *device = to_mei_cl_device(dev); + struct mei_cl_driver *driver; + + if (!device || !dev->driver) + return 0; + + if (device->event_cb) { + device->event_cb = NULL; + cancel_work_sync(&device->event_work); + } + + driver = to_mei_cl_driver(dev->driver); + if (!driver->remove) { + dev->driver = NULL; + + return 0; + } + + return driver->remove(device); +} + +static ssize_t modalias_show(struct device *dev, struct device_attribute *a, + char *buf) +{ + int len; + + len = snprintf(buf, PAGE_SIZE, "mei:%s\n", dev_name(dev)); + + return (len >= PAGE_SIZE) ? (PAGE_SIZE - 1) : len; +} + +static struct device_attribute mei_cl_dev_attrs[] = { + __ATTR_RO(modalias), + __ATTR_NULL, +}; + +static int mei_cl_uevent(struct device *dev, struct kobj_uevent_env *env) +{ + if (add_uevent_var(env, "MODALIAS=mei:%s", dev_name(dev))) + return -ENOMEM; + + return 0; +} + +static struct bus_type mei_cl_bus_type = { + .name = "mei", + .dev_attrs = mei_cl_dev_attrs, + .match = mei_cl_device_match, + .probe = mei_cl_device_probe, + .remove = mei_cl_device_remove, + .uevent = mei_cl_uevent, +}; + +static void mei_cl_dev_release(struct device *dev) +{ + kfree(to_mei_cl_device(dev)); +} + +static struct device_type mei_cl_device_type = { + .release = mei_cl_dev_release, +}; + +static struct mei_cl *mei_bus_find_mei_cl_by_uuid(struct mei_device *dev, + uuid_le uuid) +{ + struct mei_cl *cl, *next; + + list_for_each_entry_safe(cl, next, &dev->device_list, device_link) { + if (!uuid_le_cmp(uuid, cl->device_uuid)) + return cl; + } + + return NULL; +} +struct mei_cl_device *mei_cl_add_device(struct mei_device *dev, + uuid_le uuid, char *name, + struct mei_cl_ops *ops) +{ + struct mei_cl_device *device; + struct mei_cl *cl; + int status; + + cl = mei_bus_find_mei_cl_by_uuid(dev, uuid); + if (cl == NULL) + return NULL; + + device = kzalloc(sizeof(struct mei_cl_device), GFP_KERNEL); + if (!device) + return NULL; + + device->cl = cl; + device->ops = ops; + + device->dev.parent = &dev->pdev->dev; + device->dev.bus = &mei_cl_bus_type; + device->dev.type = &mei_cl_device_type; + + dev_set_name(&device->dev, "%s", name); + + status = device_register(&device->dev); + if (status) { + dev_err(&dev->pdev->dev, "Failed to register MEI device\n"); + kfree(device); + return NULL; + } + + cl->device = device; + + dev_dbg(&device->dev, "client %s registered\n", name); + + return device; +} +EXPORT_SYMBOL_GPL(mei_cl_add_device); + +void mei_cl_remove_device(struct mei_cl_device *device) +{ + device_unregister(&device->dev); +} +EXPORT_SYMBOL_GPL(mei_cl_remove_device); + +int __mei_cl_driver_register(struct mei_cl_driver *driver, struct module *owner) +{ + int err; + + driver->driver.name = driver->name; + driver->driver.owner = owner; + driver->driver.bus = &mei_cl_bus_type; + + err = driver_register(&driver->driver); + if (err) + return err; + + pr_debug("mei: driver [%s] registered\n", driver->driver.name); + + return 0; +} +EXPORT_SYMBOL_GPL(__mei_cl_driver_register); + +void mei_cl_driver_unregister(struct mei_cl_driver *driver) +{ + driver_unregister(&driver->driver); + + pr_debug("mei: driver [%s] unregistered\n", driver->driver.name); +} +EXPORT_SYMBOL_GPL(mei_cl_driver_unregister); + +static int ___mei_cl_send(struct mei_cl *cl, u8 *buf, size_t length, + bool blocking) +{ + struct mei_device *dev; + struct mei_cl_cb *cb; + int id; + int rets; + + if (WARN_ON(!cl || !cl->dev)) + return -ENODEV; + + dev = cl->dev; + + if (cl->state != MEI_FILE_CONNECTED) + return -ENODEV; + + /* Check if we have an ME client device */ + id = mei_me_cl_by_id(dev, cl->me_client_id); + if (id < 0) + return -ENODEV; + + if (length > dev->me_clients[id].props.max_msg_length) + return -EINVAL; + + cb = mei_io_cb_init(cl, NULL); + if (!cb) + return -ENOMEM; + + rets = mei_io_cb_alloc_req_buf(cb, length); + if (rets < 0) { + mei_io_cb_free(cb); + return rets; + } + + memcpy(cb->request_buffer.data, buf, length); + + mutex_lock(&dev->device_lock); + + rets = mei_cl_write(cl, cb, blocking); + + mutex_unlock(&dev->device_lock); + if (rets < 0) + mei_io_cb_free(cb); + + return rets; +} + +int __mei_cl_recv(struct mei_cl *cl, u8 *buf, size_t length) +{ + struct mei_device *dev; + struct mei_cl_cb *cb; + size_t r_length; + int err; + + if (WARN_ON(!cl || !cl->dev)) + return -ENODEV; + + dev = cl->dev; + + mutex_lock(&dev->device_lock); + + if (!cl->read_cb) { + err = mei_cl_read_start(cl, length); + if (err < 0) { + mutex_unlock(&dev->device_lock); + return err; + } + } + + if (cl->reading_state != MEI_READ_COMPLETE && + !waitqueue_active(&cl->rx_wait)) { + mutex_unlock(&dev->device_lock); + + if (wait_event_interruptible(cl->rx_wait, + (MEI_READ_COMPLETE == cl->reading_state))) { + if (signal_pending(current)) + return -EINTR; + return -ERESTARTSYS; + } + + mutex_lock(&dev->device_lock); + } + + cb = cl->read_cb; + + if (cl->reading_state != MEI_READ_COMPLETE) { + r_length = 0; + goto out; + } + + r_length = min_t(size_t, length, cb->buf_idx); + + memcpy(buf, cb->response_buffer.data, r_length); + + mei_io_cb_free(cb); + cl->reading_state = MEI_IDLE; + cl->read_cb = NULL; + +out: + mutex_unlock(&dev->device_lock); + + return r_length; +} + +inline int __mei_cl_async_send(struct mei_cl *cl, u8 *buf, size_t length) +{ + return ___mei_cl_send(cl, buf, length, 0); +} + +inline int __mei_cl_send(struct mei_cl *cl, u8 *buf, size_t length) +{ + return ___mei_cl_send(cl, buf, length, 1); +} + +int mei_cl_send(struct mei_cl_device *device, u8 *buf, size_t length) +{ + struct mei_cl *cl = device->cl; + + if (cl == NULL) + return -ENODEV; + + if (device->ops && device->ops->send) + return device->ops->send(device, buf, length); + + return __mei_cl_send(cl, buf, length); +} +EXPORT_SYMBOL_GPL(mei_cl_send); + +int mei_cl_recv(struct mei_cl_device *device, u8 *buf, size_t length) +{ + struct mei_cl *cl = device->cl; + + if (cl == NULL) + return -ENODEV; + + if (device->ops && device->ops->recv) + return device->ops->recv(device, buf, length); + + return __mei_cl_recv(cl, buf, length); +} +EXPORT_SYMBOL_GPL(mei_cl_recv); + +static void mei_bus_event_work(struct work_struct *work) +{ + struct mei_cl_device *device; + + device = container_of(work, struct mei_cl_device, event_work); + + if (device->event_cb) + device->event_cb(device, device->events, device->event_context); + + device->events = 0; + + /* Prepare for the next read */ + mei_cl_read_start(device->cl, 0); +} + +int mei_cl_register_event_cb(struct mei_cl_device *device, + mei_cl_event_cb_t event_cb, void *context) +{ + if (device->event_cb) + return -EALREADY; + + device->events = 0; + device->event_cb = event_cb; + device->event_context = context; + INIT_WORK(&device->event_work, mei_bus_event_work); + + mei_cl_read_start(device->cl, 0); + + return 0; +} +EXPORT_SYMBOL_GPL(mei_cl_register_event_cb); + +void *mei_cl_get_drvdata(const struct mei_cl_device *device) +{ + return dev_get_drvdata(&device->dev); +} +EXPORT_SYMBOL_GPL(mei_cl_get_drvdata); + +void mei_cl_set_drvdata(struct mei_cl_device *device, void *data) +{ + dev_set_drvdata(&device->dev, data); +} +EXPORT_SYMBOL_GPL(mei_cl_set_drvdata); + +int mei_cl_enable_device(struct mei_cl_device *device) +{ + int err; + struct mei_device *dev; + struct mei_cl *cl = device->cl; + + if (cl == NULL) + return -ENODEV; + + dev = cl->dev; + + mutex_lock(&dev->device_lock); + + cl->state = MEI_FILE_CONNECTING; + + err = mei_cl_connect(cl, NULL); + if (err < 0) { + mutex_unlock(&dev->device_lock); + dev_err(&dev->pdev->dev, "Could not connect to the ME client"); + + return err; + } + + mutex_unlock(&dev->device_lock); + + if (device->event_cb && !cl->read_cb) + mei_cl_read_start(device->cl, 0); + + if (!device->ops || !device->ops->enable) + return 0; + + return device->ops->enable(device); +} +EXPORT_SYMBOL_GPL(mei_cl_enable_device); + +int mei_cl_disable_device(struct mei_cl_device *device) +{ + int err; + struct mei_device *dev; + struct mei_cl *cl = device->cl; + + if (cl == NULL) + return -ENODEV; + + dev = cl->dev; + + mutex_lock(&dev->device_lock); + + if (cl->state != MEI_FILE_CONNECTED) { + mutex_unlock(&dev->device_lock); + dev_err(&dev->pdev->dev, "Already disconnected"); + + return 0; + } + + cl->state = MEI_FILE_DISCONNECTING; + + err = mei_cl_disconnect(cl); + if (err < 0) { + mutex_unlock(&dev->device_lock); + dev_err(&dev->pdev->dev, + "Could not disconnect from the ME client"); + + return err; + } + + /* Flush queues and remove any pending read */ + mei_cl_flush_queues(cl); + + if (cl->read_cb) { + struct mei_cl_cb *cb = NULL; + + cb = mei_cl_find_read_cb(cl); + /* Remove entry from read list */ + if (cb) + list_del(&cb->list); + + cb = cl->read_cb; + cl->read_cb = NULL; + + if (cb) { + mei_io_cb_free(cb); + cb = NULL; + } + } + + mutex_unlock(&dev->device_lock); + + if (!device->ops || !device->ops->disable) + return 0; + + return device->ops->disable(device); +} +EXPORT_SYMBOL_GPL(mei_cl_disable_device); + +void mei_cl_bus_rx_event(struct mei_cl *cl) +{ + struct mei_cl_device *device = cl->device; + + if (!device || !device->event_cb) + return; + + set_bit(MEI_CL_EVENT_RX, &device->events); + + schedule_work(&device->event_work); +} + +int __init mei_cl_bus_init(void) +{ + return bus_register(&mei_cl_bus_type); +} + +void __exit mei_cl_bus_exit(void) +{ + bus_unregister(&mei_cl_bus_type); +} diff --git a/drivers/misc/mei/client.c b/drivers/misc/mei/client.c index 1569afe935de..71892745e2e8 100644 --- a/drivers/misc/mei/client.c +++ b/drivers/misc/mei/client.c @@ -216,6 +216,7 @@ void mei_cl_init(struct mei_cl *cl, struct mei_device *dev) init_waitqueue_head(&cl->rx_wait); init_waitqueue_head(&cl->tx_wait); INIT_LIST_HEAD(&cl->link); + INIT_LIST_HEAD(&cl->device_link); cl->reading_state = MEI_IDLE; cl->writing_state = MEI_IDLE; cl->dev = dev; @@ -357,6 +358,9 @@ void mei_host_client_init(struct work_struct *work) mei_amthif_host_init(dev); else if (!uuid_le_cmp(client_props->protocol_name, mei_wd_guid)) mei_wd_host_init(dev); + else if (!uuid_le_cmp(client_props->protocol_name, mei_nfc_guid)) + mei_nfc_host_init(dev); + } dev->dev_state = MEI_DEV_ENABLED; @@ -620,7 +624,7 @@ int mei_cl_flow_ctrl_reduce(struct mei_cl *cl) * * returns 0 on success, <0 on failure. */ -int mei_cl_read_start(struct mei_cl *cl) +int mei_cl_read_start(struct mei_cl *cl, size_t length) { struct mei_device *dev; struct mei_cl_cb *cb; @@ -653,8 +657,9 @@ int mei_cl_read_start(struct mei_cl *cl) if (!cb) return -ENOMEM; - rets = mei_io_cb_alloc_resp_buf(cb, - dev->me_clients[i].props.max_msg_length); + /* always allocate at least client max message */ + length = max_t(size_t, length, dev->me_clients[i].props.max_msg_length); + rets = mei_io_cb_alloc_resp_buf(cb, length); if (rets) goto err; @@ -677,6 +682,111 @@ err: } /** + * mei_cl_write - submit a write cb to mei device + assumes device_lock is locked + * + * @cl: host client + * @cl: write callback with filled data + * + * returns numbe of bytes sent on success, <0 on failure. + */ +int mei_cl_write(struct mei_cl *cl, struct mei_cl_cb *cb, bool blocking) +{ + struct mei_device *dev; + struct mei_msg_data *buf; + struct mei_msg_hdr mei_hdr; + int rets; + + + if (WARN_ON(!cl || !cl->dev)) + return -ENODEV; + + if (WARN_ON(!cb)) + return -EINVAL; + + dev = cl->dev; + + + buf = &cb->request_buffer; + + dev_dbg(&dev->pdev->dev, "mei_cl_write %d\n", buf->size); + + + cb->fop_type = MEI_FOP_WRITE; + + rets = mei_cl_flow_ctrl_creds(cl); + if (rets < 0) + goto err; + + /* Host buffer is not ready, we queue the request */ + if (rets == 0 || !dev->hbuf_is_ready) { + cb->buf_idx = 0; + /* unseting complete will enqueue the cb for write */ + mei_hdr.msg_complete = 0; + cl->writing_state = MEI_WRITING; + rets = buf->size; + goto out; + } + + dev->hbuf_is_ready = false; + + /* Check for a maximum length */ + if (buf->size > mei_hbuf_max_len(dev)) { + mei_hdr.length = mei_hbuf_max_len(dev); + mei_hdr.msg_complete = 0; + } else { + mei_hdr.length = buf->size; + mei_hdr.msg_complete = 1; + } + + mei_hdr.host_addr = cl->host_client_id; + mei_hdr.me_addr = cl->me_client_id; + mei_hdr.reserved = 0; + + dev_dbg(&dev->pdev->dev, "write " MEI_HDR_FMT "\n", + MEI_HDR_PRM(&mei_hdr)); + + + if (mei_write_message(dev, &mei_hdr, buf->data)) { + rets = -EIO; + goto err; + } + + cl->writing_state = MEI_WRITING; + cb->buf_idx = mei_hdr.length; + + rets = buf->size; +out: + if (mei_hdr.msg_complete) { + if (mei_cl_flow_ctrl_reduce(cl)) { + rets = -ENODEV; + goto err; + } + list_add_tail(&cb->list, &dev->write_waiting_list.list); + } else { + list_add_tail(&cb->list, &dev->write_list.list); + } + + + if (blocking && cl->writing_state != MEI_WRITE_COMPLETE) { + + mutex_unlock(&dev->device_lock); + if (wait_event_interruptible(cl->tx_wait, + cl->writing_state == MEI_WRITE_COMPLETE)) { + if (signal_pending(current)) + rets = -EINTR; + else + rets = -ERESTARTSYS; + } + mutex_lock(&dev->device_lock); + } +err: + return rets; +} + + + +/** * mei_cl_all_disconnect - disconnect forcefully all connected clients * * @dev - mei device diff --git a/drivers/misc/mei/client.h b/drivers/misc/mei/client.h index 214b2397ec3e..cfdb144526aa 100644 --- a/drivers/misc/mei/client.h +++ b/drivers/misc/mei/client.h @@ -86,17 +86,16 @@ int mei_cl_flow_ctrl_reduce(struct mei_cl *cl); */ bool mei_cl_is_other_connecting(struct mei_cl *cl); int mei_cl_disconnect(struct mei_cl *cl); - -int mei_cl_read_start(struct mei_cl *cl); - int mei_cl_connect(struct mei_cl *cl, struct file *file); +int mei_cl_read_start(struct mei_cl *cl, size_t length); +int mei_cl_write(struct mei_cl *cl, struct mei_cl_cb *cb, bool blocking); void mei_host_client_init(struct work_struct *work); + void mei_cl_all_disconnect(struct mei_device *dev); void mei_cl_all_read_wakeup(struct mei_device *dev); void mei_cl_all_write_clear(struct mei_device *dev); - #endif /* _MEI_CLIENT_H_ */ diff --git a/drivers/misc/mei/debugfs.c b/drivers/misc/mei/debugfs.c new file mode 100644 index 000000000000..e3870f22d238 --- /dev/null +++ b/drivers/misc/mei/debugfs.c @@ -0,0 +1,143 @@ +/* + * + * Intel Management Engine Interface (Intel MEI) Linux driver + * Copyright (c) 2012-2013, 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. + * + */ +#include <linux/slab.h> +#include <linux/kernel.h> +#include <linux/device.h> +#include <linux/debugfs.h> +#include <linux/pci.h> + +#include <linux/mei.h> + +#include "mei_dev.h" +#include "hw.h" + +static ssize_t mei_dbgfs_read_meclients(struct file *fp, char __user *ubuf, + size_t cnt, loff_t *ppos) +{ + struct mei_device *dev = fp->private_data; + struct mei_me_client *cl; + const size_t bufsz = 1024; + char *buf = kzalloc(bufsz, GFP_KERNEL); + int i; + int pos = 0; + int ret; + + if (!buf) + return -ENOMEM; + + pos += scnprintf(buf + pos, bufsz - pos, + " |id|addr| UUID |con|msg len|\n"); + + mutex_lock(&dev->device_lock); + + /* if the driver is not enabled the list won't b consitent */ + if (dev->dev_state != MEI_DEV_ENABLED) + goto out; + + for (i = 0; i < dev->me_clients_num; i++) { + cl = &dev->me_clients[i]; + + /* skip me clients that cannot be connected */ + if (cl->props.max_number_of_connections == 0) + continue; + + pos += scnprintf(buf + pos, bufsz - pos, + "%2d|%2d|%4d|%pUl|%3d|%7d|\n", + i, cl->client_id, + cl->props.fixed_address, + &cl->props.protocol_name, + cl->props.max_number_of_connections, + cl->props.max_msg_length); + } +out: + mutex_unlock(&dev->device_lock); + ret = simple_read_from_buffer(ubuf, cnt, ppos, buf, pos); + kfree(buf); + return ret; +} + +static const struct file_operations mei_dbgfs_fops_meclients = { + .open = simple_open, + .read = mei_dbgfs_read_meclients, + .llseek = generic_file_llseek, +}; + +static ssize_t mei_dbgfs_read_devstate(struct file *fp, char __user *ubuf, + size_t cnt, loff_t *ppos) +{ + struct mei_device *dev = fp->private_data; + const size_t bufsz = 1024; + char *buf = kzalloc(bufsz, GFP_KERNEL); + int pos = 0; + int ret; + + if (!buf) + return -ENOMEM; + + pos += scnprintf(buf + pos, bufsz - pos, "%s\n", + mei_dev_state_str(dev->dev_state)); + ret = simple_read_from_buffer(ubuf, cnt, ppos, buf, pos); + kfree(buf); + return ret; +} +static const struct file_operations mei_dbgfs_fops_devstate = { + .open = simple_open, + .read = mei_dbgfs_read_devstate, + .llseek = generic_file_llseek, +}; + +/** + * mei_dbgfs_deregister - Remove the debugfs files and directories + * @mei - pointer to mei device private dat + */ +void mei_dbgfs_deregister(struct mei_device *dev) +{ + if (!dev->dbgfs_dir) + return; + debugfs_remove_recursive(dev->dbgfs_dir); + dev->dbgfs_dir = NULL; +} + +/** + * Add the debugfs files + * + */ +int mei_dbgfs_register(struct mei_device *dev, const char *name) +{ + struct dentry *dir, *f; + dir = debugfs_create_dir(name, NULL); + if (!dir) + return -ENOMEM; + + f = debugfs_create_file("meclients", S_IRUSR, dir, + dev, &mei_dbgfs_fops_meclients); + if (!f) { + dev_err(&dev->pdev->dev, "meclients: registration failed\n"); + goto err; + } + f = debugfs_create_file("devstate", S_IRUSR, dir, + dev, &mei_dbgfs_fops_devstate); + if (!f) { + dev_err(&dev->pdev->dev, "devstate: registration failed\n"); + goto err; + } + dev->dbgfs_dir = dir; + return 0; +err: + mei_dbgfs_deregister(dev); + return -ENODEV; +} + diff --git a/drivers/misc/mei/hbm.c b/drivers/misc/mei/hbm.c index fb9e63ba3bb1..db605f5cf187 100644 --- a/drivers/misc/mei/hbm.c +++ b/drivers/misc/mei/hbm.c @@ -52,7 +52,7 @@ static void mei_hbm_me_cl_allocate(struct mei_device *dev) sizeof(struct mei_me_client), GFP_KERNEL); if (!clients) { dev_err(&dev->pdev->dev, "memory allocation for ME clients failed.\n"); - dev->dev_state = MEI_DEV_RESETING; + dev->dev_state = MEI_DEV_RESETTING; mei_reset(dev, 1); return; } @@ -123,12 +123,33 @@ static bool is_treat_specially_client(struct mei_cl *cl, return false; } +int mei_hbm_start_wait(struct mei_device *dev) +{ + int ret; + if (dev->hbm_state > MEI_HBM_START) + return 0; + + mutex_unlock(&dev->device_lock); + ret = wait_event_interruptible_timeout(dev->wait_recvd_msg, + dev->hbm_state == MEI_HBM_IDLE || + dev->hbm_state > MEI_HBM_START, + mei_secs_to_jiffies(MEI_INTEROP_TIMEOUT)); + mutex_lock(&dev->device_lock); + + if (ret <= 0 && (dev->hbm_state <= MEI_HBM_START)) { + dev->hbm_state = MEI_HBM_IDLE; + dev_err(&dev->pdev->dev, "wating for mei start failed\n"); + return -ETIMEDOUT; + } + return 0; +} + /** * mei_hbm_start_req - sends start request message. * * @dev: the device structure */ -void mei_hbm_start_req(struct mei_device *dev) +int mei_hbm_start_req(struct mei_device *dev) { struct mei_msg_hdr *mei_hdr = &dev->wr_msg.hdr; struct hbm_host_version_request *start_req; @@ -143,18 +164,19 @@ void mei_hbm_start_req(struct mei_device *dev) start_req->host_version.major_version = HBM_MAJOR_VERSION; start_req->host_version.minor_version = HBM_MINOR_VERSION; - dev->recvd_msg = false; + dev->hbm_state = MEI_HBM_IDLE; if (mei_write_message(dev, mei_hdr, dev->wr_msg.data)) { - dev_dbg(&dev->pdev->dev, "write send version message to FW fail.\n"); - dev->dev_state = MEI_DEV_RESETING; + dev_err(&dev->pdev->dev, "version message writet failed\n"); + dev->dev_state = MEI_DEV_RESETTING; mei_reset(dev, 1); + return -ENODEV; } - dev->init_clients_state = MEI_START_MESSAGE; + dev->hbm_state = MEI_HBM_START; dev->init_clients_timer = MEI_CLIENTS_INIT_TIMEOUT; - return ; + return 0; } -/** +/* * mei_hbm_enum_clients_req - sends enumeration client request message. * * @dev: the device structure @@ -174,11 +196,11 @@ static void mei_hbm_enum_clients_req(struct mei_device *dev) enum_req->hbm_cmd = HOST_ENUM_REQ_CMD; if (mei_write_message(dev, mei_hdr, dev->wr_msg.data)) { - dev->dev_state = MEI_DEV_RESETING; - dev_dbg(&dev->pdev->dev, "write send enumeration request message to FW fail.\n"); + dev->dev_state = MEI_DEV_RESETTING; + dev_err(&dev->pdev->dev, "enumeration request write failed.\n"); mei_reset(dev, 1); } - dev->init_clients_state = MEI_ENUM_CLIENTS_MESSAGE; + dev->hbm_state = MEI_HBM_ENUM_CLIENTS; dev->init_clients_timer = MEI_CLIENTS_INIT_TIMEOUT; return; } @@ -208,6 +230,7 @@ static int mei_hbm_prop_req(struct mei_device *dev) /* We got all client properties */ if (next_client_index == MEI_CLIENTS_MAX) { + dev->hbm_state = MEI_HBM_STARTED; schedule_work(&dev->init_work); return 0; @@ -226,8 +249,8 @@ static int mei_hbm_prop_req(struct mei_device *dev) prop_req->address = next_client_index; if (mei_write_message(dev, mei_hdr, dev->wr_msg.data)) { - dev->dev_state = MEI_DEV_RESETING; - dev_err(&dev->pdev->dev, "Properties request command failed\n"); + dev->dev_state = MEI_DEV_RESETTING; + dev_err(&dev->pdev->dev, "properties request write failed\n"); mei_reset(dev, 1); return -EIO; @@ -542,27 +565,28 @@ void mei_hbm_dispatch(struct mei_device *dev, struct mei_msg_hdr *hdr) dev->version = version_res->me_max_version; dev_dbg(&dev->pdev->dev, "version mismatch.\n"); + dev->hbm_state = MEI_HBM_STOP; mei_hbm_stop_req_prepare(dev, &dev->wr_msg.hdr, dev->wr_msg.data); mei_write_message(dev, &dev->wr_msg.hdr, dev->wr_msg.data); + return; } dev->version.major_version = HBM_MAJOR_VERSION; dev->version.minor_version = HBM_MINOR_VERSION; if (dev->dev_state == MEI_DEV_INIT_CLIENTS && - dev->init_clients_state == MEI_START_MESSAGE) { + dev->hbm_state == MEI_HBM_START) { dev->init_clients_timer = 0; mei_hbm_enum_clients_req(dev); } else { - dev->recvd_msg = false; - dev_dbg(&dev->pdev->dev, "reset due to received hbm: host start\n"); + dev_err(&dev->pdev->dev, "reset: wrong host start response\n"); mei_reset(dev, 1); return; } - dev->recvd_msg = true; + wake_up_interruptible(&dev->wait_recvd_msg); dev_dbg(&dev->pdev->dev, "host start response message received.\n"); break; @@ -591,23 +615,20 @@ void mei_hbm_dispatch(struct mei_device *dev, struct mei_msg_hdr *hdr) me_client = &dev->me_clients[dev->me_client_presentation_num]; if (props_res->status || !dev->me_clients) { - dev_dbg(&dev->pdev->dev, "reset due to received host client properties response bus message wrong status.\n"); + dev_err(&dev->pdev->dev, "reset: properties response hbm wrong status.\n"); mei_reset(dev, 1); return; } if (me_client->client_id != props_res->address) { - dev_err(&dev->pdev->dev, - "Host client properties reply mismatch\n"); + dev_err(&dev->pdev->dev, "reset: host properties response address mismatch\n"); mei_reset(dev, 1); - return; } if (dev->dev_state != MEI_DEV_INIT_CLIENTS || - dev->init_clients_state != MEI_CLIENT_PROPERTIES_MESSAGE) { - dev_err(&dev->pdev->dev, - "Unexpected client properties reply\n"); + dev->hbm_state != MEI_HBM_CLIENT_PROPERTIES) { + dev_err(&dev->pdev->dev, "reset: unexpected properties response\n"); mei_reset(dev, 1); return; @@ -626,26 +647,28 @@ void mei_hbm_dispatch(struct mei_device *dev, struct mei_msg_hdr *hdr) enum_res = (struct hbm_host_enum_response *) mei_msg; memcpy(dev->me_clients_map, enum_res->valid_addresses, 32); if (dev->dev_state == MEI_DEV_INIT_CLIENTS && - dev->init_clients_state == MEI_ENUM_CLIENTS_MESSAGE) { + dev->hbm_state == MEI_HBM_ENUM_CLIENTS) { dev->init_clients_timer = 0; dev->me_client_presentation_num = 0; dev->me_client_index = 0; mei_hbm_me_cl_allocate(dev); - dev->init_clients_state = - MEI_CLIENT_PROPERTIES_MESSAGE; + dev->hbm_state = MEI_HBM_CLIENT_PROPERTIES; /* first property reqeust */ mei_hbm_prop_req(dev); } else { - dev_dbg(&dev->pdev->dev, "reset due to received host enumeration clients response bus message.\n"); + dev_err(&dev->pdev->dev, "reset: unexpected enumeration response hbm.\n"); mei_reset(dev, 1); return; } break; case HOST_STOP_RES_CMD: + + if (dev->hbm_state != MEI_HBM_STOP) + dev_err(&dev->pdev->dev, "unexpected stop response hbm.\n"); dev->dev_state = MEI_DEV_DISABLED; - dev_dbg(&dev->pdev->dev, "resetting because of FW stop response.\n"); + dev_info(&dev->pdev->dev, "reset: FW stop response.\n"); mei_reset(dev, 1); break; @@ -657,6 +680,7 @@ void mei_hbm_dispatch(struct mei_device *dev, struct mei_msg_hdr *hdr) case ME_STOP_REQ_CMD: + dev->hbm_state = MEI_HBM_STOP; mei_hbm_stop_req_prepare(dev, &dev->wr_ext_msg.hdr, dev->wr_ext_msg.data); break; diff --git a/drivers/misc/mei/hbm.h b/drivers/misc/mei/hbm.h index b552afbaf85c..e80dc24ef3e2 100644 --- a/drivers/misc/mei/hbm.h +++ b/drivers/misc/mei/hbm.h @@ -17,6 +17,27 @@ #ifndef _MEI_HBM_H_ #define _MEI_HBM_H_ +struct mei_device; +struct mei_msg_hdr; +struct mei_cl; + +/** + * enum mei_hbm_state - host bus message protocol state + * + * @MEI_HBM_IDLE : protocol not started + * @MEI_HBM_START : start request message was sent + * @MEI_HBM_ENUM_CLIENTS : enumeration request was sent + * @MEI_HBM_CLIENT_PROPERTIES : acquiring clients properties + */ +enum mei_hbm_state { + MEI_HBM_IDLE = 0, + MEI_HBM_START, + MEI_HBM_ENUM_CLIENTS, + MEI_HBM_CLIENT_PROPERTIES, + MEI_HBM_STARTED, + MEI_HBM_STOP, +}; + void mei_hbm_dispatch(struct mei_device *dev, struct mei_msg_hdr *hdr); static inline void mei_hbm_hdr(struct mei_msg_hdr *hdr, size_t length) @@ -28,8 +49,8 @@ static inline void mei_hbm_hdr(struct mei_msg_hdr *hdr, size_t length) hdr->reserved = 0; } -void mei_hbm_start_req(struct mei_device *dev); - +int mei_hbm_start_req(struct mei_device *dev); +int mei_hbm_start_wait(struct mei_device *dev); int mei_hbm_cl_flow_control_req(struct mei_device *dev, struct mei_cl *cl); int mei_hbm_cl_disconnect_req(struct mei_device *dev, struct mei_cl *cl); int mei_hbm_cl_connect_req(struct mei_device *dev, struct mei_cl *cl); diff --git a/drivers/misc/mei/hw-me.c b/drivers/misc/mei/hw-me.c index 642c6223fa6c..fc032270916a 100644 --- a/drivers/misc/mei/hw-me.c +++ b/drivers/misc/mei/hw-me.c @@ -26,14 +26,14 @@ /** - * mei_reg_read - Reads 32bit data from the mei device + * mei_me_reg_read - Reads 32bit data from the mei device * * @dev: the device structure * @offset: offset from which to read the data * * returns register value (u32) */ -static inline u32 mei_reg_read(const struct mei_me_hw *hw, +static inline u32 mei_me_reg_read(const struct mei_me_hw *hw, unsigned long offset) { return ioread32(hw->mem_addr + offset); @@ -41,20 +41,20 @@ static inline u32 mei_reg_read(const struct mei_me_hw *hw, /** - * mei_reg_write - Writes 32bit data to the mei device + * mei_me_reg_write - Writes 32bit data to the mei device * * @dev: the device structure * @offset: offset from which to write the data * @value: register value to write (u32) */ -static inline void mei_reg_write(const struct mei_me_hw *hw, +static inline void mei_me_reg_write(const struct mei_me_hw *hw, unsigned long offset, u32 value) { iowrite32(value, hw->mem_addr + offset); } /** - * mei_mecbrw_read - Reads 32bit data from ME circular buffer + * mei_me_mecbrw_read - Reads 32bit data from ME circular buffer * read window register * * @dev: the device structure @@ -63,18 +63,18 @@ static inline void mei_reg_write(const struct mei_me_hw *hw, */ static u32 mei_me_mecbrw_read(const struct mei_device *dev) { - return mei_reg_read(to_me_hw(dev), ME_CB_RW); + return mei_me_reg_read(to_me_hw(dev), ME_CB_RW); } /** - * mei_mecsr_read - Reads 32bit data from the ME CSR + * mei_me_mecsr_read - Reads 32bit data from the ME CSR * * @dev: the device structure * * returns ME_CSR_HA register value (u32) */ -static inline u32 mei_mecsr_read(const struct mei_me_hw *hw) +static inline u32 mei_me_mecsr_read(const struct mei_me_hw *hw) { - return mei_reg_read(hw, ME_CSR_HA); + return mei_me_reg_read(hw, ME_CSR_HA); } /** @@ -86,7 +86,7 @@ static inline u32 mei_mecsr_read(const struct mei_me_hw *hw) */ static inline u32 mei_hcsr_read(const struct mei_me_hw *hw) { - return mei_reg_read(hw, H_CSR); + return mei_me_reg_read(hw, H_CSR); } /** @@ -98,7 +98,7 @@ static inline u32 mei_hcsr_read(const struct mei_me_hw *hw) static inline void mei_hcsr_set(struct mei_me_hw *hw, u32 hcsr) { hcsr &= ~H_IS; - mei_reg_write(hw, H_CSR, hcsr); + mei_me_reg_write(hw, H_CSR, hcsr); } @@ -123,7 +123,7 @@ static void mei_me_intr_clear(struct mei_device *dev) struct mei_me_hw *hw = to_me_hw(dev); u32 hcsr = mei_hcsr_read(hw); if ((hcsr & H_IS) == H_IS) - mei_reg_write(hw, H_CSR, hcsr); + mei_me_reg_write(hw, H_CSR, hcsr); } /** * mei_me_intr_enable - enables mei device interrupts @@ -228,10 +228,42 @@ static bool mei_me_host_is_ready(struct mei_device *dev) static bool mei_me_hw_is_ready(struct mei_device *dev) { struct mei_me_hw *hw = to_me_hw(dev); - hw->me_hw_state = mei_mecsr_read(hw); + hw->me_hw_state = mei_me_mecsr_read(hw); return (hw->me_hw_state & ME_RDY_HRA) == ME_RDY_HRA; } +static int mei_me_hw_ready_wait(struct mei_device *dev) +{ + int err; + if (mei_me_hw_is_ready(dev)) + return 0; + + mutex_unlock(&dev->device_lock); + err = wait_event_interruptible_timeout(dev->wait_hw_ready, + dev->recvd_hw_ready, MEI_INTEROP_TIMEOUT); + mutex_lock(&dev->device_lock); + if (!err && !dev->recvd_hw_ready) { + dev_err(&dev->pdev->dev, + "wait hw ready failed. status = 0x%x\n", err); + return -ETIMEDOUT; + } + + dev->recvd_hw_ready = false; + return 0; +} + +static int mei_me_hw_start(struct mei_device *dev) +{ + int ret = mei_me_hw_ready_wait(dev); + if (ret) + return ret; + dev_dbg(&dev->pdev->dev, "hw is ready\n"); + + mei_me_host_set_ready(dev); + return ret; +} + + /** * mei_hbuf_filled_slots - gets number of device filled buffer slots * @@ -305,10 +337,11 @@ static int mei_me_write_message(struct mei_device *dev, unsigned char *buf) { struct mei_me_hw *hw = to_me_hw(dev); - unsigned long rem, dw_cnt; + unsigned long rem; unsigned long length = header->length; u32 *reg_buf = (u32 *)buf; u32 hcsr; + u32 dw_cnt; int i; int empty_slots; @@ -321,16 +354,16 @@ static int mei_me_write_message(struct mei_device *dev, if (empty_slots < 0 || dw_cnt > empty_slots) return -EIO; - mei_reg_write(hw, H_CB_WW, *((u32 *) header)); + mei_me_reg_write(hw, H_CB_WW, *((u32 *) header)); for (i = 0; i < length / 4; i++) - mei_reg_write(hw, H_CB_WW, reg_buf[i]); + mei_me_reg_write(hw, H_CB_WW, reg_buf[i]); rem = length & 0x3; if (rem > 0) { u32 reg = 0; memcpy(®, &buf[length - rem], rem); - mei_reg_write(hw, H_CB_WW, reg); + mei_me_reg_write(hw, H_CB_WW, reg); } hcsr = mei_hcsr_read(hw) | H_IG; @@ -354,7 +387,7 @@ static int mei_me_count_full_read_slots(struct mei_device *dev) char read_ptr, write_ptr; unsigned char buffer_depth, filled_slots; - hw->me_hw_state = mei_mecsr_read(hw); + hw->me_hw_state = mei_me_mecsr_read(hw); buffer_depth = (unsigned char)((hw->me_hw_state & ME_CBD_HRA) >> 24); read_ptr = (char) ((hw->me_hw_state & ME_CBRP_HRA) >> 8); write_ptr = (char) ((hw->me_hw_state & ME_CBWP_HRA) >> 16); @@ -414,7 +447,7 @@ irqreturn_t mei_me_irq_quick_handler(int irq, void *dev_id) return IRQ_NONE; /* clear H_IS bit in H_CSR */ - mei_reg_write(hw, H_CSR, csr_reg); + mei_me_reg_write(hw, H_CSR, csr_reg); return IRQ_WAKE_THREAD; } @@ -433,12 +466,8 @@ irqreturn_t mei_me_irq_thread_handler(int irq, void *dev_id) { struct mei_device *dev = (struct mei_device *) dev_id; struct mei_cl_cb complete_list; - struct mei_cl_cb *cb_pos = NULL, *cb_next = NULL; - struct mei_cl *cl; s32 slots; int rets; - bool bus_message_received; - dev_dbg(&dev->pdev->dev, "function called after ISR to handle the interrupt processing.\n"); /* initialize our complete list */ @@ -452,7 +481,7 @@ irqreturn_t mei_me_irq_thread_handler(int irq, void *dev_id) /* check if ME wants a reset */ if (!mei_hw_is_ready(dev) && - dev->dev_state != MEI_DEV_RESETING && + dev->dev_state != MEI_DEV_RESETTING && dev->dev_state != MEI_DEV_INITIALIZING) { dev_dbg(&dev->pdev->dev, "FW not ready.\n"); mei_reset(dev, 1); @@ -465,14 +494,9 @@ irqreturn_t mei_me_irq_thread_handler(int irq, void *dev_id) if (mei_hw_is_ready(dev)) { dev_dbg(&dev->pdev->dev, "we need to start the dev.\n"); - mei_host_set_ready(dev); + dev->recvd_hw_ready = true; + wake_up_interruptible(&dev->wait_hw_ready); - dev_dbg(&dev->pdev->dev, "link is established start sending messages.\n"); - /* link is established * start sending messages. */ - - dev->dev_state = MEI_DEV_INIT_CLIENTS; - - mei_hbm_start_req(dev); mutex_unlock(&dev->device_lock); return IRQ_HANDLED; } else { @@ -499,44 +523,20 @@ end: dev_dbg(&dev->pdev->dev, "end of bottom half function.\n"); dev->hbuf_is_ready = mei_hbuf_is_ready(dev); - bus_message_received = false; - if (dev->recvd_msg && waitqueue_active(&dev->wait_recvd_msg)) { - dev_dbg(&dev->pdev->dev, "received waiting bus message\n"); - bus_message_received = true; - } mutex_unlock(&dev->device_lock); - if (bus_message_received) { - dev_dbg(&dev->pdev->dev, "wake up dev->wait_recvd_msg\n"); - wake_up_interruptible(&dev->wait_recvd_msg); - bus_message_received = false; - } - if (list_empty(&complete_list.list)) - return IRQ_HANDLED; + mei_irq_compl_handler(dev, &complete_list); - list_for_each_entry_safe(cb_pos, cb_next, &complete_list.list, list) { - cl = cb_pos->cl; - list_del(&cb_pos->list); - if (cl) { - if (cl != &dev->iamthif_cl) { - dev_dbg(&dev->pdev->dev, "completing call back.\n"); - mei_irq_complete_handler(cl, cb_pos); - cb_pos = NULL; - } else if (cl == &dev->iamthif_cl) { - mei_amthif_complete(dev, cb_pos); - } - } - } return IRQ_HANDLED; } static const struct mei_hw_ops mei_me_hw_ops = { - .host_set_ready = mei_me_host_set_ready, .host_is_ready = mei_me_host_is_ready, .hw_is_ready = mei_me_hw_is_ready, .hw_reset = mei_me_hw_reset, - .hw_config = mei_me_hw_config, + .hw_config = mei_me_hw_config, + .hw_start = mei_me_hw_start, .intr_clear = mei_me_intr_clear, .intr_enable = mei_me_intr_enable, @@ -571,14 +571,6 @@ struct mei_device *mei_me_dev_init(struct pci_dev *pdev) mei_device_init(dev); - INIT_LIST_HEAD(&dev->wd_cl.link); - INIT_LIST_HEAD(&dev->iamthif_cl.link); - mei_io_list_init(&dev->amthif_cmd_list); - mei_io_list_init(&dev->amthif_rd_complete_list); - - INIT_DELAYED_WORK(&dev->timer_work, mei_timer); - INIT_WORK(&dev->init_work, mei_host_client_init); - dev->ops = &mei_me_hw_ops; dev->pdev = pdev; diff --git a/drivers/misc/mei/hw-me.h b/drivers/misc/mei/hw-me.h index 8518d3eeb838..80bd829fbd9a 100644 --- a/drivers/misc/mei/hw-me.h +++ b/drivers/misc/mei/hw-me.h @@ -36,12 +36,6 @@ struct mei_me_hw { struct mei_device *mei_me_dev_init(struct pci_dev *pdev); -/* get slots (dwords) from a message length + header (bytes) */ -static inline unsigned char mei_data2slots(size_t length) -{ - return DIV_ROUND_UP(sizeof(struct mei_msg_hdr) + length, 4); -} - irqreturn_t mei_me_irq_quick_handler(int irq, void *dev_id); irqreturn_t mei_me_irq_thread_handler(int irq, void *dev_id); diff --git a/drivers/misc/mei/init.c b/drivers/misc/mei/init.c index 356179991a2e..713d89fedc46 100644 --- a/drivers/misc/mei/init.c +++ b/drivers/misc/mei/init.c @@ -14,6 +14,7 @@ * */ +#include <linux/export.h> #include <linux/pci.h> #include <linux/sched.h> #include <linux/wait.h> @@ -22,6 +23,7 @@ #include <linux/mei.h> #include "mei_dev.h" +#include "hbm.h" #include "client.h" const char *mei_dev_state_str(int state) @@ -31,9 +33,8 @@ const char *mei_dev_state_str(int state) MEI_DEV_STATE(INITIALIZING); MEI_DEV_STATE(INIT_CLIENTS); MEI_DEV_STATE(ENABLED); - MEI_DEV_STATE(RESETING); + MEI_DEV_STATE(RESETTING); MEI_DEV_STATE(DISABLED); - MEI_DEV_STATE(RECOVERING_FROM_RESET); MEI_DEV_STATE(POWER_DOWN); MEI_DEV_STATE(POWER_UP); default: @@ -46,7 +47,9 @@ void mei_device_init(struct mei_device *dev) { /* setup our list array */ INIT_LIST_HEAD(&dev->file_list); + INIT_LIST_HEAD(&dev->device_list); mutex_init(&dev->device_lock); + init_waitqueue_head(&dev->wait_hw_ready); init_waitqueue_head(&dev->wait_recvd_msg); init_waitqueue_head(&dev->wait_stop_wd); dev->dev_state = MEI_DEV_INITIALIZING; @@ -56,19 +59,27 @@ void mei_device_init(struct mei_device *dev) mei_io_list_init(&dev->write_waiting_list); mei_io_list_init(&dev->ctrl_wr_list); mei_io_list_init(&dev->ctrl_rd_list); + + INIT_DELAYED_WORK(&dev->timer_work, mei_timer); + INIT_WORK(&dev->init_work, mei_host_client_init); + + INIT_LIST_HEAD(&dev->wd_cl.link); + INIT_LIST_HEAD(&dev->iamthif_cl.link); + mei_io_list_init(&dev->amthif_cmd_list); + mei_io_list_init(&dev->amthif_rd_complete_list); + } +EXPORT_SYMBOL_GPL(mei_device_init); /** - * mei_hw_init - initializes host and fw to start work. + * mei_start - initializes host and fw to start work. * * @dev: the device structure * * returns 0 on success, <0 on failure. */ -int mei_hw_init(struct mei_device *dev) +int mei_start(struct mei_device *dev) { - int ret = 0; - mutex_lock(&dev->device_lock); /* acknowledge interrupt and stop interupts */ @@ -76,29 +87,15 @@ int mei_hw_init(struct mei_device *dev) mei_hw_config(dev); - dev->recvd_msg = false; dev_dbg(&dev->pdev->dev, "reset in start the mei device.\n"); mei_reset(dev, 1); - /* wait for ME to turn on ME_RDY */ - if (!dev->recvd_msg) { - mutex_unlock(&dev->device_lock); - ret = wait_event_interruptible_timeout(dev->wait_recvd_msg, - dev->recvd_msg, - mei_secs_to_jiffies(MEI_INTEROP_TIMEOUT)); - mutex_lock(&dev->device_lock); - } - - if (ret <= 0 && !dev->recvd_msg) { - dev->dev_state = MEI_DEV_DISABLED; - dev_dbg(&dev->pdev->dev, - "wait_event_interruptible_timeout failed" - "on wait for ME to turn on ME_RDY.\n"); + if (mei_hbm_start_wait(dev)) { + dev_err(&dev->pdev->dev, "HBM haven't started"); goto err; } - if (!mei_host_is_ready(dev)) { dev_err(&dev->pdev->dev, "host is not ready.\n"); goto err; @@ -115,7 +112,6 @@ int mei_hw_init(struct mei_device *dev) goto err; } - dev->recvd_msg = false; dev_dbg(&dev->pdev->dev, "link layer has been established.\n"); mutex_unlock(&dev->device_lock); @@ -126,6 +122,7 @@ err: mutex_unlock(&dev->device_lock); return -ENODEV; } +EXPORT_SYMBOL_GPL(mei_start); /** * mei_reset - resets host and fw. @@ -137,9 +134,6 @@ void mei_reset(struct mei_device *dev, int interrupts_enabled) { bool unexpected; - if (dev->dev_state == MEI_DEV_RECOVERING_FROM_RESET) - return; - unexpected = (dev->dev_state != MEI_DEV_INITIALIZING && dev->dev_state != MEI_DEV_DISABLED && dev->dev_state != MEI_DEV_POWER_DOWN && @@ -147,11 +141,12 @@ void mei_reset(struct mei_device *dev, int interrupts_enabled) mei_hw_reset(dev, interrupts_enabled); + dev->hbm_state = MEI_HBM_IDLE; if (dev->dev_state != MEI_DEV_INITIALIZING) { if (dev->dev_state != MEI_DEV_DISABLED && dev->dev_state != MEI_DEV_POWER_DOWN) - dev->dev_state = MEI_DEV_RESETING; + dev->dev_state = MEI_DEV_RESETTING; mei_cl_all_disconnect(dev); @@ -176,12 +171,27 @@ void mei_reset(struct mei_device *dev, int interrupts_enabled) dev_warn(&dev->pdev->dev, "unexpected reset: dev_state = %s\n", mei_dev_state_str(dev->dev_state)); + if (!interrupts_enabled) { + dev_dbg(&dev->pdev->dev, "intr not enabled end of reset\n"); + return; + } + + mei_hw_start(dev); + + dev_dbg(&dev->pdev->dev, "link is established start sending messages.\n"); + /* link is established * start sending messages. */ + + dev->dev_state = MEI_DEV_INIT_CLIENTS; + + mei_hbm_start_req(dev); + /* wake up all readings so they can be interrupted */ mei_cl_all_read_wakeup(dev); /* remove all waiting requests */ mei_cl_all_write_clear(dev); } +EXPORT_SYMBOL_GPL(mei_reset); void mei_stop(struct mei_device *dev) { @@ -193,14 +203,18 @@ void mei_stop(struct mei_device *dev) mei_wd_stop(dev); + mei_nfc_host_exit(); + dev->dev_state = MEI_DEV_POWER_DOWN; mei_reset(dev, 0); mutex_unlock(&dev->device_lock); flush_scheduled_work(); -} + mei_watchdog_unregister(dev); +} +EXPORT_SYMBOL_GPL(mei_stop); diff --git a/drivers/misc/mei/interrupt.c b/drivers/misc/mei/interrupt.c index 3535b2676c97..1473cfdbc426 100644 --- a/drivers/misc/mei/interrupt.c +++ b/drivers/misc/mei/interrupt.c @@ -15,6 +15,7 @@ */ +#include <linux/export.h> #include <linux/pci.h> #include <linux/kthread.h> #include <linux/interrupt.h> @@ -30,103 +31,153 @@ /** - * mei_complete_handler - processes completed operation. + * mei_cl_complete_handler - processes completed operation for a client * * @cl: private data of the file object. - * @cb_pos: callback block. + * @cb: callback block. */ -void mei_irq_complete_handler(struct mei_cl *cl, struct mei_cl_cb *cb_pos) +static void mei_cl_complete_handler(struct mei_cl *cl, struct mei_cl_cb *cb) { - if (cb_pos->fop_type == MEI_FOP_WRITE) { - mei_io_cb_free(cb_pos); - cb_pos = NULL; + if (cb->fop_type == MEI_FOP_WRITE) { + mei_io_cb_free(cb); + cb = NULL; cl->writing_state = MEI_WRITE_COMPLETE; if (waitqueue_active(&cl->tx_wait)) wake_up_interruptible(&cl->tx_wait); - } else if (cb_pos->fop_type == MEI_FOP_READ && + } else if (cb->fop_type == MEI_FOP_READ && MEI_READING == cl->reading_state) { cl->reading_state = MEI_READ_COMPLETE; if (waitqueue_active(&cl->rx_wait)) wake_up_interruptible(&cl->rx_wait); + else + mei_cl_bus_rx_event(cl); + + } +} + +/** + * mei_irq_compl_handler - dispatch complete handelers + * for the completed callbacks + * + * @dev - mei device + * @compl_list - list of completed cbs + */ +void mei_irq_compl_handler(struct mei_device *dev, struct mei_cl_cb *compl_list) +{ + struct mei_cl_cb *cb, *next; + struct mei_cl *cl; + + list_for_each_entry_safe(cb, next, &compl_list->list, list) { + cl = cb->cl; + list_del(&cb->list); + if (!cl) + continue; + dev_dbg(&dev->pdev->dev, "completing call back.\n"); + if (cl == &dev->iamthif_cl) + mei_amthif_complete(dev, cb); + else + mei_cl_complete_handler(cl, cb); } } +EXPORT_SYMBOL_GPL(mei_irq_compl_handler); /** - * _mei_irq_thread_state_ok - checks if mei header matches file private data + * mei_cl_hbm_equal - check if hbm is addressed to the client * - * @cl: private data of the file object + * @cl: host client * @mei_hdr: header of mei client message * - * returns !=0 if matches, 0 if no match. + * returns true if matches, false otherwise + */ +static inline int mei_cl_hbm_equal(struct mei_cl *cl, + struct mei_msg_hdr *mei_hdr) +{ + return cl->host_client_id == mei_hdr->host_addr && + cl->me_client_id == mei_hdr->me_addr; +} +/** + * mei_cl_is_reading - checks if the client + is the one to read this message + * + * @cl: mei client + * @mei_hdr: header of mei message + * + * returns true on match and false otherwise */ -static int _mei_irq_thread_state_ok(struct mei_cl *cl, - struct mei_msg_hdr *mei_hdr) +static bool mei_cl_is_reading(struct mei_cl *cl, struct mei_msg_hdr *mei_hdr) { - return (cl->host_client_id == mei_hdr->host_addr && - cl->me_client_id == mei_hdr->me_addr && + return mei_cl_hbm_equal(cl, mei_hdr) && cl->state == MEI_FILE_CONNECTED && - MEI_READ_COMPLETE != cl->reading_state); + cl->reading_state != MEI_READ_COMPLETE; } /** - * mei_irq_thread_read_client_message - bottom half read routine after ISR to - * handle the read mei client message data processing. + * mei_irq_read_client_message - process client message * - * @complete_list: An instance of our list structure * @dev: the device structure * @mei_hdr: header of mei client message + * @complete_list: An instance of our list structure * * returns 0 on success, <0 on failure. */ -static int mei_irq_thread_read_client_message(struct mei_cl_cb *complete_list, - struct mei_device *dev, - struct mei_msg_hdr *mei_hdr) +static int mei_cl_irq_read_msg(struct mei_device *dev, + struct mei_msg_hdr *mei_hdr, + struct mei_cl_cb *complete_list) { struct mei_cl *cl; - struct mei_cl_cb *cb_pos = NULL, *cb_next = NULL; + struct mei_cl_cb *cb, *next; unsigned char *buffer = NULL; - dev_dbg(&dev->pdev->dev, "start client msg\n"); - if (list_empty(&dev->read_list.list)) - goto quit; + list_for_each_entry_safe(cb, next, &dev->read_list.list, list) { + cl = cb->cl; + if (!cl || !mei_cl_is_reading(cl, mei_hdr)) + continue; - list_for_each_entry_safe(cb_pos, cb_next, &dev->read_list.list, list) { - cl = cb_pos->cl; - if (cl && _mei_irq_thread_state_ok(cl, mei_hdr)) { - cl->reading_state = MEI_READING; - buffer = cb_pos->response_buffer.data + cb_pos->buf_idx; + cl->reading_state = MEI_READING; - if (cb_pos->response_buffer.size < - mei_hdr->length + cb_pos->buf_idx) { - dev_dbg(&dev->pdev->dev, "message overflow.\n"); - list_del(&cb_pos->list); + if (cb->response_buffer.size == 0 || + cb->response_buffer.data == NULL) { + dev_err(&dev->pdev->dev, "response buffer is not allocated.\n"); + list_del(&cb->list); + return -ENOMEM; + } + + if (cb->response_buffer.size < mei_hdr->length + cb->buf_idx) { + dev_dbg(&dev->pdev->dev, "message overflow. size %d len %d idx %ld\n", + cb->response_buffer.size, + mei_hdr->length, cb->buf_idx); + buffer = krealloc(cb->response_buffer.data, + mei_hdr->length + cb->buf_idx, + GFP_KERNEL); + + if (!buffer) { + dev_err(&dev->pdev->dev, "allocation failed.\n"); + list_del(&cb->list); return -ENOMEM; } - if (buffer) - mei_read_slots(dev, buffer, mei_hdr->length); - - cb_pos->buf_idx += mei_hdr->length; - if (mei_hdr->msg_complete) { - cl->status = 0; - list_del(&cb_pos->list); - dev_dbg(&dev->pdev->dev, - "completed read H cl = %d, ME cl = %d, length = %lu\n", - cl->host_client_id, - cl->me_client_id, - cb_pos->buf_idx); - - list_add_tail(&cb_pos->list, - &complete_list->list); - } - - break; + cb->response_buffer.data = buffer; + cb->response_buffer.size = + mei_hdr->length + cb->buf_idx; } + buffer = cb->response_buffer.data + cb->buf_idx; + mei_read_slots(dev, buffer, mei_hdr->length); + + cb->buf_idx += mei_hdr->length; + if (mei_hdr->msg_complete) { + cl->status = 0; + list_del(&cb->list); + dev_dbg(&dev->pdev->dev, "completed read H cl = %d, ME cl = %d, length = %lu\n", + cl->host_client_id, + cl->me_client_id, + cb->buf_idx); + list_add_tail(&cb->list, &complete_list->list); + } + break; } -quit: dev_dbg(&dev->pdev->dev, "message read\n"); if (!buffer) { mei_read_slots(dev, dev->rd_msg_buf, mei_hdr->length); @@ -153,25 +204,27 @@ static int _mei_irq_thread_close(struct mei_device *dev, s32 *slots, struct mei_cl *cl, struct mei_cl_cb *cmpl_list) { - if ((*slots * sizeof(u32)) < (sizeof(struct mei_msg_hdr) + - sizeof(struct hbm_client_connect_request))) - return -EBADMSG; + u32 msg_slots = + mei_data2slots(sizeof(struct hbm_client_connect_request)); - *slots -= mei_data2slots(sizeof(struct hbm_client_connect_request)); + if (*slots < msg_slots) + return -EMSGSIZE; + + *slots -= msg_slots; if (mei_hbm_cl_disconnect_req(dev, cl)) { cl->status = 0; cb_pos->buf_idx = 0; list_move_tail(&cb_pos->list, &cmpl_list->list); - return -EMSGSIZE; - } else { - cl->state = MEI_FILE_DISCONNECTING; - cl->status = 0; - cb_pos->buf_idx = 0; - list_move_tail(&cb_pos->list, &dev->ctrl_rd_list.list); - cl->timer_count = MEI_CONNECT_TIMEOUT; + return -EIO; } + cl->state = MEI_FILE_DISCONNECTING; + cl->status = 0; + cb_pos->buf_idx = 0; + list_move_tail(&cb_pos->list, &dev->ctrl_rd_list.list); + cl->timer_count = MEI_CONNECT_TIMEOUT; + return 0; } @@ -192,14 +245,15 @@ static int _mei_irq_thread_read(struct mei_device *dev, s32 *slots, struct mei_cl *cl, struct mei_cl_cb *cmpl_list) { - if ((*slots * sizeof(u32)) < (sizeof(struct mei_msg_hdr) + - sizeof(struct hbm_flow_control))) { + u32 msg_slots = mei_data2slots(sizeof(struct hbm_flow_control)); + + if (*slots < msg_slots) { /* return the cancel routine */ list_del(&cb_pos->list); - return -EBADMSG; + return -EMSGSIZE; } - *slots -= mei_data2slots(sizeof(struct hbm_flow_control)); + *slots -= msg_slots; if (mei_hbm_cl_flow_control_req(dev, cl)) { cl->status = -ENODEV; @@ -229,15 +283,19 @@ static int _mei_irq_thread_ioctl(struct mei_device *dev, s32 *slots, struct mei_cl *cl, struct mei_cl_cb *cmpl_list) { - if ((*slots * sizeof(u32)) < (sizeof(struct mei_msg_hdr) + - sizeof(struct hbm_client_connect_request))) { + u32 msg_slots = + mei_data2slots(sizeof(struct hbm_client_connect_request)); + + if (*slots < msg_slots) { /* return the cancel routine */ list_del(&cb_pos->list); - return -EBADMSG; + return -EMSGSIZE; } + *slots -= msg_slots; + cl->state = MEI_FILE_CONNECTING; - *slots -= mei_data2slots(sizeof(struct hbm_client_connect_request)); + if (mei_hbm_cl_connect_req(dev, cl)) { cl->status = -ENODEV; cb_pos->buf_idx = 0; @@ -266,7 +324,7 @@ static int mei_irq_thread_write_complete(struct mei_device *dev, s32 *slots, struct mei_msg_hdr mei_hdr; struct mei_cl *cl = cb->cl; size_t len = cb->request_buffer.size - cb->buf_idx; - size_t msg_slots = mei_data2slots(len); + u32 msg_slots = mei_data2slots(len); mei_hdr.host_addr = cl->host_client_id; mei_hdr.me_addr = cl->me_client_id; @@ -298,13 +356,14 @@ static int mei_irq_thread_write_complete(struct mei_device *dev, s32 *slots, return -ENODEV; } - if (mei_cl_flow_ctrl_reduce(cl)) - return -ENODEV; cl->status = 0; cb->buf_idx += mei_hdr.length; - if (mei_hdr.msg_complete) + if (mei_hdr.msg_complete) { + if (mei_cl_flow_ctrl_reduce(cl)) + return -ENODEV; list_move_tail(&cb->list, &dev->write_waiting_list.list); + } return 0; } @@ -350,8 +409,7 @@ int mei_irq_read_handler(struct mei_device *dev, " client = %d, ME client = %d\n", cl_pos->host_client_id, cl_pos->me_client_id); - if (cl_pos->host_client_id == mei_hdr->host_addr && - cl_pos->me_client_id == mei_hdr->me_addr) + if (mei_cl_hbm_equal(cl_pos, mei_hdr)) break; } @@ -362,7 +420,7 @@ int mei_irq_read_handler(struct mei_device *dev, } } if (((*slots) * sizeof(u32)) < mei_hdr->length) { - dev_dbg(&dev->pdev->dev, + dev_err(&dev->pdev->dev, "we can't read the message slots =%08x.\n", *slots); /* we can't read the message */ @@ -378,20 +436,19 @@ int mei_irq_read_handler(struct mei_device *dev, } else if (mei_hdr->host_addr == dev->iamthif_cl.host_client_id && (MEI_FILE_CONNECTED == dev->iamthif_cl.state) && (dev->iamthif_state == MEI_IAMTHIF_READING)) { - dev_dbg(&dev->pdev->dev, "call mei_irq_thread_read_iamthif_message.\n"); + dev_dbg(&dev->pdev->dev, "call mei_irq_thread_read_iamthif_message.\n"); dev_dbg(&dev->pdev->dev, MEI_HDR_FMT, MEI_HDR_PRM(mei_hdr)); - ret = mei_amthif_irq_read_message(cmpl_list, dev, mei_hdr); + ret = mei_amthif_irq_read_msg(dev, mei_hdr, cmpl_list); if (ret) goto end; } else { - dev_dbg(&dev->pdev->dev, "call mei_irq_thread_read_client_message.\n"); - ret = mei_irq_thread_read_client_message(cmpl_list, - dev, mei_hdr); + dev_dbg(&dev->pdev->dev, "call mei_cl_irq_read_msg.\n"); + dev_dbg(&dev->pdev->dev, MEI_HDR_FMT, MEI_HDR_PRM(mei_hdr)); + ret = mei_cl_irq_read_msg(dev, mei_hdr, cmpl_list); if (ret) goto end; - } /* reset the number of slots and header */ @@ -400,7 +457,7 @@ int mei_irq_read_handler(struct mei_device *dev, if (*slots == -EOVERFLOW) { /* overflow - reset */ - dev_dbg(&dev->pdev->dev, "resetting due to slots overflow.\n"); + dev_err(&dev->pdev->dev, "resetting due to slots overflow.\n"); /* set the event since message has been read */ ret = -ERANGE; goto end; @@ -408,6 +465,7 @@ int mei_irq_read_handler(struct mei_device *dev, end: return ret; } +EXPORT_SYMBOL_GPL(mei_irq_read_handler); /** @@ -419,8 +477,7 @@ end: * * returns 0 on success, <0 on failure. */ -int mei_irq_write_handler(struct mei_device *dev, - struct mei_cl_cb *cmpl_list) +int mei_irq_write_handler(struct mei_device *dev, struct mei_cl_cb *cmpl_list) { struct mei_cl *cl; @@ -559,6 +616,7 @@ int mei_irq_write_handler(struct mei_device *dev, } return 0; } +EXPORT_SYMBOL_GPL(mei_irq_write_handler); @@ -586,8 +644,8 @@ void mei_timer(struct work_struct *work) if (dev->dev_state == MEI_DEV_INIT_CLIENTS) { if (dev->init_clients_timer) { if (--dev->init_clients_timer == 0) { - dev_dbg(&dev->pdev->dev, "IMEI reset due to init clients timeout ,init clients state = %d.\n", - dev->init_clients_state); + dev_err(&dev->pdev->dev, "reset: init clients timeout hbm_state = %d.\n", + dev->hbm_state); mei_reset(dev, 1); } } @@ -598,7 +656,7 @@ void mei_timer(struct work_struct *work) list_for_each_entry_safe(cl_pos, cl_next, &dev->file_list, link) { if (cl_pos->timer_count) { if (--cl_pos->timer_count == 0) { - dev_dbg(&dev->pdev->dev, "HECI reset due to connect/disconnect timeout.\n"); + dev_err(&dev->pdev->dev, "reset: connect/disconnect timeout.\n"); mei_reset(dev, 1); goto out; } @@ -607,7 +665,7 @@ void mei_timer(struct work_struct *work) if (dev->iamthif_stall_timer) { if (--dev->iamthif_stall_timer == 0) { - dev_dbg(&dev->pdev->dev, "resetting because of hang to amthi.\n"); + dev_err(&dev->pdev->dev, "reset: amthif hanged.\n"); mei_reset(dev, 1); dev->iamthif_msg_buf_size = 0; dev->iamthif_msg_buf_index = 0; diff --git a/drivers/misc/mei/main.c b/drivers/misc/mei/main.c index 903f809b21f7..7c44c8dbae42 100644 --- a/drivers/misc/mei/main.c +++ b/drivers/misc/mei/main.c @@ -48,7 +48,7 @@ * * @inode: pointer to inode structure * @file: pointer to file structure - * + e * returns 0 on success, <0 on error */ static int mei_open(struct inode *inode, struct file *file) @@ -244,7 +244,7 @@ static ssize_t mei_read(struct file *file, char __user *ubuf, goto out; } - err = mei_cl_read_start(cl); + err = mei_cl_read_start(cl, length); if (err && err != -EBUSY) { dev_dbg(&dev->pdev->dev, "mei start read failure with status = %d\n", err); @@ -292,9 +292,8 @@ static ssize_t mei_read(struct file *file, char __user *ubuf, } /* now copy the data to user space */ copy_buffer: - dev_dbg(&dev->pdev->dev, "cb->response_buffer size - %d\n", - cb->response_buffer.size); - dev_dbg(&dev->pdev->dev, "cb->buf_idx - %lu\n", cb->buf_idx); + dev_dbg(&dev->pdev->dev, "buf.size = %d buf.idx= %ld\n", + cb->response_buffer.size, cb->buf_idx); if (length == 0 || ubuf == NULL || *offset > cb->buf_idx) { rets = -EMSGSIZE; goto free; @@ -342,11 +341,10 @@ static ssize_t mei_write(struct file *file, const char __user *ubuf, { struct mei_cl *cl = file->private_data; struct mei_cl_cb *write_cb = NULL; - struct mei_msg_hdr mei_hdr; struct mei_device *dev; unsigned long timeout = 0; int rets; - int i; + int id; if (WARN_ON(!cl || !cl->dev)) return -ENODEV; @@ -357,24 +355,24 @@ static ssize_t mei_write(struct file *file, const char __user *ubuf, if (dev->dev_state != MEI_DEV_ENABLED) { rets = -ENODEV; - goto err; + goto out; } - i = mei_me_cl_by_id(dev, cl->me_client_id); - if (i < 0) { + id = mei_me_cl_by_id(dev, cl->me_client_id); + if (id < 0) { rets = -ENODEV; - goto err; + goto out; } - if (length > dev->me_clients[i].props.max_msg_length || length <= 0) { + if (length > dev->me_clients[id].props.max_msg_length || length <= 0) { rets = -EMSGSIZE; - goto err; + goto out; } if (cl->state != MEI_FILE_CONNECTED) { - rets = -ENODEV; dev_err(&dev->pdev->dev, "host client = %d, is not connected to ME client = %d", cl->host_client_id, cl->me_client_id); - goto err; + rets = -ENODEV; + goto out; } if (cl == &dev->iamthif_cl) { write_cb = mei_amthif_find_read_list_entry(dev, file); @@ -412,17 +410,15 @@ static ssize_t mei_write(struct file *file, const char __user *ubuf, if (!write_cb) { dev_err(&dev->pdev->dev, "write cb allocation failed\n"); rets = -ENOMEM; - goto err; + goto out; } rets = mei_io_cb_alloc_req_buf(write_cb, length); if (rets) - goto err; - - dev_dbg(&dev->pdev->dev, "cb request size = %zd\n", length); + goto out; rets = copy_from_user(write_cb->request_buffer.data, ubuf, length); if (rets) - goto err; + goto out; cl->sm_state = 0; if (length == 4 && @@ -440,65 +436,17 @@ static ssize_t mei_write(struct file *file, const char __user *ubuf, if (rets) { dev_err(&dev->pdev->dev, "amthif write failed with status = %d\n", rets); - goto err; + goto out; } mutex_unlock(&dev->device_lock); return length; } - write_cb->fop_type = MEI_FOP_WRITE; - - dev_dbg(&dev->pdev->dev, "host client = %d, ME client = %d\n", - cl->host_client_id, cl->me_client_id); - rets = mei_cl_flow_ctrl_creds(cl); - if (rets < 0) - goto err; - - if (rets == 0 || !dev->hbuf_is_ready) { - write_cb->buf_idx = 0; - mei_hdr.msg_complete = 0; - cl->writing_state = MEI_WRITING; - goto out; - } - - dev->hbuf_is_ready = false; - if (length > mei_hbuf_max_len(dev)) { - mei_hdr.length = mei_hbuf_max_len(dev); - mei_hdr.msg_complete = 0; - } else { - mei_hdr.length = length; - mei_hdr.msg_complete = 1; - } - mei_hdr.host_addr = cl->host_client_id; - mei_hdr.me_addr = cl->me_client_id; - mei_hdr.reserved = 0; - - dev_dbg(&dev->pdev->dev, "write " MEI_HDR_FMT "\n", - MEI_HDR_PRM(&mei_hdr)); - if (mei_write_message(dev, &mei_hdr, write_cb->request_buffer.data)) { - rets = -ENODEV; - goto err; - } - cl->writing_state = MEI_WRITING; - write_cb->buf_idx = mei_hdr.length; - + rets = mei_cl_write(cl, write_cb, false); out: - if (mei_hdr.msg_complete) { - if (mei_cl_flow_ctrl_reduce(cl)) { - rets = -ENODEV; - goto err; - } - list_add_tail(&write_cb->list, &dev->write_waiting_list.list); - } else { - list_add_tail(&write_cb->list, &dev->write_list.list); - } - mutex_unlock(&dev->device_lock); - return length; - -err: - mutex_unlock(&dev->device_lock); - mei_io_cb_free(write_cb); + if (rets < 0) + mei_io_cb_free(write_cb); return rets; } @@ -753,17 +701,44 @@ static struct miscdevice mei_misc_device = { .minor = MISC_DYNAMIC_MINOR, }; -int mei_register(struct device *dev) + +int mei_register(struct mei_device *dev) { - mei_misc_device.parent = dev; - return misc_register(&mei_misc_device); + int ret; + mei_misc_device.parent = &dev->pdev->dev; + ret = misc_register(&mei_misc_device); + if (ret) + return ret; + + if (mei_dbgfs_register(dev, mei_misc_device.name)) + dev_err(&dev->pdev->dev, "cannot register debugfs\n"); + + return 0; } +EXPORT_SYMBOL_GPL(mei_register); -void mei_deregister(void) +void mei_deregister(struct mei_device *dev) { + mei_dbgfs_deregister(dev); misc_deregister(&mei_misc_device); mei_misc_device.parent = NULL; } +EXPORT_SYMBOL_GPL(mei_deregister); + +static int __init mei_init(void) +{ + return mei_cl_bus_init(); +} + +static void __exit mei_exit(void) +{ + mei_cl_bus_exit(); +} + +module_init(mei_init); +module_exit(mei_exit); +MODULE_AUTHOR("Intel Corporation"); +MODULE_DESCRIPTION("Intel(R) Management Engine Interface"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/misc/mei/mei_dev.h b/drivers/misc/mei/mei_dev.h index 97873812e33b..4de5140e7379 100644 --- a/drivers/misc/mei/mei_dev.h +++ b/drivers/misc/mei/mei_dev.h @@ -21,9 +21,11 @@ #include <linux/watchdog.h> #include <linux/poll.h> #include <linux/mei.h> +#include <linux/mei_cl_bus.h> #include "hw.h" #include "hw-me-regs.h" +#include "hbm.h" /* * watch dog definition @@ -95,22 +97,14 @@ enum mei_dev_state { MEI_DEV_INITIALIZING = 0, MEI_DEV_INIT_CLIENTS, MEI_DEV_ENABLED, - MEI_DEV_RESETING, + MEI_DEV_RESETTING, MEI_DEV_DISABLED, - MEI_DEV_RECOVERING_FROM_RESET, MEI_DEV_POWER_DOWN, MEI_DEV_POWER_UP }; const char *mei_dev_state_str(int state); -/* init clients states*/ -enum mei_init_clients_states { - MEI_START_MESSAGE = 0, - MEI_ENUM_CLIENTS_MESSAGE, - MEI_CLIENT_PROPERTIES_MESSAGE -}; - enum iamthif_states { MEI_IAMTHIF_IDLE, MEI_IAMTHIF_WRITING, @@ -153,7 +147,7 @@ enum mei_cb_file_ops { /* * Intel MEI message data struct */ -struct mei_message_data { +struct mei_msg_data { u32 size; unsigned char *data; }; @@ -184,8 +178,8 @@ struct mei_cl_cb { struct list_head list; struct mei_cl *cl; enum mei_cb_file_ops fop_type; - struct mei_message_data request_buffer; - struct mei_message_data response_buffer; + struct mei_msg_data request_buffer; + struct mei_msg_data response_buffer; unsigned long buf_idx; unsigned long read_time; struct file *file_object; @@ -209,15 +203,20 @@ struct mei_cl { enum mei_file_transaction_states writing_state; int sm_state; struct mei_cl_cb *read_cb; + + /* MEI CL bus data */ + struct mei_cl_device *device; + struct list_head device_link; + uuid_le device_uuid; }; /** struct mei_hw_ops * - * @host_set_ready - notify FW that host side is ready * @host_is_ready - query for host readiness * @hw_is_ready - query if hw is ready * @hw_reset - reset hw + * @hw_start - start hw after reset * @hw_config - configure hw * @intr_clear - clear pending interrupts @@ -237,11 +236,11 @@ struct mei_cl { */ struct mei_hw_ops { - void (*host_set_ready) (struct mei_device *dev); bool (*host_is_ready) (struct mei_device *dev); bool (*hw_is_ready) (struct mei_device *dev); void (*hw_reset) (struct mei_device *dev, bool enable); + int (*hw_start) (struct mei_device *dev); void (*hw_config) (struct mei_device *dev); void (*intr_clear) (struct mei_device *dev); @@ -263,9 +262,77 @@ struct mei_hw_ops { unsigned char *buf, unsigned long len); }; +/* MEI bus API*/ + +/** + * struct mei_cl_ops - MEI CL device ops + * This structure allows ME host clients to implement technology + * specific operations. + * + * @enable: Enable an MEI CL device. Some devices require specific + * HECI commands to initialize completely. + * @disable: Disable an MEI CL device. + * @send: Tx hook for the device. This allows ME host clients to trap + * the device driver buffers before actually physically + * pushing it to the ME. + * @recv: Rx hook for the device. This allows ME host clients to trap the + * ME buffers before forwarding them to the device driver. + */ +struct mei_cl_ops { + int (*enable)(struct mei_cl_device *device); + int (*disable)(struct mei_cl_device *device); + int (*send)(struct mei_cl_device *device, u8 *buf, size_t length); + int (*recv)(struct mei_cl_device *device, u8 *buf, size_t length); +}; + +struct mei_cl_device *mei_cl_add_device(struct mei_device *dev, + uuid_le uuid, char *name, + struct mei_cl_ops *ops); +void mei_cl_remove_device(struct mei_cl_device *device); + +int __mei_cl_async_send(struct mei_cl *cl, u8 *buf, size_t length); +int __mei_cl_send(struct mei_cl *cl, u8 *buf, size_t length); +int __mei_cl_recv(struct mei_cl *cl, u8 *buf, size_t length); +void mei_cl_bus_rx_event(struct mei_cl *cl); +int mei_cl_bus_init(void); +void mei_cl_bus_exit(void); + + +/** + * struct mei_cl_device - MEI device handle + * An mei_cl_device pointer is returned from mei_add_device() + * and links MEI bus clients to their actual ME host client pointer. + * Drivers for MEI devices will get an mei_cl_device pointer + * when being probed and shall use it for doing ME bus I/O. + * + * @dev: linux driver model device pointer + * @uuid: me client uuid + * @cl: mei client + * @ops: ME transport ops + * @event_cb: Drivers register this callback to get asynchronous ME + * events (e.g. Rx buffer pending) notifications. + * @events: Events bitmask sent to the driver. + * @priv_data: client private data + */ +struct mei_cl_device { + struct device dev; + + struct mei_cl *cl; + + const struct mei_cl_ops *ops; + + struct work_struct event_work; + mei_cl_event_cb_t event_cb; + void *event_context; + unsigned long events; + + void *priv_data; +}; + /** * struct mei_device - MEI private device struct + * @hbm_state - state of host bus message protocol * @mem_addr - mem mapped base register address * @hbuf_depth - depth of hardware host/write buffer is slots @@ -296,11 +363,12 @@ struct mei_device { */ struct mutex device_lock; /* device lock */ struct delayed_work timer_work; /* MEI timer delayed work (timeouts) */ - bool recvd_msg; + bool recvd_hw_ready; /* * waiting queue for receive message from FW */ + wait_queue_head_t wait_hw_ready; wait_queue_head_t wait_recvd_msg; wait_queue_head_t wait_stop_wd; @@ -308,7 +376,7 @@ struct mei_device { * mei device states */ enum mei_dev_state dev_state; - enum mei_init_clients_states init_clients_state; + enum mei_hbm_state hbm_state; u16 init_clients_timer; unsigned char rd_msg_buf[MEI_RD_MSG_BUF_SIZE]; /* control messages */ @@ -365,6 +433,14 @@ struct mei_device { struct work_struct init_work; + /* List of bus devices */ + struct list_head device_list; + +#if IS_ENABLED(CONFIG_DEBUG_FS) + struct dentry *dbgfs_dir; +#endif /* CONFIG_DEBUG_FS */ + + const struct mei_hw_ops *ops; char hw[0] __aligned(sizeof(void *)); }; @@ -374,13 +450,23 @@ static inline unsigned long mei_secs_to_jiffies(unsigned long sec) return msecs_to_jiffies(sec * MSEC_PER_SEC); } +/** + * mei_data2slots - get slots - number of (dwords) from a message length + * + size of the mei header + * @length - size of the messages in bytes + * returns - number of slots + */ +static inline u32 mei_data2slots(size_t length) +{ + return DIV_ROUND_UP(sizeof(struct mei_msg_hdr) + length, 4); +} /* * mei init function prototypes */ void mei_device_init(struct mei_device *dev); void mei_reset(struct mei_device *dev, int interrupts); -int mei_hw_init(struct mei_device *dev); +int mei_start(struct mei_device *dev); void mei_stop(struct mei_device *dev); /* @@ -392,8 +478,7 @@ int mei_irq_read_handler(struct mei_device *dev, struct mei_cl_cb *cmpl_list, s32 *slots); int mei_irq_write_handler(struct mei_device *dev, struct mei_cl_cb *cmpl_list); - -void mei_irq_complete_handler(struct mei_cl *cl, struct mei_cl_cb *cb_pos); +void mei_irq_compl_handler(struct mei_device *dev, struct mei_cl_cb *cmpl_list); /* * AMTHIF - AMT Host Interface Functions @@ -417,6 +502,25 @@ struct mei_cl_cb *mei_amthif_find_read_list_entry(struct mei_device *dev, void mei_amthif_run_next_cmd(struct mei_device *dev); +int mei_amthif_irq_write_complete(struct mei_device *dev, s32 *slots, + struct mei_cl_cb *cb, struct mei_cl_cb *cmpl_list); + +void mei_amthif_complete(struct mei_device *dev, struct mei_cl_cb *cb); +int mei_amthif_irq_read_msg(struct mei_device *dev, + struct mei_msg_hdr *mei_hdr, + struct mei_cl_cb *complete_list); +int mei_amthif_irq_read(struct mei_device *dev, s32 *slots); + +/* + * NFC functions + */ +int mei_nfc_host_init(struct mei_device *dev); +void mei_nfc_host_exit(void); + +/* + * NFC Client UUID + */ +extern const uuid_le mei_nfc_guid; int mei_amthif_irq_write_complete(struct mei_device *dev, s32 *slots, struct mei_cl_cb *cb, struct mei_cl_cb *cmpl_list); @@ -455,6 +559,11 @@ static inline void mei_hw_reset(struct mei_device *dev, bool enable) dev->ops->hw_reset(dev, enable); } +static inline void mei_hw_start(struct mei_device *dev) +{ + dev->ops->hw_start(dev); +} + static inline void mei_clear_interrupts(struct mei_device *dev) { dev->ops->intr_clear(dev); @@ -470,10 +579,6 @@ static inline void mei_disable_interrupts(struct mei_device *dev) dev->ops->intr_disable(dev); } -static inline void mei_host_set_ready(struct mei_device *dev) -{ - dev->ops->host_set_ready(dev); -} static inline bool mei_host_is_ready(struct mei_device *dev) { return dev->ops->host_is_ready(dev); @@ -521,8 +626,19 @@ static inline int mei_count_full_read_slots(struct mei_device *dev) return dev->ops->rdbuf_full_slots(dev); } -int mei_register(struct device *dev); -void mei_deregister(void); +#if IS_ENABLED(CONFIG_DEBUG_FS) +int mei_dbgfs_register(struct mei_device *dev, const char *name); +void mei_dbgfs_deregister(struct mei_device *dev); +#else +static inline int mei_dbgfs_register(struct mei_device *dev, const char *name) +{ + return 0; +} +static inline void mei_dbgfs_deregister(struct mei_device *dev) {} +#endif /* CONFIG_DEBUG_FS */ + +int mei_register(struct mei_device *dev); +void mei_deregister(struct mei_device *dev); #define MEI_HDR_FMT "hdr:host=%02d me=%02d len=%d comp=%1d" #define MEI_HDR_PRM(hdr) \ diff --git a/drivers/misc/mei/nfc.c b/drivers/misc/mei/nfc.c new file mode 100644 index 000000000000..3adf8a70f26e --- /dev/null +++ b/drivers/misc/mei/nfc.c @@ -0,0 +1,554 @@ +/* + * + * Intel Management Engine Interface (Intel MEI) Linux driver + * Copyright (c) 2003-2013, 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. + * + */ + +#include <linux/kernel.h> +#include <linux/sched.h> +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/device.h> +#include <linux/pci.h> +#include <linux/mei_cl_bus.h> + +#include "mei_dev.h" +#include "client.h" + +struct mei_nfc_cmd { + u8 command; + u8 status; + u16 req_id; + u32 reserved; + u16 data_size; + u8 sub_command; + u8 data[]; +} __packed; + +struct mei_nfc_reply { + u8 command; + u8 status; + u16 req_id; + u32 reserved; + u16 data_size; + u8 sub_command; + u8 reply_status; + u8 data[]; +} __packed; + +struct mei_nfc_if_version { + u8 radio_version_sw[3]; + u8 reserved[3]; + u8 radio_version_hw[3]; + u8 i2c_addr; + u8 fw_ivn; + u8 vendor_id; + u8 radio_type; +} __packed; + +struct mei_nfc_connect { + u8 fw_ivn; + u8 vendor_id; +} __packed; + +struct mei_nfc_connect_resp { + u8 fw_ivn; + u8 vendor_id; + u16 me_major; + u16 me_minor; + u16 me_hotfix; + u16 me_build; +} __packed; + +struct mei_nfc_hci_hdr { + u8 cmd; + u8 status; + u16 req_id; + u32 reserved; + u16 data_size; +} __packed; + +#define MEI_NFC_CMD_MAINTENANCE 0x00 +#define MEI_NFC_CMD_HCI_SEND 0x01 +#define MEI_NFC_CMD_HCI_RECV 0x02 + +#define MEI_NFC_SUBCMD_CONNECT 0x00 +#define MEI_NFC_SUBCMD_IF_VERSION 0x01 + +#define MEI_NFC_HEADER_SIZE 10 + +/** mei_nfc_dev - NFC mei device + * + * @cl: NFC host client + * @cl_info: NFC info host client + * @init_work: perform connection to the info client + * @fw_ivn: NFC Intervace Version Number + * @vendor_id: NFC manufacturer ID + * @radio_type: NFC radio type + */ +struct mei_nfc_dev { + struct mei_cl *cl; + struct mei_cl *cl_info; + struct work_struct init_work; + wait_queue_head_t send_wq; + u8 fw_ivn; + u8 vendor_id; + u8 radio_type; + char *bus_name; + + u16 req_id; + u16 recv_req_id; +}; + +static struct mei_nfc_dev nfc_dev; + +/* UUIDs for NFC F/W clients */ +const uuid_le mei_nfc_guid = UUID_LE(0x0bb17a78, 0x2a8e, 0x4c50, + 0x94, 0xd4, 0x50, 0x26, + 0x67, 0x23, 0x77, 0x5c); + +static const uuid_le mei_nfc_info_guid = UUID_LE(0xd2de1625, 0x382d, 0x417d, + 0x48, 0xa4, 0xef, 0xab, + 0xba, 0x8a, 0x12, 0x06); + +/* Vendors */ +#define MEI_NFC_VENDOR_INSIDE 0x00 +#define MEI_NFC_VENDOR_NXP 0x01 + +/* Radio types */ +#define MEI_NFC_VENDOR_INSIDE_UREAD 0x00 +#define MEI_NFC_VENDOR_NXP_PN544 0x01 + +static void mei_nfc_free(struct mei_nfc_dev *ndev) +{ + if (ndev->cl) { + list_del(&ndev->cl->device_link); + mei_cl_unlink(ndev->cl); + kfree(ndev->cl); + } + + if (ndev->cl_info) { + list_del(&ndev->cl_info->device_link); + mei_cl_unlink(ndev->cl_info); + kfree(ndev->cl_info); + } +} + +static int mei_nfc_build_bus_name(struct mei_nfc_dev *ndev) +{ + struct mei_device *dev; + + if (!ndev->cl) + return -ENODEV; + + dev = ndev->cl->dev; + + switch (ndev->vendor_id) { + case MEI_NFC_VENDOR_INSIDE: + switch (ndev->radio_type) { + case MEI_NFC_VENDOR_INSIDE_UREAD: + ndev->bus_name = "microread"; + return 0; + + default: + dev_err(&dev->pdev->dev, "Unknow radio type 0x%x\n", + ndev->radio_type); + + return -EINVAL; + } + + case MEI_NFC_VENDOR_NXP: + switch (ndev->radio_type) { + case MEI_NFC_VENDOR_NXP_PN544: + ndev->bus_name = "pn544"; + return 0; + default: + dev_err(&dev->pdev->dev, "Unknow radio type 0x%x\n", + ndev->radio_type); + + return -EINVAL; + } + + default: + dev_err(&dev->pdev->dev, "Unknow vendor ID 0x%x\n", + ndev->vendor_id); + + return -EINVAL; + } + + return 0; +} + +static int mei_nfc_connect(struct mei_nfc_dev *ndev) +{ + struct mei_device *dev; + struct mei_cl *cl; + struct mei_nfc_cmd *cmd, *reply; + struct mei_nfc_connect *connect; + struct mei_nfc_connect_resp *connect_resp; + size_t connect_length, connect_resp_length; + int bytes_recv, ret; + + cl = ndev->cl; + dev = cl->dev; + + connect_length = sizeof(struct mei_nfc_cmd) + + sizeof(struct mei_nfc_connect); + + connect_resp_length = sizeof(struct mei_nfc_cmd) + + sizeof(struct mei_nfc_connect_resp); + + cmd = kzalloc(connect_length, GFP_KERNEL); + if (!cmd) + return -ENOMEM; + connect = (struct mei_nfc_connect *)cmd->data; + + reply = kzalloc(connect_resp_length, GFP_KERNEL); + if (!reply) { + kfree(cmd); + return -ENOMEM; + } + + connect_resp = (struct mei_nfc_connect_resp *)reply->data; + + cmd->command = MEI_NFC_CMD_MAINTENANCE; + cmd->data_size = 3; + cmd->sub_command = MEI_NFC_SUBCMD_CONNECT; + connect->fw_ivn = ndev->fw_ivn; + connect->vendor_id = ndev->vendor_id; + + ret = __mei_cl_send(cl, (u8 *)cmd, connect_length); + if (ret < 0) { + dev_err(&dev->pdev->dev, "Could not send connect cmd\n"); + goto err; + } + + bytes_recv = __mei_cl_recv(cl, (u8 *)reply, connect_resp_length); + if (bytes_recv < 0) { + dev_err(&dev->pdev->dev, "Could not read connect response\n"); + ret = bytes_recv; + goto err; + } + + dev_info(&dev->pdev->dev, "IVN 0x%x Vendor ID 0x%x\n", + connect_resp->fw_ivn, connect_resp->vendor_id); + + dev_info(&dev->pdev->dev, "ME FW %d.%d.%d.%d\n", + connect_resp->me_major, connect_resp->me_minor, + connect_resp->me_hotfix, connect_resp->me_build); + + ret = 0; + +err: + kfree(reply); + kfree(cmd); + + return ret; +} + +static int mei_nfc_if_version(struct mei_nfc_dev *ndev) +{ + struct mei_device *dev; + struct mei_cl *cl; + + struct mei_nfc_cmd cmd; + struct mei_nfc_reply *reply = NULL; + struct mei_nfc_if_version *version; + size_t if_version_length; + int bytes_recv, ret; + + cl = ndev->cl_info; + dev = cl->dev; + + memset(&cmd, 0, sizeof(struct mei_nfc_cmd)); + cmd.command = MEI_NFC_CMD_MAINTENANCE; + cmd.data_size = 1; + cmd.sub_command = MEI_NFC_SUBCMD_IF_VERSION; + + ret = __mei_cl_send(cl, (u8 *)&cmd, sizeof(struct mei_nfc_cmd)); + if (ret < 0) { + dev_err(&dev->pdev->dev, "Could not send IF version cmd\n"); + return ret; + } + + /* to be sure on the stack we alloc memory */ + if_version_length = sizeof(struct mei_nfc_reply) + + sizeof(struct mei_nfc_if_version); + + reply = kzalloc(if_version_length, GFP_KERNEL); + if (!reply) + return -ENOMEM; + + bytes_recv = __mei_cl_recv(cl, (u8 *)reply, if_version_length); + if (bytes_recv < 0 || bytes_recv < sizeof(struct mei_nfc_reply)) { + dev_err(&dev->pdev->dev, "Could not read IF version\n"); + ret = -EIO; + goto err; + } + + version = (struct mei_nfc_if_version *)reply->data; + + ndev->fw_ivn = version->fw_ivn; + ndev->vendor_id = version->vendor_id; + ndev->radio_type = version->radio_type; + +err: + kfree(reply); + return ret; +} + +static int mei_nfc_enable(struct mei_cl_device *cldev) +{ + struct mei_device *dev; + struct mei_nfc_dev *ndev = &nfc_dev; + int ret; + + dev = ndev->cl->dev; + + ret = mei_nfc_connect(ndev); + if (ret < 0) { + dev_err(&dev->pdev->dev, "Could not connect to NFC"); + return ret; + } + + return 0; +} + +static int mei_nfc_disable(struct mei_cl_device *cldev) +{ + return 0; +} + +static int mei_nfc_send(struct mei_cl_device *cldev, u8 *buf, size_t length) +{ + struct mei_device *dev; + struct mei_nfc_dev *ndev; + struct mei_nfc_hci_hdr *hdr; + u8 *mei_buf; + int err; + + ndev = (struct mei_nfc_dev *) cldev->priv_data; + dev = ndev->cl->dev; + + mei_buf = kzalloc(length + MEI_NFC_HEADER_SIZE, GFP_KERNEL); + if (!mei_buf) + return -ENOMEM; + + hdr = (struct mei_nfc_hci_hdr *) mei_buf; + hdr->cmd = MEI_NFC_CMD_HCI_SEND; + hdr->status = 0; + hdr->req_id = ndev->req_id; + hdr->reserved = 0; + hdr->data_size = length; + + memcpy(mei_buf + MEI_NFC_HEADER_SIZE, buf, length); + + err = __mei_cl_send(ndev->cl, mei_buf, length + MEI_NFC_HEADER_SIZE); + if (err < 0) + return err; + + kfree(mei_buf); + + if (!wait_event_interruptible_timeout(ndev->send_wq, + ndev->recv_req_id == ndev->req_id, HZ)) { + dev_err(&dev->pdev->dev, "NFC MEI command timeout\n"); + err = -ETIMEDOUT; + } else { + ndev->req_id++; + } + + return err; +} + +static int mei_nfc_recv(struct mei_cl_device *cldev, u8 *buf, size_t length) +{ + struct mei_nfc_dev *ndev; + struct mei_nfc_hci_hdr *hci_hdr; + int received_length; + + ndev = (struct mei_nfc_dev *)cldev->priv_data; + + received_length = __mei_cl_recv(ndev->cl, buf, length); + if (received_length < 0) + return received_length; + + hci_hdr = (struct mei_nfc_hci_hdr *) buf; + + if (hci_hdr->cmd == MEI_NFC_CMD_HCI_SEND) { + ndev->recv_req_id = hci_hdr->req_id; + wake_up(&ndev->send_wq); + + return 0; + } + + return received_length; +} + +static struct mei_cl_ops nfc_ops = { + .enable = mei_nfc_enable, + .disable = mei_nfc_disable, + .send = mei_nfc_send, + .recv = mei_nfc_recv, +}; + +static void mei_nfc_init(struct work_struct *work) +{ + struct mei_device *dev; + struct mei_cl_device *cldev; + struct mei_nfc_dev *ndev; + struct mei_cl *cl_info; + + ndev = container_of(work, struct mei_nfc_dev, init_work); + + cl_info = ndev->cl_info; + dev = cl_info->dev; + + mutex_lock(&dev->device_lock); + + if (mei_cl_connect(cl_info, NULL) < 0) { + mutex_unlock(&dev->device_lock); + dev_err(&dev->pdev->dev, + "Could not connect to the NFC INFO ME client"); + + goto err; + } + + mutex_unlock(&dev->device_lock); + + if (mei_nfc_if_version(ndev) < 0) { + dev_err(&dev->pdev->dev, "Could not get the NFC interfave version"); + + goto err; + } + + dev_info(&dev->pdev->dev, + "NFC MEI VERSION: IVN 0x%x Vendor ID 0x%x Type 0x%x\n", + ndev->fw_ivn, ndev->vendor_id, ndev->radio_type); + + mutex_lock(&dev->device_lock); + + if (mei_cl_disconnect(cl_info) < 0) { + mutex_unlock(&dev->device_lock); + dev_err(&dev->pdev->dev, + "Could not disconnect the NFC INFO ME client"); + + goto err; + } + + mutex_unlock(&dev->device_lock); + + if (mei_nfc_build_bus_name(ndev) < 0) { + dev_err(&dev->pdev->dev, + "Could not build the bus ID name\n"); + return; + } + + cldev = mei_cl_add_device(dev, mei_nfc_guid, ndev->bus_name, &nfc_ops); + if (!cldev) { + dev_err(&dev->pdev->dev, + "Could not add the NFC device to the MEI bus\n"); + + goto err; + } + + cldev->priv_data = ndev; + + + return; + +err: + mei_nfc_free(ndev); + + return; +} + + +int mei_nfc_host_init(struct mei_device *dev) +{ + struct mei_nfc_dev *ndev = &nfc_dev; + struct mei_cl *cl_info, *cl = NULL; + int i, ret; + + /* already initialzed */ + if (ndev->cl_info) + return 0; + + cl_info = mei_cl_allocate(dev); + cl = mei_cl_allocate(dev); + + if (!cl || !cl_info) { + ret = -ENOMEM; + goto err; + } + + /* check for valid client id */ + i = mei_me_cl_by_uuid(dev, &mei_nfc_info_guid); + if (i < 0) { + dev_info(&dev->pdev->dev, "nfc: failed to find the client\n"); + ret = -ENOENT; + goto err; + } + + cl_info->me_client_id = dev->me_clients[i].client_id; + + ret = mei_cl_link(cl_info, MEI_HOST_CLIENT_ID_ANY); + if (ret) + goto err; + + cl_info->device_uuid = mei_nfc_info_guid; + + list_add_tail(&cl_info->device_link, &dev->device_list); + + /* check for valid client id */ + i = mei_me_cl_by_uuid(dev, &mei_nfc_guid); + if (i < 0) { + dev_info(&dev->pdev->dev, "nfc: failed to find the client\n"); + ret = -ENOENT; + goto err; + } + + cl->me_client_id = dev->me_clients[i].client_id; + + ret = mei_cl_link(cl, MEI_HOST_CLIENT_ID_ANY); + if (ret) + goto err; + + cl->device_uuid = mei_nfc_guid; + + list_add_tail(&cl->device_link, &dev->device_list); + + ndev->cl_info = cl_info; + ndev->cl = cl; + ndev->req_id = 1; + + INIT_WORK(&ndev->init_work, mei_nfc_init); + init_waitqueue_head(&ndev->send_wq); + schedule_work(&ndev->init_work); + + return 0; + +err: + mei_nfc_free(ndev); + + return ret; +} + +void mei_nfc_host_exit(void) +{ + struct mei_nfc_dev *ndev = &nfc_dev; + + if (ndev->cl && ndev->cl->device) + mei_cl_remove_device(ndev->cl->device); + + mei_nfc_free(ndev); +} diff --git a/drivers/misc/mei/pci-me.c b/drivers/misc/mei/pci-me.c index b8b5c9c3ad03..88aec6a90ff4 100644 --- a/drivers/misc/mei/pci-me.c +++ b/drivers/misc/mei/pci-me.c @@ -47,7 +47,7 @@ static struct pci_dev *mei_pdev; /* mei_pci_tbl - PCI Device ID Table */ -static DEFINE_PCI_DEVICE_TABLE(mei_pci_tbl) = { +static DEFINE_PCI_DEVICE_TABLE(mei_me_pci_tbl) = { {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_82946GZ)}, {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_82G35)}, {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_82Q965)}, @@ -86,7 +86,7 @@ static DEFINE_PCI_DEVICE_TABLE(mei_pci_tbl) = { {0, } }; -MODULE_DEVICE_TABLE(pci, mei_pci_tbl); +MODULE_DEVICE_TABLE(pci, mei_me_pci_tbl); static DEFINE_MUTEX(mei_mutex); @@ -97,7 +97,7 @@ static DEFINE_MUTEX(mei_mutex); * * returns true if ME Interface is valid, false otherwise */ -static bool mei_quirk_probe(struct pci_dev *pdev, +static bool mei_me_quirk_probe(struct pci_dev *pdev, const struct pci_device_id *ent) { u32 reg; @@ -119,7 +119,7 @@ static bool mei_quirk_probe(struct pci_dev *pdev, * * returns 0 on success, <0 on failure. */ -static int mei_probe(struct pci_dev *pdev, const struct pci_device_id *ent) +static int mei_me_probe(struct pci_dev *pdev, const struct pci_device_id *ent) { struct mei_device *dev; struct mei_me_hw *hw; @@ -127,7 +127,7 @@ static int mei_probe(struct pci_dev *pdev, const struct pci_device_id *ent) mutex_lock(&mei_mutex); - if (!mei_quirk_probe(pdev, ent)) { + if (!mei_me_quirk_probe(pdev, ent)) { err = -ENODEV; goto end; } @@ -184,20 +184,19 @@ static int mei_probe(struct pci_dev *pdev, const struct pci_device_id *ent) goto disable_msi; } - if (mei_hw_init(dev)) { + if (mei_start(dev)) { dev_err(&pdev->dev, "init hw failure.\n"); err = -ENODEV; goto release_irq; } - err = mei_register(&pdev->dev); + err = mei_register(dev); if (err) goto release_irq; mei_pdev = pdev; pci_set_drvdata(pdev, dev); - schedule_delayed_work(&dev->timer_work, HZ); mutex_unlock(&mei_mutex); @@ -233,7 +232,7 @@ end: * mei_remove is called by the PCI subsystem to alert the driver * that it should release a PCI device. */ -static void mei_remove(struct pci_dev *pdev) +static void mei_me_remove(struct pci_dev *pdev) { struct mei_device *dev; struct mei_me_hw *hw; @@ -253,8 +252,6 @@ static void mei_remove(struct pci_dev *pdev) mei_pdev = NULL; - mei_watchdog_unregister(dev); - /* disable interrupts */ mei_disable_interrupts(dev); @@ -265,16 +262,17 @@ static void mei_remove(struct pci_dev *pdev) if (hw->mem_addr) pci_iounmap(pdev, hw->mem_addr); + mei_deregister(dev); + kfree(dev); pci_release_regions(pdev); pci_disable_device(pdev); - mei_deregister(); } #ifdef CONFIG_PM -static int mei_pci_suspend(struct device *device) +static int mei_me_pci_suspend(struct device *device) { struct pci_dev *pdev = to_pci_dev(device); struct mei_device *dev = pci_get_drvdata(pdev); @@ -294,7 +292,7 @@ static int mei_pci_suspend(struct device *device) return 0; } -static int mei_pci_resume(struct device *device) +static int mei_me_pci_resume(struct device *device) { struct pci_dev *pdev = to_pci_dev(device); struct mei_device *dev; @@ -334,24 +332,24 @@ static int mei_pci_resume(struct device *device) return err; } -static SIMPLE_DEV_PM_OPS(mei_pm_ops, mei_pci_suspend, mei_pci_resume); -#define MEI_PM_OPS (&mei_pm_ops) +static SIMPLE_DEV_PM_OPS(mei_me_pm_ops, mei_me_pci_suspend, mei_me_pci_resume); +#define MEI_ME_PM_OPS (&mei_me_pm_ops) #else -#define MEI_PM_OPS NULL +#define MEI_ME_PM_OPS NULL #endif /* CONFIG_PM */ /* * PCI driver structure */ -static struct pci_driver mei_driver = { +static struct pci_driver mei_me_driver = { .name = KBUILD_MODNAME, - .id_table = mei_pci_tbl, - .probe = mei_probe, - .remove = mei_remove, - .shutdown = mei_remove, - .driver.pm = MEI_PM_OPS, + .id_table = mei_me_pci_tbl, + .probe = mei_me_probe, + .remove = mei_me_remove, + .shutdown = mei_me_remove, + .driver.pm = MEI_ME_PM_OPS, }; -module_pci_driver(mei_driver); +module_pci_driver(mei_me_driver); MODULE_AUTHOR("Intel Corporation"); MODULE_DESCRIPTION("Intel(R) Management Engine Interface"); diff --git a/drivers/misc/mei/wd.c b/drivers/misc/mei/wd.c index 2413247fc392..eb3f05c8d7d0 100644 --- a/drivers/misc/mei/wd.c +++ b/drivers/misc/mei/wd.c @@ -317,7 +317,8 @@ end: * * returns 0 if success, negative errno code for failure */ -static int mei_wd_ops_set_timeout(struct watchdog_device *wd_dev, unsigned int timeout) +static int mei_wd_ops_set_timeout(struct watchdog_device *wd_dev, + unsigned int timeout) { struct mei_device *dev; diff --git a/drivers/misc/tsl2550.c b/drivers/misc/tsl2550.c index 1e7bc0eb081e..1dfde4d543db 100644 --- a/drivers/misc/tsl2550.c +++ b/drivers/misc/tsl2550.c @@ -417,24 +417,26 @@ static int tsl2550_remove(struct i2c_client *client) return 0; } -#ifdef CONFIG_PM +#ifdef CONFIG_PM_SLEEP -static int tsl2550_suspend(struct i2c_client *client, pm_message_t mesg) +static int tsl2550_suspend(struct device *dev) { - return tsl2550_set_power_state(client, 0); + return tsl2550_set_power_state(to_i2c_client(dev), 0); } -static int tsl2550_resume(struct i2c_client *client) +static int tsl2550_resume(struct device *dev) { - return tsl2550_set_power_state(client, 1); + return tsl2550_set_power_state(to_i2c_client(dev), 1); } +static SIMPLE_DEV_PM_OPS(tsl2550_pm_ops, tsl2550_suspend, tsl2550_resume); +#define TSL2550_PM_OPS (&tsl2550_pm_ops) + #else -#define tsl2550_suspend NULL -#define tsl2550_resume NULL +#define TSL2550_PM_OPS NULL -#endif /* CONFIG_PM */ +#endif /* CONFIG_PM_SLEEP */ static const struct i2c_device_id tsl2550_id[] = { { "tsl2550", 0 }, @@ -446,9 +448,8 @@ static struct i2c_driver tsl2550_driver = { .driver = { .name = TSL2550_DRV_NAME, .owner = THIS_MODULE, + .pm = TSL2550_PM_OPS, }, - .suspend = tsl2550_suspend, - .resume = tsl2550_resume, .probe = tsl2550_probe, .remove = tsl2550_remove, .id_table = tsl2550_id, diff --git a/drivers/mmc/host/sdricoh_cs.c b/drivers/mmc/host/sdricoh_cs.c index 7009f17ad6cd..50adbd155f35 100644 --- a/drivers/mmc/host/sdricoh_cs.c +++ b/drivers/mmc/host/sdricoh_cs.c @@ -543,25 +543,7 @@ static struct pcmcia_driver sdricoh_driver = { .suspend = sdricoh_pcmcia_suspend, .resume = sdricoh_pcmcia_resume, }; - -/*****************************************************************************\ - * * - * Driver init/exit * - * * -\*****************************************************************************/ - -static int __init sdricoh_drv_init(void) -{ - return pcmcia_register_driver(&sdricoh_driver); -} - -static void __exit sdricoh_drv_exit(void) -{ - pcmcia_unregister_driver(&sdricoh_driver); -} - -module_init(sdricoh_drv_init); -module_exit(sdricoh_drv_exit); +module_pcmcia_driver(sdricoh_driver); module_param(switchlocked, uint, 0444); diff --git a/drivers/mtd/mtdchar.c b/drivers/mtd/mtdchar.c index 92ab30ab00dc..dc571ebc1aa0 100644 --- a/drivers/mtd/mtdchar.c +++ b/drivers/mtd/mtdchar.c @@ -1123,33 +1123,6 @@ static unsigned long mtdchar_get_unmapped_area(struct file *file, } #endif -static inline unsigned long get_vm_size(struct vm_area_struct *vma) -{ - return vma->vm_end - vma->vm_start; -} - -static inline resource_size_t get_vm_offset(struct vm_area_struct *vma) -{ - return (resource_size_t) vma->vm_pgoff << PAGE_SHIFT; -} - -/* - * Set a new vm offset. - * - * Verify that the incoming offset really works as a page offset, - * and that the offset and size fit in a resource_size_t. - */ -static inline int set_vm_offset(struct vm_area_struct *vma, resource_size_t off) -{ - pgoff_t pgoff = off >> PAGE_SHIFT; - if (off != (resource_size_t) pgoff << PAGE_SHIFT) - return -EINVAL; - if (off + get_vm_size(vma) - 1 < off) - return -EINVAL; - vma->vm_pgoff = pgoff; - return 0; -} - /* * set up a mapping for shared memory segments */ @@ -1159,45 +1132,17 @@ static int mtdchar_mmap(struct file *file, struct vm_area_struct *vma) struct mtd_file_info *mfi = file->private_data; struct mtd_info *mtd = mfi->mtd; struct map_info *map = mtd->priv; - resource_size_t start, off; - unsigned long len, vma_len; /* This is broken because it assumes the MTD device is map-based and that mtd->priv is a valid struct map_info. It should be replaced with something that uses the mtd_get_unmapped_area() operation properly. */ if (0 /*mtd->type == MTD_RAM || mtd->type == MTD_ROM*/) { - off = get_vm_offset(vma); - start = map->phys; - len = PAGE_ALIGN((start & ~PAGE_MASK) + map->size); - start &= PAGE_MASK; - vma_len = get_vm_size(vma); - - /* Overflow in off+len? */ - if (vma_len + off < off) - return -EINVAL; - /* Does it fit in the mapping? */ - if (vma_len + off > len) - return -EINVAL; - - off += start; - /* Did that overflow? */ - if (off < start) - return -EINVAL; - if (set_vm_offset(vma, off) < 0) - return -EINVAL; - vma->vm_flags |= VM_IO | VM_DONTEXPAND | VM_DONTDUMP; - #ifdef pgprot_noncached - if (file->f_flags & O_DSYNC || off >= __pa(high_memory)) + if (file->f_flags & O_DSYNC || map->phys >= __pa(high_memory)) vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); #endif - if (io_remap_pfn_range(vma, vma->vm_start, off >> PAGE_SHIFT, - vma->vm_end - vma->vm_start, - vma->vm_page_prot)) - return -EAGAIN; - - return 0; + return vm_iomap_memory(vma, map->phys, map->size); } return -ENOSYS; #else diff --git a/drivers/net/arcnet/com20020_cs.c b/drivers/net/arcnet/com20020_cs.c index 5bed4c4e2508..74dc1875f9cd 100644 --- a/drivers/net/arcnet/com20020_cs.c +++ b/drivers/net/arcnet/com20020_cs.c @@ -333,16 +333,4 @@ static struct pcmcia_driver com20020_cs_driver = { .suspend = com20020_suspend, .resume = com20020_resume, }; - -static int __init init_com20020_cs(void) -{ - return pcmcia_register_driver(&com20020_cs_driver); -} - -static void __exit exit_com20020_cs(void) -{ - pcmcia_unregister_driver(&com20020_cs_driver); -} - -module_init(init_com20020_cs); -module_exit(exit_com20020_cs); +module_pcmcia_driver(com20020_cs_driver); diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c index 07401a3e256b..dbbea0eec134 100644 --- a/drivers/net/bonding/bond_main.c +++ b/drivers/net/bonding/bond_main.c @@ -846,8 +846,10 @@ static void bond_mc_swap(struct bonding *bond, struct slave *new_active, if (bond->dev->flags & IFF_ALLMULTI) dev_set_allmulti(old_active->dev, -1); + netif_addr_lock_bh(bond->dev); netdev_for_each_mc_addr(ha, bond->dev) dev_mc_del(old_active->dev, ha->addr); + netif_addr_unlock_bh(bond->dev); } if (new_active) { @@ -858,8 +860,10 @@ static void bond_mc_swap(struct bonding *bond, struct slave *new_active, if (bond->dev->flags & IFF_ALLMULTI) dev_set_allmulti(new_active->dev, 1); + netif_addr_lock_bh(bond->dev); netdev_for_each_mc_addr(ha, bond->dev) dev_mc_add(new_active->dev, ha->addr); + netif_addr_unlock_bh(bond->dev); } } @@ -1901,11 +1905,29 @@ err_dest_symlinks: bond_destroy_slave_symlinks(bond_dev, slave_dev); err_detach: + if (!USES_PRIMARY(bond->params.mode)) { + netif_addr_lock_bh(bond_dev); + bond_mc_list_flush(bond_dev, slave_dev); + netif_addr_unlock_bh(bond_dev); + } + bond_del_vlans_from_slave(bond, slave_dev); write_lock_bh(&bond->lock); bond_detach_slave(bond, new_slave); + if (bond->primary_slave == new_slave) + bond->primary_slave = NULL; write_unlock_bh(&bond->lock); + if (bond->curr_active_slave == new_slave) { + read_lock(&bond->lock); + write_lock_bh(&bond->curr_slave_lock); + bond_change_active_slave(bond, NULL); + bond_select_active_slave(bond); + write_unlock_bh(&bond->curr_slave_lock); + read_unlock(&bond->lock); + } + slave_disable_netpoll(new_slave); err_close: + slave_dev->priv_flags &= ~IFF_BONDING; dev_close(slave_dev); err_unset_master: @@ -3168,11 +3190,20 @@ static int bond_slave_netdev_event(unsigned long event, struct net_device *slave_dev) { struct slave *slave = bond_slave_get_rtnl(slave_dev); - struct bonding *bond = slave->bond; - struct net_device *bond_dev = slave->bond->dev; + struct bonding *bond; + struct net_device *bond_dev; u32 old_speed; u8 old_duplex; + /* A netdev event can be generated while enslaving a device + * before netdev_rx_handler_register is called in which case + * slave will be NULL + */ + if (!slave) + return NOTIFY_DONE; + bond_dev = slave->bond->dev; + bond = slave->bond; + switch (event) { case NETDEV_UNREGISTER: if (bond->setup_by_slave) @@ -3286,20 +3317,22 @@ static int bond_xmit_hash_policy_l2(struct sk_buff *skb, int count) */ static int bond_xmit_hash_policy_l23(struct sk_buff *skb, int count) { - struct ethhdr *data = (struct ethhdr *)skb->data; - struct iphdr *iph; - struct ipv6hdr *ipv6h; + const struct ethhdr *data; + const struct iphdr *iph; + const struct ipv6hdr *ipv6h; u32 v6hash; - __be32 *s, *d; + const __be32 *s, *d; if (skb->protocol == htons(ETH_P_IP) && - skb_network_header_len(skb) >= sizeof(*iph)) { + pskb_network_may_pull(skb, sizeof(*iph))) { iph = ip_hdr(skb); + data = (struct ethhdr *)skb->data; return ((ntohl(iph->saddr ^ iph->daddr) & 0xffff) ^ (data->h_dest[5] ^ data->h_source[5])) % count; } else if (skb->protocol == htons(ETH_P_IPV6) && - skb_network_header_len(skb) >= sizeof(*ipv6h)) { + pskb_network_may_pull(skb, sizeof(*ipv6h))) { ipv6h = ipv6_hdr(skb); + data = (struct ethhdr *)skb->data; s = &ipv6h->saddr.s6_addr32[0]; d = &ipv6h->daddr.s6_addr32[0]; v6hash = (s[1] ^ d[1]) ^ (s[2] ^ d[2]) ^ (s[3] ^ d[3]); @@ -3318,33 +3351,36 @@ static int bond_xmit_hash_policy_l23(struct sk_buff *skb, int count) static int bond_xmit_hash_policy_l34(struct sk_buff *skb, int count) { u32 layer4_xor = 0; - struct iphdr *iph; - struct ipv6hdr *ipv6h; - __be32 *s, *d; - __be16 *layer4hdr; + const struct iphdr *iph; + const struct ipv6hdr *ipv6h; + const __be32 *s, *d; + const __be16 *l4 = NULL; + __be16 _l4[2]; + int noff = skb_network_offset(skb); + int poff; if (skb->protocol == htons(ETH_P_IP) && - skb_network_header_len(skb) >= sizeof(*iph)) { + pskb_may_pull(skb, noff + sizeof(*iph))) { iph = ip_hdr(skb); - if (!ip_is_fragment(iph) && - (iph->protocol == IPPROTO_TCP || - iph->protocol == IPPROTO_UDP) && - (skb_headlen(skb) - skb_network_offset(skb) >= - iph->ihl * sizeof(u32) + sizeof(*layer4hdr) * 2)) { - layer4hdr = (__be16 *)((u32 *)iph + iph->ihl); - layer4_xor = ntohs(*layer4hdr ^ *(layer4hdr + 1)); + poff = proto_ports_offset(iph->protocol); + + if (!ip_is_fragment(iph) && poff >= 0) { + l4 = skb_header_pointer(skb, noff + (iph->ihl << 2) + poff, + sizeof(_l4), &_l4); + if (l4) + layer4_xor = ntohs(l4[0] ^ l4[1]); } return (layer4_xor ^ ((ntohl(iph->saddr ^ iph->daddr)) & 0xffff)) % count; } else if (skb->protocol == htons(ETH_P_IPV6) && - skb_network_header_len(skb) >= sizeof(*ipv6h)) { + pskb_may_pull(skb, noff + sizeof(*ipv6h))) { ipv6h = ipv6_hdr(skb); - if ((ipv6h->nexthdr == IPPROTO_TCP || - ipv6h->nexthdr == IPPROTO_UDP) && - (skb_headlen(skb) - skb_network_offset(skb) >= - sizeof(*ipv6h) + sizeof(*layer4hdr) * 2)) { - layer4hdr = (__be16 *)(ipv6h + 1); - layer4_xor = ntohs(*layer4hdr ^ *(layer4hdr + 1)); + poff = proto_ports_offset(ipv6h->nexthdr); + if (poff >= 0) { + l4 = skb_header_pointer(skb, noff + sizeof(*ipv6h) + poff, + sizeof(_l4), &_l4); + if (l4) + layer4_xor = ntohs(l4[0] ^ l4[1]); } s = &ipv6h->saddr.s6_addr32[0]; d = &ipv6h->daddr.s6_addr32[0]; diff --git a/drivers/net/can/mcp251x.c b/drivers/net/can/mcp251x.c index f32b9fc6a983..9aa0c64c33c8 100644 --- a/drivers/net/can/mcp251x.c +++ b/drivers/net/can/mcp251x.c @@ -929,6 +929,7 @@ static int mcp251x_open(struct net_device *net) struct mcp251x_priv *priv = netdev_priv(net); struct spi_device *spi = priv->spi; struct mcp251x_platform_data *pdata = spi->dev.platform_data; + unsigned long flags; int ret; ret = open_candev(net); @@ -945,9 +946,14 @@ static int mcp251x_open(struct net_device *net) priv->tx_skb = NULL; priv->tx_len = 0; + flags = IRQF_ONESHOT; + if (pdata->irq_flags) + flags |= pdata->irq_flags; + else + flags |= IRQF_TRIGGER_FALLING; + ret = request_threaded_irq(spi->irq, NULL, mcp251x_can_ist, - pdata->irq_flags ? pdata->irq_flags : IRQF_TRIGGER_FALLING, - DEVICE_NAME, priv); + flags, DEVICE_NAME, priv); if (ret) { dev_err(&spi->dev, "failed to acquire irq %d\n", spi->irq); if (pdata->transceiver_enable) diff --git a/drivers/net/can/sja1000/ems_pcmcia.c b/drivers/net/can/sja1000/ems_pcmcia.c index 5c2f3fbbf5ae..321c27e1c7fc 100644 --- a/drivers/net/can/sja1000/ems_pcmcia.c +++ b/drivers/net/can/sja1000/ems_pcmcia.c @@ -316,15 +316,4 @@ static struct pcmcia_driver ems_pcmcia_driver = { .remove = ems_pcmcia_remove, .id_table = ems_pcmcia_tbl, }; - -static int __init ems_pcmcia_init(void) -{ - return pcmcia_register_driver(&ems_pcmcia_driver); -} -module_init(ems_pcmcia_init); - -static void __exit ems_pcmcia_exit(void) -{ - pcmcia_unregister_driver(&ems_pcmcia_driver); -} -module_exit(ems_pcmcia_exit); +module_pcmcia_driver(ems_pcmcia_driver); diff --git a/drivers/net/can/sja1000/peak_pcmcia.c b/drivers/net/can/sja1000/peak_pcmcia.c index 1a7020ba37f5..0a707f70661c 100644 --- a/drivers/net/can/sja1000/peak_pcmcia.c +++ b/drivers/net/can/sja1000/peak_pcmcia.c @@ -740,15 +740,4 @@ static struct pcmcia_driver pcan_driver = { .remove = pcan_remove, .id_table = pcan_table, }; - -static int __init pcan_init(void) -{ - return pcmcia_register_driver(&pcan_driver); -} -module_init(pcan_init); - -static void __exit pcan_exit(void) -{ - pcmcia_unregister_driver(&pcan_driver); -} -module_exit(pcan_exit); +module_pcmcia_driver(pcan_driver); diff --git a/drivers/net/can/sja1000/sja1000_of_platform.c b/drivers/net/can/sja1000/sja1000_of_platform.c index 6433b81256cd..8e0c4a001939 100644 --- a/drivers/net/can/sja1000/sja1000_of_platform.c +++ b/drivers/net/can/sja1000/sja1000_of_platform.c @@ -96,8 +96,8 @@ static int sja1000_ofp_probe(struct platform_device *ofdev) struct net_device *dev; struct sja1000_priv *priv; struct resource res; - const u32 *prop; - int err, irq, res_size, prop_size; + u32 prop; + int err, irq, res_size; void __iomem *base; err = of_address_to_resource(np, 0, &res); @@ -138,27 +138,27 @@ static int sja1000_ofp_probe(struct platform_device *ofdev) priv->read_reg = sja1000_ofp_read_reg; priv->write_reg = sja1000_ofp_write_reg; - prop = of_get_property(np, "nxp,external-clock-frequency", &prop_size); - if (prop && (prop_size == sizeof(u32))) - priv->can.clock.freq = *prop / 2; + err = of_property_read_u32(np, "nxp,external-clock-frequency", &prop); + if (!err) + priv->can.clock.freq = prop / 2; else priv->can.clock.freq = SJA1000_OFP_CAN_CLOCK; /* default */ - prop = of_get_property(np, "nxp,tx-output-mode", &prop_size); - if (prop && (prop_size == sizeof(u32))) - priv->ocr |= *prop & OCR_MODE_MASK; + err = of_property_read_u32(np, "nxp,tx-output-mode", &prop); + if (!err) + priv->ocr |= prop & OCR_MODE_MASK; else priv->ocr |= OCR_MODE_NORMAL; /* default */ - prop = of_get_property(np, "nxp,tx-output-config", &prop_size); - if (prop && (prop_size == sizeof(u32))) - priv->ocr |= (*prop << OCR_TX_SHIFT) & OCR_TX_MASK; + err = of_property_read_u32(np, "nxp,tx-output-config", &prop); + if (!err) + priv->ocr |= (prop << OCR_TX_SHIFT) & OCR_TX_MASK; else priv->ocr |= OCR_TX0_PULLDOWN; /* default */ - prop = of_get_property(np, "nxp,clock-out-frequency", &prop_size); - if (prop && (prop_size == sizeof(u32)) && *prop) { - u32 divider = priv->can.clock.freq * 2 / *prop; + err = of_property_read_u32(np, "nxp,clock-out-frequency", &prop); + if (!err && prop) { + u32 divider = priv->can.clock.freq * 2 / prop; if (divider > 1) priv->cdr |= divider / 2 - 1; @@ -168,8 +168,7 @@ static int sja1000_ofp_probe(struct platform_device *ofdev) priv->cdr |= CDR_CLK_OFF; /* default */ } - prop = of_get_property(np, "nxp,no-comparator-bypass", NULL); - if (!prop) + if (!of_property_read_bool(np, "nxp,no-comparator-bypass")) priv->cdr |= CDR_CBP; /* default */ priv->irq_flags = IRQF_SHARED; diff --git a/drivers/net/can/softing/softing_cs.c b/drivers/net/can/softing/softing_cs.c index c2c0a5bb0b21..498605f833dd 100644 --- a/drivers/net/can/softing/softing_cs.c +++ b/drivers/net/can/softing/softing_cs.c @@ -27,7 +27,7 @@ #include "softing_platform.h" static int softingcs_index; -static spinlock_t softingcs_index_lock; +static DEFINE_SPINLOCK(softingcs_index_lock); static int softingcs_reset(struct platform_device *pdev, int v); static int softingcs_enable_irq(struct platform_device *pdev, int v); @@ -340,19 +340,7 @@ static struct pcmcia_driver softingcs_driver = { .remove = softingcs_remove, }; -static int __init softingcs_start(void) -{ - spin_lock_init(&softingcs_index_lock); - return pcmcia_register_driver(&softingcs_driver); -} - -static void __exit softingcs_stop(void) -{ - pcmcia_unregister_driver(&softingcs_driver); -} - -module_init(softingcs_start); -module_exit(softingcs_stop); +module_pcmcia_driver(softingcs_driver); MODULE_DESCRIPTION("softing CANcard driver" ", links PCMCIA card to softing driver"); diff --git a/drivers/net/ethernet/3com/3c574_cs.c b/drivers/net/ethernet/3com/3c574_cs.c index ffd8de28a76a..6fc994fa4abe 100644 --- a/drivers/net/ethernet/3com/3c574_cs.c +++ b/drivers/net/ethernet/3com/3c574_cs.c @@ -1165,16 +1165,4 @@ static struct pcmcia_driver tc574_driver = { .suspend = tc574_suspend, .resume = tc574_resume, }; - -static int __init init_tc574(void) -{ - return pcmcia_register_driver(&tc574_driver); -} - -static void __exit exit_tc574(void) -{ - pcmcia_unregister_driver(&tc574_driver); -} - -module_init(init_tc574); -module_exit(exit_tc574); +module_pcmcia_driver(tc574_driver); diff --git a/drivers/net/ethernet/3com/3c589_cs.c b/drivers/net/ethernet/3com/3c589_cs.c index a556c01e011b..078480aaa168 100644 --- a/drivers/net/ethernet/3com/3c589_cs.c +++ b/drivers/net/ethernet/3com/3c589_cs.c @@ -928,16 +928,4 @@ static struct pcmcia_driver tc589_driver = { .suspend = tc589_suspend, .resume = tc589_resume, }; - -static int __init init_tc589(void) -{ - return pcmcia_register_driver(&tc589_driver); -} - -static void __exit exit_tc589(void) -{ - pcmcia_unregister_driver(&tc589_driver); -} - -module_init(init_tc589); -module_exit(exit_tc589); +module_pcmcia_driver(tc589_driver); diff --git a/drivers/net/ethernet/8390/ax88796.c b/drivers/net/ethernet/8390/ax88796.c index cab306a9888e..e1d26433d619 100644 --- a/drivers/net/ethernet/8390/ax88796.c +++ b/drivers/net/ethernet/8390/ax88796.c @@ -828,7 +828,7 @@ static int ax_probe(struct platform_device *pdev) struct ei_device *ei_local; struct ax_device *ax; struct resource *irq, *mem, *mem2; - resource_size_t mem_size, mem2_size = 0; + unsigned long mem_size, mem2_size = 0; int ret = 0; dev = ax__alloc_ei_netdev(sizeof(struct ax_device)); diff --git a/drivers/net/ethernet/8390/axnet_cs.c b/drivers/net/ethernet/8390/axnet_cs.c index e1b3941bd149..d801c1410fb0 100644 --- a/drivers/net/ethernet/8390/axnet_cs.c +++ b/drivers/net/ethernet/8390/axnet_cs.c @@ -728,19 +728,7 @@ static struct pcmcia_driver axnet_cs_driver = { .suspend = axnet_suspend, .resume = axnet_resume, }; - -static int __init init_axnet_cs(void) -{ - return pcmcia_register_driver(&axnet_cs_driver); -} - -static void __exit exit_axnet_cs(void) -{ - pcmcia_unregister_driver(&axnet_cs_driver); -} - -module_init(init_axnet_cs); -module_exit(exit_axnet_cs); +module_pcmcia_driver(axnet_cs_driver); /*====================================================================*/ diff --git a/drivers/net/ethernet/8390/pcnet_cs.c b/drivers/net/ethernet/8390/pcnet_cs.c index de1af0bfed4c..46c5aadaca8e 100644 --- a/drivers/net/ethernet/8390/pcnet_cs.c +++ b/drivers/net/ethernet/8390/pcnet_cs.c @@ -1694,16 +1694,4 @@ static struct pcmcia_driver pcnet_driver = { .suspend = pcnet_suspend, .resume = pcnet_resume, }; - -static int __init init_pcnet_cs(void) -{ - return pcmcia_register_driver(&pcnet_driver); -} - -static void __exit exit_pcnet_cs(void) -{ - pcmcia_unregister_driver(&pcnet_driver); -} - -module_init(init_pcnet_cs); -module_exit(exit_pcnet_cs); +module_pcmcia_driver(pcnet_driver); diff --git a/drivers/net/ethernet/amd/nmclan_cs.c b/drivers/net/ethernet/amd/nmclan_cs.c index 9f59bf63514b..d4ed89130c52 100644 --- a/drivers/net/ethernet/amd/nmclan_cs.c +++ b/drivers/net/ethernet/amd/nmclan_cs.c @@ -1508,16 +1508,4 @@ static struct pcmcia_driver nmclan_cs_driver = { .suspend = nmclan_suspend, .resume = nmclan_resume, }; - -static int __init init_nmclan_cs(void) -{ - return pcmcia_register_driver(&nmclan_cs_driver); -} - -static void __exit exit_nmclan_cs(void) -{ - pcmcia_unregister_driver(&nmclan_cs_driver); -} - -module_init(init_nmclan_cs); -module_exit(exit_nmclan_cs); +module_pcmcia_driver(nmclan_cs_driver); diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c index 4046f97378c2..57619dd4a92b 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c @@ -2614,6 +2614,9 @@ int bnx2x_nic_load(struct bnx2x *bp, int load_mode) } } + /* initialize FW coalescing state machines in RAM */ + bnx2x_update_coalesce(bp); + /* setup the leading queue */ rc = bnx2x_setup_leading(bp); if (rc) { @@ -4580,11 +4583,11 @@ static void storm_memset_hc_disable(struct bnx2x *bp, u8 port, u32 enable_flag = disable ? 0 : (1 << HC_INDEX_DATA_HC_ENABLED_SHIFT); u32 addr = BAR_CSTRORM_INTMEM + CSTORM_STATUS_BLOCK_DATA_FLAGS_OFFSET(fw_sb_id, sb_index); - u16 flags = REG_RD16(bp, addr); + u8 flags = REG_RD8(bp, addr); /* clear and set */ flags &= ~HC_INDEX_DATA_HC_ENABLED; flags |= enable_flag; - REG_WR16(bp, addr, flags); + REG_WR8(bp, addr, flags); DP(NETIF_MSG_IFUP, "port %x fw_sb_id %d sb_index %d disable %d\n", port, fw_sb_id, sb_index, disable); diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c index 8e58da909f5c..c50696b396f1 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c @@ -9878,6 +9878,10 @@ static int bnx2x_prev_unload_common(struct bnx2x *bp) REG_RD(bp, NIG_REG_NIG_INT_STS_CLR_0); } } + if (!CHIP_IS_E1x(bp)) + /* block FW from writing to host */ + REG_WR(bp, PGLUE_B_REG_INTERNAL_PFID_ENABLE_MASTER, 0); + /* wait until BRB is empty */ tmp_reg = REG_RD(bp, BRB1_REG_NUM_OF_FULL_BLOCKS); while (timer_count) { diff --git a/drivers/net/ethernet/emulex/benet/be_main.c b/drivers/net/ethernet/emulex/benet/be_main.c index 08e54f3d288b..2886c9b63f90 100644 --- a/drivers/net/ethernet/emulex/benet/be_main.c +++ b/drivers/net/ethernet/emulex/benet/be_main.c @@ -759,8 +759,9 @@ static struct sk_buff *be_insert_vlan_in_pkt(struct be_adapter *adapter, if (vlan_tx_tag_present(skb)) { vlan_tag = be_get_tx_vlan_tag(adapter, skb); - __vlan_put_tag(skb, vlan_tag); - skb->vlan_tci = 0; + skb = __vlan_put_tag(skb, vlan_tag); + if (skb) + skb->vlan_tci = 0; } return skb; diff --git a/drivers/net/ethernet/freescale/fec.c b/drivers/net/ethernet/freescale/fec.c index f292c3aa423f..73195f643c9c 100644 --- a/drivers/net/ethernet/freescale/fec.c +++ b/drivers/net/ethernet/freescale/fec.c @@ -1002,6 +1002,7 @@ static void fec_enet_adjust_link(struct net_device *ndev) } else { if (fep->link) { fec_stop(ndev); + fep->link = phy_dev->link; status_change = 1; } } diff --git a/drivers/net/ethernet/fujitsu/fmvj18x_cs.c b/drivers/net/ethernet/fujitsu/fmvj18x_cs.c index 2418faf2251a..ab98b77df309 100644 --- a/drivers/net/ethernet/fujitsu/fmvj18x_cs.c +++ b/drivers/net/ethernet/fujitsu/fmvj18x_cs.c @@ -705,19 +705,7 @@ static struct pcmcia_driver fmvj18x_cs_driver = { .suspend = fmvj18x_suspend, .resume = fmvj18x_resume, }; - -static int __init init_fmvj18x_cs(void) -{ - return pcmcia_register_driver(&fmvj18x_cs_driver); -} - -static void __exit exit_fmvj18x_cs(void) -{ - pcmcia_unregister_driver(&fmvj18x_cs_driver); -} - -module_init(init_fmvj18x_cs); -module_exit(exit_fmvj18x_cs); +module_pcmcia_driver(fmvj18x_cs_driver); /*====================================================================*/ diff --git a/drivers/net/ethernet/intel/igb/igb.h b/drivers/net/ethernet/intel/igb/igb.h index 25151401c2ab..ab577a763a20 100644 --- a/drivers/net/ethernet/intel/igb/igb.h +++ b/drivers/net/ethernet/intel/igb/igb.h @@ -284,18 +284,10 @@ struct igb_q_vector { enum e1000_ring_flags_t { IGB_RING_FLAG_RX_SCTP_CSUM, IGB_RING_FLAG_RX_LB_VLAN_BSWAP, - IGB_RING_FLAG_RX_BUILD_SKB_ENABLED, IGB_RING_FLAG_TX_CTX_IDX, IGB_RING_FLAG_TX_DETECT_HANG }; -#define ring_uses_build_skb(ring) \ - test_bit(IGB_RING_FLAG_RX_BUILD_SKB_ENABLED, &(ring)->flags) -#define set_ring_build_skb_enabled(ring) \ - set_bit(IGB_RING_FLAG_RX_BUILD_SKB_ENABLED, &(ring)->flags) -#define clear_ring_build_skb_enabled(ring) \ - clear_bit(IGB_RING_FLAG_RX_BUILD_SKB_ENABLED, &(ring)->flags) - #define IGB_TXD_DCMD (E1000_ADVTXD_DCMD_EOP | E1000_ADVTXD_DCMD_RS) #define IGB_RX_DESC(R, i) \ diff --git a/drivers/net/ethernet/intel/igb/igb_main.c b/drivers/net/ethernet/intel/igb/igb_main.c index 8496adfc6a68..64f75291e3a5 100644 --- a/drivers/net/ethernet/intel/igb/igb_main.c +++ b/drivers/net/ethernet/intel/igb/igb_main.c @@ -3350,20 +3350,6 @@ void igb_configure_rx_ring(struct igb_adapter *adapter, wr32(E1000_RXDCTL(reg_idx), rxdctl); } -static void igb_set_rx_buffer_len(struct igb_adapter *adapter, - struct igb_ring *rx_ring) -{ -#define IGB_MAX_BUILD_SKB_SIZE \ - (SKB_WITH_OVERHEAD(IGB_RX_BUFSZ) - \ - (NET_SKB_PAD + NET_IP_ALIGN + IGB_TS_HDR_LEN)) - - /* set build_skb flag */ - if (adapter->max_frame_size <= IGB_MAX_BUILD_SKB_SIZE) - set_ring_build_skb_enabled(rx_ring); - else - clear_ring_build_skb_enabled(rx_ring); -} - /** * igb_configure_rx - Configure receive Unit after Reset * @adapter: board private structure @@ -3383,11 +3369,8 @@ static void igb_configure_rx(struct igb_adapter *adapter) /* Setup the HW Rx Head and Tail Descriptor Pointers and * the Base and Length of the Rx Descriptor Ring */ - for (i = 0; i < adapter->num_rx_queues; i++) { - struct igb_ring *rx_ring = adapter->rx_ring[i]; - igb_set_rx_buffer_len(adapter, rx_ring); - igb_configure_rx_ring(adapter, rx_ring); - } + for (i = 0; i < adapter->num_rx_queues; i++) + igb_configure_rx_ring(adapter, adapter->rx_ring[i]); } /** @@ -6203,78 +6186,6 @@ static bool igb_add_rx_frag(struct igb_ring *rx_ring, return igb_can_reuse_rx_page(rx_buffer, page, truesize); } -static struct sk_buff *igb_build_rx_buffer(struct igb_ring *rx_ring, - union e1000_adv_rx_desc *rx_desc) -{ - struct igb_rx_buffer *rx_buffer; - struct sk_buff *skb; - struct page *page; - void *page_addr; - unsigned int size = le16_to_cpu(rx_desc->wb.upper.length); -#if (PAGE_SIZE < 8192) - unsigned int truesize = IGB_RX_BUFSZ; -#else - unsigned int truesize = SKB_DATA_ALIGN(sizeof(struct skb_shared_info)) + - SKB_DATA_ALIGN(NET_SKB_PAD + - NET_IP_ALIGN + - size); -#endif - - /* If we spanned a buffer we have a huge mess so test for it */ - BUG_ON(unlikely(!igb_test_staterr(rx_desc, E1000_RXD_STAT_EOP))); - - rx_buffer = &rx_ring->rx_buffer_info[rx_ring->next_to_clean]; - page = rx_buffer->page; - prefetchw(page); - - page_addr = page_address(page) + rx_buffer->page_offset; - - /* prefetch first cache line of first page */ - prefetch(page_addr + NET_SKB_PAD + NET_IP_ALIGN); -#if L1_CACHE_BYTES < 128 - prefetch(page_addr + L1_CACHE_BYTES + NET_SKB_PAD + NET_IP_ALIGN); -#endif - - /* build an skb to around the page buffer */ - skb = build_skb(page_addr, truesize); - if (unlikely(!skb)) { - rx_ring->rx_stats.alloc_failed++; - return NULL; - } - - /* we are reusing so sync this buffer for CPU use */ - dma_sync_single_range_for_cpu(rx_ring->dev, - rx_buffer->dma, - rx_buffer->page_offset, - IGB_RX_BUFSZ, - DMA_FROM_DEVICE); - - /* update pointers within the skb to store the data */ - skb_reserve(skb, NET_IP_ALIGN + NET_SKB_PAD); - __skb_put(skb, size); - - /* pull timestamp out of packet data */ - if (igb_test_staterr(rx_desc, E1000_RXDADV_STAT_TSIP)) { - igb_ptp_rx_pktstamp(rx_ring->q_vector, skb->data, skb); - __skb_pull(skb, IGB_TS_HDR_LEN); - } - - if (igb_can_reuse_rx_page(rx_buffer, page, truesize)) { - /* hand second half of page back to the ring */ - igb_reuse_rx_page(rx_ring, rx_buffer); - } else { - /* we are not reusing the buffer so unmap it */ - dma_unmap_page(rx_ring->dev, rx_buffer->dma, - PAGE_SIZE, DMA_FROM_DEVICE); - } - - /* clear contents of buffer_info */ - rx_buffer->dma = 0; - rx_buffer->page = NULL; - - return skb; -} - static struct sk_buff *igb_fetch_rx_buffer(struct igb_ring *rx_ring, union e1000_adv_rx_desc *rx_desc, struct sk_buff *skb) @@ -6690,10 +6601,7 @@ static bool igb_clean_rx_irq(struct igb_q_vector *q_vector, const int budget) rmb(); /* retrieve a buffer from the ring */ - if (ring_uses_build_skb(rx_ring)) - skb = igb_build_rx_buffer(rx_ring, rx_desc); - else - skb = igb_fetch_rx_buffer(rx_ring, rx_desc, skb); + skb = igb_fetch_rx_buffer(rx_ring, rx_desc, skb); /* exit if we failed to retrieve a buffer */ if (!skb) @@ -6780,14 +6688,6 @@ static bool igb_alloc_mapped_page(struct igb_ring *rx_ring, return true; } -static inline unsigned int igb_rx_offset(struct igb_ring *rx_ring) -{ - if (ring_uses_build_skb(rx_ring)) - return NET_SKB_PAD + NET_IP_ALIGN; - else - return 0; -} - /** * igb_alloc_rx_buffers - Replace used receive buffers; packet split * @adapter: address of board private structure @@ -6814,9 +6714,7 @@ void igb_alloc_rx_buffers(struct igb_ring *rx_ring, u16 cleaned_count) * Refresh the desc even if buffer_addrs didn't change * because each write-back erases this info. */ - rx_desc->read.pkt_addr = cpu_to_le64(bi->dma + - bi->page_offset + - igb_rx_offset(rx_ring)); + rx_desc->read.pkt_addr = cpu_to_le64(bi->dma + bi->page_offset); rx_desc++; bi++; diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_sriov.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_sriov.c index d44b4d21268c..97e33669c0b9 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_sriov.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_sriov.c @@ -1049,6 +1049,12 @@ int ixgbe_ndo_set_vf_vlan(struct net_device *netdev, int vf, u16 vlan, u8 qos) if ((vf >= adapter->num_vfs) || (vlan > 4095) || (qos > 7)) return -EINVAL; if (vlan || qos) { + if (adapter->vfinfo[vf].pf_vlan) + err = ixgbe_set_vf_vlan(adapter, false, + adapter->vfinfo[vf].pf_vlan, + vf); + if (err) + goto out; err = ixgbe_set_vf_vlan(adapter, true, vlan, vf); if (err) goto out; diff --git a/drivers/net/ethernet/marvell/Kconfig b/drivers/net/ethernet/marvell/Kconfig index edfba9370922..434e33c527df 100644 --- a/drivers/net/ethernet/marvell/Kconfig +++ b/drivers/net/ethernet/marvell/Kconfig @@ -33,6 +33,7 @@ config MV643XX_ETH config MVMDIO tristate "Marvell MDIO interface support" + select PHYLIB ---help--- This driver supports the MDIO interface found in the network interface units of the Marvell EBU SoCs (Kirkwood, Orion5x, @@ -45,7 +46,6 @@ config MVMDIO config MVNETA tristate "Marvell Armada 370/XP network interface support" depends on MACH_ARMADA_370_XP - select PHYLIB select MVMDIO ---help--- This driver supports the network interface units in the diff --git a/drivers/net/ethernet/marvell/mvneta.c b/drivers/net/ethernet/marvell/mvneta.c index 1e628ce57201..a47a097c21e1 100644 --- a/drivers/net/ethernet/marvell/mvneta.c +++ b/drivers/net/ethernet/marvell/mvneta.c @@ -374,7 +374,6 @@ static int rxq_number = 8; static int txq_number = 8; static int rxq_def; -static int txq_def; #define MVNETA_DRIVER_NAME "mvneta" #define MVNETA_DRIVER_VERSION "1.0" @@ -1475,7 +1474,8 @@ error: static int mvneta_tx(struct sk_buff *skb, struct net_device *dev) { struct mvneta_port *pp = netdev_priv(dev); - struct mvneta_tx_queue *txq = &pp->txqs[txq_def]; + u16 txq_id = skb_get_queue_mapping(skb); + struct mvneta_tx_queue *txq = &pp->txqs[txq_id]; struct mvneta_tx_desc *tx_desc; struct netdev_queue *nq; int frags = 0; @@ -1485,7 +1485,7 @@ static int mvneta_tx(struct sk_buff *skb, struct net_device *dev) goto out; frags = skb_shinfo(skb)->nr_frags + 1; - nq = netdev_get_tx_queue(dev, txq_def); + nq = netdev_get_tx_queue(dev, txq_id); /* Get a descriptor for the first part of the packet */ tx_desc = mvneta_txq_next_desc_get(txq); @@ -2689,7 +2689,7 @@ static int mvneta_probe(struct platform_device *pdev) return -EINVAL; } - dev = alloc_etherdev_mq(sizeof(struct mvneta_port), 8); + dev = alloc_etherdev_mqs(sizeof(struct mvneta_port), txq_number, rxq_number); if (!dev) return -ENOMEM; @@ -2844,4 +2844,3 @@ module_param(rxq_number, int, S_IRUGO); module_param(txq_number, int, S_IRUGO); module_param(rxq_def, int, S_IRUGO); -module_param(txq_def, int, S_IRUGO); diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.c index cd5ae8813cb3..edd63f1230f3 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.c +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.c @@ -1500,6 +1500,12 @@ int qlcnic_83xx_loopback_test(struct net_device *netdev, u8 mode) } } while ((adapter->ahw->linkup && ahw->has_link_events) != 1); + /* Make sure carrier is off and queue is stopped during loopback */ + if (netif_running(netdev)) { + netif_carrier_off(netdev); + netif_stop_queue(netdev); + } + ret = qlcnic_do_lb_test(adapter, mode); qlcnic_83xx_clear_lb_mode(adapter, mode); @@ -2780,6 +2786,7 @@ static u64 *qlcnic_83xx_fill_stats(struct qlcnic_adapter *adapter, void qlcnic_83xx_get_stats(struct qlcnic_adapter *adapter, u64 *data) { struct qlcnic_cmd_args cmd; + struct net_device *netdev = adapter->netdev; int ret = 0; qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_GET_STATISTICS); @@ -2789,7 +2796,7 @@ void qlcnic_83xx_get_stats(struct qlcnic_adapter *adapter, u64 *data) data = qlcnic_83xx_fill_stats(adapter, &cmd, data, QLC_83XX_STAT_TX, &ret); if (ret) { - dev_info(&adapter->pdev->dev, "Error getting MAC stats\n"); + netdev_err(netdev, "Error getting Tx stats\n"); goto out; } /* Get MAC stats */ @@ -2799,8 +2806,7 @@ void qlcnic_83xx_get_stats(struct qlcnic_adapter *adapter, u64 *data) data = qlcnic_83xx_fill_stats(adapter, &cmd, data, QLC_83XX_STAT_MAC, &ret); if (ret) { - dev_info(&adapter->pdev->dev, - "Error getting Rx stats\n"); + netdev_err(netdev, "Error getting MAC stats\n"); goto out; } /* Get Rx stats */ @@ -2810,8 +2816,7 @@ void qlcnic_83xx_get_stats(struct qlcnic_adapter *adapter, u64 *data) data = qlcnic_83xx_fill_stats(adapter, &cmd, data, QLC_83XX_STAT_RX, &ret); if (ret) - dev_info(&adapter->pdev->dev, - "Error getting Tx stats\n"); + netdev_err(netdev, "Error getting Rx stats\n"); out: qlcnic_free_mbx_args(&cmd); } diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_io.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_io.c index 0e630061bff3..5fa847fe388a 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_io.c +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_io.c @@ -358,8 +358,7 @@ set_flags: memcpy(&first_desc->eth_addr, skb->data, ETH_ALEN); } opcode = TX_ETHER_PKT; - if ((adapter->netdev->features & (NETIF_F_TSO | NETIF_F_TSO6)) && - skb_shinfo(skb)->gso_size > 0) { + if (skb_is_gso(skb)) { hdr_len = skb_transport_offset(skb) + tcp_hdrlen(skb); first_desc->mss = cpu_to_le16(skb_shinfo(skb)->gso_size); first_desc->total_hdr_length = hdr_len; diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sysfs.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sysfs.c index 987fb6f8adc3..5ef328af61d0 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sysfs.c +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sysfs.c @@ -200,10 +200,10 @@ beacon_err: } err = qlcnic_config_led(adapter, b_state, b_rate); - if (!err) + if (!err) { err = len; - else ahw->beacon_state = b_state; + } if (test_and_clear_bit(__QLCNIC_DIAG_RES_ALLOC, &adapter->state)) qlcnic_diag_free_res(adapter->netdev, max_sds_rings); diff --git a/drivers/net/ethernet/qlogic/qlge/qlge.h b/drivers/net/ethernet/qlogic/qlge/qlge.h index a131d7b5d2fe..7e8d68263963 100644 --- a/drivers/net/ethernet/qlogic/qlge/qlge.h +++ b/drivers/net/ethernet/qlogic/qlge/qlge.h @@ -18,7 +18,7 @@ */ #define DRV_NAME "qlge" #define DRV_STRING "QLogic 10 Gigabit PCI-E Ethernet Driver " -#define DRV_VERSION "v1.00.00.31" +#define DRV_VERSION "v1.00.00.32" #define WQ_ADDR_ALIGN 0x3 /* 4 byte alignment */ diff --git a/drivers/net/ethernet/qlogic/qlge/qlge_ethtool.c b/drivers/net/ethernet/qlogic/qlge/qlge_ethtool.c index 6f316ab23257..0780e039b271 100644 --- a/drivers/net/ethernet/qlogic/qlge/qlge_ethtool.c +++ b/drivers/net/ethernet/qlogic/qlge/qlge_ethtool.c @@ -379,13 +379,13 @@ static int ql_get_settings(struct net_device *ndev, ecmd->supported = SUPPORTED_10000baseT_Full; ecmd->advertising = ADVERTISED_10000baseT_Full; - ecmd->autoneg = AUTONEG_ENABLE; ecmd->transceiver = XCVR_EXTERNAL; if ((qdev->link_status & STS_LINK_TYPE_MASK) == STS_LINK_TYPE_10GBASET) { ecmd->supported |= (SUPPORTED_TP | SUPPORTED_Autoneg); ecmd->advertising |= (ADVERTISED_TP | ADVERTISED_Autoneg); ecmd->port = PORT_TP; + ecmd->autoneg = AUTONEG_ENABLE; } else { ecmd->supported |= SUPPORTED_FIBRE; ecmd->advertising |= ADVERTISED_FIBRE; diff --git a/drivers/net/ethernet/qlogic/qlge/qlge_main.c b/drivers/net/ethernet/qlogic/qlge/qlge_main.c index b13ab544a7eb..8033555e53c2 100644 --- a/drivers/net/ethernet/qlogic/qlge/qlge_main.c +++ b/drivers/net/ethernet/qlogic/qlge/qlge_main.c @@ -1434,11 +1434,13 @@ map_error: } /* Categorizing receive firmware frame errors */ -static void ql_categorize_rx_err(struct ql_adapter *qdev, u8 rx_err) +static void ql_categorize_rx_err(struct ql_adapter *qdev, u8 rx_err, + struct rx_ring *rx_ring) { struct nic_stats *stats = &qdev->nic_stats; stats->rx_err_count++; + rx_ring->rx_errors++; switch (rx_err & IB_MAC_IOCB_RSP_ERR_MASK) { case IB_MAC_IOCB_RSP_ERR_CODE_ERR: @@ -1474,6 +1476,12 @@ static void ql_process_mac_rx_gro_page(struct ql_adapter *qdev, struct bq_desc *lbq_desc = ql_get_curr_lchunk(qdev, rx_ring); struct napi_struct *napi = &rx_ring->napi; + /* Frame error, so drop the packet. */ + if (ib_mac_rsp->flags2 & IB_MAC_IOCB_RSP_ERR_MASK) { + ql_categorize_rx_err(qdev, ib_mac_rsp->flags2, rx_ring); + put_page(lbq_desc->p.pg_chunk.page); + return; + } napi->dev = qdev->ndev; skb = napi_get_frags(napi); @@ -1529,6 +1537,12 @@ static void ql_process_mac_rx_page(struct ql_adapter *qdev, addr = lbq_desc->p.pg_chunk.va; prefetch(addr); + /* Frame error, so drop the packet. */ + if (ib_mac_rsp->flags2 & IB_MAC_IOCB_RSP_ERR_MASK) { + ql_categorize_rx_err(qdev, ib_mac_rsp->flags2, rx_ring); + goto err_out; + } + /* The max framesize filter on this chip is set higher than * MTU since FCoE uses 2k frames. */ @@ -1614,6 +1628,13 @@ static void ql_process_mac_rx_skb(struct ql_adapter *qdev, memcpy(skb_put(new_skb, length), skb->data, length); skb = new_skb; + /* Frame error, so drop the packet. */ + if (ib_mac_rsp->flags2 & IB_MAC_IOCB_RSP_ERR_MASK) { + ql_categorize_rx_err(qdev, ib_mac_rsp->flags2, rx_ring); + dev_kfree_skb_any(skb); + return; + } + /* loopback self test for ethtool */ if (test_bit(QL_SELFTEST, &qdev->flags)) { ql_check_lb_frame(qdev, skb); @@ -1919,6 +1940,13 @@ static void ql_process_mac_split_rx_intr(struct ql_adapter *qdev, return; } + /* Frame error, so drop the packet. */ + if (ib_mac_rsp->flags2 & IB_MAC_IOCB_RSP_ERR_MASK) { + ql_categorize_rx_err(qdev, ib_mac_rsp->flags2, rx_ring); + dev_kfree_skb_any(skb); + return; + } + /* The max framesize filter on this chip is set higher than * MTU since FCoE uses 2k frames. */ @@ -2000,12 +2028,6 @@ static unsigned long ql_process_mac_rx_intr(struct ql_adapter *qdev, QL_DUMP_IB_MAC_RSP(ib_mac_rsp); - /* Frame error, so drop the packet. */ - if (ib_mac_rsp->flags2 & IB_MAC_IOCB_RSP_ERR_MASK) { - ql_categorize_rx_err(qdev, ib_mac_rsp->flags2); - return (unsigned long)length; - } - if (ib_mac_rsp->flags4 & IB_MAC_IOCB_RSP_HV) { /* The data and headers are split into * separate buffers. diff --git a/drivers/net/ethernet/smsc/smc91c92_cs.c b/drivers/net/ethernet/smsc/smc91c92_cs.c index 04393b5fef71..656d2e2ebfc9 100644 --- a/drivers/net/ethernet/smsc/smc91c92_cs.c +++ b/drivers/net/ethernet/smsc/smc91c92_cs.c @@ -2054,16 +2054,4 @@ static struct pcmcia_driver smc91c92_cs_driver = { .suspend = smc91c92_suspend, .resume = smc91c92_resume, }; - -static int __init init_smc91c92_cs(void) -{ - return pcmcia_register_driver(&smc91c92_cs_driver); -} - -static void __exit exit_smc91c92_cs(void) -{ - pcmcia_unregister_driver(&smc91c92_cs_driver); -} - -module_init(init_smc91c92_cs); -module_exit(exit_smc91c92_cs); +module_pcmcia_driver(smc91c92_cs_driver); diff --git a/drivers/net/ethernet/stmicro/stmmac/mmc_core.c b/drivers/net/ethernet/stmicro/stmmac/mmc_core.c index 0c74a702d461..50617c5a0bdb 100644 --- a/drivers/net/ethernet/stmicro/stmmac/mmc_core.c +++ b/drivers/net/ethernet/stmicro/stmmac/mmc_core.c @@ -149,6 +149,7 @@ void dwmac_mmc_intr_all_mask(void __iomem *ioaddr) { writel(MMC_DEFAULT_MASK, ioaddr + MMC_RX_INTR_MASK); writel(MMC_DEFAULT_MASK, ioaddr + MMC_TX_INTR_MASK); + writel(MMC_DEFAULT_MASK, ioaddr + MMC_RX_IPC_INTR_MASK); } /* This reads the MAC core counters (if actaully supported). diff --git a/drivers/net/ethernet/ti/cpsw.c b/drivers/net/ethernet/ti/cpsw.c index 80cad06e5eb2..4781d3d8e182 100644 --- a/drivers/net/ethernet/ti/cpsw.c +++ b/drivers/net/ethernet/ti/cpsw.c @@ -1380,7 +1380,7 @@ static int cpsw_probe_dt(struct cpsw_platform_data *data, memcpy(slave_data->mac_addr, mac_addr, ETH_ALEN); if (data->dual_emac) { - if (of_property_read_u32(node, "dual_emac_res_vlan", + if (of_property_read_u32(slave_node, "dual_emac_res_vlan", &prop)) { pr_err("Missing dual_emac_res_vlan in DT.\n"); slave_data->dual_emac_res_vlan = i+1; diff --git a/drivers/net/ethernet/xircom/xirc2ps_cs.c b/drivers/net/ethernet/xircom/xirc2ps_cs.c index 98e09d0d3ce2..1025b4e937d2 100644 --- a/drivers/net/ethernet/xircom/xirc2ps_cs.c +++ b/drivers/net/ethernet/xircom/xirc2ps_cs.c @@ -1775,21 +1775,7 @@ static struct pcmcia_driver xirc2ps_cs_driver = { .suspend = xirc2ps_suspend, .resume = xirc2ps_resume, }; - -static int __init -init_xirc2ps_cs(void) -{ - return pcmcia_register_driver(&xirc2ps_cs_driver); -} - -static void __exit -exit_xirc2ps_cs(void) -{ - pcmcia_unregister_driver(&xirc2ps_cs_driver); -} - -module_init(init_xirc2ps_cs); -module_exit(exit_xirc2ps_cs); +module_pcmcia_driver(xirc2ps_cs_driver); #ifndef MODULE static int __init setup_xirc2ps_cs(char *str) diff --git a/drivers/net/tun.c b/drivers/net/tun.c index b7c457adc0dc..729ed533bb33 100644 --- a/drivers/net/tun.c +++ b/drivers/net/tun.c @@ -1594,7 +1594,7 @@ static int tun_set_iff(struct net *net, struct file *file, struct ifreq *ifr) if (tun->flags & TUN_TAP_MQ && (tun->numqueues + tun->numdisabled > 1)) - return err; + return -EBUSY; } else { char *name; diff --git a/drivers/net/usb/cdc_mbim.c b/drivers/net/usb/cdc_mbim.c index 16c842997291..6bd91676d2cb 100644 --- a/drivers/net/usb/cdc_mbim.c +++ b/drivers/net/usb/cdc_mbim.c @@ -134,7 +134,7 @@ static struct sk_buff *cdc_mbim_tx_fixup(struct usbnet *dev, struct sk_buff *skb goto error; if (skb) { - if (skb->len <= sizeof(ETH_HLEN)) + if (skb->len <= ETH_HLEN) goto error; /* mapping VLANs to MBIM sessions: diff --git a/drivers/net/usb/qmi_wwan.c b/drivers/net/usb/qmi_wwan.c index 968d5d50751d..2a3579f67910 100644 --- a/drivers/net/usb/qmi_wwan.c +++ b/drivers/net/usb/qmi_wwan.c @@ -13,6 +13,7 @@ #include <linux/module.h> #include <linux/netdevice.h> #include <linux/ethtool.h> +#include <linux/etherdevice.h> #include <linux/mii.h> #include <linux/usb.h> #include <linux/usb/cdc.h> @@ -52,6 +53,96 @@ struct qmi_wwan_state { struct usb_interface *data; }; +/* default ethernet address used by the modem */ +static const u8 default_modem_addr[ETH_ALEN] = {0x02, 0x50, 0xf3}; + +/* Make up an ethernet header if the packet doesn't have one. + * + * A firmware bug common among several devices cause them to send raw + * IP packets under some circumstances. There is no way for the + * driver/host to know when this will happen. And even when the bug + * hits, some packets will still arrive with an intact header. + * + * The supported devices are only capably of sending IPv4, IPv6 and + * ARP packets on a point-to-point link. Any packet with an ethernet + * header will have either our address or a broadcast/multicast + * address as destination. ARP packets will always have a header. + * + * This means that this function will reliably add the appropriate + * header iff necessary, provided our hardware address does not start + * with 4 or 6. + * + * Another common firmware bug results in all packets being addressed + * to 00:a0:c6:00:00:00 despite the host address being different. + * This function will also fixup such packets. + */ +static int qmi_wwan_rx_fixup(struct usbnet *dev, struct sk_buff *skb) +{ + __be16 proto; + + /* usbnet rx_complete guarantees that skb->len is at least + * hard_header_len, so we can inspect the dest address without + * checking skb->len + */ + switch (skb->data[0] & 0xf0) { + case 0x40: + proto = htons(ETH_P_IP); + break; + case 0x60: + proto = htons(ETH_P_IPV6); + break; + case 0x00: + if (is_multicast_ether_addr(skb->data)) + return 1; + /* possibly bogus destination - rewrite just in case */ + skb_reset_mac_header(skb); + goto fix_dest; + default: + /* pass along other packets without modifications */ + return 1; + } + if (skb_headroom(skb) < ETH_HLEN) + return 0; + skb_push(skb, ETH_HLEN); + skb_reset_mac_header(skb); + eth_hdr(skb)->h_proto = proto; + memset(eth_hdr(skb)->h_source, 0, ETH_ALEN); +fix_dest: + memcpy(eth_hdr(skb)->h_dest, dev->net->dev_addr, ETH_ALEN); + return 1; +} + +/* very simplistic detection of IPv4 or IPv6 headers */ +static bool possibly_iphdr(const char *data) +{ + return (data[0] & 0xd0) == 0x40; +} + +/* disallow addresses which may be confused with IP headers */ +static int qmi_wwan_mac_addr(struct net_device *dev, void *p) +{ + int ret; + struct sockaddr *addr = p; + + ret = eth_prepare_mac_addr_change(dev, p); + if (ret < 0) + return ret; + if (possibly_iphdr(addr->sa_data)) + return -EADDRNOTAVAIL; + eth_commit_mac_addr_change(dev, p); + return 0; +} + +static const struct net_device_ops qmi_wwan_netdev_ops = { + .ndo_open = usbnet_open, + .ndo_stop = usbnet_stop, + .ndo_start_xmit = usbnet_start_xmit, + .ndo_tx_timeout = usbnet_tx_timeout, + .ndo_change_mtu = usbnet_change_mtu, + .ndo_set_mac_address = qmi_wwan_mac_addr, + .ndo_validate_addr = eth_validate_addr, +}; + /* using a counter to merge subdriver requests with our own into a combined state */ static int qmi_wwan_manage_power(struct usbnet *dev, int on) { @@ -229,6 +320,18 @@ next_desc: usb_driver_release_interface(driver, info->data); } + /* Never use the same address on both ends of the link, even + * if the buggy firmware told us to. + */ + if (!compare_ether_addr(dev->net->dev_addr, default_modem_addr)) + eth_hw_addr_random(dev->net); + + /* make MAC addr easily distinguishable from an IP header */ + if (possibly_iphdr(dev->net->dev_addr)) { + dev->net->dev_addr[0] |= 0x02; /* set local assignment bit */ + dev->net->dev_addr[0] &= 0xbf; /* clear "IP" bit */ + } + dev->net->netdev_ops = &qmi_wwan_netdev_ops; err: return status; } @@ -307,6 +410,7 @@ static const struct driver_info qmi_wwan_info = { .bind = qmi_wwan_bind, .unbind = qmi_wwan_unbind, .manage_power = qmi_wwan_manage_power, + .rx_fixup = qmi_wwan_rx_fixup, }; #define HUAWEI_VENDOR_ID 0x12D1 diff --git a/drivers/net/wireless/airo_cs.c b/drivers/net/wireless/airo_cs.c index 956024a636e6..14128fd265ac 100644 --- a/drivers/net/wireless/airo_cs.c +++ b/drivers/net/wireless/airo_cs.c @@ -180,16 +180,7 @@ static struct pcmcia_driver airo_driver = { .suspend = airo_suspend, .resume = airo_resume, }; - -static int __init airo_cs_init(void) -{ - return pcmcia_register_driver(&airo_driver); -} - -static void __exit airo_cs_cleanup(void) -{ - pcmcia_unregister_driver(&airo_driver); -} +module_pcmcia_driver(airo_driver); /* This program is free software; you can redistribute it and/or @@ -229,6 +220,3 @@ static void __exit airo_cs_cleanup(void) IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ - -module_init(airo_cs_init); -module_exit(airo_cs_cleanup); diff --git a/drivers/net/wireless/ath/ath9k/ar9580_1p0_initvals.h b/drivers/net/wireless/ath/ath9k/ar9580_1p0_initvals.h index 28fd99203f64..bdee2ed67219 100644 --- a/drivers/net/wireless/ath/ath9k/ar9580_1p0_initvals.h +++ b/drivers/net/wireless/ath/ath9k/ar9580_1p0_initvals.h @@ -519,7 +519,7 @@ static const u32 ar9580_1p0_mac_core[][2] = { {0x00008258, 0x00000000}, {0x0000825c, 0x40000000}, {0x00008260, 0x00080922}, - {0x00008264, 0x9bc00010}, + {0x00008264, 0x9d400010}, {0x00008268, 0xffffffff}, {0x0000826c, 0x0000ffff}, {0x00008270, 0x00000000}, diff --git a/drivers/net/wireless/ath/ath9k/dfs_pattern_detector.c b/drivers/net/wireless/ath/ath9k/dfs_pattern_detector.c index 467b60014b7b..73fe8d6db566 100644 --- a/drivers/net/wireless/ath/ath9k/dfs_pattern_detector.c +++ b/drivers/net/wireless/ath/ath9k/dfs_pattern_detector.c @@ -143,14 +143,14 @@ channel_detector_create(struct dfs_pattern_detector *dpd, u16 freq) u32 sz, i; struct channel_detector *cd; - cd = kmalloc(sizeof(*cd), GFP_KERNEL); + cd = kmalloc(sizeof(*cd), GFP_ATOMIC); if (cd == NULL) goto fail; INIT_LIST_HEAD(&cd->head); cd->freq = freq; sz = sizeof(cd->detectors) * dpd->num_radar_types; - cd->detectors = kzalloc(sz, GFP_KERNEL); + cd->detectors = kzalloc(sz, GFP_ATOMIC); if (cd->detectors == NULL) goto fail; diff --git a/drivers/net/wireless/ath/ath9k/dfs_pri_detector.c b/drivers/net/wireless/ath/ath9k/dfs_pri_detector.c index 91b8dceeadb1..5e48c5515b8c 100644 --- a/drivers/net/wireless/ath/ath9k/dfs_pri_detector.c +++ b/drivers/net/wireless/ath/ath9k/dfs_pri_detector.c @@ -218,7 +218,7 @@ static bool pulse_queue_enqueue(struct pri_detector *pde, u64 ts) { struct pulse_elem *p = pool_get_pulse_elem(); if (p == NULL) { - p = kmalloc(sizeof(*p), GFP_KERNEL); + p = kmalloc(sizeof(*p), GFP_ATOMIC); if (p == NULL) { DFS_POOL_STAT_INC(pulse_alloc_error); return false; @@ -299,7 +299,7 @@ static bool pseq_handler_create_sequences(struct pri_detector *pde, ps.deadline_ts = ps.first_ts + ps.dur; new_ps = pool_get_pseq_elem(); if (new_ps == NULL) { - new_ps = kmalloc(sizeof(*new_ps), GFP_KERNEL); + new_ps = kmalloc(sizeof(*new_ps), GFP_ATOMIC); if (new_ps == NULL) { DFS_POOL_STAT_INC(pseq_alloc_error); return false; diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_init.c b/drivers/net/wireless/ath/ath9k/htc_drv_init.c index 716058b67557..a47f5e05fc04 100644 --- a/drivers/net/wireless/ath/ath9k/htc_drv_init.c +++ b/drivers/net/wireless/ath/ath9k/htc_drv_init.c @@ -796,7 +796,7 @@ static int ath9k_init_firmware_version(struct ath9k_htc_priv *priv) * required version. */ if (priv->fw_version_major != MAJOR_VERSION_REQ || - priv->fw_version_minor != MINOR_VERSION_REQ) { + priv->fw_version_minor < MINOR_VERSION_REQ) { dev_err(priv->dev, "ath9k_htc: Please upgrade to FW version %d.%d\n", MAJOR_VERSION_REQ, MINOR_VERSION_REQ); return -EINVAL; diff --git a/drivers/net/wireless/atmel_cs.c b/drivers/net/wireless/atmel_cs.c index b42930f457c2..522572219217 100644 --- a/drivers/net/wireless/atmel_cs.c +++ b/drivers/net/wireless/atmel_cs.c @@ -245,16 +245,7 @@ static struct pcmcia_driver atmel_driver = { .suspend = atmel_suspend, .resume = atmel_resume, }; - -static int __init atmel_cs_init(void) -{ - return pcmcia_register_driver(&atmel_driver); -} - -static void __exit atmel_cs_cleanup(void) -{ - pcmcia_unregister_driver(&atmel_driver); -} +module_pcmcia_driver(atmel_driver); /* This program is free software; you can redistribute it and/or @@ -294,6 +285,3 @@ static void __exit atmel_cs_cleanup(void) IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ - -module_init(atmel_cs_init); -module_exit(atmel_cs_cleanup); diff --git a/drivers/net/wireless/b43/pcmcia.c b/drivers/net/wireless/b43/pcmcia.c index f2ea2ceec8a9..55f2bd7f8f74 100644 --- a/drivers/net/wireless/b43/pcmcia.c +++ b/drivers/net/wireless/b43/pcmcia.c @@ -130,6 +130,10 @@ static struct pcmcia_driver b43_pcmcia_driver = { .resume = b43_pcmcia_resume, }; +/* + * These are not module init/exit functions! + * The module_pcmcia_driver() helper cannot be used here. + */ int b43_pcmcia_init(void) { return pcmcia_register_driver(&b43_pcmcia_driver); diff --git a/drivers/net/wireless/b43/phy_n.c b/drivers/net/wireless/b43/phy_n.c index e8486c1e091a..b70f220bc4b3 100644 --- a/drivers/net/wireless/b43/phy_n.c +++ b/drivers/net/wireless/b43/phy_n.c @@ -5165,7 +5165,8 @@ static void b43_nphy_pmu_spur_avoid(struct b43_wldev *dev, bool avoid) #endif #ifdef CONFIG_B43_SSB case B43_BUS_SSB: - /* FIXME */ + ssb_pmu_spuravoid_pllupdate(&dev->dev->sdev->bus->chipco, + avoid); break; #endif } diff --git a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c index ec46ffff5409..78da3eff75e8 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c @@ -4128,10 +4128,6 @@ static const struct ieee80211_iface_limit brcmf_iface_limits[] = { }, { .max = 1, - .types = BIT(NL80211_IFTYPE_P2P_DEVICE) - }, - { - .max = 1, .types = BIT(NL80211_IFTYPE_P2P_CLIENT) | BIT(NL80211_IFTYPE_P2P_GO) }, @@ -4187,8 +4183,7 @@ static struct wiphy *brcmf_setup_wiphy(struct device *phydev) BIT(NL80211_IFTYPE_ADHOC) | BIT(NL80211_IFTYPE_AP) | BIT(NL80211_IFTYPE_P2P_CLIENT) | - BIT(NL80211_IFTYPE_P2P_GO) | - BIT(NL80211_IFTYPE_P2P_DEVICE); + BIT(NL80211_IFTYPE_P2P_GO); wiphy->iface_combinations = brcmf_iface_combos; wiphy->n_iface_combinations = ARRAY_SIZE(brcmf_iface_combos); wiphy->bands[IEEE80211_BAND_2GHZ] = &__wl_band_2ghz; diff --git a/drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.c b/drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.c index c6451c61407a..e2340b231aa1 100644 --- a/drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.c +++ b/drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.c @@ -274,6 +274,130 @@ static void brcms_set_basic_rate(struct brcm_rateset *rs, u16 rate, bool is_br) } } +/** + * This function frees the WL per-device resources. + * + * This function frees resources owned by the WL device pointed to + * by the wl parameter. + * + * precondition: can both be called locked and unlocked + * + */ +static void brcms_free(struct brcms_info *wl) +{ + struct brcms_timer *t, *next; + + /* free ucode data */ + if (wl->fw.fw_cnt) + brcms_ucode_data_free(&wl->ucode); + if (wl->irq) + free_irq(wl->irq, wl); + + /* kill dpc */ + tasklet_kill(&wl->tasklet); + + if (wl->pub) { + brcms_debugfs_detach(wl->pub); + brcms_c_module_unregister(wl->pub, "linux", wl); + } + + /* free common resources */ + if (wl->wlc) { + brcms_c_detach(wl->wlc); + wl->wlc = NULL; + wl->pub = NULL; + } + + /* virtual interface deletion is deferred so we cannot spinwait */ + + /* wait for all pending callbacks to complete */ + while (atomic_read(&wl->callbacks) > 0) + schedule(); + + /* free timers */ + for (t = wl->timers; t; t = next) { + next = t->next; +#ifdef DEBUG + kfree(t->name); +#endif + kfree(t); + } +} + +/* +* called from both kernel as from this kernel module (error flow on attach) +* precondition: perimeter lock is not acquired. +*/ +static void brcms_remove(struct bcma_device *pdev) +{ + struct ieee80211_hw *hw = bcma_get_drvdata(pdev); + struct brcms_info *wl = hw->priv; + + if (wl->wlc) { + wiphy_rfkill_set_hw_state(wl->pub->ieee_hw->wiphy, false); + wiphy_rfkill_stop_polling(wl->pub->ieee_hw->wiphy); + ieee80211_unregister_hw(hw); + } + + brcms_free(wl); + + bcma_set_drvdata(pdev, NULL); + ieee80211_free_hw(hw); +} + +/* + * Precondition: Since this function is called in brcms_pci_probe() context, + * no locking is required. + */ +static void brcms_release_fw(struct brcms_info *wl) +{ + int i; + for (i = 0; i < MAX_FW_IMAGES; i++) { + release_firmware(wl->fw.fw_bin[i]); + release_firmware(wl->fw.fw_hdr[i]); + } +} + +/* + * Precondition: Since this function is called in brcms_pci_probe() context, + * no locking is required. + */ +static int brcms_request_fw(struct brcms_info *wl, struct bcma_device *pdev) +{ + int status; + struct device *device = &pdev->dev; + char fw_name[100]; + int i; + + memset(&wl->fw, 0, sizeof(struct brcms_firmware)); + for (i = 0; i < MAX_FW_IMAGES; i++) { + if (brcms_firmwares[i] == NULL) + break; + sprintf(fw_name, "%s-%d.fw", brcms_firmwares[i], + UCODE_LOADER_API_VER); + status = request_firmware(&wl->fw.fw_bin[i], fw_name, device); + if (status) { + wiphy_err(wl->wiphy, "%s: fail to load firmware %s\n", + KBUILD_MODNAME, fw_name); + return status; + } + sprintf(fw_name, "%s_hdr-%d.fw", brcms_firmwares[i], + UCODE_LOADER_API_VER); + status = request_firmware(&wl->fw.fw_hdr[i], fw_name, device); + if (status) { + wiphy_err(wl->wiphy, "%s: fail to load firmware %s\n", + KBUILD_MODNAME, fw_name); + return status; + } + wl->fw.hdr_num_entries[i] = + wl->fw.fw_hdr[i]->size / (sizeof(struct firmware_hdr)); + } + wl->fw.fw_cnt = i; + status = brcms_ucode_data_init(wl, &wl->ucode); + brcms_release_fw(wl); + return status; +} + static void brcms_ops_tx(struct ieee80211_hw *hw, struct ieee80211_tx_control *control, struct sk_buff *skb) @@ -306,6 +430,14 @@ static int brcms_ops_start(struct ieee80211_hw *hw) if (!blocked) wiphy_rfkill_stop_polling(wl->pub->ieee_hw->wiphy); + if (!wl->ucode.bcm43xx_bomminor) { + err = brcms_request_fw(wl, wl->wlc->hw->d11core); + if (err) { + brcms_remove(wl->wlc->hw->d11core); + return -ENOENT; + } + } + spin_lock_bh(&wl->lock); /* avoid acknowledging frames before a non-monitor device is added */ wl->mute_tx = true; @@ -793,128 +925,6 @@ void brcms_dpc(unsigned long data) wake_up(&wl->tx_flush_wq); } -/* - * Precondition: Since this function is called in brcms_pci_probe() context, - * no locking is required. - */ -static int brcms_request_fw(struct brcms_info *wl, struct bcma_device *pdev) -{ - int status; - struct device *device = &pdev->dev; - char fw_name[100]; - int i; - - memset(&wl->fw, 0, sizeof(struct brcms_firmware)); - for (i = 0; i < MAX_FW_IMAGES; i++) { - if (brcms_firmwares[i] == NULL) - break; - sprintf(fw_name, "%s-%d.fw", brcms_firmwares[i], - UCODE_LOADER_API_VER); - status = request_firmware(&wl->fw.fw_bin[i], fw_name, device); - if (status) { - wiphy_err(wl->wiphy, "%s: fail to load firmware %s\n", - KBUILD_MODNAME, fw_name); - return status; - } - sprintf(fw_name, "%s_hdr-%d.fw", brcms_firmwares[i], - UCODE_LOADER_API_VER); - status = request_firmware(&wl->fw.fw_hdr[i], fw_name, device); - if (status) { - wiphy_err(wl->wiphy, "%s: fail to load firmware %s\n", - KBUILD_MODNAME, fw_name); - return status; - } - wl->fw.hdr_num_entries[i] = - wl->fw.fw_hdr[i]->size / (sizeof(struct firmware_hdr)); - } - wl->fw.fw_cnt = i; - return brcms_ucode_data_init(wl, &wl->ucode); -} - -/* - * Precondition: Since this function is called in brcms_pci_probe() context, - * no locking is required. - */ -static void brcms_release_fw(struct brcms_info *wl) -{ - int i; - for (i = 0; i < MAX_FW_IMAGES; i++) { - release_firmware(wl->fw.fw_bin[i]); - release_firmware(wl->fw.fw_hdr[i]); - } -} - -/** - * This function frees the WL per-device resources. - * - * This function frees resources owned by the WL device pointed to - * by the wl parameter. - * - * precondition: can both be called locked and unlocked - * - */ -static void brcms_free(struct brcms_info *wl) -{ - struct brcms_timer *t, *next; - - /* free ucode data */ - if (wl->fw.fw_cnt) - brcms_ucode_data_free(&wl->ucode); - if (wl->irq) - free_irq(wl->irq, wl); - - /* kill dpc */ - tasklet_kill(&wl->tasklet); - - if (wl->pub) { - brcms_debugfs_detach(wl->pub); - brcms_c_module_unregister(wl->pub, "linux", wl); - } - - /* free common resources */ - if (wl->wlc) { - brcms_c_detach(wl->wlc); - wl->wlc = NULL; - wl->pub = NULL; - } - - /* virtual interface deletion is deferred so we cannot spinwait */ - - /* wait for all pending callbacks to complete */ - while (atomic_read(&wl->callbacks) > 0) - schedule(); - - /* free timers */ - for (t = wl->timers; t; t = next) { - next = t->next; -#ifdef DEBUG - kfree(t->name); -#endif - kfree(t); - } -} - -/* -* called from both kernel as from this kernel module (error flow on attach) -* precondition: perimeter lock is not acquired. -*/ -static void brcms_remove(struct bcma_device *pdev) -{ - struct ieee80211_hw *hw = bcma_get_drvdata(pdev); - struct brcms_info *wl = hw->priv; - - if (wl->wlc) { - wiphy_rfkill_set_hw_state(wl->pub->ieee_hw->wiphy, false); - wiphy_rfkill_stop_polling(wl->pub->ieee_hw->wiphy); - ieee80211_unregister_hw(hw); - } - - brcms_free(wl); - - bcma_set_drvdata(pdev, NULL); - ieee80211_free_hw(hw); -} - static irqreturn_t brcms_isr(int irq, void *dev_id) { struct brcms_info *wl; @@ -1047,18 +1057,8 @@ static struct brcms_info *brcms_attach(struct bcma_device *pdev) spin_lock_init(&wl->lock); spin_lock_init(&wl->isr_lock); - /* prepare ucode */ - if (brcms_request_fw(wl, pdev) < 0) { - wiphy_err(wl->wiphy, "%s: Failed to find firmware usually in " - "%s\n", KBUILD_MODNAME, "/lib/firmware/brcm"); - brcms_release_fw(wl); - brcms_remove(pdev); - return NULL; - } - /* common load-time initialization */ wl->wlc = brcms_c_attach((void *)wl, pdev, unit, false, &err); - brcms_release_fw(wl); if (!wl->wlc) { wiphy_err(wl->wiphy, "%s: attach() failed with code %d\n", KBUILD_MODNAME, err); diff --git a/drivers/net/wireless/hostap/hostap_cs.c b/drivers/net/wireless/hostap/hostap_cs.c index 89e9d3a78c3c..56cd01ca8ad0 100644 --- a/drivers/net/wireless/hostap/hostap_cs.c +++ b/drivers/net/wireless/hostap/hostap_cs.c @@ -709,17 +709,4 @@ static struct pcmcia_driver hostap_driver = { .suspend = hostap_cs_suspend, .resume = hostap_cs_resume, }; - -static int __init init_prism2_pccard(void) -{ - return pcmcia_register_driver(&hostap_driver); -} - -static void __exit exit_prism2_pccard(void) -{ - pcmcia_unregister_driver(&hostap_driver); -} - - -module_init(init_prism2_pccard); -module_exit(exit_prism2_pccard); +module_pcmcia_driver(hostap_driver); diff --git a/drivers/net/wireless/libertas/if_cs.c b/drivers/net/wireless/libertas/if_cs.c index 16beaf39dc53..c94dd6802672 100644 --- a/drivers/net/wireless/libertas/if_cs.c +++ b/drivers/net/wireless/libertas/if_cs.c @@ -999,7 +999,6 @@ static const struct pcmcia_device_id if_cs_ids[] = { }; MODULE_DEVICE_TABLE(pcmcia, if_cs_ids); - static struct pcmcia_driver lbs_driver = { .owner = THIS_MODULE, .name = DRV_NAME, @@ -1007,26 +1006,4 @@ static struct pcmcia_driver lbs_driver = { .remove = if_cs_detach, .id_table = if_cs_ids, }; - - -static int __init if_cs_init(void) -{ - int ret; - - lbs_deb_enter(LBS_DEB_CS); - ret = pcmcia_register_driver(&lbs_driver); - lbs_deb_leave(LBS_DEB_CS); - return ret; -} - - -static void __exit if_cs_exit(void) -{ - lbs_deb_enter(LBS_DEB_CS); - pcmcia_unregister_driver(&lbs_driver); - lbs_deb_leave(LBS_DEB_CS); -} - - -module_init(if_cs_init); -module_exit(if_cs_exit); +module_pcmcia_driver(lbs_driver); diff --git a/drivers/net/wireless/orinoco/orinoco_cs.c b/drivers/net/wireless/orinoco/orinoco_cs.c index d7dbc00bcfbe..d21d95939316 100644 --- a/drivers/net/wireless/orinoco/orinoco_cs.c +++ b/drivers/net/wireless/orinoco/orinoco_cs.c @@ -338,18 +338,4 @@ static struct pcmcia_driver orinoco_driver = { .suspend = orinoco_cs_suspend, .resume = orinoco_cs_resume, }; - -static int __init -init_orinoco_cs(void) -{ - return pcmcia_register_driver(&orinoco_driver); -} - -static void __exit -exit_orinoco_cs(void) -{ - pcmcia_unregister_driver(&orinoco_driver); -} - -module_init(init_orinoco_cs); -module_exit(exit_orinoco_cs); +module_pcmcia_driver(orinoco_driver); diff --git a/drivers/net/wireless/orinoco/spectrum_cs.c b/drivers/net/wireless/orinoco/spectrum_cs.c index 6e28ee4e9c52..e2264bc12ebf 100644 --- a/drivers/net/wireless/orinoco/spectrum_cs.c +++ b/drivers/net/wireless/orinoco/spectrum_cs.c @@ -318,18 +318,4 @@ static struct pcmcia_driver orinoco_driver = { .resume = spectrum_cs_resume, .id_table = spectrum_cs_ids, }; - -static int __init -init_spectrum_cs(void) -{ - return pcmcia_register_driver(&orinoco_driver); -} - -static void __exit -exit_spectrum_cs(void) -{ - pcmcia_unregister_driver(&orinoco_driver); -} - -module_init(init_spectrum_cs); -module_exit(exit_spectrum_cs); +module_pcmcia_driver(orinoco_driver); diff --git a/drivers/net/wireless/wl3501_cs.c b/drivers/net/wireless/wl3501_cs.c index 730186d0449b..38d2089f338a 100644 --- a/drivers/net/wireless/wl3501_cs.c +++ b/drivers/net/wireless/wl3501_cs.c @@ -2013,19 +2013,7 @@ static struct pcmcia_driver wl3501_driver = { .suspend = wl3501_suspend, .resume = wl3501_resume, }; - -static int __init wl3501_init_module(void) -{ - return pcmcia_register_driver(&wl3501_driver); -} - -static void __exit wl3501_exit_module(void) -{ - pcmcia_unregister_driver(&wl3501_driver); -} - -module_init(wl3501_init_module); -module_exit(wl3501_exit_module); +module_pcmcia_driver(wl3501_driver); MODULE_AUTHOR("Fox Chen <mhchen@golf.ccl.itri.org.tw>, " "Arnaldo Carvalho de Melo <acme@conectiva.com.br>," diff --git a/drivers/parport/parport_amiga.c b/drivers/parport/parport_amiga.c index ee78e0ee6e05..09503b8d12e6 100644 --- a/drivers/parport/parport_amiga.c +++ b/drivers/parport/parport_amiga.c @@ -244,20 +244,7 @@ static struct platform_driver amiga_parallel_driver = { }, }; -static int __init amiga_parallel_init(void) -{ - return platform_driver_probe(&amiga_parallel_driver, - amiga_parallel_probe); -} - -module_init(amiga_parallel_init); - -static void __exit amiga_parallel_exit(void) -{ - platform_driver_unregister(&amiga_parallel_driver); -} - -module_exit(amiga_parallel_exit); +module_platform_driver_probe(amiga_parallel_driver, amiga_parallel_probe); MODULE_AUTHOR("Joerg Dorchain <joerg@dorchain.net>"); MODULE_DESCRIPTION("Parport Driver for Amiga builtin Port"); diff --git a/drivers/parport/parport_cs.c b/drivers/parport/parport_cs.c index 067ad517c1f5..e9b52e4a4648 100644 --- a/drivers/parport/parport_cs.c +++ b/drivers/parport/parport_cs.c @@ -193,16 +193,4 @@ static struct pcmcia_driver parport_cs_driver = { .remove = parport_detach, .id_table = parport_ids, }; - -static int __init init_parport_cs(void) -{ - return pcmcia_register_driver(&parport_cs_driver); -} - -static void __exit exit_parport_cs(void) -{ - pcmcia_unregister_driver(&parport_cs_driver); -} - -module_init(init_parport_cs); -module_exit(exit_parport_cs); +module_pcmcia_driver(parport_cs_driver); diff --git a/drivers/parport/parport_gsc.c b/drivers/parport/parport_gsc.c index 050773c36823..a5251cb5fb0c 100644 --- a/drivers/parport/parport_gsc.c +++ b/drivers/parport/parport_gsc.c @@ -246,14 +246,14 @@ struct parport *parport_gsc_probe_port(unsigned long base, printk (KERN_DEBUG "parport (0x%lx): no memory!\n", base); return NULL; } - ops = kmalloc (sizeof (struct parport_operations), GFP_KERNEL); + ops = kmemdup(&parport_gsc_ops, sizeof(struct parport_operations), + GFP_KERNEL); if (!ops) { printk (KERN_DEBUG "parport (0x%lx): no memory for ops!\n", base); kfree (priv); return NULL; } - memcpy (ops, &parport_gsc_ops, sizeof (struct parport_operations)); priv->ctr = 0xc; priv->ctr_writable = 0xff; priv->dma_buf = 0; diff --git a/drivers/parport/parport_sunbpp.c b/drivers/parport/parport_sunbpp.c index 5c4b6a1db6ca..dffd6d0bd15b 100644 --- a/drivers/parport/parport_sunbpp.c +++ b/drivers/parport/parport_sunbpp.c @@ -284,12 +284,11 @@ static int bpp_probe(struct platform_device *op) size = resource_size(&op->resource[0]); dma = PARPORT_DMA_NONE; - ops = kmalloc(sizeof(struct parport_operations), GFP_KERNEL); + ops = kmemdup(&parport_sunbpp_ops, sizeof(struct parport_operations), + GFP_KERNEL); if (!ops) goto out_unmap; - memcpy (ops, &parport_sunbpp_ops, sizeof(struct parport_operations)); - dprintk(("register_port\n")); if (!(p = parport_register_port((unsigned long)base, irq, dma, ops))) goto out_free_ops; diff --git a/drivers/parport/procfs.c b/drivers/parport/procfs.c index 3f56bc086cb5..92ed045a5f93 100644 --- a/drivers/parport/procfs.c +++ b/drivers/parport/procfs.c @@ -476,10 +476,9 @@ int parport_proc_register(struct parport *port) struct parport_sysctl_table *t; int i; - t = kmalloc(sizeof(*t), GFP_KERNEL); + t = kmemdup(&parport_sysctl_template, sizeof(*t), GFP_KERNEL); if (t == NULL) return -ENOMEM; - memcpy(t, &parport_sysctl_template, sizeof(*t)); t->device_dir[0].extra1 = port; @@ -523,10 +522,9 @@ int parport_device_proc_register(struct pardevice *device) struct parport_device_sysctl_table *t; struct parport * port = device->port; - t = kmalloc(sizeof(*t), GFP_KERNEL); + t = kmemdup(&parport_device_sysctl_template, sizeof(*t), GFP_KERNEL); if (t == NULL) return -ENOMEM; - memcpy(t, &parport_device_sysctl_template, sizeof(*t)); t->dev_dir[0].child = t->parport_dir; t->parport_dir[0].child = t->port_dir; diff --git a/drivers/pci/bus.c b/drivers/pci/bus.c index 8647dc6f52d0..748f8f3e9ff5 100644 --- a/drivers/pci/bus.c +++ b/drivers/pci/bus.c @@ -202,20 +202,16 @@ void pci_bus_add_devices(const struct pci_bus *bus) if (dev->is_added) continue; retval = pci_bus_add_device(dev); + if (retval) + dev_err(&dev->dev, "Error adding device (%d)\n", + retval); } list_for_each_entry(dev, &bus->devices, bus_list) { BUG_ON(!dev->is_added); - child = dev->subordinate; - - if (!child) - continue; - pci_bus_add_devices(child); - - if (child->is_added) - continue; - child->is_added = 1; + if (child) + pci_bus_add_devices(child); } } diff --git a/drivers/pci/hotplug/Kconfig b/drivers/pci/hotplug/Kconfig index 13e9e63a7266..9fcb87f353d4 100644 --- a/drivers/pci/hotplug/Kconfig +++ b/drivers/pci/hotplug/Kconfig @@ -52,15 +52,12 @@ config HOTPLUG_PCI_IBM When in doubt, say N. config HOTPLUG_PCI_ACPI - tristate "ACPI PCI Hotplug driver" - depends on (!ACPI_DOCK && ACPI) || (ACPI_DOCK) + bool "ACPI PCI Hotplug driver" + depends on HOTPLUG_PCI=y && ((!ACPI_DOCK && ACPI) || (ACPI_DOCK)) help Say Y here if you have a system that supports PCI Hotplug using ACPI. - To compile this driver as a module, choose M here: the - module will be called acpiphp. - When in doubt, say N. config HOTPLUG_PCI_ACPI_IBM diff --git a/drivers/pci/hotplug/acpiphp.h b/drivers/pci/hotplug/acpiphp.h index b70ac00a117e..6fdd49c6f0b9 100644 --- a/drivers/pci/hotplug/acpiphp.h +++ b/drivers/pci/hotplug/acpiphp.h @@ -73,8 +73,9 @@ static inline const char *slot_name(struct slot *slot) */ struct acpiphp_bridge { struct list_head list; + struct list_head slots; + struct kref ref; acpi_handle handle; - struct acpiphp_slot *slots; /* Ejectable PCI-to-PCI bridge (PCI bridge and PCI function) */ struct acpiphp_func *func; @@ -97,7 +98,7 @@ struct acpiphp_bridge { * PCI slot information for each *physical* PCI slot */ struct acpiphp_slot { - struct acpiphp_slot *next; + struct list_head node; struct acpiphp_bridge *bridge; /* parent */ struct list_head funcs; /* one slot may have different objects (i.e. for each function) */ @@ -119,7 +120,6 @@ struct acpiphp_slot { */ struct acpiphp_func { struct acpiphp_slot *slot; /* parent */ - struct acpiphp_bridge *bridge; /* Ejectable PCI-to-PCI bridge */ struct list_head sibling; struct notifier_block nb; @@ -146,10 +146,6 @@ struct acpiphp_attention_info #define ACPI_PCI_HOST_HID "PNP0A03" /* ACPI _STA method value (ignore bit 4; battery present) */ -#define ACPI_STA_PRESENT (0x00000001) -#define ACPI_STA_ENABLED (0x00000002) -#define ACPI_STA_SHOW_IN_UI (0x00000004) -#define ACPI_STA_FUNCTIONING (0x00000008) #define ACPI_STA_ALL (0x0000000f) /* bridge flags */ @@ -174,25 +170,24 @@ struct acpiphp_attention_info /* function prototypes */ /* acpiphp_core.c */ -extern int acpiphp_register_attention(struct acpiphp_attention_info*info); -extern int acpiphp_unregister_attention(struct acpiphp_attention_info *info); -extern int acpiphp_register_hotplug_slot(struct acpiphp_slot *slot); -extern void acpiphp_unregister_hotplug_slot(struct acpiphp_slot *slot); +int acpiphp_register_attention(struct acpiphp_attention_info*info); +int acpiphp_unregister_attention(struct acpiphp_attention_info *info); +int acpiphp_register_hotplug_slot(struct acpiphp_slot *slot); +void acpiphp_unregister_hotplug_slot(struct acpiphp_slot *slot); /* acpiphp_glue.c */ -extern int acpiphp_glue_init (void); -extern void acpiphp_glue_exit (void); typedef int (*acpiphp_callback)(struct acpiphp_slot *slot, void *data); -extern int acpiphp_enable_slot (struct acpiphp_slot *slot); -extern int acpiphp_disable_slot (struct acpiphp_slot *slot); -extern int acpiphp_eject_slot (struct acpiphp_slot *slot); -extern u8 acpiphp_get_power_status (struct acpiphp_slot *slot); -extern u8 acpiphp_get_attention_status (struct acpiphp_slot *slot); -extern u8 acpiphp_get_latch_status (struct acpiphp_slot *slot); -extern u8 acpiphp_get_adapter_status (struct acpiphp_slot *slot); +int acpiphp_enable_slot(struct acpiphp_slot *slot); +int acpiphp_disable_slot(struct acpiphp_slot *slot); +int acpiphp_eject_slot(struct acpiphp_slot *slot); +u8 acpiphp_get_power_status(struct acpiphp_slot *slot); +u8 acpiphp_get_attention_status(struct acpiphp_slot *slot); +u8 acpiphp_get_latch_status(struct acpiphp_slot *slot); +u8 acpiphp_get_adapter_status(struct acpiphp_slot *slot); /* variables */ extern bool acpiphp_debug; +extern bool acpiphp_disabled; #endif /* _ACPIPHP_H */ diff --git a/drivers/pci/hotplug/acpiphp_core.c b/drivers/pci/hotplug/acpiphp_core.c index c2fd3095701f..ca8127950fcd 100644 --- a/drivers/pci/hotplug/acpiphp_core.c +++ b/drivers/pci/hotplug/acpiphp_core.c @@ -37,6 +37,7 @@ #include <linux/kernel.h> #include <linux/pci.h> +#include <linux/pci-acpi.h> #include <linux/pci_hotplug.h> #include <linux/slab.h> #include <linux/smp.h> @@ -48,6 +49,7 @@ #define SLOT_NAME_SIZE 21 /* {_SUN} */ bool acpiphp_debug; +bool acpiphp_disabled; /* local variables */ static struct acpiphp_attention_info *attention_info; @@ -60,7 +62,9 @@ MODULE_AUTHOR(DRIVER_AUTHOR); MODULE_DESCRIPTION(DRIVER_DESC); MODULE_LICENSE("GPL"); MODULE_PARM_DESC(debug, "Debugging mode enabled or not"); +MODULE_PARM_DESC(disable, "disable acpiphp driver"); module_param_named(debug, acpiphp_debug, bool, 0644); +module_param_named(disable, acpiphp_disabled, bool, 0444); /* export the attention callback registration methods */ EXPORT_SYMBOL_GPL(acpiphp_register_attention); @@ -351,27 +355,9 @@ void acpiphp_unregister_hotplug_slot(struct acpiphp_slot *acpiphp_slot) } -static int __init acpiphp_init(void) +void __init acpiphp_init(void) { - info(DRIVER_DESC " version: " DRIVER_VERSION "\n"); - - if (acpi_pci_disabled) - return 0; - - /* read all the ACPI info from the system */ - /* initialize internal data structure etc. */ - return acpiphp_glue_init(); -} - - -static void __exit acpiphp_exit(void) -{ - if (acpi_pci_disabled) - return; - - /* deallocate internal data structures etc. */ - acpiphp_glue_exit(); + info(DRIVER_DESC " version: " DRIVER_VERSION "%s\n", + acpiphp_disabled ? ", disabled by user; please report a bug" + : ""); } - -module_init(acpiphp_init); -module_exit(acpiphp_exit); diff --git a/drivers/pci/hotplug/acpiphp_glue.c b/drivers/pci/hotplug/acpiphp_glue.c index 270fdbadc19c..96fed19c6d90 100644 --- a/drivers/pci/hotplug/acpiphp_glue.c +++ b/drivers/pci/hotplug/acpiphp_glue.c @@ -54,6 +54,7 @@ #include "acpiphp.h" static LIST_HEAD(bridge_list); +static DEFINE_MUTEX(bridge_mutex); #define MY_NAME "acpiphp_glue" @@ -61,6 +62,7 @@ static void handle_hotplug_event_bridge (acpi_handle, u32, void *); static void acpiphp_sanitize_bus(struct pci_bus *bus); static void acpiphp_set_hpp_values(struct pci_bus *bus); static void handle_hotplug_event_func(acpi_handle handle, u32 type, void *context); +static void free_bridge(struct kref *kref); /* callback routine to check for the existence of a pci dock device */ static acpi_status @@ -76,6 +78,39 @@ is_pci_dock_device(acpi_handle handle, u32 lvl, void *context, void **rv) } } +static inline void get_bridge(struct acpiphp_bridge *bridge) +{ + kref_get(&bridge->ref); +} + +static inline void put_bridge(struct acpiphp_bridge *bridge) +{ + kref_put(&bridge->ref, free_bridge); +} + +static void free_bridge(struct kref *kref) +{ + struct acpiphp_bridge *bridge; + struct acpiphp_slot *slot, *next; + struct acpiphp_func *func, *tmp; + + bridge = container_of(kref, struct acpiphp_bridge, ref); + + list_for_each_entry_safe(slot, next, &bridge->slots, node) { + list_for_each_entry_safe(func, tmp, &slot->funcs, sibling) { + kfree(func); + } + kfree(slot); + } + + /* Release reference acquired by acpiphp_bridge_handle_to_function() */ + if ((bridge->flags & BRIDGE_HAS_EJ0) && bridge->func) + put_bridge(bridge->func->slot->bridge); + put_device(&bridge->pci_bus->dev); + pci_dev_put(bridge->pci_dev); + kfree(bridge); +} + /* * the _DCK method can do funny things... and sometimes not * hah-hah funny. @@ -154,9 +189,10 @@ register_slot(acpi_handle handle, u32 lvl, void *context, void **rv) acpi_handle tmp; acpi_status status = AE_OK; unsigned long long adr, sun; - int device, function, retval; + int device, function, retval, found = 0; struct pci_bus *pbus = bridge->pci_bus; struct pci_dev *pdev; + u32 val; if (!acpi_pci_check_ejectable(pbus, handle) && !is_dock_device(handle)) return AE_OK; @@ -170,7 +206,7 @@ register_slot(acpi_handle handle, u32 lvl, void *context, void **rv) device = (adr >> 16) & 0xffff; function = adr & 0xffff; - pdev = pbus->self; + pdev = bridge->pci_dev; if (pdev && device_is_managed_by_native_pciehp(pdev)) return AE_OK; @@ -178,7 +214,6 @@ register_slot(acpi_handle handle, u32 lvl, void *context, void **rv) if (!newfunc) return AE_NO_MEMORY; - INIT_LIST_HEAD(&newfunc->sibling); newfunc->handle = handle; newfunc->function = function; @@ -207,14 +242,15 @@ register_slot(acpi_handle handle, u32 lvl, void *context, void **rv) } /* search for objects that share the same slot */ - for (slot = bridge->slots; slot; slot = slot->next) + list_for_each_entry(slot, &bridge->slots, node) if (slot->device == device) { if (slot->sun != sun) warn("sibling found, but _SUN doesn't match!\n"); + found = 1; break; } - if (!slot) { + if (!found) { slot = kzalloc(sizeof(struct acpiphp_slot), GFP_KERNEL); if (!slot) { kfree(newfunc); @@ -227,9 +263,9 @@ register_slot(acpi_handle handle, u32 lvl, void *context, void **rv) INIT_LIST_HEAD(&slot->funcs); mutex_init(&slot->crit_sect); - slot->next = bridge->slots; - bridge->slots = slot; - + mutex_lock(&bridge_mutex); + list_add_tail(&slot->node, &bridge->slots); + mutex_unlock(&bridge_mutex); bridge->nr_slots++; dbg("found ACPI PCI Hotplug slot %llu at PCI %04x:%02x:%02x\n", @@ -247,13 +283,13 @@ register_slot(acpi_handle handle, u32 lvl, void *context, void **rv) } newfunc->slot = slot; + mutex_lock(&bridge_mutex); list_add_tail(&newfunc->sibling, &slot->funcs); + mutex_unlock(&bridge_mutex); - pdev = pci_get_slot(pbus, PCI_DEVFN(device, function)); - if (pdev) { + if (pci_bus_read_dev_vendor_id(pbus, PCI_DEVFN(device, function), + &val, 60*1000)) slot->flags |= (SLOT_ENABLED | SLOT_POWEREDON); - pci_dev_put(pdev); - } if (is_dock_device(handle)) { /* we don't want to call this device's _EJ0 @@ -290,7 +326,9 @@ register_slot(acpi_handle handle, u32 lvl, void *context, void **rv) err_exit: bridge->nr_slots--; - bridge->slots = slot->next; + mutex_lock(&bridge_mutex); + list_del(&slot->node); + mutex_unlock(&bridge_mutex); kfree(slot); kfree(newfunc); @@ -315,13 +353,17 @@ static void init_bridge_misc(struct acpiphp_bridge *bridge) acpi_status status; /* must be added to the list prior to calling register_slot */ + mutex_lock(&bridge_mutex); list_add(&bridge->list, &bridge_list); + mutex_unlock(&bridge_mutex); /* register all slot objects under this bridge */ status = acpi_walk_namespace(ACPI_TYPE_DEVICE, bridge->handle, (u32)1, register_slot, NULL, bridge, NULL); if (ACPI_FAILURE(status)) { + mutex_lock(&bridge_mutex); list_del(&bridge->list); + mutex_unlock(&bridge_mutex); return; } @@ -351,178 +393,46 @@ static struct acpiphp_func *acpiphp_bridge_handle_to_function(acpi_handle handle { struct acpiphp_bridge *bridge; struct acpiphp_slot *slot; - struct acpiphp_func *func; + struct acpiphp_func *func = NULL; + mutex_lock(&bridge_mutex); list_for_each_entry(bridge, &bridge_list, list) { - for (slot = bridge->slots; slot; slot = slot->next) { + list_for_each_entry(slot, &bridge->slots, node) { list_for_each_entry(func, &slot->funcs, sibling) { - if (func->handle == handle) + if (func->handle == handle) { + get_bridge(func->slot->bridge); + mutex_unlock(&bridge_mutex); return func; + } } } } + mutex_unlock(&bridge_mutex); return NULL; } -static inline void config_p2p_bridge_flags(struct acpiphp_bridge *bridge) -{ - acpi_handle dummy_handle; - struct acpiphp_func *func; - - if (ACPI_SUCCESS(acpi_get_handle(bridge->handle, - "_EJ0", &dummy_handle))) { - bridge->flags |= BRIDGE_HAS_EJ0; - - dbg("found ejectable p2p bridge\n"); - - /* make link between PCI bridge and PCI function */ - func = acpiphp_bridge_handle_to_function(bridge->handle); - if (!func) - return; - bridge->func = func; - func->bridge = bridge; - } -} - - -/* allocate and initialize host bridge data structure */ -static void add_host_bridge(struct acpi_pci_root *root) -{ - struct acpiphp_bridge *bridge; - acpi_handle handle = root->device->handle; - - bridge = kzalloc(sizeof(struct acpiphp_bridge), GFP_KERNEL); - if (bridge == NULL) - return; - - bridge->handle = handle; - - bridge->pci_bus = root->bus; - - init_bridge_misc(bridge); -} - - -/* allocate and initialize PCI-to-PCI bridge data structure */ -static void add_p2p_bridge(acpi_handle *handle) -{ - struct acpiphp_bridge *bridge; - - bridge = kzalloc(sizeof(struct acpiphp_bridge), GFP_KERNEL); - if (bridge == NULL) { - err("out of memory\n"); - return; - } - - bridge->handle = handle; - config_p2p_bridge_flags(bridge); - - bridge->pci_dev = acpi_get_pci_dev(handle); - bridge->pci_bus = bridge->pci_dev->subordinate; - if (!bridge->pci_bus) { - err("This is not a PCI-to-PCI bridge!\n"); - goto err; - } - - /* - * Grab a ref to the subordinate PCI bus in case the bus is - * removed via PCI core logical hotplug. The ref pins the bus - * (which we access during module unload). - */ - get_device(&bridge->pci_bus->dev); - - init_bridge_misc(bridge); - return; - err: - pci_dev_put(bridge->pci_dev); - kfree(bridge); - return; -} - - -/* callback routine to find P2P bridges */ -static acpi_status -find_p2p_bridge(acpi_handle handle, u32 lvl, void *context, void **rv) -{ - acpi_status status; - struct pci_dev *dev; - - dev = acpi_get_pci_dev(handle); - if (!dev || !dev->subordinate) - goto out; - - /* check if this bridge has ejectable slots */ - if ((detect_ejectable_slots(handle) > 0)) { - dbg("found PCI-to-PCI bridge at PCI %s\n", pci_name(dev)); - add_p2p_bridge(handle); - } - - /* search P2P bridges under this p2p bridge */ - status = acpi_walk_namespace(ACPI_TYPE_DEVICE, handle, (u32)1, - find_p2p_bridge, NULL, NULL, NULL); - if (ACPI_FAILURE(status)) - warn("find_p2p_bridge failed (error code = 0x%x)\n", status); - - out: - pci_dev_put(dev); - return AE_OK; -} - - -/* find hot-pluggable slots, and then find P2P bridge */ -static int add_bridge(struct acpi_pci_root *root) -{ - acpi_status status; - unsigned long long tmp; - acpi_handle dummy_handle; - acpi_handle handle = root->device->handle; - - /* if the bridge doesn't have _STA, we assume it is always there */ - status = acpi_get_handle(handle, "_STA", &dummy_handle); - if (ACPI_SUCCESS(status)) { - status = acpi_evaluate_integer(handle, "_STA", NULL, &tmp); - if (ACPI_FAILURE(status)) { - dbg("%s: _STA evaluation failure\n", __func__); - return 0; - } - if ((tmp & ACPI_STA_FUNCTIONING) == 0) - /* don't register this object */ - return 0; - } - - /* check if this bridge has ejectable slots */ - if (detect_ejectable_slots(handle) > 0) { - dbg("found PCI host-bus bridge with hot-pluggable slots\n"); - add_host_bridge(root); - } - - /* search P2P bridges under this host bridge */ - status = acpi_walk_namespace(ACPI_TYPE_DEVICE, handle, (u32)1, - find_p2p_bridge, NULL, NULL, NULL); - - if (ACPI_FAILURE(status)) - warn("find_p2p_bridge failed (error code = 0x%x)\n", status); - - return 0; -} - static struct acpiphp_bridge *acpiphp_handle_to_bridge(acpi_handle handle) { struct acpiphp_bridge *bridge; + mutex_lock(&bridge_mutex); list_for_each_entry(bridge, &bridge_list, list) - if (bridge->handle == handle) + if (bridge->handle == handle) { + get_bridge(bridge); + mutex_unlock(&bridge_mutex); return bridge; + } + mutex_unlock(&bridge_mutex); return NULL; } static void cleanup_bridge(struct acpiphp_bridge *bridge) { - struct acpiphp_slot *slot, *next; - struct acpiphp_func *func, *tmp; + struct acpiphp_slot *slot; + struct acpiphp_func *func; acpi_status status; acpi_handle handle = bridge->handle; @@ -543,10 +453,8 @@ static void cleanup_bridge(struct acpiphp_bridge *bridge) err("failed to install interrupt notify handler\n"); } - slot = bridge->slots; - while (slot) { - next = slot->next; - list_for_each_entry_safe(func, tmp, &slot->funcs, sibling) { + list_for_each_entry(slot, &bridge->slots, node) { + list_for_each_entry(func, &slot->funcs, sibling) { if (is_dock_device(func->handle)) { unregister_hotplug_dock_device(func->handle); unregister_dock_notifier(&func->nb); @@ -558,63 +466,13 @@ static void cleanup_bridge(struct acpiphp_bridge *bridge) if (ACPI_FAILURE(status)) err("failed to remove notify handler\n"); } - list_del(&func->sibling); - kfree(func); } acpiphp_unregister_hotplug_slot(slot); - list_del(&slot->funcs); - kfree(slot); - slot = next; } - /* - * Only P2P bridges have a pci_dev - */ - if (bridge->pci_dev) - put_device(&bridge->pci_bus->dev); - - pci_dev_put(bridge->pci_dev); + mutex_lock(&bridge_mutex); list_del(&bridge->list); - kfree(bridge); -} - -static acpi_status -cleanup_p2p_bridge(acpi_handle handle, u32 lvl, void *context, void **rv) -{ - struct acpiphp_bridge *bridge; - - /* cleanup p2p bridges under this P2P bridge - in a depth-first manner */ - acpi_walk_namespace(ACPI_TYPE_DEVICE, handle, (u32)1, - cleanup_p2p_bridge, NULL, NULL, NULL); - - bridge = acpiphp_handle_to_bridge(handle); - if (bridge) - cleanup_bridge(bridge); - - return AE_OK; -} - -static void remove_bridge(struct acpi_pci_root *root) -{ - struct acpiphp_bridge *bridge; - acpi_handle handle = root->device->handle; - - /* cleanup p2p bridges under this host bridge - in a depth-first manner */ - acpi_walk_namespace(ACPI_TYPE_DEVICE, handle, - (u32)1, cleanup_p2p_bridge, NULL, NULL, NULL); - - /* - * On root bridges with hotplug slots directly underneath (ie, - * no p2p bridge between), we call cleanup_bridge(). - * - * The else clause cleans up root bridges that either had no - * hotplug slots at all, or had a p2p bridge underneath. - */ - bridge = acpiphp_handle_to_bridge(handle); - if (bridge) - cleanup_bridge(bridge); + mutex_unlock(&bridge_mutex); } static int power_on_slot(struct acpiphp_slot *slot) @@ -798,6 +656,7 @@ static void check_hotplug_bridge(struct acpiphp_slot *slot, struct pci_dev *dev) } } } + /** * enable_device - enable, configure a slot * @slot: slot to be enabled @@ -810,9 +669,7 @@ static int __ref enable_device(struct acpiphp_slot *slot) struct pci_dev *dev; struct pci_bus *bus = slot->bridge->pci_bus; struct acpiphp_func *func; - int retval = 0; int num, max, pass; - acpi_status status; if (slot->flags & SLOT_ENABLED) goto err_exit; @@ -867,23 +724,11 @@ static int __ref enable_device(struct acpiphp_slot *slot) slot->flags &= (~SLOT_ENABLED); continue; } - - if (dev->hdr_type != PCI_HEADER_TYPE_BRIDGE && - dev->hdr_type != PCI_HEADER_TYPE_CARDBUS) { - pci_dev_put(dev); - continue; - } - - status = find_p2p_bridge(func->handle, (u32)1, bus, NULL); - if (ACPI_FAILURE(status)) - warn("find_p2p_bridge failed (error code = 0x%x)\n", - status); - pci_dev_put(dev); } err_exit: - return retval; + return 0; } /* return first device in slot, acquiring a reference on it */ @@ -912,23 +757,6 @@ static int disable_device(struct acpiphp_slot *slot) { struct acpiphp_func *func; struct pci_dev *pdev; - struct pci_bus *bus = slot->bridge->pci_bus; - - /* The slot will be enabled when func 0 is added, so check - func 0 before disable the slot. */ - pdev = pci_get_slot(bus, PCI_DEVFN(slot->device, 0)); - if (!pdev) - goto err_exit; - pci_dev_put(pdev); - - list_for_each_entry(func, &slot->funcs, sibling) { - if (func->bridge) { - /* cleanup p2p bridges under this P2P bridge */ - cleanup_p2p_bridge(func->bridge->handle, - (u32)1, NULL, NULL); - func->bridge = NULL; - } - } /* * enable_device() enumerates all functions in this device via @@ -947,7 +775,6 @@ static int disable_device(struct acpiphp_slot *slot) slot->flags &= (~SLOT_ENABLED); -err_exit: return 0; } @@ -1037,7 +864,7 @@ static int acpiphp_check_bridge(struct acpiphp_bridge *bridge) enabled = disabled = 0; - for (slot = bridge->slots; slot; slot = slot->next) { + list_for_each_entry(slot, &bridge->slots, node) { unsigned int status = get_slot_status(slot); if (slot->flags & SLOT_ENABLED) { if (status == ACPI_STA_ALL) @@ -1082,11 +909,11 @@ static void acpiphp_set_hpp_values(struct pci_bus *bus) */ static void acpiphp_sanitize_bus(struct pci_bus *bus) { - struct pci_dev *dev; + struct pci_dev *dev, *tmp; int i; unsigned long type_mask = IORESOURCE_IO | IORESOURCE_MEM; - list_for_each_entry(dev, &bus->devices, bus_list) { + list_for_each_entry_safe(dev, tmp, &bus->devices, bus_list) { for (i=0; i<PCI_BRIDGE_RESOURCES; i++) { struct resource *res = &dev->resource[i]; if ((res->flags & type_mask) && !res->start && @@ -1118,6 +945,7 @@ check_sub_bridges(acpi_handle handle, u32 lvl, void *context, void **rv) dbg("%s: re-enumerating slots under %s\n", __func__, objname); acpiphp_check_bridge(bridge); + put_bridge(bridge); } return AE_OK ; } @@ -1195,6 +1023,7 @@ static void _handle_hotplug_event_bridge(struct work_struct *work) acpi_scan_lock_release(); kfree(hp_work); /* allocated in handle_hotplug_event_bridge */ + put_bridge(bridge); } /** @@ -1208,6 +1037,8 @@ static void _handle_hotplug_event_bridge(struct work_struct *work) static void handle_hotplug_event_bridge(acpi_handle handle, u32 type, void *context) { + struct acpiphp_bridge *bridge = context; + /* * Currently the code adds all hotplug events to the kacpid_wq * queue when it should add hotplug events to the kacpi_hotplug_wq. @@ -1216,6 +1047,7 @@ static void handle_hotplug_event_bridge(acpi_handle handle, u32 type, * For now just re-add this work to the kacpi_hotplug_wq so we * don't deadlock on hotplug actions. */ + get_bridge(bridge); alloc_acpi_hp_work(handle, type, context, _handle_hotplug_event_bridge); } @@ -1270,6 +1102,7 @@ static void _handle_hotplug_event_func(struct work_struct *work) acpi_scan_lock_release(); kfree(hp_work); /* allocated in handle_hotplug_event_func */ + put_bridge(func->slot->bridge); } /** @@ -1283,6 +1116,8 @@ static void _handle_hotplug_event_func(struct work_struct *work) static void handle_hotplug_event_func(acpi_handle handle, u32 type, void *context) { + struct acpiphp_func *func = context; + /* * Currently the code adds all hotplug events to the kacpid_wq * queue when it should add hotplug events to the kacpi_hotplug_wq. @@ -1291,33 +1126,69 @@ static void handle_hotplug_event_func(acpi_handle handle, u32 type, * For now just re-add this work to the kacpi_hotplug_wq so we * don't deadlock on hotplug actions. */ + get_bridge(func->slot->bridge); alloc_acpi_hp_work(handle, type, context, _handle_hotplug_event_func); } -static struct acpi_pci_driver acpi_pci_hp_driver = { - .add = add_bridge, - .remove = remove_bridge, -}; - -/** - * acpiphp_glue_init - initializes all PCI hotplug - ACPI glue data structures +/* + * Create hotplug slots for the PCI bus. + * It should always return 0 to avoid skipping following notifiers. */ -int __init acpiphp_glue_init(void) +void acpiphp_enumerate_slots(struct pci_bus *bus, acpi_handle handle) { - acpi_pci_register_driver(&acpi_pci_hp_driver); + acpi_handle dummy_handle; + struct acpiphp_bridge *bridge; - return 0; -} + if (acpiphp_disabled) + return; + if (detect_ejectable_slots(handle) <= 0) + return; -/** - * acpiphp_glue_exit - terminates all PCI hotplug - ACPI glue data structures - * - * This function frees all data allocated in acpiphp_glue_init(). - */ -void acpiphp_glue_exit(void) + bridge = kzalloc(sizeof(struct acpiphp_bridge), GFP_KERNEL); + if (bridge == NULL) { + err("out of memory\n"); + return; + } + + INIT_LIST_HEAD(&bridge->slots); + kref_init(&bridge->ref); + bridge->handle = handle; + bridge->pci_dev = pci_dev_get(bus->self); + bridge->pci_bus = bus; + + /* + * Grab a ref to the subordinate PCI bus in case the bus is + * removed via PCI core logical hotplug. The ref pins the bus + * (which we access during module unload). + */ + get_device(&bus->dev); + + if (!pci_is_root_bus(bridge->pci_bus) && + ACPI_SUCCESS(acpi_get_handle(bridge->handle, + "_EJ0", &dummy_handle))) { + dbg("found ejectable p2p bridge\n"); + bridge->flags |= BRIDGE_HAS_EJ0; + bridge->func = acpiphp_bridge_handle_to_function(handle); + } + + init_bridge_misc(bridge); +} + +/* Destroy hotplug slots associated with the PCI bus */ +void acpiphp_remove_slots(struct pci_bus *bus) { - acpi_pci_unregister_driver(&acpi_pci_hp_driver); + struct acpiphp_bridge *bridge, *tmp; + + if (acpiphp_disabled) + return; + + list_for_each_entry_safe(bridge, tmp, &bridge_list, list) + if (bridge->pci_bus == bus) { + cleanup_bridge(bridge); + put_bridge(bridge); + break; + } } /** @@ -1396,7 +1267,7 @@ u8 acpiphp_get_latch_status(struct acpiphp_slot *slot) sta = get_slot_status(slot); - return (sta & ACPI_STA_SHOW_IN_UI) ? 0 : 1; + return (sta & ACPI_STA_DEVICE_UI) ? 0 : 1; } diff --git a/drivers/pci/hotplug/cpci_hotplug.h b/drivers/pci/hotplug/cpci_hotplug.h index 9fff878cf026..1356211431d0 100644 --- a/drivers/pci/hotplug/cpci_hotplug.h +++ b/drivers/pci/hotplug/cpci_hotplug.h @@ -75,28 +75,36 @@ static inline const char *slot_name(struct slot *slot) return hotplug_slot_name(slot->hotplug_slot); } -extern int cpci_hp_register_controller(struct cpci_hp_controller *controller); -extern int cpci_hp_unregister_controller(struct cpci_hp_controller *controller); -extern int cpci_hp_register_bus(struct pci_bus *bus, u8 first, u8 last); -extern int cpci_hp_unregister_bus(struct pci_bus *bus); -extern int cpci_hp_start(void); -extern int cpci_hp_stop(void); +int cpci_hp_register_controller(struct cpci_hp_controller *controller); +int cpci_hp_unregister_controller(struct cpci_hp_controller *controller); +int cpci_hp_register_bus(struct pci_bus *bus, u8 first, u8 last); +int cpci_hp_unregister_bus(struct pci_bus *bus); +int cpci_hp_start(void); +int cpci_hp_stop(void); /* * Internal function prototypes, these functions should not be used by * board/chassis drivers. */ -extern u8 cpci_get_attention_status(struct slot *slot); -extern u8 cpci_get_latch_status(struct slot *slot); -extern u8 cpci_get_adapter_status(struct slot *slot); -extern u16 cpci_get_hs_csr(struct slot * slot); -extern int cpci_set_attention_status(struct slot *slot, int status); -extern int cpci_check_and_clear_ins(struct slot * slot); -extern int cpci_check_ext(struct slot * slot); -extern int cpci_clear_ext(struct slot * slot); -extern int cpci_led_on(struct slot * slot); -extern int cpci_led_off(struct slot * slot); -extern int cpci_configure_slot(struct slot *slot); -extern int cpci_unconfigure_slot(struct slot *slot); +u8 cpci_get_attention_status(struct slot *slot); +u8 cpci_get_latch_status(struct slot *slot); +u8 cpci_get_adapter_status(struct slot *slot); +u16 cpci_get_hs_csr(struct slot * slot); +int cpci_set_attention_status(struct slot *slot, int status); +int cpci_check_and_clear_ins(struct slot * slot); +int cpci_check_ext(struct slot * slot); +int cpci_clear_ext(struct slot * slot); +int cpci_led_on(struct slot * slot); +int cpci_led_off(struct slot * slot); +int cpci_configure_slot(struct slot *slot); +int cpci_unconfigure_slot(struct slot *slot); + +#ifdef CONFIG_HOTPLUG_PCI_CPCI +int cpci_hotplug_init(int debug); +void cpci_hotplug_exit(void); +#else +static inline int cpci_hotplug_init(int debug) { return 0; } +static inline void cpci_hotplug_exit(void) { } +#endif #endif /* _CPCI_HOTPLUG_H */ diff --git a/drivers/pci/hotplug/cpqphp.h b/drivers/pci/hotplug/cpqphp.h index d8ffc7366801..516b87738b6e 100644 --- a/drivers/pci/hotplug/cpqphp.h +++ b/drivers/pci/hotplug/cpqphp.h @@ -404,50 +404,44 @@ struct resource_lists { /* debugfs functions for the hotplug controller info */ -extern void cpqhp_initialize_debugfs(void); -extern void cpqhp_shutdown_debugfs(void); -extern void cpqhp_create_debugfs_files(struct controller *ctrl); -extern void cpqhp_remove_debugfs_files(struct controller *ctrl); +void cpqhp_initialize_debugfs(void); +void cpqhp_shutdown_debugfs(void); +void cpqhp_create_debugfs_files(struct controller *ctrl); +void cpqhp_remove_debugfs_files(struct controller *ctrl); /* controller functions */ -extern void cpqhp_pushbutton_thread(unsigned long event_pointer); -extern irqreturn_t cpqhp_ctrl_intr(int IRQ, void *data); -extern int cpqhp_find_available_resources(struct controller *ctrl, - void __iomem *rom_start); -extern int cpqhp_event_start_thread(void); -extern void cpqhp_event_stop_thread(void); -extern struct pci_func *cpqhp_slot_create(unsigned char busnumber); -extern struct pci_func *cpqhp_slot_find(unsigned char bus, unsigned char device, - unsigned char index); -extern int cpqhp_process_SI(struct controller *ctrl, struct pci_func *func); -extern int cpqhp_process_SS(struct controller *ctrl, struct pci_func *func); -extern int cpqhp_hardware_test(struct controller *ctrl, int test_num); +void cpqhp_pushbutton_thread(unsigned long event_pointer); +irqreturn_t cpqhp_ctrl_intr(int IRQ, void *data); +int cpqhp_find_available_resources(struct controller *ctrl, + void __iomem *rom_start); +int cpqhp_event_start_thread(void); +void cpqhp_event_stop_thread(void); +struct pci_func *cpqhp_slot_create(unsigned char busnumber); +struct pci_func *cpqhp_slot_find(unsigned char bus, unsigned char device, + unsigned char index); +int cpqhp_process_SI(struct controller *ctrl, struct pci_func *func); +int cpqhp_process_SS(struct controller *ctrl, struct pci_func *func); +int cpqhp_hardware_test(struct controller *ctrl, int test_num); /* resource functions */ -extern int cpqhp_resource_sort_and_combine (struct pci_resource **head); +int cpqhp_resource_sort_and_combine (struct pci_resource **head); /* pci functions */ -extern int cpqhp_set_irq(u8 bus_num, u8 dev_num, u8 int_pin, u8 irq_num); -extern int cpqhp_get_bus_dev(struct controller *ctrl, u8 *bus_num, u8 *dev_num, - u8 slot); -extern int cpqhp_save_config(struct controller *ctrl, int busnumber, - int is_hot_plug); -extern int cpqhp_save_base_addr_length(struct controller *ctrl, - struct pci_func *func); -extern int cpqhp_save_used_resources(struct controller *ctrl, - struct pci_func *func); -extern int cpqhp_configure_board(struct controller *ctrl, - struct pci_func *func); -extern int cpqhp_save_slot_config(struct controller *ctrl, - struct pci_func *new_slot); -extern int cpqhp_valid_replace(struct controller *ctrl, struct pci_func *func); -extern void cpqhp_destroy_board_resources(struct pci_func *func); -extern int cpqhp_return_board_resources (struct pci_func *func, - struct resource_lists *resources); -extern void cpqhp_destroy_resource_list(struct resource_lists *resources); -extern int cpqhp_configure_device(struct controller *ctrl, - struct pci_func *func); -extern int cpqhp_unconfigure_device(struct pci_func *func); +int cpqhp_set_irq(u8 bus_num, u8 dev_num, u8 int_pin, u8 irq_num); +int cpqhp_get_bus_dev(struct controller *ctrl, u8 *bus_num, u8 *dev_num, + u8 slot); +int cpqhp_save_config(struct controller *ctrl, int busnumber, int is_hot_plug); +int cpqhp_save_base_addr_length(struct controller *ctrl, struct pci_func *func); +int cpqhp_save_used_resources(struct controller *ctrl, struct pci_func *func); +int cpqhp_configure_board(struct controller *ctrl, struct pci_func *func); +int cpqhp_save_slot_config(struct controller *ctrl, struct pci_func *new_slot); +int cpqhp_valid_replace(struct controller *ctrl, struct pci_func *func); +void cpqhp_destroy_board_resources(struct pci_func *func); +int cpqhp_return_board_resources(struct pci_func *func, + struct resource_lists *resources); +void cpqhp_destroy_resource_list(struct resource_lists *resources); +int cpqhp_configure_device(struct controller *ctrl, struct pci_func *func); +int cpqhp_unconfigure_device(struct pci_func *func); /* Global variables */ extern int cpqhp_debug; diff --git a/drivers/pci/hotplug/cpqphp_nvram.h b/drivers/pci/hotplug/cpqphp_nvram.h index e89c0702119d..34e4e54fcf15 100644 --- a/drivers/pci/hotplug/cpqphp_nvram.h +++ b/drivers/pci/hotplug/cpqphp_nvram.h @@ -30,26 +30,26 @@ #ifndef CONFIG_HOTPLUG_PCI_COMPAQ_NVRAM -static inline void compaq_nvram_init (void __iomem *rom_start) +static inline void compaq_nvram_init(void __iomem *rom_start) { return; } -static inline int compaq_nvram_load (void __iomem *rom_start, struct controller *ctrl) +static inline int compaq_nvram_load(void __iomem *rom_start, struct controller *ctrl) { return 0; } -static inline int compaq_nvram_store (void __iomem *rom_start) +static inline int compaq_nvram_store(void __iomem *rom_start) { return 0; } #else -extern void compaq_nvram_init (void __iomem *rom_start); -extern int compaq_nvram_load (void __iomem *rom_start, struct controller *ctrl); -extern int compaq_nvram_store (void __iomem *rom_start); +void compaq_nvram_init(void __iomem *rom_start); +int compaq_nvram_load(void __iomem *rom_start, struct controller *ctrl); +int compaq_nvram_store(void __iomem *rom_start); #endif diff --git a/drivers/pci/hotplug/ibmphp.h b/drivers/pci/hotplug/ibmphp.h index a8d391a4957d..8c5b25871d02 100644 --- a/drivers/pci/hotplug/ibmphp.h +++ b/drivers/pci/hotplug/ibmphp.h @@ -275,17 +275,17 @@ extern struct list_head ibmphp_slot_head; * FUNCTION PROTOTYPES * ***********************************************************/ -extern void ibmphp_free_ebda_hpc_queue (void); -extern int ibmphp_access_ebda (void); -extern struct slot *ibmphp_get_slot_from_physical_num (u8); -extern int ibmphp_get_total_hp_slots (void); -extern void ibmphp_free_ibm_slot (struct slot *); -extern void ibmphp_free_bus_info_queue (void); -extern void ibmphp_free_ebda_pci_rsrc_queue (void); -extern struct bus_info *ibmphp_find_same_bus_num (u32); -extern int ibmphp_get_bus_index (u8); -extern u16 ibmphp_get_total_controllers (void); -extern int ibmphp_register_pci (void); +void ibmphp_free_ebda_hpc_queue(void); +int ibmphp_access_ebda(void); +struct slot *ibmphp_get_slot_from_physical_num(u8); +int ibmphp_get_total_hp_slots(void); +void ibmphp_free_ibm_slot(struct slot *); +void ibmphp_free_bus_info_queue(void); +void ibmphp_free_ebda_pci_rsrc_queue(void); +struct bus_info *ibmphp_find_same_bus_num(u32); +int ibmphp_get_bus_index(u8); +u16 ibmphp_get_total_controllers(void); +int ibmphp_register_pci(void); /* passed parameters */ #define MEM 0 @@ -381,24 +381,24 @@ struct res_needed { /* functions */ -extern int ibmphp_rsrc_init (void); -extern int ibmphp_add_resource (struct resource_node *); -extern int ibmphp_remove_resource (struct resource_node *); -extern int ibmphp_find_resource (struct bus_node *, u32, struct resource_node **, int); -extern int ibmphp_check_resource (struct resource_node *, u8); -extern int ibmphp_remove_bus (struct bus_node *, u8); -extern void ibmphp_free_resources (void); -extern int ibmphp_add_pfmem_from_mem (struct resource_node *); -extern struct bus_node *ibmphp_find_res_bus (u8); -extern void ibmphp_print_test (void); /* for debugging purposes */ +int ibmphp_rsrc_init(void); +int ibmphp_add_resource(struct resource_node *); +int ibmphp_remove_resource(struct resource_node *); +int ibmphp_find_resource(struct bus_node *, u32, struct resource_node **, int); +int ibmphp_check_resource(struct resource_node *, u8); +int ibmphp_remove_bus(struct bus_node *, u8); +void ibmphp_free_resources(void); +int ibmphp_add_pfmem_from_mem(struct resource_node *); +struct bus_node *ibmphp_find_res_bus(u8); +void ibmphp_print_test(void); /* for debugging purposes */ -extern void ibmphp_hpc_initvars (void); -extern int ibmphp_hpc_readslot (struct slot *, u8, u8 *); -extern int ibmphp_hpc_writeslot (struct slot *, u8); -extern void ibmphp_lock_operations (void); -extern void ibmphp_unlock_operations (void); -extern int ibmphp_hpc_start_poll_thread (void); -extern void ibmphp_hpc_stop_poll_thread (void); +void ibmphp_hpc_initvars(void); +int ibmphp_hpc_readslot(struct slot *, u8, u8 *); +int ibmphp_hpc_writeslot(struct slot *, u8); +void ibmphp_lock_operations(void); +void ibmphp_unlock_operations(void); +int ibmphp_hpc_start_poll_thread(void); +void ibmphp_hpc_stop_poll_thread(void); //---------------------------------------------------------------------------- @@ -749,11 +749,11 @@ struct controller { /* Functions */ -extern int ibmphp_init_devno (struct slot **); /* This function is called from EBDA, so we need it not be static */ -extern int ibmphp_do_disable_slot (struct slot *slot_cur); -extern int ibmphp_update_slot_info (struct slot *); /* This function is called from HPC, so we need it to not be be static */ -extern int ibmphp_configure_card (struct pci_func *, u8); -extern int ibmphp_unconfigure_card (struct slot **, int); +int ibmphp_init_devno(struct slot **); /* This function is called from EBDA, so we need it not be static */ +int ibmphp_do_disable_slot(struct slot *slot_cur); +int ibmphp_update_slot_info(struct slot *); /* This function is called from HPC, so we need it to not be be static */ +int ibmphp_configure_card(struct pci_func *, u8); +int ibmphp_unconfigure_card(struct slot **, int); extern struct hotplug_slot_ops ibmphp_hotplug_slot_ops; #endif //__IBMPHP_H diff --git a/drivers/pci/hotplug/pci_hotplug_core.c b/drivers/pci/hotplug/pci_hotplug_core.c index 202f4a969eb5..ec20f74c8981 100644 --- a/drivers/pci/hotplug/pci_hotplug_core.c +++ b/drivers/pci/hotplug/pci_hotplug_core.c @@ -41,6 +41,7 @@ #include <linux/pci_hotplug.h> #include <asm/uaccess.h> #include "../pci.h" +#include "cpci_hotplug.h" #define MY_NAME "pci_hotplug" @@ -63,14 +64,6 @@ static bool debug; static LIST_HEAD(pci_hotplug_slot_list); static DEFINE_MUTEX(pci_hp_mutex); -#ifdef CONFIG_HOTPLUG_PCI_CPCI -extern int cpci_hotplug_init(int debug); -extern void cpci_hotplug_exit(void); -#else -static inline int cpci_hotplug_init(int debug) { return 0; } -static inline void cpci_hotplug_exit(void) { } -#endif - /* Weee, fun with macros... */ #define GET_STATUS(name,type) \ static int get_##name (struct hotplug_slot *slot, type *value) \ @@ -524,13 +517,11 @@ int pci_hp_deregister(struct hotplug_slot *hotplug) * * Returns 0 if successful, anything else for an error. */ -int __must_check pci_hp_change_slot_info(struct hotplug_slot *hotplug, - struct hotplug_slot_info *info) +int pci_hp_change_slot_info(struct hotplug_slot *hotplug, + struct hotplug_slot_info *info) { - struct pci_slot *slot; if (!hotplug || !info) return -ENODEV; - slot = hotplug->pci_slot; memcpy(hotplug->info, info, sizeof(struct hotplug_slot_info)); diff --git a/drivers/pci/hotplug/pciehp.h b/drivers/pci/hotplug/pciehp.h index 2c113de94323..7fb326983ed6 100644 --- a/drivers/pci/hotplug/pciehp.h +++ b/drivers/pci/hotplug/pciehp.h @@ -127,15 +127,15 @@ struct controller { #define NO_CMD_CMPL(ctrl) ((ctrl)->slot_cap & PCI_EXP_SLTCAP_NCCS) #define PSN(ctrl) ((ctrl)->slot_cap >> 19) -extern int pciehp_sysfs_enable_slot(struct slot *slot); -extern int pciehp_sysfs_disable_slot(struct slot *slot); -extern u8 pciehp_handle_attention_button(struct slot *p_slot); -extern u8 pciehp_handle_switch_change(struct slot *p_slot); -extern u8 pciehp_handle_presence_change(struct slot *p_slot); -extern u8 pciehp_handle_power_fault(struct slot *p_slot); -extern int pciehp_configure_device(struct slot *p_slot); -extern int pciehp_unconfigure_device(struct slot *p_slot); -extern void pciehp_queue_pushbutton_work(struct work_struct *work); +int pciehp_sysfs_enable_slot(struct slot *slot); +int pciehp_sysfs_disable_slot(struct slot *slot); +u8 pciehp_handle_attention_button(struct slot *p_slot); +u8 pciehp_handle_switch_change(struct slot *p_slot); +u8 pciehp_handle_presence_change(struct slot *p_slot); +u8 pciehp_handle_power_fault(struct slot *p_slot); +int pciehp_configure_device(struct slot *p_slot); +int pciehp_unconfigure_device(struct slot *p_slot); +void pciehp_queue_pushbutton_work(struct work_struct *work); struct controller *pcie_init(struct pcie_device *dev); int pcie_init_notification(struct controller *ctrl); int pciehp_enable_slot(struct slot *p_slot); @@ -166,8 +166,8 @@ static inline const char *slot_name(struct slot *slot) #include <acpi/acpi_bus.h> #include <linux/pci-acpi.h> -extern void __init pciehp_acpi_slot_detection_init(void); -extern int pciehp_acpi_slot_detection_check(struct pci_dev *dev); +void __init pciehp_acpi_slot_detection_init(void); +int pciehp_acpi_slot_detection_check(struct pci_dev *dev); static inline void pciehp_firmware_init(void) { diff --git a/drivers/pci/hotplug/pciehp_acpi.c b/drivers/pci/hotplug/pciehp_acpi.c index 24d709b7388c..ead7c534095e 100644 --- a/drivers/pci/hotplug/pciehp_acpi.c +++ b/drivers/pci/hotplug/pciehp_acpi.c @@ -90,7 +90,7 @@ static int __init dummy_probe(struct pcie_device *dev) slot = kzalloc(sizeof(*slot), GFP_KERNEL); if (!slot) return -ENOMEM; - slot->number = slot_cap >> 19; + slot->number = (slot_cap & PCI_EXP_SLTCAP_PSN) >> 19; list_for_each_entry(tmp, &dummy_slots, list) { if (tmp->number == slot->number) dup_slot_id++; diff --git a/drivers/pci/hotplug/rpadlpar.h b/drivers/pci/hotplug/rpadlpar.h index 4a0a59b82eae..81df93931ad0 100644 --- a/drivers/pci/hotplug/rpadlpar.h +++ b/drivers/pci/hotplug/rpadlpar.h @@ -15,10 +15,10 @@ #ifndef _RPADLPAR_IO_H_ #define _RPADLPAR_IO_H_ -extern int dlpar_sysfs_init(void); -extern void dlpar_sysfs_exit(void); +int dlpar_sysfs_init(void); +void dlpar_sysfs_exit(void); -extern int dlpar_add_slot(char *drc_name); -extern int dlpar_remove_slot(char *drc_name); +int dlpar_add_slot(char *drc_name); +int dlpar_remove_slot(char *drc_name); #endif diff --git a/drivers/pci/hotplug/rpaphp.h b/drivers/pci/hotplug/rpaphp.h index df5677440a08..3135856e5e1c 100644 --- a/drivers/pci/hotplug/rpaphp.h +++ b/drivers/pci/hotplug/rpaphp.h @@ -86,18 +86,18 @@ extern struct list_head rpaphp_slot_head; /* function prototypes */ /* rpaphp_pci.c */ -extern int rpaphp_enable_slot(struct slot *slot); -extern int rpaphp_get_sensor_state(struct slot *slot, int *state); +int rpaphp_enable_slot(struct slot *slot); +int rpaphp_get_sensor_state(struct slot *slot, int *state); /* rpaphp_core.c */ -extern int rpaphp_add_slot(struct device_node *dn); -extern int rpaphp_get_drc_props(struct device_node *dn, int *drc_index, +int rpaphp_add_slot(struct device_node *dn); +int rpaphp_get_drc_props(struct device_node *dn, int *drc_index, char **drc_name, char **drc_type, int *drc_power_domain); /* rpaphp_slot.c */ -extern void dealloc_slot_struct(struct slot *slot); -extern struct slot *alloc_slot_struct(struct device_node *dn, int drc_index, char *drc_name, int power_domain); -extern int rpaphp_register_slot(struct slot *slot); -extern int rpaphp_deregister_slot(struct slot *slot); +void dealloc_slot_struct(struct slot *slot); +struct slot *alloc_slot_struct(struct device_node *dn, int drc_index, char *drc_name, int power_domain); +int rpaphp_register_slot(struct slot *slot); +int rpaphp_deregister_slot(struct slot *slot); #endif /* _PPC64PHP_H */ diff --git a/drivers/pci/hotplug/s390_pci_hpc.c b/drivers/pci/hotplug/s390_pci_hpc.c index 7db249a25016..46a7b738f61f 100644 --- a/drivers/pci/hotplug/s390_pci_hpc.c +++ b/drivers/pci/hotplug/s390_pci_hpc.c @@ -16,6 +16,7 @@ #include <linux/pci.h> #include <linux/pci_hotplug.h> #include <linux/init.h> +#include <asm/pci_debug.h> #include <asm/sclp.h> #define SLOT_NAME_SIZE 10 @@ -49,6 +50,7 @@ static int enable_slot(struct hotplug_slot *hotplug_slot) return -EIO; rc = sclp_pci_configure(slot->zdev->fid); + zpci_dbg(3, "conf fid:%x, rc:%d\n", slot->zdev->fid, rc); if (!rc) { slot->zdev->state = ZPCI_FN_STATE_CONFIGURED; /* automatically scan the device after is was configured */ @@ -66,16 +68,16 @@ static int disable_slot(struct hotplug_slot *hotplug_slot) if (!zpci_fn_configured(slot->zdev->state)) return -EIO; + rc = zpci_disable_device(slot->zdev); + if (rc) + return rc; /* TODO: we rely on the user to unbind/remove the device, is that plausible * or do we need to trigger that here? */ rc = sclp_pci_deconfigure(slot->zdev->fid); - if (!rc) { - /* Fixme: better call List-PCI to find the disabled FH - for the FID since the FH should be opaque... */ - slot->zdev->fh &= 0x7fffffff; + zpci_dbg(3, "deconf fid:%x, rc:%d\n", slot->zdev->fid, rc); + if (!rc) slot->zdev->state = ZPCI_FN_STATE_STANDBY; - } return rc; } diff --git a/drivers/pci/hotplug/shpchp.h b/drivers/pci/hotplug/shpchp.h index b849f995075a..e260f207a90e 100644 --- a/drivers/pci/hotplug/shpchp.h +++ b/drivers/pci/hotplug/shpchp.h @@ -168,19 +168,19 @@ struct controller { #define WRONG_BUS_FREQUENCY 0x0000000D #define POWER_FAILURE 0x0000000E -extern int __must_check shpchp_create_ctrl_files(struct controller *ctrl); -extern void shpchp_remove_ctrl_files(struct controller *ctrl); -extern int shpchp_sysfs_enable_slot(struct slot *slot); -extern int shpchp_sysfs_disable_slot(struct slot *slot); -extern u8 shpchp_handle_attention_button(u8 hp_slot, struct controller *ctrl); -extern u8 shpchp_handle_switch_change(u8 hp_slot, struct controller *ctrl); -extern u8 shpchp_handle_presence_change(u8 hp_slot, struct controller *ctrl); -extern u8 shpchp_handle_power_fault(u8 hp_slot, struct controller *ctrl); -extern int shpchp_configure_device(struct slot *p_slot); -extern int shpchp_unconfigure_device(struct slot *p_slot); -extern void cleanup_slots(struct controller *ctrl); -extern void shpchp_queue_pushbutton_work(struct work_struct *work); -extern int shpc_init( struct controller *ctrl, struct pci_dev *pdev); +int __must_check shpchp_create_ctrl_files(struct controller *ctrl); +void shpchp_remove_ctrl_files(struct controller *ctrl); +int shpchp_sysfs_enable_slot(struct slot *slot); +int shpchp_sysfs_disable_slot(struct slot *slot); +u8 shpchp_handle_attention_button(u8 hp_slot, struct controller *ctrl); +u8 shpchp_handle_switch_change(u8 hp_slot, struct controller *ctrl); +u8 shpchp_handle_presence_change(u8 hp_slot, struct controller *ctrl); +u8 shpchp_handle_power_fault(u8 hp_slot, struct controller *ctrl); +int shpchp_configure_device(struct slot *p_slot); +int shpchp_unconfigure_device(struct slot *p_slot); +void cleanup_slots(struct controller *ctrl); +void shpchp_queue_pushbutton_work(struct work_struct *work); +int shpc_init( struct controller *ctrl, struct pci_dev *pdev); static inline const char *slot_name(struct slot *slot) { diff --git a/drivers/pci/hotplug/shpchp_sysfs.c b/drivers/pci/hotplug/shpchp_sysfs.c index eeb23ceae4a8..e8c31fe20566 100644 --- a/drivers/pci/hotplug/shpchp_sysfs.c +++ b/drivers/pci/hotplug/shpchp_sysfs.c @@ -85,7 +85,7 @@ static ssize_t show_ctrl (struct device *dev, struct device_attribute *attr, cha } static DEVICE_ATTR (ctrl, S_IRUGO, show_ctrl, NULL); -int __must_check shpchp_create_ctrl_files (struct controller *ctrl) +int shpchp_create_ctrl_files (struct controller *ctrl) { return device_create_file (&ctrl->pci_dev->dev, &dev_attr_ctrl); } diff --git a/drivers/pci/msi.c b/drivers/pci/msi.c index 00cc78c7aa04..d40bed726769 100644 --- a/drivers/pci/msi.c +++ b/drivers/pci/msi.c @@ -22,10 +22,12 @@ #include <linux/slab.h> #include "pci.h" -#include "msi.h" static int pci_msi_enable = 1; +#define msix_table_size(flags) ((flags & PCI_MSIX_FLAGS_QSIZE) + 1) + + /* Arch hooks */ #ifndef arch_msi_check_device @@ -111,32 +113,26 @@ void default_restore_msi_irqs(struct pci_dev *dev, int irq) } #endif -static void msi_set_enable(struct pci_dev *dev, int pos, int enable) +static void msi_set_enable(struct pci_dev *dev, int enable) { u16 control; - BUG_ON(!pos); - - pci_read_config_word(dev, pos + PCI_MSI_FLAGS, &control); + pci_read_config_word(dev, dev->msi_cap + PCI_MSI_FLAGS, &control); control &= ~PCI_MSI_FLAGS_ENABLE; if (enable) control |= PCI_MSI_FLAGS_ENABLE; - pci_write_config_word(dev, pos + PCI_MSI_FLAGS, control); + pci_write_config_word(dev, dev->msi_cap + PCI_MSI_FLAGS, control); } static void msix_set_enable(struct pci_dev *dev, int enable) { - int pos; u16 control; - pos = pci_find_capability(dev, PCI_CAP_ID_MSIX); - if (pos) { - pci_read_config_word(dev, pos + PCI_MSIX_FLAGS, &control); - control &= ~PCI_MSIX_FLAGS_ENABLE; - if (enable) - control |= PCI_MSIX_FLAGS_ENABLE; - pci_write_config_word(dev, pos + PCI_MSIX_FLAGS, control); - } + pci_read_config_word(dev, dev->msix_cap + PCI_MSIX_FLAGS, &control); + control &= ~PCI_MSIX_FLAGS_ENABLE; + if (enable) + control |= PCI_MSIX_FLAGS_ENABLE; + pci_write_config_word(dev, dev->msix_cap + PCI_MSIX_FLAGS, control); } static inline __attribute_const__ u32 msi_mask(unsigned x) @@ -247,18 +243,18 @@ void __read_msi_msg(struct msi_desc *entry, struct msi_msg *msg) msg->data = readl(base + PCI_MSIX_ENTRY_DATA); } else { struct pci_dev *dev = entry->dev; - int pos = entry->msi_attrib.pos; + int pos = dev->msi_cap; u16 data; - pci_read_config_dword(dev, msi_lower_address_reg(pos), - &msg->address_lo); + pci_read_config_dword(dev, pos + PCI_MSI_ADDRESS_LO, + &msg->address_lo); if (entry->msi_attrib.is_64) { - pci_read_config_dword(dev, msi_upper_address_reg(pos), - &msg->address_hi); - pci_read_config_word(dev, msi_data_reg(pos, 1), &data); + pci_read_config_dword(dev, pos + PCI_MSI_ADDRESS_HI, + &msg->address_hi); + pci_read_config_word(dev, pos + PCI_MSI_DATA_64, &data); } else { msg->address_hi = 0; - pci_read_config_word(dev, msi_data_reg(pos, 0), &data); + pci_read_config_word(dev, pos + PCI_MSI_DATA_32, &data); } msg->data = data; } @@ -302,24 +298,24 @@ void __write_msi_msg(struct msi_desc *entry, struct msi_msg *msg) writel(msg->data, base + PCI_MSIX_ENTRY_DATA); } else { struct pci_dev *dev = entry->dev; - int pos = entry->msi_attrib.pos; + int pos = dev->msi_cap; u16 msgctl; - pci_read_config_word(dev, msi_control_reg(pos), &msgctl); + pci_read_config_word(dev, pos + PCI_MSI_FLAGS, &msgctl); msgctl &= ~PCI_MSI_FLAGS_QSIZE; msgctl |= entry->msi_attrib.multiple << 4; - pci_write_config_word(dev, msi_control_reg(pos), msgctl); + pci_write_config_word(dev, pos + PCI_MSI_FLAGS, msgctl); - pci_write_config_dword(dev, msi_lower_address_reg(pos), - msg->address_lo); + pci_write_config_dword(dev, pos + PCI_MSI_ADDRESS_LO, + msg->address_lo); if (entry->msi_attrib.is_64) { - pci_write_config_dword(dev, msi_upper_address_reg(pos), - msg->address_hi); - pci_write_config_word(dev, msi_data_reg(pos, 1), - msg->data); + pci_write_config_dword(dev, pos + PCI_MSI_ADDRESS_HI, + msg->address_hi); + pci_write_config_word(dev, pos + PCI_MSI_DATA_64, + msg->data); } else { - pci_write_config_word(dev, msi_data_reg(pos, 0), - msg->data); + pci_write_config_word(dev, pos + PCI_MSI_DATA_32, + msg->data); } } entry->msg = *msg; @@ -391,7 +387,6 @@ static void pci_intx_for_msi(struct pci_dev *dev, int enable) static void __pci_restore_msi_state(struct pci_dev *dev) { - int pos; u16 control; struct msi_desc *entry; @@ -399,22 +394,20 @@ static void __pci_restore_msi_state(struct pci_dev *dev) return; entry = irq_get_msi_desc(dev->irq); - pos = entry->msi_attrib.pos; pci_intx_for_msi(dev, 0); - msi_set_enable(dev, pos, 0); + msi_set_enable(dev, 0); arch_restore_msi_irqs(dev, dev->irq); - pci_read_config_word(dev, pos + PCI_MSI_FLAGS, &control); + pci_read_config_word(dev, dev->msi_cap + PCI_MSI_FLAGS, &control); msi_mask_irq(entry, msi_capable_mask(control), entry->masked); control &= ~PCI_MSI_FLAGS_QSIZE; control |= (entry->msi_attrib.multiple << 4) | PCI_MSI_FLAGS_ENABLE; - pci_write_config_word(dev, pos + PCI_MSI_FLAGS, control); + pci_write_config_word(dev, dev->msi_cap + PCI_MSI_FLAGS, control); } static void __pci_restore_msix_state(struct pci_dev *dev) { - int pos; struct msi_desc *entry; u16 control; @@ -422,13 +415,12 @@ static void __pci_restore_msix_state(struct pci_dev *dev) return; BUG_ON(list_empty(&dev->msi_list)); entry = list_first_entry(&dev->msi_list, struct msi_desc, list); - pos = entry->msi_attrib.pos; - pci_read_config_word(dev, pos + PCI_MSIX_FLAGS, &control); + pci_read_config_word(dev, dev->msix_cap + PCI_MSIX_FLAGS, &control); /* route the table */ pci_intx_for_msi(dev, 0); control |= PCI_MSIX_FLAGS_ENABLE | PCI_MSIX_FLAGS_MASKALL; - pci_write_config_word(dev, pos + PCI_MSIX_FLAGS, control); + pci_write_config_word(dev, dev->msix_cap + PCI_MSIX_FLAGS, control); list_for_each_entry(entry, &dev->msi_list, list) { arch_restore_msi_irqs(dev, entry->irq); @@ -436,7 +428,7 @@ static void __pci_restore_msix_state(struct pci_dev *dev) } control &= ~PCI_MSIX_FLAGS_MASKALL; - pci_write_config_word(dev, pos + PCI_MSIX_FLAGS, control); + pci_write_config_word(dev, dev->msix_cap + PCI_MSIX_FLAGS, control); } void pci_restore_msi_state(struct pci_dev *dev) @@ -484,12 +476,12 @@ static struct msi_attribute mode_attribute = __ATTR(mode, S_IRUGO, show_msi_mode, NULL); -struct attribute *msi_irq_default_attrs[] = { +static struct attribute *msi_irq_default_attrs[] = { &mode_attribute.attr, NULL }; -void msi_kobj_release(struct kobject *kobj) +static void msi_kobj_release(struct kobject *kobj) { struct msi_desc *entry = to_msi_desc(kobj); @@ -552,27 +544,27 @@ out_unroll: static int msi_capability_init(struct pci_dev *dev, int nvec) { struct msi_desc *entry; - int pos, ret; + int ret; u16 control; unsigned mask; - pos = pci_find_capability(dev, PCI_CAP_ID_MSI); - msi_set_enable(dev, pos, 0); /* Disable MSI during set up */ + msi_set_enable(dev, 0); /* Disable MSI during set up */ - pci_read_config_word(dev, msi_control_reg(pos), &control); + pci_read_config_word(dev, dev->msi_cap + PCI_MSI_FLAGS, &control); /* MSI Entry Initialization */ entry = alloc_msi_entry(dev); if (!entry) return -ENOMEM; entry->msi_attrib.is_msix = 0; - entry->msi_attrib.is_64 = is_64bit_address(control); + entry->msi_attrib.is_64 = !!(control & PCI_MSI_FLAGS_64BIT); entry->msi_attrib.entry_nr = 0; - entry->msi_attrib.maskbit = is_mask_bit_support(control); + entry->msi_attrib.maskbit = !!(control & PCI_MSI_FLAGS_MASKBIT); entry->msi_attrib.default_irq = dev->irq; /* Save IOAPIC IRQ */ - entry->msi_attrib.pos = pos; + entry->msi_attrib.pos = dev->msi_cap; - entry->mask_pos = msi_mask_reg(pos, entry->msi_attrib.is_64); + entry->mask_pos = dev->msi_cap + (control & PCI_MSI_FLAGS_64BIT) ? + PCI_MSI_MASK_64 : PCI_MSI_MASK_32; /* All MSIs are unmasked by default, Mask them all */ if (entry->msi_attrib.maskbit) pci_read_config_dword(dev, entry->mask_pos, &entry->masked); @@ -598,31 +590,30 @@ static int msi_capability_init(struct pci_dev *dev, int nvec) /* Set MSI enabled bits */ pci_intx_for_msi(dev, 0); - msi_set_enable(dev, pos, 1); + msi_set_enable(dev, 1); dev->msi_enabled = 1; dev->irq = entry->irq; return 0; } -static void __iomem *msix_map_region(struct pci_dev *dev, unsigned pos, - unsigned nr_entries) +static void __iomem *msix_map_region(struct pci_dev *dev, unsigned nr_entries) { resource_size_t phys_addr; u32 table_offset; u8 bir; - pci_read_config_dword(dev, msix_table_offset_reg(pos), &table_offset); - bir = (u8)(table_offset & PCI_MSIX_FLAGS_BIRMASK); - table_offset &= ~PCI_MSIX_FLAGS_BIRMASK; + pci_read_config_dword(dev, dev->msix_cap + PCI_MSIX_TABLE, + &table_offset); + bir = (u8)(table_offset & PCI_MSIX_TABLE_BIR); + table_offset &= PCI_MSIX_TABLE_OFFSET; phys_addr = pci_resource_start(dev, bir) + table_offset; return ioremap_nocache(phys_addr, nr_entries * PCI_MSIX_ENTRY_SIZE); } -static int msix_setup_entries(struct pci_dev *dev, unsigned pos, - void __iomem *base, struct msix_entry *entries, - int nvec) +static int msix_setup_entries(struct pci_dev *dev, void __iomem *base, + struct msix_entry *entries, int nvec) { struct msi_desc *entry; int i; @@ -642,7 +633,7 @@ static int msix_setup_entries(struct pci_dev *dev, unsigned pos, entry->msi_attrib.is_64 = 1; entry->msi_attrib.entry_nr = entries[i].entry; entry->msi_attrib.default_irq = dev->irq; - entry->msi_attrib.pos = pos; + entry->msi_attrib.pos = dev->msix_cap; entry->mask_base = base; list_add_tail(&entry->list, &dev->msi_list); @@ -652,7 +643,7 @@ static int msix_setup_entries(struct pci_dev *dev, unsigned pos, } static void msix_program_entries(struct pci_dev *dev, - struct msix_entry *entries) + struct msix_entry *entries) { struct msi_desc *entry; int i = 0; @@ -682,23 +673,22 @@ static void msix_program_entries(struct pci_dev *dev, static int msix_capability_init(struct pci_dev *dev, struct msix_entry *entries, int nvec) { - int pos, ret; + int ret; u16 control; void __iomem *base; - pos = pci_find_capability(dev, PCI_CAP_ID_MSIX); - pci_read_config_word(dev, pos + PCI_MSIX_FLAGS, &control); + pci_read_config_word(dev, dev->msix_cap + PCI_MSIX_FLAGS, &control); /* Ensure MSI-X is disabled while it is set up */ control &= ~PCI_MSIX_FLAGS_ENABLE; - pci_write_config_word(dev, pos + PCI_MSIX_FLAGS, control); + pci_write_config_word(dev, dev->msix_cap + PCI_MSIX_FLAGS, control); /* Request & Map MSI-X table region */ - base = msix_map_region(dev, pos, multi_msix_capable(control)); + base = msix_map_region(dev, msix_table_size(control)); if (!base) return -ENOMEM; - ret = msix_setup_entries(dev, pos, base, entries, nvec); + ret = msix_setup_entries(dev, base, entries, nvec); if (ret) return ret; @@ -712,7 +702,7 @@ static int msix_capability_init(struct pci_dev *dev, * interrupts coming in before they're fully set up. */ control |= PCI_MSIX_FLAGS_MASKALL | PCI_MSIX_FLAGS_ENABLE; - pci_write_config_word(dev, pos + PCI_MSIX_FLAGS, control); + pci_write_config_word(dev, dev->msix_cap + PCI_MSIX_FLAGS, control); msix_program_entries(dev, entries); @@ -727,7 +717,7 @@ static int msix_capability_init(struct pci_dev *dev, dev->msix_enabled = 1; control &= ~PCI_MSIX_FLAGS_MASKALL; - pci_write_config_word(dev, pos + PCI_MSIX_FLAGS, control); + pci_write_config_word(dev, dev->msix_cap + PCI_MSIX_FLAGS, control); return 0; @@ -795,9 +785,6 @@ static int pci_msi_check_device(struct pci_dev *dev, int nvec, int type) if (ret) return ret; - if (!pci_find_capability(dev, type)) - return -EINVAL; - return 0; } @@ -816,13 +803,13 @@ static int pci_msi_check_device(struct pci_dev *dev, int nvec, int type) */ int pci_enable_msi_block(struct pci_dev *dev, unsigned int nvec) { - int status, pos, maxvec; + int status, maxvec; u16 msgctl; - pos = pci_find_capability(dev, PCI_CAP_ID_MSI); - if (!pos) + if (!dev->msi_cap) return -EINVAL; - pci_read_config_word(dev, pos + PCI_MSI_FLAGS, &msgctl); + + pci_read_config_word(dev, dev->msi_cap + PCI_MSI_FLAGS, &msgctl); maxvec = 1 << ((msgctl & PCI_MSI_FLAGS_QMASK) >> 1); if (nvec > maxvec) return maxvec; @@ -847,14 +834,13 @@ EXPORT_SYMBOL(pci_enable_msi_block); int pci_enable_msi_block_auto(struct pci_dev *dev, unsigned int *maxvec) { - int ret, pos, nvec; + int ret, nvec; u16 msgctl; - pos = pci_find_capability(dev, PCI_CAP_ID_MSI); - if (!pos) + if (!dev->msi_cap) return -EINVAL; - pci_read_config_word(dev, pos + PCI_MSI_FLAGS, &msgctl); + pci_read_config_word(dev, dev->msi_cap + PCI_MSI_FLAGS, &msgctl); ret = 1 << ((msgctl & PCI_MSI_FLAGS_QMASK) >> 1); if (maxvec) @@ -876,21 +862,19 @@ void pci_msi_shutdown(struct pci_dev *dev) struct msi_desc *desc; u32 mask; u16 ctrl; - unsigned pos; if (!pci_msi_enable || !dev || !dev->msi_enabled) return; BUG_ON(list_empty(&dev->msi_list)); desc = list_first_entry(&dev->msi_list, struct msi_desc, list); - pos = desc->msi_attrib.pos; - msi_set_enable(dev, pos, 0); + msi_set_enable(dev, 0); pci_intx_for_msi(dev, 1); dev->msi_enabled = 0; /* Return the device with MSI unmasked as initial states */ - pci_read_config_word(dev, pos + PCI_MSI_FLAGS, &ctrl); + pci_read_config_word(dev, dev->msi_cap + PCI_MSI_FLAGS, &ctrl); mask = msi_capable_mask(ctrl); /* Keep cached state to be restored */ __msi_mask_irq(desc, mask, ~mask); @@ -917,15 +901,13 @@ EXPORT_SYMBOL(pci_disable_msi); */ int pci_msix_table_size(struct pci_dev *dev) { - int pos; u16 control; - pos = pci_find_capability(dev, PCI_CAP_ID_MSIX); - if (!pos) + if (!dev->msix_cap) return 0; - pci_read_config_word(dev, msi_control_reg(pos), &control); - return multi_msix_capable(control); + pci_read_config_word(dev, dev->msix_cap + PCI_MSIX_FLAGS, &control); + return msix_table_size(control); } /** @@ -948,7 +930,7 @@ int pci_enable_msix(struct pci_dev *dev, struct msix_entry *entries, int nvec) int status, nr_entries; int i, j; - if (!entries) + if (!entries || !dev->msix_cap) return -EINVAL; status = pci_msi_check_device(dev, nvec, PCI_CAP_ID_MSIX); @@ -1048,15 +1030,17 @@ EXPORT_SYMBOL(pci_msi_enabled); void pci_msi_init_pci_dev(struct pci_dev *dev) { - int pos; INIT_LIST_HEAD(&dev->msi_list); /* Disable the msi hardware to avoid screaming interrupts * during boot. This is the power on reset default so * usually this should be a noop. */ - pos = pci_find_capability(dev, PCI_CAP_ID_MSI); - if (pos) - msi_set_enable(dev, pos, 0); - msix_set_enable(dev, 0); + dev->msi_cap = pci_find_capability(dev, PCI_CAP_ID_MSI); + if (dev->msi_cap) + msi_set_enable(dev, 0); + + dev->msix_cap = pci_find_capability(dev, PCI_CAP_ID_MSIX); + if (dev->msix_cap) + msix_set_enable(dev, 0); } diff --git a/drivers/pci/msi.h b/drivers/pci/msi.h deleted file mode 100644 index 65c42f80f23e..000000000000 --- a/drivers/pci/msi.h +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright (C) 2003-2004 Intel - * Copyright (C) Tom Long Nguyen (tom.l.nguyen@intel.com) - */ - -#ifndef MSI_H -#define MSI_H - -#define msi_control_reg(base) (base + PCI_MSI_FLAGS) -#define msi_lower_address_reg(base) (base + PCI_MSI_ADDRESS_LO) -#define msi_upper_address_reg(base) (base + PCI_MSI_ADDRESS_HI) -#define msi_data_reg(base, is64bit) \ - (base + ((is64bit == 1) ? PCI_MSI_DATA_64 : PCI_MSI_DATA_32)) -#define msi_mask_reg(base, is64bit) \ - (base + ((is64bit == 1) ? PCI_MSI_MASK_64 : PCI_MSI_MASK_32)) -#define is_64bit_address(control) (!!(control & PCI_MSI_FLAGS_64BIT)) -#define is_mask_bit_support(control) (!!(control & PCI_MSI_FLAGS_MASKBIT)) - -#define msix_table_offset_reg(base) (base + PCI_MSIX_TABLE) -#define msix_pba_offset_reg(base) (base + PCI_MSIX_PBA) -#define msix_table_size(control) ((control & PCI_MSIX_FLAGS_QSIZE)+1) -#define multi_msix_capable(control) msix_table_size((control)) - -#endif /* MSI_H */ diff --git a/drivers/pci/pci-acpi.c b/drivers/pci/pci-acpi.c index 5147c210df52..e4b1fb2c0f5d 100644 --- a/drivers/pci/pci-acpi.c +++ b/drivers/pci/pci-acpi.c @@ -288,6 +288,32 @@ static struct pci_platform_pm_ops acpi_pci_platform_pm = { .run_wake = acpi_pci_run_wake, }; +void acpi_pci_add_bus(struct pci_bus *bus) +{ + acpi_handle handle = NULL; + + if (bus->bridge) + handle = ACPI_HANDLE(bus->bridge); + if (acpi_pci_disabled || handle == NULL) + return; + + acpi_pci_slot_enumerate(bus, handle); + acpiphp_enumerate_slots(bus, handle); +} + +void acpi_pci_remove_bus(struct pci_bus *bus) +{ + /* + * bus->bridge->acpi_node.handle has already been reset to NULL + * when acpi_pci_remove_bus() is called, so don't check ACPI handle. + */ + if (acpi_pci_disabled) + return; + + acpiphp_remove_slots(bus); + acpi_pci_slot_remove(bus); +} + /* ACPI bus type */ static int acpi_pci_find_device(struct device *dev, acpi_handle *handle) { @@ -362,7 +388,11 @@ static int __init acpi_pci_init(void) ret = register_acpi_bus_type(&acpi_pci_bus); if (ret) return 0; + pci_set_platform_pm(&acpi_pci_platform_pm); + acpi_pci_slot_init(); + acpiphp_init(); + return 0; } arch_initcall(acpi_pci_init); diff --git a/drivers/pci/pci-sysfs.c b/drivers/pci/pci-sysfs.c index 9c6e9bb674ec..5b4a9d9cd200 100644 --- a/drivers/pci/pci-sysfs.c +++ b/drivers/pci/pci-sysfs.c @@ -897,7 +897,7 @@ int pci_mmap_fits(struct pci_dev *pdev, int resno, struct vm_area_struct *vma, if (pci_resource_len(pdev, resno) == 0) return 0; - nr = (vma->vm_end - vma->vm_start) >> PAGE_SHIFT; + nr = vma_pages(vma); start = vma->vm_pgoff; size = ((pci_resource_len(pdev, resno) - 1) >> PAGE_SHIFT) + 1; pci_start = (mmap_api == PCI_MMAP_PROCFS) ? diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index b099e0025d2b..a899d8bb190d 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -646,15 +646,11 @@ static int pci_platform_power_transition(struct pci_dev *dev, pci_power_t state) error = platform_pci_set_power_state(dev, state); if (!error) pci_update_current_state(dev, state); - /* Fall back to PCI_D0 if native PM is not supported */ - if (!dev->pm_cap) - dev->current_state = PCI_D0; - } else { + } else error = -ENODEV; - /* Fall back to PCI_D0 if native PM is not supported */ - if (!dev->pm_cap) - dev->current_state = PCI_D0; - } + + if (error && !dev->pm_cap) /* Fall back to PCI_D0 */ + dev->current_state = PCI_D0; return error; } @@ -1575,7 +1571,7 @@ void pci_pme_active(struct pci_dev *dev, bool enable) { u16 pmcsr; - if (!dev->pm_cap) + if (!dev->pme_support) return; pci_read_config_word(dev, dev->pm_cap + PCI_PM_CTRL, &pmcsr); @@ -1924,6 +1920,7 @@ void pci_pm_init(struct pci_dev *dev) dev->wakeup_prepared = false; dev->pm_cap = 0; + dev->pme_support = 0; /* find PCI PM capability in list */ pm = pci_find_capability(dev, PCI_CAP_ID_PM); @@ -1975,8 +1972,6 @@ void pci_pm_init(struct pci_dev *dev) device_set_wakeup_capable(&dev->dev, true); /* Disable the PME# generation functionality */ pci_pme_active(dev, false); - } else { - dev->pme_support = 0; } } @@ -2619,7 +2614,7 @@ void pci_release_selected_regions(struct pci_dev *pdev, int bars) pci_release_region(pdev, i); } -int __pci_request_selected_regions(struct pci_dev *pdev, int bars, +static int __pci_request_selected_regions(struct pci_dev *pdev, int bars, const char *res_name, int excl) { int i; @@ -3699,7 +3694,7 @@ static DEFINE_SPINLOCK(resource_alignment_lock); * RETURNS: Resource alignment if it is specified. * Zero if it is not specified. */ -resource_size_t pci_specified_resource_alignment(struct pci_dev *dev) +static resource_size_t pci_specified_resource_alignment(struct pci_dev *dev) { int seg, bus, slot, func, align_order, count; resource_size_t align = 0; @@ -3812,7 +3807,7 @@ void pci_reassigndev_resource_alignment(struct pci_dev *dev) } } -ssize_t pci_set_resource_alignment_param(const char *buf, size_t count) +static ssize_t pci_set_resource_alignment_param(const char *buf, size_t count) { if (count > RESOURCE_ALIGNMENT_PARAM_SIZE - 1) count = RESOURCE_ALIGNMENT_PARAM_SIZE - 1; @@ -3823,7 +3818,7 @@ ssize_t pci_set_resource_alignment_param(const char *buf, size_t count) return count; } -ssize_t pci_get_resource_alignment_param(char *buf, size_t size) +static ssize_t pci_get_resource_alignment_param(char *buf, size_t size) { size_t count; spin_lock(&resource_alignment_lock); diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h index 7346ee68f47d..68678ed76b0d 100644 --- a/drivers/pci/pci.h +++ b/drivers/pci/pci.h @@ -8,26 +8,25 @@ /* Functions internal to the PCI core code */ -extern int pci_create_sysfs_dev_files(struct pci_dev *pdev); -extern void pci_remove_sysfs_dev_files(struct pci_dev *pdev); +int pci_create_sysfs_dev_files(struct pci_dev *pdev); +void pci_remove_sysfs_dev_files(struct pci_dev *pdev); #if !defined(CONFIG_DMI) && !defined(CONFIG_ACPI) static inline void pci_create_firmware_label_files(struct pci_dev *pdev) { return; } static inline void pci_remove_firmware_label_files(struct pci_dev *pdev) { return; } #else -extern void pci_create_firmware_label_files(struct pci_dev *pdev); -extern void pci_remove_firmware_label_files(struct pci_dev *pdev); +void pci_create_firmware_label_files(struct pci_dev *pdev); +void pci_remove_firmware_label_files(struct pci_dev *pdev); #endif -extern void pci_cleanup_rom(struct pci_dev *dev); +void pci_cleanup_rom(struct pci_dev *dev); #ifdef HAVE_PCI_MMAP enum pci_mmap_api { PCI_MMAP_SYSFS, /* mmap on /sys/bus/pci/devices/<BDF>/resource<N> */ PCI_MMAP_PROCFS /* mmap on /proc/bus/pci/<BDF> */ }; -extern int pci_mmap_fits(struct pci_dev *pdev, int resno, - struct vm_area_struct *vmai, - enum pci_mmap_api mmap_api); +int pci_mmap_fits(struct pci_dev *pdev, int resno, struct vm_area_struct *vmai, + enum pci_mmap_api mmap_api); #endif int pci_probe_reset_function(struct pci_dev *dev); @@ -60,17 +59,17 @@ struct pci_platform_pm_ops { int (*run_wake)(struct pci_dev *dev, bool enable); }; -extern int pci_set_platform_pm(struct pci_platform_pm_ops *ops); -extern void pci_update_current_state(struct pci_dev *dev, pci_power_t state); -extern void pci_power_up(struct pci_dev *dev); -extern void pci_disable_enabled_device(struct pci_dev *dev); -extern int pci_finish_runtime_suspend(struct pci_dev *dev); -extern int __pci_pme_wakeup(struct pci_dev *dev, void *ign); -extern void pci_wakeup_bus(struct pci_bus *bus); -extern void pci_config_pm_runtime_get(struct pci_dev *dev); -extern void pci_config_pm_runtime_put(struct pci_dev *dev); -extern void pci_pm_init(struct pci_dev *dev); -extern void pci_allocate_cap_save_buffers(struct pci_dev *dev); +int pci_set_platform_pm(struct pci_platform_pm_ops *ops); +void pci_update_current_state(struct pci_dev *dev, pci_power_t state); +void pci_power_up(struct pci_dev *dev); +void pci_disable_enabled_device(struct pci_dev *dev); +int pci_finish_runtime_suspend(struct pci_dev *dev); +int __pci_pme_wakeup(struct pci_dev *dev, void *ign); +void pci_wakeup_bus(struct pci_bus *bus); +void pci_config_pm_runtime_get(struct pci_dev *dev); +void pci_config_pm_runtime_put(struct pci_dev *dev); +void pci_pm_init(struct pci_dev *dev); +void pci_allocate_cap_save_buffers(struct pci_dev *dev); void pci_free_cap_save_buffers(struct pci_dev *dev); static inline void pci_wakeup_event(struct pci_dev *dev) @@ -96,7 +95,7 @@ struct pci_vpd { struct bin_attribute *attr; /* descriptor for sysfs VPD entry */ }; -extern int pci_vpd_pci22_init(struct pci_dev *dev); +int pci_vpd_pci22_init(struct pci_dev *dev); static inline void pci_vpd_release(struct pci_dev *dev) { if (dev->vpd) @@ -105,9 +104,9 @@ static inline void pci_vpd_release(struct pci_dev *dev) /* PCI /proc functions */ #ifdef CONFIG_PROC_FS -extern int pci_proc_attach_device(struct pci_dev *dev); -extern int pci_proc_detach_device(struct pci_dev *dev); -extern int pci_proc_detach_bus(struct pci_bus *bus); +int pci_proc_attach_device(struct pci_dev *dev); +int pci_proc_detach_device(struct pci_dev *dev); +int pci_proc_detach_bus(struct pci_bus *bus); #else static inline int pci_proc_attach_device(struct pci_dev *dev) { return 0; } static inline int pci_proc_detach_device(struct pci_dev *dev) { return 0; } @@ -118,8 +117,8 @@ static inline int pci_proc_detach_bus(struct pci_bus *bus) { return 0; } int pci_hp_add_bridge(struct pci_dev *dev); #ifdef HAVE_PCI_LEGACY -extern void pci_create_legacy_files(struct pci_bus *bus); -extern void pci_remove_legacy_files(struct pci_bus *bus); +void pci_create_legacy_files(struct pci_bus *bus); +void pci_remove_legacy_files(struct pci_bus *bus); #else static inline void pci_create_legacy_files(struct pci_bus *bus) { return; } static inline void pci_remove_legacy_files(struct pci_bus *bus) { return; } @@ -134,7 +133,7 @@ extern unsigned int pci_pm_d3_delay; #ifdef CONFIG_PCI_MSI void pci_no_msi(void); -extern void pci_msi_init_pci_dev(struct pci_dev *dev); +void pci_msi_init_pci_dev(struct pci_dev *dev); #else static inline void pci_no_msi(void) { } static inline void pci_msi_init_pci_dev(struct pci_dev *dev) { } @@ -198,12 +197,11 @@ enum pci_bar_type { bool pci_bus_read_dev_vendor_id(struct pci_bus *bus, int devfn, u32 *pl, int crs_timeout); -extern int pci_setup_device(struct pci_dev *dev); -extern int __pci_read_base(struct pci_dev *dev, enum pci_bar_type type, - struct resource *res, unsigned int reg); -extern int pci_resource_bar(struct pci_dev *dev, int resno, - enum pci_bar_type *type); -extern void pci_configure_ari(struct pci_dev *dev); +int pci_setup_device(struct pci_dev *dev); +int __pci_read_base(struct pci_dev *dev, enum pci_bar_type type, + struct resource *res, unsigned int reg); +int pci_resource_bar(struct pci_dev *dev, int resno, enum pci_bar_type *type); +void pci_configure_ari(struct pci_dev *dev); /** * pci_ari_enabled - query ARI forwarding status @@ -217,7 +215,7 @@ static inline int pci_ari_enabled(struct pci_bus *bus) } void pci_reassigndev_resource_alignment(struct pci_dev *dev); -extern void pci_disable_bridge_window(struct pci_dev *dev); +void pci_disable_bridge_window(struct pci_dev *dev); /* Single Root I/O Virtualization */ struct pci_sriov { @@ -241,7 +239,7 @@ struct pci_sriov { }; #ifdef CONFIG_PCI_ATS -extern void pci_restore_ats_state(struct pci_dev *dev); +void pci_restore_ats_state(struct pci_dev *dev); #else static inline void pci_restore_ats_state(struct pci_dev *dev) { @@ -249,14 +247,13 @@ static inline void pci_restore_ats_state(struct pci_dev *dev) #endif /* CONFIG_PCI_ATS */ #ifdef CONFIG_PCI_IOV -extern int pci_iov_init(struct pci_dev *dev); -extern void pci_iov_release(struct pci_dev *dev); -extern int pci_iov_resource_bar(struct pci_dev *dev, int resno, - enum pci_bar_type *type); -extern resource_size_t pci_sriov_resource_alignment(struct pci_dev *dev, - int resno); -extern void pci_restore_iov_state(struct pci_dev *dev); -extern int pci_iov_bus_range(struct pci_bus *bus); +int pci_iov_init(struct pci_dev *dev); +void pci_iov_release(struct pci_dev *dev); +int pci_iov_resource_bar(struct pci_dev *dev, int resno, + enum pci_bar_type *type); +resource_size_t pci_sriov_resource_alignment(struct pci_dev *dev, int resno); +void pci_restore_iov_state(struct pci_dev *dev); +int pci_iov_bus_range(struct pci_bus *bus); #else static inline int pci_iov_init(struct pci_dev *dev) @@ -282,10 +279,10 @@ static inline int pci_iov_bus_range(struct pci_bus *bus) #endif /* CONFIG_PCI_IOV */ -extern unsigned long pci_cardbus_resource_alignment(struct resource *); +unsigned long pci_cardbus_resource_alignment(struct resource *); static inline resource_size_t pci_resource_alignment(struct pci_dev *dev, - struct resource *res) + struct resource *res) { #ifdef CONFIG_PCI_IOV int resno = res - dev->resource; @@ -298,7 +295,7 @@ static inline resource_size_t pci_resource_alignment(struct pci_dev *dev, return resource_alignment(res); } -extern void pci_enable_acs(struct pci_dev *dev); +void pci_enable_acs(struct pci_dev *dev); struct pci_dev_reset_methods { u16 vendor; @@ -307,7 +304,7 @@ struct pci_dev_reset_methods { }; #ifdef CONFIG_PCI_QUIRKS -extern int pci_dev_specific_reset(struct pci_dev *dev, int probe); +int pci_dev_specific_reset(struct pci_dev *dev, int probe); #else static inline int pci_dev_specific_reset(struct pci_dev *dev, int probe) { diff --git a/drivers/pci/pcie/Kconfig b/drivers/pci/pcie/Kconfig index fde4a32a0295..569f82fc9e22 100644 --- a/drivers/pci/pcie/Kconfig +++ b/drivers/pci/pcie/Kconfig @@ -82,4 +82,4 @@ endchoice config PCIE_PME def_bool y - depends on PCIEPORTBUS && PM_RUNTIME && ACPI + depends on PCIEPORTBUS && PM_RUNTIME diff --git a/drivers/pci/pcie/aer/aer_inject.c b/drivers/pci/pcie/aer/aer_inject.c index 4e24cb8a94ae..587e7e853107 100644 --- a/drivers/pci/pcie/aer/aer_inject.c +++ b/drivers/pci/pcie/aer/aer_inject.c @@ -212,8 +212,8 @@ out: return ops->read(bus, devfn, where, size, val); } -int pci_write_aer(struct pci_bus *bus, unsigned int devfn, int where, int size, - u32 val) +static int pci_write_aer(struct pci_bus *bus, unsigned int devfn, int where, + int size, u32 val) { u32 *sim; struct aer_error *err; @@ -334,13 +334,13 @@ static int aer_inject(struct aer_error_inj *einj) return -ENODEV; rpdev = pcie_find_root_port(dev); if (!rpdev) { - ret = -ENOTTY; + ret = -ENODEV; goto out_put; } pos_cap_err = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ERR); if (!pos_cap_err) { - ret = -ENOTTY; + ret = -EPERM; goto out_put; } pci_read_config_dword(dev, pos_cap_err + PCI_ERR_UNCOR_SEVER, &sever); @@ -350,7 +350,7 @@ static int aer_inject(struct aer_error_inj *einj) rp_pos_cap_err = pci_find_ext_capability(rpdev, PCI_EXT_CAP_ID_ERR); if (!rp_pos_cap_err) { - ret = -ENOTTY; + ret = -EPERM; goto out_put; } diff --git a/drivers/pci/pcie/aer/aerdrv.h b/drivers/pci/pcie/aer/aerdrv.h index 22f840f4dda1..d12c77cd6991 100644 --- a/drivers/pci/pcie/aer/aerdrv.h +++ b/drivers/pci/pcie/aer/aerdrv.h @@ -110,15 +110,15 @@ static inline pci_ers_result_t merge_result(enum pci_ers_result orig, } extern struct bus_type pcie_port_bus_type; -extern void aer_do_secondary_bus_reset(struct pci_dev *dev); -extern int aer_init(struct pcie_device *dev); -extern void aer_isr(struct work_struct *work); -extern void aer_print_error(struct pci_dev *dev, struct aer_err_info *info); -extern void aer_print_port_info(struct pci_dev *dev, struct aer_err_info *info); -extern irqreturn_t aer_irq(int irq, void *context); +void aer_do_secondary_bus_reset(struct pci_dev *dev); +int aer_init(struct pcie_device *dev); +void aer_isr(struct work_struct *work); +void aer_print_error(struct pci_dev *dev, struct aer_err_info *info); +void aer_print_port_info(struct pci_dev *dev, struct aer_err_info *info); +irqreturn_t aer_irq(int irq, void *context); #ifdef CONFIG_ACPI_APEI -extern int pcie_aer_get_firmware_first(struct pci_dev *pci_dev); +int pcie_aer_get_firmware_first(struct pci_dev *pci_dev); #else static inline int pcie_aer_get_firmware_first(struct pci_dev *pci_dev) { diff --git a/drivers/pci/pcie/aer/aerdrv_core.c b/drivers/pci/pcie/aer/aerdrv_core.c index 564d97f94b6c..8ec8b4f48560 100644 --- a/drivers/pci/pcie/aer/aerdrv_core.c +++ b/drivers/pci/pcie/aer/aerdrv_core.c @@ -89,8 +89,6 @@ static int add_error_device(struct aer_err_info *e_info, struct pci_dev *dev) return -ENOSPC; } -#define PCI_BUS(x) (((x) >> 8) & 0xff) - /** * is_error_source - check whether the device is source of reported error * @dev: pointer to pci_dev to be checked @@ -106,7 +104,7 @@ static bool is_error_source(struct pci_dev *dev, struct aer_err_info *e_info) * When bus id is equal to 0, it might be a bad id * reported by root port. */ - if (!nosourceid && (PCI_BUS(e_info->id) != 0)) { + if (!nosourceid && (PCI_BUS_NUM(e_info->id) != 0)) { /* Device ID match? */ if (e_info->id == ((dev->bus->number << 8) | dev->devfn)) return true; diff --git a/drivers/pci/pcie/pme.c b/drivers/pci/pcie/pme.c index 9ca0dc9ffd84..795db1f9d50c 100644 --- a/drivers/pci/pcie/pme.c +++ b/drivers/pci/pcie/pme.c @@ -19,8 +19,6 @@ #include <linux/interrupt.h> #include <linux/device.h> #include <linux/pcieport_if.h> -#include <linux/acpi.h> -#include <linux/pci-acpi.h> #include <linux/pm_runtime.h> #include "../pci.h" diff --git a/drivers/pci/pcie/portdrv.h b/drivers/pci/pcie/portdrv.h index eea2ca2375e6..d2eb80aab569 100644 --- a/drivers/pci/pcie/portdrv.h +++ b/drivers/pci/pcie/portdrv.h @@ -21,18 +21,18 @@ #define get_descriptor_id(type, service) (((type - 4) << 4) | service) extern struct bus_type pcie_port_bus_type; -extern int pcie_port_device_register(struct pci_dev *dev); +int pcie_port_device_register(struct pci_dev *dev); #ifdef CONFIG_PM -extern int pcie_port_device_suspend(struct device *dev); -extern int pcie_port_device_resume(struct device *dev); +int pcie_port_device_suspend(struct device *dev); +int pcie_port_device_resume(struct device *dev); #endif -extern void pcie_port_device_remove(struct pci_dev *dev); -extern int __must_check pcie_port_bus_register(void); -extern void pcie_port_bus_unregister(void); +void pcie_port_device_remove(struct pci_dev *dev); +int __must_check pcie_port_bus_register(void); +void pcie_port_bus_unregister(void); struct pci_dev; -extern void pcie_clear_root_pme_status(struct pci_dev *dev); +void pcie_clear_root_pme_status(struct pci_dev *dev); #ifdef CONFIG_HOTPLUG_PCI_PCIE extern bool pciehp_msi_disabled; @@ -59,7 +59,7 @@ static inline bool pcie_pme_no_msi(void) return pcie_pme_msi_disabled; } -extern void pcie_pme_interrupt_enable(struct pci_dev *dev, bool enable); +void pcie_pme_interrupt_enable(struct pci_dev *dev, bool enable); #else /* !CONFIG_PCIE_PME */ static inline void pcie_pme_disable_msi(void) {} static inline bool pcie_pme_no_msi(void) { return false; } @@ -67,7 +67,7 @@ static inline void pcie_pme_interrupt_enable(struct pci_dev *dev, bool en) {} #endif /* !CONFIG_PCIE_PME */ #ifdef CONFIG_ACPI -extern int pcie_port_acpi_setup(struct pci_dev *port, int *mask); +int pcie_port_acpi_setup(struct pci_dev *port, int *mask); static inline int pcie_port_platform_notify(struct pci_dev *port, int *mask) { diff --git a/drivers/pci/pcie/portdrv_acpi.c b/drivers/pci/pcie/portdrv_acpi.c index a86b56e5f2f2..b4d2894ee3fc 100644 --- a/drivers/pci/pcie/portdrv_acpi.c +++ b/drivers/pci/pcie/portdrv_acpi.c @@ -17,6 +17,7 @@ #include "aer/aerdrv.h" #include "../pci.h" +#include "portdrv.h" /** * pcie_port_acpi_setup - Request the BIOS to release control of PCIe services. diff --git a/drivers/pci/pcie/portdrv_pci.c b/drivers/pci/pcie/portdrv_pci.c index ed4d09498337..696caed5fdf5 100644 --- a/drivers/pci/pcie/portdrv_pci.c +++ b/drivers/pci/pcie/portdrv_pci.c @@ -259,11 +259,9 @@ static pci_ers_result_t pcie_portdrv_error_detected(struct pci_dev *dev, enum pci_channel_state error) { struct aer_broadcast_data data = {error, PCI_ERS_RESULT_CAN_RECOVER}; - int ret; - - /* can not fail */ - ret = device_for_each_child(&dev->dev, &data, error_detected_iter); + /* get true return value from &data */ + device_for_each_child(&dev->dev, &data, error_detected_iter); return data.result; } @@ -295,10 +293,9 @@ static int mmio_enabled_iter(struct device *device, void *data) static pci_ers_result_t pcie_portdrv_mmio_enabled(struct pci_dev *dev) { pci_ers_result_t status = PCI_ERS_RESULT_RECOVERED; - int retval; /* get true return value from &status */ - retval = device_for_each_child(&dev->dev, &status, mmio_enabled_iter); + device_for_each_child(&dev->dev, &status, mmio_enabled_iter); return status; } @@ -330,7 +327,6 @@ static int slot_reset_iter(struct device *device, void *data) static pci_ers_result_t pcie_portdrv_slot_reset(struct pci_dev *dev) { pci_ers_result_t status = PCI_ERS_RESULT_RECOVERED; - int retval; /* If fatal, restore cfg space for possible link reset at upstream */ if (dev->error_state == pci_channel_io_frozen) { @@ -341,8 +337,7 @@ static pci_ers_result_t pcie_portdrv_slot_reset(struct pci_dev *dev) } /* get true return value from &status */ - retval = device_for_each_child(&dev->dev, &status, slot_reset_iter); - + device_for_each_child(&dev->dev, &status, slot_reset_iter); return status; } @@ -368,9 +363,7 @@ static int resume_iter(struct device *device, void *data) static void pcie_portdrv_err_resume(struct pci_dev *dev) { - int retval; - /* nothing to do with error value, if it ever happens */ - retval = device_for_each_child(&dev->dev, NULL, resume_iter); + device_for_each_child(&dev->dev, NULL, resume_iter); } /* diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index b494066ef32f..43ece5d41d36 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -673,6 +673,8 @@ add_dev: ret = device_register(&child->dev); WARN_ON(ret < 0); + pcibios_add_bus(child); + /* Create legacy_io and legacy_mem files for this bus */ pci_create_legacy_files(child); @@ -1627,8 +1629,7 @@ unsigned int pci_scan_child_bus(struct pci_bus *bus) if (!bus->is_added) { dev_dbg(&bus->dev, "fixups for bus\n"); pcibios_fixup_bus(bus); - if (pci_is_root_bus(bus)) - bus->is_added = 1; + bus->is_added = 1; } for (pass=0; pass < 2; pass++) @@ -1661,6 +1662,14 @@ int __weak pcibios_root_bridge_prepare(struct pci_host_bridge *bridge) return 0; } +void __weak pcibios_add_bus(struct pci_bus *bus) +{ +} + +void __weak pcibios_remove_bus(struct pci_bus *bus) +{ +} + struct pci_bus *pci_create_root_bus(struct device *parent, int bus, struct pci_ops *ops, void *sysdata, struct list_head *resources) { @@ -1715,6 +1724,8 @@ struct pci_bus *pci_create_root_bus(struct device *parent, int bus, if (error) goto class_dev_reg_err; + pcibios_add_bus(b); + /* Create legacy_io and legacy_mem files for this bus */ pci_create_legacy_files(b); diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c index 0369fb6fc1da..7d68aeebf56b 100644 --- a/drivers/pci/quirks.c +++ b/drivers/pci/quirks.c @@ -324,29 +324,30 @@ static void quirk_cs5536_vsa(struct pci_dev *dev) } DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_CS5536_ISA, quirk_cs5536_vsa); -static void quirk_io_region(struct pci_dev *dev, unsigned region, - unsigned size, int nr, const char *name) +static void quirk_io_region(struct pci_dev *dev, int port, + unsigned size, int nr, const char *name) { - region &= ~(size-1); - if (region) { - struct pci_bus_region bus_region; - struct resource *res = dev->resource + nr; + u16 region; + struct pci_bus_region bus_region; + struct resource *res = dev->resource + nr; - res->name = pci_name(dev); - res->start = region; - res->end = region + size - 1; - res->flags = IORESOURCE_IO; + pci_read_config_word(dev, port, ®ion); + region &= ~(size - 1); - /* Convert from PCI bus to resource space. */ - bus_region.start = res->start; - bus_region.end = res->end; - pcibios_bus_to_resource(dev, res, &bus_region); + if (!region) + return; - if (pci_claim_resource(dev, nr) == 0) - dev_info(&dev->dev, "quirk: %pR claimed by %s\n", - res, name); - } -} + res->name = pci_name(dev); + res->flags = IORESOURCE_IO; + + /* Convert from PCI bus to resource space */ + bus_region.start = region; + bus_region.end = region + size - 1; + pcibios_bus_to_resource(dev, res, &bus_region); + + if (!pci_claim_resource(dev, nr)) + dev_info(&dev->dev, "quirk: %pR claimed by %s\n", res, name); +} /* * ATI Northbridge setups MCE the processor if you even @@ -374,12 +375,8 @@ DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RS100, quirk_ati_ */ static void quirk_ali7101_acpi(struct pci_dev *dev) { - u16 region; - - pci_read_config_word(dev, 0xE0, ®ion); - quirk_io_region(dev, region, 64, PCI_BRIDGE_RESOURCES, "ali7101 ACPI"); - pci_read_config_word(dev, 0xE2, ®ion); - quirk_io_region(dev, region, 32, PCI_BRIDGE_RESOURCES+1, "ali7101 SMB"); + quirk_io_region(dev, 0xE0, 64, PCI_BRIDGE_RESOURCES, "ali7101 ACPI"); + quirk_io_region(dev, 0xE2, 32, PCI_BRIDGE_RESOURCES+1, "ali7101 SMB"); } DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_AL, PCI_DEVICE_ID_AL_M7101, quirk_ali7101_acpi); @@ -442,12 +439,10 @@ static void piix4_mem_quirk(struct pci_dev *dev, const char *name, unsigned int */ static void quirk_piix4_acpi(struct pci_dev *dev) { - u32 region, res_a; + u32 res_a; - pci_read_config_dword(dev, 0x40, ®ion); - quirk_io_region(dev, region, 64, PCI_BRIDGE_RESOURCES, "PIIX4 ACPI"); - pci_read_config_dword(dev, 0x90, ®ion); - quirk_io_region(dev, region, 16, PCI_BRIDGE_RESOURCES+1, "PIIX4 SMB"); + quirk_io_region(dev, 0x40, 64, PCI_BRIDGE_RESOURCES, "PIIX4 ACPI"); + quirk_io_region(dev, 0x90, 16, PCI_BRIDGE_RESOURCES+1, "PIIX4 SMB"); /* Device resource A has enables for some of the other ones */ pci_read_config_dword(dev, 0x5c, &res_a); @@ -491,7 +486,6 @@ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82443MX_3, qui */ static void quirk_ich4_lpc_acpi(struct pci_dev *dev) { - u32 region; u8 enable; /* @@ -503,22 +497,14 @@ static void quirk_ich4_lpc_acpi(struct pci_dev *dev) */ pci_read_config_byte(dev, ICH_ACPI_CNTL, &enable); - if (enable & ICH4_ACPI_EN) { - pci_read_config_dword(dev, ICH_PMBASE, ®ion); - region &= PCI_BASE_ADDRESS_IO_MASK; - if (region >= PCIBIOS_MIN_IO) - quirk_io_region(dev, region, 128, PCI_BRIDGE_RESOURCES, - "ICH4 ACPI/GPIO/TCO"); - } + if (enable & ICH4_ACPI_EN) + quirk_io_region(dev, ICH_PMBASE, 128, PCI_BRIDGE_RESOURCES, + "ICH4 ACPI/GPIO/TCO"); pci_read_config_byte(dev, ICH4_GPIO_CNTL, &enable); - if (enable & ICH4_GPIO_EN) { - pci_read_config_dword(dev, ICH4_GPIOBASE, ®ion); - region &= PCI_BASE_ADDRESS_IO_MASK; - if (region >= PCIBIOS_MIN_IO) - quirk_io_region(dev, region, 64, - PCI_BRIDGE_RESOURCES + 1, "ICH4 GPIO"); - } + if (enable & ICH4_GPIO_EN) + quirk_io_region(dev, ICH4_GPIOBASE, 64, PCI_BRIDGE_RESOURCES+1, + "ICH4 GPIO"); } DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801AA_0, quirk_ich4_lpc_acpi); DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801AB_0, quirk_ich4_lpc_acpi); @@ -533,26 +519,17 @@ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ESB_1, qui static void ich6_lpc_acpi_gpio(struct pci_dev *dev) { - u32 region; u8 enable; pci_read_config_byte(dev, ICH_ACPI_CNTL, &enable); - if (enable & ICH6_ACPI_EN) { - pci_read_config_dword(dev, ICH_PMBASE, ®ion); - region &= PCI_BASE_ADDRESS_IO_MASK; - if (region >= PCIBIOS_MIN_IO) - quirk_io_region(dev, region, 128, PCI_BRIDGE_RESOURCES, - "ICH6 ACPI/GPIO/TCO"); - } + if (enable & ICH6_ACPI_EN) + quirk_io_region(dev, ICH_PMBASE, 128, PCI_BRIDGE_RESOURCES, + "ICH6 ACPI/GPIO/TCO"); pci_read_config_byte(dev, ICH6_GPIO_CNTL, &enable); - if (enable & ICH6_GPIO_EN) { - pci_read_config_dword(dev, ICH6_GPIOBASE, ®ion); - region &= PCI_BASE_ADDRESS_IO_MASK; - if (region >= PCIBIOS_MIN_IO) - quirk_io_region(dev, region, 64, - PCI_BRIDGE_RESOURCES + 1, "ICH6 GPIO"); - } + if (enable & ICH6_GPIO_EN) + quirk_io_region(dev, ICH6_GPIOBASE, 64, PCI_BRIDGE_RESOURCES+1, + "ICH6 GPIO"); } static void ich6_lpc_generic_decode(struct pci_dev *dev, unsigned reg, const char *name, int dynsize) @@ -650,13 +627,9 @@ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH10_1, qui */ static void quirk_vt82c586_acpi(struct pci_dev *dev) { - u32 region; - - if (dev->revision & 0x10) { - pci_read_config_dword(dev, 0x48, ®ion); - region &= PCI_BASE_ADDRESS_IO_MASK; - quirk_io_region(dev, region, 256, PCI_BRIDGE_RESOURCES, "vt82c586 ACPI"); - } + if (dev->revision & 0x10) + quirk_io_region(dev, 0x48, 256, PCI_BRIDGE_RESOURCES, + "vt82c586 ACPI"); } DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C586_3, quirk_vt82c586_acpi); @@ -668,18 +641,12 @@ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C586_3, quirk_vt */ static void quirk_vt82c686_acpi(struct pci_dev *dev) { - u16 hm; - u32 smb; - quirk_vt82c586_acpi(dev); - pci_read_config_word(dev, 0x70, &hm); - hm &= PCI_BASE_ADDRESS_IO_MASK; - quirk_io_region(dev, hm, 128, PCI_BRIDGE_RESOURCES + 1, "vt82c686 HW-mon"); + quirk_io_region(dev, 0x70, 128, PCI_BRIDGE_RESOURCES+1, + "vt82c686 HW-mon"); - pci_read_config_dword(dev, 0x90, &smb); - smb &= PCI_BASE_ADDRESS_IO_MASK; - quirk_io_region(dev, smb, 16, PCI_BRIDGE_RESOURCES + 2, "vt82c686 SMB"); + quirk_io_region(dev, 0x90, 16, PCI_BRIDGE_RESOURCES+2, "vt82c686 SMB"); } DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C686_4, quirk_vt82c686_acpi); @@ -690,15 +657,8 @@ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C686_4, quirk_vt */ static void quirk_vt8235_acpi(struct pci_dev *dev) { - u16 pm, smb; - - pci_read_config_word(dev, 0x88, &pm); - pm &= PCI_BASE_ADDRESS_IO_MASK; - quirk_io_region(dev, pm, 128, PCI_BRIDGE_RESOURCES, "vt8235 PM"); - - pci_read_config_word(dev, 0xd0, &smb); - smb &= PCI_BASE_ADDRESS_IO_MASK; - quirk_io_region(dev, smb, 16, PCI_BRIDGE_RESOURCES + 1, "vt8235 SMB"); + quirk_io_region(dev, 0x88, 128, PCI_BRIDGE_RESOURCES, "vt8235 PM"); + quirk_io_region(dev, 0xd0, 16, PCI_BRIDGE_RESOURCES+1, "vt8235 SMB"); } DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_8235, quirk_vt8235_acpi); @@ -2594,6 +2554,14 @@ static void quirk_msi_intx_disable_ati_bug(struct pci_dev *dev) dev->dev_flags |= PCI_DEV_FLAGS_MSI_INTX_DISABLE_BUG; pci_dev_put(p); } +static void quirk_msi_intx_disable_qca_bug(struct pci_dev *dev) +{ + /* AR816X/AR817X/E210X MSI is fixed at HW level from revision 0x18 */ + if (dev->revision < 0x18) { + dev_info(&dev->dev, "set MSI_INTX_DISABLE_BUG flag\n"); + dev->dev_flags |= PCI_DEV_FLAGS_MSI_INTX_DISABLE_BUG; + } +} DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5780, quirk_msi_intx_disable_bug); @@ -2643,6 +2611,16 @@ DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_ATTANSIC, 0x1073, quirk_msi_intx_disable_bug); DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_ATTANSIC, 0x1083, quirk_msi_intx_disable_bug); +DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_ATTANSIC, 0x1090, + quirk_msi_intx_disable_qca_bug); +DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_ATTANSIC, 0x1091, + quirk_msi_intx_disable_qca_bug); +DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_ATTANSIC, 0x10a0, + quirk_msi_intx_disable_qca_bug); +DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_ATTANSIC, 0x10a1, + quirk_msi_intx_disable_qca_bug); +DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_ATTANSIC, 0xe091, + quirk_msi_intx_disable_qca_bug); #endif /* CONFIG_PCI_MSI */ /* Allow manual resource allocation for PCI hotplug bridges diff --git a/drivers/pci/remove.c b/drivers/pci/remove.c index cc875e6ed159..8fc54b7327bc 100644 --- a/drivers/pci/remove.c +++ b/drivers/pci/remove.c @@ -50,10 +50,8 @@ void pci_remove_bus(struct pci_bus *bus) list_del(&bus->node); pci_bus_release_busn_res(bus); up_write(&pci_bus_sem); - if (!bus->is_added) - return; - pci_remove_legacy_files(bus); + pcibios_remove_bus(bus); device_unregister(&bus->dev); } EXPORT_SYMBOL(pci_remove_bus); diff --git a/drivers/pci/setup-bus.c b/drivers/pci/setup-bus.c index 7e8739e25b9e..16abaaa1f83c 100644 --- a/drivers/pci/setup-bus.c +++ b/drivers/pci/setup-bus.c @@ -1044,7 +1044,7 @@ handle_done: ; } -void __ref __pci_bus_size_bridges(struct pci_bus *bus, +static void __ref __pci_bus_size_bridges(struct pci_bus *bus, struct list_head *realloc_head) { struct pci_dev *dev; @@ -1545,6 +1545,8 @@ again: enable_all: retval = pci_reenable_device(bridge); + if (retval) + dev_err(&bridge->dev, "Error reenabling bridge (%d)\n", retval); pci_set_master(bridge); pci_enable_bridges(parent); } diff --git a/drivers/pci/setup-res.c b/drivers/pci/setup-res.c index 81b88bda7930..07f2eddc09ce 100644 --- a/drivers/pci/setup-res.c +++ b/drivers/pci/setup-res.c @@ -261,7 +261,6 @@ int pci_assign_resource(struct pci_dev *dev, int resno) { struct resource *res = dev->resource + resno; resource_size_t align, size; - struct pci_bus *bus; int ret; align = pci_resource_alignment(dev, res); @@ -271,7 +270,6 @@ int pci_assign_resource(struct pci_dev *dev, int resno) return -EINVAL; } - bus = dev->bus; size = resource_size(res); ret = _pci_assign_resource(dev, resno, size, align); diff --git a/drivers/pci/slot.c b/drivers/pci/slot.c index ac6412fb8d6f..c1e9284a677b 100644 --- a/drivers/pci/slot.c +++ b/drivers/pci/slot.c @@ -377,14 +377,17 @@ void pci_hp_create_module_link(struct pci_slot *pci_slot) { struct hotplug_slot *slot = pci_slot->hotplug; struct kobject *kobj = NULL; - int no_warn; + int ret; if (!slot || !slot->ops) return; kobj = kset_find_obj(module_kset, slot->ops->mod_name); if (!kobj) return; - no_warn = sysfs_create_link(&pci_slot->kobj, kobj, "module"); + ret = sysfs_create_link(&pci_slot->kobj, kobj, "module"); + if (ret) + dev_err(&pci_slot->bus->dev, "Error creating sysfs link (%d)\n", + ret); kobject_put(kobj); } EXPORT_SYMBOL_GPL(pci_hp_create_module_link); diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig index 34f51d2d90d2..f910962baaa7 100644 --- a/drivers/pinctrl/Kconfig +++ b/drivers/pinctrl/Kconfig @@ -106,20 +106,11 @@ config PINCTRL_LANTIQ select PINMUX select PINCONF -config PINCTRL_PXA3xx - bool - select PINMUX - config PINCTRL_FALCON bool depends on SOC_FALCON depends on PINCTRL_LANTIQ -config PINCTRL_MMP2 - bool "MMP2 pin controller driver" - depends on ARCH_MMP - select PINCTRL_PXA3xx - config PINCTRL_MXS bool select PINMUX @@ -151,21 +142,12 @@ config PINCTRL_DB8540 bool "DB8540 pin controller driver" depends on PINCTRL_NOMADIK && ARCH_U8500 -config PINCTRL_PXA168 - bool "PXA168 pin controller driver" - depends on ARCH_MMP - select PINCTRL_PXA3xx - -config PINCTRL_PXA910 - bool "PXA910 pin controller driver" - depends on ARCH_MMP - select PINCTRL_PXA3xx - config PINCTRL_SINGLE tristate "One-register-per-pin type device tree based pinctrl driver" depends on OF select PINMUX select PINCONF + select GENERIC_PINCONF help This selects the device tree based generic pinctrl driver. @@ -226,6 +208,11 @@ config PINCTRL_EXYNOS5440 select PINMUX select PINCONF +config PINCTRL_S3C64XX + bool "Samsung S3C64XX SoC pinctrl driver" + depends on ARCH_S3C64XX + select PINCTRL_SAMSUNG + source "drivers/pinctrl/mvebu/Kconfig" source "drivers/pinctrl/sh-pfc/Kconfig" source "drivers/pinctrl/spear/Kconfig" diff --git a/drivers/pinctrl/Makefile b/drivers/pinctrl/Makefile index f82cc5baf767..988279ae23cd 100644 --- a/drivers/pinctrl/Makefile +++ b/drivers/pinctrl/Makefile @@ -21,9 +21,7 @@ obj-$(CONFIG_PINCTRL_IMX35) += pinctrl-imx35.o obj-$(CONFIG_PINCTRL_IMX51) += pinctrl-imx51.o obj-$(CONFIG_PINCTRL_IMX53) += pinctrl-imx53.o obj-$(CONFIG_PINCTRL_IMX6Q) += pinctrl-imx6q.o -obj-$(CONFIG_PINCTRL_PXA3xx) += pinctrl-pxa3xx.o obj-$(CONFIG_PINCTRL_FALCON) += pinctrl-falcon.o -obj-$(CONFIG_PINCTRL_MMP2) += pinctrl-mmp2.o obj-$(CONFIG_PINCTRL_MXS) += pinctrl-mxs.o obj-$(CONFIG_PINCTRL_IMX23) += pinctrl-imx23.o obj-$(CONFIG_PINCTRL_IMX28) += pinctrl-imx28.o @@ -31,8 +29,6 @@ obj-$(CONFIG_PINCTRL_NOMADIK) += pinctrl-nomadik.o obj-$(CONFIG_PINCTRL_STN8815) += pinctrl-nomadik-stn8815.o obj-$(CONFIG_PINCTRL_DB8500) += pinctrl-nomadik-db8500.o obj-$(CONFIG_PINCTRL_DB8540) += pinctrl-nomadik-db8540.o -obj-$(CONFIG_PINCTRL_PXA168) += pinctrl-pxa168.o -obj-$(CONFIG_PINCTRL_PXA910) += pinctrl-pxa910.o obj-$(CONFIG_PINCTRL_SINGLE) += pinctrl-single.o obj-$(CONFIG_PINCTRL_SIRF) += pinctrl-sirf.o obj-$(CONFIG_PINCTRL_SUNXI) += pinctrl-sunxi.o @@ -45,6 +41,7 @@ obj-$(CONFIG_PINCTRL_COH901) += pinctrl-coh901.o obj-$(CONFIG_PINCTRL_SAMSUNG) += pinctrl-samsung.o obj-$(CONFIG_PINCTRL_EXYNOS) += pinctrl-exynos.o obj-$(CONFIG_PINCTRL_EXYNOS5440) += pinctrl-exynos5440.o +obj-$(CONFIG_PINCTRL_S3C64XX) += pinctrl-s3c64xx.o obj-$(CONFIG_PINCTRL_XWAY) += pinctrl-xway.o obj-$(CONFIG_PINCTRL_LANTIQ) += pinctrl-lantiq.o diff --git a/drivers/pinctrl/core.c b/drivers/pinctrl/core.c index b0de6e7f1fdb..c3d222ed39a2 100644 --- a/drivers/pinctrl/core.c +++ b/drivers/pinctrl/core.c @@ -27,6 +27,11 @@ #include <linux/pinctrl/consumer.h> #include <linux/pinctrl/pinctrl.h> #include <linux/pinctrl/machine.h> + +#ifdef CONFIG_GPIOLIB +#include <asm-generic/gpio.h> +#endif + #include "core.h" #include "devicetree.h" #include "pinmux.h" @@ -35,11 +40,17 @@ static bool pinctrl_dummy_state; -/* Mutex taken by all entry points */ -DEFINE_MUTEX(pinctrl_mutex); +/* Mutex taken to protect pinctrl_list */ +DEFINE_MUTEX(pinctrl_list_mutex); + +/* Mutex taken to protect pinctrl_maps */ +DEFINE_MUTEX(pinctrl_maps_mutex); + +/* Mutex taken to protect pinctrldev_list */ +DEFINE_MUTEX(pinctrldev_list_mutex); /* Global list of pin control devices (struct pinctrl_dev) */ -LIST_HEAD(pinctrldev_list); +static LIST_HEAD(pinctrldev_list); /* List of pin controller handles (struct pinctrl) */ static LIST_HEAD(pinctrl_list); @@ -106,6 +117,23 @@ struct pinctrl_dev *get_pinctrl_dev_from_devname(const char *devname) return found ? pctldev : NULL; } +struct pinctrl_dev *get_pinctrl_dev_from_of_node(struct device_node *np) +{ + struct pinctrl_dev *pctldev; + + mutex_lock(&pinctrldev_list_mutex); + + list_for_each_entry(pctldev, &pinctrldev_list, node) + if (pctldev->dev->of_node == np) { + mutex_unlock(&pinctrldev_list_mutex); + return pctldev; + } + + mutex_lock(&pinctrldev_list_mutex); + + return NULL; +} + /** * pin_get_from_name() - look up a pin number from a name * @pctldev: the pin control device to lookup the pin on @@ -165,9 +193,9 @@ bool pin_is_valid(struct pinctrl_dev *pctldev, int pin) if (pin < 0) return false; - mutex_lock(&pinctrl_mutex); + mutex_lock(&pctldev->mutex); pindesc = pin_desc_get(pctldev, pin); - mutex_unlock(&pinctrl_mutex); + mutex_unlock(&pctldev->mutex); return pindesc != NULL; } @@ -264,19 +292,58 @@ pinctrl_match_gpio_range(struct pinctrl_dev *pctldev, unsigned gpio) { struct pinctrl_gpio_range *range = NULL; + mutex_lock(&pctldev->mutex); /* Loop over the ranges */ list_for_each_entry(range, &pctldev->gpio_ranges, node) { /* Check if we're in the valid range */ if (gpio >= range->base && gpio < range->base + range->npins) { + mutex_unlock(&pctldev->mutex); return range; } } - + mutex_unlock(&pctldev->mutex); return NULL; } /** + * pinctrl_ready_for_gpio_range() - check if other GPIO pins of + * the same GPIO chip are in range + * @gpio: gpio pin to check taken from the global GPIO pin space + * + * This function is complement of pinctrl_match_gpio_range(). If the return + * value of pinctrl_match_gpio_range() is NULL, this function could be used + * to check whether pinctrl device is ready or not. Maybe some GPIO pins + * of the same GPIO chip don't have back-end pinctrl interface. + * If the return value is true, it means that pinctrl device is ready & the + * certain GPIO pin doesn't have back-end pinctrl device. If the return value + * is false, it means that pinctrl device may not be ready. + */ +#ifdef CONFIG_GPIOLIB +static bool pinctrl_ready_for_gpio_range(unsigned gpio) +{ + struct pinctrl_dev *pctldev; + struct pinctrl_gpio_range *range = NULL; + struct gpio_chip *chip = gpio_to_chip(gpio); + + /* Loop over the pin controllers */ + list_for_each_entry(pctldev, &pinctrldev_list, node) { + /* Loop over the ranges */ + list_for_each_entry(range, &pctldev->gpio_ranges, node) { + /* Check if any gpio range overlapped with gpio chip */ + if (range->base + range->npins - 1 < chip->base || + range->base > chip->base + chip->ngpio - 1) + continue; + return true; + } + } + return false; +} +#else +static bool pinctrl_ready_for_gpio_range(unsigned gpio) { return true; } +#endif + +/** * pinctrl_get_device_gpio_range() - find device for GPIO range * @gpio: the pin to locate the pin controller for * @outdev: the pin control device if found @@ -319,9 +386,9 @@ static int pinctrl_get_device_gpio_range(unsigned gpio, void pinctrl_add_gpio_range(struct pinctrl_dev *pctldev, struct pinctrl_gpio_range *range) { - mutex_lock(&pinctrl_mutex); + mutex_lock(&pctldev->mutex); list_add_tail(&range->node, &pctldev->gpio_ranges); - mutex_unlock(&pinctrl_mutex); + mutex_unlock(&pctldev->mutex); } EXPORT_SYMBOL_GPL(pinctrl_add_gpio_range); @@ -339,17 +406,25 @@ EXPORT_SYMBOL_GPL(pinctrl_add_gpio_ranges); struct pinctrl_dev *pinctrl_find_and_add_gpio_range(const char *devname, struct pinctrl_gpio_range *range) { - struct pinctrl_dev *pctldev = get_pinctrl_dev_from_devname(devname); + struct pinctrl_dev *pctldev; + + mutex_lock(&pinctrldev_list_mutex); + + pctldev = get_pinctrl_dev_from_devname(devname); /* * If we can't find this device, let's assume that is because * it has not probed yet, so the driver trying to register this * range need to defer probing. */ - if (!pctldev) + if (!pctldev) { + mutex_unlock(&pinctrldev_list_mutex); return ERR_PTR(-EPROBE_DEFER); - + } pinctrl_add_gpio_range(pctldev, range); + + mutex_unlock(&pinctrldev_list_mutex); + return pctldev; } EXPORT_SYMBOL_GPL(pinctrl_find_and_add_gpio_range); @@ -365,14 +440,17 @@ pinctrl_find_gpio_range_from_pin(struct pinctrl_dev *pctldev, { struct pinctrl_gpio_range *range = NULL; + mutex_lock(&pctldev->mutex); /* Loop over the ranges */ list_for_each_entry(range, &pctldev->gpio_ranges, node) { /* Check if we're in the valid range */ if (pin >= range->pin_base && pin < range->pin_base + range->npins) { + mutex_unlock(&pctldev->mutex); return range; } } + mutex_unlock(&pctldev->mutex); return NULL; } @@ -386,9 +464,9 @@ EXPORT_SYMBOL_GPL(pinctrl_find_gpio_range_from_pin); void pinctrl_remove_gpio_range(struct pinctrl_dev *pctldev, struct pinctrl_gpio_range *range) { - mutex_lock(&pinctrl_mutex); + mutex_lock(&pctldev->mutex); list_del(&range->node); - mutex_unlock(&pinctrl_mutex); + mutex_unlock(&pctldev->mutex); } EXPORT_SYMBOL_GPL(pinctrl_remove_gpio_range); @@ -439,11 +517,13 @@ int pinctrl_request_gpio(unsigned gpio) int ret; int pin; - mutex_lock(&pinctrl_mutex); + mutex_lock(&pinctrldev_list_mutex); ret = pinctrl_get_device_gpio_range(gpio, &pctldev, &range); if (ret) { - mutex_unlock(&pinctrl_mutex); + if (pinctrl_ready_for_gpio_range(gpio)) + ret = 0; + mutex_unlock(&pinctrldev_list_mutex); return ret; } @@ -452,7 +532,7 @@ int pinctrl_request_gpio(unsigned gpio) ret = pinmux_request_gpio(pctldev, range, pin, gpio); - mutex_unlock(&pinctrl_mutex); + mutex_unlock(&pinctrldev_list_mutex); return ret; } EXPORT_SYMBOL_GPL(pinctrl_request_gpio); @@ -472,20 +552,22 @@ void pinctrl_free_gpio(unsigned gpio) int ret; int pin; - mutex_lock(&pinctrl_mutex); + mutex_lock(&pinctrldev_list_mutex); ret = pinctrl_get_device_gpio_range(gpio, &pctldev, &range); if (ret) { - mutex_unlock(&pinctrl_mutex); + mutex_unlock(&pinctrldev_list_mutex); return; } + mutex_lock(&pctldev->mutex); /* Convert to the pin controllers number space */ pin = gpio - range->base + range->pin_base; pinmux_free_gpio(pctldev, pin, range); - mutex_unlock(&pinctrl_mutex); + mutex_unlock(&pctldev->mutex); + mutex_unlock(&pinctrldev_list_mutex); } EXPORT_SYMBOL_GPL(pinctrl_free_gpio); @@ -496,14 +578,24 @@ static int pinctrl_gpio_direction(unsigned gpio, bool input) int ret; int pin; + mutex_lock(&pinctrldev_list_mutex); + ret = pinctrl_get_device_gpio_range(gpio, &pctldev, &range); - if (ret) + if (ret) { + mutex_unlock(&pinctrldev_list_mutex); return ret; + } + + mutex_lock(&pctldev->mutex); /* Convert to the pin controllers number space */ pin = gpio - range->base + range->pin_base; + ret = pinmux_gpio_direction(pctldev, range, pin, input); + + mutex_unlock(&pctldev->mutex); + mutex_unlock(&pinctrldev_list_mutex); - return pinmux_gpio_direction(pctldev, range, pin, input); + return ret; } /** @@ -516,11 +608,7 @@ static int pinctrl_gpio_direction(unsigned gpio, bool input) */ int pinctrl_gpio_direction_input(unsigned gpio) { - int ret; - mutex_lock(&pinctrl_mutex); - ret = pinctrl_gpio_direction(gpio, true); - mutex_unlock(&pinctrl_mutex); - return ret; + return pinctrl_gpio_direction(gpio, true); } EXPORT_SYMBOL_GPL(pinctrl_gpio_direction_input); @@ -534,11 +622,7 @@ EXPORT_SYMBOL_GPL(pinctrl_gpio_direction_input); */ int pinctrl_gpio_direction_output(unsigned gpio) { - int ret; - mutex_lock(&pinctrl_mutex); - ret = pinctrl_gpio_direction(gpio, false); - mutex_unlock(&pinctrl_mutex); - return ret; + return pinctrl_gpio_direction(gpio, false); } EXPORT_SYMBOL_GPL(pinctrl_gpio_direction_output); @@ -641,14 +725,18 @@ static struct pinctrl *find_pinctrl(struct device *dev) { struct pinctrl *p; + mutex_lock(&pinctrl_list_mutex); list_for_each_entry(p, &pinctrl_list, node) - if (p->dev == dev) + if (p->dev == dev) { + mutex_unlock(&pinctrl_list_mutex); return p; + } + mutex_unlock(&pinctrl_list_mutex); return NULL; } -static void pinctrl_put_locked(struct pinctrl *p, bool inlist); +static void pinctrl_free(struct pinctrl *p, bool inlist); static struct pinctrl *create_pinctrl(struct device *dev) { @@ -681,6 +769,7 @@ static struct pinctrl *create_pinctrl(struct device *dev) devname = dev_name(dev); + mutex_lock(&pinctrl_maps_mutex); /* Iterate over the pin control maps to locate the right ones */ for_each_maps(maps_node, i, map) { /* Map must be for this device */ @@ -702,13 +791,16 @@ static struct pinctrl *create_pinctrl(struct device *dev) * an -EPROBE_DEFER later, as that is the worst case. */ if (ret == -EPROBE_DEFER) { - pinctrl_put_locked(p, false); + pinctrl_free(p, false); + mutex_unlock(&pinctrl_maps_mutex); return ERR_PTR(ret); } } + mutex_unlock(&pinctrl_maps_mutex); + if (ret < 0) { /* If some other error than deferral occured, return here */ - pinctrl_put_locked(p, false); + pinctrl_free(p, false); return ERR_PTR(ret); } @@ -720,7 +812,11 @@ static struct pinctrl *create_pinctrl(struct device *dev) return p; } -static struct pinctrl *pinctrl_get_locked(struct device *dev) +/** + * pinctrl_get() - retrieves the pinctrl handle for a device + * @dev: the device to obtain the handle for + */ +struct pinctrl *pinctrl_get(struct device *dev) { struct pinctrl *p; @@ -741,43 +837,35 @@ static struct pinctrl *pinctrl_get_locked(struct device *dev) return create_pinctrl(dev); } +EXPORT_SYMBOL_GPL(pinctrl_get); -/** - * pinctrl_get() - retrieves the pinctrl handle for a device - * @dev: the device to obtain the handle for - */ -struct pinctrl *pinctrl_get(struct device *dev) +static void pinctrl_free_setting(bool disable_setting, + struct pinctrl_setting *setting) { - struct pinctrl *p; - - mutex_lock(&pinctrl_mutex); - p = pinctrl_get_locked(dev); - mutex_unlock(&pinctrl_mutex); - - return p; + switch (setting->type) { + case PIN_MAP_TYPE_MUX_GROUP: + if (disable_setting) + pinmux_disable_setting(setting); + pinmux_free_setting(setting); + break; + case PIN_MAP_TYPE_CONFIGS_PIN: + case PIN_MAP_TYPE_CONFIGS_GROUP: + pinconf_free_setting(setting); + break; + default: + break; + } } -EXPORT_SYMBOL_GPL(pinctrl_get); -static void pinctrl_put_locked(struct pinctrl *p, bool inlist) +static void pinctrl_free(struct pinctrl *p, bool inlist) { struct pinctrl_state *state, *n1; struct pinctrl_setting *setting, *n2; + mutex_lock(&pinctrl_list_mutex); list_for_each_entry_safe(state, n1, &p->states, node) { list_for_each_entry_safe(setting, n2, &state->settings, node) { - switch (setting->type) { - case PIN_MAP_TYPE_MUX_GROUP: - if (state == p->state) - pinmux_disable_setting(setting); - pinmux_free_setting(setting); - break; - case PIN_MAP_TYPE_CONFIGS_PIN: - case PIN_MAP_TYPE_CONFIGS_GROUP: - pinconf_free_setting(setting); - break; - default: - break; - } + pinctrl_free_setting(state == p->state, setting); list_del(&setting->node); kfree(setting); } @@ -790,6 +878,7 @@ static void pinctrl_put_locked(struct pinctrl *p, bool inlist) if (inlist) list_del(&p->node); kfree(p); + mutex_unlock(&pinctrl_list_mutex); } /** @@ -800,7 +889,7 @@ static void pinctrl_release(struct kref *kref) { struct pinctrl *p = container_of(kref, struct pinctrl, users); - pinctrl_put_locked(p, true); + pinctrl_free(p, true); } /** @@ -809,14 +898,17 @@ static void pinctrl_release(struct kref *kref) */ void pinctrl_put(struct pinctrl *p) { - mutex_lock(&pinctrl_mutex); kref_put(&p->users, pinctrl_release); - mutex_unlock(&pinctrl_mutex); } EXPORT_SYMBOL_GPL(pinctrl_put); -static struct pinctrl_state *pinctrl_lookup_state_locked(struct pinctrl *p, - const char *name) +/** + * pinctrl_lookup_state() - retrieves a state handle from a pinctrl handle + * @p: the pinctrl handle to retrieve the state from + * @name: the state name to retrieve + */ +struct pinctrl_state *pinctrl_lookup_state(struct pinctrl *p, + const char *name) { struct pinctrl_state *state; @@ -833,28 +925,17 @@ static struct pinctrl_state *pinctrl_lookup_state_locked(struct pinctrl *p, return state; } +EXPORT_SYMBOL_GPL(pinctrl_lookup_state); /** - * pinctrl_lookup_state() - retrieves a state handle from a pinctrl handle - * @p: the pinctrl handle to retrieve the state from - * @name: the state name to retrieve + * pinctrl_select_state() - select/activate/program a pinctrl state to HW + * @p: the pinctrl handle for the device that requests configuration + * @state: the state handle to select/activate/program */ -struct pinctrl_state *pinctrl_lookup_state(struct pinctrl *p, const char *name) -{ - struct pinctrl_state *s; - - mutex_lock(&pinctrl_mutex); - s = pinctrl_lookup_state_locked(p, name); - mutex_unlock(&pinctrl_mutex); - - return s; -} -EXPORT_SYMBOL_GPL(pinctrl_lookup_state); - -static int pinctrl_select_state_locked(struct pinctrl *p, - struct pinctrl_state *state) +int pinctrl_select_state(struct pinctrl *p, struct pinctrl_state *state) { struct pinctrl_setting *setting, *setting2; + struct pinctrl_state *old_state = p->state; int ret; if (p->state == state) @@ -888,7 +969,7 @@ static int pinctrl_select_state_locked(struct pinctrl *p, } } - p->state = state; + p->state = NULL; /* Apply all the settings for the new state */ list_for_each_entry(setting, &state->settings, node) { @@ -904,27 +985,36 @@ static int pinctrl_select_state_locked(struct pinctrl *p, ret = -EINVAL; break; } + if (ret < 0) { - /* FIXME: Difficult to return to prev state */ - return ret; + goto unapply_new_state; } } + p->state = state; + return 0; -} -/** - * pinctrl_select() - select/activate/program a pinctrl state to HW - * @p: the pinctrl handle for the device that requests configuratio - * @state: the state handle to select/activate/program - */ -int pinctrl_select_state(struct pinctrl *p, struct pinctrl_state *state) -{ - int ret; +unapply_new_state: + dev_err(p->dev, "Error applying setting, reverse things back\n"); - mutex_lock(&pinctrl_mutex); - ret = pinctrl_select_state_locked(p, state); - mutex_unlock(&pinctrl_mutex); + list_for_each_entry(setting2, &state->settings, node) { + if (&setting2->node == &setting->node) + break; + /* + * All we can do here is pinmux_disable_setting. + * That means that some pins are muxed differently now + * than they were before applying the setting (We can't + * "unmux a pin"!), but it's not a big deal since the pins + * are free to be muxed by another apply_setting. + */ + if (setting2->type == PIN_MAP_TYPE_MUX_GROUP) + pinmux_disable_setting(setting2); + } + + /* There's no infinite recursive loop here because p->state is NULL */ + if (old_state) + pinctrl_select_state(p, old_state); return ret; } @@ -979,9 +1069,8 @@ static int devm_pinctrl_match(struct device *dev, void *res, void *data) */ void devm_pinctrl_put(struct pinctrl *p) { - WARN_ON(devres_destroy(p->dev, devm_pinctrl_release, + WARN_ON(devres_release(p->dev, devm_pinctrl_release, devm_pinctrl_match, p)); - pinctrl_put(p); } EXPORT_SYMBOL_GPL(devm_pinctrl_put); @@ -1055,10 +1144,10 @@ int pinctrl_register_map(struct pinctrl_map const *maps, unsigned num_maps, } if (!locked) - mutex_lock(&pinctrl_mutex); + mutex_lock(&pinctrl_maps_mutex); list_add_tail(&maps_node->node, &pinctrl_maps); if (!locked) - mutex_unlock(&pinctrl_mutex); + mutex_unlock(&pinctrl_maps_mutex); return 0; } @@ -1080,12 +1169,15 @@ void pinctrl_unregister_map(struct pinctrl_map const *map) { struct pinctrl_maps *maps_node; + mutex_lock(&pinctrl_maps_mutex); list_for_each_entry(maps_node, &pinctrl_maps, node) { if (maps_node->maps == map) { list_del(&maps_node->node); + mutex_unlock(&pinctrl_maps_mutex); return; } } + mutex_unlock(&pinctrl_maps_mutex); } /** @@ -1122,7 +1214,7 @@ static int pinctrl_pins_show(struct seq_file *s, void *what) seq_printf(s, "registered pins: %d\n", pctldev->desc->npins); - mutex_lock(&pinctrl_mutex); + mutex_lock(&pctldev->mutex); /* The pin number can be retrived from the pin controller descriptor */ for (i = 0; i < pctldev->desc->npins; i++) { @@ -1144,7 +1236,7 @@ static int pinctrl_pins_show(struct seq_file *s, void *what) seq_puts(s, "\n"); } - mutex_unlock(&pinctrl_mutex); + mutex_unlock(&pctldev->mutex); return 0; } @@ -1155,8 +1247,9 @@ static int pinctrl_groups_show(struct seq_file *s, void *what) const struct pinctrl_ops *ops = pctldev->desc->pctlops; unsigned ngroups, selector = 0; + mutex_lock(&pctldev->mutex); + ngroups = ops->get_groups_count(pctldev); - mutex_lock(&pinctrl_mutex); seq_puts(s, "registered pin groups:\n"); while (selector < ngroups) { @@ -1177,7 +1270,7 @@ static int pinctrl_groups_show(struct seq_file *s, void *what) for (i = 0; i < num_pins; i++) { pname = pin_get_name(pctldev, pins[i]); if (WARN_ON(!pname)) { - mutex_unlock(&pinctrl_mutex); + mutex_unlock(&pctldev->mutex); return -EINVAL; } seq_printf(s, "pin %d (%s)\n", pins[i], pname); @@ -1187,7 +1280,7 @@ static int pinctrl_groups_show(struct seq_file *s, void *what) selector++; } - mutex_unlock(&pinctrl_mutex); + mutex_unlock(&pctldev->mutex); return 0; } @@ -1199,7 +1292,7 @@ static int pinctrl_gpioranges_show(struct seq_file *s, void *what) seq_puts(s, "GPIO ranges handled:\n"); - mutex_lock(&pinctrl_mutex); + mutex_lock(&pctldev->mutex); /* Loop over the ranges */ list_for_each_entry(range, &pctldev->gpio_ranges, node) { @@ -1210,7 +1303,7 @@ static int pinctrl_gpioranges_show(struct seq_file *s, void *what) (range->pin_base + range->npins - 1)); } - mutex_unlock(&pinctrl_mutex); + mutex_unlock(&pctldev->mutex); return 0; } @@ -1221,7 +1314,7 @@ static int pinctrl_devices_show(struct seq_file *s, void *what) seq_puts(s, "name [pinmux] [pinconf]\n"); - mutex_lock(&pinctrl_mutex); + mutex_lock(&pinctrldev_list_mutex); list_for_each_entry(pctldev, &pinctrldev_list, node) { seq_printf(s, "%s ", pctldev->desc->name); @@ -1236,7 +1329,7 @@ static int pinctrl_devices_show(struct seq_file *s, void *what) seq_puts(s, "\n"); } - mutex_unlock(&pinctrl_mutex); + mutex_unlock(&pinctrldev_list_mutex); return 0; } @@ -1265,8 +1358,7 @@ static int pinctrl_maps_show(struct seq_file *s, void *what) seq_puts(s, "Pinctrl maps:\n"); - mutex_lock(&pinctrl_mutex); - + mutex_lock(&pinctrl_maps_mutex); for_each_maps(maps_node, i, map) { seq_printf(s, "device %s\nstate %s\ntype %s (%d)\n", map->dev_name, map->name, map_type(map->type), @@ -1290,8 +1382,7 @@ static int pinctrl_maps_show(struct seq_file *s, void *what) seq_printf(s, "\n"); } - - mutex_unlock(&pinctrl_mutex); + mutex_unlock(&pinctrl_maps_mutex); return 0; } @@ -1304,7 +1395,7 @@ static int pinctrl_show(struct seq_file *s, void *what) seq_puts(s, "Requested pin control handlers their pinmux maps:\n"); - mutex_lock(&pinctrl_mutex); + mutex_lock(&pinctrl_list_mutex); list_for_each_entry(p, &pinctrl_list, node) { seq_printf(s, "device: %s current state: %s\n", @@ -1336,7 +1427,7 @@ static int pinctrl_show(struct seq_file *s, void *what) } } - mutex_unlock(&pinctrl_mutex); + mutex_unlock(&pinctrl_list_mutex); return 0; } @@ -1522,6 +1613,7 @@ struct pinctrl_dev *pinctrl_register(struct pinctrl_desc *pctldesc, INIT_RADIX_TREE(&pctldev->pin_desc_tree, GFP_KERNEL); INIT_LIST_HEAD(&pctldev->gpio_ranges); pctldev->dev = dev; + mutex_init(&pctldev->mutex); /* check core ops for sanity */ if (pinctrl_check_ops(pctldev)) { @@ -1551,38 +1643,37 @@ struct pinctrl_dev *pinctrl_register(struct pinctrl_desc *pctldesc, goto out_err; } - mutex_lock(&pinctrl_mutex); - + mutex_lock(&pinctrldev_list_mutex); list_add_tail(&pctldev->node, &pinctrldev_list); + mutex_unlock(&pinctrldev_list_mutex); + + pctldev->p = pinctrl_get(pctldev->dev); - pctldev->p = pinctrl_get_locked(pctldev->dev); if (!IS_ERR(pctldev->p)) { pctldev->hog_default = - pinctrl_lookup_state_locked(pctldev->p, - PINCTRL_STATE_DEFAULT); + pinctrl_lookup_state(pctldev->p, PINCTRL_STATE_DEFAULT); if (IS_ERR(pctldev->hog_default)) { dev_dbg(dev, "failed to lookup the default state\n"); } else { - if (pinctrl_select_state_locked(pctldev->p, + if (pinctrl_select_state(pctldev->p, pctldev->hog_default)) dev_err(dev, "failed to select default state\n"); } pctldev->hog_sleep = - pinctrl_lookup_state_locked(pctldev->p, + pinctrl_lookup_state(pctldev->p, PINCTRL_STATE_SLEEP); if (IS_ERR(pctldev->hog_sleep)) dev_dbg(dev, "failed to lookup the sleep state\n"); } - mutex_unlock(&pinctrl_mutex); - pinctrl_init_device_debugfs(pctldev); return pctldev; out_err: + mutex_destroy(&pctldev->mutex); kfree(pctldev); return NULL; } @@ -1600,12 +1691,13 @@ void pinctrl_unregister(struct pinctrl_dev *pctldev) if (pctldev == NULL) return; - pinctrl_remove_device_debugfs(pctldev); + mutex_lock(&pinctrldev_list_mutex); + mutex_lock(&pctldev->mutex); - mutex_lock(&pinctrl_mutex); + pinctrl_remove_device_debugfs(pctldev); if (!IS_ERR(pctldev->p)) - pinctrl_put_locked(pctldev->p, true); + pinctrl_put(pctldev->p); /* TODO: check that no pinmuxes are still active? */ list_del(&pctldev->node); @@ -1616,9 +1708,10 @@ void pinctrl_unregister(struct pinctrl_dev *pctldev) list_for_each_entry_safe(range, n, &pctldev->gpio_ranges, node) list_del(&range->node); + mutex_unlock(&pctldev->mutex); + mutex_destroy(&pctldev->mutex); kfree(pctldev); - - mutex_unlock(&pinctrl_mutex); + mutex_unlock(&pinctrldev_list_mutex); } EXPORT_SYMBOL_GPL(pinctrl_unregister); diff --git a/drivers/pinctrl/core.h b/drivers/pinctrl/core.h index ee72f1f6d862..75476b3d87da 100644 --- a/drivers/pinctrl/core.h +++ b/drivers/pinctrl/core.h @@ -33,6 +33,7 @@ struct pinctrl_gpio_range; * @p: result of pinctrl_get() for this device * @hog_default: default state for pins hogged by this device * @hog_sleep: sleep state for pins hogged by this device + * @mutex: mutex taken on each pin controller specific action * @device_root: debugfs root for this device */ struct pinctrl_dev { @@ -46,6 +47,7 @@ struct pinctrl_dev { struct pinctrl *p; struct pinctrl_state *hog_default; struct pinctrl_state *hog_sleep; + struct mutex mutex; #ifdef CONFIG_DEBUG_FS struct dentry *device_root; #endif @@ -72,7 +74,7 @@ struct pinctrl { /** * struct pinctrl_state - a pinctrl state for a device - * @node: list not for struct pinctrl's @states field + * @node: list node for struct pinctrl's @states field * @name: the name of this state * @settings: a list of settings for this state */ @@ -168,6 +170,7 @@ struct pinctrl_maps { }; struct pinctrl_dev *get_pinctrl_dev_from_devname(const char *dev_name); +struct pinctrl_dev *get_pinctrl_dev_from_of_node(struct device_node *np); int pin_get_from_name(struct pinctrl_dev *pctldev, const char *name); const char *pin_get_name(struct pinctrl_dev *pctldev, const unsigned pin); int pinctrl_get_group_selector(struct pinctrl_dev *pctldev, @@ -186,8 +189,7 @@ void pinctrl_unregister_map(struct pinctrl_map const *map); extern int pinctrl_force_sleep(struct pinctrl_dev *pctldev); extern int pinctrl_force_default(struct pinctrl_dev *pctldev); -extern struct mutex pinctrl_mutex; -extern struct list_head pinctrldev_list; +extern struct mutex pinctrl_maps_mutex; extern struct list_head pinctrl_maps; #define for_each_maps(_maps_node_, _i_, _map_) \ diff --git a/drivers/pinctrl/devicetree.c b/drivers/pinctrl/devicetree.c index fd40a11ad645..340fb4e6c600 100644 --- a/drivers/pinctrl/devicetree.c +++ b/drivers/pinctrl/devicetree.c @@ -41,7 +41,7 @@ static void dt_free_map(struct pinctrl_dev *pctldev, struct pinctrl_map *map, unsigned num_maps) { if (pctldev) { - struct pinctrl_ops *ops = pctldev->desc->pctlops; + const struct pinctrl_ops *ops = pctldev->desc->pctlops; ops->dt_free_map(pctldev, map, num_maps); } else { /* There is no pctldev for PIN_MAP_TYPE_DUMMY_STATE */ @@ -95,22 +95,11 @@ static int dt_remember_or_free_map(struct pinctrl *p, const char *statename, return pinctrl_register_map(map, num_maps, false, true); } -static struct pinctrl_dev *find_pinctrl_by_of_node(struct device_node *np) -{ - struct pinctrl_dev *pctldev; - - list_for_each_entry(pctldev, &pinctrldev_list, node) - if (pctldev->dev->of_node == np) - return pctldev; - - return NULL; -} - struct pinctrl_dev *of_pinctrl_get(struct device_node *np) { struct pinctrl_dev *pctldev; - pctldev = find_pinctrl_by_of_node(np); + pctldev = get_pinctrl_dev_from_of_node(np); if (!pctldev) return NULL; @@ -122,7 +111,7 @@ static int dt_to_map_one_config(struct pinctrl *p, const char *statename, { struct device_node *np_pctldev; struct pinctrl_dev *pctldev; - struct pinctrl_ops *ops; + const struct pinctrl_ops *ops; int ret; struct pinctrl_map *map; unsigned num_maps; @@ -138,7 +127,7 @@ static int dt_to_map_one_config(struct pinctrl *p, const char *statename, /* OK let's just assume this will appear later then */ return -EPROBE_DEFER; } - pctldev = find_pinctrl_by_of_node(np_pctldev); + pctldev = get_pinctrl_dev_from_of_node(np_pctldev); if (pctldev) break; /* Do not defer probing of hogs (circular loop) */ diff --git a/drivers/pinctrl/mvebu/pinctrl-mvebu.c b/drivers/pinctrl/mvebu/pinctrl-mvebu.c index 2d2f0a43d36b..bb7ddb1bc89f 100644 --- a/drivers/pinctrl/mvebu/pinctrl-mvebu.c +++ b/drivers/pinctrl/mvebu/pinctrl-mvebu.c @@ -263,7 +263,7 @@ static void mvebu_pinconf_group_dbg_show(struct pinctrl_dev *pctldev, return; } -static struct pinconf_ops mvebu_pinconf_ops = { +static const struct pinconf_ops mvebu_pinconf_ops = { .pin_config_group_get = mvebu_pinconf_group_get, .pin_config_group_set = mvebu_pinconf_group_set, .pin_config_group_dbg_show = mvebu_pinconf_group_dbg_show, @@ -369,7 +369,7 @@ static int mvebu_pinmux_gpio_set_direction(struct pinctrl_dev *pctldev, return -ENOTSUPP; } -static struct pinmux_ops mvebu_pinmux_ops = { +static const struct pinmux_ops mvebu_pinmux_ops = { .get_functions_count = mvebu_pinmux_get_funcs_count, .get_function_name = mvebu_pinmux_get_func_name, .get_function_groups = mvebu_pinmux_get_groups, @@ -470,7 +470,7 @@ static void mvebu_pinctrl_dt_free_map(struct pinctrl_dev *pctldev, kfree(map); } -static struct pinctrl_ops mvebu_pinctrl_ops = { +static const struct pinctrl_ops mvebu_pinctrl_ops = { .get_groups_count = mvebu_pinctrl_get_groups_count, .get_group_name = mvebu_pinctrl_get_group_name, .get_group_pins = mvebu_pinctrl_get_group_pins, @@ -478,8 +478,12 @@ static struct pinctrl_ops mvebu_pinctrl_ops = { .dt_free_map = mvebu_pinctrl_dt_free_map, }; -static int _add_function(struct mvebu_pinctrl_function *funcs, const char *name) +static int _add_function(struct mvebu_pinctrl_function *funcs, int *funcsize, + const char *name) { + if (*funcsize <= 0) + return -EOVERFLOW; + while (funcs->num_groups) { /* function already there */ if (strcmp(funcs->name, name) == 0) { @@ -488,8 +492,12 @@ static int _add_function(struct mvebu_pinctrl_function *funcs, const char *name) } funcs++; } + + /* append new unique function */ funcs->name = name; funcs->num_groups = 1; + (*funcsize)--; + return 0; } @@ -497,12 +505,12 @@ static int mvebu_pinctrl_build_functions(struct platform_device *pdev, struct mvebu_pinctrl *pctl) { struct mvebu_pinctrl_function *funcs; - int num = 0; + int num = 0, funcsize = pctl->desc.npins; int n, s; /* we allocate functions for number of pins and hope - * there are less unique functions than pins available */ - funcs = devm_kzalloc(&pdev->dev, pctl->desc.npins * + * there are fewer unique functions than pins available */ + funcs = devm_kzalloc(&pdev->dev, funcsize * sizeof(struct mvebu_pinctrl_function), GFP_KERNEL); if (!funcs) return -ENOMEM; @@ -510,26 +518,27 @@ static int mvebu_pinctrl_build_functions(struct platform_device *pdev, for (n = 0; n < pctl->num_groups; n++) { struct mvebu_pinctrl_group *grp = &pctl->groups[n]; for (s = 0; s < grp->num_settings; s++) { + int ret; + /* skip unsupported settings on this variant */ if (pctl->variant && !(pctl->variant & grp->settings[s].variant)) continue; /* check for unique functions and count groups */ - if (_add_function(funcs, grp->settings[s].name)) + ret = _add_function(funcs, &funcsize, + grp->settings[s].name); + if (ret == -EOVERFLOW) + dev_err(&pdev->dev, + "More functions than pins(%d)\n", + pctl->desc.npins); + if (ret < 0) continue; num++; } } - /* with the number of unique functions and it's groups known, - reallocate functions and assign group names */ - funcs = krealloc(funcs, num * sizeof(struct mvebu_pinctrl_function), - GFP_KERNEL); - if (!funcs) - return -ENOMEM; - pctl->num_functions = num; pctl->functions = funcs; diff --git a/drivers/pinctrl/pinconf-generic.c b/drivers/pinctrl/pinconf-generic.c index 06c304ac6f7d..2ad5a8d337b5 100644 --- a/drivers/pinctrl/pinconf-generic.c +++ b/drivers/pinctrl/pinconf-generic.c @@ -12,6 +12,7 @@ #define pr_fmt(fmt) "generic pinconfig core: " fmt #include <linux/kernel.h> +#include <linux/module.h> #include <linux/init.h> #include <linux/device.h> #include <linux/slab.h> @@ -33,7 +34,7 @@ struct pin_config_item { #define PCONFDUMP(a, b, c) { .param = a, .display = b, .format = c } -struct pin_config_item conf_items[] = { +static struct pin_config_item conf_items[] = { PCONFDUMP(PIN_CONFIG_BIAS_DISABLE, "input bias disabled", NULL), PCONFDUMP(PIN_CONFIG_BIAS_HIGH_IMPEDANCE, "input bias high impedance", NULL), PCONFDUMP(PIN_CONFIG_BIAS_PULL_UP, "input bias pull up", NULL), @@ -59,7 +60,7 @@ void pinconf_generic_dump_pin(struct pinctrl_dev *pctldev, if (!ops->is_generic) return; - for(i = 0; i < ARRAY_SIZE(conf_items); i++) { + for (i = 0; i < ARRAY_SIZE(conf_items); i++) { unsigned long config; int ret; @@ -94,7 +95,7 @@ void pinconf_generic_dump_group(struct pinctrl_dev *pctldev, if (!ops->is_generic) return; - for(i = 0; i < ARRAY_SIZE(conf_items); i++) { + for (i = 0; i < ARRAY_SIZE(conf_items); i++) { unsigned long config; int ret; @@ -120,4 +121,17 @@ void pinconf_generic_dump_group(struct pinctrl_dev *pctldev, } } +void pinconf_generic_dump_config(struct pinctrl_dev *pctldev, + struct seq_file *s, unsigned long config) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(conf_items); i++) { + if (pinconf_to_config_param(config) != conf_items[i].param) + continue; + seq_printf(s, "%s: 0x%x", conf_items[i].display, + pinconf_to_config_argument(config)); + } +} +EXPORT_SYMBOL_GPL(pinconf_generic_dump_config); #endif diff --git a/drivers/pinctrl/pinconf.c b/drivers/pinctrl/pinconf.c index d611ecfcbf70..c67c37e23dd7 100644 --- a/drivers/pinctrl/pinconf.c +++ b/drivers/pinctrl/pinconf.c @@ -17,6 +17,7 @@ #include <linux/slab.h> #include <linux/debugfs.h> #include <linux/seq_file.h> +#include <linux/uaccess.h> #include <linux/pinctrl/machine.h> #include <linux/pinctrl/pinctrl.h> #include <linux/pinctrl/pinconf.h> @@ -88,14 +89,14 @@ int pin_config_get(const char *dev_name, const char *name, struct pinctrl_dev *pctldev; int pin; - mutex_lock(&pinctrl_mutex); - pctldev = get_pinctrl_dev_from_devname(dev_name); if (!pctldev) { pin = -EINVAL; - goto unlock; + return pin; } + mutex_lock(&pctldev->mutex); + pin = pin_get_from_name(pctldev, name); if (pin < 0) goto unlock; @@ -103,7 +104,7 @@ int pin_config_get(const char *dev_name, const char *name, pin = pin_config_get_for_pin(pctldev, pin, config); unlock: - mutex_unlock(&pinctrl_mutex); + mutex_unlock(&pctldev->mutex); return pin; } EXPORT_SYMBOL(pin_config_get); @@ -144,14 +145,14 @@ int pin_config_set(const char *dev_name, const char *name, struct pinctrl_dev *pctldev; int pin, ret; - mutex_lock(&pinctrl_mutex); - pctldev = get_pinctrl_dev_from_devname(dev_name); if (!pctldev) { ret = -EINVAL; - goto unlock; + return ret; } + mutex_lock(&pctldev->mutex); + pin = pin_get_from_name(pctldev, name); if (pin < 0) { ret = pin; @@ -161,7 +162,7 @@ int pin_config_set(const char *dev_name, const char *name, ret = pin_config_set_for_pin(pctldev, pin, config); unlock: - mutex_unlock(&pinctrl_mutex); + mutex_unlock(&pctldev->mutex); return ret; } EXPORT_SYMBOL(pin_config_set); @@ -173,13 +174,14 @@ int pin_config_group_get(const char *dev_name, const char *pin_group, const struct pinconf_ops *ops; int selector, ret; - mutex_lock(&pinctrl_mutex); - pctldev = get_pinctrl_dev_from_devname(dev_name); if (!pctldev) { ret = -EINVAL; - goto unlock; + return ret; } + + mutex_lock(&pctldev->mutex); + ops = pctldev->desc->confops; if (!ops || !ops->pin_config_group_get) { @@ -199,7 +201,7 @@ int pin_config_group_get(const char *dev_name, const char *pin_group, ret = ops->pin_config_group_get(pctldev, selector, config); unlock: - mutex_unlock(&pinctrl_mutex); + mutex_unlock(&pctldev->mutex); return ret; } EXPORT_SYMBOL(pin_config_group_get); @@ -216,13 +218,14 @@ int pin_config_group_set(const char *dev_name, const char *pin_group, int ret; int i; - mutex_lock(&pinctrl_mutex); - pctldev = get_pinctrl_dev_from_devname(dev_name); if (!pctldev) { ret = -EINVAL; - goto unlock; + return ret; } + + mutex_lock(&pctldev->mutex); + ops = pctldev->desc->confops; pctlops = pctldev->desc->pctlops; @@ -278,7 +281,7 @@ int pin_config_group_set(const char *dev_name, const char *pin_group, ret = 0; unlock: - mutex_unlock(&pinctrl_mutex); + mutex_unlock(&pctldev->mutex); return ret; } @@ -486,7 +489,7 @@ static int pinconf_pins_show(struct seq_file *s, void *what) seq_puts(s, "Pin config settings per pin\n"); seq_puts(s, "Format: pin (name): configs\n"); - mutex_lock(&pinctrl_mutex); + mutex_lock(&pctldev->mutex); /* The pin number can be retrived from the pin controller descriptor */ for (i = 0; i < pctldev->desc->npins; i++) { @@ -506,7 +509,7 @@ static int pinconf_pins_show(struct seq_file *s, void *what) seq_printf(s, "\n"); } - mutex_unlock(&pinctrl_mutex); + mutex_unlock(&pctldev->mutex); return 0; } @@ -574,122 +577,58 @@ static const struct file_operations pinconf_groups_ops = { .release = single_release, }; -/* 32bit read/write ressources */ -#define MAX_NAME_LEN 16 -char dbg_pinname[MAX_NAME_LEN]; /* shared: name of the state of the pin*/ -char dbg_state_name[MAX_NAME_LEN]; /* shared: state of the pin*/ -static u32 dbg_config; /* shared: config to be read/set for the pin & state*/ - -static int pinconf_dbg_pinname_print(struct seq_file *s, void *d) -{ - if (strlen(dbg_pinname)) - seq_printf(s, "%s\n", dbg_pinname); - else - seq_printf(s, "No pin name set\n"); - return 0; -} - -static int pinconf_dbg_pinname_open(struct inode *inode, struct file *file) -{ - return single_open(file, pinconf_dbg_pinname_print, inode->i_private); -} - -static int pinconf_dbg_pinname_write(struct file *file, - const char __user *user_buf, size_t count, loff_t *ppos) -{ - int err; - - if (count > MAX_NAME_LEN) - return -EINVAL; - - err = sscanf(user_buf, "%15s", dbg_pinname); - - if (err != 1) - return -EINVAL; - - return count; -} +#define MAX_NAME_LEN 15 -static const struct file_operations pinconf_dbg_pinname_fops = { - .open = pinconf_dbg_pinname_open, - .write = pinconf_dbg_pinname_write, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, - .owner = THIS_MODULE, +struct dbg_cfg { + enum pinctrl_map_type map_type; + char dev_name[MAX_NAME_LEN+1]; + char state_name[MAX_NAME_LEN+1]; + char pin_name[MAX_NAME_LEN+1]; }; -static int pinconf_dbg_state_print(struct seq_file *s, void *d) -{ - if (strlen(dbg_state_name)) - seq_printf(s, "%s\n", dbg_state_name); - else - seq_printf(s, "No pin state set\n"); - return 0; -} - -static int pinconf_dbg_state_open(struct inode *inode, struct file *file) -{ - return single_open(file, pinconf_dbg_state_print, inode->i_private); -} - -static int pinconf_dbg_state_write(struct file *file, - const char __user *user_buf, size_t count, loff_t *ppos) -{ - int err; - - if (count > MAX_NAME_LEN) - return -EINVAL; - - err = sscanf(user_buf, "%15s", dbg_state_name); - - if (err != 1) - return -EINVAL; - - return count; -} - -static const struct file_operations pinconf_dbg_pinstate_fops = { - .open = pinconf_dbg_state_open, - .write = pinconf_dbg_state_write, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, - .owner = THIS_MODULE, -}; +/* + * Goal is to keep this structure as global in order to simply read the + * pinconf-config file after a write to check config is as expected + */ +static struct dbg_cfg pinconf_dbg_conf; /** * pinconf_dbg_config_print() - display the pinctrl config from the pinctrl - * map, of a pin/state pair based on pinname and state that have been - * selected with the debugfs entries pinconf-name and pinconf-state - * @s: contains the 32bits config to be written + * map, of the dev/pin/state that was last written to pinconf-config file. + * @s: string filled in with config description * @d: not used */ static int pinconf_dbg_config_print(struct seq_file *s, void *d) { struct pinctrl_maps *maps_node; - struct pinctrl_map const *map; + const struct pinctrl_map *map; struct pinctrl_dev *pctldev = NULL; - struct pinconf_ops *confops = NULL; + const struct pinconf_ops *confops = NULL; + const struct pinctrl_map_configs *configs; + struct dbg_cfg *dbg = &pinconf_dbg_conf; int i, j; bool found = false; + unsigned long config; - mutex_lock(&pinctrl_mutex); + mutex_lock(&pctldev->mutex); /* Parse the pinctrl map and look for the elected pin/state */ for_each_maps(maps_node, i, map) { - if (map->type != PIN_MAP_TYPE_CONFIGS_PIN) + if (map->type != dbg->map_type) continue; - - if (strncmp(map->name, dbg_state_name, MAX_NAME_LEN) > 0) + if (strcmp(map->dev_name, dbg->dev_name)) + continue; + if (strcmp(map->name, dbg->state_name)) continue; for (j = 0; j < map->data.configs.num_configs; j++) { - if (0 == strncmp(map->data.configs.group_or_pin, - dbg_pinname, MAX_NAME_LEN)) { - /* We found the right pin / state, read the - * config and store the pctldev */ - dbg_config = map->data.configs.configs[j]; + if (!strcmp(map->data.configs.group_or_pin, + dbg->pin_name)) { + /* + * We found the right pin / state, read the + * config and he pctldev for later use + */ + configs = &map->data.configs; pctldev = get_pinctrl_dev_from_devname (map->ctrl_dev_name); found = true; @@ -698,74 +637,166 @@ static int pinconf_dbg_config_print(struct seq_file *s, void *d) } } - mutex_unlock(&pinctrl_mutex); + if (!found) { + seq_printf(s, "No config found for dev/state/pin, expected:\n"); + seq_printf(s, "Searched dev:%s\n", dbg->dev_name); + seq_printf(s, "Searched state:%s\n", dbg->state_name); + seq_printf(s, "Searched pin:%s\n", dbg->pin_name); + seq_printf(s, "Use: modify config_pin <devname> "\ + "<state> <pinname> <value>\n"); + goto exit; + } - if (found) { - seq_printf(s, "Config of %s in state %s: 0x%08X\n", dbg_pinname, - dbg_state_name, dbg_config); + config = *(configs->configs); + seq_printf(s, "Dev %s has config of %s in state %s: 0x%08lX\n", + dbg->dev_name, dbg->pin_name, + dbg->state_name, config); - if (pctldev) - confops = pctldev->desc->confops; + if (pctldev) + confops = pctldev->desc->confops; - if (confops && confops->pin_config_config_dbg_show) - confops->pin_config_config_dbg_show(pctldev, - s, dbg_config); - } else { - seq_printf(s, "No pin found for defined name/state\n"); - } + if (confops && confops->pin_config_config_dbg_show) + confops->pin_config_config_dbg_show(pctldev, s, config); - return 0; -} +exit: + mutex_unlock(&pctldev->mutex); -static int pinconf_dbg_config_open(struct inode *inode, struct file *file) -{ - return single_open(file, pinconf_dbg_config_print, inode->i_private); + return 0; } /** - * pinconf_dbg_config_write() - overwrite the pinctrl config in thepinctrl - * map, of a pin/state pair based on pinname and state that have been - * selected with the debugfs entries pinconf-name and pinconf-state + * pinconf_dbg_config_write() - modify the pinctrl config in the pinctrl + * map, of a dev/pin/state entry based on user entries to pinconf-config + * @user_buf: contains the modification request with expected format: + * modify config_pin <devicename> <state> <pinname> <newvalue> + * modify is literal string, alternatives like add/delete not supported yet + * config_pin is literal, alternatives like config_mux not supported yet + * <devicename> <state> <pinname> are values that should match the pinctrl-maps + * <newvalue> reflects the new config and is driver dependant */ static int pinconf_dbg_config_write(struct file *file, const char __user *user_buf, size_t count, loff_t *ppos) { - int err; - unsigned long config; struct pinctrl_maps *maps_node; - struct pinctrl_map const *map; - int i, j; + const struct pinctrl_map *map; + struct pinctrl_dev *pctldev = NULL; + const struct pinconf_ops *confops = NULL; + struct dbg_cfg *dbg = &pinconf_dbg_conf; + const struct pinctrl_map_configs *configs; + char config[MAX_NAME_LEN+1]; + bool found = false; + char buf[128]; + char *b = &buf[0]; + int buf_size; + char *token; + int i; + + /* Get userspace string and assure termination */ + buf_size = min(count, (sizeof(buf)-1)); + if (copy_from_user(buf, user_buf, buf_size)) + return -EFAULT; + buf[buf_size] = 0; + + /* + * need to parse entry and extract parameters: + * modify configs_pin devicename state pinname newvalue + */ + + /* Get arg: 'modify' */ + token = strsep(&b, " "); + if (!token) + return -EINVAL; + if (strcmp(token, "modify")) + return -EINVAL; - err = kstrtoul_from_user(user_buf, count, 0, &config); + /* Get arg type: "config_pin" type supported so far */ + token = strsep(&b, " "); + if (!token) + return -EINVAL; + if (strcmp(token, "config_pin")) + return -EINVAL; + dbg->map_type = PIN_MAP_TYPE_CONFIGS_PIN; - if (err) - return err; + /* get arg 'device_name' */ + token = strsep(&b, " "); + if (token == NULL) + return -EINVAL; + if (strlen(token) >= MAX_NAME_LEN) + return -EINVAL; + strncpy(dbg->dev_name, token, MAX_NAME_LEN); - dbg_config = config; + /* get arg 'state_name' */ + token = strsep(&b, " "); + if (token == NULL) + return -EINVAL; + if (strlen(token) >= MAX_NAME_LEN) + return -EINVAL; + strncpy(dbg->state_name, token, MAX_NAME_LEN); - mutex_lock(&pinctrl_mutex); + /* get arg 'pin_name' */ + token = strsep(&b, " "); + if (token == NULL) + return -EINVAL; + if (strlen(token) >= MAX_NAME_LEN) + return -EINVAL; + strncpy(dbg->pin_name, token, MAX_NAME_LEN); - /* Parse the pinctrl map and look for the selected pin/state */ + /* get new_value of config' */ + token = strsep(&b, " "); + if (token == NULL) + return -EINVAL; + if (strlen(token) >= MAX_NAME_LEN) + return -EINVAL; + strncpy(config, token, MAX_NAME_LEN); + + mutex_lock(&pinctrl_maps_mutex); + + /* Parse the pinctrl map and look for the selected dev/state/pin */ for_each_maps(maps_node, i, map) { - if (map->type != PIN_MAP_TYPE_CONFIGS_PIN) + if (strcmp(map->dev_name, dbg->dev_name)) continue; - - if (strncmp(map->name, dbg_state_name, MAX_NAME_LEN) > 0) + if (map->type != dbg->map_type) + continue; + if (strcmp(map->name, dbg->state_name)) continue; /* we found the right pin / state, so overwrite config */ - for (j = 0; j < map->data.configs.num_configs; j++) { - if (strncmp(map->data.configs.group_or_pin, dbg_pinname, - MAX_NAME_LEN) == 0) - map->data.configs.configs[j] = dbg_config; + if (!strcmp(map->data.configs.group_or_pin, dbg->pin_name)) { + found = true; + pctldev = get_pinctrl_dev_from_devname( + map->ctrl_dev_name); + configs = &map->data.configs; + break; } } - mutex_unlock(&pinctrl_mutex); + if (!found) { + count = -EINVAL; + goto exit; + } + + if (pctldev) + confops = pctldev->desc->confops; + + if (confops && confops->pin_config_dbg_parse_modify) { + for (i = 0; i < configs->num_configs; i++) { + confops->pin_config_dbg_parse_modify(pctldev, + config, + &configs->configs[i]); + } + } + +exit: + mutex_unlock(&pinctrl_maps_mutex); return count; } +static int pinconf_dbg_config_open(struct inode *inode, struct file *file) +{ + return single_open(file, pinconf_dbg_config_print, inode->i_private); +} + static const struct file_operations pinconf_dbg_pinconfig_fops = { .open = pinconf_dbg_config_open, .write = pinconf_dbg_config_write, @@ -782,10 +813,6 @@ void pinconf_init_device_debugfs(struct dentry *devroot, devroot, pctldev, &pinconf_pins_ops); debugfs_create_file("pinconf-groups", S_IFREG | S_IRUGO, devroot, pctldev, &pinconf_groups_ops); - debugfs_create_file("pinconf-name", (S_IRUGO | S_IWUSR | S_IWGRP), - devroot, pctldev, &pinconf_dbg_pinname_fops); - debugfs_create_file("pinconf-state", (S_IRUGO | S_IWUSR | S_IWGRP), - devroot, pctldev, &pinconf_dbg_pinstate_fops); debugfs_create_file("pinconf-config", (S_IRUGO | S_IWUSR | S_IWGRP), devroot, pctldev, &pinconf_dbg_pinconfig_fops); } diff --git a/drivers/pinctrl/pinconf.h b/drivers/pinctrl/pinconf.h index bfda73d64eed..92c7267244d2 100644 --- a/drivers/pinctrl/pinconf.h +++ b/drivers/pinctrl/pinconf.h @@ -98,6 +98,8 @@ void pinconf_generic_dump_pin(struct pinctrl_dev *pctldev, void pinconf_generic_dump_group(struct pinctrl_dev *pctldev, struct seq_file *s, const char *gname); +void pinconf_generic_dump_config(struct pinctrl_dev *pctldev, + struct seq_file *s, unsigned long config); #else static inline void pinconf_generic_dump_pin(struct pinctrl_dev *pctldev, @@ -114,4 +116,10 @@ static inline void pinconf_generic_dump_group(struct pinctrl_dev *pctldev, return; } +static inline void pinconf_generic_dump_config(struct pinctrl_dev *pctldev, + struct seq_file *s, + unsigned long config) +{ + return; +} #endif diff --git a/drivers/pinctrl/pinctrl-ab8500.c b/drivers/pinctrl/pinctrl-ab8500.c index 3b471d87c211..2ac2d0ad3025 100644 --- a/drivers/pinctrl/pinctrl-ab8500.c +++ b/drivers/pinctrl/pinctrl-ab8500.c @@ -389,7 +389,8 @@ static const struct abx500_function ab8500_functions[] = { * alt_A | 1 | 0 | 0 */ -struct alternate_functions ab8500_alternate_functions[AB8500_GPIO_MAX_NUMBER + 1] = { +static struct +alternate_functions ab8500_alternate_functions[AB8500_GPIO_MAX_NUMBER + 1] = { ALTERNATE_FUNCTIONS(0, UNUSED, UNUSED, UNUSED, 0, 0, 0), /* no GPIO0 */ ALTERNATE_FUNCTIONS(1, 0, UNUSED, UNUSED, 0, 0, 0), /* GPIO1, altA controlled by bit 0 */ ALTERNATE_FUNCTIONS(2, 1, UNUSED, UNUSED, 0, 0, 0), /* GPIO2, altA controlled by bit 1 */ @@ -455,7 +456,7 @@ struct alternate_functions ab8500_alternate_functions[AB8500_GPIO_MAX_NUMBER + 1 * GPIO24 and GPIO25 * GPIO36 to GPIO41 */ -struct abx500_gpio_irq_cluster ab8500_gpio_irq_cluster[] = { +static struct abx500_gpio_irq_cluster ab8500_gpio_irq_cluster[] = { GPIO_IRQ_CLUSTER(6, 13, AB8500_INT_GPIO6R), GPIO_IRQ_CLUSTER(24, 25, AB8500_INT_GPIO24R), GPIO_IRQ_CLUSTER(36, 41, AB8500_INT_GPIO36R), diff --git a/drivers/pinctrl/pinctrl-ab8505.c b/drivers/pinctrl/pinctrl-ab8505.c index 3a4238e879e3..bf0ef4ac376f 100644 --- a/drivers/pinctrl/pinctrl-ab8505.c +++ b/drivers/pinctrl/pinctrl-ab8505.c @@ -271,7 +271,8 @@ static const struct abx500_function ab8505_functions[] = { * alt_A | 1 | 0 | 0 */ -struct alternate_functions ab8505_alternate_functions[AB8505_GPIO_MAX_NUMBER + 1] = { +static struct +alternate_functions ab8505_alternate_functions[AB8505_GPIO_MAX_NUMBER + 1] = { ALTERNATE_FUNCTIONS(0, UNUSED, UNUSED, UNUSED, 0, 0, 0), /* no GPIO0 */ ALTERNATE_FUNCTIONS(1, 0, UNUSED, UNUSED, 0, 0, 0), /* GPIO1, altA controlled by bit 0 */ ALTERNATE_FUNCTIONS(2, 1, UNUSED, UNUSED, 0, 0, 0), /* GPIO2, altA controlled by bit 1 */ @@ -284,7 +285,7 @@ struct alternate_functions ab8505_alternate_functions[AB8505_GPIO_MAX_NUMBER + 1 ALTERNATE_FUNCTIONS(9, UNUSED, UNUSED, UNUSED, 0, 0, 0), /* no GPIO9, bit 0 reserved */ ALTERNATE_FUNCTIONS(10, 1, 0, UNUSED, 1, 0, 0), /* GPIO10, altA and altB controlled by bit 0 */ - ALTERNATE_FUNCTIONS(11, 2, UNUSED, UNUSED, 0, 0, 0), /* GPIO11, altA controlled by bit 2 */ + ALTERNATE_FUNCTIONS(11, 2, 1, UNUSED, 0, 0, 0), /* GPIO11, altA controlled by bit 2 */ ALTERNATE_FUNCTIONS(12, UNUSED, UNUSED, UNUSED, 0, 0, 0), /* no GPIO12, bit3 reseved */ ALTERNATE_FUNCTIONS(13, 4, 3, 4, 1, 0, 2), /* GPIO13, altA altB and altC controlled by bit 3 and 4 */ ALTERNATE_FUNCTIONS(14, 5, UNUSED, UNUSED, 0, 0, 0), /* GPIO14, altA controlled by bit 5 */ @@ -348,7 +349,7 @@ struct alternate_functions ab8505_alternate_functions[AB8505_GPIO_MAX_NUMBER + 1 * GPIO50 * GPIO52 to GPIO53 */ -struct abx500_gpio_irq_cluster ab8505_gpio_irq_cluster[] = { +static struct abx500_gpio_irq_cluster ab8505_gpio_irq_cluster[] = { GPIO_IRQ_CLUSTER(10, 11, AB8500_INT_GPIO10R), GPIO_IRQ_CLUSTER(13, 13, AB8500_INT_GPIO13R), GPIO_IRQ_CLUSTER(40, 41, AB8500_INT_GPIO40R), diff --git a/drivers/pinctrl/pinctrl-ab8540.c b/drivers/pinctrl/pinctrl-ab8540.c index 8ee1e8d95f65..9867535d49c1 100644 --- a/drivers/pinctrl/pinctrl-ab8540.c +++ b/drivers/pinctrl/pinctrl-ab8540.c @@ -299,7 +299,8 @@ static const struct abx500_function ab8540_functions[] = { * */ -struct alternate_functions ab8540_alternate_functions[AB8540_GPIO_MAX_NUMBER + 1] = { +static struct +alternate_functions ab8540_alternate_functions[AB8540_GPIO_MAX_NUMBER + 1] = { /* GPIOSEL1 - bit 4-7 reserved */ ALTERNATE_FUNCTIONS(0, UNUSED, UNUSED, UNUSED, 0, 0, 0), /* no GPIO0 */ ALTERNATE_FUNCTIONS(1, 0, UNUSED, UNUSED, 0, 0, 0), /* GPIO1, altA controlled by bit 0 */ @@ -376,7 +377,7 @@ static struct pullud ab8540_pullud = { * GPIO43 to GPIO44 * GPIO51 to GPIO54 */ -struct abx500_gpio_irq_cluster ab8540_gpio_irq_cluster[] = { +static struct abx500_gpio_irq_cluster ab8540_gpio_irq_cluster[] = { GPIO_IRQ_CLUSTER(43, 43, AB8540_INT_GPIO43F), GPIO_IRQ_CLUSTER(44, 44, AB8540_INT_GPIO44F), GPIO_IRQ_CLUSTER(51, 54, AB9540_INT_GPIO51R), diff --git a/drivers/pinctrl/pinctrl-ab9540.c b/drivers/pinctrl/pinctrl-ab9540.c index 7610bd012b98..1a281ca95dac 100644 --- a/drivers/pinctrl/pinctrl-ab9540.c +++ b/drivers/pinctrl/pinctrl-ab9540.c @@ -379,7 +379,8 @@ static const struct abx500_function ab9540_functions[] = { * alt_A | 1 | 0 | 0 */ -struct alternate_functions ab9540alternate_functions[AB9540_GPIO_MAX_NUMBER + 1] = { +static struct +alternate_functions ab9540alternate_functions[AB9540_GPIO_MAX_NUMBER + 1] = { /* GPIOSEL1 - bits 4-7 are reserved */ ALTERNATE_FUNCTIONS(0, UNUSED, UNUSED, UNUSED, 0, 0, 0), /* no GPIO0 */ ALTERNATE_FUNCTIONS(1, 0, UNUSED, UNUSED, 0, 0, 0), /* GPIO1, altA controlled by bit 0 */ @@ -393,7 +394,7 @@ struct alternate_functions ab9540alternate_functions[AB9540_GPIO_MAX_NUMBER + 1] /* GPIOSEL2 - bits 0 and 3 are reserved */ ALTERNATE_FUNCTIONS(9, UNUSED, UNUSED, UNUSED, 0, 0, 0), /* no GPIO9 */ ALTERNATE_FUNCTIONS(10, 1, 0, UNUSED, 1, 0, 0), /* GPIO10, altA and altB controlled by bit 0 */ - ALTERNATE_FUNCTIONS(11, 2, UNUSED, UNUSED, 0, 0, 0), /* GPIO11, altA controlled by bit 1 */ + ALTERNATE_FUNCTIONS(11, 2, 1, UNUSED, 0, 0, 0), /* GPIO11, altA controlled by bit 1 */ ALTERNATE_FUNCTIONS(12, UNUSED, UNUSED, UNUSED, 0, 0, 0), /* no GPIO12 */ ALTERNATE_FUNCTIONS(13, 4, 3, 4, 1, 0, 2), /* GPIO13, altA altB and altC controlled by bit 3 and 4 */ ALTERNATE_FUNCTIONS(14, 5, UNUSED, UNUSED, 0, 0, 0), /* GPIO14, altA controlled by bit 5 */ @@ -454,7 +455,7 @@ struct alternate_functions ab9540alternate_functions[AB9540_GPIO_MAX_NUMBER + 1] ALTERNATE_FUNCTIONS(54, 5, UNUSED, UNUSED, 0, 0, 0), /* GPIO54 = GPIO60, altA controlled by bit 5 */ }; -struct abx500_gpio_irq_cluster ab9540_gpio_irq_cluster[] = { +static struct abx500_gpio_irq_cluster ab9540_gpio_irq_cluster[] = { GPIO_IRQ_CLUSTER(10, 13, AB8500_INT_GPIO10R), GPIO_IRQ_CLUSTER(24, 25, AB8500_INT_GPIO24R), GPIO_IRQ_CLUSTER(40, 41, AB8500_INT_GPIO40R), diff --git a/drivers/pinctrl/pinctrl-abx500.c b/drivers/pinctrl/pinctrl-abx500.c index c542a97c82f3..aa17f7580f61 100644 --- a/drivers/pinctrl/pinctrl-abx500.c +++ b/drivers/pinctrl/pinctrl-abx500.c @@ -517,14 +517,14 @@ static inline void abx500_gpio_dbg_show_one(struct seq_file *s, #define abx500_gpio_dbg_show NULL #endif -int abx500_gpio_request(struct gpio_chip *chip, unsigned offset) +static int abx500_gpio_request(struct gpio_chip *chip, unsigned offset) { int gpio = chip->base + offset; return pinctrl_request_gpio(gpio); } -void abx500_gpio_free(struct gpio_chip *chip, unsigned offset) +static void abx500_gpio_free(struct gpio_chip *chip, unsigned offset) { int gpio = chip->base + offset; @@ -611,7 +611,7 @@ static void abx500_pmx_disable(struct pinctrl_dev *pctldev, dev_dbg(pct->dev, "disable group %s, %u pins\n", g->name, g->npins); } -int abx500_gpio_request_enable(struct pinctrl_dev *pctldev, +static int abx500_gpio_request_enable(struct pinctrl_dev *pctldev, struct pinctrl_gpio_range *range, unsigned offset) { @@ -656,7 +656,7 @@ static void abx500_gpio_disable_free(struct pinctrl_dev *pctldev, { } -static struct pinmux_ops abx500_pinmux_ops = { +static const struct pinmux_ops abx500_pinmux_ops = { .get_functions_count = abx500_pmx_get_funcs_cnt, .get_function_name = abx500_pmx_get_func_name, .get_function_groups = abx500_pmx_get_func_groups, @@ -704,21 +704,21 @@ static void abx500_pin_dbg_show(struct pinctrl_dev *pctldev, chip->base + offset - 1); } -static struct pinctrl_ops abx500_pinctrl_ops = { +static const struct pinctrl_ops abx500_pinctrl_ops = { .get_groups_count = abx500_get_groups_cnt, .get_group_name = abx500_get_group_name, .get_group_pins = abx500_get_group_pins, .pin_dbg_show = abx500_pin_dbg_show, }; -int abx500_pin_config_get(struct pinctrl_dev *pctldev, +static int abx500_pin_config_get(struct pinctrl_dev *pctldev, unsigned pin, unsigned long *config) { return -ENOSYS; } -int abx500_pin_config_set(struct pinctrl_dev *pctldev, +static int abx500_pin_config_set(struct pinctrl_dev *pctldev, unsigned pin, unsigned long config) { @@ -778,7 +778,7 @@ int abx500_pin_config_set(struct pinctrl_dev *pctldev, return ret; } -static struct pinconf_ops abx500_pinconf_ops = { +static const struct pinconf_ops abx500_pinconf_ops = { .pin_config_get = abx500_pin_config_get, .pin_config_set = abx500_pin_config_set, }; @@ -834,6 +834,7 @@ static const struct of_device_id abx500_gpio_match[] = { { .compatible = "stericsson,ab8505-gpio", .data = (void *)PINCTRL_AB8505, }, { .compatible = "stericsson,ab8540-gpio", .data = (void *)PINCTRL_AB8540, }, { .compatible = "stericsson,ab9540-gpio", .data = (void *)PINCTRL_AB9540, }, + { } }; static int abx500_gpio_probe(struct platform_device *pdev) @@ -879,7 +880,6 @@ static int abx500_gpio_probe(struct platform_device *pdev) pct->parent = dev_get_drvdata(pdev->dev.parent); pct->chip = abx500gpio_chip; pct->chip.dev = &pdev->dev; - pct->chip.base = pdata->gpio_base; pct->chip.base = (np) ? -1 : pdata->gpio_base; /* initialize the lock */ diff --git a/drivers/pinctrl/pinctrl-at91.c b/drivers/pinctrl/pinctrl-at91.c index efb7f10e902a..4d7f531e945d 100644 --- a/drivers/pinctrl/pinctrl-at91.c +++ b/drivers/pinctrl/pinctrl-at91.c @@ -294,7 +294,7 @@ static void at91_dt_free_map(struct pinctrl_dev *pctldev, { } -static struct pinctrl_ops at91_pctrl_ops = { +static const struct pinctrl_ops at91_pctrl_ops = { .get_groups_count = at91_get_groups_count, .get_group_name = at91_get_group_name, .get_group_pins = at91_get_group_pins, @@ -303,7 +303,7 @@ static struct pinctrl_ops at91_pctrl_ops = { .dt_free_map = at91_dt_free_map, }; -static void __iomem * pin_to_controller(struct at91_pinctrl *info, +static void __iomem *pin_to_controller(struct at91_pinctrl *info, unsigned int bank) { return gpio_chips[bank]->regbase; @@ -501,7 +501,7 @@ static void at91_pin_dbg(const struct device *dev, const struct at91_pmx_pin *pi } } -static int pin_check_config(struct at91_pinctrl *info, const char* name, +static int pin_check_config(struct at91_pinctrl *info, const char *name, int index, const struct at91_pmx_pin *pin) { int mux; @@ -579,7 +579,7 @@ static int at91_pmx_enable(struct pinctrl_dev *pctldev, unsigned selector, pio = pin_to_controller(info, pin->bank); mask = pin_to_mask(pin->pin); at91_mux_disable_interrupt(pio, mask); - switch(pin->mux) { + switch (pin->mux) { case AT91_MUX_GPIO: at91_mux_gpio_enable(pio, mask, 1); break; @@ -696,7 +696,7 @@ static void at91_gpio_disable_free(struct pinctrl_dev *pctldev, /* Set the pin to some default state, GPIO is usually default */ } -static struct pinmux_ops at91_pmx_ops = { +static const struct pinmux_ops at91_pmx_ops = { .get_functions_count = at91_pmx_get_funcs_count, .get_function_name = at91_pmx_get_func_name, .get_function_groups = at91_pmx_get_groups, @@ -776,7 +776,7 @@ static void at91_pinconf_group_dbg_show(struct pinctrl_dev *pctldev, { } -static struct pinconf_ops at91_pinconf_ops = { +static const struct pinconf_ops at91_pinconf_ops = { .pin_config_get = at91_pinconf_get, .pin_config_set = at91_pinconf_set, .pin_config_dbg_show = at91_pinconf_dbg_show, @@ -812,7 +812,7 @@ static int at91_pinctrl_mux_mask(struct at91_pinctrl *info, { int ret = 0; int size; - const const __be32 *list; + const __be32 *list; list = of_get_property(np, "atmel,mux-mask", &size); if (!list) { @@ -846,7 +846,7 @@ static int at91_pinctrl_parse_groups(struct device_node *np, { struct at91_pmx_pin *pin; int size; - const const __be32 *list; + const __be32 *list; int i, j; dev_dbg(info->dev, "group(%d): %s\n", index, np->name); @@ -944,7 +944,7 @@ static int at91_pinctrl_probe_dt(struct platform_device *pdev, return -ENODEV; info->dev = &pdev->dev; - info->ops = (struct at91_pinctrl_mux_ops*) + info->ops = (struct at91_pinctrl_mux_ops *) of_match_device(at91_pinctrl_of_match, &pdev->dev)->data; at91_pinctrl_child_count(info, np); @@ -1002,7 +1002,7 @@ static int at91_pinctrl_probe(struct platform_device *pdev) { struct at91_pinctrl *info; struct pinctrl_pin_desc *pdesc; - int ret, i, j ,k; + int ret, i, j, k; info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL); if (!info) @@ -1568,7 +1568,7 @@ static int at91_gpio_probe(struct platform_device *pdev) goto err; } - at91_chip->ops = (struct at91_pinctrl_mux_ops*) + at91_chip->ops = (struct at91_pinctrl_mux_ops *) of_match_device(at91_gpio_of_match, &pdev->dev)->data; at91_chip->pioc_virq = irq; at91_chip->pioc_idx = alias_idx; @@ -1605,7 +1605,8 @@ static int at91_gpio_probe(struct platform_device *pdev) chip->ngpio = ngpio; } - names = devm_kzalloc(&pdev->dev, sizeof(char*) * chip->ngpio, GFP_KERNEL); + names = devm_kzalloc(&pdev->dev, sizeof(char *) * chip->ngpio, + GFP_KERNEL); if (!names) { ret = -ENOMEM; @@ -1615,7 +1616,7 @@ static int at91_gpio_probe(struct platform_device *pdev) for (i = 0; i < chip->ngpio; i++) names[i] = kasprintf(GFP_KERNEL, "pio%c%d", alias_idx + 'A', i); - chip->names = (const char*const*)names; + chip->names = (const char *const *)names; range = &at91_chip->range; range->name = chip->label; diff --git a/drivers/pinctrl/pinctrl-bcm2835.c b/drivers/pinctrl/pinctrl-bcm2835.c index 4eb6d2c4e4df..f28d4b08771a 100644 --- a/drivers/pinctrl/pinctrl-bcm2835.c +++ b/drivers/pinctrl/pinctrl-bcm2835.c @@ -795,7 +795,7 @@ out: return err; } -static struct pinctrl_ops bcm2835_pctl_ops = { +static const struct pinctrl_ops bcm2835_pctl_ops = { .get_groups_count = bcm2835_pctl_get_groups_count, .get_group_name = bcm2835_pctl_get_group_name, .get_group_pins = bcm2835_pctl_get_group_pins, @@ -872,7 +872,7 @@ static int bcm2835_pmx_gpio_set_direction(struct pinctrl_dev *pctldev, return 0; } -static struct pinmux_ops bcm2835_pmx_ops = { +static const struct pinmux_ops bcm2835_pmx_ops = { .get_functions_count = bcm2835_pmx_get_functions_count, .get_function_name = bcm2835_pmx_get_function_name, .get_function_groups = bcm2835_pmx_get_function_groups, @@ -916,7 +916,7 @@ static int bcm2835_pinconf_set(struct pinctrl_dev *pctldev, return 0; } -static struct pinconf_ops bcm2835_pinconf_ops = { +static const struct pinconf_ops bcm2835_pinconf_ops = { .pin_config_get = bcm2835_pinconf_get, .pin_config_set = bcm2835_pinconf_set, }; diff --git a/drivers/pinctrl/pinctrl-coh901.c b/drivers/pinctrl/pinctrl-coh901.c index 8b7e7bc2226b..edde3acc4186 100644 --- a/drivers/pinctrl/pinctrl-coh901.c +++ b/drivers/pinctrl/pinctrl-coh901.c @@ -318,13 +318,16 @@ static int u300_gpio_to_irq(struct gpio_chip *chip, unsigned offset) struct u300_gpio_port *port = NULL; struct list_head *p; int retirq; + bool found = false; list_for_each(p, &gpio->port_list) { port = list_entry(p, struct u300_gpio_port, node); - if (port->number == portno) + if (port->number == portno) { + found = true; break; + } } - if (port == NULL) { + if (!found) { dev_err(gpio->dev, "could not locate port for GPIO %d IRQ\n", offset); return -EINVAL; @@ -359,7 +362,7 @@ int u300_gpio_config_get(struct gpio_chip *chip, drmode &= (U300_GPIO_PXPCR_PIN_MODE_MASK << ((offset & 0x07) << 1)); drmode >>= ((offset & 0x07) << 1); - switch(param) { + switch (param) { case PIN_CONFIG_BIAS_HIGH_IMPEDANCE: *config = 0; if (biasmode) diff --git a/drivers/pinctrl/pinctrl-exynos.c b/drivers/pinctrl/pinctrl-exynos.c index 538b9ddaadf7..8b10b1ac9071 100644 --- a/drivers/pinctrl/pinctrl-exynos.c +++ b/drivers/pinctrl/pinctrl-exynos.c @@ -26,6 +26,7 @@ #include <linux/of_irq.h> #include <linux/io.h> #include <linux/slab.h> +#include <linux/spinlock.h> #include <linux/err.h> #include <asm/mach/irq.h> @@ -33,6 +34,17 @@ #include "pinctrl-samsung.h" #include "pinctrl-exynos.h" + +static struct samsung_pin_bank_type bank_type_off = { + .fld_width = { 4, 1, 2, 2, 2, 2, }, + .reg_offset = { 0x00, 0x04, 0x08, 0x0c, 0x10, 0x14, }, +}; + +static struct samsung_pin_bank_type bank_type_alive = { + .fld_width = { 4, 1, 2, 2, }, + .reg_offset = { 0x00, 0x04, 0x08, 0x0c, }, +}; + /* list of external wakeup controllers supported */ static const struct of_device_id exynos_wkup_irq_ids[] = { { .compatible = "samsung,exynos4210-wakeup-eint", }, @@ -75,12 +87,14 @@ static void exynos_gpio_irq_ack(struct irq_data *irqd) static int exynos_gpio_irq_set_type(struct irq_data *irqd, unsigned int type) { struct samsung_pin_bank *bank = irq_data_get_irq_chip_data(irqd); + struct samsung_pin_bank_type *bank_type = bank->type; struct samsung_pinctrl_drv_data *d = bank->drvdata; struct samsung_pin_ctrl *ctrl = d->ctrl; unsigned int pin = irqd->hwirq; unsigned int shift = EXYNOS_EINT_CON_LEN * pin; unsigned int con, trig_type; unsigned long reg_con = ctrl->geint_con + bank->eint_offset; + unsigned long flags; unsigned int mask; switch (type) { @@ -114,15 +128,19 @@ static int exynos_gpio_irq_set_type(struct irq_data *irqd, unsigned int type) con |= trig_type << shift; writel(con, d->virt_base + reg_con); - reg_con = bank->pctl_offset; - shift = pin * bank->func_width; - mask = (1 << bank->func_width) - 1; + reg_con = bank->pctl_offset + bank_type->reg_offset[PINCFG_TYPE_FUNC]; + shift = pin * bank_type->fld_width[PINCFG_TYPE_FUNC]; + mask = (1 << bank_type->fld_width[PINCFG_TYPE_FUNC]) - 1; + + spin_lock_irqsave(&bank->slock, flags); con = readl(d->virt_base + reg_con); con &= ~(mask << shift); con |= EXYNOS_EINT_FUNC << shift; writel(con, d->virt_base + reg_con); + spin_unlock_irqrestore(&bank->slock, flags); + return 0; } @@ -253,11 +271,13 @@ static void exynos_wkup_irq_ack(struct irq_data *irqd) static int exynos_wkup_irq_set_type(struct irq_data *irqd, unsigned int type) { struct samsung_pin_bank *bank = irq_data_get_irq_chip_data(irqd); + struct samsung_pin_bank_type *bank_type = bank->type; struct samsung_pinctrl_drv_data *d = bank->drvdata; unsigned int pin = irqd->hwirq; unsigned long reg_con = d->ctrl->weint_con + bank->eint_offset; unsigned long shift = EXYNOS_EINT_CON_LEN * pin; unsigned long con, trig_type; + unsigned long flags; unsigned int mask; switch (type) { @@ -291,15 +311,19 @@ static int exynos_wkup_irq_set_type(struct irq_data *irqd, unsigned int type) con |= trig_type << shift; writel(con, d->virt_base + reg_con); - reg_con = bank->pctl_offset; - shift = pin * bank->func_width; - mask = (1 << bank->func_width) - 1; + reg_con = bank->pctl_offset + bank_type->reg_offset[PINCFG_TYPE_FUNC]; + shift = pin * bank_type->fld_width[PINCFG_TYPE_FUNC]; + mask = (1 << bank_type->fld_width[PINCFG_TYPE_FUNC]) - 1; + + spin_lock_irqsave(&bank->slock, flags); con = readl(d->virt_base + reg_con); con &= ~(mask << shift); con |= EXYNOS_EINT_FUNC << shift; writel(con, d->virt_base + reg_con); + spin_unlock_irqrestore(&bank->slock, flags); + return 0; } diff --git a/drivers/pinctrl/pinctrl-exynos.h b/drivers/pinctrl/pinctrl-exynos.h index 0a708890d8b4..9b1f77a5bf0f 100644 --- a/drivers/pinctrl/pinctrl-exynos.h +++ b/drivers/pinctrl/pinctrl-exynos.h @@ -48,26 +48,18 @@ #define EXYNOS_PIN_BANK_EINTN(pins, reg, id) \ { \ + .type = &bank_type_off, \ .pctl_offset = reg, \ .nr_pins = pins, \ - .func_width = 4, \ - .pud_width = 2, \ - .drv_width = 2, \ - .conpdn_width = 2, \ - .pudpdn_width = 2, \ .eint_type = EINT_TYPE_NONE, \ .name = id \ } #define EXYNOS_PIN_BANK_EINTG(pins, reg, id, offs) \ { \ + .type = &bank_type_off, \ .pctl_offset = reg, \ .nr_pins = pins, \ - .func_width = 4, \ - .pud_width = 2, \ - .drv_width = 2, \ - .conpdn_width = 2, \ - .pudpdn_width = 2, \ .eint_type = EINT_TYPE_GPIO, \ .eint_offset = offs, \ .name = id \ @@ -75,11 +67,9 @@ #define EXYNOS_PIN_BANK_EINTW(pins, reg, id, offs) \ { \ + .type = &bank_type_alive, \ .pctl_offset = reg, \ .nr_pins = pins, \ - .func_width = 4, \ - .pud_width = 2, \ - .drv_width = 2, \ .eint_type = EINT_TYPE_WKUP, \ .eint_offset = offs, \ .name = id \ diff --git a/drivers/pinctrl/pinctrl-exynos5440.c b/drivers/pinctrl/pinctrl-exynos5440.c index 1376eb7305db..6038503ed929 100644 --- a/drivers/pinctrl/pinctrl-exynos5440.c +++ b/drivers/pinctrl/pinctrl-exynos5440.c @@ -20,6 +20,9 @@ #include <linux/pinctrl/pinctrl.h> #include <linux/pinctrl/pinmux.h> #include <linux/pinctrl/pinconf.h> +#include <linux/interrupt.h> +#include <linux/irqdomain.h> +#include <linux/of_irq.h> #include "core.h" /* EXYNOS5440 GPIO and Pinctrl register offsets */ @@ -37,6 +40,7 @@ #define GPIO_DS1 0x2C #define EXYNOS5440_MAX_PINS 23 +#define EXYNOS5440_MAX_GPIO_INT 8 #define PIN_NAME_LENGTH 10 #define GROUP_SUFFIX "-grp" @@ -109,6 +113,7 @@ struct exynos5440_pmx_func { struct exynos5440_pinctrl_priv_data { void __iomem *reg_base; struct gpio_chip *gc; + struct irq_domain *irq_domain; const struct exynos5440_pin_group *pin_groups; unsigned int nr_groups; @@ -116,6 +121,16 @@ struct exynos5440_pinctrl_priv_data { unsigned int nr_functions; }; +/** + * struct exynos5440_gpio_intr_data: private data for gpio interrupts. + * @priv: driver's private runtime data. + * @gpio_int: gpio interrupt number. + */ +struct exynos5440_gpio_intr_data { + struct exynos5440_pinctrl_priv_data *priv; + unsigned int gpio_int; +}; + /* list of all possible config options supported */ static struct pin_config { char *prop_cfg; @@ -286,7 +301,7 @@ static void exynos5440_dt_free_map(struct pinctrl_dev *pctldev, } /* list of pinctrl callbacks for the pinctrl core */ -static struct pinctrl_ops exynos5440_pctrl_ops = { +static const struct pinctrl_ops exynos5440_pctrl_ops = { .get_groups_count = exynos5440_get_group_count, .get_group_name = exynos5440_get_group_name, .get_group_pins = exynos5440_get_group_pins, @@ -374,7 +389,7 @@ static int exynos5440_pinmux_gpio_set_direction(struct pinctrl_dev *pctldev, } /* list of pinmux callbacks for the pinmux vertical in pinctrl core */ -static struct pinmux_ops exynos5440_pinmux_ops = { +static const struct pinmux_ops exynos5440_pinmux_ops = { .get_functions_count = exynos5440_get_functions_count, .get_function_name = exynos5440_pinmux_get_fname, .get_function_groups = exynos5440_pinmux_get_groups, @@ -523,7 +538,7 @@ static int exynos5440_pinconf_group_get(struct pinctrl_dev *pctldev, } /* list of pinconfig callbacks for pinconfig vertical in the pinctrl code */ -static struct pinconf_ops exynos5440_pinconf_ops = { +static const struct pinconf_ops exynos5440_pinconf_ops = { .pin_config_get = exynos5440_pinconf_get, .pin_config_set = exynos5440_pinconf_set, .pin_config_group_get = exynos5440_pinconf_group_get, @@ -598,6 +613,22 @@ static int exynos5440_gpio_direction_output(struct gpio_chip *gc, unsigned offse return 0; } +/* gpiolib gpio_to_irq callback function */ +static int exynos5440_gpio_to_irq(struct gpio_chip *gc, unsigned offset) +{ + struct exynos5440_pinctrl_priv_data *priv = dev_get_drvdata(gc->dev); + unsigned int virq; + + if (offset < 16 || offset > 23) + return -ENXIO; + + if (!priv->irq_domain) + return -ENXIO; + + virq = irq_create_mapping(priv->irq_domain, offset - 16); + return virq ? : -ENXIO; +} + /* parse the pin numbers listed in the 'samsung,exynos5440-pins' property */ static int exynos5440_pinctrl_parse_dt_pins(struct platform_device *pdev, struct device_node *cfg_np, unsigned int **pin_list, @@ -670,8 +701,10 @@ static int exynos5440_pinctrl_parse_dt(struct platform_device *pdev, ret = exynos5440_pinctrl_parse_dt_pins(pdev, cfg_np, &pin_list, &npins); - if (ret) - return ret; + if (ret) { + gname = NULL; + goto skip_to_pin_function; + } /* derive pin group name from the node name */ gname = devm_kzalloc(dev, strlen(cfg_np->name) + GSUFFIX_LEN, @@ -687,6 +720,7 @@ static int exynos5440_pinctrl_parse_dt(struct platform_device *pdev, grp->num_pins = npins; grp++; +skip_to_pin_function: ret = of_property_read_u32(cfg_np, "samsung,exynos5440-pin-function", &function); if (ret) @@ -709,7 +743,7 @@ static int exynos5440_pinctrl_parse_dt(struct platform_device *pdev, return -ENOMEM; } func->groups[0] = gname; - func->num_groups = 1; + func->num_groups = gname ? 1 : 0; func->function = function; func++; func_idx++; @@ -818,6 +852,7 @@ static int exynos5440_gpiolib_register(struct platform_device *pdev, gc->get = exynos5440_gpio_get; gc->direction_input = exynos5440_gpio_direction_input; gc->direction_output = exynos5440_gpio_direction_output; + gc->to_irq = exynos5440_gpio_to_irq; gc->label = "gpiolib-exynos5440"; gc->owner = THIS_MODULE; ret = gpiochip_add(gc); @@ -842,6 +877,110 @@ static int exynos5440_gpiolib_unregister(struct platform_device *pdev, return 0; } +static void exynos5440_gpio_irq_unmask(struct irq_data *irqd) +{ + struct exynos5440_pinctrl_priv_data *d; + unsigned long gpio_int; + + d = irq_data_get_irq_chip_data(irqd); + gpio_int = readl(d->reg_base + GPIO_INT); + gpio_int |= 1 << irqd->hwirq; + writel(gpio_int, d->reg_base + GPIO_INT); +} + +static void exynos5440_gpio_irq_mask(struct irq_data *irqd) +{ + struct exynos5440_pinctrl_priv_data *d; + unsigned long gpio_int; + + d = irq_data_get_irq_chip_data(irqd); + gpio_int = readl(d->reg_base + GPIO_INT); + gpio_int &= ~(1 << irqd->hwirq); + writel(gpio_int, d->reg_base + GPIO_INT); +} + +/* irq_chip for gpio interrupts */ +static struct irq_chip exynos5440_gpio_irq_chip = { + .name = "exynos5440_gpio_irq_chip", + .irq_unmask = exynos5440_gpio_irq_unmask, + .irq_mask = exynos5440_gpio_irq_mask, +}; + +/* interrupt handler for GPIO interrupts 0..7 */ +static irqreturn_t exynos5440_gpio_irq(int irq, void *data) +{ + struct exynos5440_gpio_intr_data *intd = data; + struct exynos5440_pinctrl_priv_data *d = intd->priv; + int virq; + + virq = irq_linear_revmap(d->irq_domain, intd->gpio_int); + if (!virq) + return IRQ_NONE; + generic_handle_irq(virq); + return IRQ_HANDLED; +} + +static int exynos5440_gpio_irq_map(struct irq_domain *h, unsigned int virq, + irq_hw_number_t hw) +{ + struct exynos5440_pinctrl_priv_data *d = h->host_data; + + irq_set_chip_data(virq, d); + irq_set_chip_and_handler(virq, &exynos5440_gpio_irq_chip, + handle_level_irq); + set_irq_flags(virq, IRQF_VALID); + return 0; +} + +/* irq domain callbacks for gpio interrupt controller */ +static const struct irq_domain_ops exynos5440_gpio_irqd_ops = { + .map = exynos5440_gpio_irq_map, + .xlate = irq_domain_xlate_twocell, +}; + +/* setup handling of gpio interrupts */ +static int exynos5440_gpio_irq_init(struct platform_device *pdev, + struct exynos5440_pinctrl_priv_data *priv) +{ + struct device *dev = &pdev->dev; + struct exynos5440_gpio_intr_data *intd; + int i, irq, ret; + + intd = devm_kzalloc(dev, sizeof(*intd) * EXYNOS5440_MAX_GPIO_INT, + GFP_KERNEL); + if (!intd) { + dev_err(dev, "failed to allocate memory for gpio intr data\n"); + return -ENOMEM; + } + + for (i = 0; i < EXYNOS5440_MAX_GPIO_INT; i++) { + irq = irq_of_parse_and_map(dev->of_node, i); + if (irq <= 0) { + dev_err(dev, "irq parsing failed\n"); + return -EINVAL; + } + + intd->gpio_int = i; + intd->priv = priv; + ret = devm_request_irq(dev, irq, exynos5440_gpio_irq, + 0, dev_name(dev), intd++); + if (ret) { + dev_err(dev, "irq request failed\n"); + return -ENXIO; + } + } + + priv->irq_domain = irq_domain_add_linear(dev->of_node, + EXYNOS5440_MAX_GPIO_INT, + &exynos5440_gpio_irqd_ops, priv); + if (!priv->irq_domain) { + dev_err(dev, "failed to create irq domain\n"); + return -ENXIO; + } + + return 0; +} + static int exynos5440_pinctrl_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; @@ -854,7 +993,7 @@ static int exynos5440_pinctrl_probe(struct platform_device *pdev) return -ENODEV; } - priv = devm_kzalloc(dev, sizeof(priv), GFP_KERNEL); + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); if (!priv) { dev_err(dev, "could not allocate memory for private data\n"); return -ENOMEM; @@ -880,6 +1019,12 @@ static int exynos5440_pinctrl_probe(struct platform_device *pdev) return ret; } + ret = exynos5440_gpio_irq_init(pdev, priv); + if (ret) { + dev_err(dev, "failed to setup gpio interrupts\n"); + return ret; + } + platform_set_drvdata(pdev, priv); dev_info(dev, "EXYNOS5440 pinctrl driver registered\n"); return 0; diff --git a/drivers/pinctrl/pinctrl-falcon.c b/drivers/pinctrl/pinctrl-falcon.c index af97a1f90007..f9b2a1d4854f 100644 --- a/drivers/pinctrl/pinctrl-falcon.c +++ b/drivers/pinctrl/pinctrl-falcon.c @@ -353,7 +353,7 @@ static void falcon_pinconf_group_dbg_show(struct pinctrl_dev *pctrldev, { } -static struct pinconf_ops falcon_pinconf_ops = { +static const struct pinconf_ops falcon_pinconf_ops = { .pin_config_get = falcon_pinconf_get, .pin_config_set = falcon_pinconf_set, .pin_config_group_get = falcon_pinconf_group_get, diff --git a/drivers/pinctrl/pinctrl-imx.c b/drivers/pinctrl/pinctrl-imx.c index 4cebb9c6c5c5..0ef190449eab 100644 --- a/drivers/pinctrl/pinctrl-imx.c +++ b/drivers/pinctrl/pinctrl-imx.c @@ -207,7 +207,7 @@ static void imx_dt_free_map(struct pinctrl_dev *pctldev, kfree(map); } -static struct pinctrl_ops imx_pctrl_ops = { +static const struct pinctrl_ops imx_pctrl_ops = { .get_groups_count = imx_get_groups_count, .get_group_name = imx_get_group_name, .get_group_pins = imx_get_group_pins, @@ -299,7 +299,7 @@ static int imx_pmx_get_groups(struct pinctrl_dev *pctldev, unsigned selector, return 0; } -static struct pinmux_ops imx_pmx_ops = { +static const struct pinmux_ops imx_pmx_ops = { .get_functions_count = imx_pmx_get_funcs_count, .get_function_name = imx_pmx_get_func_name, .get_function_groups = imx_pmx_get_groups, @@ -397,7 +397,7 @@ static void imx_pinconf_group_dbg_show(struct pinctrl_dev *pctldev, } } -static struct pinconf_ops imx_pinconf_ops = { +static const struct pinconf_ops imx_pinconf_ops = { .pin_config_get = imx_pinconf_get, .pin_config_set = imx_pinconf_set, .pin_config_dbg_show = imx_pinconf_dbg_show, diff --git a/drivers/pinctrl/pinctrl-lantiq.c b/drivers/pinctrl/pinctrl-lantiq.c index a70384611351..615c5002b757 100644 --- a/drivers/pinctrl/pinctrl-lantiq.c +++ b/drivers/pinctrl/pinctrl-lantiq.c @@ -169,7 +169,7 @@ static int ltq_pinctrl_dt_node_to_map(struct pinctrl_dev *pctldev, return 0; } -static struct pinctrl_ops ltq_pctrl_ops = { +static const struct pinctrl_ops ltq_pctrl_ops = { .get_groups_count = ltq_get_group_count, .get_group_name = ltq_get_group_name, .get_group_pins = ltq_get_group_pins, @@ -311,7 +311,7 @@ static int ltq_pmx_gpio_request_enable(struct pinctrl_dev *pctrldev, return info->apply_mux(pctrldev, mfp, pin_func); } -static struct pinmux_ops ltq_pmx_ops = { +static const struct pinmux_ops ltq_pmx_ops = { .get_functions_count = ltq_pmx_func_count, .get_function_name = ltq_pmx_func_name, .get_function_groups = ltq_pmx_get_groups, diff --git a/drivers/pinctrl/pinctrl-mmp2.c b/drivers/pinctrl/pinctrl-mmp2.c deleted file mode 100644 index 4afa56a3a51d..000000000000 --- a/drivers/pinctrl/pinctrl-mmp2.c +++ /dev/null @@ -1,722 +0,0 @@ -/* - * linux/drivers/pinctrl/pinmux-mmp2.c - * - * 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 - * publishhed by the Free Software Foundation. - * - * Copyright (C) 2011, Marvell Technology Group Ltd. - * - * Author: Haojian Zhuang <haojian.zhuang@marvell.com> - * - */ - -#include <linux/device.h> -#include <linux/module.h> -#include <linux/io.h> -#include <linux/platform_device.h> -#include "pinctrl-pxa3xx.h" - -#define MMP2_DS_MASK 0x1800 -#define MMP2_DS_SHIFT 11 -#define MMP2_SLEEP_MASK 0x38 -#define MMP2_SLEEP_SELECT (1 << 9) -#define MMP2_SLEEP_DATA (1 << 8) -#define MMP2_SLEEP_DIR (1 << 7) - -#define MFPR_MMP2(a, r, f0, f1, f2, f3, f4, f5, f6, f7) \ - { \ - .name = #a, \ - .pin = a, \ - .mfpr = r, \ - .func = { \ - MMP2_MUX_##f0, \ - MMP2_MUX_##f1, \ - MMP2_MUX_##f2, \ - MMP2_MUX_##f3, \ - MMP2_MUX_##f4, \ - MMP2_MUX_##f5, \ - MMP2_MUX_##f6, \ - MMP2_MUX_##f7, \ - }, \ - } - -#define GRP_MMP2(a, m, p) \ - { .name = a, .mux = MMP2_MUX_##m, .pins = p, .npins = ARRAY_SIZE(p), } - -/* 174 pins */ -enum mmp2_pin_list { - /* 0~168: GPIO0~GPIO168 */ - TWSI4_SCL = 169, - TWSI4_SDA, /* 170 */ - G_CLKREQ, - VCXO_REQ, - VCXO_OUT, -}; - -enum mmp2_mux { - /* PXA3xx_MUX_GPIO = 0 (predefined in pinctrl-pxa3xx.h) */ - MMP2_MUX_GPIO = 0, - MMP2_MUX_G_CLKREQ, - MMP2_MUX_VCXO_REQ, - MMP2_MUX_VCXO_OUT, - MMP2_MUX_KP_MK, - MMP2_MUX_KP_DK, - MMP2_MUX_CCIC1, - MMP2_MUX_CCIC2, - MMP2_MUX_SPI, - MMP2_MUX_SSPA2, - MMP2_MUX_ROT, - MMP2_MUX_I2S, - MMP2_MUX_TB, - MMP2_MUX_CAM2, - MMP2_MUX_HDMI, - MMP2_MUX_TWSI2, - MMP2_MUX_TWSI3, - MMP2_MUX_TWSI4, - MMP2_MUX_TWSI5, - MMP2_MUX_TWSI6, - MMP2_MUX_UART1, - MMP2_MUX_UART2, - MMP2_MUX_UART3, - MMP2_MUX_UART4, - MMP2_MUX_SSP1_RX, - MMP2_MUX_SSP1_FRM, - MMP2_MUX_SSP1_TXRX, - MMP2_MUX_SSP2_RX, - MMP2_MUX_SSP2_FRM, - MMP2_MUX_SSP1, - MMP2_MUX_SSP2, - MMP2_MUX_SSP3, - MMP2_MUX_SSP4, - MMP2_MUX_MMC1, - MMP2_MUX_MMC2, - MMP2_MUX_MMC3, - MMP2_MUX_MMC4, - MMP2_MUX_ULPI, - MMP2_MUX_AC, - MMP2_MUX_CA, - MMP2_MUX_PWM, - MMP2_MUX_USIM, - MMP2_MUX_TIPU, - MMP2_MUX_PLL, - MMP2_MUX_NAND, - MMP2_MUX_FSIC, - MMP2_MUX_SLEEP_IND, - MMP2_MUX_EXT_DMA, - MMP2_MUX_ONE_WIRE, - MMP2_MUX_LCD, - MMP2_MUX_SMC, - MMP2_MUX_SMC_INT, - MMP2_MUX_MSP, - MMP2_MUX_G_CLKOUT, - MMP2_MUX_32K_CLKOUT, - MMP2_MUX_PRI_JTAG, - MMP2_MUX_AAS_JTAG, - MMP2_MUX_AAS_GPIO, - MMP2_MUX_AAS_SPI, - MMP2_MUX_AAS_TWSI, - MMP2_MUX_AAS_DEU_EX, - MMP2_MUX_NONE = 0xffff, -}; - -static struct pinctrl_pin_desc mmp2_pads[] = { - /* - * The name indicates function 0 of this pin. - * After reset, function 0 is the default function of pin. - */ - PINCTRL_PIN(GPIO0, "GPIO0"), - PINCTRL_PIN(GPIO1, "GPIO1"), - PINCTRL_PIN(GPIO2, "GPIO2"), - PINCTRL_PIN(GPIO3, "GPIO3"), - PINCTRL_PIN(GPIO4, "GPIO4"), - PINCTRL_PIN(GPIO5, "GPIO5"), - PINCTRL_PIN(GPIO6, "GPIO6"), - PINCTRL_PIN(GPIO7, "GPIO7"), - PINCTRL_PIN(GPIO8, "GPIO8"), - PINCTRL_PIN(GPIO9, "GPIO9"), - PINCTRL_PIN(GPIO10, "GPIO10"), - PINCTRL_PIN(GPIO11, "GPIO11"), - PINCTRL_PIN(GPIO12, "GPIO12"), - PINCTRL_PIN(GPIO13, "GPIO13"), - PINCTRL_PIN(GPIO14, "GPIO14"), - PINCTRL_PIN(GPIO15, "GPIO15"), - PINCTRL_PIN(GPIO16, "GPIO16"), - PINCTRL_PIN(GPIO17, "GPIO17"), - PINCTRL_PIN(GPIO18, "GPIO18"), - PINCTRL_PIN(GPIO19, "GPIO19"), - PINCTRL_PIN(GPIO20, "GPIO20"), - PINCTRL_PIN(GPIO21, "GPIO21"), - PINCTRL_PIN(GPIO22, "GPIO22"), - PINCTRL_PIN(GPIO23, "GPIO23"), - PINCTRL_PIN(GPIO24, "GPIO24"), - PINCTRL_PIN(GPIO25, "GPIO25"), - PINCTRL_PIN(GPIO26, "GPIO26"), - PINCTRL_PIN(GPIO27, "GPIO27"), - PINCTRL_PIN(GPIO28, "GPIO28"), - PINCTRL_PIN(GPIO29, "GPIO29"), - PINCTRL_PIN(GPIO30, "GPIO30"), - PINCTRL_PIN(GPIO31, "GPIO31"), - PINCTRL_PIN(GPIO32, "GPIO32"), - PINCTRL_PIN(GPIO33, "GPIO33"), - PINCTRL_PIN(GPIO34, "GPIO34"), - PINCTRL_PIN(GPIO35, "GPIO35"), - PINCTRL_PIN(GPIO36, "GPIO36"), - PINCTRL_PIN(GPIO37, "GPIO37"), - PINCTRL_PIN(GPIO38, "GPIO38"), - PINCTRL_PIN(GPIO39, "GPIO39"), - PINCTRL_PIN(GPIO40, "GPIO40"), - PINCTRL_PIN(GPIO41, "GPIO41"), - PINCTRL_PIN(GPIO42, "GPIO42"), - PINCTRL_PIN(GPIO43, "GPIO43"), - PINCTRL_PIN(GPIO44, "GPIO44"), - PINCTRL_PIN(GPIO45, "GPIO45"), - PINCTRL_PIN(GPIO46, "GPIO46"), - PINCTRL_PIN(GPIO47, "GPIO47"), - PINCTRL_PIN(GPIO48, "GPIO48"), - PINCTRL_PIN(GPIO49, "GPIO49"), - PINCTRL_PIN(GPIO50, "GPIO50"), - PINCTRL_PIN(GPIO51, "GPIO51"), - PINCTRL_PIN(GPIO52, "GPIO52"), - PINCTRL_PIN(GPIO53, "GPIO53"), - PINCTRL_PIN(GPIO54, "GPIO54"), - PINCTRL_PIN(GPIO55, "GPIO55"), - PINCTRL_PIN(GPIO56, "GPIO56"), - PINCTRL_PIN(GPIO57, "GPIO57"), - PINCTRL_PIN(GPIO58, "GPIO58"), - PINCTRL_PIN(GPIO59, "GPIO59"), - PINCTRL_PIN(GPIO60, "GPIO60"), - PINCTRL_PIN(GPIO61, "GPIO61"), - PINCTRL_PIN(GPIO62, "GPIO62"), - PINCTRL_PIN(GPIO63, "GPIO63"), - PINCTRL_PIN(GPIO64, "GPIO64"), - PINCTRL_PIN(GPIO65, "GPIO65"), - PINCTRL_PIN(GPIO66, "GPIO66"), - PINCTRL_PIN(GPIO67, "GPIO67"), - PINCTRL_PIN(GPIO68, "GPIO68"), - PINCTRL_PIN(GPIO69, "GPIO69"), - PINCTRL_PIN(GPIO70, "GPIO70"), - PINCTRL_PIN(GPIO71, "GPIO71"), - PINCTRL_PIN(GPIO72, "GPIO72"), - PINCTRL_PIN(GPIO73, "GPIO73"), - PINCTRL_PIN(GPIO74, "GPIO74"), - PINCTRL_PIN(GPIO75, "GPIO75"), - PINCTRL_PIN(GPIO76, "GPIO76"), - PINCTRL_PIN(GPIO77, "GPIO77"), - PINCTRL_PIN(GPIO78, "GPIO78"), - PINCTRL_PIN(GPIO79, "GPIO79"), - PINCTRL_PIN(GPIO80, "GPIO80"), - PINCTRL_PIN(GPIO81, "GPIO81"), - PINCTRL_PIN(GPIO82, "GPIO82"), - PINCTRL_PIN(GPIO83, "GPIO83"), - PINCTRL_PIN(GPIO84, "GPIO84"), - PINCTRL_PIN(GPIO85, "GPIO85"), - PINCTRL_PIN(GPIO86, "GPIO86"), - PINCTRL_PIN(GPIO87, "GPIO87"), - PINCTRL_PIN(GPIO88, "GPIO88"), - PINCTRL_PIN(GPIO89, "GPIO89"), - PINCTRL_PIN(GPIO90, "GPIO90"), - PINCTRL_PIN(GPIO91, "GPIO91"), - PINCTRL_PIN(GPIO92, "GPIO92"), - PINCTRL_PIN(GPIO93, "GPIO93"), - PINCTRL_PIN(GPIO94, "GPIO94"), - PINCTRL_PIN(GPIO95, "GPIO95"), - PINCTRL_PIN(GPIO96, "GPIO96"), - PINCTRL_PIN(GPIO97, "GPIO97"), - PINCTRL_PIN(GPIO98, "GPIO98"), - PINCTRL_PIN(GPIO99, "GPIO99"), - PINCTRL_PIN(GPIO100, "GPIO100"), - PINCTRL_PIN(GPIO101, "GPIO101"), - PINCTRL_PIN(GPIO102, "GPIO102"), - PINCTRL_PIN(GPIO103, "GPIO103"), - PINCTRL_PIN(GPIO104, "GPIO104"), - PINCTRL_PIN(GPIO105, "GPIO105"), - PINCTRL_PIN(GPIO106, "GPIO106"), - PINCTRL_PIN(GPIO107, "GPIO107"), - PINCTRL_PIN(GPIO108, "GPIO108"), - PINCTRL_PIN(GPIO109, "GPIO109"), - PINCTRL_PIN(GPIO110, "GPIO110"), - PINCTRL_PIN(GPIO111, "GPIO111"), - PINCTRL_PIN(GPIO112, "GPIO112"), - PINCTRL_PIN(GPIO113, "GPIO113"), - PINCTRL_PIN(GPIO114, "GPIO114"), - PINCTRL_PIN(GPIO115, "GPIO115"), - PINCTRL_PIN(GPIO116, "GPIO116"), - PINCTRL_PIN(GPIO117, "GPIO117"), - PINCTRL_PIN(GPIO118, "GPIO118"), - PINCTRL_PIN(GPIO119, "GPIO119"), - PINCTRL_PIN(GPIO120, "GPIO120"), - PINCTRL_PIN(GPIO121, "GPIO121"), - PINCTRL_PIN(GPIO122, "GPIO122"), - PINCTRL_PIN(GPIO123, "GPIO123"), - PINCTRL_PIN(GPIO124, "GPIO124"), - PINCTRL_PIN(GPIO125, "GPIO125"), - PINCTRL_PIN(GPIO126, "GPIO126"), - PINCTRL_PIN(GPIO127, "GPIO127"), - PINCTRL_PIN(GPIO128, "GPIO128"), - PINCTRL_PIN(GPIO129, "GPIO129"), - PINCTRL_PIN(GPIO130, "GPIO130"), - PINCTRL_PIN(GPIO131, "GPIO131"), - PINCTRL_PIN(GPIO132, "GPIO132"), - PINCTRL_PIN(GPIO133, "GPIO133"), - PINCTRL_PIN(GPIO134, "GPIO134"), - PINCTRL_PIN(GPIO135, "GPIO135"), - PINCTRL_PIN(GPIO136, "GPIO136"), - PINCTRL_PIN(GPIO137, "GPIO137"), - PINCTRL_PIN(GPIO138, "GPIO138"), - PINCTRL_PIN(GPIO139, "GPIO139"), - PINCTRL_PIN(GPIO140, "GPIO140"), - PINCTRL_PIN(GPIO141, "GPIO141"), - PINCTRL_PIN(GPIO142, "GPIO142"), - PINCTRL_PIN(GPIO143, "GPIO143"), - PINCTRL_PIN(GPIO144, "GPIO144"), - PINCTRL_PIN(GPIO145, "GPIO145"), - PINCTRL_PIN(GPIO146, "GPIO146"), - PINCTRL_PIN(GPIO147, "GPIO147"), - PINCTRL_PIN(GPIO148, "GPIO148"), - PINCTRL_PIN(GPIO149, "GPIO149"), - PINCTRL_PIN(GPIO150, "GPIO150"), - PINCTRL_PIN(GPIO151, "GPIO151"), - PINCTRL_PIN(GPIO152, "GPIO152"), - PINCTRL_PIN(GPIO153, "GPIO153"), - PINCTRL_PIN(GPIO154, "GPIO154"), - PINCTRL_PIN(GPIO155, "GPIO155"), - PINCTRL_PIN(GPIO156, "GPIO156"), - PINCTRL_PIN(GPIO157, "GPIO157"), - PINCTRL_PIN(GPIO158, "GPIO158"), - PINCTRL_PIN(GPIO159, "GPIO159"), - PINCTRL_PIN(GPIO160, "GPIO160"), - PINCTRL_PIN(GPIO161, "GPIO161"), - PINCTRL_PIN(GPIO162, "GPIO162"), - PINCTRL_PIN(GPIO163, "GPIO163"), - PINCTRL_PIN(GPIO164, "GPIO164"), - PINCTRL_PIN(GPIO165, "GPIO165"), - PINCTRL_PIN(GPIO166, "GPIO166"), - PINCTRL_PIN(GPIO167, "GPIO167"), - PINCTRL_PIN(GPIO168, "GPIO168"), - PINCTRL_PIN(TWSI4_SCL, "TWSI4_SCL"), - PINCTRL_PIN(TWSI4_SDA, "TWSI4_SDA"), - PINCTRL_PIN(G_CLKREQ, "G_CLKREQ"), - PINCTRL_PIN(VCXO_REQ, "VCXO_REQ"), - PINCTRL_PIN(VCXO_OUT, "VCXO_OUT"), -}; - -struct pxa3xx_mfp_pin mmp2_mfp[] = { - /* pin offs f0 f1 f2 f3 f4 f5 f6 f7 */ - MFPR_MMP2(GPIO0, 0x054, GPIO, KP_MK, NONE, SPI, NONE, NONE, NONE, NONE), - MFPR_MMP2(GPIO1, 0x058, GPIO, KP_MK, NONE, SPI, NONE, NONE, NONE, NONE), - MFPR_MMP2(GPIO2, 0x05C, GPIO, KP_MK, NONE, SPI, NONE, NONE, NONE, NONE), - MFPR_MMP2(GPIO3, 0x060, GPIO, KP_MK, NONE, SPI, NONE, NONE, NONE, NONE), - MFPR_MMP2(GPIO4, 0x064, GPIO, KP_MK, NONE, NONE, NONE, NONE, NONE, NONE), - MFPR_MMP2(GPIO5, 0x068, GPIO, KP_MK, NONE, SPI, NONE, NONE, NONE, NONE), - MFPR_MMP2(GPIO6, 0x06C, GPIO, KP_MK, NONE, SPI, NONE, NONE, NONE, NONE), - MFPR_MMP2(GPIO7, 0x070, GPIO, KP_MK, NONE, SPI, NONE, NONE, NONE, NONE), - MFPR_MMP2(GPIO8, 0x074, GPIO, KP_MK, NONE, NONE, NONE, NONE, NONE, NONE), - MFPR_MMP2(GPIO9, 0x078, GPIO, KP_MK, NONE, NONE, NONE, NONE, NONE, NONE), - MFPR_MMP2(GPIO10, 0x07C, GPIO, KP_MK, NONE, NONE, NONE, NONE, NONE, NONE), - MFPR_MMP2(GPIO11, 0x080, GPIO, KP_MK, NONE, NONE, NONE, NONE, NONE, NONE), - MFPR_MMP2(GPIO12, 0x084, GPIO, KP_MK, NONE, CCIC1, NONE, NONE, NONE, NONE), - MFPR_MMP2(GPIO13, 0x088, GPIO, KP_MK, NONE, CCIC1, NONE, NONE, NONE, NONE), - MFPR_MMP2(GPIO14, 0x08C, GPIO, KP_MK, NONE, CCIC1, NONE, NONE, NONE, NONE), - MFPR_MMP2(GPIO15, 0x090, GPIO, KP_MK, KP_DK, CCIC1, NONE, NONE, NONE, NONE), - MFPR_MMP2(GPIO16, 0x094, GPIO, KP_DK, ROT, CCIC1, NONE, NONE, NONE, NONE), - MFPR_MMP2(GPIO17, 0x098, GPIO, KP_DK, ROT, CCIC1, NONE, NONE, NONE, NONE), - MFPR_MMP2(GPIO18, 0x09C, GPIO, KP_DK, ROT, CCIC1, NONE, NONE, NONE, NONE), - MFPR_MMP2(GPIO19, 0x0A0, GPIO, KP_DK, ROT, CCIC1, NONE, NONE, NONE, NONE), - MFPR_MMP2(GPIO20, 0x0A4, GPIO, KP_DK, TB, CCIC1, NONE, NONE, NONE, NONE), - MFPR_MMP2(GPIO21, 0x0A8, GPIO, KP_DK, TB, CCIC1, NONE, NONE, NONE, NONE), - MFPR_MMP2(GPIO22, 0x0AC, GPIO, KP_DK, TB, CCIC1, NONE, NONE, NONE, NONE), - MFPR_MMP2(GPIO23, 0x0B0, GPIO, KP_DK, TB, CCIC1, NONE, NONE, NONE, NONE), - MFPR_MMP2(GPIO24, 0x0B4, GPIO, I2S, VCXO_OUT, NONE, NONE, NONE, NONE, NONE), - MFPR_MMP2(GPIO25, 0x0B8, GPIO, I2S, HDMI, SSPA2, NONE, NONE, NONE, NONE), - MFPR_MMP2(GPIO26, 0x0BC, GPIO, I2S, HDMI, SSPA2, NONE, NONE, NONE, NONE), - MFPR_MMP2(GPIO27, 0x0C0, GPIO, I2S, HDMI, SSPA2, NONE, NONE, NONE, NONE), - MFPR_MMP2(GPIO28, 0x0C4, GPIO, I2S, NONE, SSPA2, NONE, NONE, NONE, NONE), - MFPR_MMP2(GPIO29, 0x0C8, GPIO, UART1, KP_MK, NONE, NONE, NONE, AAS_SPI, NONE), - MFPR_MMP2(GPIO30, 0x0CC, GPIO, UART1, KP_MK, NONE, NONE, NONE, AAS_SPI, NONE), - MFPR_MMP2(GPIO31, 0x0D0, GPIO, UART1, KP_MK, NONE, NONE, NONE, AAS_SPI, NONE), - MFPR_MMP2(GPIO32, 0x0D4, GPIO, UART1, KP_MK, NONE, NONE, NONE, AAS_SPI, NONE), - MFPR_MMP2(GPIO33, 0x0D8, GPIO, SSPA2, I2S, NONE, NONE, NONE, NONE, NONE), - MFPR_MMP2(GPIO34, 0x0DC, GPIO, SSPA2, I2S, NONE, NONE, NONE, NONE, NONE), - MFPR_MMP2(GPIO35, 0x0E0, GPIO, SSPA2, I2S, NONE, NONE, NONE, NONE, NONE), - MFPR_MMP2(GPIO36, 0x0E4, GPIO, SSPA2, I2S, NONE, NONE, NONE, NONE, NONE), - MFPR_MMP2(GPIO37, 0x0E8, GPIO, MMC2, SSP1, TWSI2, UART2, UART3, AAS_SPI, AAS_TWSI), - MFPR_MMP2(GPIO38, 0x0EC, GPIO, MMC2, SSP1, TWSI2, UART2, UART3, AAS_SPI, AAS_TWSI), - MFPR_MMP2(GPIO39, 0x0F0, GPIO, MMC2, SSP1, TWSI2, UART2, UART3, AAS_SPI, AAS_TWSI), - MFPR_MMP2(GPIO40, 0x0F4, GPIO, MMC2, SSP1, TWSI2, UART2, UART3, AAS_SPI, AAS_TWSI), - MFPR_MMP2(GPIO41, 0x0F8, GPIO, MMC2, TWSI5, NONE, NONE, NONE, NONE, NONE), - MFPR_MMP2(GPIO42, 0x0FC, GPIO, MMC2, TWSI5, NONE, NONE, NONE, NONE, NONE), - MFPR_MMP2(GPIO43, 0x100, GPIO, TWSI2, UART4, SSP1, UART2, UART3, NONE, AAS_TWSI), - MFPR_MMP2(GPIO44, 0x104, GPIO, TWSI2, UART4, SSP1, UART2, UART3, NONE, AAS_TWSI), - MFPR_MMP2(GPIO45, 0x108, GPIO, UART1, UART4, SSP1, UART2, UART3, NONE, NONE), - MFPR_MMP2(GPIO46, 0x10C, GPIO, UART1, UART4, SSP1, UART2, UART3, NONE, NONE), - MFPR_MMP2(GPIO47, 0x110, GPIO, UART2, SSP2, TWSI6, CAM2, AAS_SPI, AAS_GPIO, NONE), - MFPR_MMP2(GPIO48, 0x114, GPIO, UART2, SSP2, TWSI6, CAM2, AAS_SPI, AAS_GPIO, NONE), - MFPR_MMP2(GPIO49, 0x118, GPIO, UART2, SSP2, PWM, CCIC2, AAS_SPI, NONE, NONE), - MFPR_MMP2(GPIO50, 0x11C, GPIO, UART2, SSP2, PWM, CCIC2, AAS_SPI, NONE, NONE), - MFPR_MMP2(GPIO51, 0x120, GPIO, UART3, ROT, AAS_GPIO, PWM, NONE, NONE, NONE), - MFPR_MMP2(GPIO52, 0x124, GPIO, UART3, ROT, AAS_GPIO, PWM, NONE, NONE, NONE), - MFPR_MMP2(GPIO53, 0x128, GPIO, UART3, TWSI2, VCXO_REQ, NONE, PWM, NONE, AAS_TWSI), - MFPR_MMP2(GPIO54, 0x12C, GPIO, UART3, TWSI2, VCXO_OUT, HDMI, PWM, NONE, AAS_TWSI), - MFPR_MMP2(GPIO55, 0x130, GPIO, SSP2, SSP1, UART2, ROT, TWSI2, SSP3, AAS_TWSI), - MFPR_MMP2(GPIO56, 0x134, GPIO, SSP2, SSP1, UART2, ROT, TWSI2, KP_DK, AAS_TWSI), - MFPR_MMP2(GPIO57, 0x138, GPIO, SSP2_RX, SSP1_TXRX, SSP2_FRM, SSP1_RX, VCXO_REQ, KP_DK, NONE), - MFPR_MMP2(GPIO58, 0x13C, GPIO, SSP2, SSP1_RX, SSP1_FRM, SSP1_TXRX, VCXO_REQ, KP_DK, NONE), - MFPR_MMP2(GPIO59, 0x280, GPIO, CCIC1, ULPI, MMC3, CCIC2, UART3, UART4, NONE), - MFPR_MMP2(GPIO60, 0x284, GPIO, CCIC1, ULPI, MMC3, CCIC2, UART3, UART4, NONE), - MFPR_MMP2(GPIO61, 0x288, GPIO, CCIC1, ULPI, MMC3, CCIC2, UART3, HDMI, NONE), - MFPR_MMP2(GPIO62, 0x28C, GPIO, CCIC1, ULPI, MMC3, CCIC2, UART3, NONE, NONE), - MFPR_MMP2(GPIO63, 0x290, GPIO, CCIC1, ULPI, MMC3, CCIC2, MSP, UART4, NONE), - MFPR_MMP2(GPIO64, 0x294, GPIO, CCIC1, ULPI, MMC3, CCIC2, MSP, UART4, NONE), - MFPR_MMP2(GPIO65, 0x298, GPIO, CCIC1, ULPI, MMC3, CCIC2, MSP, UART4, NONE), - MFPR_MMP2(GPIO66, 0x29C, GPIO, CCIC1, ULPI, MMC3, CCIC2, MSP, UART4, NONE), - MFPR_MMP2(GPIO67, 0x2A0, GPIO, CCIC1, ULPI, MMC3, CCIC2, MSP, NONE, NONE), - MFPR_MMP2(GPIO68, 0x2A4, GPIO, CCIC1, ULPI, MMC3, CCIC2, MSP, LCD, NONE), - MFPR_MMP2(GPIO69, 0x2A8, GPIO, CCIC1, ULPI, MMC3, CCIC2, NONE, LCD, NONE), - MFPR_MMP2(GPIO70, 0x2AC, GPIO, CCIC1, ULPI, MMC3, CCIC2, MSP, LCD, NONE), - MFPR_MMP2(GPIO71, 0x2B0, GPIO, TWSI3, NONE, PWM, NONE, NONE, LCD, AAS_TWSI), - MFPR_MMP2(GPIO72, 0x2B4, GPIO, TWSI3, HDMI, PWM, NONE, NONE, LCD, AAS_TWSI), - MFPR_MMP2(GPIO73, 0x2B8, GPIO, VCXO_REQ, 32K_CLKOUT, PWM, VCXO_OUT, NONE, LCD, NONE), - MFPR_MMP2(GPIO74, 0x170, GPIO, LCD, SMC, MMC4, SSP3, UART2, UART4, TIPU), - MFPR_MMP2(GPIO75, 0x174, GPIO, LCD, SMC, MMC4, SSP3, UART2, UART4, TIPU), - MFPR_MMP2(GPIO76, 0x178, GPIO, LCD, SMC, MMC4, SSP3, UART2, UART4, TIPU), - MFPR_MMP2(GPIO77, 0x17C, GPIO, LCD, SMC, MMC4, SSP3, UART2, UART4, TIPU), - MFPR_MMP2(GPIO78, 0x180, GPIO, LCD, HDMI, MMC4, NONE, SSP4, AAS_SPI, TIPU), - MFPR_MMP2(GPIO79, 0x184, GPIO, LCD, AAS_GPIO, MMC4, NONE, SSP4, AAS_SPI, TIPU), - MFPR_MMP2(GPIO80, 0x188, GPIO, LCD, AAS_GPIO, MMC4, NONE, SSP4, AAS_SPI, TIPU), - MFPR_MMP2(GPIO81, 0x18C, GPIO, LCD, AAS_GPIO, MMC4, NONE, SSP4, AAS_SPI, TIPU), - MFPR_MMP2(GPIO82, 0x190, GPIO, LCD, NONE, MMC4, NONE, NONE, CCIC2, TIPU), - MFPR_MMP2(GPIO83, 0x194, GPIO, LCD, NONE, MMC4, NONE, NONE, CCIC2, TIPU), - MFPR_MMP2(GPIO84, 0x198, GPIO, LCD, SMC, MMC2, NONE, TWSI5, AAS_TWSI, TIPU), - MFPR_MMP2(GPIO85, 0x19C, GPIO, LCD, SMC, MMC2, NONE, TWSI5, AAS_TWSI, TIPU), - MFPR_MMP2(GPIO86, 0x1A0, GPIO, LCD, SMC, MMC2, NONE, TWSI6, CCIC2, TIPU), - MFPR_MMP2(GPIO87, 0x1A4, GPIO, LCD, SMC, MMC2, NONE, TWSI6, CCIC2, TIPU), - MFPR_MMP2(GPIO88, 0x1A8, GPIO, LCD, AAS_GPIO, MMC2, NONE, NONE, CCIC2, TIPU), - MFPR_MMP2(GPIO89, 0x1AC, GPIO, LCD, AAS_GPIO, MMC2, NONE, NONE, CCIC2, TIPU), - MFPR_MMP2(GPIO90, 0x1B0, GPIO, LCD, AAS_GPIO, MMC2, NONE, NONE, CCIC2, TIPU), - MFPR_MMP2(GPIO91, 0x1B4, GPIO, LCD, AAS_GPIO, MMC2, NONE, NONE, CCIC2, TIPU), - MFPR_MMP2(GPIO92, 0x1B8, GPIO, LCD, AAS_GPIO, MMC2, NONE, NONE, CCIC2, TIPU), - MFPR_MMP2(GPIO93, 0x1BC, GPIO, LCD, AAS_GPIO, MMC2, NONE, NONE, CCIC2, TIPU), - MFPR_MMP2(GPIO94, 0x1C0, GPIO, LCD, AAS_GPIO, SPI, NONE, AAS_SPI, CCIC2, TIPU), - MFPR_MMP2(GPIO95, 0x1C4, GPIO, LCD, TWSI3, SPI, AAS_DEU_EX, AAS_SPI, CCIC2, TIPU), - MFPR_MMP2(GPIO96, 0x1C8, GPIO, LCD, TWSI3, SPI, AAS_DEU_EX, AAS_SPI, NONE, TIPU), - MFPR_MMP2(GPIO97, 0x1CC, GPIO, LCD, TWSI6, SPI, AAS_DEU_EX, AAS_SPI, NONE, TIPU), - MFPR_MMP2(GPIO98, 0x1D0, GPIO, LCD, TWSI6, SPI, ONE_WIRE, NONE, NONE, TIPU), - MFPR_MMP2(GPIO99, 0x1D4, GPIO, LCD, SMC, SPI, TWSI5, NONE, NONE, TIPU), - MFPR_MMP2(GPIO100, 0x1D8, GPIO, LCD, SMC, SPI, TWSI5, NONE, NONE, TIPU), - MFPR_MMP2(GPIO101, 0x1DC, GPIO, LCD, SMC, SPI, NONE, NONE, NONE, TIPU), - MFPR_MMP2(GPIO102, 0x000, USIM, GPIO, FSIC, KP_DK, LCD, NONE, NONE, NONE), - MFPR_MMP2(GPIO103, 0x004, USIM, GPIO, FSIC, KP_DK, LCD, NONE, NONE, NONE), - MFPR_MMP2(GPIO104, 0x1FC, NAND, GPIO, NONE, NONE, NAND, NONE, NONE, NONE), - MFPR_MMP2(GPIO105, 0x1F8, NAND, GPIO, NONE, NONE, NAND, NONE, NONE, NONE), - MFPR_MMP2(GPIO106, 0x1F4, NAND, GPIO, NONE, NONE, NAND, NONE, NONE, NONE), - MFPR_MMP2(GPIO107, 0x1F0, NAND, GPIO, NONE, NONE, NAND, NONE, NONE, NONE), - MFPR_MMP2(GPIO108, 0x21C, NAND, GPIO, NONE, NONE, NAND, NONE, NONE, NONE), - MFPR_MMP2(GPIO109, 0x218, NAND, GPIO, NONE, NONE, NAND, NONE, NONE, NONE), - MFPR_MMP2(GPIO110, 0x214, NAND, GPIO, NONE, NONE, NAND, NONE, NONE, NONE), - MFPR_MMP2(GPIO111, 0x200, NAND, GPIO, MMC3, NONE, NONE, NONE, NONE, NONE), - MFPR_MMP2(GPIO112, 0x244, NAND, GPIO, MMC3, SMC, NONE, NONE, NONE, NONE), - MFPR_MMP2(GPIO113, 0x25C, SMC, GPIO, EXT_DMA, MMC3, SMC, HDMI, NONE, NONE), - MFPR_MMP2(GPIO114, 0x164, G_CLKOUT, 32K_CLKOUT, HDMI, NONE, NONE, NONE, NONE, NONE), - MFPR_MMP2(GPIO115, 0x260, GPIO, NONE, AC, UART4, UART3, SSP1, NONE, NONE), - MFPR_MMP2(GPIO116, 0x264, GPIO, NONE, AC, UART4, UART3, SSP1, NONE, NONE), - MFPR_MMP2(GPIO117, 0x268, GPIO, NONE, AC, UART4, UART3, SSP1, NONE, NONE), - MFPR_MMP2(GPIO118, 0x26C, GPIO, NONE, AC, UART4, UART3, SSP1, NONE, NONE), - MFPR_MMP2(GPIO119, 0x270, GPIO, NONE, CA, SSP3, NONE, NONE, NONE, NONE), - MFPR_MMP2(GPIO120, 0x274, GPIO, NONE, CA, SSP3, NONE, NONE, NONE, NONE), - MFPR_MMP2(GPIO121, 0x278, GPIO, NONE, CA, SSP3, NONE, NONE, NONE, NONE), - MFPR_MMP2(GPIO122, 0x27C, GPIO, NONE, CA, SSP3, NONE, NONE, NONE, NONE), - MFPR_MMP2(GPIO123, 0x148, GPIO, SLEEP_IND, ONE_WIRE, 32K_CLKOUT, NONE, NONE, NONE, NONE), - MFPR_MMP2(GPIO124, 0x00C, GPIO, MMC1, LCD, MMC3, NAND, NONE, NONE, NONE), - MFPR_MMP2(GPIO125, 0x010, GPIO, MMC1, LCD, MMC3, NAND, NONE, NONE, NONE), - MFPR_MMP2(GPIO126, 0x014, GPIO, MMC1, LCD, MMC3, NAND, NONE, NONE, NONE), - MFPR_MMP2(GPIO127, 0x018, GPIO, NONE, LCD, MMC3, NAND, NONE, NONE, NONE), - MFPR_MMP2(GPIO128, 0x01C, GPIO, NONE, LCD, MMC3, NAND, NONE, NONE, NONE), - MFPR_MMP2(GPIO129, 0x020, GPIO, MMC1, LCD, MMC3, NAND, NONE, NONE, NONE), - MFPR_MMP2(GPIO130, 0x024, GPIO, MMC1, LCD, MMC3, NAND, NONE, NONE, NONE), - MFPR_MMP2(GPIO131, 0x028, GPIO, MMC1, NONE, MSP, NONE, NONE, NONE, NONE), - MFPR_MMP2(GPIO132, 0x02C, GPIO, MMC1, PRI_JTAG, MSP, SSP3, AAS_JTAG, NONE, NONE), - MFPR_MMP2(GPIO133, 0x030, GPIO, MMC1, PRI_JTAG, MSP, SSP3, AAS_JTAG, NONE, NONE), - MFPR_MMP2(GPIO134, 0x034, GPIO, MMC1, PRI_JTAG, MSP, SSP3, AAS_JTAG, NONE, NONE), - MFPR_MMP2(GPIO135, 0x038, GPIO, NONE, LCD, MMC3, NAND, NONE, NONE, NONE), - MFPR_MMP2(GPIO136, 0x03C, GPIO, MMC1, PRI_JTAG, MSP, SSP3, AAS_JTAG, NONE, NONE), - MFPR_MMP2(GPIO137, 0x040, GPIO, HDMI, LCD, MSP, NONE, NONE, NONE, NONE), - MFPR_MMP2(GPIO138, 0x044, GPIO, NONE, LCD, MMC3, SMC, NONE, NONE, NONE), - MFPR_MMP2(GPIO139, 0x048, GPIO, MMC1, PRI_JTAG, MSP, NONE, AAS_JTAG, NONE, NONE), - MFPR_MMP2(GPIO140, 0x04C, GPIO, MMC1, LCD, NONE, NONE, UART2, UART1, NONE), - MFPR_MMP2(GPIO141, 0x050, GPIO, MMC1, LCD, NONE, NONE, UART2, UART1, NONE), - MFPR_MMP2(GPIO142, 0x008, USIM, GPIO, FSIC, KP_DK, NONE, NONE, NONE, NONE), - MFPR_MMP2(GPIO143, 0x220, NAND, GPIO, SMC, NONE, NAND, NONE, NONE, NONE), - MFPR_MMP2(GPIO144, 0x224, NAND, GPIO, SMC_INT, SMC, NAND, NONE, NONE, NONE), - MFPR_MMP2(GPIO145, 0x228, SMC, GPIO, NONE, NONE, SMC, NONE, NONE, NONE), - MFPR_MMP2(GPIO146, 0x22C, SMC, GPIO, NONE, NONE, SMC, NONE, NONE, NONE), - MFPR_MMP2(GPIO147, 0x230, NAND, GPIO, NONE, NONE, NAND, NONE, NONE, NONE), - MFPR_MMP2(GPIO148, 0x234, NAND, GPIO, NONE, NONE, NAND, NONE, NONE, NONE), - MFPR_MMP2(GPIO149, 0x238, NAND, GPIO, NONE, NONE, NONE, NONE, NONE, NONE), - MFPR_MMP2(GPIO150, 0x23C, NAND, GPIO, NONE, NONE, NAND, NONE, NONE, NONE), - MFPR_MMP2(GPIO151, 0x240, SMC, GPIO, MMC3, NONE, NONE, NONE, NONE, NONE), - MFPR_MMP2(GPIO152, 0x248, SMC, GPIO, NONE, NONE, SMC, NONE, NONE, NONE), - MFPR_MMP2(GPIO153, 0x24C, SMC, GPIO, NONE, NONE, SMC, NONE, NONE, NONE), - MFPR_MMP2(GPIO154, 0x254, SMC_INT, GPIO, SMC, NONE, NAND, NONE, NONE, NONE), - MFPR_MMP2(GPIO155, 0x258, EXT_DMA, GPIO, SMC, NONE, EXT_DMA, NONE, NONE, NONE), - MFPR_MMP2(GPIO156, 0x14C, PRI_JTAG, GPIO, PWM, NONE, NONE, NONE, NONE, NONE), - MFPR_MMP2(GPIO157, 0x150, PRI_JTAG, GPIO, PWM, NONE, NONE, NONE, NONE, NONE), - MFPR_MMP2(GPIO158, 0x154, PRI_JTAG, GPIO, PWM, NONE, NONE, NONE, NONE, NONE), - MFPR_MMP2(GPIO159, 0x158, PRI_JTAG, GPIO, PWM, NONE, NONE, NONE, NONE, NONE), - MFPR_MMP2(GPIO160, 0x250, NAND, GPIO, SMC, NONE, NAND, NONE, NONE, NONE), - MFPR_MMP2(GPIO161, 0x210, NAND, GPIO, NONE, NONE, NAND, NONE, NONE, NONE), - MFPR_MMP2(GPIO162, 0x20C, NAND, GPIO, MMC3, NONE, NONE, NONE, NONE, NONE), - MFPR_MMP2(GPIO163, 0x208, NAND, GPIO, MMC3, NONE, NONE, NONE, NONE, NONE), - MFPR_MMP2(GPIO164, 0x204, NAND, GPIO, MMC3, NONE, NONE, NONE, NONE, NONE), - MFPR_MMP2(GPIO165, 0x1EC, NAND, GPIO, MMC3, NONE, NONE, NONE, NONE, NONE), - MFPR_MMP2(GPIO166, 0x1E8, NAND, GPIO, MMC3, NONE, NONE, NONE, NONE, NONE), - MFPR_MMP2(GPIO167, 0x1E4, NAND, GPIO, MMC3, NONE, NONE, NONE, NONE, NONE), - MFPR_MMP2(GPIO168, 0x1E0, NAND, GPIO, MMC3, NONE, NONE, NONE, NONE, NONE), - MFPR_MMP2(TWSI4_SCL, 0x2BC, TWSI4, LCD, NONE, NONE, NONE, NONE, NONE, NONE), - MFPR_MMP2(TWSI4_SDA, 0x2C0, TWSI4, LCD, NONE, NONE, NONE, NONE, NONE, NONE), - MFPR_MMP2(G_CLKREQ, 0x160, G_CLKREQ, ONE_WIRE, NONE, NONE, NONE, NONE, NONE, NONE), - MFPR_MMP2(VCXO_REQ, 0x168, VCXO_REQ, ONE_WIRE, PLL, NONE, NONE, NONE, NONE, NONE), - MFPR_MMP2(VCXO_OUT, 0x16C, VCXO_OUT, 32K_CLKOUT, NONE, NONE, NONE, NONE, NONE, NONE), -}; - -static const unsigned mmp2_uart1_pin1[] = {GPIO29, GPIO30, GPIO31, GPIO32}; -static const unsigned mmp2_uart1_pin2[] = {GPIO45, GPIO46}; -static const unsigned mmp2_uart1_pin3[] = {GPIO140, GPIO141}; -static const unsigned mmp2_uart2_pin1[] = {GPIO37, GPIO38, GPIO39, GPIO40}; -static const unsigned mmp2_uart2_pin2[] = {GPIO43, GPIO44, GPIO45, GPIO46}; -static const unsigned mmp2_uart2_pin3[] = {GPIO47, GPIO48, GPIO49, GPIO50}; -static const unsigned mmp2_uart2_pin4[] = {GPIO74, GPIO75, GPIO76, GPIO77}; -static const unsigned mmp2_uart2_pin5[] = {GPIO55, GPIO56}; -static const unsigned mmp2_uart2_pin6[] = {GPIO140, GPIO141}; -static const unsigned mmp2_uart3_pin1[] = {GPIO37, GPIO38, GPIO39, GPIO40}; -static const unsigned mmp2_uart3_pin2[] = {GPIO43, GPIO44, GPIO45, GPIO46}; -static const unsigned mmp2_uart3_pin3[] = {GPIO51, GPIO52, GPIO53, GPIO54}; -static const unsigned mmp2_uart3_pin4[] = {GPIO59, GPIO60, GPIO61, GPIO62}; -static const unsigned mmp2_uart3_pin5[] = {GPIO115, GPIO116, GPIO117, GPIO118}; -static const unsigned mmp2_uart3_pin6[] = {GPIO51, GPIO52}; -static const unsigned mmp2_uart4_pin1[] = {GPIO43, GPIO44, GPIO45, GPIO46}; -static const unsigned mmp2_uart4_pin2[] = {GPIO63, GPIO64, GPIO65, GPIO66}; -static const unsigned mmp2_uart4_pin3[] = {GPIO74, GPIO75, GPIO76, GPIO77}; -static const unsigned mmp2_uart4_pin4[] = {GPIO115, GPIO116, GPIO117, GPIO118}; -static const unsigned mmp2_uart4_pin5[] = {GPIO59, GPIO60}; -static const unsigned mmp2_kpdk_pin1[] = {GPIO16, GPIO17, GPIO18, GPIO19}; -static const unsigned mmp2_kpdk_pin2[] = {GPIO16, GPIO17}; -static const unsigned mmp2_twsi2_pin1[] = {GPIO37, GPIO38}; -static const unsigned mmp2_twsi2_pin2[] = {GPIO39, GPIO40}; -static const unsigned mmp2_twsi2_pin3[] = {GPIO43, GPIO44}; -static const unsigned mmp2_twsi2_pin4[] = {GPIO53, GPIO54}; -static const unsigned mmp2_twsi2_pin5[] = {GPIO55, GPIO56}; -static const unsigned mmp2_twsi3_pin1[] = {GPIO71, GPIO72}; -static const unsigned mmp2_twsi3_pin2[] = {GPIO95, GPIO96}; -static const unsigned mmp2_twsi4_pin1[] = {TWSI4_SCL, TWSI4_SDA}; -static const unsigned mmp2_twsi5_pin1[] = {GPIO41, GPIO42}; -static const unsigned mmp2_twsi5_pin2[] = {GPIO84, GPIO85}; -static const unsigned mmp2_twsi5_pin3[] = {GPIO99, GPIO100}; -static const unsigned mmp2_twsi6_pin1[] = {GPIO47, GPIO48}; -static const unsigned mmp2_twsi6_pin2[] = {GPIO86, GPIO87}; -static const unsigned mmp2_twsi6_pin3[] = {GPIO97, GPIO98}; -static const unsigned mmp2_ccic1_pin1[] = {GPIO12, GPIO13, GPIO14, GPIO15, - GPIO16, GPIO17, GPIO18, GPIO19, GPIO20, GPIO21, GPIO22, GPIO23}; -static const unsigned mmp2_ccic1_pin2[] = {GPIO59, GPIO60, GPIO61, GPIO62, - GPIO63, GPIO64, GPIO65, GPIO66, GPIO67, GPIO68, GPIO69, GPIO70}; -static const unsigned mmp2_ccic2_pin1[] = {GPIO59, GPIO60, GPIO61, GPIO62, - GPIO63, GPIO64, GPIO65, GPIO66, GPIO67, GPIO68, GPIO69, GPIO70}; -static const unsigned mmp2_ccic2_pin2[] = {GPIO82, GPIO83, GPIO86, GPIO87, - GPIO88, GPIO89, GPIO90, GPIO91, GPIO92, GPIO93, GPIO94, GPIO95}; -static const unsigned mmp2_ulpi_pin1[] = {GPIO59, GPIO60, GPIO61, GPIO62, - GPIO63, GPIO64, GPIO65, GPIO66, GPIO67, GPIO68, GPIO69, GPIO70}; -static const unsigned mmp2_ro_pin1[] = {GPIO16, GPIO17}; -static const unsigned mmp2_ro_pin2[] = {GPIO18, GPIO19}; -static const unsigned mmp2_ro_pin3[] = {GPIO51, GPIO52}; -static const unsigned mmp2_ro_pin4[] = {GPIO55, GPIO56}; -static const unsigned mmp2_i2s_pin1[] = {GPIO24, GPIO25, GPIO26, GPIO27, - GPIO28}; -static const unsigned mmp2_i2s_pin2[] = {GPIO33, GPIO34, GPIO35, GPIO36}; -static const unsigned mmp2_ssp1_pin1[] = {GPIO37, GPIO38, GPIO39, GPIO40}; -static const unsigned mmp2_ssp1_pin2[] = {GPIO43, GPIO44, GPIO45, GPIO46}; -static const unsigned mmp2_ssp1_pin3[] = {GPIO115, GPIO116, GPIO117, GPIO118}; -static const unsigned mmp2_ssp2_pin1[] = {GPIO47, GPIO48, GPIO49, GPIO50}; -static const unsigned mmp2_ssp3_pin1[] = {GPIO119, GPIO120, GPIO121, GPIO122}; -static const unsigned mmp2_ssp3_pin2[] = {GPIO132, GPIO133, GPIO133, GPIO136}; -static const unsigned mmp2_sspa2_pin1[] = {GPIO25, GPIO26, GPIO27, GPIO28}; -static const unsigned mmp2_sspa2_pin2[] = {GPIO33, GPIO34, GPIO35, GPIO36}; -static const unsigned mmp2_mmc1_pin1[] = {GPIO131, GPIO132, GPIO133, GPIO134, - GPIO136, GPIO139, GPIO140, GPIO141}; -static const unsigned mmp2_mmc2_pin1[] = {GPIO37, GPIO38, GPIO39, GPIO40, - GPIO41, GPIO42}; -static const unsigned mmp2_mmc3_pin1[] = {GPIO111, GPIO112, GPIO151, GPIO162, - GPIO163, GPIO164, GPIO165, GPIO166, GPIO167, GPIO168}; - -static struct pxa3xx_pin_group mmp2_grps[] = { - GRP_MMP2("uart1 4p1", UART1, mmp2_uart1_pin1), - GRP_MMP2("uart1 2p2", UART1, mmp2_uart1_pin2), - GRP_MMP2("uart1 2p3", UART1, mmp2_uart1_pin3), - GRP_MMP2("uart2 4p1", UART2, mmp2_uart2_pin1), - GRP_MMP2("uart2 4p2", UART2, mmp2_uart2_pin2), - GRP_MMP2("uart2 4p3", UART2, mmp2_uart2_pin3), - GRP_MMP2("uart2 4p4", UART2, mmp2_uart2_pin4), - GRP_MMP2("uart2 2p5", UART2, mmp2_uart2_pin5), - GRP_MMP2("uart2 2p6", UART2, mmp2_uart2_pin6), - GRP_MMP2("uart3 4p1", UART3, mmp2_uart3_pin1), - GRP_MMP2("uart3 4p2", UART3, mmp2_uart3_pin2), - GRP_MMP2("uart3 4p3", UART3, mmp2_uart3_pin3), - GRP_MMP2("uart3 4p4", UART3, mmp2_uart3_pin4), - GRP_MMP2("uart3 4p5", UART3, mmp2_uart3_pin5), - GRP_MMP2("uart3 2p6", UART3, mmp2_uart3_pin6), - GRP_MMP2("uart4 4p1", UART4, mmp2_uart4_pin1), - GRP_MMP2("uart4 4p2", UART4, mmp2_uart4_pin2), - GRP_MMP2("uart4 4p3", UART4, mmp2_uart4_pin3), - GRP_MMP2("uart4 4p4", UART4, mmp2_uart4_pin4), - GRP_MMP2("uart4 2p5", UART4, mmp2_uart4_pin5), - GRP_MMP2("kpdk 4p1", KP_DK, mmp2_kpdk_pin1), - GRP_MMP2("kpdk 4p2", KP_DK, mmp2_kpdk_pin2), - GRP_MMP2("twsi2-1", TWSI2, mmp2_twsi2_pin1), - GRP_MMP2("twsi2-2", TWSI2, mmp2_twsi2_pin2), - GRP_MMP2("twsi2-3", TWSI2, mmp2_twsi2_pin3), - GRP_MMP2("twsi2-4", TWSI2, mmp2_twsi2_pin4), - GRP_MMP2("twsi2-5", TWSI2, mmp2_twsi2_pin5), - GRP_MMP2("twsi3-1", TWSI3, mmp2_twsi3_pin1), - GRP_MMP2("twsi3-2", TWSI3, mmp2_twsi3_pin2), - GRP_MMP2("twsi4", TWSI4, mmp2_twsi4_pin1), - GRP_MMP2("twsi5-1", TWSI5, mmp2_twsi5_pin1), - GRP_MMP2("twsi5-2", TWSI5, mmp2_twsi5_pin2), - GRP_MMP2("twsi5-3", TWSI5, mmp2_twsi5_pin3), - GRP_MMP2("twsi6-1", TWSI6, mmp2_twsi6_pin1), - GRP_MMP2("twsi6-2", TWSI6, mmp2_twsi6_pin2), - GRP_MMP2("twsi6-3", TWSI6, mmp2_twsi6_pin3), - GRP_MMP2("ccic1-1", CCIC1, mmp2_ccic1_pin1), - GRP_MMP2("ccic1-2", CCIC1, mmp2_ccic1_pin2), - GRP_MMP2("ccic2-1", CCIC2, mmp2_ccic2_pin1), - GRP_MMP2("ccic2-1", CCIC2, mmp2_ccic2_pin2), - GRP_MMP2("ulpi", ULPI, mmp2_ulpi_pin1), - GRP_MMP2("ro-1", ROT, mmp2_ro_pin1), - GRP_MMP2("ro-2", ROT, mmp2_ro_pin2), - GRP_MMP2("ro-3", ROT, mmp2_ro_pin3), - GRP_MMP2("ro-4", ROT, mmp2_ro_pin4), - GRP_MMP2("i2s 5p1", I2S, mmp2_i2s_pin1), - GRP_MMP2("i2s 4p2", I2S, mmp2_i2s_pin2), - GRP_MMP2("ssp1 4p1", SSP1, mmp2_ssp1_pin1), - GRP_MMP2("ssp1 4p2", SSP1, mmp2_ssp1_pin2), - GRP_MMP2("ssp1 4p3", SSP1, mmp2_ssp1_pin3), - GRP_MMP2("ssp2 4p1", SSP2, mmp2_ssp2_pin1), - GRP_MMP2("ssp3 4p1", SSP3, mmp2_ssp3_pin1), - GRP_MMP2("ssp3 4p2", SSP3, mmp2_ssp3_pin2), - GRP_MMP2("sspa2 4p1", SSPA2, mmp2_sspa2_pin1), - GRP_MMP2("sspa2 4p2", SSPA2, mmp2_sspa2_pin2), - GRP_MMP2("mmc1 8p1", MMC1, mmp2_mmc1_pin1), - GRP_MMP2("mmc2 6p1", MMC2, mmp2_mmc2_pin1), - GRP_MMP2("mmc3 10p1", MMC3, mmp2_mmc3_pin1), -}; - -static const char * const mmp2_uart1_grps[] = {"uart1 4p1", "uart1 2p2", - "uart1 2p3"}; -static const char * const mmp2_uart2_grps[] = {"uart2 4p1", "uart2 4p2", - "uart2 4p3", "uart2 4p4", "uart2 4p5", "uart2 4p6"}; -static const char * const mmp2_uart3_grps[] = {"uart3 4p1", "uart3 4p2", - "uart3 4p3", "uart3 4p4", "uart3 4p5", "uart3 2p6"}; -static const char * const mmp2_uart4_grps[] = {"uart4 4p1", "uart4 4p2", - "uart4 4p3", "uart4 4p4", "uart4 2p5"}; -static const char * const mmp2_kpdk_grps[] = {"kpdk 4p1", "kpdk 4p2"}; -static const char * const mmp2_twsi2_grps[] = {"twsi2-1", "twsi2-2", - "twsi2-3", "twsi2-4", "twsi2-5"}; -static const char * const mmp2_twsi3_grps[] = {"twsi3-1", "twsi3-2"}; -static const char * const mmp2_twsi4_grps[] = {"twsi4"}; -static const char * const mmp2_twsi5_grps[] = {"twsi5-1", "twsi5-2", - "twsi5-3"}; -static const char * const mmp2_twsi6_grps[] = {"twsi6-1", "twsi6-2", - "twsi6-3"}; -static const char * const mmp2_ccic1_grps[] = {"ccic1-1", "ccic1-2"}; -static const char * const mmp2_ccic2_grps[] = {"ccic2-1", "ccic2-2"}; -static const char * const mmp2_ulpi_grps[] = {"ulpi"}; -static const char * const mmp2_ro_grps[] = {"ro-1", "ro-2", "ro-3", "ro-4"}; -static const char * const mmp2_i2s_grps[] = {"i2s 5p1", "i2s 4p2"}; -static const char * const mmp2_ssp1_grps[] = {"ssp1 4p1", "ssp1 4p2", - "ssp1 4p3"}; -static const char * const mmp2_ssp2_grps[] = {"ssp2 4p1"}; -static const char * const mmp2_ssp3_grps[] = {"ssp3 4p1", "ssp3 4p2"}; -static const char * const mmp2_sspa2_grps[] = {"sspa2 4p1", "sspa2 4p2"}; -static const char * const mmp2_mmc1_grps[] = {"mmc1 8p1"}; -static const char * const mmp2_mmc2_grps[] = {"mmc2 6p1"}; -static const char * const mmp2_mmc3_grps[] = {"mmc3 10p1"}; - -static struct pxa3xx_pmx_func mmp2_funcs[] = { - {"uart1", ARRAY_AND_SIZE(mmp2_uart1_grps)}, - {"uart2", ARRAY_AND_SIZE(mmp2_uart2_grps)}, - {"uart3", ARRAY_AND_SIZE(mmp2_uart3_grps)}, - {"uart4", ARRAY_AND_SIZE(mmp2_uart4_grps)}, - {"kpdk", ARRAY_AND_SIZE(mmp2_kpdk_grps)}, - {"twsi2", ARRAY_AND_SIZE(mmp2_twsi2_grps)}, - {"twsi3", ARRAY_AND_SIZE(mmp2_twsi3_grps)}, - {"twsi4", ARRAY_AND_SIZE(mmp2_twsi4_grps)}, - {"twsi5", ARRAY_AND_SIZE(mmp2_twsi5_grps)}, - {"twsi6", ARRAY_AND_SIZE(mmp2_twsi6_grps)}, - {"ccic1", ARRAY_AND_SIZE(mmp2_ccic1_grps)}, - {"ccic2", ARRAY_AND_SIZE(mmp2_ccic2_grps)}, - {"ulpi", ARRAY_AND_SIZE(mmp2_ulpi_grps)}, - {"ro", ARRAY_AND_SIZE(mmp2_ro_grps)}, - {"i2s", ARRAY_AND_SIZE(mmp2_i2s_grps)}, - {"ssp1", ARRAY_AND_SIZE(mmp2_ssp1_grps)}, - {"ssp2", ARRAY_AND_SIZE(mmp2_ssp2_grps)}, - {"ssp3", ARRAY_AND_SIZE(mmp2_ssp3_grps)}, - {"sspa2", ARRAY_AND_SIZE(mmp2_sspa2_grps)}, - {"mmc1", ARRAY_AND_SIZE(mmp2_mmc1_grps)}, - {"mmc2", ARRAY_AND_SIZE(mmp2_mmc2_grps)}, - {"mmc3", ARRAY_AND_SIZE(mmp2_mmc3_grps)}, -}; - -static struct pinctrl_desc mmp2_pctrl_desc = { - .name = "mmp2-pinctrl", - .owner = THIS_MODULE, -}; - -static struct pxa3xx_pinmux_info mmp2_info = { - .mfp = mmp2_mfp, - .num_mfp = ARRAY_SIZE(mmp2_mfp), - .grps = mmp2_grps, - .num_grps = ARRAY_SIZE(mmp2_grps), - .funcs = mmp2_funcs, - .num_funcs = ARRAY_SIZE(mmp2_funcs), - .num_gpio = 169, - .desc = &mmp2_pctrl_desc, - .pads = mmp2_pads, - .num_pads = ARRAY_SIZE(mmp2_pads), - - .cputype = PINCTRL_MMP2, - .ds_mask = MMP2_DS_MASK, - .ds_shift = MMP2_DS_SHIFT, -}; - -static int mmp2_pinmux_probe(struct platform_device *pdev) -{ - return pxa3xx_pinctrl_register(pdev, &mmp2_info); -} - -static int mmp2_pinmux_remove(struct platform_device *pdev) -{ - return pxa3xx_pinctrl_unregister(pdev); -} - -static struct platform_driver mmp2_pinmux_driver = { - .driver = { - .name = "mmp2-pinmux", - .owner = THIS_MODULE, - }, - .probe = mmp2_pinmux_probe, - .remove = mmp2_pinmux_remove, -}; - -static int __init mmp2_pinmux_init(void) -{ - return platform_driver_register(&mmp2_pinmux_driver); -} -core_initcall_sync(mmp2_pinmux_init); - -static void __exit mmp2_pinmux_exit(void) -{ - platform_driver_unregister(&mmp2_pinmux_driver); -} -module_exit(mmp2_pinmux_exit); - -MODULE_AUTHOR("Haojian Zhuang <haojian.zhuang@marvell.com>"); -MODULE_DESCRIPTION("PXA3xx pin control driver"); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/pinctrl/pinctrl-mxs.c b/drivers/pinctrl/pinctrl-mxs.c index 23af9f1f9c35..b45c4eb35798 100644 --- a/drivers/pinctrl/pinctrl-mxs.c +++ b/drivers/pinctrl/pinctrl-mxs.c @@ -158,7 +158,7 @@ static void mxs_dt_free_map(struct pinctrl_dev *pctldev, kfree(map); } -static struct pinctrl_ops mxs_pinctrl_ops = { +static const struct pinctrl_ops mxs_pinctrl_ops = { .get_groups_count = mxs_get_groups_count, .get_group_name = mxs_get_group_name, .get_group_pins = mxs_get_group_pins, @@ -219,7 +219,7 @@ static int mxs_pinctrl_enable(struct pinctrl_dev *pctldev, unsigned selector, return 0; } -static struct pinmux_ops mxs_pinmux_ops = { +static const struct pinmux_ops mxs_pinmux_ops = { .get_functions_count = mxs_pinctrl_get_funcs_count, .get_function_name = mxs_pinctrl_get_func_name, .get_function_groups = mxs_pinctrl_get_func_groups, @@ -319,7 +319,7 @@ static void mxs_pinconf_group_dbg_show(struct pinctrl_dev *pctldev, seq_printf(s, "0x%lx", config); } -static struct pinconf_ops mxs_pinconf_ops = { +static const struct pinconf_ops mxs_pinconf_ops = { .pin_config_get = mxs_pinconf_get, .pin_config_set = mxs_pinconf_set, .pin_config_group_get = mxs_pinconf_group_get, diff --git a/drivers/pinctrl/pinctrl-nomadik-db8500.c b/drivers/pinctrl/pinctrl-nomadik-db8500.c index 30b4da91ef7e..c74840729648 100644 --- a/drivers/pinctrl/pinctrl-nomadik-db8500.c +++ b/drivers/pinctrl/pinctrl-nomadik-db8500.c @@ -466,7 +466,7 @@ static const unsigned mc1_a_1_pins[] = { DB8500_PIN_AH16, DB8500_PIN_AG15, DB8500_PIN_AJ15, DB8500_PIN_AG14, DB8500_PIN_AF13, DB8500_PIN_AG13, DB8500_PIN_AH15 }; static const unsigned mc1_a_2_pins[] = { DB8500_PIN_AH16, DB8500_PIN_AJ15, - DB8500_PIN_AG14, DB8500_PIN_AF13, DB8500_PIN_AG13,DB8500_PIN_AH15 }; + DB8500_PIN_AG14, DB8500_PIN_AF13, DB8500_PIN_AG13, DB8500_PIN_AH15 }; static const unsigned mc1dir_a_1_pins[] = { DB8500_PIN_AH13, DB8500_PIN_AG12, DB8500_PIN_AH12, DB8500_PIN_AH11 }; static const unsigned hsir_a_1_pins[] = { DB8500_PIN_AG10, DB8500_PIN_AH10, @@ -663,7 +663,7 @@ static const unsigned hwobs_oc4_1_pins[] = { DB8500_PIN_D17, DB8500_PIN_D16, DB8500_PIN_D21, DB8500_PIN_D20, DB8500_PIN_C20, DB8500_PIN_B21, DB8500_PIN_C21, DB8500_PIN_A22, DB8500_PIN_B24, DB8500_PIN_C22 }; -#define DB8500_PIN_GROUP(a,b) { .name = #a, .pins = a##_pins, \ +#define DB8500_PIN_GROUP(a, b) { .name = #a, .pins = a##_pins, \ .npins = ARRAY_SIZE(a##_pins), .altsetting = b } static const struct nmk_pingroup nmk_db8500_groups[] = { diff --git a/drivers/pinctrl/pinctrl-nomadik-stn8815.c b/drivers/pinctrl/pinctrl-nomadik-stn8815.c index 924a3393fa82..ed39dcafd4f8 100644 --- a/drivers/pinctrl/pinctrl-nomadik-stn8815.c +++ b/drivers/pinctrl/pinctrl-nomadik-stn8815.c @@ -299,7 +299,7 @@ static const unsigned i2c0_a_1_pins[] = { STN8815_PIN_D3, STN8815_PIN_D2 }; static const unsigned u1_b_1_pins[] = { STN8815_PIN_B16, STN8815_PIN_A16 }; static const unsigned i2cusb_b_1_pins[] = { STN8815_PIN_C21, STN8815_PIN_C20 }; -#define STN8815_PIN_GROUP(a,b) { .name = #a, .pins = a##_pins, \ +#define STN8815_PIN_GROUP(a, b) { .name = #a, .pins = a##_pins, \ .npins = ARRAY_SIZE(a##_pins), .altsetting = b } static const struct nmk_pingroup nmk_stn8815_groups[] = { diff --git a/drivers/pinctrl/pinctrl-nomadik.c b/drivers/pinctrl/pinctrl-nomadik.c index 36d20293de5c..435bf3078d2c 100644 --- a/drivers/pinctrl/pinctrl-nomadik.c +++ b/drivers/pinctrl/pinctrl-nomadik.c @@ -1565,8 +1565,8 @@ static int nmk_dt_add_map_configs(struct pinctrl_map **map, return 0; } -#define NMK_CONFIG_PIN(x,y) { .property = x, .config = y, } -#define NMK_CONFIG_PIN_ARRAY(x,y) { .property = x, .choice = y, \ +#define NMK_CONFIG_PIN(x, y) { .property = x, .config = y, } +#define NMK_CONFIG_PIN_ARRAY(x, y) { .property = x, .choice = y, \ .size = ARRAY_SIZE(y), } static const unsigned long nmk_pin_input_modes[] = { @@ -1764,7 +1764,7 @@ int nmk_pinctrl_dt_node_to_map(struct pinctrl_dev *pctldev, return 0; } -static struct pinctrl_ops nmk_pinctrl_ops = { +static const struct pinctrl_ops nmk_pinctrl_ops = { .get_groups_count = nmk_get_groups_cnt, .get_group_name = nmk_get_group_name, .get_group_pins = nmk_get_group_pins, @@ -1975,7 +1975,7 @@ static void nmk_gpio_disable_free(struct pinctrl_dev *pctldev, /* Set the pin to some default state, GPIO is usually default */ } -static struct pinmux_ops nmk_pinmux_ops = { +static const struct pinmux_ops nmk_pinmux_ops = { .get_functions_count = nmk_pmx_get_funcs_cnt, .get_function_name = nmk_pmx_get_func_name, .get_function_groups = nmk_pmx_get_func_groups, @@ -2068,7 +2068,7 @@ static int nmk_pin_config_set(struct pinctrl_dev *pctldev, unsigned pin, pin, cfg, pullnames[pull], slpmnames[slpm], output ? "output " : "input", output ? (val ? "high" : "low") : "", - lowemi ? "on" : "off" ); + lowemi ? "on" : "off"); clk_enable(nmk_chip->clk); bit = pin % NMK_GPIO_PER_CHIP; @@ -2089,7 +2089,7 @@ static int nmk_pin_config_set(struct pinctrl_dev *pctldev, unsigned pin, return 0; } -static struct pinconf_ops nmk_pinconf_ops = { +static const struct pinconf_ops nmk_pinconf_ops = { .pin_config_get = nmk_pin_config_get, .pin_config_set = nmk_pin_config_set, }; @@ -2111,6 +2111,10 @@ static const struct of_device_id nmk_pinctrl_match[] = { .compatible = "stericsson,nmk-pinctrl", .data = (void *)PINCTRL_NMK_DB8500, }, + { + .compatible = "stericsson,nmk-pinctrl-db8540", + .data = (void *)PINCTRL_NMK_DB8540, + }, {}, }; diff --git a/drivers/pinctrl/pinctrl-pxa168.c b/drivers/pinctrl/pinctrl-pxa168.c deleted file mode 100644 index d9cd2b457484..000000000000 --- a/drivers/pinctrl/pinctrl-pxa168.c +++ /dev/null @@ -1,651 +0,0 @@ -/* - * linux/drivers/pinctrl/pinmux-pxa168.c - * - * 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 - * publishhed by the Free Software Foundation. - * - * Copyright (C) 2011, Marvell Technology Group Ltd. - * - * Author: Haojian Zhuang <haojian.zhuang@marvell.com> - * - */ - -#include <linux/device.h> -#include <linux/module.h> -#include <linux/io.h> -#include <linux/platform_device.h> -#include "pinctrl-pxa3xx.h" - -#define PXA168_DS_MASK 0x1800 -#define PXA168_DS_SHIFT 11 -#define PXA168_SLEEP_MASK 0x38 -#define PXA168_SLEEP_SELECT (1 << 9) -#define PXA168_SLEEP_DATA (1 << 8) -#define PXA168_SLEEP_DIR (1 << 7) - -#define MFPR_168(a, r, f0, f1, f2, f3, f4, f5, f6, f7) \ - { \ - .name = #a, \ - .pin = a, \ - .mfpr = r, \ - .func = { \ - PXA168_MUX_##f0, \ - PXA168_MUX_##f1, \ - PXA168_MUX_##f2, \ - PXA168_MUX_##f3, \ - PXA168_MUX_##f4, \ - PXA168_MUX_##f5, \ - PXA168_MUX_##f6, \ - PXA168_MUX_##f7, \ - }, \ - } - -#define GRP_168(a, m, p) \ - { .name = a, .mux = PXA168_MUX_##m, .pins = p, .npins = ARRAY_SIZE(p), } - -/* 131 pins */ -enum pxa168_pin_list { - /* 0~122: GPIO0~GPIO122 */ - PWR_SCL = 123, - PWR_SDA, - TDI, - TMS, - TCK, - TDO, - TRST, - WAKEUP = 130, -}; - -enum pxa168_mux { - /* PXA3xx_MUX_GPIO = 0 (predefined in pinctrl-pxa3xx.h) */ - PXA168_MUX_GPIO = 0, - PXA168_MUX_DFIO, - PXA168_MUX_NAND, - PXA168_MUX_SMC, - PXA168_MUX_SMC_CS0, - PXA168_MUX_SMC_CS1, - PXA168_MUX_SMC_INT, - PXA168_MUX_SMC_RDY, - PXA168_MUX_MMC1, - PXA168_MUX_MMC2, - PXA168_MUX_MMC2_CMD, - PXA168_MUX_MMC2_CLK, - PXA168_MUX_MMC3, - PXA168_MUX_MMC3_CMD, - PXA168_MUX_MMC3_CLK, - PXA168_MUX_MMC4, - PXA168_MUX_MSP, - PXA168_MUX_MSP_DAT3, - PXA168_MUX_MSP_INS, - PXA168_MUX_I2C, - PXA168_MUX_PWRI2C, - PXA168_MUX_AC97, - PXA168_MUX_AC97_SYSCLK, - PXA168_MUX_PWM, - PXA168_MUX_PWM1, - PXA168_MUX_XD, - PXA168_MUX_XP, - PXA168_MUX_LCD, - PXA168_MUX_CCIC, - PXA168_MUX_CF, - PXA168_MUX_CF_RDY, - PXA168_MUX_CF_nINPACK, - PXA168_MUX_CF_nWAIT, - PXA168_MUX_KP_MKOUT, - PXA168_MUX_KP_MKIN, - PXA168_MUX_KP_DK, - PXA168_MUX_ETH, - PXA168_MUX_ETH_TX, - PXA168_MUX_ETH_RX, - PXA168_MUX_ONE_WIRE, - PXA168_MUX_UART1, - PXA168_MUX_UART1_TX, - PXA168_MUX_UART1_CTS, - PXA168_MUX_UART1_nRI, - PXA168_MUX_UART1_DTR, - PXA168_MUX_UART2, - PXA168_MUX_UART2_TX, - PXA168_MUX_UART3, - PXA168_MUX_UART3_TX, - PXA168_MUX_UART3_CTS, - PXA168_MUX_SSP1, - PXA168_MUX_SSP1_TX, - PXA168_MUX_SSP2, - PXA168_MUX_SSP2_TX, - PXA168_MUX_SSP3, - PXA168_MUX_SSP3_TX, - PXA168_MUX_SSP4, - PXA168_MUX_SSP4_TX, - PXA168_MUX_SSP5, - PXA168_MUX_SSP5_TX, - PXA168_MUX_USB, - PXA168_MUX_JTAG, - PXA168_MUX_RESET, - PXA168_MUX_WAKEUP, - PXA168_MUX_EXT_32K_IN, - PXA168_MUX_NONE = 0xffff, -}; - -static struct pinctrl_pin_desc pxa168_pads[] = { - PINCTRL_PIN(GPIO0, "GPIO0"), - PINCTRL_PIN(GPIO1, "GPIO1"), - PINCTRL_PIN(GPIO2, "GPIO2"), - PINCTRL_PIN(GPIO3, "GPIO3"), - PINCTRL_PIN(GPIO4, "GPIO4"), - PINCTRL_PIN(GPIO5, "GPIO5"), - PINCTRL_PIN(GPIO6, "GPIO6"), - PINCTRL_PIN(GPIO7, "GPIO7"), - PINCTRL_PIN(GPIO8, "GPIO8"), - PINCTRL_PIN(GPIO9, "GPIO9"), - PINCTRL_PIN(GPIO10, "GPIO10"), - PINCTRL_PIN(GPIO11, "GPIO11"), - PINCTRL_PIN(GPIO12, "GPIO12"), - PINCTRL_PIN(GPIO13, "GPIO13"), - PINCTRL_PIN(GPIO14, "GPIO14"), - PINCTRL_PIN(GPIO15, "GPIO15"), - PINCTRL_PIN(GPIO16, "GPIO16"), - PINCTRL_PIN(GPIO17, "GPIO17"), - PINCTRL_PIN(GPIO18, "GPIO18"), - PINCTRL_PIN(GPIO19, "GPIO19"), - PINCTRL_PIN(GPIO20, "GPIO20"), - PINCTRL_PIN(GPIO21, "GPIO21"), - PINCTRL_PIN(GPIO22, "GPIO22"), - PINCTRL_PIN(GPIO23, "GPIO23"), - PINCTRL_PIN(GPIO24, "GPIO24"), - PINCTRL_PIN(GPIO25, "GPIO25"), - PINCTRL_PIN(GPIO26, "GPIO26"), - PINCTRL_PIN(GPIO27, "GPIO27"), - PINCTRL_PIN(GPIO28, "GPIO28"), - PINCTRL_PIN(GPIO29, "GPIO29"), - PINCTRL_PIN(GPIO30, "GPIO30"), - PINCTRL_PIN(GPIO31, "GPIO31"), - PINCTRL_PIN(GPIO32, "GPIO32"), - PINCTRL_PIN(GPIO33, "GPIO33"), - PINCTRL_PIN(GPIO34, "GPIO34"), - PINCTRL_PIN(GPIO35, "GPIO35"), - PINCTRL_PIN(GPIO36, "GPIO36"), - PINCTRL_PIN(GPIO37, "GPIO37"), - PINCTRL_PIN(GPIO38, "GPIO38"), - PINCTRL_PIN(GPIO39, "GPIO39"), - PINCTRL_PIN(GPIO40, "GPIO40"), - PINCTRL_PIN(GPIO41, "GPIO41"), - PINCTRL_PIN(GPIO42, "GPIO42"), - PINCTRL_PIN(GPIO43, "GPIO43"), - PINCTRL_PIN(GPIO44, "GPIO44"), - PINCTRL_PIN(GPIO45, "GPIO45"), - PINCTRL_PIN(GPIO46, "GPIO46"), - PINCTRL_PIN(GPIO47, "GPIO47"), - PINCTRL_PIN(GPIO48, "GPIO48"), - PINCTRL_PIN(GPIO49, "GPIO49"), - PINCTRL_PIN(GPIO50, "GPIO50"), - PINCTRL_PIN(GPIO51, "GPIO51"), - PINCTRL_PIN(GPIO52, "GPIO52"), - PINCTRL_PIN(GPIO53, "GPIO53"), - PINCTRL_PIN(GPIO54, "GPIO54"), - PINCTRL_PIN(GPIO55, "GPIO55"), - PINCTRL_PIN(GPIO56, "GPIO56"), - PINCTRL_PIN(GPIO57, "GPIO57"), - PINCTRL_PIN(GPIO58, "GPIO58"), - PINCTRL_PIN(GPIO59, "GPIO59"), - PINCTRL_PIN(GPIO60, "GPIO60"), - PINCTRL_PIN(GPIO61, "GPIO61"), - PINCTRL_PIN(GPIO62, "GPIO62"), - PINCTRL_PIN(GPIO63, "GPIO63"), - PINCTRL_PIN(GPIO64, "GPIO64"), - PINCTRL_PIN(GPIO65, "GPIO65"), - PINCTRL_PIN(GPIO66, "GPIO66"), - PINCTRL_PIN(GPIO67, "GPIO67"), - PINCTRL_PIN(GPIO68, "GPIO68"), - PINCTRL_PIN(GPIO69, "GPIO69"), - PINCTRL_PIN(GPIO70, "GPIO70"), - PINCTRL_PIN(GPIO71, "GPIO71"), - PINCTRL_PIN(GPIO72, "GPIO72"), - PINCTRL_PIN(GPIO73, "GPIO73"), - PINCTRL_PIN(GPIO74, "GPIO74"), - PINCTRL_PIN(GPIO75, "GPIO75"), - PINCTRL_PIN(GPIO76, "GPIO76"), - PINCTRL_PIN(GPIO77, "GPIO77"), - PINCTRL_PIN(GPIO78, "GPIO78"), - PINCTRL_PIN(GPIO79, "GPIO79"), - PINCTRL_PIN(GPIO80, "GPIO80"), - PINCTRL_PIN(GPIO81, "GPIO81"), - PINCTRL_PIN(GPIO82, "GPIO82"), - PINCTRL_PIN(GPIO83, "GPIO83"), - PINCTRL_PIN(GPIO84, "GPIO84"), - PINCTRL_PIN(GPIO85, "GPIO85"), - PINCTRL_PIN(GPIO86, "GPIO86"), - PINCTRL_PIN(GPIO87, "GPIO87"), - PINCTRL_PIN(GPIO88, "GPIO88"), - PINCTRL_PIN(GPIO89, "GPIO89"), - PINCTRL_PIN(GPIO90, "GPIO90"), - PINCTRL_PIN(GPIO91, "GPIO91"), - PINCTRL_PIN(GPIO92, "GPIO92"), - PINCTRL_PIN(GPIO93, "GPIO93"), - PINCTRL_PIN(GPIO94, "GPIO94"), - PINCTRL_PIN(GPIO95, "GPIO95"), - PINCTRL_PIN(GPIO96, "GPIO96"), - PINCTRL_PIN(GPIO97, "GPIO97"), - PINCTRL_PIN(GPIO98, "GPIO98"), - PINCTRL_PIN(GPIO99, "GPIO99"), - PINCTRL_PIN(GPIO100, "GPIO100"), - PINCTRL_PIN(GPIO101, "GPIO101"), - PINCTRL_PIN(GPIO102, "GPIO102"), - PINCTRL_PIN(GPIO103, "GPIO103"), - PINCTRL_PIN(GPIO104, "GPIO104"), - PINCTRL_PIN(GPIO105, "GPIO105"), - PINCTRL_PIN(GPIO106, "GPIO106"), - PINCTRL_PIN(GPIO107, "GPIO107"), - PINCTRL_PIN(GPIO108, "GPIO108"), - PINCTRL_PIN(GPIO109, "GPIO109"), - PINCTRL_PIN(GPIO110, "GPIO110"), - PINCTRL_PIN(GPIO111, "GPIO111"), - PINCTRL_PIN(GPIO112, "GPIO112"), - PINCTRL_PIN(GPIO113, "GPIO113"), - PINCTRL_PIN(GPIO114, "GPIO114"), - PINCTRL_PIN(GPIO115, "GPIO115"), - PINCTRL_PIN(GPIO116, "GPIO116"), - PINCTRL_PIN(GPIO117, "GPIO117"), - PINCTRL_PIN(GPIO118, "GPIO118"), - PINCTRL_PIN(GPIO119, "GPIO119"), - PINCTRL_PIN(GPIO120, "GPIO120"), - PINCTRL_PIN(GPIO121, "GPIO121"), - PINCTRL_PIN(GPIO122, "GPIO122"), - PINCTRL_PIN(PWR_SCL, "PWR_SCL"), - PINCTRL_PIN(PWR_SDA, "PWR_SDA"), - PINCTRL_PIN(TDI, "TDI"), - PINCTRL_PIN(TMS, "TMS"), - PINCTRL_PIN(TCK, "TCK"), - PINCTRL_PIN(TDO, "TDO"), - PINCTRL_PIN(TRST, "TRST"), - PINCTRL_PIN(WAKEUP, "WAKEUP"), -}; - -struct pxa3xx_mfp_pin pxa168_mfp[] = { - /* pin offs f0 f1 f2 f3 f4 f5 f6 f7 */ - MFPR_168(GPIO0, 0x04C, DFIO, NONE, NONE, MSP, MMC3_CMD, GPIO, MMC3, NONE), - MFPR_168(GPIO1, 0x050, DFIO, NONE, NONE, MSP, MMC3_CLK, GPIO, MMC3, NONE), - MFPR_168(GPIO2, 0x054, DFIO, NONE, NONE, MSP, NONE, GPIO, MMC3, NONE), - MFPR_168(GPIO3, 0x058, DFIO, NONE, NONE, NONE, NONE, GPIO, MMC3, NONE), - MFPR_168(GPIO4, 0x05C, DFIO, NONE, NONE, MSP_DAT3, NONE, GPIO, MMC3, NONE), - MFPR_168(GPIO5, 0x060, DFIO, NONE, NONE, MSP, NONE, GPIO, MMC3, NONE), - MFPR_168(GPIO6, 0x064, DFIO, NONE, NONE, MSP, NONE, GPIO, MMC3, NONE), - MFPR_168(GPIO7, 0x068, DFIO, NONE, NONE, MSP, NONE, GPIO, MMC3, NONE), - MFPR_168(GPIO8, 0x06C, DFIO, MMC2, UART3_TX, NONE, MMC2_CMD, GPIO, MMC3_CLK, NONE), - MFPR_168(GPIO9, 0x070, DFIO, MMC2, UART3, NONE, MMC2_CLK, GPIO, MMC3_CMD, NONE), - MFPR_168(GPIO10, 0x074, DFIO, MMC2, UART3, NONE, NONE, GPIO, MSP_DAT3, NONE), - MFPR_168(GPIO11, 0x078, DFIO, MMC2, UART3, NONE, NONE, GPIO, MSP, NONE), - MFPR_168(GPIO12, 0x07C, DFIO, MMC2, UART3, NONE, NONE, GPIO, MSP, NONE), - MFPR_168(GPIO13, 0x080, DFIO, MMC2, UART3, NONE, NONE, GPIO, MSP, NONE), - MFPR_168(GPIO14, 0x084, DFIO, MMC2, NONE, NONE, NONE, GPIO, MSP, NONE), - MFPR_168(GPIO15, 0x088, DFIO, MMC2, NONE, NONE, NONE, GPIO, MSP, NONE), - MFPR_168(GPIO16, 0x08C, GPIO, NAND, SMC_CS0, SMC_CS1, NONE, NONE, MMC3, NONE), - MFPR_168(GPIO17, 0x090, NAND, NONE, NONE, NONE, NONE, GPIO, MSP, NONE), - MFPR_168(GPIO18, 0x094, GPIO, NAND, SMC_CS1, SMC_CS0, NONE, NONE, NONE, NONE), - MFPR_168(GPIO19, 0x098, SMC_CS0, NONE, NONE, CF, NONE, GPIO, NONE, NONE), - MFPR_168(GPIO20, 0x09C, GPIO, NONE, SMC_CS1, CF, CF_RDY, NONE, NONE, NONE), - MFPR_168(GPIO21, 0x0A0, NAND, MMC2_CLK, NONE, NONE, NONE, GPIO, NONE, NONE), - MFPR_168(GPIO22, 0x0A4, NAND, MMC2_CMD, NONE, NONE, NONE, GPIO, NONE, NONE), - MFPR_168(GPIO23, 0x0A8, SMC, NAND, NONE, CF, NONE, GPIO, NONE, NONE), - MFPR_168(GPIO24, 0x0AC, NAND, NONE, NONE, NONE, NONE, GPIO, NONE, NONE), - MFPR_168(GPIO25, 0x0B0, SMC, NAND, NONE, CF, NONE, GPIO, NONE, NONE), - MFPR_168(GPIO26, 0x0B4, GPIO, NAND, NONE, NONE, CF, NONE, NONE, NONE), - MFPR_168(GPIO27, 0x0B8, SMC_INT, NAND, SMC, NONE, SMC_RDY, GPIO, NONE, NONE), - MFPR_168(GPIO28, 0x0BC, SMC_RDY, MMC4, SMC, CF_RDY, NONE, GPIO, MMC2_CMD, NONE), - MFPR_168(GPIO29, 0x0C0, SMC, MMC4, NONE, CF, NONE, GPIO, MMC2_CLK, KP_DK), - MFPR_168(GPIO30, 0x0C4, SMC, MMC4, UART3_TX, CF, NONE, GPIO, MMC2, KP_DK), - MFPR_168(GPIO31, 0x0C8, SMC, MMC4, UART3, CF, NONE, GPIO, MMC2, KP_DK), - MFPR_168(GPIO32, 0x0CC, SMC, MMC4, UART3, CF, NONE, GPIO, MMC2, KP_DK), - MFPR_168(GPIO33, 0x0D0, SMC, MMC4, UART3, CF, CF_nINPACK, GPIO, MMC2, KP_DK), - MFPR_168(GPIO34, 0x0D4, GPIO, NONE, SMC_CS1, CF, CF_nWAIT, NONE, MMC3, KP_DK), - MFPR_168(GPIO35, 0x0D8, GPIO, NONE, SMC, CF_nINPACK, NONE, NONE, MMC3_CMD, KP_DK), - MFPR_168(GPIO36, 0x0DC, GPIO, NONE, SMC, CF_nWAIT, NONE, NONE, MMC3_CLK, KP_DK), - MFPR_168(GPIO37, 0x000, GPIO, MMC1, NONE, KP_MKOUT, CCIC, XP, KP_MKIN, KP_DK), - MFPR_168(GPIO38, 0x004, GPIO, MMC1, NONE, KP_MKOUT, CCIC, XP, KP_MKIN, KP_DK), - MFPR_168(GPIO39, 0x008, GPIO, NONE, NONE, KP_MKOUT, CCIC, XP, KP_MKIN, KP_DK), - MFPR_168(GPIO40, 0x00C, GPIO, MMC1, MSP, KP_MKOUT, CCIC, XP, KP_MKIN, KP_DK), - MFPR_168(GPIO41, 0x010, GPIO, MMC1, MSP, NONE, CCIC, XP, KP_MKIN, KP_DK), - MFPR_168(GPIO42, 0x014, GPIO, I2C, NONE, MSP, CCIC, XP, KP_MKIN, KP_DK), - MFPR_168(GPIO43, 0x018, GPIO, MMC1, MSP, MSP_INS, NONE, NONE, KP_MKIN, KP_DK), - MFPR_168(GPIO44, 0x01C, GPIO, MMC1, MSP_DAT3, MSP, CCIC, XP, KP_MKIN, KP_DK), - MFPR_168(GPIO45, 0x020, GPIO, NONE, NONE, MSP, CCIC, XP, NONE, KP_DK), - MFPR_168(GPIO46, 0x024, GPIO, MMC1, MSP_INS, MSP, CCIC, NONE, KP_MKOUT, KP_DK), - MFPR_168(GPIO47, 0x028, GPIO, NONE, NONE, MSP_INS, NONE, XP, NONE, KP_DK), - MFPR_168(GPIO48, 0x02C, GPIO, MMC1, NONE, MSP_DAT3, CCIC, NONE, NONE, KP_DK), - MFPR_168(GPIO49, 0x030, GPIO, MMC1, NONE, MSP, NONE, XD, KP_MKOUT, NONE), - MFPR_168(GPIO50, 0x034, GPIO, I2C, NONE, MSP, CCIC, XD, KP_MKOUT, NONE), - MFPR_168(GPIO51, 0x038, GPIO, MMC1, NONE, MSP, NONE, XD, KP_MKOUT, NONE), - MFPR_168(GPIO52, 0x03C, GPIO, MMC1, NONE, MSP, NONE, XD, KP_MKOUT, NONE), - MFPR_168(GPIO53, 0x040, GPIO, MMC1, NONE, NONE, NONE, XD, KP_MKOUT, NONE), - MFPR_168(GPIO54, 0x044, GPIO, MMC1, NONE, NONE, CCIC, XD, KP_MKOUT, NONE), - MFPR_168(GPIO55, 0x048, GPIO, NONE, NONE, MSP, CCIC, XD, KP_MKOUT, NONE), - MFPR_168(GPIO56, 0x0E0, GPIO, LCD, NONE, NONE, NONE, NONE, NONE, NONE), - MFPR_168(GPIO57, 0x0E4, GPIO, LCD, NONE, NONE, NONE, NONE, NONE, NONE), - MFPR_168(GPIO58, 0x0E8, GPIO, LCD, NONE, NONE, NONE, NONE, NONE, NONE), - MFPR_168(GPIO59, 0x0EC, GPIO, LCD, NONE, NONE, NONE, NONE, NONE, NONE), - MFPR_168(GPIO60, 0x0F0, GPIO, LCD, NONE, NONE, NONE, NONE, NONE, NONE), - MFPR_168(GPIO61, 0x0F4, GPIO, LCD, NONE, NONE, NONE, NONE, NONE, NONE), - MFPR_168(GPIO62, 0x0F8, GPIO, LCD, NONE, NONE, NONE, NONE, NONE, NONE), - MFPR_168(GPIO63, 0x0FC, GPIO, LCD, NONE, NONE, NONE, NONE, NONE, NONE), - MFPR_168(GPIO64, 0x100, GPIO, LCD, NONE, NONE, NONE, NONE, NONE, NONE), - MFPR_168(GPIO65, 0x104, GPIO, LCD, NONE, NONE, NONE, NONE, NONE, NONE), - MFPR_168(GPIO66, 0x108, GPIO, LCD, NONE, NONE, NONE, NONE, NONE, NONE), - MFPR_168(GPIO67, 0x10C, GPIO, LCD, NONE, NONE, NONE, NONE, NONE, NONE), - MFPR_168(GPIO68, 0x110, GPIO, LCD, NONE, XD, NONE, NONE, NONE, NONE), - MFPR_168(GPIO69, 0x114, GPIO, LCD, NONE, XD, NONE, NONE, NONE, NONE), - MFPR_168(GPIO70, 0x118, GPIO, LCD, NONE, XD, NONE, NONE, NONE, NONE), - MFPR_168(GPIO71, 0x11C, GPIO, LCD, NONE, XD, NONE, NONE, NONE, NONE), - MFPR_168(GPIO72, 0x120, GPIO, LCD, NONE, XD, NONE, NONE, NONE, NONE), - MFPR_168(GPIO73, 0x124, GPIO, LCD, NONE, XD, NONE, NONE, NONE, NONE), - MFPR_168(GPIO74, 0x128, GPIO, LCD, PWM, XD, NONE, NONE, NONE, NONE), - MFPR_168(GPIO75, 0x12C, GPIO, LCD, PWM, XD, ONE_WIRE, NONE, NONE, NONE), - MFPR_168(GPIO76, 0x130, GPIO, LCD, PWM, I2C, NONE, NONE, MSP_INS, NONE), - MFPR_168(GPIO77, 0x134, GPIO, LCD, PWM1, I2C, ONE_WIRE, NONE, XD, NONE), - MFPR_168(GPIO78, 0x138, GPIO, LCD, NONE, NONE, NONE, MMC4, NONE, NONE), - MFPR_168(GPIO79, 0x13C, GPIO, LCD, NONE, NONE, ONE_WIRE, MMC4, NONE, NONE), - MFPR_168(GPIO80, 0x140, GPIO, LCD, NONE, I2C, NONE, MMC4, NONE, NONE), - MFPR_168(GPIO81, 0x144, GPIO, LCD, NONE, I2C, ONE_WIRE, MMC4, NONE, NONE), - MFPR_168(GPIO82, 0x148, GPIO, LCD, PWM, NONE, NONE, MMC4, NONE, NONE), - MFPR_168(GPIO83, 0x14C, GPIO, LCD, PWM, NONE, RESET, MMC4, NONE, NONE), - MFPR_168(GPIO84, 0x150, GPIO, NONE, PWM, ONE_WIRE, PWM1, NONE, NONE, EXT_32K_IN), - MFPR_168(GPIO85, 0x154, GPIO, NONE, PWM1, NONE, NONE, NONE, NONE, USB), - MFPR_168(GPIO86, 0x158, GPIO, MMC2, UART2, NONE, JTAG, ETH_TX, SSP5_TX, SSP5), - MFPR_168(GPIO87, 0x15C, GPIO, MMC2, UART2, NONE, JTAG, ETH_TX, SSP5, SSP5_TX), - MFPR_168(GPIO88, 0x160, GPIO, MMC2, UART2, UART2_TX, JTAG, ETH_TX, ETH_RX, SSP5), - MFPR_168(GPIO89, 0x164, GPIO, MMC2, UART2_TX, UART2, JTAG, ETH_TX, ETH_RX, SSP5), - MFPR_168(GPIO90, 0x168, GPIO, MMC2, NONE, SSP3, JTAG, ETH_TX, ETH_RX, NONE), - MFPR_168(GPIO91, 0x16C, GPIO, MMC2, NONE, SSP3, SSP4, ETH_TX, ETH_RX, NONE), - MFPR_168(GPIO92, 0x170, GPIO, MMC2, NONE, SSP3, SSP3_TX, ETH, NONE, NONE), - MFPR_168(GPIO93, 0x174, GPIO, MMC2, NONE, SSP3_TX, SSP3, ETH, NONE, NONE), - MFPR_168(GPIO94, 0x178, GPIO, MMC2_CMD, SSP3, AC97_SYSCLK, AC97, ETH, NONE, NONE), - MFPR_168(GPIO95, 0x17C, GPIO, MMC2_CLK, NONE, NONE, AC97, ETH, NONE, NONE), - MFPR_168(GPIO96, 0x180, GPIO, PWM, NONE, MMC2, NONE, ETH_RX, ETH_TX, NONE), - MFPR_168(GPIO97, 0x184, GPIO, PWM, ONE_WIRE, NONE, NONE, ETH_RX, ETH_TX, NONE), - MFPR_168(GPIO98, 0x188, GPIO, PWM1, UART3_TX, UART3, NONE, ETH_RX, ETH_TX, NONE), - MFPR_168(GPIO99, 0x18C, GPIO, ONE_WIRE, UART3, UART3_TX, NONE, ETH_RX, ETH_TX, NONE), - MFPR_168(GPIO100, 0x190, GPIO, NONE, UART3_CTS, UART3, NONE, ETH, NONE, NONE), - MFPR_168(GPIO101, 0x194, GPIO, NONE, UART3, UART3_CTS, NONE, ETH, NONE, NONE), - MFPR_168(GPIO102, 0x198, GPIO, I2C, UART3, SSP4, NONE, NONE, NONE, NONE), - MFPR_168(GPIO103, 0x19C, GPIO, I2C, UART3, SSP4, SSP2, ETH, NONE, NONE), - MFPR_168(GPIO104, 0x1A0, GPIO, PWM, UART1, SSP4, SSP4_TX, AC97, KP_MKOUT, NONE), - MFPR_168(GPIO105, 0x1A4, GPIO, I2C, UART1, SSP4_TX, SSP4, AC97, KP_MKOUT, NONE), - MFPR_168(GPIO106, 0x1A8, GPIO, I2C, PWM1, AC97_SYSCLK, MMC2, NONE, KP_MKOUT, NONE), - MFPR_168(GPIO107, 0x1AC, GPIO, UART1_TX, UART1, NONE, SSP2, MSP_DAT3, NONE, KP_MKIN), - MFPR_168(GPIO108, 0x1B0, GPIO, UART1, UART1_TX, NONE, SSP2_TX, MSP, NONE, KP_MKIN), - MFPR_168(GPIO109, 0x1B4, GPIO, UART1_CTS, UART1, NONE, AC97_SYSCLK, MSP, NONE, KP_MKIN), - MFPR_168(GPIO110, 0x1B8, GPIO, UART1, UART1_CTS, NONE, SMC_RDY, MSP, NONE, KP_MKIN), - MFPR_168(GPIO111, 0x1BC, GPIO, UART1_nRI, UART1, SSP3, SSP2, MSP, XD, KP_MKOUT), - MFPR_168(GPIO112, 0x1C0, GPIO, UART1_DTR, UART1, ONE_WIRE, SSP2, MSP, XD, KP_MKOUT), - MFPR_168(GPIO113, 0x1C4, GPIO, NONE, NONE, NONE, NONE, NONE, AC97_SYSCLK, NONE), - MFPR_168(GPIO114, 0x1C8, GPIO, SSP1, NONE, NONE, NONE, NONE, AC97, NONE), - MFPR_168(GPIO115, 0x1CC, GPIO, SSP1, NONE, NONE, NONE, NONE, AC97, NONE), - MFPR_168(GPIO116, 0x1D0, GPIO, SSP1_TX, SSP1, NONE, NONE, NONE, AC97, NONE), - MFPR_168(GPIO117, 0x1D4, GPIO, SSP1, SSP1_TX, NONE, MMC2_CMD, NONE, AC97, NONE), - MFPR_168(GPIO118, 0x1D8, GPIO, SSP2, NONE, NONE, MMC2_CLK, NONE, AC97, KP_MKIN), - MFPR_168(GPIO119, 0x1DC, GPIO, SSP2, NONE, NONE, MMC2, NONE, AC97, KP_MKIN), - MFPR_168(GPIO120, 0x1E0, GPIO, SSP2, SSP2_TX, NONE, MMC2, NONE, NONE, KP_MKIN), - MFPR_168(GPIO121, 0x1E4, GPIO, SSP2_TX, SSP2, NONE, MMC2, NONE, NONE, KP_MKIN), - MFPR_168(GPIO122, 0x1E8, GPIO, AC97_SYSCLK, SSP2, PWM, MMC2, NONE, NONE, NONE), - MFPR_168(PWR_SCL, 0x1EC, PWRI2C, NONE, NONE, NONE, NONE, NONE, GPIO, MMC4), - MFPR_168(PWR_SDA, 0x1F0, PWRI2C, NONE, NONE, NONE, NONE, NONE, GPIO, NONE), - MFPR_168(TDI, 0x1F4, JTAG, PWM1, UART2, MMC4, SSP5, NONE, XD, MMC4), - MFPR_168(TMS, 0x1F8, JTAG, PWM, UART2, NONE, SSP5, NONE, XD, MMC4), - MFPR_168(TCK, 0x1FC, JTAG, PWM, UART2, UART2_TX, SSP5, NONE, XD, MMC4), - MFPR_168(TDO, 0x200, JTAG, PWM, UART2_TX, UART2, SSP5_TX, NONE, XD, MMC4), - MFPR_168(TRST, 0x204, JTAG, ONE_WIRE, SSP2, SSP3, AC97_SYSCLK, NONE, XD, MMC4), - MFPR_168(WAKEUP, 0x208, WAKEUP, ONE_WIRE, PWM1, PWM, SSP2, NONE, GPIO, MMC4), -}; - -static const unsigned p168_jtag_pin1[] = {TDI, TMS, TCK, TDO, TRST}; -static const unsigned p168_wakeup_pin1[] = {WAKEUP}; -static const unsigned p168_ssp1rx_pin1[] = {GPIO114, GPIO115, GPIO116}; -static const unsigned p168_ssp1tx_pin1[] = {GPIO117}; -static const unsigned p168_ssp4rx_pin1[] = {GPIO102, GPIO103, GPIO104}; -static const unsigned p168_ssp4tx_pin1[] = {GPIO105}; -static const unsigned p168_ssp5rx_pin1[] = {GPIO86, GPIO88, GPIO89}; -static const unsigned p168_ssp5tx_pin1[] = {GPIO87}; -static const unsigned p168_i2c_pin1[] = {GPIO105, GPIO106}; -static const unsigned p168_pwri2c_pin1[] = {PWR_SCL, PWR_SDA}; -static const unsigned p168_mmc1_pin1[] = {GPIO40, GPIO41, GPIO43, GPIO46, - GPIO49, GPIO51, GPIO52, GPIO53}; -static const unsigned p168_mmc2_data_pin1[] = {GPIO90, GPIO91, GPIO92, GPIO93}; -static const unsigned p168_mmc2_cmd_pin1[] = {GPIO94}; -static const unsigned p168_mmc2_clk_pin1[] = {GPIO95}; -static const unsigned p168_mmc3_data_pin1[] = {GPIO0, GPIO1, GPIO2, GPIO3, - GPIO4, GPIO5, GPIO6, GPIO7}; -static const unsigned p168_mmc3_cmd_pin1[] = {GPIO9}; -static const unsigned p168_mmc3_clk_pin1[] = {GPIO8}; -static const unsigned p168_eth_pin1[] = {GPIO92, GPIO93, GPIO100, GPIO101, - GPIO103}; -static const unsigned p168_ethtx_pin1[] = {GPIO86, GPIO87, GPIO88, GPIO89, - GPIO90, GPIO91}; -static const unsigned p168_ethrx_pin1[] = {GPIO94, GPIO95, GPIO96, GPIO97, - GPIO98, GPIO99}; -static const unsigned p168_uart1rx_pin1[] = {GPIO107}; -static const unsigned p168_uart1tx_pin1[] = {GPIO108}; -static const unsigned p168_uart3rx_pin1[] = {GPIO98, GPIO100, GPIO101}; -static const unsigned p168_uart3tx_pin1[] = {GPIO99}; -static const unsigned p168_msp_pin1[] = {GPIO40, GPIO41, GPIO42, GPIO43, - GPIO44, GPIO50}; -static const unsigned p168_ccic_pin1[] = {GPIO37, GPIO38, GPIO39, GPIO40, - GPIO41, GPIO42, GPIO44, GPIO45, GPIO46, GPIO48, GPIO54, GPIO55}; -static const unsigned p168_xd_pin1[] = {GPIO37, GPIO38, GPIO39, GPIO40, - GPIO41, GPIO42, GPIO44, GPIO45, GPIO47, GPIO48, GPIO49, GPIO50, - GPIO51, GPIO52}; -static const unsigned p168_lcd_pin1[] = {GPIO56, GPIO57, GPIO58, GPIO59, - GPIO60, GPIO61, GPIO62, GPIO63, GPIO64, GPIO65, GPIO66, GPIO67, - GPIO68, GPIO69, GPIO70, GPIO71, GPIO72, GPIO73, GPIO74, GPIO75, - GPIO76, GPIO77, GPIO78, GPIO79, GPIO80, GPIO81, GPIO82, GPIO83}; -static const unsigned p168_dfio_pin1[] = {GPIO0, GPIO1, GPIO2, GPIO3, - GPIO4, GPIO5, GPIO6, GPIO7, GPIO8, GPIO9, GPIO10, GPIO11, GPIO12, - GPIO13, GPIO14, GPIO15}; -static const unsigned p168_nand_pin1[] = {GPIO16, GPIO17, GPIO21, GPIO22, - GPIO24, GPIO26}; -static const unsigned p168_smc_pin1[] = {GPIO23, GPIO25, GPIO29, GPIO35, - GPIO36}; -static const unsigned p168_smccs0_pin1[] = {GPIO18}; -static const unsigned p168_smccs1_pin1[] = {GPIO34}; -static const unsigned p168_smcrdy_pin1[] = {GPIO28}; -static const unsigned p168_ac97sysclk_pin1[] = {GPIO113}; -static const unsigned p168_ac97_pin1[] = {GPIO114, GPIO115, GPIO117, GPIO118, - GPIO119}; -static const unsigned p168_cf_pin1[] = {GPIO19, GPIO20, GPIO23, GPIO25, - GPIO28, GPIO29, GPIO30, GPIO31, GPIO32, GPIO33, GPIO34, GPIO35, - GPIO36}; -static const unsigned p168_kpmkin_pin1[] = {GPIO109, GPIO110, GPIO121}; -static const unsigned p168_kpmkout_pin1[] = {GPIO111, GPIO112}; -static const unsigned p168_gpio86_pin1[] = {WAKEUP}; -static const unsigned p168_gpio86_pin2[] = {GPIO86}; -static const unsigned p168_gpio87_pin1[] = {GPIO87}; -static const unsigned p168_gpio87_pin2[] = {PWR_SDA}; -static const unsigned p168_gpio88_pin1[] = {GPIO88}; -static const unsigned p168_gpio88_pin2[] = {PWR_SCL}; - -static struct pxa3xx_pin_group pxa168_grps[] = { - GRP_168("uart1rx-1", UART1, p168_uart1rx_pin1), - GRP_168("uart1tx-1", UART1_TX, p168_uart1tx_pin1), - GRP_168("uart3rx-1", UART3, p168_uart3rx_pin1), - GRP_168("uart3tx-1", UART3_TX, p168_uart3tx_pin1), - GRP_168("ssp1rx-1", SSP1, p168_ssp1rx_pin1), - GRP_168("ssp1tx-1", SSP1_TX, p168_ssp1tx_pin1), - GRP_168("ssp4rx-1", SSP4, p168_ssp4rx_pin1), - GRP_168("ssp4tx-1", SSP4_TX, p168_ssp4tx_pin1), - GRP_168("ssp5rx-1", SSP5, p168_ssp5rx_pin1), - GRP_168("ssp5tx-1", SSP5_TX, p168_ssp5tx_pin1), - GRP_168("jtag", JTAG, p168_jtag_pin1), - GRP_168("wakeup", WAKEUP, p168_wakeup_pin1), - GRP_168("i2c", I2C, p168_i2c_pin1), - GRP_168("pwri2c", PWRI2C, p168_pwri2c_pin1), - GRP_168("mmc1 8p1", MMC1, p168_mmc1_pin1), - GRP_168("mmc2 4p1", MMC2, p168_mmc2_data_pin1), - GRP_168("mmc2 cmd1", MMC2_CMD, p168_mmc2_cmd_pin1), - GRP_168("mmc2 clk1", MMC2_CLK, p168_mmc2_clk_pin1), - GRP_168("mmc3 8p1", MMC3, p168_mmc3_data_pin1), - GRP_168("mmc3 cmd1", MMC3_CMD, p168_mmc3_cmd_pin1), - GRP_168("mmc3 clk1", MMC3_CLK, p168_mmc3_clk_pin1), - GRP_168("eth", ETH, p168_eth_pin1), - GRP_168("eth rx", ETH_RX, p168_ethrx_pin1), - GRP_168("eth tx", ETH_TX, p168_ethtx_pin1), - GRP_168("msp", MSP, p168_msp_pin1), - GRP_168("ccic", CCIC, p168_ccic_pin1), - GRP_168("xd", XD, p168_xd_pin1), - GRP_168("lcd", LCD, p168_lcd_pin1), - GRP_168("dfio", DFIO, p168_dfio_pin1), - GRP_168("nand", NAND, p168_nand_pin1), - GRP_168("smc", SMC, p168_smc_pin1), - GRP_168("smc cs0", SMC_CS0, p168_smccs0_pin1), - GRP_168("smc cs1", SMC_CS1, p168_smccs1_pin1), - GRP_168("smc rdy", SMC_RDY, p168_smcrdy_pin1), - GRP_168("ac97 sysclk", AC97_SYSCLK, p168_ac97sysclk_pin1), - GRP_168("ac97", AC97, p168_ac97_pin1), - GRP_168("cf", CF, p168_cf_pin1), - GRP_168("kp mkin 3p1", KP_MKIN, p168_kpmkin_pin1), - GRP_168("kp mkout 2p1", KP_MKOUT, p168_kpmkout_pin1), - GRP_168("gpio86-1", GPIO, p168_gpio86_pin1), - GRP_168("gpio86-2", GPIO, p168_gpio86_pin2), - GRP_168("gpio87-1", GPIO, p168_gpio87_pin1), - GRP_168("gpio87-2", GPIO, p168_gpio87_pin2), - GRP_168("gpio88-1", GPIO, p168_gpio88_pin1), - GRP_168("gpio88-2", GPIO, p168_gpio88_pin2), -}; - -static const char * const p168_uart1rx_grps[] = {"uart1rx-1"}; -static const char * const p168_uart1tx_grps[] = {"uart1tx-1"}; -static const char * const p168_uart3rx_grps[] = {"uart3rx-1"}; -static const char * const p168_uart3tx_grps[] = {"uart3tx-1"}; -static const char * const p168_ssp1rx_grps[] = {"ssp1rx-1"}; -static const char * const p168_ssp1tx_grps[] = {"ssp1tx-1"}; -static const char * const p168_ssp4rx_grps[] = {"ssp4rx-1"}; -static const char * const p168_ssp4tx_grps[] = {"ssp4tx-1"}; -static const char * const p168_ssp5rx_grps[] = {"ssp5rx-1"}; -static const char * const p168_ssp5tx_grps[] = {"ssp5tx-1"}; -static const char * const p168_i2c_grps[] = {"i2c"}; -static const char * const p168_pwri2c_grps[] = {"pwri2c"}; -static const char * const p168_mmc1_grps[] = {"mmc1 8p1"}; -static const char * const p168_mmc2_data_grps[] = {"mmc2 4p1"}; -static const char * const p168_mmc2_cmd_grps[] = {"mmc2 cmd1"}; -static const char * const p168_mmc2_clk_grps[] = {"mmc2 clk1"}; -static const char * const p168_mmc3_data_grps[] = {"mmc3 8p1"}; -static const char * const p168_mmc3_cmd_grps[] = {"mmc3 cmd1"}; -static const char * const p168_mmc3_clk_grps[] = {"mmc3 clk1"}; -static const char * const p168_eth_grps[] = {"eth"}; -static const char * const p168_ethrx_grps[] = {"eth rx"}; -static const char * const p168_ethtx_grps[] = {"eth tx"}; -static const char * const p168_msp_grps[] = {"msp"}; -static const char * const p168_ccic_grps[] = {"ccic"}; -static const char * const p168_xd_grps[] = {"xd"}; -static const char * const p168_lcd_grps[] = {"lcd"}; -static const char * const p168_dfio_grps[] = {"dfio"}; -static const char * const p168_nand_grps[] = {"nand"}; -static const char * const p168_smc_grps[] = {"smc"}; -static const char * const p168_smccs0_grps[] = {"smc cs0"}; -static const char * const p168_smccs1_grps[] = {"smc cs1"}; -static const char * const p168_smcrdy_grps[] = {"smc rdy"}; -static const char * const p168_ac97sysclk_grps[] = {"ac97 sysclk"}; -static const char * const p168_ac97_grps[] = {"ac97"}; -static const char * const p168_cf_grps[] = {"cf"}; -static const char * const p168_kpmkin_grps[] = {"kp mkin 3p1"}; -static const char * const p168_kpmkout_grps[] = {"kp mkout 2p1"}; -static const char * const p168_gpio86_grps[] = {"gpio86-1", "gpio86-2"}; -static const char * const p168_gpio87_grps[] = {"gpio87-1", "gpio87-2"}; -static const char * const p168_gpio88_grps[] = {"gpio88-1", "gpio88-2"}; - -static struct pxa3xx_pmx_func pxa168_funcs[] = { - {"uart1 rx", ARRAY_AND_SIZE(p168_uart1rx_grps)}, - {"uart1 tx", ARRAY_AND_SIZE(p168_uart1tx_grps)}, - {"uart3 rx", ARRAY_AND_SIZE(p168_uart3rx_grps)}, - {"uart3 tx", ARRAY_AND_SIZE(p168_uart3tx_grps)}, - {"ssp1 rx", ARRAY_AND_SIZE(p168_ssp1rx_grps)}, - {"ssp1 tx", ARRAY_AND_SIZE(p168_ssp1tx_grps)}, - {"ssp4 rx", ARRAY_AND_SIZE(p168_ssp4rx_grps)}, - {"ssp4 tx", ARRAY_AND_SIZE(p168_ssp4tx_grps)}, - {"ssp5 rx", ARRAY_AND_SIZE(p168_ssp5rx_grps)}, - {"ssp5 tx", ARRAY_AND_SIZE(p168_ssp5tx_grps)}, - {"i2c", ARRAY_AND_SIZE(p168_i2c_grps)}, - {"pwri2c", ARRAY_AND_SIZE(p168_pwri2c_grps)}, - {"mmc1", ARRAY_AND_SIZE(p168_mmc1_grps)}, - {"mmc2", ARRAY_AND_SIZE(p168_mmc2_data_grps)}, - {"mmc2 cmd", ARRAY_AND_SIZE(p168_mmc2_cmd_grps)}, - {"mmc2 clk", ARRAY_AND_SIZE(p168_mmc2_clk_grps)}, - {"mmc3", ARRAY_AND_SIZE(p168_mmc3_data_grps)}, - {"mmc3 cmd", ARRAY_AND_SIZE(p168_mmc3_cmd_grps)}, - {"mmc3 clk", ARRAY_AND_SIZE(p168_mmc3_clk_grps)}, - {"eth", ARRAY_AND_SIZE(p168_eth_grps)}, - {"eth rx", ARRAY_AND_SIZE(p168_ethrx_grps)}, - {"eth tx", ARRAY_AND_SIZE(p168_ethtx_grps)}, - {"msp", ARRAY_AND_SIZE(p168_msp_grps)}, - {"ccic", ARRAY_AND_SIZE(p168_ccic_grps)}, - {"xd", ARRAY_AND_SIZE(p168_xd_grps)}, - {"lcd", ARRAY_AND_SIZE(p168_lcd_grps)}, - {"dfio", ARRAY_AND_SIZE(p168_dfio_grps)}, - {"nand", ARRAY_AND_SIZE(p168_nand_grps)}, - {"smc", ARRAY_AND_SIZE(p168_smc_grps)}, - {"smc cs0", ARRAY_AND_SIZE(p168_smccs0_grps)}, - {"smc cs1", ARRAY_AND_SIZE(p168_smccs1_grps)}, - {"smc rdy", ARRAY_AND_SIZE(p168_smcrdy_grps)}, - {"ac97", ARRAY_AND_SIZE(p168_ac97_grps)}, - {"ac97 sysclk", ARRAY_AND_SIZE(p168_ac97sysclk_grps)}, - {"cf", ARRAY_AND_SIZE(p168_cf_grps)}, - {"kpmkin", ARRAY_AND_SIZE(p168_kpmkin_grps)}, - {"kpmkout", ARRAY_AND_SIZE(p168_kpmkout_grps)}, - {"gpio86", ARRAY_AND_SIZE(p168_gpio86_grps)}, - {"gpio87", ARRAY_AND_SIZE(p168_gpio87_grps)}, - {"gpio88", ARRAY_AND_SIZE(p168_gpio88_grps)}, -}; - -static struct pinctrl_desc pxa168_pctrl_desc = { - .name = "pxa168-pinctrl", - .owner = THIS_MODULE, -}; - -static struct pxa3xx_pinmux_info pxa168_info = { - .mfp = pxa168_mfp, - .num_mfp = ARRAY_SIZE(pxa168_mfp), - .grps = pxa168_grps, - .num_grps = ARRAY_SIZE(pxa168_grps), - .funcs = pxa168_funcs, - .num_funcs = ARRAY_SIZE(pxa168_funcs), - .num_gpio = 128, - .desc = &pxa168_pctrl_desc, - .pads = pxa168_pads, - .num_pads = ARRAY_SIZE(pxa168_pads), - - .cputype = PINCTRL_PXA168, - .ds_mask = PXA168_DS_MASK, - .ds_shift = PXA168_DS_SHIFT, -}; - -static int pxa168_pinmux_probe(struct platform_device *pdev) -{ - return pxa3xx_pinctrl_register(pdev, &pxa168_info); -} - -static int pxa168_pinmux_remove(struct platform_device *pdev) -{ - return pxa3xx_pinctrl_unregister(pdev); -} - -static struct platform_driver pxa168_pinmux_driver = { - .driver = { - .name = "pxa168-pinmux", - .owner = THIS_MODULE, - }, - .probe = pxa168_pinmux_probe, - .remove = pxa168_pinmux_remove, -}; - -static int __init pxa168_pinmux_init(void) -{ - return platform_driver_register(&pxa168_pinmux_driver); -} -core_initcall_sync(pxa168_pinmux_init); - -static void __exit pxa168_pinmux_exit(void) -{ - platform_driver_unregister(&pxa168_pinmux_driver); -} -module_exit(pxa168_pinmux_exit); - -MODULE_AUTHOR("Haojian Zhuang <haojian.zhuang@marvell.com>"); -MODULE_DESCRIPTION("PXA3xx pin control driver"); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/pinctrl/pinctrl-pxa3xx.c b/drivers/pinctrl/pinctrl-pxa3xx.c deleted file mode 100644 index 1f49bb02a6af..000000000000 --- a/drivers/pinctrl/pinctrl-pxa3xx.c +++ /dev/null @@ -1,227 +0,0 @@ -/* - * linux/drivers/pinctrl/pinctrl-pxa3xx.c - * - * 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 - * publishhed by the Free Software Foundation. - * - * Copyright (C) 2011, Marvell Technology Group Ltd. - * - * Author: Haojian Zhuang <haojian.zhuang@marvell.com> - * - */ - -#include <linux/err.h> -#include <linux/module.h> -#include <linux/device.h> -#include <linux/io.h> -#include <linux/platform_device.h> -#include <linux/slab.h> -#include "pinctrl-pxa3xx.h" - -static struct pinctrl_gpio_range pxa3xx_pinctrl_gpio_range = { - .name = "PXA3xx GPIO", - .id = 0, - .base = 0, - .pin_base = 0, -}; - -static int pxa3xx_get_groups_count(struct pinctrl_dev *pctrldev) -{ - struct pxa3xx_pinmux_info *info = pinctrl_dev_get_drvdata(pctrldev); - - return info->num_grps; -} - -static const char *pxa3xx_get_group_name(struct pinctrl_dev *pctrldev, - unsigned selector) -{ - struct pxa3xx_pinmux_info *info = pinctrl_dev_get_drvdata(pctrldev); - - return info->grps[selector].name; -} - -static int pxa3xx_get_group_pins(struct pinctrl_dev *pctrldev, - unsigned selector, - const unsigned **pins, - unsigned *num_pins) -{ - struct pxa3xx_pinmux_info *info = pinctrl_dev_get_drvdata(pctrldev); - - *pins = info->grps[selector].pins; - *num_pins = info->grps[selector].npins; - return 0; -} - -static struct pinctrl_ops pxa3xx_pctrl_ops = { - .get_groups_count = pxa3xx_get_groups_count, - .get_group_name = pxa3xx_get_group_name, - .get_group_pins = pxa3xx_get_group_pins, -}; - -static int pxa3xx_pmx_get_funcs_count(struct pinctrl_dev *pctrldev) -{ - struct pxa3xx_pinmux_info *info = pinctrl_dev_get_drvdata(pctrldev); - - return info->num_funcs; -} - -static const char *pxa3xx_pmx_get_func_name(struct pinctrl_dev *pctrldev, - unsigned func) -{ - struct pxa3xx_pinmux_info *info = pinctrl_dev_get_drvdata(pctrldev); - return info->funcs[func].name; -} - -static int pxa3xx_pmx_get_groups(struct pinctrl_dev *pctrldev, unsigned func, - const char * const **groups, - unsigned * const num_groups) -{ - struct pxa3xx_pinmux_info *info = pinctrl_dev_get_drvdata(pctrldev); - *groups = info->funcs[func].groups; - *num_groups = info->funcs[func].num_groups; - return 0; -} - -/* Return function number. If failure, return negative value. */ -static int match_mux(struct pxa3xx_mfp_pin *mfp, unsigned mux) -{ - int i; - for (i = 0; i < PXA3xx_MAX_MUX; i++) { - if (mfp->func[i] == mux) - break; - } - if (i >= PXA3xx_MAX_MUX) - return -EINVAL; - return i; -} - -/* check whether current pin configuration is valid. Negative for failure */ -static int match_group_mux(struct pxa3xx_pin_group *grp, - struct pxa3xx_pinmux_info *info, - unsigned mux) -{ - int i, pin, ret = 0; - for (i = 0; i < grp->npins; i++) { - pin = grp->pins[i]; - ret = match_mux(&info->mfp[pin], mux); - if (ret < 0) { - dev_err(info->dev, "Can't find mux %d on pin%d\n", - mux, pin); - break; - } - } - return ret; -} - -static int pxa3xx_pmx_enable(struct pinctrl_dev *pctrldev, unsigned func, - unsigned group) -{ - struct pxa3xx_pinmux_info *info = pinctrl_dev_get_drvdata(pctrldev); - struct pxa3xx_pin_group *pin_grp = &info->grps[group]; - unsigned int data; - int i, mfpr, pin, pin_func; - - if (!pin_grp->npins || - (match_group_mux(pin_grp, info, pin_grp->mux) < 0)) { - dev_err(info->dev, "Failed to set the pin group: %d\n", group); - return -EINVAL; - } - for (i = 0; i < pin_grp->npins; i++) { - pin = pin_grp->pins[i]; - pin_func = match_mux(&info->mfp[pin], pin_grp->mux); - mfpr = info->mfp[pin].mfpr; - data = readl_relaxed(info->virt_base + mfpr); - data &= ~MFPR_FUNC_MASK; - data |= pin_func; - writel_relaxed(data, info->virt_base + mfpr); - } - return 0; -} - -static int pxa3xx_pmx_request_gpio(struct pinctrl_dev *pctrldev, - struct pinctrl_gpio_range *range, - unsigned pin) -{ - struct pxa3xx_pinmux_info *info = pinctrl_dev_get_drvdata(pctrldev); - unsigned int data; - int pin_func, mfpr; - - pin_func = match_mux(&info->mfp[pin], PXA3xx_MUX_GPIO); - if (pin_func < 0) { - dev_err(info->dev, "No GPIO function on pin%d (%s)\n", - pin, info->pads[pin].name); - return -EINVAL; - } - mfpr = info->mfp[pin].mfpr; - /* write gpio function into mfpr register */ - data = readl_relaxed(info->virt_base + mfpr) & ~MFPR_FUNC_MASK; - data |= pin_func; - writel_relaxed(data, info->virt_base + mfpr); - return 0; -} - -static struct pinmux_ops pxa3xx_pmx_ops = { - .get_functions_count = pxa3xx_pmx_get_funcs_count, - .get_function_name = pxa3xx_pmx_get_func_name, - .get_function_groups = pxa3xx_pmx_get_groups, - .enable = pxa3xx_pmx_enable, - .gpio_request_enable = pxa3xx_pmx_request_gpio, -}; - -int pxa3xx_pinctrl_register(struct platform_device *pdev, - struct pxa3xx_pinmux_info *info) -{ - struct pinctrl_desc *desc; - struct resource *res; - - if (!info || !info->cputype) - return -EINVAL; - desc = info->desc; - desc->pins = info->pads; - desc->npins = info->num_pads; - desc->pctlops = &pxa3xx_pctrl_ops; - desc->pmxops = &pxa3xx_pmx_ops; - info->dev = &pdev->dev; - pxa3xx_pinctrl_gpio_range.npins = info->num_gpio; - - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) - return -ENOENT; - info->virt_base = devm_ioremap_resource(&pdev->dev, res); - if (IS_ERR(info->virt_base)) - return PTR_ERR(info->virt_base); - info->pctrl = pinctrl_register(desc, &pdev->dev, info); - if (!info->pctrl) { - dev_err(&pdev->dev, "failed to register PXA pinmux driver\n"); - return -EINVAL; - } - pinctrl_add_gpio_range(info->pctrl, &pxa3xx_pinctrl_gpio_range); - platform_set_drvdata(pdev, info); - return 0; -} - -int pxa3xx_pinctrl_unregister(struct platform_device *pdev) -{ - struct pxa3xx_pinmux_info *info = platform_get_drvdata(pdev); - - pinctrl_unregister(info->pctrl); - platform_set_drvdata(pdev, NULL); - return 0; -} - -static int __init pxa3xx_pinctrl_init(void) -{ - pr_info("pxa3xx-pinctrl: PXA3xx pinctrl driver initializing\n"); - return 0; -} -core_initcall_sync(pxa3xx_pinctrl_init); - -static void __exit pxa3xx_pinctrl_exit(void) -{ -} -module_exit(pxa3xx_pinctrl_exit); - -MODULE_AUTHOR("Haojian Zhuang <haojian.zhuang@marvell.com>"); -MODULE_DESCRIPTION("PXA3xx pin control driver"); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/pinctrl/pinctrl-pxa3xx.h b/drivers/pinctrl/pinctrl-pxa3xx.h deleted file mode 100644 index 92fad0880834..000000000000 --- a/drivers/pinctrl/pinctrl-pxa3xx.h +++ /dev/null @@ -1,262 +0,0 @@ -/* - * linux/drivers/pinctrl/pinctrl-pxa3xx.h - * - * 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 - * publishhed by the Free Software Foundation. - * - * Copyright (C) 2011, Marvell Technology Group Ltd. - * - * Author: Haojian Zhuang <haojian.zhuang@marvell.com> - * - */ - -#ifndef __PINCTRL_PXA3XX_H - -#include <linux/pinctrl/pinctrl.h> -#include <linux/pinctrl/pinmux.h> - -#define ARRAY_AND_SIZE(x) (x), ARRAY_SIZE(x) - -#define PXA3xx_MUX_GPIO 0 - -#define PXA3xx_MAX_MUX 8 -#define MFPR_FUNC_MASK 0x7 - -enum pxa_cpu_type { - PINCTRL_INVALID = 0, - PINCTRL_PXA300, - PINCTRL_PXA310, - PINCTRL_PXA320, - PINCTRL_PXA168, - PINCTRL_PXA910, - PINCTRL_PXA930, - PINCTRL_PXA955, - PINCTRL_MMP2, - PINCTRL_MAX, -}; - -struct pxa3xx_mfp_pin { - const char *name; - const unsigned int pin; - const unsigned int mfpr; /* register offset */ - const unsigned short func[8]; -}; - -struct pxa3xx_pin_group { - const char *name; - const unsigned mux; - const unsigned *pins; - const unsigned npins; -}; - -struct pxa3xx_pmx_func { - const char *name; - const char * const * groups; - const unsigned num_groups; -}; - -struct pxa3xx_pinmux_info { - struct device *dev; - struct pinctrl_dev *pctrl; - enum pxa_cpu_type cputype; - void __iomem *virt_base; - - struct pxa3xx_mfp_pin *mfp; - unsigned int num_mfp; - struct pxa3xx_pin_group *grps; - unsigned int num_grps; - struct pxa3xx_pmx_func *funcs; - unsigned int num_funcs; - unsigned int num_gpio; - struct pinctrl_desc *desc; - struct pinctrl_pin_desc *pads; - unsigned int num_pads; - - unsigned ds_mask; /* drive strength mask */ - unsigned ds_shift; /* drive strength shift */ - unsigned slp_mask; /* sleep mask */ - unsigned slp_input_low; - unsigned slp_input_high; - unsigned slp_output_low; - unsigned slp_output_high; - unsigned slp_float; -}; - -enum pxa3xx_pin_list { - GPIO0 = 0, - GPIO1, - GPIO2, - GPIO3, - GPIO4, - GPIO5, - GPIO6, - GPIO7, - GPIO8, - GPIO9, - GPIO10, /* 10 */ - GPIO11, - GPIO12, - GPIO13, - GPIO14, - GPIO15, - GPIO16, - GPIO17, - GPIO18, - GPIO19, - GPIO20, /* 20 */ - GPIO21, - GPIO22, - GPIO23, - GPIO24, - GPIO25, - GPIO26, - GPIO27, - GPIO28, - GPIO29, - GPIO30, /* 30 */ - GPIO31, - GPIO32, - GPIO33, - GPIO34, - GPIO35, - GPIO36, - GPIO37, - GPIO38, - GPIO39, - GPIO40, /* 40 */ - GPIO41, - GPIO42, - GPIO43, - GPIO44, - GPIO45, - GPIO46, - GPIO47, - GPIO48, - GPIO49, - GPIO50, /* 50 */ - GPIO51, - GPIO52, - GPIO53, - GPIO54, - GPIO55, - GPIO56, - GPIO57, - GPIO58, - GPIO59, - GPIO60, /* 60 */ - GPIO61, - GPIO62, - GPIO63, - GPIO64, - GPIO65, - GPIO66, - GPIO67, - GPIO68, - GPIO69, - GPIO70, /* 70 */ - GPIO71, - GPIO72, - GPIO73, - GPIO74, - GPIO75, - GPIO76, - GPIO77, - GPIO78, - GPIO79, - GPIO80, /* 80 */ - GPIO81, - GPIO82, - GPIO83, - GPIO84, - GPIO85, - GPIO86, - GPIO87, - GPIO88, - GPIO89, - GPIO90, /* 90 */ - GPIO91, - GPIO92, - GPIO93, - GPIO94, - GPIO95, - GPIO96, - GPIO97, - GPIO98, - GPIO99, - GPIO100, /* 100 */ - GPIO101, - GPIO102, - GPIO103, - GPIO104, - GPIO105, - GPIO106, - GPIO107, - GPIO108, - GPIO109, - GPIO110, /* 110 */ - GPIO111, - GPIO112, - GPIO113, - GPIO114, - GPIO115, - GPIO116, - GPIO117, - GPIO118, - GPIO119, - GPIO120, /* 120 */ - GPIO121, - GPIO122, - GPIO123, - GPIO124, - GPIO125, - GPIO126, - GPIO127, - GPIO128, - GPIO129, - GPIO130, /* 130 */ - GPIO131, - GPIO132, - GPIO133, - GPIO134, - GPIO135, - GPIO136, - GPIO137, - GPIO138, - GPIO139, - GPIO140, /* 140 */ - GPIO141, - GPIO142, - GPIO143, - GPIO144, - GPIO145, - GPIO146, - GPIO147, - GPIO148, - GPIO149, - GPIO150, /* 150 */ - GPIO151, - GPIO152, - GPIO153, - GPIO154, - GPIO155, - GPIO156, - GPIO157, - GPIO158, - GPIO159, - GPIO160, /* 160 */ - GPIO161, - GPIO162, - GPIO163, - GPIO164, - GPIO165, - GPIO166, - GPIO167, - GPIO168, - GPIO169, -}; - -extern int pxa3xx_pinctrl_register(struct platform_device *pdev, - struct pxa3xx_pinmux_info *info); -extern int pxa3xx_pinctrl_unregister(struct platform_device *pdev); -#endif /* __PINCTRL_PXA3XX_H */ diff --git a/drivers/pinctrl/pinctrl-pxa910.c b/drivers/pinctrl/pinctrl-pxa910.c deleted file mode 100644 index a2f917b847fb..000000000000 --- a/drivers/pinctrl/pinctrl-pxa910.c +++ /dev/null @@ -1,1007 +0,0 @@ -/* - * linux/drivers/pinctrl/pinmux-pxa910.c - * - * 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 - * publishhed by the Free Software Foundation. - * - * Copyright (C) 2011, Marvell Technology Group Ltd. - * - * Author: Haojian Zhuang <haojian.zhuang@marvell.com> - * - */ - -#include <linux/device.h> -#include <linux/module.h> -#include <linux/io.h> -#include <linux/platform_device.h> -#include "pinctrl-pxa3xx.h" - -#define PXA910_DS_MASK 0x1800 -#define PXA910_DS_SHIFT 11 -#define PXA910_SLEEP_MASK 0x38 -#define PXA910_SLEEP_SELECT (1 << 9) -#define PXA910_SLEEP_DATA (1 << 8) -#define PXA910_SLEEP_DIR (1 << 7) - -#define MFPR_910(a, r, f0, f1, f2, f3, f4, f5, f6, f7) \ - { \ - .name = #a, \ - .pin = a, \ - .mfpr = r, \ - .func = { \ - PXA910_MUX_##f0, \ - PXA910_MUX_##f1, \ - PXA910_MUX_##f2, \ - PXA910_MUX_##f3, \ - PXA910_MUX_##f4, \ - PXA910_MUX_##f5, \ - PXA910_MUX_##f6, \ - PXA910_MUX_##f7, \ - }, \ - } - -#define GRP_910(a, m, p) \ - { .name = a, .mux = PXA910_MUX_##m, .pins = p, .npins = ARRAY_SIZE(p), } - -/* 170 pins */ -enum pxa910_pin_list { - /* 0~127: GPIO0~GPIO127 */ - ND_IO15 = 128, - ND_IO14, - ND_IO13, /* 130 */ - ND_IO12, - ND_IO11, - ND_IO10, - ND_IO9, - ND_IO8, - ND_IO7, - ND_IO6, - ND_IO5, - ND_IO4, - ND_IO3, /* 140 */ - ND_IO2, - ND_IO1, - ND_IO0, - ND_NCS0, - ND_NCS1, - SM_NCS0, - SM_NCS1, - ND_NWE, - ND_NRE, - ND_CLE, /* 150 */ - ND_ALE, - SM_SCLK, - ND_RDY0, - SM_ADV, - ND_RDY1, - SM_ADVMUX, - SM_RDY, - MMC1_DAT7, - MMC1_DAT6, - MMC1_DAT5, /* 160 */ - MMC1_DAT4, - MMC1_DAT3, - MMC1_DAT2, - MMC1_DAT1, - MMC1_DAT0, - MMC1_CMD, - MMC1_CLK, - MMC1_CD, - VCXO_OUT, -}; - -enum pxa910_mux { - /* PXA3xx_MUX_GPIO = 0 (predefined in pinctrl-pxa3xx.h) */ - PXA910_MUX_GPIO = 0, - PXA910_MUX_NAND, - PXA910_MUX_USIM2, - PXA910_MUX_EXT_DMA, - PXA910_MUX_EXT_INT, - PXA910_MUX_MMC1, - PXA910_MUX_MMC2, - PXA910_MUX_MMC3, - PXA910_MUX_SM_INT, - PXA910_MUX_PRI_JTAG, - PXA910_MUX_SEC1_JTAG, - PXA910_MUX_SEC2_JTAG, - PXA910_MUX_RESET, /* SLAVE RESET OUT */ - PXA910_MUX_CLK_REQ, - PXA910_MUX_VCXO_REQ, - PXA910_MUX_VCXO_OUT, - PXA910_MUX_VCXO_REQ2, - PXA910_MUX_VCXO_OUT2, - PXA910_MUX_SPI, - PXA910_MUX_SPI2, - PXA910_MUX_GSSP, - PXA910_MUX_SSP0, - PXA910_MUX_SSP1, - PXA910_MUX_SSP2, - PXA910_MUX_DSSP2, - PXA910_MUX_DSSP3, - PXA910_MUX_UART0, - PXA910_MUX_UART1, - PXA910_MUX_UART2, - PXA910_MUX_TWSI, - PXA910_MUX_CCIC, - PXA910_MUX_PWM0, - PXA910_MUX_PWM1, - PXA910_MUX_PWM2, - PXA910_MUX_PWM3, - PXA910_MUX_HSL, - PXA910_MUX_ONE_WIRE, - PXA910_MUX_LCD, - PXA910_MUX_DAC_ST23, - PXA910_MUX_ULPI, - PXA910_MUX_TB, - PXA910_MUX_KP_MK, - PXA910_MUX_KP_DK, - PXA910_MUX_TCU_GPOA, - PXA910_MUX_TCU_GPOB, - PXA910_MUX_ROT, - PXA910_MUX_TDS, - PXA910_MUX_32K_CLK, /* 32KHz CLK OUT */ - PXA910_MUX_MN_CLK, /* MN CLK OUT */ - PXA910_MUX_SMC, - PXA910_MUX_SM_ADDR18, - PXA910_MUX_SM_ADDR19, - PXA910_MUX_SM_ADDR20, - PXA910_MUX_NONE = 0xffff, -}; - - -static struct pinctrl_pin_desc pxa910_pads[] = { - PINCTRL_PIN(GPIO0, "GPIO0"), - PINCTRL_PIN(GPIO1, "GPIO1"), - PINCTRL_PIN(GPIO2, "GPIO2"), - PINCTRL_PIN(GPIO3, "GPIO3"), - PINCTRL_PIN(GPIO4, "GPIO4"), - PINCTRL_PIN(GPIO5, "GPIO5"), - PINCTRL_PIN(GPIO6, "GPIO6"), - PINCTRL_PIN(GPIO7, "GPIO7"), - PINCTRL_PIN(GPIO8, "GPIO8"), - PINCTRL_PIN(GPIO9, "GPIO9"), - PINCTRL_PIN(GPIO10, "GPIO10"), - PINCTRL_PIN(GPIO11, "GPIO11"), - PINCTRL_PIN(GPIO12, "GPIO12"), - PINCTRL_PIN(GPIO13, "GPIO13"), - PINCTRL_PIN(GPIO14, "GPIO14"), - PINCTRL_PIN(GPIO15, "GPIO15"), - PINCTRL_PIN(GPIO16, "GPIO16"), - PINCTRL_PIN(GPIO17, "GPIO17"), - PINCTRL_PIN(GPIO18, "GPIO18"), - PINCTRL_PIN(GPIO19, "GPIO19"), - PINCTRL_PIN(GPIO20, "GPIO20"), - PINCTRL_PIN(GPIO21, "GPIO21"), - PINCTRL_PIN(GPIO22, "GPIO22"), - PINCTRL_PIN(GPIO23, "GPIO23"), - PINCTRL_PIN(GPIO24, "GPIO24"), - PINCTRL_PIN(GPIO25, "GPIO25"), - PINCTRL_PIN(GPIO26, "GPIO26"), - PINCTRL_PIN(GPIO27, "GPIO27"), - PINCTRL_PIN(GPIO28, "GPIO28"), - PINCTRL_PIN(GPIO29, "GPIO29"), - PINCTRL_PIN(GPIO30, "GPIO30"), - PINCTRL_PIN(GPIO31, "GPIO31"), - PINCTRL_PIN(GPIO32, "GPIO32"), - PINCTRL_PIN(GPIO33, "GPIO33"), - PINCTRL_PIN(GPIO34, "GPIO34"), - PINCTRL_PIN(GPIO35, "GPIO35"), - PINCTRL_PIN(GPIO36, "GPIO36"), - PINCTRL_PIN(GPIO37, "GPIO37"), - PINCTRL_PIN(GPIO38, "GPIO38"), - PINCTRL_PIN(GPIO39, "GPIO39"), - PINCTRL_PIN(GPIO40, "GPIO40"), - PINCTRL_PIN(GPIO41, "GPIO41"), - PINCTRL_PIN(GPIO42, "GPIO42"), - PINCTRL_PIN(GPIO43, "GPIO43"), - PINCTRL_PIN(GPIO44, "GPIO44"), - PINCTRL_PIN(GPIO45, "GPIO45"), - PINCTRL_PIN(GPIO46, "GPIO46"), - PINCTRL_PIN(GPIO47, "GPIO47"), - PINCTRL_PIN(GPIO48, "GPIO48"), - PINCTRL_PIN(GPIO49, "GPIO49"), - PINCTRL_PIN(GPIO50, "GPIO50"), - PINCTRL_PIN(GPIO51, "GPIO51"), - PINCTRL_PIN(GPIO52, "GPIO52"), - PINCTRL_PIN(GPIO53, "GPIO53"), - PINCTRL_PIN(GPIO54, "GPIO54"), - PINCTRL_PIN(GPIO55, "GPIO55"), - PINCTRL_PIN(GPIO56, "GPIO56"), - PINCTRL_PIN(GPIO57, "GPIO57"), - PINCTRL_PIN(GPIO58, "GPIO58"), - PINCTRL_PIN(GPIO59, "GPIO59"), - PINCTRL_PIN(GPIO60, "GPIO60"), - PINCTRL_PIN(GPIO61, "GPIO61"), - PINCTRL_PIN(GPIO62, "GPIO62"), - PINCTRL_PIN(GPIO63, "GPIO63"), - PINCTRL_PIN(GPIO64, "GPIO64"), - PINCTRL_PIN(GPIO65, "GPIO65"), - PINCTRL_PIN(GPIO66, "GPIO66"), - PINCTRL_PIN(GPIO67, "GPIO67"), - PINCTRL_PIN(GPIO68, "GPIO68"), - PINCTRL_PIN(GPIO69, "GPIO69"), - PINCTRL_PIN(GPIO70, "GPIO70"), - PINCTRL_PIN(GPIO71, "GPIO71"), - PINCTRL_PIN(GPIO72, "GPIO72"), - PINCTRL_PIN(GPIO73, "GPIO73"), - PINCTRL_PIN(GPIO74, "GPIO74"), - PINCTRL_PIN(GPIO75, "GPIO75"), - PINCTRL_PIN(GPIO76, "GPIO76"), - PINCTRL_PIN(GPIO77, "GPIO77"), - PINCTRL_PIN(GPIO78, "GPIO78"), - PINCTRL_PIN(GPIO79, "GPIO79"), - PINCTRL_PIN(GPIO80, "GPIO80"), - PINCTRL_PIN(GPIO81, "GPIO81"), - PINCTRL_PIN(GPIO82, "GPIO82"), - PINCTRL_PIN(GPIO83, "GPIO83"), - PINCTRL_PIN(GPIO84, "GPIO84"), - PINCTRL_PIN(GPIO85, "GPIO85"), - PINCTRL_PIN(GPIO86, "GPIO86"), - PINCTRL_PIN(GPIO87, "GPIO87"), - PINCTRL_PIN(GPIO88, "GPIO88"), - PINCTRL_PIN(GPIO89, "GPIO89"), - PINCTRL_PIN(GPIO90, "GPIO90"), - PINCTRL_PIN(GPIO91, "GPIO91"), - PINCTRL_PIN(GPIO92, "GPIO92"), - PINCTRL_PIN(GPIO93, "GPIO93"), - PINCTRL_PIN(GPIO94, "GPIO94"), - PINCTRL_PIN(GPIO95, "GPIO95"), - PINCTRL_PIN(GPIO96, "GPIO96"), - PINCTRL_PIN(GPIO97, "GPIO97"), - PINCTRL_PIN(GPIO98, "GPIO98"), - PINCTRL_PIN(GPIO99, "GPIO99"), - PINCTRL_PIN(GPIO100, "GPIO100"), - PINCTRL_PIN(GPIO101, "GPIO101"), - PINCTRL_PIN(GPIO102, "GPIO102"), - PINCTRL_PIN(GPIO103, "GPIO103"), - PINCTRL_PIN(GPIO104, "GPIO104"), - PINCTRL_PIN(GPIO105, "GPIO105"), - PINCTRL_PIN(GPIO106, "GPIO106"), - PINCTRL_PIN(GPIO107, "GPIO107"), - PINCTRL_PIN(GPIO108, "GPIO108"), - PINCTRL_PIN(GPIO109, "GPIO109"), - PINCTRL_PIN(GPIO110, "GPIO110"), - PINCTRL_PIN(GPIO111, "GPIO111"), - PINCTRL_PIN(GPIO112, "GPIO112"), - PINCTRL_PIN(GPIO113, "GPIO113"), - PINCTRL_PIN(GPIO114, "GPIO114"), - PINCTRL_PIN(GPIO115, "GPIO115"), - PINCTRL_PIN(GPIO116, "GPIO116"), - PINCTRL_PIN(GPIO117, "GPIO117"), - PINCTRL_PIN(GPIO118, "GPIO118"), - PINCTRL_PIN(GPIO119, "GPIO119"), - PINCTRL_PIN(GPIO120, "GPIO120"), - PINCTRL_PIN(GPIO121, "GPIO121"), - PINCTRL_PIN(GPIO122, "GPIO122"), - PINCTRL_PIN(GPIO123, "GPIO123"), - PINCTRL_PIN(GPIO124, "GPIO124"), - PINCTRL_PIN(GPIO125, "GPIO125"), - PINCTRL_PIN(GPIO126, "GPIO126"), - PINCTRL_PIN(GPIO127, "GPIO127"), - PINCTRL_PIN(ND_IO15, "ND_IO15"), - PINCTRL_PIN(ND_IO14, "ND_IO14"), - PINCTRL_PIN(ND_IO13, "ND_IO13"), - PINCTRL_PIN(ND_IO12, "ND_IO12"), - PINCTRL_PIN(ND_IO11, "ND_IO11"), - PINCTRL_PIN(ND_IO10, "ND_IO10"), - PINCTRL_PIN(ND_IO9, "ND_IO9"), - PINCTRL_PIN(ND_IO8, "ND_IO8"), - PINCTRL_PIN(ND_IO7, "ND_IO7"), - PINCTRL_PIN(ND_IO6, "ND_IO6"), - PINCTRL_PIN(ND_IO5, "ND_IO5"), - PINCTRL_PIN(ND_IO4, "ND_IO4"), - PINCTRL_PIN(ND_IO3, "ND_IO3"), - PINCTRL_PIN(ND_IO2, "ND_IO2"), - PINCTRL_PIN(ND_IO1, "ND_IO1"), - PINCTRL_PIN(ND_IO0, "ND_IO0"), - PINCTRL_PIN(ND_NCS0, "ND_NCS0_SM_NCS2"), - PINCTRL_PIN(ND_NCS1, "ND_NCS1_SM_NCS3"), - PINCTRL_PIN(SM_NCS0, "SM_NCS0"), - PINCTRL_PIN(SM_NCS1, "SM_NCS1"), - PINCTRL_PIN(ND_NWE, "ND_NWE"), - PINCTRL_PIN(ND_NRE, "ND_NRE"), - PINCTRL_PIN(ND_CLE, "ND_CLE_SM_NOE"), - PINCTRL_PIN(ND_ALE, "ND_ALE_SM_NWE"), - PINCTRL_PIN(SM_SCLK, "SM_SCLK"), - PINCTRL_PIN(ND_RDY0, "ND_RDY0"), - PINCTRL_PIN(SM_ADV, "SM_ADV"), - PINCTRL_PIN(ND_RDY1, "ND_RDY1"), - PINCTRL_PIN(SM_RDY, "SM_RDY"), - PINCTRL_PIN(MMC1_DAT7, "MMC1_DAT7"), - PINCTRL_PIN(MMC1_DAT6, "MMC1_DAT6"), - PINCTRL_PIN(MMC1_DAT5, "MMC1_DAT5"), - PINCTRL_PIN(MMC1_DAT4, "MMC1_DAT4"), - PINCTRL_PIN(MMC1_DAT3, "MMC1_DAT3"), - PINCTRL_PIN(MMC1_DAT2, "MMC1_DAT2"), - PINCTRL_PIN(MMC1_DAT1, "MMC1_DAT1"), - PINCTRL_PIN(MMC1_DAT0, "MMC1_DAT0"), - PINCTRL_PIN(MMC1_CMD, "MMC1 CMD"), - PINCTRL_PIN(MMC1_CLK, "MMC1 CLK"), - PINCTRL_PIN(MMC1_CD, "MMC1 CD"), - PINCTRL_PIN(VCXO_OUT, "VCXO_OUT"), -}; - -struct pxa3xx_mfp_pin pxa910_mfp[] = { - /* pin offs f0 f1 f2 f3 f4 f5 f6 f7 */ - MFPR_910(GPIO0, 0x0DC, GPIO, KP_MK, NONE, NONE, NONE, NONE, NONE, NONE), - MFPR_910(GPIO1, 0x0E0, GPIO, KP_MK, NONE, NONE, NONE, NONE, NONE, NONE), - MFPR_910(GPIO2, 0x0E4, GPIO, KP_MK, NONE, NONE, NONE, NONE, NONE, NONE), - MFPR_910(GPIO3, 0x0E8, GPIO, KP_MK, NONE, NONE, NONE, NONE, NONE, NONE), - MFPR_910(GPIO4, 0x0EC, GPIO, KP_MK, NONE, NONE, NONE, NONE, NONE, NONE), - MFPR_910(GPIO5, 0x0F0, GPIO, KP_MK, NONE, NONE, NONE, NONE, NONE, NONE), - MFPR_910(GPIO6, 0x0F4, GPIO, KP_MK, NONE, NONE, NONE, NONE, NONE, NONE), - MFPR_910(GPIO7, 0x0F8, GPIO, KP_MK, NONE, NONE, NONE, NONE, NONE, NONE), - MFPR_910(GPIO8, 0x0FC, GPIO, KP_MK, NONE, NONE, NONE, NONE, NONE, NONE), - MFPR_910(GPIO9, 0x100, GPIO, KP_MK, NONE, NONE, NONE, NONE, NONE, NONE), - MFPR_910(GPIO10, 0x104, GPIO, KP_MK, NONE, NONE, NONE, NONE, NONE, NONE), - MFPR_910(GPIO11, 0x108, GPIO, KP_MK, NONE, NONE, NONE, NONE, NONE, NONE), - MFPR_910(GPIO12, 0x10C, GPIO, KP_MK, NONE, NONE, KP_DK, NONE, NONE, NONE), - MFPR_910(GPIO13, 0x110, GPIO, KP_MK, NONE, NONE, KP_DK, NONE, NONE, NONE), - MFPR_910(GPIO14, 0x114, GPIO, KP_MK, NONE, NONE, KP_DK, TB, NONE, NONE), - MFPR_910(GPIO15, 0x118, GPIO, KP_MK, NONE, NONE, KP_DK, TB, NONE, NONE), - MFPR_910(GPIO16, 0x11C, GPIO, KP_DK, NONE, NONE, NONE, TB, NONE, NONE), - MFPR_910(GPIO17, 0x120, GPIO, KP_DK, NONE, NONE, NONE, TB, NONE, NONE), - MFPR_910(GPIO18, 0x124, GPIO, KP_DK, NONE, NONE, ROT, NONE, NONE, NONE), - MFPR_910(GPIO19, 0x128, GPIO, KP_DK, NONE, NONE, ROT, NONE, NONE, NONE), - MFPR_910(GPIO20, 0x12C, GPIO, SSP1, NONE, NONE, VCXO_OUT, NONE, NONE, NONE), - MFPR_910(GPIO21, 0x130, GPIO, SSP1, NONE, NONE, NONE, NONE, NONE, NONE), - MFPR_910(GPIO22, 0x134, GPIO, SSP1, NONE, NONE, NONE, NONE, NONE, NONE), - MFPR_910(GPIO23, 0x138, GPIO, SSP1, NONE, NONE, NONE, NONE, NONE, NONE), - MFPR_910(GPIO24, 0x13C, GPIO, SSP1, NONE, NONE, NONE, NONE, NONE, NONE), - MFPR_910(GPIO25, 0x140, GPIO, GSSP, NONE, NONE, NONE, NONE, NONE, NONE), - MFPR_910(GPIO26, 0x144, GPIO, GSSP, NONE, NONE, NONE, NONE, NONE, NONE), - MFPR_910(GPIO27, 0x148, GPIO, GSSP, NONE, NONE, NONE, NONE, NONE, NONE), - MFPR_910(GPIO28, 0x14C, GPIO, GSSP, NONE, NONE, NONE, NONE, NONE, NONE), - MFPR_910(GPIO29, 0x150, GPIO, UART0, NONE, NONE, UART1, NONE, NONE, NONE), - MFPR_910(GPIO30, 0x154, GPIO, UART0, NONE, NONE, UART1, NONE, NONE, NONE), - MFPR_910(GPIO31, 0x158, GPIO, UART0, NONE, NONE, UART1, NONE, NONE, NONE), - MFPR_910(GPIO32, 0x15C, GPIO, UART0, DAC_ST23, NONE, UART1, NONE, NONE, NONE), - MFPR_910(GPIO33, 0x160, GPIO, MMC2, SSP0, SSP2, NONE, SPI, NONE, MMC3), - MFPR_910(GPIO34, 0x164, GPIO, MMC2, SSP0, SSP2, NONE, SPI, NONE, MMC3), - MFPR_910(GPIO35, 0x168, GPIO, MMC2, SSP0, SSP2, NONE, SPI, NONE, MMC3), - MFPR_910(GPIO36, 0x16C, GPIO, MMC2, SSP0, SSP2, NONE, SPI, NONE, MMC3), - MFPR_910(GPIO37, 0x170, GPIO, MMC2, NONE, NONE, NONE, SPI, HSL, NONE), - MFPR_910(GPIO38, 0x174, GPIO, MMC2, NONE, NONE, NONE, NONE, HSL, NONE), - MFPR_910(GPIO39, 0x178, GPIO, MMC2, NONE, NONE, NONE, NONE, HSL, NONE), - MFPR_910(GPIO40, 0x17C, GPIO, MMC2, NONE, NONE, NONE, NONE, HSL, NONE), - MFPR_910(GPIO41, 0x180, GPIO, MMC2, NONE, NONE, NONE, NONE, HSL, NONE), - MFPR_910(GPIO42, 0x184, GPIO, MMC2, NONE, NONE, NONE, NONE, HSL, NONE), - MFPR_910(GPIO43, 0x188, GPIO, UART1, NONE, DAC_ST23, NONE, DSSP2, SPI, UART2), - MFPR_910(GPIO44, 0x18C, GPIO, UART1, NONE, EXT_INT, NONE, DSSP2, SPI, UART2), - MFPR_910(GPIO45, 0x190, GPIO, UART1, NONE, EXT_INT, NONE, DSSP2, SPI, UART2), - MFPR_910(GPIO46, 0x194, GPIO, UART1, NONE, EXT_INT, NONE, DSSP2, SPI, UART2), - MFPR_910(GPIO47, 0x198, GPIO, SSP0, NONE, NONE, NONE, SSP2, UART1, NONE), - MFPR_910(GPIO48, 0x19C, GPIO, SSP0, NONE, NONE, NONE, SSP2, UART1, NONE), - MFPR_910(GPIO49, 0x1A0, GPIO, SSP0, UART0, VCXO_REQ, NONE, SSP2, NONE, MMC3), - MFPR_910(GPIO50, 0x1A4, GPIO, SSP0, UART0, VCXO_OUT, NONE, SSP2, NONE, MMC3), - MFPR_910(GPIO51, 0x1A8, GPIO, UART2, PWM1, TWSI, SSP0, NONE, DSSP3, NONE), - MFPR_910(GPIO52, 0x1AC, GPIO, UART2, DAC_ST23, TWSI, SSP0, NONE, DSSP3, NONE), - MFPR_910(GPIO53, 0x1B0, GPIO, UART2, TWSI, NONE, SSP0, NONE, DSSP3, NONE), - MFPR_910(GPIO54, 0x1B4, GPIO, UART2, TWSI, SSP0, NONE, NONE, DSSP3, NONE), - MFPR_910(GPIO55, 0x2F0, TDS, GPIO, TB, NONE, NONE, NONE, NONE, NONE), - MFPR_910(GPIO56, 0x2F4, TDS, GPIO, TB, NONE, NONE, NONE, NONE, NONE), - MFPR_910(GPIO57, 0x2F8, TDS, GPIO, TB, NONE, NONE, NONE, NONE, NONE), - MFPR_910(GPIO58, 0x2FC, TDS, GPIO, TB, NONE, NONE, NONE, NONE, NONE), - MFPR_910(GPIO59, 0x300, TDS, GPIO, TCU_GPOA, TCU_GPOB, ONE_WIRE, NONE, NONE, NONE), - MFPR_910(GPIO60, 0x304, GPIO, NONE, NONE, NONE, NONE, NONE, NONE, NONE), - MFPR_910(GPIO61, 0x308, GPIO, NONE, NONE, NONE, NONE, NONE, NONE, HSL), - MFPR_910(GPIO62, 0x30C, GPIO, NONE, NONE, NONE, NONE, NONE, NONE, HSL), - MFPR_910(GPIO63, 0x310, GPIO, NONE, NONE, NONE, NONE, NONE, NONE, HSL), - MFPR_910(GPIO64, 0x314, GPIO, SPI2, NONE, NONE, NONE, NONE, NONE, HSL), - MFPR_910(GPIO65, 0x318, GPIO, SPI2, NONE, NONE, NONE, NONE, ONE_WIRE, HSL), - MFPR_910(GPIO66, 0x31C, GPIO, NONE, NONE, NONE, NONE, NONE, NONE, HSL), - MFPR_910(GPIO67, 0x1B8, GPIO, CCIC, SPI, NONE, NONE, ULPI, NONE, USIM2), - MFPR_910(GPIO68, 0x1BC, GPIO, CCIC, SPI, NONE, NONE, ULPI, NONE, USIM2), - MFPR_910(GPIO69, 0x1C0, GPIO, CCIC, SPI, NONE, NONE, ULPI, NONE, USIM2), - MFPR_910(GPIO70, 0x1C4, GPIO, CCIC, SPI, NONE, NONE, ULPI, NONE, NONE), - MFPR_910(GPIO71, 0x1C8, GPIO, CCIC, SPI, NONE, NONE, ULPI, NONE, NONE), - MFPR_910(GPIO72, 0x1CC, GPIO, CCIC, EXT_DMA, NONE, NONE, ULPI, NONE, NONE), - MFPR_910(GPIO73, 0x1D0, GPIO, CCIC, EXT_DMA, NONE, NONE, ULPI, NONE, NONE), - MFPR_910(GPIO74, 0x1D4, GPIO, CCIC, EXT_DMA, NONE, NONE, ULPI, NONE, NONE), - MFPR_910(GPIO75, 0x1D8, GPIO, CCIC, NONE, NONE, NONE, ULPI, NONE, NONE), - MFPR_910(GPIO76, 0x1DC, GPIO, CCIC, NONE, NONE, NONE, ULPI, NONE, NONE), - MFPR_910(GPIO77, 0x1E0, GPIO, CCIC, NONE, NONE, NONE, ULPI, NONE, NONE), - MFPR_910(GPIO78, 0x1E4, GPIO, CCIC, NONE, NONE, NONE, ULPI, NONE, NONE), - MFPR_910(GPIO79, 0x1E8, GPIO, TWSI, NONE, NONE, NONE, NONE, NONE, NONE), - MFPR_910(GPIO80, 0x1EC, GPIO, TWSI, NONE, NONE, NONE, NONE, NONE, NONE), - MFPR_910(GPIO81, 0x1F0, GPIO, LCD, NONE, NONE, NONE, NONE, NONE, NONE), - MFPR_910(GPIO82, 0x1F4, GPIO, LCD, NONE, NONE, NONE, NONE, NONE, NONE), - MFPR_910(GPIO83, 0x1F8, GPIO, LCD, NONE, NONE, NONE, NONE, NONE, NONE), - MFPR_910(GPIO84, 0x1FC, GPIO, LCD, VCXO_REQ2, NONE, NONE, NONE, NONE, NONE), - MFPR_910(GPIO85, 0x200, GPIO, LCD, NONE, NONE, NONE, NONE, NONE, NONE), - MFPR_910(GPIO86, 0x204, GPIO, LCD, VCXO_OUT2, NONE, NONE, NONE, NONE, NONE), - MFPR_910(GPIO87, 0x208, GPIO, LCD, NONE, NONE, NONE, NONE, NONE, NONE), - MFPR_910(GPIO88, 0x20C, GPIO, LCD, NONE, NONE, NONE, NONE, NONE, NONE), - MFPR_910(GPIO89, 0x210, GPIO, LCD, NONE, NONE, NONE, NONE, NONE, NONE), - MFPR_910(GPIO90, 0x214, GPIO, LCD, NONE, NONE, NONE, NONE, NONE, NONE), - MFPR_910(GPIO91, 0x218, GPIO, LCD, NONE, NONE, NONE, NONE, NONE, NONE), - MFPR_910(GPIO92, 0x21C, GPIO, LCD, NONE, NONE, NONE, NONE, NONE, NONE), - MFPR_910(GPIO93, 0x220, GPIO, LCD, NONE, NONE, NONE, NONE, NONE, NONE), - MFPR_910(GPIO94, 0x224, GPIO, LCD, NONE, NONE, NONE, NONE, NONE, NONE), - MFPR_910(GPIO95, 0x228, GPIO, LCD, NONE, NONE, NONE, NONE, NONE, NONE), - MFPR_910(GPIO96, 0x22C, GPIO, LCD, NONE, NONE, NONE, NONE, NONE, NONE), - MFPR_910(GPIO97, 0x230, GPIO, LCD, NONE, NONE, NONE, NONE, NONE, NONE), - MFPR_910(GPIO98, 0x234, GPIO, LCD, NONE, NONE, NONE, NONE, NONE, NONE), - MFPR_910(GPIO99, 0x0B0, MMC1, GPIO, NONE, NONE, NONE, NONE, NONE, NONE), - MFPR_910(GPIO100, 0x238, GPIO, LCD, NONE, NONE, NONE, NONE, NONE, NONE), - MFPR_910(GPIO101, 0x23C, GPIO, LCD, NONE, NONE, NONE, NONE, NONE, NONE), - MFPR_910(GPIO102, 0x240, GPIO, LCD, DSSP2, SPI, NONE, NONE, NONE, SPI2), - MFPR_910(GPIO103, 0x244, GPIO, LCD, DSSP2, SPI, NONE, NONE, NONE, SPI2), - MFPR_910(GPIO104, 0x248, GPIO, LCD, DSSP2, SPI, NONE, NONE, NONE, NONE), - MFPR_910(GPIO105, 0x24C, GPIO, LCD, DSSP2, SPI, NONE, NONE, NONE, NONE), - MFPR_910(GPIO106, 0x250, GPIO, LCD, DSSP3, ONE_WIRE, NONE, NONE, NONE, NONE), - MFPR_910(GPIO107, 0x254, GPIO, LCD, DSSP3, SPI, NONE, NONE, NONE, NONE), - MFPR_910(GPIO108, 0x258, GPIO, LCD, DSSP3, SPI, NONE, NONE, NONE, NONE), - MFPR_910(GPIO109, 0x25C, GPIO, LCD, DSSP3, SPI, NONE, NONE, NONE, NONE), - MFPR_910(GPIO110, 0x298, GPIO, NONE, NONE, NONE, NONE, NONE, NONE, NONE), - MFPR_910(GPIO111, 0x29C, GPIO, NONE, DSSP2, NONE, NONE, NONE, NONE, NONE), - MFPR_910(GPIO112, 0x2A0, GPIO, NONE, DSSP2, NONE, NONE, NONE, NONE, NONE), - MFPR_910(GPIO113, 0x2A4, GPIO, NONE, DSSP2, NONE, NONE, NONE, NONE, NONE), - MFPR_910(GPIO114, 0x2A8, GPIO, NONE, DSSP3, NONE, NONE, NONE, NONE, NONE), - MFPR_910(GPIO115, 0x2AC, GPIO, NONE, DSSP3, NONE, NONE, NONE, NONE, NONE), - MFPR_910(GPIO116, 0x2B0, GPIO, NONE, DSSP3, NONE, NONE, NONE, NONE, NONE), - MFPR_910(GPIO117, 0x0B4, PRI_JTAG, GPIO, PWM0, NONE, NONE, NONE, NONE, NONE), - MFPR_910(GPIO118, 0x0B8, PRI_JTAG, GPIO, PWM1, NONE, NONE, NONE, NONE, NONE), - MFPR_910(GPIO119, 0x0BC, PRI_JTAG, GPIO, PWM2, NONE, NONE, NONE, NONE, NONE), - MFPR_910(GPIO120, 0x0C0, PRI_JTAG, GPIO, PWM3, NONE, NONE, NONE, NONE, NONE), - MFPR_910(GPIO121, 0x32C, GPIO, NONE, NONE, NONE, NONE, NONE, NONE, NONE), - MFPR_910(GPIO122, 0x0C8, RESET, GPIO, 32K_CLK, NONE, NONE, NONE, NONE, NONE), - MFPR_910(GPIO123, 0x0CC, CLK_REQ, GPIO, ONE_WIRE, EXT_DMA, NONE, NONE, NONE, NONE), - MFPR_910(GPIO124, 0x0D0, GPIO, MN_CLK, DAC_ST23, NONE, NONE, NONE, NONE, NONE), - MFPR_910(GPIO125, 0x0D4, VCXO_REQ, GPIO, NONE, EXT_INT, NONE, NONE, NONE, NONE), - MFPR_910(GPIO126, 0x06C, GPIO, SMC, NONE, SM_ADDR18, NONE, EXT_DMA, NONE, NONE), - MFPR_910(GPIO127, 0x070, GPIO, SMC, NONE, NONE, NONE, NONE, NONE, NONE), - MFPR_910(ND_IO15, 0x004, NAND, GPIO, USIM2, EXT_DMA, NONE, NONE, NONE, NONE), - MFPR_910(ND_IO14, 0x008, NAND, GPIO, USIM2, NONE, NONE, NONE, NONE, NONE), - MFPR_910(ND_IO13, 0x00C, NAND, GPIO, USIM2, EXT_INT, NONE, NONE, NONE, NONE), - MFPR_910(ND_IO12, 0x010, NAND, GPIO, SSP2, EXT_INT, NONE, NONE, NONE, NONE), - MFPR_910(ND_IO11, 0x014, NAND, GPIO, SSP2, NONE, NONE, NONE, NONE, NONE), - MFPR_910(ND_IO10, 0x018, NAND, GPIO, SSP2, NONE, NONE, NONE, NONE, NONE), - MFPR_910(ND_IO9, 0x01C, NAND, GPIO, SSP2, NONE, VCXO_OUT2, NONE, NONE, NONE), - MFPR_910(ND_IO8, 0x020, NAND, GPIO, NONE, NONE, PWM3, NONE, NONE, NONE), - MFPR_910(ND_IO7, 0x024, NAND, MMC3, NONE, NONE, NONE, NONE, NONE, NONE), - MFPR_910(ND_IO6, 0x028, NAND, MMC3, NONE, NONE, NONE, NONE, NONE, NONE), - MFPR_910(ND_IO5, 0x02C, NAND, MMC3, NONE, NONE, NONE, NONE, NONE, NONE), - MFPR_910(ND_IO4, 0x030, NAND, MMC3, NONE, NONE, NONE, NONE, NONE, NONE), - MFPR_910(ND_IO3, 0x034, NAND, MMC3, NONE, NONE, NONE, NONE, NONE, NONE), - MFPR_910(ND_IO2, 0x038, NAND, MMC3, NONE, NONE, NONE, NONE, NONE, NONE), - MFPR_910(ND_IO1, 0x03C, NAND, MMC3, NONE, NONE, NONE, NONE, NONE, NONE), - MFPR_910(ND_IO0, 0x040, NAND, MMC3, NONE, NONE, NONE, NONE, NONE, NONE), - MFPR_910(ND_NCS0, 0x044, NAND, GPIO, NONE, NONE, NONE, NONE, NONE, NONE), - MFPR_910(ND_NCS1, 0x048, NAND, GPIO, NONE, NONE, NONE, NONE, NONE, NONE), - MFPR_910(SM_NCS0, 0x04C, SMC, GPIO, NONE, NONE, NONE, NONE, NONE, NONE), - MFPR_910(SM_NCS1, 0x050, SMC, GPIO, NONE, NONE, NONE, NONE, NONE, NONE), - MFPR_910(ND_NWE, 0x054, GPIO, NAND, NONE, SM_ADDR20, NONE, SMC, NONE, NONE), - MFPR_910(ND_NRE, 0x058, GPIO, NAND, NONE, SMC, NONE, EXT_DMA, NONE, NONE), - MFPR_910(ND_CLE, 0x05C, NAND, MMC3, NONE, NONE, NONE, NONE, NONE, NONE), - MFPR_910(ND_ALE, 0x060, GPIO, NAND, NONE, NONE, NONE, NONE, NONE, NONE), - MFPR_910(SM_SCLK, 0x064, MMC3, NONE, NONE, NONE, NONE, NONE, NONE, NONE), - MFPR_910(ND_RDY0, 0x068, NAND, GPIO, NONE, SMC, NONE, NONE, NONE, NONE), - MFPR_910(SM_ADV, 0x074, SMC, GPIO, NONE, NONE, NONE, NONE, NONE, NONE), - MFPR_910(ND_RDY1, 0x078, NAND, GPIO, NONE, SMC, NONE, NONE, NONE, NONE), - MFPR_910(SM_ADVMUX, 0x07C, SMC, GPIO, NONE, SM_ADDR19, NONE, NONE, NONE, NONE), - MFPR_910(SM_RDY, 0x080, SMC, GPIO, NONE, NONE, NONE, NONE, NONE, NONE), - MFPR_910(MMC1_DAT7, 0x084, MMC1, GPIO, SEC1_JTAG, TB, NONE, NONE, NONE, NONE), - MFPR_910(MMC1_DAT6, 0x088, MMC1, GPIO, SEC1_JTAG, TB, NONE, NONE, NONE, NONE), - MFPR_910(MMC1_DAT5, 0x08C, MMC1, GPIO, SEC1_JTAG, TB, NONE, NONE, NONE, NONE), - MFPR_910(MMC1_DAT4, 0x090, MMC1, GPIO, NONE, TB, NONE, NONE, NONE, NONE), - MFPR_910(MMC1_DAT3, 0x094, MMC1, HSL, SEC2_JTAG, SSP0, NONE, NONE, NONE, NONE), - MFPR_910(MMC1_DAT2, 0x098, MMC1, HSL, SEC2_JTAG, SSP2, SSP0, NONE, NONE, NONE), - MFPR_910(MMC1_DAT1, 0x09C, MMC1, HSL, SEC2_JTAG, SSP2, SSP0, NONE, NONE, NONE), - MFPR_910(MMC1_DAT0, 0x0A0, MMC1, HSL, SEC2_JTAG, SSP2, NONE, NONE, NONE, NONE), - MFPR_910(MMC1_CMD, 0x0A4, MMC1, HSL, SEC1_JTAG, SSP2, NONE, NONE, NONE, NONE), - MFPR_910(MMC1_CLK, 0x0A8, MMC1, HSL, SEC2_JTAG, SSP0, NONE, NONE, NONE, NONE), - MFPR_910(MMC1_CD, 0x0AC, MMC1, GPIO, SEC1_JTAG, NONE, NONE, NONE, NONE, NONE), - MFPR_910(VCXO_OUT, 0x0D8, VCXO_OUT, PWM3, NONE, NONE, NONE, NONE, NONE, NONE), -}; - - -static const unsigned p910_usim2_pin1[] = {GPIO67, GPIO68, GPIO69}; -static const unsigned p910_usim2_pin2[] = {ND_IO15, ND_IO14, ND_IO13}; -static const unsigned p910_mmc1_pin1[] = {MMC1_DAT7, MMC1_DAT6, MMC1_DAT5, - MMC1_DAT4, MMC1_DAT3, MMC1_DAT2, MMC1_DAT1, MMC1_DAT0, MMC1_CMD, - MMC1_CLK, MMC1_CD, GPIO99}; -static const unsigned p910_mmc2_pin1[] = {GPIO33, GPIO34, GPIO35, GPIO36, - GPIO37, GPIO38, GPIO39, GPIO40, GPIO41, GPIO42}; -static const unsigned p910_mmc3_pin1[] = {GPIO33, GPIO34, GPIO35, GPIO36, - GPIO49, GPIO50}; -static const unsigned p910_mmc3_pin2[] = {ND_IO7, ND_IO6, ND_IO5, ND_IO4, - ND_IO3, ND_IO2, ND_IO1, ND_IO0, ND_CLE, SM_SCLK}; -static const unsigned p910_uart0_pin1[] = {GPIO29, GPIO30, GPIO31, GPIO32}; -static const unsigned p910_uart1_pin1[] = {GPIO47, GPIO48}; -static const unsigned p910_uart1_pin2[] = {GPIO31, GPIO32}; -static const unsigned p910_uart1_pin3[] = {GPIO45, GPIO46}; -static const unsigned p910_uart1_pin4[] = {GPIO29, GPIO30, GPIO31, GPIO32}; -static const unsigned p910_uart1_pin5[] = {GPIO43, GPIO44, GPIO45, GPIO46}; -static const unsigned p910_uart2_pin1[] = {GPIO43, GPIO44}; -static const unsigned p910_uart2_pin2[] = {GPIO51, GPIO52}; -static const unsigned p910_uart2_pin3[] = {GPIO43, GPIO44, GPIO45, GPIO46}; -static const unsigned p910_uart2_pin4[] = {GPIO51, GPIO52, GPIO53, GPIO54}; -static const unsigned p910_twsi_pin1[] = {GPIO51, GPIO52}; -static const unsigned p910_twsi_pin2[] = {GPIO53, GPIO54}; -static const unsigned p910_twsi_pin3[] = {GPIO79, GPIO80}; -static const unsigned p910_ccic_pin1[] = {GPIO67, GPIO68, GPIO69, GPIO70, - GPIO71, GPIO72, GPIO73, GPIO74, GPIO75, GPIO76, GPIO77, GPIO78}; -static const unsigned p910_lcd_pin1[] = {GPIO81, GPIO82, GPIO83, GPIO84, - GPIO85, GPIO86, GPIO87, GPIO88, GPIO89, GPIO90, GPIO91, GPIO92, - GPIO93, GPIO94, GPIO95, GPIO96, GPIO97, GPIO98, GPIO100, GPIO101, - GPIO102, GPIO103}; -static const unsigned p910_spi_pin1[] = {GPIO104, GPIO105, GPIO107, GPIO108}; -static const unsigned p910_spi_pin2[] = {GPIO43, GPIO44, GPIO45, GPIO46}; -static const unsigned p910_spi_pin3[] = {GPIO33, GPIO34, GPIO35, GPIO36, - GPIO37}; -static const unsigned p910_spi_pin4[] = {GPIO67, GPIO68, GPIO69, GPIO70, - GPIO71}; -static const unsigned p910_spi2_pin1[] = {GPIO64, GPIO65}; -static const unsigned p910_spi2_pin2[] = {GPIO102, GPIO103}; -static const unsigned p910_dssp2_pin1[] = {GPIO102, GPIO103, GPIO104, GPIO105}; -static const unsigned p910_dssp2_pin2[] = {GPIO43, GPIO44, GPIO45, GPIO46}; -static const unsigned p910_dssp2_pin3[] = {GPIO111, GPIO112, GPIO113}; -static const unsigned p910_dssp3_pin1[] = {GPIO106, GPIO107, GPIO108, GPIO109}; -static const unsigned p910_dssp3_pin2[] = {GPIO51, GPIO52, GPIO53, GPIO54}; -static const unsigned p910_dssp3_pin3[] = {GPIO114, GPIO115, GPIO116}; -static const unsigned p910_ssp0_pin1[] = {MMC1_DAT3, MMC1_DAT2, MMC1_DAT1, - MMC1_CLK}; -static const unsigned p910_ssp0_pin2[] = {GPIO33, GPIO34, GPIO35, GPIO36}; -static const unsigned p910_ssp0_pin3[] = {GPIO47, GPIO48, GPIO49, GPIO50}; -static const unsigned p910_ssp0_pin4[] = {GPIO51, GPIO52, GPIO53, GPIO54}; -static const unsigned p910_ssp1_pin1[] = {GPIO21, GPIO22, GPIO23, GPIO24}; -static const unsigned p910_ssp1_pin2[] = {GPIO20, GPIO21, GPIO22, GPIO23, - GPIO24}; -static const unsigned p910_ssp2_pin1[] = {MMC1_DAT2, MMC1_DAT1, MMC1_DAT0, - MMC1_CMD}; -static const unsigned p910_ssp2_pin2[] = {GPIO33, GPIO34, GPIO35, GPIO36}; -static const unsigned p910_ssp2_pin3[] = {GPIO47, GPIO48, GPIO49, GPIO50}; -static const unsigned p910_ssp2_pin4[] = {ND_IO12, ND_IO11, ND_IO10, ND_IO9}; -static const unsigned p910_gssp_pin1[] = {GPIO25, GPIO26, GPIO27, GPIO28}; -static const unsigned p910_pwm0_pin1[] = {GPIO117}; -static const unsigned p910_pwm1_pin1[] = {GPIO118}; -static const unsigned p910_pwm1_pin2[] = {GPIO51}; -static const unsigned p910_pwm2_pin1[] = {GPIO119}; -static const unsigned p910_pwm3_pin1[] = {GPIO120}; -static const unsigned p910_pwm3_pin2[] = {ND_IO8}; -static const unsigned p910_pwm3_pin3[] = {VCXO_OUT}; -static const unsigned p910_pri_jtag_pin1[] = {GPIO117, GPIO118, GPIO119, - GPIO120}; -static const unsigned p910_sec1_jtag_pin1[] = {MMC1_DAT7, MMC1_DAT6, MMC1_DAT5, - MMC1_CMD, MMC1_CD}; -static const unsigned p910_sec2_jtag_pin1[] = {MMC1_DAT3, MMC1_DAT2, MMC1_DAT1, - MMC1_DAT0, MMC1_CLK}; -static const unsigned p910_hsl_pin1[] = {GPIO37, GPIO38, GPIO39, GPIO40, - GPIO41, GPIO42}; -static const unsigned p910_hsl_pin2[] = {GPIO61, GPIO62, GPIO63, GPIO64, - GPIO65, GPIO66}; -static const unsigned p910_hsl_pin3[] = {MMC1_DAT3, MMC1_DAT2, MMC1_DAT1, - MMC1_DAT0, MMC1_CMD, MMC1_CLK}; -static const unsigned p910_w1_pin1[] = {GPIO59}; -static const unsigned p910_w1_pin2[] = {GPIO65}; -static const unsigned p910_w1_pin3[] = {GPIO106}; -static const unsigned p910_w1_pin4[] = {GPIO123}; -static const unsigned p910_kpmk_pin1[] = {GPIO0, GPIO1, GPIO2, GPIO3, GPIO4, - GPIO5, GPIO6, GPIO7, GPIO8, GPIO9, GPIO10, GPIO11, GPIO12, GPIO13, - GPIO14, GPIO15}; -static const unsigned p910_kpmk_pin2[] = {GPIO0, GPIO1, GPIO2, GPIO3, GPIO4, - GPIO5, GPIO6, GPIO7, GPIO8, GPIO9, GPIO12}; -static const unsigned p910_kpdk_pin1[] = {GPIO12, GPIO13, GPIO14, GPIO15, - GPIO16, GPIO17, GPIO18, GPIO19}; -static const unsigned p910_tds_pin1[] = {GPIO55, GPIO56, GPIO57, GPIO58, - GPIO59}; -static const unsigned p910_tds_pin2[] = {GPIO55, GPIO57, GPIO58, GPIO59}; -static const unsigned p910_tb_pin1[] = {GPIO14, GPIO15, GPIO16, GPIO17}; -static const unsigned p910_tb_pin2[] = {GPIO55, GPIO56, GPIO57, GPIO58}; -static const unsigned p910_tb_pin3[] = {MMC1_DAT7, MMC1_DAT6, MMC1_DAT5, - MMC1_DAT4}; -static const unsigned p910_ext_dma0_pin1[] = {GPIO72}; -static const unsigned p910_ext_dma0_pin2[] = {ND_IO15}; -static const unsigned p910_ext_dma0_pin3[] = {ND_NRE}; -static const unsigned p910_ext_dma1_pin1[] = {GPIO73}; -static const unsigned p910_ext_dma1_pin2[] = {GPIO123}; -static const unsigned p910_ext_dma1_pin3[] = {GPIO126}; -static const unsigned p910_ext_dma2_pin1[] = {GPIO74}; -static const unsigned p910_ext0_int_pin1[] = {GPIO44}; -static const unsigned p910_ext0_int_pin2[] = {ND_IO13}; -static const unsigned p910_ext1_int_pin1[] = {GPIO45}; -static const unsigned p910_ext1_int_pin2[] = {ND_IO12}; -static const unsigned p910_ext2_int_pin1[] = {GPIO46}; -static const unsigned p910_ext2_int_pin2[] = {GPIO125}; -static const unsigned p910_dac_st23_pin1[] = {GPIO32}; -static const unsigned p910_dac_st23_pin2[] = {GPIO43}; -static const unsigned p910_dac_st23_pin3[] = {GPIO52}; -static const unsigned p910_dac_st23_pin4[] = {GPIO124}; -static const unsigned p910_vcxo_out_pin1[] = {GPIO50}; -static const unsigned p910_vcxo_out_pin2[] = {VCXO_OUT}; -static const unsigned p910_vcxo_out_pin3[] = {GPIO20}; -static const unsigned p910_vcxo_req_pin1[] = {GPIO49}; -static const unsigned p910_vcxo_req_pin2[] = {GPIO125}; -static const unsigned p910_vcxo_out2_pin1[] = {GPIO86}; -static const unsigned p910_vcxo_out2_pin2[] = {ND_IO9}; -static const unsigned p910_vcxo_req2_pin1[] = {GPIO84}; -static const unsigned p910_ulpi_pin1[] = {GPIO67, GPIO68, GPIO69, GPIO70, - GPIO71, GPIO72, GPIO73, GPIO74, GPIO75, GPIO76, GPIO77, GPIO78}; -static const unsigned p910_nand_pin1[] = {ND_IO15, ND_IO14, ND_IO13, ND_IO12, - ND_IO11, ND_IO10, ND_IO9, ND_IO8, ND_IO7, ND_IO6, ND_IO5, ND_IO4, - ND_IO3, ND_IO2, ND_IO1, ND_IO0, ND_NCS0, ND_NWE, ND_NRE, ND_CLE, - ND_ALE, ND_RDY0}; -static const unsigned p910_gpio0_pin1[] = {GPIO0}; -static const unsigned p910_gpio0_pin2[] = {SM_ADV}; -static const unsigned p910_gpio1_pin1[] = {GPIO1}; -static const unsigned p910_gpio1_pin2[] = {ND_RDY1}; -static const unsigned p910_gpio2_pin1[] = {GPIO2}; -static const unsigned p910_gpio2_pin2[] = {SM_ADVMUX}; -static const unsigned p910_gpio3_pin1[] = {GPIO3}; -static const unsigned p910_gpio3_pin2[] = {SM_RDY}; -static const unsigned p910_gpio20_pin1[] = {GPIO20}; -static const unsigned p910_gpio20_pin2[] = {ND_IO15}; -static const unsigned p910_gpio20_pin3[] = {MMC1_DAT6}; -static const unsigned p910_gpio21_pin1[] = {GPIO21}; -static const unsigned p910_gpio21_pin2[] = {ND_IO14}; -static const unsigned p910_gpio21_pin3[] = {MMC1_DAT5}; -static const unsigned p910_gpio22_pin1[] = {GPIO22}; -static const unsigned p910_gpio22_pin2[] = {ND_IO13}; -static const unsigned p910_gpio22_pin3[] = {MMC1_DAT4}; -static const unsigned p910_gpio23_pin1[] = {GPIO23}; -static const unsigned p910_gpio23_pin2[] = {ND_IO12}; -static const unsigned p910_gpio23_pin3[] = {MMC1_CD}; -static const unsigned p910_gpio24_pin1[] = {GPIO24}; -static const unsigned p910_gpio24_pin2[] = {ND_IO11}; -static const unsigned p910_gpio24_pin3[] = {MMC1_DAT7}; -static const unsigned p910_gpio25_pin1[] = {GPIO25}; -static const unsigned p910_gpio25_pin2[] = {ND_IO10}; -static const unsigned p910_gpio26_pin1[] = {GPIO26}; -static const unsigned p910_gpio26_pin2[] = {ND_IO9}; -static const unsigned p910_gpio27_pin1[] = {GPIO27}; -static const unsigned p910_gpio27_pin2[] = {ND_IO8}; -static const unsigned p910_gpio85_pin1[] = {GPIO85}; -static const unsigned p910_gpio85_pin2[] = {ND_NCS0}; -static const unsigned p910_gpio86_pin1[] = {GPIO86}; -static const unsigned p910_gpio86_pin2[] = {ND_NCS1}; -static const unsigned p910_gpio87_pin1[] = {GPIO87}; -static const unsigned p910_gpio87_pin2[] = {SM_NCS0}; -static const unsigned p910_gpio88_pin1[] = {GPIO88}; -static const unsigned p910_gpio88_pin2[] = {SM_NCS1}; -static const unsigned p910_gpio89_pin1[] = {GPIO89}; -static const unsigned p910_gpio89_pin2[] = {ND_NWE}; -static const unsigned p910_gpio90_pin1[] = {GPIO90}; -static const unsigned p910_gpio90_pin2[] = {ND_NRE}; -static const unsigned p910_gpio91_pin1[] = {GPIO91}; -static const unsigned p910_gpio91_pin2[] = {ND_ALE}; -static const unsigned p910_gpio92_pin1[] = {GPIO92}; -static const unsigned p910_gpio92_pin2[] = {ND_RDY0}; - -static struct pxa3xx_pin_group pxa910_grps[] = { - GRP_910("usim2 3p1", USIM2, p910_usim2_pin1), - GRP_910("usim2 3p2", USIM2, p910_usim2_pin2), - GRP_910("mmc1 12p", MMC1, p910_mmc1_pin1), - GRP_910("mmc2 10p", MMC2, p910_mmc2_pin1), - GRP_910("mmc3 6p", MMC3, p910_mmc3_pin1), - GRP_910("mmc3 10p", MMC3, p910_mmc3_pin2), - GRP_910("uart0 4p", UART0, p910_uart0_pin1), - GRP_910("uart1 2p1", UART1, p910_uart1_pin1), - GRP_910("uart1 2p2", UART1, p910_uart1_pin2), - GRP_910("uart1 2p3", UART1, p910_uart1_pin3), - GRP_910("uart1 4p4", UART1, p910_uart1_pin4), - GRP_910("uart1 4p5", UART1, p910_uart1_pin5), - GRP_910("uart2 2p1", UART2, p910_uart2_pin1), - GRP_910("uart2 2p2", UART2, p910_uart2_pin2), - GRP_910("uart2 4p3", UART2, p910_uart2_pin3), - GRP_910("uart2 4p4", UART2, p910_uart2_pin4), - GRP_910("twsi 2p1", TWSI, p910_twsi_pin1), - GRP_910("twsi 2p2", TWSI, p910_twsi_pin2), - GRP_910("twsi 2p3", TWSI, p910_twsi_pin3), - GRP_910("ccic", CCIC, p910_ccic_pin1), - GRP_910("lcd", LCD, p910_lcd_pin1), - GRP_910("spi 4p1", SPI, p910_spi_pin1), - GRP_910("spi 4p2", SPI, p910_spi_pin2), - GRP_910("spi 5p3", SPI, p910_spi_pin3), - GRP_910("spi 5p4", SPI, p910_spi_pin4), - GRP_910("dssp2 4p1", DSSP2, p910_dssp2_pin1), - GRP_910("dssp2 4p2", DSSP2, p910_dssp2_pin2), - GRP_910("dssp2 3p3", DSSP2, p910_dssp2_pin3), - GRP_910("dssp3 4p1", DSSP3, p910_dssp3_pin1), - GRP_910("dssp3 4p2", DSSP3, p910_dssp3_pin2), - GRP_910("dssp3 3p3", DSSP3, p910_dssp3_pin3), - GRP_910("ssp0 4p1", SSP0, p910_ssp0_pin1), - GRP_910("ssp0 4p2", SSP0, p910_ssp0_pin2), - GRP_910("ssp0 4p3", SSP0, p910_ssp0_pin3), - GRP_910("ssp0 4p4", SSP0, p910_ssp0_pin4), - GRP_910("ssp1 4p1", SSP1, p910_ssp1_pin1), - GRP_910("ssp1 5p2", SSP1, p910_ssp1_pin2), - GRP_910("ssp2 4p1", SSP2, p910_ssp2_pin1), - GRP_910("ssp2 4p2", SSP2, p910_ssp2_pin2), - GRP_910("ssp2 4p3", SSP2, p910_ssp2_pin3), - GRP_910("ssp2 4p4", SSP2, p910_ssp2_pin4), - GRP_910("gssp", GSSP, p910_gssp_pin1), - GRP_910("pwm0", PWM0, p910_pwm0_pin1), - GRP_910("pwm1-1", PWM1, p910_pwm1_pin1), - GRP_910("pwm1-2", PWM1, p910_pwm1_pin2), - GRP_910("pwm2", PWM2, p910_pwm2_pin1), - GRP_910("pwm3-1", PWM3, p910_pwm3_pin1), - GRP_910("pwm3-2", PWM3, p910_pwm3_pin2), - GRP_910("pwm3-3", PWM3, p910_pwm3_pin3), - GRP_910("pri jtag", PRI_JTAG, p910_pri_jtag_pin1), - GRP_910("sec1 jtag", SEC1_JTAG, p910_sec1_jtag_pin1), - GRP_910("sec2 jtag", SEC2_JTAG, p910_sec2_jtag_pin1), - GRP_910("hsl 6p1", HSL, p910_hsl_pin1), - GRP_910("hsl 6p2", HSL, p910_hsl_pin2), - GRP_910("hsl 6p3", HSL, p910_hsl_pin3), - GRP_910("w1-1", ONE_WIRE, p910_w1_pin1), - GRP_910("w1-2", ONE_WIRE, p910_w1_pin2), - GRP_910("w1-3", ONE_WIRE, p910_w1_pin3), - GRP_910("w1-4", ONE_WIRE, p910_w1_pin4), - GRP_910("kpmk 16p1", KP_MK, p910_kpmk_pin1), - GRP_910("kpmk 11p2", KP_MK, p910_kpmk_pin2), - GRP_910("kpdk 8p1", KP_DK, p910_kpdk_pin1), - GRP_910("tds 5p1", TDS, p910_tds_pin1), - GRP_910("tds 4p2", TDS, p910_tds_pin2), - GRP_910("tb 4p1", TB, p910_tb_pin1), - GRP_910("tb 4p2", TB, p910_tb_pin2), - GRP_910("tb 4p3", TB, p910_tb_pin3), - GRP_910("ext dma0-1", EXT_DMA, p910_ext_dma0_pin1), - GRP_910("ext dma0-2", EXT_DMA, p910_ext_dma0_pin2), - GRP_910("ext dma0-3", EXT_DMA, p910_ext_dma0_pin3), - GRP_910("ext dma1-1", EXT_DMA, p910_ext_dma1_pin1), - GRP_910("ext dma1-2", EXT_DMA, p910_ext_dma1_pin2), - GRP_910("ext dma1-3", EXT_DMA, p910_ext_dma1_pin3), - GRP_910("ext dma2", EXT_DMA, p910_ext_dma2_pin1), - GRP_910("ext0 int-1", EXT_INT, p910_ext0_int_pin1), - GRP_910("ext0 int-2", EXT_INT, p910_ext0_int_pin2), - GRP_910("ext1 int-1", EXT_INT, p910_ext1_int_pin1), - GRP_910("ext1 int-2", EXT_INT, p910_ext1_int_pin2), - GRP_910("ext2 int-1", EXT_INT, p910_ext2_int_pin1), - GRP_910("ext2 int-2", EXT_INT, p910_ext2_int_pin2), - GRP_910("dac st23-1", DAC_ST23, p910_dac_st23_pin1), - GRP_910("dac st23-2", DAC_ST23, p910_dac_st23_pin2), - GRP_910("dac st23-3", DAC_ST23, p910_dac_st23_pin3), - GRP_910("dac st23-4", DAC_ST23, p910_dac_st23_pin4), - GRP_910("vcxo out-1", VCXO_OUT, p910_vcxo_out_pin1), - GRP_910("vcxo out-2", VCXO_OUT, p910_vcxo_out_pin2), - GRP_910("vcxo out-3", VCXO_OUT, p910_vcxo_out_pin3), - GRP_910("vcxo req-1", VCXO_REQ, p910_vcxo_req_pin1), - GRP_910("vcxo req-2", VCXO_REQ, p910_vcxo_req_pin2), - GRP_910("vcxo out2-1", VCXO_OUT2, p910_vcxo_out2_pin1), - GRP_910("vcxo out2-2", VCXO_OUT2, p910_vcxo_out2_pin2), - GRP_910("vcxo req2", VCXO_REQ2, p910_vcxo_req2_pin1), - GRP_910("ulpi", ULPI, p910_ulpi_pin1), - GRP_910("nand", NAND, p910_nand_pin1), - GRP_910("gpio0-1", GPIO, p910_gpio0_pin1), - GRP_910("gpio0-2", GPIO, p910_gpio0_pin2), - GRP_910("gpio1-1", GPIO, p910_gpio1_pin1), - GRP_910("gpio1-2", GPIO, p910_gpio1_pin2), - GRP_910("gpio2-1", GPIO, p910_gpio2_pin1), - GRP_910("gpio2-2", GPIO, p910_gpio2_pin2), - GRP_910("gpio3-1", GPIO, p910_gpio3_pin1), - GRP_910("gpio3-2", GPIO, p910_gpio3_pin2), - GRP_910("gpio20-1", GPIO, p910_gpio20_pin1), - GRP_910("gpio20-2", GPIO, p910_gpio20_pin2), - GRP_910("gpio21-1", GPIO, p910_gpio21_pin1), - GRP_910("gpio21-2", GPIO, p910_gpio21_pin2), - GRP_910("gpio22-1", GPIO, p910_gpio22_pin1), - GRP_910("gpio22-2", GPIO, p910_gpio22_pin2), - GRP_910("gpio23-1", GPIO, p910_gpio23_pin1), - GRP_910("gpio23-2", GPIO, p910_gpio23_pin2), - GRP_910("gpio24-1", GPIO, p910_gpio24_pin1), - GRP_910("gpio24-2", GPIO, p910_gpio24_pin2), - GRP_910("gpio25-1", GPIO, p910_gpio25_pin1), - GRP_910("gpio25-2", GPIO, p910_gpio25_pin2), - GRP_910("gpio26-1", GPIO, p910_gpio26_pin1), - GRP_910("gpio26-2", GPIO, p910_gpio26_pin2), - GRP_910("gpio27-1", GPIO, p910_gpio27_pin1), - GRP_910("gpio27-2", GPIO, p910_gpio27_pin2), - GRP_910("gpio85-1", GPIO, p910_gpio85_pin1), - GRP_910("gpio85-2", GPIO, p910_gpio85_pin2), - GRP_910("gpio86-1", GPIO, p910_gpio86_pin1), - GRP_910("gpio86-2", GPIO, p910_gpio86_pin2), - GRP_910("gpio87-1", GPIO, p910_gpio87_pin1), - GRP_910("gpio87-2", GPIO, p910_gpio87_pin2), - GRP_910("gpio88-1", GPIO, p910_gpio88_pin1), - GRP_910("gpio88-2", GPIO, p910_gpio88_pin2), - GRP_910("gpio89-1", GPIO, p910_gpio89_pin1), - GRP_910("gpio89-2", GPIO, p910_gpio89_pin2), - GRP_910("gpio90-1", GPIO, p910_gpio90_pin1), - GRP_910("gpio90-2", GPIO, p910_gpio90_pin2), - GRP_910("gpio91-1", GPIO, p910_gpio91_pin1), - GRP_910("gpio91-2", GPIO, p910_gpio91_pin2), - GRP_910("gpio92-1", GPIO, p910_gpio92_pin1), - GRP_910("gpio92-2", GPIO, p910_gpio92_pin2), -}; - -static const char * const p910_usim2_grps[] = {"usim2 3p1", "usim2 3p2"}; -static const char * const p910_mmc1_grps[] = {"mmc1 12p"}; -static const char * const p910_mmc2_grps[] = {"mmc2 10p"}; -static const char * const p910_mmc3_grps[] = {"mmc3 6p", "mmc3 10p"}; -static const char * const p910_uart0_grps[] = {"uart0 4p"}; -static const char * const p910_uart1_grps[] = {"uart1 2p1", "uart1 2p2", - "uart1 2p3", "uart1 4p4", "uart1 4p5"}; -static const char * const p910_uart2_grps[] = {"uart2 2p1", "uart2 2p2", - "uart2 4p3", "uart2 4p4"}; -static const char * const p910_twsi_grps[] = {"twsi 2p1", "twsi 2p2", - "twsi 2p3"}; -static const char * const p910_ccic_grps[] = {"ccic"}; -static const char * const p910_lcd_grps[] = {"lcd"}; -static const char * const p910_spi_grps[] = {"spi 4p1", "spi 4p2", "spi 5p3", - "spi 5p4"}; -static const char * const p910_dssp2_grps[] = {"dssp2 4p1", "dssp2 4p2", - "dssp2 3p3"}; -static const char * const p910_dssp3_grps[] = {"dssp3 4p1", "dssp3 4p2", - "dssp3 3p3"}; -static const char * const p910_ssp0_grps[] = {"ssp0 4p1", "ssp0 4p2", - "ssp0 4p3", "ssp0 4p4"}; -static const char * const p910_ssp1_grps[] = {"ssp1 4p1", "ssp1 5p2"}; -static const char * const p910_ssp2_grps[] = {"ssp2 4p1", "ssp2 4p2", - "ssp2 4p3", "ssp2 4p4"}; -static const char * const p910_gssp_grps[] = {"gssp"}; -static const char * const p910_pwm0_grps[] = {"pwm0"}; -static const char * const p910_pwm1_grps[] = {"pwm1-1", "pwm1-2"}; -static const char * const p910_pwm2_grps[] = {"pwm2"}; -static const char * const p910_pwm3_grps[] = {"pwm3-1", "pwm3-2", "pwm3-3"}; -static const char * const p910_pri_jtag_grps[] = {"pri jtag"}; -static const char * const p910_sec1_jtag_grps[] = {"sec1 jtag"}; -static const char * const p910_sec2_jtag_grps[] = {"sec2 jtag"}; -static const char * const p910_hsl_grps[] = {"hsl 6p1", "hsl 6p2", "hsl 6p3"}; -static const char * const p910_w1_grps[] = {"w1-1", "w1-2", "w1-3", "w1-4"}; -static const char * const p910_kpmk_grps[] = {"kpmk 16p1", "kpmk 11p2"}; -static const char * const p910_kpdk_grps[] = {"kpdk 8p1"}; -static const char * const p910_tds_grps[] = {"tds 5p1", "tds 4p2"}; -static const char * const p910_tb_grps[] = {"tb 4p1", "tb 4p2", "tb 4p3"}; -static const char * const p910_dma0_grps[] = {"ext dma0-1", "ext dma0-2", - "ext dma0-3"}; -static const char * const p910_dma1_grps[] = {"ext dma1-1", "ext dma1-2", - "ext dma1-3"}; -static const char * const p910_dma2_grps[] = {"ext dma2"}; -static const char * const p910_int0_grps[] = {"ext0 int-1", "ext0 int-2"}; -static const char * const p910_int1_grps[] = {"ext1 int-1", "ext1 int-2"}; -static const char * const p910_int2_grps[] = {"ext2 int-1", "ext2 int-2"}; -static const char * const p910_dac_st23_grps[] = {"dac st23-1", "dac st23-2", - "dac st23-3", "dac st23-4"}; -static const char * const p910_vcxo_out_grps[] = {"vcxo out-1", "vcxo out-2", - "vcxo out-3"}; -static const char * const p910_vcxo_req_grps[] = {"vcxo req-1", "vcxo req-2"}; -static const char * const p910_vcxo_out2_grps[] = {"vcxo out2-1", - "vcxo out2-2"}; -static const char * const p910_vcxo_req2_grps[] = {"vcxo req2"}; -static const char * const p910_ulpi_grps[] = {"ulpi"}; -static const char * const p910_nand_grps[] = {"nand"}; -static const char * const p910_gpio0_grps[] = {"gpio0-1", "gpio0-2"}; -static const char * const p910_gpio1_grps[] = {"gpio1-1", "gpio1-2"}; -static const char * const p910_gpio2_grps[] = {"gpio2-1", "gpio2-2"}; -static const char * const p910_gpio3_grps[] = {"gpio3-1", "gpio3-2"}; -static const char * const p910_gpio20_grps[] = {"gpio20-1", "gpio20-2"}; -static const char * const p910_gpio21_grps[] = {"gpio21-1", "gpio21-2"}; -static const char * const p910_gpio22_grps[] = {"gpio22-1", "gpio22-2"}; -static const char * const p910_gpio23_grps[] = {"gpio23-1", "gpio23-2"}; -static const char * const p910_gpio24_grps[] = {"gpio24-1", "gpio24-2"}; -static const char * const p910_gpio25_grps[] = {"gpio25-1", "gpio25-2"}; -static const char * const p910_gpio26_grps[] = {"gpio26-1", "gpio26-2"}; -static const char * const p910_gpio27_grps[] = {"gpio27-1", "gpio27-2"}; -static const char * const p910_gpio85_grps[] = {"gpio85-1", "gpio85-2"}; -static const char * const p910_gpio86_grps[] = {"gpio86-1", "gpio86-2"}; -static const char * const p910_gpio87_grps[] = {"gpio87-1", "gpio87-2"}; -static const char * const p910_gpio88_grps[] = {"gpio88-1", "gpio88-2"}; -static const char * const p910_gpio89_grps[] = {"gpio89-1", "gpio89-2"}; -static const char * const p910_gpio90_grps[] = {"gpio90-1", "gpio90-2"}; -static const char * const p910_gpio91_grps[] = {"gpio91-1", "gpio91-2"}; -static const char * const p910_gpio92_grps[] = {"gpio92-1", "gpio92-2"}; - -static struct pxa3xx_pmx_func pxa910_funcs[] = { - {"usim2", ARRAY_AND_SIZE(p910_usim2_grps)}, - {"mmc1", ARRAY_AND_SIZE(p910_mmc1_grps)}, - {"mmc2", ARRAY_AND_SIZE(p910_mmc2_grps)}, - {"mmc3", ARRAY_AND_SIZE(p910_mmc3_grps)}, - {"uart0", ARRAY_AND_SIZE(p910_uart0_grps)}, - {"uart1", ARRAY_AND_SIZE(p910_uart1_grps)}, - {"uart2", ARRAY_AND_SIZE(p910_uart2_grps)}, - {"twsi", ARRAY_AND_SIZE(p910_twsi_grps)}, - {"ccic", ARRAY_AND_SIZE(p910_ccic_grps)}, - {"lcd", ARRAY_AND_SIZE(p910_lcd_grps)}, - {"spi", ARRAY_AND_SIZE(p910_spi_grps)}, - {"dssp2", ARRAY_AND_SIZE(p910_dssp2_grps)}, - {"dssp3", ARRAY_AND_SIZE(p910_dssp3_grps)}, - {"ssp0", ARRAY_AND_SIZE(p910_ssp0_grps)}, - {"ssp1", ARRAY_AND_SIZE(p910_ssp1_grps)}, - {"ssp2", ARRAY_AND_SIZE(p910_ssp2_grps)}, - {"gssp", ARRAY_AND_SIZE(p910_gssp_grps)}, - {"pwm0", ARRAY_AND_SIZE(p910_pwm0_grps)}, - {"pwm1", ARRAY_AND_SIZE(p910_pwm1_grps)}, - {"pwm2", ARRAY_AND_SIZE(p910_pwm2_grps)}, - {"pwm3", ARRAY_AND_SIZE(p910_pwm3_grps)}, - {"pri_jtag", ARRAY_AND_SIZE(p910_pri_jtag_grps)}, - {"sec1_jtag", ARRAY_AND_SIZE(p910_sec1_jtag_grps)}, - {"sec2_jtag", ARRAY_AND_SIZE(p910_sec2_jtag_grps)}, - {"hsl", ARRAY_AND_SIZE(p910_hsl_grps)}, - {"w1", ARRAY_AND_SIZE(p910_w1_grps)}, - {"kpmk", ARRAY_AND_SIZE(p910_kpmk_grps)}, - {"kpdk", ARRAY_AND_SIZE(p910_kpdk_grps)}, - {"tds", ARRAY_AND_SIZE(p910_tds_grps)}, - {"tb", ARRAY_AND_SIZE(p910_tb_grps)}, - {"dma0", ARRAY_AND_SIZE(p910_dma0_grps)}, - {"dma1", ARRAY_AND_SIZE(p910_dma1_grps)}, - {"dma2", ARRAY_AND_SIZE(p910_dma2_grps)}, - {"int0", ARRAY_AND_SIZE(p910_int0_grps)}, - {"int1", ARRAY_AND_SIZE(p910_int1_grps)}, - {"int2", ARRAY_AND_SIZE(p910_int2_grps)}, - {"dac_st23", ARRAY_AND_SIZE(p910_dac_st23_grps)}, - {"vcxo_out", ARRAY_AND_SIZE(p910_vcxo_out_grps)}, - {"vcxo_req", ARRAY_AND_SIZE(p910_vcxo_req_grps)}, - {"vcxo_out2", ARRAY_AND_SIZE(p910_vcxo_out2_grps)}, - {"vcxo_req2", ARRAY_AND_SIZE(p910_vcxo_req2_grps)}, - {"ulpi", ARRAY_AND_SIZE(p910_ulpi_grps)}, - {"nand", ARRAY_AND_SIZE(p910_nand_grps)}, - {"gpio0", ARRAY_AND_SIZE(p910_gpio0_grps)}, - {"gpio1", ARRAY_AND_SIZE(p910_gpio1_grps)}, - {"gpio2", ARRAY_AND_SIZE(p910_gpio2_grps)}, - {"gpio3", ARRAY_AND_SIZE(p910_gpio3_grps)}, - {"gpio20", ARRAY_AND_SIZE(p910_gpio20_grps)}, - {"gpio21", ARRAY_AND_SIZE(p910_gpio21_grps)}, - {"gpio22", ARRAY_AND_SIZE(p910_gpio22_grps)}, - {"gpio23", ARRAY_AND_SIZE(p910_gpio23_grps)}, - {"gpio24", ARRAY_AND_SIZE(p910_gpio24_grps)}, - {"gpio25", ARRAY_AND_SIZE(p910_gpio25_grps)}, - {"gpio26", ARRAY_AND_SIZE(p910_gpio26_grps)}, - {"gpio27", ARRAY_AND_SIZE(p910_gpio27_grps)}, - {"gpio85", ARRAY_AND_SIZE(p910_gpio85_grps)}, - {"gpio86", ARRAY_AND_SIZE(p910_gpio86_grps)}, - {"gpio87", ARRAY_AND_SIZE(p910_gpio87_grps)}, - {"gpio88", ARRAY_AND_SIZE(p910_gpio88_grps)}, - {"gpio89", ARRAY_AND_SIZE(p910_gpio89_grps)}, - {"gpio90", ARRAY_AND_SIZE(p910_gpio90_grps)}, - {"gpio91", ARRAY_AND_SIZE(p910_gpio91_grps)}, - {"gpio92", ARRAY_AND_SIZE(p910_gpio92_grps)}, -}; - -static struct pinctrl_desc pxa910_pctrl_desc = { - .name = "pxa910-pinctrl", - .owner = THIS_MODULE, -}; - -static struct pxa3xx_pinmux_info pxa910_info = { - .mfp = pxa910_mfp, - .num_mfp = ARRAY_SIZE(pxa910_mfp), - .grps = pxa910_grps, - .num_grps = ARRAY_SIZE(pxa910_grps), - .funcs = pxa910_funcs, - .num_funcs = ARRAY_SIZE(pxa910_funcs), - .num_gpio = 128, - .desc = &pxa910_pctrl_desc, - .pads = pxa910_pads, - .num_pads = ARRAY_SIZE(pxa910_pads), - - .cputype = PINCTRL_PXA910, - .ds_mask = PXA910_DS_MASK, - .ds_shift = PXA910_DS_SHIFT, -}; - -static int pxa910_pinmux_probe(struct platform_device *pdev) -{ - return pxa3xx_pinctrl_register(pdev, &pxa910_info); -} - -static int pxa910_pinmux_remove(struct platform_device *pdev) -{ - return pxa3xx_pinctrl_unregister(pdev); -} - -static struct platform_driver pxa910_pinmux_driver = { - .driver = { - .name = "pxa910-pinmux", - .owner = THIS_MODULE, - }, - .probe = pxa910_pinmux_probe, - .remove = pxa910_pinmux_remove, -}; - -static int __init pxa910_pinmux_init(void) -{ - return platform_driver_register(&pxa910_pinmux_driver); -} -core_initcall_sync(pxa910_pinmux_init); - -static void __exit pxa910_pinmux_exit(void) -{ - platform_driver_unregister(&pxa910_pinmux_driver); -} -module_exit(pxa910_pinmux_exit); - -MODULE_AUTHOR("Haojian Zhuang <haojian.zhuang@marvell.com>"); -MODULE_DESCRIPTION("PXA3xx pin control driver"); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/pinctrl/pinctrl-s3c64xx.c b/drivers/pinctrl/pinctrl-s3c64xx.c new file mode 100644 index 000000000000..89143c903000 --- /dev/null +++ b/drivers/pinctrl/pinctrl-s3c64xx.c @@ -0,0 +1,816 @@ +/* + * S3C64xx specific support for pinctrl-samsung driver. + * + * Copyright (c) 2013 Tomasz Figa <tomasz.figa@gmail.com> + * + * Based on pinctrl-exynos.c, please see the file for original copyrights. + * + * 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 file contains the Samsung S3C64xx specific information required by the + * the Samsung pinctrl/gpiolib driver. It also includes the implementation of + * external gpio and wakeup interrupt support. + */ + +#include <linux/module.h> +#include <linux/device.h> +#include <linux/interrupt.h> +#include <linux/irqdomain.h> +#include <linux/irq.h> +#include <linux/of_irq.h> +#include <linux/io.h> +#include <linux/irqchip/chained_irq.h> +#include <linux/slab.h> +#include <linux/err.h> + +#include "pinctrl-samsung.h" + +#define NUM_EINT0 28 +#define NUM_EINT0_IRQ 4 +#define EINT_MAX_PER_REG 16 +#define EINT_MAX_PER_GROUP 16 + +/* External GPIO and wakeup interrupt related definitions */ +#define SVC_GROUP_SHIFT 4 +#define SVC_GROUP_MASK 0xf +#define SVC_NUM_MASK 0xf +#define SVC_GROUP(x) ((x >> SVC_GROUP_SHIFT) & \ + SVC_GROUP_MASK) + +#define EINT12CON_REG 0x200 +#define EINT12MASK_REG 0x240 +#define EINT12PEND_REG 0x260 + +#define EINT_OFFS(i) ((i) % (2 * EINT_MAX_PER_GROUP)) +#define EINT_GROUP(i) ((i) / EINT_MAX_PER_GROUP) +#define EINT_REG(g) (4 * ((g) / 2)) + +#define EINTCON_REG(i) (EINT12CON_REG + EINT_REG(EINT_GROUP(i))) +#define EINTMASK_REG(i) (EINT12MASK_REG + EINT_REG(EINT_GROUP(i))) +#define EINTPEND_REG(i) (EINT12PEND_REG + EINT_REG(EINT_GROUP(i))) + +#define SERVICE_REG 0x284 +#define SERVICEPEND_REG 0x288 + +#define EINT0CON0_REG 0x900 +#define EINT0MASK_REG 0x920 +#define EINT0PEND_REG 0x924 + +/* S3C64xx specific external interrupt trigger types */ +#define EINT_LEVEL_LOW 0 +#define EINT_LEVEL_HIGH 1 +#define EINT_EDGE_FALLING 2 +#define EINT_EDGE_RISING 4 +#define EINT_EDGE_BOTH 6 +#define EINT_CON_MASK 0xF +#define EINT_CON_LEN 4 + +static struct samsung_pin_bank_type bank_type_4bit_off = { + .fld_width = { 4, 1, 2, 0, 2, 2, }, + .reg_offset = { 0x00, 0x04, 0x08, 0, 0x0c, 0x10, }, +}; + +static struct samsung_pin_bank_type bank_type_4bit_alive = { + .fld_width = { 4, 1, 2, }, + .reg_offset = { 0x00, 0x04, 0x08, }, +}; + +static struct samsung_pin_bank_type bank_type_4bit2_off = { + .fld_width = { 4, 1, 2, 0, 2, 2, }, + .reg_offset = { 0x00, 0x08, 0x0c, 0, 0x10, 0x14, }, +}; + +static struct samsung_pin_bank_type bank_type_4bit2_alive = { + .fld_width = { 4, 1, 2, }, + .reg_offset = { 0x00, 0x08, 0x0c, }, +}; + +static struct samsung_pin_bank_type bank_type_2bit_off = { + .fld_width = { 2, 1, 2, 0, 2, 2, }, + .reg_offset = { 0x00, 0x04, 0x08, 0, 0x0c, 0x10, }, +}; + +static struct samsung_pin_bank_type bank_type_2bit_alive = { + .fld_width = { 2, 1, 2, }, + .reg_offset = { 0x00, 0x04, 0x08, }, +}; + +#define PIN_BANK_4BIT(pins, reg, id) \ + { \ + .type = &bank_type_4bit_off, \ + .pctl_offset = reg, \ + .nr_pins = pins, \ + .eint_type = EINT_TYPE_NONE, \ + .name = id \ + } + +#define PIN_BANK_4BIT_EINTG(pins, reg, id, eoffs) \ + { \ + .type = &bank_type_4bit_off, \ + .pctl_offset = reg, \ + .nr_pins = pins, \ + .eint_type = EINT_TYPE_GPIO, \ + .eint_func = 7, \ + .eint_mask = (1 << (pins)) - 1, \ + .eint_offset = eoffs, \ + .name = id \ + } + +#define PIN_BANK_4BIT_EINTW(pins, reg, id, eoffs, emask) \ + { \ + .type = &bank_type_4bit_alive,\ + .pctl_offset = reg, \ + .nr_pins = pins, \ + .eint_type = EINT_TYPE_WKUP, \ + .eint_func = 3, \ + .eint_mask = emask, \ + .eint_offset = eoffs, \ + .name = id \ + } + +#define PIN_BANK_4BIT2_EINTG(pins, reg, id, eoffs) \ + { \ + .type = &bank_type_4bit2_off, \ + .pctl_offset = reg, \ + .nr_pins = pins, \ + .eint_type = EINT_TYPE_GPIO, \ + .eint_func = 7, \ + .eint_mask = (1 << (pins)) - 1, \ + .eint_offset = eoffs, \ + .name = id \ + } + +#define PIN_BANK_4BIT2_EINTW(pins, reg, id, eoffs, emask) \ + { \ + .type = &bank_type_4bit2_alive,\ + .pctl_offset = reg, \ + .nr_pins = pins, \ + .eint_type = EINT_TYPE_WKUP, \ + .eint_func = 3, \ + .eint_mask = emask, \ + .eint_offset = eoffs, \ + .name = id \ + } + +#define PIN_BANK_4BIT2_ALIVE(pins, reg, id) \ + { \ + .type = &bank_type_4bit2_alive,\ + .pctl_offset = reg, \ + .nr_pins = pins, \ + .eint_type = EINT_TYPE_NONE, \ + .name = id \ + } + +#define PIN_BANK_2BIT(pins, reg, id) \ + { \ + .type = &bank_type_2bit_off, \ + .pctl_offset = reg, \ + .nr_pins = pins, \ + .eint_type = EINT_TYPE_NONE, \ + .name = id \ + } + +#define PIN_BANK_2BIT_EINTG(pins, reg, id, eoffs, emask) \ + { \ + .type = &bank_type_2bit_off, \ + .pctl_offset = reg, \ + .nr_pins = pins, \ + .eint_type = EINT_TYPE_GPIO, \ + .eint_func = 3, \ + .eint_mask = emask, \ + .eint_offset = eoffs, \ + .name = id \ + } + +#define PIN_BANK_2BIT_EINTW(pins, reg, id, eoffs) \ + { \ + .type = &bank_type_2bit_alive,\ + .pctl_offset = reg, \ + .nr_pins = pins, \ + .eint_type = EINT_TYPE_WKUP, \ + .eint_func = 2, \ + .eint_mask = (1 << (pins)) - 1, \ + .eint_offset = eoffs, \ + .name = id \ + } + +/** + * struct s3c64xx_eint0_data: EINT0 common data + * @drvdata: pin controller driver data + * @domains: IRQ domains of particular EINT0 interrupts + * @pins: pin offsets inside of banks of particular EINT0 interrupts + */ +struct s3c64xx_eint0_data { + struct samsung_pinctrl_drv_data *drvdata; + struct irq_domain *domains[NUM_EINT0]; + u8 pins[NUM_EINT0]; +}; + +/** + * struct s3c64xx_eint0_domain_data: EINT0 per-domain data + * @bank: pin bank related to the domain + * @eints: EINT0 interrupts related to the domain + */ +struct s3c64xx_eint0_domain_data { + struct samsung_pin_bank *bank; + u8 eints[]; +}; + +/** + * struct s3c64xx_eint_gpio_data: GPIO EINT data + * @drvdata: pin controller driver data + * @domains: array of domains related to EINT interrupt groups + */ +struct s3c64xx_eint_gpio_data { + struct samsung_pinctrl_drv_data *drvdata; + struct irq_domain *domains[]; +}; + +/* + * Common functions for S3C64xx EINT configuration + */ + +static int s3c64xx_irq_get_trigger(unsigned int type) +{ + int trigger; + + switch (type) { + case IRQ_TYPE_EDGE_RISING: + trigger = EINT_EDGE_RISING; + break; + case IRQ_TYPE_EDGE_FALLING: + trigger = EINT_EDGE_FALLING; + break; + case IRQ_TYPE_EDGE_BOTH: + trigger = EINT_EDGE_BOTH; + break; + case IRQ_TYPE_LEVEL_HIGH: + trigger = EINT_LEVEL_HIGH; + break; + case IRQ_TYPE_LEVEL_LOW: + trigger = EINT_LEVEL_LOW; + break; + default: + return -EINVAL; + } + + return trigger; +} + +static void s3c64xx_irq_set_handler(unsigned int irq, unsigned int type) +{ + /* Edge- and level-triggered interrupts need different handlers */ + if (type & IRQ_TYPE_EDGE_BOTH) + __irq_set_handler_locked(irq, handle_edge_irq); + else + __irq_set_handler_locked(irq, handle_level_irq); +} + +static void s3c64xx_irq_set_function(struct samsung_pinctrl_drv_data *d, + struct samsung_pin_bank *bank, int pin) +{ + struct samsung_pin_bank_type *bank_type = bank->type; + unsigned long flags; + void __iomem *reg; + u8 shift; + u32 mask; + u32 val; + + /* Make sure that pin is configured as interrupt */ + reg = d->virt_base + bank->pctl_offset; + shift = pin; + if (bank_type->fld_width[PINCFG_TYPE_FUNC] * shift >= 32) { + /* 4-bit bank type with 2 con regs */ + reg += 4; + shift -= 8; + } + + shift = shift * bank_type->fld_width[PINCFG_TYPE_FUNC]; + mask = (1 << bank_type->fld_width[PINCFG_TYPE_FUNC]) - 1; + + spin_lock_irqsave(&bank->slock, flags); + + val = readl(reg); + val &= ~(mask << shift); + val |= bank->eint_func << shift; + writel(val, reg); + + spin_unlock_irqrestore(&bank->slock, flags); +} + +/* + * Functions for EINT GPIO configuration (EINT groups 1-9) + */ + +static inline void s3c64xx_gpio_irq_set_mask(struct irq_data *irqd, bool mask) +{ + struct samsung_pin_bank *bank = irq_data_get_irq_chip_data(irqd); + struct samsung_pinctrl_drv_data *d = bank->drvdata; + unsigned char index = EINT_OFFS(bank->eint_offset) + irqd->hwirq; + void __iomem *reg = d->virt_base + EINTMASK_REG(bank->eint_offset); + u32 val; + + val = readl(reg); + if (mask) + val |= 1 << index; + else + val &= ~(1 << index); + writel(val, reg); +} + +static void s3c64xx_gpio_irq_unmask(struct irq_data *irqd) +{ + s3c64xx_gpio_irq_set_mask(irqd, false); +} + +static void s3c64xx_gpio_irq_mask(struct irq_data *irqd) +{ + s3c64xx_gpio_irq_set_mask(irqd, true); +} + +static void s3c64xx_gpio_irq_ack(struct irq_data *irqd) +{ + struct samsung_pin_bank *bank = irq_data_get_irq_chip_data(irqd); + struct samsung_pinctrl_drv_data *d = bank->drvdata; + unsigned char index = EINT_OFFS(bank->eint_offset) + irqd->hwirq; + void __iomem *reg = d->virt_base + EINTPEND_REG(bank->eint_offset); + + writel(1 << index, reg); +} + +static int s3c64xx_gpio_irq_set_type(struct irq_data *irqd, unsigned int type) +{ + struct samsung_pin_bank *bank = irq_data_get_irq_chip_data(irqd); + struct samsung_pinctrl_drv_data *d = bank->drvdata; + void __iomem *reg; + int trigger; + u8 shift; + u32 val; + + trigger = s3c64xx_irq_get_trigger(type); + if (trigger < 0) { + pr_err("unsupported external interrupt type\n"); + return -EINVAL; + } + + s3c64xx_irq_set_handler(irqd->irq, type); + + /* Set up interrupt trigger */ + reg = d->virt_base + EINTCON_REG(bank->eint_offset); + shift = EINT_OFFS(bank->eint_offset) + irqd->hwirq; + shift = 4 * (shift / 4); /* 4 EINTs per trigger selector */ + + val = readl(reg); + val &= ~(EINT_CON_MASK << shift); + val |= trigger << shift; + writel(val, reg); + + s3c64xx_irq_set_function(d, bank, irqd->hwirq); + + return 0; +} + +/* + * irq_chip for gpio interrupts. + */ +static struct irq_chip s3c64xx_gpio_irq_chip = { + .name = "GPIO", + .irq_unmask = s3c64xx_gpio_irq_unmask, + .irq_mask = s3c64xx_gpio_irq_mask, + .irq_ack = s3c64xx_gpio_irq_ack, + .irq_set_type = s3c64xx_gpio_irq_set_type, +}; + +static int s3c64xx_gpio_irq_map(struct irq_domain *h, unsigned int virq, + irq_hw_number_t hw) +{ + struct samsung_pin_bank *bank = h->host_data; + + if (!(bank->eint_mask & (1 << hw))) + return -EINVAL; + + irq_set_chip_and_handler(virq, + &s3c64xx_gpio_irq_chip, handle_level_irq); + irq_set_chip_data(virq, bank); + set_irq_flags(virq, IRQF_VALID); + + return 0; +} + +/* + * irq domain callbacks for external gpio interrupt controller. + */ +static const struct irq_domain_ops s3c64xx_gpio_irqd_ops = { + .map = s3c64xx_gpio_irq_map, + .xlate = irq_domain_xlate_twocell, +}; + +static void s3c64xx_eint_gpio_irq(unsigned int irq, struct irq_desc *desc) +{ + struct irq_chip *chip = irq_get_chip(irq); + struct s3c64xx_eint_gpio_data *data = irq_get_handler_data(irq); + struct samsung_pinctrl_drv_data *drvdata = data->drvdata; + + chained_irq_enter(chip, desc); + + do { + unsigned int svc; + unsigned int group; + unsigned int pin; + unsigned int virq; + + svc = readl(drvdata->virt_base + SERVICE_REG); + group = SVC_GROUP(svc); + pin = svc & SVC_NUM_MASK; + + if (!group) + break; + + /* Group 1 is used for two pin banks */ + if (group == 1) { + if (pin < 8) + group = 0; + else + pin -= 8; + } + + virq = irq_linear_revmap(data->domains[group], pin); + /* + * Something must be really wrong if an unmapped EINT + * was unmasked... + */ + BUG_ON(!virq); + + generic_handle_irq(virq); + } while (1); + + chained_irq_exit(chip, desc); +} + +/** + * s3c64xx_eint_gpio_init() - setup handling of external gpio interrupts. + * @d: driver data of samsung pinctrl driver. + */ +static int s3c64xx_eint_gpio_init(struct samsung_pinctrl_drv_data *d) +{ + struct s3c64xx_eint_gpio_data *data; + struct samsung_pin_bank *bank; + struct device *dev = d->dev; + unsigned int nr_domains; + unsigned int i; + + if (!d->irq) { + dev_err(dev, "irq number not available\n"); + return -EINVAL; + } + + nr_domains = 0; + bank = d->ctrl->pin_banks; + for (i = 0; i < d->ctrl->nr_banks; ++i, ++bank) { + unsigned int nr_eints; + unsigned int mask; + + if (bank->eint_type != EINT_TYPE_GPIO) + continue; + + mask = bank->eint_mask; + nr_eints = fls(mask); + + bank->irq_domain = irq_domain_add_linear(bank->of_node, + nr_eints, &s3c64xx_gpio_irqd_ops, bank); + if (!bank->irq_domain) { + dev_err(dev, "gpio irq domain add failed\n"); + return -ENXIO; + } + + ++nr_domains; + } + + data = devm_kzalloc(dev, sizeof(*data) + + nr_domains * sizeof(*data->domains), GFP_KERNEL); + if (!data) { + dev_err(dev, "failed to allocate handler data\n"); + return -ENOMEM; + } + data->drvdata = d; + + bank = d->ctrl->pin_banks; + nr_domains = 0; + for (i = 0; i < d->ctrl->nr_banks; ++i, ++bank) { + if (bank->eint_type != EINT_TYPE_GPIO) + continue; + + data->domains[nr_domains++] = bank->irq_domain; + } + + irq_set_chained_handler(d->irq, s3c64xx_eint_gpio_irq); + irq_set_handler_data(d->irq, data); + + return 0; +} + +/* + * Functions for configuration of EINT0 wake-up interrupts + */ + +static inline void s3c64xx_eint0_irq_set_mask(struct irq_data *irqd, bool mask) +{ + struct s3c64xx_eint0_domain_data *ddata = + irq_data_get_irq_chip_data(irqd); + struct samsung_pinctrl_drv_data *d = ddata->bank->drvdata; + u32 val; + + val = readl(d->virt_base + EINT0MASK_REG); + if (mask) + val |= 1 << ddata->eints[irqd->hwirq]; + else + val &= ~(1 << ddata->eints[irqd->hwirq]); + writel(val, d->virt_base + EINT0MASK_REG); +} + +static void s3c64xx_eint0_irq_unmask(struct irq_data *irqd) +{ + s3c64xx_eint0_irq_set_mask(irqd, false); +} + +static void s3c64xx_eint0_irq_mask(struct irq_data *irqd) +{ + s3c64xx_eint0_irq_set_mask(irqd, true); +} + +static void s3c64xx_eint0_irq_ack(struct irq_data *irqd) +{ + struct s3c64xx_eint0_domain_data *ddata = + irq_data_get_irq_chip_data(irqd); + struct samsung_pinctrl_drv_data *d = ddata->bank->drvdata; + + writel(1 << ddata->eints[irqd->hwirq], + d->virt_base + EINT0PEND_REG); +} + +static int s3c64xx_eint0_irq_set_type(struct irq_data *irqd, unsigned int type) +{ + struct s3c64xx_eint0_domain_data *ddata = + irq_data_get_irq_chip_data(irqd); + struct samsung_pin_bank *bank = ddata->bank; + struct samsung_pinctrl_drv_data *d = bank->drvdata; + void __iomem *reg; + int trigger; + u8 shift; + u32 val; + + trigger = s3c64xx_irq_get_trigger(type); + if (trigger < 0) { + pr_err("unsupported external interrupt type\n"); + return -EINVAL; + } + + s3c64xx_irq_set_handler(irqd->irq, type); + + /* Set up interrupt trigger */ + reg = d->virt_base + EINT0CON0_REG; + shift = ddata->eints[irqd->hwirq]; + if (shift >= EINT_MAX_PER_REG) { + reg += 4; + shift -= EINT_MAX_PER_REG; + } + shift = EINT_CON_LEN * (shift / 2); + + val = readl(reg); + val &= ~(EINT_CON_MASK << shift); + val |= trigger << shift; + writel(val, reg); + + s3c64xx_irq_set_function(d, bank, irqd->hwirq); + + return 0; +} + +/* + * irq_chip for wakeup interrupts + */ +static struct irq_chip s3c64xx_eint0_irq_chip = { + .name = "EINT0", + .irq_unmask = s3c64xx_eint0_irq_unmask, + .irq_mask = s3c64xx_eint0_irq_mask, + .irq_ack = s3c64xx_eint0_irq_ack, + .irq_set_type = s3c64xx_eint0_irq_set_type, +}; + +static inline void s3c64xx_irq_demux_eint(unsigned int irq, + struct irq_desc *desc, u32 range) +{ + struct irq_chip *chip = irq_get_chip(irq); + struct s3c64xx_eint0_data *data = irq_get_handler_data(irq); + struct samsung_pinctrl_drv_data *drvdata = data->drvdata; + unsigned int pend, mask; + + chained_irq_enter(chip, desc); + + pend = readl(drvdata->virt_base + EINT0PEND_REG); + mask = readl(drvdata->virt_base + EINT0MASK_REG); + + pend = pend & range & ~mask; + pend &= range; + + while (pend) { + unsigned int virq; + + irq = fls(pend) - 1; + pend &= ~(1 << irq); + + virq = irq_linear_revmap(data->domains[irq], data->pins[irq]); + /* + * Something must be really wrong if an unmapped EINT + * was unmasked... + */ + BUG_ON(!virq); + + generic_handle_irq(virq); + } + + chained_irq_exit(chip, desc); +} + +static void s3c64xx_demux_eint0_3(unsigned int irq, struct irq_desc *desc) +{ + s3c64xx_irq_demux_eint(irq, desc, 0xf); +} + +static void s3c64xx_demux_eint4_11(unsigned int irq, struct irq_desc *desc) +{ + s3c64xx_irq_demux_eint(irq, desc, 0xff0); +} + +static void s3c64xx_demux_eint12_19(unsigned int irq, struct irq_desc *desc) +{ + s3c64xx_irq_demux_eint(irq, desc, 0xff000); +} + +static void s3c64xx_demux_eint20_27(unsigned int irq, struct irq_desc *desc) +{ + s3c64xx_irq_demux_eint(irq, desc, 0xff00000); +} + +static irq_flow_handler_t s3c64xx_eint0_handlers[NUM_EINT0_IRQ] = { + s3c64xx_demux_eint0_3, + s3c64xx_demux_eint4_11, + s3c64xx_demux_eint12_19, + s3c64xx_demux_eint20_27, +}; + +static int s3c64xx_eint0_irq_map(struct irq_domain *h, unsigned int virq, + irq_hw_number_t hw) +{ + struct s3c64xx_eint0_domain_data *ddata = h->host_data; + struct samsung_pin_bank *bank = ddata->bank; + + if (!(bank->eint_mask & (1 << hw))) + return -EINVAL; + + irq_set_chip_and_handler(virq, + &s3c64xx_eint0_irq_chip, handle_level_irq); + irq_set_chip_data(virq, ddata); + set_irq_flags(virq, IRQF_VALID); + + return 0; +} + +/* + * irq domain callbacks for external wakeup interrupt controller. + */ +static const struct irq_domain_ops s3c64xx_eint0_irqd_ops = { + .map = s3c64xx_eint0_irq_map, + .xlate = irq_domain_xlate_twocell, +}; + +/* list of external wakeup controllers supported */ +static const struct of_device_id s3c64xx_eint0_irq_ids[] = { + { .compatible = "samsung,s3c64xx-wakeup-eint", }, + { } +}; + +/** + * s3c64xx_eint_eint0_init() - setup handling of external wakeup interrupts. + * @d: driver data of samsung pinctrl driver. + */ +static int s3c64xx_eint_eint0_init(struct samsung_pinctrl_drv_data *d) +{ + struct device *dev = d->dev; + struct device_node *eint0_np = NULL; + struct device_node *np; + struct samsung_pin_bank *bank; + struct s3c64xx_eint0_data *data; + unsigned int i; + + for_each_child_of_node(dev->of_node, np) { + if (of_match_node(s3c64xx_eint0_irq_ids, np)) { + eint0_np = np; + break; + } + } + if (!eint0_np) + return -ENODEV; + + data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); + if (!data) { + dev_err(dev, "could not allocate memory for wkup eint data\n"); + return -ENOMEM; + } + data->drvdata = d; + + for (i = 0; i < NUM_EINT0_IRQ; ++i) { + unsigned int irq; + + irq = irq_of_parse_and_map(eint0_np, i); + if (!irq) { + dev_err(dev, "failed to get wakeup EINT IRQ %d\n", i); + return -ENXIO; + } + + irq_set_chained_handler(irq, s3c64xx_eint0_handlers[i]); + irq_set_handler_data(irq, data); + } + + bank = d->ctrl->pin_banks; + for (i = 0; i < d->ctrl->nr_banks; ++i, ++bank) { + struct s3c64xx_eint0_domain_data *ddata; + unsigned int nr_eints; + unsigned int mask; + unsigned int irq; + unsigned int pin; + + if (bank->eint_type != EINT_TYPE_WKUP) + continue; + + mask = bank->eint_mask; + nr_eints = fls(mask); + + ddata = devm_kzalloc(dev, + sizeof(*ddata) + nr_eints, GFP_KERNEL); + if (!ddata) { + dev_err(dev, "failed to allocate domain data\n"); + return -ENOMEM; + } + ddata->bank = bank; + + bank->irq_domain = irq_domain_add_linear(bank->of_node, + nr_eints, &s3c64xx_eint0_irqd_ops, ddata); + if (!bank->irq_domain) { + dev_err(dev, "wkup irq domain add failed\n"); + return -ENXIO; + } + + irq = bank->eint_offset; + mask = bank->eint_mask; + for (pin = 0; mask; ++pin, mask >>= 1) { + if (!(mask & 1)) + continue; + data->domains[irq] = bank->irq_domain; + data->pins[irq] = pin; + ddata->eints[pin] = irq; + ++irq; + } + } + + return 0; +} + +/* pin banks of s3c64xx pin-controller 0 */ +static struct samsung_pin_bank s3c64xx_pin_banks0[] = { + PIN_BANK_4BIT_EINTG(8, 0x000, "gpa", 0), + PIN_BANK_4BIT_EINTG(7, 0x020, "gpb", 8), + PIN_BANK_4BIT_EINTG(8, 0x040, "gpc", 16), + PIN_BANK_4BIT_EINTG(5, 0x060, "gpd", 32), + PIN_BANK_4BIT(5, 0x080, "gpe"), + PIN_BANK_2BIT_EINTG(16, 0x0a0, "gpf", 48, 0x3fff), + PIN_BANK_4BIT_EINTG(7, 0x0c0, "gpg", 64), + PIN_BANK_4BIT2_EINTG(10, 0x0e0, "gph", 80), + PIN_BANK_2BIT(16, 0x100, "gpi"), + PIN_BANK_2BIT(12, 0x120, "gpj"), + PIN_BANK_4BIT2_ALIVE(16, 0x800, "gpk"), + PIN_BANK_4BIT2_EINTW(15, 0x810, "gpl", 16, 0x7f00), + PIN_BANK_4BIT_EINTW(6, 0x820, "gpm", 23, 0x1f), + PIN_BANK_2BIT_EINTW(16, 0x830, "gpn", 0), + PIN_BANK_2BIT_EINTG(16, 0x140, "gpo", 96, 0xffff), + PIN_BANK_2BIT_EINTG(15, 0x160, "gpp", 112, 0x7fff), + PIN_BANK_2BIT_EINTG(9, 0x180, "gpq", 128, 0x1ff), +}; + +/* + * Samsung pinctrl driver data for S3C64xx SoC. S3C64xx SoC includes + * one gpio/pin-mux/pinconfig controller. + */ +struct samsung_pin_ctrl s3c64xx_pin_ctrl[] = { + { + /* pin-controller instance 1 data */ + .pin_banks = s3c64xx_pin_banks0, + .nr_banks = ARRAY_SIZE(s3c64xx_pin_banks0), + .eint_gpio_init = s3c64xx_eint_gpio_init, + .eint_wkup_init = s3c64xx_eint_eint0_init, + .label = "S3C64xx-GPIO", + }, +}; diff --git a/drivers/pinctrl/pinctrl-samsung.c b/drivers/pinctrl/pinctrl-samsung.c index f206df175656..4f54faf2971f 100644 --- a/drivers/pinctrl/pinctrl-samsung.c +++ b/drivers/pinctrl/pinctrl-samsung.c @@ -27,6 +27,7 @@ #include <linux/err.h> #include <linux/gpio.h> #include <linux/irqdomain.h> +#include <linux/spinlock.h> #include "core.h" #include "pinctrl-samsung.h" @@ -214,7 +215,7 @@ static void samsung_dt_free_map(struct pinctrl_dev *pctldev, } /* list of pinctrl callbacks for the pinctrl core */ -static struct pinctrl_ops samsung_pctrl_ops = { +static const struct pinctrl_ops samsung_pctrl_ops = { .get_groups_count = samsung_get_group_count, .get_group_name = samsung_get_group_name, .get_group_pins = samsung_get_group_pins, @@ -274,10 +275,6 @@ static void pin_to_reg_bank(struct samsung_pinctrl_drv_data *drvdata, *offset = pin - b->pin_base; if (bank) *bank = b; - - /* some banks have two config registers in a single bank */ - if (*offset * b->func_width > BITS_PER_LONG) - *reg += 4; } /* enable or disable a pinmux function */ @@ -289,6 +286,7 @@ static void samsung_pinmux_setup(struct pinctrl_dev *pctldev, unsigned selector, struct samsung_pin_bank *bank; void __iomem *reg; u32 mask, shift, data, pin_offset, cnt; + unsigned long flags; drvdata = pinctrl_dev_get_drvdata(pctldev); pins = drvdata->pin_groups[group].pins; @@ -298,16 +296,28 @@ static void samsung_pinmux_setup(struct pinctrl_dev *pctldev, unsigned selector, * pin function number in the config register. */ for (cnt = 0; cnt < drvdata->pin_groups[group].num_pins; cnt++) { + struct samsung_pin_bank_type *type; + pin_to_reg_bank(drvdata, pins[cnt] - drvdata->ctrl->base, ®, &pin_offset, &bank); - mask = (1 << bank->func_width) - 1; - shift = pin_offset * bank->func_width; + type = bank->type; + mask = (1 << type->fld_width[PINCFG_TYPE_FUNC]) - 1; + shift = pin_offset * type->fld_width[PINCFG_TYPE_FUNC]; + if (shift >= 32) { + /* Some banks have two config registers */ + shift -= 32; + reg += 4; + } + + spin_lock_irqsave(&bank->slock, flags); - data = readl(reg); + data = readl(reg + type->reg_offset[PINCFG_TYPE_FUNC]); data &= ~(mask << shift); if (enable) data |= drvdata->pin_groups[group].func << shift; - writel(data, reg); + writel(data, reg + type->reg_offset[PINCFG_TYPE_FUNC]); + + spin_unlock_irqrestore(&bank->slock, flags); } } @@ -334,30 +344,44 @@ static void samsung_pinmux_disable(struct pinctrl_dev *pctldev, static int samsung_pinmux_gpio_set_direction(struct pinctrl_dev *pctldev, struct pinctrl_gpio_range *range, unsigned offset, bool input) { + struct samsung_pin_bank_type *type; struct samsung_pin_bank *bank; struct samsung_pinctrl_drv_data *drvdata; void __iomem *reg; u32 data, pin_offset, mask, shift; + unsigned long flags; bank = gc_to_pin_bank(range->gc); + type = bank->type; drvdata = pinctrl_dev_get_drvdata(pctldev); pin_offset = offset - bank->pin_base; - reg = drvdata->virt_base + bank->pctl_offset; + reg = drvdata->virt_base + bank->pctl_offset + + type->reg_offset[PINCFG_TYPE_FUNC]; + + mask = (1 << type->fld_width[PINCFG_TYPE_FUNC]) - 1; + shift = pin_offset * type->fld_width[PINCFG_TYPE_FUNC]; + if (shift >= 32) { + /* Some banks have two config registers */ + shift -= 32; + reg += 4; + } - mask = (1 << bank->func_width) - 1; - shift = pin_offset * bank->func_width; + spin_lock_irqsave(&bank->slock, flags); data = readl(reg); data &= ~(mask << shift); if (!input) data |= FUNC_OUTPUT << shift; writel(data, reg); + + spin_unlock_irqrestore(&bank->slock, flags); + return 0; } /* list of pinmux callbacks for the pinmux vertical in pinctrl core */ -static struct pinmux_ops samsung_pinmux_ops = { +static const struct pinmux_ops samsung_pinmux_ops = { .get_functions_count = samsung_get_functions_count, .get_function_name = samsung_pinmux_get_fname, .get_function_groups = samsung_pinmux_get_groups, @@ -371,40 +395,26 @@ static int samsung_pinconf_rw(struct pinctrl_dev *pctldev, unsigned int pin, unsigned long *config, bool set) { struct samsung_pinctrl_drv_data *drvdata; + struct samsung_pin_bank_type *type; struct samsung_pin_bank *bank; void __iomem *reg_base; enum pincfg_type cfg_type = PINCFG_UNPACK_TYPE(*config); u32 data, width, pin_offset, mask, shift; u32 cfg_value, cfg_reg; + unsigned long flags; drvdata = pinctrl_dev_get_drvdata(pctldev); pin_to_reg_bank(drvdata, pin - drvdata->ctrl->base, ®_base, &pin_offset, &bank); + type = bank->type; - switch (cfg_type) { - case PINCFG_TYPE_PUD: - width = bank->pud_width; - cfg_reg = PUD_REG; - break; - case PINCFG_TYPE_DRV: - width = bank->drv_width; - cfg_reg = DRV_REG; - break; - case PINCFG_TYPE_CON_PDN: - width = bank->conpdn_width; - cfg_reg = CONPDN_REG; - break; - case PINCFG_TYPE_PUD_PDN: - width = bank->pudpdn_width; - cfg_reg = PUDPDN_REG; - break; - default: - WARN_ON(1); + if (cfg_type >= PINCFG_TYPE_NUM || !type->fld_width[cfg_type]) return -EINVAL; - } - if (!width) - return -EINVAL; + width = type->fld_width[cfg_type]; + cfg_reg = type->reg_offset[cfg_type]; + + spin_lock_irqsave(&bank->slock, flags); mask = (1 << width) - 1; shift = pin_offset * width; @@ -420,6 +430,9 @@ static int samsung_pinconf_rw(struct pinctrl_dev *pctldev, unsigned int pin, data &= mask; *config = PINCFG_PACK(cfg_type, data); } + + spin_unlock_irqrestore(&bank->slock, flags); + return 0; } @@ -468,7 +481,7 @@ static int samsung_pinconf_group_get(struct pinctrl_dev *pctldev, } /* list of pinconfig callbacks for pinconfig vertical in the pinctrl code */ -static struct pinconf_ops samsung_pinconf_ops = { +static const struct pinconf_ops samsung_pinconf_ops = { .pin_config_get = samsung_pinconf_get, .pin_config_set = samsung_pinconf_set, .pin_config_group_get = samsung_pinconf_group_get, @@ -479,16 +492,22 @@ static struct pinconf_ops samsung_pinconf_ops = { static void samsung_gpio_set(struct gpio_chip *gc, unsigned offset, int value) { struct samsung_pin_bank *bank = gc_to_pin_bank(gc); + struct samsung_pin_bank_type *type = bank->type; + unsigned long flags; void __iomem *reg; u32 data; reg = bank->drvdata->virt_base + bank->pctl_offset; - data = readl(reg + DAT_REG); + spin_lock_irqsave(&bank->slock, flags); + + data = readl(reg + type->reg_offset[PINCFG_TYPE_DAT]); data &= ~(1 << offset); if (value) data |= 1 << offset; - writel(data, reg + DAT_REG); + writel(data, reg + type->reg_offset[PINCFG_TYPE_DAT]); + + spin_unlock_irqrestore(&bank->slock, flags); } /* gpiolib gpio_get callback function */ @@ -497,10 +516,11 @@ static int samsung_gpio_get(struct gpio_chip *gc, unsigned offset) void __iomem *reg; u32 data; struct samsung_pin_bank *bank = gc_to_pin_bank(gc); + struct samsung_pin_bank_type *type = bank->type; reg = bank->drvdata->virt_base + bank->pctl_offset; - data = readl(reg + DAT_REG); + data = readl(reg + type->reg_offset[PINCFG_TYPE_DAT]); data >>= offset; data &= 1; return data; @@ -859,6 +879,7 @@ static struct samsung_pin_ctrl *samsung_pinctrl_get_soc_data( bank = ctrl->pin_banks; for (i = 0; i < ctrl->nr_banks; ++i, ++bank) { + spin_lock_init(&bank->slock); bank->drvdata = d; bank->pin_base = ctrl->nr_pins; ctrl->nr_pins += bank->nr_pins; @@ -944,10 +965,16 @@ static int samsung_pinctrl_probe(struct platform_device *pdev) } static const struct of_device_id samsung_pinctrl_dt_match[] = { +#ifdef CONFIG_PINCTRL_EXYNOS { .compatible = "samsung,exynos4210-pinctrl", .data = (void *)exynos4210_pin_ctrl }, { .compatible = "samsung,exynos4x12-pinctrl", .data = (void *)exynos4x12_pin_ctrl }, +#endif +#ifdef CONFIG_PINCTRL_S3C64XX + { .compatible = "samsung,s3c64xx-pinctrl", + .data = s3c64xx_pin_ctrl }, +#endif {}, }; MODULE_DEVICE_TABLE(of, samsung_pinctrl_dt_match); diff --git a/drivers/pinctrl/pinctrl-samsung.h b/drivers/pinctrl/pinctrl-samsung.h index e2d4e67f7e88..45f27b41e30c 100644 --- a/drivers/pinctrl/pinctrl-samsung.h +++ b/drivers/pinctrl/pinctrl-samsung.h @@ -25,28 +25,27 @@ #include <linux/gpio.h> -/* register offsets within a pin bank */ -#define DAT_REG 0x4 -#define PUD_REG 0x8 -#define DRV_REG 0xC -#define CONPDN_REG 0x10 -#define PUDPDN_REG 0x14 - /* pinmux function number for pin as gpio output line */ #define FUNC_OUTPUT 0x1 /** * enum pincfg_type - possible pin configuration types supported. + * @PINCFG_TYPE_FUNC: Function configuration. + * @PINCFG_TYPE_DAT: Pin value configuration. * @PINCFG_TYPE_PUD: Pull up/down configuration. * @PINCFG_TYPE_DRV: Drive strength configuration. * @PINCFG_TYPE_CON_PDN: Pin function in power down mode. * @PINCFG_TYPE_PUD_PDN: Pull up/down configuration in power down mode. */ enum pincfg_type { + PINCFG_TYPE_FUNC, + PINCFG_TYPE_DAT, PINCFG_TYPE_PUD, PINCFG_TYPE_DRV, PINCFG_TYPE_CON_PDN, PINCFG_TYPE_PUD_PDN, + + PINCFG_TYPE_NUM }; /* @@ -103,33 +102,40 @@ enum eint_type { struct samsung_pinctrl_drv_data; /** + * struct samsung_pin_bank_type: pin bank type description + * @fld_width: widths of configuration bitfields (0 if unavailable) + * @reg_offset: offsets of configuration registers (don't care of width is 0) + */ +struct samsung_pin_bank_type { + u8 fld_width[PINCFG_TYPE_NUM]; + u8 reg_offset[PINCFG_TYPE_NUM]; +}; + +/** * struct samsung_pin_bank: represent a controller pin-bank. + * @type: type of the bank (register offsets and bitfield widths) * @pctl_offset: starting offset of the pin-bank registers. * @pin_base: starting pin number of the bank. * @nr_pins: number of pins included in this bank. - * @func_width: width of the function selector bit field. - * @pud_width: width of the pin pull up/down selector bit field. - * @drv_width: width of the pin driver strength selector bit field. - * @conpdn_width: width of the sleep mode function selector bin field. - * @pudpdn_width: width of the sleep mode pull up/down selector bit field. + * @eint_func: function to set in CON register to configure pin as EINT. * @eint_type: type of the external interrupt supported by the bank. + * @eint_mask: bit mask of pins which support EINT function. * @name: name to be prefixed for each pin in this pin bank. * @of_node: OF node of the bank. * @drvdata: link to controller driver data * @irq_domain: IRQ domain of the bank. * @gpio_chip: GPIO chip of the bank. * @grange: linux gpio pin range supported by this bank. + * @slock: spinlock protecting bank registers */ struct samsung_pin_bank { + struct samsung_pin_bank_type *type; u32 pctl_offset; u32 pin_base; u8 nr_pins; - u8 func_width; - u8 pud_width; - u8 drv_width; - u8 conpdn_width; - u8 pudpdn_width; + u8 eint_func; enum eint_type eint_type; + u32 eint_mask; u32 eint_offset; char *name; struct device_node *of_node; @@ -137,6 +143,7 @@ struct samsung_pin_bank { struct irq_domain *irq_domain; struct gpio_chip gpio_chip; struct pinctrl_gpio_range grange; + spinlock_t slock; }; /** @@ -237,5 +244,6 @@ struct samsung_pmx_func { /* list of all exported SoC specific data */ extern struct samsung_pin_ctrl exynos4210_pin_ctrl[]; extern struct samsung_pin_ctrl exynos4x12_pin_ctrl[]; +extern struct samsung_pin_ctrl s3c64xx_pin_ctrl[]; #endif /* __PINCTRL_SAMSUNG_H */ diff --git a/drivers/pinctrl/pinctrl-single.c b/drivers/pinctrl/pinctrl-single.c index 5c32e880bcb2..5f2d2bfd356e 100644 --- a/drivers/pinctrl/pinctrl-single.c +++ b/drivers/pinctrl/pinctrl-single.c @@ -22,8 +22,10 @@ #include <linux/pinctrl/pinctrl.h> #include <linux/pinctrl/pinmux.h> +#include <linux/pinctrl/pinconf-generic.h> #include "core.h" +#include "pinconf.h" #define DRIVER_NAME "pinctrl-single" #define PCS_MUX_PINS_NAME "pinctrl-single,pins" @@ -59,6 +61,33 @@ struct pcs_func_vals { }; /** + * struct pcs_conf_vals - pinconf parameter, pinconf register offset + * and value, enable, disable, mask + * @param: config parameter + * @val: user input bits in the pinconf register + * @enable: enable bits in the pinconf register + * @disable: disable bits in the pinconf register + * @mask: mask bits in the register value + */ +struct pcs_conf_vals { + enum pin_config_param param; + unsigned val; + unsigned enable; + unsigned disable; + unsigned mask; +}; + +/** + * struct pcs_conf_type - pinconf property name, pinconf param pair + * @name: property name in DTS file + * @param: config parameter + */ +struct pcs_conf_type { + const char *name; + enum pin_config_param param; +}; + +/** * struct pcs_function - pinctrl function * @name: pinctrl function name * @vals: register and vals array @@ -73,6 +102,22 @@ struct pcs_function { unsigned nvals; const char **pgnames; int npgnames; + struct pcs_conf_vals *conf; + int nconfs; + struct list_head node; +}; + +/** + * struct pcs_gpiofunc_range - pin ranges with same mux value of gpio function + * @offset: offset base of pins + * @npins: number pins with the same mux value of gpio function + * @gpiofunc: mux value of gpio function + * @node: list node + */ +struct pcs_gpiofunc_range { + unsigned offset; + unsigned npins; + unsigned gpiofunc; struct list_head node; }; @@ -117,12 +162,14 @@ struct pcs_name { * @fshift: function register shift * @foff: value to turn mux off * @fmax: max number of functions in fmask + * @is_pinconf: whether supports pinconf * @names: array of register names for pins * @pins: physical pins on the SoC * @pgtree: pingroup index radix tree * @ftree: function index radix tree * @pingroups: list of pingroups * @functions: list of functions + * @gpiofuncs: list of gpio functions * @ngroups: number of pingroups * @nfuncs: number of functions * @desc: pin controller descriptor @@ -142,12 +189,14 @@ struct pcs_device { unsigned foff; unsigned fmax; bool bits_per_mux; + bool is_pinconf; struct pcs_name *names; struct pcs_data pins; struct radix_tree_root pgtree; struct radix_tree_root ftree; struct list_head pingroups; struct list_head functions; + struct list_head gpiofuncs; unsigned ngroups; unsigned nfuncs; struct pinctrl_desc desc; @@ -155,6 +204,16 @@ struct pcs_device { void (*write)(unsigned val, void __iomem *reg); }; +static int pcs_pinconf_get(struct pinctrl_dev *pctldev, unsigned pin, + unsigned long *config); +static int pcs_pinconf_set(struct pinctrl_dev *pctldev, unsigned pin, + unsigned long config); + +static enum pin_config_param pcs_bias[] = { + PIN_CONFIG_BIAS_PULL_DOWN, + PIN_CONFIG_BIAS_PULL_UP, +}; + /* * REVISIT: Reads and writes could eventually use regmap or something * generic. But at least on omaps, some mux registers are performance @@ -270,7 +329,7 @@ static int pcs_dt_node_to_map(struct pinctrl_dev *pctldev, struct device_node *np_config, struct pinctrl_map **map, unsigned *num_maps); -static struct pinctrl_ops pcs_pinctrl_ops = { +static const struct pinctrl_ops pcs_pinctrl_ops = { .get_groups_count = pcs_get_groups_count, .get_group_name = pcs_get_group_name, .get_group_pins = pcs_get_group_pins, @@ -326,6 +385,28 @@ static int pcs_get_function_groups(struct pinctrl_dev *pctldev, return 0; } +static int pcs_get_function(struct pinctrl_dev *pctldev, unsigned pin, + struct pcs_function **func) +{ + struct pcs_device *pcs = pinctrl_dev_get_drvdata(pctldev); + struct pin_desc *pdesc = pin_desc_get(pctldev, pin); + const struct pinctrl_setting_mux *setting; + unsigned fselector; + + /* If pin is not described in DTS & enabled, mux_setting is NULL. */ + setting = pdesc->mux_setting; + if (!setting) + return -ENOTSUPP; + fselector = setting->func; + *func = radix_tree_lookup(&pcs->ftree, fselector); + if (!(*func)) { + dev_err(pcs->dev, "%s could not find function%i\n", + __func__, fselector); + return -ENOTSUPP; + } + return 0; +} + static int pcs_enable(struct pinctrl_dev *pctldev, unsigned fselector, unsigned group) { @@ -334,6 +415,9 @@ static int pcs_enable(struct pinctrl_dev *pctldev, unsigned fselector, int i; pcs = pinctrl_dev_get_drvdata(pctldev); + /* If function mask is null, needn't enable it. */ + if (!pcs->fmask) + return 0; func = radix_tree_lookup(&pcs->ftree, fselector); if (!func) return -EINVAL; @@ -368,6 +452,10 @@ static void pcs_disable(struct pinctrl_dev *pctldev, unsigned fselector, int i; pcs = pinctrl_dev_get_drvdata(pctldev); + /* If function mask is null, needn't disable it. */ + if (!pcs->fmask) + return; + func = radix_tree_lookup(&pcs->ftree, fselector); if (!func) { dev_err(pcs->dev, "%s could not find function%i\n", @@ -403,12 +491,33 @@ static void pcs_disable(struct pinctrl_dev *pctldev, unsigned fselector, } static int pcs_request_gpio(struct pinctrl_dev *pctldev, - struct pinctrl_gpio_range *range, unsigned offset) + struct pinctrl_gpio_range *range, unsigned pin) { - return -ENOTSUPP; + struct pcs_device *pcs = pinctrl_dev_get_drvdata(pctldev); + struct pcs_gpiofunc_range *frange = NULL; + struct list_head *pos, *tmp; + int mux_bytes = 0; + unsigned data; + + /* If function mask is null, return directly. */ + if (!pcs->fmask) + return -ENOTSUPP; + + list_for_each_safe(pos, tmp, &pcs->gpiofuncs) { + frange = list_entry(pos, struct pcs_gpiofunc_range, node); + if (pin >= frange->offset + frange->npins + || pin < frange->offset) + continue; + mux_bytes = pcs->width / BITS_PER_BYTE; + data = pcs->read(pcs->base + pin * mux_bytes) & ~pcs->fmask; + data |= frange->gpiofunc; + pcs->write(data, pcs->base + pin * mux_bytes); + break; + } + return 0; } -static struct pinmux_ops pcs_pinmux_ops = { +static const struct pinmux_ops pcs_pinmux_ops = { .get_functions_count = pcs_get_functions_count, .get_function_name = pcs_get_function_name, .get_function_groups = pcs_get_function_groups, @@ -417,32 +526,190 @@ static struct pinmux_ops pcs_pinmux_ops = { .gpio_request_enable = pcs_request_gpio, }; +/* Clear BIAS value */ +static void pcs_pinconf_clear_bias(struct pinctrl_dev *pctldev, unsigned pin) +{ + unsigned long config; + int i; + for (i = 0; i < ARRAY_SIZE(pcs_bias); i++) { + config = pinconf_to_config_packed(pcs_bias[i], 0); + pcs_pinconf_set(pctldev, pin, config); + } +} + +/* + * Check whether PIN_CONFIG_BIAS_DISABLE is valid. + * It's depend on that PULL_DOWN & PULL_UP configs are all invalid. + */ +static bool pcs_pinconf_bias_disable(struct pinctrl_dev *pctldev, unsigned pin) +{ + unsigned long config; + int i; + + for (i = 0; i < ARRAY_SIZE(pcs_bias); i++) { + config = pinconf_to_config_packed(pcs_bias[i], 0); + if (!pcs_pinconf_get(pctldev, pin, &config)) + goto out; + } + return true; +out: + return false; +} + static int pcs_pinconf_get(struct pinctrl_dev *pctldev, unsigned pin, unsigned long *config) { + struct pcs_device *pcs = pinctrl_dev_get_drvdata(pctldev); + struct pcs_function *func; + enum pin_config_param param; + unsigned offset = 0, data = 0, i, j, ret; + + ret = pcs_get_function(pctldev, pin, &func); + if (ret) + return ret; + + for (i = 0; i < func->nconfs; i++) { + param = pinconf_to_config_param(*config); + if (param == PIN_CONFIG_BIAS_DISABLE) { + if (pcs_pinconf_bias_disable(pctldev, pin)) { + *config = 0; + return 0; + } else { + return -ENOTSUPP; + } + } else if (param != func->conf[i].param) { + continue; + } + + offset = pin * (pcs->width / BITS_PER_BYTE); + data = pcs->read(pcs->base + offset) & func->conf[i].mask; + switch (func->conf[i].param) { + /* 4 parameters */ + case PIN_CONFIG_BIAS_PULL_DOWN: + case PIN_CONFIG_BIAS_PULL_UP: + case PIN_CONFIG_INPUT_SCHMITT_ENABLE: + if ((data != func->conf[i].enable) || + (data == func->conf[i].disable)) + return -ENOTSUPP; + *config = 0; + break; + /* 2 parameters */ + case PIN_CONFIG_INPUT_SCHMITT: + for (j = 0; j < func->nconfs; j++) { + switch (func->conf[j].param) { + case PIN_CONFIG_INPUT_SCHMITT_ENABLE: + if (data != func->conf[j].enable) + return -ENOTSUPP; + break; + default: + break; + } + } + *config = data; + break; + case PIN_CONFIG_DRIVE_STRENGTH: + case PIN_CONFIG_SLEW_RATE: + default: + *config = data; + break; + } + return 0; + } return -ENOTSUPP; } static int pcs_pinconf_set(struct pinctrl_dev *pctldev, unsigned pin, unsigned long config) { + struct pcs_device *pcs = pinctrl_dev_get_drvdata(pctldev); + struct pcs_function *func; + unsigned offset = 0, shift = 0, i, data, ret; + u16 arg; + + ret = pcs_get_function(pctldev, pin, &func); + if (ret) + return ret; + + for (i = 0; i < func->nconfs; i++) { + if (pinconf_to_config_param(config) == func->conf[i].param) { + offset = pin * (pcs->width / BITS_PER_BYTE); + data = pcs->read(pcs->base + offset); + arg = pinconf_to_config_argument(config); + switch (func->conf[i].param) { + /* 2 parameters */ + case PIN_CONFIG_INPUT_SCHMITT: + case PIN_CONFIG_DRIVE_STRENGTH: + case PIN_CONFIG_SLEW_RATE: + shift = ffs(func->conf[i].mask) - 1; + data &= ~func->conf[i].mask; + data |= (arg << shift) & func->conf[i].mask; + break; + /* 4 parameters */ + case PIN_CONFIG_BIAS_DISABLE: + pcs_pinconf_clear_bias(pctldev, pin); + break; + case PIN_CONFIG_BIAS_PULL_DOWN: + case PIN_CONFIG_BIAS_PULL_UP: + if (arg) + pcs_pinconf_clear_bias(pctldev, pin); + /* fall through */ + case PIN_CONFIG_INPUT_SCHMITT_ENABLE: + data &= ~func->conf[i].mask; + if (arg) + data |= func->conf[i].enable; + else + data |= func->conf[i].disable; + break; + default: + return -ENOTSUPP; + } + pcs->write(data, pcs->base + offset); + return 0; + } + } return -ENOTSUPP; } static int pcs_pinconf_group_get(struct pinctrl_dev *pctldev, unsigned group, unsigned long *config) { - return -ENOTSUPP; + const unsigned *pins; + unsigned npins, old = 0; + int i, ret; + + ret = pcs_get_group_pins(pctldev, group, &pins, &npins); + if (ret) + return ret; + for (i = 0; i < npins; i++) { + if (pcs_pinconf_get(pctldev, pins[i], config)) + return -ENOTSUPP; + /* configs do not match between two pins */ + if (i && (old != *config)) + return -ENOTSUPP; + old = *config; + } + return 0; } static int pcs_pinconf_group_set(struct pinctrl_dev *pctldev, unsigned group, unsigned long config) { - return -ENOTSUPP; + const unsigned *pins; + unsigned npins; + int i, ret; + + ret = pcs_get_group_pins(pctldev, group, &pins, &npins); + if (ret) + return ret; + for (i = 0; i < npins; i++) { + if (pcs_pinconf_set(pctldev, pins[i], config)) + return -ENOTSUPP; + } + return 0; } static void pcs_pinconf_dbg_show(struct pinctrl_dev *pctldev, - struct seq_file *s, unsigned offset) + struct seq_file *s, unsigned pin) { } @@ -451,13 +718,22 @@ static void pcs_pinconf_group_dbg_show(struct pinctrl_dev *pctldev, { } -static struct pinconf_ops pcs_pinconf_ops = { +static void pcs_pinconf_config_dbg_show(struct pinctrl_dev *pctldev, + struct seq_file *s, + unsigned long config) +{ + pinconf_generic_dump_config(pctldev, s, config); +} + +static const struct pinconf_ops pcs_pinconf_ops = { .pin_config_get = pcs_pinconf_get, .pin_config_set = pcs_pinconf_set, .pin_config_group_get = pcs_pinconf_group_get, .pin_config_group_set = pcs_pinconf_group_set, .pin_config_dbg_show = pcs_pinconf_dbg_show, .pin_config_group_dbg_show = pcs_pinconf_group_dbg_show, + .pin_config_config_dbg_show = pcs_pinconf_config_dbg_show, + .is_generic = true, }; /** @@ -648,11 +924,158 @@ static int pcs_get_pin_by_offset(struct pcs_device *pcs, unsigned offset) return index; } +/* + * check whether data matches enable bits or disable bits + * Return value: 1 for matching enable bits, 0 for matching disable bits, + * and negative value for matching failure. + */ +static int pcs_config_match(unsigned data, unsigned enable, unsigned disable) +{ + int ret = -EINVAL; + + if (data == enable) + ret = 1; + else if (data == disable) + ret = 0; + return ret; +} + +static void add_config(struct pcs_conf_vals **conf, enum pin_config_param param, + unsigned value, unsigned enable, unsigned disable, + unsigned mask) +{ + (*conf)->param = param; + (*conf)->val = value; + (*conf)->enable = enable; + (*conf)->disable = disable; + (*conf)->mask = mask; + (*conf)++; +} + +static void add_setting(unsigned long **setting, enum pin_config_param param, + unsigned arg) +{ + **setting = pinconf_to_config_packed(param, arg); + (*setting)++; +} + +/* add pinconf setting with 2 parameters */ +static void pcs_add_conf2(struct pcs_device *pcs, struct device_node *np, + const char *name, enum pin_config_param param, + struct pcs_conf_vals **conf, unsigned long **settings) +{ + unsigned value[2], shift; + int ret; + + ret = of_property_read_u32_array(np, name, value, 2); + if (ret) + return; + /* set value & mask */ + value[0] &= value[1]; + shift = ffs(value[1]) - 1; + /* skip enable & disable */ + add_config(conf, param, value[0], 0, 0, value[1]); + add_setting(settings, param, value[0] >> shift); +} + +/* add pinconf setting with 4 parameters */ +static void pcs_add_conf4(struct pcs_device *pcs, struct device_node *np, + const char *name, enum pin_config_param param, + struct pcs_conf_vals **conf, unsigned long **settings) +{ + unsigned value[4]; + int ret; + + /* value to set, enable, disable, mask */ + ret = of_property_read_u32_array(np, name, value, 4); + if (ret) + return; + if (!value[3]) { + dev_err(pcs->dev, "mask field of the property can't be 0\n"); + return; + } + value[0] &= value[3]; + value[1] &= value[3]; + value[2] &= value[3]; + ret = pcs_config_match(value[0], value[1], value[2]); + if (ret < 0) + dev_dbg(pcs->dev, "failed to match enable or disable bits\n"); + add_config(conf, param, value[0], value[1], value[2], value[3]); + add_setting(settings, param, ret); +} + +static int pcs_parse_pinconf(struct pcs_device *pcs, struct device_node *np, + struct pcs_function *func, + struct pinctrl_map **map) + +{ + struct pinctrl_map *m = *map; + int i = 0, nconfs = 0; + unsigned long *settings = NULL, *s = NULL; + struct pcs_conf_vals *conf = NULL; + struct pcs_conf_type prop2[] = { + { "pinctrl-single,drive-strength", PIN_CONFIG_DRIVE_STRENGTH, }, + { "pinctrl-single,slew-rate", PIN_CONFIG_SLEW_RATE, }, + { "pinctrl-single,input-schmitt", PIN_CONFIG_INPUT_SCHMITT, }, + }; + struct pcs_conf_type prop4[] = { + { "pinctrl-single,bias-pullup", PIN_CONFIG_BIAS_PULL_UP, }, + { "pinctrl-single,bias-pulldown", PIN_CONFIG_BIAS_PULL_DOWN, }, + { "pinctrl-single,input-schmitt-enable", + PIN_CONFIG_INPUT_SCHMITT_ENABLE, }, + }; + + /* If pinconf isn't supported, don't parse properties in below. */ + if (!pcs->is_pinconf) + return 0; + + /* cacluate how much properties are supported in current node */ + for (i = 0; i < ARRAY_SIZE(prop2); i++) { + if (of_find_property(np, prop2[i].name, NULL)) + nconfs++; + } + for (i = 0; i < ARRAY_SIZE(prop4); i++) { + if (of_find_property(np, prop4[i].name, NULL)) + nconfs++; + } + if (!nconfs) + return 0; + + func->conf = devm_kzalloc(pcs->dev, + sizeof(struct pcs_conf_vals) * nconfs, + GFP_KERNEL); + if (!func->conf) + return -ENOMEM; + func->nconfs = nconfs; + conf = &(func->conf[0]); + m++; + settings = devm_kzalloc(pcs->dev, sizeof(unsigned long) * nconfs, + GFP_KERNEL); + if (!settings) + return -ENOMEM; + s = &settings[0]; + + for (i = 0; i < ARRAY_SIZE(prop2); i++) + pcs_add_conf2(pcs, np, prop2[i].name, prop2[i].param, + &conf, &s); + for (i = 0; i < ARRAY_SIZE(prop4); i++) + pcs_add_conf4(pcs, np, prop4[i].name, prop4[i].param, + &conf, &s); + m->type = PIN_MAP_TYPE_CONFIGS_GROUP; + m->data.configs.group_or_pin = np->name; + m->data.configs.configs = settings; + m->data.configs.num_configs = nconfs; + return 0; +} + +static void pcs_free_pingroups(struct pcs_device *pcs); + /** * smux_parse_one_pinctrl_entry() - parses a device tree mux entry * @pcs: pinctrl driver instance * @np: device node of the mux entry * @map: map entry + * @num_maps: number of map * @pgnames: pingroup names * * Note that this binding currently supports only sets of one register + value. @@ -669,6 +1092,7 @@ static int pcs_get_pin_by_offset(struct pcs_device *pcs, unsigned offset) static int pcs_parse_one_pinctrl_entry(struct pcs_device *pcs, struct device_node *np, struct pinctrl_map **map, + unsigned *num_maps, const char **pgnames) { struct pcs_func_vals *vals; @@ -741,8 +1165,18 @@ static int pcs_parse_one_pinctrl_entry(struct pcs_device *pcs, (*map)->data.mux.group = np->name; (*map)->data.mux.function = np->name; + if (pcs->is_pinconf) { + if (pcs_parse_pinconf(pcs, np, function, map)) + goto free_pingroups; + *num_maps = 2; + } else { + *num_maps = 1; + } return 0; +free_pingroups: + pcs_free_pingroups(pcs); + *num_maps = 1; free_function: pcs_remove_function(pcs, function); @@ -771,7 +1205,8 @@ static int pcs_dt_node_to_map(struct pinctrl_dev *pctldev, pcs = pinctrl_dev_get_drvdata(pctldev); - *map = devm_kzalloc(pcs->dev, sizeof(**map), GFP_KERNEL); + /* create 2 maps. One is for pinmux, and the other is for pinconf. */ + *map = devm_kzalloc(pcs->dev, sizeof(**map) * 2, GFP_KERNEL); if (!*map) return -ENOMEM; @@ -783,13 +1218,13 @@ static int pcs_dt_node_to_map(struct pinctrl_dev *pctldev, goto free_map; } - ret = pcs_parse_one_pinctrl_entry(pcs, np_config, map, pgnames); + ret = pcs_parse_one_pinctrl_entry(pcs, np_config, map, num_maps, + pgnames); if (ret < 0) { dev_err(pcs->dev, "no pins entries for %s\n", np_config->name); goto free_pgnames; } - *num_maps = 1; return 0; @@ -879,6 +1314,37 @@ static void pcs_free_resources(struct pcs_device *pcs) static struct of_device_id pcs_of_match[]; +static int pcs_add_gpio_func(struct device_node *node, struct pcs_device *pcs) +{ + const char *propname = "pinctrl-single,gpio-range"; + const char *cellname = "#pinctrl-single,gpio-range-cells"; + struct of_phandle_args gpiospec; + struct pcs_gpiofunc_range *range; + int ret, i; + + for (i = 0; ; i++) { + ret = of_parse_phandle_with_args(node, propname, cellname, + i, &gpiospec); + /* Do not treat it as error. Only treat it as end condition. */ + if (ret) { + ret = 0; + break; + } + range = devm_kzalloc(pcs->dev, sizeof(*range), GFP_KERNEL); + if (!range) { + ret = -ENOMEM; + break; + } + range->offset = gpiospec.args[0]; + range->npins = gpiospec.args[1]; + range->gpiofunc = gpiospec.args[2]; + mutex_lock(&pcs->mutex); + list_add_tail(&range->node, &pcs->gpiofuncs); + mutex_unlock(&pcs->mutex); + } + return ret; +} + static int pcs_probe(struct platform_device *pdev) { struct device_node *np = pdev->dev.of_node; @@ -900,14 +1366,23 @@ static int pcs_probe(struct platform_device *pdev) mutex_init(&pcs->mutex); INIT_LIST_HEAD(&pcs->pingroups); INIT_LIST_HEAD(&pcs->functions); + INIT_LIST_HEAD(&pcs->gpiofuncs); + pcs->is_pinconf = match->data; PCS_GET_PROP_U32("pinctrl-single,register-width", &pcs->width, "register width not specified\n"); - PCS_GET_PROP_U32("pinctrl-single,function-mask", &pcs->fmask, - "function register mask not specified\n"); - pcs->fshift = ffs(pcs->fmask) - 1; - pcs->fmax = pcs->fmask >> pcs->fshift; + ret = of_property_read_u32(np, "pinctrl-single,function-mask", + &pcs->fmask); + if (!ret) { + pcs->fshift = ffs(pcs->fmask) - 1; + pcs->fmax = pcs->fmask >> pcs->fshift; + } else { + /* If mask property doesn't exist, function mux is invalid. */ + pcs->fmask = 0; + pcs->fshift = 0; + pcs->fmax = 0; + } ret = of_property_read_u32(np, "pinctrl-single,function-off", &pcs->foff); @@ -961,7 +1436,8 @@ static int pcs_probe(struct platform_device *pdev) pcs->desc.name = DRIVER_NAME; pcs->desc.pctlops = &pcs_pinctrl_ops; pcs->desc.pmxops = &pcs_pinmux_ops; - pcs->desc.confops = &pcs_pinconf_ops; + if (pcs->is_pinconf) + pcs->desc.confops = &pcs_pinconf_ops; pcs->desc.owner = THIS_MODULE; ret = pcs_allocate_pin_table(pcs); @@ -975,6 +1451,10 @@ static int pcs_probe(struct platform_device *pdev) goto free; } + ret = pcs_add_gpio_func(np, pcs); + if (ret < 0) + goto free; + dev_info(pcs->dev, "%i pins at pa %p size %u\n", pcs->desc.npins, pcs->base, pcs->size); @@ -999,7 +1479,8 @@ static int pcs_remove(struct platform_device *pdev) } static struct of_device_id pcs_of_match[] = { - { .compatible = DRIVER_NAME, }, + { .compatible = "pinctrl-single", .data = (void *)false }, + { .compatible = "pinconf-single", .data = (void *)true }, { }, }; MODULE_DEVICE_TABLE(of, pcs_of_match); diff --git a/drivers/pinctrl/pinctrl-sirf.c b/drivers/pinctrl/pinctrl-sirf.c index d02498b30c6e..fb9062570745 100644 --- a/drivers/pinctrl/pinctrl-sirf.c +++ b/drivers/pinctrl/pinctrl-sirf.c @@ -979,7 +979,7 @@ static void sirfsoc_dt_free_map(struct pinctrl_dev *pctldev, kfree(map); } -static struct pinctrl_ops sirfsoc_pctrl_ops = { +static const struct pinctrl_ops sirfsoc_pctrl_ops = { .get_groups_count = sirfsoc_get_groups_count, .get_group_name = sirfsoc_get_group_name, .get_group_pins = sirfsoc_get_group_pins, @@ -1181,7 +1181,7 @@ static int sirfsoc_pinmux_request_gpio(struct pinctrl_dev *pmxdev, return 0; } -static struct pinmux_ops sirfsoc_pinmux_ops = { +static const struct pinmux_ops sirfsoc_pinmux_ops = { .enable = sirfsoc_pinmux_enable, .disable = sirfsoc_pinmux_disable, .get_functions_count = sirfsoc_pinmux_get_funcs_count, @@ -1685,15 +1685,12 @@ static void sirfsoc_gpio_set_pullup(const u32 *pullups) const unsigned long *p = (const unsigned long *)pullups; for (i = 0; i < SIRFSOC_GPIO_NO_OF_BANKS; i++) { - n = find_first_bit(p + i, BITS_PER_LONG); - while (n < BITS_PER_LONG) { + for_each_set_bit(n, p + i, BITS_PER_LONG) { u32 offset = SIRFSOC_GPIO_CTRL(i, n); u32 val = readl(sgpio_bank[i].chip.regs + offset); val |= SIRFSOC_GPIO_CTL_PULL_MASK; val |= SIRFSOC_GPIO_CTL_PULL_HIGH; writel(val, sgpio_bank[i].chip.regs + offset); - - n = find_next_bit(p + i, BITS_PER_LONG, n + 1); } } } @@ -1704,15 +1701,12 @@ static void sirfsoc_gpio_set_pulldown(const u32 *pulldowns) const unsigned long *p = (const unsigned long *)pulldowns; for (i = 0; i < SIRFSOC_GPIO_NO_OF_BANKS; i++) { - n = find_first_bit(p + i, BITS_PER_LONG); - while (n < BITS_PER_LONG) { + for_each_set_bit(n, p + i, BITS_PER_LONG) { u32 offset = SIRFSOC_GPIO_CTRL(i, n); u32 val = readl(sgpio_bank[i].chip.regs + offset); val |= SIRFSOC_GPIO_CTL_PULL_MASK; val &= ~SIRFSOC_GPIO_CTL_PULL_HIGH; writel(val, sgpio_bank[i].chip.regs + offset); - - n = find_next_bit(p + i, BITS_PER_LONG, n + 1); } } } diff --git a/drivers/pinctrl/pinctrl-sunxi.c b/drivers/pinctrl/pinctrl-sunxi.c index 80b11e3415bc..c52fc2c08732 100644 --- a/drivers/pinctrl/pinctrl-sunxi.c +++ b/drivers/pinctrl/pinctrl-sunxi.c @@ -11,6 +11,7 @@ */ #include <linux/io.h> +#include <linux/clk.h> #include <linux/gpio.h> #include <linux/module.h> #include <linux/of.h> @@ -30,482 +31,856 @@ static const struct sunxi_desc_pin sun4i_a10_pins[] = { SUNXI_PIN(SUNXI_PINCTRL_PIN_PA0, SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out")), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "emac"), /* ERXD3 */ + SUNXI_FUNCTION(0x3, "spi1"), /* CS0 */ + SUNXI_FUNCTION(0x4, "uart2")), /* RTS */ SUNXI_PIN(SUNXI_PINCTRL_PIN_PA1, SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out")), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "emac"), /* ERXD2 */ + SUNXI_FUNCTION(0x3, "spi1"), /* CLK */ + SUNXI_FUNCTION(0x4, "uart2")), /* CTS */ SUNXI_PIN(SUNXI_PINCTRL_PIN_PA2, SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out")), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "emac"), /* ERXD1 */ + SUNXI_FUNCTION(0x3, "spi1"), /* MOSI */ + SUNXI_FUNCTION(0x4, "uart2")), /* TX */ SUNXI_PIN(SUNXI_PINCTRL_PIN_PA3, SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out")), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "emac"), /* ERXD0 */ + SUNXI_FUNCTION(0x3, "spi1"), /* MISO */ + SUNXI_FUNCTION(0x4, "uart2")), /* RX */ SUNXI_PIN(SUNXI_PINCTRL_PIN_PA4, SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out")), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "emac"), /* ETXD3 */ + SUNXI_FUNCTION(0x3, "spi1")), /* CS1 */ SUNXI_PIN(SUNXI_PINCTRL_PIN_PA5, SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out")), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "emac"), /* ETXD2 */ + SUNXI_FUNCTION(0x3, "spi3")), /* CS0 */ SUNXI_PIN(SUNXI_PINCTRL_PIN_PA6, SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out")), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "emac"), /* ETXD1 */ + SUNXI_FUNCTION(0x3, "spi3")), /* CLK */ SUNXI_PIN(SUNXI_PINCTRL_PIN_PA7, SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out")), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "emac"), /* ETXD0 */ + SUNXI_FUNCTION(0x3, "spi3")), /* MOSI */ SUNXI_PIN(SUNXI_PINCTRL_PIN_PA8, SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out")), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "emac"), /* ERXCK */ + SUNXI_FUNCTION(0x3, "spi3")), /* MISO */ SUNXI_PIN(SUNXI_PINCTRL_PIN_PA9, SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out")), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "emac"), /* ERXERR */ + SUNXI_FUNCTION(0x3, "spi3")), /* CS1 */ SUNXI_PIN(SUNXI_PINCTRL_PIN_PA10, SUNXI_FUNCTION(0x0, "gpio_in"), SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "emac"), /* ERXDV */ SUNXI_FUNCTION(0x4, "uart1")), /* TX */ SUNXI_PIN(SUNXI_PINCTRL_PIN_PA11, SUNXI_FUNCTION(0x0, "gpio_in"), SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "emac"), /* EMDC */ SUNXI_FUNCTION(0x4, "uart1")), /* RX */ SUNXI_PIN(SUNXI_PINCTRL_PIN_PA12, SUNXI_FUNCTION(0x0, "gpio_in"), SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "emac"), /* EMDIO */ + SUNXI_FUNCTION(0x3, "uart6"), /* TX */ SUNXI_FUNCTION(0x4, "uart1")), /* RTS */ SUNXI_PIN(SUNXI_PINCTRL_PIN_PA13, SUNXI_FUNCTION(0x0, "gpio_in"), SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "emac"), /* ETXEN */ + SUNXI_FUNCTION(0x3, "uart6"), /* RX */ SUNXI_FUNCTION(0x4, "uart1")), /* CTS */ SUNXI_PIN(SUNXI_PINCTRL_PIN_PA14, SUNXI_FUNCTION(0x0, "gpio_in"), SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "emac"), /* ETXCK */ + SUNXI_FUNCTION(0x3, "uart7"), /* TX */ SUNXI_FUNCTION(0x4, "uart1")), /* DTR */ SUNXI_PIN(SUNXI_PINCTRL_PIN_PA15, SUNXI_FUNCTION(0x0, "gpio_in"), SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "emac"), /* ECRS */ + SUNXI_FUNCTION(0x3, "uart7"), /* RX */ SUNXI_FUNCTION(0x4, "uart1")), /* DSR */ SUNXI_PIN(SUNXI_PINCTRL_PIN_PA16, SUNXI_FUNCTION(0x0, "gpio_in"), SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "emac"), /* ECOL */ + SUNXI_FUNCTION(0x3, "can"), /* TX */ SUNXI_FUNCTION(0x4, "uart1")), /* DCD */ SUNXI_PIN(SUNXI_PINCTRL_PIN_PA17, SUNXI_FUNCTION(0x0, "gpio_in"), SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "emac"), /* ETXERR */ + SUNXI_FUNCTION(0x3, "can"), /* RX */ SUNXI_FUNCTION(0x4, "uart1")), /* RING */ /* Hole */ SUNXI_PIN(SUNXI_PINCTRL_PIN_PB0, SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out")), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "i2c0")), /* SCK */ SUNXI_PIN(SUNXI_PINCTRL_PIN_PB1, SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out")), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "i2c0")), /* SDA */ SUNXI_PIN(SUNXI_PINCTRL_PIN_PB2, SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out")), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "pwm")), /* PWM0 */ SUNXI_PIN(SUNXI_PINCTRL_PIN_PB3, SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out")), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "ir0")), /* TX */ SUNXI_PIN(SUNXI_PINCTRL_PIN_PB4, SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out")), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "ir0")), /* RX */ SUNXI_PIN(SUNXI_PINCTRL_PIN_PB5, SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out")), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "i2s"), /* MCLK */ + SUNXI_FUNCTION(0x3, "ac97")), /* MCLK */ SUNXI_PIN(SUNXI_PINCTRL_PIN_PB6, SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out")), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "i2s"), /* BCLK */ + SUNXI_FUNCTION(0x3, "ac97")), /* BCLK */ SUNXI_PIN(SUNXI_PINCTRL_PIN_PB7, SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out")), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "i2s"), /* LRCK */ + SUNXI_FUNCTION(0x3, "ac97")), /* SYNC */ SUNXI_PIN(SUNXI_PINCTRL_PIN_PB8, SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out")), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "i2s"), /* DO0 */ + SUNXI_FUNCTION(0x3, "ac97")), /* DO */ SUNXI_PIN(SUNXI_PINCTRL_PIN_PB9, SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out")), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "i2s")), /* DO1 */ SUNXI_PIN(SUNXI_PINCTRL_PIN_PB10, SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out")), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "i2s")), /* DO2 */ SUNXI_PIN(SUNXI_PINCTRL_PIN_PB11, SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out")), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "i2s")), /* DO3 */ SUNXI_PIN(SUNXI_PINCTRL_PIN_PB12, SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out")), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "i2s"), /* DI */ + SUNXI_FUNCTION(0x3, "ac97")), /* DI */ SUNXI_PIN(SUNXI_PINCTRL_PIN_PB13, SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out")), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "spi2")), /* CS1 */ SUNXI_PIN(SUNXI_PINCTRL_PIN_PB14, SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out")), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "spi2"), /* CS0 */ + SUNXI_FUNCTION(0x3, "jtag")), /* MS0 */ SUNXI_PIN(SUNXI_PINCTRL_PIN_PB15, SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out")), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "spi2"), /* CLK */ + SUNXI_FUNCTION(0x3, "jtag")), /* CK0 */ SUNXI_PIN(SUNXI_PINCTRL_PIN_PB16, SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out")), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "spi2"), /* MOSI */ + SUNXI_FUNCTION(0x3, "jtag")), /* DO0 */ SUNXI_PIN(SUNXI_PINCTRL_PIN_PB17, SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out")), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "spi2"), /* MISO */ + SUNXI_FUNCTION(0x3, "jtag")), /* DI0 */ SUNXI_PIN(SUNXI_PINCTRL_PIN_PB18, SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out")), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "i2c1")), /* SCK */ SUNXI_PIN(SUNXI_PINCTRL_PIN_PB19, SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out")), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "i2c1")), /* SDA */ SUNXI_PIN(SUNXI_PINCTRL_PIN_PB20, SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out")), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "i2c2")), /* SCK */ SUNXI_PIN(SUNXI_PINCTRL_PIN_PB21, SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out")), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "i2c2")), /* SDA */ SUNXI_PIN(SUNXI_PINCTRL_PIN_PB22, SUNXI_FUNCTION(0x0, "gpio_in"), SUNXI_FUNCTION(0x1, "gpio_out"), - SUNXI_FUNCTION(0x2, "uart0")), /* TX */ + SUNXI_FUNCTION(0x2, "uart0"), /* TX */ + SUNXI_FUNCTION(0x3, "ir1")), /* TX */ SUNXI_PIN(SUNXI_PINCTRL_PIN_PB23, SUNXI_FUNCTION(0x0, "gpio_in"), SUNXI_FUNCTION(0x1, "gpio_out"), - SUNXI_FUNCTION(0x2, "uart0")), /* RX */ + SUNXI_FUNCTION(0x2, "uart0"), /* RX */ + SUNXI_FUNCTION(0x3, "ir1")), /* RX */ /* Hole */ SUNXI_PIN(SUNXI_PINCTRL_PIN_PC0, SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out")), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "nand0"), /* NWE */ + SUNXI_FUNCTION(0x3, "spi0")), /* MOSI */ SUNXI_PIN(SUNXI_PINCTRL_PIN_PC1, SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out")), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "nand0"), /* NALE */ + SUNXI_FUNCTION(0x3, "spi0")), /* MISO */ SUNXI_PIN(SUNXI_PINCTRL_PIN_PC2, SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out")), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "nand0"), /* NCLE */ + SUNXI_FUNCTION(0x3, "spi0")), /* SCK */ SUNXI_PIN(SUNXI_PINCTRL_PIN_PC3, SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out")), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "nand0")), /* NCE1 */ SUNXI_PIN(SUNXI_PINCTRL_PIN_PC4, SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out")), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "nand0")), /* NCE0 */ SUNXI_PIN(SUNXI_PINCTRL_PIN_PC5, SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out")), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "nand0")), /* NRE# */ SUNXI_PIN(SUNXI_PINCTRL_PIN_PC6, SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out")), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "nand0"), /* NRB0 */ + SUNXI_FUNCTION(0x3, "mmc2")), /* CMD */ SUNXI_PIN(SUNXI_PINCTRL_PIN_PC7, SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out")), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "nand0"), /* NRB1 */ + SUNXI_FUNCTION(0x3, "mmc2")), /* CLK */ SUNXI_PIN(SUNXI_PINCTRL_PIN_PC8, SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out")), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "nand0"), /* NDQ0 */ + SUNXI_FUNCTION(0x3, "mmc2")), /* D0 */ SUNXI_PIN(SUNXI_PINCTRL_PIN_PC9, SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out")), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "nand0"), /* NDQ1 */ + SUNXI_FUNCTION(0x3, "mmc2")), /* D1 */ SUNXI_PIN(SUNXI_PINCTRL_PIN_PC10, SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out")), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "nand0"), /* NDQ2 */ + SUNXI_FUNCTION(0x3, "mmc2")), /* D2 */ SUNXI_PIN(SUNXI_PINCTRL_PIN_PC11, SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out")), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "nand0"), /* NDQ3 */ + SUNXI_FUNCTION(0x3, "mmc2")), /* D3 */ SUNXI_PIN(SUNXI_PINCTRL_PIN_PC12, SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out")), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "nand0")), /* NDQ4 */ SUNXI_PIN(SUNXI_PINCTRL_PIN_PC13, SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out")), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "nand0")), /* NDQ5 */ SUNXI_PIN(SUNXI_PINCTRL_PIN_PC14, SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out")), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "nand0")), /* NDQ6 */ SUNXI_PIN(SUNXI_PINCTRL_PIN_PC15, SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out")), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "nand0")), /* NDQ7 */ SUNXI_PIN(SUNXI_PINCTRL_PIN_PC16, SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out")), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "nand0")), /* NWP */ SUNXI_PIN(SUNXI_PINCTRL_PIN_PC17, SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out")), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "nand0")), /* NCE2 */ SUNXI_PIN(SUNXI_PINCTRL_PIN_PC18, SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out")), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "nand0")), /* NCE3 */ SUNXI_PIN(SUNXI_PINCTRL_PIN_PC19, SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out")), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "nand0"), /* NCE4 */ + SUNXI_FUNCTION(0x3, "spi2")), /* CS0 */ SUNXI_PIN(SUNXI_PINCTRL_PIN_PC20, SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out")), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "nand0"), /* NCE5 */ + SUNXI_FUNCTION(0x3, "spi2")), /* CLK */ SUNXI_PIN(SUNXI_PINCTRL_PIN_PC21, SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out")), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "nand0"), /* NCE6 */ + SUNXI_FUNCTION(0x3, "spi2")), /* MOSI */ SUNXI_PIN(SUNXI_PINCTRL_PIN_PC22, SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out")), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "nand0"), /* NCE7 */ + SUNXI_FUNCTION(0x3, "spi2")), /* MISO */ SUNXI_PIN(SUNXI_PINCTRL_PIN_PC23, SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out")), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x3, "spi0")), /* CS0 */ SUNXI_PIN(SUNXI_PINCTRL_PIN_PC24, SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out")), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "nand0")), /* NDQS */ /* Hole */ SUNXI_PIN(SUNXI_PINCTRL_PIN_PD0, SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out")), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "lcd0"), /* D0 */ + SUNXI_FUNCTION(0x3, "lvds0")), /* VP0 */ SUNXI_PIN(SUNXI_PINCTRL_PIN_PD1, SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out")), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "lcd0"), /* D1 */ + SUNXI_FUNCTION(0x3, "lvds0")), /* VN0 */ SUNXI_PIN(SUNXI_PINCTRL_PIN_PD2, SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out")), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "lcd0"), /* D2 */ + SUNXI_FUNCTION(0x3, "lvds0")), /* VP1 */ SUNXI_PIN(SUNXI_PINCTRL_PIN_PD3, SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out")), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "lcd0"), /* D3 */ + SUNXI_FUNCTION(0x3, "lvds0")), /* VN1 */ SUNXI_PIN(SUNXI_PINCTRL_PIN_PD4, SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out")), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "lcd0"), /* D4 */ + SUNXI_FUNCTION(0x3, "lvds0")), /* VP2 */ SUNXI_PIN(SUNXI_PINCTRL_PIN_PD5, SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out")), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "lcd0"), /* D5 */ + SUNXI_FUNCTION(0x3, "lvds0")), /* VN2 */ SUNXI_PIN(SUNXI_PINCTRL_PIN_PD6, SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out")), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "lcd0"), /* D6 */ + SUNXI_FUNCTION(0x3, "lvds0")), /* VPC */ SUNXI_PIN(SUNXI_PINCTRL_PIN_PD7, SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out")), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "lcd0"), /* D7 */ + SUNXI_FUNCTION(0x3, "lvds0")), /* VNC */ SUNXI_PIN(SUNXI_PINCTRL_PIN_PD8, SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out")), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "lcd0"), /* D8 */ + SUNXI_FUNCTION(0x3, "lvds0")), /* VP3 */ SUNXI_PIN(SUNXI_PINCTRL_PIN_PD9, SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out")), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "lcd0"), /* D9 */ + SUNXI_FUNCTION(0x3, "lvds0")), /* VM3 */ SUNXI_PIN(SUNXI_PINCTRL_PIN_PD10, SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out")), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "lcd0"), /* D10 */ + SUNXI_FUNCTION(0x3, "lvds1")), /* VP0 */ SUNXI_PIN(SUNXI_PINCTRL_PIN_PD11, SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out")), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "lcd0"), /* D11 */ + SUNXI_FUNCTION(0x3, "lvds1")), /* VN0 */ SUNXI_PIN(SUNXI_PINCTRL_PIN_PD12, SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out")), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "lcd0"), /* D12 */ + SUNXI_FUNCTION(0x3, "lvds1")), /* VP1 */ SUNXI_PIN(SUNXI_PINCTRL_PIN_PD13, SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out")), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "lcd0"), /* D13 */ + SUNXI_FUNCTION(0x3, "lvds1")), /* VN1 */ SUNXI_PIN(SUNXI_PINCTRL_PIN_PD14, SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out")), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "lcd0"), /* D14 */ + SUNXI_FUNCTION(0x3, "lvds1")), /* VP2 */ SUNXI_PIN(SUNXI_PINCTRL_PIN_PD15, SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out")), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "lcd0"), /* D15 */ + SUNXI_FUNCTION(0x3, "lvds1")), /* VN2 */ SUNXI_PIN(SUNXI_PINCTRL_PIN_PD16, SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out")), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "lcd0"), /* D16 */ + SUNXI_FUNCTION(0x3, "lvds1")), /* VPC */ SUNXI_PIN(SUNXI_PINCTRL_PIN_PD17, SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out")), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "lcd0"), /* D17 */ + SUNXI_FUNCTION(0x3, "lvds1")), /* VNC */ SUNXI_PIN(SUNXI_PINCTRL_PIN_PD18, SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out")), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "lcd0"), /* D18 */ + SUNXI_FUNCTION(0x3, "lvds1")), /* VP3 */ SUNXI_PIN(SUNXI_PINCTRL_PIN_PD19, SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out")), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "lcd0"), /* D19 */ + SUNXI_FUNCTION(0x3, "lvds1")), /* VN3 */ SUNXI_PIN(SUNXI_PINCTRL_PIN_PD20, SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out")), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "lcd0"), /* D20 */ + SUNXI_FUNCTION(0x3, "csi1")), /* MCLK */ SUNXI_PIN(SUNXI_PINCTRL_PIN_PD21, SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out")), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "lcd0"), /* D21 */ + SUNXI_FUNCTION(0x3, "sim")), /* VPPEN */ SUNXI_PIN(SUNXI_PINCTRL_PIN_PD22, SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out")), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "lcd0"), /* D22 */ + SUNXI_FUNCTION(0x3, "sim")), /* VPPPP */ SUNXI_PIN(SUNXI_PINCTRL_PIN_PD23, SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out")), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "lcd0"), /* D23 */ + SUNXI_FUNCTION(0x3, "sim")), /* DET */ SUNXI_PIN(SUNXI_PINCTRL_PIN_PD24, SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out")), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "lcd0"), /* CLK */ + SUNXI_FUNCTION(0x3, "sim")), /* VCCEN */ SUNXI_PIN(SUNXI_PINCTRL_PIN_PD25, SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out")), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "lcd0"), /* DE */ + SUNXI_FUNCTION(0x3, "sim")), /* RST */ SUNXI_PIN(SUNXI_PINCTRL_PIN_PD26, SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out")), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "lcd0"), /* HSYNC */ + SUNXI_FUNCTION(0x3, "sim")), /* SCK */ SUNXI_PIN(SUNXI_PINCTRL_PIN_PD27, SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out")), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "lcd0"), /* VSYNC */ + SUNXI_FUNCTION(0x3, "sim")), /* SDA */ /* Hole */ SUNXI_PIN(SUNXI_PINCTRL_PIN_PE0, SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out")), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "ts0"), /* CLK */ + SUNXI_FUNCTION(0x3, "csi0")), /* PCK */ SUNXI_PIN(SUNXI_PINCTRL_PIN_PE1, SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out")), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "ts0"), /* ERR */ + SUNXI_FUNCTION(0x3, "csi0")), /* CK */ SUNXI_PIN(SUNXI_PINCTRL_PIN_PE2, SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out")), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "ts0"), /* SYNC */ + SUNXI_FUNCTION(0x3, "csi0")), /* HSYNC */ SUNXI_PIN(SUNXI_PINCTRL_PIN_PE3, SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out")), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "ts0"), /* DVLD */ + SUNXI_FUNCTION(0x3, "csi0")), /* VSYNC */ SUNXI_PIN(SUNXI_PINCTRL_PIN_PE4, SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out")), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "ts0"), /* D0 */ + SUNXI_FUNCTION(0x3, "csi0")), /* D0 */ SUNXI_PIN(SUNXI_PINCTRL_PIN_PE5, SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out")), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "ts0"), /* D1 */ + SUNXI_FUNCTION(0x3, "csi0"), /* D1 */ + SUNXI_FUNCTION(0x4, "sim")), /* VPPEN */ SUNXI_PIN(SUNXI_PINCTRL_PIN_PE6, SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out")), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "ts0"), /* D2 */ + SUNXI_FUNCTION(0x3, "csi0")), /* D2 */ SUNXI_PIN(SUNXI_PINCTRL_PIN_PE7, SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out")), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "ts0"), /* D3 */ + SUNXI_FUNCTION(0x3, "csi0")), /* D3 */ SUNXI_PIN(SUNXI_PINCTRL_PIN_PE8, SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out")), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "ts0"), /* D4 */ + SUNXI_FUNCTION(0x3, "csi0")), /* D4 */ SUNXI_PIN(SUNXI_PINCTRL_PIN_PE9, SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out")), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "ts0"), /* D5 */ + SUNXI_FUNCTION(0x3, "csi0")), /* D5 */ SUNXI_PIN(SUNXI_PINCTRL_PIN_PE10, SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out")), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "ts0"), /* D6 */ + SUNXI_FUNCTION(0x3, "csi0")), /* D6 */ SUNXI_PIN(SUNXI_PINCTRL_PIN_PE11, SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out")), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "ts0"), /* D7 */ + SUNXI_FUNCTION(0x3, "csi0")), /* D7 */ /* Hole */ SUNXI_PIN(SUNXI_PINCTRL_PIN_PF0, SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out")), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "mmc0"), /* D1 */ + SUNXI_FUNCTION(0x4, "jtag")), /* MSI */ SUNXI_PIN(SUNXI_PINCTRL_PIN_PF1, SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out")), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "mmc0"), /* D0 */ + SUNXI_FUNCTION(0x4, "jtag")), /* DI1 */ SUNXI_PIN(SUNXI_PINCTRL_PIN_PF2, SUNXI_FUNCTION(0x0, "gpio_in"), SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "mmc0"), /* CLK */ SUNXI_FUNCTION(0x4, "uart0")), /* TX */ SUNXI_PIN(SUNXI_PINCTRL_PIN_PF3, SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out")), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "mmc0"), /* CMD */ + SUNXI_FUNCTION(0x4, "jtag")), /* DO1 */ SUNXI_PIN(SUNXI_PINCTRL_PIN_PF4, SUNXI_FUNCTION(0x0, "gpio_in"), SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "mmc0"), /* D3 */ SUNXI_FUNCTION(0x4, "uart0")), /* RX */ SUNXI_PIN(SUNXI_PINCTRL_PIN_PF5, SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out")), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "mmc0"), /* D2 */ + SUNXI_FUNCTION(0x4, "jtag")), /* CK1 */ /* Hole */ SUNXI_PIN(SUNXI_PINCTRL_PIN_PG0, SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out")), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "ts1"), /* CLK */ + SUNXI_FUNCTION(0x3, "csi1"), /* PCK */ + SUNXI_FUNCTION(0x4, "mmc1")), /* CMD */ SUNXI_PIN(SUNXI_PINCTRL_PIN_PG1, SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out")), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "ts1"), /* ERR */ + SUNXI_FUNCTION(0x3, "csi1"), /* CK */ + SUNXI_FUNCTION(0x4, "mmc1")), /* CLK */ SUNXI_PIN(SUNXI_PINCTRL_PIN_PG2, SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out")), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "ts1"), /* SYNC */ + SUNXI_FUNCTION(0x3, "csi1"), /* HSYNC */ + SUNXI_FUNCTION(0x4, "mmc1")), /* D0 */ SUNXI_PIN(SUNXI_PINCTRL_PIN_PG3, SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out")), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "ts1"), /* DVLD */ + SUNXI_FUNCTION(0x3, "csi1"), /* VSYNC */ + SUNXI_FUNCTION(0x4, "mmc1")), /* D1 */ SUNXI_PIN(SUNXI_PINCTRL_PIN_PG4, SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out")), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "ts1"), /* D0 */ + SUNXI_FUNCTION(0x3, "csi1"), /* D0 */ + SUNXI_FUNCTION(0x4, "mmc1"), /* D2 */ + SUNXI_FUNCTION(0x5, "csi0")), /* D8 */ SUNXI_PIN(SUNXI_PINCTRL_PIN_PG5, SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out")), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "ts1"), /* D1 */ + SUNXI_FUNCTION(0x3, "csi1"), /* D1 */ + SUNXI_FUNCTION(0x4, "mmc1"), /* D3 */ + SUNXI_FUNCTION(0x5, "csi0")), /* D9 */ SUNXI_PIN(SUNXI_PINCTRL_PIN_PG6, SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out")), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "ts1"), /* D2 */ + SUNXI_FUNCTION(0x3, "csi1"), /* D2 */ + SUNXI_FUNCTION(0x4, "uart3"), /* TX */ + SUNXI_FUNCTION(0x5, "csi0")), /* D10 */ SUNXI_PIN(SUNXI_PINCTRL_PIN_PG7, SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out")), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "ts1"), /* D3 */ + SUNXI_FUNCTION(0x3, "csi1"), /* D3 */ + SUNXI_FUNCTION(0x4, "uart3"), /* RX */ + SUNXI_FUNCTION(0x5, "csi0")), /* D11 */ SUNXI_PIN(SUNXI_PINCTRL_PIN_PG8, SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out")), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "ts1"), /* D4 */ + SUNXI_FUNCTION(0x3, "csi1"), /* D4 */ + SUNXI_FUNCTION(0x4, "uart3"), /* RTS */ + SUNXI_FUNCTION(0x5, "csi0")), /* D12 */ SUNXI_PIN(SUNXI_PINCTRL_PIN_PG9, SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out")), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "ts1"), /* D5 */ + SUNXI_FUNCTION(0x3, "csi1"), /* D5 */ + SUNXI_FUNCTION(0x4, "uart3"), /* CTS */ + SUNXI_FUNCTION(0x5, "csi0")), /* D13 */ SUNXI_PIN(SUNXI_PINCTRL_PIN_PG10, SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out")), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "ts1"), /* D6 */ + SUNXI_FUNCTION(0x3, "csi1"), /* D6 */ + SUNXI_FUNCTION(0x4, "uart4"), /* TX */ + SUNXI_FUNCTION(0x5, "csi0")), /* D14 */ SUNXI_PIN(SUNXI_PINCTRL_PIN_PG11, SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out")), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "ts1"), /* D7 */ + SUNXI_FUNCTION(0x3, "csi1"), /* D7 */ + SUNXI_FUNCTION(0x4, "uart4"), /* RX */ + SUNXI_FUNCTION(0x5, "csi0")), /* D15 */ /* Hole */ SUNXI_PIN(SUNXI_PINCTRL_PIN_PH0, SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out")), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "lcd1"), /* D0 */ + SUNXI_FUNCTION(0x3, "pata"), /* ATAA0 */ + SUNXI_FUNCTION(0x4, "uart3"), /* TX */ + SUNXI_FUNCTION(0x7, "csi1")), /* D0 */ SUNXI_PIN(SUNXI_PINCTRL_PIN_PH1, SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out")), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "lcd1"), /* D1 */ + SUNXI_FUNCTION(0x3, "pata"), /* ATAA1 */ + SUNXI_FUNCTION(0x4, "uart3"), /* RX */ + SUNXI_FUNCTION(0x7, "csi1")), /* D1 */ SUNXI_PIN(SUNXI_PINCTRL_PIN_PH2, SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out")), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "lcd1"), /* D2 */ + SUNXI_FUNCTION(0x3, "pata"), /* ATAA2 */ + SUNXI_FUNCTION(0x4, "uart3"), /* RTS */ + SUNXI_FUNCTION(0x7, "csi1")), /* D2 */ SUNXI_PIN(SUNXI_PINCTRL_PIN_PH3, SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out")), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "lcd1"), /* D3 */ + SUNXI_FUNCTION(0x3, "pata"), /* ATAIRQ */ + SUNXI_FUNCTION(0x4, "uart3"), /* CTS */ + SUNXI_FUNCTION(0x7, "csi1")), /* D3 */ SUNXI_PIN(SUNXI_PINCTRL_PIN_PH4, SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out")), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "lcd1"), /* D4 */ + SUNXI_FUNCTION(0x3, "pata"), /* ATAD0 */ + SUNXI_FUNCTION(0x4, "uart4"), /* TX */ + SUNXI_FUNCTION(0x7, "csi1")), /* D4 */ SUNXI_PIN(SUNXI_PINCTRL_PIN_PH5, SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out")), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "lcd1"), /* D5 */ + SUNXI_FUNCTION(0x3, "pata"), /* ATAD1 */ + SUNXI_FUNCTION(0x4, "uart4"), /* RX */ + SUNXI_FUNCTION(0x7, "csi1")), /* D5 */ SUNXI_PIN(SUNXI_PINCTRL_PIN_PH6, SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out")), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "lcd1"), /* D6 */ + SUNXI_FUNCTION(0x3, "pata"), /* ATAD2 */ + SUNXI_FUNCTION(0x4, "uart5"), /* TX */ + SUNXI_FUNCTION(0x5, "ms"), /* BS */ + SUNXI_FUNCTION(0x7, "csi1")), /* D6 */ SUNXI_PIN(SUNXI_PINCTRL_PIN_PH7, SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out")), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "lcd1"), /* D7 */ + SUNXI_FUNCTION(0x3, "pata"), /* ATAD3 */ + SUNXI_FUNCTION(0x4, "uart5"), /* RX */ + SUNXI_FUNCTION(0x5, "ms"), /* CLK */ + SUNXI_FUNCTION(0x7, "csi1")), /* D7 */ SUNXI_PIN(SUNXI_PINCTRL_PIN_PH8, SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out")), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "lcd1"), /* D8 */ + SUNXI_FUNCTION(0x3, "pata"), /* ATAD4 */ + SUNXI_FUNCTION(0x4, "keypad"), /* IN0 */ + SUNXI_FUNCTION(0x5, "ms"), /* D0 */ + SUNXI_FUNCTION(0x7, "csi1")), /* D8 */ SUNXI_PIN(SUNXI_PINCTRL_PIN_PH9, SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out")), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "lcd1"), /* D9 */ + SUNXI_FUNCTION(0x3, "pata"), /* ATAD5 */ + SUNXI_FUNCTION(0x4, "keypad"), /* IN1 */ + SUNXI_FUNCTION(0x5, "ms"), /* D1 */ + SUNXI_FUNCTION(0x7, "csi1")), /* D9 */ SUNXI_PIN(SUNXI_PINCTRL_PIN_PH10, SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out")), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "lcd1"), /* D10 */ + SUNXI_FUNCTION(0x3, "pata"), /* ATAD6 */ + SUNXI_FUNCTION(0x4, "keypad"), /* IN2 */ + SUNXI_FUNCTION(0x5, "ms"), /* D2 */ + SUNXI_FUNCTION(0x7, "csi1")), /* D10 */ SUNXI_PIN(SUNXI_PINCTRL_PIN_PH11, SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out")), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "lcd1"), /* D11 */ + SUNXI_FUNCTION(0x3, "pata"), /* ATAD7 */ + SUNXI_FUNCTION(0x4, "keypad"), /* IN3 */ + SUNXI_FUNCTION(0x5, "ms"), /* D3 */ + SUNXI_FUNCTION(0x7, "csi1")), /* D11 */ SUNXI_PIN(SUNXI_PINCTRL_PIN_PH12, SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out")), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "lcd1"), /* D12 */ + SUNXI_FUNCTION(0x3, "pata"), /* ATAD8 */ + SUNXI_FUNCTION(0x4, "ps2"), /* SCK1 */ + SUNXI_FUNCTION(0x7, "csi1")), /* D12 */ SUNXI_PIN(SUNXI_PINCTRL_PIN_PH13, SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out")), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "lcd1"), /* D13 */ + SUNXI_FUNCTION(0x3, "pata"), /* ATAD9 */ + SUNXI_FUNCTION(0x4, "ps2"), /* SDA1 */ + SUNXI_FUNCTION(0x5, "sim"), /* RST */ + SUNXI_FUNCTION(0x7, "csi1")), /* D13 */ SUNXI_PIN(SUNXI_PINCTRL_PIN_PH14, SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out")), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "lcd1"), /* D14 */ + SUNXI_FUNCTION(0x3, "pata"), /* ATAD10 */ + SUNXI_FUNCTION(0x4, "keypad"), /* IN4 */ + SUNXI_FUNCTION(0x5, "sim"), /* VPPEN */ + SUNXI_FUNCTION(0x7, "csi1")), /* D14 */ SUNXI_PIN(SUNXI_PINCTRL_PIN_PH15, SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out")), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "lcd1"), /* D15 */ + SUNXI_FUNCTION(0x3, "pata"), /* ATAD11 */ + SUNXI_FUNCTION(0x4, "keypad"), /* IN5 */ + SUNXI_FUNCTION(0x5, "sim"), /* VPPPP */ + SUNXI_FUNCTION(0x7, "csi1")), /* D15 */ SUNXI_PIN(SUNXI_PINCTRL_PIN_PH16, SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out")), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "lcd1"), /* D16 */ + SUNXI_FUNCTION(0x3, "pata"), /* ATAD12 */ + SUNXI_FUNCTION(0x4, "keypad"), /* IN6 */ + SUNXI_FUNCTION(0x7, "csi1")), /* D16 */ SUNXI_PIN(SUNXI_PINCTRL_PIN_PH17, SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out")), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "lcd1"), /* D17 */ + SUNXI_FUNCTION(0x3, "pata"), /* ATAD13 */ + SUNXI_FUNCTION(0x4, "keypad"), /* IN7 */ + SUNXI_FUNCTION(0x5, "sim"), /* VCCEN */ + SUNXI_FUNCTION(0x7, "csi1")), /* D17 */ SUNXI_PIN(SUNXI_PINCTRL_PIN_PH18, SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out")), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "lcd1"), /* D18 */ + SUNXI_FUNCTION(0x3, "pata"), /* ATAD14 */ + SUNXI_FUNCTION(0x4, "keypad"), /* OUT0 */ + SUNXI_FUNCTION(0x5, "sim"), /* SCK */ + SUNXI_FUNCTION(0x7, "csi1")), /* D18 */ SUNXI_PIN(SUNXI_PINCTRL_PIN_PH19, SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out")), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "lcd1"), /* D19 */ + SUNXI_FUNCTION(0x3, "pata"), /* ATAD15 */ + SUNXI_FUNCTION(0x4, "keypad"), /* OUT1 */ + SUNXI_FUNCTION(0x5, "sim"), /* SDA */ + SUNXI_FUNCTION(0x7, "csi1")), /* D19 */ SUNXI_PIN(SUNXI_PINCTRL_PIN_PH20, SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out")), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "lcd1"), /* D20 */ + SUNXI_FUNCTION(0x3, "pata"), /* ATAOE */ + SUNXI_FUNCTION(0x4, "can"), /* TX */ + SUNXI_FUNCTION(0x7, "csi1")), /* D20 */ SUNXI_PIN(SUNXI_PINCTRL_PIN_PH21, SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out")), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "lcd1"), /* D21 */ + SUNXI_FUNCTION(0x3, "pata"), /* ATADREQ */ + SUNXI_FUNCTION(0x4, "can"), /* RX */ + SUNXI_FUNCTION(0x7, "csi1")), /* D21 */ SUNXI_PIN(SUNXI_PINCTRL_PIN_PH22, SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out")), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "lcd1"), /* D22 */ + SUNXI_FUNCTION(0x3, "pata"), /* ATADACK */ + SUNXI_FUNCTION(0x4, "keypad"), /* OUT2 */ + SUNXI_FUNCTION(0x5, "mmc1"), /* CMD */ + SUNXI_FUNCTION(0x7, "csi1")), /* D22 */ SUNXI_PIN(SUNXI_PINCTRL_PIN_PH23, SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out")), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "lcd1"), /* D23 */ + SUNXI_FUNCTION(0x3, "pata"), /* ATACS0 */ + SUNXI_FUNCTION(0x4, "keypad"), /* OUT3 */ + SUNXI_FUNCTION(0x5, "mmc1"), /* CLK */ + SUNXI_FUNCTION(0x7, "csi1")), /* D23 */ SUNXI_PIN(SUNXI_PINCTRL_PIN_PH24, SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out")), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "lcd1"), /* CLK */ + SUNXI_FUNCTION(0x3, "pata"), /* ATACS1 */ + SUNXI_FUNCTION(0x4, "keypad"), /* OUT4 */ + SUNXI_FUNCTION(0x5, "mmc1"), /* D0 */ + SUNXI_FUNCTION(0x7, "csi1")), /* PCLK */ SUNXI_PIN(SUNXI_PINCTRL_PIN_PH25, SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out")), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "lcd1"), /* DE */ + SUNXI_FUNCTION(0x3, "pata"), /* ATAIORDY */ + SUNXI_FUNCTION(0x4, "keypad"), /* OUT5 */ + SUNXI_FUNCTION(0x5, "mmc1"), /* D1 */ + SUNXI_FUNCTION(0x7, "csi1")), /* FIELD */ SUNXI_PIN(SUNXI_PINCTRL_PIN_PH26, SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out")), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "lcd1"), /* HSYNC */ + SUNXI_FUNCTION(0x3, "pata"), /* ATAIOR */ + SUNXI_FUNCTION(0x4, "keypad"), /* OUT6 */ + SUNXI_FUNCTION(0x5, "mmc1"), /* D2 */ + SUNXI_FUNCTION(0x7, "csi1")), /* HSYNC */ SUNXI_PIN(SUNXI_PINCTRL_PIN_PH27, SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out")), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "lcd1"), /* VSYNC */ + SUNXI_FUNCTION(0x3, "pata"), /* ATAIOW */ + SUNXI_FUNCTION(0x4, "keypad"), /* OUT7 */ + SUNXI_FUNCTION(0x5, "mmc1"), /* D3 */ + SUNXI_FUNCTION(0x7, "csi1")), /* VSYNC */ /* Hole */ SUNXI_PIN(SUNXI_PINCTRL_PIN_PI0, SUNXI_FUNCTION(0x0, "gpio_in"), @@ -518,277 +893,401 @@ static const struct sunxi_desc_pin sun4i_a10_pins[] = { SUNXI_FUNCTION(0x1, "gpio_out")), SUNXI_PIN(SUNXI_PINCTRL_PIN_PI3, SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out")), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "pwm")), /* PWM1 */ SUNXI_PIN(SUNXI_PINCTRL_PIN_PI4, SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out")), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "mmc3")), /* CMD */ SUNXI_PIN(SUNXI_PINCTRL_PIN_PI5, SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out")), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "mmc3")), /* CLK */ SUNXI_PIN(SUNXI_PINCTRL_PIN_PI6, SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out")), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "mmc3")), /* D0 */ SUNXI_PIN(SUNXI_PINCTRL_PIN_PI7, SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out")), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "mmc3")), /* D1 */ SUNXI_PIN(SUNXI_PINCTRL_PIN_PI8, SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out")), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "mmc3")), /* D2 */ SUNXI_PIN(SUNXI_PINCTRL_PIN_PI9, SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out")), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "mmc3")), /* D3 */ SUNXI_PIN(SUNXI_PINCTRL_PIN_PI10, SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out")), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "spi0"), /* CS0 */ + SUNXI_FUNCTION(0x3, "uart5")), /* TX */ SUNXI_PIN(SUNXI_PINCTRL_PIN_PI11, SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out")), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "spi0"), /* CLK */ + SUNXI_FUNCTION(0x3, "uart5")), /* RX */ SUNXI_PIN(SUNXI_PINCTRL_PIN_PI12, SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out")), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "spi0"), /* MOSI */ + SUNXI_FUNCTION(0x3, "uart6")), /* TX */ SUNXI_PIN(SUNXI_PINCTRL_PIN_PI13, SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out")), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "spi0"), /* MISO */ + SUNXI_FUNCTION(0x3, "uart6")), /* RX */ SUNXI_PIN(SUNXI_PINCTRL_PIN_PI14, SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out")), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "spi0"), /* CS1 */ + SUNXI_FUNCTION(0x3, "ps2"), /* SCK1 */ + SUNXI_FUNCTION(0x4, "timer4")), /* TCLKIN0 */ SUNXI_PIN(SUNXI_PINCTRL_PIN_PI15, SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out")), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "spi1"), /* CS1 */ + SUNXI_FUNCTION(0x3, "ps2"), /* SDA1 */ + SUNXI_FUNCTION(0x4, "timer5")), /* TCLKIN1 */ SUNXI_PIN(SUNXI_PINCTRL_PIN_PI16, SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out")), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "spi1"), /* CS0 */ + SUNXI_FUNCTION(0x3, "uart2")), /* RTS */ SUNXI_PIN(SUNXI_PINCTRL_PIN_PI17, SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out")), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "spi1"), /* CLK */ + SUNXI_FUNCTION(0x3, "uart2")), /* CTS */ SUNXI_PIN(SUNXI_PINCTRL_PIN_PI18, SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out")), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "spi1"), /* MOSI */ + SUNXI_FUNCTION(0x3, "uart2")), /* TX */ SUNXI_PIN(SUNXI_PINCTRL_PIN_PI19, SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out")), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "spi1"), /* MISO */ + SUNXI_FUNCTION(0x3, "uart2")), /* RX */ SUNXI_PIN(SUNXI_PINCTRL_PIN_PI20, SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out")), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "ps2"), /* SCK0 */ + SUNXI_FUNCTION(0x3, "uart7"), /* TX */ + SUNXI_FUNCTION(0x4, "hdmi")), /* HSCL */ SUNXI_PIN(SUNXI_PINCTRL_PIN_PI21, SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out")), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "ps2"), /* SDA0 */ + SUNXI_FUNCTION(0x3, "uart7"), /* RX */ + SUNXI_FUNCTION(0x4, "hdmi")), /* HSDA */ }; static const struct sunxi_desc_pin sun5i_a13_pins[] = { /* Hole */ SUNXI_PIN(SUNXI_PINCTRL_PIN_PB0, SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out")), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "i2c0")), /* SCK */ SUNXI_PIN(SUNXI_PINCTRL_PIN_PB1, SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out")), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "i2c0")), /* SDA */ SUNXI_PIN(SUNXI_PINCTRL_PIN_PB2, SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out")), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "pwm")), SUNXI_PIN(SUNXI_PINCTRL_PIN_PB3, SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out")), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "ir0")), /* TX */ SUNXI_PIN(SUNXI_PINCTRL_PIN_PB4, SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out")), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "ir0")), /* RX */ /* Hole */ SUNXI_PIN(SUNXI_PINCTRL_PIN_PB10, SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out")), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "spi2")), /* CS1 */ /* Hole */ SUNXI_PIN(SUNXI_PINCTRL_PIN_PB15, SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out")), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "i2c1")), /* SCK */ SUNXI_PIN(SUNXI_PINCTRL_PIN_PB16, SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out")), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "i2c1")), /* SDA */ SUNXI_PIN(SUNXI_PINCTRL_PIN_PB17, SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out")), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "i2c2")), /* SCK */ SUNXI_PIN(SUNXI_PINCTRL_PIN_PB18, SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out")), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "i2c2")), /* SDA */ /* Hole */ SUNXI_PIN(SUNXI_PINCTRL_PIN_PC0, SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out")), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "nand0"), /* NWE */ + SUNXI_FUNCTION(0x3, "spi0")), /* MOSI */ SUNXI_PIN(SUNXI_PINCTRL_PIN_PC1, SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out")), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "nand0"), /* NALE */ + SUNXI_FUNCTION(0x3, "spi0")), /* MISO */ SUNXI_PIN(SUNXI_PINCTRL_PIN_PC2, SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out")), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "nand0"), /* NCLE */ + SUNXI_FUNCTION(0x3, "spi0")), /* CLK */ SUNXI_PIN(SUNXI_PINCTRL_PIN_PC3, SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out")), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "nand0"), /* NCE1 */ + SUNXI_FUNCTION(0x3, "spi0")), /* CS0 */ SUNXI_PIN(SUNXI_PINCTRL_PIN_PC4, SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out")), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "nand0")), /* NCE0 */ SUNXI_PIN(SUNXI_PINCTRL_PIN_PC5, SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out")), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "nand0")), /* NRE */ SUNXI_PIN(SUNXI_PINCTRL_PIN_PC6, SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out")), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "nand0"), /* NRB0 */ + SUNXI_FUNCTION(0x3, "mmc2")), /* CMD */ SUNXI_PIN(SUNXI_PINCTRL_PIN_PC7, SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out")), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "nand0"), /* NRB1 */ + SUNXI_FUNCTION(0x3, "mmc2")), /* CLK */ SUNXI_PIN(SUNXI_PINCTRL_PIN_PC8, SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out")), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "nand0"), /* NDQ0 */ + SUNXI_FUNCTION(0x3, "mmc2")), /* D0 */ SUNXI_PIN(SUNXI_PINCTRL_PIN_PC9, SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out")), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "nand0"), /* NDQ1 */ + SUNXI_FUNCTION(0x3, "mmc2")), /* D1 */ SUNXI_PIN(SUNXI_PINCTRL_PIN_PC10, SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out")), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "nand0"), /* NDQ2 */ + SUNXI_FUNCTION(0x3, "mmc2")), /* D2 */ SUNXI_PIN(SUNXI_PINCTRL_PIN_PC11, SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out")), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "nand0"), /* NDQ3 */ + SUNXI_FUNCTION(0x3, "mmc2")), /* D3 */ SUNXI_PIN(SUNXI_PINCTRL_PIN_PC12, SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out")), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "nand0"), /* NDQ4 */ + SUNXI_FUNCTION(0x3, "mmc2")), /* D4 */ SUNXI_PIN(SUNXI_PINCTRL_PIN_PC13, SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out")), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "nand0"), /* NDQ5 */ + SUNXI_FUNCTION(0x3, "mmc2")), /* D5 */ SUNXI_PIN(SUNXI_PINCTRL_PIN_PC14, SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out")), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "nand0"), /* NDQ6 */ + SUNXI_FUNCTION(0x3, "mmc2")), /* D6 */ SUNXI_PIN(SUNXI_PINCTRL_PIN_PC15, SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out")), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "nand0"), /* NDQ7 */ + SUNXI_FUNCTION(0x3, "mmc2")), /* D7 */ /* Hole */ SUNXI_PIN(SUNXI_PINCTRL_PIN_PC19, SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out")), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "nand0"), /* NDQS */ + SUNXI_FUNCTION(0x4, "uart3")), /* RTS */ /* Hole */ SUNXI_PIN(SUNXI_PINCTRL_PIN_PD2, SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out")), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "lcd0")), /* D2 */ SUNXI_PIN(SUNXI_PINCTRL_PIN_PD3, SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out")), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "lcd0")), /* D3 */ SUNXI_PIN(SUNXI_PINCTRL_PIN_PD4, SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out")), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "lcd0")), /* D4 */ SUNXI_PIN(SUNXI_PINCTRL_PIN_PD5, SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out")), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "lcd0")), /* D5 */ SUNXI_PIN(SUNXI_PINCTRL_PIN_PD6, SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out")), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "lcd0")), /* D6 */ SUNXI_PIN(SUNXI_PINCTRL_PIN_PD7, SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out")), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "lcd0")), /* D7 */ /* Hole */ SUNXI_PIN(SUNXI_PINCTRL_PIN_PD10, SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out")), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "lcd0")), /* D10 */ SUNXI_PIN(SUNXI_PINCTRL_PIN_PD11, SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out")), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "lcd0")), /* D11 */ SUNXI_PIN(SUNXI_PINCTRL_PIN_PD12, SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out")), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "lcd0")), /* D12 */ SUNXI_PIN(SUNXI_PINCTRL_PIN_PD13, SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out")), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "lcd0")), /* D13 */ SUNXI_PIN(SUNXI_PINCTRL_PIN_PD14, SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out")), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "lcd0")), /* D14 */ SUNXI_PIN(SUNXI_PINCTRL_PIN_PD15, SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out")), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "lcd0")), /* D15 */ /* Hole */ SUNXI_PIN(SUNXI_PINCTRL_PIN_PD18, SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out")), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "lcd0")), /* D18 */ SUNXI_PIN(SUNXI_PINCTRL_PIN_PD19, SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out")), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "lcd0")), /* D19 */ SUNXI_PIN(SUNXI_PINCTRL_PIN_PD20, SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out")), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "lcd0")), /* D20 */ SUNXI_PIN(SUNXI_PINCTRL_PIN_PD21, SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out")), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "lcd0")), /* D21 */ SUNXI_PIN(SUNXI_PINCTRL_PIN_PD22, SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out")), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "lcd0")), /* D22 */ SUNXI_PIN(SUNXI_PINCTRL_PIN_PD23, SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out")), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "lcd0")), /* D23 */ SUNXI_PIN(SUNXI_PINCTRL_PIN_PD24, SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out")), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "lcd0")), /* CLK */ SUNXI_PIN(SUNXI_PINCTRL_PIN_PD25, SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out")), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "lcd0")), /* DE */ SUNXI_PIN(SUNXI_PINCTRL_PIN_PD26, SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out")), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "lcd0")), /* HSYNC */ SUNXI_PIN(SUNXI_PINCTRL_PIN_PD27, SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out")), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "lcd0")), /* VSYNC */ /* Hole */ SUNXI_PIN(SUNXI_PINCTRL_PIN_PE0, SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out")), + SUNXI_FUNCTION(0x3, "csi0"), /* PCLK */ + SUNXI_FUNCTION(0x4, "spi2")), /* CS0 */ SUNXI_PIN(SUNXI_PINCTRL_PIN_PE1, SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out")), + SUNXI_FUNCTION(0x3, "csi0"), /* MCLK */ + SUNXI_FUNCTION(0x4, "spi2")), /* CLK */ SUNXI_PIN(SUNXI_PINCTRL_PIN_PE2, SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out")), + SUNXI_FUNCTION(0x3, "csi0"), /* HSYNC */ + SUNXI_FUNCTION(0x4, "spi2")), /* MOSI */ SUNXI_PIN(SUNXI_PINCTRL_PIN_PE3, SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out")), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x3, "csi0"), /* VSYNC */ + SUNXI_FUNCTION(0x4, "spi2")), /* MISO */ SUNXI_PIN(SUNXI_PINCTRL_PIN_PE4, SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out")), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x3, "csi0"), /* D0 */ + SUNXI_FUNCTION(0x4, "mmc2")), /* D0 */ SUNXI_PIN(SUNXI_PINCTRL_PIN_PE5, SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out")), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x3, "csi0"), /* D1 */ + SUNXI_FUNCTION(0x4, "mmc2")), /* D1 */ SUNXI_PIN(SUNXI_PINCTRL_PIN_PE6, SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out")), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x3, "csi0"), /* D2 */ + SUNXI_FUNCTION(0x4, "mmc2")), /* D2 */ SUNXI_PIN(SUNXI_PINCTRL_PIN_PE7, SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out")), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x3, "csi0"), /* D3 */ + SUNXI_FUNCTION(0x4, "mmc2")), /* D3 */ SUNXI_PIN(SUNXI_PINCTRL_PIN_PE8, SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out")), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x3, "csi0"), /* D4 */ + SUNXI_FUNCTION(0x4, "mmc2")), /* CMD */ SUNXI_PIN(SUNXI_PINCTRL_PIN_PE9, SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out")), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x3, "csi0"), /* D5 */ + SUNXI_FUNCTION(0x4, "mmc2")), /* CLK */ SUNXI_PIN(SUNXI_PINCTRL_PIN_PE10, SUNXI_FUNCTION(0x0, "gpio_in"), SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x3, "csi0"), /* D6 */ SUNXI_FUNCTION(0x4, "uart1")), /* TX */ SUNXI_PIN(SUNXI_PINCTRL_PIN_PE11, SUNXI_FUNCTION(0x0, "gpio_in"), SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x3, "csi0"), /* D7 */ SUNXI_FUNCTION(0x4, "uart1")), /* RX */ /* Hole */ SUNXI_PIN(SUNXI_PINCTRL_PIN_PF0, SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out")), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x4, "mmc0")), /* D1 */ SUNXI_PIN(SUNXI_PINCTRL_PIN_PF1, SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out")), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x4, "mmc0")), /* D0 */ SUNXI_PIN(SUNXI_PINCTRL_PIN_PF2, SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out")), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x4, "mmc0")), /* CLK */ SUNXI_PIN(SUNXI_PINCTRL_PIN_PF3, SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out")), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x4, "mmc0")), /* CMD */ SUNXI_PIN(SUNXI_PINCTRL_PIN_PF4, SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out")), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x4, "mmc0")), /* D3 */ SUNXI_PIN(SUNXI_PINCTRL_PIN_PF5, SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out")), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x4, "mmc0")), /* D2 */ /* Hole */ SUNXI_PIN(SUNXI_PINCTRL_PIN_PG0, SUNXI_FUNCTION(0x0, "gpio_in"), @@ -802,24 +1301,34 @@ static const struct sunxi_desc_pin sun5i_a13_pins[] = { SUNXI_PIN(SUNXI_PINCTRL_PIN_PG3, SUNXI_FUNCTION(0x0, "gpio_in"), SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "mmc1"), /* CMD */ SUNXI_FUNCTION(0x4, "uart1")), /* TX */ SUNXI_PIN(SUNXI_PINCTRL_PIN_PG4, SUNXI_FUNCTION(0x0, "gpio_in"), SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "mmc1"), /* CLK */ SUNXI_FUNCTION(0x4, "uart1")), /* RX */ - /* Hole */ +/* Hole */ SUNXI_PIN(SUNXI_PINCTRL_PIN_PG9, SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out")), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "spi1"), /* CS0 */ + SUNXI_FUNCTION(0x3, "uart3")), /* TX */ SUNXI_PIN(SUNXI_PINCTRL_PIN_PG10, SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out")), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "spi1"), /* CLK */ + SUNXI_FUNCTION(0x3, "uart3")), /* RX */ SUNXI_PIN(SUNXI_PINCTRL_PIN_PG11, SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out")), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "spi1"), /* MOSI */ + SUNXI_FUNCTION(0x3, "uart3")), /* CTS */ SUNXI_PIN(SUNXI_PINCTRL_PIN_PG12, SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out")), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "spi1"), /* MISO */ + SUNXI_FUNCTION(0x3, "uart3")), /* RTS */ }; static const struct sunxi_pinctrl_desc sun4i_a10_pinctrl_data = { @@ -1029,7 +1538,7 @@ static void sunxi_pctrl_dt_free_map(struct pinctrl_dev *pctldev, kfree(map); } -static struct pinctrl_ops sunxi_pctrl_ops = { +static const struct pinctrl_ops sunxi_pctrl_ops = { .dt_node_to_map = sunxi_pctrl_dt_node_to_map, .dt_free_map = sunxi_pctrl_dt_free_map, .get_groups_count = sunxi_pctrl_get_groups_count, @@ -1098,7 +1607,7 @@ static int sunxi_pconf_group_set(struct pinctrl_dev *pctldev, return 0; } -static struct pinconf_ops sunxi_pconf_ops = { +static const struct pinconf_ops sunxi_pconf_ops = { .pin_config_group_get = sunxi_pconf_group_get, .pin_config_group_set = sunxi_pconf_group_set, }; @@ -1204,7 +1713,7 @@ error: return ret; } -static struct pinmux_ops sunxi_pmx_ops = { +static const struct pinmux_ops sunxi_pmx_ops = { .get_functions_count = sunxi_pmx_get_funcs_cnt, .get_function_name = sunxi_pmx_get_func_name, .get_function_groups = sunxi_pmx_get_func_groups, @@ -1409,6 +1918,7 @@ static int sunxi_pinctrl_probe(struct platform_device *pdev) struct pinctrl_pin_desc *pins; struct sunxi_pinctrl *pctl; int i, ret, last_pin; + struct clk *clk; pctl = devm_kzalloc(&pdev->dev, sizeof(*pctl), GFP_KERNEL); if (!pctl) @@ -1479,6 +1989,12 @@ static int sunxi_pinctrl_probe(struct platform_device *pdev) goto gpiochip_error; } + clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(clk)) + goto gpiochip_error; + + clk_prepare_enable(clk); + dev_info(&pdev->dev, "initialized sunXi PIO driver\n"); return 0; diff --git a/drivers/pinctrl/pinctrl-tegra.c b/drivers/pinctrl/pinctrl-tegra.c index f195d77a3572..2fa9bc6cd7ab 100644 --- a/drivers/pinctrl/pinctrl-tegra.c +++ b/drivers/pinctrl/pinctrl-tegra.c @@ -316,7 +316,7 @@ static int tegra_pinctrl_dt_node_to_map(struct pinctrl_dev *pctldev, return 0; } -static struct pinctrl_ops tegra_pinctrl_ops = { +static const struct pinctrl_ops tegra_pinctrl_ops = { .get_groups_count = tegra_pinctrl_get_groups_count, .get_group_name = tegra_pinctrl_get_group_name, .get_group_pins = tegra_pinctrl_get_group_pins, @@ -401,7 +401,7 @@ static void tegra_pinctrl_disable(struct pinctrl_dev *pctldev, pmx_writel(pmx, val, g->mux_bank, g->mux_reg); } -static struct pinmux_ops tegra_pinmux_ops = { +static const struct pinmux_ops tegra_pinmux_ops = { .get_functions_count = tegra_pinctrl_get_funcs_count, .get_function_name = tegra_pinctrl_get_func_name, .get_function_groups = tegra_pinctrl_get_func_groups, @@ -676,7 +676,7 @@ static void tegra_pinconf_config_dbg_show(struct pinctrl_dev *pctldev, } #endif -static struct pinconf_ops tegra_pinconf_ops = { +static const struct pinconf_ops tegra_pinconf_ops = { .pin_config_get = tegra_pinconf_get, .pin_config_set = tegra_pinconf_set, .pin_config_group_get = tegra_pinconf_group_get, diff --git a/drivers/pinctrl/pinctrl-u300.c b/drivers/pinctrl/pinctrl-u300.c index 2b5772550836..6a3a7503e6a0 100644 --- a/drivers/pinctrl/pinctrl-u300.c +++ b/drivers/pinctrl/pinctrl-u300.c @@ -860,7 +860,7 @@ static void u300_pin_dbg_show(struct pinctrl_dev *pctldev, struct seq_file *s, seq_printf(s, " " DRIVER_NAME); } -static struct pinctrl_ops u300_pctrl_ops = { +static const struct pinctrl_ops u300_pctrl_ops = { .get_groups_count = u300_get_groups_count, .get_group_name = u300_get_group_name, .get_group_pins = u300_get_group_pins, @@ -1003,7 +1003,7 @@ static int u300_pmx_get_groups(struct pinctrl_dev *pctldev, unsigned selector, return 0; } -static struct pinmux_ops u300_pmx_ops = { +static const struct pinmux_ops u300_pmx_ops = { .get_functions_count = u300_pmx_get_funcs_count, .get_function_name = u300_pmx_get_func_name, .get_function_groups = u300_pmx_get_groups, @@ -1046,7 +1046,7 @@ static int u300_pin_config_set(struct pinctrl_dev *pctldev, unsigned pin, return 0; } -static struct pinconf_ops u300_pconf_ops = { +static const struct pinconf_ops u300_pconf_ops = { .is_generic = true, .pin_config_get = u300_pin_config_get, .pin_config_set = u300_pin_config_set, diff --git a/drivers/pinctrl/pinctrl-xway.c b/drivers/pinctrl/pinctrl-xway.c index 068224efa6fa..f2977cff8366 100644 --- a/drivers/pinctrl/pinctrl-xway.c +++ b/drivers/pinctrl/pinctrl-xway.c @@ -553,7 +553,7 @@ int xway_pinconf_group_set(struct pinctrl_dev *pctldev, return ret; } -static struct pinconf_ops xway_pinconf_ops = { +static const struct pinconf_ops xway_pinconf_ops = { .pin_config_get = xway_pinconf_get, .pin_config_set = xway_pinconf_set, .pin_config_group_set = xway_pinconf_group_set, diff --git a/drivers/pinctrl/pinmux.c b/drivers/pinctrl/pinmux.c index bd83c8b01cd1..88cc5095d0c9 100644 --- a/drivers/pinctrl/pinmux.c +++ b/drivers/pinctrl/pinmux.c @@ -506,7 +506,7 @@ static int pinmux_functions_show(struct seq_file *s, void *what) if (!pmxops) return 0; - mutex_lock(&pinctrl_mutex); + mutex_lock(&pctldev->mutex); nfuncs = pmxops->get_functions_count(pctldev); while (func_selector < nfuncs) { const char *func = pmxops->get_function_name(pctldev, @@ -530,7 +530,7 @@ static int pinmux_functions_show(struct seq_file *s, void *what) func_selector++; } - mutex_unlock(&pinctrl_mutex); + mutex_unlock(&pctldev->mutex); return 0; } @@ -548,7 +548,7 @@ static int pinmux_pins_show(struct seq_file *s, void *what) seq_puts(s, "Pinmux settings per pin\n"); seq_puts(s, "Format: pin (name): mux_owner gpio_owner hog?\n"); - mutex_lock(&pinctrl_mutex); + mutex_lock(&pctldev->mutex); /* The pin number can be retrived from the pin controller descriptor */ for (i = 0; i < pctldev->desc->npins; i++) { @@ -583,7 +583,7 @@ static int pinmux_pins_show(struct seq_file *s, void *what) seq_printf(s, "\n"); } - mutex_unlock(&pinctrl_mutex); + mutex_unlock(&pctldev->mutex); return 0; } diff --git a/drivers/pinctrl/spear/pinctrl-spear.c b/drivers/pinctrl/spear/pinctrl-spear.c index 6a7dae70db08..116da0412c4b 100644 --- a/drivers/pinctrl/spear/pinctrl-spear.c +++ b/drivers/pinctrl/spear/pinctrl-spear.c @@ -198,7 +198,7 @@ static void spear_pinctrl_dt_free_map(struct pinctrl_dev *pctldev, kfree(map); } -static struct pinctrl_ops spear_pinctrl_ops = { +static const struct pinctrl_ops spear_pinctrl_ops = { .get_groups_count = spear_pinctrl_get_groups_cnt, .get_group_name = spear_pinctrl_get_group_name, .get_group_pins = spear_pinctrl_get_group_pins, @@ -340,7 +340,7 @@ static void gpio_disable_free(struct pinctrl_dev *pctldev, gpio_request_endisable(pctldev, range, offset, false); } -static struct pinmux_ops spear_pinmux_ops = { +static const struct pinmux_ops spear_pinmux_ops = { .get_functions_count = spear_pinctrl_get_funcs_count, .get_function_name = spear_pinctrl_get_func_name, .get_function_groups = spear_pinctrl_get_func_groups, diff --git a/drivers/platform/x86/hp-wmi.c b/drivers/platform/x86/hp-wmi.c index 45cacf79f3a7..1a779bbfb87d 100644 --- a/drivers/platform/x86/hp-wmi.c +++ b/drivers/platform/x86/hp-wmi.c @@ -134,7 +134,6 @@ static const struct key_entry hp_wmi_keymap[] = { { KE_KEY, 0x2142, { KEY_MEDIA } }, { KE_KEY, 0x213b, { KEY_INFO } }, { KE_KEY, 0x2169, { KEY_DIRECTION } }, - { KE_KEY, 0x216a, { KEY_SETUP } }, { KE_KEY, 0x231b, { KEY_HELP } }, { KE_END, 0 } }; @@ -925,9 +924,6 @@ static int __init hp_wmi_init(void) err = hp_wmi_input_setup(); if (err) return err; - - //Enable magic for hotkeys that run on the SMBus - ec_write(0xe6,0x6e); } if (bios_capable) { diff --git a/drivers/platform/x86/thinkpad_acpi.c b/drivers/platform/x86/thinkpad_acpi.c index 9a907567f41e..edec135b1685 100644 --- a/drivers/platform/x86/thinkpad_acpi.c +++ b/drivers/platform/x86/thinkpad_acpi.c @@ -1964,9 +1964,6 @@ struct tp_nvram_state { /* kthread for the hotkey poller */ static struct task_struct *tpacpi_hotkey_task; -/* Acquired while the poller kthread is running, use to sync start/stop */ -static struct mutex hotkey_thread_mutex; - /* * Acquire mutex to write poller control variables as an * atomic block. @@ -2462,8 +2459,6 @@ static int hotkey_kthread(void *data) unsigned int poll_freq; bool was_frozen; - mutex_lock(&hotkey_thread_mutex); - if (tpacpi_lifecycle == TPACPI_LIFE_EXITING) goto exit; @@ -2523,7 +2518,6 @@ static int hotkey_kthread(void *data) } exit: - mutex_unlock(&hotkey_thread_mutex); return 0; } @@ -2533,9 +2527,6 @@ static void hotkey_poll_stop_sync(void) if (tpacpi_hotkey_task) { kthread_stop(tpacpi_hotkey_task); tpacpi_hotkey_task = NULL; - mutex_lock(&hotkey_thread_mutex); - /* at this point, the thread did exit */ - mutex_unlock(&hotkey_thread_mutex); } } @@ -3234,7 +3225,6 @@ static int __init hotkey_init(struct ibm_init_struct *iibm) mutex_init(&hotkey_mutex); #ifdef CONFIG_THINKPAD_ACPI_HOTKEY_POLL - mutex_init(&hotkey_thread_mutex); mutex_init(&hotkey_thread_data_mutex); #endif diff --git a/drivers/s390/block/dasd.c b/drivers/s390/block/dasd.c index f1b7fdc58a5f..82758cbb220b 100644 --- a/drivers/s390/block/dasd.c +++ b/drivers/s390/block/dasd.c @@ -246,7 +246,7 @@ static struct dentry *dasd_debugfs_setup(const char *name, static int dasd_state_known_to_basic(struct dasd_device *device) { struct dasd_block *block = device->block; - int rc; + int rc = 0; /* Allocate and register gendisk structure. */ if (block) { @@ -273,7 +273,8 @@ static int dasd_state_known_to_basic(struct dasd_device *device) DBF_DEV_EVENT(DBF_EMERG, device, "%s", "debug area created"); device->state = DASD_STATE_BASIC; - return 0; + + return rc; } /* @@ -282,6 +283,7 @@ static int dasd_state_known_to_basic(struct dasd_device *device) static int dasd_state_basic_to_known(struct dasd_device *device) { int rc; + if (device->block) { dasd_profile_exit(&device->block->profile); if (device->block->debugfs_dentry) @@ -332,8 +334,10 @@ static int dasd_state_basic_to_ready(struct dasd_device *device) if (block->base->discipline->do_analysis != NULL) rc = block->base->discipline->do_analysis(block); if (rc) { - if (rc != -EAGAIN) + if (rc != -EAGAIN) { device->state = DASD_STATE_UNFMT; + goto out; + } return rc; } dasd_setup_queue(block); @@ -341,11 +345,16 @@ static int dasd_state_basic_to_ready(struct dasd_device *device) block->blocks << block->s2b_shift); device->state = DASD_STATE_READY; rc = dasd_scan_partitions(block); - if (rc) + if (rc) { device->state = DASD_STATE_BASIC; + return rc; + } } else { device->state = DASD_STATE_READY; } +out: + if (device->discipline->basic_to_ready) + rc = device->discipline->basic_to_ready(device); return rc; } @@ -368,6 +377,11 @@ static int dasd_state_ready_to_basic(struct dasd_device *device) { int rc; + if (device->discipline->ready_to_basic) { + rc = device->discipline->ready_to_basic(device); + if (rc) + return rc; + } device->state = DASD_STATE_BASIC; if (device->block) { struct dasd_block *block = device->block; @@ -402,16 +416,10 @@ static int dasd_state_unfmt_to_basic(struct dasd_device *device) static int dasd_state_ready_to_online(struct dasd_device * device) { - int rc; struct gendisk *disk; struct disk_part_iter piter; struct hd_struct *part; - if (device->discipline->ready_to_online) { - rc = device->discipline->ready_to_online(device); - if (rc) - return rc; - } device->state = DASD_STATE_ONLINE; if (device->block) { dasd_schedule_block_bh(device->block); @@ -444,6 +452,7 @@ static int dasd_state_online_to_ready(struct dasd_device *device) if (rc) return rc; } + device->state = DASD_STATE_READY; if (device->block && !(device->features & DASD_FEATURE_USERAW)) { disk = device->block->bdev->bd_disk; @@ -2223,6 +2232,77 @@ static int _dasd_sleep_on(struct dasd_ccw_req *maincqr, int interruptible) return rc; } +static inline int _wait_for_wakeup_queue(struct list_head *ccw_queue) +{ + struct dasd_ccw_req *cqr; + + list_for_each_entry(cqr, ccw_queue, blocklist) { + if (cqr->callback_data != DASD_SLEEPON_END_TAG) + return 0; + } + + return 1; +} + +static int _dasd_sleep_on_queue(struct list_head *ccw_queue, int interruptible) +{ + struct dasd_device *device; + int rc; + struct dasd_ccw_req *cqr, *n; + +retry: + list_for_each_entry_safe(cqr, n, ccw_queue, blocklist) { + device = cqr->startdev; + if (cqr->status != DASD_CQR_FILLED) /*could be failed*/ + continue; + + if (test_bit(DASD_FLAG_LOCK_STOLEN, &device->flags) && + !test_bit(DASD_CQR_ALLOW_SLOCK, &cqr->flags)) { + cqr->status = DASD_CQR_FAILED; + cqr->intrc = -EPERM; + continue; + } + /*Non-temporary stop condition will trigger fail fast*/ + if (device->stopped & ~DASD_STOPPED_PENDING && + test_bit(DASD_CQR_FLAGS_FAILFAST, &cqr->flags) && + !dasd_eer_enabled(device)) { + cqr->status = DASD_CQR_FAILED; + cqr->intrc = -EAGAIN; + continue; + } + + /*Don't try to start requests if device is stopped*/ + if (interruptible) { + rc = wait_event_interruptible( + generic_waitq, !device->stopped); + if (rc == -ERESTARTSYS) { + cqr->status = DASD_CQR_FAILED; + cqr->intrc = rc; + continue; + } + } else + wait_event(generic_waitq, !(device->stopped)); + + if (!cqr->callback) + cqr->callback = dasd_wakeup_cb; + cqr->callback_data = DASD_SLEEPON_START_TAG; + dasd_add_request_tail(cqr); + } + + wait_event(generic_waitq, _wait_for_wakeup_queue(ccw_queue)); + + rc = 0; + list_for_each_entry_safe(cqr, n, ccw_queue, blocklist) { + if (__dasd_sleep_on_erp(cqr)) + rc = 1; + } + if (rc) + goto retry; + + + return 0; +} + /* * Queue a request to the tail of the device ccw_queue and wait for * it's completion. @@ -2233,6 +2313,15 @@ int dasd_sleep_on(struct dasd_ccw_req *cqr) } /* + * Start requests from a ccw_queue and wait for their completion. + */ +int dasd_sleep_on_queue(struct list_head *ccw_queue) +{ + return _dasd_sleep_on_queue(ccw_queue, 0); +} +EXPORT_SYMBOL(dasd_sleep_on_queue); + +/* * Queue a request to the tail of the device ccw_queue and wait * interruptible for it's completion. */ @@ -2663,6 +2752,26 @@ static void _dasd_wake_block_flush_cb(struct dasd_ccw_req *cqr, void *data) } /* + * Requeue a request back to the block request queue + * only works for block requests + */ +static int _dasd_requeue_request(struct dasd_ccw_req *cqr) +{ + struct dasd_block *block = cqr->block; + struct request *req; + unsigned long flags; + + if (!block) + return -EINVAL; + spin_lock_irqsave(&block->queue_lock, flags); + req = (struct request *) cqr->callback_data; + blk_requeue_request(block->request_queue, req); + spin_unlock_irqrestore(&block->queue_lock, flags); + + return 0; +} + +/* * Go through all request on the dasd_block request queue, cancel them * on the respective dasd_device, and return them to the generic * block layer. @@ -3380,10 +3489,11 @@ EXPORT_SYMBOL_GPL(dasd_generic_verify_path); int dasd_generic_pm_freeze(struct ccw_device *cdev) { + struct dasd_device *device = dasd_device_from_cdev(cdev); + struct list_head freeze_queue; struct dasd_ccw_req *cqr, *n; + struct dasd_ccw_req *refers; int rc; - struct list_head freeze_queue; - struct dasd_device *device = dasd_device_from_cdev(cdev); if (IS_ERR(device)) return PTR_ERR(device); @@ -3396,7 +3506,8 @@ int dasd_generic_pm_freeze(struct ccw_device *cdev) /* disallow new I/O */ dasd_device_set_stop_bits(device, DASD_STOPPED_PM); - /* clear active requests */ + + /* clear active requests and requeue them to block layer if possible */ INIT_LIST_HEAD(&freeze_queue); spin_lock_irq(get_ccwdev_lock(cdev)); rc = 0; @@ -3416,7 +3527,6 @@ int dasd_generic_pm_freeze(struct ccw_device *cdev) } list_move_tail(&cqr->devlist, &freeze_queue); } - spin_unlock_irq(get_ccwdev_lock(cdev)); list_for_each_entry_safe(cqr, n, &freeze_queue, devlist) { @@ -3424,12 +3534,38 @@ int dasd_generic_pm_freeze(struct ccw_device *cdev) (cqr->status != DASD_CQR_CLEAR_PENDING)); if (cqr->status == DASD_CQR_CLEARED) cqr->status = DASD_CQR_QUEUED; + + /* requeue requests to blocklayer will only work for + block device requests */ + if (_dasd_requeue_request(cqr)) + continue; + + /* remove requests from device and block queue */ + list_del_init(&cqr->devlist); + while (cqr->refers != NULL) { + refers = cqr->refers; + /* remove the request from the block queue */ + list_del(&cqr->blocklist); + /* free the finished erp request */ + dasd_free_erp_request(cqr, cqr->memdev); + cqr = refers; + } + if (cqr->block) + list_del_init(&cqr->blocklist); + cqr->block->base->discipline->free_cp( + cqr, (struct request *) cqr->callback_data); } - /* move freeze_queue to start of the ccw_queue */ - spin_lock_irq(get_ccwdev_lock(cdev)); - list_splice_tail(&freeze_queue, &device->ccw_queue); - spin_unlock_irq(get_ccwdev_lock(cdev)); + /* + * if requests remain then they are internal request + * and go back to the device queue + */ + if (!list_empty(&freeze_queue)) { + /* move freeze_queue to start of the ccw_queue */ + spin_lock_irq(get_ccwdev_lock(cdev)); + list_splice_tail(&freeze_queue, &device->ccw_queue); + spin_unlock_irq(get_ccwdev_lock(cdev)); + } dasd_put_device(device); return rc; } diff --git a/drivers/s390/block/dasd_devmap.c b/drivers/s390/block/dasd_devmap.c index c196827c228f..a71bb8aaca1d 100644 --- a/drivers/s390/block/dasd_devmap.c +++ b/drivers/s390/block/dasd_devmap.c @@ -410,8 +410,7 @@ dasd_add_busid(const char *bus_id, int features) struct dasd_devmap *devmap, *new, *tmp; int hash; - new = (struct dasd_devmap *) - kzalloc(sizeof(struct dasd_devmap), GFP_KERNEL); + new = kzalloc(sizeof(struct dasd_devmap), GFP_KERNEL); if (!new) return ERR_PTR(-ENOMEM); spin_lock(&dasd_devmap_lock); diff --git a/drivers/s390/block/dasd_eckd.c b/drivers/s390/block/dasd_eckd.c index 6999fd919e94..6a44b27623ed 100644 --- a/drivers/s390/block/dasd_eckd.c +++ b/drivers/s390/block/dasd_eckd.c @@ -2022,7 +2022,7 @@ static int dasd_eckd_do_analysis(struct dasd_block *block) return dasd_eckd_end_analysis(block); } -static int dasd_eckd_ready_to_online(struct dasd_device *device) +static int dasd_eckd_basic_to_ready(struct dasd_device *device) { return dasd_alias_add_device(device); }; @@ -2031,6 +2031,11 @@ static int dasd_eckd_online_to_ready(struct dasd_device *device) { cancel_work_sync(&device->reload_device); cancel_work_sync(&device->kick_validate); + return 0; +}; + +static int dasd_eckd_ready_to_basic(struct dasd_device *device) +{ return dasd_alias_remove_device(device); }; @@ -2050,45 +2055,34 @@ dasd_eckd_fill_geometry(struct dasd_block *block, struct hd_geometry *geo) } static struct dasd_ccw_req * -dasd_eckd_format_device(struct dasd_device * device, - struct format_data_t * fdata) +dasd_eckd_build_format(struct dasd_device *base, + struct format_data_t *fdata) { - struct dasd_eckd_private *private; + struct dasd_eckd_private *base_priv; + struct dasd_eckd_private *start_priv; + struct dasd_device *startdev; struct dasd_ccw_req *fcp; struct eckd_count *ect; + struct ch_t address; struct ccw1 *ccw; void *data; int rpt; - struct ch_t address; int cplength, datasize; - int i; + int i, j; int intensity = 0; int r0_perm; + int nr_tracks; - private = (struct dasd_eckd_private *) device->private; - rpt = recs_per_track(&private->rdc_data, 0, fdata->blksize); - set_ch_t(&address, - fdata->start_unit / private->rdc_data.trk_per_cyl, - fdata->start_unit % private->rdc_data.trk_per_cyl); + startdev = dasd_alias_get_start_dev(base); + if (!startdev) + startdev = base; - /* Sanity checks. */ - if (fdata->start_unit >= - (private->real_cyl * private->rdc_data.trk_per_cyl)) { - dev_warn(&device->cdev->dev, "Start track number %d used in " - "formatting is too big\n", fdata->start_unit); - return ERR_PTR(-EINVAL); - } - if (fdata->start_unit > fdata->stop_unit) { - dev_warn(&device->cdev->dev, "Start track %d used in " - "formatting exceeds end track\n", fdata->start_unit); - return ERR_PTR(-EINVAL); - } - if (dasd_check_blocksize(fdata->blksize) != 0) { - dev_warn(&device->cdev->dev, - "The DASD cannot be formatted with block size %d\n", - fdata->blksize); - return ERR_PTR(-EINVAL); - } + start_priv = (struct dasd_eckd_private *) startdev->private; + base_priv = (struct dasd_eckd_private *) base->private; + + rpt = recs_per_track(&base_priv->rdc_data, 0, fdata->blksize); + + nr_tracks = fdata->stop_unit - fdata->start_unit + 1; /* * fdata->intensity is a bit string that tells us what to do: @@ -2106,149 +2100,282 @@ dasd_eckd_format_device(struct dasd_device * device, r0_perm = 1; intensity = fdata->intensity; } + switch (intensity) { case 0x00: /* Normal format */ case 0x08: /* Normal format, use cdl. */ - cplength = 2 + rpt; - datasize = sizeof(struct DE_eckd_data) + + cplength = 2 + (rpt*nr_tracks); + datasize = sizeof(struct PFX_eckd_data) + sizeof(struct LO_eckd_data) + - rpt * sizeof(struct eckd_count); + rpt * nr_tracks * sizeof(struct eckd_count); break; case 0x01: /* Write record zero and format track. */ case 0x09: /* Write record zero and format track, use cdl. */ - cplength = 3 + rpt; - datasize = sizeof(struct DE_eckd_data) + + cplength = 2 + rpt * nr_tracks; + datasize = sizeof(struct PFX_eckd_data) + sizeof(struct LO_eckd_data) + sizeof(struct eckd_count) + - rpt * sizeof(struct eckd_count); + rpt * nr_tracks * sizeof(struct eckd_count); break; case 0x04: /* Invalidate track. */ case 0x0c: /* Invalidate track, use cdl. */ cplength = 3; - datasize = sizeof(struct DE_eckd_data) + + datasize = sizeof(struct PFX_eckd_data) + sizeof(struct LO_eckd_data) + sizeof(struct eckd_count); break; default: - dev_warn(&device->cdev->dev, "An I/O control call used " - "incorrect flags 0x%x\n", fdata->intensity); + dev_warn(&startdev->cdev->dev, + "An I/O control call used incorrect flags 0x%x\n", + fdata->intensity); return ERR_PTR(-EINVAL); } /* Allocate the format ccw request. */ - fcp = dasd_smalloc_request(DASD_ECKD_MAGIC, cplength, datasize, device); + fcp = dasd_smalloc_request(DASD_ECKD_MAGIC, cplength, + datasize, startdev); if (IS_ERR(fcp)) return fcp; + start_priv->count++; data = fcp->data; ccw = fcp->cpaddr; switch (intensity & ~0x08) { case 0x00: /* Normal format. */ - define_extent(ccw++, (struct DE_eckd_data *) data, - fdata->start_unit, fdata->start_unit, - DASD_ECKD_CCW_WRITE_CKD, device); + prefix(ccw++, (struct PFX_eckd_data *) data, + fdata->start_unit, fdata->stop_unit, + DASD_ECKD_CCW_WRITE_CKD, base, startdev); /* grant subsystem permission to format R0 */ if (r0_perm) - ((struct DE_eckd_data *)data)->ga_extended |= 0x04; - data += sizeof(struct DE_eckd_data); + ((struct PFX_eckd_data *)data) + ->define_extent.ga_extended |= 0x04; + data += sizeof(struct PFX_eckd_data); ccw[-1].flags |= CCW_FLAG_CC; locate_record(ccw++, (struct LO_eckd_data *) data, - fdata->start_unit, 0, rpt, - DASD_ECKD_CCW_WRITE_CKD, device, + fdata->start_unit, 0, rpt*nr_tracks, + DASD_ECKD_CCW_WRITE_CKD, base, fdata->blksize); data += sizeof(struct LO_eckd_data); break; case 0x01: /* Write record zero + format track. */ - define_extent(ccw++, (struct DE_eckd_data *) data, - fdata->start_unit, fdata->start_unit, - DASD_ECKD_CCW_WRITE_RECORD_ZERO, - device); - data += sizeof(struct DE_eckd_data); + prefix(ccw++, (struct PFX_eckd_data *) data, + fdata->start_unit, fdata->stop_unit, + DASD_ECKD_CCW_WRITE_RECORD_ZERO, + base, startdev); + data += sizeof(struct PFX_eckd_data); ccw[-1].flags |= CCW_FLAG_CC; locate_record(ccw++, (struct LO_eckd_data *) data, - fdata->start_unit, 0, rpt + 1, - DASD_ECKD_CCW_WRITE_RECORD_ZERO, device, - device->block->bp_block); + fdata->start_unit, 0, rpt * nr_tracks + 1, + DASD_ECKD_CCW_WRITE_RECORD_ZERO, base, + base->block->bp_block); data += sizeof(struct LO_eckd_data); break; case 0x04: /* Invalidate track. */ - define_extent(ccw++, (struct DE_eckd_data *) data, - fdata->start_unit, fdata->start_unit, - DASD_ECKD_CCW_WRITE_CKD, device); - data += sizeof(struct DE_eckd_data); + prefix(ccw++, (struct PFX_eckd_data *) data, + fdata->start_unit, fdata->stop_unit, + DASD_ECKD_CCW_WRITE_CKD, base, startdev); + data += sizeof(struct PFX_eckd_data); ccw[-1].flags |= CCW_FLAG_CC; locate_record(ccw++, (struct LO_eckd_data *) data, fdata->start_unit, 0, 1, - DASD_ECKD_CCW_WRITE_CKD, device, 8); + DASD_ECKD_CCW_WRITE_CKD, base, 8); data += sizeof(struct LO_eckd_data); break; } - if (intensity & 0x01) { /* write record zero */ - ect = (struct eckd_count *) data; - data += sizeof(struct eckd_count); - ect->cyl = address.cyl; - ect->head = address.head; - ect->record = 0; - ect->kl = 0; - ect->dl = 8; - ccw[-1].flags |= CCW_FLAG_CC; - ccw->cmd_code = DASD_ECKD_CCW_WRITE_RECORD_ZERO; - ccw->flags = CCW_FLAG_SLI; - ccw->count = 8; - ccw->cda = (__u32)(addr_t) ect; - ccw++; - } - if ((intensity & ~0x08) & 0x04) { /* erase track */ - ect = (struct eckd_count *) data; - data += sizeof(struct eckd_count); - ect->cyl = address.cyl; - ect->head = address.head; - ect->record = 1; - ect->kl = 0; - ect->dl = 0; - ccw[-1].flags |= CCW_FLAG_CC; - ccw->cmd_code = DASD_ECKD_CCW_WRITE_CKD; - ccw->flags = CCW_FLAG_SLI; - ccw->count = 8; - ccw->cda = (__u32)(addr_t) ect; - } else { /* write remaining records */ - for (i = 0; i < rpt; i++) { + + for (j = 0; j < nr_tracks; j++) { + /* calculate cylinder and head for the current track */ + set_ch_t(&address, + (fdata->start_unit + j) / + base_priv->rdc_data.trk_per_cyl, + (fdata->start_unit + j) % + base_priv->rdc_data.trk_per_cyl); + if (intensity & 0x01) { /* write record zero */ ect = (struct eckd_count *) data; data += sizeof(struct eckd_count); ect->cyl = address.cyl; ect->head = address.head; - ect->record = i + 1; + ect->record = 0; ect->kl = 0; - ect->dl = fdata->blksize; - /* Check for special tracks 0-1 when formatting CDL */ - if ((intensity & 0x08) && - fdata->start_unit == 0) { - if (i < 3) { - ect->kl = 4; - ect->dl = sizes_trk0[i] - 4; - } - } - if ((intensity & 0x08) && - fdata->start_unit == 1) { - ect->kl = 44; - ect->dl = LABEL_SIZE - 44; - } + ect->dl = 8; ccw[-1].flags |= CCW_FLAG_CC; - ccw->cmd_code = DASD_ECKD_CCW_WRITE_CKD; + ccw->cmd_code = DASD_ECKD_CCW_WRITE_RECORD_ZERO; ccw->flags = CCW_FLAG_SLI; ccw->count = 8; ccw->cda = (__u32)(addr_t) ect; ccw++; } + if ((intensity & ~0x08) & 0x04) { /* erase track */ + ect = (struct eckd_count *) data; + data += sizeof(struct eckd_count); + ect->cyl = address.cyl; + ect->head = address.head; + ect->record = 1; + ect->kl = 0; + ect->dl = 0; + ccw[-1].flags |= CCW_FLAG_CC; + ccw->cmd_code = DASD_ECKD_CCW_WRITE_CKD; + ccw->flags = CCW_FLAG_SLI; + ccw->count = 8; + ccw->cda = (__u32)(addr_t) ect; + } else { /* write remaining records */ + for (i = 0; i < rpt; i++) { + ect = (struct eckd_count *) data; + data += sizeof(struct eckd_count); + ect->cyl = address.cyl; + ect->head = address.head; + ect->record = i + 1; + ect->kl = 0; + ect->dl = fdata->blksize; + /* + * Check for special tracks 0-1 + * when formatting CDL + */ + if ((intensity & 0x08) && + fdata->start_unit == 0) { + if (i < 3) { + ect->kl = 4; + ect->dl = sizes_trk0[i] - 4; + } + } + if ((intensity & 0x08) && + fdata->start_unit == 1) { + ect->kl = 44; + ect->dl = LABEL_SIZE - 44; + } + ccw[-1].flags |= CCW_FLAG_CC; + if (i != 0 || j == 0) + ccw->cmd_code = + DASD_ECKD_CCW_WRITE_CKD; + else + ccw->cmd_code = + DASD_ECKD_CCW_WRITE_CKD_MT; + ccw->flags = CCW_FLAG_SLI; + ccw->count = 8; + ccw->cda = (__u32)(addr_t) ect; + ccw++; + } + } } - fcp->startdev = device; - fcp->memdev = device; + + fcp->startdev = startdev; + fcp->memdev = startdev; fcp->retries = 256; + fcp->expires = startdev->default_expires * HZ; fcp->buildclk = get_tod_clock(); fcp->status = DASD_CQR_FILLED; + return fcp; } +static int +dasd_eckd_format_device(struct dasd_device *base, + struct format_data_t *fdata) +{ + struct dasd_ccw_req *cqr, *n; + struct dasd_block *block; + struct dasd_eckd_private *private; + struct list_head format_queue; + struct dasd_device *device; + int old_stop, format_step; + int step, rc = 0; + + block = base->block; + private = (struct dasd_eckd_private *) base->private; + + /* Sanity checks. */ + if (fdata->start_unit >= + (private->real_cyl * private->rdc_data.trk_per_cyl)) { + dev_warn(&base->cdev->dev, + "Start track number %u used in formatting is too big\n", + fdata->start_unit); + return -EINVAL; + } + if (fdata->stop_unit >= + (private->real_cyl * private->rdc_data.trk_per_cyl)) { + dev_warn(&base->cdev->dev, + "Stop track number %u used in formatting is too big\n", + fdata->stop_unit); + return -EINVAL; + } + if (fdata->start_unit > fdata->stop_unit) { + dev_warn(&base->cdev->dev, + "Start track %u used in formatting exceeds end track\n", + fdata->start_unit); + return -EINVAL; + } + if (dasd_check_blocksize(fdata->blksize) != 0) { + dev_warn(&base->cdev->dev, + "The DASD cannot be formatted with block size %u\n", + fdata->blksize); + return -EINVAL; + } + + INIT_LIST_HEAD(&format_queue); + old_stop = fdata->stop_unit; + + while (fdata->start_unit <= 1) { + fdata->stop_unit = fdata->start_unit; + cqr = dasd_eckd_build_format(base, fdata); + list_add(&cqr->blocklist, &format_queue); + + fdata->stop_unit = old_stop; + fdata->start_unit++; + + if (fdata->start_unit > fdata->stop_unit) + goto sleep; + } + +retry: + format_step = 255 / recs_per_track(&private->rdc_data, 0, + fdata->blksize); + while (fdata->start_unit <= old_stop) { + step = fdata->stop_unit - fdata->start_unit + 1; + if (step > format_step) + fdata->stop_unit = fdata->start_unit + format_step - 1; + + cqr = dasd_eckd_build_format(base, fdata); + if (IS_ERR(cqr)) { + if (PTR_ERR(cqr) == -ENOMEM) { + /* + * not enough memory available + * go to out and start requests + * retry after first requests were finished + */ + fdata->stop_unit = old_stop; + goto sleep; + } else + return PTR_ERR(cqr); + } + list_add(&cqr->blocklist, &format_queue); + + fdata->start_unit = fdata->stop_unit + 1; + fdata->stop_unit = old_stop; + } + +sleep: + dasd_sleep_on_queue(&format_queue); + + list_for_each_entry_safe(cqr, n, &format_queue, blocklist) { + device = cqr->startdev; + private = (struct dasd_eckd_private *) device->private; + if (cqr->status == DASD_CQR_FAILED) + rc = -EIO; + list_del_init(&cqr->blocklist); + dasd_sfree_request(cqr, device); + private->count--; + } + + /* + * in case of ENOMEM we need to retry after + * first requests are finished + */ + if (fdata->start_unit <= fdata->stop_unit) + goto retry; + + return rc; +} + static void dasd_eckd_handle_terminated_request(struct dasd_ccw_req *cqr) { cqr->status = DASD_CQR_FILLED; @@ -4305,8 +4432,9 @@ static struct dasd_discipline dasd_eckd_discipline = { .uncheck_device = dasd_eckd_uncheck_device, .do_analysis = dasd_eckd_do_analysis, .verify_path = dasd_eckd_verify_path, - .ready_to_online = dasd_eckd_ready_to_online, + .basic_to_ready = dasd_eckd_basic_to_ready, .online_to_ready = dasd_eckd_online_to_ready, + .ready_to_basic = dasd_eckd_ready_to_basic, .fill_geometry = dasd_eckd_fill_geometry, .start_IO = dasd_start_IO, .term_IO = dasd_term_IO, diff --git a/drivers/s390/block/dasd_int.h b/drivers/s390/block/dasd_int.h index 899e3f5a56e5..0785bd9bd5b6 100644 --- a/drivers/s390/block/dasd_int.h +++ b/drivers/s390/block/dasd_int.h @@ -300,10 +300,11 @@ struct dasd_discipline { * Last things to do when a device is set online, and first things * when it is set offline. */ - int (*ready_to_online) (struct dasd_device *); + int (*basic_to_ready) (struct dasd_device *); int (*online_to_ready) (struct dasd_device *); + int (*ready_to_basic) (struct dasd_device *); - /* + /* (struct dasd_device *); * Device operation functions. build_cp creates a ccw chain for * a block device request, start_io starts the request and * term_IO cancels it (e.g. in case of a timeout). format_device @@ -317,8 +318,8 @@ struct dasd_discipline { int (*start_IO) (struct dasd_ccw_req *); int (*term_IO) (struct dasd_ccw_req *); void (*handle_terminated_request) (struct dasd_ccw_req *); - struct dasd_ccw_req *(*format_device) (struct dasd_device *, - struct format_data_t *); + int (*format_device) (struct dasd_device *, + struct format_data_t *); int (*free_cp) (struct dasd_ccw_req *, struct request *); /* @@ -672,6 +673,7 @@ int dasd_term_IO(struct dasd_ccw_req *); void dasd_schedule_device_bh(struct dasd_device *); void dasd_schedule_block_bh(struct dasd_block *); int dasd_sleep_on(struct dasd_ccw_req *); +int dasd_sleep_on_queue(struct list_head *); int dasd_sleep_on_immediatly(struct dasd_ccw_req *); int dasd_sleep_on_interruptible(struct dasd_ccw_req *); void dasd_device_set_timer(struct dasd_device *, int); diff --git a/drivers/s390/block/dasd_ioctl.c b/drivers/s390/block/dasd_ioctl.c index 03c0e0444553..8be1b51e9311 100644 --- a/drivers/s390/block/dasd_ioctl.c +++ b/drivers/s390/block/dasd_ioctl.c @@ -143,12 +143,12 @@ static int dasd_ioctl_resume(struct dasd_block *block) /* * performs formatting of _device_ according to _fdata_ * Note: The discipline's format_function is assumed to deliver formatting - * commands to format a single unit of the device. In terms of the ECKD - * devices this means CCWs are generated to format a single track. + * commands to format multiple units of the device. In terms of the ECKD + * devices this means CCWs are generated to format multiple tracks. */ -static int dasd_format(struct dasd_block *block, struct format_data_t *fdata) +static int +dasd_format(struct dasd_block *block, struct format_data_t *fdata) { - struct dasd_ccw_req *cqr; struct dasd_device *base; int rc; @@ -157,8 +157,8 @@ static int dasd_format(struct dasd_block *block, struct format_data_t *fdata) return -EPERM; if (base->state != DASD_STATE_BASIC) { - pr_warning("%s: The DASD cannot be formatted while it is " - "enabled\n", dev_name(&base->cdev->dev)); + pr_warn("%s: The DASD cannot be formatted while it is enabled\n", + dev_name(&base->cdev->dev)); return -EBUSY; } @@ -178,21 +178,10 @@ static int dasd_format(struct dasd_block *block, struct format_data_t *fdata) bdput(bdev); } - while (fdata->start_unit <= fdata->stop_unit) { - cqr = base->discipline->format_device(base, fdata); - if (IS_ERR(cqr)) - return PTR_ERR(cqr); - rc = dasd_sleep_on_interruptible(cqr); - dasd_sfree_request(cqr, cqr->memdev); - if (rc) { - if (rc != -ERESTARTSYS) - pr_err("%s: Formatting unit %d failed with " - "rc=%d\n", dev_name(&base->cdev->dev), - fdata->start_unit, rc); - return rc; - } - fdata->start_unit++; - } + rc = base->discipline->format_device(base, fdata); + if (rc) + return rc; + return 0; } diff --git a/drivers/s390/block/scm_blk.c b/drivers/s390/block/scm_blk.c index e9b9c8392832..b303cab76a7f 100644 --- a/drivers/s390/block/scm_blk.c +++ b/drivers/s390/block/scm_blk.c @@ -465,7 +465,7 @@ static int __init scm_blk_init(void) scm_major = ret; ret = scm_alloc_rqs(nr_requests); if (ret) - goto out_unreg; + goto out_free; scm_debug = debug_register("scm_log", 16, 1, 16); if (!scm_debug) { @@ -486,7 +486,6 @@ out_dbf: debug_unregister(scm_debug); out_free: scm_free_rqs(); -out_unreg: unregister_blkdev(scm_major, "scm"); out: return ret; diff --git a/drivers/s390/block/scm_blk_cluster.c b/drivers/s390/block/scm_blk_cluster.c index f4bb61b0cea1..c0d102e3a48b 100644 --- a/drivers/s390/block/scm_blk_cluster.c +++ b/drivers/s390/block/scm_blk_cluster.c @@ -223,6 +223,8 @@ void scm_cluster_request_irq(struct scm_request *scmrq) bool scm_cluster_size_valid(void) { - return write_cluster_size == 0 || write_cluster_size == 32 || - write_cluster_size == 64 || write_cluster_size == 128; + if (write_cluster_size == 1 || write_cluster_size > 128) + return false; + + return !(write_cluster_size & (write_cluster_size - 1)); } diff --git a/drivers/s390/char/con3215.c b/drivers/s390/char/con3215.c index 7b00fa634d40..eb5d22795c47 100644 --- a/drivers/s390/char/con3215.c +++ b/drivers/s390/char/con3215.c @@ -502,7 +502,7 @@ static void raw3215_make_room(struct raw3215_info *raw, unsigned int length) raw3215_try_io(raw); raw->flags &= ~RAW3215_FLUSHING; #ifdef CONFIG_TN3215_CONSOLE - wait_cons_dev(); + ccw_device_wait_idle(raw->cdev); #endif /* Enough room freed up ? */ if (RAW3215_BUFFER_SIZE - raw->count >= length) @@ -858,7 +858,7 @@ static void con3215_flush(void) raw = raw3215[0]; /* console 3215 is the first one */ if (raw->port.flags & ASYNC_SUSPENDED) /* The console is still frozen for suspend. */ - if (ccw_device_force_console()) + if (ccw_device_force_console(raw->cdev)) /* Forcing didn't work, no panic message .. */ return; spin_lock_irqsave(get_ccwdev_lock(raw->cdev), flags); diff --git a/drivers/s390/char/monreader.c b/drivers/s390/char/monreader.c index f4ff515db251..0da3ae3cd63b 100644 --- a/drivers/s390/char/monreader.c +++ b/drivers/s390/char/monreader.c @@ -174,8 +174,7 @@ static void mon_free_mem(struct mon_private *monpriv) int i; for (i = 0; i < MON_MSGLIM; i++) - if (monpriv->msg_array[i]) - kfree(monpriv->msg_array[i]); + kfree(monpriv->msg_array[i]); kfree(monpriv); } diff --git a/drivers/s390/char/raw3270.c b/drivers/s390/char/raw3270.c index 4c9030a5b9f2..24a08e8f19e1 100644 --- a/drivers/s390/char/raw3270.c +++ b/drivers/s390/char/raw3270.c @@ -796,7 +796,7 @@ struct raw3270 __init *raw3270_setup_console(struct ccw_device *cdev) do { __raw3270_reset_device(rp); while (!raw3270_state_final(rp)) { - wait_cons_dev(); + ccw_device_wait_idle(rp->cdev); barrier(); } } while (rp->state != RAW3270_STATE_READY); @@ -810,7 +810,7 @@ raw3270_wait_cons_dev(struct raw3270 *rp) unsigned long flags; spin_lock_irqsave(get_ccwdev_lock(rp->cdev), flags); - wait_cons_dev(); + ccw_device_wait_idle(rp->cdev); spin_unlock_irqrestore(get_ccwdev_lock(rp->cdev), flags); } @@ -1274,7 +1274,7 @@ void raw3270_pm_unfreeze(struct raw3270_view *view) rp = view->dev; if (rp && test_bit(RAW3270_FLAGS_FROZEN, &rp->flags)) - ccw_device_force_console(); + ccw_device_force_console(rp->cdev); #endif } diff --git a/drivers/s390/char/sclp_cmd.c b/drivers/s390/char/sclp_cmd.c index cd798386b622..178836ec252b 100644 --- a/drivers/s390/char/sclp_cmd.c +++ b/drivers/s390/char/sclp_cmd.c @@ -561,6 +561,8 @@ static void __init sclp_add_standby_memory(void) add_memory_merged(0); } +#define MEM_SCT_SIZE (1UL << SECTION_SIZE_BITS) + static void __init insert_increment(u16 rn, int standby, int assigned) { struct memory_increment *incr, *new_incr; @@ -573,7 +575,7 @@ static void __init insert_increment(u16 rn, int standby, int assigned) new_incr->rn = rn; new_incr->standby = standby; if (!standby) - new_incr->usecount = 1; + new_incr->usecount = rzm > MEM_SCT_SIZE ? rzm/MEM_SCT_SIZE : 1; last_rn = 0; prev = &sclp_mem_list; list_for_each_entry(incr, &sclp_mem_list, list) { diff --git a/drivers/s390/char/zcore.c b/drivers/s390/char/zcore.c index 1d61a01576d2..22820610022c 100644 --- a/drivers/s390/char/zcore.c +++ b/drivers/s390/char/zcore.c @@ -127,7 +127,7 @@ static int memcpy_hsa(void *dest, unsigned long src, size_t count, int mode) } if (mode == TO_USER) { if (copy_to_user((__force __user void*) dest + offs, buf, - PAGE_SIZE)) + count - offs)) return -EFAULT; } else memcpy(dest + offs, buf, count - offs); diff --git a/drivers/s390/cio/chp.c b/drivers/s390/cio/chp.c index 50ad5fdd815d..21fabc6d5a9c 100644 --- a/drivers/s390/cio/chp.c +++ b/drivers/s390/cio/chp.c @@ -377,6 +377,26 @@ static void chp_release(struct device *dev) } /** + * chp_update_desc - update channel-path description + * @chp - channel-path + * + * Update the channel-path description of the specified channel-path. + * Return zero on success, non-zero otherwise. + */ +int chp_update_desc(struct channel_path *chp) +{ + int rc; + + rc = chsc_determine_base_channel_path_desc(chp->chpid, &chp->desc); + if (rc) + return rc; + + rc = chsc_determine_fmt1_channel_path_desc(chp->chpid, &chp->desc_fmt1); + + return rc; +} + +/** * chp_new - register a new channel-path * @chpid - channel-path ID * @@ -403,7 +423,7 @@ int chp_new(struct chp_id chpid) mutex_init(&chp->lock); /* Obtain channel path description and fill it in. */ - ret = chsc_determine_base_channel_path_desc(chpid, &chp->desc); + ret = chp_update_desc(chp); if (ret) goto out_free; if ((chp->desc.flags & 0x80) == 0) { diff --git a/drivers/s390/cio/chp.h b/drivers/s390/cio/chp.h index e1399dbee834..9284b785a06f 100644 --- a/drivers/s390/cio/chp.h +++ b/drivers/s390/cio/chp.h @@ -44,6 +44,7 @@ struct channel_path { struct mutex lock; /* Serialize access to below members. */ int state; struct channel_path_desc desc; + struct channel_path_desc_fmt1 desc_fmt1; /* Channel-measurement related stuff: */ int cmg; int shared; @@ -62,6 +63,7 @@ int chp_is_registered(struct chp_id chpid); void *chp_get_chp_desc(struct chp_id chpid); void chp_remove_cmg_attr(struct channel_path *chp); int chp_add_cmg_attr(struct channel_path *chp); +int chp_update_desc(struct channel_path *chp); int chp_new(struct chp_id chpid); void chp_cfg_schedule(struct chp_id chpid, int configure); void chp_cfg_cancel_deconfigure(struct chp_id chpid); diff --git a/drivers/s390/cio/chsc.c b/drivers/s390/cio/chsc.c index e16c553f6556..8ea7d9b2c671 100644 --- a/drivers/s390/cio/chsc.c +++ b/drivers/s390/cio/chsc.c @@ -376,7 +376,7 @@ static void chsc_process_sei_chp_avail(struct chsc_sei_nt0_area *sei_area) continue; } mutex_lock(&chp->lock); - chsc_determine_base_channel_path_desc(chpid, &chp->desc); + chp_update_desc(chp); mutex_unlock(&chp->lock); } } @@ -631,8 +631,8 @@ int chsc_chp_vary(struct chp_id chpid, int on) * Redo PathVerification on the devices the chpid connects to */ if (on) { - /* Try to update the channel path descritor. */ - chsc_determine_base_channel_path_desc(chpid, &chp->desc); + /* Try to update the channel path description. */ + chp_update_desc(chp); for_each_subchannel_staged(s390_subchannel_vary_chpid_on, __s390_vary_chpid_on, &chpid); } else @@ -825,9 +825,10 @@ int chsc_determine_fmt1_channel_path_desc(struct chp_id chpid, { struct chsc_response_struct *chsc_resp; struct chsc_scpd *scpd_area; + unsigned long flags; int ret; - spin_lock_irq(&chsc_page_lock); + spin_lock_irqsave(&chsc_page_lock, flags); scpd_area = chsc_page; ret = chsc_determine_channel_path_desc(chpid, 0, 0, 1, 0, scpd_area); if (ret) @@ -835,7 +836,7 @@ int chsc_determine_fmt1_channel_path_desc(struct chp_id chpid, chsc_resp = (void *)&scpd_area->response; memcpy(desc, &chsc_resp->data, sizeof(*desc)); out: - spin_unlock_irq(&chsc_page_lock); + spin_unlock_irqrestore(&chsc_page_lock, flags); return ret; } diff --git a/drivers/s390/cio/cio.c b/drivers/s390/cio/cio.c index 986ef6a92a41..935d80b4e9ce 100644 --- a/drivers/s390/cio/cio.c +++ b/drivers/s390/cio/cio.c @@ -471,15 +471,6 @@ int cio_disable_subchannel(struct subchannel *sch) } EXPORT_SYMBOL_GPL(cio_disable_subchannel); -int cio_create_sch_lock(struct subchannel *sch) -{ - sch->lock = kmalloc(sizeof(spinlock_t), GFP_KERNEL); - if (!sch->lock) - return -ENOMEM; - spin_lock_init(sch->lock); - return 0; -} - static int cio_check_devno_blacklisted(struct subchannel *sch) { if (is_blacklisted(sch->schid.ssid, sch->schib.pmcw.dev)) { @@ -536,32 +527,19 @@ int cio_validate_subchannel(struct subchannel *sch, struct subchannel_id schid) sprintf(dbf_txt, "valsch%x", schid.sch_no); CIO_TRACE_EVENT(4, dbf_txt); - /* Nuke all fields. */ - memset(sch, 0, sizeof(struct subchannel)); - - sch->schid = schid; - if (cio_is_console(schid)) { - sch->lock = cio_get_console_lock(); - } else { - err = cio_create_sch_lock(sch); - if (err) - goto out; - } - mutex_init(&sch->reg_mutex); - /* * The first subchannel that is not-operational (ccode==3) - * indicates that there aren't any more devices available. + * indicates that there aren't any more devices available. * If stsch gets an exception, it means the current subchannel set - * is not valid. + * is not valid. */ - ccode = stsch_err (schid, &sch->schib); + ccode = stsch_err(schid, &sch->schib); if (ccode) { err = (ccode == 3) ? -ENXIO : ccode; goto out; } - /* Copy subchannel type from path management control word. */ sch->st = sch->schib.pmcw.st; + sch->schid = schid; switch (sch->st) { case SUBCHANNEL_TYPE_IO: @@ -578,11 +556,7 @@ int cio_validate_subchannel(struct subchannel *sch, struct subchannel_id schid) CIO_MSG_EVENT(4, "Subchannel 0.%x.%04x reports subchannel type %04X\n", sch->schid.ssid, sch->schid.sch_no, sch->st); - return 0; out: - if (!cio_is_console(schid)) - kfree(sch->lock); - sch->lock = NULL; return err; } @@ -650,15 +624,13 @@ void __irq_entry do_IRQ(struct pt_regs *regs) } #ifdef CONFIG_CCW_CONSOLE -static struct subchannel console_subchannel; -static struct io_subchannel_private console_priv; -static int console_subchannel_in_use; +static struct subchannel *console_sch; /* * Use cio_tsch to update the subchannel status and call the interrupt handler - * if status had been pending. Called with the console_subchannel lock. + * if status had been pending. Called with the subchannel's lock held. */ -static void cio_tsch(struct subchannel *sch) +void cio_tsch(struct subchannel *sch) { struct irb *irb; int irq_context; @@ -675,6 +647,7 @@ static void cio_tsch(struct subchannel *sch) local_bh_disable(); irq_enter(); } + kstat_incr_irqs_this_cpu(IO_INTERRUPT, NULL); if (sch->driver && sch->driver->irq) sch->driver->irq(sch); else @@ -685,135 +658,90 @@ static void cio_tsch(struct subchannel *sch) } } -void *cio_get_console_priv(void) -{ - return &console_priv; -} - -/* - * busy wait for the next interrupt on the console - */ -void wait_cons_dev(void) +static int cio_test_for_console(struct subchannel_id schid, void *data) { - if (!console_subchannel_in_use) - return; - - while (1) { - cio_tsch(&console_subchannel); - if (console_subchannel.schib.scsw.cmd.actl == 0) - break; - udelay_simple(100); - } -} + struct schib schib; -static int -cio_test_for_console(struct subchannel_id schid, void *data) -{ - if (stsch_err(schid, &console_subchannel.schib) != 0) + if (stsch_err(schid, &schib) != 0) return -ENXIO; - if ((console_subchannel.schib.pmcw.st == SUBCHANNEL_TYPE_IO) && - console_subchannel.schib.pmcw.dnv && - (console_subchannel.schib.pmcw.dev == console_devno)) { + if ((schib.pmcw.st == SUBCHANNEL_TYPE_IO) && schib.pmcw.dnv && + (schib.pmcw.dev == console_devno)) { console_irq = schid.sch_no; return 1; /* found */ } return 0; } - -static int -cio_get_console_sch_no(void) +static int cio_get_console_sch_no(void) { struct subchannel_id schid; - + struct schib schib; + init_subchannel_id(&schid); if (console_irq != -1) { /* VM provided us with the irq number of the console. */ schid.sch_no = console_irq; - if (stsch_err(schid, &console_subchannel.schib) != 0 || - (console_subchannel.schib.pmcw.st != SUBCHANNEL_TYPE_IO) || - !console_subchannel.schib.pmcw.dnv) + if (stsch_err(schid, &schib) != 0 || + (schib.pmcw.st != SUBCHANNEL_TYPE_IO) || !schib.pmcw.dnv) return -1; - console_devno = console_subchannel.schib.pmcw.dev; + console_devno = schib.pmcw.dev; } else if (console_devno != -1) { /* At least the console device number is known. */ for_each_subchannel(cio_test_for_console, NULL); - if (console_irq == -1) - return -1; - } else { - /* unlike in 2.4, we cannot autoprobe here, since - * the channel subsystem is not fully initialized. - * With some luck, the HWC console can take over */ - return -1; } return console_irq; } -struct subchannel * -cio_probe_console(void) +struct subchannel *cio_probe_console(void) { - int sch_no, ret; struct subchannel_id schid; + struct subchannel *sch; + int sch_no, ret; - if (xchg(&console_subchannel_in_use, 1) != 0) - return ERR_PTR(-EBUSY); sch_no = cio_get_console_sch_no(); if (sch_no == -1) { - console_subchannel_in_use = 0; pr_warning("No CCW console was found\n"); return ERR_PTR(-ENODEV); } - memset(&console_subchannel, 0, sizeof(struct subchannel)); init_subchannel_id(&schid); schid.sch_no = sch_no; - ret = cio_validate_subchannel(&console_subchannel, schid); - if (ret) { - console_subchannel_in_use = 0; - return ERR_PTR(-ENODEV); - } + sch = css_alloc_subchannel(schid); + if (IS_ERR(sch)) + return sch; - /* - * enable console I/O-interrupt subclass - */ isc_register(CONSOLE_ISC); - console_subchannel.config.isc = CONSOLE_ISC; - console_subchannel.config.intparm = (u32)(addr_t)&console_subchannel; - ret = cio_commit_config(&console_subchannel); + sch->config.isc = CONSOLE_ISC; + sch->config.intparm = (u32)(addr_t)sch; + ret = cio_commit_config(sch); if (ret) { isc_unregister(CONSOLE_ISC); - console_subchannel_in_use = 0; + put_device(&sch->dev); return ERR_PTR(ret); } - return &console_subchannel; -} - -void -cio_release_console(void) -{ - console_subchannel.config.intparm = 0; - cio_commit_config(&console_subchannel); - isc_unregister(CONSOLE_ISC); - console_subchannel_in_use = 0; + console_sch = sch; + return sch; } -/* Bah... hack to catch console special sausages. */ -int -cio_is_console(struct subchannel_id schid) +int cio_is_console(struct subchannel_id schid) { - if (!console_subchannel_in_use) + if (!console_sch) return 0; - return schid_equal(&schid, &console_subchannel.schid); + return schid_equal(&schid, &console_sch->schid); } -struct subchannel * -cio_get_console_subchannel(void) +void cio_register_early_subchannels(void) { - if (!console_subchannel_in_use) - return NULL; - return &console_subchannel; + int ret; + + if (!console_sch) + return; + + ret = css_register_subchannel(console_sch); + if (ret) + put_device(&console_sch->dev); } +#endif /* CONFIG_CCW_CONSOLE */ -#endif static int __disable_subchannel_easy(struct subchannel_id schid, struct schib *schib) { diff --git a/drivers/s390/cio/cio.h b/drivers/s390/cio/cio.h index 4a1ff5c2eb88..d62f5e7f3cf1 100644 --- a/drivers/s390/cio/cio.h +++ b/drivers/s390/cio/cio.h @@ -121,23 +121,18 @@ extern int cio_commit_config(struct subchannel *sch); int cio_tm_start_key(struct subchannel *sch, struct tcw *tcw, u8 lpm, u8 key); int cio_tm_intrg(struct subchannel *sch); -int cio_create_sch_lock(struct subchannel *); void do_adapter_IO(u8 isc); void do_IRQ(struct pt_regs *); /* Use with care. */ #ifdef CONFIG_CCW_CONSOLE extern struct subchannel *cio_probe_console(void); -extern void cio_release_console(void); extern int cio_is_console(struct subchannel_id); -extern struct subchannel *cio_get_console_subchannel(void); -extern spinlock_t * cio_get_console_lock(void); -extern void *cio_get_console_priv(void); +extern void cio_register_early_subchannels(void); +extern void cio_tsch(struct subchannel *sch); #else #define cio_is_console(schid) 0 -#define cio_get_console_subchannel() NULL -#define cio_get_console_lock() NULL -#define cio_get_console_priv() NULL +static inline void cio_register_early_subchannels(void) {} #endif #endif diff --git a/drivers/s390/cio/css.c b/drivers/s390/cio/css.c index a239237d43f3..1ebe5d3ddebb 100644 --- a/drivers/s390/cio/css.c +++ b/drivers/s390/cio/css.c @@ -137,37 +137,53 @@ out: static void css_sch_todo(struct work_struct *work); -static struct subchannel * -css_alloc_subchannel(struct subchannel_id schid) +static int css_sch_create_locks(struct subchannel *sch) +{ + sch->lock = kmalloc(sizeof(*sch->lock), GFP_KERNEL); + if (!sch->lock) + return -ENOMEM; + + spin_lock_init(sch->lock); + mutex_init(&sch->reg_mutex); + + return 0; +} + +static void css_subchannel_release(struct device *dev) +{ + struct subchannel *sch = to_subchannel(dev); + + sch->config.intparm = 0; + cio_commit_config(sch); + kfree(sch->lock); + kfree(sch); +} + +struct subchannel *css_alloc_subchannel(struct subchannel_id schid) { struct subchannel *sch; int ret; - sch = kmalloc (sizeof (*sch), GFP_KERNEL | GFP_DMA); - if (sch == NULL) + sch = kzalloc(sizeof(*sch), GFP_KERNEL | GFP_DMA); + if (!sch) return ERR_PTR(-ENOMEM); - ret = cio_validate_subchannel (sch, schid); - if (ret < 0) { - kfree(sch); - return ERR_PTR(ret); - } + + ret = cio_validate_subchannel(sch, schid); + if (ret < 0) + goto err; + + ret = css_sch_create_locks(sch); + if (ret) + goto err; + INIT_WORK(&sch->todo_work, css_sch_todo); + sch->dev.release = &css_subchannel_release; + device_initialize(&sch->dev); return sch; -} - -static void -css_subchannel_release(struct device *dev) -{ - struct subchannel *sch; - sch = to_subchannel(dev); - if (!cio_is_console(sch->schid)) { - /* Reset intparm to zeroes. */ - sch->config.intparm = 0; - cio_commit_config(sch); - kfree(sch->lock); - kfree(sch); - } +err: + kfree(sch); + return ERR_PTR(ret); } static int css_sch_device_register(struct subchannel *sch) @@ -177,7 +193,7 @@ static int css_sch_device_register(struct subchannel *sch) mutex_lock(&sch->reg_mutex); dev_set_name(&sch->dev, "0.%x.%04x", sch->schid.ssid, sch->schid.sch_no); - ret = device_register(&sch->dev); + ret = device_add(&sch->dev); mutex_unlock(&sch->reg_mutex); return ret; } @@ -228,16 +244,11 @@ void css_update_ssd_info(struct subchannel *sch) { int ret; - if (cio_is_console(sch->schid)) { - /* Console is initialized too early for functions requiring - * memory allocation. */ + ret = chsc_get_ssd_info(sch->schid, &sch->ssd_info); + if (ret) ssd_from_pmcw(&sch->ssd_info, &sch->schib.pmcw); - } else { - ret = chsc_get_ssd_info(sch->schid, &sch->ssd_info); - if (ret) - ssd_from_pmcw(&sch->ssd_info, &sch->schib.pmcw); - ssd_register_chpids(&sch->ssd_info); - } + + ssd_register_chpids(&sch->ssd_info); } static ssize_t type_show(struct device *dev, struct device_attribute *attr, @@ -275,14 +286,13 @@ static const struct attribute_group *default_subch_attr_groups[] = { NULL, }; -static int css_register_subchannel(struct subchannel *sch) +int css_register_subchannel(struct subchannel *sch) { int ret; /* Initialize the subchannel structure */ sch->dev.parent = &channel_subsystems[0]->device; sch->dev.bus = &css_bus_type; - sch->dev.release = &css_subchannel_release; sch->dev.groups = default_subch_attr_groups; /* * We don't want to generate uevents for I/O subchannels that don't @@ -314,23 +324,19 @@ static int css_register_subchannel(struct subchannel *sch) return ret; } -int css_probe_device(struct subchannel_id schid) +static int css_probe_device(struct subchannel_id schid) { - int ret; struct subchannel *sch; + int ret; + + sch = css_alloc_subchannel(schid); + if (IS_ERR(sch)) + return PTR_ERR(sch); - if (cio_is_console(schid)) - sch = cio_get_console_subchannel(); - else { - sch = css_alloc_subchannel(schid); - if (IS_ERR(sch)) - return PTR_ERR(sch); - } ret = css_register_subchannel(sch); - if (ret) { - if (!cio_is_console(schid)) - put_device(&sch->dev); - } + if (ret) + put_device(&sch->dev); + return ret; } @@ -770,7 +776,7 @@ static int __init setup_css(int nr) css->pseudo_subchannel->dev.release = css_subchannel_release; dev_set_name(&css->pseudo_subchannel->dev, "defunct"); mutex_init(&css->pseudo_subchannel->reg_mutex); - ret = cio_create_sch_lock(css->pseudo_subchannel); + ret = css_sch_create_locks(css->pseudo_subchannel); if (ret) { kfree(css->pseudo_subchannel); return ret; @@ -870,8 +876,7 @@ static struct notifier_block css_power_notifier = { /* * Now that the driver core is running, we can setup our channel subsystem. - * The struct subchannel's are created during probing (except for the - * static console subchannel). + * The struct subchannel's are created during probing. */ static int __init css_bus_init(void) { @@ -1050,6 +1055,8 @@ int css_complete_work(void) */ static int __init channel_subsystem_init_sync(void) { + /* Register subchannels which are already in use. */ + cio_register_early_subchannels(); /* Start initial subchannel evaluation. */ css_schedule_eval_all(); css_complete_work(); @@ -1065,9 +1072,8 @@ void channel_subsystem_reinit(void) chsc_enable_facility(CHSC_SDA_OC_MSS); chp_id_for_each(&chpid) { chp = chpid_to_chp(chpid); - if (!chp) - continue; - chsc_determine_base_channel_path_desc(chpid, &chp->desc); + if (chp) + chp_update_desc(chp); } } diff --git a/drivers/s390/cio/css.h b/drivers/s390/cio/css.h index 4af3dfe70ef5..b1de60335238 100644 --- a/drivers/s390/cio/css.h +++ b/drivers/s390/cio/css.h @@ -101,7 +101,8 @@ extern int css_driver_register(struct css_driver *); extern void css_driver_unregister(struct css_driver *); extern void css_sch_device_unregister(struct subchannel *); -extern int css_probe_device(struct subchannel_id); +extern int css_register_subchannel(struct subchannel *); +extern struct subchannel *css_alloc_subchannel(struct subchannel_id); extern struct subchannel *get_subchannel_by_schid(struct subchannel_id); extern int css_init_done; extern int max_ssid; @@ -109,7 +110,6 @@ int for_each_subchannel_staged(int (*fn_known)(struct subchannel *, void *), int (*fn_unknown)(struct subchannel_id, void *), void *data); extern int for_each_subchannel(int(*fn)(struct subchannel_id, void *), void *); -extern void css_reiterate_subchannels(void); void css_update_ssd_info(struct subchannel *sch); struct channel_subsystem { diff --git a/drivers/s390/cio/device.c b/drivers/s390/cio/device.c index c6767f5a58b2..1ab5f6c36d9b 100644 --- a/drivers/s390/cio/device.c +++ b/drivers/s390/cio/device.c @@ -19,6 +19,7 @@ #include <linux/list.h> #include <linux/device.h> #include <linux/workqueue.h> +#include <linux/delay.h> #include <linux/timer.h> #include <linux/kernel_stat.h> @@ -43,6 +44,10 @@ static DEFINE_SPINLOCK(recovery_lock); static int recovery_phase; static const unsigned long recovery_delay[] = { 3, 30, 300 }; +static atomic_t ccw_device_init_count = ATOMIC_INIT(0); +static DECLARE_WAIT_QUEUE_HEAD(ccw_device_init_wq); +static struct bus_type ccw_bus_type; + /******************* bus type handling ***********************/ /* The Linux driver model distinguishes between a bus type and @@ -127,8 +132,6 @@ static int ccw_uevent(struct device *dev, struct kobj_uevent_env *env) return ret; } -static struct bus_type ccw_bus_type; - static void io_subchannel_irq(struct subchannel *); static int io_subchannel_probe(struct subchannel *); static int io_subchannel_remove(struct subchannel *); @@ -137,8 +140,6 @@ static int io_subchannel_sch_event(struct subchannel *, int); static int io_subchannel_chp_event(struct subchannel *, struct chp_link *, int); static void recovery_func(unsigned long data); -wait_queue_head_t ccw_device_init_wq; -atomic_t ccw_device_init_count; static struct css_device_id io_subchannel_ids[] = { { .match_flags = 0x1, .type = SUBCHANNEL_TYPE_IO, }, @@ -191,10 +192,7 @@ int __init io_subchannel_init(void) { int ret; - init_waitqueue_head(&ccw_device_init_wq); - atomic_set(&ccw_device_init_count, 0); setup_timer(&recovery_timer, recovery_func, 0); - ret = bus_register(&ccw_bus_type); if (ret) return ret; @@ -1086,19 +1084,14 @@ static int io_subchannel_probe(struct subchannel *sch) dev_set_uevent_suppress(&sch->dev, 0); kobject_uevent(&sch->dev.kobj, KOBJ_ADD); cdev = sch_get_cdev(sch); - cdev->dev.groups = ccwdev_attr_groups; - device_initialize(&cdev->dev); - cdev->private->flags.initialized = 1; - ccw_device_register(cdev); - /* - * Check if the device is already online. If it is - * the reference count needs to be corrected since we - * didn't obtain a reference in ccw_device_set_online. - */ - if (cdev->private->state != DEV_STATE_NOT_OPER && - cdev->private->state != DEV_STATE_OFFLINE && - cdev->private->state != DEV_STATE_BOXED) - get_device(&cdev->dev); + rc = ccw_device_register(cdev); + if (rc) { + /* Release online reference. */ + put_device(&cdev->dev); + goto out_schedule; + } + if (atomic_dec_and_test(&ccw_device_init_count)) + wake_up(&ccw_device_init_wq); return 0; } io_subchannel_init_fields(sch); @@ -1580,88 +1573,102 @@ out: } #ifdef CONFIG_CCW_CONSOLE -static struct ccw_device console_cdev; -static struct ccw_device_private console_private; -static int console_cdev_in_use; - -static DEFINE_SPINLOCK(ccw_console_lock); - -spinlock_t * cio_get_console_lock(void) -{ - return &ccw_console_lock; -} - static int ccw_device_console_enable(struct ccw_device *cdev, struct subchannel *sch) { - struct io_subchannel_private *io_priv = cio_get_console_priv(); int rc; - /* Attach subchannel private data. */ - memset(io_priv, 0, sizeof(*io_priv)); - set_io_private(sch, io_priv); io_subchannel_init_fields(sch); rc = cio_commit_config(sch); if (rc) return rc; sch->driver = &io_subchannel_driver; - /* Initialize the ccw_device structure. */ - cdev->dev.parent= &sch->dev; sch_set_cdev(sch, cdev); io_subchannel_recog(cdev, sch); /* Now wait for the async. recognition to come to an end. */ spin_lock_irq(cdev->ccwlock); while (!dev_fsm_final_state(cdev)) - wait_cons_dev(); - rc = -EIO; - if (cdev->private->state != DEV_STATE_OFFLINE) + ccw_device_wait_idle(cdev); + + /* Hold on to an extra reference while device is online. */ + get_device(&cdev->dev); + rc = ccw_device_online(cdev); + if (rc) goto out_unlock; - ccw_device_online(cdev); + while (!dev_fsm_final_state(cdev)) - wait_cons_dev(); - if (cdev->private->state != DEV_STATE_ONLINE) - goto out_unlock; - rc = 0; + ccw_device_wait_idle(cdev); + + if (cdev->private->state == DEV_STATE_ONLINE) + cdev->online = 1; + else + rc = -EIO; out_unlock: spin_unlock_irq(cdev->ccwlock); + if (rc) /* Give up online reference since onlining failed. */ + put_device(&cdev->dev); return rc; } -struct ccw_device * -ccw_device_probe_console(void) +struct ccw_device *ccw_device_probe_console(void) { + struct io_subchannel_private *io_priv; + struct ccw_device *cdev; struct subchannel *sch; int ret; - if (xchg(&console_cdev_in_use, 1) != 0) - return ERR_PTR(-EBUSY); sch = cio_probe_console(); - if (IS_ERR(sch)) { - console_cdev_in_use = 0; - return (void *) sch; + if (IS_ERR(sch)) + return ERR_CAST(sch); + + io_priv = kzalloc(sizeof(*io_priv), GFP_KERNEL | GFP_DMA); + if (!io_priv) { + put_device(&sch->dev); + return ERR_PTR(-ENOMEM); } - memset(&console_cdev, 0, sizeof(struct ccw_device)); - memset(&console_private, 0, sizeof(struct ccw_device_private)); - console_cdev.private = &console_private; - console_private.cdev = &console_cdev; - console_private.int_class = IRQIO_CIO; - ret = ccw_device_console_enable(&console_cdev, sch); + cdev = io_subchannel_create_ccwdev(sch); + if (IS_ERR(cdev)) { + put_device(&sch->dev); + kfree(io_priv); + return cdev; + } + set_io_private(sch, io_priv); + ret = ccw_device_console_enable(cdev, sch); if (ret) { - cio_release_console(); - console_cdev_in_use = 0; + set_io_private(sch, NULL); + put_device(&sch->dev); + put_device(&cdev->dev); + kfree(io_priv); return ERR_PTR(ret); } - console_cdev.online = 1; - return &console_cdev; + return cdev; +} + +/** + * ccw_device_wait_idle() - busy wait for device to become idle + * @cdev: ccw device + * + * Poll until activity control is zero, that is, no function or data + * transfer is pending/active. + * Called with device lock being held. + */ +void ccw_device_wait_idle(struct ccw_device *cdev) +{ + struct subchannel *sch = to_subchannel(cdev->dev.parent); + + while (1) { + cio_tsch(sch); + if (sch->schib.scsw.cmd.actl == 0) + break; + udelay_simple(100); + } } static int ccw_device_pm_restore(struct device *dev); -int ccw_device_force_console(void) +int ccw_device_force_console(struct ccw_device *cdev) { - if (!console_cdev_in_use) - return -ENODEV; - return ccw_device_pm_restore(&console_cdev.dev); + return ccw_device_pm_restore(&cdev->dev); } EXPORT_SYMBOL_GPL(ccw_device_force_console); #endif diff --git a/drivers/s390/cio/device.h b/drivers/s390/cio/device.h index 7d4ecb65db00..8d1d29873172 100644 --- a/drivers/s390/cio/device.h +++ b/drivers/s390/cio/device.h @@ -81,8 +81,6 @@ dev_fsm_final_state(struct ccw_device *cdev) cdev->private->state == DEV_STATE_BOXED); } -extern wait_queue_head_t ccw_device_init_wq; -extern atomic_t ccw_device_init_count; int __init io_subchannel_init(void); void io_subchannel_recog_done(struct ccw_device *cdev); diff --git a/drivers/s390/cio/device_ops.c b/drivers/s390/cio/device_ops.c index c77b6e06bf64..4845d64f2842 100644 --- a/drivers/s390/cio/device_ops.c +++ b/drivers/s390/cio/device_ops.c @@ -704,9 +704,9 @@ EXPORT_SYMBOL(ccw_device_tm_start_timeout); int ccw_device_get_mdc(struct ccw_device *cdev, u8 mask) { struct subchannel *sch = to_subchannel(cdev->dev.parent); - struct channel_path_desc_fmt1 desc; + struct channel_path *chp; struct chp_id chpid; - int mdc = 0, ret, i; + int mdc = 0, i; /* Adjust requested path mask to excluded varied off paths. */ if (mask) @@ -719,14 +719,20 @@ int ccw_device_get_mdc(struct ccw_device *cdev, u8 mask) if (!(mask & (0x80 >> i))) continue; chpid.id = sch->schib.pmcw.chpid[i]; - ret = chsc_determine_fmt1_channel_path_desc(chpid, &desc); - if (ret) - return ret; - if (!desc.f) + chp = chpid_to_chp(chpid); + if (!chp) + continue; + + mutex_lock(&chp->lock); + if (!chp->desc_fmt1.f) { + mutex_unlock(&chp->lock); return 0; - if (!desc.r) + } + if (!chp->desc_fmt1.r) mdc = 1; - mdc = mdc ? min(mdc, (int)desc.mdc) : desc.mdc; + mdc = mdc ? min_t(int, mdc, chp->desc_fmt1.mdc) : + chp->desc_fmt1.mdc; + mutex_unlock(&chp->lock); } return mdc; diff --git a/drivers/s390/cio/idset.c b/drivers/s390/cio/idset.c index 65d13e38803f..5a999084a229 100644 --- a/drivers/s390/cio/idset.c +++ b/drivers/s390/cio/idset.c @@ -17,7 +17,7 @@ struct idset { static inline unsigned long bitmap_size(int num_ssid, int num_id) { - return __BITOPS_WORDS(num_ssid * num_id) * sizeof(unsigned long); + return BITS_TO_LONGS(num_ssid * num_id) * sizeof(unsigned long); } static struct idset *idset_new(int num_ssid, int num_id) diff --git a/drivers/sbus/char/bbc_i2c.c b/drivers/sbus/char/bbc_i2c.c index 1a9d1e3ce64c..c1441ed282eb 100644 --- a/drivers/sbus/char/bbc_i2c.c +++ b/drivers/sbus/char/bbc_i2c.c @@ -282,7 +282,7 @@ static irqreturn_t bbc_i2c_interrupt(int irq, void *dev_id) return IRQ_HANDLED; } -static void __init reset_one_i2c(struct bbc_i2c_bus *bp) +static void reset_one_i2c(struct bbc_i2c_bus *bp) { writeb(I2C_PCF_PIN, bp->i2c_control_regs + 0x0); writeb(bp->own, bp->i2c_control_regs + 0x1); @@ -291,7 +291,7 @@ static void __init reset_one_i2c(struct bbc_i2c_bus *bp) writeb(I2C_PCF_IDLE, bp->i2c_control_regs + 0x0); } -static struct bbc_i2c_bus * __init attach_one_i2c(struct platform_device *op, int index) +static struct bbc_i2c_bus * attach_one_i2c(struct platform_device *op, int index) { struct bbc_i2c_bus *bp; struct device_node *dp; diff --git a/drivers/scsi/megaraid/megaraid_sas.h b/drivers/scsi/megaraid/megaraid_sas.h index 408d2548a748..684cc343cf09 100644 --- a/drivers/scsi/megaraid/megaraid_sas.h +++ b/drivers/scsi/megaraid/megaraid_sas.h @@ -1488,7 +1488,4 @@ struct megasas_mgmt_info { int max_index; }; -#define msi_control_reg(base) (base + PCI_MSI_FLAGS) -#define PCI_MSIX_FLAGS_ENABLE (1 << 15) - #endif /*LSI_MEGARAID_SAS_H */ diff --git a/drivers/scsi/megaraid/megaraid_sas_base.c b/drivers/scsi/megaraid/megaraid_sas_base.c index 9d53540207ec..7c90d57b867e 100644 --- a/drivers/scsi/megaraid/megaraid_sas_base.c +++ b/drivers/scsi/megaraid/megaraid_sas_base.c @@ -3984,12 +3984,12 @@ static int megasas_probe_one(struct pci_dev *pdev, if (reset_devices) { pos = pci_find_capability(pdev, PCI_CAP_ID_MSIX); if (pos) { - pci_read_config_word(pdev, msi_control_reg(pos), + pci_read_config_word(pdev, pos + PCI_MSIX_FLAGS, &control); if (control & PCI_MSIX_FLAGS_ENABLE) { dev_info(&pdev->dev, "resetting MSI-X\n"); pci_write_config_word(pdev, - msi_control_reg(pos), + pos + PCI_MSIX_FLAGS, control & ~PCI_MSIX_FLAGS_ENABLE); } diff --git a/drivers/scsi/mvsas/mv_init.c b/drivers/scsi/mvsas/mv_init.c index ce90d0546cdd..74550922ad55 100644 --- a/drivers/scsi/mvsas/mv_init.c +++ b/drivers/scsi/mvsas/mv_init.c @@ -703,7 +703,7 @@ static struct pci_device_id mvs_pci_table[] = { { PCI_VDEVICE(TTI, 0x2744), chip_9480 }, { PCI_VDEVICE(TTI, 0x2760), chip_9480 }, { - .vendor = 0x1b4b, + .vendor = PCI_VENDOR_ID_MARVELL_EXT, .device = 0x9480, .subvendor = PCI_ANY_ID, .subdevice = 0x9480, @@ -712,7 +712,7 @@ static struct pci_device_id mvs_pci_table[] = { .driver_data = chip_9480, }, { - .vendor = 0x1b4b, + .vendor = PCI_VENDOR_ID_MARVELL_EXT, .device = 0x9445, .subvendor = PCI_ANY_ID, .subdevice = 0x9480, @@ -721,7 +721,7 @@ static struct pci_device_id mvs_pci_table[] = { .driver_data = chip_9445, }, { - .vendor = 0x1b4b, + .vendor = PCI_VENDOR_ID_MARVELL_EXT, .device = 0x9485, .subvendor = PCI_ANY_ID, .subdevice = 0x9480, diff --git a/drivers/scsi/mvumi.c b/drivers/scsi/mvumi.c index 4594ccaaf49b..c3601b57a80c 100644 --- a/drivers/scsi/mvumi.c +++ b/drivers/scsi/mvumi.c @@ -49,8 +49,8 @@ MODULE_AUTHOR("jyli@marvell.com"); MODULE_DESCRIPTION("Marvell UMI Driver"); static DEFINE_PCI_DEVICE_TABLE(mvumi_pci_table) = { - { PCI_DEVICE(PCI_VENDOR_ID_MARVELL_2, PCI_DEVICE_ID_MARVELL_MV9143) }, - { PCI_DEVICE(PCI_VENDOR_ID_MARVELL_2, PCI_DEVICE_ID_MARVELL_MV9580) }, + { PCI_DEVICE(PCI_VENDOR_ID_MARVELL_EXT, PCI_DEVICE_ID_MARVELL_MV9143) }, + { PCI_DEVICE(PCI_VENDOR_ID_MARVELL_EXT, PCI_DEVICE_ID_MARVELL_MV9580) }, { 0 } }; diff --git a/drivers/scsi/mvumi.h b/drivers/scsi/mvumi.h index e360135fd1bd..41f168702ac7 100644 --- a/drivers/scsi/mvumi.h +++ b/drivers/scsi/mvumi.h @@ -32,7 +32,6 @@ #define VER_BUILD 1500 #define MV_DRIVER_NAME "mvumi" -#define PCI_VENDOR_ID_MARVELL_2 0x1b4b #define PCI_DEVICE_ID_MARVELL_MV9143 0x9143 #define PCI_DEVICE_ID_MARVELL_MV9580 0x9580 diff --git a/drivers/ssb/driver_chipcommon_pmu.c b/drivers/ssb/driver_chipcommon_pmu.c index 4c0f6d883dd3..7b0bce936762 100644 --- a/drivers/ssb/driver_chipcommon_pmu.c +++ b/drivers/ssb/driver_chipcommon_pmu.c @@ -675,3 +675,32 @@ u32 ssb_pmu_get_controlclock(struct ssb_chipcommon *cc) return 0; } } + +void ssb_pmu_spuravoid_pllupdate(struct ssb_chipcommon *cc, int spuravoid) +{ + u32 pmu_ctl = 0; + + switch (cc->dev->bus->chip_id) { + case 0x4322: + ssb_chipco_pll_write(cc, SSB_PMU1_PLLCTL0, 0x11100070); + ssb_chipco_pll_write(cc, SSB_PMU1_PLLCTL1, 0x1014140a); + ssb_chipco_pll_write(cc, SSB_PMU1_PLLCTL5, 0x88888854); + if (spuravoid == 1) + ssb_chipco_pll_write(cc, SSB_PMU1_PLLCTL2, 0x05201828); + else + ssb_chipco_pll_write(cc, SSB_PMU1_PLLCTL2, 0x05001828); + pmu_ctl = SSB_CHIPCO_PMU_CTL_PLL_UPD; + break; + case 43222: + /* TODO: BCM43222 requires updating PLLs too */ + return; + default: + ssb_printk(KERN_ERR PFX + "Unknown spuravoidance settings for chip 0x%04X, not changing PLL\n", + cc->dev->bus->chip_id); + return; + } + + chipco_set32(cc, SSB_CHIPCO_PMU_CTL, pmu_ctl); +} +EXPORT_SYMBOL_GPL(ssb_pmu_spuravoid_pllupdate); diff --git a/drivers/ssbi/Kconfig b/drivers/ssbi/Kconfig new file mode 100644 index 000000000000..1ae4040afedd --- /dev/null +++ b/drivers/ssbi/Kconfig @@ -0,0 +1,16 @@ +# +# SSBI bus support +# + +menu "Qualcomm MSM SSBI bus support" + +config SSBI + tristate "Qualcomm Single-wire Serial Bus Interface (SSBI)" + help + If you say yes to this option, support will be included for the + built-in SSBI interface on Qualcomm MSM family processors. + + This is required for communicating with Qualcomm PMICs and + other devices that have the SSBI interface. + +endmenu diff --git a/drivers/ssbi/Makefile b/drivers/ssbi/Makefile new file mode 100644 index 000000000000..38fb70c31caf --- /dev/null +++ b/drivers/ssbi/Makefile @@ -0,0 +1 @@ +obj-$(CONFIG_SSBI) += ssbi.o diff --git a/drivers/ssbi/ssbi.c b/drivers/ssbi/ssbi.c new file mode 100644 index 000000000000..f32da0258a8e --- /dev/null +++ b/drivers/ssbi/ssbi.c @@ -0,0 +1,379 @@ +/* Copyright (c) 2009-2013, The Linux Foundation. All rights reserved. + * Copyright (c) 2010, Google Inc. + * + * Original authors: Code Aurora Forum + * + * Author: Dima Zavin <dima@android.com> + * - Largely rewritten from original to not be an i2c 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 and + * only 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. + */ + +#define pr_fmt(fmt) "%s: " fmt, __func__ + +#include <linux/delay.h> +#include <linux/err.h> +#include <linux/io.h> +#include <linux/kernel.h> +#include <linux/platform_device.h> +#include <linux/slab.h> +#include <linux/ssbi.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_device.h> + +/* SSBI 2.0 controller registers */ +#define SSBI2_CMD 0x0008 +#define SSBI2_RD 0x0010 +#define SSBI2_STATUS 0x0014 +#define SSBI2_MODE2 0x001C + +/* SSBI_CMD fields */ +#define SSBI_CMD_RDWRN (1 << 24) + +/* SSBI_STATUS fields */ +#define SSBI_STATUS_RD_READY (1 << 2) +#define SSBI_STATUS_READY (1 << 1) +#define SSBI_STATUS_MCHN_BUSY (1 << 0) + +/* SSBI_MODE2 fields */ +#define SSBI_MODE2_REG_ADDR_15_8_SHFT 0x04 +#define SSBI_MODE2_REG_ADDR_15_8_MASK (0x7f << SSBI_MODE2_REG_ADDR_15_8_SHFT) + +#define SET_SSBI_MODE2_REG_ADDR_15_8(MD, AD) \ + (((MD) & 0x0F) | ((((AD) >> 8) << SSBI_MODE2_REG_ADDR_15_8_SHFT) & \ + SSBI_MODE2_REG_ADDR_15_8_MASK)) + +/* SSBI PMIC Arbiter command registers */ +#define SSBI_PA_CMD 0x0000 +#define SSBI_PA_RD_STATUS 0x0004 + +/* SSBI_PA_CMD fields */ +#define SSBI_PA_CMD_RDWRN (1 << 24) +#define SSBI_PA_CMD_ADDR_MASK 0x7fff /* REG_ADDR_7_0, REG_ADDR_8_14*/ + +/* SSBI_PA_RD_STATUS fields */ +#define SSBI_PA_RD_STATUS_TRANS_DONE (1 << 27) +#define SSBI_PA_RD_STATUS_TRANS_DENIED (1 << 26) + +#define SSBI_TIMEOUT_US 100 + +struct ssbi { + struct device *slave; + void __iomem *base; + spinlock_t lock; + enum ssbi_controller_type controller_type; + int (*read)(struct ssbi *, u16 addr, u8 *buf, int len); + int (*write)(struct ssbi *, u16 addr, u8 *buf, int len); +}; + +#define to_ssbi(dev) platform_get_drvdata(to_platform_device(dev)) + +static inline u32 ssbi_readl(struct ssbi *ssbi, u32 reg) +{ + return readl(ssbi->base + reg); +} + +static inline void ssbi_writel(struct ssbi *ssbi, u32 val, u32 reg) +{ + writel(val, ssbi->base + reg); +} + +/* + * Via private exchange with one of the original authors, the hardware + * should generally finish a transaction in about 5us. The worst + * case, is when using the arbiter and both other CPUs have just + * started trying to use the SSBI bus will result in a time of about + * 20us. It should never take longer than this. + * + * As such, this wait merely spins, with a udelay. + */ +static int ssbi_wait_mask(struct ssbi *ssbi, u32 set_mask, u32 clr_mask) +{ + u32 timeout = SSBI_TIMEOUT_US; + u32 val; + + while (timeout--) { + val = ssbi_readl(ssbi, SSBI2_STATUS); + if (((val & set_mask) == set_mask) && ((val & clr_mask) == 0)) + return 0; + udelay(1); + } + + return -ETIMEDOUT; +} + +static int +ssbi_read_bytes(struct ssbi *ssbi, u16 addr, u8 *buf, int len) +{ + u32 cmd = SSBI_CMD_RDWRN | ((addr & 0xff) << 16); + int ret = 0; + + if (ssbi->controller_type == MSM_SBI_CTRL_SSBI2) { + u32 mode2 = ssbi_readl(ssbi, SSBI2_MODE2); + mode2 = SET_SSBI_MODE2_REG_ADDR_15_8(mode2, addr); + ssbi_writel(ssbi, mode2, SSBI2_MODE2); + } + + while (len) { + ret = ssbi_wait_mask(ssbi, SSBI_STATUS_READY, 0); + if (ret) + goto err; + + ssbi_writel(ssbi, cmd, SSBI2_CMD); + ret = ssbi_wait_mask(ssbi, SSBI_STATUS_RD_READY, 0); + if (ret) + goto err; + *buf++ = ssbi_readl(ssbi, SSBI2_RD) & 0xff; + len--; + } + +err: + return ret; +} + +static int +ssbi_write_bytes(struct ssbi *ssbi, u16 addr, u8 *buf, int len) +{ + int ret = 0; + + if (ssbi->controller_type == MSM_SBI_CTRL_SSBI2) { + u32 mode2 = ssbi_readl(ssbi, SSBI2_MODE2); + mode2 = SET_SSBI_MODE2_REG_ADDR_15_8(mode2, addr); + ssbi_writel(ssbi, mode2, SSBI2_MODE2); + } + + while (len) { + ret = ssbi_wait_mask(ssbi, SSBI_STATUS_READY, 0); + if (ret) + goto err; + + ssbi_writel(ssbi, ((addr & 0xff) << 16) | *buf, SSBI2_CMD); + ret = ssbi_wait_mask(ssbi, 0, SSBI_STATUS_MCHN_BUSY); + if (ret) + goto err; + buf++; + len--; + } + +err: + return ret; +} + +/* + * See ssbi_wait_mask for an explanation of the time and the + * busywait. + */ +static inline int +ssbi_pa_transfer(struct ssbi *ssbi, u32 cmd, u8 *data) +{ + u32 timeout = SSBI_TIMEOUT_US; + u32 rd_status = 0; + + ssbi_writel(ssbi, cmd, SSBI_PA_CMD); + + while (timeout--) { + rd_status = ssbi_readl(ssbi, SSBI_PA_RD_STATUS); + + if (rd_status & SSBI_PA_RD_STATUS_TRANS_DENIED) + return -EPERM; + + if (rd_status & SSBI_PA_RD_STATUS_TRANS_DONE) { + if (data) + *data = rd_status & 0xff; + return 0; + } + udelay(1); + } + + return -ETIMEDOUT; +} + +static int +ssbi_pa_read_bytes(struct ssbi *ssbi, u16 addr, u8 *buf, int len) +{ + u32 cmd; + int ret = 0; + + cmd = SSBI_PA_CMD_RDWRN | (addr & SSBI_PA_CMD_ADDR_MASK) << 8; + + while (len) { + ret = ssbi_pa_transfer(ssbi, cmd, buf); + if (ret) + goto err; + buf++; + len--; + } + +err: + return ret; +} + +static int +ssbi_pa_write_bytes(struct ssbi *ssbi, u16 addr, u8 *buf, int len) +{ + u32 cmd; + int ret = 0; + + while (len) { + cmd = (addr & SSBI_PA_CMD_ADDR_MASK) << 8 | *buf; + ret = ssbi_pa_transfer(ssbi, cmd, NULL); + if (ret) + goto err; + buf++; + len--; + } + +err: + return ret; +} + +int ssbi_read(struct device *dev, u16 addr, u8 *buf, int len) +{ + struct ssbi *ssbi = to_ssbi(dev); + unsigned long flags; + int ret; + + spin_lock_irqsave(&ssbi->lock, flags); + ret = ssbi->read(ssbi, addr, buf, len); + spin_unlock_irqrestore(&ssbi->lock, flags); + + return ret; +} +EXPORT_SYMBOL_GPL(ssbi_read); + +int ssbi_write(struct device *dev, u16 addr, u8 *buf, int len) +{ + struct ssbi *ssbi = to_ssbi(dev); + unsigned long flags; + int ret; + + spin_lock_irqsave(&ssbi->lock, flags); + ret = ssbi->write(ssbi, addr, buf, len); + spin_unlock_irqrestore(&ssbi->lock, flags); + + return ret; +} +EXPORT_SYMBOL_GPL(ssbi_write); + +static int ssbi_probe(struct platform_device *pdev) +{ + struct device_node *np = pdev->dev.of_node; + struct resource *mem_res; + struct ssbi *ssbi; + int ret = 0; + const char *type; + + ssbi = kzalloc(sizeof(struct ssbi), GFP_KERNEL); + if (!ssbi) { + pr_err("can not allocate ssbi_data\n"); + return -ENOMEM; + } + + mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!mem_res) { + pr_err("missing mem resource\n"); + ret = -EINVAL; + goto err_get_mem_res; + } + + ssbi->base = ioremap(mem_res->start, resource_size(mem_res)); + if (!ssbi->base) { + pr_err("ioremap of 0x%p failed\n", (void *)mem_res->start); + ret = -EINVAL; + goto err_ioremap; + } + platform_set_drvdata(pdev, ssbi); + + type = of_get_property(np, "qcom,controller-type", NULL); + if (type == NULL) { + pr_err("Missing qcom,controller-type property\n"); + ret = -EINVAL; + goto err_ssbi_controller; + } + dev_info(&pdev->dev, "SSBI controller type: '%s'\n", type); + if (strcmp(type, "ssbi") == 0) + ssbi->controller_type = MSM_SBI_CTRL_SSBI; + else if (strcmp(type, "ssbi2") == 0) + ssbi->controller_type = MSM_SBI_CTRL_SSBI2; + else if (strcmp(type, "pmic-arbiter") == 0) + ssbi->controller_type = MSM_SBI_CTRL_PMIC_ARBITER; + else { + pr_err("Unknown qcom,controller-type\n"); + ret = -EINVAL; + goto err_ssbi_controller; + } + + if (ssbi->controller_type == MSM_SBI_CTRL_PMIC_ARBITER) { + ssbi->read = ssbi_pa_read_bytes; + ssbi->write = ssbi_pa_write_bytes; + } else { + ssbi->read = ssbi_read_bytes; + ssbi->write = ssbi_write_bytes; + } + + spin_lock_init(&ssbi->lock); + + ret = of_platform_populate(np, NULL, NULL, &pdev->dev); + if (ret) + goto err_ssbi_controller; + + return 0; + +err_ssbi_controller: + platform_set_drvdata(pdev, NULL); + iounmap(ssbi->base); +err_ioremap: +err_get_mem_res: + kfree(ssbi); + return ret; +} + +static int ssbi_remove(struct platform_device *pdev) +{ + struct ssbi *ssbi = platform_get_drvdata(pdev); + + platform_set_drvdata(pdev, NULL); + iounmap(ssbi->base); + kfree(ssbi); + return 0; +} + +static struct of_device_id ssbi_match_table[] = { + { .compatible = "qcom,ssbi" }, + {} +}; + +static struct platform_driver ssbi_driver = { + .probe = ssbi_probe, + .remove = ssbi_remove, + .driver = { + .name = "ssbi", + .owner = THIS_MODULE, + .of_match_table = ssbi_match_table, + }, +}; + +static int __init ssbi_init(void) +{ + return platform_driver_register(&ssbi_driver); +} +module_init(ssbi_init); + +static void __exit ssbi_exit(void) +{ + platform_driver_unregister(&ssbi_driver); +} +module_exit(ssbi_exit) + +MODULE_LICENSE("GPL v2"); +MODULE_VERSION("1.0"); +MODULE_ALIAS("platform:ssbi"); +MODULE_AUTHOR("Dima Zavin <dima@android.com>"); diff --git a/drivers/tty/serial/8250/serial_cs.c b/drivers/tty/serial/8250/serial_cs.c index b7d48b346393..1b74b88e1e1e 100644 --- a/drivers/tty/serial/8250/serial_cs.c +++ b/drivers/tty/serial/8250/serial_cs.c @@ -852,18 +852,6 @@ static struct pcmcia_driver serial_cs_driver = { .suspend = serial_suspend, .resume = serial_resume, }; - -static int __init init_serial_cs(void) -{ - return pcmcia_register_driver(&serial_cs_driver); -} - -static void __exit exit_serial_cs(void) -{ - pcmcia_unregister_driver(&serial_cs_driver); -} - -module_init(init_serial_cs); -module_exit(exit_serial_cs); +module_pcmcia_driver(serial_cs_driver); MODULE_LICENSE("GPL"); diff --git a/drivers/tty/tty_io.c b/drivers/tty/tty_io.c index 05400acbc456..b0452688308c 100644 --- a/drivers/tty/tty_io.c +++ b/drivers/tty/tty_io.c @@ -941,6 +941,14 @@ void start_tty(struct tty_struct *tty) EXPORT_SYMBOL(start_tty); +static void tty_update_time(struct timespec *time) +{ + unsigned long sec = get_seconds(); + sec -= sec % 60; + if ((long)(sec - time->tv_sec) > 0) + time->tv_sec = sec; +} + /** * tty_read - read method for tty device files * @file: pointer to tty file @@ -960,10 +968,11 @@ static ssize_t tty_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) { int i; + struct inode *inode = file_inode(file); struct tty_struct *tty = file_tty(file); struct tty_ldisc *ld; - if (tty_paranoia_check(tty, file_inode(file), "tty_read")) + if (tty_paranoia_check(tty, inode, "tty_read")) return -EIO; if (!tty || (test_bit(TTY_IO_ERROR, &tty->flags))) return -EIO; @@ -977,6 +986,9 @@ static ssize_t tty_read(struct file *file, char __user *buf, size_t count, i = -EIO; tty_ldisc_deref(ld); + if (i > 0) + tty_update_time(&inode->i_atime); + return i; } @@ -1077,8 +1089,10 @@ static inline ssize_t do_tty_write( break; cond_resched(); } - if (written) + if (written) { + tty_update_time(&file_inode(file)->i_mtime); ret = written; + } out: tty_write_unlock(tty); return ret; diff --git a/drivers/uio/uio.c b/drivers/uio/uio.c index c8b926291e28..b645c47501b4 100644 --- a/drivers/uio/uio.c +++ b/drivers/uio/uio.c @@ -374,6 +374,7 @@ static int uio_get_minor(struct uio_device *idev) retval = idr_alloc(&uio_idr, idev, 0, UIO_MAX_DEVICES, GFP_KERNEL); if (retval >= 0) { idev->minor = retval; + retval = 0; } else if (retval == -ENOSPC) { dev_err(idev->dev, "too many uio devices\n"); retval = -EINVAL; diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c index f81b92572735..e092b414dc50 100644 --- a/drivers/usb/core/usb.c +++ b/drivers/usb/core/usb.c @@ -317,7 +317,8 @@ static const struct dev_pm_ops usb_device_pm_ops = { #endif /* CONFIG_PM */ -static char *usb_devnode(struct device *dev, umode_t *mode) +static char *usb_devnode(struct device *dev, + umode_t *mode, kuid_t *uid, kgid_t *gid) { struct usb_device *usb_dev; diff --git a/drivers/usb/host/sl811_cs.c b/drivers/usb/host/sl811_cs.c index 3b6f50eaec91..469564e57a52 100644 --- a/drivers/usb/host/sl811_cs.c +++ b/drivers/usb/host/sl811_cs.c @@ -200,17 +200,4 @@ static struct pcmcia_driver sl811_cs_driver = { .remove = sl811_cs_detach, .id_table = sl811_ids, }; - -/*====================================================================*/ - -static int __init init_sl811_cs(void) -{ - return pcmcia_register_driver(&sl811_cs_driver); -} -module_init(init_sl811_cs); - -static void __exit exit_sl811_cs(void) -{ - pcmcia_unregister_driver(&sl811_cs_driver); -} -module_exit(exit_sl811_cs); +module_pcmcia_driver(sl811_cs_driver); diff --git a/drivers/vfio/pci/vfio_pci.c b/drivers/vfio/pci/vfio_pci.c index 7abc5c81af2c..09d2e3ffd6fc 100644 --- a/drivers/vfio/pci/vfio_pci.c +++ b/drivers/vfio/pci/vfio_pci.c @@ -70,7 +70,7 @@ static int vfio_pci_enable(struct vfio_pci_device *vdev) pci_write_config_word(pdev, PCI_COMMAND, cmd); } - msix_pos = pci_find_capability(pdev, PCI_CAP_ID_MSIX); + msix_pos = pdev->msix_cap; if (msix_pos) { u16 flags; u32 table; @@ -78,8 +78,8 @@ static int vfio_pci_enable(struct vfio_pci_device *vdev) pci_read_config_word(pdev, msix_pos + PCI_MSIX_FLAGS, &flags); pci_read_config_dword(pdev, msix_pos + PCI_MSIX_TABLE, &table); - vdev->msix_bar = table & PCI_MSIX_FLAGS_BIRMASK; - vdev->msix_offset = table & ~PCI_MSIX_FLAGS_BIRMASK; + vdev->msix_bar = table & PCI_MSIX_TABLE_BIR; + vdev->msix_offset = table & PCI_MSIX_TABLE_OFFSET; vdev->msix_size = ((flags & PCI_MSIX_FLAGS_QSIZE) + 1) * 16; } else vdev->msix_bar = 0xFF; @@ -183,7 +183,7 @@ static int vfio_pci_get_irq_count(struct vfio_pci_device *vdev, int irq_type) u8 pos; u16 flags; - pos = pci_find_capability(vdev->pdev, PCI_CAP_ID_MSI); + pos = vdev->pdev->msi_cap; if (pos) { pci_read_config_word(vdev->pdev, pos + PCI_MSI_FLAGS, &flags); @@ -194,7 +194,7 @@ static int vfio_pci_get_irq_count(struct vfio_pci_device *vdev, int irq_type) u8 pos; u16 flags; - pos = pci_find_capability(vdev->pdev, PCI_CAP_ID_MSIX); + pos = vdev->pdev->msix_cap; if (pos) { pci_read_config_word(vdev->pdev, pos + PCI_MSIX_FLAGS, &flags); diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig index 4c1546f71d56..981c1c08c727 100644 --- a/drivers/video/Kconfig +++ b/drivers/video/Kconfig @@ -31,26 +31,8 @@ config VIDEO_OUTPUT_CONTROL This framework adds support for low-level control of the video output switch. -config DISPLAY_TIMING - bool - -config VIDEOMODE - bool - -config OF_DISPLAY_TIMING - bool "Enable device tree display timing support" - depends on OF - select DISPLAY_TIMING - help - helper to parse display timings from the devicetree - -config OF_VIDEOMODE - bool "Enable device tree videomode support" - depends on OF - select VIDEOMODE - select OF_DISPLAY_TIMING - help - helper to get videomodes from the devicetree +config VIDEOMODE_HELPERS + bool config HDMI bool @@ -212,14 +194,6 @@ config FB_SYS_FOPS depends on FB default n -config FB_WMT_GE_ROPS - tristate - depends on FB - default n - ---help--- - Include functions for accelerated rectangle filling and area - copying using WonderMedia Graphics Engine operations. - config FB_DEFERRED_IO bool depends on FB @@ -1797,22 +1771,37 @@ config FB_AU1200 option au1200fb:panel=<name>. config FB_VT8500 - bool "VT8500 LCD Driver" + bool "VIA VT8500 framebuffer support" depends on (FB = y) && ARM && ARCH_VT8500 - select FB_WMT_GE_ROPS + select FB_SYS_FILLRECT if (!FB_WMT_GE_ROPS) + select FB_SYS_COPYAREA if (!FB_WMT_GE_ROPS) select FB_SYS_IMAGEBLIT + select FB_MODE_HELPERS + select VIDEOMODE_HELPERS help This is the framebuffer driver for VIA VT8500 integrated LCD controller. config FB_WM8505 - bool "WM8505 frame buffer support" + bool "Wondermedia WM8xxx-series frame buffer support" depends on (FB = y) && ARM && ARCH_VT8500 - select FB_WMT_GE_ROPS + select FB_SYS_FILLRECT if (!FB_WMT_GE_ROPS) + select FB_SYS_COPYAREA if (!FB_WMT_GE_ROPS) select FB_SYS_IMAGEBLIT + select FB_MODE_HELPERS + select VIDEOMODE_HELPERS + help + This is the framebuffer driver for WonderMedia WM8xxx-series + integrated LCD controller. This driver covers the WM8505, WM8650 + and WM8850 SoCs. + +config FB_WMT_GE_ROPS + bool "VT8500/WM8xxx accelerated raster ops support" + depends on (FB = y) && (FB_VT8500 || FB_WM8505) + default n help - This is the framebuffer driver for WonderMedia WM8505/WM8650 - integrated LCD controller. + This adds support for accelerated raster operations on the + VIA VT8500 and Wondermedia 85xx series SoCs. source "drivers/video/geode/Kconfig" @@ -2277,7 +2266,7 @@ config XEN_FBDEV_FRONTEND select FB_SYS_IMAGEBLIT select FB_SYS_FOPS select FB_DEFERRED_IO - select INPUT_XEN_KBDDEV_FRONTEND + select INPUT_XEN_KBDDEV_FRONTEND if INPUT_MISC select XEN_XENBUS_FRONTEND default y help diff --git a/drivers/video/Makefile b/drivers/video/Makefile index 9df387334cb7..e414378d6a51 100644 --- a/drivers/video/Makefile +++ b/drivers/video/Makefile @@ -171,7 +171,7 @@ obj-$(CONFIG_FB_VIRTUAL) += vfb.o #video output switch sysfs driver obj-$(CONFIG_VIDEO_OUTPUT_CONTROL) += output.o -obj-$(CONFIG_DISPLAY_TIMING) += display_timing.o -obj-$(CONFIG_OF_DISPLAY_TIMING) += of_display_timing.o -obj-$(CONFIG_VIDEOMODE) += videomode.o -obj-$(CONFIG_OF_VIDEOMODE) += of_videomode.o +obj-$(CONFIG_VIDEOMODE_HELPERS) += display_timing.o videomode.o +ifeq ($(CONFIG_OF),y) +obj-$(CONFIG_VIDEOMODE_HELPERS) += of_display_timing.o of_videomode.o +endif diff --git a/drivers/video/amifb.c b/drivers/video/amifb.c index 7fa1bf823729..77cb4ffa1fe4 100644 --- a/drivers/video/amifb.c +++ b/drivers/video/amifb.c @@ -3788,19 +3788,7 @@ static struct platform_driver amifb_driver = { }, }; -static int __init amifb_init(void) -{ - return platform_driver_probe(&amifb_driver, amifb_probe); -} - -module_init(amifb_init); - -static void __exit amifb_exit(void) -{ - platform_driver_unregister(&amifb_driver); -} - -module_exit(amifb_exit); +module_platform_driver_probe(amifb_driver, amifb_probe); MODULE_LICENSE("GPL"); MODULE_ALIAS("platform:amiga-video"); diff --git a/drivers/video/atmel_lcdfb.c b/drivers/video/atmel_lcdfb.c index 025428e04c33..98348ec0b3ce 100644 --- a/drivers/video/atmel_lcdfb.c +++ b/drivers/video/atmel_lcdfb.c @@ -1158,18 +1158,7 @@ static struct platform_driver atmel_lcdfb_driver = { }, }; -static int __init atmel_lcdfb_init(void) -{ - return platform_driver_probe(&atmel_lcdfb_driver, atmel_lcdfb_probe); -} - -static void __exit atmel_lcdfb_exit(void) -{ - platform_driver_unregister(&atmel_lcdfb_driver); -} - -module_init(atmel_lcdfb_init); -module_exit(atmel_lcdfb_exit); +module_platform_driver_probe(atmel_lcdfb_driver, atmel_lcdfb_probe); MODULE_DESCRIPTION("AT91/AT32 LCD Controller framebuffer driver"); MODULE_AUTHOR("Nicolas Ferre <nicolas.ferre@atmel.com>"); diff --git a/drivers/video/auo_k1900fb.c b/drivers/video/auo_k1900fb.c index 1a9ac6e1f4b3..f5b668e77af3 100644 --- a/drivers/video/auo_k1900fb.c +++ b/drivers/video/auo_k1900fb.c @@ -60,9 +60,12 @@ static void auok1900_init(struct auok190xfb_par *par) { + struct device *dev = par->info->device; struct auok190x_board *board = par->board; u16 init_param = 0; + pm_runtime_get_sync(dev); + init_param |= AUOK1900_INIT_TEMP_AVERAGE; init_param |= AUOK1900_INIT_ROTATE(par->rotation); init_param |= AUOK190X_INIT_INVERSE_WHITE; @@ -74,6 +77,9 @@ static void auok1900_init(struct auok190xfb_par *par) /* let the controller finish */ board->wait_for_rdy(par); + + pm_runtime_mark_last_busy(dev); + pm_runtime_put_autosuspend(dev); } static void auok1900_update_region(struct auok190xfb_par *par, int mode, @@ -82,6 +88,7 @@ static void auok1900_update_region(struct auok190xfb_par *par, int mode, struct device *dev = par->info->device; unsigned char *buf = (unsigned char *)par->info->screen_base; int xres = par->info->var.xres; + int line_length = par->info->fix.line_length; u16 args[4]; pm_runtime_get_sync(dev); @@ -100,9 +107,9 @@ static void auok1900_update_region(struct auok190xfb_par *par, int mode, args[1] = y1 + 1; args[2] = xres; args[3] = y2 - y1; - buf += y1 * xres; + buf += y1 * line_length; auok190x_send_cmdargs_pixels(par, AUOK1900_CMD_PARTIALDISP, 4, args, - ((y2 - y1) * xres)/2, (u16 *) buf); + ((y2 - y1) * line_length)/2, (u16 *) buf); auok190x_send_command(par, AUOK190X_CMD_DATA_STOP); par->update_cnt++; diff --git a/drivers/video/auo_k1901fb.c b/drivers/video/auo_k1901fb.c index d1db1653cd88..12b9adcb75c5 100644 --- a/drivers/video/auo_k1901fb.c +++ b/drivers/video/auo_k1901fb.c @@ -101,9 +101,12 @@ static void auok1901_init(struct auok190xfb_par *par) { + struct device *dev = par->info->device; struct auok190x_board *board = par->board; u16 init_param = 0; + pm_runtime_get_sync(dev); + init_param |= AUOK190X_INIT_INVERSE_WHITE; init_param |= AUOK190X_INIT_FORMAT0; init_param |= AUOK1901_INIT_RESOLUTION(par->resolution); @@ -113,6 +116,9 @@ static void auok1901_init(struct auok190xfb_par *par) /* let the controller finish */ board->wait_for_rdy(par); + + pm_runtime_mark_last_busy(dev); + pm_runtime_put_autosuspend(dev); } static void auok1901_update_region(struct auok190xfb_par *par, int mode, @@ -121,6 +127,7 @@ static void auok1901_update_region(struct auok190xfb_par *par, int mode, struct device *dev = par->info->device; unsigned char *buf = (unsigned char *)par->info->screen_base; int xres = par->info->var.xres; + int line_length = par->info->fix.line_length; u16 args[5]; pm_runtime_get_sync(dev); @@ -139,9 +146,9 @@ static void auok1901_update_region(struct auok190xfb_par *par, int mode, args[1] = y1 + 1; args[2] = xres; args[3] = y2 - y1; - buf += y1 * xres; + buf += y1 * line_length; auok190x_send_cmdargs_pixels_nowait(par, AUOK1901_CMD_DMA_START, 4, - args, ((y2 - y1) * xres)/2, + args, ((y2 - y1) * line_length)/2, (u16 *) buf); auok190x_send_command_nowait(par, AUOK190X_CMD_DATA_STOP); diff --git a/drivers/video/auo_k190x.c b/drivers/video/auo_k190x.c index 53846cb534d4..b1f19b266da7 100644 --- a/drivers/video/auo_k190x.c +++ b/drivers/video/auo_k190x.c @@ -40,6 +40,14 @@ static struct panel_info panel_table[] = { .w = 1024, .h = 768, }, + [AUOK190X_RESOLUTION_600_800] = { + .w = 600, + .h = 800, + }, + [AUOK190X_RESOLUTION_768_1024] = { + .w = 768, + .h = 1024, + }, }; /* @@ -60,8 +68,48 @@ static void auok190x_issue_cmd(struct auok190xfb_par *par, u16 data) par->board->set_ctl(par, AUOK190X_I80_DC, 1); } -static int auok190x_issue_pixels(struct auok190xfb_par *par, int size, - u16 *data) +/** + * Conversion of 16bit color to 4bit grayscale + * does roughly (0.3 * R + 0.6 G + 0.1 B) / 2 + */ +static inline int rgb565_to_gray4(u16 data, struct fb_var_screeninfo *var) +{ + return ((((data & 0xF800) >> var->red.offset) * 77 + + ((data & 0x07E0) >> (var->green.offset + 1)) * 151 + + ((data & 0x1F) >> var->blue.offset) * 28) >> 8 >> 1); +} + +static int auok190x_issue_pixels_rgb565(struct auok190xfb_par *par, int size, + u16 *data) +{ + struct fb_var_screeninfo *var = &par->info->var; + struct device *dev = par->info->device; + int i; + u16 tmp; + + if (size & 7) { + dev_err(dev, "issue_pixels: size %d must be a multiple of 8\n", + size); + return -EINVAL; + } + + for (i = 0; i < (size >> 2); i++) { + par->board->set_ctl(par, AUOK190X_I80_WR, 0); + + tmp = (rgb565_to_gray4(data[4*i], var) & 0x000F); + tmp |= (rgb565_to_gray4(data[4*i+1], var) << 4) & 0x00F0; + tmp |= (rgb565_to_gray4(data[4*i+2], var) << 8) & 0x0F00; + tmp |= (rgb565_to_gray4(data[4*i+3], var) << 12) & 0xF000; + + par->board->set_hdb(par, tmp); + par->board->set_ctl(par, AUOK190X_I80_WR, 1); + } + + return 0; +} + +static int auok190x_issue_pixels_gray8(struct auok190xfb_par *par, int size, + u16 *data) { struct device *dev = par->info->device; int i; @@ -91,6 +139,23 @@ static int auok190x_issue_pixels(struct auok190xfb_par *par, int size, return 0; } +static int auok190x_issue_pixels(struct auok190xfb_par *par, int size, + u16 *data) +{ + struct fb_info *info = par->info; + struct device *dev = par->info->device; + + if (info->var.bits_per_pixel == 8 && info->var.grayscale) + auok190x_issue_pixels_gray8(par, size, data); + else if (info->var.bits_per_pixel == 16) + auok190x_issue_pixels_rgb565(par, size, data); + else + dev_err(dev, "unsupported color mode (bits: %d, gray: %d)\n", + info->var.bits_per_pixel, info->var.grayscale); + + return 0; +} + static u16 auok190x_read_data(struct auok190xfb_par *par) { u16 data; @@ -224,8 +289,8 @@ static void auok190xfb_dpy_deferred_io(struct fb_info *info, { struct fb_deferred_io *fbdefio = info->fbdefio; struct auok190xfb_par *par = info->par; + u16 line_length = info->fix.line_length; u16 yres = info->var.yres; - u16 xres = info->var.xres; u16 y1 = 0, h = 0; int prev_index = -1; struct page *cur; @@ -254,7 +319,7 @@ static void auok190xfb_dpy_deferred_io(struct fb_info *info, } /* height increment is fixed per page */ - h_inc = DIV_ROUND_UP(PAGE_SIZE , xres); + h_inc = DIV_ROUND_UP(PAGE_SIZE , line_length); /* calculate number of pages from pixel height */ threshold = par->consecutive_threshold / h_inc; @@ -265,7 +330,7 @@ static void auok190xfb_dpy_deferred_io(struct fb_info *info, list_for_each_entry(cur, &fbdefio->pagelist, lru) { if (prev_index < 0) { /* just starting so assign first page */ - y1 = (cur->index << PAGE_SHIFT) / xres; + y1 = (cur->index << PAGE_SHIFT) / line_length; h = h_inc; } else if ((cur->index - prev_index) <= threshold) { /* page is within our threshold for single updates */ @@ -275,7 +340,7 @@ static void auok190xfb_dpy_deferred_io(struct fb_info *info, par->update_partial(par, y1, y1 + h); /* start over with our non consecutive page */ - y1 = (cur->index << PAGE_SHIFT) / xres; + y1 = (cur->index << PAGE_SHIFT) / line_length; h = h_inc; } prev_index = cur->index; @@ -376,27 +441,127 @@ static void auok190xfb_imageblit(struct fb_info *info, static int auok190xfb_check_var(struct fb_var_screeninfo *var, struct fb_info *info) { - if (info->var.xres != var->xres || info->var.yres != var->yres || - info->var.xres_virtual != var->xres_virtual || - info->var.yres_virtual != var->yres_virtual) { - pr_info("%s: Resolution not supported: X%u x Y%u\n", - __func__, var->xres, var->yres); + struct device *dev = info->device; + struct auok190xfb_par *par = info->par; + struct panel_info *panel = &panel_table[par->resolution]; + int size; + + /* + * Color depth + */ + + if (var->bits_per_pixel == 8 && var->grayscale == 1) { + /* + * For 8-bit grayscale, R, G, and B offset are equal. + */ + var->red.length = 8; + var->red.offset = 0; + var->red.msb_right = 0; + + var->green.length = 8; + var->green.offset = 0; + var->green.msb_right = 0; + + var->blue.length = 8; + var->blue.offset = 0; + var->blue.msb_right = 0; + + var->transp.length = 0; + var->transp.offset = 0; + var->transp.msb_right = 0; + } else if (var->bits_per_pixel == 16) { + var->red.length = 5; + var->red.offset = 11; + var->red.msb_right = 0; + + var->green.length = 6; + var->green.offset = 5; + var->green.msb_right = 0; + + var->blue.length = 5; + var->blue.offset = 0; + var->blue.msb_right = 0; + + var->transp.length = 0; + var->transp.offset = 0; + var->transp.msb_right = 0; + } else { + dev_warn(dev, "unsupported color mode (bits: %d, grayscale: %d)\n", + info->var.bits_per_pixel, info->var.grayscale); return -EINVAL; } /* + * Dimensions + */ + + switch (var->rotate) { + case FB_ROTATE_UR: + case FB_ROTATE_UD: + var->xres = panel->w; + var->yres = panel->h; + break; + case FB_ROTATE_CW: + case FB_ROTATE_CCW: + var->xres = panel->h; + var->yres = panel->w; + break; + default: + dev_dbg(dev, "Invalid rotation request\n"); + return -EINVAL; + } + + var->xres_virtual = var->xres; + var->yres_virtual = var->yres; + + /* * Memory limit */ - if ((info->fix.line_length * var->yres_virtual) > info->fix.smem_len) { - pr_info("%s: Memory Limit requested yres_virtual = %u\n", - __func__, var->yres_virtual); + size = var->xres_virtual * var->yres_virtual * var->bits_per_pixel / 8; + if (size > info->fix.smem_len) { + dev_err(dev, "Memory limit exceeded, requested %dK\n", + size >> 10); return -ENOMEM; } return 0; } +static int auok190xfb_set_fix(struct fb_info *info) +{ + struct fb_fix_screeninfo *fix = &info->fix; + struct fb_var_screeninfo *var = &info->var; + + fix->line_length = var->xres_virtual * var->bits_per_pixel / 8; + + fix->type = FB_TYPE_PACKED_PIXELS; + fix->accel = FB_ACCEL_NONE; + fix->visual = (var->grayscale) ? FB_VISUAL_STATIC_PSEUDOCOLOR + : FB_VISUAL_TRUECOLOR; + fix->xpanstep = 0; + fix->ypanstep = 0; + fix->ywrapstep = 0; + + return 0; +} + +static int auok190xfb_set_par(struct fb_info *info) +{ + struct auok190xfb_par *par = info->par; + + par->rotation = info->var.rotate; + auok190xfb_set_fix(info); + + /* reinit the controller to honor the rotation */ + par->init(par); + + /* wait for init to complete */ + par->board->wait_for_rdy(par); + + return 0; +} + static struct fb_ops auok190xfb_ops = { .owner = THIS_MODULE, .fb_read = fb_sys_read, @@ -405,6 +570,7 @@ static struct fb_ops auok190xfb_ops = { .fb_copyarea = auok190xfb_copyarea, .fb_imageblit = auok190xfb_imageblit, .fb_check_var = auok190xfb_check_var, + .fb_set_par = auok190xfb_set_par, }; /* @@ -588,10 +754,16 @@ static int auok190x_power(struct auok190xfb_par *par, bool on) static void auok190x_recover(struct auok190xfb_par *par) { + struct device *dev = par->info->device; + auok190x_power(par, 0); msleep(100); auok190x_power(par, 1); + /* after powercycling the device, it's always active */ + pm_runtime_set_active(dev); + par->standby = 0; + par->init(par); /* wait for init to complete */ @@ -875,42 +1047,17 @@ int auok190x_common_probe(struct platform_device *pdev, /* initialise fix, var, resolution and rotation */ strlcpy(info->fix.id, init->id, 16); - info->fix.type = FB_TYPE_PACKED_PIXELS; - info->fix.visual = FB_VISUAL_STATIC_PSEUDOCOLOR; - info->fix.xpanstep = 0; - info->fix.ypanstep = 0; - info->fix.ywrapstep = 0; - info->fix.accel = FB_ACCEL_NONE; - info->var.bits_per_pixel = 8; info->var.grayscale = 1; - info->var.red.length = 8; - info->var.green.length = 8; - info->var.blue.length = 8; panel = &panel_table[board->resolution]; - /* if 90 degree rotation, switch width and height */ - if (board->rotation & 1) { - info->var.xres = panel->h; - info->var.yres = panel->w; - info->var.xres_virtual = panel->h; - info->var.yres_virtual = panel->w; - info->fix.line_length = panel->h; - } else { - info->var.xres = panel->w; - info->var.yres = panel->h; - info->var.xres_virtual = panel->w; - info->var.yres_virtual = panel->h; - info->fix.line_length = panel->w; - } - par->resolution = board->resolution; - par->rotation = board->rotation; + par->rotation = 0; /* videomemory handling */ - videomemorysize = roundup((panel->w * panel->h), PAGE_SIZE); + videomemorysize = roundup((panel->w * panel->h) * 2, PAGE_SIZE); videomemory = vmalloc(videomemorysize); if (!videomemory) { ret = -ENOMEM; @@ -924,6 +1071,12 @@ int auok190x_common_probe(struct platform_device *pdev, info->flags = FBINFO_FLAG_DEFAULT | FBINFO_VIRTFB; info->fbops = &auok190xfb_ops; + ret = auok190xfb_check_var(&info->var, info); + if (ret) + goto err_defio; + + auok190xfb_set_fix(info); + /* deferred io init */ info->fbdefio = devm_kzalloc(info->device, diff --git a/drivers/video/controlfb.c b/drivers/video/controlfb.c index 0c189b32a4c5..67b77b40aa7f 100644 --- a/drivers/video/controlfb.c +++ b/drivers/video/controlfb.c @@ -285,36 +285,26 @@ static int controlfb_pan_display(struct fb_var_screeninfo *var, static int controlfb_mmap(struct fb_info *info, struct vm_area_struct *vma) { - unsigned long off, start; - u32 len; - - off = vma->vm_pgoff << PAGE_SHIFT; - - /* frame buffer memory */ - start = info->fix.smem_start; - len = PAGE_ALIGN((start & ~PAGE_MASK)+info->fix.smem_len); - if (off >= len) { - /* memory mapped io */ - off -= len; - if (info->var.accel_flags) - return -EINVAL; - start = info->fix.mmio_start; - len = PAGE_ALIGN((start & ~PAGE_MASK)+info->fix.mmio_len); - vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); - } else { - /* framebuffer */ - vma->vm_page_prot = pgprot_cached_wthru(vma->vm_page_prot); - } - start &= PAGE_MASK; - if ((vma->vm_end - vma->vm_start + off) > len) - return -EINVAL; - off += start; - vma->vm_pgoff = off >> PAGE_SHIFT; - if (io_remap_pfn_range(vma, vma->vm_start, off >> PAGE_SHIFT, - vma->vm_end - vma->vm_start, vma->vm_page_prot)) - return -EAGAIN; - - return 0; + unsigned long mmio_pgoff; + unsigned long start; + u32 len; + + start = info->fix.smem_start; + len = info->fix.smem_len; + mmio_pgoff = PAGE_ALIGN((start & ~PAGE_MASK) + len) >> PAGE_SHIFT; + if (vma->vm_pgoff >= mmio_pgoff) { + if (info->var.accel_flags) + return -EINVAL; + vma->vm_pgoff -= mmio_pgoff; + start = info->fix.mmio_start; + len = info->fix.mmio_len; + vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); + } else { + /* framebuffer */ + vma->vm_page_prot = pgprot_cached_wthru(vma->vm_page_prot); + } + + return vm_iomap_memory(vma, start, len); } static int controlfb_blank(int blank_mode, struct fb_info *info) diff --git a/drivers/video/exynos/exynos_mipi_dsi.c b/drivers/video/exynos/exynos_mipi_dsi.c index fac7df6d1aba..3dd43ca2b95f 100644 --- a/drivers/video/exynos/exynos_mipi_dsi.c +++ b/drivers/video/exynos/exynos_mipi_dsi.c @@ -35,8 +35,6 @@ #include <video/exynos_mipi_dsim.h> -#include <plat/fb.h> - #include "exynos_mipi_dsi_common.h" #include "exynos_mipi_dsi_lowlevel.h" diff --git a/drivers/video/exynos/exynos_mipi_dsi_common.c b/drivers/video/exynos/exynos_mipi_dsi_common.c index c70cb8926df6..520fc9bd887b 100644 --- a/drivers/video/exynos/exynos_mipi_dsi_common.c +++ b/drivers/video/exynos/exynos_mipi_dsi_common.c @@ -31,8 +31,6 @@ #include <video/mipi_display.h> #include <video/exynos_mipi_dsim.h> -#include <mach/map.h> - #include "exynos_mipi_dsi_regs.h" #include "exynos_mipi_dsi_lowlevel.h" #include "exynos_mipi_dsi_common.h" diff --git a/drivers/video/exynos/exynos_mipi_dsi_lowlevel.c b/drivers/video/exynos/exynos_mipi_dsi_lowlevel.c index 95cb99a1fe2d..15c5abd408dc 100644 --- a/drivers/video/exynos/exynos_mipi_dsi_lowlevel.c +++ b/drivers/video/exynos/exynos_mipi_dsi_lowlevel.c @@ -26,8 +26,6 @@ #include <video/exynos_mipi_dsim.h> -#include <mach/map.h> - #include "exynos_mipi_dsi_regs.h" void exynos_mipi_dsi_func_reset(struct mipi_dsim_device *dsim) diff --git a/drivers/video/fb-puv3.c b/drivers/video/fb-puv3.c index 7d106f1f4906..27fc956166fa 100644 --- a/drivers/video/fb-puv3.c +++ b/drivers/video/fb-puv3.c @@ -640,21 +640,9 @@ static int unifb_pan_display(struct fb_var_screeninfo *var, int unifb_mmap(struct fb_info *info, struct vm_area_struct *vma) { - unsigned long size = vma->vm_end - vma->vm_start; - unsigned long offset = vma->vm_pgoff << PAGE_SHIFT; - unsigned long pos = info->fix.smem_start + offset; - - if (offset + size > info->fix.smem_len) - return -EINVAL; - vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); - if (io_remap_pfn_range(vma, vma->vm_start, pos >> PAGE_SHIFT, size, - vma->vm_page_prot)) - return -EAGAIN; - - /* VM_IO | VM_DONTEXPAND | VM_DONTDUMP are set by remap_pfn_range() */ - return 0; + return vm_iomap_memory(vma, info->fix.smem_start, info->fix.smem_len); } static struct fb_ops unifb_ops = { diff --git a/drivers/video/fbmem.c b/drivers/video/fbmem.c index 7c254084b6a0..dcb669eb4532 100644 --- a/drivers/video/fbmem.c +++ b/drivers/video/fbmem.c @@ -1373,15 +1373,12 @@ fb_mmap(struct file *file, struct vm_area_struct * vma) { struct fb_info *info = file_fb_info(file); struct fb_ops *fb; - unsigned long off; + unsigned long mmio_pgoff; unsigned long start; u32 len; if (!info) return -ENODEV; - if (vma->vm_pgoff > (~0UL >> PAGE_SHIFT)) - return -EINVAL; - off = vma->vm_pgoff << PAGE_SHIFT; fb = info->fbops; if (!fb) return -ENODEV; @@ -1393,32 +1390,29 @@ fb_mmap(struct file *file, struct vm_area_struct * vma) return res; } - /* frame buffer memory */ + /* + * Ugh. This can be either the frame buffer mapping, or + * if pgoff points past it, the mmio mapping. + */ start = info->fix.smem_start; - len = PAGE_ALIGN((start & ~PAGE_MASK) + info->fix.smem_len); - if (off >= len) { - /* memory mapped io */ - off -= len; + len = info->fix.smem_len; + mmio_pgoff = PAGE_ALIGN((start & ~PAGE_MASK) + len) >> PAGE_SHIFT; + if (vma->vm_pgoff >= mmio_pgoff) { if (info->var.accel_flags) { mutex_unlock(&info->mm_lock); return -EINVAL; } + + vma->vm_pgoff -= mmio_pgoff; start = info->fix.mmio_start; - len = PAGE_ALIGN((start & ~PAGE_MASK) + info->fix.mmio_len); + len = info->fix.mmio_len; } mutex_unlock(&info->mm_lock); - start &= PAGE_MASK; - if ((vma->vm_end - vma->vm_start + off) > len) - return -EINVAL; - off += start; - vma->vm_pgoff = off >> PAGE_SHIFT; - /* VM_IO | VM_DONTEXPAND | VM_DONTDUMP are set by io_remap_pfn_range()*/ + vma->vm_page_prot = vm_get_page_prot(vma->vm_flags); - fb_pgprotect(file, vma, off); - if (io_remap_pfn_range(vma, vma->vm_start, off >> PAGE_SHIFT, - vma->vm_end - vma->vm_start, vma->vm_page_prot)) - return -EAGAIN; - return 0; + fb_pgprotect(file, vma, start); + + return vm_iomap_memory(vma, start, len); } static int diff --git a/drivers/video/fbmon.c b/drivers/video/fbmon.c index 7f6709991a5c..6103fa6fb54f 100644 --- a/drivers/video/fbmon.c +++ b/drivers/video/fbmon.c @@ -1376,7 +1376,7 @@ int fb_get_mode(int flags, u32 val, struct fb_var_screeninfo *var, struct fb_inf return err; } -#if IS_ENABLED(CONFIG_VIDEOMODE) +#ifdef CONFIG_VIDEOMODE_HELPERS int fb_videomode_from_videomode(const struct videomode *vm, struct fb_videomode *fbmode) { @@ -1398,13 +1398,13 @@ int fb_videomode_from_videomode(const struct videomode *vm, fbmode->sync = 0; fbmode->vmode = 0; - if (vm->dmt_flags & VESA_DMT_HSYNC_HIGH) + if (vm->flags & DISPLAY_FLAGS_HSYNC_HIGH) fbmode->sync |= FB_SYNC_HOR_HIGH_ACT; - if (vm->dmt_flags & VESA_DMT_VSYNC_HIGH) + if (vm->flags & DISPLAY_FLAGS_VSYNC_HIGH) fbmode->sync |= FB_SYNC_VERT_HIGH_ACT; - if (vm->data_flags & DISPLAY_FLAGS_INTERLACED) + if (vm->flags & DISPLAY_FLAGS_INTERLACED) fbmode->vmode |= FB_VMODE_INTERLACED; - if (vm->data_flags & DISPLAY_FLAGS_DOUBLESCAN) + if (vm->flags & DISPLAY_FLAGS_DOUBLESCAN) fbmode->vmode |= FB_VMODE_DOUBLE; fbmode->flag = 0; @@ -1424,9 +1424,8 @@ int fb_videomode_from_videomode(const struct videomode *vm, return 0; } EXPORT_SYMBOL_GPL(fb_videomode_from_videomode); -#endif -#if IS_ENABLED(CONFIG_OF_VIDEOMODE) +#ifdef CONFIG_OF static inline void dump_fb_videomode(const struct fb_videomode *m) { pr_debug("fb_videomode = %ux%u@%uHz (%ukHz) %u %u %u %u %u %u %u %u %u\n", @@ -1465,7 +1464,8 @@ int of_get_fb_videomode(struct device_node *np, struct fb_videomode *fb, return 0; } EXPORT_SYMBOL_GPL(of_get_fb_videomode); -#endif +#endif /* CONFIG_OF */ +#endif /* CONFIG_VIDEOMODE_HELPERS */ #else int fb_parse_edid(unsigned char *edid, struct fb_var_screeninfo *var) diff --git a/drivers/video/fsl-diu-fb.c b/drivers/video/fsl-diu-fb.c index 41fbd9453c5f..6c278056fc60 100644 --- a/drivers/video/fsl-diu-fb.c +++ b/drivers/video/fsl-diu-fb.c @@ -375,7 +375,10 @@ struct fsl_diu_data { struct diu_ad dummy_ad __aligned(8); struct diu_ad ad[NUM_AOIS] __aligned(8); u8 gamma[256 * 3] __aligned(32); - u8 cursor[MAX_CURS * MAX_CURS * 2] __aligned(32); + /* It's easier to parse the cursor data as little-endian */ + __le16 cursor[MAX_CURS * MAX_CURS] __aligned(32); + /* Blank cursor data -- used to hide the cursor */ + __le16 blank_cursor[MAX_CURS * MAX_CURS] __aligned(32); uint8_t edid_data[EDID_LENGTH]; bool has_edid; } __aligned(32); @@ -824,7 +827,6 @@ static void update_lcdc(struct fb_info *info) /* Program DIU registers */ out_be32(&hw->gamma, DMA_ADDR(data, gamma)); - out_be32(&hw->cursor, DMA_ADDR(data, cursor)); out_be32(&hw->bgnd, 0x007F7F7F); /* Set background to grey */ out_be32(&hw->disp_size, (var->yres << 16) | var->xres); @@ -968,6 +970,156 @@ static u32 fsl_diu_get_pixel_format(unsigned int bits_per_pixel) } /* + * Copies a cursor image from user space to the proper place in driver + * memory so that the hardware can display the cursor image. + * + * Cursor data is represented as a sequence of 'width' bits packed into bytes. + * That is, the first 8 bits are in the first byte, the second 8 bits in the + * second byte, and so on. Therefore, the each row of the cursor is (width + + * 7) / 8 bytes of 'data' + * + * The DIU only supports cursors up to 32x32 (MAX_CURS). We reject cursors + * larger than this, so we already know that 'width' <= 32. Therefore, we can + * simplify our code by using a 32-bit big-endian integer ("line") to read in + * a single line of pixels, and only look at the top 'width' bits of that + * integer. + * + * This could result in an unaligned 32-bit read. For example, if the cursor + * is 24x24, then the first three bytes of 'image' contain the pixel data for + * the top line of the cursor. We do a 32-bit read of 'image', but we look + * only at the top 24 bits. Then we increment 'image' by 3 bytes. The next + * read is unaligned. The only problem is that we might read past the end of + * 'image' by 1-3 bytes, but that should not cause any problems. + */ +static void fsl_diu_load_cursor_image(struct fb_info *info, + const void *image, uint16_t bg, uint16_t fg, + unsigned int width, unsigned int height) +{ + struct mfb_info *mfbi = info->par; + struct fsl_diu_data *data = mfbi->parent; + __le16 *cursor = data->cursor; + __le16 _fg = cpu_to_le16(fg); + __le16 _bg = cpu_to_le16(bg); + unsigned int h, w; + + for (h = 0; h < height; h++) { + uint32_t mask = 1 << 31; + uint32_t line = be32_to_cpup(image); + + for (w = 0; w < width; w++) { + cursor[w] = (line & mask) ? _fg : _bg; + mask >>= 1; + } + + cursor += MAX_CURS; + image += DIV_ROUND_UP(width, 8); + } +} + +/* + * Set a hardware cursor. The image data for the cursor is passed via the + * fb_cursor object. + */ +static int fsl_diu_cursor(struct fb_info *info, struct fb_cursor *cursor) +{ + struct mfb_info *mfbi = info->par; + struct fsl_diu_data *data = mfbi->parent; + struct diu __iomem *hw = data->diu_reg; + + if (cursor->image.width > MAX_CURS || cursor->image.height > MAX_CURS) + return -EINVAL; + + /* The cursor size has changed */ + if (cursor->set & FB_CUR_SETSIZE) { + /* + * The DIU cursor is a fixed size, so when we get this + * message, instead of resizing the cursor, we just clear + * all the image data, in expectation of new data. However, + * in tests this control does not appear to be normally + * called. + */ + memset(data->cursor, 0, sizeof(data->cursor)); + } + + /* The cursor position has changed (cursor->image.dx|dy) */ + if (cursor->set & FB_CUR_SETPOS) { + uint32_t xx, yy; + + yy = (cursor->image.dy - info->var.yoffset) & 0x7ff; + xx = (cursor->image.dx - info->var.xoffset) & 0x7ff; + + out_be32(&hw->curs_pos, yy << 16 | xx); + } + + /* + * FB_CUR_SETIMAGE - the cursor image has changed + * FB_CUR_SETCMAP - the cursor colors has changed + * FB_CUR_SETSHAPE - the cursor bitmask has changed + */ + if (cursor->set & (FB_CUR_SETSHAPE | FB_CUR_SETCMAP | FB_CUR_SETIMAGE)) { + unsigned int image_size = + DIV_ROUND_UP(cursor->image.width, 8) * cursor->image.height; + unsigned int image_words = + DIV_ROUND_UP(image_size, sizeof(uint32_t)); + unsigned int bg_idx = cursor->image.bg_color; + unsigned int fg_idx = cursor->image.fg_color; + uint8_t buffer[image_size]; + uint32_t *image, *source, *mask; + uint16_t fg, bg; + unsigned int i; + + if (info->state != FBINFO_STATE_RUNNING) + return 0; + + /* + * Determine the size of the cursor image data. Normally, + * it's 8x16. + */ + image_size = DIV_ROUND_UP(cursor->image.width, 8) * + cursor->image.height; + + bg = ((info->cmap.red[bg_idx] & 0xf8) << 7) | + ((info->cmap.green[bg_idx] & 0xf8) << 2) | + ((info->cmap.blue[bg_idx] & 0xf8) >> 3) | + 1 << 15; + + fg = ((info->cmap.red[fg_idx] & 0xf8) << 7) | + ((info->cmap.green[fg_idx] & 0xf8) << 2) | + ((info->cmap.blue[fg_idx] & 0xf8) >> 3) | + 1 << 15; + + /* Use 32-bit operations on the data to improve performance */ + image = (uint32_t *)buffer; + source = (uint32_t *)cursor->image.data; + mask = (uint32_t *)cursor->mask; + + if (cursor->rop == ROP_XOR) + for (i = 0; i < image_words; i++) + image[i] = source[i] ^ mask[i]; + else + for (i = 0; i < image_words; i++) + image[i] = source[i] & mask[i]; + + fsl_diu_load_cursor_image(info, image, bg, fg, + cursor->image.width, cursor->image.height); + }; + + /* + * Show or hide the cursor. The cursor data is always stored in the + * 'cursor' memory block, and the actual cursor position is always in + * the DIU's CURS_POS register. To hide the cursor, we redirect the + * CURSOR register to a blank cursor. The show the cursor, we + * redirect the CURSOR register to the real cursor data. + */ + if (cursor->enable) + out_be32(&hw->cursor, DMA_ADDR(data, cursor)); + else + out_be32(&hw->cursor, DMA_ADDR(data, blank_cursor)); + + return 0; +} + +/* * Using the fb_var_screeninfo in fb_info we set the resolution of this * particular framebuffer. This function alters the fb_fix_screeninfo stored * in fb_info. It does not alter var in fb_info since we are using that @@ -1312,6 +1464,7 @@ static struct fb_ops fsl_diu_ops = { .fb_ioctl = fsl_diu_ioctl, .fb_open = fsl_diu_open, .fb_release = fsl_diu_release, + .fb_cursor = fsl_diu_cursor, }; static int install_fb(struct fb_info *info) diff --git a/drivers/video/gbefb.c b/drivers/video/gbefb.c index bda5e3941510..ceab37020fff 100644 --- a/drivers/video/gbefb.c +++ b/drivers/video/gbefb.c @@ -1016,7 +1016,9 @@ static int gbefb_mmap(struct fb_info *info, /* check range */ if (vma->vm_pgoff > (~0UL >> PAGE_SHIFT)) return -EINVAL; - if (offset + size > gbe_mem_size) + if (size > gbe_mem_size) + return -EINVAL; + if (offset > gbe_mem_size - size) return -EINVAL; /* remap using the fastest write-through mode on architecture */ diff --git a/drivers/video/mmp/core.c b/drivers/video/mmp/core.c index 9ed83419038b..84de2632857a 100644 --- a/drivers/video/mmp/core.c +++ b/drivers/video/mmp/core.c @@ -252,7 +252,5 @@ void mmp_unregister_path(struct mmp_path *path) kfree(path); mutex_unlock(&disp_lock); - - dev_info(path->dev, "de-register %s\n", path->name); } EXPORT_SYMBOL_GPL(mmp_unregister_path); diff --git a/drivers/video/of_display_timing.c b/drivers/video/of_display_timing.c index 13ecd9897010..56009bc02b02 100644 --- a/drivers/video/of_display_timing.c +++ b/drivers/video/of_display_timing.c @@ -79,25 +79,24 @@ static struct display_timing *of_get_display_timing(struct device_node *np) ret |= parse_timing_property(np, "vsync-len", &dt->vsync_len); ret |= parse_timing_property(np, "clock-frequency", &dt->pixelclock); - dt->dmt_flags = 0; - dt->data_flags = 0; + dt->flags = 0; if (!of_property_read_u32(np, "vsync-active", &val)) - dt->dmt_flags |= val ? VESA_DMT_VSYNC_HIGH : - VESA_DMT_VSYNC_LOW; + dt->flags |= val ? DISPLAY_FLAGS_VSYNC_HIGH : + DISPLAY_FLAGS_VSYNC_LOW; if (!of_property_read_u32(np, "hsync-active", &val)) - dt->dmt_flags |= val ? VESA_DMT_HSYNC_HIGH : - VESA_DMT_HSYNC_LOW; + dt->flags |= val ? DISPLAY_FLAGS_HSYNC_HIGH : + DISPLAY_FLAGS_HSYNC_LOW; if (!of_property_read_u32(np, "de-active", &val)) - dt->data_flags |= val ? DISPLAY_FLAGS_DE_HIGH : + dt->flags |= val ? DISPLAY_FLAGS_DE_HIGH : DISPLAY_FLAGS_DE_LOW; if (!of_property_read_u32(np, "pixelclk-active", &val)) - dt->data_flags |= val ? DISPLAY_FLAGS_PIXDATA_POSEDGE : + dt->flags |= val ? DISPLAY_FLAGS_PIXDATA_POSEDGE : DISPLAY_FLAGS_PIXDATA_NEGEDGE; if (of_property_read_bool(np, "interlaced")) - dt->data_flags |= DISPLAY_FLAGS_INTERLACED; + dt->flags |= DISPLAY_FLAGS_INTERLACED; if (of_property_read_bool(np, "doublescan")) - dt->data_flags |= DISPLAY_FLAGS_DOUBLESCAN; + dt->flags |= DISPLAY_FLAGS_DOUBLESCAN; if (ret) { pr_err("%s: error reading timing properties\n", diff --git a/drivers/video/of_videomode.c b/drivers/video/of_videomode.c index 5b8066cd397f..111c2d1911d3 100644 --- a/drivers/video/of_videomode.c +++ b/drivers/video/of_videomode.c @@ -43,7 +43,7 @@ int of_get_videomode(struct device_node *np, struct videomode *vm, if (index == OF_USE_NATIVE_MODE) index = disp->native_mode; - ret = videomode_from_timing(disp, vm, index); + ret = videomode_from_timings(disp, vm, index); if (ret) return ret; diff --git a/drivers/video/omap/Kconfig b/drivers/video/omap/Kconfig index e512581300fc..0bc3a936ce2b 100644 --- a/drivers/video/omap/Kconfig +++ b/drivers/video/omap/Kconfig @@ -39,17 +39,6 @@ config FB_OMAP_LCD_MIPID the Mobile Industry Processor Interface DBI-C/DCS specification. (Supported LCDs: Philips LPH8923, Sharp LS041Y3) -config FB_OMAP_CONSISTENT_DMA_SIZE - int "Consistent DMA memory size (MB)" - depends on FB_OMAP - range 1 14 - default 2 - help - Increase the DMA consistent memory size according to your video - memory needs, for example if you want to use multiple planes. - The size must be 2MB aligned. - If unsure say 1. - config FB_OMAP_DMA_TUNE bool "Set DMA SDRAM access priority high" depends on FB_OMAP diff --git a/drivers/video/omap2/omapfb/omapfb-main.c b/drivers/video/omap2/omapfb/omapfb-main.c index ca585ef37f25..717f13a93351 100644 --- a/drivers/video/omap2/omapfb/omapfb-main.c +++ b/drivers/video/omap2/omapfb/omapfb-main.c @@ -1101,41 +1101,25 @@ static int omapfb_mmap(struct fb_info *fbi, struct vm_area_struct *vma) struct omapfb_info *ofbi = FB2OFB(fbi); struct fb_fix_screeninfo *fix = &fbi->fix; struct omapfb2_mem_region *rg; - unsigned long off; unsigned long start; u32 len; - int r = -EINVAL; - - if (vma->vm_end - vma->vm_start == 0) - return 0; - if (vma->vm_pgoff > (~0UL >> PAGE_SHIFT)) - return -EINVAL; - off = vma->vm_pgoff << PAGE_SHIFT; + int r; rg = omapfb_get_mem_region(ofbi->region); start = omapfb_get_region_paddr(ofbi); len = fix->smem_len; - if (off >= len) - goto error; - if ((vma->vm_end - vma->vm_start + off) > len) - goto error; - - off += start; - DBG("user mmap region start %lx, len %d, off %lx\n", start, len, off); + DBG("user mmap region start %lx, len %d, off %lx\n", start, len, + vma->vm_pgoff << PAGE_SHIFT); - vma->vm_pgoff = off >> PAGE_SHIFT; - /* VM_IO | VM_DONTEXPAND | VM_DONTDUMP are set by remap_pfn_range() */ vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot); vma->vm_ops = &mmap_user_ops; vma->vm_private_data = rg; - if (io_remap_pfn_range(vma, vma->vm_start, off >> PAGE_SHIFT, - vma->vm_end - vma->vm_start, - vma->vm_page_prot)) { - r = -EAGAIN; + + r = vm_iomap_memory(vma, start, len); + if (r) goto error; - } /* vm_ops.open won't be called for mmap itself. */ atomic_inc(&rg->map_count); @@ -1144,7 +1128,7 @@ static int omapfb_mmap(struct fb_info *fbi, struct vm_area_struct *vma) return 0; - error: +error: omapfb_put_mem_region(ofbi->region); return r; diff --git a/drivers/video/omap2/vrfb.c b/drivers/video/omap2/vrfb.c index 10560efeb35a..5261229c79af 100644 --- a/drivers/video/omap2/vrfb.c +++ b/drivers/video/omap2/vrfb.c @@ -397,18 +397,7 @@ static struct platform_driver vrfb_driver = { .remove = __exit_p(vrfb_remove), }; -static int __init vrfb_init(void) -{ - return platform_driver_probe(&vrfb_driver, &vrfb_probe); -} - -static void __exit vrfb_exit(void) -{ - platform_driver_unregister(&vrfb_driver); -} - -module_init(vrfb_init); -module_exit(vrfb_exit); +module_platform_driver_probe(vrfb_driver, vrfb_probe); MODULE_AUTHOR("Tomi Valkeinen <tomi.valkeinen@ti.com>"); MODULE_DESCRIPTION("OMAP VRFB"); diff --git a/drivers/video/ps3fb.c b/drivers/video/ps3fb.c index 920c27bf3947..d9f08c653d62 100644 --- a/drivers/video/ps3fb.c +++ b/drivers/video/ps3fb.c @@ -705,21 +705,15 @@ static int ps3fb_pan_display(struct fb_var_screeninfo *var, static int ps3fb_mmap(struct fb_info *info, struct vm_area_struct *vma) { - unsigned long size, offset; + int r; - size = vma->vm_end - vma->vm_start; - offset = vma->vm_pgoff << PAGE_SHIFT; - if (offset + size > info->fix.smem_len) - return -EINVAL; - - offset += info->fix.smem_start; - if (remap_pfn_range(vma, vma->vm_start, offset >> PAGE_SHIFT, - size, vma->vm_page_prot)) - return -EAGAIN; + r = vm_iomap_memory(vma, info->fix.smem_start, info->fix.smem_len); dev_dbg(info->device, "ps3fb: mmap framebuffer P(%lx)->V(%lx)\n", - offset, vma->vm_start); - return 0; + info->fix.smem_start + vma->vm_pgoff << PAGE_SHIFT, + vma->vm_start); + + return r; } /* diff --git a/drivers/video/s3c-fb.c b/drivers/video/s3c-fb.c index 968a62571df7..2e7991c7ca08 100644 --- a/drivers/video/s3c-fb.c +++ b/drivers/video/s3c-fb.c @@ -24,10 +24,9 @@ #include <linux/uaccess.h> #include <linux/interrupt.h> #include <linux/pm_runtime.h> +#include <linux/platform_data/video_s3c.h> #include <video/samsung_fimd.h> -#include <mach/map.h> -#include <plat/fb.h> /* This driver will export a number of framebuffer interfaces depending * on the configuration passed in via the platform data. Each fb instance diff --git a/drivers/video/sa1100fb.c b/drivers/video/sa1100fb.c index cfbde5e85cbf..f34c858642e8 100644 --- a/drivers/video/sa1100fb.c +++ b/drivers/video/sa1100fb.c @@ -556,7 +556,7 @@ static int sa1100fb_mmap(struct fb_info *info, struct vm_area_struct *vma) { struct sa1100fb_info *fbi = (struct sa1100fb_info *)info; - unsigned long start, len, off = vma->vm_pgoff << PAGE_SHIFT; + unsigned long off = vma->vm_pgoff << PAGE_SHIFT; if (off < info->fix.smem_len) { vma->vm_pgoff += 1; /* skip over the palette */ @@ -564,19 +564,9 @@ static int sa1100fb_mmap(struct fb_info *info, fbi->map_dma, fbi->map_size); } - start = info->fix.mmio_start; - len = PAGE_ALIGN((start & ~PAGE_MASK) + info->fix.mmio_len); - - if ((vma->vm_end - vma->vm_start + off) > len) - return -EINVAL; - - off += start & PAGE_MASK; - vma->vm_pgoff = off >> PAGE_SHIFT; - vma->vm_flags |= VM_IO; vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); - return io_remap_pfn_range(vma, vma->vm_start, off >> PAGE_SHIFT, - vma->vm_end - vma->vm_start, - vma->vm_page_prot); + + return vm_iomap_memory(vma, info->fix.mmio_start, info->fix.mmio_len); } static struct fb_ops sa1100fb_ops = { diff --git a/drivers/video/sgivwfb.c b/drivers/video/sgivwfb.c index 2331fadc272b..b2a8912f6435 100644 --- a/drivers/video/sgivwfb.c +++ b/drivers/video/sgivwfb.c @@ -705,23 +705,17 @@ static int sgivwfb_setcolreg(u_int regno, u_int red, u_int green, static int sgivwfb_mmap(struct fb_info *info, struct vm_area_struct *vma) { - unsigned long size = vma->vm_end - vma->vm_start; - unsigned long offset = vma->vm_pgoff << PAGE_SHIFT; + int r; - if (vma->vm_pgoff > (~0UL >> PAGE_SHIFT)) - return -EINVAL; - if (offset + size > sgivwfb_mem_size) - return -EINVAL; - offset += sgivwfb_mem_phys; pgprot_val(vma->vm_page_prot) = - pgprot_val(vma->vm_page_prot) | _PAGE_PCD; - vma->vm_flags |= VM_IO; - if (remap_pfn_range(vma, vma->vm_start, offset >> PAGE_SHIFT, - size, vma->vm_page_prot)) - return -EAGAIN; + pgprot_val(vma->vm_page_prot) | _PAGE_PCD; + + r = vm_iomap_memory(vma, sgivwfb_mem_phys, sgivwfb_mem_size); + printk(KERN_DEBUG "sgivwfb: mmap framebuffer P(%lx)->V(%lx)\n", offset, vma->vm_start); - return 0; + + return r; } int __init sgivwfb_setup(char *options) diff --git a/drivers/video/sh_mipi_dsi.c b/drivers/video/sh_mipi_dsi.c index 701b461cf8a9..6cad53075e99 100644 --- a/drivers/video/sh_mipi_dsi.c +++ b/drivers/video/sh_mipi_dsi.c @@ -581,17 +581,7 @@ static struct platform_driver sh_mipi_driver = { }, }; -static int __init sh_mipi_init(void) -{ - return platform_driver_probe(&sh_mipi_driver, sh_mipi_probe); -} -module_init(sh_mipi_init); - -static void __exit sh_mipi_exit(void) -{ - platform_driver_unregister(&sh_mipi_driver); -} -module_exit(sh_mipi_exit); +module_platform_driver_probe(sh_mipi_driver, sh_mipi_probe); MODULE_AUTHOR("Guennadi Liakhovetski <g.liakhovetski@gmx.de>"); MODULE_DESCRIPTION("SuperH / ARM-shmobile MIPI DSI driver"); diff --git a/drivers/video/sh_mobile_hdmi.c b/drivers/video/sh_mobile_hdmi.c index 930e550e752a..bfe4728480fd 100644 --- a/drivers/video/sh_mobile_hdmi.c +++ b/drivers/video/sh_mobile_hdmi.c @@ -1445,17 +1445,7 @@ static struct platform_driver sh_hdmi_driver = { }, }; -static int __init sh_hdmi_init(void) -{ - return platform_driver_probe(&sh_hdmi_driver, sh_hdmi_probe); -} -module_init(sh_hdmi_init); - -static void __exit sh_hdmi_exit(void) -{ - platform_driver_unregister(&sh_hdmi_driver); -} -module_exit(sh_hdmi_exit); +module_platform_driver_probe(sh_hdmi_driver, sh_hdmi_probe); MODULE_AUTHOR("Guennadi Liakhovetski <g.liakhovetski@gmx.de>"); MODULE_DESCRIPTION("SuperH / ARM-shmobile HDMI driver"); diff --git a/drivers/video/smscufx.c b/drivers/video/smscufx.c index 97bd6620c364..b2b33fc1ac3f 100644 --- a/drivers/video/smscufx.c +++ b/drivers/video/smscufx.c @@ -782,7 +782,11 @@ static int ufx_ops_mmap(struct fb_info *info, struct vm_area_struct *vma) unsigned long offset = vma->vm_pgoff << PAGE_SHIFT; unsigned long page, pos; - if (offset + size > info->fix.smem_len) + if (vma->vm_pgoff > (~0UL >> PAGE_SHIFT)) + return -EINVAL; + if (size > info->fix.smem_len) + return -EINVAL; + if (offset > info->fix.smem_len - size) return -EINVAL; pos = (unsigned long)info->fix.smem_start + offset; diff --git a/drivers/video/udlfb.c b/drivers/video/udlfb.c index 86d449ea3169..ec03e726c940 100644 --- a/drivers/video/udlfb.c +++ b/drivers/video/udlfb.c @@ -324,7 +324,11 @@ static int dlfb_ops_mmap(struct fb_info *info, struct vm_area_struct *vma) unsigned long offset = vma->vm_pgoff << PAGE_SHIFT; unsigned long page, pos; - if (offset + size > info->fix.smem_len) + if (vma->vm_pgoff > (~0UL >> PAGE_SHIFT)) + return -EINVAL; + if (size > info->fix.smem_len) + return -EINVAL; + if (offset > info->fix.smem_len - size) return -EINVAL; pos = (unsigned long)info->fix.smem_start + offset; diff --git a/drivers/video/vermilion/vermilion.c b/drivers/video/vermilion/vermilion.c index 0aa516fc59cd..09a136633f35 100644 --- a/drivers/video/vermilion/vermilion.c +++ b/drivers/video/vermilion/vermilion.c @@ -1003,24 +1003,18 @@ static int vmlfb_setcolreg(u_int regno, u_int red, u_int green, u_int blue, static int vmlfb_mmap(struct fb_info *info, struct vm_area_struct *vma) { struct vml_info *vinfo = container_of(info, struct vml_info, info); - unsigned long size = vma->vm_end - vma->vm_start; unsigned long offset = vma->vm_pgoff << PAGE_SHIFT; int ret; - if (vma->vm_pgoff > (~0UL >> PAGE_SHIFT)) - return -EINVAL; - if (offset + size > vinfo->vram_contig_size) - return -EINVAL; ret = vmlfb_vram_offset(vinfo, offset); if (ret) return -EINVAL; - offset += vinfo->vram_start; + pgprot_val(vma->vm_page_prot) |= _PAGE_PCD; pgprot_val(vma->vm_page_prot) &= ~_PAGE_PWT; - if (remap_pfn_range(vma, vma->vm_start, offset >> PAGE_SHIFT, - size, vma->vm_page_prot)) - return -EAGAIN; - return 0; + + return vm_iomap_memory(vma, vinfo->vram_start, + vinfo->vram_contig_size); } static int vmlfb_sync(struct fb_info *info) diff --git a/drivers/video/vfb.c b/drivers/video/vfb.c index 8bc1f9398945..ee5985efa15c 100644 --- a/drivers/video/vfb.c +++ b/drivers/video/vfb.c @@ -420,9 +420,12 @@ static int vfb_mmap(struct fb_info *info, unsigned long offset = vma->vm_pgoff << PAGE_SHIFT; unsigned long page, pos; - if (offset + size > info->fix.smem_len) { + if (vma->vm_pgoff > (~0UL >> PAGE_SHIFT)) + return -EINVAL; + if (size > info->fix.smem_len) + return -EINVAL; + if (offset > info->fix.smem_len - size) return -EINVAL; - } pos = (unsigned long)info->fix.smem_start + offset; diff --git a/drivers/video/videomode.c b/drivers/video/videomode.c index 21c47a202afa..df375c96c5d3 100644 --- a/drivers/video/videomode.c +++ b/drivers/video/videomode.c @@ -11,7 +11,25 @@ #include <video/display_timing.h> #include <video/videomode.h> -int videomode_from_timing(const struct display_timings *disp, +void videomode_from_timing(const struct display_timing *dt, + struct videomode *vm) +{ + vm->pixelclock = dt->pixelclock.typ; + vm->hactive = dt->hactive.typ; + vm->hfront_porch = dt->hfront_porch.typ; + vm->hback_porch = dt->hback_porch.typ; + vm->hsync_len = dt->hsync_len.typ; + + vm->vactive = dt->vactive.typ; + vm->vfront_porch = dt->vfront_porch.typ; + vm->vback_porch = dt->vback_porch.typ; + vm->vsync_len = dt->vsync_len.typ; + + vm->flags = dt->flags; +} +EXPORT_SYMBOL_GPL(videomode_from_timing); + +int videomode_from_timings(const struct display_timings *disp, struct videomode *vm, unsigned int index) { struct display_timing *dt; @@ -20,20 +38,8 @@ int videomode_from_timing(const struct display_timings *disp, if (!dt) return -EINVAL; - vm->pixelclock = display_timing_get_value(&dt->pixelclock, TE_TYP); - vm->hactive = display_timing_get_value(&dt->hactive, TE_TYP); - vm->hfront_porch = display_timing_get_value(&dt->hfront_porch, TE_TYP); - vm->hback_porch = display_timing_get_value(&dt->hback_porch, TE_TYP); - vm->hsync_len = display_timing_get_value(&dt->hsync_len, TE_TYP); - - vm->vactive = display_timing_get_value(&dt->vactive, TE_TYP); - vm->vfront_porch = display_timing_get_value(&dt->vfront_porch, TE_TYP); - vm->vback_porch = display_timing_get_value(&dt->vback_porch, TE_TYP); - vm->vsync_len = display_timing_get_value(&dt->vsync_len, TE_TYP); - - vm->dmt_flags = dt->dmt_flags; - vm->data_flags = dt->data_flags; + videomode_from_timing(dt, vm); return 0; } -EXPORT_SYMBOL_GPL(videomode_from_timing); +EXPORT_SYMBOL_GPL(videomode_from_timings); diff --git a/drivers/video/vt8500lcdfb.c b/drivers/video/vt8500lcdfb.c index aa2579c2364a..9547e1831e03 100644 --- a/drivers/video/vt8500lcdfb.c +++ b/drivers/video/vt8500lcdfb.c @@ -15,22 +15,21 @@ * GNU General Public License for more details. */ -#include <linux/module.h> -#include <linux/kernel.h> -#include <linux/errno.h> -#include <linux/string.h> -#include <linux/mm.h> -#include <linux/slab.h> #include <linux/delay.h> +#include <linux/dma-mapping.h> +#include <linux/errno.h> #include <linux/fb.h> #include <linux/init.h> #include <linux/interrupt.h> #include <linux/io.h> -#include <linux/dma-mapping.h> +#include <linux/kernel.h> +#include <linux/mm.h> +#include <linux/module.h> #include <linux/platform_device.h> +#include <linux/slab.h> +#include <linux/string.h> #include <linux/wait.h> - -#include <linux/platform_data/video-vt8500lcdfb.h> +#include <video/of_display_timing.h> #include "vt8500lcdfb.h" #include "wmt_ge_rops.h" @@ -277,11 +276,11 @@ static int vt8500lcd_probe(struct platform_device *pdev) { struct vt8500lcd_info *fbi; struct resource *res; + struct display_timings *disp_timing; void *addr; int irq, ret; struct fb_videomode of_mode; - struct device_node *np; u32 bpp; dma_addr_t fb_mem_phys; unsigned long fb_mem_len; @@ -346,32 +345,18 @@ static int vt8500lcd_probe(struct platform_device *pdev) goto failed_free_res; } - np = of_parse_phandle(pdev->dev.of_node, "default-mode", 0); - if (!np) { - pr_err("%s: No display description in Device Tree\n", __func__); - ret = -EINVAL; - goto failed_free_res; - } + disp_timing = of_get_display_timings(pdev->dev.of_node); + if (!disp_timing) + return -EINVAL; - /* - * This code is copied from Sascha Hauer's of_videomode helper - * and can be replaced with a call to the helper once mainlined - */ - ret = 0; - ret |= of_property_read_u32(np, "hactive", &of_mode.xres); - ret |= of_property_read_u32(np, "vactive", &of_mode.yres); - ret |= of_property_read_u32(np, "hback-porch", &of_mode.left_margin); - ret |= of_property_read_u32(np, "hfront-porch", &of_mode.right_margin); - ret |= of_property_read_u32(np, "hsync-len", &of_mode.hsync_len); - ret |= of_property_read_u32(np, "vback-porch", &of_mode.upper_margin); - ret |= of_property_read_u32(np, "vfront-porch", &of_mode.lower_margin); - ret |= of_property_read_u32(np, "vsync-len", &of_mode.vsync_len); - ret |= of_property_read_u32(np, "bpp", &bpp); - if (ret) { - pr_err("%s: Unable to read display properties\n", __func__); - goto failed_free_res; - } - of_mode.vmode = FB_VMODE_NONINTERLACED; + ret = of_get_fb_videomode(pdev->dev.of_node, &of_mode, + OF_USE_NATIVE_MODE); + if (ret) + return ret; + + ret = of_property_read_u32(pdev->dev.of_node, "bits-per-pixel", &bpp); + if (ret) + return ret; /* try allocating the framebuffer */ fb_mem_len = of_mode.xres * of_mode.yres * 2 * (bpp / 8); diff --git a/drivers/video/wm8505fb.c b/drivers/video/wm8505fb.c index 4dd0580f96fd..01f9ace068e2 100644 --- a/drivers/video/wm8505fb.c +++ b/drivers/video/wm8505fb.c @@ -14,25 +14,25 @@ * GNU General Public License for more details. */ -#include <linux/module.h> -#include <linux/kernel.h> -#include <linux/errno.h> -#include <linux/string.h> -#include <linux/mm.h> -#include <linux/slab.h> #include <linux/delay.h> +#include <linux/dma-mapping.h> #include <linux/fb.h> +#include <linux/errno.h> +#include <linux/err.h> #include <linux/init.h> #include <linux/interrupt.h> #include <linux/io.h> -#include <linux/dma-mapping.h> -#include <linux/platform_device.h> -#include <linux/wait.h> +#include <linux/kernel.h> +#include <linux/memblock.h> +#include <linux/mm.h> +#include <linux/module.h> #include <linux/of.h> #include <linux/of_fdt.h> -#include <linux/memblock.h> - -#include <linux/platform_data/video-vt8500lcdfb.h> +#include <linux/platform_device.h> +#include <linux/slab.h> +#include <linux/string.h> +#include <linux/wait.h> +#include <video/of_display_timing.h> #include "wm8505fb_regs.h" #include "wmt_ge_rops.h" @@ -263,26 +263,22 @@ static struct fb_ops wm8505fb_ops = { static int wm8505fb_probe(struct platform_device *pdev) { struct wm8505fb_info *fbi; - struct resource *res; + struct resource *res; + struct display_timings *disp_timing; void *addr; int ret; - struct fb_videomode of_mode; - struct device_node *np; + struct fb_videomode mode; u32 bpp; dma_addr_t fb_mem_phys; unsigned long fb_mem_len; void *fb_mem_virt; - ret = -ENOMEM; - fbi = NULL; - fbi = devm_kzalloc(&pdev->dev, sizeof(struct wm8505fb_info) + sizeof(u32) * 16, GFP_KERNEL); if (!fbi) { dev_err(&pdev->dev, "Failed to initialize framebuffer device\n"); - ret = -ENOMEM; - goto failed; + return -ENOMEM; } strcpy(fbi->fb.fix.id, DRIVER_NAME); @@ -308,54 +304,23 @@ static int wm8505fb_probe(struct platform_device *pdev) fbi->fb.pseudo_palette = addr; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (res == NULL) { - dev_err(&pdev->dev, "no I/O memory resource defined\n"); - ret = -ENODEV; - goto failed_fbi; - } + fbi->regbase = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(fbi->regbase)) + return PTR_ERR(fbi->regbase); - res = request_mem_region(res->start, resource_size(res), DRIVER_NAME); - if (res == NULL) { - dev_err(&pdev->dev, "failed to request I/O memory\n"); - ret = -EBUSY; - goto failed_fbi; - } - - fbi->regbase = ioremap(res->start, resource_size(res)); - if (fbi->regbase == NULL) { - dev_err(&pdev->dev, "failed to map I/O memory\n"); - ret = -EBUSY; - goto failed_free_res; - } + disp_timing = of_get_display_timings(pdev->dev.of_node); + if (!disp_timing) + return -EINVAL; - np = of_parse_phandle(pdev->dev.of_node, "default-mode", 0); - if (!np) { - pr_err("%s: No display description in Device Tree\n", __func__); - ret = -EINVAL; - goto failed_free_res; - } + ret = of_get_fb_videomode(pdev->dev.of_node, &mode, OF_USE_NATIVE_MODE); + if (ret) + return ret; - /* - * This code is copied from Sascha Hauer's of_videomode helper - * and can be replaced with a call to the helper once mainlined - */ - ret = 0; - ret |= of_property_read_u32(np, "hactive", &of_mode.xres); - ret |= of_property_read_u32(np, "vactive", &of_mode.yres); - ret |= of_property_read_u32(np, "hback-porch", &of_mode.left_margin); - ret |= of_property_read_u32(np, "hfront-porch", &of_mode.right_margin); - ret |= of_property_read_u32(np, "hsync-len", &of_mode.hsync_len); - ret |= of_property_read_u32(np, "vback-porch", &of_mode.upper_margin); - ret |= of_property_read_u32(np, "vfront-porch", &of_mode.lower_margin); - ret |= of_property_read_u32(np, "vsync-len", &of_mode.vsync_len); - ret |= of_property_read_u32(np, "bpp", &bpp); - if (ret) { - pr_err("%s: Unable to read display properties\n", __func__); - goto failed_free_res; - } + ret = of_property_read_u32(pdev->dev.of_node, "bits-per-pixel", &bpp); + if (ret) + return ret; - of_mode.vmode = FB_VMODE_NONINTERLACED; - fb_videomode_to_var(&fbi->fb.var, &of_mode); + fb_videomode_to_var(&fbi->fb.var, &mode); fbi->fb.var.nonstd = 0; fbi->fb.var.activate = FB_ACTIVATE_NOW; @@ -364,16 +329,16 @@ static int wm8505fb_probe(struct platform_device *pdev) fbi->fb.var.width = -1; /* try allocating the framebuffer */ - fb_mem_len = of_mode.xres * of_mode.yres * 2 * (bpp / 8); - fb_mem_virt = dma_alloc_coherent(&pdev->dev, fb_mem_len, &fb_mem_phys, + fb_mem_len = mode.xres * mode.yres * 2 * (bpp / 8); + fb_mem_virt = dmam_alloc_coherent(&pdev->dev, fb_mem_len, &fb_mem_phys, GFP_KERNEL); if (!fb_mem_virt) { pr_err("%s: Failed to allocate framebuffer\n", __func__); return -ENOMEM; - }; + } - fbi->fb.var.xres_virtual = of_mode.xres; - fbi->fb.var.yres_virtual = of_mode.yres * 2; + fbi->fb.var.xres_virtual = mode.xres; + fbi->fb.var.yres_virtual = mode.yres * 2; fbi->fb.var.bits_per_pixel = bpp; fbi->fb.fix.smem_start = fb_mem_phys; @@ -381,28 +346,29 @@ static int wm8505fb_probe(struct platform_device *pdev) fbi->fb.screen_base = fb_mem_virt; fbi->fb.screen_size = fb_mem_len; + fbi->contrast = 0x10; + ret = wm8505fb_set_par(&fbi->fb); + if (ret) { + dev_err(&pdev->dev, "Failed to set parameters\n"); + return ret; + } + if (fb_alloc_cmap(&fbi->fb.cmap, 256, 0) < 0) { dev_err(&pdev->dev, "Failed to allocate color map\n"); - ret = -ENOMEM; - goto failed_free_io; + return -ENOMEM; } wm8505fb_init_hw(&fbi->fb); - fbi->contrast = 0x80; - ret = wm8505fb_set_par(&fbi->fb); - if (ret) { - dev_err(&pdev->dev, "Failed to set parameters\n"); - goto failed_free_cmap; - } - platform_set_drvdata(pdev, fbi); ret = register_framebuffer(&fbi->fb); if (ret < 0) { dev_err(&pdev->dev, "Failed to register framebuffer device: %d\n", ret); - goto failed_free_cmap; + if (fbi->fb.cmap.len) + fb_dealloc_cmap(&fbi->fb.cmap); + return ret; } ret = device_create_file(&pdev->dev, &dev_attr_contrast); @@ -416,25 +382,11 @@ static int wm8505fb_probe(struct platform_device *pdev) fbi->fb.fix.smem_start + fbi->fb.fix.smem_len - 1); return 0; - -failed_free_cmap: - if (fbi->fb.cmap.len) - fb_dealloc_cmap(&fbi->fb.cmap); -failed_free_io: - iounmap(fbi->regbase); -failed_free_res: - release_mem_region(res->start, resource_size(res)); -failed_fbi: - platform_set_drvdata(pdev, NULL); - kfree(fbi); -failed: - return ret; } static int wm8505fb_remove(struct platform_device *pdev) { struct wm8505fb_info *fbi = platform_get_drvdata(pdev); - struct resource *res; device_remove_file(&pdev->dev, &dev_attr_contrast); @@ -445,13 +397,6 @@ static int wm8505fb_remove(struct platform_device *pdev) if (fbi->fb.cmap.len) fb_dealloc_cmap(&fbi->fb.cmap); - iounmap(fbi->regbase); - - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - release_mem_region(res->start, resource_size(res)); - - kfree(fbi); - return 0; } diff --git a/drivers/video/wmt_ge_rops.h b/drivers/video/wmt_ge_rops.h index 87380751a443..f73ec6377a46 100644 --- a/drivers/video/wmt_ge_rops.h +++ b/drivers/video/wmt_ge_rops.h @@ -1,5 +1,28 @@ +#ifdef CONFIG_FB_WMT_GE_ROPS + extern void wmt_ge_fillrect(struct fb_info *info, const struct fb_fillrect *rect); extern void wmt_ge_copyarea(struct fb_info *info, const struct fb_copyarea *area); extern int wmt_ge_sync(struct fb_info *info); + +#else + +static inline int wmt_ge_sync(struct fb_info *p) +{ + return 0; +} + +static inline void wmt_ge_fillrect(struct fb_info *p, + const struct fb_fillrect *rect) +{ + sys_fillrect(p, rect); +} + +static inline void wmt_ge_copyarea(struct fb_info *p, + const struct fb_copyarea *area) +{ + sys_copyarea(p, area); +} + +#endif diff --git a/drivers/w1/masters/mxc_w1.c b/drivers/w1/masters/mxc_w1.c index 950d354d50e2..47e12cfc2a57 100644 --- a/drivers/w1/masters/mxc_w1.c +++ b/drivers/w1/masters/mxc_w1.c @@ -121,9 +121,9 @@ static int mxc_w1_probe(struct platform_device *pdev) mdev->clkdiv = (clk_get_rate(mdev->clk) / 1000000) - 1; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - mdev->regs = devm_request_and_ioremap(&pdev->dev, res); - if (!mdev->regs) - return -EBUSY; + mdev->regs = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(mdev->regs)) + return PTR_ERR(mdev->regs); clk_prepare_enable(mdev->clk); __raw_writeb(mdev->clkdiv, mdev->regs + MXC_W1_TIME_DIVIDER); diff --git a/drivers/w1/slaves/Kconfig b/drivers/w1/slaves/Kconfig index 762561fbabbf..5e6a3c9e510b 100644 --- a/drivers/w1/slaves/Kconfig +++ b/drivers/w1/slaves/Kconfig @@ -22,6 +22,16 @@ config W1_SLAVE_DS2408 Say Y here if you want to use a 1-wire DS2408 8-Channel Addressable Switch device support +config W1_SLAVE_DS2408_READBACK + bool "Read-back values written to DS2408's output register" + depends on W1_SLAVE_DS2408 + default y + help + Enabling this will cause the driver to read back the values written + to the chip's output register in order to detect errors. + + This is slower but useful when debugging chips and/or busses. + config W1_SLAVE_DS2413 tristate "Dual Channel Addressable Switch 0x3a family support (DS2413)" help diff --git a/drivers/w1/slaves/w1_ds2408.c b/drivers/w1/slaves/w1_ds2408.c index 441ad3a3b586..e45eca1044bd 100644 --- a/drivers/w1/slaves/w1_ds2408.c +++ b/drivers/w1/slaves/w1_ds2408.c @@ -178,6 +178,15 @@ static ssize_t w1_f29_write_output( w1_write_block(sl->master, w1_buf, 3); readBack = w1_read_8(sl->master); + + if (readBack != W1_F29_SUCCESS_CONFIRM_BYTE) { + if (w1_reset_resume_command(sl->master)) + goto error; + /* try again, the slave is ready for a command */ + continue; + } + +#ifdef CONFIG_W1_SLAVE_DS2408_READBACK /* here the master could read another byte which would be the PIO reg (the actual pin logic state) since in this driver we don't know which pins are @@ -186,11 +195,6 @@ static ssize_t w1_f29_write_output( if (w1_reset_resume_command(sl->master)) goto error; - if (readBack != 0xAA) { - /* try again, the slave is ready for a command */ - continue; - } - /* go read back the output latches */ /* (the direct effect of the write above) */ w1_buf[0] = W1_F29_FUNC_READ_PIO_REGS; @@ -198,7 +202,9 @@ static ssize_t w1_f29_write_output( w1_buf[2] = 0; w1_write_block(sl->master, w1_buf, 3); /* read the result of the READ_PIO_REGS command */ - if (w1_read_8(sl->master) == *buf) { + if (w1_read_8(sl->master) == *buf) +#endif + { /* success! */ mutex_unlock(&sl->master->bus_mutex); dev_dbg(&sl->dev, @@ -297,8 +303,7 @@ error: -#define NB_SYSFS_BIN_FILES 6 -static struct bin_attribute w1_f29_sysfs_bin_files[NB_SYSFS_BIN_FILES] = { +static struct bin_attribute w1_f29_sysfs_bin_files[] = { { .attr = { .name = "state", @@ -357,7 +362,7 @@ static int w1_f29_add_slave(struct w1_slave *sl) int err = 0; int i; - for (i = 0; i < NB_SYSFS_BIN_FILES && !err; ++i) + for (i = 0; i < ARRAY_SIZE(w1_f29_sysfs_bin_files) && !err; ++i) err = sysfs_create_bin_file( &sl->dev.kobj, &(w1_f29_sysfs_bin_files[i])); @@ -371,7 +376,7 @@ static int w1_f29_add_slave(struct w1_slave *sl) static void w1_f29_remove_slave(struct w1_slave *sl) { int i; - for (i = NB_SYSFS_BIN_FILES - 1; i >= 0; --i) + for (i = ARRAY_SIZE(w1_f29_sysfs_bin_files) - 1; i >= 0; --i) sysfs_remove_bin_file(&sl->dev.kobj, &(w1_f29_sysfs_bin_files[i])); } diff --git a/drivers/xen/events.c b/drivers/xen/events.c index 2647ad8e1f19..d8cc8127f19c 100644 --- a/drivers/xen/events.c +++ b/drivers/xen/events.c @@ -85,8 +85,7 @@ enum xen_irq_type { * event channel - irq->event channel mapping * cpu - cpu this event channel is bound to * index - type-specific information: - * PIRQ - vector, with MSB being "needs EIO", or physical IRQ of the HVM - * guest, or GSI (real passthrough IRQ) of the device. + * PIRQ - physical IRQ, GSI, flags, and owner domain * VIRQ - virq number * IPI - IPI vector * EVTCHN - @@ -105,7 +104,6 @@ struct irq_info { struct { unsigned short pirq; unsigned short gsi; - unsigned char vector; unsigned char flags; uint16_t domid; } pirq; @@ -211,7 +209,6 @@ static void xen_irq_info_pirq_init(unsigned irq, unsigned short evtchn, unsigned short pirq, unsigned short gsi, - unsigned short vector, uint16_t domid, unsigned char flags) { @@ -221,7 +218,6 @@ static void xen_irq_info_pirq_init(unsigned irq, info->u.pirq.pirq = pirq; info->u.pirq.gsi = gsi; - info->u.pirq.vector = vector; info->u.pirq.domid = domid; info->u.pirq.flags = flags; } @@ -519,6 +515,9 @@ static void xen_free_irq(unsigned irq) { struct irq_info *info = irq_get_handler_data(irq); + if (WARN_ON(!info)) + return; + list_del(&info->list); irq_set_handler_data(irq, NULL); @@ -714,7 +713,7 @@ int xen_bind_pirq_gsi_to_irq(unsigned gsi, goto out; } - xen_irq_info_pirq_init(irq, 0, pirq, gsi, irq_op.vector, DOMID_SELF, + xen_irq_info_pirq_init(irq, 0, pirq, gsi, DOMID_SELF, shareable ? PIRQ_SHAREABLE : 0); pirq_query_unmask(irq); @@ -762,8 +761,7 @@ int xen_allocate_pirq_msi(struct pci_dev *dev, struct msi_desc *msidesc) } int xen_bind_pirq_msi_to_irq(struct pci_dev *dev, struct msi_desc *msidesc, - int pirq, int vector, const char *name, - domid_t domid) + int pirq, const char *name, domid_t domid) { int irq, ret; @@ -776,7 +774,7 @@ int xen_bind_pirq_msi_to_irq(struct pci_dev *dev, struct msi_desc *msidesc, irq_set_chip_and_handler_name(irq, &xen_pirq_chip, handle_edge_irq, name); - xen_irq_info_pirq_init(irq, 0, pirq, 0, vector, domid, 0); + xen_irq_info_pirq_init(irq, 0, pirq, 0, domid, 0); ret = irq_set_msi_desc(irq, msidesc); if (ret < 0) goto error_irq; @@ -1008,6 +1006,9 @@ static void unbind_from_irq(unsigned int irq) int evtchn = evtchn_from_irq(irq); struct irq_info *info = irq_get_handler_data(irq); + if (WARN_ON(!info)) + return; + mutex_lock(&irq_mapping_update_lock); if (info->refcnt > 0) { @@ -1135,6 +1136,10 @@ int bind_ipi_to_irqhandler(enum ipi_vector ipi, void unbind_from_irqhandler(unsigned int irq, void *dev_id) { + struct irq_info *info = irq_get_handler_data(irq); + + if (WARN_ON(!info)) + return; free_irq(irq, dev_id); unbind_from_irq(irq); } @@ -1457,6 +1462,9 @@ void rebind_evtchn_irq(int evtchn, int irq) { struct irq_info *info = info_for_irq(irq); + if (WARN_ON(!info)) + return; + /* Make sure the irq is masked, since the new event channel will also be masked. */ disable_irq(irq); @@ -1730,7 +1738,12 @@ void xen_poll_irq(int irq) int xen_test_irq_shared(int irq) { struct irq_info *info = info_for_irq(irq); - struct physdev_irq_status_query irq_status = { .irq = info->u.pirq.pirq }; + struct physdev_irq_status_query irq_status; + + if (WARN_ON(!info)) + return -ENOENT; + + irq_status.irq = info->u.pirq.pirq; if (HYPERVISOR_physdev_op(PHYSDEVOP_irq_status_query, &irq_status)) return 0; diff --git a/drivers/xen/xen-acpi-processor.c b/drivers/xen/xen-acpi-processor.c index 90e34ac7e522..8abd7d579037 100644 --- a/drivers/xen/xen-acpi-processor.c +++ b/drivers/xen/xen-acpi-processor.c @@ -25,6 +25,7 @@ #include <linux/init.h> #include <linux/module.h> #include <linux/types.h> +#include <linux/syscore_ops.h> #include <acpi/acpi_bus.h> #include <acpi/acpi_drivers.h> #include <acpi/processor.h> @@ -51,9 +52,9 @@ static DEFINE_MUTEX(acpi_ids_mutex); /* Which ACPI ID we have processed from 'struct acpi_processor'. */ static unsigned long *acpi_ids_done; /* Which ACPI ID exist in the SSDT/DSDT processor definitions. */ -static unsigned long __initdata *acpi_id_present; +static unsigned long *acpi_id_present; /* And if there is an _CST definition (or a PBLK) for the ACPI IDs */ -static unsigned long __initdata *acpi_id_cst_present; +static unsigned long *acpi_id_cst_present; static int push_cxx_to_hypervisor(struct acpi_processor *_pr) { @@ -329,7 +330,7 @@ static unsigned int __init get_max_acpi_id(void) * for_each_[present|online]_cpu macros which are banded to the virtual * CPU amount. */ -static acpi_status __init +static acpi_status read_acpi_id(acpi_handle handle, u32 lvl, void *context, void **rv) { u32 acpi_id; @@ -384,12 +385,16 @@ read_acpi_id(acpi_handle handle, u32 lvl, void *context, void **rv) return AE_OK; } -static int __init check_acpi_ids(struct acpi_processor *pr_backup) +static int check_acpi_ids(struct acpi_processor *pr_backup) { if (!pr_backup) return -ENODEV; + if (acpi_id_present && acpi_id_cst_present) + /* OK, done this once .. skip to uploading */ + goto upload; + /* All online CPUs have been processed at this stage. Now verify * whether in fact "online CPUs" == physical CPUs. */ @@ -408,6 +413,7 @@ static int __init check_acpi_ids(struct acpi_processor *pr_backup) read_acpi_id, NULL, NULL, NULL); acpi_get_devices("ACPI0007", read_acpi_id, NULL, NULL); +upload: if (!bitmap_equal(acpi_id_present, acpi_ids_done, nr_acpi_bits)) { unsigned int i; for_each_set_bit(i, acpi_id_present, nr_acpi_bits) { @@ -417,10 +423,7 @@ static int __init check_acpi_ids(struct acpi_processor *pr_backup) (void)upload_pm_data(pr_backup); } } - kfree(acpi_id_present); - acpi_id_present = NULL; - kfree(acpi_id_cst_present); - acpi_id_cst_present = NULL; + return 0; } static int __init check_prereq(void) @@ -467,10 +470,47 @@ static void free_acpi_perf_data(void) free_percpu(acpi_perf_data); } -static int __init xen_acpi_processor_init(void) +static int xen_upload_processor_pm_data(void) { struct acpi_processor *pr_backup = NULL; unsigned int i; + int rc = 0; + + pr_info(DRV_NAME "Uploading Xen processor PM info\n"); + + for_each_possible_cpu(i) { + struct acpi_processor *_pr; + _pr = per_cpu(processors, i /* APIC ID */); + if (!_pr) + continue; + + if (!pr_backup) { + pr_backup = kzalloc(sizeof(struct acpi_processor), GFP_KERNEL); + if (pr_backup) + memcpy(pr_backup, _pr, sizeof(struct acpi_processor)); + } + (void)upload_pm_data(_pr); + } + + rc = check_acpi_ids(pr_backup); + kfree(pr_backup); + + return rc; +} + +static void xen_acpi_processor_resume(void) +{ + bitmap_zero(acpi_ids_done, nr_acpi_bits); + xen_upload_processor_pm_data(); +} + +static struct syscore_ops xap_syscore_ops = { + .resume = xen_acpi_processor_resume, +}; + +static int __init xen_acpi_processor_init(void) +{ + unsigned int i; int rc = check_prereq(); if (rc) @@ -514,27 +554,12 @@ static int __init xen_acpi_processor_init(void) goto err_out; } - for_each_possible_cpu(i) { - struct acpi_processor *_pr; - _pr = per_cpu(processors, i /* APIC ID */); - if (!_pr) - continue; - - if (!pr_backup) { - pr_backup = kzalloc(sizeof(struct acpi_processor), GFP_KERNEL); - if (pr_backup) - memcpy(pr_backup, _pr, sizeof(struct acpi_processor)); - } - (void)upload_pm_data(_pr); - } - rc = check_acpi_ids(pr_backup); - - kfree(pr_backup); - pr_backup = NULL; - + rc = xen_upload_processor_pm_data(); if (rc) goto err_unregister; + register_syscore_ops(&xap_syscore_ops); + return 0; err_unregister: for_each_possible_cpu(i) { @@ -552,7 +577,10 @@ static void __exit xen_acpi_processor_exit(void) { int i; + unregister_syscore_ops(&xap_syscore_ops); kfree(acpi_ids_done); + kfree(acpi_id_present); + kfree(acpi_id_cst_present); for_each_possible_cpu(i) { struct acpi_processor_performance *perf; perf = per_cpu_ptr(acpi_perf_data, i); |