diff options
Diffstat (limited to 'drivers')
1604 files changed, 107658 insertions, 100788 deletions
diff --git a/drivers/Kconfig b/drivers/Kconfig index 202fa6d051b..78a956e286e 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 dce39a95fa7..33360de6365 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -79,7 +79,7 @@ obj-$(CONFIG_ATA_OVER_ETH) += block/aoe/ obj-$(CONFIG_PARIDE) += block/paride/ obj-$(CONFIG_TC) += tc/ obj-$(CONFIG_UWB) += uwb/ -obj-$(CONFIG_USB_OTG_UTILS) += usb/ +obj-$(CONFIG_USB_PHY) += usb/ obj-$(CONFIG_USB) += usb/ obj-$(CONFIG_PCI) += usb/ obj-$(CONFIG_USB_GADGET) += usb/ @@ -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 6ae5e440436..ac8688b8970 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 cd1434eb1de..033d1179bdb 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 5e7e991717d..f54d1985e59 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 6a67b07de49..251e57d3894 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 958238dda8f..40254f4df58 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/base.h b/drivers/base/base.h index 6ee17bb391a..b8bdfe61daa 100644 --- a/drivers/base/base.h +++ b/drivers/base/base.h @@ -101,6 +101,8 @@ static inline int hypervisor_init(void) { return 0; } extern int platform_bus_init(void); extern void cpu_dev_init(void); +struct kobject *virtual_device_parent(struct device *dev); + extern int bus_add_device(struct device *dev); extern void bus_probe_device(struct device *dev); extern void bus_remove_device(struct device *dev); diff --git a/drivers/base/bus.c b/drivers/base/bus.c index 519865b53f7..1a68f947ded 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 @@ -1205,26 +1205,10 @@ static void system_root_device_release(struct device *dev) { kfree(dev); } -/** - * subsys_system_register - register a subsystem at /sys/devices/system/ - * @subsys: system subsystem - * @groups: default attributes for the root device - * - * All 'system' subsystems have a /sys/devices/system/<name> root device - * with the name of the subsystem. The root device can carry subsystem- - * wide attributes. All registered devices are below this single root - * device and are named after the subsystem with a simple enumeration - * number appended. The registered devices are not explicitely named; - * only 'id' in the device needs to be set. - * - * Do not use this interface for anything new, it exists for compatibility - * with bad ideas only. New subsystems should use plain subsystems; and - * add the subsystem-wide attributes should be added to the subsystem - * directory itself and not some create fake root-device placed in - * /sys/devices/system/<name>. - */ -int subsys_system_register(struct bus_type *subsys, - const struct attribute_group **groups) + +static int subsys_register(struct bus_type *subsys, + const struct attribute_group **groups, + struct kobject *parent_of_root) { struct device *dev; int err; @@ -1243,7 +1227,7 @@ int subsys_system_register(struct bus_type *subsys, if (err < 0) goto err_name; - dev->kobj.parent = &system_kset->kobj; + dev->kobj.parent = parent_of_root; dev->groups = groups; dev->release = system_root_device_release; @@ -1263,8 +1247,55 @@ err_dev: bus_unregister(subsys); return err; } + +/** + * subsys_system_register - register a subsystem at /sys/devices/system/ + * @subsys: system subsystem + * @groups: default attributes for the root device + * + * All 'system' subsystems have a /sys/devices/system/<name> root device + * with the name of the subsystem. The root device can carry subsystem- + * wide attributes. All registered devices are below this single root + * device and are named after the subsystem with a simple enumeration + * number appended. The registered devices are not explicitely named; + * only 'id' in the device needs to be set. + * + * Do not use this interface for anything new, it exists for compatibility + * with bad ideas only. New subsystems should use plain subsystems; and + * add the subsystem-wide attributes should be added to the subsystem + * directory itself and not some create fake root-device placed in + * /sys/devices/system/<name>. + */ +int subsys_system_register(struct bus_type *subsys, + const struct attribute_group **groups) +{ + return subsys_register(subsys, groups, &system_kset->kobj); +} EXPORT_SYMBOL_GPL(subsys_system_register); +/** + * subsys_virtual_register - register a subsystem at /sys/devices/virtual/ + * @subsys: virtual subsystem + * @groups: default attributes for the root device + * + * All 'virtual' subsystems have a /sys/devices/system/<name> root device + * with the name of the subystem. The root device can carry subsystem-wide + * attributes. All registered devices are below this single root device. + * There's no restriction on device naming. This is for kernel software + * constructs which need sysfs interface. + */ +int subsys_virtual_register(struct bus_type *subsys, + const struct attribute_group **groups) +{ + struct kobject *virtual_dir; + + virtual_dir = virtual_device_parent(NULL); + if (!virtual_dir) + return -ENOMEM; + + return subsys_register(subsys, groups, virtual_dir); +} + int __init buses_init(void) { bus_kset = kset_create_and_add("bus", &bus_uevent_ops, NULL); diff --git a/drivers/base/core.c b/drivers/base/core.c index 56536f4b0f6..01631243757 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; } @@ -690,7 +703,7 @@ void device_initialize(struct device *dev) set_dev_node(dev, -1); } -static struct kobject *virtual_device_parent(struct device *dev) +struct kobject *virtual_device_parent(struct device *dev) { static struct kobject *virtual_dir = NULL; @@ -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 fb10728f637..3d48fc887ef 100644 --- a/drivers/base/cpu.c +++ b/drivers/base/cpu.c @@ -25,6 +25,15 @@ EXPORT_SYMBOL_GPL(cpu_subsys); static DEFINE_PER_CPU(struct device *, cpu_sys_devices); #ifdef CONFIG_HOTPLUG_CPU +static void change_cpu_under_node(struct cpu *cpu, + unsigned int from_nid, unsigned int to_nid) +{ + int cpuid = cpu->dev.id; + unregister_cpu_under_node(cpuid, from_nid); + register_cpu_under_node(cpuid, to_nid); + cpu->node_id = to_nid; +} + static ssize_t show_online(struct device *dev, struct device_attribute *attr, char *buf) @@ -39,17 +48,29 @@ static ssize_t __ref store_online(struct device *dev, const char *buf, size_t count) { struct cpu *cpu = container_of(dev, struct cpu, dev); + int cpuid = cpu->dev.id; + int from_nid, to_nid; ssize_t ret; cpu_hotplug_driver_lock(); switch (buf[0]) { case '0': - ret = cpu_down(cpu->dev.id); + ret = cpu_down(cpuid); if (!ret) kobject_uevent(&dev->kobj, KOBJ_OFFLINE); break; case '1': - ret = cpu_up(cpu->dev.id); + from_nid = cpu_to_node(cpuid); + ret = cpu_up(cpuid); + + /* + * When hot adding memory to memoryless node and enabling a cpu + * on the node, node number of the cpu may internally change. + */ + to_nid = cpu_to_node(cpuid); + if (from_nid != to_nid) + change_cpu_under_node(cpu, from_nid, to_nid); + if (!ret) kobject_uevent(&dev->kobj, KOBJ_ONLINE); break; @@ -132,6 +153,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 +291,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 bb5645ea028..35fa3689891 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 01fc5b07f95..7413d065906 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/memory.c b/drivers/base/memory.c index a51007b7903..14f8a6954da 100644 --- a/drivers/base/memory.c +++ b/drivers/base/memory.c @@ -93,16 +93,6 @@ int register_memory(struct memory_block *memory) return error; } -static void -unregister_memory(struct memory_block *memory) -{ - BUG_ON(memory->dev.bus != &memory_subsys); - - /* drop the ref. we got in remove_memory_block() */ - kobject_put(&memory->dev.kobj); - device_unregister(&memory->dev); -} - unsigned long __weak memory_block_size_bytes(void) { return MIN_MEMORY_BLOCK_SIZE; @@ -217,8 +207,7 @@ int memory_isolate_notify(unsigned long val, void *v) * The probe routines leave the pages reserved, just as the bootmem code does. * Make sure they're still that way. */ -static bool pages_correctly_reserved(unsigned long start_pfn, - unsigned long nr_pages) +static bool pages_correctly_reserved(unsigned long start_pfn) { int i, j; struct page *page; @@ -266,7 +255,7 @@ memory_block_action(unsigned long phys_index, unsigned long action, int online_t switch (action) { case MEM_ONLINE: - if (!pages_correctly_reserved(start_pfn, nr_pages)) + if (!pages_correctly_reserved(start_pfn)) return -EBUSY; ret = online_pages(start_pfn, nr_pages, online_type); @@ -637,8 +626,28 @@ static int add_memory_section(int nid, struct mem_section *section, return ret; } -int remove_memory_block(unsigned long node_id, struct mem_section *section, - int phys_device) +/* + * need an interface for the VM to add new memory regions, + * but without onlining it. + */ +int register_new_memory(int nid, struct mem_section *section) +{ + return add_memory_section(nid, section, NULL, MEM_OFFLINE, HOTPLUG); +} + +#ifdef CONFIG_MEMORY_HOTREMOVE +static void +unregister_memory(struct memory_block *memory) +{ + BUG_ON(memory->dev.bus != &memory_subsys); + + /* drop the ref. we got in remove_memory_block() */ + kobject_put(&memory->dev.kobj); + device_unregister(&memory->dev); +} + +static int remove_memory_block(unsigned long node_id, + struct mem_section *section, int phys_device) { struct memory_block *mem; @@ -661,15 +670,6 @@ int remove_memory_block(unsigned long node_id, struct mem_section *section, return 0; } -/* - * need an interface for the VM to add new memory regions, - * but without onlining it. - */ -int register_new_memory(int nid, struct mem_section *section) -{ - return add_memory_section(nid, section, NULL, MEM_OFFLINE, HOTPLUG); -} - int unregister_memory_section(struct mem_section *section) { if (!present_section(section)) @@ -677,6 +677,7 @@ int unregister_memory_section(struct mem_section *section) return remove_memory_block(0, section, 0); } +#endif /* CONFIG_MEMORY_HOTREMOVE */ /* * offline one memory block. If the memory block has been offlined, do nothing. diff --git a/drivers/base/node.c b/drivers/base/node.c index fac124a7e1c..7616a77ca32 100644 --- a/drivers/base/node.c +++ b/drivers/base/node.c @@ -7,6 +7,7 @@ #include <linux/mm.h> #include <linux/memory.h> #include <linux/vmstat.h> +#include <linux/notifier.h> #include <linux/node.h> #include <linux/hugetlb.h> #include <linux/compaction.h> @@ -683,8 +684,11 @@ static int __init register_node_type(void) ret = subsys_system_register(&node_subsys, cpu_root_attr_groups); if (!ret) { - hotplug_memory_notifier(node_memory_callback, - NODE_CALLBACK_PRI); + static struct notifier_block node_memory_callback_nb = { + .notifier_call = node_memory_callback, + .priority = NODE_CALLBACK_PRI, + }; + register_hotmemory_notifier(&node_memory_callback_nb); } /* diff --git a/drivers/base/platform.c b/drivers/base/platform.c index c0b8df38402..9eda84246ff 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/base/regmap/internal.h b/drivers/base/regmap/internal.h index 5a22bd33ce3..c130536e0ab 100644 --- a/drivers/base/regmap/internal.h +++ b/drivers/base/regmap/internal.h @@ -38,7 +38,8 @@ struct regmap_format { unsigned int reg, unsigned int val); void (*format_reg)(void *buf, unsigned int reg, unsigned int shift); void (*format_val)(void *buf, unsigned int val, unsigned int shift); - unsigned int (*parse_val)(void *buf); + unsigned int (*parse_val)(const void *buf); + void (*parse_inplace)(void *buf); }; struct regmap_async { @@ -76,6 +77,7 @@ struct regmap { unsigned int debugfs_tot_len; struct list_head debugfs_off_cache; + struct mutex cache_lock; #endif unsigned int max_register; @@ -125,6 +127,9 @@ struct regmap { void *cache; u32 cache_dirty; + unsigned long *cache_present; + unsigned int cache_present_nbits; + struct reg_default *patch; int patch_regs; @@ -187,12 +192,35 @@ int regcache_read(struct regmap *map, int regcache_write(struct regmap *map, unsigned int reg, unsigned int value); int regcache_sync(struct regmap *map); - -unsigned int regcache_get_val(const void *base, unsigned int idx, - unsigned int word_size); -bool regcache_set_val(void *base, unsigned int idx, - unsigned int val, unsigned int word_size); +int regcache_sync_block(struct regmap *map, void *block, + unsigned int block_base, unsigned int start, + unsigned int end); + +static inline const void *regcache_get_val_addr(struct regmap *map, + const void *base, + unsigned int idx) +{ + return base + (map->cache_word_size * idx); +} + +unsigned int regcache_get_val(struct regmap *map, const void *base, + unsigned int idx); +bool regcache_set_val(struct regmap *map, void *base, unsigned int idx, + unsigned int val); int regcache_lookup_reg(struct regmap *map, unsigned int reg); +int regcache_set_reg_present(struct regmap *map, unsigned int reg); + +static inline bool regcache_reg_present(struct regmap *map, unsigned int reg) +{ + if (!map->cache_present) + return true; + if (reg > map->cache_present_nbits) + return false; + return map->cache_present[BIT_WORD(reg)] & BIT_MASK(reg); +} + +int _regmap_raw_write(struct regmap *map, unsigned int reg, + const void *val, size_t val_len, bool async); void regmap_async_complete_cb(struct regmap_async *async, int ret); diff --git a/drivers/base/regmap/regcache-lzo.c b/drivers/base/regmap/regcache-lzo.c index afd6aa91a0d..e210a6d1406 100644 --- a/drivers/base/regmap/regcache-lzo.c +++ b/drivers/base/regmap/regcache-lzo.c @@ -260,8 +260,7 @@ static int regcache_lzo_read(struct regmap *map, ret = regcache_lzo_decompress_cache_block(map, lzo_block); if (ret >= 0) /* fetch the value from the cache */ - *value = regcache_get_val(lzo_block->dst, blkpos, - map->cache_word_size); + *value = regcache_get_val(map, lzo_block->dst, blkpos); kfree(lzo_block->dst); /* restore the pointer and length of the compressed block */ @@ -304,8 +303,7 @@ static int regcache_lzo_write(struct regmap *map, } /* write the new value to the cache */ - if (regcache_set_val(lzo_block->dst, blkpos, value, - map->cache_word_size)) { + if (regcache_set_val(map, lzo_block->dst, blkpos, value)) { kfree(lzo_block->dst); goto out; } diff --git a/drivers/base/regmap/regcache-rbtree.c b/drivers/base/regmap/regcache-rbtree.c index 79f4fca9877..aa0875f6f1b 100644 --- a/drivers/base/regmap/regcache-rbtree.c +++ b/drivers/base/regmap/regcache-rbtree.c @@ -47,22 +47,21 @@ static inline void regcache_rbtree_get_base_top_reg( *top = rbnode->base_reg + ((rbnode->blklen - 1) * map->reg_stride); } -static unsigned int regcache_rbtree_get_register( - struct regcache_rbtree_node *rbnode, unsigned int idx, - unsigned int word_size) +static unsigned int regcache_rbtree_get_register(struct regmap *map, + struct regcache_rbtree_node *rbnode, unsigned int idx) { - return regcache_get_val(rbnode->block, idx, word_size); + return regcache_get_val(map, rbnode->block, idx); } -static void regcache_rbtree_set_register(struct regcache_rbtree_node *rbnode, - unsigned int idx, unsigned int val, - unsigned int word_size) +static void regcache_rbtree_set_register(struct regmap *map, + struct regcache_rbtree_node *rbnode, + unsigned int idx, unsigned int val) { - regcache_set_val(rbnode->block, idx, val, word_size); + regcache_set_val(map, rbnode->block, idx, val); } static struct regcache_rbtree_node *regcache_rbtree_lookup(struct regmap *map, - unsigned int reg) + unsigned int reg) { struct regcache_rbtree_ctx *rbtree_ctx = map->cache; struct rb_node *node; @@ -139,15 +138,21 @@ static int rbtree_show(struct seq_file *s, void *ignored) struct regcache_rbtree_node *n; struct rb_node *node; unsigned int base, top; + size_t mem_size; int nodes = 0; int registers = 0; int this_registers, average; map->lock(map); + mem_size = sizeof(*rbtree_ctx); + mem_size += BITS_TO_LONGS(map->cache_present_nbits) * sizeof(long); + for (node = rb_first(&rbtree_ctx->root); node != NULL; node = rb_next(node)) { n = container_of(node, struct regcache_rbtree_node, node); + mem_size += sizeof(*n); + mem_size += (n->blklen * map->cache_word_size); regcache_rbtree_get_base_top_reg(map, n, &base, &top); this_registers = ((top - base) / map->reg_stride) + 1; @@ -162,8 +167,8 @@ static int rbtree_show(struct seq_file *s, void *ignored) else average = 0; - seq_printf(s, "%d nodes, %d registers, average %d registers\n", - nodes, registers, average); + seq_printf(s, "%d nodes, %d registers, average %d registers, used %zu bytes\n", + nodes, registers, average, mem_size); map->unlock(map); @@ -260,8 +265,9 @@ static int regcache_rbtree_read(struct regmap *map, rbnode = regcache_rbtree_lookup(map, reg); if (rbnode) { reg_tmp = (reg - rbnode->base_reg) / map->reg_stride; - *value = regcache_rbtree_get_register(rbnode, reg_tmp, - map->cache_word_size); + if (!regcache_reg_present(map, reg)) + return -ENOENT; + *value = regcache_rbtree_get_register(map, rbnode, reg_tmp); } else { return -ENOENT; } @@ -270,21 +276,23 @@ static int regcache_rbtree_read(struct regmap *map, } -static int regcache_rbtree_insert_to_block(struct regcache_rbtree_node *rbnode, +static int regcache_rbtree_insert_to_block(struct regmap *map, + struct regcache_rbtree_node *rbnode, unsigned int pos, unsigned int reg, - unsigned int value, unsigned int word_size) + unsigned int value) { u8 *blk; blk = krealloc(rbnode->block, - (rbnode->blklen + 1) * word_size, GFP_KERNEL); + (rbnode->blklen + 1) * map->cache_word_size, + GFP_KERNEL); if (!blk) return -ENOMEM; /* insert the register value in the correct place in the rbnode block */ - memmove(blk + (pos + 1) * word_size, - blk + pos * word_size, - (rbnode->blklen - pos) * word_size); + memmove(blk + (pos + 1) * map->cache_word_size, + blk + pos * map->cache_word_size, + (rbnode->blklen - pos) * map->cache_word_size); /* update the rbnode block, its size and the base register */ rbnode->block = blk; @@ -292,7 +300,7 @@ static int regcache_rbtree_insert_to_block(struct regcache_rbtree_node *rbnode, if (!pos) rbnode->base_reg = reg; - regcache_rbtree_set_register(rbnode, pos, value, word_size); + regcache_rbtree_set_register(map, rbnode, pos, value); return 0; } @@ -302,25 +310,24 @@ static int regcache_rbtree_write(struct regmap *map, unsigned int reg, struct regcache_rbtree_ctx *rbtree_ctx; struct regcache_rbtree_node *rbnode, *rbnode_tmp; struct rb_node *node; - unsigned int val; unsigned int reg_tmp; unsigned int pos; int i; int ret; rbtree_ctx = map->cache; + /* update the reg_present bitmap, make space if necessary */ + ret = regcache_set_reg_present(map, reg); + if (ret < 0) + return ret; + /* if we can't locate it in the cached rbnode we'll have * to traverse the rbtree looking for it. */ rbnode = regcache_rbtree_lookup(map, reg); if (rbnode) { reg_tmp = (reg - rbnode->base_reg) / map->reg_stride; - val = regcache_rbtree_get_register(rbnode, reg_tmp, - map->cache_word_size); - if (val == value) - return 0; - regcache_rbtree_set_register(rbnode, reg_tmp, value, - map->cache_word_size); + regcache_rbtree_set_register(map, rbnode, reg_tmp, value); } else { /* look for an adjacent register to the one we are about to add */ for (node = rb_first(&rbtree_ctx->root); node; @@ -337,9 +344,10 @@ static int regcache_rbtree_write(struct regmap *map, unsigned int reg, pos = i + 1; else pos = i; - ret = regcache_rbtree_insert_to_block(rbnode_tmp, pos, - reg, value, - map->cache_word_size); + ret = regcache_rbtree_insert_to_block(map, + rbnode_tmp, + pos, reg, + value); if (ret) return ret; rbtree_ctx->cached_rbnode = rbnode_tmp; @@ -354,7 +362,7 @@ static int regcache_rbtree_write(struct regmap *map, unsigned int reg, rbnode = kzalloc(sizeof *rbnode, GFP_KERNEL); if (!rbnode) return -ENOMEM; - rbnode->blklen = 1; + rbnode->blklen = sizeof(*rbnode); rbnode->base_reg = reg; rbnode->block = kmalloc(rbnode->blklen * map->cache_word_size, GFP_KERNEL); @@ -362,7 +370,7 @@ static int regcache_rbtree_write(struct regmap *map, unsigned int reg, kfree(rbnode); return -ENOMEM; } - regcache_rbtree_set_register(rbnode, 0, value, map->cache_word_size); + regcache_rbtree_set_register(map, rbnode, 0, value); regcache_rbtree_insert(map, &rbtree_ctx->root, rbnode); rbtree_ctx->cached_rbnode = rbnode; } @@ -376,10 +384,8 @@ static int regcache_rbtree_sync(struct regmap *map, unsigned int min, struct regcache_rbtree_ctx *rbtree_ctx; struct rb_node *node; struct regcache_rbtree_node *rbnode; - unsigned int regtmp; - unsigned int val; int ret; - int i, base, end; + int base, end; rbtree_ctx = map->cache; for (node = rb_first(&rbtree_ctx->root); node; node = rb_next(node)) { @@ -402,27 +408,13 @@ static int regcache_rbtree_sync(struct regmap *map, unsigned int min, else end = rbnode->blklen; - for (i = base; i < end; i++) { - regtmp = rbnode->base_reg + (i * map->reg_stride); - val = regcache_rbtree_get_register(rbnode, i, - map->cache_word_size); - - /* Is this the hardware default? If so skip. */ - ret = regcache_lookup_reg(map, regtmp); - if (ret >= 0 && val == map->reg_defaults[ret].def) - continue; - - map->cache_bypass = 1; - ret = _regmap_write(map, regtmp, val); - map->cache_bypass = 0; - if (ret) - return ret; - dev_dbg(map->dev, "Synced register %#x, value %#x\n", - regtmp, val); - } + ret = regcache_sync_block(map, rbnode->block, rbnode->base_reg, + base, end); + if (ret != 0) + return ret; } - return 0; + return regmap_async_complete(map); } struct regcache_ops regcache_rbtree_ops = { diff --git a/drivers/base/regmap/regcache.c b/drivers/base/regmap/regcache.c index e69ff3e4742..75923f2396b 100644 --- a/drivers/base/regmap/regcache.c +++ b/drivers/base/regmap/regcache.c @@ -45,8 +45,8 @@ static int regcache_hw_init(struct regmap *map) tmp_buf = kmalloc(map->cache_size_raw, GFP_KERNEL); if (!tmp_buf) return -EINVAL; - ret = regmap_bulk_read(map, 0, tmp_buf, - map->num_reg_defaults_raw); + ret = regmap_raw_read(map, 0, tmp_buf, + map->num_reg_defaults_raw); map->cache_bypass = cache_bypass; if (ret < 0) { kfree(tmp_buf); @@ -58,8 +58,7 @@ static int regcache_hw_init(struct regmap *map) /* calculate the size of reg_defaults */ for (count = 0, i = 0; i < map->num_reg_defaults_raw; i++) { - val = regcache_get_val(map->reg_defaults_raw, - i, map->cache_word_size); + val = regcache_get_val(map, map->reg_defaults_raw, i); if (regmap_volatile(map, i * map->reg_stride)) continue; count++; @@ -75,8 +74,7 @@ static int regcache_hw_init(struct regmap *map) /* fill the reg_defaults */ map->num_reg_defaults = count; for (i = 0, j = 0; i < map->num_reg_defaults_raw; i++) { - val = regcache_get_val(map->reg_defaults_raw, - i, map->cache_word_size); + val = regcache_get_val(map, map->reg_defaults_raw, i); if (regmap_volatile(map, i * map->reg_stride)) continue; map->reg_defaults[j].reg = i * map->reg_stride; @@ -123,6 +121,8 @@ int regcache_init(struct regmap *map, const struct regmap_config *config) map->reg_defaults_raw = config->reg_defaults_raw; map->cache_word_size = DIV_ROUND_UP(config->val_bits, 8); map->cache_size_raw = map->cache_word_size * config->num_reg_defaults_raw; + map->cache_present = NULL; + map->cache_present_nbits = 0; map->cache = NULL; map->cache_ops = cache_types[i]; @@ -181,6 +181,7 @@ void regcache_exit(struct regmap *map) BUG_ON(!map->cache_ops); + kfree(map->cache_present); kfree(map->reg_defaults); if (map->cache_free) kfree(map->reg_defaults_raw); @@ -417,28 +418,68 @@ void regcache_cache_bypass(struct regmap *map, bool enable) } EXPORT_SYMBOL_GPL(regcache_cache_bypass); -bool regcache_set_val(void *base, unsigned int idx, - unsigned int val, unsigned int word_size) +int regcache_set_reg_present(struct regmap *map, unsigned int reg) { - switch (word_size) { + unsigned long *cache_present; + unsigned int cache_present_size; + unsigned int nregs; + int i; + + nregs = reg + 1; + cache_present_size = BITS_TO_LONGS(nregs); + cache_present_size *= sizeof(long); + + if (!map->cache_present) { + cache_present = kmalloc(cache_present_size, GFP_KERNEL); + if (!cache_present) + return -ENOMEM; + bitmap_zero(cache_present, nregs); + map->cache_present = cache_present; + map->cache_present_nbits = nregs; + } + + if (nregs > map->cache_present_nbits) { + cache_present = krealloc(map->cache_present, + cache_present_size, GFP_KERNEL); + if (!cache_present) + return -ENOMEM; + for (i = 0; i < nregs; i++) + if (i >= map->cache_present_nbits) + clear_bit(i, cache_present); + map->cache_present = cache_present; + map->cache_present_nbits = nregs; + } + + set_bit(reg, map->cache_present); + return 0; +} + +bool regcache_set_val(struct regmap *map, void *base, unsigned int idx, + unsigned int val) +{ + if (regcache_get_val(map, base, idx) == val) + return true; + + /* Use device native format if possible */ + if (map->format.format_val) { + map->format.format_val(base + (map->cache_word_size * idx), + val, 0); + return false; + } + + switch (map->cache_word_size) { case 1: { u8 *cache = base; - if (cache[idx] == val) - return true; cache[idx] = val; break; } case 2: { u16 *cache = base; - if (cache[idx] == val) - return true; cache[idx] = val; break; } case 4: { u32 *cache = base; - if (cache[idx] == val) - return true; cache[idx] = val; break; } @@ -448,13 +489,18 @@ bool regcache_set_val(void *base, unsigned int idx, return false; } -unsigned int regcache_get_val(const void *base, unsigned int idx, - unsigned int word_size) +unsigned int regcache_get_val(struct regmap *map, const void *base, + unsigned int idx) { if (!base) return -EINVAL; - switch (word_size) { + /* Use device native format if possible */ + if (map->format.parse_val) + return map->format.parse_val(regcache_get_val_addr(map, base, + idx)); + + switch (map->cache_word_size) { case 1: { const u8 *cache = base; return cache[idx]; @@ -498,3 +544,117 @@ int regcache_lookup_reg(struct regmap *map, unsigned int reg) else return -ENOENT; } + +static int regcache_sync_block_single(struct regmap *map, void *block, + unsigned int block_base, + unsigned int start, unsigned int end) +{ + unsigned int i, regtmp, val; + int ret; + + for (i = start; i < end; i++) { + regtmp = block_base + (i * map->reg_stride); + + if (!regcache_reg_present(map, regtmp)) + continue; + + val = regcache_get_val(map, block, i); + + /* Is this the hardware default? If so skip. */ + ret = regcache_lookup_reg(map, regtmp); + if (ret >= 0 && val == map->reg_defaults[ret].def) + continue; + + map->cache_bypass = 1; + + ret = _regmap_write(map, regtmp, val); + + map->cache_bypass = 0; + if (ret != 0) + return ret; + dev_dbg(map->dev, "Synced register %#x, value %#x\n", + regtmp, val); + } + + return 0; +} + +static int regcache_sync_block_raw_flush(struct regmap *map, const void **data, + unsigned int base, unsigned int cur) +{ + size_t val_bytes = map->format.val_bytes; + int ret, count; + + if (*data == NULL) + return 0; + + count = cur - base; + + dev_dbg(map->dev, "Writing %zu bytes for %d registers from 0x%x-0x%x\n", + count * val_bytes, count, base, cur - 1); + + map->cache_bypass = 1; + + ret = _regmap_raw_write(map, base, *data, count * val_bytes, + false); + + map->cache_bypass = 0; + + *data = NULL; + + return ret; +} + +static int regcache_sync_block_raw(struct regmap *map, void *block, + unsigned int block_base, unsigned int start, + unsigned int end) +{ + unsigned int i, val; + unsigned int regtmp = 0; + unsigned int base = 0; + const void *data = NULL; + int ret; + + for (i = start; i < end; i++) { + regtmp = block_base + (i * map->reg_stride); + + if (!regcache_reg_present(map, regtmp)) { + ret = regcache_sync_block_raw_flush(map, &data, + base, regtmp); + if (ret != 0) + return ret; + continue; + } + + val = regcache_get_val(map, block, i); + + /* Is this the hardware default? If so skip. */ + ret = regcache_lookup_reg(map, regtmp); + if (ret >= 0 && val == map->reg_defaults[ret].def) { + ret = regcache_sync_block_raw_flush(map, &data, + base, regtmp); + if (ret != 0) + return ret; + continue; + } + + if (!data) { + data = regcache_get_val_addr(map, block, i); + base = regtmp; + } + } + + return regcache_sync_block_raw_flush(map, &data, base, regtmp); +} + +int regcache_sync_block(struct regmap *map, void *block, + unsigned int block_base, unsigned int start, + unsigned int end) +{ + if (regmap_can_raw_write(map)) + return regcache_sync_block_raw(map, block, block_base, + start, end); + else + return regcache_sync_block_single(map, block, block_base, + start, end); +} diff --git a/drivers/base/regmap/regmap-debugfs.c b/drivers/base/regmap/regmap-debugfs.c index 81d6f605c92..23b701f5fd2 100644 --- a/drivers/base/regmap/regmap-debugfs.c +++ b/drivers/base/regmap/regmap-debugfs.c @@ -88,16 +88,16 @@ static unsigned int regmap_debugfs_get_dump_start(struct regmap *map, * If we don't have a cache build one so we don't have to do a * linear scan each time. */ + mutex_lock(&map->cache_lock); + i = base; if (list_empty(&map->debugfs_off_cache)) { - for (i = base; i <= map->max_register; i += map->reg_stride) { + for (; i <= map->max_register; i += map->reg_stride) { /* Skip unprinted registers, closing off cache entry */ if (!regmap_readable(map, i) || regmap_precious(map, i)) { if (c) { c->max = p - 1; - fpos_offset = c->max - c->min; - reg_offset = fpos_offset / map->debugfs_tot_len; - c->max_reg = c->base_reg + reg_offset; + c->max_reg = i - map->reg_stride; list_add_tail(&c->list, &map->debugfs_off_cache); c = NULL; @@ -111,6 +111,7 @@ static unsigned int regmap_debugfs_get_dump_start(struct regmap *map, c = kzalloc(sizeof(*c), GFP_KERNEL); if (!c) { regmap_debugfs_free_dump_cache(map); + mutex_unlock(&map->cache_lock); return base; } c->min = p; @@ -124,9 +125,7 @@ static unsigned int regmap_debugfs_get_dump_start(struct regmap *map, /* Close the last entry off if we didn't scan beyond it */ if (c) { c->max = p - 1; - fpos_offset = c->max - c->min; - reg_offset = fpos_offset / map->debugfs_tot_len; - c->max_reg = c->base_reg + reg_offset; + c->max_reg = i - map->reg_stride; list_add_tail(&c->list, &map->debugfs_off_cache); } @@ -145,12 +144,14 @@ static unsigned int regmap_debugfs_get_dump_start(struct regmap *map, fpos_offset = from - c->min; reg_offset = fpos_offset / map->debugfs_tot_len; *pos = c->min + (reg_offset * map->debugfs_tot_len); + mutex_unlock(&map->cache_lock); return c->base_reg + reg_offset; } *pos = c->max; ret = c->max_reg; } + mutex_unlock(&map->cache_lock); return ret; } @@ -311,6 +312,79 @@ static const struct file_operations regmap_range_fops = { .llseek = default_llseek, }; +static ssize_t regmap_reg_ranges_read_file(struct file *file, + char __user *user_buf, size_t count, + loff_t *ppos) +{ + struct regmap *map = file->private_data; + struct regmap_debugfs_off_cache *c; + loff_t p = 0; + size_t buf_pos = 0; + char *buf; + char *entry; + int ret; + + if (*ppos < 0 || !count) + return -EINVAL; + + buf = kmalloc(count, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + entry = kmalloc(PAGE_SIZE, GFP_KERNEL); + if (!entry) { + kfree(buf); + return -ENOMEM; + } + + /* While we are at it, build the register dump cache + * now so the read() operation on the `registers' file + * can benefit from using the cache. We do not care + * about the file position information that is contained + * in the cache, just about the actual register blocks */ + regmap_calc_tot_len(map, buf, count); + regmap_debugfs_get_dump_start(map, 0, *ppos, &p); + + /* Reset file pointer as the fixed-format of the `registers' + * file is not compatible with the `range' file */ + p = 0; + mutex_lock(&map->cache_lock); + list_for_each_entry(c, &map->debugfs_off_cache, list) { + snprintf(entry, PAGE_SIZE, "%x-%x", + c->base_reg, c->max_reg); + if (p >= *ppos) { + if (buf_pos + 1 + strlen(entry) > count) + break; + snprintf(buf + buf_pos, count - buf_pos, + "%s", entry); + buf_pos += strlen(entry); + buf[buf_pos] = '\n'; + buf_pos++; + } + p += strlen(entry) + 1; + } + mutex_unlock(&map->cache_lock); + + kfree(entry); + ret = buf_pos; + + if (copy_to_user(user_buf, buf, buf_pos)) { + ret = -EFAULT; + goto out_buf; + } + + *ppos += buf_pos; +out_buf: + kfree(buf); + return ret; +} + +static const struct file_operations regmap_reg_ranges_fops = { + .open = simple_open, + .read = regmap_reg_ranges_read_file, + .llseek = default_llseek, +}; + static ssize_t regmap_access_read_file(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) @@ -385,6 +459,7 @@ void regmap_debugfs_init(struct regmap *map, const char *name) struct regmap_range_node *range_node; INIT_LIST_HEAD(&map->debugfs_off_cache); + mutex_init(&map->cache_lock); if (name) { map->debugfs_name = kasprintf(GFP_KERNEL, "%s-%s", @@ -403,6 +478,9 @@ void regmap_debugfs_init(struct regmap *map, const char *name) debugfs_create_file("name", 0400, map->debugfs, map, ®map_name_fops); + debugfs_create_file("range", 0400, map->debugfs, + map, ®map_reg_ranges_fops); + if (map->max_register) { debugfs_create_file("registers", 0400, map->debugfs, map, ®map_map_fops); @@ -435,7 +513,9 @@ void regmap_debugfs_init(struct regmap *map, const char *name) void regmap_debugfs_exit(struct regmap *map) { debugfs_remove_recursive(map->debugfs); + mutex_lock(&map->cache_lock); regmap_debugfs_free_dump_cache(map); + mutex_unlock(&map->cache_lock); kfree(map->debugfs_name); } diff --git a/drivers/base/regmap/regmap-irq.c b/drivers/base/regmap/regmap-irq.c index 020ea2b9fd2..1643e889baf 100644 --- a/drivers/base/regmap/regmap-irq.c +++ b/drivers/base/regmap/regmap-irq.c @@ -460,7 +460,8 @@ int regmap_add_irq_chip(struct regmap *map, int irq, int irq_flags, ret = request_threaded_irq(irq, NULL, regmap_irq_thread, irq_flags, chip->name, d); if (ret != 0) { - dev_err(map->dev, "Failed to request IRQ %d: %d\n", irq, ret); + dev_err(map->dev, "Failed to request IRQ %d for %s: %d\n", + irq, chip->name, ret); goto err_domain; } diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c index 58cfb323242..a941dcfe759 100644 --- a/drivers/base/regmap/regmap.c +++ b/drivers/base/regmap/regmap.c @@ -228,30 +228,39 @@ static void regmap_format_32_native(void *buf, unsigned int val, *(u32 *)buf = val << shift; } -static unsigned int regmap_parse_8(void *buf) +static void regmap_parse_inplace_noop(void *buf) { - u8 *b = buf; +} + +static unsigned int regmap_parse_8(const void *buf) +{ + const u8 *b = buf; return b[0]; } -static unsigned int regmap_parse_16_be(void *buf) +static unsigned int regmap_parse_16_be(const void *buf) +{ + const __be16 *b = buf; + + return be16_to_cpu(b[0]); +} + +static void regmap_parse_16_be_inplace(void *buf) { __be16 *b = buf; b[0] = be16_to_cpu(b[0]); - - return b[0]; } -static unsigned int regmap_parse_16_native(void *buf) +static unsigned int regmap_parse_16_native(const void *buf) { return *(u16 *)buf; } -static unsigned int regmap_parse_24(void *buf) +static unsigned int regmap_parse_24(const void *buf) { - u8 *b = buf; + const u8 *b = buf; unsigned int ret = b[2]; ret |= ((unsigned int)b[1]) << 8; ret |= ((unsigned int)b[0]) << 16; @@ -259,16 +268,21 @@ static unsigned int regmap_parse_24(void *buf) return ret; } -static unsigned int regmap_parse_32_be(void *buf) +static unsigned int regmap_parse_32_be(const void *buf) +{ + const __be32 *b = buf; + + return be32_to_cpu(b[0]); +} + +static void regmap_parse_32_be_inplace(void *buf) { __be32 *b = buf; b[0] = be32_to_cpu(b[0]); - - return b[0]; } -static unsigned int regmap_parse_32_native(void *buf) +static unsigned int regmap_parse_32_native(const void *buf) { return *(u32 *)buf; } @@ -555,16 +569,21 @@ struct regmap *regmap_init(struct device *dev, goto err_map; } + if (val_endian == REGMAP_ENDIAN_NATIVE) + map->format.parse_inplace = regmap_parse_inplace_noop; + switch (config->val_bits) { case 8: map->format.format_val = regmap_format_8; map->format.parse_val = regmap_parse_8; + map->format.parse_inplace = regmap_parse_inplace_noop; break; case 16: switch (val_endian) { case REGMAP_ENDIAN_BIG: map->format.format_val = regmap_format_16_be; map->format.parse_val = regmap_parse_16_be; + map->format.parse_inplace = regmap_parse_16_be_inplace; break; case REGMAP_ENDIAN_NATIVE: map->format.format_val = regmap_format_16_native; @@ -585,6 +604,7 @@ struct regmap *regmap_init(struct device *dev, case REGMAP_ENDIAN_BIG: map->format.format_val = regmap_format_32_be; map->format.parse_val = regmap_parse_32_be; + map->format.parse_inplace = regmap_parse_32_be_inplace; break; case REGMAP_ENDIAN_NATIVE: map->format.format_val = regmap_format_32_native; @@ -917,8 +937,8 @@ static int _regmap_select_page(struct regmap *map, unsigned int *reg, return 0; } -static int _regmap_raw_write(struct regmap *map, unsigned int reg, - const void *val, size_t val_len, bool async) +int _regmap_raw_write(struct regmap *map, unsigned int reg, + const void *val, size_t val_len, bool async) { struct regmap_range_node *range; unsigned long flags; @@ -930,7 +950,7 @@ static int _regmap_raw_write(struct regmap *map, unsigned int reg, size_t len; int i; - BUG_ON(!map->bus); + WARN_ON(!map->bus); /* Check for unwritable registers before we start */ if (map->writeable_reg) @@ -943,8 +963,7 @@ static int _regmap_raw_write(struct regmap *map, unsigned int reg, unsigned int ival; int val_bytes = map->format.val_bytes; for (i = 0; i < val_len / val_bytes; i++) { - memcpy(map->work_buf, val + (i * val_bytes), val_bytes); - ival = map->format.parse_val(map->work_buf); + ival = map->format.parse_val(val + (i * val_bytes)); ret = regcache_write(map, reg + (i * map->reg_stride), ival); if (ret) { @@ -999,6 +1018,8 @@ static int _regmap_raw_write(struct regmap *map, unsigned int reg, if (!async) return -ENOMEM; + trace_regmap_async_write_start(map->dev, reg, val_len); + async->work_buf = kzalloc(map->format.buf_size, GFP_KERNEL | GFP_DMA); if (!async->work_buf) { @@ -1079,6 +1100,17 @@ static int _regmap_raw_write(struct regmap *map, unsigned int reg, return ret; } +/** + * regmap_can_raw_write - Test if regmap_raw_write() is supported + * + * @map: Map to check. + */ +bool regmap_can_raw_write(struct regmap *map) +{ + return map->bus && map->format.format_val && map->format.format_reg; +} +EXPORT_SYMBOL_GPL(regmap_can_raw_write); + static int _regmap_bus_formatted_write(void *context, unsigned int reg, unsigned int val) { @@ -1086,7 +1118,7 @@ static int _regmap_bus_formatted_write(void *context, unsigned int reg, struct regmap_range_node *range; struct regmap *map = context; - BUG_ON(!map->bus || !map->format.format_write); + WARN_ON(!map->bus || !map->format.format_write); range = _regmap_range_lookup(map, reg); if (range) { @@ -1112,7 +1144,7 @@ static int _regmap_bus_raw_write(void *context, unsigned int reg, { struct regmap *map = context; - BUG_ON(!map->bus || !map->format.format_val); + WARN_ON(!map->bus || !map->format.format_val); map->format.format_val(map->work_buf + map->format.reg_bytes + map->format.pad_bytes, val, 0); @@ -1202,12 +1234,10 @@ int regmap_raw_write(struct regmap *map, unsigned int reg, { int ret; - if (!map->bus) + if (!regmap_can_raw_write(map)) return -EINVAL; if (val_len % map->format.val_bytes) return -EINVAL; - if (reg % map->reg_stride) - return -EINVAL; map->lock(map->lock_arg); @@ -1242,7 +1272,7 @@ int regmap_bulk_write(struct regmap *map, unsigned int reg, const void *val, if (!map->bus) return -EINVAL; - if (!map->format.parse_val) + if (!map->format.parse_inplace) return -EINVAL; if (reg % map->reg_stride) return -EINVAL; @@ -1260,7 +1290,7 @@ int regmap_bulk_write(struct regmap *map, unsigned int reg, const void *val, goto out; } for (i = 0; i < val_count * val_bytes; i += val_bytes) - map->format.parse_val(wval + i); + map->format.parse_inplace(wval + i); } /* * Some devices does not support bulk write, for @@ -1338,7 +1368,7 @@ static int _regmap_raw_read(struct regmap *map, unsigned int reg, void *val, u8 *u8 = map->work_buf; int ret; - BUG_ON(!map->bus); + WARN_ON(!map->bus); range = _regmap_range_lookup(map, reg); if (range) { @@ -1393,7 +1423,7 @@ static int _regmap_read(struct regmap *map, unsigned int reg, int ret; void *context = _regmap_map_get_context(map); - BUG_ON(!map->reg_read); + WARN_ON(!map->reg_read); if (!map->cache_bypass) { ret = regcache_read(map, reg, val); @@ -1521,7 +1551,7 @@ int regmap_bulk_read(struct regmap *map, unsigned int reg, void *val, if (!map->bus) return -EINVAL; - if (!map->format.parse_val) + if (!map->format.parse_inplace) return -EINVAL; if (reg % map->reg_stride) return -EINVAL; @@ -1548,7 +1578,7 @@ int regmap_bulk_read(struct regmap *map, unsigned int reg, void *val, } for (i = 0; i < val_count * val_bytes; i += val_bytes) - map->format.parse_val(val + i); + map->format.parse_inplace(val + i); } else { for (i = 0; i < val_count; i++) { unsigned int ival; @@ -1642,6 +1672,8 @@ void regmap_async_complete_cb(struct regmap_async *async, int ret) struct regmap *map = async->map; bool wake; + trace_regmap_async_io_complete(map->dev); + spin_lock(&map->async_lock); list_del(&async->list); @@ -1688,6 +1720,8 @@ int regmap_async_complete(struct regmap *map) if (!map->bus->async_write) return 0; + trace_regmap_async_complete_start(map->dev); + wait_event(map->async_waitq, regmap_async_is_done(map)); spin_lock_irqsave(&map->async_lock, flags); @@ -1695,6 +1729,8 @@ int regmap_async_complete(struct regmap *map) map->async_ret = 0; spin_unlock_irqrestore(&map->async_lock, flags); + trace_regmap_async_complete_done(map->dev); + return ret; } EXPORT_SYMBOL_GPL(regmap_async_complete); diff --git a/drivers/block/drbd/drbd_receiver.c b/drivers/block/drbd/drbd_receiver.c index a9eccfc6079..83c5ae0ed56 100644 --- a/drivers/block/drbd/drbd_receiver.c +++ b/drivers/block/drbd/drbd_receiver.c @@ -757,7 +757,8 @@ static struct socket *drbd_wait_for_connect(struct drbd_tconn *tconn, struct acc rcu_read_unlock(); timeo = connect_int * HZ; - timeo += (random32() & 1) ? timeo / 7 : -timeo / 7; /* 28.5% random jitter */ + /* 28.5% random jitter */ + timeo += (prandom_u32() & 1) ? timeo / 7 : -timeo / 7; err = wait_for_completion_interruptible_timeout(&ad->door_bell, timeo); if (err <= 0) @@ -953,7 +954,7 @@ retry: conn_warn(tconn, "Error receiving initial packet\n"); sock_release(s); randomize: - if (random32() & 1) + if (prandom_u32() & 1) goto retry; } } diff --git a/drivers/block/rbd.c b/drivers/block/rbd.c index f556f8a8b3f..b7b7a88d9f6 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 0d26851d6e4..6c3e3d43c71 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 7ffd3f40714..a1aaa3ba2a4 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 35a553a9061..beb262f2dc4 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 036cb366fe6..33f3a6950c0 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 25373df1dcf..974321a2508 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 e3f9a99b852..d784650d14f 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 f05d85713fd..895d0b8fb9a 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 30991989d65..d34a24a0d48 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 3b22a606f79..2e2036e940f 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/clk/Kconfig b/drivers/clk/Kconfig index a47e6ee98b8..0357ac44638 100644 --- a/drivers/clk/Kconfig +++ b/drivers/clk/Kconfig @@ -55,6 +55,16 @@ config COMMON_CLK_MAX77686 ---help--- This driver supports Maxim 77686 crystal oscillator clock. +config COMMON_CLK_SI5351 + tristate "Clock driver for SiLabs 5351A/B/C" + depends on I2C + depends on OF + select REGMAP_I2C + select RATIONAL + ---help--- + This driver supports Silicon Labs 5351A/B/C programmable clock + generators. + config CLK_TWL6040 tristate "External McPDM functional clock from twl6040" depends on TWL6040_CORE @@ -63,6 +73,14 @@ config CLK_TWL6040 McPDM. McPDM module is using the external bit clock on the McPDM bus as functional clock. +config COMMON_CLK_AXI_CLKGEN + tristate "AXI clkgen driver" + depends on ARCH_ZYNQ || MICROBLAZE + help + ---help--- + Support for the Analog Devices axi-clkgen pcore clock generator for Xilinx + FPGAs. It is commonly used in Analog Devices' reference designs. + endmenu source "drivers/clk/mvebu/Kconfig" diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile index 300d4775d92..e7f7fe9b2f0 100644 --- a/drivers/clk/Makefile +++ b/drivers/clk/Makefile @@ -7,6 +7,7 @@ obj-$(CONFIG_COMMON_CLK) += clk-fixed-factor.o obj-$(CONFIG_COMMON_CLK) += clk-fixed-rate.o obj-$(CONFIG_COMMON_CLK) += clk-gate.o obj-$(CONFIG_COMMON_CLK) += clk-mux.o +obj-$(CONFIG_COMMON_CLK) += clk-composite.o # SoCs specific obj-$(CONFIG_ARCH_BCM2835) += clk-bcm2835.o @@ -23,6 +24,7 @@ ifeq ($(CONFIG_COMMON_CLK), y) obj-$(CONFIG_ARCH_MMP) += mmp/ endif obj-$(CONFIG_MACH_LOONGSON1) += clk-ls1x.o +obj-$(CONFIG_ARCH_SUNXI) += sunxi/ obj-$(CONFIG_ARCH_U8500) += ux500/ obj-$(CONFIG_ARCH_VT8500) += clk-vt8500.o obj-$(CONFIG_ARCH_ZYNQ) += clk-zynq.o @@ -31,6 +33,8 @@ obj-$(CONFIG_ARCH_TEGRA) += tegra/ obj-$(CONFIG_X86) += x86/ # Chip specific +obj-$(CONFIG_COMMON_CLK_AXI_CLKGEN) += clk-axi-clkgen.o obj-$(CONFIG_COMMON_CLK_WM831X) += clk-wm831x.o obj-$(CONFIG_COMMON_CLK_MAX77686) += clk-max77686.o +obj-$(CONFIG_COMMON_CLK_SI5351) += clk-si5351.o obj-$(CONFIG_CLK_TWL6040) += clk-twl6040.o diff --git a/drivers/clk/clk-axi-clkgen.c b/drivers/clk/clk-axi-clkgen.c new file mode 100644 index 00000000000..8137327847c --- /dev/null +++ b/drivers/clk/clk-axi-clkgen.c @@ -0,0 +1,331 @@ +/* + * AXI clkgen driver + * + * Copyright 2012-2013 Analog Devices Inc. + * Author: Lars-Peter Clausen <lars@metafoo.de> + * + * Licensed under the GPL-2. + * + */ + +#include <linux/platform_device.h> +#include <linux/clk-provider.h> +#include <linux/clk.h> +#include <linux/slab.h> +#include <linux/io.h> +#include <linux/of.h> +#include <linux/module.h> +#include <linux/err.h> + +#define AXI_CLKGEN_REG_UPDATE_ENABLE 0x04 +#define AXI_CLKGEN_REG_CLK_OUT1 0x08 +#define AXI_CLKGEN_REG_CLK_OUT2 0x0c +#define AXI_CLKGEN_REG_CLK_DIV 0x10 +#define AXI_CLKGEN_REG_CLK_FB1 0x14 +#define AXI_CLKGEN_REG_CLK_FB2 0x18 +#define AXI_CLKGEN_REG_LOCK1 0x1c +#define AXI_CLKGEN_REG_LOCK2 0x20 +#define AXI_CLKGEN_REG_LOCK3 0x24 +#define AXI_CLKGEN_REG_FILTER1 0x28 +#define AXI_CLKGEN_REG_FILTER2 0x2c + +struct axi_clkgen { + void __iomem *base; + struct clk_hw clk_hw; +}; + +static uint32_t axi_clkgen_lookup_filter(unsigned int m) +{ + switch (m) { + case 0: + return 0x01001990; + case 1: + return 0x01001190; + case 2: + return 0x01009890; + case 3: + return 0x01001890; + case 4: + return 0x01008890; + case 5 ... 8: + return 0x01009090; + case 9 ... 11: + return 0x01000890; + case 12: + return 0x08009090; + case 13 ... 22: + return 0x01001090; + case 23 ... 36: + return 0x01008090; + case 37 ... 46: + return 0x08001090; + default: + return 0x08008090; + } +} + +static const uint32_t axi_clkgen_lock_table[] = { + 0x060603e8, 0x060603e8, 0x080803e8, 0x0b0b03e8, + 0x0e0e03e8, 0x111103e8, 0x131303e8, 0x161603e8, + 0x191903e8, 0x1c1c03e8, 0x1f1f0384, 0x1f1f0339, + 0x1f1f02ee, 0x1f1f02bc, 0x1f1f028a, 0x1f1f0271, + 0x1f1f023f, 0x1f1f0226, 0x1f1f020d, 0x1f1f01f4, + 0x1f1f01db, 0x1f1f01c2, 0x1f1f01a9, 0x1f1f0190, + 0x1f1f0190, 0x1f1f0177, 0x1f1f015e, 0x1f1f015e, + 0x1f1f0145, 0x1f1f0145, 0x1f1f012c, 0x1f1f012c, + 0x1f1f012c, 0x1f1f0113, 0x1f1f0113, 0x1f1f0113, +}; + +static uint32_t axi_clkgen_lookup_lock(unsigned int m) +{ + if (m < ARRAY_SIZE(axi_clkgen_lock_table)) + return axi_clkgen_lock_table[m]; + return 0x1f1f00fa; +} + +static const unsigned int fpfd_min = 10000; +static const unsigned int fpfd_max = 300000; +static const unsigned int fvco_min = 600000; +static const unsigned int fvco_max = 1200000; + +static void axi_clkgen_calc_params(unsigned long fin, unsigned long fout, + unsigned int *best_d, unsigned int *best_m, unsigned int *best_dout) +{ + unsigned long d, d_min, d_max, _d_min, _d_max; + unsigned long m, m_min, m_max; + unsigned long f, dout, best_f, fvco; + + fin /= 1000; + fout /= 1000; + + best_f = ULONG_MAX; + *best_d = 0; + *best_m = 0; + *best_dout = 0; + + d_min = max_t(unsigned long, DIV_ROUND_UP(fin, fpfd_max), 1); + d_max = min_t(unsigned long, fin / fpfd_min, 80); + + m_min = max_t(unsigned long, DIV_ROUND_UP(fvco_min, fin) * d_min, 1); + m_max = min_t(unsigned long, fvco_max * d_max / fin, 64); + + for (m = m_min; m <= m_max; m++) { + _d_min = max(d_min, DIV_ROUND_UP(fin * m, fvco_max)); + _d_max = min(d_max, fin * m / fvco_min); + + for (d = _d_min; d <= _d_max; d++) { + fvco = fin * m / d; + + dout = DIV_ROUND_CLOSEST(fvco, fout); + dout = clamp_t(unsigned long, dout, 1, 128); + f = fvco / dout; + if (abs(f - fout) < abs(best_f - fout)) { + best_f = f; + *best_d = d; + *best_m = m; + *best_dout = dout; + if (best_f == fout) + return; + } + } + } +} + +static void axi_clkgen_calc_clk_params(unsigned int divider, unsigned int *low, + unsigned int *high, unsigned int *edge, unsigned int *nocount) +{ + if (divider == 1) + *nocount = 1; + else + *nocount = 0; + + *high = divider / 2; + *edge = divider % 2; + *low = divider - *high; +} + +static void axi_clkgen_write(struct axi_clkgen *axi_clkgen, + unsigned int reg, unsigned int val) +{ + writel(val, axi_clkgen->base + reg); +} + +static void axi_clkgen_read(struct axi_clkgen *axi_clkgen, + unsigned int reg, unsigned int *val) +{ + *val = readl(axi_clkgen->base + reg); +} + +static struct axi_clkgen *clk_hw_to_axi_clkgen(struct clk_hw *clk_hw) +{ + return container_of(clk_hw, struct axi_clkgen, clk_hw); +} + +static int axi_clkgen_set_rate(struct clk_hw *clk_hw, + unsigned long rate, unsigned long parent_rate) +{ + struct axi_clkgen *axi_clkgen = clk_hw_to_axi_clkgen(clk_hw); + unsigned int d, m, dout; + unsigned int nocount; + unsigned int high; + unsigned int edge; + unsigned int low; + uint32_t filter; + uint32_t lock; + + if (parent_rate == 0 || rate == 0) + return -EINVAL; + + axi_clkgen_calc_params(parent_rate, rate, &d, &m, &dout); + + if (d == 0 || dout == 0 || m == 0) + return -EINVAL; + + filter = axi_clkgen_lookup_filter(m - 1); + lock = axi_clkgen_lookup_lock(m - 1); + + axi_clkgen_write(axi_clkgen, AXI_CLKGEN_REG_UPDATE_ENABLE, 0); + + axi_clkgen_calc_clk_params(dout, &low, &high, &edge, &nocount); + axi_clkgen_write(axi_clkgen, AXI_CLKGEN_REG_CLK_OUT1, + (high << 6) | low); + axi_clkgen_write(axi_clkgen, AXI_CLKGEN_REG_CLK_OUT2, + (edge << 7) | (nocount << 6)); + + axi_clkgen_calc_clk_params(d, &low, &high, &edge, &nocount); + axi_clkgen_write(axi_clkgen, AXI_CLKGEN_REG_CLK_DIV, + (edge << 13) | (nocount << 12) | (high << 6) | low); + + axi_clkgen_calc_clk_params(m, &low, &high, &edge, &nocount); + axi_clkgen_write(axi_clkgen, AXI_CLKGEN_REG_CLK_FB1, + (high << 6) | low); + axi_clkgen_write(axi_clkgen, AXI_CLKGEN_REG_CLK_FB2, + (edge << 7) | (nocount << 6)); + + axi_clkgen_write(axi_clkgen, AXI_CLKGEN_REG_LOCK1, lock & 0x3ff); + axi_clkgen_write(axi_clkgen, AXI_CLKGEN_REG_LOCK2, + (((lock >> 16) & 0x1f) << 10) | 0x1); + axi_clkgen_write(axi_clkgen, AXI_CLKGEN_REG_LOCK3, + (((lock >> 24) & 0x1f) << 10) | 0x3e9); + axi_clkgen_write(axi_clkgen, AXI_CLKGEN_REG_FILTER1, filter >> 16); + axi_clkgen_write(axi_clkgen, AXI_CLKGEN_REG_FILTER2, filter); + + axi_clkgen_write(axi_clkgen, AXI_CLKGEN_REG_UPDATE_ENABLE, 1); + + return 0; +} + +static long axi_clkgen_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *parent_rate) +{ + unsigned int d, m, dout; + + axi_clkgen_calc_params(*parent_rate, rate, &d, &m, &dout); + + if (d == 0 || dout == 0 || m == 0) + return -EINVAL; + + return *parent_rate / d * m / dout; +} + +static unsigned long axi_clkgen_recalc_rate(struct clk_hw *clk_hw, + unsigned long parent_rate) +{ + struct axi_clkgen *axi_clkgen = clk_hw_to_axi_clkgen(clk_hw); + unsigned int d, m, dout; + unsigned int reg; + unsigned long long tmp; + + axi_clkgen_read(axi_clkgen, AXI_CLKGEN_REG_CLK_OUT1, ®); + dout = (reg & 0x3f) + ((reg >> 6) & 0x3f); + axi_clkgen_read(axi_clkgen, AXI_CLKGEN_REG_CLK_DIV, ®); + d = (reg & 0x3f) + ((reg >> 6) & 0x3f); + axi_clkgen_read(axi_clkgen, AXI_CLKGEN_REG_CLK_FB1, ®); + m = (reg & 0x3f) + ((reg >> 6) & 0x3f); + + if (d == 0 || dout == 0) + return 0; + + tmp = (unsigned long long)(parent_rate / d) * m; + do_div(tmp, dout); + + if (tmp > ULONG_MAX) + return ULONG_MAX; + + return tmp; +} + +static const struct clk_ops axi_clkgen_ops = { + .recalc_rate = axi_clkgen_recalc_rate, + .round_rate = axi_clkgen_round_rate, + .set_rate = axi_clkgen_set_rate, +}; + +static int axi_clkgen_probe(struct platform_device *pdev) +{ + struct axi_clkgen *axi_clkgen; + struct clk_init_data init; + const char *parent_name; + const char *clk_name; + struct resource *mem; + struct clk *clk; + + axi_clkgen = devm_kzalloc(&pdev->dev, sizeof(*axi_clkgen), GFP_KERNEL); + if (!axi_clkgen) + return -ENOMEM; + + mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + axi_clkgen->base = devm_ioremap_resource(&pdev->dev, mem); + if (IS_ERR(axi_clkgen->base)) + return PTR_ERR(axi_clkgen->base); + + parent_name = of_clk_get_parent_name(pdev->dev.of_node, 0); + if (!parent_name) + return -EINVAL; + + clk_name = pdev->dev.of_node->name; + of_property_read_string(pdev->dev.of_node, "clock-output-names", + &clk_name); + + init.name = clk_name; + init.ops = &axi_clkgen_ops; + init.flags = 0; + init.parent_names = &parent_name; + init.num_parents = 1; + + axi_clkgen->clk_hw.init = &init; + clk = devm_clk_register(&pdev->dev, &axi_clkgen->clk_hw); + if (IS_ERR(clk)) + return PTR_ERR(clk); + + return of_clk_add_provider(pdev->dev.of_node, of_clk_src_simple_get, + clk); +} + +static int axi_clkgen_remove(struct platform_device *pdev) +{ + of_clk_del_provider(pdev->dev.of_node); + + return 0; +} + +static const struct of_device_id axi_clkgen_ids[] = { + { .compatible = "adi,axi-clkgen-1.00.a" }, + { }, +}; +MODULE_DEVICE_TABLE(of, axi_clkgen_ids); + +static struct platform_driver axi_clkgen_driver = { + .driver = { + .name = "adi-axi-clkgen", + .owner = THIS_MODULE, + .of_match_table = axi_clkgen_ids, + }, + .probe = axi_clkgen_probe, + .remove = axi_clkgen_remove, +}; +module_platform_driver(axi_clkgen_driver); + +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>"); +MODULE_DESCRIPTION("Driver for the Analog Devices' AXI clkgen pcore clock generator"); diff --git a/drivers/clk/clk-composite.c b/drivers/clk/clk-composite.c new file mode 100644 index 00000000000..a33f46f20a4 --- /dev/null +++ b/drivers/clk/clk-composite.c @@ -0,0 +1,210 @@ +/* + * Copyright (c) 2013 NVIDIA CORPORATION. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <linux/clk.h> +#include <linux/clk-provider.h> +#include <linux/err.h> +#include <linux/slab.h> + +#define to_clk_composite(_hw) container_of(_hw, struct clk_composite, hw) + +static u8 clk_composite_get_parent(struct clk_hw *hw) +{ + struct clk_composite *composite = to_clk_composite(hw); + const struct clk_ops *mux_ops = composite->mux_ops; + struct clk_hw *mux_hw = composite->mux_hw; + + mux_hw->clk = hw->clk; + + return mux_ops->get_parent(mux_hw); +} + +static int clk_composite_set_parent(struct clk_hw *hw, u8 index) +{ + struct clk_composite *composite = to_clk_composite(hw); + const struct clk_ops *mux_ops = composite->mux_ops; + struct clk_hw *mux_hw = composite->mux_hw; + + mux_hw->clk = hw->clk; + + return mux_ops->set_parent(mux_hw, index); +} + +static unsigned long clk_composite_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct clk_composite *composite = to_clk_composite(hw); + const struct clk_ops *rate_ops = composite->rate_ops; + struct clk_hw *rate_hw = composite->rate_hw; + + rate_hw->clk = hw->clk; + + return rate_ops->recalc_rate(rate_hw, parent_rate); +} + +static long clk_composite_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *prate) +{ + struct clk_composite *composite = to_clk_composite(hw); + const struct clk_ops *rate_ops = composite->rate_ops; + struct clk_hw *rate_hw = composite->rate_hw; + + rate_hw->clk = hw->clk; + + return rate_ops->round_rate(rate_hw, rate, prate); +} + +static int clk_composite_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct clk_composite *composite = to_clk_composite(hw); + const struct clk_ops *rate_ops = composite->rate_ops; + struct clk_hw *rate_hw = composite->rate_hw; + + rate_hw->clk = hw->clk; + + return rate_ops->set_rate(rate_hw, rate, parent_rate); +} + +static int clk_composite_is_enabled(struct clk_hw *hw) +{ + struct clk_composite *composite = to_clk_composite(hw); + const struct clk_ops *gate_ops = composite->gate_ops; + struct clk_hw *gate_hw = composite->gate_hw; + + gate_hw->clk = hw->clk; + + return gate_ops->is_enabled(gate_hw); +} + +static int clk_composite_enable(struct clk_hw *hw) +{ + struct clk_composite *composite = to_clk_composite(hw); + const struct clk_ops *gate_ops = composite->gate_ops; + struct clk_hw *gate_hw = composite->gate_hw; + + gate_hw->clk = hw->clk; + + return gate_ops->enable(gate_hw); +} + +static void clk_composite_disable(struct clk_hw *hw) +{ + struct clk_composite *composite = to_clk_composite(hw); + const struct clk_ops *gate_ops = composite->gate_ops; + struct clk_hw *gate_hw = composite->gate_hw; + + gate_hw->clk = hw->clk; + + gate_ops->disable(gate_hw); +} + +struct clk *clk_register_composite(struct device *dev, const char *name, + const char **parent_names, int num_parents, + struct clk_hw *mux_hw, const struct clk_ops *mux_ops, + struct clk_hw *rate_hw, const struct clk_ops *rate_ops, + struct clk_hw *gate_hw, const struct clk_ops *gate_ops, + unsigned long flags) +{ + struct clk *clk; + struct clk_init_data init; + struct clk_composite *composite; + struct clk_ops *clk_composite_ops; + + composite = kzalloc(sizeof(*composite), GFP_KERNEL); + if (!composite) { + pr_err("%s: could not allocate composite clk\n", __func__); + return ERR_PTR(-ENOMEM); + } + + init.name = name; + init.flags = flags | CLK_IS_BASIC; + init.parent_names = parent_names; + init.num_parents = num_parents; + + clk_composite_ops = &composite->ops; + + if (mux_hw && mux_ops) { + if (!mux_ops->get_parent || !mux_ops->set_parent) { + clk = ERR_PTR(-EINVAL); + goto err; + } + + composite->mux_hw = mux_hw; + composite->mux_ops = mux_ops; + clk_composite_ops->get_parent = clk_composite_get_parent; + clk_composite_ops->set_parent = clk_composite_set_parent; + } + + if (rate_hw && rate_ops) { + if (!rate_ops->recalc_rate) { + clk = ERR_PTR(-EINVAL); + goto err; + } + + /* .round_rate is a prerequisite for .set_rate */ + if (rate_ops->round_rate) { + clk_composite_ops->round_rate = clk_composite_round_rate; + if (rate_ops->set_rate) { + clk_composite_ops->set_rate = clk_composite_set_rate; + } + } else { + WARN(rate_ops->set_rate, + "%s: missing round_rate op is required\n", + __func__); + } + + composite->rate_hw = rate_hw; + composite->rate_ops = rate_ops; + clk_composite_ops->recalc_rate = clk_composite_recalc_rate; + } + + if (gate_hw && gate_ops) { + if (!gate_ops->is_enabled || !gate_ops->enable || + !gate_ops->disable) { + clk = ERR_PTR(-EINVAL); + goto err; + } + + composite->gate_hw = gate_hw; + composite->gate_ops = gate_ops; + clk_composite_ops->is_enabled = clk_composite_is_enabled; + clk_composite_ops->enable = clk_composite_enable; + clk_composite_ops->disable = clk_composite_disable; + } + + init.ops = clk_composite_ops; + composite->hw.init = &init; + + clk = clk_register(dev, &composite->hw); + if (IS_ERR(clk)) + goto err; + + if (composite->mux_hw) + composite->mux_hw->clk = clk; + + if (composite->rate_hw) + composite->rate_hw->clk = clk; + + if (composite->gate_hw) + composite->gate_hw->clk = clk; + + return clk; + +err: + kfree(composite); + return clk; +} diff --git a/drivers/clk/clk-divider.c b/drivers/clk/clk-divider.c index 68b40210117..6d967416043 100644 --- a/drivers/clk/clk-divider.c +++ b/drivers/clk/clk-divider.c @@ -109,8 +109,9 @@ static unsigned long clk_divider_recalc_rate(struct clk_hw *hw, div = _get_div(divider, val); if (!div) { - WARN(1, "%s: Invalid divisor for clock %s\n", __func__, - __clk_get_name(hw->clk)); + WARN(!(divider->flags & CLK_DIVIDER_ALLOW_ZERO), + "%s: Zero divisor and CLK_DIVIDER_ALLOW_ZERO not set\n", + __clk_get_name(hw->clk)); return parent_rate; } diff --git a/drivers/clk/clk-fixed-factor.c b/drivers/clk/clk-fixed-factor.c index 1ef271e4759..9ff7d510faa 100644 --- a/drivers/clk/clk-fixed-factor.c +++ b/drivers/clk/clk-fixed-factor.c @@ -11,6 +11,7 @@ #include <linux/clk-provider.h> #include <linux/slab.h> #include <linux/err.h> +#include <linux/of.h> /* * DOC: basic fixed multiplier and divider clock that cannot gate @@ -96,3 +97,38 @@ struct clk *clk_register_fixed_factor(struct device *dev, const char *name, return clk; } +#ifdef CONFIG_OF +/** + * of_fixed_factor_clk_setup() - Setup function for simple fixed factor clock + */ +void __init of_fixed_factor_clk_setup(struct device_node *node) +{ + struct clk *clk; + const char *clk_name = node->name; + const char *parent_name; + u32 div, mult; + + if (of_property_read_u32(node, "clock-div", &div)) { + pr_err("%s Fixed factor clock <%s> must have a clock-div property\n", + __func__, node->name); + return; + } + + if (of_property_read_u32(node, "clock-mult", &mult)) { + pr_err("%s Fixed factor clock <%s> must have a clokc-mult property\n", + __func__, node->name); + return; + } + + of_property_read_string(node, "clock-output-names", &clk_name); + parent_name = of_clk_get_parent_name(node, 0); + + clk = clk_register_fixed_factor(NULL, clk_name, parent_name, 0, + mult, div); + if (!IS_ERR(clk)) + of_clk_add_provider(node, of_clk_src_simple_get, clk); +} +EXPORT_SYMBOL_GPL(of_fixed_factor_clk_setup); +CLK_OF_DECLARE(fixed_factor_clk, "fixed-factor-clock", + of_fixed_factor_clk_setup); +#endif diff --git a/drivers/clk/clk-mux.c b/drivers/clk/clk-mux.c index 508c032edce..25b1734560d 100644 --- a/drivers/clk/clk-mux.c +++ b/drivers/clk/clk-mux.c @@ -32,6 +32,7 @@ static u8 clk_mux_get_parent(struct clk_hw *hw) { struct clk_mux *mux = to_clk_mux(hw); + int num_parents = __clk_get_num_parents(hw->clk); u32 val; /* @@ -42,7 +43,16 @@ static u8 clk_mux_get_parent(struct clk_hw *hw) * val = 0x4 really means "bit 2, index starts at bit 0" */ val = readl(mux->reg) >> mux->shift; - val &= (1 << mux->width) - 1; + val &= mux->mask; + + if (mux->table) { + int i; + + for (i = 0; i < num_parents; i++) + if (mux->table[i] == val) + return i; + return -EINVAL; + } if (val && (mux->flags & CLK_MUX_INDEX_BIT)) val = ffs(val) - 1; @@ -50,7 +60,7 @@ static u8 clk_mux_get_parent(struct clk_hw *hw) if (val && (mux->flags & CLK_MUX_INDEX_ONE)) val--; - if (val >= __clk_get_num_parents(hw->clk)) + if (val >= num_parents) return -EINVAL; return val; @@ -62,17 +72,22 @@ static int clk_mux_set_parent(struct clk_hw *hw, u8 index) u32 val; unsigned long flags = 0; - if (mux->flags & CLK_MUX_INDEX_BIT) - index = (1 << ffs(index)); + if (mux->table) + index = mux->table[index]; - if (mux->flags & CLK_MUX_INDEX_ONE) - index++; + else { + if (mux->flags & CLK_MUX_INDEX_BIT) + index = (1 << ffs(index)); + + if (mux->flags & CLK_MUX_INDEX_ONE) + index++; + } if (mux->lock) spin_lock_irqsave(mux->lock, flags); val = readl(mux->reg); - val &= ~(((1 << mux->width) - 1) << mux->shift); + val &= ~(mux->mask << mux->shift); val |= index << mux->shift; writel(val, mux->reg); @@ -88,10 +103,10 @@ const struct clk_ops clk_mux_ops = { }; EXPORT_SYMBOL_GPL(clk_mux_ops); -struct clk *clk_register_mux(struct device *dev, const char *name, +struct clk *clk_register_mux_table(struct device *dev, const char *name, const char **parent_names, u8 num_parents, unsigned long flags, - void __iomem *reg, u8 shift, u8 width, - u8 clk_mux_flags, spinlock_t *lock) + void __iomem *reg, u8 shift, u32 mask, + u8 clk_mux_flags, u32 *table, spinlock_t *lock) { struct clk_mux *mux; struct clk *clk; @@ -113,9 +128,10 @@ struct clk *clk_register_mux(struct device *dev, const char *name, /* struct clk_mux assignments */ mux->reg = reg; mux->shift = shift; - mux->width = width; + mux->mask = mask; mux->flags = clk_mux_flags; mux->lock = lock; + mux->table = table; mux->hw.init = &init; clk = clk_register(dev, &mux->hw); @@ -125,3 +141,15 @@ struct clk *clk_register_mux(struct device *dev, const char *name, return clk; } + +struct clk *clk_register_mux(struct device *dev, const char *name, + const char **parent_names, u8 num_parents, unsigned long flags, + void __iomem *reg, u8 shift, u8 width, + u8 clk_mux_flags, spinlock_t *lock) +{ + u32 mask = BIT(width) - 1; + + return clk_register_mux_table(dev, name, parent_names, num_parents, + flags, reg, shift, mask, clk_mux_flags, + NULL, lock); +} diff --git a/drivers/clk/clk-prima2.c b/drivers/clk/clk-prima2.c index f8e9d0c27be..643ca653fef 100644 --- a/drivers/clk/clk-prima2.c +++ b/drivers/clk/clk-prima2.c @@ -1113,7 +1113,7 @@ void __init sirfsoc_of_clk_init(void) for (i = pll1; i < maxclk; i++) { prima2_clks[i] = clk_register(NULL, prima2_clk_hw_array[i]); - BUG_ON(!prima2_clks[i]); + BUG_ON(IS_ERR(prima2_clks[i])); } clk_register_clkdev(prima2_clks[cpu], NULL, "cpu"); clk_register_clkdev(prima2_clks[io], NULL, "io"); diff --git a/drivers/clk/clk-si5351.c b/drivers/clk/clk-si5351.c new file mode 100644 index 00000000000..892728412e9 --- /dev/null +++ b/drivers/clk/clk-si5351.c @@ -0,0 +1,1510 @@ +/* + * clk-si5351.c: Silicon Laboratories Si5351A/B/C I2C Clock Generator + * + * Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com> + * Rabeeh Khoury <rabeeh@solid-run.com> + * + * References: + * [1] "Si5351A/B/C Data Sheet" + * http://www.silabs.com/Support%20Documents/TechnicalDocs/Si5351.pdf + * [2] "Manually Generating an Si5351 Register Map" + * http://www.silabs.com/Support%20Documents/TechnicalDocs/AN619.pdf + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/clkdev.h> +#include <linux/clk-provider.h> +#include <linux/delay.h> +#include <linux/err.h> +#include <linux/errno.h> +#include <linux/rational.h> +#include <linux/i2c.h> +#include <linux/of_platform.h> +#include <linux/platform_data/si5351.h> +#include <linux/regmap.h> +#include <linux/slab.h> +#include <linux/string.h> +#include <asm/div64.h> + +#include "clk-si5351.h" + +struct si5351_driver_data; + +struct si5351_parameters { + unsigned long p1; + unsigned long p2; + unsigned long p3; + int valid; +}; + +struct si5351_hw_data { + struct clk_hw hw; + struct si5351_driver_data *drvdata; + struct si5351_parameters params; + unsigned char num; +}; + +struct si5351_driver_data { + enum si5351_variant variant; + struct i2c_client *client; + struct regmap *regmap; + struct clk_onecell_data onecell; + + struct clk *pxtal; + const char *pxtal_name; + struct clk_hw xtal; + struct clk *pclkin; + const char *pclkin_name; + struct clk_hw clkin; + + struct si5351_hw_data pll[2]; + struct si5351_hw_data *msynth; + struct si5351_hw_data *clkout; +}; + +static const char const *si5351_input_names[] = { + "xtal", "clkin" +}; +static const char const *si5351_pll_names[] = { + "plla", "pllb", "vxco" +}; +static const char const *si5351_msynth_names[] = { + "ms0", "ms1", "ms2", "ms3", "ms4", "ms5", "ms6", "ms7" +}; +static const char const *si5351_clkout_names[] = { + "clk0", "clk1", "clk2", "clk3", "clk4", "clk5", "clk6", "clk7" +}; + +/* + * Si5351 i2c regmap + */ +static inline u8 si5351_reg_read(struct si5351_driver_data *drvdata, u8 reg) +{ + u32 val; + int ret; + + ret = regmap_read(drvdata->regmap, reg, &val); + if (ret) { + dev_err(&drvdata->client->dev, + "unable to read from reg%02x\n", reg); + return 0; + } + + return (u8)val; +} + +static inline int si5351_bulk_read(struct si5351_driver_data *drvdata, + u8 reg, u8 count, u8 *buf) +{ + return regmap_bulk_read(drvdata->regmap, reg, buf, count); +} + +static inline int si5351_reg_write(struct si5351_driver_data *drvdata, + u8 reg, u8 val) +{ + return regmap_write(drvdata->regmap, reg, val); +} + +static inline int si5351_bulk_write(struct si5351_driver_data *drvdata, + u8 reg, u8 count, const u8 *buf) +{ + return regmap_raw_write(drvdata->regmap, reg, buf, count); +} + +static inline int si5351_set_bits(struct si5351_driver_data *drvdata, + u8 reg, u8 mask, u8 val) +{ + return regmap_update_bits(drvdata->regmap, reg, mask, val); +} + +static inline u8 si5351_msynth_params_address(int num) +{ + if (num > 5) + return SI5351_CLK6_PARAMETERS + (num - 6); + return SI5351_CLK0_PARAMETERS + (SI5351_PARAMETERS_LENGTH * num); +} + +static void si5351_read_parameters(struct si5351_driver_data *drvdata, + u8 reg, struct si5351_parameters *params) +{ + u8 buf[SI5351_PARAMETERS_LENGTH]; + + switch (reg) { + case SI5351_CLK6_PARAMETERS: + case SI5351_CLK7_PARAMETERS: + buf[0] = si5351_reg_read(drvdata, reg); + params->p1 = buf[0]; + params->p2 = 0; + params->p3 = 1; + break; + default: + si5351_bulk_read(drvdata, reg, SI5351_PARAMETERS_LENGTH, buf); + params->p1 = ((buf[2] & 0x03) << 16) | (buf[3] << 8) | buf[4]; + params->p2 = ((buf[5] & 0x0f) << 16) | (buf[6] << 8) | buf[7]; + params->p3 = ((buf[5] & 0xf0) << 12) | (buf[0] << 8) | buf[1]; + } + params->valid = 1; +} + +static void si5351_write_parameters(struct si5351_driver_data *drvdata, + u8 reg, struct si5351_parameters *params) +{ + u8 buf[SI5351_PARAMETERS_LENGTH]; + + switch (reg) { + case SI5351_CLK6_PARAMETERS: + case SI5351_CLK7_PARAMETERS: + buf[0] = params->p1 & 0xff; + si5351_reg_write(drvdata, reg, buf[0]); + break; + default: + buf[0] = ((params->p3 & 0x0ff00) >> 8) & 0xff; + buf[1] = params->p3 & 0xff; + /* save rdiv and divby4 */ + buf[2] = si5351_reg_read(drvdata, reg + 2) & ~0x03; + buf[2] |= ((params->p1 & 0x30000) >> 16) & 0x03; + buf[3] = ((params->p1 & 0x0ff00) >> 8) & 0xff; + buf[4] = params->p1 & 0xff; + buf[5] = ((params->p3 & 0xf0000) >> 12) | + ((params->p2 & 0xf0000) >> 16); + buf[6] = ((params->p2 & 0x0ff00) >> 8) & 0xff; + buf[7] = params->p2 & 0xff; + si5351_bulk_write(drvdata, reg, SI5351_PARAMETERS_LENGTH, buf); + } +} + +static bool si5351_regmap_is_volatile(struct device *dev, unsigned int reg) +{ + switch (reg) { + case SI5351_DEVICE_STATUS: + case SI5351_INTERRUPT_STATUS: + case SI5351_PLL_RESET: + return true; + } + return false; +} + +static bool si5351_regmap_is_writeable(struct device *dev, unsigned int reg) +{ + /* reserved registers */ + if (reg >= 4 && reg <= 8) + return false; + if (reg >= 10 && reg <= 14) + return false; + if (reg >= 173 && reg <= 176) + return false; + if (reg >= 178 && reg <= 182) + return false; + /* read-only */ + if (reg == SI5351_DEVICE_STATUS) + return false; + return true; +} + +static struct regmap_config si5351_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .cache_type = REGCACHE_RBTREE, + .max_register = 187, + .writeable_reg = si5351_regmap_is_writeable, + .volatile_reg = si5351_regmap_is_volatile, +}; + +/* + * Si5351 xtal clock input + */ +static int si5351_xtal_prepare(struct clk_hw *hw) +{ + struct si5351_driver_data *drvdata = + container_of(hw, struct si5351_driver_data, xtal); + si5351_set_bits(drvdata, SI5351_FANOUT_ENABLE, + SI5351_XTAL_ENABLE, SI5351_XTAL_ENABLE); + return 0; +} + +static void si5351_xtal_unprepare(struct clk_hw *hw) +{ + struct si5351_driver_data *drvdata = + container_of(hw, struct si5351_driver_data, xtal); + si5351_set_bits(drvdata, SI5351_FANOUT_ENABLE, + SI5351_XTAL_ENABLE, 0); +} + +static const struct clk_ops si5351_xtal_ops = { + .prepare = si5351_xtal_prepare, + .unprepare = si5351_xtal_unprepare, +}; + +/* + * Si5351 clkin clock input (Si5351C only) + */ +static int si5351_clkin_prepare(struct clk_hw *hw) +{ + struct si5351_driver_data *drvdata = + container_of(hw, struct si5351_driver_data, clkin); + si5351_set_bits(drvdata, SI5351_FANOUT_ENABLE, + SI5351_CLKIN_ENABLE, SI5351_CLKIN_ENABLE); + return 0; +} + +static void si5351_clkin_unprepare(struct clk_hw *hw) +{ + struct si5351_driver_data *drvdata = + container_of(hw, struct si5351_driver_data, clkin); + si5351_set_bits(drvdata, SI5351_FANOUT_ENABLE, + SI5351_CLKIN_ENABLE, 0); +} + +/* + * CMOS clock source constraints: + * The input frequency range of the PLL is 10Mhz to 40MHz. + * If CLKIN is >40MHz, the input divider must be used. + */ +static unsigned long si5351_clkin_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct si5351_driver_data *drvdata = + container_of(hw, struct si5351_driver_data, clkin); + unsigned long rate; + unsigned char idiv; + + rate = parent_rate; + if (parent_rate > 160000000) { + idiv = SI5351_CLKIN_DIV_8; + rate /= 8; + } else if (parent_rate > 80000000) { + idiv = SI5351_CLKIN_DIV_4; + rate /= 4; + } else if (parent_rate > 40000000) { + idiv = SI5351_CLKIN_DIV_2; + rate /= 2; + } else { + idiv = SI5351_CLKIN_DIV_1; + } + + si5351_set_bits(drvdata, SI5351_PLL_INPUT_SOURCE, + SI5351_CLKIN_DIV_MASK, idiv); + + dev_dbg(&drvdata->client->dev, "%s - clkin div = %d, rate = %lu\n", + __func__, (1 << (idiv >> 6)), rate); + + return rate; +} + +static const struct clk_ops si5351_clkin_ops = { + .prepare = si5351_clkin_prepare, + .unprepare = si5351_clkin_unprepare, + .recalc_rate = si5351_clkin_recalc_rate, +}; + +/* + * Si5351 vxco clock input (Si5351B only) + */ + +static int si5351_vxco_prepare(struct clk_hw *hw) +{ + struct si5351_hw_data *hwdata = + container_of(hw, struct si5351_hw_data, hw); + + dev_warn(&hwdata->drvdata->client->dev, "VXCO currently unsupported\n"); + + return 0; +} + +static void si5351_vxco_unprepare(struct clk_hw *hw) +{ +} + +static unsigned long si5351_vxco_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + return 0; +} + +static int si5351_vxco_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent) +{ + return 0; +} + +static const struct clk_ops si5351_vxco_ops = { + .prepare = si5351_vxco_prepare, + .unprepare = si5351_vxco_unprepare, + .recalc_rate = si5351_vxco_recalc_rate, + .set_rate = si5351_vxco_set_rate, +}; + +/* + * Si5351 pll a/b + * + * Feedback Multisynth Divider Equations [2] + * + * fVCO = fIN * (a + b/c) + * + * with 15 + 0/1048575 <= (a + b/c) <= 90 + 0/1048575 and + * fIN = fXTAL or fIN = fCLKIN/CLKIN_DIV + * + * Feedback Multisynth Register Equations + * + * (1) MSNx_P1[17:0] = 128 * a + floor(128 * b/c) - 512 + * (2) MSNx_P2[19:0] = 128 * b - c * floor(128 * b/c) = (128*b) mod c + * (3) MSNx_P3[19:0] = c + * + * Transposing (2) yields: (4) floor(128 * b/c) = (128 * b / MSNx_P2)/c + * + * Using (4) on (1) yields: + * MSNx_P1 = 128 * a + (128 * b/MSNx_P2)/c - 512 + * MSNx_P1 + 512 + MSNx_P2/c = 128 * a + 128 * b/c + * + * a + b/c = (MSNx_P1 + MSNx_P2/MSNx_P3 + 512)/128 + * = (MSNx_P1*MSNx_P3 + MSNx_P2 + 512*MSNx_P3)/(128*MSNx_P3) + * + */ +static int _si5351_pll_reparent(struct si5351_driver_data *drvdata, + int num, enum si5351_pll_src parent) +{ + u8 mask = (num == 0) ? SI5351_PLLA_SOURCE : SI5351_PLLB_SOURCE; + + if (parent == SI5351_PLL_SRC_DEFAULT) + return 0; + + if (num > 2) + return -EINVAL; + + if (drvdata->variant != SI5351_VARIANT_C && + parent != SI5351_PLL_SRC_XTAL) + return -EINVAL; + + si5351_set_bits(drvdata, SI5351_PLL_INPUT_SOURCE, mask, + (parent == SI5351_PLL_SRC_XTAL) ? 0 : mask); + return 0; +} + +static unsigned char si5351_pll_get_parent(struct clk_hw *hw) +{ + struct si5351_hw_data *hwdata = + container_of(hw, struct si5351_hw_data, hw); + u8 mask = (hwdata->num == 0) ? SI5351_PLLA_SOURCE : SI5351_PLLB_SOURCE; + u8 val; + + val = si5351_reg_read(hwdata->drvdata, SI5351_PLL_INPUT_SOURCE); + + return (val & mask) ? 1 : 0; +} + +static int si5351_pll_set_parent(struct clk_hw *hw, u8 index) +{ + struct si5351_hw_data *hwdata = + container_of(hw, struct si5351_hw_data, hw); + + if (hwdata->drvdata->variant != SI5351_VARIANT_C && + index > 0) + return -EPERM; + + if (index > 1) + return -EINVAL; + + return _si5351_pll_reparent(hwdata->drvdata, hwdata->num, + (index == 0) ? SI5351_PLL_SRC_XTAL : + SI5351_PLL_SRC_CLKIN); +} + +static unsigned long si5351_pll_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct si5351_hw_data *hwdata = + container_of(hw, struct si5351_hw_data, hw); + u8 reg = (hwdata->num == 0) ? SI5351_PLLA_PARAMETERS : + SI5351_PLLB_PARAMETERS; + unsigned long long rate; + + if (!hwdata->params.valid) + si5351_read_parameters(hwdata->drvdata, reg, &hwdata->params); + + if (hwdata->params.p3 == 0) + return parent_rate; + + /* fVCO = fIN * (P1*P3 + 512*P3 + P2)/(128*P3) */ + rate = hwdata->params.p1 * hwdata->params.p3; + rate += 512 * hwdata->params.p3; + rate += hwdata->params.p2; + rate *= parent_rate; + do_div(rate, 128 * hwdata->params.p3); + + dev_dbg(&hwdata->drvdata->client->dev, + "%s - %s: p1 = %lu, p2 = %lu, p3 = %lu, parent_rate = %lu, rate = %lu\n", + __func__, __clk_get_name(hwdata->hw.clk), + hwdata->params.p1, hwdata->params.p2, hwdata->params.p3, + parent_rate, (unsigned long)rate); + + return (unsigned long)rate; +} + +static long si5351_pll_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *parent_rate) +{ + struct si5351_hw_data *hwdata = + container_of(hw, struct si5351_hw_data, hw); + unsigned long rfrac, denom, a, b, c; + unsigned long long lltmp; + + if (rate < SI5351_PLL_VCO_MIN) + rate = SI5351_PLL_VCO_MIN; + if (rate > SI5351_PLL_VCO_MAX) + rate = SI5351_PLL_VCO_MAX; + + /* determine integer part of feedback equation */ + a = rate / *parent_rate; + + if (a < SI5351_PLL_A_MIN) + rate = *parent_rate * SI5351_PLL_A_MIN; + if (a > SI5351_PLL_A_MAX) + rate = *parent_rate * SI5351_PLL_A_MAX; + + /* find best approximation for b/c = fVCO mod fIN */ + denom = 1000 * 1000; + lltmp = rate % (*parent_rate); + lltmp *= denom; + do_div(lltmp, *parent_rate); + rfrac = (unsigned long)lltmp; + + b = 0; + c = 1; + if (rfrac) + rational_best_approximation(rfrac, denom, + SI5351_PLL_B_MAX, SI5351_PLL_C_MAX, &b, &c); + + /* calculate parameters */ + hwdata->params.p3 = c; + hwdata->params.p2 = (128 * b) % c; + hwdata->params.p1 = 128 * a; + hwdata->params.p1 += (128 * b / c); + hwdata->params.p1 -= 512; + + /* recalculate rate by fIN * (a + b/c) */ + lltmp = *parent_rate; + lltmp *= b; + do_div(lltmp, c); + + rate = (unsigned long)lltmp; + rate += *parent_rate * a; + + dev_dbg(&hwdata->drvdata->client->dev, + "%s - %s: a = %lu, b = %lu, c = %lu, parent_rate = %lu, rate = %lu\n", + __func__, __clk_get_name(hwdata->hw.clk), a, b, c, + *parent_rate, rate); + + return rate; +} + +static int si5351_pll_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct si5351_hw_data *hwdata = + container_of(hw, struct si5351_hw_data, hw); + u8 reg = (hwdata->num == 0) ? SI5351_PLLA_PARAMETERS : + SI5351_PLLB_PARAMETERS; + + /* write multisynth parameters */ + si5351_write_parameters(hwdata->drvdata, reg, &hwdata->params); + + /* plla/pllb ctrl is in clk6/clk7 ctrl registers */ + si5351_set_bits(hwdata->drvdata, SI5351_CLK6_CTRL + hwdata->num, + SI5351_CLK_INTEGER_MODE, + (hwdata->params.p2 == 0) ? SI5351_CLK_INTEGER_MODE : 0); + + dev_dbg(&hwdata->drvdata->client->dev, + "%s - %s: p1 = %lu, p2 = %lu, p3 = %lu, parent_rate = %lu, rate = %lu\n", + __func__, __clk_get_name(hwdata->hw.clk), + hwdata->params.p1, hwdata->params.p2, hwdata->params.p3, + parent_rate, rate); + + return 0; +} + +static const struct clk_ops si5351_pll_ops = { + .set_parent = si5351_pll_set_parent, + .get_parent = si5351_pll_get_parent, + .recalc_rate = si5351_pll_recalc_rate, + .round_rate = si5351_pll_round_rate, + .set_rate = si5351_pll_set_rate, +}; + +/* + * Si5351 multisync divider + * + * for fOUT <= 150 MHz: + * + * fOUT = (fIN * (a + b/c)) / CLKOUTDIV + * + * with 6 + 0/1048575 <= (a + b/c) <= 1800 + 0/1048575 and + * fIN = fVCO0, fVCO1 + * + * Output Clock Multisynth Register Equations + * + * MSx_P1[17:0] = 128 * a + floor(128 * b/c) - 512 + * MSx_P2[19:0] = 128 * b - c * floor(128 * b/c) = (128*b) mod c + * MSx_P3[19:0] = c + * + * MS[6,7] are integer (P1) divide only, P2 = 0, P3 = 0 + * + * for 150MHz < fOUT <= 160MHz: + * + * MSx_P1 = 0, MSx_P2 = 0, MSx_P3 = 1, MSx_INT = 1, MSx_DIVBY4 = 11b + */ +static int _si5351_msynth_reparent(struct si5351_driver_data *drvdata, + int num, enum si5351_multisynth_src parent) +{ + if (parent == SI5351_MULTISYNTH_SRC_DEFAULT) + return 0; + + if (num > 8) + return -EINVAL; + + si5351_set_bits(drvdata, SI5351_CLK0_CTRL + num, SI5351_CLK_PLL_SELECT, + (parent == SI5351_MULTISYNTH_SRC_VCO0) ? 0 : + SI5351_CLK_PLL_SELECT); + return 0; +} + +static unsigned char si5351_msynth_get_parent(struct clk_hw *hw) +{ + struct si5351_hw_data *hwdata = + container_of(hw, struct si5351_hw_data, hw); + u8 val; + + val = si5351_reg_read(hwdata->drvdata, SI5351_CLK0_CTRL + hwdata->num); + + return (val & SI5351_CLK_PLL_SELECT) ? 1 : 0; +} + +static int si5351_msynth_set_parent(struct clk_hw *hw, u8 index) +{ + struct si5351_hw_data *hwdata = + container_of(hw, struct si5351_hw_data, hw); + + return _si5351_msynth_reparent(hwdata->drvdata, hwdata->num, + (index == 0) ? SI5351_MULTISYNTH_SRC_VCO0 : + SI5351_MULTISYNTH_SRC_VCO1); +} + +static unsigned long si5351_msynth_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct si5351_hw_data *hwdata = + container_of(hw, struct si5351_hw_data, hw); + u8 reg = si5351_msynth_params_address(hwdata->num); + unsigned long long rate; + unsigned long m; + + if (!hwdata->params.valid) + si5351_read_parameters(hwdata->drvdata, reg, &hwdata->params); + + if (hwdata->params.p3 == 0) + return parent_rate; + + /* + * multisync0-5: fOUT = (128 * P3 * fIN) / (P1*P3 + P2 + 512*P3) + * multisync6-7: fOUT = fIN / P1 + */ + rate = parent_rate; + if (hwdata->num > 5) { + m = hwdata->params.p1; + } else if ((si5351_reg_read(hwdata->drvdata, reg + 2) & + SI5351_OUTPUT_CLK_DIVBY4) == SI5351_OUTPUT_CLK_DIVBY4) { + m = 4; + } else { + rate *= 128 * hwdata->params.p3; + m = hwdata->params.p1 * hwdata->params.p3; + m += hwdata->params.p2; + m += 512 * hwdata->params.p3; + } + + if (m == 0) + return 0; + do_div(rate, m); + + dev_dbg(&hwdata->drvdata->client->dev, + "%s - %s: p1 = %lu, p2 = %lu, p3 = %lu, m = %lu, parent_rate = %lu, rate = %lu\n", + __func__, __clk_get_name(hwdata->hw.clk), + hwdata->params.p1, hwdata->params.p2, hwdata->params.p3, + m, parent_rate, (unsigned long)rate); + + return (unsigned long)rate; +} + +static long si5351_msynth_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *parent_rate) +{ + struct si5351_hw_data *hwdata = + container_of(hw, struct si5351_hw_data, hw); + unsigned long long lltmp; + unsigned long a, b, c; + int divby4; + + /* multisync6-7 can only handle freqencies < 150MHz */ + if (hwdata->num >= 6 && rate > SI5351_MULTISYNTH67_MAX_FREQ) + rate = SI5351_MULTISYNTH67_MAX_FREQ; + + /* multisync frequency is 1MHz .. 160MHz */ + if (rate > SI5351_MULTISYNTH_MAX_FREQ) + rate = SI5351_MULTISYNTH_MAX_FREQ; + if (rate < SI5351_MULTISYNTH_MIN_FREQ) + rate = SI5351_MULTISYNTH_MIN_FREQ; + + divby4 = 0; + if (rate > SI5351_MULTISYNTH_DIVBY4_FREQ) + divby4 = 1; + + /* multisync can set pll */ + if (__clk_get_flags(hwdata->hw.clk) & CLK_SET_RATE_PARENT) { + /* + * find largest integer divider for max + * vco frequency and given target rate + */ + if (divby4 == 0) { + lltmp = SI5351_PLL_VCO_MAX; + do_div(lltmp, rate); + a = (unsigned long)lltmp; + } else + a = 4; + + b = 0; + c = 1; + + *parent_rate = a * rate; + } else { + unsigned long rfrac, denom; + + /* disable divby4 */ + if (divby4) { + rate = SI5351_MULTISYNTH_DIVBY4_FREQ; + divby4 = 0; + } + + /* determine integer part of divider equation */ + a = *parent_rate / rate; + if (a < SI5351_MULTISYNTH_A_MIN) + a = SI5351_MULTISYNTH_A_MIN; + if (hwdata->num >= 6 && a > SI5351_MULTISYNTH67_A_MAX) + a = SI5351_MULTISYNTH67_A_MAX; + else if (a > SI5351_MULTISYNTH_A_MAX) + a = SI5351_MULTISYNTH_A_MAX; + + /* find best approximation for b/c = fVCO mod fOUT */ + denom = 1000 * 1000; + lltmp = (*parent_rate) % rate; + lltmp *= denom; + do_div(lltmp, rate); + rfrac = (unsigned long)lltmp; + + b = 0; + c = 1; + if (rfrac) + rational_best_approximation(rfrac, denom, + SI5351_MULTISYNTH_B_MAX, SI5351_MULTISYNTH_C_MAX, + &b, &c); + } + + /* recalculate rate by fOUT = fIN / (a + b/c) */ + lltmp = *parent_rate; + lltmp *= c; + do_div(lltmp, a * c + b); + rate = (unsigned long)lltmp; + + /* calculate parameters */ + if (divby4) { + hwdata->params.p3 = 1; + hwdata->params.p2 = 0; + hwdata->params.p1 = 0; + } else { + hwdata->params.p3 = c; + hwdata->params.p2 = (128 * b) % c; + hwdata->params.p1 = 128 * a; + hwdata->params.p1 += (128 * b / c); + hwdata->params.p1 -= 512; + } + + dev_dbg(&hwdata->drvdata->client->dev, + "%s - %s: a = %lu, b = %lu, c = %lu, divby4 = %d, parent_rate = %lu, rate = %lu\n", + __func__, __clk_get_name(hwdata->hw.clk), a, b, c, divby4, + *parent_rate, rate); + + return rate; +} + +static int si5351_msynth_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct si5351_hw_data *hwdata = + container_of(hw, struct si5351_hw_data, hw); + u8 reg = si5351_msynth_params_address(hwdata->num); + int divby4 = 0; + + /* write multisynth parameters */ + si5351_write_parameters(hwdata->drvdata, reg, &hwdata->params); + + if (rate > SI5351_MULTISYNTH_DIVBY4_FREQ) + divby4 = 1; + + /* enable/disable integer mode and divby4 on multisynth0-5 */ + if (hwdata->num < 6) { + si5351_set_bits(hwdata->drvdata, reg + 2, + SI5351_OUTPUT_CLK_DIVBY4, + (divby4) ? SI5351_OUTPUT_CLK_DIVBY4 : 0); + si5351_set_bits(hwdata->drvdata, SI5351_CLK0_CTRL + hwdata->num, + SI5351_CLK_INTEGER_MODE, + (hwdata->params.p2 == 0) ? SI5351_CLK_INTEGER_MODE : 0); + } + + dev_dbg(&hwdata->drvdata->client->dev, + "%s - %s: p1 = %lu, p2 = %lu, p3 = %lu, divby4 = %d, parent_rate = %lu, rate = %lu\n", + __func__, __clk_get_name(hwdata->hw.clk), + hwdata->params.p1, hwdata->params.p2, hwdata->params.p3, + divby4, parent_rate, rate); + + return 0; +} + +static const struct clk_ops si5351_msynth_ops = { + .set_parent = si5351_msynth_set_parent, + .get_parent = si5351_msynth_get_parent, + .recalc_rate = si5351_msynth_recalc_rate, + .round_rate = si5351_msynth_round_rate, + .set_rate = si5351_msynth_set_rate, +}; + +/* + * Si5351 clkout divider + */ +static int _si5351_clkout_reparent(struct si5351_driver_data *drvdata, + int num, enum si5351_clkout_src parent) +{ + u8 val; + + if (num > 8) + return -EINVAL; + + switch (parent) { + case SI5351_CLKOUT_SRC_MSYNTH_N: + val = SI5351_CLK_INPUT_MULTISYNTH_N; + break; + case SI5351_CLKOUT_SRC_MSYNTH_0_4: + /* clk0/clk4 can only connect to its own multisync */ + if (num == 0 || num == 4) + val = SI5351_CLK_INPUT_MULTISYNTH_N; + else + val = SI5351_CLK_INPUT_MULTISYNTH_0_4; + break; + case SI5351_CLKOUT_SRC_XTAL: + val = SI5351_CLK_INPUT_XTAL; + break; + case SI5351_CLKOUT_SRC_CLKIN: + if (drvdata->variant != SI5351_VARIANT_C) + return -EINVAL; + + val = SI5351_CLK_INPUT_CLKIN; + break; + default: + return 0; + } + + si5351_set_bits(drvdata, SI5351_CLK0_CTRL + num, + SI5351_CLK_INPUT_MASK, val); + return 0; +} + +static int _si5351_clkout_set_drive_strength( + struct si5351_driver_data *drvdata, int num, + enum si5351_drive_strength drive) +{ + u8 mask; + + if (num > 8) + return -EINVAL; + + switch (drive) { + case SI5351_DRIVE_2MA: + mask = SI5351_CLK_DRIVE_STRENGTH_2MA; + break; + case SI5351_DRIVE_4MA: + mask = SI5351_CLK_DRIVE_STRENGTH_4MA; + break; + case SI5351_DRIVE_6MA: + mask = SI5351_CLK_DRIVE_STRENGTH_6MA; + break; + case SI5351_DRIVE_8MA: + mask = SI5351_CLK_DRIVE_STRENGTH_8MA; + break; + default: + return 0; + } + + si5351_set_bits(drvdata, SI5351_CLK0_CTRL + num, + SI5351_CLK_DRIVE_STRENGTH_MASK, mask); + return 0; +} + +static int si5351_clkout_prepare(struct clk_hw *hw) +{ + struct si5351_hw_data *hwdata = + container_of(hw, struct si5351_hw_data, hw); + + si5351_set_bits(hwdata->drvdata, SI5351_CLK0_CTRL + hwdata->num, + SI5351_CLK_POWERDOWN, 0); + si5351_set_bits(hwdata->drvdata, SI5351_OUTPUT_ENABLE_CTRL, + (1 << hwdata->num), 0); + return 0; +} + +static void si5351_clkout_unprepare(struct clk_hw *hw) +{ + struct si5351_hw_data *hwdata = + container_of(hw, struct si5351_hw_data, hw); + + si5351_set_bits(hwdata->drvdata, SI5351_CLK0_CTRL + hwdata->num, + SI5351_CLK_POWERDOWN, SI5351_CLK_POWERDOWN); + si5351_set_bits(hwdata->drvdata, SI5351_OUTPUT_ENABLE_CTRL, + (1 << hwdata->num), (1 << hwdata->num)); +} + +static u8 si5351_clkout_get_parent(struct clk_hw *hw) +{ + struct si5351_hw_data *hwdata = + container_of(hw, struct si5351_hw_data, hw); + int index = 0; + unsigned char val; + + val = si5351_reg_read(hwdata->drvdata, SI5351_CLK0_CTRL + hwdata->num); + switch (val & SI5351_CLK_INPUT_MASK) { + case SI5351_CLK_INPUT_MULTISYNTH_N: + index = 0; + break; + case SI5351_CLK_INPUT_MULTISYNTH_0_4: + index = 1; + break; + case SI5351_CLK_INPUT_XTAL: + index = 2; + break; + case SI5351_CLK_INPUT_CLKIN: + index = 3; + break; + } + + return index; +} + +static int si5351_clkout_set_parent(struct clk_hw *hw, u8 index) +{ + struct si5351_hw_data *hwdata = + container_of(hw, struct si5351_hw_data, hw); + enum si5351_clkout_src parent = SI5351_CLKOUT_SRC_DEFAULT; + + switch (index) { + case 0: + parent = SI5351_CLKOUT_SRC_MSYNTH_N; + break; + case 1: + parent = SI5351_CLKOUT_SRC_MSYNTH_0_4; + break; + case 2: + parent = SI5351_CLKOUT_SRC_XTAL; + break; + case 3: + parent = SI5351_CLKOUT_SRC_CLKIN; + break; + } + + return _si5351_clkout_reparent(hwdata->drvdata, hwdata->num, parent); +} + +static unsigned long si5351_clkout_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct si5351_hw_data *hwdata = + container_of(hw, struct si5351_hw_data, hw); + unsigned char reg; + unsigned char rdiv; + + if (hwdata->num > 5) + reg = si5351_msynth_params_address(hwdata->num) + 2; + else + reg = SI5351_CLK6_7_OUTPUT_DIVIDER; + + rdiv = si5351_reg_read(hwdata->drvdata, reg); + if (hwdata->num == 6) { + rdiv &= SI5351_OUTPUT_CLK6_DIV_MASK; + } else { + rdiv &= SI5351_OUTPUT_CLK_DIV_MASK; + rdiv >>= SI5351_OUTPUT_CLK_DIV_SHIFT; + } + + return parent_rate >> rdiv; +} + +static long si5351_clkout_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *parent_rate) +{ + struct si5351_hw_data *hwdata = + container_of(hw, struct si5351_hw_data, hw); + unsigned char rdiv; + + /* clkout6/7 can only handle output freqencies < 150MHz */ + if (hwdata->num >= 6 && rate > SI5351_CLKOUT67_MAX_FREQ) + rate = SI5351_CLKOUT67_MAX_FREQ; + + /* clkout freqency is 8kHz - 160MHz */ + if (rate > SI5351_CLKOUT_MAX_FREQ) + rate = SI5351_CLKOUT_MAX_FREQ; + if (rate < SI5351_CLKOUT_MIN_FREQ) + rate = SI5351_CLKOUT_MIN_FREQ; + + /* request frequency if multisync master */ + if (__clk_get_flags(hwdata->hw.clk) & CLK_SET_RATE_PARENT) { + /* use r divider for frequencies below 1MHz */ + rdiv = SI5351_OUTPUT_CLK_DIV_1; + while (rate < SI5351_MULTISYNTH_MIN_FREQ && + rdiv < SI5351_OUTPUT_CLK_DIV_128) { + rdiv += 1; + rate *= 2; + } + *parent_rate = rate; + } else { + unsigned long new_rate, new_err, err; + + /* round to closed rdiv */ + rdiv = SI5351_OUTPUT_CLK_DIV_1; + new_rate = *parent_rate; + err = abs(new_rate - rate); + do { + new_rate >>= 1; + new_err = abs(new_rate - rate); + if (new_err > err || rdiv == SI5351_OUTPUT_CLK_DIV_128) + break; + rdiv++; + err = new_err; + } while (1); + } + rate = *parent_rate >> rdiv; + + dev_dbg(&hwdata->drvdata->client->dev, + "%s - %s: rdiv = %u, parent_rate = %lu, rate = %lu\n", + __func__, __clk_get_name(hwdata->hw.clk), (1 << rdiv), + *parent_rate, rate); + + return rate; +} + +static int si5351_clkout_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct si5351_hw_data *hwdata = + container_of(hw, struct si5351_hw_data, hw); + unsigned long new_rate, new_err, err; + unsigned char rdiv; + + /* round to closed rdiv */ + rdiv = SI5351_OUTPUT_CLK_DIV_1; + new_rate = parent_rate; + err = abs(new_rate - rate); + do { + new_rate >>= 1; + new_err = abs(new_rate - rate); + if (new_err > err || rdiv == SI5351_OUTPUT_CLK_DIV_128) + break; + rdiv++; + err = new_err; + } while (1); + + /* write output divider */ + switch (hwdata->num) { + case 6: + si5351_set_bits(hwdata->drvdata, SI5351_CLK6_7_OUTPUT_DIVIDER, + SI5351_OUTPUT_CLK6_DIV_MASK, rdiv); + break; + case 7: + si5351_set_bits(hwdata->drvdata, SI5351_CLK6_7_OUTPUT_DIVIDER, + SI5351_OUTPUT_CLK_DIV_MASK, + rdiv << SI5351_OUTPUT_CLK_DIV_SHIFT); + break; + default: + si5351_set_bits(hwdata->drvdata, + si5351_msynth_params_address(hwdata->num) + 2, + SI5351_OUTPUT_CLK_DIV_MASK, + rdiv << SI5351_OUTPUT_CLK_DIV_SHIFT); + } + + /* powerup clkout */ + si5351_set_bits(hwdata->drvdata, SI5351_CLK0_CTRL + hwdata->num, + SI5351_CLK_POWERDOWN, 0); + + dev_dbg(&hwdata->drvdata->client->dev, + "%s - %s: rdiv = %u, parent_rate = %lu, rate = %lu\n", + __func__, __clk_get_name(hwdata->hw.clk), (1 << rdiv), + parent_rate, rate); + + return 0; +} + +static const struct clk_ops si5351_clkout_ops = { + .prepare = si5351_clkout_prepare, + .unprepare = si5351_clkout_unprepare, + .set_parent = si5351_clkout_set_parent, + .get_parent = si5351_clkout_get_parent, + .recalc_rate = si5351_clkout_recalc_rate, + .round_rate = si5351_clkout_round_rate, + .set_rate = si5351_clkout_set_rate, +}; + +/* + * Si5351 i2c probe and DT + */ +#ifdef CONFIG_OF +static const struct of_device_id si5351_dt_ids[] = { + { .compatible = "silabs,si5351a", .data = (void *)SI5351_VARIANT_A, }, + { .compatible = "silabs,si5351a-msop", + .data = (void *)SI5351_VARIANT_A3, }, + { .compatible = "silabs,si5351b", .data = (void *)SI5351_VARIANT_B, }, + { .compatible = "silabs,si5351c", .data = (void *)SI5351_VARIANT_C, }, + { } +}; +MODULE_DEVICE_TABLE(of, si5351_dt_ids); + +static int si5351_dt_parse(struct i2c_client *client) +{ + struct device_node *child, *np = client->dev.of_node; + struct si5351_platform_data *pdata; + const struct of_device_id *match; + struct property *prop; + const __be32 *p; + int num = 0; + u32 val; + + if (np == NULL) + return 0; + + match = of_match_node(si5351_dt_ids, np); + if (match == NULL) + return -EINVAL; + + pdata = devm_kzalloc(&client->dev, sizeof(*pdata), GFP_KERNEL); + if (!pdata) + return -ENOMEM; + + pdata->variant = (enum si5351_variant)match->data; + pdata->clk_xtal = of_clk_get(np, 0); + if (!IS_ERR(pdata->clk_xtal)) + clk_put(pdata->clk_xtal); + pdata->clk_clkin = of_clk_get(np, 1); + if (!IS_ERR(pdata->clk_clkin)) + clk_put(pdata->clk_clkin); + + /* + * property silabs,pll-source : <num src>, [<..>] + * allow to selectively set pll source + */ + of_property_for_each_u32(np, "silabs,pll-source", prop, p, num) { + if (num >= 2) { + dev_err(&client->dev, + "invalid pll %d on pll-source prop\n", num); + return -EINVAL; + } + + p = of_prop_next_u32(prop, p, &val); + if (!p) { + dev_err(&client->dev, + "missing pll-source for pll %d\n", num); + return -EINVAL; + } + + switch (val) { + case 0: + pdata->pll_src[num] = SI5351_PLL_SRC_XTAL; + break; + case 1: + if (pdata->variant != SI5351_VARIANT_C) { + dev_err(&client->dev, + "invalid parent %d for pll %d\n", + val, num); + return -EINVAL; + } + pdata->pll_src[num] = SI5351_PLL_SRC_CLKIN; + break; + default: + dev_err(&client->dev, + "invalid parent %d for pll %d\n", val, num); + return -EINVAL; + } + } + + /* per clkout properties */ + for_each_child_of_node(np, child) { + if (of_property_read_u32(child, "reg", &num)) { + dev_err(&client->dev, "missing reg property of %s\n", + child->name); + return -EINVAL; + } + + if (num >= 8 || + (pdata->variant == SI5351_VARIANT_A3 && num >= 3)) { + dev_err(&client->dev, "invalid clkout %d\n", num); + return -EINVAL; + } + + if (!of_property_read_u32(child, "silabs,multisynth-source", + &val)) { + switch (val) { + case 0: + pdata->clkout[num].multisynth_src = + SI5351_MULTISYNTH_SRC_VCO0; + break; + case 1: + pdata->clkout[num].multisynth_src = + SI5351_MULTISYNTH_SRC_VCO1; + break; + default: + dev_err(&client->dev, + "invalid parent %d for multisynth %d\n", + val, num); + return -EINVAL; + } + } + + if (!of_property_read_u32(child, "silabs,clock-source", &val)) { + switch (val) { + case 0: + pdata->clkout[num].clkout_src = + SI5351_CLKOUT_SRC_MSYNTH_N; + break; + case 1: + pdata->clkout[num].clkout_src = + SI5351_CLKOUT_SRC_MSYNTH_0_4; + break; + case 2: + pdata->clkout[num].clkout_src = + SI5351_CLKOUT_SRC_XTAL; + break; + case 3: + if (pdata->variant != SI5351_VARIANT_C) { + dev_err(&client->dev, + "invalid parent %d for clkout %d\n", + val, num); + return -EINVAL; + } + pdata->clkout[num].clkout_src = + SI5351_CLKOUT_SRC_CLKIN; + break; + default: + dev_err(&client->dev, + "invalid parent %d for clkout %d\n", + val, num); + return -EINVAL; + } + } + + if (!of_property_read_u32(child, "silabs,drive-strength", + &val)) { + switch (val) { + case SI5351_DRIVE_2MA: + case SI5351_DRIVE_4MA: + case SI5351_DRIVE_6MA: + case SI5351_DRIVE_8MA: + pdata->clkout[num].drive = val; + break; + default: + dev_err(&client->dev, + "invalid drive strength %d for clkout %d\n", + val, num); + return -EINVAL; + } + } + + if (!of_property_read_u32(child, "clock-frequency", &val)) + pdata->clkout[num].rate = val; + + pdata->clkout[num].pll_master = + of_property_read_bool(child, "silabs,pll-master"); + } + client->dev.platform_data = pdata; + + return 0; +} +#else +static int si5351_dt_parse(struct i2c_client *client) +{ + return 0; +} +#endif /* CONFIG_OF */ + +static int si5351_i2c_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct si5351_platform_data *pdata; + struct si5351_driver_data *drvdata; + struct clk_init_data init; + struct clk *clk; + const char *parent_names[4]; + u8 num_parents, num_clocks; + int ret, n; + + ret = si5351_dt_parse(client); + if (ret) + return ret; + + pdata = client->dev.platform_data; + if (!pdata) + return -EINVAL; + + drvdata = devm_kzalloc(&client->dev, sizeof(*drvdata), GFP_KERNEL); + if (drvdata == NULL) { + dev_err(&client->dev, "unable to allocate driver data\n"); + return -ENOMEM; + } + + i2c_set_clientdata(client, drvdata); + drvdata->client = client; + drvdata->variant = pdata->variant; + drvdata->pxtal = pdata->clk_xtal; + drvdata->pclkin = pdata->clk_clkin; + + drvdata->regmap = devm_regmap_init_i2c(client, &si5351_regmap_config); + if (IS_ERR(drvdata->regmap)) { + dev_err(&client->dev, "failed to allocate register map\n"); + return PTR_ERR(drvdata->regmap); + } + + /* Disable interrupts */ + si5351_reg_write(drvdata, SI5351_INTERRUPT_MASK, 0xf0); + /* Set disabled output drivers to drive low */ + si5351_reg_write(drvdata, SI5351_CLK3_0_DISABLE_STATE, 0x00); + si5351_reg_write(drvdata, SI5351_CLK7_4_DISABLE_STATE, 0x00); + /* Ensure pll select is on XTAL for Si5351A/B */ + if (drvdata->variant != SI5351_VARIANT_C) + si5351_set_bits(drvdata, SI5351_PLL_INPUT_SOURCE, + SI5351_PLLA_SOURCE | SI5351_PLLB_SOURCE, 0); + + /* setup clock configuration */ + for (n = 0; n < 2; n++) { + ret = _si5351_pll_reparent(drvdata, n, pdata->pll_src[n]); + if (ret) { + dev_err(&client->dev, + "failed to reparent pll %d to %d\n", + n, pdata->pll_src[n]); + return ret; + } + } + + for (n = 0; n < 8; n++) { + ret = _si5351_msynth_reparent(drvdata, n, + pdata->clkout[n].multisynth_src); + if (ret) { + dev_err(&client->dev, + "failed to reparent multisynth %d to %d\n", + n, pdata->clkout[n].multisynth_src); + return ret; + } + + ret = _si5351_clkout_reparent(drvdata, n, + pdata->clkout[n].clkout_src); + if (ret) { + dev_err(&client->dev, + "failed to reparent clkout %d to %d\n", + n, pdata->clkout[n].clkout_src); + return ret; + } + + ret = _si5351_clkout_set_drive_strength(drvdata, n, + pdata->clkout[n].drive); + if (ret) { + dev_err(&client->dev, + "failed set drive strength of clkout%d to %d\n", + n, pdata->clkout[n].drive); + return ret; + } + } + + /* register xtal input clock gate */ + memset(&init, 0, sizeof(init)); + init.name = si5351_input_names[0]; + init.ops = &si5351_xtal_ops; + init.flags = 0; + if (!IS_ERR(drvdata->pxtal)) { + drvdata->pxtal_name = __clk_get_name(drvdata->pxtal); + init.parent_names = &drvdata->pxtal_name; + init.num_parents = 1; + } + drvdata->xtal.init = &init; + clk = devm_clk_register(&client->dev, &drvdata->xtal); + if (IS_ERR(clk)) { + dev_err(&client->dev, "unable to register %s\n", init.name); + return PTR_ERR(clk); + } + + /* register clkin input clock gate */ + if (drvdata->variant == SI5351_VARIANT_C) { + memset(&init, 0, sizeof(init)); + init.name = si5351_input_names[1]; + init.ops = &si5351_clkin_ops; + if (!IS_ERR(drvdata->pclkin)) { + drvdata->pclkin_name = __clk_get_name(drvdata->pclkin); + init.parent_names = &drvdata->pclkin_name; + init.num_parents = 1; + } + drvdata->clkin.init = &init; + clk = devm_clk_register(&client->dev, &drvdata->clkin); + if (IS_ERR(clk)) { + dev_err(&client->dev, "unable to register %s\n", + init.name); + return PTR_ERR(clk); + } + } + + /* Si5351C allows to mux either xtal or clkin to PLL input */ + num_parents = (drvdata->variant == SI5351_VARIANT_C) ? 2 : 1; + parent_names[0] = si5351_input_names[0]; + parent_names[1] = si5351_input_names[1]; + + /* register PLLA */ + drvdata->pll[0].num = 0; + drvdata->pll[0].drvdata = drvdata; + drvdata->pll[0].hw.init = &init; + memset(&init, 0, sizeof(init)); + init.name = si5351_pll_names[0]; + init.ops = &si5351_pll_ops; + init.flags = 0; + init.parent_names = parent_names; + init.num_parents = num_parents; + clk = devm_clk_register(&client->dev, &drvdata->pll[0].hw); + if (IS_ERR(clk)) { + dev_err(&client->dev, "unable to register %s\n", init.name); + return -EINVAL; + } + + /* register PLLB or VXCO (Si5351B) */ + drvdata->pll[1].num = 1; + drvdata->pll[1].drvdata = drvdata; + drvdata->pll[1].hw.init = &init; + memset(&init, 0, sizeof(init)); + if (drvdata->variant == SI5351_VARIANT_B) { + init.name = si5351_pll_names[2]; + init.ops = &si5351_vxco_ops; + init.flags = CLK_IS_ROOT; + init.parent_names = NULL; + init.num_parents = 0; + } else { + init.name = si5351_pll_names[1]; + init.ops = &si5351_pll_ops; + init.flags = 0; + init.parent_names = parent_names; + init.num_parents = num_parents; + } + clk = devm_clk_register(&client->dev, &drvdata->pll[1].hw); + if (IS_ERR(clk)) { + dev_err(&client->dev, "unable to register %s\n", init.name); + return -EINVAL; + } + + /* register clk multisync and clk out divider */ + num_clocks = (drvdata->variant == SI5351_VARIANT_A3) ? 3 : 8; + parent_names[0] = si5351_pll_names[0]; + if (drvdata->variant == SI5351_VARIANT_B) |